Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Swagger (OpenAPI) と PHPStan で REST API でも型安全っぽく使う
Search
kalibora
November 26, 2019
Programming
3.6k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Swagger (OpenAPI) と PHPStan で REST API でも型安全っぽく使う
Swagger と swagger-php(+自作拡張)と swagger-codegen と phpstan を使ってやや型安全に開発をしている話。
kalibora
November 26, 2019
More Decks by kalibora
See All by kalibora
QA環境で誰でも自由自在に現在時刻を操って検証できるようにした話
kalibora
0
570
PHPのアノテーション(アトリビュート)からOpenAPIのドキュメントを出力し、レスポンスもそれを元にシリアライズすることで仕様と実装を乖離させず、色々楽できたよって話
kalibora
0
270
Symfony2 の Functional Test のメモリ使用量と実行時間を削減した話
kalibora
0
26
WebAudioと音の話
kalibora
0
460
Other Decks in Programming
See All in Programming
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
5k
AWS DevOps Agentで インシデント対応をAIに任せたい
honmarkhunt
7
3.2k
半永久的に提供し続けられるプライベートクラウドを目指して ― 利用者の認知負荷を抑えるAPI抽象化とハードウェア世代交代の基盤設計
tomokon
0
100
Java 27新機能 / Java 27 new features
kishida
2
170
モバイル交通系ICへのチャージ実例から考える、クロスプラットフォーム開発におけるiOS実機テスト設計とCI運用
yusuga
1
540
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
0
1.9k
仕様駆動開発による爆速プロダクト開発 / Bakusoku Spec Driven Development
kobakei
0
140
Ghostty + Neovimで作る 透明でカッコ良い開発環境
j341nono
0
130
AgentCore CLI で進化した AWS での AI エージェントの作り方 : 必要な機能を必要な時に
icoxfog417
PRO
3
380
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
2
300
setup-vp GitLab対応の裏側
naokihaba
0
120
Jetpack Compose メカニズム
skydoves
0
480
Featured
See All Featured
Ethics towards AI in product and experience design
skipperchong
2
380
The SEO identity crisis: Don't let AI make you average
varn
0
560
Paper Plane
katiecoart
PRO
4
53k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Code Review Best Practice
trishagee
74
20k
Design in an AI World
tapps
1
330
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
330
Marketing to machines
jonoalderson
1
5.8k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
430
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
3
1.3k
Transcript
Swagger (OpenAPI) と PHPStan で REST API でも型安全っぽく使う
⾃⼰紹介 ID: @kalibora 所属: 株式会社オトバンク オーディオブック(⽿で聴く本)を作ってる会社 サーバーサイド(API, batch, web ...
)の開発をしております 今⽇話すのは API の開発をこういう感じでやってるけど、なかなかいいよ という共有の話です
おさらい : Swagger とは RESTful Web サービスの設計、構築、⽂書化、使⽤に役⽴つツール で、⼤規模なエコシステムによってバックアップされたオープンソー スのソフトウェアフレームワーク。 で、その中にある
Swagger Specification (Swagger 仕様)に関して は、 OpenAPI Specification に改名された。
実際に OpenAPI で仕様書書いてる⼈どれくら いいますか? swagger: "2.0" info: description: " こういうやつですね"
version: "1.0.0" 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 で仕様書くの、ダルくな いですか?
OpenAPI 仕様を書くための⼿段 Swagger Editor ブラウザベースでOpenAPI を記述できるエディター https://editor.swagger.io/ 普通はこれを使う(のかな) swagger-php アノテーションベースでOpenAPI
仕様を記述できる https://github.com/zircote/swagger-php ダルさを解消するために、これを拡張して使っております
swagger-php (v2) の使⽤例 例えば User というクラスに name プロパティがあって、 これをAPI のレスポンスのI/F
として定義したい場合、 /** * @SWG\Definition() */ class User { /** * ユーザー名 * @var string * @SWG\Property() */ public $name; }
出⼒結果 ↑ 先程のアノテーションを解析して、下記のようなOpenAPI 仕様が書 き出されます。 swagger: '2.0' definitions: User: properties:
name: description: ユーザー名 type: string
でもまだダルい
まだダルポイントその 1 getter などのアクセサに対応していない 実際の業務で使っているエンティティクラスは private なプロパ ティ + getter
の構成が多いのでこのままだとうまく使えない
対策 1: swagger-php を拡張して getter に対応した /** * @SWG\Definition() */
class User { private $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
でもやっぱりまだダルい
まだダルポイントその 2 そもそもだけど、アノテーション書いたらそのままAPI の挙動にも 反映したい アノテーションから 仕様(OpenAPI 仕様の出⼒結果) 実装(実際にAPI のレスポンスとして返す値)
この両⽅に影響を与えたい
対策 2: オブジェクトから配列に変換する処理を書いた /** * @SWG\Definition() */ class User {
private $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 とは PHP の静的解析ツールの1 つ コードを実⾏せずとも分かる、いろんなエラーを検出してくれる そのためには型が重要になる
例えばこんなのは class Main { public function execute() : void {
echo Util::getTomorrow(new \DateTimeImmutable('1980-01-01')) ->format('Y/m/d'), PHP_EOL; } } class Util { public static function getTomorrow(\DateTimeImmutable $today) : ?\DateTimeImmutable { // ノストラダムスの⼤予⾔によって世界が滅びるので明⽇は無い $endDayOfTheWorld = new \DateTimeImmutable('1999-07-31'); return ($today >= $endDayOfTheWorld) ? null : $today->modify('+1 day'); } }
こうなる $ ./vendor/bin/phpstan analyse src --level=max 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100% ------
-------------------------------------------------------- Line Main.php ------ -------------------------------------------------------- 4 Cannot call method format() on DateTimeImmutable|null. ------ -------------------------------------------------------- [ERROR] Found 1 error getTommorow() の戻り値が nullable なのに null チェックをしないで format() メソッドを使⽤しているため。
と⾔うわけで、例えばこんなケース
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
API を呼び出すクライアント側 OpenAPI 仕様から⾃動的にAPI クライアントを作るツールは⾊々あ るので好きなものを使えば良いと思う 今回 OpenAPI 仕様で required
などを⽤い nullable か否かをきちん と出⼒するようにしたので、それを加味して⽣成するツールであれ ば良い 加味しない場合でもクラス⽣成のテンプレートをカスタマイズでき たりするので、それを使うことも可能 弊社では swagger-api/swagger-codegen + テンプレートをカスタマ イズして nullable かどうかも加味するようにしている
⾃動⽣成されたクラス class Campaign implements ModelInterface, ArrayAccess { /** * Gets
started_at * @return \DateTime */ public function getStartedAt() { /* 実装は省略 */ } /** * Gets ended_at * @return \DateTime|null */ public function getEndedAt() { /* 実装は省略 */ } }
PHPStan でのチェック クライアント側で⾃動⽣成されたクラスでも ちゃんと getEndedAt() が nullable になっているので、 API を呼び出す側の実装者が、うっかりキャンペーンの終了⽇時が
nullable である。 という仕様を知らなかった(読み取り損ねた)としても、⾃動的にエ ラーを検出可能。
まとめ
まとめ(ツール) API 側 zircote/swagger-php getter にも適⽤する拡張(type hint で nullable も加味)
アノテーションからオブジェクトを連想配列にする実装(仕様 と実装をあわせる) クライアント側 swagger-api/swagger-codegen テンプレートをちょっとカスタマイズ(nullable も加味) phpstan/phpstan
まとめ(図)