Slide 1

Slide 1 text

PSR と各ライブラリ実装から DI コンテナの要件を整理する PHPカンファレンス名古屋2025 1 ふわせぐ @fuwasegu

Slide 2

Slide 2 text

2 PHPカンファレンス名古屋2025 • 竹下 拓秀 / ふわせぐ (@fuwasegu) • 株式会社ゆめみ – コーポレートエンジニア – PHP テックリード • PHP / Laravel がメイン – フロントなら Svelte(TS) が好き • 長崎県出身 / 愛知県日進市在住 • (5歳・0歳)の父(26歳) 自己紹介 メ タ 原 則

Slide 3

Slide 3 text

3 今日は DI Container のお話 PHPカンファレンス名古屋2025

Slide 4

Slide 4 text

4 DI コンテナ,作ったことありますか? PHPカンファレンス名古屋2025

Slide 5

Slide 5 text

5 人生で一度はその時が来る PHPカンファレンス名古屋2025

Slide 6

Slide 6 text

• フレームワークがよしなにやってくれてる魔法の 仕組みを理解できる – インスタンスの管理 – オブジェクトのライフサイクル(スコープ) – 依存解決 • 言語仕様の理解が深まる – リフレクション – 動的オブジェクト生成 6 DI コンテナを自作するモチベ PHPカンファレンス名古屋2025

Slide 7

Slide 7 text

• フレームワーク搭載の DI コンテナは高機能 • 作りたい機能を考えだしたらきりがない 最低限ゴールを決めたい 7 とは言え,なにから始めれば... PHPカンファレンス名古屋2025 DI コンテナの機能を整理して 要件を定めよう!

Slide 8

Slide 8 text

• DI コンテナで実現したいことを考える – 要件定義の2W1H(Why・What・How) • 既存ライブラリをいくつか調査する • 機能ごとに優先度を定めて要件を整理する 8 今日やること PHPカンファレンス名古屋2025

Slide 9

Slide 9 text

Why: なぜ DI コンテナが必要なのか 9 DI コンテナで実現したいことを考える PHPカンファレンス名古屋2025

Slide 10

Slide 10 text

10 密結合なコードが引き起こす問題 PHPカンファレンス名古屋2025

Slide 11

Slide 11 text

11 密結合なコードが引き起こす問題 PHPカンファレンス名古屋2025 • SmptMailer に直接依存している –メールの送信方法を外から変更できない –メール以外が使えない • 単体テスト時,メールが送信されてしまう –モックに差し替えが難しい

Slide 12

Slide 12 text

12 DI による改善 PHPカンファレンス名古屋2025

Slide 13

Slide 13 text

13 依存性注入(DI)による改善 PHPカンファレンス名古屋2025 • MailerInterface に抽象化 – Smtp 以外のメールにも対応可能 • 依存をコンストラクタから設定 – オブジェクトの生成と利用が分離された – NotificationService のインスタンスを作るときに Mailer の実装を決定できるように – もちろんモックの注入も可能

Slide 14

Slide 14 text

14 DI 最強!DI しか勝たん! PHPカンファレンス名古屋2025 コンストラクタ DI しまくるぞ〜!

Slide 15

Slide 15 text

全部 new するの辛スギィ! PHPカンファレンス名古屋2025 15

Slide 16

Slide 16 text

• とにかく new が多い! – どこかでインスタンスの生成は必要とは言え... – ネストが深ければ深いほど,依存クラスが多けれ ば多いほど,大量に new が必要 • 変更に弱い – コンストラクタのシグネチャー変更に弱い – new している箇所すべての変更が必要 16 手動 DI の限界 PHPカンファレンス名古屋2025

Slide 17

Slide 17 text

17 手動 DI の限界 PHPカンファレンス名古屋2025

Slide 18

Slide 18 text

What: DI コンテナは何をするのか How: どうやって実現するのか 18 DI コンテナで実現したいことを考える PHPカンファレンス名古屋2025

Slide 19

Slide 19 text

What: DI コンテナは何をするのか How: どうやって実現するのか 19 DI コンテナで実現したいことを考える PHPカンファレンス名古屋2025

Slide 20

Slide 20 text

• php-fig/container – PSR-11 で定義される DI コンテナのインタフェース • illuminate/container – Laravel で利用されている DI コンテナ • symfony/dependency-injection – Symfony FW のコンポーネントとして提供される DI コンテナ • PHP-DI/PHP-DI – スタンドアロンでも利用しやすい DI ライブラリ 20 調査対象の DI ライブラリ PHPカンファレンス名古屋2025

Slide 21

Slide 21 text

• php-fig/container – PSR-11 で定義される DI コンテナのインタフェース 21 調査対象の DI ライブラリ PHPカンファレンス名古屋2025 とりあえず PSR を確認してスタートラインを決める

Slide 22

Slide 22 text

22 php-fig/container PHPカンファレンス名古屋2025 便宜上コメントは削除しています 要求する実装はこれだけ!

Slide 23

Slide 23 text

• get(string $id): mixed メソッド – 識別子に対応するオブジェクトを取得する – 識別子がなければ例外をスローする – 取得は冪等であるべきだが,かならずそうとは限らな い(都度生成されるかもしれない) • has(string $id): bool メソッド – 識別子があるか確認 – false な場合,get() は必ず例外をスローする 23 php-fig/container PHPカンファレンス名古屋2025

Slide 24

Slide 24 text

• コンテナからの「取得」を標準化 – 「設定」方法は標準化しない(多様性を尊重) • 設定方法はコンテナごとに大きく異なるため • フレームワーク・ライブラリが任意の PSR-11 準拠コンテナを利用可能に – 相互運用性の向上 – ユーザーにコンテナ選択の自由 24 php-fig/container PHPカンファレンス名古屋2025

Slide 25

Slide 25 text

• 識別子を指定してオブジェクトを取得できる • 識別子を指定してオブジェクトの有無を確認できる 25 要件メモ PHPカンファレンス名古屋2025

Slide 26

Slide 26 text

• 識別子を指定してオブジェクトを取得できる • 識別子を指定してオブジェクトの有無を確認できる 26 要件メモ PHPカンファレンス名古屋2025 ちなみに... これから扱う残り3つのライブラリは,どれも PSR-11 に準拠しています. PSR-11 はほぼ必須要件と言っても過言ではないですね!

Slide 27

Slide 27 text

27 最小要件から段階的に拡張 PHPカンファレンス名古屋2025 オブジェクトが取得できる ?

Slide 28

Slide 28 text

28 最小要件から段階的に拡張 PHPカンファレンス名古屋2025 オブジェクトが取得できる オブジェクトが登録できる

Slide 29

Slide 29 text

• 登録できないと取得できないよねという発想 • “登録する”をもう一段階分解すると – インスタンスを登録する – クラス名を登録する • 取得されるとき/登録されるときに new する(登録+生成) • ただし依存が絡むと難しいのでまずはシンプルなもののみ 29 オブジェクトが登録できる PHPカンファレンス名古屋2025

Slide 30

Slide 30 text

30 各ライブラリでは? PHPカンファレンス名古屋2025 インスタンスの登録 クラス名での登録 illuminate/container ◯ ◯ symfony/dependency-injection ◯ ◯ PHP-DI/PHP-DI ◯ ◯

Slide 31

Slide 31 text

illuminate/container PHPカンファレンス名古屋2025 31

Slide 32

Slide 32 text

symfony/dependency-injection PHPカンファレンス名古屋2025 32

Slide 33

Slide 33 text

PHP-DI/PHP-DI PHPカンファレンス名古屋2025 33

Slide 34

Slide 34 text

オブジェクトの登録まとめ • どのライブラリでも対応 – インスタンスの登録 – クラス名での登録 • ただし,コンストラクタ引数はまだ考慮していない • getter / setter のような関係で用意されている PHPカンファレンス名古屋2025 34

Slide 35

Slide 35 text

• 識別子に対応するオブジェクトを登録できる – インスタンス / クラス名 のどちらでも登録可能 35 要件メモ PHPカンファレンス名古屋2025

Slide 36

Slide 36 text

36 最小要件から段階的に拡張 PHPカンファレンス名古屋2025 オブジェクトが取得できる オブジェクトが登録できる ?

Slide 37

Slide 37 text

37 最小要件から段階的に拡張 PHPカンファレンス名古屋2025 オブジェクトが取得できる オブジェクトが登録できる 依存解決ができる

Slide 38

Slide 38 text

• コンストラクタに引数がある場合もオブジェクトを生 成したい – 必要な依存オブジェクトを自動的に生成・注入 – 再帰的に解決(依存オブジェクトの依存オブジェクト) 38 依存解決(Autowiring)ができる PHPカンファレンス名古屋2025 ここまでくると • 手動での生成・注入が不要 -> コードが簡潔 • 依存関係の変更に強い 実用性が高くなってきた...!

Slide 39

Slide 39 text

39 各ライブラリでは? PHPカンファレンス名古屋2025 Autowiring illuminate/container ◯ symfony/dependency-injection ◯ PHP-DI/PHP-DI ◯

Slide 40

Slide 40 text

40 サンプル実装の依存関係 PHPカンファレンス名古屋2025 UserService UserRepository Logger Formatter

Slide 41

Slide 41 text

41 illuminate/container PHPカンファレンス名古屋2025

Slide 42

Slide 42 text

42 symfony/dependency-injection PHPカンファレンス名古屋2025

Slide 43

Slide 43 text

43 symfony/dependency-injection(補足) PHPカンファレンス名古屋2025 • クラスを一括スキャンして自動登録する方法もある

Slide 44

Slide 44 text

44 PHP-DI/PHP-DI PHPカンファレンス名古屋2025

Slide 45

Slide 45 text

PHP-DI/PHP-DI(補足) PHPカンファレンス名古屋2025 45 実はこれでもいける

Slide 46

Slide 46 text

依存解決(Autowiring)まとめ • どのライブラリでも Autowiring はできる – コンストラクタの引数の型を解析 – 特に Laravel・PHP-DI では簡単に全自動が可能 • 依存がネストしていも問題なし! じゃぁ Interface やスカラー値では...? PHPカンファレンス名古屋2025 46

Slide 47

Slide 47 text

• Autowiring できる – 再帰的に解析 47 要件メモ PHPカンファレンス名古屋2025

Slide 48

Slide 48 text

48 最小要件から段階的に拡張 PHPカンファレンス名古屋2025 オブジェクトが取得できる オブジェクトが登録できる 依存解決ができる ?

Slide 49

Slide 49 text

49 最小要件から段階的に拡張 PHPカンファレンス名古屋2025 オブジェクトが取得できる オブジェクトが登録できる 依存解決ができる スカラー値が注入できる

Slide 50

Slide 50 text

• オブジェクトは new $class_string() でいけた • スカラー値はそうはいかない • でもスカラー値を注入したいことは多々ある – Config 系とか • Test 時は API の URL を example.com に変えたりと かしたい – しきい値とか 50 スカラー値が注入できる PHPカンファレンス名古屋2025

Slide 51

Slide 51 text

51 各ライブラリでは? PHPカンファレンス名古屋2025 スカラー値の注入 illuminate/container ◯ symfony/dependency-injection ◯ PHP-DI/PHP-DI ◯

Slide 52

Slide 52 text

52 illuminate/container PHPカンファレンス名古屋2025

Slide 53

Slide 53 text

53 symfony/dependency-injection PHPカンファレンス名古屋2025

Slide 54

Slide 54 text

54 PHP-DI/PHP-DI PHPカンファレンス名古屋2025

Slide 55

Slide 55 text

• クラス(オブジェクト)は型の情報から自動解 決できるが,スカラー値はそうはいかない – “値”の明示的な設定が必要 • 各ライブラリでスカラー値の注入は可能 – パラメータ名 => 値 の対応を手動で設定 55 スカラー値の注入まとめ PHPカンファレンス名古屋2025

Slide 56

Slide 56 text

• スカラー値を注入できる 56 要件メモ PHPカンファレンス名古屋2025

Slide 57

Slide 57 text

57 最小要件から段階的に拡張 PHPカンファレンス名古屋2025 オブジェクトが取得できる オブジェクトが登録できる 依存解決ができる スカラー値が注入できる ?

Slide 58

Slide 58 text

58 最小要件から段階的に拡張 PHPカンファレンス名古屋2025 オブジェクトが取得できる オブジェクトが登録できる 依存解決ができる スカラー値が注入できる Interface のバインド

Slide 59

Slide 59 text

• 依存性逆転のために Interface に依存させる ケースは多い • でも Interface は new できない – Autowiring のとき実体クラスが分からないので困 る • どこかで「Interface → 実体クラス」の対応を どこかで決めてあげないといけない 59 Interface のバインド PHPカンファレンス名古屋2025

Slide 60

Slide 60 text

60 各ライブラリでは? PHPカンファレンス名古屋2025 Interface のバインド illuminate/container ◯ symfony/dependency-injection ◯ PHP-DI/PHP-DI ◯

Slide 61

Slide 61 text

61 サンプル実装の依存関係 PHPカンファレンス名古屋2025 UserService UserRepository LoggerInterface Formatter EchoLogger NullLogger

Slide 62

Slide 62 text

62 illuminate/container PHPカンファレンス名古屋2025

Slide 63

Slide 63 text

63 symfony/dependency-injection PHPカンファレンス名古屋2025

Slide 64

Slide 64 text

64 PHP-DI/PHP-DI PHPカンファレンス名古屋2025

Slide 65

Slide 65 text

• Intarface に依存する場合,どの実装を注入 するかコンテナに明示的に登録が必要 • 各ライブラリで Interface の登録は可能 • 方法として,バインドとエイリアスがある – バインド・・・どうやって生成するかを登録 – エイリアス・・・既存のサービスに別名を貼る 65 Interface のバインド まとめ PHPカンファレンス名古屋2025

Slide 66

Slide 66 text

• Interface がバインドできる 66 要件メモ PHPカンファレンス名古屋2025

Slide 67

Slide 67 text

67 結構盛りだくさんになってきたのでストップ! PHPカンファレンス名古屋2025

Slide 68

Slide 68 text

• ライフサイクルの制御 – get() されたら都度新しくインスタンスを作る(Transient) – 一度 new したらそれを使い回す(Singleton) – Laravel はデフォルトが Transient,Symfony はデフォルト が Singleton • Contextual Binding – 一つに Interface に対して実装の選択肢が複数ある場合, コンテキストによってバインドする実装を変えられる 68 他にも DI コンテナの機能は盛りだくさん PHPカンファレンス名古屋2025

Slide 69

Slide 69 text

69 なんちゃって DI コンテナの要件をまとめる PHPカンファレンス名古屋2025

Slide 70

Slide 70 text

• 識別子を指定してオブジェクトを取得できる • 識別子を指定してオブジェクトの有無を確認できる • 識別子に対応するオブジェクトを登録できる – インスタンス / クラス名 のどちらでも登録可能 • Autowiring できる – 再帰的に解析 • スカラー値を注入できる • Interface がバインドできる 70 作りたい機能がこんなに PHPカンファレンス名古屋2025

Slide 71

Slide 71 text

• 識別子を指定してオブジェクトを取得できる • 識別子を指定してオブジェクトの有無を確認できる • 識別子に対応するオブジェクトを登録できる – インスタンス / クラス名 のどちらでも登録可能 • Autowiring できる – 再帰的に解析 • スカラー値を注入できる • Interface がバインドできる 71 作りたい機能がこんなに PHPカンファレンス名古屋2025 実装難易度に結構差があるので 松・竹・梅 でレベル分けしてみる

Slide 72

Slide 72 text

• 任意のインスタンスを登録できる – 識別子を key にインスタンスの map を内部にもつ • 任意のクラス(依存無し)を登録できる – new $class_string() するだけ • PSR-11 に準拠したオブジェクトの取得と確認 – 識別子を key に内部の map から取り出す 72 要件: 松 PHPカンファレンス名古屋2025

Slide 73

Slide 73 text

• Interface をバインドできる – クラス名での登録とほぼ同じ • スカラー値を注入できる – バインドの設定の拡張 • ライフサイクル管理(Optional) – Singleton / Transient を選べるように 73 要件: 竹 PHPカンファレンス名古屋2025

Slide 74

Slide 74 text

• Autowiring できる – アプローチは2通り A) あらかじめ依存関係の設定ファイルを書かせる B) Reflection で再帰的にコンストラクタを解析 • Contextual Binding(Optional) – いまなんのクラスを生成中かを把握する仕組み – コンテキスト -> 依存 -> 値 のマッピング – 自作するにはちょっとマニアックなので Optional で 74 要件: 梅 PHPカンファレンス名古屋2025

Slide 75

Slide 75 text

75 DI コンテナを作って新しい自分と出会おう PHPカンファレンス名古屋2025

Slide 76

Slide 76 text

• php-fig/container – Copyright (c) 2013-2016 container-interop, 2016 PHP Framework Interoperability Group – License: MIT License (https://github.com/php-fig/container/blob/master/LICENSE) • illuminate/container – Copyright (c) Taylor Otwell – License: MIT License (https://github.com/illuminate/container/blob/master/LICENSE.md) • symfony/dependency-injection – Copyright (c) 2004-present Fabien Potencier – License: MIT License (https://github.com/symfony/dependency-injection/blob/7.2/LICENSE) • PHP-DI/PHP-DI – Copyright (c) Matthieu Napoli – License: MIT License (https://github.com/PHP-DI/PHP-DI/blob/master/LICENSE) 76 Copyright and License PHPカンファレンス名古屋2025