mcpField
P
php-specialist

php-specialist

github.com/pixel-process-ug開発382 回インストール34 週間前に更新
リポジトリ

php-specialist について

PHP 8.x コードの作成、レビュー、リファクタリングを行う開発者向けのスキルです。最新の言語機能、PSR 規格、Composer、静的解析、SOLID に沿った設計を扱います。

主な機能

  1. 01PHP 8.x 言語機能の指針
  2. 02PSR 規格と Composer の実践
  3. 03PHPStan と Psalm による静的解析
  4. 04PHP CS Fixer または Pint によるスタイル検査
  5. 05型安全性と SOLID 設計パターン

活用シーン

  1. 01インターフェースを定義して PHP 機能を開発
  2. 02型安全性を高めるコードのリファクタリング
  3. 03Composer 依存関係とコード品質の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/pixel-process-ug/superkit-agents --skill php-specialist

インストール後、Claude Code で/php-specialistを入力して読み込まれたか確認します。

詳細

PHP スペシャリスト

概要

PSR 標準と SOLID 原則に準拠した、モダンで型安全かつ保守しやすい PHP 8.x コードを作成します。このスキルでは、PHP 8.0 から 8.4 で導入された言語機能、PSR 相互運用標準、Composer による依存関係管理、PHPStan と Psalm による静的解析、PHP CS Fixer と Pint によるコーディングスタイルの適用、そして実行時ではなくコンパイル時の型システムを活用して正確性を保証するアーキテクチャパターンまで、モダンな PHP ツールチェーン全体を扱います。

フレームワークを使用する場合も、単独で使用する場合も、PHP コードの作成、レビュー、リファクタリング時には常にこのスキルを適用してください。

複数フェーズのプロセス

フェーズ 1:環境の評価
  1. composer.json の require.php から PHP のバージョンを確認します
  2. composer.json を確認し、オートロード方式(PSR-4 名前空間)を把握します
  3. 静的解析の設定(phpstan.neon、psalm.xml)を確認します
  4. コーディング規約ツール(pint.json、.php-cs-fixer.php)を特定します
  5. 既存のパターン(列挙型、DTO、値オブジェクト、インターフェース)を一覧にします

停止 — PHP のバージョンとオートロード方式を把握するまで、コードを書いてはいけません。

フェーズ 2:設計
  1. 実装に先立ち、インターフェースと契約を定義します
  2. readonly プロパティを使って値オブジェクトと DTO を設計します
  3. 該当する場合は、ドメイン概念を値付き列挙型に対応付けます
  4. ドメインの例外階層を計画します
  5. 依存性注入のための境界を特定します

停止 — 重要な境界のインターフェースを定義するまで、実装してはいけません。

フェーズ 3:実装
  1. まずインターフェースを記述します — 具象クラスより先に契約を定めます
  2. コンストラクタープロモーション、readonly プロパティ、ユニオン型/インターセクション型を使って実装します
  3. switch より match 式を使い、明確さのために名前付き引数を使います
  4. 関数型の合成には、ファーストクラス callable 構文を活用します
  5. すべてのクラス境界で SOLID 原則を適用します

停止 — PHP ファイルで strict_types 宣言を省略してはいけません。

フェーズ 4:品質保証
  1. 達成可能な最高レベル(目標レベル 9)で PHPStan を実行します
  2. PHP CS Fixer または Laravel Pint でコーディングスタイルを徹底します
  3. 型の網羅性を確認します — 正当な理由なく mixed を使ってはいけません
  4. SOLID 原則への違反やコードの不吉な兆候がないか確認します
  5. Composer のオートロードが最適化されていることを確認します(--classmap-authoritative)

PHP バージョン機能の選択表

機能最低バージョン使用する場面
コンストラクタープロモーション8.0コンストラクターパラメーターがあるすべてのクラス
名前付き引数8.0パラメーターが 3 つ以上ある関数、または真偽値フラグを使う場合
Match 式8.0あらゆる switch 文(厳密で、値を返す)
ユニオン型8.0パラメーターが複数の型を受け付ける場合
値付き列挙型8.1値を持つ名前付き定数の集合
Readonly プロパティ8.1不変の DTO、値オブジェクト
ファイバー8.1非同期フレームワーク(直接使うことはまれ)
ファーストクラス callable8.1関数型の合成、array_map/filter
Readonly クラス8.2プロパティをすべて readonly にした 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());
    }
}
Readonly プロパティとクラス(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();
}
ファーストクラス callable 構文(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();
ファイバー(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-7HTTP メッセージインターフェイスミドルウェアパイプラインで使用する不変のリクエスト/レスポンスオブジェクト
PSR-11コンテナインターフェイス依存性注入コンテナの相互運用性
PSR-12拡張コーディングスタイルPSR-2 に代わる標準:書式、空白、宣言
PSR-15HTTP サーバーミドルウェアMiddlewareInterface と RequestHandlerInterface
PSR-17HTTP ファクトリーPSR-7 オブジェクト(RequestFactory、ResponseFactory)を作成
PSR-18HTTP クライアント相互運用可能な HTTP クライアント向けの ClientInterface
PSR-4 オートローディング
{
    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Domain\\": "src/Domain/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    }
}

ルール:名前空間の各セグメントは、ディレクトリに 1 対 1 で対応します。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 --strictcomposer.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+ デッドコード、常に true/false となる条件
5+ 関数呼び出しの引数の型
6+ 型ヒントの不足を報告
7+ ユニオン型を網羅的にチェック
8+ nullable 型を厳密にチェック
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 を使用します。Pint は Laravel 固有のデフォルト設定を備えた PHP CS Fixer のラッパーです。

PHP における SOLID 原則

原則指針PHP の仕組み
S — 単一責任クラスを変更する理由は 1 つだけアクションクラス、小規模なサービス
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);
}

型安全性パターン

Readonly クラスによるブランド型/不透明型
// 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を逃げ道として使う型安全性に穴が開きます共用体型またはジェネリクスで型を絞り込みます
文字列に依存した型付けのコードタイプミスによる実行時エラーが発生します名前付き定数にはバックドEnumを使います
巨大クラス(責務が多すぎる)テストが困難で、結合度が高くなりますActionクラスに分割します
静的解析を抑制する実際のバグが隠れてしまいます問題を修正し、説明を添えた場合に限り@phpstan-ignoreを追加します
declare(strict_types=1)がない気付かないうちに型変換によるバグが発生しますすべてのPHPファイルに追加します
配列形式のドメインデータIDEのサポートがなく、型安全性もありませんreadonly DTOまたは値オブジェクトを使います
サービスロケーター(ロジック内のapp())依存関係が隠れ、テストが困難になりますコンストラクターインジェクションを使います
\Exceptionを広範囲に捕捉する想定外のエラーまで握りつぶします具体的な例外型を捕捉します
可変の値オブジェクト共有状態によるバグが発生しますreadonlyクラスを使い、新しいインスタンスを返します
composer auditを無視する既知の脆弱性が本番環境に残りますCIで実行し、ビルド失敗として扱います
深い継承(3階層以上)基底クラスが脆弱になりますコンポジションとインターフェースを優先します
finalが付いていないクラス意図しない拡張が可能になりますデフォルトでfinalを付け、拡張を意図して設計した場合のみ外します

安易な正当化を防ぐルール

  • 「小さなスクリプトにすぎない」という理由でdeclare(strict_types=1)を省略しないでください。すべてのファイルに追加します。
  • より限定的な型を使えない理由を説明するコメントなしに、mixedを使わないでください。
  • コードが正しい理由を記した説明なしに、PHPStanのエラーを抑制しないでください。
  • Laravelを使っている場合でも、ビジネスロジックでサービスロケーターパターン(app())を使わないでください。
  • 「実装が1つしかない」という理由で、重要な境界にインターフェースを用意するのを省略しないでください。実装は2つになります。

ドキュメントの参照(Context7)

最新のドキュメントを参照するには、mcp__context7__resolve-library-idの後にmcp__context7__query-docsを使います。取得したドキュメントは、記憶している知識より優先されます。

  • php — 言語機能、組み込み関数、またはPHP 8.x構文の場合
  • composer — パッケージ管理、自動読み込み、またはスクリプト設定の場合

統合ポイント

スキル連携方法
laravel-specialistPHP 8.xの機能は、Eloquentのキャスト、Enum、readonly DTO、型付きコレクションを支えます
senior-backendSOLIDアーキテクチャ、インターフェース主導の設計、エラー処理パターン
test-driven-development厳密な型アサーションを用いたPHPUnit/Pestのテスト
clean-codePHPレベルでのSOLID、DRY、コードの不吉な臭いの検出
security-review入力検証、型変換のリスク、依存関係の脆弱性
laravel-boostガイドラインとMCPツールによるAI生成PHPコードの品質向上

スキルの種類

FLEXIBLE — 作業の規模に応じてプロセスのフェーズを調整してください。単一の関数であれば、Phase 3と4のみで十分な場合があります。新しいモジュールやパッケージでは、4つすべてのフェーズに従ってください。規模にかかわらず必須なのは、declare(strict_types=1)、プロジェクトで設定されたレベルでのPHPStan準拠、そしてPSR-4自動読み込みです。

コメント