Yii2学习笔记系列18——Application Structure-Extensions(应用结构之扩展)

六 21 五月 2016

扩展

扩展专门设计的可以在Yii应用中随时被拿来使用并且可以重新发布的软件包,例如,yiisoft/yii2-debug扩展可以实现在你的应用的每个页面底部添加一个用于调试的工具栏,帮助你简单的抓取页面生成的情况。你可以使用扩展来加速你的软件开发进程,你也可以将你的代码打包成扩展来分享给他人使用。

补充:本文中我们使用的术语“扩展”特指Yii软件包,而术语“软件包”和“库”特指Yii专用,而非通常意义的软件包。

使用扩展

想要使用一个扩展,你需要先安装它。大多数的扩展以Composer软件包的形式发布,这样儿的软件包可以通过以下简单的两步来完成安装:

  1. 修改你的应用程序的composer.json文件,写明你要安装的扩展(Composer软件包)。
  2. 运行composer install命令来安装指定的扩展。

注意,如果没有安装Composer命令的话需要先进行安装。

默认情况下,Composer会安装的是在Packagist(最大的开源Composer代码库)中注册过的软件包。你可以在Packagist中查找所需软件包,也可以创建你自己的代码库并且配置Composer来使用该仓库。如果你是在开发私有扩展并且只想在你的项目内共享的话,这种方式就非常有用了。

Composer安装的扩展保存在BasePath/vendor目录下,其中BasePath代指应用程序的基础路径。因为Composer同时还是一个依赖管理器,当你使用Composer来安装一个软件包时,也会将该软件包所依赖的所有软件包也相应的安装。

例如,想要安装yiisoft/yii2-imagine扩展,你需要先参照如下所示修改你的composer.json

{
    // ...
    "require": {
        // ... 其他依赖
        "yiisoft/yii2-imagine": "*"
    }
}

安装完成后你应该可以在BasePath/vendor目录下看到yiisoft/yii2-imagine目录了,同时你也应该可以看到一个imagine/imagine目录,其中安装所依赖的软件包。

补充: yiisoft/yii2-imagine是Yii开发团队开发和维护的一个核心扩展,所有核心扩展均由Packagist集中管理,命名为yiisoft/yii2-xyz,其中的xyz表示不同的扩展。

现在你可以使用扩展了,它们就像是应用程序的一部分一样。下面的示例展示了如何使用yiisoft/yii2-imagine扩展提供的yii\imagine\Image类:

use Yii;
use yii\imagine\Image;

// 生成一张缩略图
Image::thumbnail('@webroot/img/test-image.jpg', 120, 120)
    ->save(Yii::getAlias('@runtime/thumb-test-image.jpg'), ['quality' => 50]);

补充: 扩展类由Yii class autoloader自动加载。

手动安装扩展

在极少数情况下,你可能想要手动安装一些或者全部的扩展,而不是依赖Composer来安装。要做到这一点,你需要:

  1. 下载扩展压缩包并且将其解压到vendor目录
  2. 如果扩展提供了自动加载器类的话,安装它
  3. 按照说明下载和安装所有的依赖扩展包

如果扩展包中没有类的自动加载器但是该扩展包也遵循了PSR-4标准的话,你可以使用Yii提供的类自动加载器来加载扩展类。你需要做的仅仅是为该扩展的根目录声明一个root alias。例如,假设你已经在vendor.mycompany/myext目录下安装了一个扩展,并且扩展类在myext命名空间下,然后你就可以将如下的代码包含在你的应用程序配置文件中:

[
    'aliases' => [
        '@myext' => '@vendor/mycompany/myext',
    ],
]

创建扩展

当你想要把自己的代码分享给其他人使用的时候,你可能考虑创建扩展。扩展可以包含任何你想要的代码,比如一个帮助类、一个小部件、一个模块,等等。

建议你按照Composer的条款创建一个扩展,以便更容易安装和方便他人使用,如上一章节的描述。

以下是将扩展创建为一个Composer软件包的基本步骤:

  1. 为你的扩展创建一个工程,并将它存放在版本控制库中,例如github.com,扩展的开发和维护工作都应该在这个工程中进行。
  2. 在项目工程根目录下,创建Composer所需的composer.json文件,详情请参考后续的章节。
  3. 在一个Composer仓库中注册你的扩展,比如Packagist,以便其他用户能够找到你的扩展并且使用Composer进行安装。
composer.json

每个Composer软件包根目录下都包含一个composer.json文件,该文件包含了软件包的元数据。你可以在Composer手册中找到该文件的完整说明。以下是yiisoft/yii2-imaginecomposer.json内容:

{
    // package name - 包名
    "name": "yiisoft/yii2-imagine",

    // package type - 包类型
    "type": "yii2-extension",

    "description": "The Image integration for Yii framework",
    "keywords": ["yii2", "imagine", "image", "helper"],
    "license": "BSD-D-Clause",
    "support": {
        "issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Aimagine",
        "forum": "http://www.yiiframework.com/forum/",
        "wiki": "http://www.yiiframework.com/wiki/",
        "irc": "irc://irc.freenode.net/yii",
        "source": "https://github.com/yiisoft/yii2",
    },
    "authors": [
        {
            "name": "Antonio Ramirez",
            "email": "amigo.cobos@gmail.com",
        }
    ],
    // package dependencies - 包依赖
    "require" : {
        "yiisoft/yii2": "*",
        "imagine/imagine": "v0.5.0",
    },
    // class autoloading specs - 类自动加载功能
    "autoload": {
        "psr-4": {
            "yii\\imagine\\": ""
        }
    }
}
Package Name - 包名

每个Composer软件包都应当包含一个唯一的包名,用以跟其它的软件包区分。包名的格式是vendorName/projectName,例如包名yiisoft/yii2-imagine中,vendor名和project名分别是yiisoftyii2-imagine

不要使用yiisoft作为你的vendor名,因为它被Yii的核心代码预留使用了。

我们推荐使用yii2-作为你的包名的前缀,表示它是Yii2的扩展,例如myname/yii2-mywidget,以便用户辨别是否Yii2的扩展。

Package Type - 包类型

将你的扩展指明为yii2-extension类型很重要,便于在安装的时候能够被识别到是Yii扩展。

当一个用户运行composer install命令来安装一个扩展的时候,vendor/yiisoft/extension.php文件会自动升级并包含新扩展的信息。从该文件中,Yii应用程序能够知道哪些扩展被安装了(可以通过yii\base\Application::extensions访问)。

Dependencies - 依赖

你的扩展依赖于Yii(必须的),所以你应该将其(yiisoft/yii2)列在你的composer.json文件中的require部分。如果你的扩展还依赖其他的扩展或者第三方类库,你应该也把它们都列出来。确保你为每一个依赖包列出了适当的版本约束条件(比如1.*@stable)等,当你发布一个稳定版本时,你所依赖的软件包也应当使用稳定版本。

大多数的JS/CSS包是用Bower来管理而非Composer,你可以使用Composer asset插件使其可以通过Composer来管理这类包。如果你的扩展依赖Bower软件包,你可以如下例所示那样儿简单的在composer.json中将其列出来:

{
    // package dependencies - 包依赖
    "require": {
        "bower-asset/jquery": ">=1.11.*"
    }
}

上述代码表明该扩展依赖于jquery Bower包,一般来说,你可以在composer.json中通过bower-asset/PackageName指定Bower包,通过npm-asset/PackageName指定NPM包。当Composer安装Bower和NPM软件包时,会默认分别安装到@vendor/bower/PackageName@vendor/npm/Packages这两个目录下,分别可以用@bower/PackageName@npm/PackageName别名指向。

更多关于资源管理的详情,请参考Assets章节。

Class Autoloading - 类自动加载

为了让你的类能够被Yii或者Composer类自动加载器自动加载,你应该在composer.json文件中指定autoload条目,如下所示:

{
    // ....

    "autoload": {
        "psr-4": {
            "yii\\imagine\\": ""
        }
    }
}

你可以列出一个或多个根命名空间以及他们相应的文件路径。

当扩展安装到应用中后,Yii能够为每个所列出的根命名空间创建一个指向命名空间对应的目录的别名。例如,上述的autoload条目声明将对应别名@yii/imagine

推荐的做法

由于扩展意味着会被其他人使用,所以你在开发过程中通常需要额外的付出,下面我们介绍一些通用的以及推荐的做法,以便帮助你创建高质量的扩展。

命名空间

为了避免命名冲突以及可以使你的扩展中的类能够被自动加载,你的泪应当使用命名空间,并且遵循PSR-4标准PSR-0标准来命名你的扩展中的类。

你的泪的命名空间应该以vendorName\extensionName,其中extensionName和项目名相同,除了它没有yii2-前缀。例如,对yiisoft/yii2-imagine扩展来说,我们用yii\imagine作为它的类的命名空间。

不要使用yiiyii2或者yiisoft作为你的vendor名,这些名称已经被Yii核心代码预留使用了。

引导启动类

有时你可能想要你的扩展能够在应用程序的引导启动过程阶段执行一些代码。例如,你的扩展可能需要响应应用程序的beginRequest事件来做一些环境设置工作。除了可以明确地指导使用你的扩展的用户将你的事件处理代码附加到beginRequest事件上,一个更好的方式就是让该过程自动化处理。

为了实现这个目标,你可以创建一个所谓的bootstrapping class(启动引导类)实现yii\base\BootstrapInterface接口。例如,

namespace myname\mywidget;

use yii\base\BootstrapInterface;
use yii\base\Application;

class MyBootstrapClass implements BootstrapInterface
{
    public function bootstrap($app)
    {
        $app->on(Application::EVENT_BEFORE_REQUEST, function() {
            // 做点什么
        });
    }
}

然后将这个类在composer.json文件中列出来,如下所示:

{
    // ...

    "extra": {
        "bootstrap": "myname\\mywidget\\MyBootstrapClass"
    }
}

当这个扩展安装到应用后,Yii将在每一个请求的引导启动过程中自动实例化并调用其yii\base\BootstrapInterface::bootstrap()方法。

使用数据库

你的扩展可能需要使用数据库,不要认为使用你的扩展的应用程序总是会使用Yii::$db作为数据库连接。你应该在需要访问数据库的类中声明一个db属性,该属性允许使用你的扩展的用户可以自定义扩展所需要使用的数据库连接。例如,你可以参考yii\caching\DbCache类看一下它是如何声明和使用db属性的。

如果你的扩展需要创建特定的数据库表,或者修改数据库表结构,你应该:

  • 提供数据迁移来操作数据库,而不是使用SQL文本文件
  • 尽量使迁移文件适用于不同的DBMS
  • 在迁移文件中避免使用Active Record
使用Assets

如果你的扩展是一个小部件或者一个模块,它可能需要使用一些Assets。例如,一个模块可能会展示一些包含图片、JS和CSS的页面。由于扩展的文件都放在同一个目录下,安装后无法通过Web访问,你有两种方式可以使得这些asset文件目录通过Web被访问到:

  • 让扩展的使用者手动将这些资源文件拷贝到特定的可以通过Web访问的文件夹下
  • 声明一个资源包并且依靠asset发布机制自动将这些文件(资源包中列出的文件)拷贝到可以通过Web访问的文件夹

我们推荐你使用第二种方式以便你的扩展更加易用,更详细的关于如何处理assets的细节请参阅资源一节。

国际化和本地化

你的扩展可能需要在支持不同语言的应用中使用,因此,如果你的扩展想要显示内容给终端用户,你应当尝试实现国际化和本地化,特别是:

  • 如果扩展显示面向最终用户的消息,这些消息应该被Yii::t()方法包裹起来以便他们可以被翻译。只给开发者参考的信息(如内部异常信息)等不需要做翻译。
  • 如果扩展显示数字、日期等,你应该用yii\i18n\Formatter中适当的格式化规则做格式化处理。

更多详情请参考国际化一节。

测试

你肯定想让你的扩展可以顺畅无瑕疵的运行并且不会给使用者带来麻烦,想要实现这个目标,你需要在发布你的扩展之前进行测试。

建议你创建各种测试用例来覆盖测试你的扩展代码,而非仅仅通过手工测试。每次你发布新版本的扩展之前都可以简单的运行一下这些测试用例确保一切正常。Yii提供了测试支持,可以让你更容易的编写单元测试、验收测试和功能测试。详情请参考测试一节。

版本控制

你应该为每一个扩展定一个版本号(如1.0.1),我们推荐版本号命名时参照semantic versioning来决定用什么样儿的版本号。

发布

想让其他人知道你的扩展,你应该公开发布。

如果你是第一次发布一个扩展,你应该在Composer仓库(例如Packgist)中将其进行注册。之后,你只需在版本管理库中创建一个标签(例如v1.0.1),然后通知Composer代码库,其他人就能够查找到这个新的发布版本了,并且可以通过Composer代码库安装和更新该扩展。

在发布你的扩展的时候,除了代码文件,你还应该考虑包含如下内容,以帮助使用者更好地了解和使用你的扩展:

  • 根目录下的README文件:它用来描述你的扩展能做什么以及如何安装和使用。我们建议你采用Markdown格式并且将其命名为readme.md
  • 根目录下的修改日志文件:它列举每个版本的发布都做了哪些更改。该文件可以用Markdown格式编写并命名为changelog.md
  • 根目录下的升级文件:它给出如何从旧版本升级该扩展的指导,该文件可以用Markdown格式编写并命名为upgrade.md
  • 入门指南、演示代码、截图等:如果你的扩展提供了许多功能并且无法在readme文件中完整描述时,可以用这些文件
  • API文档:你的代码应该文档齐全并且允许其他人更容易阅读和理解。你可以参考Object class file来学习如何为你的代码写文档

补充:你的代码注释可以以Markdown格式编写。yiisoft/yii2-apidoc扩展提供了从你的代码注释生成漂亮的API文档的功能。

补充:虽然不做具体要求,但我们还是建议你的扩展遵循某个编码规范,你可以参考核心框架代码规范

核心扩展

Yii提供了以下的核心扩展,由Yii开发团队开发和维护,这些扩展全都在Packagist中注册,并且像使用扩展章节中描述的那样容易安装。

Category: PHP Develop

Comments