如何配置并详解Composer的post-install钩子脚本执行过程?
- 内容介绍
- 文章标签
- 相关推荐
本文共计931个文字,预计阅读时间需要4分钟。
`post-install-cmd 不是每次执行的命令,它仅在软件安装后执行一次。
post-install-cmd 触发条件很具体
很多人配好了脚本却没反应,根本原因不是写错了命令,而是压根没满足触发前提:
- 项目已有
vendor/且composer.lock没变 → Composer 跳过安装流程,post-install-cmd不执行 -
composer install --no-scripts(CI 环境常默认启用)→ 所有脚本被静默禁用 -
composer.lock存在但内容过期(比如改了composer.json的 require 却没更新 lock)→ 此时 Composer 会先重生成 lock,再安装,post-install-cmd才会跑 - 事件名写成
PostInstall、post_install_cmd或post-install→ Composer 完全不识别,必须严格为post-install-cmd(全小写、短横线)
在 composer.json 中正确声明脚本
必须放在顶层 scripts 字段下,值必须是数组(5.6+ 强制要求),不能是单字符串:
{ "scripts": { "post-install-cmd": [ "@php -r \"file_put_contents('runtime/.installed', date('c'));\"", "php artisan config:clear", "php bin/generate-config.php" ] } }
关键点:
- 以
@php开头的命令会被 Composer 替换为当前 PHP 可执行路径,比硬写php更可靠 - 外部命令如
npm install要求系统 PATH 中存在npm,CI 镜像里得提前装 Node.js - PHP 脚本建议用
php -f script.php,不用./script.php(可能无执行权限) - 不要在脚本里用
__DIR__推导路径——Composer 是用proc_open启新进程,__DIR__指向的是 PHP 解释器位置,不是你的脚本目录
常见失败原因:权限、路径、时机
脚本报错或静默跳过,往往卡在这几个实际问题上:
-
sh: 1: npm: not found→ CI 容器没装 Node.js,不能只看本地有就认为线上也有 -
Permission denied: ./artisan→ Laravel 的artisan缺少可执行位,应改用php artisan - 相对路径失效(如
../config/app.php)→ 当前工作目录是composer.json所在目录,不是脚本所在目录;推荐用__DIR__ . '/vendor/autoload.php'显式引入自动加载 - 脚本里用了未启用的 PHP 扩展(如
mbstring),而容器里没装 → 运行直接崩,但 Composer 默认不报详细错误 - 想读
composer.json内容?别file_get_contents(__DIR__ . '/../composer.json')—— 改用ComposerInstalledVersions::getRawData()(v2+)更稳定
和 post-update-cmd、post-autoload-dump 的区别
这三个钩子时机不同,混用容易出问题:
-
post-install-cmd:只在composer install真正安装依赖后触发(适合部署初始化) -
post-update-cmd:在composer update和composer install --update时都稳定触发(更适合开发阶段同步变更) -
post-autoload-dump:每次自动生成 autoload 文件后都触发(包括 install/update),时机稍晚但更频繁(适合缓存清理、代理类生成)
真正难搞的不是怎么写,而是判断该用哪个钩子——尤其在 CI 流水线里,composer install 行为会因 vendor/ 和 lock 文件状态动态变化,不验证就假设“肯定执行”会踩坑。
本文共计931个文字,预计阅读时间需要4分钟。
`post-install-cmd 不是每次执行的命令,它仅在软件安装后执行一次。
post-install-cmd 触发条件很具体
很多人配好了脚本却没反应,根本原因不是写错了命令,而是压根没满足触发前提:
- 项目已有
vendor/且composer.lock没变 → Composer 跳过安装流程,post-install-cmd不执行 -
composer install --no-scripts(CI 环境常默认启用)→ 所有脚本被静默禁用 -
composer.lock存在但内容过期(比如改了composer.json的 require 却没更新 lock)→ 此时 Composer 会先重生成 lock,再安装,post-install-cmd才会跑 - 事件名写成
PostInstall、post_install_cmd或post-install→ Composer 完全不识别,必须严格为post-install-cmd(全小写、短横线)
在 composer.json 中正确声明脚本
必须放在顶层 scripts 字段下,值必须是数组(5.6+ 强制要求),不能是单字符串:
{ "scripts": { "post-install-cmd": [ "@php -r \"file_put_contents('runtime/.installed', date('c'));\"", "php artisan config:clear", "php bin/generate-config.php" ] } }
关键点:
- 以
@php开头的命令会被 Composer 替换为当前 PHP 可执行路径,比硬写php更可靠 - 外部命令如
npm install要求系统 PATH 中存在npm,CI 镜像里得提前装 Node.js - PHP 脚本建议用
php -f script.php,不用./script.php(可能无执行权限) - 不要在脚本里用
__DIR__推导路径——Composer 是用proc_open启新进程,__DIR__指向的是 PHP 解释器位置,不是你的脚本目录
常见失败原因:权限、路径、时机
脚本报错或静默跳过,往往卡在这几个实际问题上:
-
sh: 1: npm: not found→ CI 容器没装 Node.js,不能只看本地有就认为线上也有 -
Permission denied: ./artisan→ Laravel 的artisan缺少可执行位,应改用php artisan - 相对路径失效(如
../config/app.php)→ 当前工作目录是composer.json所在目录,不是脚本所在目录;推荐用__DIR__ . '/vendor/autoload.php'显式引入自动加载 - 脚本里用了未启用的 PHP 扩展(如
mbstring),而容器里没装 → 运行直接崩,但 Composer 默认不报详细错误 - 想读
composer.json内容?别file_get_contents(__DIR__ . '/../composer.json')—— 改用ComposerInstalledVersions::getRawData()(v2+)更稳定
和 post-update-cmd、post-autoload-dump 的区别
这三个钩子时机不同,混用容易出问题:
-
post-install-cmd:只在composer install真正安装依赖后触发(适合部署初始化) -
post-update-cmd:在composer update和composer install --update时都稳定触发(更适合开发阶段同步变更) -
post-autoload-dump:每次自动生成 autoload 文件后都触发(包括 install/update),时机稍晚但更频繁(适合缓存清理、代理类生成)
真正难搞的不是怎么写,而是判断该用哪个钩子——尤其在 CI 流水线里,composer install 行为会因 vendor/ 和 lock 文件状态动态变化,不验证就假设“肯定执行”会踩坑。

