PHP 自动加载原理解析
<h1 style="color: black; text-align: left; margin-bottom: 10px;">前言</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">PHP 自5.3的版本之后,<span style="color: black;">已然</span>重焕新生,命名空间、性状(trait)、闭包、接口、PSR 规范、以及 composer 的<span style="color: black;">显现</span><span style="color: black;">已然</span>让 PHP 变<span style="color: black;">成为了</span>一门现代化的脚本语言。PHP 的生态系统<span style="color: black;">亦</span><span style="color: black;">始终</span>在演进,而 composer 的<span style="color: black;">显现</span><span style="color: black;">更加是</span>彻底的改变了以往构建 PHP 应用的方式,<span style="color: black;">咱们</span><span style="color: black;">能够</span><span style="color: black;">按照</span> PHP 的应用<span style="color: black;">需要</span>混合搭配最合适的 PHP 组件。当然这<span style="color: black;">亦</span>得益于 PSR 规范的提出</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">PHP 自动加载功能</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">PHP 自动加载功能的由来</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">在 PHP <span style="color: black;">研发</span>过程中,<span style="color: black;">倘若</span><span style="color: black;">期盼</span>从<span style="color: black;">外边</span>引入一个 Class ,<span style="color: black;">一般</span>会<span style="color: black;">运用</span> include 和 require <span style="color: black;">办法</span>,去把定义这个 Class 的文件<span style="color: black;">包括</span>进来。这个在小规模<span style="color: black;">研发</span>的时候,没什么大问题。但在大型的<span style="color: black;">研发</span>项目中,<span style="color: black;">运用</span>这种方式会带来<span style="color: black;">有些</span>隐含的问题:<span style="color: black;">倘若</span>一个 PHP 文件需要<span style="color: black;">运用</span><span style="color: black;">非常多</span>其它类,<span style="color: black;">那样</span>就需要<span style="color: black;">非常多</span>的 require/include 语句,<span style="color: black;">这般</span>有可能会 <span style="color: black;">导致</span>遗漏 <span style="color: black;">或</span> <span style="color: black;">包括</span>进不必要的类文件。<span style="color: black;">倘若</span><span style="color: black;">海量</span>的文件都需要<span style="color: black;">运用</span>其它的类,<span style="color: black;">那样</span>要<span style="color: black;">保准</span><span style="color: black;">每一个</span>文件都<span style="color: black;">包括</span>正确的类文件肯定是一个噩梦, 况且 require或 incloud 的性能代价很大。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">PHP5 为这个问题<span style="color: black;">供给</span>了一个<span style="color: black;">处理</span><span style="color: black;">方法</span>,这<span style="color: black;">便是</span> 类的自动加载(autoload)机制。autoload机制 <span style="color: black;">能够</span>使得 PHP 程序有可能在<span style="color: black;">运用</span>类时才自动<span style="color: black;">包括</span>类文件,而不是一<span style="color: black;">起始</span>就将所有的类文件include进来,这种机制<span style="color: black;">亦</span><span style="color: black;">叫作</span>为 Lazy loading (惰性加载)。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">自动加载功能的优点</span></p><span style="color: black;"><span style="color: black;">运用</span>类之前无需 include / require</span><span style="color: black;"><span style="color: black;">运用</span>类的时候才会 include / require 文件,实现了 lazy loading ,避免了 include / require 多余文件</span><span style="color: black;">无需<span style="color: black;">思虑</span>引入类的<span style="color: black;">实质</span>磁盘<span style="color: black;">位置</span> ,实现了<span style="color: black;">规律</span>和实体文件的分离</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">PHP 自动加载函数 __autoload()</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">从 PHP5 <span style="color: black;">起始</span>,当<span style="color: black;">咱们</span>在<span style="color: black;">运用</span>一个类时,<span style="color: black;">倘若</span><span style="color: black;">发掘</span>这个类<span style="color: black;">无</span>加载,就会自动运行 __autoload() 函数,这个函数是<span style="color: black;">咱们</span>在程序中自定义的,在这个函数中<span style="color: black;">咱们</span><span style="color: black;">能够</span>加载需要<span style="color: black;">运用</span>的类。下面是个简单的示例</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">__autoload</span><span style="color: black;">($classname)</span> </span>{
<span style="color: black;">require_once</span> ($classname . <span style="color: black;">".class.php"</span>);
}</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">在<span style="color: black;">咱们</span>这个简单的例子中,<span style="color: black;">咱们</span>直接将类名加上扩展名 .class.php 构<span style="color: black;">成为了</span>类文件名,<span style="color: black;">而后</span><span style="color: black;">运用</span> require_once 将其加载</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">从这个例子中,<span style="color: black;">咱们</span><span style="color: black;">能够</span>看出 __autoload <span style="color: black;">最少</span>要做三件事情:</span></p><span style="color: black;"><span style="color: black;">按照</span>类名确定类文件名</span><span style="color: black;">确定类文件所在的磁盘路径</span><span style="color: black;">将类从磁盘文件中加载到系统中</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">第三步最简单,只需要<span style="color: black;">运用</span> include / require <span style="color: black;">就可</span>。要实现<span style="color: black;">第1</span>步,第二步的功能,必须在<span style="color: black;">研发</span>时约定类名与磁盘文件的映射<span style="color: black;">办法</span>,<span style="color: black;">仅有</span><span style="color: black;">这般</span><span style="color: black;">咱们</span><span style="color: black;">才可</span><span style="color: black;">按照</span>类名找到它对应的磁盘文件</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">当有<span style="color: black;">海量</span>的类文件要<span style="color: black;">包括</span>的时候,<span style="color: black;">咱们</span>只要确定相应的规则,<span style="color: black;">而后</span>在 __autoload() 函数中,将类名与<span style="color: black;">实质</span>的磁盘文件对应起来,就<span style="color: black;">能够</span>实现 lazy loading 的效果</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">PHP autoload函数说明</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">__autoload() 函数存在的问题</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">倘若</span>在一个系统的实现中,<span style="color: black;">倘若</span>需要<span style="color: black;">运用</span><span style="color: black;">非常多</span>其它的类库,这些类库可能<span style="color: black;">是由于</span><span style="color: black;">区别</span>的<span style="color: black;">研发</span>人员编写的, 其类名与<span style="color: black;">实质</span>的磁盘文件的映射规则不尽相同。<span style="color: black;">此时</span><span style="color: black;">倘若</span>要实现类库文件的自动加载,就必须 在 __autoload() 函数中将所有的映射规则<span style="color: black;">所有</span>实现,<span style="color: black;">这般</span>的话 __autoload() 函数有可能会非常<span style="color: black;">繁杂</span>,<span style="color: black;">乃至</span><span style="color: black;">没法</span>实现。最后可能会<span style="color: black;">引起</span> __autoload() 函数<span style="color: black;">非常</span>臃肿,<span style="color: black;">此时</span>即便能够实现,<span style="color: black;">亦</span>会给将来的<span style="color: black;">守护</span>和系统效率带来很大的<span style="color: black;">消极</span>影响</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">那样</span>问题出<span style="color: black;">此刻</span>哪里呢?问题出<span style="color: black;">此刻</span> __autoload() 是全局函数只能定义一次 ,<span style="color: black;">不足</span>灵活,<span style="color: black;">因此</span>所有的类名与文件名对应的<span style="color: black;">规律</span>规则都要在一个函数里面实现,<span style="color: black;">导致</span>这个函数的臃肿。<span style="color: black;">那样</span><span style="color: black;">怎样</span>来<span style="color: black;">处理</span>这个问题呢?答案<span style="color: black;">便是</span><span style="color: black;">运用</span>一个 __autoload调用堆栈 ,<span style="color: black;">区别</span>的映射关系写到<span style="color: black;">区别</span>的 __autoload函数 中去,<span style="color: black;">而后</span>统一注册统一管理,这个<span style="color: black;">便是</span> PHP5 引入的</span><span style="color: black;"><span style="color: black;">SPL Autoload</span></span> 。</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">SPL Autoload</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">SPL是 Standard PHP Library(标准PHP库)的缩写。它是 PHP5 引入的一个扩展标准库,<span style="color: black;">包含</span> spl autoload <span style="color: black;">关联</span>的函数以及<span style="color: black;">各样</span>数据结构和迭代器的接口或类。spl autoload <span style="color: black;">关联</span>的函数<span style="color: black;">详细</span>可见 php中spl_autoload</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">// __autoload 函数</span>
<span style="color: black;">//</span>
<span style="color: black;">// function __autoload($class) {</span>
<span style="color: black;">// include classes/ . $class . .class.php;</span>
<span style="color: black;">// }</span>
<span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">my_autoloader</span><span style="color: black;">($class)</span> </span>{
<span style="color: black;">include</span> <span style="color: black;">classes/</span> . $class . <span style="color: black;">.class.php</span>;
}
spl_autoload_register(<span style="color: black;">my_autoloader</span>);
<span style="color: black;">// 定义的 autoload 函数在 class 里</span>
<span style="color: black;">// 静态<span style="color: black;">办法</span></span>
<span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">MyClass</span> </span>{
<span style="color: black;">public</span> <span style="color: black;">static</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">autoload</span><span style="color: black;">($className)</span> </span>{
<span style="color: black;">// ...</span>
}
}
spl_autoload_register(<span style="color: black;">array</span>(<span style="color: black;">MyClass</span>, <span style="color: black;">autoload</span>));<span style="color: black;">// 非静态<span style="color: black;">办法</span></span>
<span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">MyClass</span> </span>{
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">autoload</span><span style="color: black;">($className)</span> </span>{
<span style="color: black;">// ...</span>
}
}
$instance = <span style="color: black;">new</span> MyClass();
spl_autoload_register(<span style="color: black;">array</span>($instance,<span style="color: black;">autoload</span>));
</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">spl_autoload_register() <span style="color: black;">便是</span><span style="color: black;">咱们</span>上面所说的__autoload调用堆栈,<span style="color: black;">咱们</span><span style="color: black;">能够</span>向这个函数注册多个<span style="color: black;">咱们</span>自己的 autoload() 函数,当 PHP 找不到类名时,PHP就会调用这个堆栈,<span style="color: black;">而后</span>去调用自定义的 autoload() 函数,实现自动加载功能。<span style="color: black;">倘若</span><span style="color: black;">咱们</span>不向这个函数输入任何参数,<span style="color: black;">那样</span>就会默认注册 spl_autoload() 函数。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">PSR4 标准</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">PSR-4 规范了<span style="color: black;">怎样</span>指定文件路径从而自动加载类定义,<span style="color: black;">同期</span>规范了自动加载文件的位置。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">一个完整的类名需<span style="color: black;">拥有</span>以下结构</p><span style="color: black;">完整的类名必须要有一个顶级命名空间,被<span style="color: black;">叫作</span>为 "vendor namespace"</span><span style="color: black;">完整的类名<span style="color: black;">能够</span>有一个或多个子命名空间</span><span style="color: black;">完整的类名必须有一个<span style="color: black;">最后</span>的类名</span><span style="color: black;">完整的类名中任意一部分中的下滑线都是<span style="color: black;">无</span>特殊含义的</span><span style="color: black;">完整的类名<span style="color: black;">能够</span>由任意<span style="color: black;">体积</span>写字母<span style="color: black;">构成</span></span><span style="color: black;">所有类名都必须是<span style="color: black;">体积</span>写<span style="color: black;">敏锐</span>的</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">按照</span>完整的类名载入相应的文件</p><span style="color: black;">完整的类名中,去掉最前面的命名空间分隔符,前面连续的一个或多个命名空间和子命名空间,<span style="color: black;">做为</span>「命名空间前缀」,其必须与<span style="color: black;">最少</span>一个「文件基目录」相对应</span><span style="color: black;">紧接命名空间前缀后的子命名空间 必须 与相应的「文件基目录」相匹配,其中的命名空间分隔符将<span style="color: black;">做为</span>目录分隔符。</span><span style="color: black;">末尾的类名必须与对应的以 .php 为后缀的文件同名</span><span style="color: black;">自动加载器(autoloader)的实现<span style="color: black;">必定</span>不可抛出<span style="color: black;">反常</span>、<span style="color: black;">必定</span>不可触发任一级别的错误信息以及<span style="color: black;">不该</span>该有返回值</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">例子</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">PSR-4风格</span></p>类名:ZendAbc
命名空间前缀:Zend
文件基目录:<span style="color: black;">/usr/includes/Zend/</span>文件路径:/usr/includes/Zend/Abc.php类名:SymfonyCoreRequest
命名空间前缀:SymfonyCore
文件基目录:.<span style="color: black;">/vendor/Symfony/Core/</span>
文件路径:./vendor/Symfony/Core/Request.php<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">目录结构</span></p>-vendor/
<span style="color: black;">| -vendor_name/
|</span> <span style="color: black;">| -package_name/
|</span> <span style="color: black;">| |</span> -src/
<span style="color: black;">| |</span> <span style="color: black;">| |</span>-ClassName.php<span style="color: black;"># Vendor_Name\Package_Name\ClassName</span>
<span style="color: black;">| |</span> <span style="color: black;">| -tests/
|</span> <span style="color: black;">| |</span> <span style="color: black;">| -ClassNameTest.php # Vendor_Name\Package_Name\ClassNameTest</span>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">Composer 自动加载过程</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Composer 做了<span style="color: black;">那些</span>事情</p><span style="color: black;">你有一个项目依赖于若干个库。</span><span style="color: black;">其中<span style="color: black;">有些</span>库依赖于其他库。</span><span style="color: black;">你声明你所依赖的东西。</span><span style="color: black;">Composer 会找出哪个版本的包需要安装,并安装它们(将它们下载到你的项目中)。</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">例如,你正在创建一个项目,需要做<span style="color: black;">有些</span>单元测试。你决定<span style="color: black;">运用</span> phpunit 。为了将它添加到你的项目中,你所需要做的就是在 composer.json 文件里描述项目的依赖关系。</span></p>{
<span style="color: black;">"require"</span>: {
<span style="color: black;">"phpunit/phpunit"</span>:<span style="color: black;">"~6.0"</span>,
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">而后</span>在 composer require 之后<span style="color: black;">咱们</span>只要在项目里面直接 use phpunit 的类<span style="color: black;">就可</span><span style="color: black;">运用</span>。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">执行 composer require 时<span style="color: black;">出现</span>了什么</p><span style="color: black;">composer 会找到符合 PR4 规范的第三方库的源</span><span style="color: black;">将其加载到 vendor 目录下</span><span style="color: black;">初始化顶级域名的映射并写入到指定的文件里</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">如:</span><span style="color: black;"><span style="color: black;">PHPUnit\\Framework\\Assert => __DIR__ . /.. . /phpunit/phpunit/src/Framework/Assert.php</span></span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">写好一个 autoload 函数,并且注册到 spl_autoload_register()里</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">题外话:<span style="color: black;">此刻</span><span style="color: black;">非常多</span>框架都<span style="color: black;">已然</span>帮<span style="color: black;">咱们</span>写好了顶级域名映射了,<span style="color: black;">咱们</span>只需要在框架里面新建文件,在新建的文件中写好命名空间,就<span style="color: black;">能够</span>在任何<span style="color: black;">地区</span> use <span style="color: black;">咱们</span>的命名空间了</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">Composer 源码分析</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">下面<span style="color: black;">咱们</span><span style="color: black;">经过</span>对源码的分析来<span style="color: black;">瞧瞧</span> composer 是<span style="color: black;">怎样</span>实现 PSR4标准 的自动加载功能。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">非常多</span>框架在初始化的时候都会引入 composer 来<span style="color: black;">帮助</span>自动加载的,以 Laravel 为例,它入口文件 index.php <span style="color: black;">第1</span>句<span style="color: black;">便是</span>利用 composer 来实现自动加载功能</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">起步</span></p><span style="color: black;"><span style="color: black;"><?php</span>
define(<span style="color: black;">LARAVEL_START</span>, microtime(<span style="color: black;">true</span>));
<span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/../vendor/autoload.php</span>;</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">去 vendor 目录下的 autoload.php :</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">// autoload.php @generated by Composer</span>
<span style="color: black;">require_once</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/composer/autoload_real.php</span>;
<span style="color: black;">return</span> ComposerAutoloaderInit6ed409f9f3791196a1d5a1f407fb5184::getLoader();</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">这儿</span><span style="color: black;">便是</span> Composer 真正<span style="color: black;">起始</span>的<span style="color: black;">地区</span>了</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">Composer自动加载文件</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">Composer自动加载所用到的源文件。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">autoload_real.php: 自动加载功能的引导类composer 加载类的初始化(顶级命名空间与文件路径映射初始化)和注册(spl_autoload_register())ClassLoader.php : composer 加载类composer 自动加载功能的核心类autoload_static.php : 顶级命名空间初始化类用于给核心类初始化顶级命名空间autoload_classmap.php : 自动加载的最简单形式有完整的命名空间和文件目录的映射autoload_files.php : 用于加载全局函数的文件存放各个全局函数所在的文件路径名autoload_namespaces.php : 符合 PSR0 标准的自动加载文件存放着顶级命名空间与文件的映射autoload_psr4.php : 符合 PSR4 标准的自动加载文件存放着顶级命名空间与文件的映射</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">autoload_real 引导类</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">在 vendor 目录下的 autoload.php 文件中<span style="color: black;">咱们</span><span style="color: black;">能够</span>看出,程序<span style="color: black;">重点</span>调用了引导类的静态<span style="color: black;">办法</span> getLoader() ,<span style="color: black;">咱们</span>接着<span style="color: black;">瞧瞧</span>这个函数</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">自动加载引导类分为 5 个部分。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">第1</span>部分——单例</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">第1</span>部分很简单,<span style="color: black;">便是</span>个最经典的单例模式,自动加载类只能有一个。</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">if</span> (<span style="color: black;">null</span> !== <span style="color: black;">self</span>::$loader) {
<span style="color: black;">return</span> <span style="color: black;">self</span>::$loader;
}</span>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">第二部分——构造ClassLoader核心类</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">第二部分 new 一个自动加载的核心类对象。</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">/***********************<span style="color: black;">得到</span>自动加载核心类对象********************/</span>spl_autoload_register(<span style="color: black;">array</span>(<span style="color: black;">ComposerAutoloaderInit7b790917ce8899df9af8ed53631a1c29</span>, <span style="color: black;">loadClassLoader</span>), <span style="color: black;">true</span>, <span style="color: black;">true</span>
);
<span style="color: black;">self</span>::$loader = $loader = <span style="color: black;">new</span>\Composer\Autoload\ClassLoader();
spl_autoload_unregister(<span style="color: black;">array</span>(<span style="color: black;">ComposerAutoloaderInit7b790917ce8899df9af8ed53631a1c29</span>, <span style="color: black;">loadClassLoader</span>)
);
</span><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">public</span> <span style="color: black;">static</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">loadClassLoader</span><span style="color: black;">($class)</span>
</span>{
<span style="color: black;">if</span> (<span style="color: black;">Composer\Autoload\ClassLoader</span> === $class) {
<span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/ClassLoader.php</span>;
}
}</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">从程序里面<span style="color: black;">咱们</span><span style="color: black;">能够</span>看出,composer 先向 PHP 自动加载机制注册了一个函数,这个函数 require 了 ClassLoader 文件。成功 new 出该文件中核心类 ClassLoader() 后,又销毁了该函数</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">第三部分 —— 初始化核心类对象</h1><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">/***********************初始化自动加载核心类对象********************/</span>
$useStaticLoader = PHP_VERSION_ID >= <span style="color: black;">50600</span> && !defined(<span style="color: black;">HHVM_VERSION</span>);
<span style="color: black;">if</span> ($useStaticLoader) {
<span style="color: black;">require_once</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/autoload_static.php</span>;
call_user_func(
\Composer\Autoload\ComposerStaticInit7b790917ce8899df9af8ed53631a1c29::getInitializer($loader)
);
}<span style="color: black;">else</span>{
$map =<span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/autoload_namespaces.php</span>;
<span style="color: black;">foreach</span> ($map <span style="color: black;">as</span> $namespace => $path) {
$loader->set($namespace, $path);
}
$map = <span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/autoload_psr4.php</span>;
<span style="color: black;">foreach</span> ($map <span style="color: black;">as</span> $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = <span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/autoload_classmap.php</span>;
<span style="color: black;">if</span> ($classMap) {
$loader->addClassMap($classMap);
}
}
</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这一部分<span style="color: black;">便是</span>对自动加载类的初始化,<span style="color: black;">重点</span>是给自动加载核心类初始化顶级命名空间映射。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">初始化的<span style="color: black;">办法</span>有两种:</span></p><span style="color: black;"><span style="color: black;">运用</span> autoload_static 进行静态初始化</span><span style="color: black;">调用核心类接口初始化</span>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">autoload_static 静态初始化 ( PHP >= 5.6 )</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">静态初始化只支持 PHP5.6 以上版本并且不支持 HHVM 虚拟机。<span style="color: black;">咱们</span>深入 autoload_static.php 这个文件<span style="color: black;">发掘</span>这个文件定义了一个用于静态初始化的类,名字叫</span><span style="color: black;"><span style="color: black;">ComposerStaticInit7b790917ce8899df9af8ed53631a1c29</span></span>,仍然为了避免冲突而加了 hash 值。这个类很简单</p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ComposerStaticInit7b790917ce8899df9af8ed53631a1c29</span></span>{
<span style="color: black;">public</span> <span style="color: black;">static</span> $files = <span style="color: black;">array</span>(...);
<span style="color: black;">public</span> <span style="color: black;">static</span> $prefixLengthsPsr4 = <span style="color: black;">array</span>(...);
<span style="color: black;">public</span> <span style="color: black;">static</span> $prefixDirsPsr4 = <span style="color: black;">array</span>(...);
<span style="color: black;">public</span> <span style="color: black;">static</span> $prefixesPsr0 = <span style="color: black;">array</span>(...);
<span style="color: black;">public</span> <span style="color: black;">static</span> $classMap = <span style="color: black;">array</span> (...);
<span style="color: black;">public</span> <span style="color: black;">static</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">getInitializer</span><span style="color: black;">(ClassLoader $loader)</span>
</span>{
<span style="color: black;">return</span> \Closure::bind(<span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">()</span> <span style="color: black;">use</span> <span style="color: black;">($loader)</span> </span>{
$loader->prefixLengthsPsr4 = ComposerStaticInit6ed409f9f3791196a1d5a1f407fb5184::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit6ed409f9f3791196a1d5a1f407fb5184::$prefixDirsPsr4;
$loader->prefixesPsr0 = ComposerStaticInit6ed409f9f3791196a1d5a1f407fb5184::$prefixesPsr0;
$loader->classMap = ComposerStaticInit6ed409f9f3791196a1d5a1f407fb5184::$classMap;
},<span style="color: black;">null</span>, ClassLoader::class);
}
}</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这个静态初始化类的核心<span style="color: black;">便是</span> getInitializer() 函数,它将自己类中的顶级命名空间映射给了 ClassLoader 类。值得<span style="color: black;">重视</span>的是这个函数返回的是一个匿名函数,<span style="color: black;">为何</span>呢?<span style="color: black;">原由</span><span style="color: black;">便是</span> ClassLoader类 中的 prefixLengthsPsr4 、prefixDirsPsr4等等变量都是 private的。利用匿名函数的绑定功能就<span style="color: black;">能够</span>将这些 private 变量赋给 ClassLoader 类 里的成员变量。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">classMap(命名空间映射)</h1><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">public</span> <span style="color: black;">static</span> $classMap = <span style="color: black;">array</span> (
<span style="color: black;">App\\Console\\Kernel</span>
=> <span style="color: black;">__DIR__</span> . <span style="color: black;">/../..</span> . <span style="color: black;">/app/Console/Kernel.php</span>,
<span style="color: black;">App\\Exceptions\\Handler</span>
=> <span style="color: black;">__DIR__</span> . <span style="color: black;">/../..</span> . <span style="color: black;">/app/Exceptions/Handler.php</span>,
<span style="color: black;">App\\Http\\Controllers\\Auth\\ForgotPasswordController</span>
=> <span style="color: black;">__DIR__</span> . <span style="color: black;">/../..</span> . <span style="color: black;">/app/Http/Controllers/Auth/ForgotPasswordController.php</span>,
<span style="color: black;">App\\Http\\Controllers\\Auth\\LoginController</span>
=> <span style="color: black;">__DIR__</span> . <span style="color: black;">/../..</span> . <span style="color: black;">/app/Http/Controllers/Auth/LoginController.php</span>,
<span style="color: black;">App\\Http\\Controllers\\Auth\\RegisterController</span>
=> <span style="color: black;">__DIR__</span> . <span style="color: black;">/../..</span> . <span style="color: black;">/app/Http/Controllers/Auth/RegisterController.php</span>,
...)</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">直接命名空间全名与目录的映射,简单粗暴,<span style="color: black;">亦</span><span style="color: black;">引起</span>这个数组相当的大</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">PSR4 标准顶级命名空间映射数组</h1><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">public</span> <span style="color: black;">static</span> $prefixLengthsPsr4 = <span style="color: black;">array</span>(
<span style="color: black;">p</span> => <span style="color: black;">array</span> (
<span style="color: black;">phpDocumentor\\Reflection\\</span> => <span style="color: black;">25</span>,
),<span style="color: black;">S</span> => <span style="color: black;">array</span> (
<span style="color: black;">Symfony\\Polyfill\\Mbstring\\</span> => <span style="color: black;">26</span>,
<span style="color: black;">Symfony\\Component\\Yaml\\</span> => <span style="color: black;">23</span>,
<span style="color: black;">Symfony\\Component\\VarDumper\\</span> => <span style="color: black;">28</span>,
...
),
...);<span style="color: black;">public</span> <span style="color: black;">static</span> $prefixDirsPsr4 = <span style="color: black;">array</span> (
<span style="color: black;">phpDocumentor\\Reflection\\</span> => <span style="color: black;">array</span> (
<span style="color: black;">0</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/phpdocumentor/reflection-common/src</span>,
<span style="color: black;">1</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/phpdocumentor/type-resolver/src</span>,
<span style="color: black;">2</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/phpdocumentor/reflection-docblock/src</span>,
),
<span style="color: black;">Symfony\\Polyfill\\Mbstring\\</span> => <span style="color: black;">array</span> (
<span style="color: black;">0</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/symfony/polyfill-mbstring</span>,
),
<span style="color: black;">Symfony\\Component\\Yaml\\</span> => <span style="color: black;">array</span> (
<span style="color: black;">0</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/symfony/yaml</span>,
),
...)</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">PSR4 标准顶级命名空间映射用了两个数组,<span style="color: black;">第1</span>个是用命名空间<span style="color: black;">第1</span>个字母<span style="color: black;">做为</span>前缀索引,<span style="color: black;">而后</span>是 顶级命名空间,<span style="color: black;">然则</span><span style="color: black;">最后</span>并不是文件路径,而是 顶级命名空间的长度。<span style="color: black;">为何</span>呢?</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">由于</span> PSR4 标准是用顶级命名空间目录替换顶级命名空间,<span style="color: black;">因此</span><span style="color: black;">得到</span>顶级命名空间的长度很重要。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">详细</span>说明这些数组的<span style="color: black;">功效</span>:</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">假如<span style="color: black;">咱们</span>找 Symfony\Polyfill\Mbstring\example 这个命名空间,<span style="color: black;">经过</span>前缀索引和字符串匹配<span style="color: black;">咱们</span>得到了</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">Symfony\\Polyfill\\Mbstring\\</span> => <span style="color: black;">26</span>,</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这条记录,键是顶级命名空间,值是命名空间的长度。拿到顶级命名空间后去 $prefixDirsPsr4数组 获取它的映射目录数组:(<span style="color: black;">重视</span>映射目录可能不止一条)</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">Symfony\\Polyfill\\Mbstring\\</span> => <span style="color: black;">array</span> (
<span style="color: black;">0</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/symfony/polyfill-mbstring</span>,
)</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">而后</span><span style="color: black;">咱们</span>就<span style="color: black;">能够</span>将命名空间 Symfony\Polyfill\Mbstring\example 前26个字符替换成目录 </span><strong style="color: blue;"><span style="color: black;">DIR</span></strong><span style="color: black;">. /.. . /symfony/polyfill-mbstring ,<span style="color: black;">咱们</span>就得到了__DIR__ . /.. . /symfony/polyfill-mbstring/example.php,先验证磁盘上这个文件<span style="color: black;">是不是</span>存在,<span style="color: black;">倘若</span>不存在接着遍历。<span style="color: black;">倘若</span>遍历后<span style="color: black;">无</span>找到,则加载失败。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">ClassLoader 接口初始化( PHP < 5.6 )</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">倘若</span>PHP版本<span style="color: black;">小于</span> 5.6 <span style="color: black;">或</span><span style="color: black;">运用</span> HHVM 虚拟机环境,<span style="color: black;">那样</span>就要<span style="color: black;">运用</span>核心类的接口进行初始化。</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">// PSR0 标准</span>
$map = <span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/autoload_namespaces.php</span>;
<span style="color: black;">foreach</span> ($map <span style="color: black;">as</span>$namespace => $path) {
$loader->set($namespace, $path);
}<span style="color: black;">// PSR4 标准</span>
$map = <span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/autoload_psr4.php</span>;
<span style="color: black;">foreach</span> ($map <span style="color: black;">as</span>$namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap =<span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/autoload_classmap.php</span>;
<span style="color: black;">if</span>($classMap) {
$loader->addClassMap($classMap);
}</span>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">PSR4 标准的映射</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">autoload_psr4.php 的顶级命名空间映射</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">return</span> <span style="color: black;">array</span>(
<span style="color: black;">XdgBaseDir\\</span>
=> <span style="color: black;">array</span>($vendorDir . <span style="color: black;">/dnoegel/php-xdg-base-dir/src</span>),
<span style="color: black;">Webmozart\\Assert\\</span>
=> <span style="color: black;">array</span>($vendorDir . <span style="color: black;">/webmozart/assert/src</span>),
<span style="color: black;">TijsVerkoyen\\CssToInlineStyles\\</span>
=> <span style="color: black;">array</span>($vendorDir . <span style="color: black;">/tijsverkoyen/css-to-inline-styles/src</span>),
<span style="color: black;">Tests\\</span>=><span style="color: black;">array</span>($baseDir . <span style="color: black;">/tests</span>),
<span style="color: black;">Symfony\\Polyfill\\Mbstring\\</span>
=> <span style="color: black;">array</span>($vendorDir . <span style="color: black;">/symfony/polyfill-mbstring</span>),
...
)</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">PSR4 标准的初始化接口:</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">setPsr4</span><span style="color: black;">($prefix, $paths)</span>
</span>{
<span style="color: black;">if</span> (!$prefix) {
<span style="color: black;">$this</span>->fallbackDirsPsr4 = (<span style="color: black;">array</span>) $paths;
} <span style="color: black;">else</span> {
$length = strlen($prefix);
<span style="color: black;">if</span> (<span style="color: black;">\\</span>!== $prefix[$length -<span style="color: black;">1</span>]) {
<span style="color: black;">throw</span> <span style="color: black;">new</span> \InvalidArgumentException(
<span style="color: black;">"A non-empty PSR-4 prefix must end with a namespace separator."</span>
);
}
<span style="color: black;">$this</span>->prefixLengthsPsr4[$prefix[<span style="color: black;">0</span>]][$prefix] = $length;
<span style="color: black;">$this</span>->prefixDirsPsr4[$prefix] = (<span style="color: black;">array</span>) $paths;
}
}</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">总结下上面的顶级命名空间映射过程:</span></p>( 前缀 -> 顶级命名空间,顶级命名空间 -> 顶级命名空间长度 )
( 顶级命名空间 -> 目录 )<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这两个映射数组。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">命名空间映射</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">autoload_classmap</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">public</span> <span style="color: black;">static</span> $classMap = <span style="color: black;">array</span> (
<span style="color: black;">App\\Console\\Kernel</span>
=> <span style="color: black;">__DIR__</span> . <span style="color: black;">/../..</span> . <span style="color: black;">/app/Console/Kernel.php</span>,<span style="color: black;">App\\Exceptions\\Handler</span>
=> <span style="color: black;">__DIR__</span> . <span style="color: black;">/../..</span> . <span style="color: black;">/app/Exceptions/Handler.php</span>,
...
)</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">addClassMap</span></p><span style="color: black;"><span style="color: black;"><?php</span>
<span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">addClassMap</span><span style="color: black;">(array $classMap)</span>
</span>{<span style="color: black;">if</span> (<span style="color: black;">$this</span>->classMap) {
<span style="color: black;">$this</span>->classMap = array_merge(<span style="color: black;">$this</span>->classMap, $classMap);
} <span style="color: black;">else</span> {
<span style="color: black;">$this</span>->classMap = $classMap;
}
}</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">自动加载核心类 ClassLoader 的静态初始化到<span style="color: black;">这儿</span>就完<span style="color: black;">成为了</span>!</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">其实说是5部分,真正重要的就两部分——初始化与注册。初始化负责顶层命名空间的目录映射,注册负责实现顶层以下的命名空间映射规则</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">第四部分 —— 注册</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">讲完了 Composer 自动加载功能的<span style="color: black;">起步</span>与初始化,经过<span style="color: black;">起步</span>与初始化,自动加载核心类对象<span style="color: black;">已然</span><span style="color: black;">得到</span>了顶级命名空间与相应目录的映射,<span style="color: black;">亦</span><span style="color: black;">便是</span>说,<span style="color: black;">倘若</span>有命名空间</span><span style="color: black;"><span style="color: black;">App\Console\Kernel</span></span>,<span style="color: black;">咱们</span><span style="color: black;">已然</span><span style="color: black;">能够</span>找到它对应的类文件所在位置。<span style="color: black;">那样</span>,它是什么时候被触发去找的呢?</p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">此刻</span><span style="color: black;">咱们</span><span style="color: black;">起始</span>引导类的第四部分:注册自动加载核心类对象。<span style="color: black;">咱们</span>来<span style="color: black;">瞧瞧</span>核心类的 register() 函数:</span></p><span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">register</span><span style="color: black;">($prepend = false)</span>
</span>{
spl_autoload_register(<span style="color: black;">array</span>(<span style="color: black;">$this</span>, <span style="color: black;">loadClass</span>), <span style="color: black;">true</span>, $prepend);
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">其实奥秘都在自动加载核心类 ClassLoader 的 loadClass() 函数上:</span></p><span style="color: black;">public</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">loadClass</span><span style="color: black;">($class)</span>
</span>{
<span style="color: black;">if</span> ($file = <span style="color: black;">$this</span>->findFile($class)) {
includeFile($file);
<span style="color: black;">return</span> <span style="color: black;">true</span>;
}
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">这个函数负责<span style="color: black;">根据</span> PSR 标准将顶层命名空间以下的内容转为对应的目录,<span style="color: black;">亦</span><span style="color: black;">便是</span>上面所说的将 App\Console\Kernel 中 Console\Kernel 这一段转为目录,至于怎么转的在下面 “运行”的部分讲。核心类 ClassLoader 将 loadClass() 函数注册到PHP SPL中的 spl_autoload_register() 里面去。<span style="color: black;">这般</span>,每当PHP遇到一个不认识的命名空间的时候,PHP会自动调用注册到 spl_autoload_register 里面的 loadClass() 函数,<span style="color: black;">而后</span>找到命名空间对应的文件</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">全局函数的自动加载</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">Composer 不止<span style="color: black;">能够</span>自动加载命名空间,还<span style="color: black;">能够</span>加载全局函数。怎么实现的呢?把全局函数写到特定的文件里面去,在程序运行前挨个 require就行了。这个<span style="color: black;">便是</span> composer 自动加载的第五步,加载全局函数。</span></p><span style="color: black;">if</span>($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit7b790917ce8899df9af8ed53631a1c29::$files;
}<span style="color: black;">else</span> {
$includeFiles = <span style="color: black;">require</span> <span style="color: black;">__DIR__</span> . <span style="color: black;">/autoload_files.php</span>;
}
<span style="color: black;">foreach</span> ($includeFiles <span style="color: black;">as</span>$fileIdentifier => $file) {
composerRequire7b790917ce8899df9af8ed53631a1c29($fileIdentifier, $file);
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">跟核心类的初始化<span style="color: black;">同样</span>,全局函数自动加载<span style="color: black;">亦</span>分为两种:静态初始化和普通初始化,静态加载只支持PHP5.6以上并且不支持HHVM。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">静态初始化:</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">ComposerStaticInit7b790917ce8899df9af8ed53631a1c29::$files:</span></p><span style="color: black;">public</span> <span style="color: black;">static</span> $files = <span style="color: black;">array</span> (
<span style="color: black;">0e6d7bf4a5811bfa5cf40c5ccd6fae6a</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/symfony/polyfill-mbstring/bootstrap.php</span>,
<span style="color: black;">667aeda72477189d0494fecd327c3641</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/symfony/var-dumper/Resources/functions/dump.php</span>,
...
);<h1 style="color: black; text-align: left; margin-bottom: 10px;">普通初始化</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">autoload_files:</span></p>$vendorDir = dirname(dirname(<span style="color: black;">__FILE__</span>));
$baseDir = dirname($vendorDir);
<span style="color: black;">return</span> <span style="color: black;">array</span>(
<span style="color: black;">0e6d7bf4a5811bfa5cf40c5ccd6fae6a</span> => $vendorDir . <span style="color: black;">/symfony/polyfill-mbstring/bootstrap.php</span>,
<span style="color: black;">667aeda72477189d0494fecd327c3641</span> => $vendorDir . <span style="color: black;">/symfony/var-dumper/Resources/functions/dump.php</span>,
....
);<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">其实跟静态初始化区别不大。</span></p>
<h1 style="color: black; text-align: left; margin-bottom: 10px;">加载全局函数</h1><span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">ComposerAutoloaderInit7b790917ce8899df9af8ed53631a1c29</span></span>{
<span style="color: black;">public</span> <span style="color: black;">static</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">getLoader</span><span style="color: black;">()</span></span>{
...
<span style="color: black;">foreach</span> ($includeFiles <span style="color: black;">as</span>$fileIdentifier => $file) {
composerRequire7b790917ce8899df9af8ed53631a1c29($fileIdentifier, $file);
}
...
}
}<span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">composerRequire7b790917ce8899df9af8ed53631a1c29</span><span style="color: black;">($fileIdentifier, $file)</span>
</span>{
<span style="color: black;">if</span> (<span style="color: black;">empty</span>(\$GLOBALS[<span style="color: black;">__composer_autoload_files</span>][\$fileIdentifier])) {
<span style="color: black;">require</span> $file;
$GLOBALS[<span style="color: black;">__composer_autoload_files</span>][$fileIdentifier] =<span style="color: black;">true</span>;
}
}<h1 style="color: black; text-align: left; margin-bottom: 10px;">第五部分 —— 运行</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">到<span style="color: black;">这儿</span>,<span style="color: black;">最终</span>来到了核心的核心—— composer 自动加载的真相,命名空间<span style="color: black;">怎样</span><span style="color: black;">经过</span> composer 转为对应目录文件的奥秘就在这一章。 前面说过,ClassLoader 的 register() 函数将 loadClass() 函数注册到 PHP 的 SPL 函数堆栈中,每当 PHP 遇到不认识的命名空间时就会调用函数堆栈的<span style="color: black;">每一个</span>函数,直到加载命名空间成功。<span style="color: black;">因此</span> loadClass() 函数<span style="color: black;">便是</span>自动加载的关键了。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">看下 loadClass() 函数:</span></p><span style="color: black;">public</span> function loadClass($<span style="color: black;"><span style="color: black;">class</span>)</span>
{
<span style="color: black;">if</span> ($file = $<span style="color: black;">this</span>->findFile($<span style="color: black;"><span style="color: black;">class</span>)) </span>{
includeFile($file);
<span style="color: black;">return</span> <span style="color: black;">true</span>;
}
}
<span style="color: black;">public</span> function findFile($<span style="color: black;"><span style="color: black;">class</span>)</span>
{
<span style="color: black;">// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731</span>
<span style="color: black;">if</span> (<span style="color: black;">\\</span> == $<span style="color: black;"><span style="color: black;">class</span>) </span>{
$<span style="color: black;"><span style="color: black;">class</span> = <span style="color: black;">substr</span></span>($<span style="color: black;"><span style="color: black;">class</span>, <span style="color: black;">1);</span></span>
}
<span style="color: black;">// class map lookup</span>
<span style="color: black;">if</span> (isset($<span style="color: black;">this</span>->classMap[$<span style="color: black;"><span style="color: black;">class</span>])) </span>{
<span style="color: black;">return</span> $<span style="color: black;">this</span>->classMap[$<span style="color: black;"><span style="color: black;">class</span>];</span>
}
<span style="color: black;">if</span> ($<span style="color: black;">this</span>->classMapAuthoritative) {
<span style="color: black;">return</span> <span style="color: black;">false</span>;
}
$file = $<span style="color: black;">this</span>->findFileWithExtension($<span style="color: black;"><span style="color: black;">class</span>, <span style="color: black;">.php);</span></span>
<span style="color: black;">// Search for Hack files if we are running on HHVM</span>
<span style="color: black;">if</span> ($file === <span style="color: black;">null</span> && defined(<span style="color: black;">HHVM_VERSION</span>)) {
$file = $<span style="color: black;">this</span>->findFileWithExtension($<span style="color: black;"><span style="color: black;">class</span>, <span style="color: black;">.hh);</span></span>
}
<span style="color: black;">if</span> ($file === <span style="color: black;">null</span>) {
<span style="color: black;">// Remember that this class does not exist.</span>
<span style="color: black;">return</span> $<span style="color: black;">this</span>->classMap[$<span style="color: black;"><span style="color: black;">class</span>] = <span style="color: black;">false</span>;</span>
}
<span style="color: black;">return</span> $file;
}<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">咱们</span>看到 loadClass() ,<span style="color: black;">重点</span>调用 findFile() 函数。findFile() 在解析命名空间的时候<span style="color: black;">重点</span>分为两部分:classMap 和 findFileWithExtension() 函数。classMap 很简单,直接看命名空间<span style="color: black;">是不是</span>在映射数组中<span style="color: black;">就可</span>。麻烦的是 findFileWithExtension() 函数,这个函数<span style="color: black;">包括</span>了 PSR0 和 PSR4 标准的实现。还有个值得<span style="color: black;">咱们</span><span style="color: black;">重视</span>的是<span style="color: black;">查询</span>路径成功后 includeFile() 仍然是外面的函数,并不是 ClassLoader 的成员函数,原理跟上面<span style="color: black;">同样</span>,防止有用户写 $this 或 self。还有<span style="color: black;">便是</span><span style="color: black;">倘若</span>命名空间是以\开头的,要去掉\<span style="color: black;">而后</span>再匹配。</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">看下 findFileWithExtension 函数</span></p><span style="color: black;">private</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">findFileWithExtension</span><span style="color: black;">($class, $ext)</span>
</span>{
<span style="color: black;">// PSR-4 lookup</span>
$logicalPathPsr4 = strtr($class, <span style="color: black;">\\</span>, DIRECTORY_SEPARATOR) . $ext;
$first = $class[<span style="color: black;">0</span>];
<span style="color: black;">if</span> (<span style="color: black;">isset</span>(<span style="color: black;">$this</span>->prefixLengthsPsr4[$first])) {<span style="color: black;">foreach</span> (<span style="color: black;">$this</span>->prefixLengthsPsr4[$first] <span style="color: black;">as</span> $prefix => $length) {
<span style="color: black;">if</span> (<span style="color: black;">0</span> === strpos($class, $prefix)) {
<span style="color: black;">foreach</span> (<span style="color: black;">$this</span>->prefixDirsPsr4[$prefix]<span style="color: black;">as</span> $dir) {
<span style="color: black;">if</span> (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
<span style="color: black;">return</span>$file;
}
}
}
}
}<span style="color: black;">// PSR-4 fallback dirs</span>
<span style="color: black;">foreach</span> (<span style="color: black;">$this</span>->fallbackDirsPsr4 <span style="color: black;">as</span> $dir) {
<span style="color: black;">if</span> (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
<span style="color: black;">return</span>$file;
}
}<span style="color: black;">// PSR-0 lookup</span>
<span style="color: black;">if</span> (<span style="color: black;">false</span> !== $pos = strrpos($class, <span style="color: black;">\\</span>)) {
<span style="color: black;">// namespaced class name</span>
$logicalPathPsr0 = substr($logicalPathPsr4, <span style="color: black;">0</span>, $pos + <span style="color: black;">1</span>)
. strtr(substr($logicalPathPsr4, $pos +<span style="color: black;">1</span>), <span style="color: black;">_</span>, DIRECTORY_SEPARATOR);
} <span style="color: black;">else</span> {
<span style="color: black;">// PEAR-like class name</span>
$logicalPathPsr0 = strtr($class, <span style="color: black;">_</span>, DIRECTORY_SEPARATOR) . $ext;
}<span style="color: black;">if</span> (<span style="color: black;">isset</span>(<span style="color: black;">$this</span>->prefixesPsr0[$first])) {
<span style="color: black;">foreach</span> (<span style="color: black;">$this</span>->prefixesPsr0[$first] <span style="color: black;">as</span> $prefix => $dirs) {
<span style="color: black;">if</span> (<span style="color: black;">0</span> === strpos($class, $prefix)) {
<span style="color: black;">foreach</span> ($dirs <span style="color: black;">as</span> $dir) {
<span style="color: black;">if</span> (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
<span style="color: black;">return</span> $file;
}
}
}
}
}
<span style="color: black;">// PSR-0 fallback dirs</span>
<span style="color: black;">foreach</span> (<span style="color: black;">$this</span>->fallbackDirsPsr0 <span style="color: black;">as</span> $dir) {
<span style="color: black;">if</span> (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
<span style="color: black;">return</span> $file;
}
}
<span style="color: black;">// PSR-0 include paths.</span>
<span style="color: black;">if</span> (<span style="color: black;">$this</span>->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
<span style="color: black;">return</span> $file;
}
}<h1 style="color: black; text-align: left; margin-bottom: 10px;">最后小结</h1>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">咱们</span><span style="color: black;">经过</span>举例<span style="color: black;">来讲</span>下上面代码的流程:</span></p>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">倘若</span><span style="color: black;">咱们</span>在代码中写下 new phpDocumentor\Reflection\Element(),PHP 会<span style="color: black;">经过</span> SPL_autoload_register 调用 loadClass -> findFile -> findFileWithExtension。<span style="color: black;">过程</span>如下:</span></p><span style="color: black;">将 \ 转为文件分隔符/,加上后缀php,变成 $logicalPathPsr4, 即 phpDocumentor/Reflection//Element.php;</span><span style="color: black;">利用命名空间<span style="color: black;">第1</span>个字母p<span style="color: black;">做为</span>前缀索引搜索 prefixLengthsPsr4 数组,查到下面这个数组:</span> p<span style="color: black;"> =>
array (
</span>phpDocumentor\\Reflection\\<span style="color: black;"> => 25,
</span>phpDocumentor\\Fake\\<span style="color: black;"> => 19,
)</span><span style="color: black;">遍历这个数组,得到两个顶层命名空间 phpDocumentor\Reflection\ 和 phpDocumentor\Fake\</span><span style="color: black;">在这个数组中<span style="color: black;">查询</span> phpDocumentor\Reflection\Element,找出 phpDocumentor\Reflection\ 这个顶层命名空间并且长度为25。</span><span style="color: black;">在prefixDirsPsr4 映射数组中得到phpDocumentor\Reflection\ 的目录映射为:</span><span style="color: black;">phpDocumentor\\Reflection\\</span> =>
<span style="color: black;">array</span> (
<span style="color: black;">0</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/phpdocumentor/reflection-common/src</span>,
<span style="color: black;">1</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/phpdocumentor/type-resolver/src</span>,
<span style="color: black;">2</span> => <span style="color: black;">__DIR__</span> . <span style="color: black;">/..</span> . <span style="color: black;">/phpdocumentor/reflection-docblock/src</span>,
),<span style="color: black;">遍历这个映射数组,得到三个目录映射;</span>
<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">以上<span style="color: black;">便是</span> composer 自动加载的原理解析</span></p>
软文发布论坛开幕式圆满成功。 http://www.fok120.com 感谢你的精彩评论,为我的思绪打开了新的窗口。
页:
[1]