Own PHP Framework based on Simfony components
-------------------------------

1.  [Introduction](https://symfony.com/doc/current/create_framework/introduction.html)
2.  [**HttpFoundation Component**](#HttpFoundation)
    1.  Going OOP with the HttpFoundation Component
3.  [Front Controller](#FrontCtr)
4.  [Routing Component](#Routing)
5.  [Templating](#Templating)
6.  [HttpKernel Component: Controller Resolver](#ControllerResolver)
7.  [Separation of Concerns](#Separation)
8.  [Unit Testing](#Unit)
9.  [EventDispatcher Component](#EventDispatcher)
10.  [HttpKernel Component: HttpKernelInterface](#HttpKernelInterface)
11.  [HttpKernel Component: The HttpKernel Class](#HttpKernelClass)
12.  [DependencyInjection Component](#DependencyInjection)

1\. Introduction
----------------

  
See [https://symfony.com/doc/current/create\_framework/http\_foundation.html](https://symfony.com/doc/current/create_framework/http_foundation.html)  
This poage URL is eg. : [http://dev1:8083/fwphp/z\_test/simf\_ownfw/index.php?name=aaa](http://dev1:8083/fwphp/z_test/simf_ownfw/index.php?name=aaa)  
  
When creating a framework, following the **MVC pattern is not the right goal**.  
The main goal should be the **Separation of Concerns**; this is probably the only design pattern that you should really care about.  
The fundamental principles of the Symfony Components are focused on the [HTTP specification](https://tools.ietf.org/wg/httpbis/). As such, the framework that you are going to create should be more accurately labelled as a **HTTP framework or Request/Response framework**.  
  
Writing web code is about interacting with HTTP.  
In PHP, the request is represented by global variables (\\$\_GET, \\$\_POST, \\$\_FILE, \\$\_COOKIE, \\$\_SESSION...)  
and the response is generated by functions (echo, header, setcookie, ...).  
  
Security and testability (PHPUnit unit tests)  
  

* * *

  
First version :  
```php
$name = $_GET['name'] ?? 'World'; //if name query parameter is not defined in the URL query string     
header('Content-Type: text/html; charset=utf-8');      
//XSS (Cross-Site Scripting) - code more secure :     
printf('Hello %s', htmlspecialchars($name, ENT_QUOTES, 'UTF-8'));     
```
The first step towards better code is probably to use an Object-Oriented approach; that is the main goal of the **Symfony HttpFoundation component**:  
1\. replacing the default PHP global variables and functions by an Object-Oriented layer  
To use this component, add it as a dependency of the project:  
**composer require symfony/http-foundation**  
... Outputs :  
1 package suggestions were added by new dependencies, use \`composer suggest\` to see details.  
Generating autoload files  
3 packages you are using are looking for funding. Use \`composer fund\` command to find out more!  
  
Running this command will also automatically download the Symfony HttpFoundation component and install it under the vendor/ directory. A composer.json and a composer.lock file will be generated as well, containing the new requirement.  
  
When installing a new dependency, Composer also generates a vendor/autoload.php file that allows any class to be autoloaded. Without autoloading, you would need to require the file where a class is defined before being able to use it. But thanks to PSR-4, we can just let Composer and PHP do the hard work for us.  
  

* * *

  
  

2\. HttpFoundation Component
----------------------------

[Going OOP with the HttpFoundation Component](https://symfony.com/doc/current/create_framework/http_foundation.html#going-oop-with-the-httpfoundation-component)  
Using framework is good idea, even for the simplest snippet of code and creating your framework on top of Symfony components is better than creating a framework from scratch.  
We won't talk about the traditional benefits of using a framework when working on big applications with more than a few developers; the Internet already has plenty of good resources on that topic.  

As you might have noticed, securing your code with **htmlspecialchars** is tedious and error prone. That's one of the reasons why using a template engine like [**Twig**](https://twig.symfony.com/), where auto-escaping is enabled by default, might be a good idea (and explicit escaping is also less painful with the usage of a simple e filter).  
  
Beyond security, this code can be complex to test. Even if there is not much to test, it strikes me that writing unit tests for the simplest possible snippet of PHP code is not natural and feels ugly. Here is a tentative PHPUnit unit test for the above code:  
```php
// framework/test.php
use PHPUnit\\Framework\\TestCase;

class IndexTest extends TestCase
{
  public function testHello()
  {
    $\_GET\['name'\] = 'Fabien';
    ob\_start();
    include 'index.php';
    $content = ob\_get\_clean();
    $this->**assertEquals**('Hello Fabien', $content);
  }
}
```

If our application were just slightly bigger, we would have been able to find even more problems. If you are curious about them, read 
[Symfony versus Flat PHP (A Basic Blog)](https://symfony.com/doc/current/introduction/from_flat_php_to_symfony.html) chapter of the book.

```php
// framework/index.php
require\_once \_\_DIR\_\_.'/vendor/autoload.php';

use Symfony\\Component\\HttpFoundation\\Request;
use Symfony\\Component\\HttpFoundation\\Response;

//creates a Request object based on the current PHP global variables :
$request = Request::createFromGlobals();

$name = $request->query->get('name', 'World');

$response = new Response(sprintf('Hello %s', htmlspecialchars($name, ENT\_QUOTES, 'UTF-8')));

//sends Response object back to client (first outputs HTTP headers followed by content) :
$response->send();
```    
Before the send() call, we should have added a call to the prepare() method ($response->prepare($request);) to ensure that our Response were compliant with the HTTP specification. For instance, if we were to call the page with the HEAD method, it would remove the content of the Response.  
    
The main difference with the previous code is that you have total **control of HTTP messages**. You can create whatever request you want and you are in charge of sending the response whenever you see fit.     
We haven't explicitly set the Content-Type header in the rewritten code as the charset of the Response object defaults to UTF-8.       
With the Request class, you have all the request information at your fingertips thanks to a nice and simple API:  
```php      
// the URI being requested (e.g. /about) minus any query parameters  
$request->getPathInfo();  
  
// retrieve GET and POST variables respectively  
$request->query->get('foo');  
$request->request->get('bar', 'default value if bar does not exist');  
  
// retrieve SERVER variables  
$request->server->get('HTTP\_HOST');  
  
// retrieves an instance of UploadedFile identified by foo  
$request->files->get('foo');  
  
// retrieve a COOKIE value  
$request->cookies->get('PHPSESSID');  
  
// retrieve an HTTP request header, with normalized, lowercase keys  
$request->headers->get('host');  
$request->headers->get('content-type');  
  
$request->getMethod(); // GET, POST, PUT, DELETE, HEAD  
$request->getLanguages(); // an array of languages the client accepts  
You can also simulate a request:  
$request = Request::create('/index.php?name=Fabien');  
With the Response class, you can tweak the response:  
  
$response = new Response();  
  
$response->setContent('Hello world!');  
$response->setStatusCode(200);  
$response->headers->set('Content-Type', 'text/html');  
  
// configure the HTTP cache headers  
$response->setMaxAge(10);  
```    
To debug a response, cast it to a string; it will return the HTTP representation of the response (headers and content).  
    
Last but not least, these classes, like every other class in the Symfony code, have been [audited](https://symfony.com/blog/symfony2-security-audit) for security issues by an independent company. And being an Open-Source project also means that many other developers around the world have read the code and have already fixed potential security problems. When was the last time you ordered a professional security audit for your home-made framework?  
    
Even something as simple as getting the client IP address can be insecure:  
```php      
if ($myIp === $\_SERVER\['REMOTE\_ADDR'\]) {  
// the client is a known one, so give it some more privilege  
}  
```
It works perfectly fine until you add a reverse proxy in front of the production servers; at this point, you will have to change your code to make it work on both your development machine (where you don't have a proxy) and your servers:  
```php       
if ($myIp === $\_SERVER\['HTTP\_X\_FORWARDED\_FOR'\] || $myIp === $\_SERVER\['REMOTE\_ADDR'\]) {  
// the client is a known one, so give it some more privilege  
    }  
```
Using the Request::getClientIp() method would have given you the right behavior from day one (and it would have covered the case where you have chained proxies):  
       
    $request = Request::createFromGlobals();  
      
    if ($myIp === $request->getClientIp()) {  
    // the client is a known one, so give it some more privilege  
    }  
And there is an added benefit: it is secure by default. What does it mean? The $\_SERVER\['HTTP\_X\_FORWARDED\_FOR'\] value cannot be trusted as it can be manipulated by the end user when there is no proxy. So, if you are using this code in production without a proxy, it becomes trivially easy to abuse your system. That's not the case with the getClientIp() method as you must explicitly trust your reverse proxies by calling setTrustedProxies():  
       
    Request::setTrustedProxies(\['10.0.0.1'\]);  
      
    if ($myIp === $request->getClientIp()) {  
       // the client is a known one, so give it some more privilege  
    }  
So, the getClientIp() method works securely in all circumstances. You can use it in all your projects, whatever the configuration is, it will behave correctly and safely. That's one of the goals of using a framework. If you were to write a framework from scratch, you would have to think about all these cases by yourself. Why not use a technology that already works?  
If you want to learn more about the HttpFoundation component, you can have a look at the Symfony\\Component\\HttpFoundation API or read its dedicated [documentation](https://symfony.com/doc/current/components/http_foundation.html).  

Believe it or not but we have our first framework. You can stop now if you want. Using just the Symfony HttpFoundation component already allows you to write better and more testable code. It also allows you to write code faster as many day-to-day problems have already been solved for you.  
As a matter of fact, projects like Drupal have adopted the HttpFoundation component; if it works for them, it will probably work for you. Don't reinvent the wheel.  
I've almost forgotten to talk about one added benefit: using the HttpFoundation component is the start of better interoperability between all frameworks and [applications using it](https://symfony.com/components/HttpFoundation) (like [Symfony](https://symfony.com/), [Drupal 8](https://www.drupal.org/), [phpBB 3](https://www.phpbb.com/), [Laravel](https://laravel.com/) and [ezPublish 5](https://ez.no/), and [more](https://symfony.com/components/HttpFoundation)).  





    
3\. Front Controller design pattern
 -----------------------------------
    
Add another page that says goodbye:lverInterface $argumentResolver)  
```php    
// framework/bye.php
require_once __DIR__.'/vendor/autoload.php';

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

$request = Request::createFromGlobals();

$response = new Response('Goodbye!');
$response->send();
```
As you can see for yourself, much of the code is exactly the same as the one we have written for the first page. Let's extract the common code that we can share between all our pages. Code sharing sounds like a good plan to create our first "real" framework!

The PHP way of doing the refactoring would probably be the creation of an include file:

```php    
// framework/init.php
require_once __DIR__.'/vendor/autoload.php';

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

$request = Request::createFromGlobals();
$response = new Response();
Let's see it in action:

 Copy
// framework/index.php
require_once __DIR__.'/init.php';

$name = $request->query->get('name', 'World');

$response->setContent(sprintf('Hello %s', htmlspecialchars($name, ENT_QUOTES, 'UTF-8')));
$response->send();
And for the "Goodbye" page:

 Copy
// framework/bye.php
require_once __DIR__.'/init.php';

$response->setContent('Goodbye!');
$response->send();
```

We have indeed moved most of the shared code into a central place, but it does not feel like a good abstraction, does it? We still have the send() method for all pages, our pages do not look like templates and we are still not able to test this code properly.  

Moreover, adding a new page means that we need to create a new PHP script, the name of which is exposed to the end user via the URL (http://127.0.0.1:4321/bye.php). There is a direct mapping between the PHP script name and the client URL. This is because the dispatching of the request is done by the web server directly. It might be a good idea to move this dispatching to our code for better flexibility. This can be achieved by routing all client requests to a single PHP script.
    
**Exposing a single PHP script to the end user** is a design pattern called "[front controller](https://symfony.com/doc/current/introduction/from_flat_php_to_symfony.html#from_flat_php-front-controller)".  
Such a script might look like the following:  

```php
// framework/front.php
require_once __DIR__.'/vendor/autoload.php';

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

$request = Request::createFromGlobals();
$response = new Response();

$map = [
    '/hello' => __DIR__.'/hello.php',
    '/bye'   => __DIR__.'/bye.php',
];

$path = $request->getPathInfo();
if (isset($map[$path])) {
    require $map[$path];
} else {
    $response->setStatusCode(404);
    $response->setContent('Not Found');
}

$response->send();
```


And here is for instance the new hello.php script:
```php
// framework/hello.php  
$name = $request->query->get('name', 'World');  
$response->setContent(sprintf('Hello %s', htmlspecialchars($name, ENT\_QUOTES, 'UTF-8')));
```
In the front.php script, $map associates URL paths with their corresponding PHP script paths.  

As a bonus, if the client asks for a path that is not defined in the URL map, we return a custom 404 page. You are now in control of your website.    

To access a page, you must now use the front.php script:  
    http://127.0.0.1:4321/front.php/hello?name=Fabien  
    http://127.0.0.1:4321/front.php/bye  
/hello and /bye are the page paths.

Most web servers like Apache or nginx are able to rewrite the incoming URLs and remove the front controller script so that your users will be able to type http://127.0.0.1:4321/hello?name=Fabien, which looks much better.  

The trick is the usage of the Request::getPathInfo() method which returns the path of the Request by removing the front controller script name including its sub-directories (only if needed -- see above tip).  

You don't even need to set up a web server to test the code. Instead, replace      
$request = Request::createFromGlobals(); call to something like      
$request = Request::create('/hello?name=Fabien');      
where the argument is the URL path you want to simulate.     

Now that the web server always accesses the same script (front.php) for all pages, we can secure the code further by moving all other PHP files outside of the web root directory:  
```php
example.com
├── composer.json
├── composer.lock
├── src
│   └── pages
│       ├── hello.php
│       └── bye.php
├── vendor
│   └── autoload.php
└── web
    └── front.php
```
























Put another way: your code dispatches an event to the dispatcher, the dispatcher 
notifies all registered listeners for the event, and each listener does whatever 
it wants with the event.  
As an example, let's create a listener that 
transparently adds the Google Analytics code to all responses.  
To make it 
work, the framework must dispatch an event just before returning the Response 
instance:  

    // example.com/src/Simplex/Framework.php  
    namespace Simplex;  
      
    use Symfony\\Component\\EventDispatcher\\EventDispatcher;  
    use 
    Symfony\\Component\\HttpFoundation\\Request;  
    use 
    Symfony\\Component\\HttpFoundation\\Response;  
    use 
    Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface;  
    use 
    Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface;  
    use 
    Symfony\\Component\\Routing\\Exception\\ResourceNotFoundException;  
    use 
    Symfony\\Component\\Routing\\Matcher\\UrlMatcherInterface;  
      
    class Framework  
    {  
    private $dispatcher;  
    private $matcher;  
    private $controllerResolver;  
    private $argumentResolver;  
      
    public function \_\_construct(EventDispatcher 
    $dispatcher, UrlMatcherInterface $matcher, ControllerResolverInterface 
    $controllerResolver, ArgumentResolverInterface $argumentResolver)  
    {  
    $this->dispatcher = $dispatcher;  
    $this->matcher = $matcher;  
    $this->controllerResolver = $controllerResolver;  
    $this->argumentResolver = 
    $argumentResolver;  
    }  
      
    public function handle(Request $request)  
    {  
    $this->matcher->getContext()->fromRequest($request);  
      
    try {  
    $request->attributes->add($this->matcher->match($request->getPathInfo()));  
      
    $controller = $this->controllerResolver->getController($request);  
    $arguments = $this->argumentResolver->getArguments($request, $controller);  
      
    $response = call\_user\_func\_array($controller, $arguments);  
    } catch 
    (ResourceNotFoundException $exception) {  
    $response = new Response('Not 
    Found', 404);  
    } catch (\\Exception $exception) {  
    $response = new 
    Response('An error occurred', 500);  
    }  
      
    // dispatch a response event  
    $this->dispatcher->dispatch(new ResponseEvent($response, $request), 'response');  
      
    return $response;  
    }  
    }  
    Each time the framework handles a Request, 
    a ResponseEvent event is now dispatched:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    24  
    25  
    26  
    27  
    28  
    // example.com/src/Simplex/ResponseEvent.php  
    namespace Simplex;  
      
    use Symfony\\Component\\HttpFoundation\\Request;  
    use 
    Symfony\\Component\\HttpFoundation\\Response;  
    use 
    Symfony\\Contracts\\EventDispatcher\\Event;  
      
    class ResponseEvent extends 
    Event  
    {  
    private $request;  
    private $response;  
      
    public function 
    \_\_construct(Response $response, Request $request)  
    {  
    $this->response = 
    $response;  
    $this->request = $request;  
    }  
      
    public function 
    getResponse()  
    {  
    return $this->response;  
    }  
      
    public function 
    getRequest()  
    {  
    return $this->request;  
    }  
    }  
    The last step is the 
    creation of the dispatcher in the front controller and the registration of a 
    listener for the response event:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    24  
    25  
    26  
    27  
    28  
    // example.com/web/front.php  
    require\_once 
    \_\_DIR\_\_.'/../vendor/autoload.php';  
      
    // ...  
      
    use 
    Symfony\\Component\\EventDispatcher\\EventDispatcher;  
      
    $dispatcher = new 
    EventDispatcher();  
    $dispatcher->addListener('response', function 
    (Simplex\\ResponseEvent $event) {  
    $response = $event->getResponse();  
      
    if 
    ($response->isRedirection()  
    || ($response->headers->has('Content-Type') && 
    false === strpos($response->headers->get('Content-Type'), 'html'))  
    || 'html' 
    !== $event->getRequest()->getRequestFormat()  
    ) {  
    return;  
    }  
      
    $response->setContent($response->getContent().'GA CODE');  
    });  
      
    $controllerResolver = new ControllerResolver();  
    $argumentResolver = new 
    ArgumentResolver();  
      
    $framework = new Simplex\\Framework($dispatcher, 
    $matcher, $controllerResolver, $argumentResolver);  
    $response = 
    $framework->handle($request);  
      
    $response->send();  
    The listener is just 
    a proof of concept and you should add the Google Analytics code just before the 
    body tag.  
    As you can see, addListener() associates a valid PHP callback to a 
    named event (response); the event name must be the same as the one used in 
    the dispatch() call.  
    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).  
    So far so good, 
    but let's add another listener on the same event. Let's say that we want to set 
    the Content-Length of the Response if it is not already set:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    $dispatcher->addListener('response', function 
    (Simplex\\ResponseEvent $event) {  
    $response = $event->getResponse();  
    $headers = $response->headers;  
      
    if (!$headers->has('Content-Length') && 
    !$headers->has('Transfer-Encoding')) {  
    $headers->set('Content-Length', 
    strlen($response->getContent()));  
    }  
    });  
    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 Content-Length header. 
    Sometimes, the order of the listeners matter but by default, all listeners are 
    registered with the same priority, 0. 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 Content-Length listener to be 
    executed last, so change the priority to -255:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    $dispatcher->addListener('response', function (Simplex\\ResponseEvent 
    $event) {  
    $response = $event->getResponse();  
    $headers = 
    $response->headers;  
      
    if (!$headers->has('Content-Length') && 
    !$headers->has('Transfer-Encoding')) {  
    $headers->set('Content-Length', 
    strlen($response->getContent()));  
    }  
    }, -255);  
    When creating your 
    framework, think about priorities (reserve some numbers for internal listeners 
    for instance) and document them thoroughly.  
    Let's refactor the code a bit by 
    moving the Google listener to its own class:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    // 
    example.com/src/Simplex/GoogleListener.php  
    namespace Simplex;  
      
    class 
    GoogleListener  
    {  
    public function onResponse(ResponseEvent $event)  
    {  
    $response = $event->getResponse();  
      
    if ($response->isRedirection()  
    || 
    ($response->headers->has('Content-Type') && false === 
    strpos($response->headers->get('Content-Type'), 'html'))  
    || 'html' !== 
    $event->getRequest()->getRequestFormat()  
    ) {  
    return;  
    }  
      
    $response->setContent($response->getContent().'GA CODE');  
    }  
    }  
    And do 
    the same with the other listener:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    // 
    example.com/src/Simplex/ContentLengthListener.php  
    namespace Simplex;  
      
    class ContentLengthListener  
    {  
    public function onResponse(ResponseEvent 
    $event)  
    {  
    $response = $event->getResponse();  
    $headers = 
    $response->headers;  
      
    if (!$headers->has('Content-Length') && 
    !$headers->has('Transfer-Encoding')) {  
    $headers->set('Content-Length', 
    strlen($response->getContent()));  
    }  
    }  
    }  
    Our front controller should 
    now look like the following:  
    1  
    2  
    3  
    $dispatcher = new 
    EventDispatcher();  
    $dispatcher->addListener('response', \[new 
    Simplex\\ContentLengthListener(), 'onResponse'\], -255);  
    $dispatcher->addListener('response', \[new Simplex\\GoogleListener(), 
    'onResponse'\]);  
    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. The solution to this dilemma is to use subscribers instead of 
    listeners:  
    1  
    2  
    3  
    $dispatcher = new EventDispatcher();  
    $dispatcher->addSubscriber(new Simplex\\ContentLengthListener());  
    $dispatcher->addSubscriber(new Simplex\\GoogleListener());  
    A subscriber knows 
    about all the events it is interested in and pass this information to the 
    dispatcher via the getSubscribedEvents() method. Have a look at the new version 
    of the GoogleListener:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    // example.com/src/Simplex/GoogleListener.php  
    namespace 
    Simplex;  
      
    use Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;  
      
    class GoogleListener implements EventSubscriberInterface  
    {  
    // ...  
      
    public static function getSubscribedEvents()  
    {  
    return \['response' => 
    'onResponse'\];  
    }  
    }  
    And here is the new version 
    of ContentLengthListener:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    // example.com/src/Simplex/ContentLengthListener.php  
    namespace Simplex;  
      
    use 
    Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;  
      
    class 
    ContentLengthListener implements EventSubscriberInterface  
    {  
    // ...  
      
    public static function getSubscribedEvents()  
    {  
    return \['response' => 
    \['onResponse', -255\]\];  
    }  
    }  
    A single subscriber can host as many 
    listeners as you want on as many events as needed.  
    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 book 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.  
    
    10\. HttpKernel Component: 
    HttpKernelInterface
    -----------------------------------------------
    
    In the conclusion of the second chapter of this book, I've talked about one 
    great benefit of using the Symfony components: the interoperability between all 
    frameworks and applications using them. Let's do a big step towards this goal by 
    making our framework implement HttpKernelInterface:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    namespace Symfony\\Component\\HttpKernel;  
      
    // 
    ...  
    interface HttpKernelInterface  
    {  
    /\*\*  
    \* @return Response A 
    Response instance  
    \*/  
    public function handle(  
    Request $request,  
    $type 
    = self::MAIN\_REQUEST,  
    $catch = true  
    );  
    }  
    HttpKernelInterface 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.  
    Update your framework so that it implements this interface:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    // 
    example.com/src/Framework.php  
      
    // ...  
    use 
    Symfony\\Component\\HttpKernel\\HttpKernelInterface;  
      
    class Framework 
    implements HttpKernelInterface  
    {  
    // ...  
      
    public function handle(  
    Request $request,  
    $type = HttpKernelInterface::MAIN\_REQUEST,  
    $catch = true  
    ) {  
    // ...  
    }  
    }  
    With this change, a little goes a long way! Let's 
    talk about one of the most impressive upsides: transparent [HTTP 
    caching](https://symfony.com/doc/current/http_cache.html) support.  
    The HttpCache class implements a fully-featured reverse 
    proxy, written in PHP; it implements HttpKernelInterface and wraps 
    another HttpKernelInterface instance:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    // example.com/web/front.php  
      
    // ...  
    use 
    Symfony\\Component\\HttpKernel;  
      
    $framework = new 
    Simplex\\Framework($dispatcher, $matcher, $controllerResolver, 
    $argumentResolver);  
    $framework = new HttpKernel\\HttpCache\\HttpCache(  
    $framework,  
    new HttpKernel\\HttpCache\\Store(\_\_DIR\_\_.'/../cache')  
    );  
      
    $response = $framework->handle($request);  
    $response->send();  
    That's all it 
    takes to add HTTP caching support to our framework. Isn't it amazing?  
    Configuring the cache needs to be done via HTTP cache headers. For instance, to 
    cache a response for 10 seconds, use the Response::setTtl() method:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    // 
    example.com/src/Calendar/Controller/LeapYearController.php  
      
    // ...  
    public function index(Request $request, $year)  
    {  
    $leapYear = new 
    LeapYear();  
    if ($leapYear->isLeapYear($year)) {  
    $response = new 
    Response('Yep, this is a leap year!');  
    } else {  
    $response = new 
    Response('Nope, this is not a leap year.');  
    }  
      
    $response->setTtl(10);  
      
    return $response;  
    }  
    If you are running your framework from the command 
    line by simulating requests (Request::create('/is\_leap\_year/2012')), you can 
    debug Response instances by dumping their string representation (echo 
    $response;) as it displays all headers as well as the response content.  
    To 
    validate that it works correctly, add a random number to the response content 
    and check that the number only changes every 10 seconds:  
    $response = new 
    Response('Yep, this is a leap year! '.rand());  
    When deploying to your 
    production environment, keep using the Symfony reverse proxy (great for shared 
    hosting) or even better, switch to a more efficient reverse proxy like [Varnish](https://varnish-cache.org/).  
    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, read the [HTTP 
    caching](https://symfony.com/doc/current/http_cache.html) chapter of the Symfony documentation.  
    The Response class contains 
    methods that let you configure the HTTP cache. One of the most powerful 
    is setCache() as it abstracts the most frequently used caching strategies into a 
    single array:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    $response->setCache(\[  
    'must\_revalidate' => false,  
    'no\_cache' => false,  
    'no\_store' => false,  
    'no\_transform' => false,  
    'public' => true,  
    'private' => false,  
    'proxy\_revalidate' => false,  
    'max\_age' => 600,  
    's\_maxage' => 600,  
    'immutable' => true,  
    'last\_modified' => new 
    \\DateTime(),  
    'etag' => 'abcdef'  
    \]);  
      
    // it is equivalent to the 
    following code  
    $response->setPublic();  
    $response->setMaxAge(600);  
    $response->setSharedMaxAge(600);  
    $response->setImmutable();  
    $response->setLastModified(new \\DateTime());  
    $response->setEtag('abcde');  
    When using the validation model, the isNotModified() method allows you to cut on 
    the response time by short-circuiting the response generation as early as 
    possible:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    $response->setETag('whatever\_you\_compute\_as\_an\_etag');  
      
    if 
    ($response->isNotModified($request)) {  
    return $response;  
    }  
      
    $response->setContent('The computed content of the response');  
      
    return 
    $response;  
    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 ([ESI](https://en.wikipedia.org/wiki/Edge_Side_Includes)) 
    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:  
    1  
    2  
    3  
    4  
    5  
    This is the content of your page  
      
    Is 2012 a leap year? 
      
      
    Some other content  
    For ESI tags to be supported by HttpCache, you need to pass it an instance of 
    the ESI class. The ESI class automatically parses ESI tags and makes 
    sub-requests to convert them to their proper content:  
    1  
    2  
    3  
    4  
    5  
    $framework = new HttpKernel\\HttpCache\\HttpCache(  
    $framework,  
    new 
    HttpKernel\\HttpCache\\Store(\_\_DIR\_\_.'/../cache'),  
    new 
    HttpKernel\\HttpCache\\Esi()  
    );  
    For ESI to work, you need to use a reverse 
    proxy that supports it like the Symfony implementation. [Varnish](https://varnish-cache.org/) is 
    the best alternative and it is Open-Source.  
    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:  
    1  
    2  
    3  
    4  
    5  
    6  
    $framework = new 
    HttpKernel\\HttpCache\\HttpCache(  
    $framework,  
    new 
    HttpKernel\\HttpCache\\Store(\_\_DIR\_\_.'/../cache'),  
    new 
    HttpKernel\\HttpCache\\Esi(),  
    \['debug' => true\]  
    );  
    The debug mode adds 
    a X-Symfony-Cache header to each response that describes what the cache layer 
    did:  
    1  
    2  
    3  
    X-Symfony-Cache: GET /is\_leap\_year/2012: stale, invalid, 
    store  
      
    X-Symfony-Cache: GET /is\_leap\_year/2012: fresh  
    HttpCache has 
    many features like support for 
    the stale-while-revalidate and stale-if-error HTTP Cache-Control extensions as 
    defined in RFC 5861.  
    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!  
    
    11\. HttpKernel Component: The HttpKernel Class
    -----------------------------------------------
    
    If you were to use our framework right now, you would probably have to add 
    support for custom error messages. We do have 404 and 500 error support but the 
    responses are hardcoded in the framework itself. Making them customizable is 
    straightforward 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?  
    Enter the HttpKernel class. Instead of 
    solving the same problem over and over again and instead of reinventing the 
    wheel each time, the HttpKernel class is a generic, extensible and flexible 
    implementation of HttpKernelInterface.  
    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.  
    Here is the new framework code:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    // example.com/src/Simplex/Framework.php  
    namespace Simplex;  
      
    use 
    Symfony\\Component\\HttpKernel\\HttpKernel;  
      
    class Framework extends 
    HttpKernel  
    {  
    }  
    And the new front controller:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    24  
    25  
    26  
    27  
    // example.com/web/front.php  
    require\_once \_\_DIR\_\_.'/../vendor/autoload.php';  
      
    use 
    Symfony\\Component\\EventDispatcher\\EventDispatcher;  
    use 
    Symfony\\Component\\HttpFoundation\\Request;  
    use 
    Symfony\\Component\\HttpFoundation\\RequestStack;  
    use 
    Symfony\\Component\\HttpFoundation\\Response;  
    use Symfony\\Component\\HttpKernel;  
    use Symfony\\Component\\Routing;  
      
    $request = Request::createFromGlobals();  
    $requestStack = new RequestStack();  
    $routes = include 
    \_\_DIR\_\_.'/../src/app.php';  
      
    $context = new Routing\\RequestContext();  
    $matcher = new Routing\\Matcher\\UrlMatcher($routes, $context);  
      
    $controllerResolver = new HttpKernel\\Controller\\ControllerResolver();  
    $argumentResolver = new HttpKernel\\Controller\\ArgumentResolver();  
      
    $dispatcher = new EventDispatcher();  
    $dispatcher->addSubscriber(new 
    HttpKernel\\EventListener\\RouterListener($matcher, $requestStack));  
      
    $framework = new Simplex\\Framework($dispatcher, $controllerResolver, 
    $requestStack, $argumentResolver);  
      
    $response = 
    $framework->handle($request);  
    $response->send();  
    RouterListener 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.  
    Our code is now much more concise and surprisingly more robust and more powerful 
    than ever. For instance, use the built-in ErrorListener to make your error 
    management configurable:  
    1  
    2  
    3  
    4  
    5  
    6  
    $errorHandler = 
    function (Symfony\\Component\\ErrorHandler\\Exception\\FlattenException $exception) 
    {  
    $msg = 'Something went wrong! ('.$exception->getMessage().')';  
      
    return new Response($msg, $exception->getStatusCode());  
    };  
    $dispatcher->addSubscriber(new 
    HttpKernel\\EventListener\\ErrorListener($errorHandler));  
    ErrorListener gives 
    you a FlattenException instance instead of the 
    thrown Exception or Error 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:  
    1  
    2  
    3  
    4  
    $listener = new HttpKernel\\EventListener\\ErrorListener(  
    'Calendar\\Controller\\ErrorController::exception'  
    );  
    $dispatcher->addSubscriber($listener);  
    The error controller reads as follows:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    // example.com/src/Calendar/Controller/ErrorController.php  
    namespace 
    Calendar\\Controller;  
      
    use 
    Symfony\\Component\\ErrorHandler\\Exception\\FlattenException;  
    use 
    Symfony\\Component\\HttpFoundation\\Response;  
      
    class ErrorController  
    {  
    public function exception(FlattenException $exception)  
    {  
    $msg = 'Something 
    went wrong! ('.$exception->getMessage().')';  
      
    return new Response($msg, 
    $exception->getStatusCode());  
    }  
    }  
    Voilà! Clean and customizable error 
    management without efforts. And if your ErrorController throws an exception, 
    HttpKernel will handle it nicely.  
    In chapter two, we talked about 
    the Response::prepare() 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 ResponseListener does:  
    $dispatcher->addSubscriber(new 
    HttpKernel\\EventListener\\ResponseListener('UTF-8'));  
    If you want out of the 
    box support for streamed responses, subscribe to StreamedResponseListener:  
    $dispatcher->addSubscriber(new 
    HttpKernel\\EventListener\\StreamedResponseListener());  
    And in your controller, 
    return a StreamedResponse instance instead of a Response instance.  
    Read the [Built-in 
    Symfony Events](https://symfony.com/doc/current/reference/events.html) reference to learn more about the events dispatched by 
    HttpKernel and how they allow you to change the flow of a request.  
    Now, let's 
    create a listener, one that allows a controller to return a string instead of a 
    full Response object:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    class LeapYearController  
    {  
    public function index($year)  
    {  
    $leapYear = new LeapYear();  
    if ($leapYear->isLeapYear($year)) {  
    return 
    'Yep, this is a leap year! ';  
    }  
      
    return 'Nope, this is not a leap 
    year.';  
    }  
    }  
    To implement this feature, we are going to listen to 
    the kernel.view 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:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    // 
    example.com/src/Simplex/StringResponseListener.php  
    namespace Simplex;  
      
    use Symfony\\Component\\EventDispatcher\\EventSubscriberInterface;  
    use 
    Symfony\\Component\\HttpFoundation\\Response;  
    use 
    Symfony\\Component\\HttpKernel\\Event\\ViewEvent;  
      
    class 
    StringResponseListener implements EventSubscriberInterface  
    {  
    public 
    function onView(ViewEvent $event)  
    {  
    $response = 
    $event->getControllerResult();  
      
    if (is\_string($response)) {  
    $event->setResponse(new Response($response));  
    }  
    }  
      
    public static 
    function getSubscribedEvents()  
    {  
    return \['kernel.view' => 'onView'\];  
    }  
    }  
    The code is simple because the kernel.view 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).  
    Don't forget to register it in the front controller:  
    $dispatcher->addSubscriber(new Simplex\\StringResponseListener());  
    If you 
    forget to register the subscriber, HttpKernel will throw an exception with a 
    nice message: The controller must return a response (Nope, this is not a leap 
    year. given)..  
    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.  
    Hopefully, 
    you now have a better understanding of why the simple 
    looking HttpKernelInterface is so powerful. Its default 
    implementation, HttpKernel, 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 Symfony framework, 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.  
    
    12\. DependencyInjection Component
    ----------------------------------
    
    In the previous chapter, we emptied the Simplex\\Framework class by extending 
    the HttpKernel class from the eponymous component. Seeing this empty class, you 
    might be tempted to move some code from the front controller to it:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    24  
    25  
    26  
    27  
    28  
    29  
    30  
    31  
    // 
    example.com/src/Simplex/Framework.php  
    namespace Simplex;  
      
    use 
    Symfony\\Component\\EventDispatcher\\EventDispatcher;  
    use 
    Symfony\\Component\\HttpFoundation;  
    use 
    Symfony\\Component\\HttpFoundation\\RequestStack;  
    use 
    Symfony\\Component\\HttpKernel;  
    use Symfony\\Component\\Routing;  
      
    class 
    Framework extends HttpKernel\\HttpKernel  
    {  
    public function 
    \_\_construct($routes)  
    {  
    $context = new Routing\\RequestContext();  
    $matcher = new Routing\\Matcher\\UrlMatcher($routes, $context);  
    $requestStack = 
    new RequestStack();  
      
    $controllerResolver = new 
    HttpKernel\\Controller\\ControllerResolver();  
    $argumentResolver = new 
    HttpKernel\\Controller\\ArgumentResolver();  
      
    $dispatcher = new 
    EventDispatcher();  
    $dispatcher->addSubscriber(new 
    HttpKernel\\EventListener\\ErrorListener(  
    'Calendar\\Controller\\ErrorController::exception'  
    ));  
    $dispatcher->addSubscriber(new HttpKernel\\EventListener\\RouterListener($matcher, 
    $requestStack));  
    $dispatcher->addSubscriber(new 
    HttpKernel\\EventListener\\ResponseListener('UTF-8'));  
    $dispatcher->addSubscriber(new StringResponseListener());  
      
    parent::\_\_construct($dispatcher, $controllerResolver, $requestStack, 
    $argumentResolver);  
    }  
    }  
    The front controller code would become more 
    concise:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    // 
    example.com/web/front.php  
    require\_once \_\_DIR\_\_.'/../vendor/autoload.php';  
      
    use Symfony\\Component\\HttpFoundation\\Request;  
      
    $request = 
    Request::createFromGlobals();  
    $routes = include \_\_DIR\_\_.'/../src/app.php';  
      
    $framework = new Simplex\\Framework($routes);  
      
    $framework->handle($request)->send();  
    Having a concise front controller 
    allows you to have several front controllers 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:  
    ini\_set('display\_errors', 1);  
    error\_reporting(-1);  
    ... 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.  
    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:  
    We are not able to register custom listeners anymore as the 
    dispatcher is not available outside the Framework class (a workaround could be 
    the adding of a Framework::getEventDispatcher() method);  
    We have lost the 
    flexibility we had before; you cannot change the implementation of 
    the UrlMatcher or of the ControllerResolver anymore;  
    Related to the previous 
    point, we cannot test our framework without much effort anymore as it's 
    impossible to mock internal objects;  
    We cannot change the charset passed 
    to ResponseListener anymore (a workaround could be to pass it as a constructor 
    argument).  
    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 dispatchers were injected into the 
    framework so that we had total control of its creation and configuration).  
    Does it mean that we have to make a choice between flexibility, customization, 
    ease of testing and not 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 Symfony dependency injection container:  
    $ composer require symfony/dependency-injection  
    Create a new file to host the 
    dependency injection container configuration:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    14  
    15  
    16  
    17  
    18  
    19  
    20  
    21  
    22  
    23  
    24  
    25  
    26  
    27  
    28  
    29  
    30  
    31  
    32  
    33  
    34  
    35  
    36  
    37  
    38  
    39  
    40  
    41  
    42  
    // example.com/src/container.php  
    use Simplex\\Framework;  
    use Symfony\\Component\\DependencyInjection;  
    use 
    Symfony\\Component\\DependencyInjection\\Reference;  
    use 
    Symfony\\Component\\EventDispatcher;  
    use Symfony\\Component\\HttpFoundation;  
    use Symfony\\Component\\HttpKernel;  
    use Symfony\\Component\\Routing;  
      
    $containerBuilder = new DependencyInjection\\ContainerBuilder();  
    $containerBuilder->register('context', Routing\\RequestContext::class);  
    $containerBuilder->register('matcher', Routing\\Matcher\\UrlMatcher::class)  
    ->setArguments(\[$routes, new Reference('context')\])  
    ;  
    $containerBuilder->register('request\_stack', 
    HttpFoundation\\RequestStack::class);  
    $containerBuilder->register('controller\_resolver', 
    HttpKernel\\Controller\\ControllerResolver::class);  
    $containerBuilder->register('argument\_resolver', 
    HttpKernel\\Controller\\ArgumentResolver::class);  
      
    $containerBuilder->register('listener.router', 
    HttpKernel\\EventListener\\RouterListener::class)  
    \->setArguments(\[new 
    Reference('matcher'), new Reference('request\_stack')\])  
    ;  
    $containerBuilder->register('listener.response', 
    HttpKernel\\EventListener\\ResponseListener::class)  
    \->setArguments(\['UTF-8'\])  
    ;  
    $containerBuilder->register('listener.exception', 
    HttpKernel\\EventListener\\ErrorListener::class)  
    ->setArguments(\['Calendar\\Controller\\ErrorController::exception'\])  
    ;  
    $containerBuilder->register('dispatcher', 
    EventDispatcher\\EventDispatcher::class)  
    \->addMethodCall('addSubscriber', \[new 
    Reference('listener.router')\])  
    \->addMethodCall('addSubscriber', \[new 
    Reference('listener.response')\])  
    \->addMethodCall('addSubscriber', \[new 
    Reference('listener.exception')\])  
    ;  
    $containerBuilder->register('framework', Framework::class)  
    \->setArguments(\[  
    new Reference('dispatcher'),  
    new Reference('controller\_resolver'),  
    new 
    Reference('request\_stack'),  
    new Reference('argument\_resolver'),  
    \])  
    ;  
      
    return $containerBuilder;  
    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.  
    For instance, to create the router listener, we tell Symfony that 
    its class name is Symfony\\Component\\HttpKernel\\EventListener\\RouterListener and 
    that its constructor takes a matcher object (new Reference('matcher')). 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.  
    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.  
    The front controller is now only about wiring 
    everything together:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    9  
    10  
    11  
    12  
    13  
    // example.com/web/front.php  
    require\_once 
    \_\_DIR\_\_.'/../vendor/autoload.php';  
      
    use 
    Symfony\\Component\\HttpFoundation\\Request;  
      
    $routes = include 
    \_\_DIR\_\_.'/../src/app.php';  
    $container = include 
    \_\_DIR\_\_.'/../src/container.php';  
      
    $request = Request::createFromGlobals();  
      
    $response = $container->get('framework')->handle($request);  
      
    $response->send();  
    As all the objects are now created in the dependency 
    injection container, the framework code should be the previous simple version:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    8  
    // example.com/src/Simplex/Framework.php  
    namespace Simplex;  
      
    use Symfony\\Component\\HttpKernel\\HttpKernel;  
      
    class Framework extends HttpKernel  
    {  
    }  
    If you want a light alternative 
    for your container, consider [Pimple](https://github.com/silexphp/Pimple), 
    a simple dependency injection container in about 60 lines of PHP code.  
    Now, 
    here is how you can register a custom listener in the front controller:  
    1  
    2  
    3  
    4  
    5  
    6  
    7  
    // ...  
    use Simplex\\StringResponseListener;  
      
    $container->register('listener.string\_response', 
    StringResponseListener::class);  
    $container->getDefinition('dispatcher')  
    ->addMethodCall('addSubscriber', \[new Reference('listener.string\_response')\])  
    ;  
    Besides 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:  
    1  
    2  
    3  
    $container->setParameter('debug', true);  
      
    echo $container->getParameter('debug');  
    These parameters can be used when 
    defining object definitions. Let's make the charset configurable:  
    1  
    2  
    3  
    4  
    // ...  
    $container->register('listener.response', 
    HttpKernel\\EventListener\\ResponseListener::class)  
    ->setArguments(\['%charset%'\])  
    ;  
    After this change, you must set the 
    charset before using the response listener object:  
    $container->setParameter('charset', 'UTF-8');  
    Instead of relying on the 
    convention that the routes are defined by the $routes variables, let's use a 
    parameter again:  
    1  
    2  
    3  
    4  
    // ...  
    $container->register('matcher', Routing\\Matcher\\UrlMatcher::class)  
    ->setArguments(\['%routes%', new Reference('context')\])  
    ;  
    And the related 
    change in the front controller:  
    $container->setParameter('routes', include 
    \_\_DIR\_\_.'/../src/app.php');  
    We have barely scratched the surface of what you 
    can do with the container: from class names as parameters, to overriding 
    existing object definitions, from shared service 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 class.  
    Don't yell at 
    me if you don't want to use a dependency injection container in your framework. 
    If you don't like it, don't use it. It's your framework, not mine.  
    This is 
    (already) the last chapter of this book on creating a framework on top of the 
    Symfony 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 Symfony framework works internally.