Composer が PSR-4 に対応していたので試してみました。
PSR-0 と PSR-4
PSR-4 はオートローダーに関する規約で PSR-0 を置き換えるものです。
ざっくり PSR-0 と比べると次のような違いがあります。
- クラス名に含まれるアンダースコアに特別な意味は無くなった
- PSR-0 ではクラス名のアンダースコアはディレクトリ区切りでした
- 名前空間の先頭とそれに続く一部のサブ名前空間(名前空間プレフィックス)が、任意の ベースディレクト に対応
- オートローダーの実装は例外を投げてはならない(MUST)、どのエラーのレベルも発生させてはならない(MUST)、値を返すべきではない(SHOULD)
これまでの Composer の PSR-0 で、例えば doctrine を require すると次のようなディレクトリにソースが配置されていました。
vendor/doctrine/ common/ lib/ Doctrine/ Common/ *** dbal/ lib/ Doctrine/ DBAL/ *** orm/ lib/ Doctrine/ ORM/ ***
パス内にパッケージ名である doctrine/orm
と名前空間である Doctrine/ORM
が含まれており、冗長な気がします。
また、Doctrine のリポジトリの方でも上記のような構造にするために
- lib の直下に Doctrine ディレクトリ
- Doctrine の直下に ORM ディレクトリ
- 大抵の場合これらのディレクトリの同階層に他のディレクトリは存在しない
- (Doctrine の場合 Doctrine ディレクトリの同階層に別のディレクトリもありましたが)
などとする必要があり、ソースのディレクトリの階層が無駄に深くなっているように感じました。
Symfony の Components は Composer の target-dir を使っているので、リポジトリのソースの階層は浅いですが PSR-0 の縛りにより src や lib などのディレクトリにソースを纏めることが出来ず、リポジトリの直下にソースファイルが配置されています。
PSR-4 な composer.json
ソースのディレクトリ構造は次のようにします。
Sample.php は ngyuki/Example/Psr4/Sample
クラスですが、名前空間でディレクトリを掘る必要はありません。
composer.json src/ Sample.php
composer.json は次のようにします。"autoload"
の "psr-4"
で "名前空間プレフィックス":"ベースディレクトリ"
と指定します。
{ "name": "ngyuki/example-psr4", ...snip... "autoload": { "psr-4": { "ngyuki\\Example\\Psr4\\": "src/" } } }
次のようにオートロードされます。
ngyuki\\Example\\Psr4\\
というプレフィックスを持つクラスが要求される- 完全修飾クラス名の先頭から
ngyuki\\Example\\Psr4\\
を取り除く - 残りの名前空間をサブディレクトリとして
src/
からソースファイルを探す
名前空間の一部をサブディレクトリにする
名前空間の一部だけをサブディレクトリにすることも出来ます。
例えば↑と同じクラス名で、Example\Psr4
の部分だけサブディレクトリにします。
composer.json src/ Example/ Psr4/ Sample.php
composer.json は次のようにします。
{ "name": "ngyuki/example-psr4", ...snip... "autoload": { "psr-4": { "ngyuki\\Example\\Psr4\\": "src/Example/Psr4/" } } }
さいごに
PEAR のように単一のディレクトリにすべてのライブラリのソースファイルを配置するような構造だと、ベンダ名やパッケージ名でディレクトリを掘ることが必須でした。 そのために PSR-0 でオートローダーの標準化がされました。
がしかし、Composer は単一のディレクトリにすべてのライブラリのソースを配置するのではなく、パッケージごとに別々のディレクトリに配置しました。 そのため、必要以上にディレクトリ階層が深く、かつ、反復的になってしまいました。
・・・というような経緯が書かれています。