post-install-cmd仅在vendor为空或被清空时触发,非每次install都执行;定义方式优先内联PHP,其次外部PHP文件,最后shell;回调中需判空$event->getComposer()并用getcwd()获取根目录。

可以直接在 composer.json 的 "scripts" 字段里定义 post-install-cmd 对应的命令,但要注意:它只在 composer install 且 vendor/ 为空或被清空时触发,不是每次安装都执行。
post-install-cmd 触发条件和常见误判
很多人以为只要运行 composer install 就会触发 post-install-cmd,其实不然:
- 如果
vendor/已存在且composer.lock未变更,Composer 会跳过安装依赖,也跳过该事件 - 使用
--no-scripts参数会直接禁用所有脚本,包括这个 -
composer update不会触发post-install-cmd,它走的是post-update-cmd - CI 环境中若提前缓存了
vendor/,本地开发没问题,CI 却不执行——这是最常被忽略的部署陷阱
定义自定义命令的三种方式
推荐按优先级排序:先尝试内联 PHP 脚本,再考虑外部脚本,最后才用 shell 命令(跨平台兼容性差)。
- 内联 PHP:适合轻量逻辑,无需额外文件,自动继承 Composer 的 autoloader
- 外部 PHP 文件:适合复用逻辑,需确保路径正确(建议用
__DIR__或$argv[0]定位) - Shell 命令:仅限简单文件操作,Windows 下容易出错,
php -r比直接写echo更可靠
{
"scripts": {
"post-install-cmd": [
"@php -r \"file_put_contents('install.stamp', date('c'));\"",
"App\\ComposerScripts::onPostInstall"
]
}
}
在 PHP 回调中安全访问 Composer 实例
自定义静态方法(如 App\ComposerScripts::onPostInstall)接收两个参数:$event(Composer\Script\Event 实例)和可选的 $io(Composer\IO\IOInterface)。关键点:
- 不要假设
$event->getComposer()总是可用——某些早期阶段可能为null,需判空 - 获取项目根目录用
$event->getComposer()->getConfig()->get('vendor-dir')不可靠,应改用$event->getComposer()->getPackage()->getAutoload()['psr-4']或更稳妥的getcwd() - 若需加载项目类,别手动 require,用
$event->getComposer()->getAutoloadGenerator()->getStaticFile()找 autoload.php 路径
调试脚本不执行的最快办法
加 --verbose 是第一步,但真正有效的是组合使用:
- 加
--no-plugins排除第三方插件干扰 - 临时删掉
vendor/和composer.lock,再跑composer install --dry-run看是否列出你的脚本 - 在回调开头加
file_put_contents('/tmp/composer-debug.log', print_r($event, true), FILE_APPEND);,确认是否进来了 - 检查
composer.json是否有语法错误:运行composer validate --no-check-publish
最隐蔽的问题是 autoload 配置缺失导致类找不到——哪怕只是 echo 一句,也要确保 autoload 或 autoload-dev 里声明了对应命名空间。










