搜尋
首頁後端開發php教程laravel学习笔记(1)- Composer概述及其自动加载探秘_PHP教程

刚开始接触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

laravel学习笔记(1)- Composer概述及其自动加载探秘_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的自动加载机制学习完毕。

 

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/770658.htmlTechArticle刚开始接触laravel,一天时间走马观花的看了一些官方文档之后便开始了laravel的学习。这里谈到的都是最基础的东西,各路大神,可直接略...
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
可以在PHP會話中存儲哪些數據?可以在PHP會話中存儲哪些數據?May 02, 2025 am 12:17 AM

phpsessionscanStorestrings,數字,數組和原始物。

您如何開始PHP會話?您如何開始PHP會話?May 02, 2025 am 12:16 AM

tostartaphpsession,usesesses_start()attheScript'Sbeginning.1)placeitbeforeanyOutputtosetThesessionCookie.2)useSessionsforuserDatalikeloginstatusorshoppingcarts.3)regenerateSessiveIdStopreventFentfixationAttacks.s.4)考慮使用AttActAcks.s.s.4)

什麼是會話再生,如何提高安全性?什麼是會話再生,如何提高安全性?May 02, 2025 am 12:15 AM

會話再生是指在用戶進行敏感操作時生成新會話ID並使舊ID失效,以防會話固定攻擊。實現步驟包括:1.檢測敏感操作,2.生成新會話ID,3.銷毀舊會話ID,4.更新用戶端會話信息。

使用PHP會話時有哪些性能考慮?使用PHP會話時有哪些性能考慮?May 02, 2025 am 12:11 AM

PHP会话对应用性能有显著影响。优化方法包括:1.使用数据库存储会话数据,提升响应速度;2.减少会话数据使用,只存储必要信息;3.采用非阻塞会话处理器,提高并发能力;4.调整会话过期时间,平衡用户体验和服务器负担;5.使用持久会话,减少数据读写次数。

PHP會話與Cookie有何不同?PHP會話與Cookie有何不同?May 02, 2025 am 12:03 AM

PHPsessionsareserver-side,whilecookiesareclient-side.1)Sessionsstoredataontheserver,aremoresecure,andhandlelargerdata.2)Cookiesstoredataontheclient,arelesssecure,andlimitedinsize.Usesessionsforsensitivedataandcookiesfornon-sensitive,client-sidedata.

PHP如何識別用戶的會話?PHP如何識別用戶的會話?May 01, 2025 am 12:23 AM

phpIdentifiesauser'ssessionSessionSessionCookiesAndSessionId.1)whiwsession_start()被稱為,phpgeneratesainiquesesesessionIdStoredInacookInAcookInAcienamedInAcienamedphpsessIdontheuser'sbrowser'sbrowser.2)thisIdallowSphptpptpptpptpptpptpptpptoretoreteretrieetrieetrieetrieetrieetrieetreetrieetrieetrieetrieetremthafromtheserver。

確保PHP會議的一些最佳實踐是什麼?確保PHP會議的一些最佳實踐是什麼?May 01, 2025 am 12:22 AM

PHP會話的安全可以通過以下措施實現:1.使用session_regenerate_id()在用戶登錄或重要操作時重新生成會話ID。 2.通過HTTPS協議加密傳輸會話ID。 3.使用session_save_path()指定安全目錄存儲會話數據,並正確設置權限。

PHP會話文件默認存儲在哪裡?PHP會話文件默認存儲在哪裡?May 01, 2025 am 12:15 AM

phpsessionFilesArestoredIntheDirectorySpecifiedBysession.save_path,通常是/tmponunix-likesystemsorc:\ windows \ windows \ temponwindows.tocustomizethis:tocustomizEthis:1)useession_save_save_save_path_path()

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。