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
130
PHPのアノテーション(アトリビュート)からOpenAPIのドキュメントを出力し、レスポンスもそれを元にシリアライズすることで仕様と実装を乖離させず、色々楽できたよって話
kalibora
November 08, 2024
Tweet
Share
More Decks by kalibora
See All by kalibora
QA環境で誰でも自由自在に現在時刻を操って検証できるようにした話
kalibora
1
110
Swagger (OpenAPI) と PHPStan で REST API でも型安全っぽく使う
kalibora
0
3k
Symfony2 の Functional Test のメモリ使用量と実行時間を削減した話
kalibora
0
5
WebAudioと音の話
kalibora
0
390
Other Decks in Programming
See All in Programming
KMP와 kotlinx.rpc로 서버와 클라이언트 동기화
kwakeuijin
0
260
LLM Supervised Fine-tuningの理論と実践
datanalyticslabo
8
1.7k
20年もののレガシープロダクトに 0からPHPStanを入れるまで / phpcon2024
hirobe1999
0
940
AppRouterを用いた大規模サービス開発におけるディレクトリ構成の変遷と問題点
eiganken
1
380
htmxって知っていますか?次世代のHTML
hiro_ghap1
0
380
為你自己學 Python
eddie
0
490
暇に任せてProxmoxコンソール 作ってみました
karugamo
2
770
Оптимизируем производительность блока Казначейство
lamodatech
0
830
EC2からECSへ 念願のコンテナ移行と巨大レガシーPHPアプリケーションの再構築
sumiyae
3
530
テストコード書いてみませんか?
onopon
2
270
Compose UIテストを使った統合テスト
hiroaki404
0
110
Effective Signals in Angular 19+: Rules and Helpers
manfredsteyer
PRO
0
270
Featured
See All Featured
RailsConf 2023
tenderlove
29
960
Being A Developer After 40
akosma
89
590k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
45
2.3k
Java REST API Framework Comparison - PWX 2021
mraible
28
8.3k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
230
52k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.1k
Mobile First: as difficult as doing things right
swwweet
222
9k
Rails Girls Zürich Keynote
gr2m
94
13k
Done Done
chrislema
182
16k
Testing 201, or: Great Expectations
jmmastey
41
7.2k
GraphQLとの向き合い方2022年版
quramy
44
13k
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 で落とす
おしまい