solidframe/phpstan-rules
Composer 安装命令:
composer require solidframe/phpstan-rules
包简介
PHPStan rules for DDD, CQRS, and Event Sourcing architectural enforcement
README 文档
README
PHPStan rules for DDD, CQRS, and Event Sourcing architectural enforcement.
Static analysis catches architectural violations before tests even run.
Installation
composer require solidframe/phpstan-rules --dev
The rules are auto-registered via PHPStan's extension mechanism. No manual configuration needed.
Rules
CQRS Rules
Command Handler Must Return Void
Command handlers perform side effects and must not return values.
// OK final readonly class PlaceOrderHandler implements CommandHandler { public function __invoke(PlaceOrder $command): void { /* ... */ } } // ERROR: Command handler must return void final readonly class PlaceOrderHandler implements CommandHandler { public function __invoke(PlaceOrder $command): Order { /* ... */ } }
Query Handler Must Not Return Void
Query handlers must return data.
// OK final readonly class GetOrderHandler implements QueryHandler { public function __invoke(GetOrder $query): OrderDto { /* ... */ } } // ERROR: Query handler must return a value final readonly class GetOrderHandler implements QueryHandler { public function __invoke(GetOrder $query): void { /* ... */ } }
Handler Must Be Invokable
Handlers must implement __invoke() and have only one public method (besides __construct).
// OK final readonly class PlaceOrderHandler implements CommandHandler { public function __invoke(PlaceOrder $command): void { /* ... */ } } // ERROR: Handler must implement __invoke() final readonly class PlaceOrderHandler implements CommandHandler { public function handle(PlaceOrder $command): void { /* ... */ } } // ERROR: Handler must have only one public method final readonly class PlaceOrderHandler implements CommandHandler { public function __invoke(PlaceOrder $command): void { /* ... */ } public function anotherMethod(): void { /* ... */ } }
Messages Must Be Final Readonly
Commands and Queries must be declared as final readonly.
// OK final readonly class PlaceOrder implements Command {} // ERROR: Command must be final class PlaceOrder implements Command {} // ERROR: Command must be readonly final class PlaceOrder implements Command {}
Messages Must Not Extend
Commands and Queries must not extend other classes. Use composition.
// OK final readonly class PlaceOrder implements Command { public function __construct(public string $orderId) {} } // ERROR: Commands/Queries must not extend other classes final readonly class PlaceOrder extends BaseCommand implements Command {}
DDD Rules
Value Object Must Be Readonly
Value objects are immutable. The class must be declared as readonly.
// OK final readonly class Email extends StringValueObject {} // ERROR: Value object must be readonly final class Email extends StringValueObject {}
No Direct Aggregate Construction
Aggregate roots must be created via static factory methods, not new.
// OK $order = Order::place($orderId, $customerId); // ERROR: Use a static factory method instead of new $order = new Order($orderId);
Construction inside the aggregate class itself is allowed.
Event Sourcing Rules
Events Must Be Final Readonly
Domain events are immutable data structures.
// OK final readonly class OrderPlaced implements DomainEventInterface {} // ERROR: Event must be final and readonly class OrderPlaced implements DomainEventInterface {}
Apply Method Must Exist
For every event recorded via recordThat(), a corresponding apply{EventName}() method must exist.
// OK final class Order extends AbstractEventSourcedAggregateRoot { public function place(): void { $this->recordThat(new OrderPlaced(/* ... */)); } protected function applyOrderPlaced(OrderPlaced $event): void { // apply state change } } // ERROR: Missing method applyOrderPlaced final class Order extends AbstractEventSourcedAggregateRoot { public function place(): void { $this->recordThat(new OrderPlaced(/* ... */)); } // no applyOrderPlaced method! }
Configuration
Rules work out of the box with SolidFrame interfaces. To use with custom interfaces, override in your phpstan.neon:
parameters: solidframe: commandHandlerInterface: App\Cqrs\CommandHandler queryHandlerInterface: App\Cqrs\QueryHandler commandInterface: App\Cqrs\Command queryInterface: App\Cqrs\Query eventInterface: App\Event\DomainEvent valueObjectInterface: App\Ddd\ValueObject aggregateRootClass: App\Ddd\AggregateRoot
Rule Summary
| Rule | ID | Area |
|---|---|---|
| Command handler returns void | solidframe.commandHandlerMustReturnVoid |
CQRS |
| Query handler returns data | solidframe.queryHandlerMustNotReturnVoid |
CQRS |
| Handler is invokable | solidframe.handlerMustBeInvokable |
CQRS |
| Handler has single public method | solidframe.handlerSinglePublicMethod |
CQRS |
| Command is final | solidframe.commandMustBeFinal |
CQRS |
| Command is readonly | solidframe.commandMustBeReadonly |
CQRS |
| Query is final | solidframe.queryMustBeFinal |
CQRS |
| Query is readonly | solidframe.queryMustBeReadonly |
CQRS |
| Message has no parent class | solidframe.messageMustNotExtend |
CQRS |
| Value object is readonly | solidframe.valueObjectMustBeReadonly |
DDD |
| No direct aggregate construction | solidframe.noDirectAggregateConstruction |
DDD |
| Event is final | solidframe.eventMustBeFinal |
Event Sourcing |
| Event is readonly | solidframe.eventMustBeReadonly |
Event Sourcing |
| Apply method exists for recorded events | solidframe.applyMethodMustExist |
Event Sourcing |
Complementary: ArchTest vs PHPStan Rules
| Concern | ArchTest | PHPStan Rules |
|---|---|---|
| Namespace dependencies | doesNotDependOn() |
— |
| Class structure (final, readonly) | areFinal(), areReadonly() |
Message/VO/Event rules |
| Handler return types | — | Command void, Query non-void |
| Handler conventions | — | Invokable, single public method |
| Event apply methods | — | applyMethodMustExist |
| Aggregate construction | — | noDirectAggregateConstruction |
| Module isolation | Modular preset | — |
Use both for comprehensive architectural enforcement.
Related Packages
- solidframe/core — DomainEventInterface
- solidframe/cqrs — Command, Query, Handler interfaces
- solidframe/ddd — ValueObjectInterface
- solidframe/event-sourcing — AbstractEventSourcedAggregateRoot
- solidframe/archtest — Complementary PHPUnit-based architecture tests
Contributing
This repository is a read-only split of the solidframe/solidframe monorepo, auto-synced on every push to main. Issues, pull requests, and discussions belong in the monorepo.
solidframe/phpstan-rules 适用场景与选型建议
solidframe/phpstan-rules 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 8 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 04 月 18 日, 在 PHP 生态内属于活跃度较高的组件。
我们在过去多个企业项目中使用过 solidframe/phpstan-rules 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 solidframe/phpstan-rules 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
统计信息
- 总下载量: 8
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 29
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-04-18