架构

3.4 版本
维护中的版本

你是我的英雄!谁会想到,学习了前面三节之后你仍在这里?你的努力将收到回报。前三章从架构角度看起来并不深奥。正是由于架构,令Symfony与诸多框架有别,让我们深入架构考查一番。

理解目录结构 

Symfony程序的目录结构是有较大弹性的,但是推荐的结构如下:

app/ 程序级别的配置、模板、翻译之所在

bin/ 存放可执行文件(比如bin/console

src/ 存放项目的php代码

tests/ 自动化测试(如Unit Test单元测试)

var/ 存放生成的文件

vendor/ 第三方依赖之所在

web/ web根目录

web/ 目录 

web根目录是所有公共和静态文件比如图片、css、js的家。而它也是前端控制器(front controller)所在,比如production controller(生产环境控制器)的代码如下:

1
2
3
4
5
6
7
8
9
10
11
// web/app.php
require_once __DIR__.'/../var/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';
 
use Symfony\Component\HttpFoundation\Request;
 
$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();

上面的控制器首先通过kernel类来启动程序(这里是AppKernel)。然后,把PHP里的超全局变量传入kernel,创建了一个Request对象。最后一步则是输出由kernel返回的响应内容。

app/ 目录 

AppKernel类是程序配的主要入口,它所在的位置是app/目录。

这个类需要实现(implement)以下两个方法:

registerBundles()

返回程序所需的全部bundle数组,后面会解释。

registerContainerConfiguration()

加载程序配置(后面会详细讲)---

自动加载过程,由Composer自动完成,这意味着你可以直接使用任意的PHP类而不需做任何事!所有的依赖,都被存于vender/目录,但这只是惯例。你可以把它们存在任何你想要的地方,服务器的全域,或是项目所在的局域。

理解Bundle系统 

本小节介绍的是Symfony框架最伟大最强力的部分,bundle系统。

一个bundle,有一点像其他软件中的插件(plugin)。但为什么叫bundle而不是plugin?这是因为在Symfony中“一切”皆bundle,从核心框架功能,到你书写的代码。

你写的程序也是被组织到bundle中。以Symfony角度审视,bundle是经过有序组织的文件集(php文件,css/js/image文件…),这些文件一起实现某个单一功能(一个博客,一个论坛,等等…),而且,bundle要能够被很容易地共享给其他程序开发者(译注:最后一点很重要)。

Bundle是Symfony程序的一等公民。这就给了你足够的灵活性,来使用预置了各种功能的第三方bundle,或是分发你自己的bundle。它令我们在程序中选取开启何种功能时,以及优化功能为我所用时,变得更加容易。最后一点,你自己的程序代码,与框架核心代码在重要性上是“一般无二”。

Symfony已经包含了AppBundle,你可以使用它直接开发程序。之后,如果你需要将程序分离成“可复用”的组件,你可以创建自己的bundle。

注册Bundle 

一套基于Symfony的应用程序,是由AppKernel类的registerBundles()方法中所定义的bundle组成的。每一个bundle所在的目录下,都有一个独立的Bundle类用来描述自己。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
        new Symfony\Bundle\SecurityBundle\SecurityBundle(),
        new Symfony\Bundle\TwigBundle\TwigBundle(),
        new Symfony\Bundle\MonologBundle\MonologBundle(),
        new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
        new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
        new Symfony\Bundle\AsseticBundle\AsseticBundle(),
        new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
        new AppBundle\AppBundle(),
    );
 
    if (in_array($this->getEnvironment(), array('dev', 'test'))) {
        $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
        $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
        $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
    }
 
    return $bundles;
}

除了AppBundle,kernel还要注册Symfony其他部分的bundle,比如FrameworkBundle,DoctrineBundle,SwiftmailerBundle和AsseticBundle。

配置Bundle 

每一个bundle都能通过YAML/XML/PHP等格式的配置文件来定制。看一下Symfony默认配置的样例:

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
# app/config/config.yml
imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: services.yml }

framework:
    #esi:             ~
    #translator:      { fallbacks: ['%locale%'] }
    secret:          '%secret%'
    router:
        resource: '%kernel.root_dir%/config/routing.yml'
        strict_requirements: '%kernel.debug%'
    form:            true
    csrf_protection: true
    validation:      { enable_annotations: true }
    templating:      { engines: ['twig'] }
    default_locale:  '%locale%'
    trusted_proxies: ~
    session:         ~
 
# Twig Configuration
twig:
    debug:            '%kernel.debug%'
    strict_variables: '%kernel.debug%'
 
# Swift Mailer Configuration
swiftmailer:
    transport: '%mailer_transport%'
    host:      '%mailer_host%'
    username:  '%mailer_user%'
    password:  "%mailer_password%"
    spool:     { type: memory }
 
# ...

代码中的每个“第一层级”,像是frameworktwigswiftmailer,定义了相应bundle的配置信息。例如,framework定义的是FrameworkBundle,而swiftmailer定义的是SwiftmailerBundle的配置。

通过提供特定的配置文件,不同的“环境(enviroment)”下可以覆写默认的配置文件。例如,dev环境下,Symfony加载了config_dev.yml文件,它导入了主力配置文件(如config.yml)并修改了它,加入了一些除错工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
# app/config/config_dev.yml
imports:
    - { resource: config.yml }

framework:
    router:   { resource: '%kernel.root_dir%/config/routing_dev.yml' }
    profiler: { only_exceptions: false }

web_profiler:
    toolbar: true
    intercept_redirects: false
 
# ...

继承一个Bundle 

除了能够完美地组织和配置代码,一个bundle可以扩展另一个。bundle继承,允许你覆写任意一个既存的budnle,来定制他的controller,模板,以及其他文件。

文件名逻辑化 

当你需要引用一个bundle中的文件时,使用下列注释:

@BUNDLE_NAME/path/to/file;

Symfony将解析@BUNDLE_NAME为该bundle的真实路径。比如,逻辑路径是@AppBundle/Controller/DefaultController.php,它将被转换为 src/AppBundle/Controller/DefaultController.php,因为Symfony知道AppBundle的位置。

控制器名逻辑化 

对于controller来说,你需要引用它的话,使用这种格式:

BUNDLE_NAME:CONTROLLER_NAME:ACTION_NAME

例如, AppBundle:Default:index指向的是indexAction方法,它来自AppBundle\Controller\DefaultController类。

扩展Bundles 

如果你遵守命名惯例,你可以使用如何利用Bundle的继承来重写Bundle局部,来覆写文件,控制器,或是模板。例如,你创建了一个bundle – NewBundle – 并且指定它覆写AppBundle。当Symfony加载AppBundle:Default:index这个controller时,它首先寻找NewBundle中的DefaultController,如果不存在,再寻找AppBundle里的。这意味着一个Bundle可以覆写另一个Bundle的任何一个部分!

你是否理解了Symfony框架的灵活程度?在程序中共享你的Bundle,把它们存在项目域还是服务器域,随你选择。

使用Vendors 

你的程序存在使用第三方库的概率。这些库文件应该被存放在vender/目录下。你不应触碰这里的文件,因为它们是由composer来“排他”管理的。该目录存有Symfony组件,swiftmailer,Doctrine ORM,TWIG模板,以及其他第三方库和第三方bundle。

理解缓存和日志 

Symfony程序可以保有若干配置文件,而且它们可以由不同格式定义(YAML/XML/PHP等)。Symfony并非在每次请求时解析和合并那些文件,而是使用缓存系统。实际上,程序配置只在最初被解析一次,然后就被编译成为PHP代码,存放在app/cache/目录下。

在开发环境下,每当你的程序发生改变,Symfony足够智能来控制缓存文件的更新。但是在生产环境下,为了加速,清除缓存就成为你在更新代码或是改变配置时所应该担负的责任。执行以下命令来执行prod环境下的清除缓存动作:

1
$  php app/console cache:clear --env=prod

当你在开发一个web程序时,可能会发生各种错误。日志文件位于app/logs目录下,它会告诉你每一次请求的细节,帮助你快速地发现和修复问题。

使用命令行界面 

Symfony有一个命令行工具(app/console),可以帮助你维护程序。该工具提供了大量帮你提高生产率的命令,可以对繁琐和重复性任务进行自动化处理。

运行以下无参命令,可以感受console工具的庞大容量:

1
$  php app/console

--help选项可以帮你深入某个命令的具体用法:

1
$  php app/console debug:router --help

架构总结 

说我疯狂并不过份,但读过这部分,你已经拨云见日并且使用Symfony来工作。Symfony中的每一样东西,都被设计为改变你的开发方式。因此,你可以按照自己喜欢的方式来重命名文件或移除目录。

至此,快速入门结束。从测试,到发邮件,你还有很多知识要学,才能成为Symfony达人。做好准备去深挖相关内容了吗?毋需再找,到中文文档里面挑选自己需要的课题来学习。

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)