scripts 是 composer.json 中定义命令别名的字段,用于通过 composer run 触发轻量任务,自动继承项目根目录、跨平台调用并支持 vendor/bin 下二进制文件。

scripts 是什么,为什么不能直接写 shell 脚本
scripts 是 composer.json 里的一个字段,用于定义可被 composer run 或 composer run-script 触发的命令别名。它不是 shell 脚本的替代品,而是 Composer 的“任务注册表”——所有条目最终都会被解析为 shell 命令执行,但关键在于:它们自动继承项目根目录为工作路径、能跨平台调用(Windows/macOS/Linux)、且天然支持依赖包中已安装的二进制文件(比如 vendor/bin/phpunit)。
常见错误是把复杂逻辑全塞进单行 scripts 字段,结果遇到引号转义、多行条件判断或环境变量传递失败。这不是 scripts 的设计目标;它适合轻量封装,重逻辑请交给独立 PHP/Shell 脚本,再从 scripts 中调用。
怎么定义和运行自定义脚本命令
在 composer.json 的 scripts 对象里加键值对,key 是命令名,value 是要执行的命令字符串:
"scripts": {
"test": "phpunit --colors=always",
"build": "php build.php",
"post-install-cmd": "chmod +x bin/deploy.sh && ./bin/deploy.sh"
}
运行方式有三种:
-
composer run test—— 推荐,语义清晰,支持参数透传(如composer run test -- --filter=MyTest) -
composer run-script test—— 兼容旧版写法,但不支持参数透传,慎用 - 触发钩子(如
post-install-cmd)—— Composer 在对应生命周期自动执行,无需手动调用
注意:scripts 中的命令默认在项目根目录执行,不 cd 进子目录;若需指定路径,必须显式写 cd path && cmd。
怎么让脚本支持参数和环境变量
scripts 本身不解析参数占位符(比如 $1),但可通过 composer run 的 -- 分隔符把参数原样透传给底层命令:
"scripts": {
"lint": "php -l"
}
执行 composer run lint -- src/MyClass.php,实际运行的是 php -l src/MyClass.php。
环境变量需显式导出或内联声明:
- Linux/macOS:
"dev-start": "APP_ENV=dev php -S localhost:8000 router.php" - Windows(PowerShell):
"dev-start": "cmd /c \"set APP_ENV=dev && php -S localhost:8000 router.php\"" - 更可靠的方式是写个
bin/start-dev.php,用getenv()或$_SERVER读取,再从scripts调用它
别依赖 $_ENV —— 它在 CLI 下默认为空,除非你手动 putenv() 或用 symfony/dotenv 加载。
钩子(hooks)执行时机与常见陷阱
Composer 提供了预置钩子名,如 pre-install-cmd、post-autoload-dump、post-root-package-install。它们按固定顺序触发,且只在对应 Composer 命令运行时生效(比如 post-install-cmd 不会在 composer update 时运行,除非你显式配置了 post-update-cmd)。
容易踩的坑:
- 钩子命令失败会导致整个 Composer 命令中断(除非加
|| true,但不推荐掩盖问题) -
post-autoload-dump在autoload.php生成后触发,适合做 classmap 验证或生成代理类,但此时vendor/autoload.php还没被 require,不能直接 new 第三方类 - 多个钩子同名时(比如两个包都声明
post-autoload-dump),执行顺序由包加载顺序决定,不可预测;应避免在非 root 包中定义全局钩子
真正需要稳定执行时机的任务(如生成配置、检查权限),建议统一收口到一个明确命名的脚本(如 setup),再让团队成员养成 composer install && composer run setup 的习惯,而不是依赖钩子。










