• 自动加载是通过一系列封装手段,让PHP调用一个类的时候可以不手动include或require来引入。当在当前环境找不到该类时,会调用魔术方法__autoload()来进一步查找该类。

    1. 自动加载的本质

  1. 自动加载的本质,是通过incldue(‘路径’)来引入文件。

    2. PHP自动加载的实现

    2.1 __autoload()

  2. 通过__autoload(string $class):void方法来监控找不到的类,尝试加载未定义的类。

  3. 一个项目中只能有一个这样的__autoload( ) 函数,因此可以使用spl_autoload_register( )来解决这个问题。

    2.2 spl_autoload_register()

  4. 方法spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] ):bool注册指定函数处理自动加载。spl_autoload_register( )支持多次调用,但一般会只注册一个方法来实现加载功能。

    1. spl_autoload_register('loadExcel'); // 注册方法loadExcel(),来实现加载
  5. 使用spl_autoload_register( )时,autoload( )会无效,如果希望autoload( )能继续使用,可以将其引入:

    1. if (function_exists('__autoload')) {
    2. spl_autoload_register('__autoload');
    3. }

    3. 自动加载文件

  • 自动加载文件:首先会初始化一个ClassLoader类给赋值给$loader,然后将autoload_namespaces.php、autoload_psr4.php、autoload_classmap.php文件中的内容加入到$loader对应的数组,然后注册\Composer\Autoload\ClassLoader()->loadClass()函数为自动加载函数,然后将autoload_files.php中的所有路径所示的文件都包含进来,并记录到$GLOBALS[‘__composer_autoload_files’]中。 ```php

    Class ComposerAutoloaderInit9c4ae0f1e3be1063039c0f96f3fd7424

// 自动加载类 $loader = new \Composer\Autoload\ClassLoader();

$map = require DIR . ‘/autoload_namespaces.php’; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); }

$map = require DIR . ‘/autoload_psr4.php’; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); }

$classMap = require DIR . ‘/autoload_classmap.php’; if ($classMap) { $loader->addClassMap($classMap); }

$loader->register(true);

$includeFiles = require DIR . ‘/autoload_files.php’;

  1. - 查找文件:当找不到一个new的类时,就会触发\Composer\Autoload\ClassLoader()->loadClass()函数来查找指定类,找到就include到项目中,找不到就返回false。(loadClass调用findFile()来找类)
  2. - findFile():先在$classMap中查找,如果找不到的话就会尝试在apcu缓存中查找,如果还是找不到的话就会调用findFileWithExtension()方法查找,如果找到了就会将该文件加到apcu缓存,如果找不到的话就会在missingClasses数组中设一个标记表示识这个类找不到;findFileWithExtension()方法根据之前通过$loader−>set(namespace, path)和$loader->setPsr4(namespace,path)方法包含进$loader的类文件的路径信息
  3. ```php
  4. # Class \Composer\Autoload\ClassLoader
  5. public function register($prepend = false) {
  6. spl_autoload_register(array($this, 'loadClass'), true, $prepend);
  7. }
  8. public function loadClass($class) {
  9. if ($file = $this->findFile($class)) {
  10. includeFile($file);
  11. return true;
  12. }
  13. }

4. 命名空间与自动加载

4.1 命名空间的使用

  • 命名空间解决了一个项目不能有多个相同名字类,使用方法是在编写类文件的时候在文件开头声明当前命名空间,使用的时候给类也加上命名空间来指定是具体哪个类,如:

    1. # 文件1路径:{__ROOT__}/src/api/model/pay/server/Respond.php
    2. <?php
    3. namespace model\pay\server;
    4. class Respond{}
    1. # 文件2路径:{__ROOT__}/src/index/model/repay/server/Respond.php
    2. <?php
    3. namespace model\repay\server;
    4. class Respond{}
    1. # 分别使用两个Respond
    2. <?php
    3. $pay = new \model\pay\server\Respond();
    4. $repay = new \model\repay\server\Respond();

    4.2 命名空间全地址

    (1)使用类时加上命名空间是:use
    (2)而本质加上的是:use ,构成:<基础路径><相对路径><类>

  • 那么,php是怎么知道指定的是 {__ROOT__}/src/api/{__ROOT__}/src/index/ 这个基础路径的呢?

    4.2 命名空间的解析

  • 同样地,php本身也不知道\model\pay\server\Respond()这个类是否存在,以及它的基础路径是哪个,因此在调用时自动调用自动加载功能。(看上文:3. 自动加载文件)当$classMap与apcu缓存查找不到时,使用findFileWithExtension()查找,根据prefixDirsPsr4[$prefix]来拼接相对路径找到指定文件。那么prefixDirsPsr4[]在何处定义?(看上文:3. 自动加载文件)$loader->setPsr4($namespace, $path);

    1. public function setPsr4($prefix, $paths) {
    2. if (!$prefix) {
    3. $this->fallbackDirsPsr4 = (array) $paths;
    4. } else {
    5. $length = strlen($prefix);
    6. if ('\\' !== $prefix[$length - 1]) {
    7. throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
    8. }
    9. $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
    10. $this->prefixDirsPsr4[$prefix] = (array) $paths;
    11. }
    12. }

    5. psr4与psr0

  • psr4与psr0标准的命名空间规范,使用起来的区别在psr0会在将类名的 _ 替换成 / ,再进行类的查找。