post-install-cmd仅在composer.lock不存在或过期时触发,并非每次install都执行;稳定执行部署逻辑应使用post-update-cmd、post-autoload-dump或显式调用composer run-script。

post-install-cmd 不是“每次 composer install 都会跑”的钩子,它只在 composer.lock 不存在或已过期(即 Composer 必须重生成 lock 文件)时才触发;想稳定执行部署逻辑,得靠 post-update-cmd、post-autoload-dump 或显式调用 composer run-script。
post-install-cmd 为什么经常不执行?
常见现象:本地改了 composer.json 后 composer install 没反应,CI 流水线里脚本完全静默——大概率不是脚本写错了,而是触发条件没满足。
-
composer.lock存在且内容与composer.json一致时,composer install只是解包,不触发post-install-cmd - 首次克隆项目、删掉
composer.lock、或修改了require导致 lock 过期,才会触发 - CI/CD 默认加
--no-scripts(如 GitLab CI 的composer install --no-interaction --no-scripts),脚本直接被跳过 - 事件名必须严格为
post-install-cmd(全小写、短横线),写成PostInstall或post_install_cmd都无效
如何让关键操作(如清缓存、设权限)稳定运行?
依赖类框架命令(如 php artisan config:clear)或文件系统操作(如 chmod)时,时机比事件名更重要:autoload 未就绪,artisan 就会报 Class not found;用户权限不对,chmod 就会 Permission denied。
- 缓存清理类操作推荐挪到
post-autoload-dump:此时vendor/autoload.php已生成,Laravel/Symfony 的artisan才真正可用 - 权限设置优先用 PHP 脚本而非
chmod -R,避免跨平台问题和过度授权:@php -r "is_writable('storage') ?: chmod('storage', 0775);" - 生产环境部署务必加
--no-dev --optimize-autoloader,但若脚本里调用了phpunit等 dev 工具,得拆出独立 script 并显式传--no-dev - 所有路径都基于项目根目录,别写
cd ./public—— Composer 执行脚本时工作目录就是getcwd()
CI/CD 和 Docker 中怎么确保脚本执行?
本地能跑 ≠ 上线能跑。CI/CD 环境默认禁用脚本、Docker 构建阶段缺少扩展、部署用户与 Web 用户不一致,是三大高频断点。
- 检查流水线中
composer install命令是否含--no-scripts或-n;必须显式加--scripts或删掉该参数 - Dockerfile 中避免分层安装:不要
RUN composer install --no-dev+ 单独RUN composer run-script post-install-cmd,应合并为一条并确保vendor/和autoload.php已就绪 - 权限问题别只靠脚本修复,构建镜像时就该
chown -R www-data:www-data /var/www/html/storage,脚本只做兜底 - 用
composer run-script post-install-cmd --no-interaction替代隐式触发,更可控,也方便调试
最常被忽略的其实是执行上下文:post-install-cmd 运行时,vendor/autoload.php 可能还没写完,.env 可能还没读取,APP_ENV 可能还是 local。真要跑框架命令,别赌时机,老实用 post-autoload-dump + 显式 --env=production。


















