可复用Bundle的最佳实践
有两种类型的bundles:
特定程序的bundles:仅用于构建你自己的程序;
可复用的bundles:意味着可以分享到多个项目中。
本文讲的全部是关于如何构建可复用bundles,以便它们能够被配置和扩展。许多推荐做法并不适用于程序专用bundles,因为你希望程序bundle尽可能简单才好。程序专用bundle只需遵循“指南”中的内容就好。
程序专用bundle的最佳实践,在Symfony框架最佳实践中进行了讨论。
Bundle命名 ¶
一个bundle同时也是一个PHP命名空间。这个命名空间,必须遵循为PHP命名空间和类名所制定的PSR-0和PSR-4互用性标准:它应起于一个vendor segment即“开发商”区段,跟着的是零或多个“类别”区段,结束于一个“必须以Bundle
后缀结尾”的短名。
一个命名空间在你添加了bundle类之后将立即成为bundle。budnle的类名受以下规则约束:
只能使用字母和下划线;
使用驼峰命名法;
使用描述性的短名称(不能超过两个单词);
类名前缀可以是开发商(和可选的类别命名)的连接组合;
使用
Bundle
作为类名后缀。
以下是有效的bundle命名空间和类名:
Namespace(bundle的命名空间) | Bundle Class Name(bundle的类名称) |
---|---|
Acme\Bundle\BlogBundle |
AcmeBlogBundle |
Acme\BlogBundle |
AcmeBlogBundle |
根据约定,bundle类的getName()
方法必须返回类名。
如果你公开发布自己的bundle,你必须使用bundle类名作为repository的名字(比如,是AcmeBlogBundle
而不是BlogBundle
)。
Symfony核心bundles不在Bundle类的名称中添加Symfony
前缀,但是有Bundle
子命名空间。例如:FrameworkBundle
。
每个bundle都有一个假名,是类bundle名称使用下划线的小写短版(AcmeBlogBundle
就是acme_blog
)。假名用于在项目内(使bundle)强制唯一,同时还用于定义bundle的配置选项(参见下文中示例)。
目录结构 ¶
AcmeBlogBundle的基本目录结构必须像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<your-bundle>/
├─ AcmeBlogBundle.php
├─ Controller/
├─ README.md
├─ LICENSE
├─ Resources/
│ ├─ config/
│ ├─ doc/
│ │ └─ index.rst
│ ├─ translations/
│ ├─ views/
│ └─ public/
└─ Tests/ |
下列文件是必须的,因为它们可以确保(满足)结构上的约定以便(框架底层的)自动工具可以依赖:
AcmeBlogBundle.php
:这就是可以把平面目录转换成一个Symfony bundle(可改为你的bundle名称)的类;README.md
:这个文件包含了bundle的基本描述信息,通常用于显示基本用法并且链接到完整文档(它可以使用任何受到gitHub支持的markup格式,比如README.rst
);LICENSE
:代码所使用的授权之完整内容。多数三方bundles使用的是MIT license,但你也可以选择任何一个license;Resources/doc/index.rst
:Bundle文档中的根文件。
对于经常被用到的类和文件来说,子目录的层数应尽可能保持在最低的程度(两级是最大了)。
bundle的目录是只读的。如果你需要写入临时文件,把它们存放到程序级别的cache/
或log/
目录下。使用工具可以生成bundle目录结构中的文件,但只有那些会成为repository一部分的文件才会被生成。
下面这些类和文件有特殊的位置(其中一些是必须的,另外一些则只是多数开发者所遵循的约定):
类型 | 目录 | 必须? |
---|---|---|
Commands | Command/ |
是 |
Controllers | Controller/ |
否 |
Service Container Extensions | DependencyInjection/ |
是 |
Event Listeners | EventListener/ |
否 |
Model classes [1] | Model/ |
否 |
Configuration | Resources/config/ |
否 |
Web Resources (CSS, JS, images) | Resources/public/ |
是 |
Translation files | Resources/translations/ |
是 |
Templates | Resources/views/ |
是 |
Unit and Functional Tests | Tests/ |
否 |
[1] 参考如何为多个Doctrine实现提供Model类以了解如何使用compiler pass来操作映射。
类 ¶
bundle的目录结构会被作为命名空间层级(hierarchy)来使用。例如,一个ContentController
控制器就是存放在Acme/BlogBundle/Controller/ContentController.php
之中并且其FQCN类名是Acme\BlogBundle\Controller\ContentController
。
bundle中所有的类和文件,必须遵循Symfony coding standardsSymfony编码标准。
一些作为主力被看到的类,类名应尽可能短,像是Commands(命令行)、Helpers(助手)、Listeners(监听)以及Controllers(控制器)这些。
连接EventDispatcher的类应该加上Listener
后缀。
异常类(Exception classes)应该被存放在Exception
子命名空间下。
开发商 ¶
一个bundle一定不能嵌入第三方PHP类库,而应当依靠Symfony的自动加载。
一个bundle一定不能嵌入第三方Javascript、CSS或其他语言的类库。
测试 ¶
每个bundle都应该自带一个用PHPUnit写就的测试包(test suite),并存放在Test\
目录下。测试应遵循以下原则:
测试包要能在一个样例程序中通过
phpunit
命令来执行;功能测试应当仅用于测试响应输出,以及一些可能存在的分析信息(profiling infomation)。
测试应当覆盖至少95%的基础代码范围。
测试包一定不能包含AllTest.php
脚本,而是必须依靠一个已经存在的phpunit.xml.dist
文件。
文档 ¶
所有的类和方法必须自带完整的PHPDoc。
可扩展的文档应该使用reStructuredText格式来提供,放在Resources/doc/
目录下;Resources/doc/index.rst
文件必须存在,而且它是文档的入口点(entry point)。
安装介绍 ¶
为了能轻松安装第三方bundles,考虑在你的README.md
文件中使用下列标准格式的简介。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
Installation
============
Step 1: Download the Bundle
---------------------------
Open a command console, enter your project directory and execute the
following command to download the latest stable version of this bundle:
```console
$ composer require <package-name> "~1"
```
This command requires you to have Composer installed globally, as explained
in the [installation chapter](https://getcomposer.org/doc/00-intro.md)
of the Composer documentation.
Step 2: Enable the Bundle
---------------------------
Then, enable the bundle by adding it to the list of registered bundles
in the `app/AppKernel.php` file of your project:
```php
<?php
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
// ...
new <vendor>\<bundle-name>\<bundle-long-name>(),
);
// ...
}
// ...
}
``` |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
Installation
============
Step 1: Download the Bundle
---------------------------
Open a command console, enter your project directory and execute the
following command to download the latest stable version of this bundle:
.. code-block:: terminal
$ composer require <package-name> "~1"
This command requires you to have Composer installed globally, as explained
in the `installation chapter`_ of the Composer documentation.
Step 2: Enable the Bundle
---------------------------
Then, enable the bundle by adding it to the list of registered bundles
in the ``app/AppKernel.php`` file of your project:
.. code-block:: php
<?php
// app/AppKernel.php
// ...
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
// ...
new <vendor>\<bundle-name>\<bundle-long-name>(),
);
// ...
}
// ...
}
.. _`installation chapter`: https://getcomposer.org/doc/00-intro.md |
这个例子假设你正在安装bundle的最新稳定版,此时你毋须提供package的版本号(比如 composer require friendsofsymfony/user-bundle
)。如果安装简介里引用了一些过去的bundle版本,或者是一些不稳定版本,这时应当把版本约束包容进来(composer require friendsofsymfony/user-bundle "~2.0@dev"
)。
可选地,你可以添加更多的安装步骤(Step 3, Step 4 等等)以便解释其他所需之安装任务,比如注册路由,或是剥离出资源(dumping assets)。
路由 ¶
如果bundle提供了路由,它们必须使用bundle的假名作为前缀。例如,如果你的bundle叫AcmeBlogBundle,则所有它的路由必须添加acme_blog_
前缀。
模板 ¶
如果bundle提供了模板,它们必须使用Twig。一个bundle一定不能提供一个主要布局,除非该bundle提供的是一套全功能程序。
翻译文件 ¶
如果bundle提供了对信息的翻译,翻译文件必须定义为XLIFF格式;翻译域(translation DOMain)则应该放在bundle名称之后(acme_blog
)。
一个bundle一定不能对其他bundle的已有翻译信息进行覆写。
配置 ¶
要提供更大的灵活性,bundle可以通过使用Symfony内置的架构来给出可配置的选项。
对于简单的配置项,可以使用Symfony配置体系中的默认parameters
参数键。Symfony参数是简单的键/值对;值可以是任何PHP有效值。每个参数的名称,应该起于所属bundle的假名,虽然这只是最佳实践所给出的建议(译注:并不强制)。参数名中的其他部分可以使用英文句号(.
)来分隔不同的部分(比如acme_blog.author.email
)。
末级用户可以在任何一种配置文件中提供所需的值:
1 2 3 |
# app/config/config.yml
parameters:
acme_blog.author.email: 'fabien@example.com' |
1 2 3 4 |
<!-- app/config/config.xml -->
<parameters>
<parameter key="acme_blog.author.email">fabien@example.com</parameter>
</parameters> |
1 2 |
// app/config/config.php
$container->setParameter('acme_blog.author.email', 'fabien@example.com'); |
在你的控制器里通过容器取出参数的值:
1 |
$container->getParameter('acme_blog.author.email'); |
尽管这种架构极其简单,你应该考虑使用更加先进的语义化bundle配置。
版本 ¶
Bundles必须按照SemAntic Versioning Standard实施语义化版本。
服务 ¶
如果在bundle中定义了服务,它们必须以bundle假名作为前缀。例如,AcmeBlogBundle的服务要以acme_blog
当作前缀。
此外,服务即意味着被程序直接使用,应当定义成private。
你可以了解更多关于服务在bundle中被加载的细节,参考如何在Bundle内加载服务配置。
Composer元数据 ¶
composer.json
文件中应当包括如下metadata(元数据):
name
- 它是vendor开发商和bundle短名的组合。如果你是自己发布bundle而不是代表公司,使用你自己的名字(比如
johnsmith/blog-bundle
)。bundle短名中剔除了vendor名,并使用中杠(hyphen)来划分单词。例如:AcmeBlogBundle
被转换为blog-bundle
,而AcmeSocialConnectBundle
被转换为social-connect-bundle
。 description
- 对bundle功能的一个简短解释。
type
- 使用
symfony-bundle
这个值。 license
-
MIT
是Symfony bundle的首选授权方式,但你也可以使用其他的license。 autoload
- 这个信息在Symfony加载bundle中的类时被用到。PSR-4自动加载标准被现代bundles所推荐,但是PSR-0标准同样被支持。
为了让其他开发者易于找到你的bundle,把它注册到Packagist,这是Composer包的官方宝库。