
scripts 里写的命令为什么没执行
Composer 的 scripts 不是写完就自动跑的,它只在你明确触发时才生效——比如运行 composer install、composer update,或手动调用 composer run-script xxx。很多人以为加进 composer.json 就“一劳永逸”,结果部署后脚本静默失效。
- 默认只有
post-install-cmd、post-update-cmd这类带post-/pre-前缀的钩子才会自动触发;普通脚本名(如"build": "npm run build")必须手动运行composer run-script build - 如果用了
--no-scripts(CI 环境常见),所有脚本都会被跳过,连post-*钩子也不执行 - 脚本运行路径是项目根目录,但当前工作目录(
getcwd())可能不是——尤其在 symlink 或 Docker 挂载场景下,./bin/build.sh可能找不到
怎么让 Composer 在 install/update 时自动跑自定义命令
靠 post-install-cmd 和 post-update-cmd 这两个标准钩子。它们是 Composer 内置识别的 key,不是随便起个名就能自动触发的。
- 在
composer.json的scripts字段下,直接写:"scripts": { "post-install-cmd": ["@php artisan optimize:clear", "npm ci --silent"], "post-update-cmd": ["@php artisan migrate --force"] } - 注意:数组里每项都是独立命令,顺序执行;用
@xxx可复用其他脚本(如@php会调用同文件中定义的php脚本) - 如果命令依赖扩展(比如
php要用mbstring),而当前 PHP CLI 环境没启用,脚本会直接报错退出,且不中断主流程(除非加--no-dev或配置了COMPOSER_NO_INTERACTION=1)
PHP 脚本 vs shell 命令:该选哪个写进 scripts
优先写 PHP 脚本,尤其是涉及逻辑判断、环境检测或 Composer 自身 API 调用时。Shell 命令看着快,但跨平台和错误处理极弱。
- PHP 脚本可读取
Composer\Script\Event对象,拿到当前命令、IO、composer 实例等上下文,比如判断是不是在 CI 上:$event->isDevMode() - Shell 命令在 Windows 下容易崩(
rm -rf、$(pwd)不兼容),而php clean.php一行就搞定 - 别在 scripts 里写长 shell 链式调用(如
git status | grep -q 'dirty' && exit 1),出错时只报 “Script xxx returned with error code 1”,根本看不出哪步挂了
本地开发和 CI 环境下 scripts 表现不一致
核心差异在 PHP 版本、扩展、PATH 和用户权限。同一个 post-install-cmd 在本地成功,在 GitHub Actions 却卡住,大概率是环境假设错了。
- CI 默认用最小化 PHP(常缺
openssl、curl),而你的脚本里用了file_get_contents('https://...'),就会超时失败 -
composer install --no-dev时,require-dev里的包不可用,但脚本仍会执行——如果脚本里调用了phpunit,就会报 command not found - 某些命令(如
chmod +x bin/*)在 macOS/Linux 正常,在 Windows Git Bash 下可能静默失败,且不报错
真正难搞的不是语法,是脚本执行时的隐式上下文:PHP 配置、扩展状态、文件权限、网络策略、甚至终端是否支持 ANSI 颜色——这些都不会出现在 composer.json 里,但每一项都可能让脚本在某个环境里突然哑火。










