程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 優雅的 laravel(1)- Composer概述及其自動加載探秘

優雅的 laravel(1)- Composer概述及其自動加載探秘

編輯:關於PHP編程

剛開始接觸laravel,一天時間走馬觀花的看了一些官方文檔之後便開始了laravel的學習。這裡談到的都是最基礎的東西,各路大神,可直接略過。

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 <[email protected]> 7 * Jordi Boggiano <[email protected]> 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 <[email protected]> 41 * @author Jordi Boggiano <[email protected]> 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的自動加載機制學習完畢。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved