<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.fabien.potencier.org/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:base="http://fabien.potencier.org/">
  <title>Fabien Potencier</title>
  <link href="http://fabien.potencier.org/" />
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>

      <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.fabien.potencier.org/aidedecamp" /><feedburner:info uri="aidedecamp" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
  <title>About Symfony: Stability over Features</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/hizCeRwYFYE/about-symfony-stability-over-features" />
  <id>article-68</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2013-04-15T11:35:00+02:00</updated>
  <content type="html">
    &lt;blockquote class="caution"&gt;&lt;p&gt;
  I know that this post won't please everyone, but I'm convinced that this
  is the right thing to do now and I think that most of the Symfony
  community will love it. Also, I've tried to be subtle in choosing my
  words, so read carefully and don't over-read what I've written.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Long story short&lt;/strong&gt;: in the coming months, the Symfony core contributors
should focus their efforts toward stabilizing the existing features instead of
working on new ones. At this point, backward compatibility and stability are
more important than everything else.&lt;/p&gt;

&lt;p&gt;Symfony is one of the few PHP projects that has been promoting radical changes
to the way developers work with PHP by leveraging many concepts, best
practices, and design patterns from other languages and technologies. But
Symfony is also one of the few to embrace true collaboration and sharing. That
was not necessarily incompatible in the "early days" of Symfony2, but to
continue doing both the right way, the community needs to slightly change its
priorities.&lt;/p&gt;

&lt;p&gt;To start easy, let's see what &lt;strong&gt;stabilization&lt;/strong&gt; means for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Refactor less&lt;/strong&gt;: We need to stop doing refactoring code for the sake of
it. Sometimes, our code is not the best architected one, sometimes we could
simplify some code, or make it easier to read, but if it does not make
things easier for our users, we should avoid doing refactoring.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fix more bugs and edge cases&lt;/strong&gt;: Symfony has twenty-plus components, small
and large, and we are commited to maintain them in the long term. And
working on fixing bugs that people encounter or fixing edge cases is a
must. Most developers prefer to work on new features rather than fixing
bugs, but personnally, I do like fixing bugs because most of time fixing a
bug is a challenge... and we like challenges, right?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write more tests&lt;/strong&gt;: Symfony2 has many unit tests but we need more. We
need to write tests that cover bugs and edge cases that we fix, but some
parts of the framework are not well tested enough.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Write more documentation&lt;/strong&gt;: Thanks to a great doc team, Symfony2
documentation is really good and it is updated and improved every single
day. We should continue this work and the more people helping, the better.
The good news is that everyone is able to contribute here.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stop breaking backward compatibility&lt;/strong&gt;: This is the most important topic
and I know that many core contributors are nervous about this one. When we
break backward compatibility, we are breaking the Symfony2 ecosystem at
large. So, except when fixing a security issue, we must not break backward
compatibility anymore after Symfony 2.3 and until Symfony 3.0.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take some examples to make things clear.&lt;/p&gt;

&lt;p&gt;What about fixing a wrong or stupid behavior on an existing feature? As we
cannot break backward compatibility, and because people might rely on the
current behavior, we should add a way to do the feature the right way instead
and thoroughly document both behaviors by emphasizing the right way and by
deprecating the wrong way until Symfony 3.0.&lt;/p&gt;

&lt;p&gt;What about renaming a miss-named thing? We cannot rename the
method/class/function/namespace/whatever, but we can add an alias and
deprecate the wrong one, which will be removed in Symfony 3.0.&lt;/p&gt;

&lt;p&gt;What about a new feature then? Of course, we will still add new features. If
it does not impact the stability of the framework, that's fine: adding the new
table helper for the console is a great example for instance, or supporting
PIDs in the process component. But I believe that we already have a solid set
of features that can be used to solve many complex web problems. And most of
the time, for features that we don't have, there is already a good PHP
librairies that we should leverage instead.&lt;/p&gt;

&lt;p&gt;What about adding a new component? Again, that's still possible either by
extracting code from other components (like we did for the new Debug component
in 2.3 -- and without breaking backward compatibility), or by adding new ones,
but that should not be our priority for the coming months.&lt;/p&gt;

&lt;p&gt;Stability is also a &lt;strong&gt;great enabler&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enhance performance&lt;/strong&gt;: I'm not talking about micro-optimizations here,
but performance on real projects. It was not really practical until now
because Symfony2 was a moving target, but stabilization will enable us to
make performance test beds and benchmarks to ensure that performance
improves with new releases.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Convert more projects&lt;/strong&gt;: I'm really happy that so many Open-Source
projects rely on Symfony one way or another, but we need to convert more.
By promoting sharing, we also promote reusability and compatibility between
many different PHP projects, and that's a very good thing to do.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Convert more "corporate" users&lt;/strong&gt;: When working in big companies, you need
to plan your work ahead of time and for many years. So, you can only work
with librairies that are maintained and stable in the long term.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also want to point out that I've tested this approach with Twig. Twig
version 1.12.3, released last week, is enterily backward compatible with Twig
1.0, released two years ago (to be fair, we made one mistake in 1.2 if I
remember correctly). Of course, Twig is much smaller than Symfony, but if you
take each Symfony component as an individual library, then the same approach
can be applied. Last year, I started to work on Twig 2.0 because I thought
that introducing some great features was not possible with the current
architecture, but with some more work, I did introduce everything I wanted in
Twig 1 without breaking backward compatibility. That's a big win. And that's
probably one of the reasons why so many projects plan to use Twig nowadays
(Drupal, eZPublish, Magento2, ...): &lt;strong&gt;Twig evolves fast but in a stable
way&lt;/strong&gt;. By the way, that's why namespaces won't be introduced in Twig anytime
soon: we don't have a need for them.&lt;/p&gt;

&lt;p&gt;At this point, I should probably answer one question: when will Symfony3 be
released? As you might have guessed now, there is no plan or schedule for
Symfony3; so it's not going to be released anytime soon. We do have an UPGRADE
file for Symfony3, but only because we need to reference deprecated features
that will be supported for all 2.x versions but that will be removed for
Symfony3. And these deprecated features won't trigger any deprecation messages
until we have a plan for Symfony3. So, you can upgrade to the new way, but not
doing so won't affect you at all.&lt;/p&gt;

&lt;p&gt;I hope that this strategy won't slow down the number of contributions. I
believe that it will ease the adoption of Symfony2 and I believe that it won't
stop us from innovating. And by making a stronger contract with our users, we
are opening great opportunities for the Symfony ecosystem: bundles, libraries,
projects, frameworks, CMSes, e-commerce solutions, ...&lt;/p&gt;

&lt;p&gt;To conclude, think of Symfony as being &lt;strong&gt;a PHP middleware&lt;/strong&gt; instead of a PHP
framework and everything should click. This is just the beginning of the
Symfony story, let's make it strong and awesome together.&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/hizCeRwYFYE" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/68/about-symfony-stability-over-features</feedburner:origLink></entry>
      <entry>
  <title>Don't use PHP libraries with known security issues</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/HlHF5Kjog3Y/don-t-use-php-libraries-with-known-security-issues" />
  <id>article-67</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2013-02-19T16:04:00+01:00</updated>
  <content type="html">
    &lt;p&gt;If you are a "connected" developer, you are probably aware of the major
vulnerabilities found in Ruby on Rails recently. To be fair, we've also found
some serious issues in the Symfony code during the last few months.&lt;/p&gt;

&lt;p&gt;As security management should be a top most priority for us and our customers,
I've recently worked on improving the current situation in the Symfony world, with an
&lt;a href="http://symfony.com/blog/security-issue-management-improvements"&gt;enhanced security process&lt;/a&gt;.
But security management is also a very important topic for me because Symfony is
quickly growing in popularity for both end-user projects and Open-Source ones; and more
exposure also means more interest from the "bad guys".&lt;/p&gt;

&lt;p&gt;One of the goal of good security issues management is &lt;strong&gt;transparency&lt;/strong&gt;. That's
why the Symfony project has a simple way of reporting security issues (via the
security [at] symfony.com email address), an easily accessible list of
&lt;a href="http://symfony.com/security#security-advisories"&gt;security advisories&lt;/a&gt;, and
a well defined blog post template to announce security issues. Recently, we
have also enforced the need to have a &lt;a href="http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=symfony"&gt;CVE identifier&lt;/a&gt;
for all security issues to help the broader community to be aware of Symfony security issues.&lt;/p&gt;

&lt;p&gt;The best advice one can give you is to upgrade your favorite libraries as soon
as possible when new versions with security fixes are released. Easier say
than done.&lt;/p&gt;

&lt;p&gt;How do you know that a new release is out? How do you know that it contains
security fixes? For Symfony, you can subscribe to the RSS feed of the Symfony
&lt;a href="http://feeds.feedburner.com/symfony/blog"&gt;blog&lt;/a&gt;, or you can have a look at
our security advisories from time to time. But sometimes, that's not possible
as the project does not even have a blog. Was the security fix announced on
Twitter? Quite possible. But most of the time, smaller libraries just release
a new version without any proper security announcement.&lt;/p&gt;

&lt;p&gt;But I want to provide a simple and efficient way to check for vulnerabilities
in a project and I want to serve more than just the Symfony community. That's
why I'm really proud to announce a new &lt;a href="http://sensiolabs.com/"&gt;SensioLabs&lt;/a&gt;
initiative: a simple way to check if your project depends on third-party
libraries with known security issues. The website explains how it works in
details
(&lt;a href="https://security.sensiolabs.org/"&gt;https://security.sensiolabs.org/&lt;/a&gt;), but
basically, this initiative gives you several ways to check for security issues
in your project dependencies based on the information contained in you
&lt;code&gt;composer.lock&lt;/code&gt; file (you are using Composer to manage your dependencies,
right?):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;website&lt;/strong&gt; itself allows you to upload a &lt;code&gt;composer.lock&lt;/code&gt; to check for
vulnerabilities;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;web service&lt;/strong&gt; can used with curl or to integrate that tool into your
own continuous integration process (it returns its results as plain text or
as a JSON array);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;strong&gt;command line tool&lt;/strong&gt; gives you the same feature as the web service and
the website but nicely packaged as a simple Symfony command.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, the most important part of this initiative is the database where
known security vulnerabilities are referenced. The database is hosted on
Github: &lt;a href="https://github.com/sensiolabs/security-advisories"&gt;https://github.com/sensiolabs/security-advisories&lt;/a&gt;.
We have already referenced known vulnerabilities for Symfony, Zend Framework,
and some well-known Symfony bundles, but the idea is for the community to
help us add more libraries and more importantly to update the database whenever a security issue is fixed in a library.
Even if you don't have a way to easily announce your security fix to the world, at least,
reference it in the database; contributing to the database is really easy: fork
the repository, contribute your changes, and send a pull request (you can even do everything from the Github web interface if you want).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://security.sensiolabs.org/check"&gt;Check&lt;/a&gt; your projects, upgrade your
dependencies when needed, and
&lt;a href="https://github.com/sensiolabs/security-advisories"&gt;contribute&lt;/a&gt; to the
database!&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/HlHF5Kjog3Y" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/67/don-t-use-php-libraries-with-known-security-issues</feedburner:origLink></entry>
      <entry>
  <title>Debugging Silex applications just got fun</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/H2mzPsMAfUA/debugging-silex-applications-just-got-funnier" />
  <id>article-66</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2013-02-07T16:45:00+01:00</updated>
  <content type="html">
    &lt;p&gt;What is the difference between a full-stack framework and a micro framework?
To me, this is not about the amount of code, but more about how you build an
application and how you structure it. Symfony and Silex share a lot of code
(Silex specific code being quite minimal), but developing an application with
one or the other is a totally different experience. And choosing between the
two really depends on your needs, the application you want to create, the
skills of your team, etc...&lt;/p&gt;

&lt;p&gt;One of the big advantages of both Symfony and Silex is that they are based on
the Symfony
&lt;a href="http://symfony.com/doc/master/components/http_kernel/introduction.html"&gt;HttpKernel&lt;/a&gt;
component. Why is it an advantage? One of the reasons is the standardization
of the HTTP dialog between the client and the application server and the
standardization of the HTTP messages via the
&lt;a href="http://symfony.com/doc/master/components/http_foundation/introduction.html"&gt;HttpFoundation&lt;/a&gt;
component; that makes all applications using it easily interoperable (like
Drupal, eZPublish, Laravel, and many others).&lt;/p&gt;

&lt;p&gt;But handling HTTP requests with the Symfony HttpKernel also gives you a lot of
free and optional features like a built-in reverse proxy written in PHP, easy
handling of ESI and Hincludes (via the Fragment sub-framework), or the
gorgeous Symfony web profiler:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://symfony.com/uploads/assets/profiler_timeline.jpg" alt="The timeline panel in the Symfony Web Profiler" /&gt;&lt;/p&gt;

&lt;p&gt;I've just published a &lt;a href="https://github.com/sensiolabs/Silex-WebProfiler"&gt;Silex provider&lt;/a&gt; that
integrates the Symfony web profiler into Silex.&lt;/p&gt;

&lt;p&gt;To get started faster, create a new application via my &lt;a href="https://github.com/fabpot/Silex-Skeleton"&gt;Silex Skeleton&lt;/a&gt;, where the
profiler is already integrated and available when in the development
environment:&lt;/p&gt;

&lt;pre class="php"&gt;composer create-project fabpot/silex-skeleton somewhere/ &lt;span class="nu0"&gt;1.0&lt;/span&gt;.x-dev
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Igor recently wrote about &lt;a href="https://igor.io/2013/02/02/http-kernel-middlewares.html"&gt;HttpKernel&lt;/a&gt;
and &lt;a href="https://igor.io/2013/02/03/http-foundation-value.html"&gt;HttpFoundation&lt;/a&gt;
on his blog... interesting read.&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/H2mzPsMAfUA" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/66/debugging-silex-applications-just-got-funnier</feedburner:origLink></entry>
      <entry>
  <title>Why Symfony?</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/IZMGW3-mv1A/why-symfony" />
  <id>article-65</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2012-09-07T11:22:00+02:00</updated>
  <content type="html">
    &lt;p&gt;Yesterday, Zend Framework 2.0 and Symfony 2.1 were released... almost at the
same time. First, I want to congratulate the Zend Framework team for this huge
milestone; I know that working on a new major version is no small task.&lt;/p&gt;

&lt;p&gt;And of course, people started to ask questions about these new versions and
one of the most popular was: "Why would I choose framework X over framework
Y?". As you can imagine, the answer is not that easy as each framework has its
own specificities.&lt;/p&gt;

&lt;p&gt;I like to think that most popular frameworks are modular enough, fast enough,
well-documented enough; they are using well-known design patterns. So, besides
the buzzwords, what is unique to Symfony? Why would you want to use Symfony
instead of X (replace X with ZF, CakePHP, Lithium, Laravel, Slim, Yii, you name it)? That's the question I'm going to answer in this post.&lt;/p&gt;

&lt;p&gt;So, without further ado, here are my main selling points for Symfony:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Symfony is not a framework but a project. Depending on your needs, you can
choose to use some of the &lt;a href="http://symfony.com/components"&gt;Symfony
Components&lt;/a&gt;, the
&lt;a href="http://silex.sensiolabs.org/"&gt;Silex&lt;/a&gt; micro-framework, or the full-stack
framework.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Symfony is used by many large companies (like the BBC or CBS), by many large websites (like &lt;a href="http://www.ted.com/"&gt;TED&lt;/a&gt;, &lt;a href="http://wetter.com/"&gt;wetter.com&lt;/a&gt;, &lt;a href="http://lockerz.com/"&gt;Lockers&lt;/a&gt; or even YouPorn just to name a few) and some Open-Source projects are
also powered by Symfony (CMSes like
&lt;a href="http://symfony.com/blog/symfony2-meets-drupal-8"&gt;Drupal&lt;/a&gt; or
&lt;a href="http://symfony.com/blog/symfony2-meets-ez-publish-5"&gt;eZpublish&lt;/a&gt;, libraries
like &lt;a href="https://github.com/sebastianbergmann/phpunit/"&gt;PHPUnit&lt;/a&gt; or
&lt;a href="http://www.doctrine-project.org/"&gt;Doctrine&lt;/a&gt;, products like
&lt;a href="https://www.phpbb.com/"&gt;phpBB&lt;/a&gt; or &lt;a href="http://www.shopware.de/"&gt;shopware&lt;/a&gt;, and even frameworks like
&lt;a href="http://www.ppi.io/"&gt;PPI&lt;/a&gt; or &lt;a href="http://laravel.com/"&gt;Laravel&lt;/a&gt;). This brings a lot of interoperability between all these solutions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Symfony enjoys a huge &lt;a href="http://connect.sensiolabs.com/"&gt;community&lt;/a&gt; of users
and &lt;a href="http://symfony.com/contributors"&gt;contributors&lt;/a&gt;; during the last year
alone, 550+ people contributed to the Symfony core and the community
created over 1,600 &lt;a href="http://knpbundles.com/"&gt;bundles&lt;/a&gt; for the full-stack
framework. Symfony also has several annual dedicated
&lt;a href="http://live.symfony.com/"&gt;conferences&lt;/a&gt; around the world and a large number
of user &lt;a href="http://symfony.com/events"&gt;groups&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Symfony has been created in 2005 and here to stay. Besides
&lt;a href="http://sensiolabs.com/"&gt;SensioLabs&lt;/a&gt;, many other companies rely on Symfony
for their clients and they contribute, invest money, and sponsor the future
of the project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Symfony embraces the "don't reinvent the wheel" philosophy, and provides
tight integration with many other Open-Source projects (like Monolog,
Assetic, Doctrine, Propel, ...).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Symfony tries to bring innovation to PHP: it was one of the first major
frameworks to embrace PHP 5.3, to introduce the usage of a Dependency
Injection container, and to use a templating engine for its templates by
default, &lt;a href="http://twig.sensiolabs.org/"&gt;Twig&lt;/a&gt;, which is now also adopted by
major CMSes like Drupal and eZpublish. Symfony also has some unique
features like its gorgeous debug toolbar and its great built-in profiler.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you think I've missed some important selling points, feel free to list them
in the comments and in a few days, I will aggregate everything in a new page
that will be hosted on symfony.com to help users make an informed decision
when choosing a framework.&lt;/p&gt;

&lt;p&gt;And if other frameworks would like to do the same, I would happily add links
to their pages on the Symfony one, and if in turn, they do the same, that
would create a ring of links that will ease the choice of a PHP framework for
developers.&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/IZMGW3-mv1A" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/65/why-symfony</feedburner:origLink></entry>
      <entry>
  <title>PHP is much better than you think</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/CRhRc-4JzKU/php-is-much-better-than-you-think" />
  <id>article-64</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2012-07-04T09:55:00+02:00</updated>
  <content type="html">
    &lt;p&gt;Rants about PHP are everywhere, and they even come from smart guys. When Jeff
Atwood wrote yet another
&lt;a href="http://www.codinghorror.com/blog/2012/06/the-php-singularity.html"&gt;rant&lt;/a&gt;
about PHP, it made me think about the good parts of PHP.&lt;/p&gt;

&lt;p&gt;The biggest problem of these rants is that they come from people stuck in the
old days of PHP. They either don't care or they don't want to admit that PHP
actually evolves at a very fast pace, both at the language level but also at
the community level. In fact, it evolves much faster than any other language
or web platform. It has not always been the case, but the last 5 years have
been an amazing journey for PHP.&lt;/p&gt;

&lt;p&gt;Before talking about the amazing things the PHP community has achieved
recently, let's have a look at some interesting numbers: PHP is used by
&lt;a href="http://w3techs.com/technologies/overview/programming_language/all"&gt;77.9%&lt;/a&gt; of
all the websites whose server-side programming language is known. WordPress is
used by
&lt;a href="http://w3techs.com/technologies/overview/content_management/all"&gt;16.6%&lt;/a&gt; of
&lt;strong&gt;all the websites&lt;/strong&gt; in the world. If you have a look at the top three CMSes,
for the websites that use a monitored content management system: Wordpress is
first with 54.3%, Joomla is second with 9.2%, and Drupal is third with 6.8%.
Three products written in PHP.&lt;/p&gt;

&lt;p&gt;PHP must have done something right, no?&lt;/p&gt;

&lt;p&gt;Now, let me tell you a secret, the PHP "tour de force": Despite the changes
over the years, PHP is still the &lt;strong&gt;easiest language&lt;/strong&gt; to learn for
non-technical people: it allows anyone to create dynamic websites faster than
with any other technologies, it allows anyone to host websites cheaply and
without any hassles. PHP is probably not the best designed language in the
world, but it lets you &lt;strong&gt;get things done&lt;/strong&gt;, and you can't argue with that.&lt;/p&gt;

&lt;h2&gt;PHP, the Language&lt;/h2&gt;

&lt;p&gt;PHP 5.0 (released in 2004) brought us a very solid object model... wait a
minute, I'm talking about something released almost 8 years ago. Fast forward
now. The latest PHP release, PHP 5.4, comes with all the bells and whistles
you might dream of in a modern web language: yes, PHP supports namespaces;
yes, PHP supports closures; yes, PHP supports traits.&lt;/p&gt;

&lt;p&gt;It took some time, but PHP 5.4 also comes with some nice syntactic sugar that
makes the whole experience better than ever: yes, PHP supports &lt;code&gt;[]&lt;/code&gt; to define
arrays; yes, PHP supports calling a method on a newly created object (&lt;code&gt;(new
Foo())-&amp;gt;bar()&lt;/code&gt;); yes, PHP supports getting an array item from any expression
(&lt;code&gt;$foo-&amp;gt;bar()[1]&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;PHP has even learned from its mistakes: &lt;code&gt;register_globals&lt;/code&gt; and &lt;code&gt;magic_quotes&lt;/code&gt;
are definitely gone.&lt;/p&gt;

&lt;p&gt;Last, but not the least, PHP even comes with a built-in web server that eases
local testing... and it starts in a matter of micro-seconds.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next challenges&lt;/em&gt;: How do we "upgrade" all the old tutorials talking about PHP
on the web? What is the best way to support the WebSocket technology in a PHP
application?&lt;/p&gt;

&lt;h2&gt;PHP, the Ecosystem&lt;/h2&gt;

&lt;p&gt;Having a good language is great, but having a great ecosystem is even better.
And the PHP ecosystem has evolved a lot in the last few years.&lt;/p&gt;

&lt;h3&gt;Git&lt;/h3&gt;

&lt;p&gt;I won't talk too much about this one. Git is everywhere and PHP embraced Git
pretty fast. Almost all major PHP libraries, frameworks, and products are now
using Git, including PHP itself.&lt;/p&gt;

&lt;h3&gt;Composer&lt;/h3&gt;

&lt;p&gt;Two years ago, I wanted to get rid of my ugly-PEAR-hack I did in symfony 1 to
support plugins. I wanted to replace it with something that was able to manage
project dependencies, not a global installer like PEAR. Managing dependencies
is not an easy task, so I tried to find the best algorithm to manage software
dependencies; I had a look at everything: from Perl to Ruby, from Debian to
Redhat. Nothing was satisfactory: only homegrown solutions that happen to
work... empirically. Then, I stumbled upon
&lt;a href="http://en.wikipedia.org/wiki/ZYpp"&gt;ZYpp&lt;/a&gt;. That was it. ZYpp uses a
&lt;a href="http://en.wikipedia.org/wiki/Boolean_satisfiability_problem"&gt;SAT&lt;/a&gt; solver to
manage dependencies. Fast forward. Thanks to the hard work of
&lt;a href="http://www.naderman.de/"&gt;Nils&lt;/a&gt; Adermann and &lt;a href="http://seld.be/"&gt;Jordi&lt;/a&gt;
Boggiano, PHP now has one of the best dependency manager,
&lt;a href="http://getcomposer.org/"&gt;Composer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Yes, PHP has a better dependency manager than any other languages.&lt;/p&gt;

&lt;p&gt;And thanks to Git, Composer, and the PHP built-in web server, it has never
been &lt;a href="https://twitter.com/fabpot/status/220254879459975169"&gt;easier&lt;/a&gt; to
download/install/test a PHP project.&lt;/p&gt;

&lt;p&gt;Want to test Symfony (using PHP 5.4)?&lt;/p&gt;

&lt;pre class="command-line"&gt;&lt;code&gt;$ composer.phar create-project symfony/framework-standard-edition
$ cd framework-standard-edition
$ ./app/console server:run
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Want to test Silex?&lt;/p&gt;

&lt;pre class="command-line"&gt;&lt;code&gt;$ composer.phar create-project fabpot/silex-skeleton
$ cd silex-skeleton
$ php -S localhost:8888 -t web/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Don't know Composer yet? You should learn about it. Browse
&lt;a href="http://packagist.org/packages/"&gt;Packagist&lt;/a&gt;, the main Composer repository: it
already has 1900+ packages available and they have been installed more than a million times
in less than 3 months.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next challenge&lt;/em&gt;: include the Composer installer in the next PHP version?&lt;/p&gt;

&lt;h3&gt;Collaboration&lt;/h3&gt;

&lt;p&gt;Community collaboration is the most important point of this post; the one I'm
the most proud of. We start to see better collaboration between PHP projects,
even from the very big ones, the ones you would think are large enough to not
care about the others.&lt;/p&gt;

&lt;p&gt;phpBB, &lt;a href="http://symfony.com/blog/symfony2-meets-drupal-8"&gt;Drupal&lt;/a&gt;, &lt;a href="http://symfony.com/blog/symfony2-meets-ez-publish-5"&gt;eZ Publish&lt;/a&gt;, Symfony, and many others (phpDocumentor, PHPUnit,
Behat, Zikula, Propel, Doctrine, Midgard, ...) are &lt;strong&gt;sharing&lt;/strong&gt; code. Yes, they
are "competitors" but they all understood that cross-pollination was a good
thing. And Composer is a good enabler.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Next challenge&lt;/em&gt;: Convince even more projects to join the trend.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Let me say it again: PHP is probably not the best language out there, and I'm
the first one to scream about its quirks, but &lt;strong&gt;PHP is the best web
platform&lt;/strong&gt;... ever.&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/CRhRc-4JzKU" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/64/php-is-much-better-than-you-think</feedburner:origLink></entry>
      <entry>
  <title>Sami: Yet another PHP API documentation generator</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/1NqPufqqSeg/sami-yet-another-php-api-documentation-generator" />
  <id>article-63</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2012-05-15T13:58:00+02:00</updated>
  <content type="html">
    &lt;p&gt;Today is my "&lt;em&gt;let open source some of my private Github repositories&lt;/em&gt;" day,
and more specifically, I'm releasing a bunch of code related to documentation.&lt;/p&gt;

&lt;p&gt;Earlier today, I've released the Sphinx
&lt;a href="https://github.com/fabpot/sphinx-php"&gt;extensions&lt;/a&gt; I'm using to generate the
&lt;a href="http://symfony.com/doc"&gt;Symfony&lt;/a&gt; documentation.&lt;/p&gt;

&lt;p&gt;And now, I'm releasing my API documentation
&lt;a href="https://github.com/fabpot/Sami"&gt;generator&lt;/a&gt;. Yes, I know that PHP already has
a bunch of such generators, but I started to work on this project several
years ago, when the only viable option was the old phpdocumentor.&lt;/p&gt;

&lt;p&gt;Nowadays, &lt;a href="http://phpdoc.org/"&gt;phpDocumentor&lt;/a&gt; version 2 is probably the best
option out there as it has a good architecture, it works fine, it is
extensible, and quite a few big PHP projects is already using it. And that's
fine. I don't want to compete with it, I don't want to replace it, I'm just
open sourcing some code used by &lt;a href="http://api.symfony.com/"&gt;Symfony&lt;/a&gt;,
&lt;a href="http://silex.sensiolabs.org/api/index.html"&gt;Twig&lt;/a&gt;, and
&lt;a href="http://silex.sensiolabs.org/api/index.html"&gt;Silex&lt;/a&gt; because I'm not
comfortable with closed-source software. And to be totally honest and
transparent, I have not released the code before because it was not "good
enough".&lt;/p&gt;

&lt;p&gt;With this disclaimer out of the way, let's see what makes Sami "different"
(compared to phpdocumentor)?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It uses a PHP file for configuration to give a very flexible way of
tweaking the API generation;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It uses &lt;a href="http://twig.sensiolabs.org/"&gt;Twig&lt;/a&gt; for templating;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It uses a dependency injection container
(&lt;a href="https://github.com/fabpot/Pimple"&gt;Pimple&lt;/a&gt;) to let you override any
internal class;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It only works with PHP 5.3 (but it can generate documentation for PHP 5.2
projects);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It uses the excellent &lt;a href="https://github.com/nikic/PHP-Parser"&gt;PHP Parser&lt;/a&gt;
project for PHP code parsing;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is able to manage versions of your code to generate documentation for
all of them in a single tree (without the overhead of re-parsing everything
for each version of course).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Curious about what Sami generates? Have a look at the &lt;a href="http://api.symfony.com/"&gt;Symfony
API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;p&gt;First, get Sami from &lt;a href="https://github.com/fabpot/Sami"&gt;Github&lt;/a&gt; (or integrate it
as a dependency in your project &lt;a href="http://packagist.org/packages/sami/sami"&gt;Composer&lt;/a&gt; file --
you are using &lt;a href="http://getcomposer.org/"&gt;Composer&lt;/a&gt;, right?):&lt;/p&gt;

&lt;pre class="text"&gt;https://github.com/fabpot/Sami
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;You can also download an &lt;a href="https://github.com/fabpot/Sami/downloads"&gt;archive&lt;/a&gt;
from Github.&lt;/p&gt;

&lt;p&gt;As Sami uses Composer to manage its dependencies, installing it is a matter of
running composer:&lt;/p&gt;

&lt;pre class="command-line"&gt;&lt;code&gt;$ composer.phar install
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Check that everything worked as expected by executing the &lt;code&gt;sami.php&lt;/code&gt; file
without any arguments:&lt;/p&gt;

&lt;pre class="command-line"&gt;&lt;code&gt;$ php sami.php
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Configuration&lt;/h2&gt;

&lt;p&gt;Before generating documentation, you must create a configuration file. Here is
the simplest possible one:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="kw2"&gt;new&lt;/span&gt; Sami\Sami&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'/path/to/symfony/src'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The configuration file must return an instance of &lt;code&gt;Sami\Sami&lt;/code&gt; and the first
argument of the constructor is the path to the code you want to generate
documentation for.&lt;/p&gt;

&lt;p&gt;Actually, instead of a directory, you can use any valid PHP iterator (and for
that matter any instance of the Symfony
&lt;a href="http://symfony.com/doc/current/components/finder.html"&gt;Finder&lt;/a&gt; class):&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
use Sami\Sami;
use Symfony\Component\Finder\Finder;
&amp;nbsp;
&lt;span class="re0"&gt;$iterator&lt;/span&gt; = Finder::&lt;span class="me2"&gt;create&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;files&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;name&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'*.php'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;exclude&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Resources'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;exclude&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Tests'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;in&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'/path/to/symfony/src'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&amp;nbsp;
&lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="kw2"&gt;new&lt;/span&gt; Sami&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$iterator&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;Sami&lt;/code&gt; constructor optionally takes an array of options as a second
argument:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="kw2"&gt;new&lt;/span&gt; Sami&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$iterator&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;
    &lt;span class="st0"&gt;'theme'&lt;/span&gt;                =&amp;gt; &lt;span class="st0"&gt;'symfony'&lt;/span&gt;,
    &lt;span class="st0"&gt;'title'&lt;/span&gt;                =&amp;gt; &lt;span class="st0"&gt;'Symfony2 API'&lt;/span&gt;,
    &lt;span class="st0"&gt;'build_dir'&lt;/span&gt;            =&amp;gt; __DIR__.&lt;span class="st0"&gt;'/build'&lt;/span&gt;,
    &lt;span class="st0"&gt;'cache_dir'&lt;/span&gt;            =&amp;gt; __DIR__.&lt;span class="st0"&gt;'/cache'&lt;/span&gt;,
    &lt;span class="st0"&gt;'default_opened_level'&lt;/span&gt; =&amp;gt; &lt;span class="nu0"&gt;2&lt;/span&gt;,
&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;And here is how you can configure different versions:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
use Sami\Sami;
use Sami\Version\GitVersionCollection;
use Symfony\Component\Finder\Finder;
&amp;nbsp;
&lt;span class="re0"&gt;$iterator&lt;/span&gt; = Finder::&lt;span class="me2"&gt;create&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;files&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;name&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'*.php'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;exclude&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Resources'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;exclude&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Tests'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;in&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$dir&lt;/span&gt; = &lt;span class="st0"&gt;'/path/to/symfony/src'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&amp;nbsp;
&lt;span class="co1"&gt;// generate documentation for all v2.0.* tags, the 2.0 branch, and the master one&lt;/span&gt;
&lt;span class="re0"&gt;$versions&lt;/span&gt; = GitVersionCollection::&lt;span class="me2"&gt;create&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$dir&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;addFromTags&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'v2.0.*'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;add&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'2.0'&lt;/span&gt;, &lt;span class="st0"&gt;'2.0 branch'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;add&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'master'&lt;/span&gt;, &lt;span class="st0"&gt;'master branch'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&amp;nbsp;
&lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="kw2"&gt;new&lt;/span&gt; Sami&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$iterator&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;
    &lt;span class="st0"&gt;'theme'&lt;/span&gt;                =&amp;gt; &lt;span class="st0"&gt;'symfony'&lt;/span&gt;,
    &lt;span class="st0"&gt;'versions'&lt;/span&gt;             =&amp;gt; &lt;span class="re0"&gt;$versions&lt;/span&gt;,
    &lt;span class="st0"&gt;'title'&lt;/span&gt;                =&amp;gt; &lt;span class="st0"&gt;'Symfony2 API'&lt;/span&gt;,
    &lt;span class="st0"&gt;'build_dir'&lt;/span&gt;            =&amp;gt; __DIR__.&lt;span class="st0"&gt;'/../build/sf2/%version%'&lt;/span&gt;,
    &lt;span class="st0"&gt;'cache_dir'&lt;/span&gt;            =&amp;gt; __DIR__.&lt;span class="st0"&gt;'/../cache/sf2/%version%'&lt;/span&gt;,
    &lt;span class="st0"&gt;'default_opened_level'&lt;/span&gt; =&amp;gt; &lt;span class="nu0"&gt;2&lt;/span&gt;,
&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;To generate documentation for a PHP 5.2 project, simply set the
&lt;code&gt;simulate_namespaces&lt;/code&gt; option to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Rendering&lt;/h2&gt;

&lt;p&gt;Now that we have a configuration file, let's generate the API documentation:&lt;/p&gt;

&lt;pre class="command-line"&gt;&lt;code&gt;$ php sami.php update /path/to/config.php
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The generated documentation can be found under the configured &lt;code&gt;build/&lt;/code&gt;
directory (note that the client side search engine does not work on Chrome due
to JavaScript execution restriction -- it works fine in Firefox).&lt;/p&gt;

&lt;p&gt;By default, Sami is configured to run in "incremental" mode. It means that
when running the &lt;code&gt;update&lt;/code&gt; command, Sami only re-generates the files that need
to be updated based on what has changed in your code since the last execution.&lt;/p&gt;

&lt;p&gt;Sami also detects problems in your phpdoc and can tell you what you need to
fix if you add the &lt;code&gt;-v&lt;/code&gt; option:&lt;/p&gt;

&lt;pre class="command-line"&gt;&lt;code&gt;$ php sami.php update /path/to/config.php -v
&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Creating a Theme&lt;/h2&gt;

&lt;p&gt;If the default themes do not suit your needs, you can very easily create a new
one, or just override an existing one.&lt;/p&gt;

&lt;p&gt;A theme is just a directory with a &lt;code&gt;manifest.yml&lt;/code&gt; file that describes the
theme (this is a YAML file):&lt;/p&gt;

&lt;pre class="php"&gt;name:   symfony
parent: enhanced
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The above configuration creates a new &lt;code&gt;symfony&lt;/code&gt; theme based on the &lt;code&gt;enhanced&lt;/code&gt;
built-in theme. To override a template, just create a file with the same name
as the original one. For instance, here is how you can extend the default
class template to prefix the class name with "Class " in the class page title:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;span class="co2"&gt;# pages/class.twig #}&lt;/span&gt;
&amp;nbsp;
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;% &lt;span class="kw2"&gt;extends&lt;/span&gt; &lt;span class="st0"&gt;'default/pages/class.twig'&lt;/span&gt; %&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;% block title %&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="kw2"&gt;Class&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt; parent&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;% endblock %&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;If you are familiar with Twig, you will be able to very easily tweak every
aspect of the templates as everything has been well isolated in named Twig
blocks.&lt;/p&gt;

&lt;p&gt;A theme can also add more templates and static files. Here is the manifest for
the default theme:&lt;/p&gt;

&lt;pre class="php"&gt;name: &lt;span class="kw2"&gt;default&lt;/span&gt;
&amp;nbsp;
&lt;a href="http://www.php.net/static"&gt;&lt;span class="kw3"&gt;static&lt;/span&gt;&lt;/a&gt;:
    &lt;span class="st0"&gt;'stylesheet.css'&lt;/span&gt;:        &lt;span class="st0"&gt;'stylesheet.css'&lt;/span&gt;
&amp;nbsp;
&lt;a href="http://www.php.net/global"&gt;&lt;span class="kw3"&gt;global&lt;/span&gt;&lt;/a&gt;:
    &lt;span class="st0"&gt;'index.twig'&lt;/span&gt;:            &lt;span class="st0"&gt;'index.html'&lt;/span&gt;
    &lt;span class="st0"&gt;'namespaces.twig'&lt;/span&gt;:       &lt;span class="st0"&gt;'namespaces-frame.html'&lt;/span&gt;
    &lt;span class="st0"&gt;'classes.twig'&lt;/span&gt;:          &lt;span class="st0"&gt;'classes-frame.html'&lt;/span&gt;
    &lt;span class="st0"&gt;'pages/opensearch.twig'&lt;/span&gt;: &lt;span class="st0"&gt;'opensearch.xml'&lt;/span&gt;
    &lt;span class="st0"&gt;'pages/index.twig'&lt;/span&gt;:      &lt;span class="st0"&gt;'doc-index.html'&lt;/span&gt;
    &lt;span class="st0"&gt;'pages/namespaces.twig'&lt;/span&gt;: &lt;span class="st0"&gt;'namespaces.html'&lt;/span&gt;
    &lt;span class="st0"&gt;'pages/interfaces.twig'&lt;/span&gt;: &lt;span class="st0"&gt;'interfaces.html'&lt;/span&gt;
    &lt;span class="st0"&gt;'pages/classes.twig'&lt;/span&gt;:    &lt;span class="st0"&gt;'classes.html'&lt;/span&gt;
&amp;nbsp;
namespace:
    &lt;span class="st0"&gt;'namespace.twig'&lt;/span&gt;:        &lt;span class="st0"&gt;'%s/namespace-frame.html'&lt;/span&gt;
    &lt;span class="st0"&gt;'pages/namespace.twig'&lt;/span&gt;:  &lt;span class="st0"&gt;'%s.html'&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt;:
    &lt;span class="st0"&gt;'pages/class.twig'&lt;/span&gt;:      &lt;span class="st0"&gt;'%s.html'&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Files are contained into sections, depending on how Sami needs to treat them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;static&lt;/code&gt;: Files are copied as is (for assets like images, stylesheets, or
JavaScript files);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;global&lt;/code&gt;: Templates that do not depend on the current class context;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;namespace&lt;/code&gt;: Templates that should be generated for every namespace;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;class&lt;/code&gt;: Templates that should be generated for every class.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all there is to it for now. More documentation and a website is coming soon.&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/1NqPufqqSeg" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/63/sami-yet-another-php-api-documentation-generator</feedburner:origLink></entry>
      <entry>
  <title>Create your own framework... on top of the Symfony2 Components (part 12)</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/2xeMj6zRXu8/create-your-own-framework-on-top-of-the-symfony2-components-part-12" />
  <id>article-62</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2012-01-25T07:39:00+01:00</updated>
  <content type="html">
    &lt;blockquote class="note"&gt;&lt;p&gt;
  This article is part of a series of articles that explains how to create a framework with
  the Symfony2 Components:
  &lt;a href="http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1"&gt;1&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/51/create-your-own-framework-on-top-of-the-symfony2-components-part-2"&gt;2&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/52/create-your-own-framework-on-top-of-the-symfony2-components-part-3"&gt;3&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/53/create-your-own-framework-on-top-of-the-symfony2-components-part-4"&gt;4&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/54/create-your-own-framework-on-top-of-the-symfony2-components-part-5"&gt;5&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/55/create-your-own-framework-on-top-of-the-symfony2-components-part-6"&gt;6&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/56/create-your-own-framework-on-top-of-the-symfony2-components-part-7"&gt;7&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/57/create-your-own-framework-on-top-of-the-symfony2-components-part-8"&gt;8&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/58/create-your-own-framework-on-top-of-the-symfony2-components-part-9"&gt;9&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/59/create-your-own-framework-on-top-of-the-symfony2-components-part-10"&gt;10&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/60/create-your-own-framework-on-top-of-the-symfony2-components-part-11"&gt;11&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/62/create-your-own-framework-on-top-of-the-symfony2-components-part-12"&gt;12&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the last installment of this series, we have emptied the
&lt;code&gt;Simplex\\Framework&lt;/code&gt; class by extending the &lt;code&gt;HttpKernel&lt;/code&gt; class from
Symfony. Seeing this empty class, you might be tempted to move some code from
the front controller to it:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/Framework.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
use Symfony\Component\Routing;
use Symfony\Component\HttpKernel;
use Symfony\Component\EventDispatcher\EventDispatcher;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; Framework &lt;span class="kw2"&gt;extends&lt;/span&gt; HttpKernel\HttpKernel
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; __construct&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$routes&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$context&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Routing\RequestContext&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="re0"&gt;$matcher&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Routing\Matcher\UrlMatcher&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$routes&lt;/span&gt;, &lt;span class="re0"&gt;$context&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="re0"&gt;$resolver&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\Controller\ControllerResolver&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class="re0"&gt;$dispatcher&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; EventDispatcher&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\EventListener\RouterListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$matcher&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\EventListener\ResponseListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'UTF-8'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        parent::__construct&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;, &lt;span class="re0"&gt;$resolver&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The front controller code would become more concise:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/web/front.php&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw1"&gt;require_once&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../vendor/.composer/autoload.php'&lt;/span&gt;;
&amp;nbsp;
use Symfony\Component\HttpFoundation\Request;
&amp;nbsp;
&lt;span class="re0"&gt;$request&lt;/span&gt; = Request::&lt;span class="me2"&gt;createFromGlobals&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$routes&lt;/span&gt; = &lt;span class="kw1"&gt;include&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../src/app.php'&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$framework&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\Framework&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$routes&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$framework&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;handle&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;send&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Having a more concise front controller means that you can have more than one
for a single application. Why would it be useful? To allow having different
configuration for the development environment and the production one for
instance. In the development environment, you might want to have error
reporting turned on and errors displayed in the browser to ease debugging:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;a href="http://www.php.net/ini_set"&gt;&lt;span class="kw3"&gt;ini_set&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'display_errors'&lt;/span&gt;, &lt;span class="nu0"&gt;1&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;a href="http://www.php.net/error_reporting"&gt;&lt;span class="kw3"&gt;error_reporting&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;-1&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;... but you certainly won't want that same configuration on the production
environment. Having two different front controllers gives you the opportunity
to have a slightly different configuration for each of them.&lt;/p&gt;

&lt;p&gt;So, moving code from the front controller to the framework class makes our
framework more configurable, but at the same time, it introduces a lot of
issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;We are not able to register custom listeners anymore as the dispatcher is
not available outside the Framework class (an easy workaround could be the
adding of a &lt;code&gt;Framework::getEventDispatcher()&lt;/code&gt; method);&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We have lost the flexibility we had before; you cannot change the
implementation of the &lt;code&gt;UrlMatcher&lt;/code&gt; or of the &lt;code&gt;ControllerResolver&lt;/code&gt; anymore;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Related to the previous point, we cannot test our framework easily anymore
as it's impossible to mock internal objects;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We cannot change the charset passed to ResponseListener anymore (a
workaround could be to pass it as a constructor argument).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The previous code did not exhibit the same issues because we used dependency
injection; all dependencies of our objects were injected into their
constructors (for instance, the event dispatcher were injected into the
framework so that we had total control of its creation and configuration).&lt;/p&gt;

&lt;p&gt;Does it means that we have to make a choice between flexibility,
customization, ease of testing and not having to copy and paste the same code
into each application front controller? As you might expect, there is a
solution. We can solve all these issues and some more by using the Symfony2
dependency injection container:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="st0"&gt;&amp;quot;require&amp;quot;&lt;/span&gt;: &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="st0"&gt;&amp;quot;symfony/class-loader&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/http-foundation&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/routing&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/http-kernel&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/event-dispatcher&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/dependency-injection&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;,
    &lt;span class="st0"&gt;&amp;quot;autoload&amp;quot;&lt;/span&gt;: &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="st0"&gt;&amp;quot;psr-0&amp;quot;&lt;/span&gt;: &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt; &lt;span class="st0"&gt;&amp;quot;Simplex&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;src/&amp;quot;&lt;/span&gt;, &lt;span class="st0"&gt;&amp;quot;Calendar&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;src/&amp;quot;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Create a new file to host the dependency injection container configuration:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/container.php&lt;/span&gt;
&amp;nbsp;
use Symfony\Component\DependencyInjection;
use Symfony\Component\DependencyInjection\Reference;
&amp;nbsp;
&lt;span class="re0"&gt;$sc&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; DependencyInjection\ContainerBuilder&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'context'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\R&lt;/span&gt;outing&lt;span class="es0"&gt;\R&lt;/span&gt;equestContext'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'matcher'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\R&lt;/span&gt;outing&lt;span class="es0"&gt;\M&lt;/span&gt;atcher&lt;span class="es0"&gt;\U&lt;/span&gt;rlMatcher'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;setArguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$routes&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'context'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'resolver'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\H&lt;/span&gt;ttpKernel&lt;span class="es0"&gt;\C&lt;/span&gt;ontroller&lt;span class="es0"&gt;\C&lt;/span&gt;ontrollerResolver'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.router'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\H&lt;/span&gt;ttpKernel&lt;span class="es0"&gt;\E&lt;/span&gt;ventListener&lt;span class="es0"&gt;\R&lt;/span&gt;outerListener'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;setArguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'matcher'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.response'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\H&lt;/span&gt;ttpKernel&lt;span class="es0"&gt;\E&lt;/span&gt;ventListener&lt;span class="es0"&gt;\R&lt;/span&gt;esponseListener'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;setArguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'UTF-8'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.exception'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\H&lt;/span&gt;ttpKernel&lt;span class="es0"&gt;\E&lt;/span&gt;ventListener&lt;span class="es0"&gt;\E&lt;/span&gt;xceptionListener'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;setArguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Calendar&lt;span class="es0"&gt;\\&lt;/span&gt;Controller&lt;span class="es0"&gt;\\&lt;/span&gt;ErrorController::exceptionAction'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'dispatcher'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\E&lt;/span&gt;ventDispatcher&lt;span class="es0"&gt;\E&lt;/span&gt;ventDispatcher'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;addMethodCall&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'addSubscriber'&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.router'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;addMethodCall&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'addSubscriber'&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.response'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;addMethodCall&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'addSubscriber'&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.exception'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'framework'&lt;/span&gt;, &lt;span class="st0"&gt;'Simplex&lt;span class="es0"&gt;\F&lt;/span&gt;ramework'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;setArguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'dispatcher'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'resolver'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&amp;nbsp;
&lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="re0"&gt;$sc&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The goal of this file is to configure your objects and their dependencies.
Nothing is instantiated during this configuration step. This is purely a
static description of the objects you need to manipulate and how to create
them. Objects will be created on-demand when you access them from the
container or when the container needs them to create other objects.&lt;/p&gt;

&lt;p&gt;For instance, to create the router listener, we tell Symfony that its class
name is &lt;code&gt;Symfony\Component\HttpKernel\EventListener\RouterListeners&lt;/code&gt;, and
that its constructor takes a matcher object (&lt;code&gt;new Reference('matcher')&lt;/code&gt;). As
you can see, each object is referenced by a name, a string that uniquely
identifies each object. The name allows us to get an object and to reference
it in other object definitions.&lt;/p&gt;

&lt;blockquote class="note"&gt;&lt;p&gt;
  By default, every time you get an object from the container, it returns the
  exact same instance. That's because a container manages your "global" objects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The front controller is now only about wiring everything together:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/web/front.php&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw1"&gt;require_once&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../vendor/.composer/autoload.php'&lt;/span&gt;;
&amp;nbsp;
use Symfony\Component\HttpFoundation\Request;
&amp;nbsp;
&lt;span class="re0"&gt;$routes&lt;/span&gt; = &lt;span class="kw1"&gt;include&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../src/app.php'&lt;/span&gt;;
&lt;span class="re0"&gt;$sc&lt;/span&gt; = &lt;span class="kw1"&gt;include&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../src/container.php'&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$request&lt;/span&gt; = Request::&lt;span class="me2"&gt;createFromGlobals&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;get&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'framework'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;handle&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;send&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;As all the objects are now created in the dependency injection container, the framework code should be the previous simple version:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/Framework.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
use Symfony\Component\HttpKernel\HttpKernel;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; Framework &lt;span class="kw2"&gt;extends&lt;/span&gt; HttpKernel
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;blockquote class="note"&gt;&lt;p&gt;
  If you want a light alternative for your container, consider
  &lt;a href="https://github.com/fabpot/Pimple"&gt;Pimple&lt;/a&gt;, a simple dependency injection
  container in about 60 lines of PHP code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, here is how you can register a custom listener in the front controller:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.string_response'&lt;/span&gt;, &lt;span class="st0"&gt;'Simplex&lt;span class="es0"&gt;\S&lt;/span&gt;tringResponseListener'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getDefinition&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'dispatcher'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;addMethodCall&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'addSubscriber'&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.string_response'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Beside describing your objects, the dependency injection container can also be
configured via parameters. Let's create one that defines if we are in debug
mode or not:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setParameter&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'debug'&lt;/span&gt;, &lt;span class="kw2"&gt;true&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;a href="http://www.php.net/echo"&gt;&lt;span class="kw3"&gt;echo&lt;/span&gt;&lt;/a&gt; &lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getParameter&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'debug'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;These parameters can be used when defining object definitions. Let's make the
charset configurable:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'listener.response'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\H&lt;/span&gt;ttpKernel&lt;span class="es0"&gt;\E&lt;/span&gt;ventListener&lt;span class="es0"&gt;\R&lt;/span&gt;esponseListener'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;setArguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'%charset%'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;After this change, you must set the charset before using the response listener
object:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setParameter&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'charset'&lt;/span&gt;, &lt;span class="st0"&gt;'UTF-8'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Instead of relying on the convention that the routes are defined by the
&lt;code&gt;$routes&lt;/code&gt; variables, let's use a parameter again:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;register&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'matcher'&lt;/span&gt;, &lt;span class="st0"&gt;'Symfony&lt;span class="es0"&gt;\C&lt;/span&gt;omponent&lt;span class="es0"&gt;\R&lt;/span&gt;outing&lt;span class="es0"&gt;\M&lt;/span&gt;atcher&lt;span class="es0"&gt;\U&lt;/span&gt;rlMatcher'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    -&amp;gt;&lt;span class="me1"&gt;setArguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'%routes%'&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; Reference&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'context'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;And the related change in the front controller:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$sc&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setParameter&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'routes'&lt;/span&gt;, &lt;span class="kw1"&gt;include&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../src/app.php'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;We have obviously barely scratched the surface of what you can do with the
container: from class names as parameters, to overriding existing object
definitions, from scope support to dumping a container to a plain PHP class,
and much more. The Symfony dependency injection container is really powerful
and is able to manage any kind of PHP classes.&lt;/p&gt;

&lt;p&gt;Don't yell at me if you don't want to have a dependency injection container in
your framework. If you don't like it, don't use it. It's your framework, not
mine.&lt;/p&gt;

&lt;p&gt;This is (already) the last part of my series on creating a framework on top of
the Symfony2 components. I'm aware that many topics have not been covered in
great details, but hopefully it gives you enough information to get started on
your own and to better understand how the Symfony2 framework works internally.&lt;/p&gt;

&lt;p&gt;If you want to learn more, I highly recommend you to read the source code of
the Silex micro-framework, and especially its &lt;a href="https://github.com/fabpot/Silex/blob/master/src/Silex/Application.php"&gt;Application&lt;/a&gt; class.&lt;/p&gt;

&lt;p&gt;Have fun!&lt;/p&gt;

&lt;p&gt;~~ FIN ~~&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P.S.:&lt;/em&gt; If there is enough interest (leave a comment on this post), I might
write some more articles on specific topics (using a configuration file for
routing, using HttpKernel debugging tools, using the build-in client to
simulate a browser are some of the topics that come to my mind for instance).&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/2xeMj6zRXu8" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/62/create-your-own-framework-on-top-of-the-symfony2-components-part-12</feedburner:origLink></entry>
      <entry>
  <title>Create your own framework... on top of the Symfony2 Components (part 11)</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/vDmSnN58Svs/create-your-own-framework-on-top-of-the-symfony2-components-part-11" />
  <id>article-60</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2012-01-23T13:51:00+01:00</updated>
  <content type="html">
    &lt;blockquote class="note"&gt;&lt;p&gt;
  This article is part of a series of articles that explains how to create a framework with
  the Symfony2 Components:
  &lt;a href="http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1"&gt;1&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/51/create-your-own-framework-on-top-of-the-symfony2-components-part-2"&gt;2&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/52/create-your-own-framework-on-top-of-the-symfony2-components-part-3"&gt;3&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/53/create-your-own-framework-on-top-of-the-symfony2-components-part-4"&gt;4&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/54/create-your-own-framework-on-top-of-the-symfony2-components-part-5"&gt;5&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/55/create-your-own-framework-on-top-of-the-symfony2-components-part-6"&gt;6&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/56/create-your-own-framework-on-top-of-the-symfony2-components-part-7"&gt;7&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/57/create-your-own-framework-on-top-of-the-symfony2-components-part-8"&gt;8&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/58/create-your-own-framework-on-top-of-the-symfony2-components-part-9"&gt;9&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/59/create-your-own-framework-on-top-of-the-symfony2-components-part-10"&gt;10&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/60/create-your-own-framework-on-top-of-the-symfony2-components-part-11"&gt;11&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/62/create-your-own-framework-on-top-of-the-symfony2-components-part-12"&gt;12&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you were to use our framework right now, you would probably have to add
support for custom error messages. Right now, we have 404 and 500 error
support but the responses are hardcoded in the framework itself. Making them
customizable is easy enough though: dispatch a new event and listen to it.
Doing it right means that the listener has to call a regular controller. But
what if the error controller throws an exception? You will end up in an
infinite loop. There should be an easier way, right?&lt;/p&gt;

&lt;p&gt;Enter the &lt;code&gt;HttpKernel&lt;/code&gt; class. Instead of solving the same problem over and
over again and instead of reinventing the wheel each time, the &lt;code&gt;HttpKernel&lt;/code&gt;
class is a generic, extensible, and flexible implementation of
&lt;code&gt;HttpKernelInterface&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This class is very similar to the framework class we have written so far: it
dispatches events at some strategic points during the handling of the request,
it uses a controller resolver to choose the controller to dispatch the request
to, and as an added bonus, it takes care of edge cases and provides great
feedback when a problem arises.&lt;/p&gt;

&lt;p&gt;Here is the new framework code:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/Framework.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
use Symfony\Component\HttpKernel\HttpKernel;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; Framework &lt;span class="kw2"&gt;extends&lt;/span&gt; HttpKernel
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;And the new front controller:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/web/front.php&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw1"&gt;require_once&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../vendor/.composer/autoload.php'&lt;/span&gt;;
&amp;nbsp;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing;
use Symfony\Component\HttpKernel;
use Symfony\Component\EventDispatcher\EventDispatcher;
&amp;nbsp;
&lt;span class="re0"&gt;$request&lt;/span&gt; = Request::&lt;span class="me2"&gt;createFromGlobals&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$routes&lt;/span&gt; = &lt;span class="kw1"&gt;include&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../src/app.php'&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$context&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Routing\RequestContext&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$matcher&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Routing\Matcher\UrlMatcher&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$routes&lt;/span&gt;, &lt;span class="re0"&gt;$context&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$resolver&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\Controller\ControllerResolver&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; EventDispatcher&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\EventListener\RouterListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$matcher&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$framework&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\Framework&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;, &lt;span class="re0"&gt;$resolver&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$framework&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;handle&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;send&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;RouterListener&lt;/code&gt; is an implementation of the same logic we had in our
framework: it matches the incoming request and populates the request
attributes with route parameters.&lt;/p&gt;

&lt;p&gt;Our code is now much more concise and surprisingly more robust and more
powerful than ever. For instance, use the built-in &lt;code&gt;ExceptionListener&lt;/code&gt; to
make your error management configurable:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$errorHandler&lt;/span&gt; = &lt;span class="kw2"&gt;function&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;HttpKernel\Exception\FlattenException &lt;span class="re0"&gt;$exception&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="re0"&gt;$msg&lt;/span&gt; = &lt;span class="st0"&gt;'Something went wrong! ('&lt;/span&gt;.&lt;span class="re0"&gt;$exception&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getMessage&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;.&lt;span class="st0"&gt;')'&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="kw2"&gt;new&lt;/span&gt; Response&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$msg&lt;/span&gt;, &lt;span class="re0"&gt;$exception&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getStatusCode&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\EventListener\ExceptionListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$errorHandler&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;ExceptionListener&lt;/code&gt; gives you a &lt;code&gt;FlattenException&lt;/code&gt; instance instead of the
thrown &lt;code&gt;Exception&lt;/code&gt; instance to ease exception manipulation and display. It
can take any valid controller as an exception handler, so you can create an
ErrorController class instead of using a Closure:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$listener&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\EventListener\ExceptionListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Calendar&lt;span class="es0"&gt;\\&lt;/span&gt;Controller&lt;span class="es0"&gt;\\&lt;/span&gt;ErrorController::exceptionAction'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$listener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The error controller reads as follows:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Calendar/Controller/ErrorController.php&lt;/span&gt;
&amp;nbsp;
namespace Calendar\Controller;
&amp;nbsp;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\FlattenException;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; ErrorController
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; exceptionAction&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;FlattenException &lt;span class="re0"&gt;$exception&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$msg&lt;/span&gt; = &lt;span class="st0"&gt;'Something went wrong! ('&lt;/span&gt;.&lt;span class="re0"&gt;$exception&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getMessage&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;.&lt;span class="st0"&gt;')'&lt;/span&gt;;
&amp;nbsp;
        &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="kw2"&gt;new&lt;/span&gt; Response&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$msg&lt;/span&gt;, &lt;span class="re0"&gt;$exception&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getStatusCode&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Voilà! Clean and customizable error management without efforts. And of course,
if your controller throws an exception, HttpKernel will handle it nicely.&lt;/p&gt;

&lt;p&gt;In part 2, we have talked about the &lt;code&gt;Response::prepare()&lt;/code&gt; method, which
ensures that a Response is compliant with the HTTP specification. It is
probably a good idea to always call it just before sending the Response to the
client; that's what the &lt;code&gt;ResponseListener&lt;/code&gt; does:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\EventListener\ResponseListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'UTF-8'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;This one was easy too! Let's take another one: do you want out of the box
support for streamed responses? Just subscribe to
&lt;code&gt;StreamedResponseListener&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; HttpKernel\EventListener\StreamedResponseListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;And in your controller, return a &lt;code&gt;StreamedResponse&lt;/code&gt; instance instead of a
&lt;code&gt;Response&lt;/code&gt; instance.&lt;/p&gt;

&lt;blockquote class="tip"&gt;&lt;p&gt;
  Read the &lt;a href="http://symfony.com/doc/current/book/internals.html#events"&gt;Internals&lt;/a&gt; chapter of the Symfony2 documentation to learn more
  about the events dispatched by HttpKernel and how they allow you to change the
  flow of a request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, let's create a listener, one that allows a controller to return a string
instead of a full Response object:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;class&lt;/span&gt; LeapYearController
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; indexAction&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Request &lt;span class="re0"&gt;$request&lt;/span&gt;, &lt;span class="re0"&gt;$year&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$leapyear&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; LeapYear&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$leapyear&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;isLeapYear&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$year&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
            &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="st0"&gt;'Yep, this is a leap year! '&lt;/span&gt;;
        &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="st0"&gt;'Nope, this is not a leap year.'&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;To implement this feature, we are going to listen to the &lt;code&gt;kernel.view&lt;/code&gt;
event, which is triggered just after the controller has been called. Its goal
is to convert the controller return value to a proper Response instance, but
only if needed:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/StringResponseListener.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\Response;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; StringResponseListener implements EventSubscriberInterface
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; onView&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;GetResponseForControllerResultEvent &lt;span class="re0"&gt;$event&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getControllerResult&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/is_string"&gt;&lt;span class="kw3"&gt;is_string&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
            &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setResponse&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Response&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;a href="http://www.php.net/static"&gt;&lt;span class="kw3"&gt;static&lt;/span&gt;&lt;/a&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; getSubscribedEvents&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'kernel.view'&lt;/span&gt; =&amp;gt; &lt;span class="st0"&gt;'onView'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The code is simple because the &lt;code&gt;kernel.view&lt;/code&gt; event is only triggered when
the controller return value is not a Response and because setting the response
on the event stops the event propagation (our listener cannot interfere with
other view listeners).&lt;/p&gt;

&lt;p&gt;Don't forget to register it in the front controller:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\StringResponseListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;blockquote class="note"&gt;&lt;p&gt;
  If you forget to register the subscriber, HttpKernel will throw an exception
  with a nice message: &lt;code&gt;The controller must return a response (Nope, this is not
  a leap year. given).&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point, our whole framework code is as compact as possible and it is
mainly composed of an assembly of existing libraries. Extending is a matter
of registering event listeners/subscribers.&lt;/p&gt;

&lt;p&gt;Hopefully, you now have a better understanding of why the simple looking
&lt;code&gt;HttpKernelInterface&lt;/code&gt; is so powerful. Its default implementation,
&lt;code&gt;HttpKernel&lt;/code&gt;, gives you access to a lot of cool features, ready to be used
out of the box, with no efforts. And because HttpKernel is actually the code
that powers the Symfony2 and Silex frameworks, you have the best of both
worlds: a custom framework, tailored to your needs, but based on a rock-solid
and well maintained low-level architecture that has been proven to work for
many websites; a code that has been audited for security issues and that has
proven to scale well.&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/vDmSnN58Svs" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/60/create-your-own-framework-on-top-of-the-symfony2-components-part-11</feedburner:origLink></entry>
      <entry>
  <title>Create your own framework... on top of the Symfony2 Components (part 10)</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/KEEHrRWsrZo/create-your-own-framework-on-top-of-the-symfony2-components-part-10" />
  <id>article-59</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2012-01-21T08:43:00+01:00</updated>
  <content type="html">
    &lt;blockquote class="note"&gt;&lt;p&gt;
  This article is part of a series of articles that explains how to create a framework with
  the Symfony2 Components:
  &lt;a href="http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1"&gt;1&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/51/create-your-own-framework-on-top-of-the-symfony2-components-part-2"&gt;2&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/52/create-your-own-framework-on-top-of-the-symfony2-components-part-3"&gt;3&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/53/create-your-own-framework-on-top-of-the-symfony2-components-part-4"&gt;4&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/54/create-your-own-framework-on-top-of-the-symfony2-components-part-5"&gt;5&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/55/create-your-own-framework-on-top-of-the-symfony2-components-part-6"&gt;6&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/56/create-your-own-framework-on-top-of-the-symfony2-components-part-7"&gt;7&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/57/create-your-own-framework-on-top-of-the-symfony2-components-part-8"&gt;8&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/58/create-your-own-framework-on-top-of-the-symfony2-components-part-9"&gt;9&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/59/create-your-own-framework-on-top-of-the-symfony2-components-part-10"&gt;10&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/60/create-your-own-framework-on-top-of-the-symfony2-components-part-11"&gt;11&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/62/create-your-own-framework-on-top-of-the-symfony2-components-part-12"&gt;12&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the conclusion of the second part of this series, I've talked about one
great benefit of using the Symfony2 components: the &lt;em&gt;interoperability&lt;/em&gt; between
all frameworks and applications using them. Let's do a big step towards this
goal by making our framework implement &lt;code&gt;HttpKernelInterface&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="php"&gt;namespace Symfony\Component\HttpKernel;
&amp;nbsp;
&lt;span class="kw2"&gt;interface&lt;/span&gt; HttpKernelInterface
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="coMULTI"&gt;/**
     * @return Response A Response instance
     */&lt;/span&gt;
    &lt;span class="kw2"&gt;function&lt;/span&gt; handle&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Request &lt;span class="re0"&gt;$request&lt;/span&gt;, &lt;span class="re0"&gt;$type&lt;/span&gt; = self::&lt;span class="me2"&gt;MASTER_REQUEST&lt;/span&gt;, &lt;span class="re0"&gt;$catch&lt;/span&gt; = &lt;span class="kw2"&gt;true&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;HttpKernelInterface&lt;/code&gt; is probably the most important piece of code in the
HttpKernel component, no kidding. Frameworks and applications that implement
this interface are fully interoperable. Moreover, a lot of great features will
come with it for free.&lt;/p&gt;

&lt;p&gt;Update your framework so that it implements this interface:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Framework.php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// ...&lt;/span&gt;
&amp;nbsp;
use Symfony\Component\HttpKernel\HttpKernelInterface;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; Framework implements HttpKernelInterface
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="co1"&gt;// ...&lt;/span&gt;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; handle&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Request &lt;span class="re0"&gt;$request&lt;/span&gt;, &lt;span class="re0"&gt;$type&lt;/span&gt; = HttpKernelInterface::&lt;span class="me2"&gt;MASTER_REQUEST&lt;/span&gt;, &lt;span class="re0"&gt;$catch&lt;/span&gt; = &lt;span class="kw2"&gt;true&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="co1"&gt;// ...&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Even if this change looks trivial, it brings us a lot! Let's talk about one of
the most impressive one: transparent &lt;a href="http://symfony.com/doc/current/book/http_cache.html"&gt;HTTP caching&lt;/a&gt; support.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;HttpCache&lt;/code&gt; class implements a fully-featured reverse proxy, written in
PHP; it implements &lt;code&gt;HttpKernelInterface&lt;/code&gt; and wraps another
&lt;code&gt;HttpKernelInterface&lt;/code&gt; instance:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="co1"&gt;// example.com/web/front.php&lt;/span&gt;
&amp;nbsp;
use Symfony\Component\HttpKernel\HttpCache\HttpCache;
use Symfony\Component\HttpKernel\HttpCache\Store;
&amp;nbsp;
&lt;span class="re0"&gt;$framework&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\Framework&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;, &lt;span class="re0"&gt;$matcher&lt;/span&gt;, &lt;span class="re0"&gt;$resolver&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$framework&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; HttpCache&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$framework&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; Store&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;__DIR__.&lt;span class="st0"&gt;'/../cache'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$framework&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;handle&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;send&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;That's all it takes to add HTTP caching support to our framework. Isn't it
amazing?&lt;/p&gt;

&lt;p&gt;Configuring the cache needs to be done via HTTP cache headers. For instance,
to cache a response for 10 seconds, use the &lt;code&gt;Response::setTtl()&lt;/code&gt; method::&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="co1"&gt;// example.com/src/Calendar/Controller/LeapYearController.php&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; indexAction&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Request &lt;span class="re0"&gt;$request&lt;/span&gt;, &lt;span class="re0"&gt;$year&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="re0"&gt;$leapyear&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; LeapYear&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$leapyear&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;isLeapYear&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$year&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Response&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Yep, this is a leap year!'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt; &lt;span class="kw1"&gt;else&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Response&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Nope, this is not a leap year.'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setTtl&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;10&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="re0"&gt;$response&lt;/span&gt;;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;blockquote class="tip"&gt;&lt;p&gt;
  If, like me, you are running your framework from the command line by
  simulating requests (&lt;code&gt;Request::create('/is_leap_year/2012')&lt;/code&gt;), you can easily
  debug Response instances by dumping their string representation (&lt;code&gt;echo
  $response;&lt;/code&gt;) as it displays all headers as well as the response content.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To validate that it works correctly, add a random number to the response
content and check that the number only changes every 10 seconds:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Response&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Yep, this is a leap year! '&lt;/span&gt;.&lt;a href="http://www.php.net/rand"&gt;&lt;span class="kw3"&gt;rand&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;blockquote class="note"&gt;&lt;p&gt;
  When deploying to your production environment, keep using the Symfony2 reverse
  proxy (great for shared hosting) or even better, switch to a more efficient
  reverse proxy like &lt;a href="https://www.varnish-cache.org/"&gt;Varnish&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using HTTP cache headers to manage your application cache is very powerful and
allows you to tune finely your caching strategy as you can use both the
expiration and the validation models of the HTTP specification. If you are not
comfortable with these concepts, I highly recommend you to read the &lt;a href="http://symfony.com/doc/current/book/http_cache.html"&gt;HTTP
caching&lt;/a&gt; chapter of the Symfony2 documentation.&lt;/p&gt;

&lt;p&gt;The Response class contains many other methods that let you configure the
HTTP cache very easily. One of the most powerful is &lt;code&gt;setCache()&lt;/code&gt; as it
abstracts the most frequently used caching strategies into one simple array:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$date&lt;/span&gt; = date_create_from_format&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Y-m-d H:i:s'&lt;/span&gt;, &lt;span class="st0"&gt;'2005-10-15 10:00:00'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setCache&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;
    &lt;span class="st0"&gt;'public'&lt;/span&gt;        =&amp;gt; &lt;span class="kw2"&gt;true&lt;/span&gt;,
    &lt;span class="st0"&gt;'etag'&lt;/span&gt;          =&amp;gt; &lt;span class="st0"&gt;'abcde'&lt;/span&gt;,
    &lt;span class="st0"&gt;'last_modified'&lt;/span&gt; =&amp;gt; &lt;span class="re0"&gt;$date&lt;/span&gt;,
    &lt;span class="st0"&gt;'max_age'&lt;/span&gt;       =&amp;gt; &lt;span class="nu0"&gt;10&lt;/span&gt;,
    &lt;span class="st0"&gt;'s_maxage'&lt;/span&gt;      =&amp;gt; &lt;span class="nu0"&gt;10&lt;/span&gt;,
&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="co1"&gt;// it is equivalent to the following code&lt;/span&gt;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setPublic&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setEtag&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'abcde'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setLastModified&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$date&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setMaxAge&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;10&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setSharedMaxAge&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="nu0"&gt;10&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;When using the validation model, the &lt;code&gt;isNotModified()&lt;/code&gt; method allows you to
easily cut on the response time by short-circuiting the response generation as
early as possible:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setETag&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'whatever_you_compute_as_an_etag'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;isNotModified&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="re0"&gt;$response&lt;/span&gt;;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setContent&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'The computed content of the response'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="re0"&gt;$response&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Using HTTP caching is great, but what if you cannot cache the whole page? What
if you can cache everything but some sidebar that is more dynamic that the
rest of the content? Edge Side Includes (&lt;a href="http://en.wikipedia.org/wiki/Edge_Side_Includes"&gt;ESI&lt;/a&gt;) to the rescue! Instead of
generating the whole content in one go, ESI allows you to mark a region of a
page as being the content of a sub-request call:&lt;/p&gt;

&lt;pre class="php"&gt;This is the content of your page
&amp;nbsp;
Is &lt;span class="nu0"&gt;2012&lt;/span&gt; a leap year? &amp;lt;esi:&lt;span class="kw1"&gt;include&lt;/span&gt; src=&lt;span class="st0"&gt;&amp;quot;/leapyear/2012&amp;quot;&lt;/span&gt; /&amp;gt;
&amp;nbsp;
Some other content
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;For ESI tags to be supported by HttpCache, you need to pass it an instance of
the &lt;code&gt;ESI&lt;/code&gt; class. The &lt;code&gt;ESI&lt;/code&gt; class automatically parses ESI tags and makes
sub-requests to convert them to their proper content:&lt;/p&gt;

&lt;pre class="php"&gt;use Symfony\Component\HttpKernel\HttpCache\ESI;
&amp;nbsp;
&lt;span class="re0"&gt;$framework&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; HttpCache&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$framework&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; Store&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;__DIR__.&lt;span class="st0"&gt;'/../cache'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; ESI&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;blockquote class="note"&gt;&lt;p&gt;
  For ESI to work, you need to use a reverse proxy that supports it like the
  Symfony2 implementation. &lt;a href="https://www.varnish-cache.org/"&gt;Varnish&lt;/a&gt; is the best alternative and it is
  Open-Source.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When using complex HTTP caching strategies and/or many ESI include tags, it
can be hard to understand why and when a resource should be cached or not. To
ease debugging, you can enable the debug mode:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$framework&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; HttpCache&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$framework&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; Store&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;__DIR__.&lt;span class="st0"&gt;'/../cache'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; ESI&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'debug'&lt;/span&gt; =&amp;gt; &lt;span class="kw2"&gt;true&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The debug mode adds a &lt;code&gt;X-Symfony-Cache&lt;/code&gt; header to each response that
describes what the cache layer did:&lt;/p&gt;

&lt;pre class="php"&gt;X-Symfony-Cache:  GET /is_leap_year/&lt;span class="nu0"&gt;2012&lt;/span&gt;: stale, invalid, store
&amp;nbsp;
X-Symfony-Cache:  GET /is_leap_year/&lt;span class="nu0"&gt;2012&lt;/span&gt;: fresh
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;HttpCache has many features like support for the
&lt;code&gt;stale-while-revalidate&lt;/code&gt; and &lt;code&gt;stale-if-error&lt;/code&gt; HTTP Cache-Control
extensions as defined in RFC 5861.&lt;/p&gt;

&lt;p&gt;With the addition of a single interface, our framework can now benefit from
the many features built into the HttpKernel component; HTTP caching being just
one of them but an important one as it can make your applications fly!&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/KEEHrRWsrZo" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/59/create-your-own-framework-on-top-of-the-symfony2-components-part-10</feedburner:origLink></entry>
      <entry>
  <title>Create your own framework... on top of the Symfony2 Components (part 9)</title>
  <link href="http://feeds.fabien.potencier.org/~r/aidedecamp/~3/tWQZJiws95w/create-your-own-framework-on-top-of-the-symfony2-components-part-9" />
  <id>article-58</id>
  <author>
    <name>Fabien Potencier</name>
    <author_email>fabien.potencier@sensio.com</author_email>
  </author>
  <updated>2012-01-19T13:09:00+01:00</updated>
  <content type="html">
    &lt;blockquote class="note"&gt;&lt;p&gt;
  This article is part of a series of articles that explains how to create a framework with
  the Symfony2 Components:
  &lt;a href="http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1"&gt;1&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/51/create-your-own-framework-on-top-of-the-symfony2-components-part-2"&gt;2&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/52/create-your-own-framework-on-top-of-the-symfony2-components-part-3"&gt;3&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/53/create-your-own-framework-on-top-of-the-symfony2-components-part-4"&gt;4&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/54/create-your-own-framework-on-top-of-the-symfony2-components-part-5"&gt;5&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/55/create-your-own-framework-on-top-of-the-symfony2-components-part-6"&gt;6&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/56/create-your-own-framework-on-top-of-the-symfony2-components-part-7"&gt;7&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/57/create-your-own-framework-on-top-of-the-symfony2-components-part-8"&gt;8&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/58/create-your-own-framework-on-top-of-the-symfony2-components-part-9"&gt;9&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/59/create-your-own-framework-on-top-of-the-symfony2-components-part-10"&gt;10&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/60/create-your-own-framework-on-top-of-the-symfony2-components-part-11"&gt;11&lt;/a&gt;,
  &lt;a href="http://fabien.potencier.org/article/62/create-your-own-framework-on-top-of-the-symfony2-components-part-12"&gt;12&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our framework is still missing a major characteristic of any good framework:
&lt;em&gt;extensibility&lt;/em&gt;. Being extensible means that the developer should be able to
easily hook into the framework life cycle to modify the way the request is
handled.&lt;/p&gt;

&lt;p&gt;What kind of hooks are we talking about? Authentication or caching for
instance. To be flexible, hooks must be plug-and-play; the ones you "register"
for an application are different from the next one depending on your specific
needs. Many software have a similar concept like Drupal or Wordpress. In some
languages, there is even a standard like &lt;a href="http://www.python.org/dev/peps/pep-0333/#middleware-components-that-play-both-sides"&gt;WSGI&lt;/a&gt; in Python or &lt;a href="http://rack.rubyforge.org/"&gt;Rack&lt;/a&gt; in Ruby.&lt;/p&gt;

&lt;p&gt;As there is no standard for PHP, we are going to use a well-known design
pattern, the &lt;em&gt;Observer&lt;/em&gt;, to allow any kind of behaviors to be attached to our
framework; the Symfony2 EventDispatcher Component implements a lightweight
version of this pattern:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="st0"&gt;&amp;quot;require&amp;quot;&lt;/span&gt;: &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="st0"&gt;&amp;quot;symfony/class-loader&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/http-foundation&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/routing&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/http-kernel&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;,
        &lt;span class="st0"&gt;&amp;quot;symfony/event-dispatcher&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;2.1.*&amp;quot;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;,
    &lt;span class="st0"&gt;&amp;quot;autoload&amp;quot;&lt;/span&gt;: &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="st0"&gt;&amp;quot;psr-0&amp;quot;&lt;/span&gt;: &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt; &lt;span class="st0"&gt;&amp;quot;Simplex&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;src/&amp;quot;&lt;/span&gt;, &lt;span class="st0"&gt;&amp;quot;Calendar&amp;quot;&lt;/span&gt;: &lt;span class="st0"&gt;&amp;quot;src/&amp;quot;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;How does it work? The &lt;em&gt;dispatcher&lt;/em&gt;, the central object of the event dispatcher
system, notifies &lt;em&gt;listeners&lt;/em&gt; of an &lt;em&gt;event&lt;/em&gt; dispatched to it. Put another way:
your code dispatches an event to the dispatcher, the dispatcher notifies all
registered listeners for the event, and each listener do whatever it wants
with the event.&lt;/p&gt;

&lt;p&gt;As an example, let's create a listener that transparently adds the Google
Analytics code to all responses.&lt;/p&gt;

&lt;p&gt;To make it work, the framework must dispatch an event just before returning
the Response instance:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/Framework.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; Framework
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    protected &lt;span class="re0"&gt;$matcher&lt;/span&gt;;
    protected &lt;span class="re0"&gt;$resolver&lt;/span&gt;;
    protected &lt;span class="re0"&gt;$dispatcher&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; __construct&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;EventDispatcher &lt;span class="re0"&gt;$dispatcher&lt;/span&gt;, UrlMatcherInterface &lt;span class="re0"&gt;$matcher&lt;/span&gt;, ControllerResolverInterface &lt;span class="re0"&gt;$resolver&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;matcher&lt;/span&gt; = &lt;span class="re0"&gt;$matcher&lt;/span&gt;;
        &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;resolver&lt;/span&gt; = &lt;span class="re0"&gt;$resolver&lt;/span&gt;;
        &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;dispatcher&lt;/span&gt; = &lt;span class="re0"&gt;$dispatcher&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; handle&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Request &lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        try &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
            &lt;span class="re0"&gt;$request&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;attributes&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;add&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;matcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;match&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getPathInfo&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
            &lt;span class="re0"&gt;$controller&lt;/span&gt; = &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;resolver&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getController&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
            &lt;span class="re0"&gt;$arguments&lt;/span&gt; = &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;resolver&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getArguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;, &lt;span class="re0"&gt;$controller&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
            &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;a href="http://www.php.net/call_user_func_array"&gt;&lt;span class="kw3"&gt;call_user_func_array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$controller&lt;/span&gt;, &lt;span class="re0"&gt;$arguments&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt; catch &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;ResourceNotFoundException &lt;span class="re0"&gt;$e&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
            &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Response&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Not Found'&lt;/span&gt;, &lt;span class="nu0"&gt;404&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt; catch &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;\Exception &lt;span class="re0"&gt;$e&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
            &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Response&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'An error occurred'&lt;/span&gt;, &lt;span class="nu0"&gt;500&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        &lt;span class="co1"&gt;// dispatch a response event&lt;/span&gt;
        &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;dispatch&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'response'&lt;/span&gt;, &lt;span class="kw2"&gt;new&lt;/span&gt; ResponseEvent&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;, &lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="re0"&gt;$response&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Each time the framework handles a Request, a &lt;code&gt;ResponseEvent&lt;/code&gt; event is
now dispatched:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/ResponseEvent.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\EventDispatcher\Event;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; ResponseEvent &lt;span class="kw2"&gt;extends&lt;/span&gt; Event
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="kw2"&gt;private&lt;/span&gt; &lt;span class="re0"&gt;$request&lt;/span&gt;;
    &lt;span class="kw2"&gt;private&lt;/span&gt; &lt;span class="re0"&gt;$response&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; __construct&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Response &lt;span class="re0"&gt;$response&lt;/span&gt;, Request &lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;response&lt;/span&gt; = &lt;span class="re0"&gt;$response&lt;/span&gt;;
        &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;request&lt;/span&gt; = &lt;span class="re0"&gt;$request&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; getResponse&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;response&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; getRequest&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;span class="re0"&gt;$this&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;request&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;The last step is the creation of the dispatcher in the front controller and
the registration of a listener for the &lt;code&gt;response&lt;/code&gt; event:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/web/front.php&lt;/span&gt;
&amp;nbsp;
&lt;span class="kw1"&gt;require_once&lt;/span&gt; __DIR__.&lt;span class="st0"&gt;'/../vendor/.composer/autoload.php'&lt;/span&gt;;
&amp;nbsp;
&lt;span class="co1"&gt;// ...&lt;/span&gt;
&amp;nbsp;
use Symfony\Component\EventDispatcher\EventDispatcher;
&amp;nbsp;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; EventDispatcher&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addListener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'response'&lt;/span&gt;, &lt;span class="kw2"&gt;function&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Simplex\ResponseEvent &lt;span class="re0"&gt;$event&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getResponse&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;isRedirection&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
        || &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;has&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Type'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &amp;amp;&amp;amp; &lt;span class="kw2"&gt;false&lt;/span&gt; === &lt;a href="http://www.php.net/strpos"&gt;&lt;span class="kw3"&gt;strpos&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;get&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Type'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;span class="st0"&gt;'html'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
        || &lt;span class="st0"&gt;'html'&lt;/span&gt; !== &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getRequest&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getRequestFormat&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="kw1"&gt;return&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
    &lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setContent&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getContent&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;.&lt;span class="st0"&gt;'GA CODE'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$framework&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\Framework&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;, &lt;span class="re0"&gt;$matcher&lt;/span&gt;, &lt;span class="re0"&gt;$resolver&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$framework&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;handle&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$request&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;send&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;blockquote class="note"&gt;&lt;p&gt;
  The listener is just a proof of concept and you should add the Google
  Analytics code just before the body tag.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you can see, &lt;code&gt;addListener()&lt;/code&gt; associates a valid PHP callback to a named
event (&lt;code&gt;response&lt;/code&gt;); the event name must be the same as the one used in the
&lt;code&gt;dispatch()&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;In the listener, we add the Google Analytics code only if the response is not
a redirection, if the requested format is HTML, and if the response content
type is HTML (these conditions demonstrate the ease of manipulating the
Request and Response data from your code).&lt;/p&gt;

&lt;p&gt;So far so good, but let's add another listener on the same event. Let's say
that I want to set the &lt;code&gt;Content-Length&lt;/code&gt; of the Response if it is not already
set:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addListener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'response'&lt;/span&gt;, &lt;span class="kw2"&gt;function&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Simplex\ResponseEvent &lt;span class="re0"&gt;$event&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getResponse&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="re0"&gt;$headers&lt;/span&gt; = &lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;headers&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;!&lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;has&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Length'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &amp;amp;&amp;amp; !&lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;has&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Transfer-Encoding'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;set&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Length'&lt;/span&gt;, &lt;a href="http://www.php.net/strlen"&gt;&lt;span class="kw3"&gt;strlen&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getContent&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Depending on whether you have added this piece of code before the previous
listener registration or after it, you will have the wrong or the right value
for the &lt;code&gt;Content-Length&lt;/code&gt; header. Sometimes, the order of the listeners
matter but by default, all listeners are registered with the same priority,
&lt;code&gt;0&lt;/code&gt;. To tell the dispatcher to run a listener early, change the priority to
a positive number; negative numbers can be used for low priority listeners.
Here, we want the &lt;code&gt;Content-Length&lt;/code&gt; listener to be executed last, so change
the priority to &lt;code&gt;-255&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addListener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'response'&lt;/span&gt;, &lt;span class="kw2"&gt;function&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;Simplex\ResponseEvent &lt;span class="re0"&gt;$event&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getResponse&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="re0"&gt;$headers&lt;/span&gt; = &lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;headers&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;!&lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;has&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Length'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &amp;amp;&amp;amp; !&lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;has&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Transfer-Encoding'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;set&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Length'&lt;/span&gt;, &lt;a href="http://www.php.net/strlen"&gt;&lt;span class="kw3"&gt;strlen&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getContent&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;, &lt;span class="nu0"&gt;-255&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;blockquote class="tip"&gt;&lt;p&gt;
  When creating your framework, think about priorities (reserve some numbers for
  internal listeners for instance) and document them thoroughly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's refactor the code a bit by moving the Google listener to its own class:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/GoogleListener.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; GoogleListener
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; onResponse&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;ResponseEvent &lt;span class="re0"&gt;$event&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getResponse&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;
        &lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;isRedirection&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
            || &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;has&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Type'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &amp;amp;&amp;amp; &lt;span class="kw2"&gt;false&lt;/span&gt; === &lt;a href="http://www.php.net/strpos"&gt;&lt;span class="kw3"&gt;strpos&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;get&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Type'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;span class="st0"&gt;'html'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
            || &lt;span class="st0"&gt;'html'&lt;/span&gt; !== &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getRequest&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getRequestFormat&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
        &lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
            &lt;span class="kw1"&gt;return&lt;/span&gt;;
        &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;
        &lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;setContent&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getContent&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;.&lt;span class="st0"&gt;'GA CODE'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;And do the same with the other listener:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/ContentLengthListener.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; ContentLengthListener
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; onResponse&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;ResponseEvent &lt;span class="re0"&gt;$event&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="re0"&gt;$response&lt;/span&gt; = &lt;span class="re0"&gt;$event&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getResponse&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="re0"&gt;$headers&lt;/span&gt; = &lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;headers&lt;/span&gt;;
&amp;nbsp;
        &lt;span class="kw1"&gt;if&lt;/span&gt; &lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;!&lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;has&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Length'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &amp;amp;&amp;amp; !&lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;has&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Transfer-Encoding'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
            &lt;span class="re0"&gt;$headers&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;set&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'Content-Length'&lt;/span&gt;, &lt;a href="http://www.php.net/strlen"&gt;&lt;span class="kw3"&gt;strlen&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re0"&gt;$response&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;getContent&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
        &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Our front controller should now look like the following:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; EventDispatcher&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addListener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'response'&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\ContentLengthListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;span class="st0"&gt;'onResponse'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;span class="nu0"&gt;-255&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addListener&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'response'&lt;/span&gt;, &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\GoogleListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, &lt;span class="st0"&gt;'onResponse'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;Even if the code is now nicely wrapped in classes, there is still a slight
issue: the knowledge of the priorities is "hardcoded" in the front controller,
instead of being in the listeners themselves. For each application, you have
to remember to set the appropriate priorities. Moreover, the listener method
names are also exposed here, which means that refactoring our listeners would
mean changing all the applications that rely on those listeners. Of course,
there is a solution: use subscribers instead of listeners:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="re0"&gt;$dispatcher&lt;/span&gt; = &lt;span class="kw2"&gt;new&lt;/span&gt; EventDispatcher&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\ContentLengthListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&lt;span class="re0"&gt;$dispatcher&lt;/span&gt;-&amp;gt;&lt;span class="me1"&gt;addSubscriber&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;new&lt;/span&gt; Simplex\GoogleListener&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;A subscriber knowns about all the events it is interested in and pass this
information to the dispatcher via the &lt;code&gt;getSubscribedEvents()&lt;/code&gt; method. Have a
look at the new version of the &lt;code&gt;GoogleListener&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/GoogleListener.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; GoogleListener implements EventSubscriberInterface
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="co1"&gt;// ...&lt;/span&gt;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;a href="http://www.php.net/static"&gt;&lt;span class="kw3"&gt;static&lt;/span&gt;&lt;/a&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; getSubscribedEvents&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'response'&lt;/span&gt; =&amp;gt; &lt;span class="st0"&gt;'onResponse'&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;p&gt;And here is the new version of &lt;code&gt;ContentLengthListener&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="php"&gt;&lt;span class="kw2"&gt;&amp;lt;?php&lt;/span&gt;
&amp;nbsp;
&lt;span class="co1"&gt;// example.com/src/Simplex/ContentLengthListener.php&lt;/span&gt;
&amp;nbsp;
namespace Simplex;
&amp;nbsp;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
&amp;nbsp;
&lt;span class="kw2"&gt;class&lt;/span&gt; ContentLengthListener implements EventSubscriberInterface
&lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
    &lt;span class="co1"&gt;// ...&lt;/span&gt;
&amp;nbsp;
    &lt;span class="kw2"&gt;public&lt;/span&gt; &lt;a href="http://www.php.net/static"&gt;&lt;span class="kw3"&gt;static&lt;/span&gt;&lt;/a&gt; &lt;span class="kw2"&gt;function&lt;/span&gt; getSubscribedEvents&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;
    &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;
        &lt;span class="kw1"&gt;return&lt;/span&gt; &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'response'&lt;/span&gt; =&amp;gt; &lt;a href="http://www.php.net/array"&gt;&lt;span class="kw3"&gt;array&lt;/span&gt;&lt;/a&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="st0"&gt;'onResponse'&lt;/span&gt;, &lt;span class="nu0"&gt;-255&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;;
    &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;
&amp;nbsp;&lt;/pre&gt;

&lt;blockquote class="tip"&gt;&lt;p&gt;
  A single subscriber can host as many listeners as you want on as many events
  as needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To make your framework truly flexible, don't hesitate to add more events; and
to make it more awesome out of the box, add more listeners. Again, this series
is not about creating a generic framework, but one that is tailored to your
needs. Stop whenever you see fit, and further evolve the code from there.&lt;/p&gt;
  &lt;img src="http://feeds.feedburner.com/~r/aidedecamp/~4/tWQZJiws95w" height="1" width="1"/&gt;</content>
<feedburner:origLink>http://fabien.potencier.org/article/58/create-your-own-framework-on-top-of-the-symfony2-components-part-9</feedburner:origLink></entry>
  </feed>
