ECC PHP 架构规则详解:薄控制器、DTO 值对象与依赖注入的 AI 辅助编码约束实践
2026/9/7 19:01:24 网站建设 项目流程

ECC PHP 架构规则详解:薄控制器、DTO 值对象与依赖注入的 AI 辅助编码约束实践

【免费下载链接】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 仓库中的 Cursor 规则文件 php-patterns.md 为主体,完整讲解这份 PHP 架构规则文件的三大核心约束——薄控制器与显式服务层、DTO 与值对象、基于构造器的依赖注入,并结合仓库内的安装适配器源码、配套规则家族(编码风格、Hooks、测试、安全)与它扩展的通用模式,说明如何在 AI 编码代理(Cursor、Claude Code、Codex 等)工作流中落地这套 PHP 架构规范。

规则文件定位:面向 PHP 与 Composer 工程的项目级约束

.cursor/rules/php-patterns.md 是 ECC 为 Cursor 环境提供的 PHP 架构模式规则。文件开头带有 YAML frontmatter,声明了规则的元信息:

--- description: "PHP patterns extending common rules" globs: ["**/*.php", "**/composer.json"] alwaysApply: false ---

各字段含义:

  • description:规则描述,表明它是"扩展通用规则的 PHP 模式文件";
  • globs: ["**/*.php", "**/composer.json"]:触发范围限定——只有当工作上下文涉及 PHP 源文件或 Composer 工程清单时,这条规则才有意义,不会污染其他语言的任务;
  • alwaysApply: false:不强制常驻注入上下文,而是按文件关联度按需生效,从而控制 token 预算。

文件正文第一行明确其继承关系:"This file extends the common patterns rule with PHP specific content"——即它在 rules/common/patterns.md 定义的通用模式(如 Repository Pattern、统一 API 响应格式)之上补充 PHP 语言特定的内容。

核心约束一:薄控制器、显式服务层(Thin Controllers, Explicit Services)

原文档给出两条要点:

  1. 控制器只负责传输层职责:认证(auth)、校验(validation)、序列化(serialization)、状态码(status codes);
  2. 业务规则下沉到应用/领域服务:这些服务应能在不启动 HTTP 环境的情况下直接测试。

这条约束针对 PHP Web 框架(Laravel、Symfony 等)中最典型的腐化模式:把数据库操作、领域判断、第三方调用全部堆进 Controller 动作方法,导致既难单测又难复用。

反例:逻辑堆在控制器里

// 控制器同时承担了校验、业务规则、数据访问 public function create(Request $request): JsonResponse { $data = $request->all(); if (empty($data['amount'])) { return response()->json(['error' => 'amount required'], 422); } // 业务规则散落在传输层,无法脱离 HTTP 测试 $discount = $data['amount'] > 1000 ? 0.1 : 0.0; $total = $data['amount'] * (1 - $discount); Order::create(['amount' => $total, 'user_id' => auth()->id()]); return response()->json(['ok' => true], 201); }

按规则重构:控制器做传输,服务做业务

public function create(CreateOrderRequest $request): JsonResponse { // 传输层:FormRequest 完成校验与认证 $dto = $request->validatedDto(); // 业务规则全部在领域服务中,可无 HTTP 环境直接单测 $order = $this->orderService->place($dto); return response()->json($order->toResponseArray(), 201); }
final class OrderService { public function __construct(private readonly OrderRepository $orders) {} public function place(CreateOrderDto $dto): Order { $discount = $dto->amount()->greaterThan(new Money(1000)) ? 0.1 : 0.0; $order = new Order($dto->userId(), $dto->amount()->less($discount)); $this->orders->save($order); return $order; } }

这样做的收益与规则意图一致:OrderService::place()可以写纯单元测试(mock 掉OrderRepository),无需引导 HTTP 内核;控制器瘦到只处理"输入怎么进来、输出怎么出去"。

核心约束二:DTO 与值对象(DTOs and Value Objects)

原文档要点:

  • 用 DTO 替换"形状沉重的关联数组",适用于请求(requests)、命令(commands)、外部 API 负载(external API payloads);
  • 用值对象封装钱(money)、标识符(identifiers)、日期区间等有约束概念

PHP 中$data['amount']$payload['user_id']这类字符串键关联数组是错误高发区:键名拼写错误无法在静态分析阶段发现、字段可任意增删、语义(类型、约束、精度)完全靠口头约定。

用 DTO 表达请求与命令

final readonly class CreateOrderDto { public function __construct( public string $userId, public Money $amount, public ?DateTimeImmutable $scheduledAt = null, ) {} /** 从框架请求输入构造,输入先过校验再进入领域逻辑 */ public static function fromRequestArray(array $data): self { // 在此处(或 FormRequest 中)完成校验: // 缺字段、类型不符、超出范围时抛出校验异常, // 不让非法数据流入领域层 if (!isset($data['amount'])) { throw new ValidationException('amount is required'); } return new self( userId: $data['user_id'], amount: Money::fromMinor($data['amount'], $data['currency'] ?? 'USD'), ); } }

用值对象封装有约束的概念

final readonly class Money { public function __construct( private int $minorUnits, // 以最小货币单位存储,避免浮点误差 private string $currency, ) {} public static function fromMinor(int $units, string $currency): self { return new self($units, strtoupper($currency)); } public function less(float $ratio): self { return new self((int) floor($this->minorUnits * (1 - $ratio)), $this->currency); } public function greaterThan(Money $other): bool { if ($this->currency !== $other->currency) { throw new DomainException('Cannot compare different currencies'); } return $this->minorUnits > $other->minorUnits; } }

值对象把"金额必须用最小货币单位整数表示""不同币种不可直接比较"这类约束内聚进类型本身,而不是散落在调用点的if判断里。仓库配套规则 php-coding-style.md 的"Immutability"小节进一步要求:跨服务边界的数据优先用不可变 DTO 与值对象,尽可能使用readonly属性或不可变构造函数——两条规则互为呼应。

核心约束三:依赖注入(Dependency Injection)

原文档要点:

  • 依赖接口或窄服务契约,而不是框架全局对象(如app()Facade、静态单例);
  • 通过构造器传递协作者,使服务无需 service-locator 查找即可测试。
interface OrderRepository { public function save(Order $order): void; public function findById(string $id): ?Order; } final class OrderService { // 依赖窄接口而非 "框架全局",协作者显式注入 public function __construct( private readonly OrderRepository $orders, private readonly EventDispatcher $events, ) {} } // 单元测试:无需启动框架容器 $service = new OrderService($fakeRepository, new NullEventDispatcher()); assertNotNull($service->place($dto));

对照反模式:在方法体内写Order::where(...)(直接依赖 Eloquent 门面)或app('mailer')(service-locator),会把"从哪里取协作者"的知识埋进方法体,测试时要么依赖真实容器要么依赖框架测试基类,与"测试无需 HTTP/容器引导"的目标相悖。构造器注入还让依赖图在类型签名上可见——从源码结构看,读一个 PHP 服务的构造函数就能列出它的全部协作者。

补充:正式版规则中的 Boundaries(边界)章节

与 Cursor 版对应的正式版规则 rules/php/patterns.md 在同样三个小节之外多出一个 "Boundaries" 章节,可视为同一约束集的完整版:

  • 隔离 ORM 模型与领域决策:当模型层做的事超出纯持久化(承载业务决策)时,要把领域判断从模型中剥离;
  • 用小型适配器包裹第三方 SDK:让代码库依赖你自己的契约,而不是 SDK 的类型。
// 依赖你的契约,而不是 Stripe 的类型 interface PaymentGateway { public function charge(Money $amount, string $customerId): PaymentResult; } final class StripeGateway implements PaymentGateway { public function __construct(private StripeClient $stripe) {} public function charge(Money $amount, string $customerId): PaymentResult { $intent = $this->stripe->paymentIntents()->create([...]); // SDK 细节被封装 return PaymentResult::fromStripeIntent($intent); } }

该正式版还通过 "Reference" 小节指向api-design技能(端点约定与响应结构)和laravel-patterns技能(Laravel 架构指导),见仓库 skills/api-design 与 skills/laravel-patterns 目录。

与通用模式规则的衔接:Repository 与统一响应包

.cursor/rules/php-patterns.md 声明自己是 common patterns 的 PHP 扩展,而 rules/common/patterns.md 提供了两条与上文直接配套的模式:

  • Repository Pattern:定义findAll / findById / create / update / delete标准操作,业务逻辑依赖抽象接口而非存储机制,方便替换数据源与 mock 测试——这正是上面OrderRepository接口的来源;
  • API Response Format:所有 API 响应使用一致的信封结构——成功/状态指示、数据负载(出错时可为空)、错误信息字段(成功时可为空)、分页元数据(total / page / limit)。

两条组合起来,"薄控制器"就有了完整的落点:控制器做校验与序列化、统一响应信封包裹 DTO 数据、数据访问走 Repository 接口、业务判断在服务层。

PHP 规则家族:风格、钩子、测试、安全如何协同

php-patterns 不是孤立文件,.cursor/rules/下存在一组同 globs(**/*.php**/composer.json)的配套规则,共同构成 PHP 工程约束面:

规则文件核心内容
php-patterns.md本文主体:薄控制器、DTO/值对象、依赖注入
php-coding-style.mdPSR-12、declare(strict_types=1)、标量类型提示与readonly属性、PHP-CS-Fixer/Laravel Pint 格式化、PHPStan/Psalm 静态分析
php-hooks.mdPostToolUse 钩子配置:编辑.php后自动格式化、静态分析、定向跑 PHPUnit/Pest;对遗留var_dump/dd/dump/die()、新增裸 SQL 或关闭 CSRF/会话保护的编辑发出警告
rules/php/testing.mdPHPUnit 为默认框架(已配置 Pest 则不混用)、--coverage-text覆盖率命令、单测与 HTTP/数据库集成测试分层、HTTP/控制器测试聚焦传输与校验而业务规则进服务层测试
rules/php/security.md框架边界处校验输入、模板默认转义、PDO/查询构造器参数化查询、白名单批量赋值字段、composer audit入 CI、password_hash()与会话 ID 轮换

测试规则中"Keep HTTP/controller tests focused on transport and validation; move business rules into service-level tests"与本文薄控制器约束形成闭环:业务逻辑既然在可脱离 HTTP 测试的服务层,测试组织方式也按此分层。

规则如何随 ECC 安装到 Cursor 项目

ECC 的 Cursor 项目级安装目标适配器 scripts/lib/install-targets/cursor-project.js 负责把规则文件投递到宿主项目的.cursor/rules/目录。从源码结构看,其关键机制包括:

  • 规则平铺与重命名toCursorRuleFileName().md规则文件改写为.mdc扩展名(Cursor 项目规则的扩展名),README 文件被跳过;
  • 投递优先级:模块内路径按.cursor(0) →rules(1) → 其他(2) 排序,.cursor根下的非规则子项以preserve-relative-path策略复制;
  • 去重takeUniqueOperations()以目标路径为键去重,同一目的地只由先出现的模块占用;
  • MCP 配置合并.mcp.jsonmerge-json策略合并进宿主项目的.cursor/mcp.json
  • 托管标记:所有操作通过createManagedOperation()打上 managed 所有权标记,配合.cursor/ecc-install-state.json安装状态文件,使后续升级/卸载可识别哪些文件由 ECC 管理。

安装到目标项目后,配合该项目的 .cursor/hooks.json(定义了afterFileEdit自动格式化、beforeReadFile敏感文件告警、beforeSubmitPrompt密钥检测等钩子),这套 PHP 规则就形成了"约束 + 自动化检查"的完整闭环:规则告诉 Agent 如何写,钩子在编辑后验证是否偏离。

适用前提与限制

  • 本文内容基于当前仓库中的规则文件与安装适配器源码,适用于将 ECC 规则体系接入 PHP / Composer 工程的场景;globs限定**/*.php**/composer.json,对非 PHP 项目不会激活;
  • 规则正文是面向 AI 编码代理的约束指令(自然语言清单),文中代码示例为按规则意图给出的落地示范,用于解释约束如何转化为实际代码,并非仓库内的既有实现;
  • 若你的项目使用 Laravel,仓库正式版规则还额外推荐了laravel-patternslaravel-tddlaravel-security等技能目录,可在对应 skills 目录下查阅。

参考文件清单

文件说明
.cursor/rules/php-patterns.md本文主体:Cursor 用 PHP 架构模式规则
rules/php/patterns.md正式版规则,含 Boundaries 章节与技能引用
rules/common/patterns.md被扩展的通用模式(Repository、API 响应信封)
.cursor/rules/php-coding-style.mdPSR-12、strict_types、不可变性、静态分析工具
.cursor/rules/php-hooks.mdPHP 编辑后的格式化/静态分析/测试钩子与遗留调试语句警告
rules/php/testing.mdPHPUnit/Pest、覆盖率、测试分层
rules/php/security.md输入校验、SQL 安全、密钥管理、认证会话
scripts/lib/install-targets/cursor-project.jsCursor 项目安装适配器:规则平铺、.mdc改名、去重、MCP 合并
.cursor/hooks.jsonCursor 项目钩子配置(自动格式化、密钥检测等)

【免费下载链接】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),仅供参考

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

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

立即咨询