Сейчас в нашем фреймворке есть один минус. Нам нужно копипастить код во front.php каждый раз, как мы создаем сайт. 40 строк кода — это не так уж и много, но было бы неплохо обернуть этот код в класс, что даст нам возможность многократного использования, и лучшую тестируемость кода.
Если присмотреться к front.php — можно увидеть, что мы принимаем данные запроса и выводим ответ. Наш класс будет придерживаться простого принципа: вся логика ограничена созданием ответа для запроса.
Так как, компоненты Symfony2 все равно требуют PHP 5.3, давайте для нашего класса определим пространство имен Simplex, и перенесем в него логику обработки запроса:
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 |
<?php // framework/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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<?php // framework/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(); |
Также все кроме определения маршрутов(/src/app.php), вынесем в пространство имен Calendar.
Для автозагрузки классов, определенных в пространствах имен Symplex и Calendar необходимо обновить файл composer.json:
1 2 3 4 5 6 7 8 9 10 11 |
{ "require": { "symfony/class-loader": "2.1.*", "symfony/http-foundation": "2.1.*", "symfony/routing": "2.1.*", "symfony/http-kernel": "2.1.*" }, "autoload": { "psr-0": { "Simplex": "src/", "Calendar": "src/" } } } |
Что бы изменения вступили в силу, выполните php composer.phar update
Перенесем контроллер в Calendar\Controller\LeapYearController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php // framework/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() также вынесем в отдельный класс:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php // framework/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); } } |
Соответственно, файл app.php:
1 2 3 4 |
$routes->add('leap_year', new Routing\Route('/is_leap_year/{year}', array( 'year' => null, '_controller' => 'Calendar\\Controller\\LeapYearController::indexAction', ))); |
Текущая структура ваших файлов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
framework ??? composer.json ? src ? ??? app.php ? ??? Simplex ? ??? Framework.php ? ??? Calendar ? ??? Controller ? ? ??? LeapYearController.php ? ??? Model ? ??? LeapYear.php ??? vendor ??? web ??? front.php |
Ну вот! Теперь наше приложение состоит из четырех разных слоев, и каждый из них служит определенной цели:
- web/front.php — front контроллер, содержит код взаимодействия с клиентом (получает запрос и отправляет ответ), а также код инициализации фреймворка и приложения.
- src/Simplex — Код фреймворка, управляющий входящим запросом (кстати, он делает ваши контроллеры и шаблоны более тестируемыми, но об этом позже)
- src/Calendar — Код реализующий наше приложение.
- src/app.php — конфигурация приложения и кастомизация фреймворка.
К содержанию >>
Оригинал статьи на английском языке >>
Исходный код из статьи >>