composer scripts 是 composer 在包生命周期事件中自动触发的钩子机制,本质是 shell 命令或 php 回调的映射表,非任务运行器或 ci 工具;它按「时机 + 命令」配置,不支持流程编排,仅在指定事件(如 post-autoload-dump)中执行,且默认不加载项目 autoloader。

composer scripts 是什么,不是什么
它不是任务运行器(比如 npm run 或 make),也不是 CI 工具;它是 Composer 在安装、更新、卸载包时自动触发的钩子机制,本质是一组 shell 命令或 PHP 回调的映射表。你配的是「时机 + 命令」,不是「流程编排」。
常见错误现象:composer install 没执行你写的 post-install-cmd;或者脚本里用了 phpunit,但本地没装全局,报 Command "phpunit" not found。
- 脚本只在 Composer 自身生命周期事件中触发(如
pre-autoload-dump、post-root-package-install),不会响应git commit或文件变化 - 默认以 shell 方式执行,不自动加载项目 autoloader —— 想调用项目内 PHP 类,得显式 require 或用
php -f - 所有命令在项目根目录下运行,路径别写相对
./src/...,除非你确认上下文
怎么写一个可靠又可复用的 script
核心原则:尽量用 php 调用单文件入口,而不是拼接长 shell 命令。这样可控、可调试、易测试。
示例:你想在 post-autoload-dump 生成 classmap 缓存
"scripts": { "post-autoload-dump": [ "php -f build/generate-classmap.php" ] }
而不是:
"scripts": { "post-autoload-dump": "php -d memory_limit=-1 vendor/bin/classmap-generator --output=vendor/composer/autoload_classmap.php" }
- 前者把逻辑收口到
build/generate-classmap.php,可以加日志、try/catch、版本判断 - 后者依赖外部二进制,一旦
vendor/bin/classmap-generator不存在或权限不对就静默失败 - 所有 script 命令默认继承 Composer 进程的环境变量,但
$_ENV不一定包含你期望的值(比如 CI 中的CI=true)—— 显式传参更稳:"php build/check-env.php --env=prod"
常见事件触发时机与陷阱
不是所有事件都适合干你想干的事。比如想“每次 install 后清空缓存”,用 post-install-cmd 看似合理,但实际它只在 composer install 首次运行时触发,composer install 重跑(无 lock 变更)时根本不会进这个钩子。
-
post-autoload-dump:最常用,autoloader 重建后必走,适合生成代理类、扫描注解、刷新缓存 -
post-root-package-install:仅在 root 包(即你的项目)首次安装时触发,不适合常规自动化 -
pre-update-cmd和post-update-cmd:对应composer update全局操作,注意它可能跨多个包更新,别在里面做强依赖单个包的逻辑 - 事件名大小写敏感:
postAutoloadDump❌,必须是post-autoload-dump✅
调试脚本失败的三步法
Composer 不会默认输出脚本 stderr,失败时只报一句 Script ... handling the ... Event returned with Error code 1,非常难定位。
- 加
-vvv参数重跑:composer install -vvv,能看到完整命令和输出 - 在 script 命令前加
set -x;(shell)或error_reporting(E_ALL); ini_set('display_errors', '1');(PHP),强制暴露执行路径 - 把 script 改成临时调用一个带
var_dump(getcwd(), $_SERVER['argv']); exit;的 PHP 文件,确认当前工作目录和参数是否符合预期
最容易被忽略的一点:script 命令里的 && 或 || 会让整个链路变成一个 shell 表达式,一旦中间某步非零退出,后续不会执行,但 Composer 仍认为“脚本运行完毕”——它只看最后一条命令的返回值。真要链式执行,拆成数组项,让 Composer 自己串行调用。