PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload)
举例来说,假设我们的项目想要使用 monolog 这个日志工具,就需要在composer.json里告诉composer我们需要它:
{ "require": { "monolog/monolog": "1.*" } }
之后执行:
php composer.phar install
好,现在安装完了,该怎么使用呢?composer自动生成了一个autoload文件,你只需要引用它
require '/path/to/vendor/autoload.php';
然后就可以非常方便的去使用第三方的类库了,是不是感觉很棒啊!对于我们需要的monolog,就可以这样用了:
use monolog\logger; use monolog\handler\streamhandler; // create a log channel $log = new logger('name'); $log->pushhandler(new streamhandler('/path/to/log/log_name.log', logger::warning)); // add records to the log $log->addwarning('foo'); $log->adderror('bar');
在这个过程中,composer做了什么呢?它生成了一个autoloader,再根据各个包自己的autoload配置,从而帮我们进行自动加载的工作。(如果对autoload这部分内容不太了解,可以看我之前的 一篇文章
)接下来让我们看看composer是怎么做的吧。
对于第三方包的自动加载,composer提供了四种方式的支持,分别是 psr-0和psr-4的自动加载(我的一篇文章也有介绍过它们),生成class-map,和直接包含files的方式。
psr-4是composer推荐使用的一种方式,因为它更易使用并能带来更简洁的目录结构。在composer.json里是这样进行配置的:
{ "autoload": { "psr-4": { "foo\\": "src/", } } }
key和value就定义出了namespace以及到相应path的映射。按照psr-4的规则,当试图自动加载 "foo\\bar\\baz" 这个class时,会去寻找 "src/bar/baz.php" 这个文件,如果它存在则进行加载。注意, "foo\\"
并没有出现在文件路径中,这是与psr-0不同的一点,如果psr-0有此配置,那么会去寻找
"src/foo/bar/baz.php"
这个文件。
另外注意psr-4和psr-0的配置里,"foo\\"结尾的命名空间分隔符必须加上并且进行转义,以防出现"foo"匹配到了"foobar"这样的意外发生。
在composer安装或更新完之后,psr-4的配置换被转换成namespace为key,dir path为value的map的形式,并写入生成的 vendor/composer/autoload_psr4.php 文件之中。
{ "autoload": { "psr-0": { "foo\\": "src/", } } }
最终这个配置也以map的形式写入生成的
vendor/composer/autoload_namespaces.php
文件之中。
class-map方式,则是通过配置指定的目录或文件,然后在composer安装或更新时,它会扫描指定目录下以.php或.inc结尾的文件中的class,生成class到指定file path的映射,并加入新生成的 vendor/composer/autoload_classmap.php 文件中,。
{ "autoload": { "classmap": ["src/", "lib/", "something.php"] } }
例如src/下有一个basecontroller类,那么在autoload_classmap.php文件中,就会生成这样的配置:
'basecontroller' => $basedir . '/src/basecontroller.php'
files方式,就是手动指定供直接加载的文件。比如说我们有一系列全局的helper functions,可以放到一个helper文件里然后直接进行加载
{ "autoload": { "files": ["src/mylibrary/functions.php"] } }
它会生成一个array,包含这些配置中指定的files,再写入新生成的
vendor/composer/autoload_files.php
文件中,以供autoloader直接进行加载。
下面来看看composer autoload的代码吧
<?php // autoload_real.php @generated by composer class composerautoloaderinit73612b48e6c3d0de8d56e03dece61d11 { private static $loader; public static function loadclassloader($class) { if ('composer\autoload\classloader' === $class) { require __dir__ . '/classloader.php'; } } public static function getloader() { if (null !== self::$loader) { return self::$loader; } spl_autoload_register(array('composerautoloaderinit73612b48e6c3d0de8d56e03dece61d11', 'loadclassloader'), true, true); self::$loader = $loader = new \composer\autoload\classloader(); spl_autoload_unregister(array('composerautoloaderinit73612b48e6c3d0de8d56e03dece61d11', 'loadclassloader')); $vendordir = dirname(__dir__); //verdor第三方类库提供者目录 $basedir = dirname($vendordir); //整个应用的目录 $includepaths = require __dir__ . '/include_paths.php'; array_push($includepaths, get_include_path()); set_include_path(join(path_separator, $includepaths)); $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) { composerrequire73612b48e6c3d0de8d56e03dece61d11($file); } return $loader; } } function composerrequire73612b48e6c3d0de8d56e03dece61d11($file) { require $file; }
首先初始化classloader类,然后依次用上面提到的4种加载方式来注册/直接加载,classloader的一些核心代码如下:
/** * @param array $classmap class to filename map */ public function addclassmap(array $classmap) { if ($this->classmap) { $this->classmap = array_merge($this->classmap, $classmap); } else { $this->classmap = $classmap; } } /** * registers a set of psr-0 directories for a given prefix, * replacing any others previously set for this prefix. * * @param string $prefix the prefix * @param array|string $paths the psr-0 base directories */ public function set($prefix, $paths) { if (!$prefix) { $this->fallbackdirspsr0 = (array) $paths; } else { $this->prefixespsr0[$prefix[0]][$prefix] = (array) $paths; } } /** * registers a set of psr-4 directories for a given namespace, * replacing any others previously set for this namespace. * * @param string $prefix the prefix/namespace, with trailing '\\' * @param array|string $paths the psr-4 base directories * * @throws \invalidargumentexception */ public function setpsr4($prefix, $paths) { if (!$prefix) { $this->fallbackdirspsr4 = (array) $paths; } else { $length = strlen($prefix); if ('\\' !== $prefix[$length - 1]) { throw new \invalidargumentexception("a non-empty psr-4 prefix must end with a namespace separator."); } $this->prefixlengthspsr4[$prefix[0]][$prefix] = $length; $this->prefixdirspsr4[$prefix] = (array) $paths; } } /** * registers this instance as an autoloader. * * @param bool $prepend whether to prepend the autoloader or not */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadclass'), true, $prepend); } /** * loads the given class or interface. * * @param string $class the name of the class * @return bool|null true if loaded, null otherwise */ public function loadclass($class) { if ($file = $this->findfile($class)) { includefile($file); return true; } } /** * finds the path to the file where the class is defined. * * @param string $class the name of the class * * @return string|false the path if found, false otherwise */ public function findfile($class) { //这是php5.3.0 - 5.3.2的一个bug 详见https://bugs.php.net/50731 if ('\\' == $class[0]) { $class = substr($class, 1); } // class map 方式的查找 if (isset($this->classmap[$class])) { return $this->classmap[$class]; } //psr-0/4方式的查找 $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; } 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; } } /** * scope isolated include. * * prevents access to $this/self from included files. */ function includefile($file) { include $file; }
推荐阅读
-
PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload)_PHP
-
PHP管理依赖(dependency)关系工具 Composer 安装与使用_PHP
-
PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload),dependencyautoload_PHP教程
-
PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload)
-
PHP管理依赖(dependency)关系工具 Composer 安装与使用
-
PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload)_PHP
-
PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload)_php技巧
-
PHP管理依赖(dependency)关系工具 Composer 安装与使用,dependencycomposer_PHP教程
-
PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload)
-
PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload),dependencyautoload