Page 1
解体新書
Composer
Anatomy of Composer, in Runtime
PHPカンファレンス北海道出張版(仮)
#phpcondo2017
PHPオフ会
公開日:
by USAMI Kenta @tadsan
に札幌市中央区の株式会社インフィニットループで開催された『PHPカンファレンス北海道出張版(仮) PHPオフ会』でレギュラーセッション(30分)として発表しました。
解体新書
Composer
Anatomy of Composer, in Runtime
PHPカンファレンス北海道出張版(仮)
#phpcondo2017
PHPオフ会
お前誰よ
うさみけんた (@tadsan) / Zonu.EXE
Composer
使ってますか?
Dependency Manager for PHP
使ってない
A.
FW経由で
B.
直接使ってる
C.
分かれそう
本日しない話
PHPのクラスのオート
ローディングの説明
プロジェクトでの
Composerの運用方法
過去の発表を
読んでください
https://niconare.nicovideo.jp/watch/kn1371
https://qiita.com/tadsan/items/86099d44d12f1103b0a0
https://qiita.com/tadsan/items/a78c9160418200e2d47a
Composerで
できること
ざっくり
プロジェクトが
依存するパッケージを ダウンロードしてくる
プロジェクトが
依存するパッケージを
利用可能にする
今日は実行時の
話をします
基本はComposerの
autoload.php
を読み込
めば準備完了する
復習
PHPスクリプト の読み込みかた
require, require_once include, include_once
場合によるけど
基本だいたい同じ
どこから?
ファイルシステムの絶対パス
A.
include_path
からの相対パス
B.
プロトコル (ラッパー)
C.
ファイルシステムの絶対パス
require '/path/to/file.php';
ファイルシステム内のフルパス
require __DIR__.'/../Klass.php';
__DIR__
マジック定数 で
スクリプトのディレクトリが入る
include_path
からの相対パス
include_path
にディレクトリを追加する
"."
だいたいデフォルトに が入ってる
require 'Hoge.php';
require 'Fuga/Piyo.php';
ディレクトリ内も探索される
プロトコル(ラッパー)
fopen('php://stderr', 'r')
とか
file_get_contents('http://example.com/')
とかやるための仕組み
……が、セキュリティの懸念もあるの
php.ini
で、デフォルトでは で
allow_url_include = Off
になってる
改めて
Composer
オートロード 機能ついてる
どうやって
読み込んでるの?
autoload.phpを
読もう
みなさまの手元のComposer
プロジェクトを
見比べながらお聞きください
% composer --version
Composer version 1.5.2 2017-09-11 16:59:25
% composer install --no-dev --prefer-dist
vendor/autoload.php
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return
ComposerAutoloaderInitbed8ae7d525347237f9cdad08c8930b3::getLoader();
vendor/autoload.php
1.autoload_real.php
を読み込む
2.謎のクラスの静的メソッドを実行
たぶん変なバイトキャッシュを
残さないためにcontent-hashが付いた
クラス名
autoload_real.php
1.$loader
が生成済みなら返すだけ
2.クラスローダーを読み込むだけのクラス
ローダーを登録して、使ったらすぐに
spl_autoload_unregister する
なんか環境依存の
autoload_real.php
if (null !== self::$loader) {
return self::$loader;
}
// Composer\Autoload クラスを読み込むだけのローダー
spl_autoload_register(array('ComposerAutoloaderInitbedRy', 'loadClassLoader'),
true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitbedRy', 'loadClassLoader'));
autoload_real.php
1.
PHPの実行時環境によってクラス定義の
読み込みを変更
2.
autoload_static.php VS 三兄弟
PHP5.6以上で、HHVMじゃなくて、
autoload_real.php
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') &&
(!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitbedRy::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
autoload_static.php
プロジェクトの依存パッケージと、 composer installのオプションに依存
デフォルトではComposer.jsonの 定義通りのローディングを生成
--optimize-autoloader オプションで
クラスマップを強制で生成する
autoload_static.php (PSR-4のみ)
<?php // autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInitbed8ae7d525347237f9cdad08c8930b3 {
public static $prefixLengthsPsr4 = array (
'T' => array (
'Teto\\' => 5,
),
);
public static $prefixDirsPsr4 = array (
'Teto\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
);
public static function getInitializer(ClassLoader $loader) {
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitbed8ae7d525347237f9cdad08c8930b3::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitbed8ae7d525347237f9cdad08c8930b3::$prefixDirsPsr4;
}, null, ClassLoader::class);
}
}
autoload_static.php (optimized)
public static $classMap = array (
'Teto\\Object\\Helper' => __DIR__ . '/../..' . '/src/Object/Helper.php',
'Teto\\Object\\MethodAlias' => __DIR__ . '/../..' . '/src/Object/MethodAlias.php', 'Teto\\Object\\ObjectArray' => __DIR__ . '/../..' . '/src/Object/ObjectArray.php',
'Teto\\Object\\PrivateGetter' => __DIR__ . '/../..' . '/src/Object/PrivateGetter.php',
'Teto\\Object\\PrivateStrictGetter' => __DIR__ . '/../..' . '/src/Object/
PrivateStrictGetter.php',
'Teto\\Object\\PropertyLikeMethod' => __DIR__ . '/../..' . '/src/Object/
PropertyLikeMethod.php',
'Teto\\Object\\ReadOnly' => __DIR__ . '/../..' . '/src/Object/ReadOnly.php',
'Teto\\Object\\ToArrayInterface' => __DIR__ . '/../..' . '/src/Object/
ToArrayInterface.php',
'Teto\\Object\\TypeAssert' => __DIR__ . '/../..' . '/src/Object/TypeAssert.php',
'Teto\\Object\\TypeDefinition' => __DIR__ . '/../..' . '/src/Object/
TypeDefinition.php',
'Teto\\Object\\TypedProperty' => __DIR__ . '/../..' . '/src/Object/TypedProperty.php',
);
autoload_static.phpの大事なとこ
Closure::bind()
でスコープ束縛して、
メソッド呼び出しをけちりながら privateメンバーにセットしてる
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitbedRy::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitbedRy::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitbedRy::$classMap;
}, null, ClassLoader::class);
ClassLoader.php
クラスローダーの実装本体
いままで設定された情報をもとにクラス
名の解決・ファイルロードをする
動的に変動する部分は分離されてるので、
バイトコードがキャッシュされても 特に問題なさそうに作ってるっぽい
ClassLoader.php
spl_autoload_register()で呼ばれる
クラスマップにあったらそれを読む
なかったらpsr-4, psr-2を読む
PEARに依存するなどの場合は
include_pathを読む
これによって 得られる知見
Composerは、いろんなPHP のバージョンとか実行環境
で動くように書かれてる
実行時にcomposer.jsonとか composer.lockは使ってなく
て、静的なPHPスクリプト
ファイルが書き出される
composer install
サーバーで
の必要はなく、手元でインス トールしてvendorディレクト
リごと転送してやればいい
よくある誤解
「レンタルサーバーの最安プ
ランでPHP動かしてるから
Composer入らないんだ…」
といったことはない
本筋から 外れた話
Autoloader
Optimization
https://getcomposer.org/doc/articles/autoloader-optimization.md
Level 1
本番運用時は
が基本
--optimize-autoloader
……そのほかは
効果あるのかな? (私は使ってないです)
予告
PHP AdventCalendar 2017では
Composerとか全然考慮されてない (zipで配布されてる)SDKをむりやり Composerで管理する話を書きます