Composer概述及其自动加载探秘

composer概述

一开始,最吸引我的当属 Composer 了,因为之前从没用过 Composer 。

Composer 是PHP中用来管理依赖关系的工具,你只需在自己的项目中声明所依赖的外部工具库,Composer就会帮你安装这些依赖的库文件。运行 Composer 需要 PHP 5.3.2+ 以上版本。

使用composer

第一步,声明依赖关系。比方说,你正在创建的一个项目需要一个库来做日志记录。你决定使用 monolog。为了将它添加到你的项目中,你所需要做的就是创建一个 composer.json 文件,其中描述了项目的依赖关系。

{
    "require": {
        "monolog/monolog": "1.2.*"
    }
}

第二步,使用composer。在项目根目录,执行安装命令,执行完毕后,monolog就会被下载到vendor/monolog/monolog 目录。

$ php composer.phar install

第三步,类的自动加载。除了库的下载,Composer 还准备了一个自动加载文件,它可以加载 Composer 下载的库中所有的类文件。使用它,你只需要将下面这行代码添加到你项目的引导文件中:

require 'vendor/autoload.php';

这使得你可以很容易的使用第三方代码。例如:如果你的项目依赖 monolog,你就可以像这样开始使用这个类库,并且他们将被自动加载。

$log = new Monolog\Logger('name');
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));

$log->addWarning('Foo');

 

Composer 自动加载探秘

在现实世界中使用工具时,如果理解了工具的工作原理,使用起来就会更加有底气。对于一个第一次接触laravel,且是第一次接触 composer 的新手来说,如果理解Composer 是如何工作的,使用起来将会更加自如。

我的理解是,composer 根据声明的依赖关系,从相关库的 源 下载代码文件,并根据依赖关系在 Composer 目录下生成供类自动加载的 PHP 脚本,使用的时候,项目开始处引入 “/vendor/autoload.php” 文件,就可以直接实例化这些第三方类库中的类了。那么,Composer 是如何实现类的自动加载的呢?接下来,我们从 laravel 的入口文件开始顺藤摸瓜往里跟进,来一睹 Composer 自动加载的奥妙。

1.代码清单 laravel/public/index.php

#laravel/public/index.php

require
__DIR__.'/../bootstrap/autoload.php'; $app = require_once __DIR__.'/../bootstrap/start.php'; $app->run();

第一行先是引入了 laravel/bootstrap/autoload.php,不做解释,打开该文件

 

2.代码清单 laravel/bootstrap/autoload.php

define('LARAVEL_START', microtime(true));
require __DIR__.'/../vendor/autoload.php';
if (file_exists($compiled = __DIR__.'/compiled.php'))
{
    require $compiled;
}
Patchwork\Utf8\Bootup::initMbstring();

第一行定义了程序开始执行的时间点。紧接着第二行,引入了 laravel/vendor/autoload.php

第七行,前面说过,引入Composer的autoload.php之后就可以直接使用第三方类库中的类了,这里就是直接使用的 Bootup 类。下面来看看 /vendor/autoload.php 到底做了什么。

 

3.代码清单 laravel/vendor/autoload.php

1 // autoload.php @generated by Composer
2 
3 require_once __DIR__ . '/composer' . '/autoload_real.php';
4 
5 return ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256::getLoader();

到这里,马上就进入自动加在的大门了。

这个文件很简单,第5行的函数名是不是看的一头雾水?别被吓到了,他就是个类名而已。这个类是在第3行引入的文件 laravel/vendor/composer/autoload_real.php 里头声明的,接下来打开该文件看 getLoader();

 

4.代码清单laravel/vendor/composer/autoload_real.php

 1 <?php
 2 
 3 // autoload_real.php @generated by Composer
 4 
 5 class ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256
 6 {
 7     private static $loader;
 8 
 9     public static function loadClassLoader($class)
10     {
11         if ('Composer\Autoload\ClassLoader' === $class) {
12             require __DIR__ . '/ClassLoader.php';
13         }
14     }
15 
16 
17     public static function getLoader()
18     {
19         if (null !== self::$loader) {
20             return self::$loader;
21         }
22 
23         spl_autoload_register(array('ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256', 'loadClassLoader'), true, true);
24         self::$loader = $loader = new \Composer\Autoload\ClassLoader();
25         spl_autoload_unregister(array('ComposerAutoloaderInit9b2a1b1cf01c9a870ab98748dc5f1256', 'loadClassLoader'));
26 
27         $vendorDir = dirname(__DIR__);        
28         $baseDir = dirname($vendorDir);
29 
30         $includePaths = require __DIR__ . '/include_paths.php';        
31 
32         array_push($includePaths, get_include_path());
33         set_include_path(join(PATH_SEPARATOR, $includePaths));
34 
35 
36         $map = require __DIR__ . '/autoload_namespaces.php';
37         foreach ($map as $namespace => $path) {
38             $loader->set($namespace, $path);
39         }
40 
41         $map = require __DIR__ . '/autoload_psr4.php';
42         foreach ($map as $namespace => $path) {
43             $loader->setPsr4($namespace, $path);
44         }
45 
46         $classMap = require __DIR__ . '/autoload_classmap.php';
47         if ($classMap) {
48             $loader->addClassMap($classMap);
49         }
50         
51 
52         $loader->register(true);
53 
54         $includeFiles = require __DIR__ . '/autoload_files.php';
55         foreach ($includeFiles as $file) {
56             composerRequire9b2a1b1cf01c9a870ab98748dc5f1256($file);
57         }
58 
59         return $loader;
60     }
61 }
62 
63 function composerRequire9b2a1b1cf01c9a870ab98748dc5f1256($file)及 $loader->addClassMap()
64 {
65     require $file;
66 }

第17行,getLoader()中先是判断当前类中的 $loader 值,如果不是 null 就返回,这个可以略过。接着实例化了 ClassLoader 类给 $loader ,laravel/vendor/composer/ClassLoader.php

这里引入了几个文件,这些文件是由composer自动生成的,当依赖关系发生改变时不需要修改这些脚本,运行composer重新生成即可。

laravel/vendor/composer/autoloade_namespace.php

laravel/vendor/composer/autoloade_prs4.php

laravel/vendor/composer/autoloade_classmap.php

laravel/vendor/composer/autoloade_files.php

 

在设置完一堆的 path 信息后,执行了$loader->set()和 $loader->setPsr4()及$loader->addClassMap(),然后 进行了$loader->register(true);现在我们一个个来看。

 

5.代码清单laravel/vendor/composer/ClassLoader.php

  1 <?php
  2 
  3 /*
  4  * This file is part of Composer.
  5  *
  6  * (c) Nils Adermann <naderman@naderman.de>
  7  *     Jordi Boggiano <j.boggiano@seld.be>
  8  *
  9  * For the full copyright and license information, please view the LICENSE
 10  * file that was distributed with this source code.
 11  */
 12 
 13 namespace Composer\Autoload;
 14 
 15 /**
 16  * ClassLoader implements a PSR-0 class loader
 17  *
 18  * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md
 19  *
 20  *     $loader = new \Composer\Autoload\ClassLoader();
 21  *
 22  *     // register classes with namespaces
 23  *     $loader->add('Symfony\Component', __DIR__.'/component');
 24  *     $loader->add('Symfony',           __DIR__.'/framework');
 25  *
 26  *     // activate the autoloader
 27  *     $loader->register();
 28  *
 29  *     // to enable searching the include path (eg. for PEAR packages)
 30  *     $loader->setUseIncludePath(true);
 31  *
 32  * In this example, if you try to use a class in the Symfony\Component
 33  * namespace or one of its children (Symfony\Component\Console for instance),
 34  * the autoloader will first look for the class under the component/
 35  * directory, and it will then fallback to the framework/ directory if not
 36  * found before giving up.
 37  *
 38  * This class is loosely based on the Symfony UniversalClassLoader.
 39  *
 40  * @author Fabien Potencier <fabien@symfony.com>
 41  * @author Jordi Boggiano <j.boggiano@seld.be>
 42  */
 43 class ClassLoader
 44 {
 45     // PSR-4
 46     private $prefixLengthsPsr4 = array();
 47     private $prefixDirsPsr4 = array();
 48     private $fallbackDirsPsr4 = array();
 49 
 50     // PSR-0
 51     private $prefixesPsr0 = array();
 52     private $fallbackDirsPsr0 = array();
 53 
 54     private $useIncludePath = false;
 55     private $classMap = array();
 56 
 57     public function getPrefixes()
 58     {
 59         return call_user_func_array('array_merge', $this->prefixesPsr0);
 60     }
 61 
 62     public function getPrefixesPsr4()
 63     {
 64         return $this->prefixDirsPsr4;
 65     }
 66 
 67     public function getFallbackDirs()
 68     {
 69         return $this->fallbackDirsPsr0;
 70     }
 71 
 72     public function getFallbackDirsPsr4()
 73     {
 74         return $this->fallbackDirsPsr4;
 75     }
 76 
 77     public function getClassMap()
 78     {
 79         return $this->classMap;
 80     }
 81 
 82     /**
 83      * @param array $classMap Class to filename map
 84      */
 85     public function addClassMap(array $classMap)
 86     {
 87         if ($this->classMap) {
 88             $this->classMap = array_merge($this->classMap, $classMap);
 89         } else {
 90             $this->classMap = $classMap;
 91         }
 92     }
 93 
 94     /**
 95      * Registers a set of PSR-0 directories for a given prefix, either
 96      * appending or prepending to the ones previously set for this prefix.
 97      *
 98      * @param string       $prefix  The prefix
 99      * @param array|string $paths   The PSR-0 root directories
100      * @param bool         $prepend Whether to prepend the directories
101      */
102     public function add($prefix, $paths, $prepend = false)
103     {
104         if (!$prefix) {
105             if ($prepend) {
106                 $this->fallbackDirsPsr0 = array_merge(
107                     (array) $paths,
108                     $this->fallbackDirsPsr0
109                 );
110             } else {
111                 $this->fallbackDirsPsr0 = array_merge(
112                     $this->fallbackDirsPsr0,
113                     (array) $paths
114                 );
115             }
116 
117             return;
118         }
119 
120         $first = $prefix[0];
121         if (!isset($this->prefixesPsr0[$first][$prefix])) {
122             $this->prefixesPsr0[$first][$prefix] = (array) $paths;
123 
124             return;
125         }
126         if ($prepend) {
127             $this->prefixesPsr0[$first][$prefix] = array_merge(
128                 (array) $paths,
129                 $this->prefixesPsr0[$first][$prefix]
130             );
131         } else {
132             $this->prefixesPsr0[$first][$prefix] = array_merge(
133                 $this->prefixesPsr0[$first][$prefix],
134                 (array) $paths
135             );
136         }
137     }
138 
139     /**
140      * Registers a set of PSR-4 directories for a given namespace, either
141      * appending or prepending to the ones previously set for this namespace.
142      *
143      * @param string       $prefix  The prefix/namespace, with trailing '\\'
144      * @param array|string $paths   The PSR-0 base directories
145      * @param bool         $prepend Whether to prepend the directories
146      */
147     public function addPsr4($prefix, $paths, $prepend = false)
148     {
149         if (!$prefix) {
150             // Register directories for the root namespace.
151             if ($prepend) {
152                 $this->fallbackDirsPsr4 = array_merge(
153                     (array) $paths,
154                     $this->fallbackDirsPsr4
155                 );
156             } else {
157                 $this->fallbackDirsPsr4 = array_merge(
158                     $this->fallbackDirsPsr4,
159                     (array) $paths
160                 );
161             }
162         } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
163             // Register directories for a new namespace.
164             $length = strlen($prefix);
165             if ('\\' !== $prefix[$length - 1]) {
166                 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
167             }
168             $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
169             $this->prefixDirsPsr4[$prefix] = (array) $paths;
170         } elseif ($prepend) {
171             // Prepend directories for an already registered namespace.
172             $this->prefixDirsPsr4[$prefix] = array_merge(
173                 (array) $paths,
174                 $this->prefixDirsPsr4[$prefix]
175             );
176         } else {
177             // Append directories for an already registered namespace.
178             $this->prefixDirsPsr4[$prefix] = array_merge(
179                 $this->prefixDirsPsr4[$prefix],
180                 (array) $paths
181             );
182         }
183     }
184 
185     /**
186      * Registers a set of PSR-0 directories for a given prefix,
187      * replacing any others previously set for this prefix.
188      *
189      * @param string       $prefix The prefix
190      * @param array|string $paths  The PSR-0 base directories
191      */
192     public function set($prefix, $paths)
193     {
194         if (!$prefix) {
195             $this->fallbackDirsPsr0 = (array) $paths;
196         } else {
197             $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
198         }
199     }
200 
201     /**
202      * Registers a set of PSR-4 directories for a given namespace,
203      * replacing any others previously set for this namespace.
204      *
205      * @param string       $prefix  The prefix/namespace, with trailing '\\'
206      * @param array|string $paths   The PSR-4 base directories
207      */
208     public function setPsr4($prefix, $paths) {
209         if (!$prefix) {
210             $this->fallbackDirsPsr4 = (array) $paths;
211         } else {
212             $length = strlen($prefix);
213             if ('\\' !== $prefix[$length - 1]) {
214                 throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
215             }
216             $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
217             $this->prefixDirsPsr4[$prefix] = (array) $paths;
218         }
219     }
220 
221     /**
222      * Turns on searching the include path for class files.
223      *
224      * @param bool $useIncludePath
225      */
226     public function setUseIncludePath($useIncludePath)
227     {
228         $this->useIncludePath = $useIncludePath;
229     }
230 
231     /**
232      * Can be used to check if the autoloader uses the include path to check
233      * for classes.
234      *
235      * @return bool
236      */
237     public function getUseIncludePath()
238     {
239         return $this->useIncludePath;
240     }
241 
242     /**
243      * Registers this instance as an autoloader.
244      *
245      * @param bool $prepend Whether to prepend the autoloader or not
246      */
247     public function register($prepend = false)
248     {
249         spl_autoload_register(array($this, 'loadClass'), true, $prepend);
250     }
251 
252     /**
253      * Unregisters this instance as an autoloader.
254      */
255     public function unregister()
256     {
257         spl_autoload_unregister(array($this, 'loadClass'));
258     }
259 
260     /**
261      * Loads the given class or interface.
262      *
263      * @param  string    $class The name of the class
264      * @return bool|null True if loaded, null otherwise
265      */
266     public function loadClass($class)
267     {
268         if ($file = $this->findFile($class)) {
269             includeFile($file);
270 
271             return true;
272         }
273     }
274 
275     /**
276      * Finds the path to the file where the class is defined.
277      *
278      * @param string $class The name of the class
279      *
280      * @return string|false The path if found, false otherwise
281      */
282     public function findFile($class)
283     {
284         // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
285         if ('\\' == $class[0]) {
286             $class = substr($class, 1);
287         }
288 
289         // class map lookup
290         if (isset($this->classMap[$class])) {
291             return $this->classMap[$class];
292         }
293 
294         $file = $this->findFileWithExtension($class, '.php');
295 
296         // Search for Hack files if we are running on HHVM
297         if ($file === null && defined('HHVM_VERSION')) {
298             $file = $this->findFileWithExtension($class, '.hh');
299         }
300 
301         if ($file === null) {
302             // Remember that this class does not exist.
303             return $this->classMap[$class] = false;
304         }
305 
306         return $file;
307     }
308 
309     private function findFileWithExtension($class, $ext)
310     {
311         // PSR-4 lookup
312         $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
313 
314         $first = $class[0];
315         if (isset($this->prefixLengthsPsr4[$first])) {
316             foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
317                 if (0 === strpos($class, $prefix)) {
318                     foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
319                         if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
320                             return $file;
321                         }
322                     }
323                 }
324             }
325         }
326 
327         // PSR-4 fallback dirs
328         foreach ($this->fallbackDirsPsr4 as $dir) {
329             if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
330                 return $file;
331             }
332         }
333 
334         // PSR-0 lookup
335         if (false !== $pos = strrpos($class, '\\')) {
336             // namespaced class name
337             $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
338                 . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
339         } else {
340             // PEAR-like class name
341             $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
342         }
343 
344         if (isset($this->prefixesPsr0[$first])) {
345             foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
346                 if (0 === strpos($class, $prefix)) {
347                     foreach ($dirs as $dir) {
348                         if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
349                             return $file;
350                         }
351                     }
352                 }
353             }
354         }
355 
356         // PSR-0 fallback dirs
357         foreach ($this->fallbackDirsPsr0 as $dir) {
358             if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
359                 return $file;
360             }
361         }
362 
363         // PSR-0 include paths.
364         if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
365             return $file;
366         }
367     }
368 }
369 
370 /**
371  * Scope isolated include.
372  *
373  * Prevents access to $this/self from included files.
374  */
375 function includeFile($file)
376 {
377     include $file;
378 }
View Code

$loader->set($namespace, $path);Psr0标准

设置命名空间对应的路径,以便于随后自动加载相关类文件。

 

$loader->setPsr4($namespace, $path);Psr4标准

设置命名空间对应的路径,以便于随后自动加载相关类文件。

 

$loader->addClassMap($classMap);

设置类文件路径与类名的对应关系,以便于随后自动加载相关类文件。

 

$loader->register(true);

public function register($prepend = false)
{
    spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}

这里设置了 欲注册的自动装载函数 $this->loadClass(),关于 spl_autoload_register 和 spl_autoload_unregister 的更多信息随后会有专门的解释。现在打开loadClass()的定义

public function loadClass($class)
{
    if ($file = $this->findFile($class)) {
        includeFile($file);
        return true;
    }
}

这里有个 findFile() 函数,如果相关类的声明所在文件的路径找到了,就包含并运行该文件,然后返回 true 。接着打开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];
    }

    $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($class, '.php') 继续查找类文件路径信息,findFileWithExtension的定义后面将会列出。

如果仍未找到类的文件路径信息,返回值为 null 且定义了 HHVM_VERSION 信息,则用“.hh”后缀继续查找类文件信息。

HHVM_VERSION 是 HHVM版本信息? HHVM 是 Facebook 开发的高性能 PHP 虚拟机,宣称比官方的快9倍。

如果仍未找到,则返回 false 。Remember that this class does not exist.(这句注释很有喜感?哈哈)

 

代码清单 findFileWithExtension()

 1 private function findFileWithExtension($class, $ext)
 2 {
 3     // PSR-4 lookup
 4     $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
 5 
 6     $first = $class[0];
 7     if (isset($this->prefixLengthsPsr4[$first])) {
 8         foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
 9             if (0 === strpos($class, $prefix)) {
10                 foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
11                     if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
12                         return $file;
13                     }
14                 }
15             }
16         }
17     }
18 
19     // PSR-4 fallback dirs
20     foreach ($this->fallbackDirsPsr4 as $dir) {
21         if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
22             return $file;
23         }
24     }
25 
26     // PSR-0 lookup
27     if (false !== $pos = strrpos($class, '\\')) {
28         // namespaced class name
29         $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
30             . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
31     } else {
32         // PEAR-like class name
33         $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
34     }
35 
36     if (isset($this->prefixesPsr0[$first])) {
37         foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
38             if (0 === strpos($class, $prefix)) {
39                 foreach ($dirs as $dir) {
40                     if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
41                         return $file;
42                     }
43                 }
44             }
45         }
46     }
47 
48     // PSR-0 fallback dirs
49     foreach ($this->fallbackDirsPsr0 as $dir) {
50         if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
51             return $file;
52         }
53     }
54 
55     // PSR-0 include paths.
56     if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
57         return $file;
58     }
59 }

该函数唯一的目的就是根据刚才通过$loader->set($namespace, $path)和$loader->setPsr4($namespace, $path)方法设置的信息找出类文件的路径信息。

接下来,我们回到代码清单laravel/vendor/composer/autoload_real.php ,为精简篇幅,这里只贴出片段(续上节的 $loader->register(true))。

        $loader->register(true);

        $includeFiles = require __DIR__ . '/autoload_files.php';
        foreach ($includeFiles as $file) {
            composerRequire9b2a1b1cf01c9a870ab98748dc5f1256($file);
        }

        return $loader;
    }
}

function composerRequire9b2a1b1cf01c9a870ab98748dc5f1256($file)
{
    require $file;
}

在经历了一番长途跋涉后,终于从 laravel/vendor/composer/ClassLoader.php 中出来了。继 $loader->register(true) 之后,又引入了laravel/vendor/composer/autoload_files.php,相比之下,这个文件要简单得多,只是个数组,列出了几个文件路径。

// autoload_files.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    $vendorDir . '/ircmaxell/password-compat/lib/password.php',
    $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
    $vendorDir . '/phpseclib/phpseclib/phpseclib/Crypt/Random.php',
    $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
);

接着就是用 composerRequire9b2a1b1cf01c9a870ab98748dc5f1256() 函数 包含并运行 这几个文件。这四个文件的具体信息,随后会专门写博文来认识。

最后 , 返回 ClassLoader 类的实例 $loader 。

 

现在在回到 代码清单 laravel/bootstrap/autoload.php 的第7行

1 define('LARAVEL_START', microtime(true));
2 require __DIR__.'/../vendor/autoload.php';
3 if (file_exists($compiled = __DIR__.'/compiled.php'))
4 {
5     require $compiled;
6 }
7 Patchwork\Utf8\Bootup::initMbstring();

注意这里第7行,之所以可以直接像 Patchwork\Utf8\Bootup::initMbstring() 这么使用而不需要手动required Bootup类文件,是因为 前面在ClassLoader中的register() 函数用 spl_autoload_register() 对Bootup类进行了注册。下面说一下 spl_autoload_register 。

 

spl_autoload_register

要使用 spl_autoload_register ,请保证你的PHP版本(PHP 5 >= 5.1.2)。

www.php.net 对 spl_autoload_register 的解释如下:注册__autoload()函数,将函数注册到SPL __autoload函数栈中。如果该栈中的函数尚未激活,则激活它们。刚接触 PHP 的同学肯定觉得这个解释云里雾里的,看完依然不知道什么意思。要想理解这句话,首先要弄明白 __autoload() 是个什么东西。

__autoload()

__autoload 的作用是尝试加载未定义的类,可以通过定义这个函数来启用类的自动加载。下面举个例子:

function __autoload($class)
{
    echo '尝试加载的类的名字是:'.$class;
}

$say= @ new say();

上例会输出:"尝试加载的类的名字是 say "。由于最后一行引用了尚未定义的类 box ,所以 __autoload 函数将被执行。

再看下面这段

 

class say
{
    public function __construct()
    {
        echo 'say 类存在,并说出了hello,所以 __autoload 函数不会执行。';
    }
}

function __autoload($class)
{
    echo '尝试加载的类的名字是:'.$class;
}

$say= @ new say();

 

这将会输出 : say 类存在,并说出了hello,所以 __autoload 函数不会执行。

理解完 __autoload 就好办了,再看上面:“将函数注册到SPL __autoload函数栈中”,意思是我们可以自定义 尝试加载未定义的类时 使用的函数。现在返回代码片段

spl_autoload_register(array($this, 'loadClass'), true, $prepend);

这下是不是很明白了,当实例化一个类的时候,如果这个类没有定义,就执行 ClassLoader 类中的 loadClass 函数。loadClass 的定义前面我们说过了,就是找到声明相关类的文件,然后包含并运行该文件,随后加载相关类。至此,composer的自动加载机制学习完毕。

 

posted @ 2014-05-16 01:58  简道  阅读(5631)  评论(2编辑  收藏  举报