Composer解体新書 (実行時篇)

公開日:

札幌市中央区株式会社インフィニットループで開催された『PHPカンファレンス北海道出張版(仮) PHPオフ会』でレギュラーセッション(30分)として発表しました。

Download PDF

スライドテキスト

Page 1

解体新書

Composer
Anatomy of Composer, in Runtime

PHPカンファレンス北海道出張版(仮)

#phpcondo2017

PHPオフ会

Page 2

お前誰よ
うさみけんた (@tadsan) / Zonu.EXE

  • GitHub/Packagistでは id: zonuexe
    • 砂川出身、北海道工業大学
  • 2012年11月にピクシブ株式会社に入社
  • Emacs Lisper, PHPer
  • 入社前は自宅警備をしながらRuby書いてた
    • Qiitaに記事を書いたり変なコメントしてるよ
  • 2017年、PHPカンファレンス全部でしゃべった

Page 3

Page 4

Composer
使ってますか?

Page 5

Dependency Manager for PHP

Page 6

使ってない

A.

FW経由で

B.
直接使ってる

C.

Page 7

分かれそう

Page 8

本日しない話

Page 9

PHPのクラスのオート
ローディングの説明

Page 10

プロジェクトでの
Composerの運用方法

Page 11

過去の発表を
読んでください

Page 12

https://niconare.nicovideo.jp/watch/kn1371

Page 13

Page 14

https://qiita.com/tadsan/items/86099d44d12f1103b0a0

Page 15

https://qiita.com/tadsan/items/a78c9160418200e2d47a

Page 16

Composerで
できること

Page 17

ざっくり

Page 18

プロジェクトが

依存するパッケージを ダウンロードしてくる

Page 19

プロジェクトが

依存するパッケージを
利用可能にする

Page 20

今日は実行時の
話をします

Page 21

基本はComposerの

autoload.php

を読み込

めば準備完了する

Page 22

Page 23

復習

Page 24

PHPスクリプト の読み込みかた

Page 25

require, require_once include, include_once

Page 26

場合によるけど
基本だいたい同じ

Page 27

どこから?

Page 28

ファイルシステムの絶対パス

A.

include_path

からの相対パス

B.
プロトコル (ラッパー)

C.

Page 29

ファイルシステムの絶対パス

require '/path/to/file.php';

ファイルシステム内のフルパス

require __DIR__.'/../Klass.php';

__DIR__

マジック定数 で
スクリプトのディレクトリが入る

Page 30

include_path

からの相対パス

include_path

にディレクトリを追加する

"."

だいたいデフォルトに が入ってる

require 'Hoge.php';

require 'Fuga/Piyo.php';

ディレクトリ内も探索される

Page 31

プロトコル(ラッパー)

fopen('php://stderr', 'r')

とか

file_get_contents('http://example.com/')

とかやるための仕組み

……が、セキュリティの懸念もあるの

php.ini

で、デフォルトでは で

allow_url_include = Off

になってる

Page 32

改めて

Composer

Page 33

オートロード 機能ついてる

Page 34

Page 35

どうやって
読み込んでるの?

Page 36

autoload.phpを
読もう

Page 37

みなさまの手元のComposer

プロジェクトを
見比べながらお聞きください

Page 38

% composer --version
Composer version 1.5.2 2017-09-11 16:59:25

% composer install --no-dev --prefer-dist

Page 39

vendor/autoload.php

<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return
ComposerAutoloaderInitbed8ae7d525347237f9cdad08c8930b3::getLoader();

Page 40

vendor/autoload.php

1.autoload_real.php

を読み込む

2.謎のクラスの静的メソッドを実行

たぶん変なバイトキャッシュを

残さないためにcontent-hashが付いた
クラス名

Page 41

Page 42

autoload_real.php

1.$loader

が生成済みなら返すだけ

2.クラスローダーを読み込むだけのクラス
ローダーを登録して、使ったらすぐに
spl_autoload_unregister する

なんか環境依存の

  • ワークアラウンドっぽい気がする

Page 43

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'));

Page 44

autoload_real.php

1.
PHPの実行時環境によってクラス定義の
読み込みを変更

2.
autoload_static.php VS 三兄弟

PHP5.6以上で、HHVMじゃなくて、

  • zend_loader_file_encoded()が無効なら
    autoload_staticが利用される

Page 45

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);
}
}

Page 46

autoload_static.php

プロジェクトの依存パッケージと、 composer installのオプションに依存

デフォルトではComposer.jsonの 定義通りのローディングを生成

--optimize-autoloader オプションで
クラスマップを強制で生成する

Page 47

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);
}
}

Page 48

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',
);

Page 49

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);

Page 50

ClassLoader.php

クラスローダーの実装本体

いままで設定された情報をもとにクラス
名の解決・ファイルロードをする

動的に変動する部分は分離されてるので、

バイトコードがキャッシュされても 特に問題なさそうに作ってるっぽい

Page 51

ClassLoader.php

spl_autoload_register()で呼ばれる

クラスマップにあったらそれを読む

なかったらpsr-4, psr-2を読む

PEARに依存するなどの場合は
include_pathを読む

Page 52

これによって 得られる知見

Page 53

Composerは、いろんなPHP のバージョンとか実行環境
で動くように書かれてる

Page 54

実行時にcomposer.jsonとか composer.lockは使ってなく
て、静的なPHPスクリプト
ファイルが書き出される

Page 55

composer install

サーバーで
の必要はなく、手元でインス トールしてvendorディレクト
リごと転送してやればいい

Page 56

よくある誤解

Page 57

「レンタルサーバーの最安プ
ランでPHP動かしてるから
Composer入らないんだ…」
といったことはない

Page 58

本筋から 外れた話

Page 59

Autoloader
Optimization
https://getcomposer.org/doc/articles/autoloader-optimization.md

Page 60

Level 1

本番運用時は

が基本

--optimize-autoloader

Page 61

……そのほかは

効果あるのかな? (私は使ってないです)

Page 62

予告

Page 63

PHP AdventCalendar 2017では
Composerとか全然考慮されてない (zipで配布されてる)SDKをむりやり Composerで管理する話を書きます