ECC PHP Hooks 实战:为 PHP 代码库构建格式化、静态分析、定向测试与安全告警的 PostToolUse 钩子体系
2026/9/7 16:50:52 网站建设 项目流程

ECC PHP Hooks 实战:为 PHP 代码库构建格式化、静态分析、定向测试与安全告警的 PostToolUse 钩子体系

【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC

本文以 ECC(The agent harness performance optimization system)仓库中的 PHP 语言钩子规则 php-hooks.md 为主体,完整解析该规则推荐的三类 PostToolUse 钩子(Pint/PHP-CS-Fixer、PHPStan/Psalm、PHPUnit/Pest)与两条 PHP 专属告警规则,并结合仓库中真实的钩子运行时实现(hooks/hooks.json、.cursor/hooks/adapter.js)与钩子编写指南(hooks/README.md),说明这些规则在 Claude Code / Cursor 环境下如何被触发、执行与降级。读完本文,你可以把 ECC 的 PHP 钩子建议落地为可复制的~/.claude/settings.json配置,并理解其底层调度机制与运行时开关。

一、规则定位:php-hooks.md 在 ECC 规则体系中的位置

ECC 在.cursor/rules/目录下按「语言 × 主题」维护了一组规则文件,PHP 相关的有五个:php-coding-style.mdphp-hooks.mdphp-patterns.mdphp-security.mdphp-testing.md。本文聚焦其中的 php-hooks.md。

该文件的 frontmatter 定义了它在 Agent 工作时的激活条件:

--- description: "PHP hooks extending common rules" globs: ["**/*.php", "**/composer.json", "**/phpstan.neon", "**/phpstan.neon.dist", "**/psalm.xml"] alwaysApply: false ---

三个字段的技术含义:

  • globs:规则按文件模式按需注入。只有当会话涉及*.php源码、composer.json依赖清单、phpstan.neon/phpstan.neon.dist/psalm.xml静态分析配置时,该规则才会被加载进上下文。这意味着它不会污染非 PHP 项目(如 JS/Go 项目)的 token 预算。
  • alwaysApply: false:不随每个会话无条件注入,与父级通用规则 common-hooks.md 的alwaysApply: true形成对比——通用钩子系统知识常驻,PHP 专项内容按需附加。
  • description中的 "extending common rules":明确声明本文件是对通用钩子规则的增量扩展,而非独立体系。

二、继承的底座:通用钩子系统(common-hooks.md)

php-hooks.md 的正文以「This file extends the common hooks rule」开头,它扩展的正是 common-hooks.md。该通用规则定义了 ECC 钩子体系的三类核心事件与使用纪律,是理解 PHP 钩子的前提:

钩子类型触发时机典型用途
PreToolUse工具执行前参数校验、阻断危险操作(可用 exit code 2 阻断)
PostToolUse工具执行后自动格式化、静态检查、质量门禁(PHP 钩子全部落在这里)
Stop每轮响应结束时最终验证、审计(如批量检查 console 调试残留)

通用规则同时给出了两条与钩子协作直接相关的纪律:

  • Auto-Accept Permissions:仅在可信、边界清晰的任务中开启自动接受;探索性工作应保持关闭;明确禁止使用dangerously-skip-permissions标志,而应通过~/.claude.json中的allowedTools做白名单。
  • TodoWrite 实践:用任务列表跟踪多步工作,暴露步骤错序、遗漏与粒度问题——钩子产生的告警信息也需要借助任务列表被逐项处理。

仓库根目录的 hooks/README.md 用一张流程式描述概括了整个事件链:

User request → Claude picks a tool → PreToolUse hook runs → Tool executes → PostToolUse hook runs

并明确了退出码语义:0继续执行,2仅 PreToolUse 可阻断工具调用,其他非零值记录但不阻断。PostToolUse 钩子只能分析输出、不能阻断——这与 PHP 钩子「事后检查、输出告警」的定位完全一致。

三、PHP 推荐的三类 PostToolUse 钩子

php-hooks.md 的核心建议如下:在~/.claude/settings.json中为 PHP 项目配置三类 PostToolUse 钩子,在每次编辑.php文件后自动运行。

3.1 Pint / PHP-CS-Fixer:自动格式化

  • 作用:编辑.php文件后自动执行代码风格修正,保证提交前风格统一。
  • 工具选型:Laravel 生态项目用Laravel Pint(零配置的 PSR-12 实现),通用项目用PHP-CS-Fixer。这与同目录 php-coding-style.md 的规则相互印证:该规则要求遵循PSR-12、新代码尽量使用declare(strict_types=1)、标量类型提示与返回类型,并指定「格式化用 PHP-CS-Fixer 或 Laravel Pint,静态分析用 PHPStan 或 Psalm」。

3.2 PHPStan / Psalm:静态分析

  • 作用:在类型化代码库(typed codebases)中编辑 PHP 后运行静态分析,尽早发现类型错误、无效方法调用与可空性问题。
  • 要点:PHPStan 通过phpstan.neon/phpstan.neon.dist配置,Psalm 通过psalm.xml配置——这正是该规则globs中把这三个配置文件与*.php并列的原因:会话只要触及静态分析配置,就应同步加载钩子规则,提醒 Agent 保持分析与编辑的一致性。

3.3 PHPUnit / Pest:定向测试

  • 作用:当编辑影响行为(affect behavior)时,针对被触及的文件或模块运行定向测试,而不是每次都跑全量测试套件。
  • 要点:「targeted tests for touched files or modules」强调的是钩子反馈环路要短——PostToolUse 发生在每次工具调用之后,全量测试会显著拖慢 Agent 工作循环,因此只在行为变更时跑受影响的测试子集。

3.4 落地写法:按仓库钩子配方模式生成配置

hooks/README.md 的「Common Hook Recipes」一节给出了编写钩子的标准模式:钩子是一个从 stdin 读取 JSON(含tool_nametool_input.file_pathtool_input.new_string等字段)、处理后必须把原始输入原样写回 stdout 的 Node.js 命令;警告用console.error输出到 stderr,阻断用process.exit(2)。仓库中的现成示例是「ruff 自动格式化 Python 文件」的 PostToolUse 配方(hooks/README.md 中的 recipe)。

按同一模式,PHP 的 Pint 自动格式化钩子可配置为:

{ "matcher": "Edit", "hooks": [{ "type": "command", "command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path||'';if(/\\.php$/.test(p)){const{execFileSync}=require('child_process');try{execFileSync('vendor/bin/pint',['--dirty',p],{stdio:'pipe'})}catch(e){}}console.log(d)})\"" }], "description": "Auto-format PHP files with Laravel Pint after edits" }

要点说明(均以 hooks/README.md 的输入 Schema 为依据):

  • matcher: "Edit"限定只在文件编辑工具后触发,与 PHPStan 配方中tsc --noEmit式「编辑后校验」的定位一致;
  • /\\.php$/判断tool_input.file_path,与规则 frontmatter 的**/*.phpglob 语义对齐;
  • 格式化命令包在try/catch中:工具缺失或执行失败时钩子静默放过,不向 Agent 抛噪音(仓库 ruff 配方采用同样的吞错策略);
  • 末尾console.log(d)把原始 stdin 回写,这是 ECC 钩子契约的硬性要求,否则会破坏宿主传入的工具上下文。

对于 PHPStan,同一模式下把命令换成execFileSync('vendor/bin/phpstan',['analyse',p,'--no-progress'],{stdio:'pipe'}),并建议将解析结果以 stderr 形式回传给 Agent,即可实现「编辑 → 即时类型反馈」闭环。

四、PHP 专属告警:调试残留与安全弱化

php-hooks.md 的 Warnings 一节定义了两条只警告、不阻断的规则,它们是 PHP 语言生态中最典型的「Agent 容易犯、人工审查容易漏」的问题:

4.1 调试残留告警

Warn onvar_dump,dd,dump, ordie()left in edited files.

  • var_dump/dd/dump:分别对应原生 PHP、Laravel 与 Symfony 生态的调试打印函数(Laravel 的dd()、Symfony 的dump()是极高频的调试入口)。
  • die():在调试路径中直接终止脚本,会掩盖真实异常堆栈。
  • 这四者的共同点是:它们属于临时调试手段,一旦进入生产代码会造成输出污染、异常被吞、请求被截断。告警机制等价于仓库中面向 JS 生态的console.log检查(见 hooks/README.md 中 Stop 事件的Console.log audit钩子与 hooks/hooks.json 的stop:check-console-log条目),只是换成了 PHP 的调试函数族。

4.2 安全弱化告警

Warn when edited PHP files add raw SQL or disable CSRF/session protections.

  • 裸 SQL(raw SQL):与同目录 php-security.md 的 Database Safety 规则形成呼应——该规则要求所有动态查询必须走预处理语句(PDO、Doctrine、Eloquent query builder),并对 ORM 批量赋值做字段白名单。钩子把这条编码规范从「事后人审」前移到「编辑即提醒」。
  • 禁用 CSRF / 会话保护:php-security.md 的 Auth and Session Safety 明确要求状态变更请求强制 CSRF 防护、密码用password_hash()/password_verify()存储、认证与权限变更后重新生成会话标识。当 Agent 在编辑中引入绕过这些保护的写法时,钩子应立即发出告警,提示走标准防护路径而不是绕过。

这两条告警在 ECC 钩子契约下都属于「exit 0 + stderr 提示」级别:PostToolUse 钩子没有阻断权限(exit code 2 仅 PreToolUse 有效),所以它们的执行方式必然是输出警告让 Agent 自我修正——这正是 hooks/README.md 所述「warn(stderr without blocking)」模式。

五、ECC 钩子运行时:这些建议如何被真实执行

ECC 仓库自身就运行着一套完整的钩子实现,PHP 钩子规则落地时,其行为受这套运行时约束。以下从源码结构给出关键证据。

5.1 单一调度器 + 双通道

hooks/hooks.json 是所有 PostToolUse 钩子的执行入口。它没有为每个检查项单开进程,而是注册了两个matcher: ".*"的 PostToolUse 条目:

  • post:dispatcher:sync:同步通道,timeout: 30(秒),在宿主等待期间跑完「必须即时反馈」的检查(hooks/hooks.json L136-L148);
  • post:dispatcher:async:异步通道,"async": truetimeout: 45,跑不阻塞主流程的分析类钩子(hooks/hooks.json L149-L162)。

两者都指向scripts/hooks/posttooluse-dispatcher.js,并通过环境变量ECC_POSTTOOLUSE_PASSTHROUGH=1传递原始输入。「同步/异步分离」正是 PHP 场景下的性能关键:Pint 格式化、PHPUnit 定向测试这类要影响下一步行为的检查走同步通道;PHPStan 全量扫描或测试报告分析可放异步通道,避免拉长每次编辑的等待时间。

5.2 Profile 分级与按 ID 禁用

ECC 钩子统一通过scripts/hooks/run-with-flags.js包装执行,并传入档位参数(如standard,strict)。hooks/README.md 的「Runtime Hook Controls」一节定义了运行时开关:

export ECC_HOOKS_ENABLED=true # 总开关 export ECC_HOOK_PROFILE=standard # minimal | standard | strict(默认 standard) export ECC_DISABLED_HOOKS="post:edit:typecheck" # 按 ID 精确禁用

三个 profile 的语义:minimal只保留必要生命周期与安全钩子;standard为默认的均衡档;strict追加更多提醒与更严护栏。对 PHP 项目的实际意义:在 CI 或大型多包项目中可把 PHPStan 类检查切到strict档提高告警密度,或在恢复期用ECC_DISABLED_HOOKS单独关掉某条 PHP 告警而不改配置。

Cursor 侧的钩子适配器 adapter.js 中的hookEnabled()函数(L63-L79)实现了同样的逻辑:读取ECC_HOOK_PROFILE(非法值回退standard)、解析逗号分隔的ECC_DISABLED_HOOKS集合,并按 hook ID 判定放行。这保证了「规则文件里的建议」与「运行时实际执行」之间有单一事实来源的开关面。

5.3 Cursor 到 Claude 钩子契约的转换

ECC 同时支持 Cursor 环境。.cursor/hooks.json 声明了 Cursor 原生事件(afterFileEditbeforeShellExecutionstop等),其中afterFileEdit指向 after-file-edit.js,它执行的动作链为:

  1. readStdin()读取 Cursor 传入的 JSON(含path/file字段);
  2. transformToClaude()(adapter.js L28-L47)把 Cursor 字段映射成 Claude Code 钩子契约字段:tool_input.commandtool_input.file_pathtool_output.output,并保留_cursor元信息(conversation_id、hook_event_name 等);
  3. 依次委托给post-edit-accumulator.js(累积本轮编辑路径,供 Stop 时批量格式化/类型检查)、post-edit-console-warn.js(调试残留告警——PHP 场景下即对应 4.1 节的调试函数告警模式),以及 profile 门控的design-quality-check.js
  4. 最后process.stdout.write(raw)原样回写输入。

适配器中runExistingHook()(adapter.js L49-L61)还有一条关键转发逻辑:子钩子以e.status === 2退出时,适配器process.exit(2)把阻断码向上传递。这说明 ECC 的钩子链保留了「链上任一环节可阻断」的能力,同时 15 秒timeout与 stdin 1MB 上限(MAX_STDIN)保证了钩子链不会拖死宿主工具调用。

六、php-hooks 与同目录 PHP 规则的协同

把 php-hooks.md 放回完整的 PHP 规则族中看,五条规则各自覆盖一个维度,globs有交集、职责不重叠:

规则文件覆盖主题典型 globs
php-coding-style.mdPSR-12、strict_types、不可变 DTO、格式化/静态分析工具选型**/*.php**/composer.json
php-hooks.mdPostToolUse 自动化(格式化/静态分析/定向测试)+ 调试残留与安全弱化告警**/*.php**/composer.json**/phpstan.neon***/psalm.xml
php-security.md预处理语句、密钥管理、password_hash、CSRF 与会话安全、composer audit**/*.php**/composer.lock**/composer.json
php-patterns.mdPHP 架构与编码模式PHP 相关文件
php-testing.mdPHPUnit/Pest 测试写法PHP 相关文件

协同关系值得注意:php-hooks.md的安全告警(裸 SQL、CSRF 绕过)是php-security.md规范的执行器,php-hooks.md的定向测试告警是php-testing.md规范在编辑时刻的触发点,而格式化钩子则是php-coding-style.md中 PSR-12 要求的自动化保障。也就是说,钩子规则文件把静态的编码规范变成了「编辑即校验」的运行时反馈环。

七、小结:从规则到运行的完整链路

综合本仓库的证据,ECC 的 PHP 钩子机制可以归纳为一条完整链路:

  1. 触发:规则 frontmatter 的globs.php/composer.json/phpstan.neon*/psalm.xml)决定 PHP 钩子规则何时进入 Agent 上下文,alwaysApply: false保证按需加载;
  2. 执行:PostToolUse 钩子按「同步/异步」双通道在posttooluse-dispatcher.js中运行,完成 Pint/PHP-CS-Fixer 格式化、PHPStan/Psalm 分析、PHPUnit/Pest 定向测试;
  3. 告警var_dump/dd/dump/die()调试残留与裸 SQL、CSRF/会话保护弱化以 exit 0 + stderr 方式提醒,语义对齐仓库既有的 console.log 审计钩子;
  4. 可控:通过ECC_HOOKS_ENABLEDECC_HOOK_PROFILE(minimal/standard/strict)、ECC_DISABLED_HOOKS在不改配置文件的前提下调节钩子密度与取舍;
  5. 跨宿主:Cursor 环境下由 adapter.js 完成事件字段转换与 exit code 2 阻断转发,同一套scripts/hooks/实现被两套宿主复用。

对 PHP 项目使用者的直接建议:先按第三节把三类 PostToolUse 钩子写入~/.claude/settings.json(可参照 hooks/README.md 的配方模式自行组装命令),再用第四节的告警规则补齐调试残留与安全弱化的检查,最后按第五节的 profile 开关把钩子密度调到与项目规模匹配的水位——这就是 ECC 在 PHP 代码库中「研究先行、编辑即反馈」工程循环的完整落地方式。

【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询