Communityコーディング&開発github.com

Redocly/php-generator

Redocly `php` クライアントジェネレーターの設計書です。`generators/php/` を変更する前に、このスキルを読んで更新してください。

php-generator とは?

このスキルは、Redoclyが提供するPHPクライアントジェネレーター(`generators/php/`)の設計を定義しています。主な目的は、ジェネレーターの振る舞いを変更する際に、まずこのスキルを編集し、その後にコードを一致させることです。生成されるのは、自己完結型の単一PHPファイルで、コンストラクタプロパティプロモーションを使用したモデルクラス、操作ごとの型付きメソッドを持つClientクラス、そして埋め込みランタイムを含みます。PHP 8.1以上を対象とし、cURL拡張によるHTTP通信を行い、Composerの依存関係はゼロです。このスキルは、モデルがfinalクラスであること、Clientはfinalではないこと、名前空間がAPIタイトルから派生すること、操作マップがクラス定数であること、エラーは例外で処理すること、日付型はDateTimeImmutableを使用することなど、多数の設計上の決定事項を詳細に記述しています。また、サービスベースのSDKからの移行ガイドや、変更ループ(スキル編集→コード変更→生成確認)も含まれています。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Redocly/redocly-cli/tree/HEAD/packages/client-generator/eject-assets/skills/php-generator

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

php ジェネレーター — そのスキル

このファイルは、イジェクトされた php ジェネレーター(generators/php/)の設計書です。 ジェネレーターを変更するには、このスキルを最初に編集し、その後コードを一致させてください — このスキルに説明文がない generators/php/ への差分は不完全です。

出力内容

一つの自己完結型 <stem>.php ファイル:コンストラクタプロパティプロモーションを使用したモデルクラス、各操作に型付きメソッドを持つ Client、そして埋め込みランタイム。PHP ≥ 8.1、cURL拡張によるHTTP通信 — Composer依存関係ゼロ。名前空間はAPIタイトルから派生します(identifierFor(title, pascal) — 例:CafeOrders)。

維持すべき設計上の決定

  • モデルは final class であり、コンストラクタプロパティプロモーションを使用し、必須パラメータを先に、オプションパラメータはnullable = null とします。ハイドレーションはクラスごとにコンパイル時に生成されます:fromArray(array $data): self と toArray(): array(ワイヤー名はインライン、nullはシリアライズ時にスキップ)— リフレクションは使用しません。omit スキーマは基底クラスを介してハイドレート/シリアライズします。DISCRIMINATEDユニオンとして型付けされたプロパティまたはレスポンスは、ユニオンの unmarshalX ディスパッチャーを介してハイドレートされるため、コンシューマーは instanceof で絞り込むことができます。その型には array が含まれます。なぜなら、不明なディスクリミネーターを持つ値は生の配列のままになるからです。その他のユニオンはデコードされたJSONのままなので、その型はJSONの型になります:クラスメンバーは array、enumメンバーはそのバッキングスカラー型、日付メンバーは string です。
  • ファイルはPHPStanレベル5に合格します。 ユーザーの自身のプロジェクトに配置されるため、ユーザーのアナライザーがこれを読み取ります。操作テーブルはPHPStanが正確に認識する定数であるため、メソッドはそのエントリがある場合にのみ $op['security'] を読み取り、それ以外の場合は [] を渡します。
  • writeOnly プロパティは、必須であってもオプションです:リクエストとレスポンスは一つのクラスを共有し、レスポンスには決して含まれません。nullable = null であるため、fromArray はそれを含まないレスポンスでもハイドレートでき、toArray はリクエストでそれを送信します。
  • Client クラスは final ではありません — PHPのテストスイートは具象クラスをモックするため(createMock(Client::class))、final にするとすべてのコンシューマーにラッパーインターフェースを強制することになります。モデルクラスは final のままです。
  • すべてのパラメータは独立した引数であるため、それらの名前はメソッド自身が宣言する引数($body、$headers、$idempotencyKey)と同じ名前空間を共有します。 これらは uniqueIdentifiers(..., { taken: … }) で構築します:OpenAPIでは一つの操作が二つの場所で同じ名前を使用することがあり(パスとクエリの両方で id)、PHPは再定義されたパラメータを完全に拒否します。ワイヤー名は変更されないため、リクエストは変更されません。
  • 操作マップはクラス定数 Client::OPERATIONS であり、self::OPERATIONS[...] で読み取られ、ファイルレベルの const ではありません。OPcacheプリロードはクラスと関数を共有メモリに保持しますが、ファイルレベルの定数は削除します。また、オートローダーは Client が存在するとファイルを再度インクルードしません。そのため、ファイルレベルのマップは最初のリクエスト以降、すべてのリクエストで未定義になります。クラス定数はクラスと共に保存されます。
  • 命名: クラスはPascalCase、プロパティ/メソッドは identifierFor(..., RESERVED_WORDS.php) によりcamelCase。予約語には末尾にアンダースコアが付加されます。
  • Enum はネイティブのバックドEnum(string/int)です。その他のスカラー型はエイリアスのままです。判別ユニオン は match ベースの unmarshalX(array $data) ディスパッチャーです。各アームは、その型のプロパティと同じ方法でマッピングターゲットをハイドレートするため、自身のクラスを持たないターゲット(プレーンな oneOf)は生の配列のままです。allOf はフラット化されます。
  • ユニオンは、PHP 8.1で表現可能な型を保持します。 スカラー、enum、クラス、または配列のユニオンは、mixed に潰されるのではなく、ネイティブのユニオン型(int|string、string|array)になります。リッチリストフィルターが一般的なケースであり、その型を失うことは型付きSDKの意味を失うことになります。メンバーが自身のPHP型を持たない場合(インラインオブジェクト、インターセクション、unknown)にのみ mixed にフォールバックします。これは、mixed がユニオン内に現れることができないためです。Null許容性は、ユニオン内では |null として(PHPは ? と | の混在を禁止)、単一型の場合は ?T として表現されます。
  • エラー: 例外がエラーモードです(ApiError/TimeoutError は \RuntimeException を継承)。errorMode は出力を変更しません(ジェネレーターは errorModes: ['throw'] を宣言するため、result は即座に失敗します)。
  • 日付: dateType: Date は format: date/date-time を \DateTimeImmutable として型付けします。ハイドレーションは new \DateTimeImmutable(...)、シリアライズは \DateTimeInterface::ATOM(date-time)または 'Y-m-d'(date)でフォーマットします。クエリパラメータも同様です。
  • メソッド引数: 必須パスパラメータは位置指定、JSONボディは次、オプションクエリパラメータはnullableな名前付き引数、次に ?array $headers、そして変更系メソッドには ?string $idempotencyKey です。
  • 非JSON成功ボディ(PDF、画像、オクテットストリーム)は生のボディを string として返します — バイナリダウンロードは決して void に劣化してはいけません。
  • PHPDocはシグネチャで表現できないものを伝えます。 PHPの array と \Generator は要素の型を消去するため、docblockでそれを明示します:コレクション戻り値は @return Customer[]、<op>Pages()/<op>Items() は @return \Generator<int, Customer>。静的解析と読み手はこれらに依存します。アノテーションなしのハイドレートされた戻り値は型付けされていないように見えます。
  • レスポンスヘッダー: 成功レスポンスヘッダーを宣言する操作は、Envelope(data、headers — int/bool/stringに型変換され、camelCaseキー、存在しない/パースできない値は省略 — および status)を返す <op>WithHeaders() バリアントを取得します。宣言されたヘッダーがない操作はバリアントを取得せず、基本メソッドはボディのみのままです(PHPはフラグによって戻り値の型を変えることができません)。
  • サーバー: 説明がサーバーを宣言する場合、Servers クラスが出力され、サーバーごとに一つの静的メソッドを持ちます。サーバーVARIABLEは、仕様のデフォルト値をデフォルトとする名前付き文字列引数になります(Servers::production(organizationId: 'org_x'))。これにより、テンプレート化されたベースURLを手動で文字列構築する必要がなくなります。クライアントの組み込みデフォルトは、変数デフォルトが置換された servers[0] のままです。
  • ストリームボディはそのまま通過します。 型付けされていない multipart/form-data 操作(ツールキットからの isUntypedMultipartBody:オブジェクトでも $ref でもないスキーマ)またはバイナリ操作(isBinaryContentType)の場合、ボディは mixed となり、ランタイムがそれを処理します(multipartBody / binaryBody):ストリーム resource はcurlによってチャンクでアップロードされ(CURLOPT_UPLOAD + CURLOPT_INFILE、メソッドは CURLOPT_CUSTOMREQUEST によって保持)、文字列に読み込まれることはなく、リトライポリシーに関係なく正確に1回の試行で行われます。消費されたストリームは再生できないからです。CURLOPT_INFILESIZE は、ストリームがシーク可能であり、かつ fstat が配列を返す場合(現在位置からの残りバイト数)にのみ設定されます。php://input、パイプ、stream_stat のないラッパーはチャンク化されます。どちらの操作の string ボディも生で送信され、json_encode はされません。その他のバイナリ操作の値は引き続き json_encode されます。型付けされていないマルチパート操作の array ボディは toMultipart パスを保持します。呼び出し元の Content-Type は、説明やマルチパート境界のものより優先され、send 内で大文字小文字を区別せずに照合されるため、ヘッダーが二度送信されることはありません。このルールはすべての操作に適用されます。リクエスト全体のcurlタイムアウトは引き続き適用されます(timeout: null で無効化)。型付けされたマルチパートボディは影響を受けません。
  • パリティ表面: 認証、Retry-After + ジッターバックオフによるリトライ、試行ごとのcurlタイムアウト、ミドルウェアコーラブル、ページネーション(<op>Pages() / <op>Items() を \Generator として)、SSE(curl_multiポンプ上の iterSse)、マルチパート。
  • ランタイムは、このフォルダ内の runtime/runtime.php に手書きされ(php -l でクリーン)、準備時に埋め込まれます。curl_close は決して呼び出されません(PHP 8.5以降で非推奨、8.0以降はno-op)。--runtime module では、クライアントが require_once する runtime.php として書き出され、その名前空間はクライアントのものに書き換えられるため、一つの名前空間が両方のファイルにまたがります。
  • onSettled は送信されたすべての呼び出しを一度だけ処理します。 send() は、リトライループの後、スローまたはリターンする前に、各 Config->onSettled コーラブル、次に各 Config::$registeredOnSettled コーラブルを、operationId、status(レスポンスなしの場合は0)、timedOut、attempts、durationMs と共に呼び出します。すべてのフックが実行されます。成功した呼び出しでは最初のフックエラーがスローされます。失敗した呼び出しでは元のエラーが優先され、フックエラーは error_log に送られます。SSEストリームは send() の外部で開かれるため、どのフックにも到達しません。
  • テレメトリーは生成時にオプトインです。 client.telemetry: on の場合、ジェネレーターはクライアントの横に <client>.telemetry.php(このフォルダ内の telemetry/telemetry.php から、準備時に埋め込まれ、クライアントの名前空間とジェネレーターバージョンが焼き付けられる)も書き出します。両方のランタイムモードで。クライアントファイルは変更されません。アプリケーションはそれをrequireし、Config->onSettled に new UsageTelemetry(...) を渡します。SDK所有者は代わりに register() を呼び出し、インスタンスを Config::$registeredOnSettled に追加するため、すべてのクライアントがその呼び出しをカウントします。UsageTelemetry::flushRegistered() は登録された各インスタンスをフラッシュします。PHPにはタイマーがありません:ウィンドウが経過したことを検出した呼び出しは最初にそれを送信し、シャットダウン関数が開いているウィンドウを送信します。定数はクラス上に存在します。OPcacheプリロードはファイルレベルの定数を削除するからです。このクラス名はすべてのSDKで予約されています。なぜなら、ファイルはモデルと名前空間を共有するからです。
  • ニュートラルなツールキットのみを使用して作成 — それ以外の場合はdogfoodingガードに失敗します。

サービスベースSDKからの移行

  • リソースごとのサービス($client->customers()->get($id))は、operationIdにちなんで名付けられたフラットなメソッド($client->getCustomer($id))にマッピングされます。オプションのクエリパラメータは名前付き引数スタイルを維持します(filter:、sort:、limit:)。

  • ページネーションのレスポンスヘッダー(getTotalItems()、getLimit())を公開するコレクションラッパーは、<op>WithHeaders() エンベロープ(->headers['paginationTotal'])にマッピングされます。プレーンな反復は <op>Items() / <op>Pages() ジェネレーターにマッピングされます。

  • フィールドエラーを公開する専用のバリデーション例外クラスは、catch (ApiError $e) + $e->status === 422 + デコードされた $e->body にマッピングされます。

  • セッション/ベアラートークンフローは、呼び出し可能な auth: ['bearer' => $tokenProvider] にマッピングされます。リクエストごとに解決されるため、リフレッシュにクライアントの再構築は不要です。

  • 自己文書化します。 client.docs(または --docs)を使用すると、docs フックが <stem>.php.md を書き出します:セキュリティスキーム、次に操作ごとにパラメータ、ボディ、レスポンス型、動作メモのセクション。呼び出しスニペットは、このジェネレーター自身の sample フックから取得されるため、ページはその横にあるSDKの構文のみを表示でき、レイアウトはオーサリングツールキットの renderReferencePage から提供されます。イジェクトされたコピーからは @redocly/client-generator を通じてアクセス可能です。ページ上のページネーションは、このジェネレーターがページネーションを解決するのと同じヘルパー paginationRuleFor によって決定されます。

変更ループ

  1. このスキルを編集します:新しい動作や決定を記述します。
  2. generators/php/ をそれに一致させます。
  3. redocly generate-client を実行し、生成された出力の git diff を検査します — 生成されたファイルは決して手動編集しません。

新しい組み込みバージョンは redocly eject-generator php --update でマージされます。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

関連スキル