支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
你是我的英雄!谁会想到,学习了前面三节之后你仍在这里?你的努力将收到回报。前三章从架构角度看起来并不深奥。正是由于架构,令Symfony与诸多框架有别,让我们深入架构考查一番。
Symfony程序的目录结构是有较大弹性的,但是推荐的结构如下:
app/
程序级别的配置、模板、翻译之所在
bin/
存放可执行文件(比如bin/console
)
src/
存放项目的php代码
tests/
自动化测试(如Unit Test单元测试)
var/
存放生成的文件
vendor/
第三方依赖之所在
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返回的响应内容。
AppKernel
类是程序配的主要入口,它所在的位置是app/
目录。
这个类需要实现(implement)以下两个方法:
registerBundles()
返回程序所需的全部bundle数组,后面会解释。
registerContainerConfiguration()
加载程序配置(后面会详细讲)---
自动加载过程,由Composer自动完成,这意味着你可以直接使用任意的PHP类而不需做任何事!所有的依赖,都被存于vender/
目录,但这只是惯例。你可以把它们存在任何你想要的地方,服务器的全域,或是项目所在的局域。
本小节介绍的是Symfony框架最伟大最强力的部分,bundle系统。
一个bundle,有一点像其他软件中的插件(plugin)。但为什么叫bundle而不是plugin?这是因为在Symfony中“一切”皆bundle,从核心框架功能,到你书写的代码。
你写的程序也是被组织到bundle中。以Symfony角度审视,bundle是经过有序组织的文件集(php文件,css/js/image文件…),这些文件一起实现某个单一功能(一个博客,一个论坛,等等…),而且,bundle要能够被很容易地共享给其他程序开发者(译注:最后一点很重要)。
Bundle是Symfony程序的一等公民。这就给了你足够的灵活性,来使用预置了各种功能的第三方bundle,或是分发你自己的bundle。它令我们在程序中选取开启何种功能时,以及优化功能为我所用时,变得更加容易。最后一点,你自己的程序代码,与框架核心代码在重要性上是“一般无二”。
Symfony已经包含了AppBundle,你可以使用它直接开发程序。之后,如果你需要将程序分离成“可复用”的组件,你可以创建自己的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都能通过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 }
# ... |
代码中的每个“第一层级”,像是framework
,twig
或swiftmailer
,定义了相应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继承,允许你覆写任意一个既存的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
类。
如果你遵守命名惯例,你可以使用如何利用Bundle的继承来重写Bundle局部,来覆写文件,控制器,或是模板。例如,你创建了一个bundle – NewBundle – 并且指定它覆写AppBundle。当Symfony加载AppBundle:Default:index
这个controller时,它首先寻找NewBundle中的DefaultController
,如果不存在,再寻找AppBundle里的。这意味着一个Bundle可以覆写另一个Bundle的任何一个部分!
你是否理解了Symfony框架的灵活程度?在程序中共享你的Bundle,把它们存在项目域还是服务器域,随你选择。
你的程序存在使用第三方库的概率。这些库文件应该被存放在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 创作共用授权。