关于 php-specialist
面向编写、审查或重构 PHP 8.x 代码的开发者技能,涵盖现代语言特性、PSR 标准、Composer、静态分析和 SOLID 设计指导。
主要功能
- 01PHP 8.x 语言特性指导
- 02PSR 标准与 Composer 实践
- 03PHPStan 和 Psalm 静态分析
- 04使用 PHP CS Fixer 或 Pint 检查风格
- 05类型安全与 SOLID 设计模式
适用场景
- 01从定义接口开始开发 PHP 功能
- 02重构代码以增强类型安全
- 03检查 Composer 依赖与代码质量
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill php-specialist安装后在 Claude Code 中输入/php-specialist确认已加载。
详细介绍
PHP 专家
概述
编写现代、类型安全且易于维护的 PHP 8.x 代码,并遵循 PSR 标准和 SOLID 原则。本技能涵盖完整的现代 PHP 工具链:PHP 8.0 至 8.4 引入的语言特性、PSR 互操作标准、Composer 依赖管理、使用 PHPStan 和 Psalm 进行静态分析、使用 PHP CS Fixer 和 Pint 强制执行编码风格,以及利用类型系统在编译时而非运行时确保正确性的架构模式。
在任何框架或独立环境中编写、审查或重构 PHP 代码时,都应运用此技能。
多阶段流程
阶段 1:环境评估
- 从
composer.json->require.php确认 PHP 版本 - 检查
composer.json中的自动加载策略(PSR-4 命名空间) - 检查静态分析配置(
phpstan.neon、psalm.xml) - 确认编码标准工具(
pint.json、.php-cs-fixer.php) - 梳理现有模式:枚举、DTO、值对象、接口
停止 — 未确认 PHP 版本和自动加载策略前,不要编写代码。
阶段 2:设计
- 在实现之前定义接口和契约
- 使用只读属性设计值对象和 DTO
- 在适用时将领域概念映射为有值枚举
- 规划领域的异常层级
- 确定依赖注入的切入点
停止 — 未为关键边界定义接口前,不要开始实现。
阶段 3:实现
- 先编写接口——先定义契约,再编写具体类
- 使用构造函数属性提升、只读属性、联合类型和交叉类型
- 优先使用 match 表达式而非 switch;使用命名参数以提高可读性
- 利用一等可调用语法进行函数式组合
- 在每个类的边界处应用 SOLID 原则
停止 — 不要在任何 PHP 文件中省略 strict_types 声明。
阶段 4:质量保证
- 将 PHPStan 运行到可达到的最高级别(目标级别 9)
- 使用 PHP CS Fixer 或 Laravel Pint 强制执行编码风格
- 检查类型覆盖率——未经说明,不得使用
mixed - 检查是否违反 SOLID 原则或存在代码异味
- 确认 Composer 自动加载已优化(
--classmap-authoritative)
PHP 版本特性决策表
| 特性 | 最低版本 | 使用时机 |
|---|---|---|
| 构造函数属性提升 | 8.0 | 任何包含构造函数参数的类 |
| 命名参数 | 8.0 | 有 3 个或更多参数的函数,或包含布尔标记的函数 |
| Match 表达式 | 8.0 | 任何 switch 语句(严格匹配并返回值) |
| 联合类型 | 8.0 | 参数可接受多种类型 |
| 有值枚举 | 8.1 | 任何包含带值命名常量的集合 |
| 只读属性 | 8.1 | 不可变 DTO、值对象 |
| Fiber | 8.1 | 异步框架(很少直接使用) |
| 一等可调用对象 | 8.1 | 函数式组合、array_map/filter |
| 只读类 | 8.2 | 全部为只读属性的 DTO(简写) |
| DNF 类型 | 8.2 | 复杂的联合类型与交叉类型组合 |
| Override 属性 | 8.3 | 重写父类方法时(安全检查) |
| 属性钩子 | 8.4 | 无需单独方法即可实现计算属性 |
现代 PHP 8.x 特性
枚举(PHP 8.1+)
// Backed enum with methods — replaces class constants and magic strings
enum OrderStatus: string
{
case Draft = 'draft';
case Pending = 'pending';
case Confirmed = 'confirmed';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
public function label(): string
{
return match ($this) {
self::Draft => 'Draft',
self::Pending => 'Pending Review',
self::Confirmed => 'Confirmed',
self::Shipped => 'Shipped',
self::Delivered => 'Delivered',
self::Cancelled => 'Cancelled',
};
}
public function isFinal(): bool
{
return in_array($this, [self::Delivered, self::Cancelled], true);
}
/** @return list<self> */
public static function active(): array
{
return array_filter(self::cases(), fn (self $s) => ! $s->isFinal());
}
}
只读属性和类(PHP 8.1 / 8.2)
// Readonly class — all properties are implicitly readonly
readonly class Money
{
public function __construct(
public int $amount,
public string $currency,
) {}
public function add(self $other): self
{
if ($this->currency !== $other->currency) {
throw new CurrencyMismatchException($this->currency, $other->currency);
}
return new self($this->amount + $other->amount, $this->currency);
}
public function isPositive(): bool
{
return $this->amount > 0;
}
}
构造函数属性提升
class CreateUserAction
{
public function __construct(
private readonly UserRepository $users,
private readonly Hasher $hasher,
private readonly EventDispatcher $events,
) {}
public function execute(CreateUserData $data): User
{
$user = $this->users->create([
'name' => $data->name,
'email' => $data->email,
'password' => $this->hasher->make($data->password),
]);
$this->events->dispatch(new UserCreated($user));
return $user;
}
}
命名参数
// Improves readability for functions with many parameters or boolean flags
$user = User::create(
name: $request->name,
email: $request->email,
isAdmin: false,
sendWelcomeEmail: true,
);
// Particularly valuable with optional parameters
$response = Http::timeout(seconds: 30)
->retry(times: 3, sleepMilliseconds: 500, throw: true)
->get($url);
Match 表达式
// match is strict (===), exhaustive, and returns a value
$discount = match (true) {
$total >= 10000 => 0.15,
$total >= 5000 => 0.10,
$total >= 1000 => 0.05,
default => 0.00,
};
// Replaces switch with no fall-through risk
$handler = match ($event::class) {
OrderPlaced::class => new HandleOrderPlaced(),
PaymentFailed::class => new HandlePaymentFailed(),
default => throw new UnhandledEventException($event),
};
联合类型和交叉类型
// Union type — accepts either type
function findUser(int|string $identifier): User
{
return is_int($identifier)
? User::findOrFail($identifier)
: User::where('email', $identifier)->firstOrFail();
}
// Intersection type — must satisfy all interfaces
function processLoggableEntity(Loggable&Serializable $entity): void
{
$entity->log();
$data = $entity->serialize();
}
// DNF types (PHP 8.2) — combine union and intersection
function handle((Renderable&Countable)|string $content): string
{
if (is_string($content)) {
return $content;
}
return $content->render();
}
一等可调用语法(PHP 8.1+)
// Create closures from named functions
$slugify = Str::slug(...);
$titles = array_map($slugify, $names);
// Method references
$validator = Validator::make(...);
// Useful for pipeline / collection patterns
$activeUsers = collect($users)
->filter(UserPolicy::isActive(...))
->map(UserTransformer::toArray(...))
->values();
Fiber(PHP 8.1+)
// Fibers enable cooperative multitasking — foundation for async frameworks
$fiber = new Fiber(function (): void {
$value = Fiber::suspend('paused');
echo "Resumed with: {$value}";
});
$result = $fiber->start(); // Returns 'paused'
$fiber->resume('hello world'); // Prints: "Resumed with: hello world"
// Practical use: async HTTP client internals, event loops (Revolt, ReactPHP)
// Application developers rarely use Fiber directly — frameworks abstract it
PSR 标准
| PSR | 名称 | 相关性 |
|---|---|---|
| PSR-1 | 基础编码标准 | 基准要求:<?php 标签、UTF-8、命名空间/类约定 |
| PSR-4 | 自动加载 | 在 composer.json 中将命名空间映射到目录 — 必须遵循 |
| PSR-7 | HTTP 消息接口 | 用于中间件管道的不可变请求/响应对象 |
| PSR-11 | 容器接口 | 依赖注入容器互操作性 |
| PSR-12 | 扩展编码风格 | 取代 PSR-2:格式、空格、声明 |
| PSR-15 | HTTP 服务端中间件 | MiddlewareInterface 和 RequestHandlerInterface |
| PSR-17 | HTTP 工厂 | 创建 PSR-7 对象(RequestFactory、ResponseFactory) |
| PSR-18 | HTTP 客户端 | 用于互操作 HTTP 客户端的 ClientInterface |
PSR-4 自动加载
{
"autoload": {
"psr-4": {
"App\\": "app/",
"Domain\\": "src/Domain/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}
}
规则:命名空间的每个部分都与目录一一对应。App\Http\Controllers\UserController 位于 app/Http/Controllers/UserController.php。
Composer 依赖管理
基本命令
| 命令 | 用途 |
|---|---|
composer require package/name | 添加生产依赖 |
composer require package/name --dev | 添加开发依赖 |
composer update --dry-run | 预览将发生的更改 |
composer why package/name | 显示安装某个包的原因 |
composer audit | 检查已知安全漏洞 |
composer bump | 将版本约束更新为已安装的版本 |
composer validate --strict | 验证 composer.json 和 composer.lock |
最佳实践
- 始终提交
composer.lock— 确保在不同环境中可重复安装 - 使用
^(插入符)约束:"laravel/framework": "^12.0"允许次要版本/补丁版本更新 - 分开管理开发依赖:测试、静态分析和调试工具放入
require-dev - 在 CI 中运行
composer audit,以发现已知漏洞 - 在生产环境中使用
composer dump-autoload --classmap-authoritative提升速度
静态分析
PHPStan 等级
| 等级 | 检查内容 |
|---|---|
| 0 | 基础检查:未定义变量、未知类、错误的函数调用 |
| 1 | + 可能未定义的变量、$this 上的未知方法 |
| 2 | + 所有表达式上的未知方法(不只是 $this) |
| 3 | + 验证返回类型 |
| 4 | + 死代码、始终为真/假的条件 |
| 5 | + 函数调用的参数类型 |
| 6 | + 报告缺失的类型提示 |
| 7 | + 全面检查联合类型 |
| 8 | + 严格检查可空类型 |
| 9 | + 禁止使用未显式处理的 mixed 类型 |
PHPStan 配置
# phpstan.neon
parameters:
level: 9
paths:
- app
- src
excludePaths:
- app/Console/Kernel.php
ignoreErrors: []
checkMissingIterableValueType: true
checkGenericClassInNonGenericObjectType: true
includes:
- vendor/larastan/larastan/extension.neon # Laravel-specific rules
PHP CS Fixer / Pint
// .php-cs-fixer.php — for non-Laravel projects
return (new PhpCsFixer\Config())
->setRules([
'@PER-CS' => true,
'strict_types' => true,
'declare_strict_types' => true,
'ordered_imports' => ['sort_algorithm' => 'alpha'],
'no_unused_imports' => true,
'trailing_comma_in_multiline' => true,
])
->setFinder(
PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests'])
);
对于 Laravel 项目,使用带有 pint.json 预设的 Pint — 它封装了采用 Laravel 特定默认设置的 PHP CS Fixer。
PHP 中的 SOLID 原则
| 原则 | 指导方针 | PHP 机制 |
|---|---|---|
| S — 单一职责 | 每个类只有一个变更理由 | Action 类、小型服务 |
| O — 开闭原则 | 无需修改源代码即可扩展行为 | 接口、策略模式、枚举 |
| L — 里氏替换原则 | 子类型必须能够替换基类型 | 协变返回类型、逆变参数 |
| I — 接口隔离原则 | 客户端只依赖其使用的方法 | 小型、专注的接口 |
| D — 依赖倒置原则 | 依赖抽象而非具体实现 | 构造函数注入、接口绑定 |
依赖倒置示例
// Contract (abstraction)
interface PaymentGateway
{
public function charge(Money $amount, PaymentMethod $method): PaymentResult;
}
// Implementation (concretion) — can be swapped without changing consumers
final class StripeGateway implements PaymentGateway
{
public function __construct(private readonly StripeClient $client) {}
public function charge(Money $amount, PaymentMethod $method): PaymentResult
{
// Stripe-specific logic
}
}
// Consumer depends on abstraction only
final class ProcessPaymentAction
{
public function __construct(private readonly PaymentGateway $gateway) {}
public function execute(Order $order): PaymentResult
{
return $this->gateway->charge($order->total, $order->paymentMethod);
}
}
错误处理模式
自定义异常层级
// Base domain exception
abstract class DomainException extends \RuntimeException {}
// Specific exceptions with factory methods
final class InsufficientFundsException extends DomainException
{
public static function forAccount(Account $account, Money $required): self
{
return new self(sprintf(
'Account %s has %d %s but %d %s is required.',
$account->id,
$account->balance->amount,
$account->balance->currency,
$required->amount,
$required->currency,
));
}
}
Result 模式(将错误作为值)
/** @template T */
readonly class Result
{
/** @param T|null $value */
private function __construct(
public bool $ok,
public mixed $value = null,
public ?string $error = null,
) {}
/** @param T $value */
public static function success(mixed $value): self
{
return new self(ok: true, value: $value);
}
public static function failure(string $error): self
{
return new self(ok: false, error: $error);
}
}
// Usage — caller must handle both paths
$result = $action->execute($data);
if (! $result->ok) {
return response()->json(['error' => $result->error], 422);
}
类型安全模式
通过只读类实现品牌类型/不透明类型
// Prevent accidental mixing of IDs from different entities
readonly class UserId
{
public function __construct(public int $value) {}
public function equals(self $other): bool
{
return $this->value === $other->value;
}
}
readonly class OrderId
{
public function __construct(public int $value) {}
}
// Compiler prevents: processOrder(new UserId(1)) when OrderId is expected
function processOrder(OrderId $orderId): void { /* ... */ }
通过 PHPStan 注解实现泛型集合
/**
* @template T
* @implements \IteratorAggregate<int, T>
*/
final class TypedCollection implements \IteratorAggregate, \Countable
{
/** @param list<T> $items */
public function __construct(private array $items = []) {}
/** @param T $item */
public function add(mixed $item): void
{
$this->items[] = $item;
}
/** @return \ArrayIterator<int, T> */
public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->items);
}
public function count(): int
{
return count($this->items);
}
}
反模式 / 常见错误
| 反模式 | 失败原因 | 应采取的做法 |
|---|---|---|
将 mixed 用作逃生口 | 类型安全网存在漏洞 | 使用联合类型或泛型缩小类型范围 |
| 使用字符串表示类型 | 拼写错误会导致运行时错误 | 使用有底层值的枚举表示具名常量 |
| 上帝类(职责过多) | 难以测试、耦合度高 | 拆分为 Action 类 |
| 抑制静态分析 | 隐藏真正的错误 | 修复问题;只有在说明原因后才添加 @phpstan-ignore |
缺少 declare(strict_types=1) | 类型会被静默强制转换,从而产生错误 | 添加到每个 PHP 文件中 |
| 使用数组结构表示领域数据 | 缺少 IDE 支持,也没有类型安全保障 | 使用只读 DTO 或值对象 |
服务定位器(在逻辑中使用 app()) | 依赖关系隐藏,难以测试 | 使用构造函数注入 |
宽泛捕获 \Exception | 会吞掉意料之外的错误 | 捕获特定的异常类型 |
| 可变值对象 | 共享状态会导致错误 | 使用 readonly 类,并返回新实例 |
忽略 composer audit | 生产环境中存在已知漏洞 | 在 CI 中运行,并将失败视为构建失败 |
| 深层继承(3 层以上) | 脆弱基类问题 | 优先使用组合和接口 |
类未标记为 final | 可能被意外扩展 | 默认使用 final,仅在有意设计为可扩展时开放 |
防止自我合理化
- 不要因为“这只是一个小脚本”而跳过
declare(strict_types=1)——所有地方都要添加。 - 不要在没有注释说明为何无法使用更窄类型的情况下使用
mixed。 - 不要在没有书面说明代码为何正确的情况下抑制 PHPStan 错误。
- 不要在业务逻辑中使用服务定位器模式(
app()),即使是在 Laravel 中也是如此。 - 不要因为“只有一种实现”而跳过关键边界的接口——以后会有第二种实现。
文档查询(Context7)
使用 mcp__context7__resolve-library-id,然后使用 mcp__context7__query-docs 获取最新文档。返回的文档优先于记忆中的知识。
php— 用于语言特性、内置函数或 PHP 8.x 语法composer— 用于包管理、自动加载或脚本配置
集成点
| 技能 | 连接方式 |
|---|---|
laravel-specialist | PHP 8.x 特性支持 Eloquent 转换、枚举、只读 DTO 和类型化集合 |
senior-backend | SOLID 架构、接口驱动设计、错误处理模式 |
test-driven-development | 使用强类型断言进行 PHPUnit/Pest 测试 |
clean-code | SOLID、DRY,以及 PHP 层面的代码异味检测 |
security-review | 输入验证、类型强制转换风险、依赖漏洞 |
laravel-boost | 通过指南和 MCP 工具提升 AI 生成 PHP 代码的质量 |
技能类型
灵活 — 根据工作范围调整各阶段的流程。单个函数可能只需要第 3 和第 4 阶段。新模块或软件包应遵循全部四个阶段。无论工作范围如何,以下要求都不可妥协:declare(strict_types=1)、符合项目配置级别的 PHPStan 要求,以及 PSR-4 自动加载。