Yii2学习笔记系列9——Application Structure-Applications(应用结构之应用)

三 13 四月 2016

Yii2学习笔记系列9——Application Structure-Applications(应用结构之应用)

应用主体

应用主体是管理Yii应用系统整体结构和声明周期的对象,每个Yii应用系统只能包含一个应用主体,应用主体在入口脚本中创建,并且可以通过表达式\Yii::$app全局范围内访问。

Yii有两种应用主体:yii\web\Applicationyii\console\Application,分别用来处理网页请求和控制台请求。

应用主体配置

如下所示,当入口脚本创建了一个应用主体,它会加载一个配置文件并传给应用主体。

<?php

require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

// 加载应用主体配置
$config = require(__DIR__ . '/../config/web.php');

// 实例化应用主体、配置应用主体
(new yii\web\Application($config))->run();

类似其他配置文件,应用主体配置文件标明如何设置应用对象初始属性,由于应用主体配置比较复杂,一般保存在多个类似web.php这样儿的配置文件中。

应用主体属性

应用主体配置文件中有许多重要的属性需要配置,这些属性指定应用主体的运行环境,例如应用主体需要知道如何加载控制器,临时文件保存到哪儿等等。

  • 必要属性

在一个应用中,至少需要配置两个属性:yii\base\Application::idyii\base\Application::basePath

yii\base\Application::id属性是用来区分其它应用的唯一标识ID,主要给程序使用,方便协作起见,最好使用数字作为应用主体ID,但不强制要求为数字。

yii\base\Application::basePath属性用来指定该应用的根目录,根目录包含应用系统所有受保护的源代码,在根目录下可以看到对应MVC设计模式的modelsviewscontrollers等子目录。

可以使用路径或者路径别名来配置yii\base\Application::basePath属性,但不管哪种方式,都要确保对应的目录必须存在,否则系统会抛出异常。系统通过调用realpath()函数来规范化配置的路径。

yii\base\Application::basePath属性经常用于派生一些其他重要路径(如runtime路径),因此,系统预定义@app代表这个路径,派生路径可以通过这个别名组成新的路径(如@app/runtime代表runtime的路径)。

  • 重要属性

本小节所描述的属性通常需要设置,因为不同的应用属性不同。

yii\base\Application::aliases属性允许你使用一个数组定义多个别名,数组的key为别名的名字,值为对应的路径,例如:

<?php
[
    'aliases' => [
        '@name1' => 'path/to/path1',
        '@name2' => 'path/to/path2',
    ],
]
?>

这个属性用来代替调用Yii::setAlias()方法,实现了通过应用配置项来定义别名。

yii\base\Application::bootstrap属性允许你使用数组来指定启动阶段yii\base\Application::bootstrap()需要运行的组件,比如,如果你希望一个模块自定义URL规则,你可以将模块ID加入到bootstrap数组中。

属性中的每个组件需要指定以下一项:

- 应用组件ID
- 模块ID
- 类名
- 配置数组
- 创建并返回一个组件的无名称函数

例如:

```php
<?php [ 'bootstrap' => [ // 应用组件ID或模块ID 'demo',

    // 类名
    'app\components\Profiler',

    // 配置数组
    [
        'class' => 'app\components\Profiler',
        'level' => 3,
    ],

    // 无名称函数
    function () {
        return new app\components\Profiler();
    }
],

] ?>

补充如果模块ID和应用组件ID同名优先使用应用组件ID如果你想使用模块ID可以使用如下无名称函数返回模块ID

```php
<?php
[
    function () {
        return Yii::$app->getModule('user');
    },
]
?>

在启动阶段,每个组件都会被实例化。如果组件类实现接口yii\base\BootstrapInterface,也会调用yii\base\BootstrapInterface::bootstrap()方法。

例如,基础应用模板的应用主体配置中,开发环境下会在启动阶段运行debuggii模块。

<?php
if (YII_ENV_DEV) {
    // configuration adjustments for 'dev' environment
    $config['bootstrap'][] = 'debug';
    $config['modules']['debug'] = 'yii\debug\Module';

    $config['bootstrap'][] = 'gii';
    $config['modules']['gii'] = 'yii\gii\Module';
}
?>

yii\web\Application::catchAll属性仅yii\web\Application网页应用支持,它指定一个要处理所有用户请求的控制器方法,通常在维护模式下使用,同一个方法处理所有用户请求。

该配置是一个数组,第一项指定动作的路由,剩下的数组项(key-value成对)指定传递给动作的参数,例如:

<?php
[
    'catchAll' => [
        'offline/notice',
        'param1' => 'value1',
        'param2' => 'value2',
    ],
]
?>

yii\base\Application::components是最重要的属性,它允许你注册多个可以在其它地方使用的应用组件,例如:

<?php
[
    'components' => [
        'cache' => [
            'class' => 'yii\caching\FileCache',
        ],
        'user' => [
            'identityClass' => 'app\models\User',
            'enableAutoLogin' => true,
        ],
    ],
]
?>

每一个应用组件指定一个key-value键值对的数组,key代表组件ID,value代表类名或配置。

在应用中可以任意注册组件,并可以通过表达式\Yii::$app->ComponentID全局访问。

详情请阅读应用组件一节。

yii\base\Application::controllerMap属性允许你指定一个控制器ID到任意控制器类。Yii遵循一个默认的规则,指定控制器ID到任意控制器类(如post对应app\controllers\PostController),通过配置这个属性,可以打破这个默认规则,在下面的例子中,account对应到app\controllers\UserControllerarticle对应到app\controllers\PostController

[
    'controllerMap' => [
        [
            'account' => 'app\controllers\UserController',
            'article' => [
                'class' => 'app\controllers\PostController',
                'enableCsrfValidation' => false,
            ],
        ],
    ],
]

数组的键代表控制器ID,数组的值代表对应的类名。

yii\base\Application::controllerNamespace属性指定控制器类默认的命名空间,默认为app\controllers,比如控制器ID为post默认对应PostController(不带命名空间),类全名为app\controllers\PostController

要保证全面的合格的控制器类能够被自动载入,并且控制器类的真实命名空间要能够和该属性的值匹配,这点是很重要的,否则当你进入应用时,你将会收到“Page Not Found”错误。如果你想改变上述的规则,可以配置controllerMap属性。

yii\base\Application::language属性指定应用展示给终端用户的语言,默认为en标识英文,如果需要更改为其他语言可以配置该属性。

该属性影响各种国际化,包括信息翻译、日期格式、数字格式等,例如yii\jui\DatePicker小部件会根据该属性展示对应语言的日历以及日期格式。

推荐遵循IETF language tag来设置语言,例如en代表英文,en-US代表英文(美国)。该属性的更多信息可以参考国际化一节。

yii\base\Application::modules属性指定应用所包含的模块,该属性使用数组包含多个模块类配置,数组的键为模块ID,例如:

<?php
[
    'modules' => [
        // "booking" 模块以及对应的类
        'booking' => 'app\modules\booking\BookingModule',

        // "comment" 模块以及对应的配置数组
        'comment' => [
            'class' => 'app\modules\comment\CommentModule',
            'db' => 'db',
        ],
    ],
]
?>

yii\base\Application::name属性指定你可能想展示给终端用户的应用名称,不同于需要唯一性的yii\base\Application::id属性,该属性可以不唯一。

yii\base\Application::params属性是一个数组,指定可以全局访问的参数,代替程序中硬编码的数字和字符,应用中的参数定义到一个单独的文件中并且保证随时可以访问是一个好习惯,例如,用参数定义缩略图的长宽如下:

<?php
[
    'params' => [
        'thumbnail.size' => [128, 128],
    ],
]
?>

然后,当你需要使用时,采用如下的方式即可:

<?php
$size = \Yii::$app->params['thumbnail.size'];
$width = \Yii::$app->params['thumbnail.size'][0];
?>

yii\base\Application::sourceLanguage属性允许你修改PHP运行环境中的默认时区,配置该属性本质上就是调用PHP函数date_default_timezone_set(),例如:

<?php
[
    'timeZone' => 'China/Shanghai',
]
?>

yii\base\Application::version属性用于指定应用的版本,默认是'1.0',其他代码不使用的话可以不配置。

  • 实用属性

本小节描述的属性不经常设置,通常使用系统默认值。

yii\base\Application::charset属性指定应用所使用的字符集,默认是'UTF-8',绝大部分应用都在使用,除非已有的系统大量使用非Unicode数据才需要更改该属性。

yii\base\Application::defaultRoute属性指定未经过配置的请求的响应路由规则,路由规则可能包含模块ID,控制器ID,动作ID等,如helppost/createadmin/post/create,如果动作ID没有指定,会使用yii\base\Controller:defaultAction中指定的默认值。

对于yii\web\Application网页应用,默认值为'site'对应SiteController控制器,并使用默认的动作,因此不带路由的访问应用,默认会显示app\controllers\SiteController::actionIndex()的结果。

对于yii\console\Application控制台应用,默认值是'help'对应yii\console\controllers\HelpController::actionIndex(),因此,如果执行的命令不带参数的话,默认会显示帮助信息。

yii\base\Application::extensions属性用数组列表指定应用安装和使用的扩展,默认使用@vendor/yiisoft/extensions.php文件返回的数组,当你使用Composer安装扩展时,extensions.php会被自动生成和维护更新,所以大多数情况下不需要配置该属性。

特殊情况下如果你想手动维护扩展,可以参照如下代码配置该属性:

<?php
[
    'extensions' => [
        [
            'name' => 'extension name',
            'version' => 'version number',
            'bootstrap' => 'BootstrapClassName',  // 可选配,可为配置数组
            'alias' => [  // 可选配
                '@alias1' => 'to/path1',
                '@alias2' => 'to/path2',
            ],
        ],

        // ... 更多像上面的扩展 ...

    ],
]
?>

上述代码表示该属性包含一个扩展定义数组,每个扩展为一个包含nameversion项的数组,如果该扩展需要在引导启动阶段运行,需要配置bootstrap以及对应的引导启动类名或configuration数组,扩展也可以定义别名。

yii\base\Application::layout属性指定渲染视图默认使用的布局名字,默认值是'main'对应布局路径下的main.php文件,如果布局路径和视图路径都是默认值,默认布局文件可以使用路径别名,如@app/views/layouts/main.php,如果不想设置默认布局文件,可以设置该属性为false,这种做法比较罕见。

yii\base\Application::layoutPath属性指定查找布局文件的路径,默认值是视图路径下的layouts子目录,如果视图路径使用默认值,默认的布局路径别名是@app/views/layouts,该属性需要配置成一个目录或者路径别名。

yii\base\Application::runtimePath属性指定临时文件如日志文件、缓存文件等保存路径,默认值为带别名的@app/runtime,可以配置该属性为一个目录或者路径别名,注意应用运行时需要有对该路径的写入权限,由于该目录下可能包含敏感信息,所以该目录不允许终端用户访问。为了简化访问该路径,Yii预定义别名@runtime代表该路径。

yii\base\Application::viewPath属性指定视图文件的根目录,默认值为带别名的@app/views,可以配置它为一个目录或者路径别名。

yii\base\Application::vendorPath属性指定Composer管理的供应商路径,该路径包含应用使用的包括Yii框架在内的所有第三方库,默认值为带别名的@app/vendor。可以配置它为一个目录或者路径别名,当你修改时,请务必修改对应的Composer配置。为了简化访问该路径,Yii预定义别名@vendor代表该路径。

yii\console\Application::enableCoreCommands属性仅yii\console\Application控制台应用支持,用来指定是否启用Yii中的核心命令,默认值是true

应用事件

应用在处理请求的过程中会触发事件,可以在配置文件中配置事件处理代码,如下所示:

<?php
[
    'on beforeRequest' => function ($event) {
        // ...
    },
]
?>

on eventName语法的用法在配置一节中有详细的描述。

另外,在应用主体实例化后,你可以在引导启动阶段附加事件处理代码,例如:

<?php
\Yii::$app->on(\yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) {
    // ...
});
?>

yii\base\Application::EVENT_BEFORE_REQUEST事件在应用处理请求之前,实际的事件名为beforeRequest。在事件触发钱,应用主体已经实例化并配置好了,所以通过事件机制将你的代码嵌入到请求处理过程中是很不错的选择。例如在事件处理中根据某些参数动态设置yii\base\Application::language语言属性。

yii\base\Application::EVENT_AFTER_REQUEST事件在应用处理请求之后但尚未返回响应之前触发,实际的事件名为afterRequest。该事件触发时,请求已经被处理完,可以做一些请求后处理或者自定义响应。注意:yii\web\Response组件在发送响应给终端用户时也会触发一些事件,这些事件都在本事件之后触发。

yii\base\Application::EVENT_BEFORE_ACTION事件在每个控制器动作运行之前会被触发,实际的事件名为beforeAction。该事件的参数为yii\base\ActionEvent实例,事件处理中可以设置yii\base\ActionEvent::isValid为false停止运行后续动作。例如:

<?php
[
    'on beforeAction' => function ($event) {
        if (some condition) {
            $event->isValid = false;
        } else {
        }
    },
]
?>

注意模块和控制器都会触发beforeAction事件,应用主体对象首先触发该事件,然后模块触发(如果存在模块的话),最后控制器触发。任何一个事件处理中设置yii\base\ActionEvent::isValid设置为false都会停止触发后面的事件。

yii\base\Application::EVENT_AFTER_ACTION事件在每个控制器动作运行之后被触发,实际的事件名为afterAction。该事件的参数为yii\base\ActionEvent实例,通过yii\base\ActionEvent::result属性,事件处理可以访问和修改动作的结果,例如:

<?php
[
    'on afterAction' => function ($event) {
        if (some condition) {
            // 修改 $event->result
        } else {
              // 干点儿别的
        }
    },
]
?>

注意模块和控制器都会触发afterAction事件,这些对象的触发顺序和beforeAction相反,也就是说,控制器最先触发,然后是模块(如果有模块的话),最后是应用主体。

应用主体生命周期

当运行入口脚本处理请求时,应用主体会经历以下生命周期:

  1. 入口脚本加载应用主体配置数组。
  2. 入口脚本创建一个应用主体实例:
    • 调用yii\base\Application::preInit()配置几个高级应用主体属性,例如yii\base\Application::basePath。
    • 注册yii\base\Application::errorHandler错误处理方法。
    • 配置应用主体属性。
    • 调用yii\base\Application::init()初始化,该函数会调用yii\base\Application::bootstrap()运行引导启动组件。
  3. 入口脚本调用yii\base\Application::run()运行应用主体:
    • 触发yii\base\Application::EVENT_BEFORE_REQUEST事件。
    • 处理请求:解析请求路由和相关参数;创建路由指定的模块、控制器和动作对应的类,并运行动作。
    • 触发yii\base\Application::EVENT_AFTER_REQUEST事件。
    • 发送响应到终端用户。
  4. 入口脚本接收应用主体传来的退出状态并完成请求的处理。

Category: PHP Develop

Comments