说明:本文主要以Laravel的容器类Container为例做简单说明Composer的自动加载机制。
注:上篇文章基于Laravel Task-Scheduler定时发送邮件小程序聊到本打算接下来聊聊Laravel的设计模式,不过水平有限还需提高一段时间,故暂不先误人子弟了。这次先一起聊聊Composer的自动加载机制,并以Laravel的Container为例举例。
Composer的自动加载机制
1、初始化一个composer项目
在一个空目录下composer安装Laravel的容器Container包:
composer require illuminate/container
然后在该目录下新建一个index.php文件,然后分析下Container类为何能被实例化:
代码语言:javascript复制<?php
/**
* Created by PhpStorm.
* User: liuxiang
* Date: 16/5/12
* Time: 19:59
*/
require_once __DIR__.'/vendor/autoload.php';
$container = new IlluminateContainerContainer();
var_dump($container);
2、分析下composer加载类的流程
使用composer最大的好处是只需最开始require一个autoload.php文件,就可以new你所需要的类了,不再需要传统的方式A文件内各种include跳到B文件又各种include,非常头疼。
Composer按照四种规范来加载文件:
- psr-4
- psr-0(这种规范某些部分不是很优雅)
- classmap(命名空间和文件路径的映射)
- files
ComposerAutoloaderInit88609474169d8656473fa0223c682a7a这个类是composer为了防止类冲突搞了一个命名ComposerAutoloaderInit hash,不管咋样,require_once这个类后需要返回的是一个加载器$loader,而这个加载器经过四种规范遍历后,由null被填充为含有各种变量值的ClassLoader对象。
如果仔细观察autoload_classmap.php、autoload_namespaces.php、autoload_psr4.php和autoload_files(这里用了Container包是没有这个文件的,但Laravel整个项目是有的)文件后,这些都按照对应的规范返回要么命名空间与路径的映射,要么完整路径与某个哈希的映射。
从上图中能看出这个composer初始化路径的流程,重点是ClassLoader这个类的loadClass(class)这个方法,是通过spl_autoload_register这个PHP自动加载函数来注册到autoload函数栈中,最后返回一个loader加载器,而这个加载器是包含一些私有变量的,由于本Container包只包含IlluminateContainer和IlluminateContracts,且都是psr-4规范,则私有变量prefixLengthsPsr4和prefixDirsPsr4就包含了命名空间路径映射的数组值,其余私有变量就是空。
最后返回一个加载器loader,然后需要实例化一个类时,就会根据loadClass(class)来寻找对应的文件,看下文。
Container类的实例化过程
loader这个加载器已经有了,而且它还是塞满了各种私有变量,这些变量值为命名空间路径映射或者路径哈希映射等,当然这里只有命名空间路径映射这种psr-4规范了。也就是说,一句require_once这个autoload.php文件后就拿到了一个饱满的loader,然后现在开始new一个类Container,那如何找到这个Container.php文件路径的呢?
从第二个序列图就可看出,首先调用ClassLoader中的loadClass()这个函数来找文件路径,传入的class变量值是“IlluminateContainerContainer”这个字符串,然后又继续调用findFile(class)函数先做classmap查找,然后进入findFileWithExtension(class,'.php')中做psr-4/psr-0查找,其实就是搜寻这些私有变量值,比如这里Container类是psr-4规范,那就去prefixLengthsPsr4/prefixDirsPsr4这些psr-4私有变量中查找文件绝对路径,返回一个file,再include下就等于这个类可以被实例化了。当然,这里Container.php文件绝对路径被找到后,发现它还实现了一个接口ContractContainer,那就再去同样方式找这个文件:psr-4根据命名空间IlluminateContractsContainerContainer去找这个接口对应的绝对路径。
总之,当实例化一个类时,这个$loader就去根据四种规范找该文件的绝对路径,如果这个类还有继承或实现关系,那就递归找。
自定义一个类文件
现在自己写一个类文件,当实例化的时候,然后让composer来自动加载,怎么做?
修改composer.json文件:
代码语言:javascript复制{
"require": {
"illuminate/container": "^5.2"
},
"autoload": {
"psr-4": {
"App\": "app/"
}
}
}
这里按照psr-4规范来,然后在项目根目录下使用命令:
代码语言:javascript复制composer install
发现autoload_psr4.php文件会多一个数组值:
代码语言:javascript复制return array(
'Illuminate\Contracts\' => array($vendorDir . '/illuminate/contracts'),
'Illuminate\Container\' => array($vendorDir . '/illuminate/container'),
'App\' => array($baseDir . '/app'),
);
然后在项目根目录下新建文件:
代码语言:javascript复制// app/Test/Test.php文件
<?php
/**
* Created by PhpStorm.
* User: liuxiang
* Date: 16/5/12
* Time: 21:52
*/
namespace AppTest;
class Test
{
public function index()
{
echo "This is a custom class which will be autoload by composern";
}
}
在index.php文件中就可以实例化Test类并调用其对象函数了:
代码语言:javascript复制require_once __DIR__.'/vendor/autoload.php';
//$container = new IlluminateContainerContainer();
//var_dump($container);
$test = new AppTestTest();
$test->index();
终端执行输出:
通过在Composer中注册下,Composer就可以帮我们找到类文件,就不需要自己各种include,只需开始一句require_once就行,真的很方便。
One More Thing...
配置Xdebug。
强烈推荐在自己的IDE中配置Xdebug,作者使用PHPStorm,并配置了Xdebug,这会提高阅读源码的效率。具体操作流程可以谷歌文档,应该很多,Netbeans或者ZendStudio应该也有很多配置文档。如果有配置不成功的,可以在本文留言下问题,作者会尽量解答。
PlantUML插件的安装。
本文UML序列图用的是PlantUML这个插件来做的,还比较好用,推荐下。可以在PHPStorm插件库里搜UML就行,然后新建一个文件时会发现多了好几个UML选项,并且还有一个PlantUML窗口:
关于这个PlantUML有一篇文章还挺好:Create Beautiful UML Diagrams in Minutes from the JetBrains IDE,还有它的官网(就是有各种广告):PlantUML。
总结:本文主要聊了下Composer的加载流程,并以Laravel的IlluminateContainer包为例具体说明实例化类时是如何找到其文件的,并讲述如何自定义自己的类并通过Composer来注册和加载。下次还想结合PHP的字符串和数组这些基础知识新开篇章,到时见。