必须用pre-install-cmd而非post-install-cmd做校验,因其能在依赖安装前中断流程,避免vendor部分写入导致状态不干净;返回非零退出码可阻止后续执行,而post-*仅适合补救,不可用于守门。

CI中必须用 pre-install-cmd 而不是 post-install-cmd 做校验
校验逻辑(如配置文件是否存在、环境变量是否就位)必须在依赖安装前触发,否则失败时 vendor 已部分写入,状态不干净,且无法阻止后续步骤。post-* 钩子是“补救型”的,只适合清理或通知,不适合守门。
-
pre-install-cmd和pre-update-cmd是唯二能中断流程的时机;它们返回非零退出码(exit(1))时,Composer 会直接终止,不执行 install/update - 别把校验脚本挂到
test或自定义命令里再手动调用——CI 流水线可能跳过该步骤,或顺序错乱 - 若校验需读取
composer.json或项目根目录下文件,确保 CI 的工作目录正确(GitLab CI 默认是项目根,GitHub Actions 需确认run: cd ${{ github.workspace }}是否必要)
校验脚本必须是独立 PHP 文件,不能内联
在 composer.json 的 scripts 字段里写 "php -r \"...\"" 看似省事,实则在 Windows 下常因引号解析失败而静默成功,在 CI 中等于没校验。
- 推荐路径:
scripts/validate-env.php,首行加#!/usr/bin/env php,Linux/macOS 上设可执行权限(chmod +x) - 脚本内用
file_exists()、getenv()、is_readable()等做判断,失败时输出具体错误到STDERR并exit(1) - 不要依赖
$_SERVER—— CLI 环境下它不可靠;改用getenv('APP_ENV')或$_ENV
CI脚本里要显式调用 composer install --no-interaction
即使你注册了 pre-install-cmd,如果 CI 步骤没运行 composer install,钩子根本不会触发。很多人误以为“只要写了钩子就自动生效”,其实它只是事件监听器,得有对应事件发生。
- CI 配置中必须包含明确的安装命令,例如:
composer install --no-interaction --no-dev --optimize-autoloader --classmap-authoritative -
--no-interaction不可省:CI 环境无 TTY,缺它会导致卡住等待输入 - 若校验耗时长(比如连远程 DB),可在调试阶段临时加
--no-scripts跳过,但生产流水线必须去掉
本地与CI行为一致的关键:权限、路径、PHP版本
同一个 validate-env.php 在本地能过,CI 报错,90% 是环境差异导致的——不是逻辑问题,而是执行上下文不对。
- 检查 CI 使用的 PHP 版本是否与本地一致(
php -v),尤其注意 8.0+ 的str_starts_with()等函数在旧版不可用 - 脚本中所有路径用
__DIR__或dirname(__FILE__)构建,避免相对路径在不同工作目录下失效 - Docker 构建时,确保
scripts/validate-env.php文件权限被保留(COPY --chmod=755或构建后RUN chmod +x)
真正容易被忽略的,是校验脚本的错误输出是否真的进了 STDERR。很多脚本用 echo "error",结果被当成普通日志淹没在 CI 控制台里,既不标红也不触发失败判定。必须用 fwrite(STDERR, "missing config.php\n") 才算数。


















