pre-commit 钩子应放在 .git/hooks/pre-commit 路径下并设为可执行;它需调用 composer validate 和 composer install --dry-run 等只读命令校验语法与依赖一致性,避免写入文件,并用 git status --porcelain 检查未暂存变更以确保工作区干净。

pre-commit 钩子该放在哪?不是 composer.json 里
Composer 本身不提供 pre-commit 钩子机制;所谓“Composer pre-commit 钩子”,实际是 Git 的 pre-commit 钩子脚本,只是在其中调用 Composer 命令(比如 composer validate 或 composer install --dry-run)。脚本必须放在 Git 仓库的 .git/hooks/pre-commit 路径下,且需可执行(chmod +x .git/hooks/pre-commit)。
常见错误是把逻辑写进 composer.json 的 scripts 段然后幻想 Git 会自动调用——不会。Git 只认 .git/hooks/ 下的文件。
钩子脚本必须显式检查工作区状态
Git 的 pre-commit 钩子运行时,暂存区(index)已更新,但工作目录可能含未暂存修改。若脚本执行 composer install 或生成文件(如 vendor/autoload.php),会污染工作区,导致提交失败或后续操作异常。
- 用
git status --porcelain检查是否有未暂存变更,有则中止并提示 - 避免在钩子里运行会写入文件的命令(如
composer install、composer dump-autoload -o);改用--dry-run或只读命令 - 若必须校验依赖一致性,用
composer install --no-interaction --dry-run,它只检查composer.lock是否匹配composer.json,不改动任何文件
如何安全集成 Composer 校验逻辑?
一个最小可用的健壮脚本应覆盖:Composer 文件语法、依赖锁定一致性、PHP 语法(可选)。关键点是失败时给出明确退出码(非零)让 Git 中止提交。
#!/bin/sh # .git/hooks/pre-commit检查 composer.json 和 composer.lock 是否存在且语法合法
if ! composer validate --no-check-publish --no-check-all >/dev/null 2>&1; then echo "❌ composer.json 或 composer.lock 格式错误" exit 1 fi
检查 lock 文件是否与 json 一致(等价于 git add 后运行 composer install 是否会改 lock)
if ! composer install --no-interaction --dry-run >/dev/null 2>&1; then echo "❌ composer.lock 与 composer.json 不一致,请运行 'composer update' 或 'composer install'" exit 1 fi
可选:检查 PHP 文件语法(跳过 vendor/)
find . -name ".php" -not -path "./vendor/" -exec php -l {} \; 2>/dev/null | grep "Errors parsing" && { echo "❌ 发现 PHP 语法错误" exit 1 }
为什么不能直接用 composer-scripts?
有人尝试在 composer.json 里定义 "pre-commit": "composer validate",再通过第三方工具(如 composer-git-hooks)绑定——这引入了额外依赖和抽象层,反而增加故障点。原生 Git 钩子更轻量、可控性更强。
真正容易被忽略的是环境一致性:钩子脚本里的 composer 命令必须和 CI/其他开发者使用的版本一致。建议硬编码路径(如 ./vendor/bin/composer)或检查 COMPOSER_HOME,否则本地全局安装的 Composer 版本可能和项目要求不兼容,导致校验误报。










