Composer自动加载类型
1、files 2、classmap 3、psr-0 4、psr-4
建议:项目代码用psr-4自动加载,helper用files自动加载,生产环境用classmap自动加载。psr-0已经被抛弃了,历史遗留代码有部分使用。
我们现在来测试一下,我们先创建一个测试根目录autoloads,然后进入:
~/codeDir/phpCode # mkdir autoloads ; cd autoloads ~/codeDir/phpCode/autoloads #
files
我们创建目录libraries,然后进入:
~/codeDir/phpCode/autoloads # mkdir libraries ; cd libraries ~/codeDir/phpCode/autoloads/libraries #
然后创建文件functions.php:
~/codeDir/phpCode/autoloads/libraries # touch functions.php ~/codeDir/phpCode/autoloads/libraries #
内容如下:
<?php function func1 () { return 'in func1.'; }
然后,我们在目录autoloads下面创建一个composer.json文件:
~/codeDir/phpCode/autoloads/libraries # cd .. ; touch composer.json
内容如下:
{ "autoload": { "files": ["libraries/functions.php"] } }
这里,autoload我们填写的是files,说明我们打算基于files来完成自动加载。
然后执行命令composer dump:
~/codeDir/phpCode/autoloads # composer dump Do not run Composer as root/super user! See https://getcomposer.org/root for details Generated autoload files containing 0 classes ~/codeDir/phpCode/autoloads #
我们会发现多了一个文件夹vendor,目录结构如下:
~/codeDir/phpCode/autoloads # tree -L 3 . ├── composer.json ├── libraries │ └── functions.php └── vendor ├── autoload.php └── composer ├── ClassLoader.php ├── LICENSE ├── autoload_classmap.php ├── autoload_files.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php └── autoload_static.php 3 directories, 11 files ~/codeDir/phpCode/autoloads #
我们会发现,composer文件夹里面的这几个autoload_*.php文件刚好和我们介绍的自动加载类型对应。并且,只有autoload_files.php文件里面有自动加载的有效信息:
return array( '4f84e339e19580763acd6b29b090e23c' => $baseDir . '/libraries/functions.php', );
然后,我们创建一个测试文件test.php来测试是否可以找到文件functions.php中的func1函数:
~/codeDir/phpCode/autoloads # touch test.php ~/codeDir/phpCode/autoloads # cat > test.php <<EOF <?php require 'vendor/autoload.php'; echo func1(); EOF ~/codeDir/phpCode/autoloads #
为了方便大家复制粘贴,我直接通过heredoc语法来编辑文件,大家直接在命令行里面粘贴进去即可。
然后,我们执行脚本:
~/codeDir/phpCode/autoloads # php test.php in func1.~/codeDir/phpCode/autoloads #
成功调用了函数func1。
这是基于files的自动加载。
classmap
现在,我们通过classmap来完成自动加载。这比files类型的加载好,因为我们不必指明具体的文件,只需指明文件所在的目录即可。所以相对于files类型的加载又更加的灵活一点了。
我们创建目录classmap:
~/codeDir/phpCode/autoloads # mkdir classmap ; cd classmap
然后创建文件functions.php:
~/codeDir/phpCode/autoloads/classmap # touch functions.php ~/codeDir/phpCode/autoloads/classmap #
编辑文件内容:
~/codeDir/phpCode/autoloads/classmap # cat > functions.php << EOF <?php Class Test { public function func1 () { return 'in Test->func1'; } } EOF ~/codeDir/phpCode/autoloads/classmap #
然后修改composer.json文件:
~/codeDir/phpCode/autoloads/classmap # cd .. ~/codeDir/phpCode/autoloads # cat > composer.json << EOF { "autoload": { "files": ["libraries/functions.php"], "classmap": ["classmap"] } } EOF
然后,我们编写测试文件:
~/codeDir/phpCode/autoloads # cat > test.php << EOF <?php require 'vendor/autoload.php'; echo func1(); \$t = new Test(); echo \$t->func1(); EOF ~/codeDir/phpCode/autoloads #
然后执行命令:
~/codeDir/phpCode/autoloads # composer dump Do not run Composer as root/super user! See https://getcomposer.org/root for details Generated autoload files containing 1 classes
现在,除了vendor/composer/autoload_files.php里面有有效的内容外,vendor/composer/autoload_classmap.php里面也有了:
return array( 'Test' => $baseDir . '/classmap/functions.php', );
接着测试脚本:
~/codeDir/phpCode/autoloads # php test.php in func1.in Test->func1~/codeDir/phpCode/autoloads #
我们发现成功的调用了Test->func1。
psr-0
psr-0用的就比较少了,这里不演示了。
psr-4
我们创建src目录:
~/codeDir/phpCode/autoloads # mkdir src ; cd src ~/codeDir/phpCode/autoloads/src #
然后创建文件Test.php:
~/codeDir/phpCode/autoloads/src # touch Test.php ~/codeDir/phpCode/autoloads/src #
文件内容如下:
~/codeDir/phpCode/autoloads/src # cat > Test.php << EOF <?php namespace App; Class Test { public function func1 () { return 'in Test->func1'; } } EOF
然后修改composer.json文件:
~/codeDir/phpCode/autoloads/src # cd .. ~/codeDir/phpCode/autoloads # cat > composer.json << EOF { "autoload": { "files": ["libraries/functions.php"], "classmap": ["classmap"], "psr-4": { "App\\": "src" } } } EOF
注意App后面不要漏了\\,否则会报错:
A non-empty PSR-4 prefix must end with a namespace separator.
翻译过来就是:
非空PSR-4前缀必须以命名空间分隔符结尾。
编写测试文件:
~/codeDir/phpCode/autoloads # cat > test.php << EOF <?php require 'vendor/autoload.php'; echo func1(); \$t = new Test(); echo \$t->func1(); \$t1 = new App\Test(); echo \$t1->func1(); EOF
然后执行命令:
~/codeDir/phpCode/autoloads # composer dump Do not run Composer as root/super user! See https://getcomposer.org/root for details Generated autoload files containing 1 classes ~/codeDir/phpCode/autoloads #
此时,文件autoload_psr4.php里面有了有效内容:
return array( 'App\\' => array($baseDir . '/src'), );
执行脚本:
~/codeDir/phpCode/autoloads # php test.php in func1.in Test->func1in Test->func1~/codeDir/phpCode/autoloads #
调用成功。
全部使用classmap自动加载
执行命令:
~/codeDir/phpCode/autoloads # composer dumpautoload -o Do not run Composer as root/super user! See https://getcomposer.org/root for details Generated optimized autoload files containing 2 classes ~/codeDir/phpCode/autoloads #
我们会发现,在文件autoload_classmap.php里面,内容更新为了:
return array( 'App\\Test' => $baseDir . '/src/Test.php', 'Test' => $baseDir . '/classmap/functions.php', );
多了’App\\Test’ => $baseDir . ‘/src/Test.php’。它把以psr-4方式加载的类也变成了以classmap方式加载了。
我们看看-o参数的解释:
~/codeDir/phpCode/autoloads # composer dumpautoload --help Options: -o, --optimize Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.
也就是说,会自动把我们配置好的psr-0以及psr-4加载方式转化为classmap的方式加载。
如果我们要取消这种优化,那么我们可以执行命令:
~/codeDir/phpCode/autoloads # composer dumpautoload Do not run Composer as root/super user! See https://getcomposer.org/root for details Generated autoload files containing 1 classes ~/codeDir/phpCode/autoloads #
此时,文件autoload_classmap.php里面的内容变成了原来的:
return array( 'Test' => $baseDir . '/classmap/functions.php', );
阅读vendor/composer/ClassLoader.php:
public function findFile($class) { // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } // 省略了其他的代码 $file = $this->findFileWithExtension($class, '.php'); // 省略了其他的代码 } private function findFileWithExtension($class, $ext) { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; //省略了其他的代码 // PSR-0 lookup if (false !== $pos = strrpos($class, '\\')) { // namespaced class name $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); } else { // PEAR-like class name $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; } // 省略了其他的代码 }
我们会发现,findFile函数会先去classmap里面查找类的文件路径然后加载类(即include类),如果没找到,再通过psr-4的方式加载类,如果psr-4方式没找到,再通过psr-0的方式加载。
因为classmap的方式是key – value,可以直接找到类文件的位置,而不需要向psr-4那样需要用到一些拼接的操作,所以通过classmap的方式找类文件会快一点。
如果,我们的类都是通过classmap的方式加载的,并且在classmap里面找不到类的时候,不再通过其他加载方式查找类(即隐含的认为classmap中就是所有合法的类,这样当类不存在的时候,就可以避免没必要的查找了),那么,我们可以通过如下的命令来优化:
~/codeDir/phpCode/autoloads # composer dumpautoload -a Do not run Composer as root/super user! See https://getcomposer.org/root for details Generated optimized autoload files (authoritative) containing 2 classes ~/codeDir/phpCode/autoloads #
然后,我们看看findFile的代码:
public function findFile($class) { // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { return false; } // 省略其他的代码
此时$this->classMapAuthoritative的值会变成true。所以,一旦:
if (isset($this->classMap[$class])) { return $this->classMap[$class]; }
这个classmap里面没有我们的类,那么就会直接返回false。这样就避免了没必要的查找了。
但是不推荐在生产环境下使用-a的优化,因为我们无法确保classmap里面真的就是包含了所有我们需要的类。我们推荐使用-o进行优化。
转载请注明:爱学习爱分享 » 现代化 PHP:生产环境下优化 Composer 加载的原理