Maison  >  Article  >  php教程  >  优雅的 laravel(1)- Composer概述及其自动加载探秘

优雅的 laravel(1)- Composer概述及其自动加载探秘

WBOY
WBOYoriginal
2016-06-13 09:35:041031parcourir

刚开始接触laravel,一天时间走马观花的看了一些官方文档之后便开始了laravel的学习。这里谈到的都是最基础的东西,各路大神,可直接略过。

composer概述

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

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

使用composer

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

<span>{
    </span><span>"</span><span>require</span><span>"</span><span>: {
        </span><span>"</span><span>monolog/monolog</span><span>"</span>: <span>"</span><span>1.2.*</span><span>"</span><span>
    }
}</span>

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

$ php composer.phar install

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

require <span>'</span><span>vendor/autoload.php</span><span>'</span>;

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

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

</span><span>$log</span>->addWarning('Foo');

 

Composer 自动加载探秘

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

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

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

<span>#laravel/public/index.php<br /><br />require</span> __DIR__.'/../bootstrap/autoload.php'<span>;

</span><span>$app</span> = <span>require_once</span> __DIR__.'/../bootstrap/start.php'<span>;

</span><span>$app</span>->run();

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

 

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

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

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

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

 

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

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

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

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

 

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

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

第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 7 * Jordi Boggiano 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 41 * @author Jordi Boggiano 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);

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

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

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

这里有个 findFile() 函数,如果相关类的声明所在文件的路径找到了,就包含并运行该文件,然后返回 true 。接着打开findFile()的定义

<span>public</span> <span>function</span> findFile(<span>$class</span><span>)
{
    </span><span>//</span><span> work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731</span>
    <span>if</span> ('\\' == <span>$class</span>[0<span>]) {
        </span><span>$class</span> = <span>substr</span>(<span>$class</span>, 1<span>);
    }

    </span><span>//</span><span> class map lookup</span>
    <span>if</span> (<span>isset</span>(<span>$this</span>->classMap[<span>$class</span><span>])) {
        </span><span>return</span> <span>$this</span>->classMap[<span>$class</span><span>];
    }

    </span><span>$file</span> = <span>$this</span>->findFileWithExtension(<span>$class</span>, '.php'<span>);

    </span><span>//</span><span> Search for Hack files if we are running on HHVM</span>
    <span>if</span> (<span>$file</span> === <span>null</span> && <span>defined</span>('HHVM_VERSION'<span>)) {
        </span><span>$file</span> = <span>$this</span>->findFileWithExtension(<span>$class</span>, '.hh'<span>);
    }

    </span><span>if</span> (<span>$file</span> === <span>null</span><span>) {
        </span><span>//</span><span> Remember that this class does not exist.</span>
        <span>return</span> <span>$this</span>->classMap[<span>$class</span>] = <span>false</span><span>;
    }

    </span><span>return</span> <span>$file</span><span>;
}</span>

先是判断类名是否以'\'开始,如果是的话,清除开头的'\'

接着,检查当前类的名字是否在 类名与声明当前类的文件的路径的关系数组 中,如果存在,直接返回相关键值(类文件路径信息)

如果上一步没有返回路径信息,执行 findFileWithExtension($class, '.php') 继续查找类文件路径信息,findFileWithExtension的定义后面将会列出。

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

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

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

 

代码清单 findFileWithExtension()

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

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

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

        <span>$loader</span>->register(<span>true</span><span>);

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

        </span><span>return</span> <span>$loader</span><span>;
    }
}

</span><span>function</span> composerRequire9b2a1b1cf01c9a870ab98748dc5f1256(<span>$file</span><span>)
{
    </span><span>require</span> <span>$file</span><span>;
}</span>

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

<span>//</span><span> autoload_files.php @generated by Composer</span>

<span>$vendorDir</span> = <span>dirname</span>(<span>dirname</span>(<span>__FILE__</span><span>));
</span><span>$baseDir</span> = <span>dirname</span>(<span>$vendorDir</span><span>);

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

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

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

 

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

<span>1</span> <span>define</span>('LARAVEL_START', <span>microtime</span>(<span>true</span><span>));
</span><span>2</span> <span>require</span> __DIR__.'/../vendor/autoload.php'<span>;
</span><span>3</span> <span>if</span> (<span>file_exists</span>(<span>$compiled</span> = __DIR__.'/compiled.php'<span>))
</span><span>4</span> <span>{
</span><span>5</span>     <span>require</span> <span>$compiled</span><span>;
</span><span>6</span> <span>}
</span><span>7</span> 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 的作用是尝试加载未定义的类,可以通过定义这个函数来启用类的自动加载。下面举个例子:

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

</span><span>$say</span>= @ <span>new</span> say();

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

再看下面这段

 

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

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

</span><span>$say</span>= @ <span>new</span> say();

 

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

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

spl_autoload_register(<span>array</span>(<span>$this</span>, 'loadClass'), <span>true</span>, <span>$prepend</span>);

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

 

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn