composer基础与实践-下篇
在composer中,psr-0,psr-4是两个比较类似的”自动加载”规范,前者已经不建议使用,后者是前者的优化版本,是当下主流选择。
在composer基础与实践-中篇中,使用classmap将某目录下所有class自动加载,使用files将指定文件一次性引入。那么psr-0和psr-4又做了什么呢?
我感受最大的区别,就是psr-0和psr-4定义了命名空间和多级目录之间的映射规则,从而可以很方便的组织一个目录层次较深,命名空间较多的项目,而classmap和files仅仅解决了单一文件或者单一目录的自动加载问题。
1,先看psr-0,编写composer.json如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[work@vultr project]$ cat composer.json { "name": "project", "require": { "monolog/monolog": "1.21.0" }, "autoload": { "classmap": ["common"], "files": ["common/func.php"], "psr-0": { "common\\util\\": "common/util" } } } [work@vultr project]$ php composer.phar dump-autoload Generating autoload files |
我期望common\util命名空间的类,都去common/util目录下查找,所以如上定义。然而psr-0之所以被淘汰的重要原因,就是它奇葩的设定。
实际上这个配置方式,它先会切换到common/util目录下,然后它会将common\\util命名空间拆分成目录层次common/util,拼接到目录后面形成common/util/common/util,在这个目录下查找对应的类文件,与我想要的效果大相径庭。
我们还是看一下这种情况下的目录分布以及调用代码。
1 2 3 4 5 6 7 8 9 10 |
[work@vultr project]$ cat common/util/common/util/Writer.php <?php namespace common\util; class Writer { public function __construct() { echo __CLASS__ . "\n"; } }; |
这是根据psr-0规则和我们的composer.json配置,编写的Writer类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[work@vultr project]$ cat index.php <?php // 引入composer的自动加载文件 require __DIR__ . '/vendor/autoload.php'; //require __DIR__ . '/common/MyLog.php'; use common\MyLog; use common\util\Writer; MyLog::instance()->info('hello'); new SomeClass(); some_func(); echo SOME_DEF; $w = new Writer(); [work@vultr project]$ php index.php [2016-08-08 12:56:29] test.INFO: hello [] [] SomeClass some_func this is a def common\util\Writer |
这是入口文件的调用方式,创建了common\util命名空间下的Writer对象,于是命中了psr-0规则,在common/util/common/util目录下找到了对应的Writer.php类。
可以看一下composer的autoload_namespaces.php文件,它记录了生成的psr-0映射规则。
1 2 3 4 5 6 7 8 9 10 11 12 |
[work@vultr project]$ cat vendor/composer/autoload_namespaces.php <?php // autoload_namespaces.php @generated by Composer $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( 'common\\util\\' => array($baseDir . '/common/util'), 'Psr\\Log\\' => array($vendorDir . '/psr/log'), ); |
虽然映射到了common/util目录,但是在实际加载的时候又把namespace部分追加到目录后面,导致目录嵌套重复。
2,psr-0不仅存在上述问题,还有其他一些小区别(不是重点)。psr-4则解决了psr-0存在的一些问题,更加好用了。
把上面的composer.json配置的psr-0改成psr-4,其他不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[work@vultr project]$ cat composer.json { "name": "project", "require": { "monolog/monolog": "1.21.0" }, "autoload": { "classmap": ["common"], "files": ["common/func.php"], "psr-4": { "common\\util\\": "common/util" } } } [work@vultr project]$ php composer.phar dump-autoload Generating autoload files |
psr-4的行为更加符合预期,它在遇到common\util命名空间的类时,会去common/util目录下直接查找类文件,而不是去common/util/common/util下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
[work@vultr project]$ cat common/util/Writer.php <?php namespace common\util; class Writer { public function __construct() { echo __CLASS__ . "\n"; } }; [work@vultr project]$ php index.php [2016-08-08 13:05:43] test.INFO: hello [] [] SomeClass some_func this is a def common\util\Writer |
效果符合预期。之前提到psr规范支持多级目录的加载,只要目录层次和命名空间一致即可。那么创建一个common/util/impl/Meta.php,命名空间根据目录结构定义为common\util\impl,并在Writer中调用它,看composer是否可以自动加载到Meta.php。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
[work@vultr project]$ cat common/util/impl/Meta.php <?php namespace common\util\impl; class Meta { public function __construct() { echo __CLASS__ . "\n"; } } [work@vultr project]$ cat common/util/Writer.php <?php namespace common\util; use common\util\impl\Meta; class Writer { public function __construct() { echo __CLASS__ . "\n"; new Meta(); } }; [work@vultr project]$ php index.php [2016-08-09 05:23:50] test.INFO: hello [] [] SomeClass some_func this is a def common\util\Writer common\util\impl\Meta |
可见,因为我们配置了psr-4加载common\util命名空间的原因,所以common\util\impl命名空间作为其子命名空间会依据目录结构自动加载。
因为Psr-0已淘汰,如果自己写项目建议忘掉psr-0直接使用psr-4即可。另外,composer在yii2.0等框架中被广泛使用,掌握其原理对管理该类项目有很大的好处,建议掌握上述内容。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~
