如何使用composer的autoload来自动加载自己编写的函数库与类库?
2017-05-02 17:26
866 查看
如何使用composer的autoload来自动加载自己编写的函数库与类库?
December 1, 2015composer的出现真是让人们眼前一亮,web开发从此变成了一件很『好玩』的事情,开发一个CMS就像在搭积木,从packagist中取出『积木』搭建在自己的代码中,一点一点搭建出一个属于自己的王国。
从此以后我基本就抛弃了require和include函数,一个项目中,这两个函数只可能出现一次,那就是
require '../vendor/autoload.php'。
那么,既然抛弃了传统的文件包含方法,我们使用所有类库都将用namespace和composer自带的autoload。可是,我们自己编写的函数库与类库,怎么用composer的方法来自动加载呢?
这就要从composer.json说起,我们需要通过修改这个文件来达到目的。
composer.json相当于是composer的配置文件,这个配置文件中有一个autoload段,比如我的一个项目:
其中又包含主要的两个选项: files 和 psr-4。
files就是需要composer自动帮我们加载的函数库(不含类),只要在后面的数组中将函数库的文件路径写入即可。
psr-4顾名思义,是一个基于psr-4(http://www.php-fig.org/psr/psr-4/)规则的类库自动加载对应关系,只要在其后的对象中,以
"命名空间": "路径"的方式写入自己的类库信息即可。
修改完成后,只要执行一下
composer update,即可完成对应工作。
之后,我们在项目中,用如下方式即可加载自定义类库:
new \Core\View();
composer的autoload将会自动包含"./core/view.php",并找到其中的Core命名空间下的View类。
我们来深挖一下,探索一下autoload的原理。
在我们修改完composer.json并执行update后,将会修改./vender/composer/autoload_psr4.php,比如我的某个项目,其中增加了这样一个对应关系:
这其实就是我刚刚在.json中添加的对应关系,他等于将.josn的配置文件,换成了php的形式。
那么我看到vendor/autoload.php:
<?php // autoload.php @generated by Composer require_once __DIR__ . '/composer' . '/autoload_real.php'; return ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326::getLoader();
其执行了一个自动生成的类
ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326中的getLoader方法。
跟进:
public static function getLoader() { if (null !== self::$loader) { return self::$loader; } spl_autoload_register(array('ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitff1d77c91141523097b07ee2acc23326', 'loadClassLoader')); $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } $loader->register(true); $includeFiles = require __DIR__ . '/autoload_files.php'; foreach ($includeFiles as $file) { composerRequireff1d77c91141523097b07ee2acc23326($file); } return $loader; }
可以明显看到,他将autoload_namespaces.php、autoload_psr4.php、autoload_classmap.php、autoload_files.php等几个配置文件包含了进来,并进行了相关处理(
setPsr4),最后注册(
register)。
那么我们跟进
register方法:
public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); }
这函数就一行,但简单明了,直接调用php自带的
spl_autoload_register函数,注册处理
__autoload的方法,也就是
loadClass方法。再跟进
loadClass方法:
public function loadClass($class) { if ($file = $this->findFile($class)) { includeFile($file); return true; } }
从函数名字就可以大概知道流程:如果存在$class对应的这个$file,则include进来。
那么进
findFile方法里看看吧:
public function findFile($class) { // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 if ('\\' == $class[0]) { $class = substr($class, 1); } // class map lookup if (isset($this->classMap[$class])) { return $this->classMap[$class]; } if ($this->classMapAuthoritative) { return false; } $file = $this->findFileWithExtension($class, '.php'); // Search for Hack files if we are running on HHVM if ($file === null && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); } if ($file === null) { // Remember that this class does not exist. return $this->classMap[$class] = false; } return $file; }
通过类名找文件,最终锁定在
findFileWithExtension方法中。
不过发现了一个小宝藏:在php5.3.0~5.3.2版本下,类名的第一个字符是\的小bug,也许以后挖漏洞会用上。
还是跟进
findFileWithExtension方法:
private function findFileWithExtension($class, $ext) { // PSR-4 lookup $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; $first = $class[0]; if (isset($this->prefixLengthsPsr4[$first])) { foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { if (0 === strpos($class, $prefix)) { foreach ($this->prefixDirsPsr4[$prefix] as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } // PSR-4 fallback dirs foreach ($this->fallbackDirsPsr4 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { return $file; } } // 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; } if (isset($this->prefixesPsr0[$first])) { foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { if (0 === strpos($class, $prefix)) { foreach ($dirs as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } } } } // PSR-0 fallback dirs foreach ($this->fallbackDirsPsr0 as $dir) { if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { return $file; } } // PSR-0 include paths. if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { return $file; } }
最终实现将
命名空间\类这样的类名,给转换成
目录名/类名.php这样的路径,并返回完整路径。
我发现composer的autoload与php自带的
spl_autoload,在包含文件时有一点小区别。那就是,
spl_autoload会查找.inc类型的文件名,但composer不会。
另外也可以发现,虽然配置文件的名字是autoload_psr4.php,但实际上psr0格式的自动加载也是支持的。二者最大的不同就是psr0中用"_"来代替目录间的"\"。
相关文章推荐
- 如何使用composer的autoload来自动加载自己编写的函数库与类库
- PHP使用__autoload()函数自动加载未定义的类
- PHP使用__autoload()函数自动加载未定义的类
- spl_autoload_register 注册自己的自动加载函数(__autoload())
- 自动加载函数流程之composer autoload_real.php
- PHP __autoload()函数(类文件自动加载规则)的使用方法
- 自动加载函数流程之composer autoload_real.php
- 自动加载函数流程之composer autoload_real.php
- [李景山php]每天laravel[004]-自动加载函数流程之composer autoload_real.php
- PHP5面向对像新特征:__autoload()函数自动加载类
- 如何编写一个自己的模块,并加载到内核中
- 使用spl_autoload_register实现自动加载
- access中使用自己编写的函数
- Java中如何使封装自己的类,建立并使用自己的类库?
- __autoload()函数自动加载类
- PHP autoload机制详解 自动加载函数
- autoload 自动加载模型 | 函数 | 类
- 如何使用 VB 编写自动反安装的程序?
- php中使用__autoload()自动加载未定义类的实现代码
- java 开发小记:如何使用 MyEclipse 开发自己的类库(mylib.jar)以及引用(使用)她