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によって決定されます。
変更ループ
- このスキルを編集します:新しい動作や決定を記述します。
generators/php/をそれに一致させます。redocly generate-clientを実行し、生成された出力のgit diffを検査します — 生成されたファイルは決して手動編集しません。
新しい組み込みバージョンは redocly eject-generator php --update でマージされます。