正文
你如果不使用框架,那你可能了解如何加载类的相关知识点,毕竟这是代码运行的基础。
但如果经常使用框架开发,又没有深究过框架原理,那就对文件加载会比较模糊。
当你在代码开发中,在类上用了use classPath\className
,然后在类中就可以使用classPath\className
的方法了,
一方面你会觉得这很方便也很神奇,另一方面你会觉得这个框架做得实在是太好了。
如果仅仅停留在这个层面上,你的技术是不会得到进步的,我们就是要挖掘里面的原因。
下面就讲讲Yii2的类自动加载机制。类自动加载就是自动include()
通过文件地址引入文件。
文件举例
project/admin/controllers/SiteController.php
内容:
namespace admin\controllers;
use common\logics\user\UserLogic;
use Yii;
use yii\web\Controller;
class SiteController extends Controller
{
public function actionIndex()
{
echo UserLogic::getDefaultName();
}
}
project/common/logics/user/UserLogic.php
内容:
namespace common\logics\user;
use Yii;
class UserLogic
{
public static function getDefaultName()
{
return 'Tom';
}
}
当我们通过网址访问site/index
时,输出 Tom 。
SiteController是Yii加载解析的,我们理解;但project/common/logics/user/UserLogic.php
却是我们随便乱写的,
包括目录结构,我们既然没有include这个文件,怎么可以use引入使用呢?
原理解析
PHP碰到没有定义的类就看有没有定义spl_autoload_register()注册的堆栈,如果有,就通过定义路径include加载文件;
如果没有定义spl_autoload_register(),就看__autoload()
是否定义,如果有就按照__autoload()
函数运行,
如果没有则说明无法使用类方法,报错。
spl_autoload_register() 的作用具体看PHP spl_autoload_register()相关。
在Yii中,所有类、接口、Traits都可以使用类的自动加载机制实现在调用前自动加载。 Yii借助了PHP的类自动加载机制高效实现了类的定位、导入,这一机制兼容 PSR-4 的标准。 在Yii中,类仅在调用时才会被加载,特别是核心类,其定位非常快,这也是Yii高效高性能的一个重要体现。
Yii的类自动加载,依赖于PHP的 spl_autoload_register() , 注册一个自己的自动加载函数(autoloader), 并插入到自动加载函数栈的最前面,确保Yii的autoloader会被最先调用。
类自动加载的这个机制的引入要从入口文件 index.php 开始说起:
project/admin/web/index.php
内容:
<?php
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
// 这个是第三方的autoloader
require __DIR__ . '/../../vendor/autoload.php';
// 这个是Yii的Autoloader,放在最后面,确保其插入的autoloader会放在最前面
require __DIR__ . '/../../vendor/yiisoft/yii2/Yii.php';
// 后面不应再有autoloader了
require __DIR__ . '/../../common/config/bootstrap.php';
require __DIR__ . '/../config/bootstrap.php';
$config = yii\helpers\ArrayHelper::merge(
require __DIR__ . '/../../common/config/main.php',
require __DIR__ . '/../../common/config/main-local.php',
require __DIR__ . '/../config/main.php',
require __DIR__ . '/../config/main-local.php'
);
(new yii\web\Application($config))->run();
这个文件主要看点在于第三方autoloader与Yii 实现的autoloader的顺序。 不管第三方的代码是如何使用 spl_autoload_register() 来注册自己的autoloader的, 只要Yii 的代码在最后面,就可以确保其可以将自己的autoloader插入到整个autoloder 栈的最前面, 从而在需要时最先被调用。
接下来,看看Yii是如何调用 spl_autoload_register() 注册autoloader的, 这要看 Yii.php 里发生了些什么:
<?php
require(__DIR__ . '/BaseYii.php');
class Yii extends \yii\BaseYii
{
}
// 重点看这个 spl_autoload_register
spl_autoload_register(['Yii', 'autoload'], true, true);
// 下面的语句读取了一个映射表
Yii::$classMap = require(__DIR__ . '/classes.php');
// 依赖注入,这里不用关心
Yii::$container = new yii\di\Container();
这段代码,调用了 spl_autoload_register(['Yii', 'autoload', true, true])
,
将 Yii::autoload()
作为autoloader插入到栈的最前面了。
并将 classes.php 读取到 Yii::$classMap
中,保存了一个映射表。
在上面的代码中,Yii类是里面没有任何代码,并未对 BaseYii::autoload()
进行重载,
所以,这个 spl_autoload_register() 实际上将 BaseYii::autoload()
注册为autoloader。
如果,你要实现自己的autoloader,可以在 Yii 类的代码中,对 autoload() 进行重载。
在调用 spl_autoload_register() 进行autoloader注册之后,
Yii将 calsses.php 这个文件作为一个映射表保存到 Yii::$classMap
当中。
这个映射表,保存了一系列的类名与其所在PHP文件的映射关系,比如:
<?php
return [
'yii\base\Action' => YII2_PATH . '/base/Action.php',
'yii\base\ActionEvent' => YII2_PATH . '/base/ActionEvent.php',
'yii\base\ActionFilter' => YII2_PATH . '/base/ActionFilter.php',
'yii\base\Application' => YII2_PATH . '/base/Application.php',
'yii\base\ArrayAccessTrait' => YII2_PATH . '/base/ArrayAccessTrait.php',
'yii\base\Arrayable' => YII2_PATH . '/base/Arrayable.php',
'yii\base\ArrayableTrait' => YII2_PATH . '/base/ArrayableTrait.php',
'yii\base\Behavior' => YII2_PATH . '/base/Behavior.php',
'yii\base\BootstrapInterface' => YII2_PATH . '/base/BootstrapInterface.php',
'yii\base\Component' => YII2_PATH . '/base/Component.php',
'yii\base\Configurable' => YII2_PATH . '/base/Configurable.php',
'yii\base\Controller' => YII2_PATH . '/base/Controller.php',
'yii\base\DynamicModel' => YII2_PATH . '/base/DynamicModel.php',
'yii\base\ErrorException' => YII2_PATH . '/base/ErrorException.php',
'yii\base\ErrorHandler' => YII2_PATH . '/base/ErrorHandler.php',
'yii\base\Event' => YII2_PATH . '/base/Event.php',
'yii\base\Exception' => YII2_PATH . '/base/Exception.php',
'yii\base\ExitException' => YII2_PATH . '/base/ExitException.php',
'yii\base\InlineAction' => YII2_PATH . '/base/InlineAction.php',
'yii\base\InvalidCallException' => YII2_PATH . '/base/InvalidCallException.php',
'yii\base\InvalidConfigException' => YII2_PATH . '/base/InvalidConfigException.php',
'yii\base\InvalidParamException' => YII2_PATH . '/base/InvalidParamException.php',
'yii\base\InvalidRouteException' => YII2_PATH . '/base/InvalidRouteException.php',
'yii\base\InvalidValueException' => YII2_PATH . '/base/InvalidValueException.php',
'yii\base\Model' => YII2_PATH . '/base/Model.php',
'yii\base\ModelEvent' => YII2_PATH . '/base/ModelEvent.php',
'yii\base\Module' => YII2_PATH . '/base/Module.php',
'yii\base\NotSupportedException' => YII2_PATH . '/base/NotSupportedException.php',
'yii\base\Object' => YII2_PATH . '/base/Object.php',
'yii\base\Request' => YII2_PATH . '/base/Request.php',
'yii\base\Response' => YII2_PATH . '/base/Response.php',
'yii\base\Security' => YII2_PATH . '/base/Security.php',
'yii\base\Theme' => YII2_PATH . '/base/Theme.php',
'yii\base\UnknownClassException' => YII2_PATH . '/base/UnknownClassException.php',
'yii\base\UnknownMethodException' => YII2_PATH . '/base/UnknownMethodException.php',
'yii\base\UnknownPropertyException' => YII2_PATH . '/base/UnknownPropertyException.php',
'yii\base\UserException' => YII2_PATH . '/base/UserException.php',
'yii\base\View' => YII2_PATH . '/base/View.php',
'yii\base\ViewContextInterface' => YII2_PATH . '/base/ViewContextInterface.php',
'yii\base\ViewEvent' => YII2_PATH . '/base/ViewEvent.php',
'yii\base\ViewNotFoundException' => YII2_PATH . '/base/ViewNotFoundException.php',
'yii\base\ViewRenderer' => YII2_PATH . '/base/ViewRenderer.php',
'yii\base\Widget' => YII2_PATH . '/base/Widget.php',
'yii\base\WidgetEvent' => YII2_PATH . '/base/WidgetEvent.php',
'yii\behaviors\AttributeBehavior' => YII2_PATH . '/behaviors/AttributeBehavior.php',
'yii\behaviors\AttributeTypecastBehavior' => YII2_PATH . '/behaviors/AttributeTypecastBehavior.php',
'yii\behaviors\BlameableBehavior' => YII2_PATH . '/behaviors/BlameableBehavior.php',
'yii\behaviors\SluggableBehavior' => YII2_PATH . '/behaviors/SluggableBehavior.php',
'yii\behaviors\TimestampBehavior' => YII2_PATH . '/behaviors/TimestampBehavior.php',
'yii\caching\ApcCache' => YII2_PATH . '/caching/ApcCache.php',
'yii\caching\ArrayCache' => YII2_PATH . '/caching/ArrayCache.php',
'yii\caching\Cache' => YII2_PATH . '/caching/Cache.php',
'yii\caching\ChainedDependency' => YII2_PATH . '/caching/ChainedDependency.php',
'yii\caching\DbCache' => YII2_PATH . '/caching/DbCache.php',
'yii\caching\DbDependency' => YII2_PATH . '/caching/DbDependency.php',
'yii\caching\Dependency' => YII2_PATH . '/caching/Dependency.php',
'yii\caching\DummyCache' => YII2_PATH . '/caching/DummyCache.php',
'yii\caching\ExpressionDependency' => YII2_PATH . '/caching/ExpressionDependency.php',
'yii\caching\FileCache' => YII2_PATH . '/caching/FileCache.php',
'yii\caching\FileDependency' => YII2_PATH . '/caching/FileDependency.php',
'yii\caching\MemCache' => YII2_PATH . '/caching/MemCache.php',
'yii\caching\MemCacheServer' => YII2_PATH . '/caching/MemCacheServer.php',
'yii\caching\TagDependency' => YII2_PATH . '/caching/TagDependency.php',
'yii\caching\WinCache' => YII2_PATH . '/caching/WinCache.php',
'yii\caching\XCache' => YII2_PATH . '/caching/XCache.php',
'yii\caching\ZendDataCache' => YII2_PATH . '/caching/ZendDataCache.php',
'yii\captcha\Captcha' => YII2_PATH . '/captcha/Captcha.php',
'yii\captcha\CaptchaAction' => YII2_PATH . '/captcha/CaptchaAction.php',
'yii\captcha\CaptchaAsset' => YII2_PATH . '/captcha/CaptchaAsset.php',
'yii\captcha\CaptchaValidator' => YII2_PATH . '/captcha/CaptchaValidator.php',
'yii\data\ActiveDataProvider' => YII2_PATH . '/data/ActiveDataProvider.php',
'yii\data\ArrayDataProvider' => YII2_PATH . '/data/ArrayDataProvider.php',
'yii\data\BaseDataProvider' => YII2_PATH . '/data/BaseDataProvider.php',
'yii\data\DataProviderInterface' => YII2_PATH . '/data/DataProviderInterface.php',
'yii\data\Pagination' => YII2_PATH . '/data/Pagination.php',
'yii\data\Sort' => YII2_PATH . '/data/Sort.php',
'yii\data\SqlDataProvider' => YII2_PATH . '/data/SqlDataProvider.php',
'yii\db\ActiveQuery' => YII2_PATH . '/db/ActiveQuery.php',
'yii\db\ActiveQueryInterface' => YII2_PATH . '/db/ActiveQueryInterface.php',
'yii\db\ActiveQueryTrait' => YII2_PATH . '/db/ActiveQueryTrait.php',
'yii\db\ActiveRecord' => YII2_PATH . '/db/ActiveRecord.php',
'yii\db\ActiveRecordInterface' => YII2_PATH . '/db/ActiveRecordInterface.php',
'yii\db\ActiveRelationTrait' => YII2_PATH . '/db/ActiveRelationTrait.php',
'yii\db\AfterSaveEvent' => YII2_PATH . '/db/AfterSaveEvent.php',
'yii\db\BaseActiveRecord' => YII2_PATH . '/db/BaseActiveRecord.php',
'yii\db\BatchQueryResult' => YII2_PATH . '/db/BatchQueryResult.php',
'yii\db\ColumnSchema' => YII2_PATH . '/db/ColumnSchema.php',
'yii\db\ColumnSchemaBuilder' => YII2_PATH . '/db/ColumnSchemaBuilder.php',
'yii\db\Command' => YII2_PATH . '/db/Command.php',
'yii\db\Connection' => YII2_PATH . '/db/Connection.php',
'yii\db\DataReader' => YII2_PATH . '/db/DataReader.php',
'yii\db\Exception' => YII2_PATH . '/db/Exception.php',
'yii\db\Expression' => YII2_PATH . '/db/Expression.php',
'yii\db\IntegrityException' => YII2_PATH . '/db/IntegrityException.php',
'yii\db\Migration' => YII2_PATH . '/db/Migration.php',
'yii\db\MigrationInterface' => YII2_PATH . '/db/MigrationInterface.php',
'yii\db\Query' => YII2_PATH . '/db/Query.php',
'yii\db\QueryBuilder' => YII2_PATH . '/db/QueryBuilder.php',
'yii\db\QueryInterface' => YII2_PATH . '/db/QueryInterface.php',
'yii\db\QueryTrait' => YII2_PATH . '/db/QueryTrait.php',
'yii\db\Schema' => YII2_PATH . '/db/Schema.php',
'yii\db\SchemaBuilderTrait' => YII2_PATH . '/db/SchemaBuilderTrait.php',
'yii\db\StaleObjectException' => YII2_PATH . '/db/StaleObjectException.php',
'yii\db\TableSchema' => YII2_PATH . '/db/TableSchema.php',
'yii\db\Transaction' => YII2_PATH . '/db/Transaction.php',
'yii\db\cubrid\ColumnSchemaBuilder' => YII2_PATH . '/db/cubrid/ColumnSchemaBuilder.php',
'yii\db\cubrid\QueryBuilder' => YII2_PATH . '/db/cubrid/QueryBuilder.php',
'yii\db\cubrid\Schema' => YII2_PATH . '/db/cubrid/Schema.php',
'yii\db\mssql\PDO' => YII2_PATH . '/db/mssql/PDO.php',
'yii\db\mssql\QueryBuilder' => YII2_PATH . '/db/mssql/QueryBuilder.php',
'yii\db\mssql\Schema' => YII2_PATH . '/db/mssql/Schema.php',
'yii\db\mssql\SqlsrvPDO' => YII2_PATH . '/db/mssql/SqlsrvPDO.php',
'yii\db\mssql\TableSchema' => YII2_PATH . '/db/mssql/TableSchema.php',
'yii\db\mysql\ColumnSchemaBuilder' => YII2_PATH . '/db/mysql/ColumnSchemaBuilder.php',
'yii\db\mysql\QueryBuilder' => YII2_PATH . '/db/mysql/QueryBuilder.php',
'yii\db\mysql\Schema' => YII2_PATH . '/db/mysql/Schema.php',
'yii\db\oci\ColumnSchemaBuilder' => YII2_PATH . '/db/oci/ColumnSchemaBuilder.php',
'yii\db\oci\QueryBuilder' => YII2_PATH . '/db/oci/QueryBuilder.php',
'yii\db\oci\Schema' => YII2_PATH . '/db/oci/Schema.php',
'yii\db\pgsql\QueryBuilder' => YII2_PATH . '/db/pgsql/QueryBuilder.php',
'yii\db\pgsql\Schema' => YII2_PATH . '/db/pgsql/Schema.php',
'yii\db\sqlite\ColumnSchemaBuilder' => YII2_PATH . '/db/sqlite/ColumnSchemaBuilder.php',
'yii\db\sqlite\QueryBuilder' => YII2_PATH . '/db/sqlite/QueryBuilder.php',
'yii\db\sqlite\Schema' => YII2_PATH . '/db/sqlite/Schema.php',
'yii\di\Container' => YII2_PATH . '/di/Container.php',
'yii\di\Instance' => YII2_PATH . '/di/Instance.php',
'yii\di\NotInstantiableException' => YII2_PATH . '/di/NotInstantiableException.php',
'yii\di\ServiceLocator' => YII2_PATH . '/di/ServiceLocator.php',
'yii\filters\AccessControl' => YII2_PATH . '/filters/AccessControl.php',
'yii\filters\AccessRule' => YII2_PATH . '/filters/AccessRule.php',
'yii\filters\ContentNegotiator' => YII2_PATH . '/filters/ContentNegotiator.php',
'yii\filters\Cors' => YII2_PATH . '/filters/Cors.php',
'yii\filters\HostControl' => YII2_PATH . '/filters/HostControl.php',
'yii\filters\HttpCache' => YII2_PATH . '/filters/HttpCache.php',
'yii\filters\PageCache' => YII2_PATH . '/filters/PageCache.php',
'yii\filters\RateLimitInterface' => YII2_PATH . '/filters/RateLimitInterface.php',
'yii\filters\RateLimiter' => YII2_PATH . '/filters/RateLimiter.php',
'yii\filters\VerbFilter' => YII2_PATH . '/filters/VerbFilter.php',
'yii\filters\auth\AuthInterface' => YII2_PATH . '/filters/auth/AuthInterface.php',
'yii\filters\auth\AuthMethod' => YII2_PATH . '/filters/auth/AuthMethod.php',
'yii\filters\auth\CompositeAuth' => YII2_PATH . '/filters/auth/CompositeAuth.php',
'yii\filters\auth\HttpBasicAuth' => YII2_PATH . '/filters/auth/HttpBasicAuth.php',
'yii\filters\auth\HttpBearerAuth' => YII2_PATH . '/filters/auth/HttpBearerAuth.php',
'yii\filters\auth\QueryParamAuth' => YII2_PATH . '/filters/auth/QueryParamAuth.php',
'yii\grid\ActionColumn' => YII2_PATH . '/grid/ActionColumn.php',
'yii\grid\CheckboxColumn' => YII2_PATH . '/grid/CheckboxColumn.php',
'yii\grid\Column' => YII2_PATH . '/grid/Column.php',
'yii\grid\DataColumn' => YII2_PATH . '/grid/DataColumn.php',
'yii\grid\GridView' => YII2_PATH . '/grid/GridView.php',
'yii\grid\GridViewAsset' => YII2_PATH . '/grid/GridViewAsset.php',
'yii\grid\RadioButtonColumn' => YII2_PATH . '/grid/RadioButtonColumn.php',
'yii\grid\SerialColumn' => YII2_PATH . '/grid/SerialColumn.php',
'yii\helpers\ArrayHelper' => YII2_PATH . '/helpers/ArrayHelper.php',
'yii\helpers\BaseArrayHelper' => YII2_PATH . '/helpers/BaseArrayHelper.php',
'yii\helpers\BaseConsole' => YII2_PATH . '/helpers/BaseConsole.php',
'yii\helpers\BaseFileHelper' => YII2_PATH . '/helpers/BaseFileHelper.php',
'yii\helpers\BaseFormatConverter' => YII2_PATH . '/helpers/BaseFormatConverter.php',
'yii\helpers\BaseHtml' => YII2_PATH . '/helpers/BaseHtml.php',
'yii\helpers\BaseHtmlPurifier' => YII2_PATH . '/helpers/BaseHtmlPurifier.php',
'yii\helpers\BaseInflector' => YII2_PATH . '/helpers/BaseInflector.php',
'yii\helpers\BaseJson' => YII2_PATH . '/helpers/BaseJson.php',
'yii\helpers\BaseMarkdown' => YII2_PATH . '/helpers/BaseMarkdown.php',
'yii\helpers\BaseStringHelper' => YII2_PATH . '/helpers/BaseStringHelper.php',
'yii\helpers\BaseUrl' => YII2_PATH . '/helpers/BaseUrl.php',
'yii\helpers\BaseVarDumper' => YII2_PATH . '/helpers/BaseVarDumper.php',
'yii\helpers\Console' => YII2_PATH . '/helpers/Console.php',
'yii\helpers\FileHelper' => YII2_PATH . '/helpers/FileHelper.php',
'yii\helpers\FormatConverter' => YII2_PATH . '/helpers/FormatConverter.php',
'yii\helpers\Html' => YII2_PATH . '/helpers/Html.php',
'yii\helpers\HtmlPurifier' => YII2_PATH . '/helpers/HtmlPurifier.php',
'yii\helpers\Inflector' => YII2_PATH . '/helpers/Inflector.php',
'yii\helpers\Json' => YII2_PATH . '/helpers/Json.php',
'yii\helpers\Markdown' => YII2_PATH . '/helpers/Markdown.php',
'yii\helpers\ReplaceArrayValue' => YII2_PATH . '/helpers/ReplaceArrayValue.php',
'yii\helpers\StringHelper' => YII2_PATH . '/helpers/StringHelper.php',
'yii\helpers\UnsetArrayValue' => YII2_PATH . '/helpers/UnsetArrayValue.php',
'yii\helpers\Url' => YII2_PATH . '/helpers/Url.php',
'yii\helpers\VarDumper' => YII2_PATH . '/helpers/VarDumper.php',
'yii\i18n\DbMessageSource' => YII2_PATH . '/i18n/DbMessageSource.php',
'yii\i18n\Formatter' => YII2_PATH . '/i18n/Formatter.php',
'yii\i18n\GettextFile' => YII2_PATH . '/i18n/GettextFile.php',
'yii\i18n\GettextMessageSource' => YII2_PATH . '/i18n/GettextMessageSource.php',
'yii\i18n\GettextMoFile' => YII2_PATH . '/i18n/GettextMoFile.php',
'yii\i18n\GettextPoFile' => YII2_PATH . '/i18n/GettextPoFile.php',
'yii\i18n\I18N' => YII2_PATH . '/i18n/I18N.php',
'yii\i18n\MessageFormatter' => YII2_PATH . '/i18n/MessageFormatter.php',
'yii\i18n\MessageSource' => YII2_PATH . '/i18n/MessageSource.php',
'yii\i18n\MissingTranslationEvent' => YII2_PATH . '/i18n/MissingTranslationEvent.php',
'yii\i18n\PhpMessageSource' => YII2_PATH . '/i18n/PhpMessageSource.php',
'yii\log\DbTarget' => YII2_PATH . '/log/DbTarget.php',
'yii\log\Dispatcher' => YII2_PATH . '/log/Dispatcher.php',
'yii\log\EmailTarget' => YII2_PATH . '/log/EmailTarget.php',
'yii\log\FileTarget' => YII2_PATH . '/log/FileTarget.php',
'yii\log\Logger' => YII2_PATH . '/log/Logger.php',
'yii\log\SyslogTarget' => YII2_PATH . '/log/SyslogTarget.php',
'yii\log\Target' => YII2_PATH . '/log/Target.php',
'yii\mail\BaseMailer' => YII2_PATH . '/mail/BaseMailer.php',
'yii\mail\BaseMessage' => YII2_PATH . '/mail/BaseMessage.php',
'yii\mail\MailEvent' => YII2_PATH . '/mail/MailEvent.php',
'yii\mail\MailerInterface' => YII2_PATH . '/mail/MailerInterface.php',
'yii\mail\MessageInterface' => YII2_PATH . '/mail/MessageInterface.php',
'yii\mutex\DbMutex' => YII2_PATH . '/mutex/DbMutex.php',
'yii\mutex\FileMutex' => YII2_PATH . '/mutex/FileMutex.php',
'yii\mutex\Mutex' => YII2_PATH . '/mutex/Mutex.php',
'yii\mutex\MysqlMutex' => YII2_PATH . '/mutex/MysqlMutex.php',
'yii\mutex\OracleMutex' => YII2_PATH . '/mutex/OracleMutex.php',
'yii\mutex\PgsqlMutex' => YII2_PATH . '/mutex/PgsqlMutex.php',
'yii\rbac\Assignment' => YII2_PATH . '/rbac/Assignment.php',
'yii\rbac\BaseManager' => YII2_PATH . '/rbac/BaseManager.php',
'yii\rbac\CheckAccessInterface' => YII2_PATH . '/rbac/CheckAccessInterface.php',
'yii\rbac\DbManager' => YII2_PATH . '/rbac/DbManager.php',
'yii\rbac\Item' => YII2_PATH . '/rbac/Item.php',
'yii\rbac\ManagerInterface' => YII2_PATH . '/rbac/ManagerInterface.php',
'yii\rbac\Permission' => YII2_PATH . '/rbac/Permission.php',
'yii\rbac\PhpManager' => YII2_PATH . '/rbac/PhpManager.php',
'yii\rbac\Role' => YII2_PATH . '/rbac/Role.php',
'yii\rbac\Rule' => YII2_PATH . '/rbac/Rule.php',
'yii\rest\Action' => YII2_PATH . '/rest/Action.php',
'yii\rest\ActiveController' => YII2_PATH . '/rest/ActiveController.php',
'yii\rest\Controller' => YII2_PATH . '/rest/Controller.php',
'yii\rest\CreateAction' => YII2_PATH . '/rest/CreateAction.php',
'yii\rest\DeleteAction' => YII2_PATH . '/rest/DeleteAction.php',
'yii\rest\IndexAction' => YII2_PATH . '/rest/IndexAction.php',
'yii\rest\OptionsAction' => YII2_PATH . '/rest/OptionsAction.php',
'yii\rest\Serializer' => YII2_PATH . '/rest/Serializer.php',
'yii\rest\UpdateAction' => YII2_PATH . '/rest/UpdateAction.php',
'yii\rest\UrlRule' => YII2_PATH . '/rest/UrlRule.php',
'yii\rest\ViewAction' => YII2_PATH . '/rest/ViewAction.php',
'yii\test\ActiveFixture' => YII2_PATH . '/test/ActiveFixture.php',
'yii\test\ArrayFixture' => YII2_PATH . '/test/ArrayFixture.php',
'yii\test\BaseActiveFixture' => YII2_PATH . '/test/BaseActiveFixture.php',
'yii\test\DbFixture' => YII2_PATH . '/test/DbFixture.php',
'yii\test\Fixture' => YII2_PATH . '/test/Fixture.php',
'yii\test\FixtureTrait' => YII2_PATH . '/test/FixtureTrait.php',
'yii\test\InitDbFixture' => YII2_PATH . '/test/InitDbFixture.php',
'yii\validators\BooleanValidator' => YII2_PATH . '/validators/BooleanValidator.php',
'yii\validators\CompareValidator' => YII2_PATH . '/validators/CompareValidator.php',
'yii\validators\DateValidator' => YII2_PATH . '/validators/DateValidator.php',
'yii\validators\DefaultValueValidator' => YII2_PATH . '/validators/DefaultValueValidator.php',
'yii\validators\EachValidator' => YII2_PATH . '/validators/EachValidator.php',
'yii\validators\EmailValidator' => YII2_PATH . '/validators/EmailValidator.php',
'yii\validators\ExistValidator' => YII2_PATH . '/validators/ExistValidator.php',
'yii\validators\FileValidator' => YII2_PATH . '/validators/FileValidator.php',
'yii\validators\FilterValidator' => YII2_PATH . '/validators/FilterValidator.php',
'yii\validators\ImageValidator' => YII2_PATH . '/validators/ImageValidator.php',
'yii\validators\InlineValidator' => YII2_PATH . '/validators/InlineValidator.php',
'yii\validators\IpValidator' => YII2_PATH . '/validators/IpValidator.php',
'yii\validators\NumberValidator' => YII2_PATH . '/validators/NumberValidator.php',
'yii\validators\PunycodeAsset' => YII2_PATH . '/validators/PunycodeAsset.php',
'yii\validators\RangeValidator' => YII2_PATH . '/validators/RangeValidator.php',
'yii\validators\RegularExpressionValidator' => YII2_PATH . '/validators/RegularExpressionValidator.php',
'yii\validators\RequiredValidator' => YII2_PATH . '/validators/RequiredValidator.php',
'yii\validators\SafeValidator' => YII2_PATH . '/validators/SafeValidator.php',
'yii\validators\StringValidator' => YII2_PATH . '/validators/StringValidator.php',
'yii\validators\UniqueValidator' => YII2_PATH . '/validators/UniqueValidator.php',
'yii\validators\UrlValidator' => YII2_PATH . '/validators/UrlValidator.php',
'yii\validators\ValidationAsset' => YII2_PATH . '/validators/ValidationAsset.php',
'yii\validators\Validator' => YII2_PATH . '/validators/Validator.php',
'yii\web\Application' => YII2_PATH . '/web/Application.php',
'yii\web\AssetBundle' => YII2_PATH . '/web/AssetBundle.php',
'yii\web\AssetConverter' => YII2_PATH . '/web/AssetConverter.php',
'yii\web\AssetConverterInterface' => YII2_PATH . '/web/AssetConverterInterface.php',
'yii\web\AssetManager' => YII2_PATH . '/web/AssetManager.php',
'yii\web\BadRequestHttpException' => YII2_PATH . '/web/BadRequestHttpException.php',
'yii\web\CacheSession' => YII2_PATH . '/web/CacheSession.php',
'yii\web\CompositeUrlRule' => YII2_PATH . '/web/CompositeUrlRule.php',
'yii\web\ConflictHttpException' => YII2_PATH . '/web/ConflictHttpException.php',
'yii\web\Controller' => YII2_PATH . '/web/Controller.php',
'yii\web\Cookie' => YII2_PATH . '/web/Cookie.php',
'yii\web\CookieCollection' => YII2_PATH . '/web/CookieCollection.php',
'yii\web\DbSession' => YII2_PATH . '/web/DbSession.php',
'yii\web\ErrorAction' => YII2_PATH . '/web/ErrorAction.php',
'yii\web\ErrorHandler' => YII2_PATH . '/web/ErrorHandler.php',
'yii\web\ForbiddenHttpException' => YII2_PATH . '/web/ForbiddenHttpException.php',
'yii\web\GoneHttpException' => YII2_PATH . '/web/GoneHttpException.php',
'yii\web\GroupUrlRule' => YII2_PATH . '/web/GroupUrlRule.php',
'yii\web\HeaderCollection' => YII2_PATH . '/web/HeaderCollection.php',
'yii\web\HtmlResponseFormatter' => YII2_PATH . '/web/HtmlResponseFormatter.php',
'yii\web\HttpException' => YII2_PATH . '/web/HttpException.php',
'yii\web\IdentityInterface' => YII2_PATH . '/web/IdentityInterface.php',
'yii\web\JqueryAsset' => YII2_PATH . '/web/JqueryAsset.php',
'yii\web\JsExpression' => YII2_PATH . '/web/JsExpression.php',
'yii\web\JsonParser' => YII2_PATH . '/web/JsonParser.php',
'yii\web\JsonResponseFormatter' => YII2_PATH . '/web/JsonResponseFormatter.php',
'yii\web\Link' => YII2_PATH . '/web/Link.php',
'yii\web\Linkable' => YII2_PATH . '/web/Linkable.php',
'yii\web\MethodNotAllowedHttpException' => YII2_PATH . '/web/MethodNotAllowedHttpException.php',
'yii\web\MultiFieldSession' => YII2_PATH . '/web/MultiFieldSession.php',
'yii\web\MultipartFormDataParser' => YII2_PATH . '/web/MultipartFormDataParser.php',
'yii\web\NotAcceptableHttpException' => YII2_PATH . '/web/NotAcceptableHttpException.php',
'yii\web\NotFoundHttpException' => YII2_PATH . '/web/NotFoundHttpException.php',
'yii\web\RangeNotSatisfiableHttpException' => YII2_PATH . '/web/RangeNotSatisfiableHttpException.php',
'yii\web\Request' => YII2_PATH . '/web/Request.php',
'yii\web\RequestParserInterface' => YII2_PATH . '/web/RequestParserInterface.php',
'yii\web\Response' => YII2_PATH . '/web/Response.php',
'yii\web\ResponseFormatterInterface' => YII2_PATH . '/web/ResponseFormatterInterface.php',
'yii\web\ServerErrorHttpException' => YII2_PATH . '/web/ServerErrorHttpException.php',
'yii\web\Session' => YII2_PATH . '/web/Session.php',
'yii\web\SessionIterator' => YII2_PATH . '/web/SessionIterator.php',
'yii\web\TooManyRequestsHttpException' => YII2_PATH . '/web/TooManyRequestsHttpException.php',
'yii\web\UnauthorizedHttpException' => YII2_PATH . '/web/UnauthorizedHttpException.php',
'yii\web\UnprocessableEntityHttpException' => YII2_PATH . '/web/UnprocessableEntityHttpException.php',
'yii\web\UnsupportedMediaTypeHttpException' => YII2_PATH . '/web/UnsupportedMediaTypeHttpException.php',
'yii\web\UploadedFile' => YII2_PATH . '/web/UploadedFile.php',
'yii\web\UrlManager' => YII2_PATH . '/web/UrlManager.php',
'yii\web\UrlNormalizer' => YII2_PATH . '/web/UrlNormalizer.php',
'yii\web\UrlNormalizerRedirectException' => YII2_PATH . '/web/UrlNormalizerRedirectException.php',
'yii\web\UrlRule' => YII2_PATH . '/web/UrlRule.php',
'yii\web\UrlRuleInterface' => YII2_PATH . '/web/UrlRuleInterface.php',
'yii\web\User' => YII2_PATH . '/web/User.php',
'yii\web\UserEvent' => YII2_PATH . '/web/UserEvent.php',
'yii\web\View' => YII2_PATH . '/web/View.php',
'yii\web\ViewAction' => YII2_PATH . '/web/ViewAction.php',
'yii\web\XmlResponseFormatter' => YII2_PATH . '/web/XmlResponseFormatter.php',
'yii\web\YiiAsset' => YII2_PATH . '/web/YiiAsset.php',
'yii\widgets\ActiveField' => YII2_PATH . '/widgets/ActiveField.php',
'yii\widgets\ActiveForm' => YII2_PATH . '/widgets/ActiveForm.php',
'yii\widgets\ActiveFormAsset' => YII2_PATH . '/widgets/ActiveFormAsset.php',
'yii\widgets\BaseListView' => YII2_PATH . '/widgets/BaseListView.php',
'yii\widgets\Block' => YII2_PATH . '/widgets/Block.php',
'yii\widgets\Breadcrumbs' => YII2_PATH . '/widgets/Breadcrumbs.php',
'yii\widgets\ContentDecorator' => YII2_PATH . '/widgets/ContentDecorator.php',
'yii\widgets\DetailView' => YII2_PATH . '/widgets/DetailView.php',
'yii\widgets\FragmentCache' => YII2_PATH . '/widgets/FragmentCache.php',
'yii\widgets\InputWidget' => YII2_PATH . '/widgets/InputWidget.php',
'yii\widgets\LinkPager' => YII2_PATH . '/widgets/LinkPager.php',
'yii\widgets\LinkSorter' => YII2_PATH . '/widgets/LinkSorter.php',
'yii\widgets\ListView' => YII2_PATH . '/widgets/ListView.php',
'yii\widgets\MaskedInput' => YII2_PATH . '/widgets/MaskedInput.php',
'yii\widgets\MaskedInputAsset' => YII2_PATH . '/widgets/MaskedInputAsset.php',
'yii\widgets\Menu' => YII2_PATH . '/widgets/Menu.php',
'yii\widgets\Pjax' => YII2_PATH . '/widgets/Pjax.php',
'yii\widgets\PjaxAsset' => YII2_PATH . '/widgets/PjaxAsset.php',
'yii\widgets\Spaceless' => YII2_PATH . '/widgets/Spaceless.php',
];
这个映射表以类名为键,以实际类文件为值,Yii所有的核心类都已经写入到这个 classes.php 文件中, 所以,核心类的加载是最便捷,最快的。
现在,来看看这个关键先生 BaseYii::autoload()
public static function autoload($className)
{
if (isset(static::$classMap[$className])) {
$classFile = static::$classMap[$className];
if ($classFile[0] === '@') {
$classFile = static::getAlias($classFile);
}
} elseif (strpos($className, '\\') !== false) {
$classFile = static::getAlias('@' . str_replace('\\', '/', $className) . '.php', 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 '$className' 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() 将类文件加载进来,实现类的加载。
路径别名
我们再看一下getAlias()解析路径别名、setAlias()设置路径别名 :
public static function getAlias($alias, $throwException = true)
{
if (strncmp($alias, '@', 1)) {
// not an alias
return $alias;
}
$pos = strpos($alias, '/');
$root = $pos === false ? $alias : substr($alias, 0, $pos);
if (isset(static::$aliases[$root])) {
if (is_string(static::$aliases[$root])) {
return $pos === false ? static::$aliases[$root] : static::$aliases[$root] . substr($alias, $pos);
}
foreach (static::$aliases[$root] as $name => $path) {
if (strpos($alias . '/', $name . '/') === 0) {
return $path . substr($alias, strlen($name));
}
}
}
if ($throwException) {
throw new InvalidParamException("Invalid path alias: $alias");
}
return false;
}
public static function setAlias($alias, $path)
{
if (strncmp($alias, '@', 1)) {
$alias = '@' . $alias;
}
$pos = strpos($alias, '/');
$root = $pos === false ? $alias : substr($alias, 0, $pos);
if ($path !== null) {
$path = strncmp($path, '@', 1) ? rtrim($path, '\\/') : static::getAlias($path);
if (!isset(static::$aliases[$root])) {
if ($pos === false) {
static::$aliases[$root] = $path;
} else {
static::$aliases[$root] = [$alias => $path];
}
} elseif (is_string(static::$aliases[$root])) {
if ($pos === false) {
static::$aliases[$root] = $path;
} else {
static::$aliases[$root] = [
$alias => $path,
$root => static::$aliases[$root],
];
}
} else {
static::$aliases[$root][$alias] = $path;
krsort(static::$aliases[$root]);
}
} elseif (isset(static::$aliases[$root])) {
if (is_array(static::$aliases[$root])) {
unset(static::$aliases[$root][$alias]);
} elseif ($pos === false) {
unset(static::$aliases[$root]);
}
}
}
从其运作原理看,最快找到类的方式是使用映射表。 其次,Yii中所有的类名, 除了符合规范外,还需要提前注册有效的根别名。
看到现在还是没找到common文件夹中文件是怎么载入的啊!那我们再看一下下面这个文件的内容:
project/common/config/bootstrap.php
内容
<?php
Yii::setAlias('@common', dirname(__DIR__));
Yii::setAlias('@admin', dirname(dirname(__DIR__)) . '/admin');
Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');
有没有找到感觉。
这里只是知道了Yii2自动加载一部分的内容,但是上面并没有找到@common所在文件被加载的地方,
也没有找到我们经常使用的 Yii::属性名
相关的依据,不过可以推测有一个@yii的参数定义,
应该是框架初始化加载的时候Yii::setAlias()
的,这部分内容放到别名笔记中分析。
vendor自动加载分析
这部分可以参阅 https://ibaiyang.github.io/blog/composer/2023/04/07/Composer-原理.html
vendor目录是composer引入的第三方库文件所在根位置。
在入口脚本中,除了Yii自己的autoloader,还有一个第三方的autoloader:
// 这个是第三方的autoloader
require __DIR__ . '/../../vendor/autoload.php';
我们看一下/vendor/autoload.php
这个文件的内容:
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitf9c5993908f68dcde48f483fcf1c6e6d::getLoader();
这个文件是Composer生成的,每次我们用Composer进行install/update
时都自动重新生成,
是为了引入ComposerAutoloaderInitf9c5993908f68dcde48f483fcf1c6e6d
这个随机类。
这个其实是Composer提供的autoloader。Yii使用Composer来作为包依赖管理器,因此,建议保留Composer的autoloader, 尽管Yii的autoloader也能自动加载使用Composer安装的第三方库、扩展等,而且更为高效。但考虑到毕竟是人家安装的, 人家还有一套自己专门的规则,从维护性、兼容性、扩展性来考虑,建议保留Composer的autoloader。
如果还有其他的autoloader,一定要在Yii的autoloader注册之前完成注册,以保证Yii的autoloader总是最先被调用。
如果你有自己的autoloader,也可以不安装Yii的autoloaer,只是这样未必能有Yii的高效,且还需要遵循一套类似的类命名和加载的规则。 就个人的经验而言,Yii的autoloader完全够用,没必要自己重复造轮子。
至于Composer如何自动加载类文件,这里就不过多的占用篇幅了。可以看看 Composer的文档 。
再看一下/vendor/composer/autoload_real.php
的文件内容:
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitf9c5993908f68dcde48f483fcf1c6e6d
{
private static $loader;
public static function loadClassLoader($class)
{
// 如果传入的类名是Composer\Autoload\ClassLoader,则加载当前相同目录下的ClassLoader.php
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
// spl_autoload_register注册类文件自动加载函数
spl_autoload_register(array('ComposerAutoloaderInitf9c5993908f68dcde48f483fcf1c6e6d', 'loadClassLoader'), true, true);
// self::$loader $loader 赋值
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
// spl_autoload_unregister取消类文件自动加载函数
spl_autoload_unregister(array('ComposerAutoloaderInitf9c5993908f68dcde48f483fcf1c6e6d', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitf9c5993908f68dcde48f483fcf1c6e6d::getInitializer($loader));
} else {
$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);
}
}
// spl_autoload_register再次注册类文件自动加载函数
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInitf9c5993908f68dcde48f483fcf1c6e6d::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequiref9c5993908f68dcde48f483fcf1c6e6d($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequiref9c5993908f68dcde48f483fcf1c6e6d($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
可以看到上面的随机类名就在这里。这里点到为止,就不继续分析了,是Composer/PSR4支持的内容。
/vendor/composer/ClassLoader.php
文件内容
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$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;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
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;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}
其他
include 和 require 语句用于在执行流中插入写在其他文件中的有用的代码。
include 和 require 除了处理错误的方式不同之外,在其他方面都是相同的:
require 生成一个致命错误(E_COMPILE_ERROR),在错误发生后脚本会停止执行。 include 生成一个警告(E_WARNING),在错误发生后脚本会继续执行。
因此,如果您希望继续执行,并向用户输出结果,即使包含文件已丢失, 那么请使用 include。否则,在框架、CMS 或者复杂的 PHP 应用程序编程中, 请始终使用 require 向执行流引用关键文件。 这有助于提高应用程序的安全性和完整性,在某个关键文件意外丢失的情况下。
提示:
require 一般放在 PHP 文件的最前面,程序在执行前就会先导入要引用的文件;
include 一般放在程序的流程控制中,当程序执行时碰到才会引用,简化程序的执行流程。
require 引入的文件有错误时,执行会中断,并返回一个致命错误;
include 引入的文件有错误时,会继续执行,并返回一个警告。
参考资料
深入理解Yii2.0 » Yii 约定 » Yii的类自动加载机制 http://www.digpage.com/autoload.html
Yii的类自动加载机制 https://www.kancloud.cn/kancloud/yii-in-depth/50795
PHP spl_autoload_register()相关 https://ibaiyang.github.io/blog/php/2019/04/01/PHP-spl_autoload_register()%E7%9B%B8%E5%85%B3.html
include 和 require 语句 http://www.runoob.com/php/php-includes.html
C 库函数 - strncmp() https://www.runoob.com/cprogramming/c-function-strncmp.html