Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit09969d9

Browse files
committed
added part 11
1 parent76e45f9 commit09969d9

File tree

1 file changed

+212
-0
lines changed

1 file changed

+212
-0
lines changed

‎book/part11.rst

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
Create your own framework... on top of the Symfony2 Components (part 11)
2+
========================================================================
3+
4+
If you were to use our framework right now, you would probably have to add
5+
support for custom error messages. Right now, we have 404 and 500 error
6+
support but the responses are hardcoded in the framework itself. Making them
7+
customizable is easy enough though: dispatch a new event and listen to it.
8+
Doing it right means that the listener have to call a regular controller. But
9+
what if the error controller throws an exception? You will end up in an
10+
infinite loop. There should be an easier way, right?
11+
12+
Enter the ``HttpKernel`` class. Instead of solving the same problem over and
13+
over again and instead of reinventing the wheel each time, the ``HttpKernel``
14+
class is a generic, extensible, and flexible implementation of
15+
``HttpKernelInterface``.
16+
17+
This class is very similar to the framework class we have written so far: it
18+
dispatches events at some strategic points during the handling of the request,
19+
it uses a controller resolver to choose the controller to dispatch the request
20+
to, and as an added bonus, it takes care of edge cases and provides great
21+
feedback when a problem arises.
22+
23+
Here is the new framework code::
24+
25+
<?php
26+
27+
// example.com/src/Simplex/Framework.php
28+
29+
namespace Simplex;
30+
31+
use Symfony\Component\HttpKernel\HttpKernel;
32+
33+
class Framework extends HttpKernel
34+
{
35+
}
36+
37+
And the new front controller::
38+
39+
<?php
40+
41+
// example.com/web/front.php
42+
43+
require_once __DIR__.'/../vendor/.composer/autoload.php';
44+
45+
use Symfony\Component\HttpFoundation\Request;
46+
use Symfony\Component\HttpFoundation\Response;
47+
use Symfony\Component\Routing;
48+
use Symfony\Component\HttpKernel;
49+
use Symfony\Component\EventDispatcher\EventDispatcher;
50+
51+
$request = Request::createFromGlobals();
52+
$routes = include __DIR__.'/../src/app.php';
53+
54+
$context = new Routing\RequestContext();
55+
$matcher = new Routing\Matcher\UrlMatcher($routes, $context);
56+
$resolver = new HttpKernel\Controller\ControllerResolver();
57+
58+
$dispatcher = new EventDispatcher();
59+
$dispatcher->addSubscriber(new HttpKernel\EventListener\RouterListener($matcher));
60+
61+
$framework = new Simplex\Framework($dispatcher, $resolver);
62+
63+
$response = $framework->handle($request);
64+
$response->send();
65+
66+
``RouterListener`` is an implementation of the same logic we had in our
67+
framework: it matches the incoming request and populates the request
68+
attributes with route parameters.
69+
70+
Our code is now much more concise and surprisingly more robust and more
71+
powerful than ever. For instance, use the built-in ``ExceptionListener`` to
72+
make your error management configurable::
73+
74+
$errorHandler = function (HttpKernel\Exception\FlattenException $exception) {
75+
$msg = 'Something went wrong! ('.$exception->getMessage().')';
76+
77+
return new Response($msg, $exception->getStatusCode());
78+
});
79+
$dispatcher->addSubscriber(new HttpKernel\EventListener\ExceptionListener($errorHandler);
80+
81+
``ExceptionListener`` gives you a ``FlattenException`` instance instead of the
82+
thrown ``Exception`` instance to ease exception manipulation and display. It
83+
can take any valid controller as an exception handler, so you can create an
84+
ErrorController class instead of using a Closure::
85+
86+
$listener = new HttpKernel\EventListener\ExceptionListener('Calendar\\Controller\\ErrorController::exceptionAction');
87+
$dispatcher->addSubscriber($listener);
88+
89+
The error controller reads as follows::
90+
91+
<?php
92+
93+
// example.com/src/Calendar/Controller/ErrorController.php
94+
95+
namespace Calendar\Controller;
96+
97+
use Symfony\Component\HttpFoundation\Response;
98+
use Symfony\Component\HttpKernel\Exception\FlattenException;
99+
100+
class ErrorController
101+
{
102+
public function exceptionAction(FlattenException $exception)
103+
{
104+
$msg = 'Something went wrong! ('.$exception->getMessage().')';
105+
106+
return new Response($msg, $exception->getStatusCode());
107+
}
108+
}
109+
110+
Voilà! Clean and customizable error management without efforts. And of course,
111+
of your controller throws an exception, HttpKernel will handle it nicely.
112+
113+
In part 2, we have talked about the ``Response::prepare()`` method, which
114+
ensures that a Response is compliant with the HTTP specification. It is
115+
probably a good idea to always call it just before sending the Response to the
116+
client; that's what the ``ResponseListener`` does::
117+
118+
$dispatcher->addSubscriber(new HttpKernel\EventListener\ResponseListener('UTF-8'));
119+
120+
This one was easy too! Let's take another one: do you want out of the box
121+
support for streamed responses? Just subscribe to
122+
``StreamedResponseListener``::
123+
124+
$dispatcher->addSubscriber(new HttpKernel\EventListener\StreamedResponseListener());
125+
126+
And in your controller, return a ``StreamedResponse`` instance instead of a
127+
``Response`` instance.
128+
129+
..tip::
130+
131+
Read the `Internals`_ chapter of the Symfony2 documentation to learn more
132+
about the events dispatched by HttpKernel and how they allow you to change
133+
the flow of a request.
134+
135+
Now, let's create a listener, one that allows a controller to return a string
136+
instead of a full Response object::
137+
138+
class LeapYearController
139+
{
140+
public function indexAction(Request $request, $year)
141+
{
142+
$leapyear = new LeapYear();
143+
if ($leapyear->isLeapYear($year)) {
144+
return 'Yep, this is a leap year! ';
145+
}
146+
147+
return 'Nope, this is not a leap year.';
148+
}
149+
}
150+
151+
To implement this feature, we are going to listen to the ``kernel.view``
152+
event, which is triggered just after the controller has been called. Its goal
153+
is to convert the controller return value to a proper Response instance, but
154+
only if needed::
155+
156+
<?php
157+
158+
// example.com/src/Simplex/StringResponseListener.php
159+
160+
namespace Simplex;
161+
162+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
163+
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
164+
use Symfony\Component\HttpFoundation\Response;
165+
166+
class StringResponseListener implements EventSubscriberInterface
167+
{
168+
public function onView(GetResponseForControllerResultEvent $event)
169+
{
170+
$response = $event->getControllerResult();
171+
172+
if (is_string($response)) {
173+
$event->setResponse(new Response($response));
174+
}
175+
}
176+
177+
public static function getSubscribedEvents()
178+
{
179+
return array('kernel.view' => 'onView');
180+
}
181+
}
182+
183+
The code is simple because the ``kernel.view`` event is only triggered when
184+
the controller return value is not a Response and because setting the response
185+
on the event stops the event propagation (our listener cannot interfere with
186+
other view listeners).
187+
188+
Don't forget to register it in the front controller::
189+
190+
$dispatcher->addSubscriber(new Simplex\StringResponseListener());
191+
192+
..note::
193+
194+
If you forget to register the subscriber, HttpKernel will throws an
195+
exception with a nice message: ``The controller must return a response
196+
(Nope, this is not a leap year. given).``.
197+
198+
At this point, our whole framework code is as compact as possible and it is
199+
mainly composed of an assembling of existing libraries. Extending is a matter
200+
of registering event listeners/subscribers.
201+
202+
Hopefully, you now have a better understanding of why the simple looking
203+
``HttpKernelInterface`` is so powerful. Its default implementation,
204+
``HttpKernel``, gives you access to a lot of cool features, ready to be used
205+
out of the box, with no efforts. And because HttpKernel is actually the code
206+
that powers the Symfony2 and Silex frameworks, you have the best of both
207+
worlds: a custom framework, tailored to your needs, but based on a rock-solid
208+
and well maintained low-level architecture that has been proven to work for
209+
many websites; a code that has been audited for security issues and that has
210+
proven to scale well.
211+
212+
.. _`Internals`:http://symfony.com/doc/current/book/internals.html#events

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp