Skip to content

Commit a635d89

Browse files
committed
added part 10
1 parent bde64c8 commit a635d89

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed

book/part10.rst

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
Create your own framework... on top of the Symfony2 Components (part 10)
2+
========================================================================
3+
4+
In the conclusion of the second part of this series, I've talked about one
5+
great benefit of using the Symfony2 components: the *interoperability* between
6+
all frameworks and applications using them. Let's do a big step towards this
7+
goal by making our framework implement ``HttpKernelInterface``::
8+
9+
namespace Symfony\Component\HttpKernel;
10+
11+
interface HttpKernelInterface
12+
{
13+
/**
14+
* @return Response A Response instance
15+
*/
16+
function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true);
17+
}
18+
19+
``HttpKernelInterface`` is probably the most important piece of code in the
20+
HttpKernel component, no kidding. Frameworks and applications that implement
21+
this interface are fully interoperable. Moreover, a lot of great features will
22+
come with it for free.
23+
24+
Update your framework so that it implements this interface::
25+
26+
<?php
27+
28+
// example.com/src/Framework.php
29+
30+
// ...
31+
32+
use Symfony\Component\HttpKernel\HttpKernelInterface;
33+
34+
class Framework implements HttpKernelInterface
35+
{
36+
// ...
37+
38+
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
39+
{
40+
// ...
41+
}
42+
}
43+
44+
Even if this change looks trivial, it brings us a lot! Let's talk about one of
45+
the most impressive one: transparent `HTTP caching`_ support.
46+
47+
The ``HttpCache`` class implements a fully-featured reverse proxy, written in
48+
PHP; it implements ``HttpKernelInterface`` and wraps another
49+
``HttpKernelInterface`` instance::
50+
51+
// example.com/web/front.php
52+
53+
use Symfony\Component\HttpKernel\HttpCache\HttpCache;
54+
use Symfony\Component\HttpKernel\HttpCache\Store;
55+
56+
$framework = new Simplex\Framework($dispatcher, $matcher, $resolver);
57+
$framework = new HttpCache($framework, new Store(__DIR__.'/../cache'));
58+
59+
$framework->handle($request)->send();
60+
61+
That's all it takes to add HTTP caching support to our framework. Isn't it
62+
amazing?
63+
64+
Configuring the cache needs to be done via HTTP cache headers. For instance,
65+
to cache a response for 10 seconds, use the ``Response::setTtl()`` method::
66+
67+
// example.com/src/Calendar/Controller/LeapYearController.php
68+
69+
public function indexAction(Request $request, $year)
70+
{
71+
$leapyear = new LeapYear();
72+
if ($leapyear->isLeapYear($year)) {
73+
$response = new Response('Yep, this is a leap year!');
74+
} else {
75+
$response = new Response('Nope, this is not a leap year.');
76+
}
77+
78+
$response->setTtl(10);
79+
80+
return $response;
81+
}
82+
83+
.. tip::
84+
85+
If, like me, you are running your framework from the command line by
86+
simulating requests (``Request::create('/is_leap_year/2012')``), you can
87+
easily debug Response instances by dumping their string representation
88+
(``echo $response;``) as it displays all headers as well as the response
89+
content.
90+
91+
To validate that it works correctly, add a random number to the response
92+
content and check that the number only changes every 10 seconds::
93+
94+
$response = new Response('Yep, this is a leap year! '.rand());
95+
96+
.. note::
97+
98+
When deploying to your production environment, keep using the Symfony2
99+
reverse proxy (great for shared hosting) or even better, switch to a more
100+
efficient reverse proxy like `Varnish`_.
101+
102+
Using HTTP cache headers to manage your application cache is very powerful and
103+
allows you to finely tuned your caching strategy as you can use both the
104+
expiration and the validation models of the HTTP specification. If you are not
105+
comfortable with these concepts, I highly recommend you to read the `HTTP
106+
caching`_ chapter of the Symfony2 documentation.
107+
108+
The Response class contains many other methods that let's you configure the
109+
HTTP cache very easily. One of the most powerful is ``setCache()`` as it
110+
abstracts the most frequently used caching strategies into one simple array::
111+
112+
$date = date_create_from_format('Y-m-d H:i:s', '2005-10-15 10:00:00');
113+
114+
$response->setCache(array(
115+
'public' => true,
116+
'etag' => 'abcde',
117+
'last_modified' => $date,
118+
'max_age' => 10,
119+
's_maxage' => 10,
120+
));
121+
122+
// it is equivalent to the following code
123+
$response->setPublic();
124+
$response->setEtag('abcde');
125+
$response->setLastModified($date);
126+
$response->setMaxAge(10);
127+
$response->setSharedMaxAge(10);
128+
129+
When using the validation model, the ``isNotModified()`` method allows you to
130+
easily cut on the response time by short-circuiting the response generation as
131+
early as possible::
132+
133+
$response->setETag('whatever_you_compute_as_an_etag');
134+
135+
if ($response->isNotModified($request)) {
136+
return $response;
137+
}
138+
$response->setContent('The computed content of the response');
139+
140+
return $response;
141+
142+
Using HTTP caching is great, but what if you cannot cache the whole page? What
143+
if you can cache everything but some sidebar that is more dynamic that the
144+
rest of the content? Edge Side Includes (`ESI`_) to the rescue! Instead of
145+
generating the whole content in one go, ESI allows you to mark a region of a
146+
page as being the content of a sub-request call::
147+
148+
This is the content of your page
149+
150+
Is 2012 a leap year? <esi:include src="/leapyear/2012" />
151+
152+
Some other content
153+
154+
For ESI tags to be supported by HttpCache, you need to pass it an instance of
155+
the ``ESI`` class. The ``ESI`` class automatically parses ESI tags and makes
156+
sub-requests to convert them to their proper content::
157+
158+
use Symfony\Component\HttpKernel\HttpCache\ESI;
159+
160+
$framework = new HttpCache($framework, new Store(__DIR__.'/../cache'), new ESI());
161+
162+
.. note::
163+
164+
For ESI to work, you need to use a reverse proxy that supports it like the
165+
Symfony2 implementation. `Varnish`_ is the best alternative and it is
166+
Open-Source.
167+
168+
When using complex HTTP caching strategies and/or many ESI include tags, it
169+
can be hard to understand why and when a resource should be cached or not. To
170+
ease debugging, you can enable the debug mode::
171+
172+
$framework = new HttpCache($framework, new Store(__DIR__.'/../cache'), new ESI(), array('debug' => true));
173+
174+
The debug mode adds a ``X-Symfony-Cache`` header to each response that
175+
describes what the cache layer did:
176+
177+
.. code-block:: text
178+
179+
X-Symfony-Cache: GET /is_leap_year/2012: stale, invalid, store
180+
181+
X-Symfony-Cache: GET /is_leap_year/2012: fresh
182+
183+
HttpCache has many some features like support for the
184+
``stale-while-revalidate`` and ``stale-if-error`` HTTP Cache-Control
185+
extensions as defined in RFC 5861.
186+
187+
With the addition of a single interface, our framework can now benefit from
188+
the many features built into the HttpKernel component; HTTP caching being just
189+
one of them but an important one as it can make your applications fly!
190+
191+
.. _`HTTP caching`: http://symfony.com/doc/current/book/http_cache.html
192+
.. _`ESI`: http://en.wikipedia.org/wiki/Edge_Side_Includes
193+
.. _`Varnish`: https://www.varnish-cache.org/

0 commit comments

Comments
 (0)