PHP Composer 的 自动加载[译]
tips:源于一次面试,问自动加载的底层,回来查了查也没查到什么好的文章来说,知道看到这篇文章,我真的觉得讲的很好,希望大家都能学习学习吧。第一次翻译技术文,如有不准确的各位同学请大胆提出,谢谢大家~
斗胆总结-到底是怎么自动加载的:
1.入口文件必然会引入composer中的autoload.php
2.autoload文件会调用
classLoader
中getLoader
方法,方法中使用spl_autoload_register
将loadClass
注册成自动加载的方法3.当使用类名的时候,类名就会传入loadClass方法中,findFile找这个文件,通过classmap里找,psr0和psr4前缀拼去找这个文件进行加载
Composer 对于任何 PHP 开发人员来说都是最棒的工具,它让管理任何扩展包的依赖变得简单。
下面的章节里,我不打算谈论Composer的主要功能“管理依赖关系”,而我会去关注在Composer中是如何管理加载大量的包。
1. 什么是自动加载
最大的烦恼之一是在每个PHP脚本文件的开头都必须要引入一长串的PHP类文件
问题:假设你正在开发一个应用程序,你有很多库需要加载进来
解决方案:在脚本的开头你去引入所有的类文件,你将可以在任何位置使用任何一个类
更好的解决方案:每当我要用一个类时,我再去加载他,这样我的应用程序就不必在每个请求中加载所有的类,这就是“自动加载”。但是如何实现呢?让我们搞清楚
2. 创建自动加载的程序
一个简单的方法,我们可以引入所有文件
<?php
// Classes/A.php
class A {}
<?php
// Classes/B.php
class B {}
<?php
// index.php
include_once 'Classes/A.php' ;
include_once 'Classes/B.php' ;
// load A class
$a = new A();
// check the list of all loaded files
var_dump(get_included_files());
脚本将会输出:
array(3) {
[0]=> string(20) "/path/to/root/index.php"
[1]=> string(24) "/path/to/root/Classes/A.php"
[2]=> string(24) "/path/to/root/Classes/B.php"
}
这意味着无论用不用B类,都会加载所有文件,随着你的项目越来越大,这样肯定是不行的。
自动加载 的出现解决了这个问题
<?php
// index.php
// my custom autoloader
function my_autoloader($class) {
include 'Classes/' . $class . '.php';
}
// register the autoloader
spl_autoload_register('my_autoloader');
// load A class
$a = new A();
// check the list of all loaded files
var_dump(get_included_files());
这种方式相当于在my_autoloader
函数中给类名和文件路径做了“映射”,而且注册了这个函数,因此你的脚本在任何时刻都可以对类进行实例化,只需要通过这个方法加载他。现在看看脚本中加载的文件:
array(2) {
[0]=> string(50) "/path/to/demos/autoload-2/index.php"
[1]=> string(54) "/path/to/demos/autoload-2/Classes/A.php"
}
现在你的脚本就只加载了需要的文件。
PHP is telling you “I will give you the chance to go and load your class even if you didn’t load it before your statement
$a = new A();
after that, I will throw an error if it didn’t work”.
PHP告诉你“即使你在$a = new A();
的时候没有加载这个类,我也会去加载你的类,之后如果它不起作用,我会抛出一个异常”。
上面的例子都没啥问题,但你需要注意下面这几个事:
- 文件名应与类名相同
- 每个文件应该只有一个类
- 类名和文件名都应该区分大小写
在你的程序中你可以去处理这些情况,任何应用程序都可以根据它自己的文件结构做映射,但是为了使用这个自动加载器,你必须遵守上面的注意事项。
Composer提出了一种适合大多数结构的方式(或者说它强制在应用程序中做了选择 that it forces the applications to choose between ),让我们深入挖掘一下。
3. Composer是怎么做自动加载的呢
在脚本目录下添加composer.json文件
{
"autoload" : {
"classmap" : [
"Classes/"
]
}
}
更新你的index.php文件
require __DIR__ . '/vendor/autoload.php';
$a = new A();
Using
classmap
, we are telling composer that this is the way to do mapping, and it is a very basic way to map namespaces to paths and if you want more details, just checkvendor/composer/autoload_classmap.php
after you run thecomposer install
:
使用classmap
,我们告诉Composer这是进行映射的方法,这是将命名空间映射到路径的一种基本方式,如果你想获得更多的信息,在运行composer install
之后,可以去看vendor/composer/autoload_classmap.php
,如下:
return array(
'A' => $baseDir . '/Classes/A.php',
'B' => $baseDir . '/Classes/B.php',
);
每当你实例化A,他就会去加载$baseDir . '/Classes/A.php'
这个文件。
这个流程很容易理解。在程序中它总是从下面这行开始:
require __DIR__ . '/vendor/autoload.php';
而且你会发现到ComposerAutoloaderInitXXXX:getLoader()
有所有的逻辑,这是在添加了不同的映射,在末尾注册成了自动加载:
// \Composer\ComposerAutoloaderInitXXXX:getLoader()
public static function getLoader() {
...
$loader->register(true); // \Composer\ClassLoader::register()
return $loader;
}
这是我们注册自动加载器的地方:
// \Composer\ClassLoader::register()
public function register($prepend = false) {
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}
每当我们实例化一个类的时候都会通过ClassLoader::loadClass()
寻找相应的文件并加载他。
4. Classmap VS PSR-0 VS PSR-4
他们都是将命名空间及其对应路径映射的不同方式,一旦选择其中之一,你就需要一直遵循他们的规则,Composer知道如何基于您所遵循的映射来寻找相应的文件并加载他。
- Classmap:是最简单、最直接的,它输出的逻辑在
vendor/composer/autoload_classmap.php
中,当Composer读到我们正在使用classmap
加载时,它会扫描composer.json
中classmap
的所有文件,而且会去创建一个命名空间和其路径映射的数组。
注: 添加新文件后,需要使用composer dumpautoload
命令重新生成对应关系。
-
PSR-0 :逻辑代码在这
vendor/composer/autoload_namespaces.php
,而且他的名字遵循了PSR-0的规则。 -
PSR-4:代码
vendor/composer/autoload_psr4.php
,遵循了PSR-4规则。
PSR-0和PSR-4有几乎相同的规则。
注:
- 这两个都不需要去执行
composer dumpautoload
,因为每次新加PHP文件时,“搜索文件路径”是动态执行的 - 你需要去use namespace,尤其是PSR-4,因为命名空间附加到文件路径上。
子目录名称必须与子命名空间名称大小写一致。 - PSR-4
- PSR-0是将下划线转换为目录分隔符,而PSR-4不同。
类名中的每个
_
都转换为一个DIRECTORY_SEPARATOR(目录分隔符)。在命名空间中_没有特殊含义。 - PSR-0
因此,$a = new Classes_A();
会加载下面的文件:
<?php
// path /Classes/A.php
class Classes_A {}
注意这里我们并没用use namespace
- 以下
composer.json
正常工作,而且命名空间前缀为App的,会在Classes目录中寻找文件,但是PSR-0相反,前缀是不会存在于文件路径中。
{
"autoload" : {
"psr-4" : {
"App\\" : "Classes"
}
}
}
-----以下是我自己加进来的哈,就是我们常见的一种引入的方式,更好的解释上面的配置---
Classes中建个C
<?php
namespace App;
class C
{
}
在index.php中use使用
<?php
require __DIR__ . '/vendor/autoload.php';
use App\C;
$a = new C();
var_dump(get_included_files());
---- 截止 ----
5. 结论
我们开始在PHP中定义了自动加载的文件,它正在解决它的问题,我们也定义了简单的例子,我们讨论了Composer,以及如何用不同的方式进行映射。
我介绍这个的主要原因是我们大多数应用程序和包中都用了Composer,我们应该去了解autoload是如何工作的。
我尽量写的很简单了,可以通过下面的链接做进一步了解。
6. 参考
- php.net/manual/en/l…
- php.net/manual/en/f…
- php.net/manual/en/f…
- getcomposer.org/doc/04-sche…
- www.php-fig.org/psr/psr-0
- www.php-fig.org/psr/psr-4
这有一篇我发现的类似的文章,可以一起看看哦
真是没想到有一天能自己翻译一篇文章呢,希望多点面试,赶紧找份工作吧。面试的过程也总是自我怀疑的过程,好好学习知识,加油啦 还有,能不能升到四级,我好难,我感觉三级这个等级已经很久了,虽然我确实写的很少。。。
转载自:https://juejin.cn/post/7207848485856968762