The Silex Book: Generated On April 10, 2019
The Silex Book: Generated On April 10, 2019
• Attribution: You must attribute the work in the manner specified by the author or licensor (but not in
any way that suggests that they endorse you or your use of the work).
• Share Alike: If you alter, transform, or build upon this work, you may distribute the resulting work only
under the same, similar or a compatible license. For any reuse or distribution, you must make clear to
others the license terms of this work.
The information in this book is distributed on an “as is” basis, without warranty. Although every precaution
has been taken in the preparation of this work, neither the author(s) nor SensioLabs shall have any liability to
any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by
the information contained in this work.
Contents at a Glance
Introduction .......................................................................................................................................5
Usage .................................................................................................................................................7
Middleware ......................................................................................................................................20
Organizing Controllers......................................................................................................................23
Services.............................................................................................................................................25
Providers ..........................................................................................................................................30
Testing .............................................................................................................................................35
Accepting a JSON Request Body .......................................................................................................39
Using PdoSessionStorage to store Sessions in the Database.................................................................41
Disabling CSRF Protection on a Form using the FormExtension.........................................................43
Using YAML to configure Validation .................................................................................................44
Making sub-Requests ........................................................................................................................45
Converting Errors to Exceptions........................................................................................................48
Using multiple Monolog Loggers.......................................................................................................50
How to Create a Custom Authentication System with Guard .............................................................52
Internals ...........................................................................................................................................55
Contributing.....................................................................................................................................57
Twig.................................................................................................................................................59
Asset ................................................................................................................................................63
Monolog...........................................................................................................................................65
Session .............................................................................................................................................68
Swiftmailer .......................................................................................................................................71
Locale...............................................................................................................................................74
Translation .......................................................................................................................................75
Validator ..........................................................................................................................................79
Form ................................................................................................................................................83
CSRF................................................................................................................................................87
HTTP Cache.....................................................................................................................................89
HTTP Fragment................................................................................................................................92
Security ............................................................................................................................................94
Remember Me ................................................................................................................................ 106
Serializer......................................................................................................................................... 108
Service Controllers .......................................................................................................................... 110
Var Dumper.................................................................................................................................... 113
Doctrine ......................................................................................................................................... 115
Routing .......................................................................................................................................... 118
Silex is a PHP microframework. It is built on the shoulders of Symfony1 and Pimple2 and also inspired by
Sinatra3.
Silex aims to be:
In a nutshell, you define controllers and map them to routes, all in one step.
Usage
Listing 1-1 1 <?php
2
3 // web/index.php
4 require_once __DIR__.'/../vendor/autoload.php';
5
6 $app = new Silex\Application();
7
8 $app->get('/hello/{name}', function ($name) use ($app) {
9 return 'Hello '.$app->escape($name);
10 });
11
12 $app->run();
All that is needed to get access to the Framework is to include the autoloader.
Next, a route for /hello/{name} that matches for GET requests is defined. When the route matches,
the function is executed and the return value is sent back to the client.
1. https://symfony.com/
2. https://pimple.symfony.com/
3. http://www.sinatrarb.com/
Installation
If you want to get started fast, use the Silex Skeleton1:
Web Server
All examples in the documentation rely on a well-configured web server; read the webserver
documentation to check yours.
Bootstrap
To bootstrap Silex, all you need to do is require the vendor/autoload.php file and create an instance
of Silex\Application. After your controller definitions, call the run method on your application:
1. https://github.com/silexphp/Silex-Skeleton
2. https://getcomposer.org/
When developing a website, you might want to turn on the debug mode to ease debugging:
$app['debug'] = true;
Listing 2-4
If your application is hosted behind a reverse proxy at address $ip, and you want Silex to trust the
X-Forwarded-For* headers, you will need to run your application like this:
use Symfony\Component\HttpFoundation\Request;
Listing 2-5
Request::setTrustedProxies(array($ip));
$app->run();
Routing
In Silex you define a route and the controller that is called when that route is matched. A route pattern
consists of:
• Pattern: The route pattern defines a path that points to a resource. The pattern can include variable
parts and you are able to set RegExp requirements for them.
• Method: One of the following HTTP methods: GET, POST, PUT, DELETE, PATCH, or OPTIONS. This describes
the interaction with the resource.
Listing 2-6
function () {
// ... do something
}
The return value of the closure becomes the content of the page.
Dynamic Routing
Now, you can create another controller for viewing individual blog posts:
This route definition has a variable {id} part which is passed to the closure.
The current Application is automatically injected by Silex to the Closure thanks to the type hinting.
When the post does not exist, you are using abort() to stop the request early. It actually throws an
exception, which you will see how to handle later on.
It is pretty straightforward.
The current request is automatically injected by Silex to the Closure thanks to the type hinting. It is an
instance of Request3, so you can fetch variables using the request get method.
Instead of returning a string you are returning an instance of Response4. This allows setting an HTTP
status code, in this case it is set to 201 Created.
Silex always uses a Response internally, it converts strings to responses with status code 200.
3. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Request.html
4. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Response.html
Forms in most web browsers do not directly support the use of other HTTP methods. To use
methods other than GET and POST you can utilize a special form field with a name of _method.
The form's method attribute must be set to POST when using this field:
Request::enableHttpMethodParameterOverride();
$app->run();
You can also call match, which will match all methods. This can be restricted via the method method:
The order in which the routes are defined is significant. The first matching route will be used, so
place more generic routes at the bottom.
Route Variables
As it has been shown before you can define variable parts in a route like this:
Listing 2-14
$app->get('/blog/{id}', function ($id) {
// ...
});
Listing 2-15
$app->get('/blog/{postId}/{commentId}', function ($postId, $commentId) {
// ...
});
While it's not recommended, you could also do this (note the switched arguments):
Listing 2-16
$app->get('/blog/{postId}/{commentId}', function ($commentId, $postId) {
// ...
});
You can also ask for the current Request and Application objects:
Listing 2-17
$app->get('/blog/{id}', function (Application $app, Request $request, $id) {
// ...
});
Note for the Application and Request objects, Silex does the injection based on the type hinting and
not on the variable name:
$app->get('/blog/{id}', function (Application $foo, Request $bar, $id) {
Listing 2-18
// ...
});
Listing 2-19
$app->get('/user/{id}', function ($id) {
// ...
})->convert('id', function ($id) { return (int) $id; });
This is useful when you want to convert route variables to objects as it allows to reuse the conversion
code across different controllers:
The converter callback also receives the Request as its second argument:
A converter can also be defined as a service. For example, here is a user converter based on Doctrine
ObjectManager:
Listing 2-22
The service will now be registered in the application, and the convert() method will be used as
converter (using the syntax service_name:method_name):
Requirements
In some cases you may want to only match certain expressions. You can define requirements using
regular expressions by calling assert on the Controller object, which is returned by the routing
methods.
The following will make sure the id argument is a positive integer, since \d+ matches any amount of
digits:
Listing 2-24
$app->get('/blog/{id}', function ($id) {
// ...
})
->assert('id', '\d+');
Conditions
Besides restricting route matching based on the HTTP method or parameter requirements, you can set
conditions on any part of the request by calling when on the Controller object, which is returned by
the routing methods:
Listing 2-26
$app->get('/blog/{id}', function ($id) {
// ...
The when argument is a Symfony Expression5 , which means that you need to add symfony/
expression-language as a dependency of your project.
Default Values
You can define a default value for any route variable by calling value on the Controller object:
Listing 2-27
$app->get('/{pageName}', function ($pageName) {
// ...
})
->value('pageName', 'index');
This will allow matching /, in which case the pageName variable will have the value index.
Named Routes
Some providers can make use of named routes. By default Silex will generate an internal route name for
you but you can give an explicit route name by calling bind:
Controllers as Classes
Instead of anonymous functions, you can also define your controllers as methods. By using the
ControllerClass::methodName syntax, you can tell Silex to lazily create the controller object for
you:
This will load the Acme\Foo class on demand, create an instance and call the bar method to get the
response. You can use Request and Silex\Application type hints to get $request and $app
injected.
It is also possible to define your controllers as services.
5. https://symfony.com/doc/current/book/routing.html#completely-customized-route-matching-with-conditions
These settings are applied to already registered controllers and they become the defaults for new
controllers.
The global configuration does not apply to controller providers you might mount as they have their
own global configuration (read the dedicated chapter for more information).
Error Handlers
When an exception is thrown, error handlers allow you to display a custom error page to the user. They
can also be used to do additional things, such as logging.
To register an error handler, pass a closure to the error method which takes an Exception argument
and returns a response:
You can also check for specific errors by using the $code argument, and handle them differently:
You can restrict an error handler to only handle some Exception classes by setting a more specific type
hint for the Closure argument:
As Silex ensures that the Response status code is set to the most appropriate one depending on the
exception, setting the status on the response alone won't work.
If you want to overwrite the status code, which you should not without a good reason, set the X-
Status-Code header (on Symfony until version 3.2):
return new Response('Error', 404 /* ignored */, array('X-Status-Code' => 200));
Listing 2-34
If you want to use a separate error handler for logging, make sure you register it with a higher priority
than response error handlers, because once a response is returned, the following handlers are ignored.
Silex ships with a provider for Monolog6 which handles logging of errors. Check out the Providers
chapter for details.
Silex comes with a default error handler that displays a detailed error message with the stack
trace when debug is true, and a simple error message otherwise. Error handlers registered via the
error() method always take precedence but you can keep the nice error messages when debug is
turned on like this:
The error handlers are also called when you use abort to abort a request early:
6. https://github.com/Seldaek/monolog
You can convert errors to Exceptions, check out the cookbook chapter for details.
View Handlers
View Handlers allow you to intercept a controller result that is not a Response and transform it before
it gets returned to the kernel.
To register a view handler, pass a callable (or string that can be resolved to a callable) to the view()
method. The callable should accept some sort of result from the controller:
Listing 2-38
$app->view(function (array $controllerResult) use ($app) {
return $app->json($controllerResult);
});
View Handlers also receive the Request as their second argument, making them a good candidate for
basic content negotiation:
View Handlers will be examined in the order they are added to the application and Silex will use type
hints to determine if a view handler should be used for the current result, continuously using the return
value of the last view handler as the input for the next.
You must ensure that Silex receives a Response or a string as the result of the last view handler (or
controller) to be run.
Redirects
You can redirect to another page by returning a RedirectResponse response, which you can create by
calling the redirect method:
Listing 2-40
$app->get('/', function () use ($app) {
return $app->redirect('/hello');
});
You can also generate the URI via the built-in URL generator:
$request = Request::create($app['url_generator']->generate('hello'), 'GET');
Listing 2-42
There's some more things that you need to keep in mind though. In most cases you will want to forward
some parts of the current master request to the sub-request. That includes: Cookies, server information,
session. Read more on how to make sub-requests.
JSON
If you want to return JSON data, you can use the json helper method. Simply pass it your data, status
code and headers, and it will create a JSON response for you:
Streaming
It's possible to stream a response, which is important in cases when you don't want to buffer the data
being sent:
Sending a file
If you want to return a file, you can use the sendFile helper method. It eases returning files that would
otherwise not be publicly available. Simply pass it your file path, status code, headers and the content
disposition and it will create a BinaryFileResponse response for you:
To further customize the response before returning it, check the API doc for
SymfonyComponentHttpFoundationBinaryFileResponse7:
Listing 2-47
return $app
->sendFile('/base/path/' . $path)
->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'pic.jpg')
;
Traits
Silex comes with PHP traits that define shortcut methods.
Almost all built-in service providers have some corresponding PHP traits. To use them, define your own
Application class and include the traits you want:
You can also define your own Route class and use some traits:
7. https://api.symfony.com/master/Symfony/Component/HttpFoundation/BinaryFileResponse.html
Listing 2-50
$app['route_class'] = 'MyRoute';
Read each provider chapter to learn more about the added methods.
Security
Make sure to protect your application against attacks.
Escaping
When outputting any user input, make sure to escape it correctly to prevent Cross-Site-Scripting attacks.
• Escaping HTML: PHP provides the htmlspecialchars function for this. Silex provides a
shortcut escape method:
If you use the Twig template engine, you should use its escaping or even auto-escaping mechanisms.
Check out the Providers chapter for details.
• Escaping JSON: If you want to provide data in JSON format you should use the Silex json
function:
Silex allows you to run code, that changes the default Silex behavior, at different stages during the
handling of a request through middleware:
Application Middleware
Application middleware is only run for the "master" Request.
Before Middleware
A before application middleware allows you to tweak the Request before the controller is executed:
Listing 3-1
$app->before(function (Request $request, Application $app) {
// ...
});
By default, the middleware is run after the routing and the security.
If you want your middleware to be run even if an exception is thrown early on (on a 404 or 403 error for
instance), then, you need to register it as an early event:
Listing 3-2
$app->before(function (Request $request, Application $app) {
// ...
}, Application::EARLY_EVENT);
In this case, the routing and the security won't have been executed, and so you won't have access to the
locale, the current route, or the security user.
Listing 3-3
$app->after(function (Request $request, Response $response) {
// ...
});
Finish Middleware
A finish application middleware allows you to execute tasks after the Response has been sent to the client
(like sending emails or logging):
Listing 3-4
$app->finish(function (Request $request, Response $response) {
// ...
// Warning: modifications to the Request or Response will be ignored
});
Route Middleware
Route middleware is added to routes or route collections and it is only triggered when the corresponding
route is matched. You can also stack them:
Before Middleware
A before route middleware is fired just before the route callback, but after the before application
middleware:
Listing 3-7 1 $after = function (Request $request, Response $response, Application $app) {
2 // ...
3 };
4
5 $app->get('/somewhere', function () {
6 // ...
7 })
8 ->after($after);
Middleware Priority
You can add as much middleware as you want, in which case they are triggered in the same order as you
added them.
You can explicitly control the priority of your middleware by passing an additional argument to the
registration methods:
Listing 3-8
$app->before(function (Request $request) {
// ...
}, 32);
As a convenience, two constants allow you to register an event as early as possible or as late as possible:
When your application starts to define too many controllers, you might want to group them logically:
mount() prefixes all routes with the given prefix and merges them into the main Application. So, / will
map to the main home page, /blog/ to the blog home page, /forum/ to the forum home page, and
/admin/blog/ to the admin blog home page.
When calling get(), match(), or any other HTTP methods on the Application, you are in
fact calling them on a default instance of ControllerCollection (stored in
$app['controllers']).
Another benefit is the ability to apply settings on a set of controllers very easily. Building on the example
from the middleware section, here is how you would secure all controllers for the backend collection:
Listing 4-2
$backend = $app['controllers_factory'];
For a better readability, you can split each controller collection into a separate file:
Silex is not only a framework, it is also a service container. It does this by extending Pimple1 which
provides a very simple service container.
Dependency Injection
You can skip this if you already know what Dependency Injection is.
Dependency Injection is a design pattern where you pass dependencies to services instead of creating
them from within the service or relying on globals. This generally leads to code that is decoupled, re-
usable, flexible and testable.
Here is an example of a class that takes a User object and stores it as a file in JSON format:
1. https://pimple.symfony.com
Pimple
Pimple makes strong use of closures and implements the ArrayAccess interface.
We will start off by creating a new instance of Pimple -- and because Silex\Application extends
Pimple\Container all of this applies to Silex as well:
Listing 5-2
$container = new Pimple\Container();
or:
Listing 5-3
$app = new Silex\Application();
Parameters
You can set parameters (which are usually strings) by setting an array key on the container:
Listing 5-4
$app['some_parameter'] = 'value';
The array key can be any value. By convention dots are used for namespacing:
Listing 5-5
$app['asset.host'] = 'http://cdn.mysite.com/';
Listing 5-6
echo $app['some_parameter'];
Service definitions
Defining services is no different than defining parameters. You just set an array key on the container to
be a closure. However, when you retrieve the service, the closure is executed. This allows for lazy service
creation:
Listing 5-7
$app['some_service'] = function () {
return new Service();
};
Listing 5-8
$service = $app['some_service'];
On first invocation, this will create the service; the same instance will then be returned on any subsequent
access.
Factory services
If you want a different instance to be returned for each service access, wrap the service definition with the
factory() method:
Listing 5-9
$app['some_service'] = $app->factory(function () {
return new Service();
});
Listing 5-10
$app['some_service'] = function ($app) {
return new Service($app['some_other_service'], $app['some_service.config']);
};
Listing 5-11
$app['user.persist_path'] = '/tmp/users';
$app['user.persister'] = function ($app) {
return new JsonUserPersister($app['user.persist_path']);
};
Protected closures
Because the container sees closures as factories for services, it will always execute them when reading
them.
In some cases you will however want to store a closure as a parameter, so that you can fetch it and execute
it yourself -- with your own arguments.
This is why Pimple allows you to protect your closures from being executed, by using the protect
method:
Note that the container is not provided as an argument to protected closures. However, you can inject it
via use($app):
Listing 5-13
$app['closure_parameter'] = $app->protect(function ($a, $b) use ($app) {
// ...
});
You can use this pattern to add functionality to :doc:Twig <providers/twig> for example:
Core services
Silex defines a range of services.
• request_stack: Controls the lifecycle of requests, an instance of RequestStack2. It gives you access
to GET, POST parameters and lots more!
Example usage:
Listing 5-15
$id = $app['request_stack']->getCurrentRequest()->get('id');
A request is only available when a request is being served; you can only access it from within a
controller, an application before/after middlewares, or an error handler.
• routes: The RouteCollection3 that is used internally. You can add, modify, read routes.
• url_generator: An instance of UrlGenerator4, using the RouteCollection5 that is provided through
the routes service. It has a generate method, which takes the route name as an argument,
followed by an array of route parameters.
• controllers: The Silex\ControllerCollection that is used internally. Check the Internals
chapter for more information.
• dispatcher: The EventDispatcher6 that is used internally. It is the core of the Symfony system and is
used quite a bit by Silex.
• resolver: The ControllerResolver7 that is used internally. It takes care of executing the controller
with the right arguments.
• kernel: The HttpKernel8 that is used internally. The HttpKernel is the heart of Symfony, it takes a
Request as input and returns a Response as output.
• request_context: The request context is a simplified representation of the request that is used by
the router and the URL generator.
• exception_handler: The Exception handler is the default handler that is used when you don't
register one via the error() method or if your handler does not return a Response. Disable it with
unset($app['exception_handler']).
2. https://api.symfony.com/master/Symfony/Component/HttpFoundation/RequestStack.html
3. https://api.symfony.com/master/Symfony/Component/Routing/RouteCollection.html
4. https://api.symfony.com/master/Symfony/Component/Routing/Generator/UrlGenerator.html
5. https://api.symfony.com/master/Symfony/Component/Routing/RouteCollection.html
6. https://api.symfony.com/master/Symfony/Component/EventDispatcher/EventDispatcher.html
7. https://api.symfony.com/master/Symfony/Component/HttpKernel/Controller/ControllerResolver.html
8. https://api.symfony.com/master/Symfony/Component/HttpKernel/HttpKernel.html
Core traits
• Silex\Application\UrlGeneratorTrait adds the following shortcuts:
Core parameters
• request.http_port (optional): Allows you to override the default port for non-HTTPS URLs. If the
current request is HTTP, it will always use the current port.
Defaults to 80.
This parameter can be used when generating URLs.
• request.https_port (optional): Allows you to override the default port for HTTPS URLs. If the
current request is HTTPS, it will always use the current port.
Defaults to 443.
This parameter can be used when generating URLs.
• debug (optional): Returns whether or not the application is running in debug mode.
Defaults to false.
• charset (optional): The charset to use for Responses.
Defaults to UTF-8.
9. https://github.com/php-fig/log/blob/master/Psr/Log/LoggerInterface.php
Providers allow the developer to reuse parts of an application into another one. Silex provides two
types of providers defined by two interfaces: ServiceProviderInterface for services and
ControllerProviderInterface for controllers.
Service Providers
Loading providers
In order to load and use a service provider, you must register it on the application:
Listing 6-1
$app = new Silex\Application();
$app->register(new Acme\DatabaseServiceProvider());
You can also provide some parameters as a second argument. These will be set after the provider is
registered, but before it is booted:
Conventions
You need to watch out in what order you do certain things when interacting with providers. Just keep
these rules in mind:
• AssetServiceProvider
• CsrfServiceProvider
• DoctrineServiceProvider
• FormServiceProvider
• HttpCacheServiceProvider
• HttpFragmentServiceProvider
• LocaleServiceProvider
• MonologServiceProvider
• RememberMeServiceProvider
• SecurityServiceProvider
• SerializerServiceProvider
• ServiceControllerServiceProvider
• SessionServiceProvider
• SwiftmailerServiceProvider
• TranslationServiceProvider
• TwigServiceProvider
• ValidatorServiceProvider
• VarDumperServiceProvider
The Silex core team maintains a WebProfiler1 provider that helps debug code in the development
environment thanks to the Symfony web debug toolbar and the Symfony profiler.
Creating a provider
Providers must implement the Pimple\ServiceProviderInterface:
Listing 6-3
interface ServiceProviderInterface
{
public function register(Container $container);
}
This is very straight forward, just create a new class that implements the register method. In the
register() method, you can define services on the application which then may make use of other
services and parameters.
1. https://github.com/silexphp/Silex-WebProfiler
2. https://github.com/silexphp/Silex/wiki/Third-Party-ServiceProviders-for-Silex-2.x
Listing 6-4
interface BootableProviderInterface
{
function boot(Application $app);
}
Listing 6-5
interface EventListenerProviderInterface
{
function subscribe(Container $app, EventDispatcherInterface $dispatcher);
}
This class provides a hello service which is a protected closure. It takes a name argument and will return
hello.default_name if no name is given. If the default is also missing, it will use an empty string.
You can now use this provider as follows:
In this example we are getting the name parameter from the query string, so the request path would have
to be /hello?name=Fabien.
Controller Providers
Loading providers
In order to load and use a controller provider, you must "mount" its controllers under a path:
Listing 6-8
$app = new Silex\Application();
All controllers defined by the provider will now be available under the /blog path.
Creating a provider
Providers must implement the Silex\Api\ControllerProviderInterface:
Listing 6-9
interface ControllerProviderInterface
{
public function connect(Application $app);
}
Listing 6-11
$app = new Silex\Application();
In this example, the /blog/ path now references the controller defined in the provider.
You can also define a provider that implements both the service and the controller provider interface
and package in the same class the services needed to make your controllers work.
Because Silex is built on top of Symfony, it is very easy to write functional tests for your application.
Functional tests are automated software tests that ensure that your code is working correctly. They go
through the user interface, using a fake browser, and mimic the actions a user would do.
Why
If you are not familiar with software tests, you may be wondering why you would need this. Every time
you make a change to your application, you have to test it. This means going through all the pages and
making sure they are still working. Functional tests save you a lot of time, because they enable you to test
your application in usually under a second by running a single command.
For more information on functional testing, unit testing, and automated software tests in general, check
out PHPUnit1 and Bulat Shakirzyanov's talk on Clean Code.
PHPUnit
PHPUnit2 is the de-facto standard testing framework for PHP. It was built for writing unit tests, but
it can be used for functional tests too. You write tests by creating a new class, that extends the
PHPUnit\Framework\TestCase. Your test cases are methods prefixed with test:
Listing 7-1 1 use PHPUnit\Framework\TestCase;
2
3 class ContactFormTest extends TestCase
4 {
5 public function testInitialPage()
6 {
7 ...
8 }
9 }
1. https://github.com/sebastianbergmann/phpunit
2. https://github.com/sebastianbergmann/phpunit
Here you see some of the available assertions. There is a full list available in the Writing Tests for
PHPUnit3 section of the PHPUnit documentation.
WebTestCase
Symfony provides a WebTestCase class that can be used to write functional tests. The Silex version of
this class is Silex\WebTestCase, and you can use it by making your test extend it:
If you need to override the setUp() method, don't forget to call the parent (parent::setUp())
to call the Silex default setup.
If you want to use the Symfony WebTestCase class you will need to explicitly install its
dependencies for your project:
For your WebTestCase, you will have to implement a createApplication method, which returns
your application instance:
Make sure you do not use require_once here, as this method will be executed before every test.
3. https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html
The WebTestCase provides a createClient method. A client acts as a browser, and allows you to
interact with your application. Here's how it works:
There are several things going on here. You have both a Client and a Crawler.
You can also access the application through $this->app.
Client
The client represents a browser. It holds your browsing history, cookies and more. The request method
allows you to make a request to a page on your application.
You can find some documentation for it in the client section of the testing chapter of the Symfony
documentation.
Crawler
The crawler allows you to inspect the content of a page. You can filter it using CSS expressions and lots
more.
Configuration
The suggested way to configure PHPUnit is to create a phpunit.xml.dist file, a tests folder and
your tests in tests/YourApp/Tests/YourTest.php. The phpunit.xml.dist file should look
like this:
Now, when running phpunit on the command line, tests should run.
A common need when building a restful API is the ability to accept a JSON encoded entity from the
request body.
An example for such an API could be a blog post creation.
Example API
In this example we will create an API for creating a blog post. The following is a spec of how we want it
to work.
Request
In the request we send the data for the blog post as a JSON object. We also indicate that using the
Content-Type header:
Listing 8-1 1 POST /blog/posts
2 Accept: application/json
3 Content-Type: application/json
4 Content-Length: 57
5
6 {"title":"Hello World!","body":"This is my first post!"}
Response
The server responds with a 201 status code, telling us that the post was created. It tells us the Content-
Type of the response, which is also JSON:
Listing 8-2 1 HTTP/1.1 201 Created
2 Content-Type: application/json
3 Content-Length: 65
4 Connection: close
5
6 {"id":"1","title":"Hello World!","body":"This is my first post!"}
Controller implementation
Our controller will create a new blog post from the data provided and will return the post object,
including its id, as JSON:
Manual testing
In order to manually test our API, we can use the curl command line utility, which allows sending
HTTP requests:
1. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.html
2. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.html
PDF brought to you by Chapter 9: Using PdoSessionStorage to store Sessions in the Database | 41
Database structure
PdoSessionStorage needs a database table with 3 columns:
You can find examples of SQL statements to create the session table in the Symfony cookbook3
3. https://symfony.com/doc/current/cookbook/configuration/pdo_session_storage.html#example-sql-statements
PDF brought to you by Chapter 9: Using PdoSessionStorage to store Sessions in the Database | 42
The FormExtension provides a service for building form in your application with the Symfony Form
component. When the CSRF Service Provider is registered, the FormExtension uses the CSRF Protection
avoiding Cross-site request forgery, a method by which a malicious user attempts to make your legitimate
users unknowingly submit data that they don't intend to submit.
You can find more details about CSRF Protection and CSRF token in the Symfony Book1.
In some cases (for example, when embedding a form in an html email) you might want not to use this
protection. The easiest way to avoid this is to understand that it is possible to give specific options to
your form builder through the createBuilder() function.
Example
Listing 10-1 1 $form = $app['form.factory']->createBuilder('form', null, array('csrf_protection' => false));
That's it, your form could be submitted from everywhere without CSRF Protection.
Going further
This specific example showed how to change the csrf_protection in the $options parameter of
the createBuilder() function. More of them could be passed through this parameter, it is as simple
as using the Symfony getDefaultOptions() method in your form classes. See more here2.
1. https://symfony.com/doc/current/book/forms.html#csrf-protection
2. https://symfony.com/doc/current/book/forms.html#book-form-creating-form-classes
PDF brought to you by Chapter 10: Disabling CSRF Protection on a Form using the FormExtension | 43
Simplicity is at the heart of Silex so there is no out of the box solution to use YAML files for validation.
But this doesn't mean that this is not possible. Let's see how to do it.
First, you need to install the YAML Component:
Next, you need to tell the Validation Service that you are not using StaticMethodLoader to load your
class metadata but a YAML file:
Now, we can replace the usage of the static method and move all the validation rules to
validation.yml:
Listing 11-3 1 # validation.yml
2 Post:
3 properties:
4 title:
5 - NotNull: ~
6 - NotBlank: ~
7 body:
8 - Min: 100
Since Silex is based on the HttpKernelInterface, it allows you to simulate requests against your
application. This means that you can embed a page within another, it also allows you to forward a request
which is essentially an internal redirect that does not change the URL.
Basics
You can make a sub-request by calling the handle method on the Application. This method takes
three arguments:
• $request:
An instance of the Request class which represents the
HTTP request.
• $type:
Must be either HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST. Certain
listeners are only executed for the master request, so it's important that this is set to SUB_REQUEST.
• $catch: Catches exceptions and turns them into a response with status code 500. This argument
defaults to true. For sub-requests you will most likely want to set it to false.
There's some more things that you need to keep in mind though. In most cases you will want to forward
some parts of the current master request to the sub-request like cookies, server information, or the
session.
Here is a more advanced example that forwards said information ($request holds the master request):
To forward this response to the client, you can simply return it from a controller:
If you want to embed the response as part of a larger page you can call Response::getContent:
Listing 12-7
$url = $request->getUriForPath('/');
$subRequest = Request::create($url, 'GET', array(), $request->cookies->all(), array(), $request->server->all());
Silex catches exceptions that are thrown from within a request/response cycle. However, it does not catch
PHP errors and notices. This recipe tells you how to catch them by converting them to exceptions.
Listing 13-1
use Symfony\Component\Debug\ErrorHandler;
ErrorHandler::register();
Listing 13-2
use Symfony\Component\Debug\ExceptionHandler;
ExceptionHandler::register();
In production you may want to disable the debug output by passing false as the $debug argument:
Listing 13-3
use Symfony\Component\Debug\ExceptionHandler;
ExceptionHandler::register(false);
Having separate instances of Monolog for different parts of your system is often desirable and allows you
to configure them independently, allowing for fine grained control of where your logging goes and in
what detail.
This simple example allows you to quickly configure several monolog instances, using the bundled
handler, but each with a different channel.
As your application grows, or your logging needs for certain areas of the system become apparent,
it should be straightforward to then configure that particular service separately, including your
customizations.
Alternatively, you could attempt to make the factory more complicated, and rely on some conventions,
such as checking for an array of handlers registered with the container with the channel name, defaulting
to the bundled handler.
Listing 14-3
Whether you need to build a traditional login form, an API token authentication system or you need to
integrate with some proprietary single-sign-on system, the Guard component can make it easy... and fun!
In this example, you'll build an API token authentication system and learn how to work with Guard.
PDF brought to you by Chapter 15: How to Create a Custom Authentication System with Guard | 52
PDF brought to you by Chapter 15: How to Create a Custom Authentication System with Guard | 53
You can use many authenticators, they are executed by the order they are configured.
You did it! You now have a fully-working API token authentication system. If your homepage required
ROLE_USER, then you could test it under different conditions:
For more details read the Symfony cookbook entry on How to Create a Custom Authentication System
with Guard1.
1. https://symfony.com/doc/current/cookbook/security/guard-authentication.html
PDF brought to you by Chapter 15: How to Create a Custom Authentication System with Guard | 54
Silex
Application
The application is the main interface to Silex. It implements Symfony's HttpKernelInterface1, so you can
pass a Request2 to the handle method and it will return a Response3.
It extends the Pimple service container, allowing for flexibility on the outside as well as the inside. You
could replace any service, and you are also able to read them.
The application makes strong use of the EventDispatcher4 to hook into the Symfony HttpKernel5 events.
This allows fetching the Request, converting string responses into Response objects and handling
Exceptions. We also use it to dispatch some custom events like before/after middlewares and errors.
Controller
The Symfony Route6 is actually quite powerful. Routes can be named, which allows for URL generation.
They can also have requirements for the variable parts. In order to allow setting these through a nice
interface, the match method (which is used by get, post, etc.) returns an instance of the Controller,
which wraps a route.
1. https://api.symfony.com/master/Symfony/Component/HttpKernel/HttpKernelInterface.html
2. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Request.html
3. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Response.html
4. https://api.symfony.com/master/Symfony/Component/EventDispatcher/EventDispatcher.html
5. https://api.symfony.com/master/Symfony/Component/HttpKernel/HttpKernel.html
6. https://api.symfony.com/master/Symfony/Component/Routing/Route.html
Symfony
Following Symfony components are used by Silex:
7. https://api.symfony.com/master/Symfony/Component/Routing/RouteCollection.html
8. https://symfony.com/
We are open to contributions to the Silex code. If you find a bug or want to contribute a provider, just
follow these steps:
Any code you contribute must be licensed under the MIT License.
1. https://github.com/silexphp/Silex
2. https://help.github.com/articles/creating-a-pull-request
3. http://docutils.sourceforge.net/rst.html
4. http://sphinx-doc.org
Parameters
• twig.path (optional): Path to the directory containing twig template files (it can also be an array of
paths).
• twig.templates (optional): An associative array of template names to template contents. Use this if
you want to define your templates inline.
• twig.options (optional): An associative array of twig options. Check out the twig documentation2
for more information.
• twig.form.templates (optional): An array of templates used to render forms (only available when
the FormServiceProvider is enabled). The default theme is form_div_layout.html.twig, but you can use
the other built-in themes: form_table_layout.html.twig, bootstrap_3_layout.html.twig, and
bootstrap_3_horizontal_layout.html.twig.
• twig.date.format (optional): Default format used by the date filter. The format string must conform
to the format accepted by date()3.
• twig.date.interval_format (optional): Default format used by the date filter when the filtered
data is of type DateInterval4. The format string must conform to the format accepted by
DateInterval::format()5.
• twig.date.timezone (optional): Default timezone used when formatting dates. If set to null the
timezone returned by date_default_timezone_get()6 is used.
• twig.number_format.decimals (optional): Default number of decimals displayed by the
number_format filter.
• twig.number_format.decimal_point (optional): Default separator for the decimal point used by
the number_format filter.
1. https://twig.symfony.com/
2. https://twig.symfony.com/doc/api.html#environment-options
3. https://secure.php.net/date
4. https://secure.php.net/DateInterval
5. https://secure.php.net/DateInterval.format
6. https://secure.php.net/date_default_timezone_get
Services
• twig: The Twig_Environment instance. The main way of interacting with Twig.
• twig.loader: The loader for Twig templates which uses the twig.path and the twig.templates options.
You can also replace the loader completely.
Registering
Listing 19-1 1 $app->register(new Silex\Provider\TwigServiceProvider(), array(
2 'twig.path' => __DIR__.'/views',
3 ));
Usage
The Twig provider provides a twig service that can render templates:
When present, the TwigServiceProvider will provide you with the following additional capabilities.
• Access to the path() and url() functions. You can find more information in the Symfony Routing
documentation7:
7. https://symfony.com/doc/current/book/routing.html#generating-urls-from-a-template
Form Support
If you are using the FormServiceProvider, you will get a set of helpers for working with forms in
templates. You can find more information in the Symfony Forms reference.
Security Support
If you are using the SecurityServiceProvider, you will have access to the is_granted()
function in templates. You can find more information in the Symfony Security documentation9.
Global Variable
When the Twig bridge is available, the global variable refers to an instance of AppVariable11. It gives
access to the following methods:
Rendering a Controller
A render function is also registered to help you render another controller from a template (available
when the HttpFragment Service Provider is registered):
8. https://symfony.com/doc/current/book/translation.html#twig-templates
9. https://symfony.com/doc/current/book/security.html#access-control-in-templates
10. https://symfony.com/doc/current/components/weblink/introduction.html
11. https://api.symfony.com/master/Symfony/Bridge/Twig/AppVariable.html
Read the Twig reference12 for Symfony document to learn more about the various Twig functions.
Traits
Silex\Application\TwigTrait adds the following shortcuts:
• render: Renders a view with the given parameters and returns a Response object.
• renderView: Renders a view with the given parameters and returns a string.
Customization
You can configure the Twig environment before using it by extending the twig service:
12. https://symfony.com/doc/current/reference/twig_reference.html#controller
13. https://twig.symfony.com
The AssetServiceProvider provides a way to manage URL generation and versioning of web assets such as
CSS stylesheets, JavaScript files and image files.
Parameters
• assets.version: Default version for assets.
• assets.version_format (optional): Default format for assets.
• assets.base_path: Default path to prepend to all assets without a package.
• assets.base_urls: (Alternative to assets.base_path) List of base URLs to choose from to prepend to
assets without a package.
• assets.named_packages (optional): Named packages. Keys are the package names and values the
configuration (supported keys are version, version_format, base_urls, and base_path).
• assets.json_manifest_path (optional): Absolute path to a JSON version manifest1.
Services
• assets.packages: The asset service.
Registering
Listing 20-1 1 $app->register(new Silex\Provider\AssetServiceProvider(), array(
2 'assets.version' => 'v1',
3 'assets.version_format' => '%s?version=%s',
4 'assets.named_packages' => array(
5 'css' => array('version' => 'css2', 'base_path' => '/whatever-makes-sense'),
6 'images' => array('base_urls' => array('https://img.example.com')),
7 ),
8 ));
1. https://symfony.com/blog/new-in-symfony-3-3-manifest-based-asset-versioning
If you want to use assets in your Twig templates, you must also install the Symfony Twig Bridge:
Usage
The AssetServiceProvider is mostly useful with the Twig provider using the asset() method. It takes
two arguments. In the case of named packages, the first is the path relative to the base_path specified in
the package definition and the second is the package name. For unmamed packages, there is only one
argument, the path relative to the assets folder:
2. https://symfony.com/doc/current/components/asset/introduction.html
The MonologServiceProvider provides a default logging mechanism through Jordi Boggiano's Monolog1
library.
It will log requests and errors and allow you to add logging to your application. This allows you to debug
and monitor the behaviour, even in production.
Parameters
• monolog.logfile: File where logs are written to.
• monolog.bubble (optional): Whether the messages that are handled can bubble up the stack or
not.
• monolog.permission (optional): File permissions default (null), nothing change.
• monolog.level (optional): Level of logging, defaults to DEBUG. Must be one of Logger::DEBUG,
Logger::INFO, Logger::WARNING, Logger::ERROR. DEBUG will log everything, INFO will
log everything except DEBUG, etc.
In addition to the Logger:: constants, it is also possible to supply the level in string form, for
example: "DEBUG", "INFO", "WARNING", "ERROR".
PSR-3 log levels from \Psr\Log\LogLevel:: constants are also supported.
• monolog.name (optional): Name of the monolog channel, defaults to myapp.
• monolog.exception.logger_filter (optional): An anonymous function that returns an error level
for on uncaught exception that should be logged.
• monolog.use_error_handler (optional): Whether errors and uncaught exceptions should be
handled by the Monolog ErrorHandler class and added to the log. By default the error handler is
enabled unless the application debug parameter is set to true.
Please note that enabling the error handler may silence some errors, ignoring the PHP
display_errors configuration setting.
1. https://github.com/Seldaek/monolog
Listing 21-1
$app['monolog']->debug('Testing the Monolog logging.');
Registering
Listing 21-2 1 $app->register(new Silex\Provider\MonologServiceProvider(), array(
2 'monolog.logfile' => __DIR__.'/development.log',
3 ));
Usage
The MonologServiceProvider provides a monolog service. You can use it to add log entries for any
logging level through debug(), info(), warning() and error():
Customization
You can configure Monolog (like adding or changing the handlers) before using it by extending the
monolog service:
Listing 21-5 1 $app->extend('monolog', function($monolog, $app) {
2 $monolog->pushHandler(...);
3
4 return $monolog;
5 });
By default, all requests, responses and errors are logged by an event listener registered as a service called
monolog.listener. You can replace or remove this service if you want to modify or disable the logged
information.
2. https://github.com/Seldaek/monolog
The SessionServiceProvider provides a service for storing data persistently between requests.
Parameters
• session.storage.save_path (optional): The path for the NativeFileSessionHandler, defaults
to the value of sys_get_temp_dir().
• session.storage.options: An array of options that is passed to the constructor of the
session.storage service.
In case of the default NativeSessionStorage1, the most useful options are:
However, all of these are optional. Default Sessions life time is 1800 seconds (30 minutes). To
override this, set the lifetime option.
For a full list of available options, read the PHP2 official documentation.
• session.test: Whether to simulate sessions or not (useful when writing functional tests).
• session.attribute_bag (optional): The attribute bag service to use in the session. Instance of
AttributeBagInterface.
• session.flash_bag (optional): The flash bag service to use in the session. Instance of
FlashBagInterface.
1. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.html
2. https://secure.php.net/session.configuration
Registering
Listing 22-1 1 $app->register(new Silex\Provider\SessionServiceProvider());
Using Handlers
The default session handler is NativeFileSessionHandler. However, there are multiple handlers
available for use by setting session.storage.handler to an instance of one of the following handler
objects:
• LegacyPdoSessionHandler5
• MemcacheSessionHandler6
• MemcachedSessionHandler7
• MongoDbSessionHandler8
• NativeFileSessionHandler9
• NativeSessionHandler10
• NullSessionHandler11
• PdoSessionHandler12
• WriteCheckSessionHandler13
Usage
The Session provider provides a session service. Here is an example that authenticates a user and
creates a session for them:
3. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Session.html
4. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.html
5. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/LegacyPdoSessionHandler.html
6. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.html
7. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.html
8. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/MongoDbSessionHandler.html
9. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.html
10. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.html
11. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.html
12. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.html
13. https://api.symfony.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/WriteCheckSessionHandler.html
The SwiftmailerServiceProvider provides a service for sending email through the Swift Mailer1 library.
You can use the mailer service to send messages easily. By default, it will attempt to send emails
through SMTP.
Parameters
• swiftmailer.use_spool: A boolean to specify whether or not to use the memory spool, defaults to
true.
• swiftmailer.options: An array of options for the default SMTP-based configuration.
The following options can be set:
Example usage:
1. https://swiftmailer.symfony.com
Services
• mailer: The mailer instance.
Example usage:
Registering
Listing 23-4 1 $app->register(new Silex\Provider\SwiftmailerServiceProvider());
Usage
The Swiftmailer provider provides a mailer service:
Listing 23-6
Usage in commands
By default, the Swiftmailer provider sends the emails using the KernelEvents::TERMINATE event,
which is fired after the response has been sent. However, as this event isn't fired for console commands,
your emails won't be sent.
For that reason, if you send emails using a command console, it is recommended that you disable the use
of the memory spool (before accessing $app['mailer']):
Listing 23-7
$app['swiftmailer.use_spool'] = false;
Alternatively, you can just make sure to flush the message spool by hand before ending the command
execution. To do so, use the following code:
Listing 23-8
$app['swiftmailer.spooltransport']
->getSpool()
->flushQueue($app['swiftmailer.transport'])
;
Traits
Silex\Application\SwiftmailerTrait adds the following shortcuts:
• mail: Sends an email.
2. https://swiftmailer.symfony.com
Parameters
• locale: The locale of the user. When set before any request handling, it defines the default locale (en
by default). When a request is being handled, it is automatically set according to the _locale request
attribute of the current route.
Services
• n/a
Registering
Listing 24-1 1 $app->register(new Silex\Provider\LocaleServiceProvider());
The TranslationServiceProvider provides a service for translating your application into different
languages.
Parameters
• translator.domains (optional): A mapping of domains/locales/messages. This parameter contains
the translation data for all languages and domains.
• locale (optional): The locale for the translator. You will most likely want to set this based on some
request parameter. Defaults to en.
• locale_fallbacks (optional): Fallback locales for the translator. It will be used when the current
locale has no messages set. Defaults to en.
• translator.cache_dir (optional): Defines the cache directory if you want translations to be cached.
Services
• translator: An instance of Translator1, that is used for translation.
• translator.loader: An instance of an implementation of the translation LoaderInterface2, defaults
to an ArrayLoader3.
• translator.message_selector: An instance of MessageSelector4.
Registering
Listing 25-1 1 $app->register(new Silex\Provider\LocaleServiceProvider());
2 $app->register(new Silex\Provider\TranslationServiceProvider(), array(
1. https://api.symfony.com/master/Symfony/Component/Translation/Translator.html
2. https://api.symfony.com/master/Symfony/Component/Translation/Loader/LoaderInterface.html
3. https://api.symfony.com/master/Symfony/Component/Translation/Loader/ArrayLoader.html
4. https://api.symfony.com/master/Symfony/Component/Translation/MessageSelector.html
Usage
The Translation provider provides a translator service and makes use of the translator.domains
parameter:
Using Resources
When translations are stored in a file, you can load them as follows:
Traits
Silex\Application\TranslationTrait adds the following shortcuts:
• trans: Translates the given message.
• transChoice: Translates the given choice message by choosing a translation according to a number.
Recipes
YAML-based language files
Having your translations in PHP files can be inconvenient. This recipe will show you how to load
translations from external YAML files.
First, add the Symfony Config and Yaml components as dependencies:
Next, you have to create the language mappings in YAML files. A naming you can use is locales/
en.yml. Just do the mapping in this file as follows:
Listing 25-7 1 hello: Hello %name%
2 goodbye: Goodbye %name%
Then, register the YamlFileLoader on the translator and add all your translation files:
Listing 25-9
The ValidatorServiceProvider provides a service for validating data. It is most useful when used with the
FormServiceProvider, but can also be used standalone.
Parameters
• validator.validator_service_ids (optional): An array of service names representing validators.
• validator.translation_domain (optional): The translation domain to use for translating validator
messages. (Defaults to validators.)
• validator.object_initializers (optional): An array of object initializers. See the relevant Validation
documentation1.
Services
• validator: An instance of Validator2.
• validator.mapping.class_metadata_factory: Factory for metadata loaders, which can read
validation constraint information from classes. Defaults to StaticMethodLoader--
ClassMetadataFactory.
This means you can define a static loadValidatorMetadata method on your data class, which
takes a ClassMetadata argument. Then you can set constraints on this ClassMetadata instance.
Registering
Listing 26-1 1 $app->register(new Silex\Provider\ValidatorServiceProvider());
1. https://symfony.com/doc/current/reference/dic_tags.html#validator-initializer
2. https://api.symfony.com/master/Symfony/Component/Validator/ValidatorInterface.html
Usage
The Validator provider provides a validator service.
Validating Values
You can validate values directly using the validate validator method:
Validating Objects
If you want to add validations to a class, you can define the constraint for the class properties and getters,
and then call the validate method:
You can also declare the class constraint by adding a static loadValidatorMetadata method to your
classes:
Translation
To be able to translate the error messages, you can use the translator provider and register the messages
under the validators domain:
3. https://symfony.com/doc/master/book/validation.html
The FormServiceProvider provides a service for building forms in your application with the Symfony
Form component.
Parameters
• none
Services
• form.factory: An instance of FormFactory1, that is used to build a form.
Registering
Listing 27-1 1 use Silex\Provider\FormServiceProvider;
2
3 $app->register(new FormServiceProvider());
If you don't want to create your own form layout, it's fine: a default one will be used. But you will
have to register the translation provider as the default form layout requires it:
$app->register(new Silex\Provider\TranslationServiceProvider(), array(
Listing 27-2
'translator.domains' => array(),
));
If you want to use validation with forms, do not forget to register the Validator provider.
1. https://api.symfony.com/master/Symfony/Component/Form/FormFactory.html
If you are going to use the validation extension with forms, you must also add a dependency to the
symfony/validator and symfony/config components:
If you want to use forms in your Twig templates, you can also install the Symfony Twig Bridge. Make
sure to install, if you didn't do that already, the Translation component in order for the bridge to
work:
Usage
The FormServiceProvider provides a form.factory service. Here is a usage example:
If you are using the validator provider, you can also add validation to your form by adding constraints on
the fields:
CSRF protection is only available and automatically enabled when the CSRF Service Provider is
registered.
Traits
Silex\Application\FormTrait adds the following shortcuts:
• form: Creates a FormBuilderInterface instance.
• namedForm: Creates a FormBuilderInterface instance (named).
2. https://symfony.com/doc/current/forms.html
The CsrfServiceProvider provides a service for building forms in your application with the Symfony Form
component.
Parameters
• csrf.session_namespace (optional): The namespace under which the token is stored in the session.
Defaults to _csrf.
Services
• csrf.token_manager: An instance of an implementation of the CsrfTokenManagerInterface1,
Registering
Listing 28-1 1 use Silex\Provider\CsrfServiceProvider;
2
3 $app->register(new CsrfServiceProvider());
1. https://api.symfony.com/master/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface.html
2. https://symfony.com/doc/current/components/security/index.html
Listing 28-3
use Symfony\Component\Security\Csrf\CsrfToken;
$csrfToken = $app['csrf.token_manager']->getToken('token_id'); //'TOKEN'
Listing 28-4
$app['csrf.token_manager']->isTokenValid(new CsrfToken('token_id', 'TOKEN'));
Parameters
• http_cache.cache_dir: The cache directory to store the HTTP cache data.
• http_cache.options (optional): An array of options for the HttpCache1 constructor.
Services
• http_cache: An instance of HttpCache2.
• http_cache.esi: An instance of Esi3, that implements the ESI capabilities to Request and Response
instances.
• http_cache.store: An instance of Store4, that implements all the logic for storing cache metadata
(Request and Response headers).
Registering
Listing 29-1 1 $app->register(new Silex\Provider\HttpCacheServiceProvider(), array(
2 'http_cache.cache_dir' => __DIR__.'/cache/',
3 ));
1. https://api.symfony.com/master/Symfony/Component/HttpKernel/HttpCache/HttpCache.html
2. https://api.symfony.com/master/Symfony/Component/HttpKernel/HttpCache/HttpCache.html
3. https://api.symfony.com/master/Symfony/Component/HttpKernel/HttpCache/Esi.html
4. https://api.symfony.com/master/Symfony/Component/HttpKernel/HttpCache/Store.html
If you want Silex to trust the X-Forwarded-For* headers from your reverse proxy at address $ip,
you will need to whitelist it as documented in Trusting Proxies5.
If you would be running Varnish in front of your application on the same machine:
use Symfony\Component\HttpFoundation\Request;
Listing 29-3
Request::setTrustedProxies(array('127.0.0.1', '::1'));
$app->run();
This provider allows you to use the Symfony reverse proxy natively with Silex applications by using the
http_cache service. The Symfony reverse proxy acts much like any other proxy would, so you will
want to whitelist it:
Listing 29-4
use Symfony\Component\HttpFoundation\Request;
Request::setTrustedProxies(array('127.0.0.1'));
$app['http_cache']->run();
If your application doesn't use ESI, you can disable it to slightly improve the overall performance:
5. https://symfony.com/doc/current/components/http_foundation/trusting_proxies.html
To help you debug caching issues, set your application debug to true. Symfony automatically adds
a X-Symfony-Cache header to each response with useful information about cache hits and misses.
If you are not using the Symfony Session provider, you might want to set the PHP
session.cache_limiter setting to an empty value to avoid the default PHP behavior.
Finally, check that your Web server does not override your caching strategy.
6. https://symfony.com/doc/current/book/http_cache.html
The HttpFragmentServiceProvider provides support for the Symfony fragment sub-framework, which
allows you to embed fragments of HTML in a template.
Parameters
• fragment.path: The path to use for the URL generated for ESI and HInclude URLs (/_fragment by
default).
• uri_signer.secret: The secret to use for the URI signer service (used for the HInclude renderer).
• fragment.renderers.hinclude.global_template: The content or Twig template to use for the
default content when using the HInclude renderer.
Services
• fragment.handler: An instance of FragmentHandler1.
• fragment.renderers: An array of fragment renderers (by default, the inline, ESI, and HInclude
renderers are pre-configured).
Registering
Listing 30-1 1 $app->register(new Silex\Provider\HttpFragmentServiceProvider());
Usage
1. https://api.symfony.com/master/Symfony/Component/HttpKernel/Fragment/FragmentHandler.html
Instead of building a page out of a single request/controller/template, the fragment framework allows
you to build a page from several controllers/sub-requests/sub-templates by using fragments.
Including "sub-pages" in the main page can be done with the Twig render() function:
The render() call is replaced by the content of the /foo URL (internally, a sub-request is handled by
Silex to render the sub-page).
Instead of making internal sub-requests, you can also use the ESI (the sub-request is handled by a reverse
proxy) or the HInclude strategies (the sub-request is handled by a web browser):
Parameters
• security.hide_user_not_found (optional): Defines whether to hide user not found exception or
not. Defaults to true.
• security.encoder.bcrypt.cost (optional): Defines BCrypt password encoder cost. Defaults to 13.
• security.role_hierarchy:(optional): Defines a map of roles including other roles.
• security.access_rules (optional): Defines rules based on paths and roles. See Defining Access Rule1.
Services
• security.token_storage: Gives access to the user token.
• security.authorization_checker: Allows to check authorizations for the users.
• security.authentication_manager: An instance of AuthenticationProviderManager2, responsible
for authentication.
• security.access_manager: An instance of AccessDecisionManager3, responsible for authorization.
• security.session_strategy: Define the session strategy used for authentication (default to a
migration strategy).
• security.user_checker: Checks user flags after authentication.
• security.last_error: Returns the last authentication error message when given a Request object.
• security.authentication_utils: Returns the AuthenticationUtils service allowing you to get last
authentication exception or last username.
• security.encoder_factory: Defines the encoding strategies for user passwords (uses
security.default_encoder).
• security.default_encoder: The encoder to use by default for all users (BCrypt).
• security.encoder.digest: Digest password encoder.
1. #defining-access-rules
2. https://api.symfony.com/master/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.html
3. https://api.symfony.com/master/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.html
The service provider defines many other services that are used internally but rarely need to be
customized.
Registering
Listing 31-1 1 $app->register(new Silex\Provider\SecurityServiceProvider(), array(
2 'security.firewalls' => // see below
3 ));
The security features are only available after the Application has been booted. So, if you want to use
it outside of the handling of a request, don't forget to call boot() first:
$app->boot();
Listing 31-3
Usage
The Symfony Security component is powerful. To learn more about it, read the Symfony Security
documentation4.
When a security configuration does not behave as expected, enable logging (with the Monolog
extension for instance) as the Security Component logs a lot of interesting information about what it
does and why.
Listing 31-4
$token = $app['security.token_storage']->getToken();
If there is no information about the user, the token is null. If the user is known, you can get it with a
call to getUser():
4. https://symfony.com/doc/current/security.html
The user can be a string, an object with a __toString() method, or an instance of UserInterface5.
The pattern is a regular expression on the URL path; the http setting tells the security layer to use
HTTP basic authentication and the users entry defines valid users.
If you want to restrict the firewall by more than the URL pattern (like the HTTP method, the client IP,
the hostname, or any Request attributes), use an instance of a RequestMatcher6 for the pattern option:
• The role or an array of roles for the user (roles are strings beginning with ROLE_ and ending with
anything you want);
• The user encoded password.
All users must at least have one role associated with them.
The default configuration of the extension enforces encoded passwords. To generate a valid encoded
password from a raw password, use the security.encoder_factory service:
When the user is authenticated, the user stored in the token is an instance of User7
5. https://api.symfony.com/master/Symfony/Component/Security/Core/User/UserInterface.html
6. https://api.symfony.com/master/Symfony/Component/HttpFoundation/RequestMatcher.html
7. https://api.symfony.com/master/Symfony/Component/Security/Core/User/User.html
• login_path: The login path where the user is redirected when they are accessing a secured area
without being authenticated so that they can enter their credentials;
• check_path: The check URL used by Symfony to validate the credentials of the user.
• The login_path path must always be defined outside the secured area (or if it is in the secured area,
the anonymous authentication mechanism must be enabled -- see below);
• The check_path path must always be defined inside the secured area.
For the login form to work, create a controller like the following:
The error and last_username variables contain the last authentication error and the last username
entered by the user in case of an authentication error.
If you want to have the last error message translated, you would need to use the
security.authentication_utils service and retrieve the actual AuthenticationException
instance.
Create the associated template:
The admin_login_check route is automatically defined by Silex and its name is derived from the
check_path value (all / are replaced with _ and the leading / is stripped).
The order of the firewall configurations is significant as the first one to match wins. The above
configuration first ensures that the /login URL is not secured (no authentication settings), and then it
secures all other URLs.
You can toggle all registered authentication mechanisms for a particular area on and off with the
security flag:
Adding a Logout
When using a form for authentication, you can let users log out if you add the logout setting, where
logout_path must match the main firewall pattern:
Listing 31-15 1 $app['security.firewalls'] = array(
2 'secured' => array(
3 'pattern' => '^/admin/',
4 'form' => array('login_path' => '/login', 'check_path' => '/admin/login_check'),
5 'logout' => array('logout_path' => '/admin/logout', 'invalidate_session' => true),
6
7 // ...
A route is automatically generated, based on the configured path (all / are replaced with _ and the
leading / is stripped):
When enabling the anonymous setting, a user will always be accessible from the security context; if the
user is not authenticated, it returns the anon. string.
Listing 31-18
if ($app['security.authorization_checker']->isGranted('ROLE_ADMIN')) {
// ...
}
You can check if a user is "fully authenticated" (not an anonymous user for instance) with the special
IS_AUTHENTICATED_FULLY role:
Listing 31-20 1 {% if is_granted('IS_AUTHENTICATED_FULLY') %}
2 <a href="{{ path('logout') }}">Logout</a>
3 {% else %}
4 <a href="{{ path('login') }}">Login</a>
5 {% endif %}
Of course you will need to define a login route for this to work.
Switching to another user is now a matter of adding the _switch_user query parameter to any URL
when logged in as a user who has the ROLE_ALLOWED_TO_SWITCH role:
You can check that you are impersonating a user by checking the special ROLE_PREVIOUS_ADMIN. This
is useful for instance to allow the user to switch back to their primary account:
Above configuration ensures that you have the same security context admin_security inside both,
login and admin firewalls. This might be useful for instance to redirect already logged in users to the
secured area of your website when they visit the login form, as you have the possibility to check if the
user has been granted the ROLE_ADMIN role inside the login firewall.
Listing 31-25
With this configuration, all users with the ROLE_ADMIN role also automatically have the ROLE_USER
and ROLE_ALLOWED_TO_SWITCH roles.
Listing 31-26
$app['security.access_rules'] = array(
array('^/admin', 'ROLE_ADMIN', 'https'),
array('^.*$', 'ROLE_USER'),
);
With the above configuration, users must have the ROLE_ADMIN to access the /admin section of the
website, and ROLE_USER for everything else. Furthermore, the admin section can only be accessible via
HTTPS (if that's not the case, the user will be automatically redirected).
Listing 31-27
'users' => function () use ($app) {
return new UserProvider($app['db']);
},
Here is a simple example of a user provider, where Doctrine DBAL is used to store the users:
8. https://api.symfony.com/master/Symfony/Component/HttpFoundation/RequestMatcher.html
9. https://api.symfony.com/master/Symfony/Component/Security/Core/User/UserProviderInterface.html
In this example, instances of the default User class are created for the users, but you can define your own
class; the only requirement is that the class must implement UserInterface10
And here is the code that you can use to create the database schema and some sample users:
If you are using the Doctrine ORM, the Symfony bridge for Doctrine provides a user provider class
that is able to load users from your entities.
10. https://api.symfony.com/master/Symfony/Component/Security/Core/User/UserInterface.html
You can now use it in your configuration like any other built-in authentication provider:
Listing 31-33
Instead of true, you can also define an array of options that customize the behavior of your
authentication factory; it will be passed as the second argument of your authentication factory (see
above).
This example uses the authentication provider classes as described in the Symfony cookbook11.
The Guard component simplifies the creation of custom authentication providers. How to Create a
Custom Authentication System with Guard
Stateless Authentication
By default, a session cookie is created to persist the security context of the user. However, if you use
certificates, HTTP authentication, WSSE and so on, the credentials are sent for each request. In that case,
you can turn off persistence by activating the stateless authentication flag:
Traits
Silex\Application\SecurityTrait adds the following shortcuts:
• encodePassword: Encode a given password.
11. https://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html
Parameters
n/a
Services
n/a
The service provider defines many other services that are used internally but rarely need to be
customized.
Registering
Before registering this service provider, you must register the SecurityServiceProvider:
Options
• key: A secret key to generate tokens (you should generate a random string).
• name: Cookie name (default: REMEMBERME).
• lifetime: Cookie lifetime (default: 31536000 ~ 1 year).
• path: Cookie path (default: /).
• domain: Cookie domain (default: null = request domain).
• secure: Cookie is secure (default: false).
• httponly: Cookie is HTTP only (default: true).
• always_remember_me: Enable remember me (default: false).
• remember_me_parameter: Name of the request parameter enabling remember_me on login. To
add the checkbox to the login form. You can find more information in the Symfony cookbook1
(default: _remember_me).
1. https://symfony.com/doc/current/cookbook/security/remember_me.html
Parameters
None.
Services
• serializer: An instance of Symfony\Component\Serializer\Serializer1.
• serializer.encoders: Symfony\Component\Serializer\Encoder\JsonEncoder2 and
Symfony\Component\Serializer\Encoder\XmlEncoder3.
• serializer.normalizers: Symfony\Component\Serializer\Normalizer\CustomNormalizer4 and
Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer5.
Registering
Listing 33-1 1 $app->register(new Silex\Provider\SerializerServiceProvider());
1. https://api.symfony.com/master/Symfony/Component/Serializer/Serializer.html
2. https://api.symfony.com/master/Symfony/Component/Serializer/Encoder/JsonEncoder.html
3. https://api.symfony.com/master/Symfony/Component/Serializer/Encoder/XmlEncoder.html
4. https://api.symfony.com/master/Symfony/Component/Serializer/Normalizer/CustomNormalizer.html
5. https://api.symfony.com/master/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.html
6. https://symfony.com/doc/current/components/serializer.html
Using a Cache
To use a cache, register a class implementing Doctrine\Common\Cache\Cache:
As your Silex application grows, you may wish to begin organizing your controllers in a more formal
fashion. Silex can use controller classes out of the box, but with a bit of work, your controllers can be
created as services, giving you the full power of dependency injection and lazy loading.
Parameters
There are currently no parameters for the ServiceControllerServiceProvider.
Services
There are no extra services provided, the ServiceControllerServiceProvider simply extends the
existing resolver service.
Usage
In this slightly contrived example of a blog API, we're going to change the /posts.json route to use a
controller, that is defined as a service.
Rewriting your controller as a service is pretty simple, create a Plain Ol' PHP Object with your
PostRepository as a dependency, along with an indexJsonAction method to handle the request.
Although not shown in the example below, you can use type hinting and parameter naming to get the
parameters you need, just like with standard Silex routes.
If you are a TDD/BDD fan (and you should be), you may notice that this controller has well defined
responsibilities and dependencies, and is easily tested/specced. You may also notice that the only
external dependency is on Symfony\Component\HttpFoundation\JsonResponse, meaning this
controller could easily be used in a Symfony (full stack) application, or potentially with other applications
or frameworks that know how to handle a Symfony/HttpFoundation1 Response object.
And lastly, define your controller as a service in the application, along with your route. The syntax in the
route definition is the name of the service, followed by a single colon (:), followed by the method name.
Listing 34-4
1. https://symfony.com/doc/master/components/http_foundation/introduction.html
In addition to using classes for service controllers, you can define any callable as a service in the
application to be used for a route.
And when defining your route, the code would look like the following:
The VarDumperServiceProvider provides a mechanism that allows exploring then dumping any PHP
variable.
Parameters
• var_dumper.dump_destination: A stream URL where dumps should be written to (defaults to
null).
Services
• n/a
Registering
Listing 35-1 1 $app->register(new Silex\Provider\VarDumperServiceProvider());
Usage
Adding the VarDumper component as a Composer dependency gives you access to the dump() PHP
function anywhere in your code.
If you are using Twig, it also provides a dump() Twig function and a dump Twig tag.
The DoctrineServiceProvider provides integration with the Doctrine DBAL1 for easy database access
(Doctrine ORM integration is not supplied).
Parameters
• db.options: Array of Doctrine DBAL options.
These options are available:
• driver: The database driver to use, defaults to pdo_mysql. Can be any of: pdo_mysql, pdo_sqlite,
pdo_pgsql, pdo_oci, oci8, ibm_db2, pdo_ibm, pdo_sqlsrv.
• dbname: The name of the database to connect to.
• host: The host of the database to connect to. Defaults to localhost.
• user: The user of the database to connect to. Defaults to root.
• password: The password of the database to connect to.
• charset: Only relevant for pdo_mysql, and pdo_oci/oci8, specifies the charset used when
connecting to the database.
• path: Only relevant for pdo_sqlite, specifies the path to the SQLite database.
• port: Only relevant for pdo_mysql, pdo_pgsql, and pdo_oci/oci8, specifies the port of the database to
connect to.
These and additional options are described in detail in the Doctrine DBAL configuration
documentation.
Services
• db: The database connection, instance of Doctrine\DBAL\Connection.
• db.config: Configuration object for Doctrine. Defaults to an empty Doctrine\DBAL\Configuration.
• db.event_manager: Event Manager for Doctrine.
1. https://www.doctrine-project.org/projects/dbal
Usage
The Doctrine provider provides a db service. Here is a usage example:
The first registered connection is the default and can simply be accessed as you would if there was only
one connection. Given the above configuration, these two lines are equivalent:
Listing 36-5
2. https://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/
The RoutingServiceProvider provides a service for generating URLs for named routes.
Parameters
• route_class: (optional): The default route class used by the route factory (defaults to Silex\Route).
Services
• url_generator: An instance of UrlGenerator1, using the RouteCollection2 that is provided through
the routes service. It has a generate method, which takes the route name as an argument, followed by
an array of route parameters.
Registering
Listing 37-1 1 $app->register(new Silex\Provider\RoutingServiceProvider());
Usage
The Routing provider provides a url_generator service:
1. https://api.symfony.com/master/Symfony/Component/Routing/Generator/UrlGenerator.html
2. https://api.symfony.com/master/Symfony/Component/Routing/RouteCollection.html
Moreover, if you have twig-bridge as a Composer dep, you will have access to the path() and url()
functions:
Traits
Silex\Application\UrlGeneratorTrait adds the following shortcuts:
• path: Generates a path.
• url: Generates an absolute URL.
Apache
If you are using Apache, make sure mod_rewrite is enabled and use the following .htaccess file:
If your site is not at the webroot level you will have to uncomment the RewriteBase statement and
adjust the path to point to your directory, relative from the webroot.
Alternatively, if you use Apache 2.2.16 or higher, you can use the FallbackResource directive1 to make
your .htaccess even easier:
If your site is not at the webroot level you will have to adjust the path to point to your directory,
relative from the webroot.
Or if you're using a VirtualHost, you can add the same directive to the VirtualHost's Directory entry:
Listing 38-3
1. https://www.adayinthelifeof.nl/2012/01/21/apaches-fallbackresource-your-new-htaccess-command/
Note that you need the leading forward slash there, unlike with the .htaccess version
nginx
The minimum configuration to get your application running under Nginx is:
IIS
If you are using the Internet Information Services from Windows, you can use this sample web.config
file:
Lighttpd
If you are using lighttpd, use this sample simple-vhost as a starting point:
PHP
PHP ships with a built-in webserver for development. This server allows you to run silex without any
configuration. However, in order to serve static files, you'll have to make sure your front controller
returns false in that case:
Assuming your front controller is at web/index.php, you can start the server from the command-line
with this command:
2.3.1 (2018-XX-XX)
• n/a
2.3.0 (2018-04-20)
• fixed validator integration into the security provider (order of registration of the validator and
security providers does not matter anymore)
• fixed compatibility issues with Symfony 3.4
2.2.2 (2018-01-12)
• [SECURITY] fixed before handlers not executed under mounts
2.2.1 (2017-12-14)
• added support for Swiftmailer SSL stream_context_options option
• fixed usage of namespaces for Twig paths
2.2.0 (2017-07-23)
• added json manifest version strategy support
• fixed EsiFragment constructor
• fixed RedirectableUrlMatcher compatibility with Symfony
• fixed compatibility with Pimple 3.2
• fixed WebTestCase compatibility with PHPUnit 6+
2.1.0 (2017-05-03)
• added more options to security.firewalls
• added WebLink component integration
• added parameters to configure the Twig core extension behavior
• fixed deprecation notices with symfony/twig-bridge 3.2+ in TwigServiceProvider
• added FormRegistry as a service to enable the extension point
• removed the build scripts
• fixed some deprecation warnings
• added support for registering Swiftmailer plugins
2.0.4 (2016-11-06)
• fixed twig.app_variable definition
• added support for latest versions of Twig 1.x and 2.0 (Twig runtime loaders)
• added support for Symfony 2.3
2.0.3 (2016-08-22)
• fixed lazy evaluation of 'monolog.use_error_handler'
• fixed PHP7 type hint on controllers
2.0.1 (2016-05-27)
• fixed the silex form extension registration to allow overriding default ones
• removed support for the obsolete Locale Symfony component (uses the Intl one now)
• added support for Symfony 3.1
2.0.0 (2016-05-18)
• decoupled the exception handler from HttpKernelServiceProvider
• Switched to BCrypt as the default encoder in the security provider
• added full support for RequestMatcher
• added support for Symfony Guard
• added support for callables in CallbackResolver
• added FormTrait::namedForm()
• added support for delivery_addresses, delivery_whitelist, and sender_address
• added support to register form types / form types extensions / form types guessers as services
• added support for callable in mounts (allow nested route collection to be built easily)
• added support for conditions on routes
• added support for the Symfony VarDumper Component
• added a global Twig variable (an AppVariable instance)
• [BC BREAK] CSRF has been moved to a standalone provider (form.secret is not available anymore)
• added support for the Symfony HttpFoundation Twig bridge extension
• added support for the Symfony Asset Component
• bumped minimum version of Symfony to 2.8
• bumped minimum version of PHP to 5.5.0
• Updated Pimple to 3.0
• Updated session listeners to extends HttpKernel ones
• [BC BREAK] Locale management has been moved to LocaleServiceProvider which must be
registered if you want Silex to manage your locale (must also be registered for the translation service
provider)
• [BC BREAK] Provider interfaces moved to SilexApi namespace, published as separate package via
subtree split
• [BC BREAK] ServiceProviderInterface split in to EventListenerProviderInterface and
BootableProviderInterface
• [BC BREAK] Service Provider support files moved under SilexProvider namespace, allowing
publishing as separate package via sub-tree split
• monolog.exception.logger_filter option added to Monolog service provider
• [BC BREAK] $app['request'] service removed, use $app['request_stack'] instead
1.3.6 (2016-XX-XX)
• n/a
1.3.4 (2015-09-15)
• fixed some new deprecations
• fixed translation registration for the validators
1.3.3 (2015-09-08)
• added support for Symfony 3.0 and Twig 2.0
• fixed some Form deprecations
• removed deprecated method call in the exception handler
• fixed Swiftmailer spool flushing when spool is not enabled
1.3.2 (2015-08-24)
• no changes
1.3.1 (2015-08-04)
• added missing support for the Expression constraint
• fixed the possibility to override translations for validator error messages
• fixed sub-mounts with same name clash
• fixed session logout handler when a firewall is stateless
1.3.0 (2015-06-05)
• added a $app['user'] to get the current user (security provider)
• added view handlers
• added support for the OPTIONS HTTP method
• added caching for the Translator provider
• deprecated $app['exception_handler']->disable() in favor of unset($app['exception_handler'])
• made Silex compatible with Symfony 2.7 an 2.8 (and keep compatibility with Symfony 2.3, 2.5, and
2.6)
• removed deprecated TwigCoreExtension class (register the new HttpFragmentServiceProvider
instead)
• bumped minimum version of PHP to 5.3.9
1.2.5 (2015-06-04)
• no code changes (last version of the 1.2 branch)
1.2.3 (2015-01-20)
• fixed remember me listener
• fixed translation files loading when they do not exist
• allowed global after middlewares to return responses like route specific ones
1.2.2 (2014-09-26)
• fixed Translator locale management
• added support for the $app argument in application middlewares (to make it consistent with route
middlewares)
• added form.types to the Form provider
1.2.1 (2014-07-01)
• added support permissions in the Monolog provider
• fixed Switfmailer spool where the event dispatcher is different from the other ones
• fixed locale when changing it on the translator itself
1.2.0 (2014-03-29)
• Allowed disabling the boot logic of MonologServiceProvider
• Reverted "convert attributes on the request that actually exist"
• [BC BREAK] Routes are now always added in the order of their registration (even for mounted
routes)
• Added run() on Route to be able to define the controller code
• Deprecated TwigCoreExtension (register the new HttpFragmentServiceProvider instead)
• Added HttpFragmentServiceProvider
• Allowed a callback to be a method call on a service (before, after, finish, error, on Application;
convert, before, after on Controller)
1.1.3 (2013-XX-XX)
• Fixed translator locale management
1.1.2 (2013-10-30)
• Added missing "security.hide_user_not_found" support in SecurityServiceProvider
• Fixed event listeners that are registered after the boot via the on() method
1.1.1 (2013-10-11)
• Removed or replaced deprecated Symfony code
• Updated code to take advantages of 2.3 new features
• Only convert attributes on the request that actually exist.
1.1.0 (2013-07-04)
• Support for any Psr\Log\LoggerInterface as opposed to the monolog-bridge one.
• Made dispatcher proxy methods on, before, after and error lazy, so that they will not instantiate the
dispatcher early.
• Dropped support for 2.1 and 2.2 versions of Symfony.
1.0.1 (2013-07-04)
• Fixed RedirectableUrlMatcher::redirect() when Silex is configured to use a logger
• Make DoctrineServiceProvider multi-db support lazy.
1.0.0 (2013-05-03)
• 2013-04-12: Added support for validators as services.
• 2013-04-01: Added support for host matching with symfony 2.2:
• 2013-03-08: Added support for form type extensions and guessers as services.
• 2013-03-08: Added support for remember-me via the RememberMeServiceProvider.
• 2013-02-07: Added Application::sendFile() to ease sending BinaryFileResponse.
• 2012-11-05: Filters have been renamed to application middlewares in the documentation.
• 2012-11-05: The before(), after(), error(), and finish() listener priorities now set the
priority of the underlying Symfony event instead of a custom one before.
• 2012-11-05: Removing the default exception handler should now be done via its disable()
method:
Before:
After:
$app['exception_handler']->disable();
• 2012-07-15: removed the monolog.configure service. Use the extend method instead:
Before:
Listing 39-2
$app['monolog.configure'] = $app->protect(function ($monolog) use ($app) {
// do something
});
After:
Before:
Listing 39-4
$controllers = new ControllerCollection();
After:
Listing 39-5
$controllers = new ControllerCollection(new Route());
// or even better
$controllers = $app['controllers_factory'];
Before:
Listing 39-6
After:
Listing 39-8
$data = array('some' => 'data');
$response = $app->json($data);
Listing 39-9
$app = new Application();
$app->get('/bar', function() { return 'foo'; });
return $app;
After:
Listing 39-10
$app = new ControllerCollection();
$app->get('/bar', function() { return 'foo'; });
return $app;
• 2011-08-08: The controller method configuration is now done on the Controller itself
Before:
Listing 39-11
$app->match('/', function () { echo 'foo'; }, 'GET|POST');
After:
Listing 39-12
$app->match('/', function () { echo 'foo'; })->method('GET|POST');