Upgrade to Pro — share decks privately, control downloads, hide ads and more …

色んなオートローダーを覗き見る #phpcon_okinawa

色んなオートローダーを覗き見る #phpcon_okinawa

PHPカンファレンス沖縄2024の発表資料です
https://fortee.jp/phpcon-okinawa-2024/proposal/92abb418-8735-41d9-bbf8-ffc3109d018b

hideki kinjyo

September 28, 2024
Tweet

More Decks by hideki kinjyo

Other Decks in Programming

Transcript

  1. 自己紹介 • 金城秀樹 / きんじょうひでき • GitHub: @o0h / 𝕏

    : @o0h_ • 好きなFWはCakePHP • アイコンは美味しい鮭親子丼の写真です • 沖縄は23年ぶり16回目くらいです多分! 
  2. spl_autoload_register 典型的な実装: 1. 与えられたクラス名に基 づいて、対応するファイ ル名(パス)を割り出す 2. 割り出したパスに対して requireを実行 3.

    ファイルの新規読み込み により、新しいクラス定 義を解決する  ちゃんと動くコードの例。 ʆ./classes/Command.php` が読み込まれる
  3. Composerがオートロードを提供するまでの流れ  $ tree -L 3 app app └─ vendor

    ├─ autoload.php └─ composer ├── ClassLoader.php ├── autoload_real.php └── autoload_static.php 関係するファイルたち
  4. 各ファイルの役割  (app/index.php) vendor/autoload.php vendor/composer/ autoload_real.php vendor/composer/ autoload_static.php vendor/composer/ ClassLoader.php

    \ComposerAutoloaderInitXXX +getLoader() \Composer\Autoload \ComposerStaticInitXXX +getInitializer() \Composer\Autoload \ClassLoader +register() +loadClass()
  5. 取り上げる3つのオートロード • Composerが提供する 主なオートロードの仕組み • Class Map • PSR-0 •

    PSR-4 • いずれも、 `loadClass()`内で `findFile()`を用いて 解決する 
  6. 取り上げる3つのオートロード  事前知識として、 クラス名(FQCN))とファイルパスの対応を 辞書として保持して、解決に利用する • Composerが提供する 主なオートロードの仕組み • Class

    Map • PSR-0 • PSR-4 • いずれも、 `loadClass()`内で `findFile()`を用いて 解決する 事前知識として、 ベースとなる名前空間とディレクトリパスの対応を 辞書として保持して、解決に利用する サブディレクトリの位置やファイル名は 命名規則から(=ルールベースで、動的に)割り出す ComposerStaticInitで 「事前知識」の部分を扱っている
  7. Class Map  // in \Composer\Autoload\ ComposerStaticInitXXXX public static $classMap

    = [ 'Composer\\InstalledVersions' => __DIR__ . '/../composer/InstalledVersions.php', 'Mobile_Detect' => __DIR__ . '/../mobiledetect/mobiledetectlib/Mobile_Detect.php', ]; // in \Composer\Autoload\ClassLoader public function findFile($class) { if (isset($this->classMap[$class])) { return $this->classMap[$class]; } クラス名(FQCN)をキーにして、 自クラスのstatic memberから 該当する{fqcn: path}を探索しパスを解決する
  8. PSR-0, PSR-4 • PSR-0/4: 名前空間・クラスと、それに対応させるファイルパスの規則 • Class Mapが「クラスとパス」の対応を利用したのに対し、 「ルートとなる名前空間(名前空間プリフィックス)」と 「対応するディレクトリパス(ベースディレクトリ)」情報を利用する

     // in \Composer\Autoload\ClassLoader public function findFile($class) { // தུ $file = $this->findFileWithExtension($class,'.php'); return $file; 命名規則から動的にFQCN->パスを解決する 実装は`findFileWithExtension()`
  9. PSR-0, PSR-4: prefix map  public static $prefixDirsPsr4 = [

    'React\\Promise\\' => [0 => __DIR__ . '/..' . '/react/promise/src'], 'Psr\\Log\\' => [0 => __DIR__ . '/..' . '/psr/log/src'], 'Psr\\Container\\' => [0 => __DIR__ . '/..' . '/psr/container/src'], ]; public static $prefixesPsr0 = [ 'D' => [ 'Diff' => [0 => __DIR__ . '/..' . '/phpspec/php-diff/lib'], ], 'H' => [ 'HTMLPurifier' => [0 => __DIR__ . '/..' . '/ezyang/htmlpurifier/library'], ], ]; ComposerStaticInitXXXXがmapを持っている。ClassLoaderに代入される
  10. PSR-0: FQCN->パスの変換@findFileWithExtension 1. 名前空間(prefix)からディレクトリのパスを取得 2. 名前空間(prefix)より下位の`\`と全ての`_` を ディレクトリの区切り文字に変換してサブディレクトリとファイル名に 3. {1

    + 2 + '.php'}でフルパスを取得  ొ࿥৘ใ($classLoader->prefixesPsr0): 'HTMLPurifier' => [0 => __DIR__ . '/..' . '/ezyang/htmlpurifier/library'] ೖྗ: 'HTMLPurifier_AttrDef_CSS_AlphaValue' ग़ྗ: '/vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrDef/CSS/AlphaValue.php'
  11. PSR-4: FQCN->パスの変換@findFileWithExtension 1. 名前空間(prefix)からディレクトリのパスを取得 2. 名前空間の区切り文字をディレクトリの区切り文字に変換して サブディレクトリとファイル名に 3. {1 +

    2 + '.php'}でフルパスを取得  ొ࿥৘ใ($classLoader->prefixesPsr4): 'React\\Promise\\' => [0 => __DIR__ . '/..' . '/react/promise/src'], ೖྗ: '\React\Promise\Exception\CompositeException' ग़ྗ: '/vendor/react/promise/src/Exception/CompositeException.php'
  12. セクションのまとめ • Composerのオートロード、Class Map / PSR-4 / PSR-0に対応している • PSR-4

    / PSR-0は、名付けのルールは異なるけれども 「ルートの名前空間を定義する」 「サブ名前空間やディレクトリの区切り方のルールがある」 「名前空間とベースクラス名からパスを知る」 と、やっていることは同じ • いずれも「パスを見つけて→ファイルをinclude」をするだけ、単純! 
  13. 1. Laravel, CakePHP(3+),Slim etc ─今風のFWたち • PSR-4準拠のオートロードに対応している • オートローダーの実装はComposerに依存 •

    = `composer require``composer create-project`した時点で使える • comopser.jsonのautoload.psr-4フィールドを設定して利用 • index.phpなど、エントリーポイントとなるファイルにおいて `require __DIR__ . '/../vendor/autoload.php'` が記述されるのが一般的 
  14. 2. Yii2 • PSR-4準拠/Class Map形式のオートロードに対応している • ただし、Composer由来のオートローダーだけではなく、 FW自前の機能を付与したオートローダーを提供 • ザックリ言うと、FW独自の規則・設定に則っていれば

    composer.jsonの改変をせずともクラスファイルの読み込みが可能になっている  鍵となる概念: クラスのオートロード | Yii 2.0 決定版ガイド | Yii PHP Framework https://www.yiiframework.com/doc/guide/2.0/ja/concept-autoloading
  15. Zend Framework2: モジュールの例 • 同一プロジェクトの中に、 複数の名前空間を簡単に持てる • PSR-4でいうprefixに該当 • アプリケーションに対して

    「この名前空間が有効である」を 伝える必要が生じる  $ tree -L 2 app app ├── config ├── module │ ├── Album │ │ ├── Module.php │ │ ├── config │ │ ├── src │ │ └── view │ └── Artist └── public `\Album` ネームスペース `\Artist` ネームスペース
  16. 知っておくと良いこと・知らなくても良いこと • Composerのオートローダーは「PSR-4実装の1つ」にすぎず、 必要に応じて使ったり使わなかったりできる • 例えば「モジュール単位で名前空間を細かく分ける」思想であれば、 `composer.json`を都度いじる(=`dumpautload`を要する)のは面倒かも • 自前で実装すれば、別の方法で名前空間の追加ができる •

    「当たり前」で留まらず一歩踏み込むと、細かく工夫もできるかも • 例えば「PSR-4を使っていればOK」から「Class Mapにするか?を考える」など • `composer dumpautoload` の `—optimize`オプションや PHPUnitの`composer.json` によるautoload.classmap指定などの例 
  17. Composerがオートロードを提供するまでの流れ  $ tree -L 2 app app └── vendor

    ├── autoload.php ├── ClassLoader.php ├── autoload_real.php └── autoload_static.php 関係するファイルたち $3より再掲
  18. 各ファイルの役割  (app/index.php) vendor/autoload.php vendor/composer/ autoload_real.php vendor/composer/ autoload_static.php vendor/composer/ ClassLoader.php

    \ComposerAutoloaderInitXXX +getLoader() \Composer\Autoload \ComposerStaticInitXXX +getInitializer() \Composer\Autoload \ClassLoader +register() +loadClass() $3より再掲
  19. 余談: 自動生成されるクラス名について • 記述内容が毎回変わるので、クラス名が動的に変更される • dumpautoload時にsuffixを付与: デフォルトだとcontent-hashの値 • ex)`ComposerStaticInit67574dcf9d425e8bc6c10ce234303952` •

    • ClassLoaderは変更がない(既存ソースのコピーで作成される) • ComposerAutoloaderInitXXXXX \Composer\Autoload\ComposerStaticInitXXXXX \Composer\Autoload\ClassLoader