Symfony #
Symfony Application (Request / Response) flow #
Front controller:
- The only entry point of every request instead of having different PHP files for each URL path
- The routing of different URLs to different parts of your application is done internally
| /index.php | executes index.php |
| /index.php/contact | executes index.php |
| /index.php/blog | executes index.php |
index.php
<?php
use App\Kernel;
use Symfony\Component\HttpFoundation\Request;
require dirname(__DIR__).'/vendor/autoload.php';
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
// Get variables from superglobals $_GET, $_POST, $_COOKIE, $_FILES, $_SERVER
$request = Request::createFromGlobals();
// Initialize the Kernel
// Uses the handle method to consume the Request object which will return the Response object
$response = $kernel->handle($request);
// Send the Response to the client
$response->send();
$kernel->terminate($request, $response);
Built-in Symfony Events (HttpKernel Component) #
https://symfony.com/doc/5.4/components/http_kernel.html
During the handling of an HTTP request, the application dispatches some events which you can use to modify how the request is handled and how the response is returned.
The dispatches are inside Symfony\Component\HttpKernel\HttpKernel, method handleRaw().
Namespace of events: Symfony\Component\HttpKernel\Event\
Each event extends KernelEvent and have the following methods:
getRequestType()
getKernel()
getRequest()
isMainRequest()
Command to list the listeners that listen to an event
bin/console debug:event-dispatcher <event-name>
$ php bin/console debug:event-dispatcher kernel.request
Registered Listeners for "kernel.request" Event
===============================================
------- --------------------------------------------------------------------------------------- ----------
Order Callable Priority
------- --------------------------------------------------------------------------------------- ----------
#1 Symfony\Component\HttpKernel\EventListener\DebugHandlersListener::configure() 2048
#2 Symfony\Component\HttpKernel\EventListener\ValidateRequestListener::onKernelRequest() 256
#3 Symfony\Component\HttpKernel\EventListener\SessionListener::onKernelRequest() 128
#4 Symfony\Component\HttpKernel\EventListener\LocaleListener::setDefaultLocale() 100
#5 Symfony\Component\HttpKernel\EventListener\RouterListener::onKernelRequest() 32
#6 Symfony\Component\HttpKernel\EventListener\LocaleListener::onKernelRequest() 16
------- --------------------------------------------------------------------------------------- ----------
kernel.request (Event class: RequestEvent)
- First event dispatched
- Purpose: Either to create and return a
Responsedirectly, or to add information to theRequest - Listened by the RouterListener to search of a matching controller
- The RouterListener stores the information of the matched controller inside
$request->attributes
kernel.controller (Event class: ControllerEvent)
- If the
kernel.requestdidn’t return a Response, the handler creates an instance of a Controller by using a ControllerResolver - The ControllerResolver uses the informations added in
$request->attributesby the RouteListener - Then the
kernel.controllerevent is dispatched- Purposes: Initialize things or change the controller just before the controller is executed.
- Example: Listened by
SensioFrameworkExtraBundle/ParamConverterListenerto transform raw data into objects and stores object in the$request->attributes
kernel.controller_arguments (Event class: ControllerArgumentsEvent)
- the ArgumentResolver uses reflection on the callable to return an array of the names and/or hint of each of the arguments
- Then the
kernel.controller_argumentsevent is dispatched - And the controller is called
kernel.view (Event Class: ViewEvent)
- Purposes: Transform a non-Response return value from a controller into a Response
- If the controller doesn’t return a Response object, then the kernel dispatches a
kernel.viewevent- Example: Used by FOSRestBundle to return different content-type responses (json, xml, html, …)
kernel.response (Event Class: ResponseEvent)
- Purposes: Modify the Response object just before it is sent (modifying headers, adding cookies, inject JS, …)
- Dispatched after the controller or any
kernel.viewlistener returns a Response- We can call
$response->send()if we want to deliver directly the Response to the client
- We can call
kernel.finish_request (Event Class: FinishRequestEvent)
- Dispatched after
kernel.response, if the Response has not been sent yet - Purpose: Reset the global state of the application
- Example:
- the translator listener resets the translator’s locale to the one of the parent request
- RouterListener: After a sub-request is done, we need to reset the routing context to the parent request so that the URL generator operates on the correct context again.
- Example:
kernel.terminate
- Dispatched after the response has been sent (see index.php)
- Purposes: Useful to perform slow or complex tasks that don’t need to be completed to send the response
- Example: Sending emails
WARNING: Internally, the HttpKernel makes use of the fastcgi_finish_request PHP function. This means that at the moment, only the PHP FPM server API is able to send a response to the client while the server’s PHP process still performs some tasks. With all other server APIs, listeners to kernel.terminate are still executed, but the response is not sent to the client until they are all completed.
kernel.exception Event Class: ExceptionEvent
- Purposes: Handle some type of exception and create an appropriate Response to return for the exception
- It’s dispatched if any exception is thrown at any point inside
Request context #
Holds information about the current request
public function __construct(
string $baseUrl = '',
string $method = 'GET',
string $host = 'localhost',
string $scheme = 'http',
int $httpPort = 80,
int $httpsPort = 443,
string $path = '/',
string $queryString = ''
) {}
Service container #
Private vs Public services #
- Public: The service can be fetched with `$container->get(‘service_id’)
- Private: The service is accessible from Dependency injection only
Services are private by default from symfony 4.0
Autowiring, Autoconfigure #
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
# Classes that implements LoggerInterface is automatically configured as a Logger service
# Auto tag => Ex: Adds the tag "twig.extension" automatically to classes that implement ExtensionInterface
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Tests,Kernel.php}'
Testing #
Different type of symfony tests
bin/console make:test
Which test type would you like?:
[TestCase ] basic PHPUnit tests
[KernelTestCase ] basic tests that have access to Symfony services
[WebTestCase ] to run browser-like scenarios, but that don't execute JavaScript code
[ApiTestCase ] to run API-oriented scenarios
[PantherTestCase] to run e2e scenarios, using a real-browser or HTTP client and a real web server