pre-update-cmd 脚本必须返回 0 才能继续 update 流程,否则中止于依赖解析前;需显式 exit(0),避免未捕获异常;动态改镜像须写入 auth.json 或本地 config;不可依赖 autoload;多脚本执行顺序不可靠,应合并为单脚本。

pre-update-cmd 脚本必须返回 0 才能继续执行 update
Composer 在遇到非零退出码时会直接中止 composer update,不会进入依赖解析阶段。这意味着你在脚本里做的任何检查(比如环境校验、镜像测速、配置验证)一旦失败,就得显式 exit 0 或确保逻辑不抛出未捕获异常——否则整个更新流程就卡住了。
常见错误现象:Script xxx handling the pre-update-cmd event returned with error code 1,然后命令直接退出,连 composer.json 的语法检查都没走完。
- PHP 脚本末尾加
exit(0)是最稳妥的做法,哪怕只是做日志输出 - 避免在脚本里调用
die()或未 catch 的throw new Exception() - 如果用 shell 命令(如
php -r "echo 'check';"),确保最后一句成功执行,否则 shell 默认返回上一条命令的 exit code
pre-update-cmd 中动态修改 repo.packagist 需写入 auth.json 或 config
想在 update 前自动切换 Packagist 镜像,不能只靠 putenv('COMPOSER_REPO_PACKAGIST=...') 或临时改 composer.json —— 这些对后续 Composer 主进程无效。真正起作用的是写入项目级 auth.json 或调用 composer config 修改本地配置。
推荐做法是让脚本执行:composer config --file auth.json repo.packagist https://mirrors.aliyun.com/composer/,或者直接写 JSON 到 auth.json 的 repositories 字段。注意路径必须是当前项目根目录,且 auth.json 需被 git 忽略(它可能含敏感信息)。
- 不要用
composer config repo.packagist ... --global,这会污染全局设置 - 如果脚本里用了 cURL 测速,记得设
CURLOPT_TIMEOUT_MS限制单个请求不超过 3000ms,避免卡死 - 测速结果建议缓存到
.mirror-cache.json,30 分钟内重复 update 可跳过重测
pre-update-cmd 不会触发 autoload dump,别在里头依赖新类
pre-update-cmd 运行时,vendor/autoload.php 还没重建,composer dump-autoload 是 post-update-cmd 或 post-autoload-dump 阶段才做的事。所以你不能在 pre-update 脚本里直接 new 一个刚 require 进来的第三方类(比如 new GuzzleHttp\Client()),除非你手动 require 它的文件或使用 include_once 加载。
更安全的做法是:把依赖逻辑抽离到独立 PHAR 或 vendor-bin 工具里,或干脆用纯 PHP+cURL 实现测速,避开 autoloading 问题。
- 不要在 pre-update 脚本里调用
bin/console cache:clear这类需要完整框架加载的命令 - 如果必须用外部库,提前用
composer require --dev guzzlehttp/guzzle并在脚本开头require __DIR__.'/../vendor/autoload.php' - 注意 PHP 版本兼容性:pre-update 脚本由当前 CLI PHP 执行,不是项目
platform.php指定的版本
多个 pre-update-cmd 脚本的执行顺序不可靠
Composer 对 scripts 数组里的多个命令不保证执行顺序,尤其当它们混用字符串和数组形式时(例如 "pre-update-cmd": ["cmd1", "cmd2"] vs "pre-update-cmd": "cmd1 && cmd2")。实际运行中可能 cmd2 先于 cmd1 结束,导致状态竞争。
如果你需要串行控制(比如先测速、再写配置、再通知),唯一可靠方式是把所有逻辑合并进一个 PHP 脚本,用明确的函数调用顺序组织。
- 避免在
scripts里写多个独立的 pre-update-cmd 键(如pre-update-cmd-a和pre-update-cmd-b),Composer 不识别这种命名 - 数组形式的脚本(
["php check.php", "php set-mirror.php"])按索引顺序执行,但每个子进程相互隔离,无法共享变量 - 调试时加
date("c")打点日志,比单纯看终端输出更能确认真实执行流


















