pre-install-cmd 是 Composer 在真正执行依赖安装前触发的生命周期钩子,仅当 vendor 不存在或 composer.lock 变更时运行;需置于 composer.json 顶层 scripts 下、值为数组,且不启用 --no-scripts 才生效。

pre-install-cmd 是什么,它到底在什么时候执行
pre-install-cmd 是 Composer 内置的生命周期事件钩子,不是命令也不是插件,它会在 composer install 命令真正开始解析依赖、下载或解包前触发——但仅当本次 install 实际要执行安装动作时才运行。如果 vendor/ 已存在且 composer.lock 未变更,Composer 会跳过整个安装流程,连 pre-install-cmd 都不会执行。
- 它不等同于“每次敲下 composer install 就跑”,而是“每次真正装包前才跑”
- 触发前提是:没加
--no-scripts,Composer 版本 ≥ 1.10(推荐 ≥ 2.2) - 它和
pre-update-cmd是对称设计,但两者触发条件不同:pre-install-cmd关注 vendor 是否缺失/lock 是否变化;pre-update-cmd只要看 lock 是否需重算 - 常见误判场景:本地反复运行
composer install,输出里没看到脚本执行日志 → 其实是被 Composer 静默跳过了,不是配置失效
怎么写才有效:scripts 字段位置和值格式要求
pre-install-cmd 必须直接写在项目根目录 composer.json 的顶层 scripts 字段下,和 autoload、require 同级;不能塞进 extra、config 或任何嵌套对象里。
- 值必须是数组(从 Composer 5.6+ 起强制要求),例如:
["php scripts/check-php-version.php", "@php -r "echo 'pre-install OK\n';""] - 字符串单值写法(如
"php check.php")会被静默忽略,不报错也不执行 - 数组中每项顺序执行;任意一项 exit code ≠ 0,后续全部中止,
composer install整体失败退出 - 以
@开头的命令(如@php)由 Composer 自行解析为当前 PHP 可执行路径,比硬写php更可靠,尤其在多 PHP 版本环境
为什么脚本没运行?常见失效原因和验证方法
最常被忽略的是“根本没走到触发点”,而不是配置写错了。先确认是否真该触发,再查配置。
- 运行
composer install -v,观察输出里有没有Executing script行;没有就说明跳过了整个流程 - 检查是否启用了
--no-scripts,或设置了环境变量COMPOSER_NO_INTERACTION=1(某些 CI 环境默认启用) - 脚本里调用的 PHP 类(如
MyToolChecker::run)必须能被自动加载——即类文件已在autoload中声明,否则报Class not found且无明确提示 - 拼写错误:写成
pre-install、preinstall-cmd、post-install-cmd都不会触发;必须严格匹配pre-install-cmd - Windows 下路径分隔符写死为
可能导致脚本找不到文件;统一用/或DIRECTORY_SEPARATOR
适合放什么逻辑?别踩的典型坑
pre-install-cmd 的定位是“前置校验与准备”,不是部署或生成任务。它的执行时机早于 autoload 生成、vendor 解包,所以不能依赖 vendor/autoload.php,也不能假设 config/ 或 storage/ 目录已存在。
- 适合做:PHP 版本/扩展检查、环境变量是否存在、.env 文件模板复制、清理旧缓存目录(如
rm -rf bootstrap/cache/*) - 不适合做:调用
php artisan migrate(Laravel 命令依赖 autoload)、生成前端资源(此时 node_modules 可能还没装)、写入数据库 - 如果脚本本身需要 Composer 自动加载能力,必须手动引入:
require __DIR__ . '/vendor/autoload.php';——但注意:此时vendor/可能还不存在或不完整,风险高 - CI/CD 流水线里慎用耗时操作(如下载远程配置),它卡在 install 最前面,会拖慢整个构建链路


















