Slide 1

Slide 1 text

モダン PHP テクニック 12 選 ―Psalm とPHP 8.1 で今はこんなこともできる!― 五十嵐 進士 (sji)

Slide 2

Slide 2 text

自己紹介 sji / sj-i / @sji_ch SNS 上でのアイコンは GitHub が自 動生成した奴

Slide 3

Slide 3 text

生まれも育ちも仙台

Slide 4

Slide 4 text

PHP カンファレンス仙台とかやった

Slide 5

Slide 5 text

ふつうのサラリーマン 株式会社インフィニットループ仙台 支社所属 スマホゲーのサーバサイドプログラ マ

Slide 6

Slide 6 text

その他 一昨年娘ができた かわいい WEB+DB PRESS の現 PHP 連載担当 12/24 (金)発売の126 号は PHP 8.1 の紹介

Slide 7

Slide 7 text

Agenda PHP の今について モダンな PHP のテクニック 12 個くらい紹介

Slide 8

Slide 8 text

PHP の今

Slide 9

Slide 9 text

PHP 8.1 が出た 型の表現力が強化され、扱いも更に厳格化 readonly never 交差型 Enum 、などなど Fiber で非同期処理が更に便利に 汎用言語化が更に進む

Slide 10

Slide 10 text

「ゆるふわ PHP が汎用言語ですって?」

Slide 11

Slide 11 text

お前らPHP の型がゆるふわゆるふわ言うけど な、PHP なら型を宣言した値が勝手にnull に なったりしねーし 一部の言語、ここで血反吐をはき倒れる

Slide 12

Slide 12 text

ていうか、今の PHP はもうそんなにゆるふわ でもないんです!

Slide 13

Slide 13 text

大静的型解析時代 型情報を使う静的解析ツールが隆盛 PHPStan Psalm Phan 静的解析ツールは言語の弱点を補うものとなりがり PHP はゆるふわな型が弱点 ガチガチな静的解析ツールで補う

Slide 14

Slide 14 text

JetBrains の調査いわく 「どんな品質ツールを使っていますか」 PHPStan Psalm Phan 計 2019 9% 1% 5% 15% 2020 11% 3% 2% 16% 2021 18% 9% 2% 29% 「静的解析ツールを使ってますか」 はい いいえ なにそれ 2021 33% 38% 28% https://www.jetbrains.com/lp/devecosystem-2019/php/ https://www.jetbrains.com/lp/devecosystem-2020/php/ https://www.jetbrains.com/lp/devecosystem-2021/php/

Slide 15

Slide 15 text

静的型解析の何が嬉しい? 実行しなくてもバグに気づける PHP の言語自体を超えた型の表現力 エディタの補完やナビゲーションの強化

Slide 16

Slide 16 text

Psalm Vimeo で使われている静的解析ツール 先日原作者は卒業した、が、今も活発に開発 比較的型の表現力が高い

Slide 17

Slide 17 text

ここから 12 のワザを大紹介

Slide 18

Slide 18 text

1. クラスをボコボコ生やす PHP でデータや処理に型をつける 最強の手段 言語機能やツールの恩恵を最大限に 得られる Constructor Property Promotion 最高 ちょっとした処理や値の表現でもク ラスを定義 class SomeThing { public function __construct( public int $id, public string $name, public Dependency $obj, ) { } }

Slide 19

Slide 19 text

2. DI コンテナで autowiring 多くの DI コンテナで利用可能 Laravel の Container や PHP-DI など コンストラクタへ依存クラスを並べ ていくだけ 自分で new するのは VO とかテスト とかごく一部に Laravel でも Facade より DI // DI コンテナが勝手に依存先を生成 public function __construct( public readonly Dependency1 $obj1 public readonly Dependency2 $obj2 ) { }

Slide 20

Slide 20 text

3. 型は早めにつける @var より @param どんどん呼び出し元へ追いやる 結果コントローラなどリクエス トの入口のほうに 誤りは早く検知できるほどよい リリースの後より前 テスト環境より CI CI より手元のエディタ リクエスト処理の後半より前半 // こっちより public function f(array $items) { /** @var Item $item */ foreach ($items as $item) { } } // こっち /** @param Item[] $items */ public function f(array $items) { foreach ($items as $item) { } }

Slide 21

Slide 21 text

4. 静的解析可能な入力値検査 一部ライブラリで静的型検査フレ ンドリーに入力値の検査やキャス トが可能 webmozart/assert azjezz/psl cuyz/valinor OpenAPI や JSON scheme など IDL からのクラス自動生成も有効 // webmozart/assert の例 Assert::integer($id); // $id はこの先 int 扱い // azjezz/psl の例 $spec = Type\shape([ 'id' => Type\int(), 'name' => Type\string(), 'age' => Type\optional( Type\int() ) ]); // $input をバリデーションしつつキャスト $input = $spec->coerce($_POST['input'] // $input はこの先で ↓ の扱い // array{id: int, name: string, age?:

Slide 22

Slide 22 text

5. 静的解析可能な config 連想配列のかわりに ValueObject を 使う DI コンテナに登録して取り出すの もよい 型の保護や補完が効く 名前付き引数を利用 順番の意識が不要に 設定項目の追加や削除なども静 的に検知可能 return new DatabaseConfiguration( driver: new MySQLDriverConfiguration host: 'localhost', port: 3306, db_name: 'test_db', username: 'test', password: 'mogera', ), )

Slide 23

Slide 23 text

6. 静的解析可能な Collection を使う 最近 illuminate/collections がジェネ リクスに対応 doctrine/collections も以前からジェ ネリクスに対応 PhpStorm もそれらのジェネリクス にある程度対応 あかん場合は @var と @psalm- ignore-var を併用 /** @param Collection $users */ public function f(Collection $users) foreach ($users as $user) { // $user は User として型推論 } }

Slide 24

Slide 24 text

7. 静的解析可能な Orm を使う doctrine/orm cycle/orm も 2 系以降でいくらか eloquent はlarave-plugin やアノテー ション生成が必要 Orm を自作してでも静的解析に対 応させる cuyz/valinor など既存 hydrator の利用も可 // $user は User|null と推論される $user = $entityManager ->getRepository(User::class) ->find(1);

Slide 25

Slide 25 text

8. 静的解析可能な配列を使う psalm の型アノテーションでは配 列のキーや値の型が指定可能 「連番の数値添字が0 から順に並ん だ配列」(list )も指定可能 non-empty-array や non-empty-list もある Shapes で構造体的な配列やタプル も宣言できる /** @param array $p */ /** @param array $p */ /** @param list $p */ /** @param non-empty-array $p /** * @param array{ * id: int, * name: string * } $p */ /** @param array{int, string} */

Slide 26

Slide 26 text

9. 静的解析可能な分岐網羅を使う PHP 8.0 で入った match は分岐網羅 検査がある 想定してない値が来ると例外 Psalm は default のない match や switch で静的に分岐網羅を検査でき る場合がある 値の Union や Enum をうまく使う 無闇に可変関数呼び出しなどの動的 処理は使わない、愚直に分岐を書く enum Result { case Succeed; case Failed; } // ResultStatus::Failed を網羅してないの // 静的解析段階でエラーになる function f(Result $status): int { return match ($status) { ResultStatus::Succeed => 1, }; }

Slide 27

Slide 27 text

10. 書き込み、状態を減らす PHP 8.1 で readonly が追加 Psalm でも @immutable や @pure などがある なるべく完全コンストラクタ+不変 状態を持たない = 各生成時点で全情 報が必要 小さなクラスが増え神クラス化 も防ぎやすくなる class ReadOnlyClass { public function __construct( public readonly int $id, public readonly string $name, ) { } } /** @psalm-immutable */ class ImmutableClass { public function __construct( public int $id, public string $name, ) { } }

Slide 28

Slide 28 text

11. なるべく多くを型で表現 ValueObject 、DTO みたいなのをバ ンバン作る ID の種類ごとに異なるクラスを定 義する ジェネリクスを使ってもよい 実クラスを定義しなくとも型 タグを使える ちかぢか同僚が会社のブロ グで紹介するかも trait ItemId { public function __construct( public readonly int $value, ) { } } class ConsumableItemId {use ItemId;} class EquipmentItemId {use ItemId;} class Consumableuser { public function useItem( ConsumableItemId $item_id ): void { } }

Slide 29

Slide 29 text

12. Package-By-Feature (に寄せる) UserController UserModel UserView ItemController ItemModel ItemView CurrencyController CurrencyModel CurrencyView User Item Currency Package By Feature UserModel ItemModel CurrencyModel UserController ItemController CurrencyController UserView ItemView CurrencyView Model Controller View Package By Layer PBL (Package-By-Layer) へ寄せすぎない 〜Controller だけのディレクトリ 〜Repository だけのディレクトリ 何が嬉しいのか @psalm-internal がうまく機能するよう書く

Slide 30

Slide 30 text

13. みんな(会社を動かして) PHP Foundation に寄付をするんだ

Slide 31

Slide 31 text

おしまい