Home  >  Article  >  Backend Development  >  PHP interview question 4: Implementing autoload

PHP interview question 4: Implementing autoload

不言
不言Original
2018-04-18 09:40:563308browse

The content of this article is about the implementation of autoload in the fourth PHP interview question. It has a certain reference value. Now I share it with you. Friends in need can refer to it.

Yii framework declares its own class loading The method is very efficient and is real "time-consuming loading". So what's so special about it? I studied the source code today and found that a layer of "path cache" is actually added at the code level.

Yii2's autoloading principle

We know that to implement your own autoload method, you need to use the spl_autoload_register() function to register an autoload method, The method registered by Yii is YiiBase::autoload(). The logic of this method will be explained later. In addition, Yii generally uses Yii::import($pathAlias, $forceInclude=false) to load the corresponding class (this method directly calls YiiBase::import()), This method can be used with YiiBase::autoload() to achieve "time-consuming loading".

Let’s talk about the general logic of import first:
1. Check whether the self::$_imports array has the corresponding $pathAlias, If there is a description that it has been loaded, directly return the class name or directory name; otherwise, continue to step 2;
2. Obtain the actual path name based on the path alias, and finally use the path alias Whether part of it is "*" You can know whether the path alias to be loaded is a file. If it is a file, go to step 3; otherwise, go to step 4;
3. If it is $forceIncludeIf true, then require this file immediately and add an item to the $_imports array $alias => $className; Otherwise, add an item to the array $classMap Cache an item in $className => $realPath;
4. For the path, this path will be cached in the array $_includePaths, and in $_importsAdd an item to the array$alias => $realPath;
5. End.
Because $forceInclude defaults to false, import will not load the corresponding class immediately, and will not actually load it until it is used. This is the job of YiiBase::autoload.

The general logic of autoload:
1. Check whether the class name has been cached in the $classMap or $_coreClasses array. If If so, directly require the corresponding file path, $_coreClasses is the mapping table of the framework’s own classes; otherwise, go to step 2;
2. Check whether YiiBase::$enableIncludePath is false, if so, go to step 3, otherwise directly include($className . '.php')
3. Traverse the $includePaths array and concatenate the directory names. Class name, check whether it is a legal php file, if so, include it, and then jump out of the loop
4, end.
It should be noted that the documentation states: If you want to use it with other class libraries, you must set $enableIncludePath to false so that when Yii::autoload() fails , the autoload methods of other class libraries have the opportunity to be executed.
//$enableIncludePath Whether to rely on PHP include paths to automatically load class files. The default is true. If your hosting environment does not allow you to change the PHP include path, you can set it to false, or you want to add another autoloader to the default Yii autoloader.

Official Description Document

In Yii, all classes, interfaces, and Traits can be automatically loaded before calling using the class's automatic loading mechanism. Yii uses PHP's automatic class loading mechanism to efficiently implement class positioning and import. This mechanism is compatible with the PSR-4 standard. In Yii, classes are only loaded when called, especially core classes, which are positioned very quickly, which is also an important manifestation of Yii's high efficiency and high performance.

Implementation of automatic loading mechanism
Yii's automatic class loading relies on PHP's spl_autoload_register(), registers an own automatic loading function (autoloader), and inserts it into the front of the automatic loading function stack. Make sure Yii's autoloader is called first.

The introduction of this mechanism of automatic class loading starts with the entry file index.php:

<?php
defined(&#39;YII_DEBUG&#39;) or define(&#39;YII_DEBUG&#39;, false);
defined(&#39;YII_ENV&#39;) or define(&#39;YII_ENV&#39;, &#39;prod&#39;);
// 这个是第三方的
autoloaderrequire(__DIR__ . &#39;/../../vendor/autoload.php&#39;);
// 这个是Yii的Autoloader,放在最后面,确保其插入的autoloader会放在最前面
require(__DIR__ . &#39;/../../vendor/yiisoft/yii2/Yii.php&#39;);
// 后面不应再有
autoloader了require(__DIR__ . &#39;/../../common/config/aliases.php&#39;);
$config = yii\helpers\ArrayHelper::merge(    
require(__DIR__ . &#39;/../../common/config/main.php&#39;),    
require(__DIR__ . &#39;/../../common/config/main-local.php&#39;),    
require(__DIR__ . &#39;/../config/main.php&#39;),    
require(__DIR__ . &#39;/../config/main-local.php&#39;)
);
$application = new yii\web\Application($config);
$application->run();

The main point of this file is the sequence of the third-party autoloader and the autoloader implemented by Yii. No matter how the third-party code uses spl_autoload_register() to register its own autoloader, as long as the Yii code is at the end, it can ensure that it can insert its own autoloader into the front of the entire autoloder stack. So that it is called first when needed.

Next, let’s see how Yii calls spl_autoload_register() to register the autoloader. It depends on what happens in Yii.php:

<?php
require(__DIR__ . &#39;/BaseYii.php&#39;);
class Yii extends \yii\BaseYii{}
// 重点看这个 
spl_autoload_registerspl_autoload_register([&#39;Yii&#39;, &#39;autoload&#39;], true, true);
// 下面的语句读取了一个映射表
Yii::$classMap = include(__DIR__ . &#39;/classes.php&#39;);

Yii::$container = new yii\di\Container;

This code , spl_autoload_register(['Yii', 'autoload', true, true]) is called, Yii::autoload() is inserted into the stack as autoloader at the front. And read classes.php into Yii::$classMap and save a mapping table.

在上面的代码中,Yii类是里面没有任何代码,并未对 BaseYii::autoload() 进行重载,所以,这个 spl_autoload_register() 实际上将 BaseYii::autoload() 注册为autoloader。如果,你要实现自己的autoloader,可以在 Yii 类的代码中,对 autoload() 进行重载。

在调用 spl_autoload_register() 进行autoloader注册之后,Yii将 calsses.php 这个文件作为一个映射表保存到 Yii::$classMap 当中。这个映射表,保存了一系列的类名与其所在PHP文件的映射关系,比如:

return [  &#39;yii\base\Action&#39; => YII2_PATH . &#39;/base/Action.php&#39;,  &#39;yii\base\ActionEvent&#39; => YII2_PATH . &#39;/base/ActionEvent.php&#39;,  ... ...

  &#39;yii\widgets\PjaxAsset&#39; => YII2_PATH . &#39;/widgets/PjaxAsset.php&#39;,  &#39;yii\widgets\Spaceless&#39; => YII2_PATH . &#39;/widgets/Spaceless.php&#39;,
];

这个映射表以类名为键,以实际类文件为值,Yii所有的核心类都已经写入到这个 classes.php 文件中,所以,核心类的加载是最便捷,最快的。现在,来看看这个关键先生 BaseYii::autoload()

public static function autoload($className){
    if (isset(static::$classMap[$className])) {        
    $classFile = static::$classMap[$className];        if ($classFile[0] === &#39;@&#39;) {            $classFile = static::getAlias($classFile);
        }
    } elseif (strpos($className, &#39;\\&#39;) !== false) {        
    $classFile = static::getAlias(&#39;@&#39; . str_replace(&#39;\\&#39;, &#39;/&#39;,            $className) . &#39;.php&#39;, false);        
    if ($classFile === false || !is_file($classFile)) {            return;
        }
    } else {        
    return;
    }    
    include($classFile);    
    if (YII_DEBUG && !class_exists($className, false) &&
        !interface_exists($className, false) && !trait_exists($className,        false)) {        
        throw new UnknownClassException(        
        "Unable to find &#39;$className&#39; in file: $classFile. Namespace missing?"
        );
    }
}

从这段代码来看Yii类自动加载机制的运作原理:

检查 $classMap[$className] 看看是否在映射表中已经有拟加载类的位置信息;

如果有,再看看这个位置信息是不是一个路径别名,即是不是以 @ 打头, 是的话,将路径别名解析成实际路径。 如果映射表中的位置信息并非一个路径别名,那么将这个路径作为类文件的所在位置。 类文件的完整路径保存在 $classFile ;

如果 $classMap[$className] 没有该类的信息, 那么,看看这个类名中是否含有 \ , 如果没有,说明这是一个不符合规范要求的类名,autoloader直接返回。 PHP会尝试使用其他已经注册的autoloader进行加载。 如果有 \ ,认为这个类名符合规范,将其转换成路径形式。 即所有的 \ 用 / 替换,并加上 .php 的后缀。

将替换后的类名,加上 @ 前缀,作为一个路径别名,进行解析。 从别名的解析过程我们知道,如果根别名不存在,将会抛出异常。 所以,类的命名,必须以有效的根别名打头:

// 有效的类名,因为@yii是一个已经预定义好的别名use yii\base\Application;// 无效的类名,因为没有 @foo 或 @foo/bar 的根别名,要提前定义好use foo\bar\SomeClass;

使用PHP的 include() 将类文件加载进来,实现类的加载。

从其运作原理看,最快找到类的方式是使用映射表。 其次,Yii中所有的类名,除了符合规范外,还需要提前注册有效的根别名。

运用自动加载机制
在入口脚本中,除了Yii自己的autoloader,还有一个第三方的autoloader:

require(__DIR__ . &#39;/../../vendor/autoload.php&#39;);

这个其实是Composer提供的autoloader。Yii使用Composer来作为包依赖管理器,因此,建议保留Composer的autoloader,尽管Yii的autoloader也能自动加载使用Composer安装的第三方库、扩展等,而且更为高效。但考虑到毕竟是人家安装的,人家还有一套自己专门的规则,从维护性、兼容性、扩展性来考虑,建议保留Composer的autoloader。

如果还有其他的autoloader,一定要在Yii的autoloader注册之前完成注册,以保证Yii的autoloader总是最先被调用。

如果你有自己的autoloader,也可以不安装Yii的autoloaer,只是这样未必能有Yii的高效,且还需要遵循一套类似的类命名和加载的规则。就个人的经验而言,Yii的autoloader完全够用,没必要自己重复造轮子。

相关推荐:

php面试题三之yii2和yii的不一样的地方

php面试题二之用到过的传输协议

php面试题一之线程和进程的区别(顺带提下协程)

The above is the detailed content of PHP interview question 4: Implementing autoload. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn