说明:本文主要以Laravel的容器类Container为例作简单说明Composer的自动加载机制。php
注:上篇文章基于Laravel Task-Scheduler定时发送邮件小程序聊到本打算接下来聊聊Laravel的设计模式,不过做者水平有限还需提升一段时间,故暂不先误人子弟了。此次先一块儿聊聊Composer的自动加载机制,并以Laravel的Container为例举例。html
一、初始化一个composer项目
在一个空目录下composer安装Laravel的容器Container包:laravel
composer require illuminate/container
而后在该目录下新建一个index.php文件,而后分析下Container类为什么能被实例化:json
<?php /** * Created by PhpStorm. * User: liuxiang * Date: 16/5/12 * Time: 19:59 */ require_once __DIR__.'/vendor/autoload.php'; $container = new Illuminate\Container\Container(); var_dump($container);
二、分析下composer加载类的流程
小程序
使用composer最大的好处是只需最开始require一个autoload.php文件,就能够new你所须要的类了,再也不须要传统的方式A文件内各类include跳到B文件又各类include,很是头疼。
segmentfault
Composer按照四种规范来加载文件:设计模式
psr-4api
psr-0(这种规范某些部分不是很优雅)数组
classmap(命名空间和文件路径的映射)app
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)来寻找对应的文件,看下文。
$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文件:
{ "require": { "illuminate/container": "^5.2" }, "autoload": { "psr-4": { "App\\": "app/" } } }
这里按照psr-4规范来,而后在项目根目录下使用命令:
composer install
发现autoload_psr4.php文件会多一个数组值:
return array( 'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'), 'Illuminate\\Container\\' => array($vendorDir . '/illuminate/container'), 'App\\' => array($baseDir . '/app'), );
而后在项目根目录下新建文件:
// app/Test/Test.php文件 <?php /** * Created by PhpStorm. * User: liuxiang * Date: 16/5/12 * Time: 21:52 */ namespace App\Test; class Test { public function index() { echo "This is a custom class which will be autoload by composer\n"; } }
在index.php文件中就能够实例化Test类并调用其对象函数了:
require_once __DIR__.'/vendor/autoload.php'; //$container = new Illuminate\Container\Container(); //var_dump($container); $test = new App\Test\Test(); $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的字符串和数组这些基础知识新开篇章,到时见。
欢迎关注Laravel-China。