読者です 読者をやめる 読者になる 読者になる

k-holyのPHPとか諸々メモ

Webで働くk-holyがPHP(スクリプト言語)とか諸々のことをメモしていきます。ソースコードはだいたいWindowsで動かしてます。

Composerの使い方を調べたメモ(3) 名前空間を使わないPSR-0準拠のライブラリをオートロード

PHP Composer

名前空間を使わないPSR-0のライブラリをComposerのオートローダで利用する方法について調べたメモです。

英語が不得手な自分は、マニュアルのサンプルを眺めながら試行錯誤を繰り返すしかないわけで、トンチンカンなことをやってる/書いてるかもしれないと、あらかじめお断りしておきます。
(英語読めるなら http://getcomposer.org/doc/04-schema.md を読んだ方が早いです、多分)

まず、Packagist未登録のPSR-0対応ライブラリをGitHubからインストールする場合。

利用側のcomposer.json

composerのinstallコマンドを実行すると、vendor/phanda/pathtranslator 以下にリポジトリのファイルが全てコピーされる。

$ php composer.phar install
Installing dependencies
  - Installing phanda/pathtranslator (dev-master)
    Cloning master

Writing lock file
Generating autoload files

インストール時にcomposerによって生成された installed.json

Packagist経由でGitからインストールした場合は source reference と dist reference ともにGitのコミットIDが設定されるのに対して、source reference が "master" で dist reference が nullになっている。

問題なのは、こうしてインストールしたパッケージのクラスは、composerのクラスローダ経由で利用できないこと。

ここで読み込んでいる autoload.php は composer コマンドで自動生成されたもの。

autoload.php

composerでインストールしたライブラリは基本的にこのファイルを読み込んだらすぐに利用できる、というのが売りのはずだけど、今の状態だと無理なようだ。
composerに伝えられる phanda/pathtranslator ライブラリの情報は利用側の composer.json に定義している repositories の部分だけだろうから、当然といえば当然か。

Composer\Autoload\ClassLoader のコードを読んだところ、addClassMap()メソッドでクラスマップ(クラス名とファイルの対応を定義した配列)を追加したり、add()メソッドでクラス接頭辞と読み込み対象のパスを追加できる。
Symfony\Component\ClassLoader\UniversalClassLoaderのコードをベースに不要な機能を削ぎ落とした感じ)

PSR-0形式のクラスは自動生成された autoload_namespaces.php の定義を利用してadd()メソッドで登録されている。
同じく自動生成された autoload_classmap.php を使うこともできるらしい。PSR-0に沿っていないクラスではこの方法を使うのかもしれない。

いずれにせよこのままでは、利用したいクラスがどのライブラリに属していてどこからインストールしたかによって読み込み方法を変えなければいけないので不便だ。
自動生成されたコードなので手を入れるわけにもいかないし、上記 $loader->register() で spl_autoload_register() が実行されてしまっているので、オートロードを利用したい場合は更に __autoload スタックにオートロード関数を追加しないといけない。(無駄に遅くなりそう)
ライブラリ側に composer.json を設置して autoload を指定すれば autoload_namespaces.php に組み込んでくれるんだろうか?

ライブラリ側のcomposer.json

こうしておいて、利用側の composer.json はそのまま(Packagistを経由せず直接GitHubのリポジトリを参照)で composer update してみた。
しかし installed.json はそのままで、composerのクラスローダが利用する autoload_classmap.php と autoload_namespaces.php にも何も定義されていない。うーむ。

まあ、ここまで下準備しておいてPackagistを使わない理由もないので、素直にリポジトリをPackagistに登録して再挑戦。
(Packagistへの登録は Composerの使い方を調べたメモ(2) GitHubのリポジトリをPackagistに登録してcomposerでインストールを参照)

Packagistに登録したので、利用側の composer.json から repositories を削除する。

再インストール

$ php composer.phar install
Installing dependencies
  - Installing phanda/pathtranslator (dev-master)
    Cloning 14e6204e7c0a0223673ecc4ef06cd964b20fd784

Writing lock file
Generating autoload files

コマンドの結果が、「Cloning master」から「Cloning 14e6204e7c0a0223673ecc4ef06cd964b20fd784」に変わっている。

無事にオートロードが効いてくれた。
composerが生成した autoload_namespaces.php には Phanda_PathTranslator の定義が追加されている。

autoload_namespaces.php

インストール時にcomposerによって生成された installed.json は、ライブラリ側に定義した composer.json の内容がマージされて、かなり長くなっている。

composerのオートロードを使いたければ、何はともあれPackagistに登録しなさいってことだろうか。

[追記]
Packagistに登録しなくても、composerでオートロードできました。
autoload.php をよく読めば分かりますが、return call_user_func() で無名関数から \Composer\Autoload\ClassLoader のインスタンスが return されてますので、include を変数に代入すれば呼び出し側で受け取れるわけですね。

autoload.php

利用側スクリプト

ライブラリ側がcomposerに対応していない場合、この方法でadd()するなりaddClassMap()するなりでオートロードできそうです。

@tanakahisateruさんから指摘していただきました。

$loader = require autoload.php で ClassLoader のインスタンスが出てくるので、それに続いて $loader->add("Oreore_Utils", "vendor/..."); って感じはどうでしょう?

https://twitter.com/tanakahisateru?iid=am-85321533413395629475329188&nid=4+status_user&uid=234777649&utm_content=profile/

スッキリしました、ありがとうございます。