ci中必须用composer install而非update,因其严格按已提交的composer.lock还原精确版本、顺序与依赖树,确保构建可重现;update会重算依赖、生成新lock,绕过代码审查引发bc break或fatal error,且需配合--no-dev、--optimize-autoloader、--classmap-authoritative三参数及缓存~/.composer/cache。

CI 里必须用 composer install,绝不能用 composer update——否则构建不可重现,线上行为随时漂移。
为什么 composer install 是唯一安全选项
CI 不是开发机,它不参与“选版本”的决策。composer update 会重算整个依赖树、生成新 composer.lock,等于绕过代码审查偷偷升级包。昨天通过的流水线,今天可能因 monolog/monolog 的一个 patch 版本引入 BC break 或 Fatal error: Class 'PHPUnit\Framework\TestCase' not found。
composer install 则严格按已提交的 composer.lock 还原:版本、顺序、嵌套关系全部锁定。前提是 Git 仓库里必须有它——CI 不会帮你生成,没这个文件,命令直接报错退出。
- 常见错误现象:
Root package 'xxx' cannot be found,大概率是漏提composer.lock - 多包项目(monorepo)中,每个子包目录下都得有自己独立的
composer.lock,否则install会自动退化为update - GitLab / GitHub Actions 默认拉取 clean checkout,只靠
composer.lock启动安装
composer install 必须加的三个生产参数
漏掉任一个,轻则 autoload 慢 2–3 倍,重则运行时崩溃。它们不是可选项,是生产构建的底线:
-
--no-dev:跳过require-dev里的包。否则phpunit、symfony/debug-bundle会打进镜像,还可能触发class_exists('PHPUnit\Framework\TestCase')这类运行时 fatal -
--optimize-autoloader(或-o):生成vendor/composer/autoload_classmap.php,把 PSR-4 映射编译成静态数组,跳过每次 class 查找时的文件系统遍历 -
--classmap-authoritative:告诉 autoloader “classmap 里没有的类,就真的不存在”,彻底禁用file_exists()探测——既提速,又防因符号链接或大小写问题误加载
正确写法:composer install --no-dev --optimize-autoloader --classmap-authoritative --no-interaction
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
缓存该缓谁?别碰 vendor/,要缓 ~/.composer/cache
缓存 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: { key: "$CI_COMMIT_REF_SLUG", paths: ["~/.composer/cache"] },别把vendor/加进paths - 缓存失效最常见原因:
composer.lock被本地手动生成但没提交,导致 key 不匹配,每次走冷安装
CI 中常见失败原因和应对点
不是命令写错,而是隐性前提不满足:
-
composer.lock未提交:CI 拉下来的代码里没有它,composer install直接报错退出 - PHP 扩展缺失:比如
ext-zip、ext-pdo、ext-openssl没装,尤其 Alpine 镜像需手动apk add php81-zip - PHP 版本不匹配:
composer.json中"php": "^8.1",但 CI 使用的是 8.0,部分包会被跳过或静默失败 - 内存不足:Composer 解析依赖图时峰值内存高,CI 默认限制常触发
Killed或Allowed memory size exhausted;应在脚本开头加export COMPOSER_MEMORY_LIMIT=-1 - 私有包拉取失败(如
Project not found or you don't have access):基本是因为没配 SSH Agent 或 deploy key,不是缓存问题
复杂点在于:这些条件不是孤立的。比如漏了 --no-dev,又恰好代码里有 class_exists('PHPUnit\Framework\TestCase'),就会在运行时才崩——而 CI 日志里根本看不出关联。










