Skip to content

依赖管理策略

Composer 是 PHP 生态的标准依赖管理工具,它自动处理包的安装、更新和自动加载。合理的依赖管理策略能够确保项目稳定性、安全性和可维护性。本节将深入讲解 Composer 版本锁定、私有仓库、平台要求以及生产与开发依赖的划分策略。

前置知识

阅读本节前,建议先了解:项目目录结构PHP 8 新特性

基础概念

Composer 的核心文件

文件作用是否提交到 Git
composer.json项目依赖声明
composer.lock依赖版本锁定
vendor/已安装的包
composer.pharComposer 可执行文件

Composer 版本约束

版本约束格式

json
{
    "require": {
        "php": "^8.1",
        "monolog/monolog": "^2.0",
        "guzzlehttp/guzzle": "^7.5",
        "symfony/console": "~6.0",
        "doctrine/orm": ">=3.0",
        "laravel/framework": ">=10.0 <11.0",
        "ext-json": "*",
        "ext-mbstring": "*",
        "ext-pdo": "*"
    }
}

版本约束详解

text
精确版本:     "1.2.3"         仅匹配 1.2.3
范围版本:     ">=1.0 <2.0"    匹配 1.0 到 2.0 之间(不含 2.0)
波浪号:       "~1.2"          匹配 >=1.2 <2.0(次版本锁定)
波浪号:       "~1.2.3"        匹配 >=1.2.3 <1.3.0(补丁版本锁定)
脱字符:       "^1.2.3"        匹配 >=1.2.3 <2.0.0(语义化版本兼容)
星号:         "*"             任意版本(不推荐)
json
{
    "require": {
        "psr/log": "^1.1",          // 1.1.x ~ 1.x.x(< 2.0)
        "symfony/console": "^6.0",  // 6.0.x ~ 6.x.x(< 7.0)
        "guzzlehttp/guzzle": "~7.5", // 7.5.x ~ 7.x.x(< 8.0)
        "phpunit/phpunit": ">=10.0 <11.0",  // 精确范围
        "my/package": "dev-main",   // 开发分支
        "my/package": "dev-main#abc1234"  // 特定 commit
    }
}

stability 配置

json
{
    "minimum-stability": "stable",
    "prefer-stable": true,
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/my/private-repo"
        }
    ]
}

composer.json 完整配置

项目模板配置

json
{
    "name": "my-company/my-project",
    "description": "My awesome PHP project",
    "version": "1.0.0",
    "type": "project",
    "keywords": ["php", "web", "api"],
    "license": "MIT",
    "authors": [
        {
            "name": "Developer Name",
            "email": "dev@example.com"
        }
    ],
    "support": {
        "email": "support@example.com",
        "issues": "https://github.com/my-company/my-project/issues"
    },

    "require": {
        "php": "^8.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "ext-json": "*",
        "ext-mbstring": "*",
        "ext-openssl": "*",
        "ext-pdo": "*",
        "guzzlehttp/guzzle": "^7.5",
        "monolog/monolog": "^2.0|^3.0",
        "nesbot/carbon": "^2.62",
        "ramsey/uuid": "^4.5",
        "symfony/console": "^6.0",
        "symfony/dotenv": "^6.0",
        "symfony/yaml": "^6.0",
        "vlucas/phpdotenv": "^5.4"
    },

    "require-dev": {
        "friendsofphp/php-cs-fixer": "^3.15",
        "nunomaduro/larastan": "^2.0",
        "phpstan/phpstan": "^1.10",
        "phpstan/phpstan-symfony": "^1.10",
        "phpunit/phpunit": "^10.0",
        "squizlabs/php_codesniffer": "^3.7",
        "symfony/phpunit-bridge": "^6.0"
    },

    "autoload": {
        "psr-4": {
            "App\\": "src/"
        },
        "psr-0": {
            "Legacy_": "src/Legacy/"
        },
        "classmap": [
            "src/functions.php"
        ],
        "files": [
            "src/helpers.php"
        ]
    },

    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },

    "autoload-suggestions": {
        "ext-pcntl": "For better signal handling in long-running processes",
        "ext-posix": "For process control functions"
    },

    "scripts": {
        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ],
        "auto-scripts": {
            "cache:clear": "symfony-cmd",
            "assets:install --symlink --relative": "symfony-cmd"
        },
        "test": "phpunit",
        "test:coverage": "phpunit --coverage-html coverage/",
        "phpstan": "phpstan analyse src/ --level=8",
        "cs-check": "php-cs-fixer fix --dry-run --diff",
        "cs-fix": "php-cs-fixer fix",
        "check": [
            "@cs-check",
            "@phpstan",
            "@test"
        ]
    },

    "scripts-descriptions": {
        "test": "Run unit tests",
        "phpstan": "Run PHPStan static analysis",
        "cs-check": "Check coding style",
        "cs-fix": "Fix coding style issues",
        "check": "Run all quality checks"
    },

    "config": {
        "optimize-autoloader": true,
        "preferred-install": {
            "*": "dist"
        },
        "sort-packages": true,
        "allow-plugins": {
            "composer/package-versions-deprecated": true,
            "symfony/flex": true
        },
        "platform": {
            "php": "8.1.0"
        },
        "platform-check": true
    },

    "extra": {
        "symfony": {
            "allow-contrib": false,
            "require": "6.0.*"
        },
        "branch-alias": {
            "dev-main": "1.0.x-dev"
        }
    },

    "bin": [
        "bin/my-app"
    ],

    "archive": {
        "exclude": ["/tests", "/.github", "/docs"]
    }
}

私有仓库配置

Satis 自建仓库

bash
# 安装 Satis
composer create-project composer/satis --stability=dev satis-repo

# satis.json 配置
cat > satis-repo/satis.json << 'EOF'
{
    "name": "My Company Packages",
    "homepage": "https://packages.example.com",
    "repositories": [
        { "type": "vcs", "url": "https://github.com/my-company/package-core" },
        { "type": "vcs", "url": "https://github.com/my-company/package-http" },
        { "type": "vcs", "url": "https://github.com/my-company/package-db" }
    ],
    "require-all": true,
    "archive": {
        "directory": "dist",
        "format": "tar",
        "prefix-url": "https://packages.example.com/dist",
        "skip-dev": true
    }
}
EOF

# 构建
php satis-repo/bin/satis build satis-repo/satis.json satis-repo/web/

使用私有仓库

json
{
    "repositories": [
        {
            "type": "composer",
            "url": "https://packages.example.com"
        },
        {
            "type": "vcs",
            "url": "https://github.com/my-company/private-package"
        },
        {
            "type": "svn",
            "url": "https://svn.example.com/my-package/trunk"
        }
    ]
}

认证配置

bash
# ~/.composer/auth.json
{
    "github-oauth": {
        "github.com": "ghp_xxxxxxxxxxxx"
    },
    "http-basic": {
        "packages.example.com": {
            "username": "deploy-token",
            "password": "token-value"
        },
        "repo.example.com": {
            "username": "git",
            "password": "personal-access-token"
        }
    },
    "bearer": {
        "packages.example.com": "api-token"
    }
}

不要将认证信息提交到 Git

auth.json 应放在 ~/.composer/ 目录中,而非项目目录。CI 环境使用环境变量或 secret 管理认证。

平台要求

PHP 版本管理

json
{
    "require": {
        "php": "^8.1"
    },
    "config": {
        "platform": {
            "php": "8.1.20"
        },
        "platform-check": true
    }
}

PHP 扩展要求

json
{
    "require": {
        "ext-ctype": "*",
        "ext-curl": "*",
        "ext-dom": "*",
        "ext-fileinfo": "*",
        "ext-filter": "*",
        "ext-gd": "*",
        "ext-iconv": "*",
        "ext-intl": "*",
        "ext-json": "*",
        "ext-mbstring": "*",
        "ext-openssl": "*",
        "ext-pdo": "*",
        "ext-session": "*",
        "ext-simplexml": "*",
        "ext-soap": "*",
        "ext-sodium": "*",
        "ext-tokenizer": "*",
        "ext-xml": "*",
        "ext-xmlwriter": "*",
        "ext-zlib": "*"
    }
}

扩展版本约束

某些扩展支持版本约束,如 "ext-simplexml": "^0.1"。大多数情况下使用 "*" 即可。

生产依赖 vs 开发依赖

依赖分类原则

json
{
    "require": {
        "guzzlehttp/guzzle": "^7.5",
        "monolog/monolog": "^2.0",
        "symfony/console": "^6.0",
        "nesbot/carbon": "^2.62"
    },
    "require-dev": {
        "phpunit/phpunit": "^10.0",
        "phpstan/phpstan": "^1.10",
        "friendsofphp/php-cs-fixer": "^3.15",
        "squizlabs/php_codesniffer": "^3.7",
        "symfony/var-dumper": "^6.0",
        "fakerphp/faker": "^1.20",
        "brianium/paratest": "^7.0"
    }
}

安装与部署

bash
# 开发环境:安装所有依赖(含 dev)
composer install

# 生产环境:仅安装生产依赖
composer install --no-dev --optimize-autoloader --no-interaction --no-progress

# 使用 classmap 优化自动加载(更快)
composer dump-autoload --optimize --classmap-authoritative --no-dev

# 生产环境检查平台要求
composer check-platform-reqs

Composer 脚本

实用脚本配置

json
{
    "scripts": {
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate"
        ],
        "post-install-cmd": [
            "App\\Console\\ComposerScripts::postInstall"
        ],
        "post-update-cmd": [
            "App\\Console\\ComposerScripts::postUpdate"
        ],

        "dev": [
            "Composer\\Config::disableProcessTimeout",
            "@composer install --prefer-dist"
        ],
        "fresh": [
            "@composer install --prefer-dist",
            "@php artisan key:generate",
            "@php artisan migrate:fresh --seed"
        ],

        "test": "phpunit",
        "test:unit": "phpunit --testsuite=Unit",
        "test:feature": "phpunit --testsuite=Feature",
        "test:parallel": "paratest --processes=4",

        "phpstan": "phpstan analyse --memory-limit=512M",
        "phpstan:baseline": "phpstan analyse --generate-baseline=phpstan-baseline.neon",

        "cs:check": "php-cs-fixer fix --dry-run --diff --using-cache=no",
        "cs:fix": "php-cs-fixer fix --using-cache=no",

        "quality": [
            "@cs:check",
            "@phpstan",
            "@test"
        ]
    }
}

Composer 常用命令

bash
# 安装依赖
composer install                    # 根据 lock 文件安装
composer update                      # 更新所有依赖
composer update package/name          # 更新特定包
composer update --prefer-dist         # 优先下载 dist 包
composer update --with-all-dependencies  # 更新及其依赖

# 添加依赖
composer require vendor/package       # 添加到 require
composer require --dev vendor/package  # 添加到 require-dev

# 移除依赖
composer remove vendor/package       # 从 require 移除
composer remove --dev vendor/package  # 从 require-dev 移除

# 诊断
composer diagnose                    # 诊断 Composer 问题
composer show                         # 显示已安装包信息
composer show vendor/package --tree   # 显示包的依赖树
composer depends vendor/package       # 查看谁依赖了这个包
composer why vendor/package           # 同上
composer outdated                     # 列出可更新的包
composer licenses                     # 显示许可证信息

# 自动加载
composer dump-autoload               # 重新生成自动加载
composer dump-autoload -o             # 优化自动加载
composer dump-autoload -a             # classmap 权威模式(生产推荐)

# 其他
composer validate                     # 验证 composer.json
composer run-script test              # 运行自定义脚本
composer global require phpunit/phpunit  # 全局安装

实战示例

多环境 composer.json 管理

bash
# 开发环境使用 .env.local 中的 PHP 版本
# CI 环境通过 platform-check 确保平台兼容

# 检查平台要求
composer check-platform-reqs
# 输出示例:
# php          8.1.20     success
# ext-curl     7.68.0     success
# ext-json     8.1.20     success
# ext-mbstring on         success

注意事项

常见问题

必须提交 composer.lock

composer.lock 必须提交到版本控制。忽略 lock 文件会导致团队成员安装不同版本的依赖,造成不一致行为。

  • composer.lock 必须提交到 Git,确保所有环境使用相同的依赖版本
  • 生产环境使用 --no-dev 跳过开发依赖
  • 定期运行 composer outdated 检查安全更新
  • 使用 prefer-dist 加快安装速度(下载 zip 而非克隆 Git)
  • 依赖版本约束不要太宽松(避免 *),也不要太严格(避免精确版本)

最佳实践

  1. 锁定版本:始终提交 composer.lock,生产环境使用 composer install
  2. 区分依赖:生产依赖和开发依赖严格分开
  3. 平台检查:配置 platform-check 确保环境兼容
  4. 优化加载:生产环境使用 --optimize-autoloader --classmap-authoritative
  5. 定期更新:每月检查依赖更新和安全补丁
  6. 最小化依赖:仅添加真正需要的包,减少攻击面

下一节

继续学习:PHP-FPM 调优

参考链接