Composer原子操作须显式拆分命名调用;post-install-cmd因--no-dev默认禁用,未进执行队列;脚本须定义在composer.json根级scripts下,运行composer run --list验证注册;原子化推荐独立命名如deploy:clear-cache并用数组顺序执行。

Composer 原子操作不能靠“自动触发”实现,必须显式拆分、命名、调用——所有看似自动的链式行为,本质都是手动组合的命令序列。
post-install-cmd 为什么总不执行?
它不是“失效”,而是根本没进执行队列:生产环境默认启用 COMPOSER_NO_DEV=1,直接跳过所有 dev 相关钩子,post-install-cmd 就属于这一类。
- 本地能跑,上线静默跳过,大概率是部署时用了
composer install --no-dev(合理),但没意识到这会连带禁用钩子 -
--no-scripts是另一个“全关开关”,别和--no-dev混用;若误加,连自定义脚本都跑不了 - 验证是否注册成功:运行
composer run --list,看到你的脚本名才说明解析正确;否则检查scripts是否写在composer.json根层级,缩进错一级就无效
怎么写真正可复用的原子脚本?
把“清缓存 + 设权限 + 生成配置”塞进一个数组里看着省事,但失败后无法定位、无法重试。原子化就是每个动作独立命名、独立测试。
- 定义清晰语义的脚本名:
"deploy:clear-cache"、"deploy:set-perms"、"deploy:config-cache" - 值用字符串数组,每项以
@开头表示复用其他脚本:"deploy:post-install": ["@deploy:clear-cache", "@deploy:config-cache"] - 避免 shell 连接符:
&&在数组里不生效;改用数组顺序执行,Composer 保证前一项 exit code 非 0 时中断后续 - 调用时显式指定:
composer install --no-dev && composer run deploy:post-install,而不是依赖钩子
PHP 脚本比 shell 命令更稳的关键点
直接写 "php ./artisan cache:clear" 看似简单,但一旦 ./artisan 权限不对、PHP CLI 版本不一致、或 autoloader 没加载,就挂。
- PHP 脚本开头必须
require_once __DIR__ . '/../vendor/autoload.php';,确保类可用 - 用
getenv('APP_ENV')判断环境,防止本地误清生产缓存 - 路径操作别硬编码:
is_writable('storage') ?: mkdir('storage', 0755, true);比chmod -R 755 storage更安全 - 敏感操作加防护:比如清缓存前检查当前用户是否为部署用户,避免 Web 进程误触发
rsync 同步时 vendor/ 和 node_modules/ 怎么处理?
它们不该同步过去——目标机必须重装,否则 autoloader、扩展兼容性、classmap 权限全乱套。
rsync -avz --exclude="vendor/" --exclude="node_modules/" --exclude=".git" ./ user@prod:/var/www/app/- 同步完立刻远程执行:
ssh user@prod 'cd /var/www/app && composer install --no-dev --optimize-autoloader --classmap-authoritative' - 前端构建产物(如
public/build/)可以同步,但必须确保构建在 CI 机完成,而非目标机上跑npm install
最易被忽略的点:所有脚本的当前工作目录是 composer.json 所在路径,但 PHP CLI 的 user、umask、open_basedir 可能和 Web 用户完全不同。权限问题不是“没设”,而是“设了但对 Web 进程无效”。


















