英文原文地址:https://symfony.com/doc/current/create_framework/separation_of_concerns.html
目前我们的框架还有一个不足之处:当我们要创建新的网站的时候,我们都需要将 front.php
的代码复制一份。虽然 40 行代码并不是很多,但是如果我们能将这些代码写成一个合适的类,将会更给力一些,比如更好的复用性以及更好的可测试性。
更进一步研究你会发现,front.php
包含一个输入,即一个请求 Request,以及一个输出,即一个相应 Response。我们的框架将遵循一个简单的原则:生成与请求对应的响应。
我们可以为框架设置一个自己的命名空间:Simplex
将处理请求的逻辑代码移动到我们的框架类中:
<?php
// example.com/src/Simplex/Framework.php
namespace Simplex;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
class Framework
{
protected $matcher;
protected $resolver;
public function __construct(UrlMatcher $matcher, ControllerResolver $resolver)
{
$this->matcher = $matcher;
$this->resolver = $resolver;
}
public function handle(Request $request)
{
try {
$request->attributes->add($this->matcher->match($request->getPathInfo()));
$controller = $this->resolver->getController($request);
$arguments = $this->resolver->getArguments($request, $controller);
return call_user_func_array($controller, $arguments);
} catch (ResourceNotFoundException $e) {
return new Response('Not Found', 404);
} catch (\Exception $e) {
return new Response('An error occurred', 500);
}
}
}
相应的我们也需要更新一下 front.php 的代码:
<?php
// example.com/web/front.php
// ...
$request = Request::createFromGlobals();
$routes = include __DIR__.'/../src/app.php';
$context = new Routing\RequestContext();
$context->fromRequest($request);
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
$resolver = new HttpKernel\Controller\ControllerResolver();
$framework = new Simplex\Framework($matcher, $resolver);
$response = $framework->handle($request);
$response->send();
让我们把除了路由定义的代码挪到另外一个命名空间 Calendar
下,以完成我们对重构代码的继续封装:
为了让在 Simplex
以及 Calendar
这两个命名空间下的代码文件能够自动加载,我们需要更新一下 composer.json
文件:
{
"...": "...",
"autoload": {
"psr-4": { "": "src/" }
}
}
为了让自动加载生效,需要运行
php composer.phar dump-autoload
将控制器代码挪到Calendar\Controller\LeapYearController
:
<?php
// example.com/src/Calendar/Controller/LeapYearController.php
namespace Calendar\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Calendar\Model\LeapYear;
class LeapYearController
{
public function indexAction(Request $request, $year)
{
$leapyear = new LeapYear();
if ($leapyear->isLeapYear($year)) {
return new Response('Yep, this is a leap year!');
}
return new Response('Nope, this is not a leap year.');
}
}
然后把 is_leap_year()
方法挪到它应该存在的类中:
<?php
// example.com/src/Calendar/Model/LeapYear.php
namespace Calendar\Model;
class LeapYear
{
public function isLeapYear($year = null)
{
if (null === $year) {
$year = date('Y');
}
return 0 == $year % 400 || (0 == $year % 4 && 0 != $year % 100);
}
}
别忘了example.com/src/app.php
也需要在相应的地方做下更新:
$routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array(
'year' => null,
'_controller' => 'Calendar\\Controller\\LeapYearController::indexAction',
)));
最终,我们得到一个新的目录结构:
example.com/
composer.json
src/
app.php
Simplex/
Framework.php
Calendar/
Controller/
LeapYearController.php
Model/
LeapYear.php
vendor/
autoload.php
web/
front.php
没错!我们的应用程序目前有4个不同的部分,而且每一个部分都有自己特有的责任:
web/front.php
:前段控制器,这是唯一一处没有被封装的php代码文件,并且唯一与客户端交互的接口(它接受请求并发送响应),并且提供了初始化框架的代码模版(boil-plate,译者注:这个单词很有意思,来源于印刷工业,指的是不能拆卸的一整块印刷母板,你可以想成是活字印刷发明之前用来印书的东西)src/Simplex
:可供复用的框架代码,作为处理请求的抽象化接口(另外,他使你的控制器/模板文件更容易测试,更多信息请看下一章)src/Calendar
:我们程序的特定代码(译者注:指一个程序的具体实现)src/app.php
:程序配置/框架自定义
使用 Symfony 的组件创建自己的 PHP 框架(第七部分:封装框架代码) by Chris Yue is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

写作累,服务器还越来越贵
求分担,祝愿好人一生平安
天使打赏人
10 Comments
笔尖
5月 27, 2015 在 9:50 下午博主你翻译的文章非常棒,我一直在看 不过是潜水的,其实关注你文章的人很多
非常感谢
lxrm
5月 20, 2013 在 3:08 下午辛苦了,楼主,花了几天,看到这里,也完成了对sf2的基本了解和实验,对比原文,楼主翻译应该是比较用心了。赞一下!
另外,我目前的版本是2.1.* 版 autoload.php 位置是 直接放到了 vender 下面
require_once __DIR__.’/../vendor/autoload.php’;即可
md
6月 10, 2012 在 11:49 下午有没有兴趣直接参与到我们这边?比如fork我们的网站,可以省了我过来翻阅的功夫,呵呵。国内用Symfony2的人星星点点的,如果能合在一处,用的人多了,各自应该能更省力,投入的时间精力应该也更划算吧。我的动机很简单,能分享的人,首先自己得有效率,想通过这个社区项目多结识一些“这种风格”的开发人员吧。我的Email:md@symfony.cn,如果有想法了,随时联系我。
Chris Yue
6月 22, 2012 在 1:48 下午因为我时间太不确定,所以直接参与这种事情我感觉我自己有点不太靠谱,毕竟参与也算是要承担了一份责任,不靠谱终归不好
另外什么是fork?
md
7月 5, 2012 在 5:59 下午把“fork”理解成在github.com协作开发/写作的动词吧:)建议你有时间了解一下,其实通过GitHub参与像symfony.cn这样的项目,并不会对你的节奏有硬性要求。你可以在任意的时间做对你有意义的事情,通过GitHub来做同步就可以了。对你对做事负责的态度,我很赞同。
Chris Yue
7月 7, 2012 在 11:08 上午那我先fork着吧:)
你们有qq群吗?
md
7月 8, 2012 在 5:22 下午qq群230078413,但没怎么管理;
我打算逐渐把你翻译的这个系列转载到symfony.cn
除了署名,有没有什么别的要求:)
Chris Yue
7月 11, 2012 在 7:28 下午注明原地址就行,没其他要求,我加群了哈
md
6月 1, 2012 在 5:32 下午辛苦了,请问这个系列的翻译是否还在进行?我想在symfony.cn属性转载你的文章,是否允许?:)
Chris Yue
6月 6, 2012 在 8:38 下午既然有人看那就继续吧,哈哈:)
没有,其实是工作原因导致没空更新。
最近应该没这么忙了,会陆续把剩下5章全部翻译完
转载没问题。