
CI 里必须用 composer install,不是 composer update —— 否则构建不可重现,线上行为就可能和本地、测试环境不一致。
为什么 CI 脚本里写 composer update 是危险操作
CI 不是开发机,它不负责“选版本”,只负责“装对的版本”。composer update 会重算整个依赖树、生成新 composer.lock,等于绕过代码审查:你 merge 的是旧 lock,CI 却装了新包,可能引入 BC break 或安全漏洞。
常见错误现象:
流水线昨天成功,今天失败,报错来自一个没改过的包(比如 monolog/monolog 升了 patch)测试通过,但部署后抛 Fatal error: Class ‘PHPUnit\Framework\TestCase’ not found(因为 –no-dev 没加,但 dev 包又被 update 拉进来了)GitLab CI 报 Root package ‘xxx’ cannot be found —— 实际是没提交 composer.lock,update 又被禁用了 fallback 逻辑
composer install 必须配齐的三个参数
漏掉任一个,轻则 autoload 慢 2–3 倍,重则运行时崩溃。它们不是可选项,是生产构建的底线:
–no-dev:跳过 require-dev 里的包。否则 phpunit、symfony/debug-bundle 会打进镜像,还可能触发 class_exists(‘PHPUnit\Framework\TestCase’) 这类运行时 fatal–optimize-autoloader:生成 vendor/composer/autoload_classmap.php,把 PSR-4 映射编译成静态数组,跳过每次 class 查找时的文件系统遍历–classmap-authoritative:告诉 autoloader “classmap 里没有的类,就真的不存在”,彻底禁用 file_exists() 探测 —— 既提速,又防因符号链接或大小写问题误加载
正确写法:composer install –no-dev –optimize-autoloader –classmap-authoritative –no-interaction
缓存该缓谁?别碰 vendor/ 直接缓
缓存 vendor/ 看似快,实则高危:不同 PHP 版本、不同 composer.lock 哈希下复用 vendor/,会导致 Class not found、autoloader 错乱,甚至测试通过、线上炸锅。
真正该缓的是 Composer 自己的下载缓存目录:~/.composer/cache(默认路径)。它存的是 ZIP 包、git clone 缓存、metadata —— 跟环境无关,只跟 composer.lock 哈希绑定。
实操建议:
GitHub Actions:用 actions/cache@v4,path 设为 ~/.composer/cache,key 用 ${{ runner.os }}-php-${{ matrix.php-version }}-composer-${{ hashFiles(‘**/composer.lock’) }}GitLab CI:在 cache: 下写 paths: ["~/.composer/cache"],别把 vendor/ 写进去如果非得缓 vendor/(比如极短构建窗口),必须用 composer.lock 哈希 + PHP 版本做二级 key,且确保所有 job 的 platform 配置完全一致
CI 报错卡在 Lock file operations: 1 install, 0 updates, 0 removals 怎么办
这不是命令写错了,大概率是 PHP 扩展缺失。Composer 在安装前会校验扩展可用性,但错误不显式抛出,只静默卡住或跳过关键步骤。
高频原因:
没装 ext-zip:导致无法解压 dist ZIP 包,install 卡死或退化为 git clone(慢且不稳定)没装 ext-curl 或 ext-openssl:连不上 packagist,尤其用了私有源时更易暴露PHP 版本低于 composer.json 中 config.platform.php 声明的最低版本,部分包会跳过安装,但不报错
验证方法:在 CI job 开头加一行 php -m | grep -E "zip|curl|openssl",确认关键扩展已启用。

评论(0)