- 自动加载是通过一系列封装手段,让PHP调用一个类的时候可以不手动include或require来引入。当在当前环境找不到该类时,会调用魔术方法__autoload()来进一步查找该类。
1. 自动加载的本质
自动加载的本质,是通过incldue(‘路径’)来引入文件。
2. PHP自动加载的实现
2.1 __autoload()
通过__autoload(string $class):void方法来监控找不到的类,尝试加载未定义的类。
一个项目中只能有一个这样的__autoload( ) 函数,因此可以使用spl_autoload_register( )来解决这个问题。
2.2 spl_autoload_register()
方法spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] ):bool注册指定函数处理自动加载。spl_autoload_register( )支持多次调用,但一般会只注册一个方法来实现加载功能。
spl_autoload_register('loadExcel'); // 注册方法loadExcel(),来实现加载
使用spl_autoload_register( )时,autoload( )会无效,如果希望autoload( )能继续使用,可以将其引入:
if (function_exists('__autoload')) {spl_autoload_register('__autoload');}
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’;
- 查找文件:当找不到一个new的类时,就会触发\Composer\Autoload\ClassLoader()->loadClass()函数来查找指定类,找到就include到项目中,找不到就返回false。(loadClass调用findFile()来找类)- findFile():先在$classMap中查找,如果找不到的话就会尝试在apcu缓存中查找,如果还是找不到的话就会调用findFileWithExtension()方法查找,如果找到了就会将该文件加到apcu缓存,如果找不到的话就会在missingClasses数组中设一个标记表示识这个类找不到;findFileWithExtension()方法根据之前通过$loader−>set(namespace, path)和$loader->setPsr4(namespace,path)方法包含进$loader的类文件的路径信息```php# Class \Composer\Autoload\ClassLoaderpublic function register($prepend = false) {spl_autoload_register(array($this, 'loadClass'), true, $prepend);}public function loadClass($class) {if ($file = $this->findFile($class)) {includeFile($file);return true;}}
4. 命名空间与自动加载
4.1 命名空间的使用
命名空间解决了一个项目不能有多个相同名字类,使用方法是在编写类文件的时候在文件开头声明当前命名空间,使用的时候给类也加上命名空间来指定是具体哪个类,如:
# 文件1路径:{__ROOT__}/src/api/model/pay/server/Respond.php<?phpnamespace model\pay\server;class Respond{}
# 文件2路径:{__ROOT__}/src/index/model/repay/server/Respond.php<?phpnamespace model\repay\server;class Respond{}
# 分别使用两个Respond<?php$pay = new \model\pay\server\Respond();$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);
public function setPsr4($prefix, $paths) {if (!$prefix) {$this->fallbackDirsPsr4 = (array) $paths;} else {$length = strlen($prefix);if ('\\' !== $prefix[$length - 1]) {throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");}$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;$this->prefixDirsPsr4[$prefix] = (array) $paths;}}
5. psr4与psr0
psr4与psr0标准的命名空间规范,使用起来的区别在psr0会在将类名的
_替换成/,再进行类的查找。
