龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > web编程 > php编程 >

PHP管理依赖(dependency)关系工具 Composer的自动加载(autoload)

时间:2014-08-19 15:25来源:网络整理 作者:网络 点击:
分享到:
Composer 是PHP的一个包依赖管理工具,类似Ruby中的RubyGems或者Node中的NPM,它并非官方,但现在已经非常流行。此文并不介绍如何使用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;
}

精彩图集

赞助商链接