Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
PHPのアノテーション(アトリビュート)からOpenAPIのドキュメントを出力し、レスポンスも...
Search
kalibora
November 08, 2024
Programming
0
9
PHPのアノテーション(アトリビュート)からOpenAPIのドキュメントを出力し、レスポンスもそれを元にシリアライズすることで仕様と実装を乖離させず、色々楽できたよって話
kalibora
November 08, 2024
Tweet
Share
More Decks by kalibora
See All by kalibora
Swagger (OpenAPI) と PHPStan で REST API でも型安全っぽく使う
kalibora
0
2.9k
Symfony2 の Functional Test のメモリ使用量と実行時間を削減した話
kalibora
0
5
WebAudioと音の話
kalibora
0
390
Other Decks in Programming
See All in Programming
Contemporary Test Cases
maaretp
0
140
3 Effective Rules for Using Signals in Angular
manfredsteyer
PRO
0
100
アジャイルを支えるテストアーキテクチャ設計/Test Architecting for Agile
goyoki
9
3.3k
とにかくAWS GameDay!AWSは世界の共通言語! / Anyway, AWS GameDay! AWS is the world's lingua franca!
seike460
PRO
1
900
ヤプリ新卒SREの オンボーディング
masaki12
0
130
Outline View in SwiftUI
1024jp
1
340
CSC509 Lecture 13
javiergs
PRO
0
110
タクシーアプリ『GO』のリアルタイムデータ分析基盤における機械学習サービスの活用
mot_techtalk
5
1.5k
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
340
Streams APIとTCPフロー制御 / Web Streams API and TCP flow control
tasshi
2
350
Figma Dev Modeで変わる!Flutterの開発体験
watanave
0
150
型付き API リクエストを実現するいくつかの手法とその選択 / Typed API Request
euxn23
8
2.3k
Featured
See All Featured
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
27
4.3k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
109
49k
How GitHub (no longer) Works
holman
310
140k
Facilitating Awesome Meetings
lara
50
6.1k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
47
2.1k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
169
50k
BBQ
matthewcrist
85
9.3k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
665
120k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
1.9k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
250
21k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Transcript
PHP のアノテーション(アトリビュー ト)からOpenAPI のドキュメントを出 力し、レスポンスもそれを元にシリア ライズすることで仕様と実装を乖離さ せず、色々楽できたよって話 Toshiyuki Fujita (@kalibora)
2024/11/08
最初に 今回の発表は2019 年11 月の KyotoLT 第25 回 で喋った Swagger (OpenAPI)
と PHPStan で REST API でも型安全っぽく使う の焼き 直しです でも多分誰も知らないと思うのここでも話します 現状はOpenAPI は3 系ですが、昔の話なので2 系(まだSwagger と呼 ばれてた時代)が話のベースとなっていますがご容赦ください 2024/11/11 追記 この発表後に現代では API Platform を使えばもっといい感じに できると教えていただきました なのでこの資料は過去の歴史としてご笑覧ください
自己紹介 Toshiyuki Fujita ID: @kalibora 20 年くらいPHP いじってご飯を食べてます Yahoo! JAPAN
-> (Crocos) -> Otobank -> RABO 今回のは Otobank 時代にやってた話です Symfony と Doctrine が好きです
OpenAPI とは - (WebAPI の仕様書くツールって、昔はいろいろあったけど結局もう これが勝ったという認識でよろしいか?) OpenAPI Specification (以前はSwagger Specification
として知ら れていた)は、Web サービスを記述、生成、消費[ 訳語疑問点] 、 可視化するための機械可読なインターフェース記述言語の仕様で ある[1] 。 See: https://ja.wikipedia.org/wiki/OpenAPI_Specification “ “
突然ですが・・・ OpenAPI の仕様書くのって ダルいですよね?
OpenAPI の仕様書くのってダルいですよね? swagger: "2.0" info: version: "1.0.0" description: "こういうやつですね" title:
"Swagger Petstore" termsOfService: "http://swagger.io/terms/" contact: email: "
[email protected]
" license: name: "Apache 2.0" url: "http://www.apache.org/licenses/LICENSE-2.0.html" host: "petstore.swagger.io" basePath: "/v2"
OpenAPI の仕様を書くための手段 Swagger Editor https://editor.swagger.io/ ブラウザベースで記述できるエディター swagger-php https://github.com/zircote/swagger-php PHP のアトリビュート(もしくはアノテーション)を使って仕様
を記述できる これを使って楽する!! (※これ調べたの昔なので最近はもっと色々増えてるかもね!)
swagger-php(v2) の使用例 例えば User というクラスに name プロパティがあって、 これをAPI のレスポンスのI/F として定義したい場合、
/** * @SWG\Definition() */ class User { /** * ユーザー名 * @SWG\Property() */ public string $name; }
出力結果 swagger: '2.0' definitions: User: properties: name: description: ユーザー名 type:
string
便利! でもデフォルトのswagger-php だと 不便な点があった
デフォルトのswagger-php だと不便な点 getter などのアクセサに対応していない 実際の業務で使っているエンティティクラスは private なプロパ ティ + getter
な構成が多いのでそのままだとうまく使えない (※2024/11/08 現在ではできるようになったのか未確認です) ので、自前で swagger-php を拡張して対応した
自前で swagger-php を拡張して getter に対応 /** * @SWG\Definition() */ class
User { private sring $name; /** * ユーザ名 * @SWG\Property() */ public function getName() : string { return $this->name; } }
出力結果 先ほどとほぼ同じ結果のOpenAPI 仕様が書き出されるように拡張。 return type hint を使い、nullable でないものは required に。
swagger: '2.0' definitions: User: required: - name properties: name: description: ユーザー名 type: string
いい!! けどせっかくなら そのままAPI の挙動にも反映したい
API の挙動にも反映したい そもそもだけど、アノテーション(アトリビュート)書いたらその ままAPI の挙動に反映できたら便利 アノテーション(アトリビュート)から 仕様(OpenAPI 仕様の出力結果) 実装(実際にレスポンスとして返す値) この両方に影響を与えたい
オブジェクトから配列に変換する処理を書いた ようするにシリアライザー(ノーマライザー?)を実装した。 /** * @SWG\Definition() */ class User { private
sring $name; /** * ユーザ名 * @SWG\Property() */ public function getName() : string { return $this->name; } } 先ほどと同じこういうクラスの定義から、
連想配列に変換 SwaggerSerializer という自前で実装したクラスが @SWG\Property アノ テーションを読み取り、連想配列にする。 $user = new User('Otobank
Taro'); var_dump($swaggerSerializer->serialize($user)); // [name => 'Otobank Taro'] これにより、 @SWG\Property を定義した getter は API のレスポンスの I/F としても露出するし、実際にAPI のレスポンスとしても返却される ようにした。
PHPStan と組み合わせる
PHPStan と組み合わせる PHPStan と組み合わせれば、サーバー(API )側とクライアント側 との間でAPI 呼び出しをしても型安全にできる
例えばこんなケース
API 側のエンティティクラス class Campaign { /** * キャンペーン開始日時 * @SWG\Property()
*/ public function getStartedAt() : \DateTimeInterface { /* 実装は省略 */ } /** * キャンペーン終了日時 * @SWG\Property() */ public function getEndedAt() : ?\DateTimeInterface { /* 実装は省略 */ } }
出力されるOpenAPI 仕様書 swagger: '2.0' definitions: Campaign: required: - startedAt properties:
startedAt: description: キャンペーン開始日時 type: string format: date-time endedAt: description: キャンペーン終了日時 type: string format: date-time x-nullable: true # ←ここに注目
OpenAPI 仕様書から自動生成されたクライアント側のクラス class Campaign implements ModelInterface, ArrayAccess { /** *
Gets started_at * @return \DateTime */ public function getStartedAt() { /* 実装は省略 */ } /** * Gets ended_at * @return \DateTime|null */ public function getEndedAt() { /* 実装は省略 */ } } ※ クライアントの自動生成は swagger-api/swagger-codegen をベースに x-nullable を うまく解釈できるようにテンプレートはカスタマイズしています
クライアント側のPHPStan でのチェック クライアント側で自動生成されたクラスでも getEndedAt() が nullable なので、API を呼び出す側の実装者が、うっかりキャンペーンの終了日 時が nullable
である。 という仕様を知らなかった(読み取りそこねた)としても、自動的に エラーを検出できる。
さらにいろんなことに活用できる
その他の活用事例 public なAPI がセンシティブな情報を返していないことをCI で担保する 仕様と実装が乖離していないため、仕様であるyaml(or json) ファイルを機械 的にパースして特定のキーワード(password など)がレスポンスにあった
らCI で落とす 詳しくは: API が不必要にセンシティブなデータを返していないことをCI で 担保する - OTOBANK Engineering Blog API のI/F 変更があったらサマリをGitHub Actions でPR に書いてもらう OpenAPITools/openapi-diff を使って仕様の差分を取る さらに(プロパティが削除されるなどの理由で)後方互換性が崩れていたら CI で落とす
おしまい