Composer scripts需严格遵循执行时机、环境变量、shell行为和返回值规范:必须注册在composer.json的scripts字段,键名合法;多命令用&&连接;PHP回调须静态且可自动加载;注意钩子触发条件、路径与环境变量陷阱;调试时用-v参数和set -x。

Composer 的 scripts 不是“写完就能跑”的黑盒,它依赖执行时机、环境变量、shell 行为和脚本返回值,稍不注意就会静默失败或行为错乱。
如何定义一个真正可触发的自定义脚本
脚本必须注册在 composer.json 的 scripts 字段下,且键名不能含空格或特殊字符(如 post-install-cmd 是合法钩子,但 build:dev server 会解析失败)。
实操建议:
- 普通命令脚本直接写字符串:
"ci-test": "phpunit --no-coverage" - 多命令用分号或
&&连接(注意 shell 兼容性):"dev-start": "composer install && php -S localhost:8000 -t public" - PHP 回调必须是静态方法,且类需能被自动加载:
"post-autoload-dump": ["My\\Script::onAutoload"] - 避免在脚本里写交互式命令(如
read),因为 Composer 默认以非交互模式运行
为什么 post-root-package-install 不起作用
这个钩子只在根项目(即你当前 composer.json 所在项目)首次安装时触发,且仅当该包未被作为依赖引入其他项目时才生效。CI 环境或 composer create-project 场景下容易误判触发条件。
常见错误现象:
- 本地
composer install没执行预期逻辑 → 实际触发的是post-install-cmd,不是post-root-package-install - 用
composer require xxx添加依赖后脚本没跑 → 它属于post-update-cmd范畴 - 脚本中用了
$_SERVER['argv']获取参数 → Composer 不透传 CLI 参数给钩子回调,要用$event->getArguments()(仅限 PHP 回调)
scripts 中的环境变量和路径陷阱
Composer 执行脚本时工作目录始终是项目根目录(即含 composer.json 的目录),但 $PATH 可能不含 vendor/bin,导致调用 php-cs-fixer 等二进制失败。
实操建议:
- 显式使用
vendor/bin/php-cs-fixer而非裸命名单词 - 需要读取项目配置时,别硬编码
./config/app.php,改用__DIR__ . '/config/app.php'(PHP 回调中)或$(pwd)/config/app.php(shell 中) -
COMPOSER_DEV_MODE环境变量仅在--dev或无--no-dev时为 true,但composer install --no-dev下所有require-dev工具不可用,脚本应提前检查是否存在 - Windows 下慎用单引号包裹命令,CMD 不识别,优先用双引号或拆成 PHP 回调
如何调试 scripts 执行失败却无报错
默认情况下 Composer 对脚本错误输出做了截断,尤其 shell 命令 exit code 非 0 时可能只显示 Script ... handling the ... event returned with error code 1,看不到具体哪行崩了。
关键排查点:
- 加
-v参数重试:composer run-script ci-test -v,会显示完整命令和 stderr - 在 shell 脚本开头加
set -x(bash/zsh)或@echo on(Windows CMD)开启命令回显 - PHP 回调中不要依赖
echo输出日志,改用$event->getIO()->writeError('...')确保可见 - 注意权限问题:某些脚本生成文件后,后续命令因权限不足读不到(如 Docker 中 uid/gid 不匹配)
最常被忽略的是脚本返回值——哪怕只是 echo "done" 后忘了 exit 0,shell 脚本也会继承上一条命令的退出码,导致整个 Composer 流程中断。这点在组合多个命令时尤其致命。










