The Silex Book



Comments



Description

The Silex Bookgenerated on November 18, 2015 The Silex Book This work is licensed under the “Attribution-Share Alike 3.0 Unported” license (http://creativecommons.org/ licenses/by-sa/3.0/). You are free to share (to copy, distribute and transmit the work), and to remix (to adapt the work) under the following conditions: • 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 .......................................................................................................................................4 Usage .................................................................................................................................................6 Middlewares .....................................................................................................................................20 Organizing Controllers......................................................................................................................24 Services.............................................................................................................................................26 Providers ..........................................................................................................................................31 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.......................................................................................................49 Managing Assets in Templates...........................................................................................................51 Internals ...........................................................................................................................................53 Contributing.....................................................................................................................................55 DoctrineServiceProvider ....................................................................................................................57 MonologServiceProvider ...................................................................................................................60 SessionServiceProvider ......................................................................................................................63 SwiftmailerServiceProvider ................................................................................................................65 TranslationServiceProvider................................................................................................................68 TwigServiceProvider..........................................................................................................................72 UrlGeneratorServiceProvider .............................................................................................................75 ValidatorServiceProvider ...................................................................................................................77 FormServiceProvider .........................................................................................................................82 HttpCacheServiceProvider.................................................................................................................86 HttpFragmentServiceProvider............................................................................................................89 SecurityServiceProvider .....................................................................................................................91 RememberMeServiceProvider .......................................................................................................... 103 SerializerServiceProvider.................................................................................................................. 105 ServiceControllerServiceProvider ..................................................................................................... 107 Webserver Configuration ................................................................................................................ 110 Changelog ...................................................................................................................................... 114 PDF brought to you by generated on November 18, 2015 Contents at a Glance | iii $app = new Silex\Application(). All that is needed to get access to the Framework is to include the autoloader.com/ 2.org/ 3.. 2015 Chapter 1: Introduction | 4 ./vendor/autoload. • Extensible: Silex has an extension system based around the Pimple service-container that makes it easy to tie in third party libraries. }). http://pimple.sensiolabs.php'. Usage Listing 1-1 1 2 3 4 5 6 7 8 9 10 11 12 <?php // web/index. It also respects the HTTP specification and encourages its proper use. 1.sinatrarb. In a nutshell. $app->run(). $app->get('/hello/{name}'. Silex aims to be: • Concise: Silex exposes an intuitive and concise API. This makes it very easy to test apps and the framework itself.Chapter 1 Introduction Silex is a PHP microframework. function ($name) use ($app) { return 'Hello '.'/. • Testable: Silex uses Symfony's HttpKernel which abstracts request and response.php require_once __DIR__. http://www. you define controllers and map them to routes.com/ PDF brought to you by generated on November 18. It is built on the shoulders of Symfony1 and Pimple2 and also inspired by Sinatra3.$app->escape($name). all in one step. http://symfony. a route for /hello/{name} that matches for GET requests is defined. the function is executed and the return value is sent back to the client. the app is run. It's really that easy! PDF brought to you by generated on November 18. Finally. When the route matches.Next. Visit /hello/world to see the result. 2015 Chapter 1: Introduction | 5 . use Composer2 instead: Listing 2-2 1 composer require silex/silex:~1.org/ PDF brought to you by generated on November 18.php If you want more flexibility. all you need to do is require the vendor/autoload. read the webserver documentation to check yours. http://getcomposer. After your controller definitions. 2015 Chapter 2: Usage | 6 . you should have the following directory structure: Listing 2-1 1 2 3 4 5 6 ├── composer. call the run method on your application: 1.3 Web Server All examples in the documentation relies on a well-configured web server. http://silex. download1 Silex as an archive and extract it.lock ├── vendor │ └── ..json ├── composer. Bootstrap To bootstrap Silex.php file and create an instance of Silex\Application.org/download 2.Chapter 2 Usage Installation If you want to get started fast. └── web └── index..sensiolabs. 'Using Silex'. Routing In Silex you define a route and the controller that is called when that route is matched.. you will need to run your application like this: Listing 2-5 1 use Symfony\Component\HttpFoundation\Request.. do something 3 } The return value of the closure becomes the content of the page. The controller is defined using a closure like this: Listing 2-6 1 function () { 2 // .. PUT. // . A route pattern consists of: • Pattern: The route pattern defines a path that points to a resource. you might want to turn on the debug mode to ease debugging: Listing 2-4 1 $app['debug'] = true. If your application is hosted behind a reverse proxy at address $ip. Example GET Route Here is an example definition of a GET route: Listing 2-7 1 $blogPosts = array( 2 1 => array( 3 'date' 4 'author' 5 'title' 6 'body' PDF brought to you by generated on November 18. or OPTIONS. 4 $app->run(). definitions $app->run().php require_once __DIR__.. '. $app = new Silex\Application(). 'igorw'. and you want Silex to trust the X-Forwarded-For* headers. This describes the interaction with the resource.php'.'.'/. POST. When developing a website. DELETE. 2 3 Request::setTrustedProxies(array($ip)). PATCH. • Method: One of the following HTTP methods: GET. Chapter 2: Usage | 7 ./vendor/autoload.... 2015 => => => => '2011-03-29'.Listing 2-3 1 2 3 4 5 6 7 8 // web/index. The pattern can include variable parts and you are able to set RegExp requirements for them. '[YourSite] Feedback'. $id) use ($blogPosts) { 2 if (!isset($blogPosts[$id])) { 3 $app->abort(404. function (Request $request) { $message = $request->get('message'). 18 }). 10 }). You will use the mail function to send an e-mail: Listing 2-9 1 2 3 4 5 6 7 8 9 use Symfony\Component\HttpFoundation\Request.com'. 9 10 $app->get('/blog'. Visiting /blog will return a list of blog post titles. 2015 Chapter 2: Usage | 8 . It is pretty straightforward. you are using abort() to stop the request early. you can create another controller for viewing individual blog posts: Listing 2-8 1 $app->get('/blog/{id}'.7 ). $app->post('/feedback'. The use statement means something different in this context. use Symfony\Component\HttpFoundation\Response. It actually throws an exception.= '<br />'. function () use ($blogPosts) { 11 $output = ''. This allows you to use it from within the closure. 9 "<p>{$post['body']}</p>". 7 8 return "<h1>{$post['title']}</h1>". 4 } 5 6 $post = $blogPosts[$id]. It tells the closure to import the $blogPosts variable from the outer scope. "Post $id does not exist."). which you will see how to handle later on. 8 ). 12 foreach ($blogPosts as $post) { 13 $output . 14 $output . This route definition has a variable {id} part which is passed to the closure. Dynamic Routing Now. return new Response('Thank you for your feedback!'. $message). 201). An example for this is a feedback form. Example POST Route POST routes signify the creation of a resource. mail('feedback@yoursite. When the post does not exist. The current Application is automatically injected by Silex to the Closure thanks to the type hinting. }).= $post['title']. function (Silex\Application $app. PDF brought to you by generated on November 18. 15 } 16 17 return $output. . You can also call match.com/master/Symfony/Component/HttpFoundation/Request. so you can fetch variables using the request get method. in this case it is set to 201 Created..html PDF brought to you by generated on November 18.. function ($id) { // . $app->delete('/blog/{id}'. 2015 Chapter 2: Usage | 9 .com/master/Symfony/Component/HttpFoundation/Response.html 4.symfony. which will match all methods.There is a SwiftmailerServiceProvider included that you can use instead of mail(). delete.. Instead of returning a string you are returning an instance of Response4. This allows setting an HTTP status code. --> 3 <input type="hidden" id="_method" name="_method" value="PUT" /> 4 </form> you need to explicitly enable this method override: Listing 2-12 1 use Symfony\Component\HttpFoundation\Request. Other methods You can create controllers for most HTTP methods. }). options: Listing 2-10 1 2 3 4 5 6 7 8 9 10 11 $app->put('/blog/{id}'. 2 3 Request::enableHttpMethodParameterOverride().symfony. The current request is automatically injected by Silex to the Closure thanks to the type hinting. http://api.. Just call one of these methods on your application: get. it converts strings to responses with status code 200. Forms in most web browsers do not directly support the use of other HTTP methods. function ($id) { // . It is an instance of Request3. To use methods other than GET and POST you can utilize a special form field with a name of _method. patch.. $app->patch('/blog/{id}'. }).. function ($id) { // . post.. Silex always uses a Response internally. http://api.. put. }). The form's method attribute must be set to POST when using this field: Listing 2-11 1 <form action="/my/target/route/" method="post"> 2 <!-. 4 $app->run(). This can be restricted via the method method: Listing 2-13 3. Route Variables As it has been shown before you can define variable parts in a route like this: Listing 2-14 1 $app->get('/blog/{id}'.. 2015 Chapter 2: Usage | 10 . 3 }).. }) ->method('PATCH'). While it's not recommended.. PDF brought to you by generated on November 18.. The first matching route will be used. $postId) { 2 // . just make sure the closure arguments match the names of the variable parts: Listing 2-15 1 $app->get('/blog/{postId}/{commentId}'. function (Application $app. It is also possible to have more than one variable part. 3 }). }). The order in which the routes are defined is significant. You can also ask for the current Request and Application objects: Listing 2-17 1 $app->get('/blog/{id}'. function () { // . $id) { 2 // . $app->match('/blog'. function ($postId.. function () { // . you could also do this (note the switched arguments): Listing 2-16 1 $app->get('/blog/{postId}/{commentId}'. 3 }). so place more generic routes at the bottom. function () { // . function ($commentId.1 2 3 4 5 6 7 8 9 10 11 12 13 $app->match('/blog'... }) ->method('PUT|POST'). $app->match('/blog'... function ($id) { 2 // .. $commentId) { 2 // .. 3 }). Request $request.... . })->convert('user'. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException.. This is useful when you want to convert route variables to objects as it allows to reuse the conversion code across different controllers: Listing 2-20 1 2 3 4 5 6 7 8 9 10 11 $userProvider = function ($id) { return new User($id). The converter callback also receives the Request as its second argument: Listing 2-21 1 2 3 4 5 6 7 $callback = function ($post. $app->get('/blog/{id}/{slug}'. function ($id) { 2 // .. function (User $user) { // . $callback). function (User $user) { // . $userProvider). 3 })... $app->get('/user/{user}/edit'.. For example. Route Variable Converters Before injecting the route variables into the controller.. $userProvider). })->convert('post'.. you can apply some converters: Listing 2-19 1 $app->get('/user/{id}'. } PDF brought to you by generated on November 18. public function __construct(ObjectManager $om) { $this->om = $om. $app->get('/user/{user}'. }). A converter can also be defined as a service.Note for the Application and Request objects. here is a user converter based on Doctrine ObjectManager: Listing 2-22 1 2 3 4 5 6 7 8 9 10 11 use Doctrine\Common\Persistence\ObjectManager. function (Application $foo. function ($id) { return (int) $id.. function (Post $post) { // . Request $bar. }. Request $request) { return new Post($request->attributes->get('slug')). 3 })->convert('id'. Silex does the injection based on the type hinting and not on the variable name: Listing 2-18 1 $app->get('/blog/{id}'. $id) { 2 // .. class UserConverter { private $om. })->convert('user'. }. 2015 Chapter 2: Usage | 11 . 12 13 14 15 16 17 18 19 20 21 } public function convert($id) { if (null === $user = $this->om->find('User', (int) $id)) { throw new NotFoundHttpException(sprintf('User %d does not exist', $id)); } return $user; } 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): Listing 2-23 1 2 3 4 5 6 7 $app['converter.user'] = $app->share(function () { return new UserConverter(); }); $app->get('/user/{user}', function (User $user) { // ... })->convert('user', 'converter.user:convert'); 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 1 $app->get('/blog/{id}', function ($id) { 2 // ... 3 }) 4 ->assert('id', '\d+'); You can also chain these calls: Listing 2-25 1 2 3 4 5 $app->get('/blog/{postId}/{commentId}', function ($postId, $commentId) { // ... }) ->assert('postId', '\d+') ->assert('commentId', '\d+'); Default Values You can define a default value for any route variable by calling value on the Controller object: Listing 2-26 1 $app->get('/{pageName}', function ($pageName) { 2 // ... 3 }) 4 ->value('pageName', 'index'); This will allow matching /, in which case the pageName variable will have the value index. PDF brought to you by generated on November 18, 2015 Chapter 2: Usage | 12 Named Routes Some providers (such as UrlGeneratorProvider) 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: Listing 2-27 1 2 3 4 5 6 7 8 9 $app->get('/', function () { // ... }) ->bind('homepage'); $app->get('/blog/{id}', function ($id) { // ... }) ->bind('blog_post'); 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: Listing 2-28 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $app->get('/', 'Acme\\Foo::bar'); use Silex\Application; use Symfony\Component\HttpFoundation\Request; namespace Acme { class Foo { public function bar(Request $request, Application $app) { // ... } } } 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. Global Configuration If a controller setting must be applied to all controllers (a converter, a middleware, a requirement, or a default value), configure it on $app['controllers'], which holds all application controllers: Listing 2-29 1 $app['controllers'] 2 ->value('id', '1') 3 ->assert('id', '\d+') 4 ->requireHttps() 5 ->method('get') 6 ->convert('id', function () { /* ... */ }) 7 ->before(function () { /* ... */ }) 8 ; PDF brought to you by generated on November 18, 2015 Chapter 2: Usage | 13 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 allows 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: Listing 2-30 1 use Symfony\Component\HttpFoundation\Response; 2 3 $app->error(function (\Exception $e, $code) { 4 return new Response('We are sorry, but something went terribly wrong.'); 5 }); You can also check for specific errors by using the $code argument, and handle them differently: Listing 2-31 1 use Symfony\Component\HttpFoundation\Response; 2 3 $app->error(function (\Exception $e, $code) { 4 switch ($code) { 5 case 404: 6 $message = 'The requested page could not be found.'; 7 break; 8 default: 9 $message = 'We are sorry, but something went terribly wrong.'; 10 } 11 12 return new Response($message); 13 }); You can restrict an error handler to only handle some Exception classes by setting a more specific type hint for the Closure argument: Listing 2-32 1 $app->error(function (\LogicException $e, $code) { 2 // this handler will only handle \LogicException exceptions 3 // and exceptions that extends \LogicException 4 }); 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 won't work. If you want to overwrite the status code, set the X-Status-Code header: Listing 2-33 1 return new Response('Error', 404 /* ignored */, array('X-Status-Code' => 200)); PDF brought to you by generated on November 18, 2015 Chapter 2: Usage | 14 . and a simple error message otherwise. To register a view handler. Silex comes with a default error handler that displays a detailed error message with the stack trace when debug is true. Silex ships with a provider for Monolog5 which handles logging of errors. 4 } 5 6 return new Response(. View Handlers also receive the Request as their second argument. 2015 Chapter 2: Usage | 15 . The callable should accept some sort of result from the controller: Listing 2-36 1 $app->view(function (array $controllerResult) use ($app) { 2 return $app->json($controllerResult). 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: Listing 2-34 1 use Symfony\Component\HttpFoundation\Response. because once a response is returned.). 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. make sure you register it with a higher priority than response error handlers..com/Seldaek/monolog PDF brought to you by generated on November 18. Check out the Providers chapter for details. 6 } 7 8 // . 7 }). 2 3 $app->error(function (\Exception $e."). making them a good candidate for basic content negotiation: Listing 2-37 5. "Post $id does not exist.If you want to use a separate error handler for logging. https://github. You can convert errors to Exceptions... pass a callable (or string that can be resolved to a callable) to the view() method. function (Silex\Application $app. 3 }). The error handlers are also called when you use abort to abort a request early: Listing 2-35 1 $app->get('/blog/{id}'. $id) use ($blogPosts) { 2 if (!isset($blogPosts[$id])) { 3 $app->abort(404. the following handlers are ignored. check out the cookbook chapter for details. $code) use ($app) { 4 if ($app['debug']) { 5 return. logic to handle the error and return a Response 9 }). 4 5 if ('json' === $bestFormat) { 6 return new JsonResponse($controllerResult). Request $request) use ($app) { 2 $acceptHeader = $request->headers->get('Accept'). use an internal sub-request: Listing 2-39 1 2 3 4 5 6 7 8 9 use Symfony\Component\HttpFoundation\Request. 7 } 8 9 if ('xml' === $bestFormat) { 10 return $app['serializer. 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.1 $app->view(function (array $controllerResult. which you can create by calling the redirect method: Listing 2-38 1 $app->get('/'. function () use ($app) { 2 return $app->redirect('/hello'). 14 }). 'GET'). array('json'. }). $app->get('/'. 'xml')). This will redirect from / to /hello. function () use ($app) { // forward to /hello $subRequest = Request::create('/hello'. PDF brought to you by generated on November 18. You must ensure that Silex receives a Response or a string as the result of the last view handler (or controller) to be run. 11 } 12 13 return $controllerResult. continuously using the return value of the last view handler as the input for the next. return $app->handle($subRequest.xml']->renderResponse($controllerResult). 3 $bestFormat = $app['negotiator']->getBestFormat($acceptHeader. Redirects You can redirect to another page by returning a RedirectResponse response. HttpKernelInterface::SUB_REQUEST). without a round-trip to the browser (as for a redirect). Forwards When you want to delegate the rendering to another controller. use Symfony\Component\HttpKernel\HttpKernelInterface. 2015 Chapter 2: Usage | 16 . 3 }). 3 4 if (!$user) { 5 $error = array('message' => 'The user was not found. status code and headers. array('Content-Type' => 'image/png')). 4 } 5 6 $stream = function () use ($file) { 7 readfile($file).example. function ($id) use ($app) { 2 $user = getUser($id). and it will create a JSON response for you: Listing 2-41 1 $app->get('/users/{id}'. Simply pass it your data.'). 1024). 200. If you need to send chunks. 5 ob_flush(). 11 }).'/images/'. make sure you call ob_flush and flush after every chunk: Listing 2-43 1 $stream = function () { 2 $fh = fopen('http://www. Read more on how to make sub-requests. session. That includes: Cookies.If you are using UrlGeneratorProvider.$file)) { 3 return $app->abort(404. 6 7 return $app->json($error. 8 }. server information. 9 10 return $app->stream($stream. function ($file) use ($app) { 2 if (!file_exists(__DIR__. 6 flush(). 8 } 9 10 return $app->json($user). 404). 'The image was not found. 3 while (!feof($fh)) { 4 echo fread($fh. 7 } PDF brought to you by generated on November 18. Streaming It's possible to stream a response. 'GET'). 11 }).com/'. which is important in cases when you don't want to buffer the data being sent: Listing 2-42 1 $app->get('/images/{file}'. you can use the json helper method. There's some more things that you need to keep in mind though. JSON If you want to return JSON data. In most cases you will want to forward some parts of the current master request to the sub-request.'). you can also generate the URI: Listing 2-40 1 $request = Request::create($app['url_generator']->generate('hello'). 'rb'). 2015 Chapter 2: Usage | 17 . headers and the content disposition and it will create a BinaryFileResponse response for you: Listing 2-44 1 $app->get('/files/{path}'. use Application\SwiftmailerTrait. Almost all built-in service providers have some corresponding PHP traits. It eases returning files that would otherwise not be publicly available. Traits Silex comes with PHP traits that define shortcut methods. To use them. You need to use PHP 5. 'pic. 7 }). use Application\TranslationTrait. status code. use Application\MonologTrait. check the API doc for 1 return $app 2 ->sendFile('/base/path/' . function ($path) use ($app) { 2 if (!file_exists('/base/path/' . 2015 Chapter 2: Usage | 18 . 4 } 5 6 return $app->sendFile('/base/path/' .com/master/Symfony/Component/HttpFoundation/BinaryFileResponse.8 9 }. To further customize the response before returning SymfonyComponentHttpFoundationBinaryFileResponse6: Listing 2-45 it.jpg') 4 . use Application\SecurityTrait. Simply pass it your file path. $path)) { 3 $app->abort(404). http://api. } 6. $path). use Application\FormTrait. class MyApplication extends Application { use Application\TwigTrait. Sending a file If you want to return a file.4 or later to benefit from this feature. $path) 3 ->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT. fclose($fh).symfony. you can use the sendFile helper method. define your own Application class and include the traits you want: Listing 2-46 1 2 3 4 5 6 7 8 9 10 11 12 use Silex\Application.html PDF brought to you by generated on November 18. use Application\UrlGeneratorTrait. • Escaping JSON: If you want to provide data in JSON format you should use the Silex json function: Listing 2-50 1 $app->get('/name. PDF brought to you by generated on November 18. • Escaping HTML: PHP provides the htmlspecialchars function for this. 5 }). } To use your newly defined route.". override the $app['route_class'] setting: Listing 2-48 1 $app['route_class'] = 'MyRoute'. Silex provides a shortcut escape method: Listing 2-49 1 $app->get('/name'. 3 4 return $app->json(array('name' => $name)). make sure to escape it correctly to prevent Cross-Site-Scripting attacks. function (Silex\Application $app) { 2 $name = $app['request']->get('name').json'. 3 4 return "You provided the name {$app->escape($name)}.You can also define your own Route class and use some traits: Listing 2-47 1 2 3 4 5 6 use Silex\Route. function (Silex\Application $app) { 2 $name = $app['request']->get('name'). 2015 Chapter 2: Usage | 19 . Check out the Providers chapter for details. If you use the Twig template engine. you should use its escaping or even auto-escaping mechanisms. class MyRoute extends Route { use Route\SecurityTrait. Security Make sure to protect your application against attacks. Escaping When outputting any user input. Read each provider chapter to learn more about the added methods. 5 }). you need to register it as an early event: Listing 3-2 1 $app->before(function (Request $request.. the middleware is run after the routing and the security.Chapter 3 Middlewares Silex allows you to run code. Before Middleware A before application middleware allows you to tweak the Request before the controller is executed: Listing 3-1 1 $app->before(function (Request $request. Application $app) { 2 // .. at different stages during the handling of a request through middlewares: • Application middlewares are triggered independently of the current handled request. 3 }. that changes the default Silex behavior. PDF brought to you by generated on November 18. or the security user.. Application Middlewares The application middlewares are only run for the "master" Request. By default. • Route middlewares are triggered when their associated route is matched. and so you won't have access to the locale. 3 }). the routing and the security won't have been executed. the current route. Application $app) { 2 // . If you want your middleware to be run even if an exception is thrown early on (on a 404 or 403 error for instance). Application::EARLY_EVENT). then. 2015 Chapter 3: Middlewares | 20 . In this case.. After Middleware An after application middleware allows you to tweak the Response before it is sent to the client: Listing 3-3 1 $app->after(function (Request $request. Response $response) { 2 // . Before Middleware A before route middleware is fired just before the route callback.. function () { // . }) ->before($before1) ->before($before2) ->after($after1) ->after($after2) . 2015 Chapter 3: Middlewares | 21 ..The before middleware is an event registered on the Symfony request event. 3 // Warning: modifications to the Request or Response will be ignored 4 }). You can also stack them: Listing 3-5 1 2 3 4 5 6 7 8 $app->get('/somewhere'. 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 1 $app->finish(function (Request $request.. The finish middleware is an event registered on the Symfony terminate event. The after middleware is an event registered on the Symfony response event. Route Middlewares Route middlewares are added to routes or route collections and they are only triggered when the corresponding route is matched. Response $response) { 2 // . 3 }).... but after the before application middlewares: PDF brought to you by generated on November 18. .. Application::LATE_EVENT)... 2015 Chapter 3: Middlewares | 22 . }. and the Response is passed to the after middlewares right away: PDF brought to you by generated on November 18. 3 }. Response $response.. }. }... function () { // . As a convenience. After Middleware An after route middleware is fired just after the route callback. Middlewares Priority You can add as many middlewares as you want.. }) ->after($after).. the request handling is short-circuited (the next middlewares won't be run.. Application $app) { // . }) ->before($before)... $app->get('/somewhere'. Application $app) { // .Listing 3-6 1 2 3 4 5 6 7 8 $before = function (Request $request. $app->get('/somewhere'. nor the route callback). $app->before(function (Request $request) { // .. You can explicitly control the priority of your middleware by passing an additional argument to the registration methods: Listing 3-8 1 $app->before(function (Request $request) { 2 // . two constants allow you to register an event as early as possible or as late as possible: Listing 3-9 1 2 3 4 5 6 7 $app->before(function (Request $request) { // . in which case they are triggered in the same order as you added them. }. 32).. but before the application after application middlewares: Listing 3-7 1 2 3 4 5 6 7 8 $after = function (Request $request. function () { // . Short-circuiting the Controller If a before middleware returns a Response object. Application::EARLY_EVENT). 2015 Chapter 3: Middlewares | 23 . PDF brought to you by generated on November 18.) { 4 return new RedirectResponse('/login'). A RuntimeException is thrown if a before middleware does not return a Response or null... 5 } 6 }).Listing 3-10 1 $app->before(function (Request $request) { 2 // redirect the user to the login screen if access to the Resource is protected 3 if (. // define controllers for a forum $forum = $app['controllers_factory']. PDF brought to you by generated on November 18. $app->mount('/forum'. and /forum/ to the forum home page. }). mount() prefixes all routes with the given prefix and merges them into the main Application. $app->mount('/blog'. $blog->get('/'. // . it is not possible to define a route for the /blog URL. }). $forum->get('/'.. function () { return 'Forum home page'. function () { return 'Main home page'. So. 2015 Chapter 4: Organizing Controllers | 24 .. The shortest possible URL is /blog/. / will map to the main home page. $blog). // define "global" controllers $app->get('/'. /blog/ to the blog home page. When mounting a route collection under /blog. you might want to group them logically: Listing 4-1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // define controllers for a blog $blog = $app['controllers_factory']. $forum). }). function () { return 'Blog home page'.Chapter 4 Organizing Controllers When your application starts to define too many controllers. $app['controllers_factory'] is a factory that returns a new instance of ControllerCollection when used. Building on the example from the middleware section. 2015 Chapter 4: Organizing Controllers | 25 .When calling get().php'). }). function () { return 'Blog home page'. 2 3 // ensure that all controllers require logged-in users 4 $backend->before($mustBeLogged). $blog->get('/'. For a better readability. Instead of requiring a file. Another benefit is the ability to apply settings on a set of controllers very easily.php $blog = $app['controllers_factory']. you are in fact calling them on a default instance of ControllerCollection (stored in $app['controllers']). PDF brought to you by generated on November 18. here is how you would secure all controllers for the backend collection: Listing 4-2 1 $backend = $app['controllers_factory']. you can split each controller collection into a separate file: Listing 4-3 1 2 3 4 5 6 7 8 // blog. match(). include 'blog.php $app->mount('/blog'. return $blog. // app. you can also create a Controller provider. or any other HTTP methods on the Application. sensiolabs. 13 $json = json_encode($data). This generally leads to code that is decoupled. Dependency Injection You can skip this if you already know what Dependency Injection is. it is also a service container.Chapter 5 Services Silex is not only a framework. Dependency Injection is a design pattern where you pass dependencies to services instead of creating them from within the service or relying on globals. LOCK_EX). It does this by extending Pimple1 which provides a very simple service container. flexible and testable.json'. reusable.$user->id. Here is an example of a class that takes a User object and stores it as a file in JSON format: Listing 5-1 1 class JsonUserPersister 2 { 3 private $basePath.'. 4 5 public function __construct($basePath) 6 { 7 $this->basePath = $basePath. http://pimple.'/'.org PDF brought to you by generated on November 18. 14 $filename = $this->basePath. 1. 15 file_put_contents($filename. 8 } 9 10 public function persist(User $user) 11 { 12 $data = $user->getAttributes(). 2015 Chapter 5: Services | 26 . $json. which means a service is only created when you actually need it.mysite. 3 }. It can recursively create dependencies of the requested services and inject them.host'] = 'http://cdn. Parameters You can set parameters (which are usually strings) by setting an array key on the container: Listing 5-4 1 $app['some_parameter'] = 'value'.16 17 } } In this simple example the dependency is the basePath property. 2015 Chapter 5: Services | 27 . You just set an array key on the container to be a closure. And to retrieve the service.com/'.and because Silex\Application extends Pimple all of this applies to Silex as well: Listing 5-2 1 $container = new Pimple(). Reading parameter values is possible with the same syntax: Listing 5-6 1 echo $app['some_parameter']. the closure is executed. This allows for lazy service creation: Listing 5-7 1 $app['some_service'] = function () { 2 return new Service(). However. when you retrieve the service. The array key can be any value. It is passed to the constructor. This means you can create several independent instances with different base paths. It does so lazily. Of course dependencies do not have to be simple strings. Service definitions Defining services is no different than defining parameters. use: Listing 5-8 PDF brought to you by generated on November 18. By convention dots are used for namespacing: Listing 5-5 1 $app['asset. or: Listing 5-3 1 $app = new Silex\Application(). Pimple Pimple makes strong use of closures and implements the ArrayAccess interface. More often they are in fact other services. A service container is responsible for creating and storing services. We will start off by creating a new instance of Pimple -. persist_path']). This will create the service on first invocation. The dependency is only created when some_service is accessed. 3 }. 3 }). 2015 Chapter 5: Services | 28 . For example when fetching services the current service depends on. This also works for shared services. $app['some_service. and then return the existing instance on any subsequent access. Access container from closure In many cases you will want to access the service container from within a service definition closure.config as configuration options. the container is passed to the closure as an argument: Listing 5-10 1 $app['some_service'] = function ($app) { 2 return new Service($app['some_other_service']. This is why Pimple allows you to protect your closures from being executed.with your own arguments. Going back to our initial example. so that you can fetch it and execute it yourself -. Protected closures Because the container sees closures as factories for services. some_service depends on some_other_service and takes some_service. it will always execute them when reading them. here's how we could use the container to manage its dependencies: Listing 5-11 1 $app['user. and it is possible to replace either of the dependencies by simply overriding those definitions. Every time you call $app['some_service']. 4 }).1 $service = $app['some_service']. by using the protect method: PDF brought to you by generated on November 18.persister'] = $app->share(function ($app) { 3 return new JsonUserPersister($app['user. 2 $app['user. a new instance of the service is created.config']). Shared services You may want to use the same instance of a service across all of your code. In order to do that you can make a shared service: Listing 5-9 1 $app['some_service'] = $app->share(function () { 2 return new Service(). In some cases you will however want to store a closure as a parameter. Because of this.persist_path'] = '/tmp/users'. Here you can see an example of Dependency Injection. symfony. • request: Contains the current request object.com/php-fig/log/blob/master/Psr/Log/LoggerInterface. // will not execute the closure $add = $app['closure_parameter']. modify. it takes a Request as input and returns a Response as output.php PDF brought to you by generated on November 18. • 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.symfony. http://api.html 3. You can add. you can only access it from within a controller. To enable logging you can either use the MonologServiceProvider or define your own logger service that conforms to the PSR logger interface. • routes: The RouteCollection3 that is used internally.com/master/Symfony/Component/HttpFoundation/Request. }). The HttpKernel is the heart of Symfony.com/master/Symfony/Component/EventDispatcher/EventDispatcher. or an error handler.html 4. http://api.html 6.com/master/Symfony/Component/HttpKernel/HttpKernel. logging is disabled as the value is set to null. • request_context: The request context is a simplified representation of the request that is used by the Router and the UrlGenerator. http://api. 2. It takes care of executing the controller with the right arguments. Disable it with unset($app['exception_handler']).html 5. Note that protected closures do not get access to the container.symfony. POST parameters and lots more! Example usage: Listing 5-13 1 $id = $app['request']->get('id').symfony. Core services Silex defines a range of services.html 7. Check the Internals chapter for more information.symfony. • logger: A LoggerInterface7 instance. which is an instance of Request2. // calling it now echo $add(2. This is only available when a request is being served.Listing 5-12 1 2 3 4 5 6 7 8 9 $app['closure_parameter'] = $app->protect(function ($a.com/master/Symfony/Component/Routing/RouteCollection. 2015 Chapter 5: Services | 29 . It is the core of the Symfony system and is used quite a bit by Silex. • dispatcher: The EventDispatcher4 that is used internally. By default. http://api. • kernel: The HttpKernel6 that is used internally. http://api. $b) { return $a + $b. It gives you access to GET. • controllers: The Silex\ControllerCollection that is used internally.com/master/Symfony/Component/HttpKernel/Controller/ControllerResolver. an application before/after middlewares. 3). • resolver: The ControllerResolver5 that is used internally. https://github. read routes. Defaults to 443. it defines the default locale (en by default).http_port (optional): Allows you to override the default port for non-HTTPS URLs.https_port (optional): Allows you to override the default port for HTTPS URLs. it is automatically set according to the _locale request attribute of the current route.All of these Silex core services are shared. When a request is being handled. Defaults to UTF-8. When set before any request handling. • locale (optional): The locale of the user. 2015 Chapter 5: Services | 30 . This parameter can be used by the UrlGeneratorProvider. • charset (optional): The charset to use for Responses. it will always use the current port. If the current request is HTTP. • debug (optional): Returns whether or not the application is running in debug mode. Defaults to 80. This parameter can be used by the UrlGeneratorProvider. Core parameters • request. PDF brought to you by generated on November 18. If the current request is HTTPS. • request. Defaults to false. it will always use the current port. 4 'database. These will be set after the provider is registered.dbname=myapp'. Conventions You need to watch out in what order you do certain things when interacting with providers. array( 2 'database. but before it is booted: Listing 6-2 1 $app->register(new Acme\DatabaseServiceProvider(). 2015 Chapter 6: Providers | 31 . Service Providers Loading providers In order to load and use a service provider. you must register it on the application: Listing 6-1 1 $app = new Silex\Application().dsn' => 'mysql:host=localhost. PDF brought to you by generated on November 18. Silex provides two types of providers defined by two interfaces: ServiceProviderInterface for services and ControllerProviderInterface for controllers. 3 'database. You can also provide some parameters as a second argument. 2 3 $app->register(new Acme\DatabaseServiceProvider()).Chapter 6 Providers Providers allow the developer to reuse parts of an application into another one. 5 )). Just keep these rules in mind: • Overriding existing services must occur after the provider is registered. the provider will overwrite it.password' => 'secret_root_password'.user' => 'root'. Reason: If the service already exists. https://github. 6 } The register() method defines services on the application which then may make use of other services and parameters. All of these are within the Silex\Provider namespace: • • • • • • • • • • • • • • DoctrineServiceProvider FormServiceProvider HttpCacheServiceProvider MonologServiceProvider RememberMeServiceProvider SecurityServiceProvider SerializerServiceProvider ServiceControllerServiceProvider SessionServiceProvider SwiftmailerServiceProvider TranslationServiceProvider TwigServiceProvider UrlGeneratorServiceProvider ValidatorServiceProvider Third party providers Some service providers are developed by the community. the provider will overwrite existing values. but before the service is accessed. Reason: Providers can set default values for parameters. Included providers There are a few providers that you get out of the box. 2015 Chapter 6: Providers | 32 . The boot() method configures the application. Those third-party providers are listed on Silex' repository wiki1.com/silexphp/Silex/wiki/Third-Party-ServiceProviders PDF brought to you by generated on November 18. 4 use Silex\ServiceProviderInterface. Here is an example of such a provider: Listing 6-4 1 namespace Acme. Just like with services. You are encouraged to share yours. just before it handles a request.• You can set parameters any time after the provider is registered. 1. 4 5 public function boot(Application $app). 2 3 use Silex\Application. Creating a provider Providers must implement the Silex\ServiceProviderInterface: Listing 6-3 1 interface ServiceProviderInterface 2 { 3 public function register(Application $app). default_name' => 'Igor'. it will use an empty string. new Acme\BlogControllerProvider()). function () use ($app) { $name = $app['request']->get('name'). 12 $name = $name ?: $default. so the request path would have to be /hello?name=Fabien. Creating a provider Providers must implement the Silex\ControllerProviderInterface: Listing 6-7 PDF brought to you by generated on November 18. 2015 Chapter 6: Providers | 33 .5 6 class HelloServiceProvider implements ServiceProviderInterface 7 { 8 public function register(Application $app) 9 { 10 $app['hello'] = $app->protect(function ($name) use ($app) { 11 $default = $app['hello. 2 3 $app->mount('/blog'.$app->escape($name). You can now use this provider as follows: Listing 6-5 1 2 3 4 5 6 7 8 9 10 11 $app = new Silex\Application(). All controllers defined by the provider will now be available under the /blog path.default_name if no name is given. $app->get('/hello'. 15 }). )). }). array( 'hello. 16 } 17 18 public function boot(Application $app) 19 { 20 } 21 } This class provides a hello service which is a protected closure. Controller Providers Loading providers In order to load and use a controller provider. It takes a name argument and will return hello. $app->register(new Acme\HelloServiceProvider(). In this example we are getting the name parameter from the query string. you must "mount" its controllers under a path: Listing 6-6 1 $app = new Silex\Application().default_name'] : ''. If the default is also missing. 13 14 return 'Hello '. return $app['hello']($name).default_name'] ? $app['hello. } } The connect method must return an instance of ControllerCollection. new Acme\HelloControllerProvider()). ControllerCollection is the class where all controller related methods are defined (like get. You can use this provider as follows: Listing 6-9 1 $app = new Silex\Application(). class HelloControllerProvider implements ControllerProviderInterface { public function connect(Application $app) { // creates a new controller based on the default route $controllers = $app['controllers_factory'].). PDF brought to you by generated on November 18. The Application class acts in fact as a proxy for these methods. 2015 Chapter 6: Providers | 34 . return $controllers. 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.. the /blog/ path now references the controller defined in the provider. 4 } Here is an example of such a provider: Listing 6-8 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 namespace Acme. 2 3 $app->mount('/blog'. }). use Silex\Application. . In this example. $controllers->get('/'. use Silex\ControllerProviderInterface. post.1 interface ControllerProviderInterface 2 { 3 public function connect(Application $app). function (Application $app) { return $app->redirect('/hello').. match. Chapter 7 Testing Because Silex is built on top of Symfony.com/sebastianbergmann/phpunit PDF brought to you by generated on November 18. and mimic the actions a user would do. unit testing. check out PHPUnit1 and Bulat Shakirzyanov's talk on Clean Code. For more information on functional testing.. you have to test it. because they enable you to test your application in usually under a second by running a single command. PHPUnit PHPUnit2 is the de-facto standard testing framework for PHP. it is very easy to write functional tests for your application. https://github. 2015 Chapter 7: Testing | 35 . Why If you are not familiar with software tests. Functional tests are automated software tests that ensure that your code is working correctly. but it can be used for functional tests too. https://github. and automated software tests in general. This means going through all the pages and making sure they are still working.com/sebastianbergmann/phpunit 2.. using a fake browser. 6 } 7 } 1. They go through the user interface. Every time you make a change to your application. that extends the PHPUnit_Framework_TestCase. you may be wondering why you would need this. Functional tests save you a lot of time. You write tests by creating a new class. It was built for writing unit tests. Your test cases are methods prefixed with test: Listing 7-1 1 class ContactFormTest extends \PHPUnit_Framework_TestCase 2 { 3 public function testInitialPage() 4 { 5 . 2015 Chapter 7: Testing | 36 . $pageContent).'/path/to/app.de/manual/current/en/writing-tests-for-phpunit. https://phpunit.In your test cases. There is a full list available in the Writing Tests for PHPUnit3 section of the PHPUnit documentation.. don't forget to call the parent (parent::setUp()) to call the Silex default setup. 3. as this method will be executed before every test..php'. $pageContent). The Silex version of this class is Silex\WebTestCase. you do assertions on the state of what you are testing. which returns your application instance: Listing 7-5 1 public function createApplication() 2 { 3 // app. } If you need to override the setUp() method... 9 } Here you see some of the available assertions. so we would want to assert that the page loaded correctly and contains our form: Listing 7-2 1 public function testInitialPage() 2 { 3 $statusCode = . 7 $this->assertContains('Contact us'. you will have to implement a createApplication method. and you can use it by making your test extend it: Listing 7-3 1 2 3 4 5 6 use Silex\WebTestCase.php must return an Application instance 4 return require __DIR__. WebTestCase Symfony provides a WebTestCase class that can be used to write functional tests.html PDF brought to you by generated on November 18. In this case we are testing a contact form. 5 6 $this->assertEquals(200.. class ContactFormTest extends WebTestCase { .. If you want to use the Symfony WebTestCase class you will need to explicitly install its dependencies for your project: Listing 7-4 1 composer require --dev symfony/browser-kit symfony/css-selector For your WebTestCase. 4 $pageContent = . 8 $this->assertContains('<form'. $statusCode). 5 } Make sure you do not use require_once here. . 4 $crawler = $client->request('GET'. it is sometimes easier to get raw exceptions instead of HTML pages. cookies and more. A client acts as a browser.'/path/to/app. set session. It holds your browsing history. You can also access the application through $this->app.. 2015 Chapter 7: Testing | 37 . 8 } If your application use sessions. the application behaves in the same way as when using it from a browser.. $crawler->filter('h1:contains("Contact us")')). 10 } There are several things going on here. 8 $this->assertCount(1. 6 7 // . Client The client represents a browser. 5 6 $this->assertTrue($client->getResponse()->isOk()). 8 } The WebTestCase provides a createClient method.test'] = true. Here's how it works: Listing 7-8 1 public function testInitialPage() 2 { 3 $client = $this->createClient().By default.. 5 unset($app['exception_handler']).test to true to simulate sessions: Listing 7-7 1 public function createApplication() 2 { 3 // .. It is rather simple if you tweak the application configuration in the createApplication() method like follows: Listing 7-6 1 public function createApplication() 2 { 3 $app = require __DIR__. 9 . 7 $this->assertCount(1. You can find some documentation for it in the client section of the testing chapter of the Symfony documentation. '/').. and allows you to interact with your application. PDF brought to you by generated on November 18. But when an error occurs. $crawler->filter('form')).php'. You have both a Client and a Crawler. 4 5 $app['session. 6 7 return $app. 4 $app['debug'] = true. The request method allows you to make a request to a page on your application. You can filter it using CSS expressions and lots more.php..Crawler The crawler allows you to inspect the content of a page./. } public function testFooBar() { . class YourTest extends WebTestCase { public function createApplication() { return require __DIR__. when running phpunit on the command line.php'..dist file should look like this: Listing 7-9 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?xml version="1./tests/</directory> </testsuite> </testsuites> </phpunit> Your tests/YourApp/Tests/YourTest.0" encoding="UTF-8"?> <phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" syntaxCheck="false" > <testsuites> <testsuite name="YourApp Test Suite"> <directory>. 2015 Chapter 7: Testing | 38 . Configuration The suggested way to configure PHPUnit is to create a phpunit.xml.. a tests folder and your tests in tests/YourApp/Tests/YourTest. use Silex\WebTestCase.. The phpunit./app.php should look like this: Listing 7-10 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 namespace YourApp\Tests.'/..xml. You can find some documentation for it in the crawler section of the testing chapter of the Symfony documentation./. } } Now. PDF brought to you by generated on November 18.dist file. tests should run. It tells us the ContentType of the response. 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.1 201 Created Content-Type: application/json Content-Length: 65 Connection: close PDF brought to you by generated on November 18.Chapter 8 Accepting a JSON Request Body A common need when building a restful API is the ability to accept a JSON encoded entity from the request body. We also indicate that using the Content-Type header: Listing 8-1 1 2 3 4 5 6 POST /blog/posts Accept: application/json Content-Type: application/json Content-Length: 57 {"title":"Hello World!". which is also JSON: Listing 8-2 1 2 3 4 HTTP/1. 2015 Chapter 8: Accepting a JSON Request Body | 39 . Request In the request we send the data for the blog post as a JSON object. The following is a spec of how we want it to work."body":"This is my first post!"} Response The server responds with a 201 status code. telling us that the post was created. which allows sending HTTP requests: Listing 8-5 1 $ curl http://blog.5 6 {"id":"1"."body":"This is my first post!"} Parsing the request body The request body should only be parsed as JSON if the Content-Type header begins with application/ json. 201). use Symfony\Component\HttpFoundation\ParameterBag. function (Request $request) use ($app) { $post = array( 'title' => $request->request->get('title')."title":"Hello World!". $request->request->replace(is_array($data) ? $data : array()). as JSON: Listing 8-4 1 2 3 4 5 6 7 8 9 10 11 12 13 use Symfony\Component\HttpFoundation\Request."body":"This is my first post!"} PDF brought to you by generated on November 18."title":"Hello World!".lo/blog/posts -d '{"title":"Hello World!". we can use the curl command line utility. including its id. the easiest solution is to use an application before middleware. 'body' => $request->request->get('body')."body":"This is my first 2 post!"}' -H 'Content-Type: application/json' {"id":"1". We simply use json_decode to parse the content of the request and then replace the request data on the $request object: Listing 8-3 1 2 3 4 5 6 7 8 9 use Symfony\Component\HttpFoundation\Request. $app->before(function (Request $request) { if (0 === strpos($request->headers->get('Content-Type'). Manual testing In order to manually test our API. 'application/json')) { $data = json_decode($request->getContent(). ). return $app->json($post. true). }). $app->post('/blog/posts'. } }). use Symfony\Component\HttpFoundation\Response. $post['id'] = createPost($post). Since we want to do this for every request. Controller implementation Our controller will create a new blog post from the data provided and will return the post object. 2015 Chapter 8: Accepting a JSON Request Body | 40 . user'] = 'myuser'. 2015 Chapter 9: Using PdoSessionStorage to store Sessions in the Database | 41 . To use it. PdoSessionHandler2. http://api.symfony. 'db_id_col' => 'session_id'. $app['session. $app['pdo'] = $app->share(function () use ($app) { return new PDO( 1.db_options'] = array( 'db_table' => 'session'.storage. $app['pdo.symfony. http://api. the SessionServiceProvider writes session information in files using Symfony NativeFileSessionStorage.handler service in your application like explained below. Most medium to large websites use a database to store sessions instead of files.com/master/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage. 'db_time_col' => 'session_time'. Symfony's NativeSessionStorage1 has multiple storage handlers and one of them uses PDO to store sessions. replace the session.html PDF brought to you by generated on November 18. $app['pdo. With a dedicated PDO service Listing 9-1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler. because databases are easier to use and scale in a multi-webserver environment.html 2. $app['pdo.password'] = 'mypassword'.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.Chapter 9 Using PdoSessionStorage to store Sessions in the Database By default. 'db_data_col' => 'session_value'. ). $app->register(new Silex\Provider\SessionServiceProvider()).dsn'] = 'mysql:dbname=mydatabase'. password'] 21 ).18 $app['pdo. 'db_data_col' => 'session_value'. Using the DoctrineServiceProvider When using the DoctrineServiceProvider You don't have to make another database connection.dsn']. 2015 Chapter 9: Using PdoSessionStorage to store Sessions in the Database | 42 . 28 $app['session. 'db_id_col' => 'session_id'. $app['session. $app['session.storage. $app['session. Listing 9-2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler.storage. $app->register(new Silex\Provider\SessionServiceProvider()). 30 }).html#example-sql-statements PDF brought to you by generated on November 18. ). 23 24 $app['session.options'] 29 ).handler'] = $app->share(function () use ($app) { 25 return new PdoSessionHandler( 26 $app['pdo'].user']. 27 $app['session.db_options'] = array( 'db_table' => 'session'. }).handler'] = $app->share(function () use ($app) { return new PdoSessionHandler( $app['db']->getWrappedConnection().db_options']. 'db_time_col' => 'session_time'. $app['session. 22 }). 19 $app['pdo.com/doc/current/cookbook/configuration/pdo_session_storage. 20 $app['pdo.options'] ). Database structure PdoSessionStorage needs a database table with 3 columns: • session_id: ID column (VARCHAR(255) or larger) • session_value: Value column (TEXT or CLOB) • session_time: Time column (INTEGER) You can find examples of SQL statements to create the session table in the Symfony cookbook3 3.db_options'].storage. http://symfony.storage. simply pass the getWrappedConnection method. it is as simple as using the Symfony getDefaultOptions() method in your form classes.com/doc/current/book/forms.Chapter 10 Disabling CSRF Protection on a Form using the FormExtension The FormExtension provides a service for building form in your application with the Symfony Form component.html#csrf-protection 2. 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.com/doc/current/book/forms. array('csrf_protection' => false)). your form could be submitted from everywhere without CSRF Protection. More of them could be passed through this parameter. null. In some cases (for example. a method by which a malicious user attempts to make your legitimate users unknowingly submit data that they don't intend to submit. See more here2. 1. By default. http://symfony. Going further This specific example showed how to change the csrf_protection in the $options parameter of the createBuilder() function. when embedding a form in an html email) you might want not to use this protection. the FormExtension uses the CSRF Protection avoiding Cross-site request forgery. You can find more details about CSRF Protection and CSRF token in the Symfony Book1.html#book-form-creating-form-classes PDF brought to you by generated on November 18. 2015 Chapter 10: Disabling CSRF Protection on a Form using the FormExtension | 43 . That's it. Example Listing 10-1 1 $form = $app['form.factory']->createBuilder('form'. http://symfony. Chapter 11 Using YAML to configure Validation Simplicity is at the heart of Silex so there is no out of the box solution to use YAML files for validation.Min: 100 PDF brought to you by generated on November 18.'/validation.NotNull: ~ 6 . Let's see how to do it. Now.yml 2 Post: 3 properties: 4 title: 5 .yml: Listing 11-3 1 # validation. But this doesn't mean that this is not possible. we can replace the usage of the static method and move all the validation rules to validation.yml') ). 2 3 $app['validator.mapping.class_metadata_factory'] = new 4 Symfony\Component\Validator\Mapping\ClassMetadataFactory( 5 new Symfony\Component\Validator\Mapping\Loader\YamlFileLoader(__DIR__.NotBlank: ~ 7 body: 8 . First. you need to tell the Validation Service that you are not using StaticMethodLoader to load your class metadata but a YAML file: Listing 11-2 1 $app->register(new ValidatorServiceProvider()). you need to install the YAML Component: Listing 11-1 1 composer require symfony/yaml Next. 2015 Chapter 11: Using YAML to configure Validation | 44 . Here's an example: Listing 12-1 1 2 3 4 5 use Symfony\Component\HttpFoundation\Request. or the session. it allows you to simulate requests against your application. you can make a sub-request manually. false). $response = $app->handle($subRequest. In most cases you will want to forward some parts of the current master request to the sub-request like cookies. it also allows you to forward a request which is essentially an internal redirect that does not change the URL. Certain listeners are only executed for the master request. HttpKernelInterface::SUB_REQUEST. Basics You can make a sub-request by calling the handle method on the Application. This argument defaults to true. 2015 Chapter 12: Making sub-Requests | 45 . • $type: Must be either HttpKernelInterface::MASTER_REQUEST or HttpKernelInterface::SUB_REQUEST.Chapter 12 Making sub-Requests Since Silex is based on the HttpKernelInterface. For sub-requests you will most likely want to set it to false. so it's important that this is set to SUB_REQUEST. $subRequest = Request::create('/'). By calling handle. use Symfony\Component\HttpKernel\HttpKernelInterface. This means that you can embed a page within another. Here is a more advanced example that forwards said information ($request holds the master request): Listing 12-2 PDF brought to you by generated on November 18. This method takes three arguments: • $request $request: An instance of the Request class which represents the HTTP request. There's some more things that you need to keep in mind though. • $catch: Catches exceptions and turns them into a response with status code 500. server information. you can simply return it from a controller: Listing 12-3 1 2 3 4 5 6 7 8 9 10 use Silex\Application. false). return $header. array(). 'GET'. This also allows you to embed pages. $app->get('/foo'..$footer. refer to the HttpCacheServiceProvider docs. $body = $response->getContent(). $request->server->all()). Request $request) { $subRequest = Request::create('/'. }). PDF brought to you by generated on November 18. refer to the TwigServiceProvider docs. $request->cookies->all(). however it also gives you the benefit of caching parts of the page. HttpKernelInterface::SUB_REQUEST. $footer = . 2015 Chapter 12: Making sub-Requests | 46 .. use Symfony\Component\HttpFoundation\Request. Rendering pages in Twig templates The TwigServiceProvider provides a render function that you can use in Twig templates.. false).1 2 3 4 5 6 7 8 9 use Symfony\Component\HttpFoundation\Request. Here is an example of how you would embed a page via ESI: Listing 12-6 1 <esi:include src="/sidebar" /> For details.. It gives you a convenient way to embed pages. if ($request->getSession()) { $subRequest->setSession($request->getSession()).. Edge Side Includes You can use ESI either through the HttpCacheServiceProvider or a reverse proxy cache such as Varnish. function (Application $app. return $response. $response = $app->handle($subRequest.). . array().. use Symfony\Component\HttpKernel\HttpKernelInterface.$body. $subRequest = Request::create('/'. To forward this response to the client. Listing 12-5 1 {{ render('/sidebar') }} For details. use Symfony\Component\HttpKernel\HttpKernelInterface. HttpKernelInterface::SUB_REQUEST. } $response = $app->handle($subRequest.. If you want to embed the response as part of a larger page you can call Response::getContent: Listing 12-4 1 2 3 4 5 $header = .. Silex accounts for this path prefix in the routing process. If your application is not hosted at the webroot of your web server.php/articles/42. Instead of injecting the request service. Since these services are mutable. Any request that is run against an application will re-use the same set of services. and keep using it. you should always inject the request_stack one instead.Dealing with the request base URL One thing to watch out for is the base URL. array(). because if you do not prepend the base path the request could mistake a part of the path you want to match as the base path and cut it off. 2 $subRequest = Request::create($url.php is your request base path. then you may have an URL like http://example. /foo/index. In this case. array(). $request->cookies->all(). it reads it from $request->server. PDF brought to you by generated on November 18. 'GET'. Any services depending on the request service will store the first request that they get (could be master or subrequest). since the application object is the container. 2015 Chapter 12: Making sub-Requests | 47 . even if that request is already over. code in a master request can affect the sub-requests and vice versa. In the context of sub-requests this can lead to issues. $request->server->all()). This is something to be aware of when making sub-requests by hand.org/foo/index. You can prevent that from happening by always prepending the base path when constructing a request: Listing 12-7 1 $url = $request->getUriForPath('/'). Services depending on the Request The container is a concept that is global to a Silex application. it does not catch PHP errors and notices.Chapter 13 Converting Errors to Exceptions Silex catches exceptions that are thrown from within a request/response cycle. Registering the ErrorHandler The Symfony/Debug package has an ErrorHandler class that solves this problem. you can additionally register a global ExceptionHandler: Listing 13-2 1 use Symfony\Component\Debug\ExceptionHandler. It is recommended that you do this as early as possible. Handling fatal errors To handle fatal errors. 2 3 ExceptionHandler::register(false). 2015 Chapter 13: Converting Errors to Exceptions | 48 . Register it by calling the static register method: Listing 13-1 1 use Symfony\Component\Debug\ErrorHandler. This recipe tells you how to catch them by converting them to exceptions. However. and exceptions are then caught by Silex. PDF brought to you by generated on November 18. 2 3 ExceptionHandler::register(). It converts all errors to exceptions. In production you may want to disable the debug output by passing false as the $debug argument: Listing 13-3 1 use Symfony\Component\Debug\ExceptionHandler. 2 3 ErrorHandler::register(). handler']). 7 $log->pushHandler($handler). This simple example allows you to quickly configure several monolog instances. including your customizations.$channel] = $app->share(function ($app) use ($channel) { return $app['monolog. PDF brought to you by generated on November 18. using the bundled handler. }). Listing 14-2 1 use Monolog\Handler\StreamHandler.factory'] = $app->protect(function ($name) use ($app) { $log = new $app['monolog. 5 $handler = new StreamHandler($app['monolog. 2 3 $app['monolog. 'payments'.level']).Chapter 14 Using multiple Monolog Loggers Having separate instances of Monolog for different parts of your system is often desirable and allows you to configure them independently. 2015 Chapter 14: Using multiple Monolog Loggers | 49 .payments. } As your application grows. 8 9 return $log.factory']($channel).logger. $log->pushHandler($app['monolog. Listing 14-1 1 2 3 4 5 6 7 8 9 10 11 12 $app['monolog.payments'] = $app->share(function ($app) { 4 $log = new $app['monolog. but each with a different channel.payment.'.class']($name).class']('payments'). or your logging needs for certain areas of the system become apparent. }). it should be straightforward to then configure that particular service separately. }). foreach (array('auth'. return $log. allowing for fine grained control of where your logging goes and in what detail. 6 $app['monolog.logfile']. 'stats') as $channel) { $app['monolog.logger. defaulting to the bundled handler.'. such as checking for an array of handlers registered with the container with the channel name. Logger::DEBUG).'.logger./payments.handler']). }). and rely on some conventions. }). foreach ($handlers as $handler) { $log->pushHandler($handler).handlers'] = $app->share(function ($app) { return array( new StreamHandler(__DIR__. PDF brought to you by generated on November 18.payments.$name. use Monolog\Logger.handlers'] : array($app['monolog.class']($name). } return $log.handlers']) ? $app['monolog. $app['monolog.'/. Listing 14-3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 use Monolog\Handler\StreamHandler. $handlers = isset($app['monolog. ).factory'] = $app->protect(function ($name) use ($app) { $log = new $app['monolog.'.$name.Alternatively. 2015 Chapter 14: Using multiple Monolog Loggers | 50 .'. you could attempt to make the factory more complicated..log'. $app['monolog. Using it in a template is as easy as before: Listing 15-4 1 {{ app.examples.asset_path }}/css/styles.css'. you might want to abstract the path by defining a Silex parameter: Listing 15-3 1 $app['asset_path'] = 'http://assets. they need to get prefixed by the base path. But what about images.examples.com'. 2015 Chapter 15: Managing Assets in Templates | 51 . Fortunately.css If your assets are hosted under a different host. the Request object contain the application base path that needs to be prepended: Listing 15-1 1 // generate a link to the stylesheets in /css/styles. define a service instead: Listing 15-5 1 $app['asset_path'] = $app->share(function () { 2 // implement whatever logic you need to determine the asset path 3 4 return 'http://assets.Chapter 15 Managing Assets in Templates A Silex application is not always hosted at the web root directory. And doing the same in a Twig template is as easy as it can get: Listing 15-2 1 {{ app. but nonetheless.css If you need to implement some logic independently of the asset. To avoid repeating the base path whenever you link to another page. 5 }).'/css/styles. or JavaScript files? Their URLs are not managed by the Silex router.request. it is highly recommended to use the URL generator service provider.com'. Usage is exactly the same as before: Listing 15-6 PDF brought to you by generated on November 18. stylesheets.css 2 $request->getBasePath().basepath }}/css/styles. 9 })).css If the asset location depends on the asset type or path.com/%s'. 2015 Chapter 15: Managing Assets in Templates | 52 .css') }} PDF brought to you by generated on November 18. ltrim($asset.1 {{ app. 6 })).examples. 7 8 return $twig.asset_path }}/css/styles. $app) { 2 $twig->addFunction(new \Twig_SimpleFunction('asset'. function($twig. The asset function can then be used in your templates: Listing 15-8 1 {{ asset('/css/styles. you will need more abstraction. here is one way to do that with a Twig function: Listing 15-7 1 $app['twig'] = $app->share($app->extend('twig'. function ($asset) { 3 // implement whatever logic you need to determine the asset path 4 5 return sprintf('http://assets. '/')). Chapter 16 Internals This chapter will tell you how Silex works internally.html 3. 1. Routes can be named.html 4.symfony. In order to allow setting these through a nice interface. the match method (which is used by get. They can also have requirements for the variable parts.symfony.) returns an instance of the Controller.symfony.com/master/Symfony/Component/Routing/Route. We also use it to dispatch some custom events like before/after middlewares and errors. You could replace any service. etc. which wraps a route. http://api. which allows for URL generation.com/master/Symfony/Component/EventDispatcher/EventDispatcher. converting string responses into Response objects and handling Exceptions.com/master/Symfony/Component/HttpFoundation/Request. post.html PDF brought to you by generated on November 18.symfony. http://api. http://api. http://api.symfony. http://api.symfony. Silex Application The application is the main interface to Silex. This allows fetching the Request. The application makes strong use of the EventDispatcher4 to hook into the Symfony HttpKernel5 events.html 6. and you are also able to read them.com/master/Symfony/Component/HttpKernel/HttpKernel. It implements Symfony's HttpKernelInterface1. Controller The Symfony Route6 is actually quite powerful. allowing for flexibility on the outside as well as the inside.com/master/Symfony/Component/HttpKernel/HttpKernelInterface.com/master/Symfony/Component/HttpFoundation/Response. 2015 Chapter 16: Internals | 53 .html 5. so you can pass a Request2 to the handle method and it will return a Response3. http://api. It extends the Pimple service container.html 2. ControllerCollection One of the goals of exposing the RouteCollection7 was to make it mutable. The Application provides a shortcut flush method for flushing the ControllerCollection. use the Symfony Following Symfony components are used by Silex: • • • • HttpFoundation: For Request and Response. HttpKernel: Because we need a heart. 7.com/master/Symfony/Component/Routing/RouteCollection.symfony. so providers could add stuff to it. 2015 Chapter 16: Internals | 54 . The Application will flush.com/ PDF brought to you by generated on November 18. This means that they can no longer be modified and will throw an Exception if you try to do so. Instead of creating an instance of $app['controllers_factory'] factory instead. check out the Symfony website8. but if you want to read the ControllerCollection before the request takes place. To solve this challenge we came up with a staging area for routes. Also. which is why flushing is now always explicit. you will have to call flush yourself. the controllers are then frozen. at which point the routes are added to the RouteCollection. The challenge here is the fact that routes know nothing about their name. The name only has meaning in context of the RouteCollection and cannot be changed. Unfortunately no good way for flushing implicitly could be found. http://symfony. RouteCollection yourself. http://api. The ControllerCollection holds the controllers until flush is called.html 8. Routing: For matching defined routes. EventDispatcher: For hooking into the HttpKernel. For more information. Make your feature addition or bug fix.Chapter 17 Contributing We are open to contributions to the Silex code. Send a pull request2. just follow these steps: • • • • • Fork the Silex repository1.github. to the correct `target branch`_. 1. 2015 Chapter 17: Contributing | 55 . add some documentation. https://github. https://help. If you find a bug or want to contribute a provider. Any code you contribute must be licensed under the MIT License.com/articles/creating-a-pull-request PDF brought to you by generated on November 18. Add tests for it.com/silexphp/Silex 2. Optionally. net/rst. http://sphinx-doc.org PDF brought to you by generated on November 18.sourceforge.Chapter 18 Writing Documentation The documentation is written in reStructuredText3 and can be generated using sphinx4. Listing 18-1 1 $ cd doc 2 $ sphinx-build -b html . 2015 Chapter 18: Writing Documentation | 56 .html 4. http://docutils. build 3. • port: Only relevant for pdo_mysql. Services • db: The database connection. ibm_db2. Can be any of: pdo_mysql. • db. • user: The user of the database to connect to. pdo_oci. pdo_sqlite. 2015 Chapter 19: DoctrineServiceProvider | 57 . Defaults Doctrine\DBAL\Configuration. • path: Only relevant for pdo_sqlite. These options are available: • driver: The database driver to use.org/projects/dbal PDF brought to you by generated on November 18. specifies the charset used when connecting to the database.config: Configuration object for Doctrine. oci8. to an empty 1. pdo_pgsql. pdo_sqlsrv. Parameters • db. http://www. Defaults to localhost. • charset: Only relevant for pdo_mysql. • host: The host of the database to connect to. specifies the path to the SQLite database. and pdo_oci/oci8. and pdo_oci/oci8. These and additional options are described in detail in the Doctrine DBAL configuration documentation. instance of Doctrine\DBAL\Connection.Chapter 19 DoctrineServiceProvider The DoctrineServiceProvider provides integration with the Doctrine DBAL1 for easy database access (Doctrine ORM integration is not supplied). defaults to pdo_mysql. specifies the port of the database to connect to. • password: The password of the database to connect to. Defaults to root.options: Array of Doctrine DBAL options.doctrine-project. • dbname: The name of the database to connect to. pdo_ibm. pdo_pgsql. 6 "<p>{$post['body']}</p>". array( 2 'db. 5 ). 5 'host' => 'mysql_read.event_manager: Event Manager for Doctrine.options.someplace. If you are using Composer. 6 )). dbs.2" Usage The Doctrine provider provides a db service. Using multiple databases The Doctrine provider can allow access to multiple databases. array( 2 'dbs.tld'. 8 'password' => 'my_password'. PDF brought to you by generated on November 18. 4 5 return "<h1>{$post['title']}</h1>".options' => array ( 3 'mysql_read' => array( 4 'driver' => 'pdo_mysql'. 4 'path' => __DIR__.db'. array((int) $id)). add it as a dependency: Listing 19-2 1 composer require "doctrine/dbal:~2. 6 'dbname' => 'my_database'. 9 'charset' => 'utf8mb4'. replace the db. function ($id) use ($app) { 2 $sql = "SELECT * FROM posts WHERE id = ?". 7 'user' => 'my_username'.tld'. 13 'host' => 'mysql_write. Registering Listing 19-1 1 $app->register(new Silex\Provider\DoctrineServiceProvider(). 7 }). 3 $post = $app['db']->fetchAssoc($sql.• db.options is an array of configurations where keys are connection names and values are options: Listing 19-4 1 $app->register(new Silex\Provider\DoctrineServiceProvider(). 11 'mysql_write' => array( 12 'driver' => 'pdo_mysql'.'/app. In order to configure the data sources. 10 ).options' => array( 3 'driver' => 'pdo_sqlite'. 2015 Chapter 19: DoctrineServiceProvider | 58 .options with dbs.someplace. Here is a usage example: Listing 19-3 1 $app->get('/blog/{id}'. Doctrine DBAL comes with the "fat" Silex archive but not with the regular one. (int) $id)). 'utf8mb4'. ). consult the Doctrine DBAL documentation2. 2 3 $app['dbs']['mysql_read']->fetchAll('SELECT * FROM table'). 2. 20 )). 'dbname' 'user' 'password' 'charset' => => => => 'my_database'. Given the above configuration. 9 "<p>{$post['body']}</p>". The first registered connection is the default and can simply be accessed as you would if there was only one connection. 'my_password'. these two lines are equivalent: Listing 19-5 1 $app['db']->fetchAll('SELECT * FROM table').doctrine-project.14 15 16 17 18 19 ). 'my_username'. 4 5 $sql = "UPDATE posts SET value = ? WHERE id = ?". Using multiple connections: Listing 19-6 1 $app->get('/blog/{id}'. For more information. 7 8 return "<h1>{$post['title']}</h1>". array('newValue'. 6 $app['dbs']['mysql_write']->executeUpdate($sql. function ($id) use ($app) { 2 $sql = "SELECT * FROM posts WHERE id = ?". 3 $post = $app['dbs']['mysql_read']->fetchAssoc($sql. array((int) $id)). http://docs. 10 }). 2015 Chapter 19: DoctrineServiceProvider | 59 .org/projects/doctrine-dbal/en/latest/ PDF brought to you by generated on November 18. com/Seldaek/monolog PDF brought to you by generated on November 18.name (optional): Name of the monolog channel. It will log requests and errors and allow you to add logging to your application. • monolog. responses and errors. "WARNING". INFO will log everything except DEBUG. defaults to DEBUG.level (optional): Level of logging.'). Logger::INFO. defaults to myapp. 1. This allows you to debug and monitor the behaviour. https://github. etc. • monolog. 2015 Chapter 20: MonologServiceProvider | 60 . Services • monolog: The monolog logger instance. • monolog. Parameters • monolog.listener: An event listener to log requests. Logger::ERROR. for example: "DEBUG".logfile: File where logs are written to. even in production. Must be one of Logger::DEBUG.bubble: (optional) Whether the messages that are handled can bubble up the stack or not.permission: (optional) File permissions default (null). Logger::WARNING. DEBUG will log everything. nothing change. • monolog. "ERROR". it is also possible to supply the level in string form. In addition to the Logger:: constants.Chapter 20 MonologServiceProvider The MonologServiceProvider provides a default logging mechanism through Jordi Boggiano's Monolog1 library. • monolog. Example usage: Listing 20-1 1 $app['monolog']->addDebug('Testing the Monolog logging. "INFO". function () use ($app) { 4 // . Monolog comes with the "fat" Silex archive but not with the regular one. responses and errors are logged by an event listener registered as a service called monolog. addInfo(). 2015 Chapter 20: MonologServiceProvider | 61 . You can replace or remove this service if you want to modify or disable the informations logged. 9 }). You can use it to add log entries for any logging level through addDebug(). PDF brought to you by generated on November 18. function($monolog. 5 6 $app['monolog']->addInfo(sprintf("User '%s' registered. $app) { 2 $monolog->pushHandler(. $username)).).log'..logfile' => __DIR__.'/development. Customization You can configure Monolog (like adding or changing the handlers) before using it by extending the monolog service: Listing 20-5 1 $app['monolog'] = $app->share($app->extend('monolog'.".Registering Listing 20-2 1 $app->register(new Silex\Provider\MonologServiceProvider(). 7 8 return new Response(''. array( 2 'monolog. 3 4 return $monolog. 2 3 $app->post('/user'... By default. Traits Silex\Application\MonologTrait adds the following shortcuts: • log: Logs a message. If you are using Composer. 3 )). 201).. all requests. add it as a dependency: Listing 20-3 1 composer require monolog/monolog Usage The MonologServiceProvider provides a monolog service. 5 })).listener. addWarning() and addError(): Listing 20-4 1 use Symfony\Component\HttpFoundation\Response. https://github.com/Seldaek/monolog PDF brought to you by generated on November 18.Listing 20-6 1 $app->log(sprintf("User '%s' registered. 2015 Chapter 20: MonologServiceProvider | 62 .". 2. $username)). For more information. check out the Monolog documentation2. com/master/Symfony/Component/HttpFoundation/Session/Session. the most useful options are: • • • • • • • name: The cookie name (_SESS by default) id: The session id (null by default) cookie_lifetime: Cookie lifetime cookie_path: Cookie path cookie_domain: Cookie domain cookie_secure: Cookie secure (HTTPS) cookie_httponly: Whether the cookie is http only However. In case of the default NativeSessionStorage1.net/session.com/master/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage. Services • session: An instance of Symfony's Session3.storage service.html 2. To override this. 2015 Chapter 21: SessionServiceProvider | 63 .test: Whether to simulate sessions or not (useful when writing functional tests).symfony.configuration 3.symfony. • session. set the lifetime option.save_path (optional): The path for the NativeFileSessionHandler. Default Sessions life time is 1800 seconds (30 minutes). For a full list of available options. read the PHP2 official documentation. Parameters • session.storage. defaults to the value of sys_get_temp_dir(). • session. all of these are optional.html PDF brought to you by generated on November 18. 1. • session.storage: A service that is used for persistence of the session data. http://api.options: An array of options that is passed to the constructor of the session. http://php.Chapter 21 SessionServiceProvider The SessionServiceProvider provides a service for storing data persistently between requests. http://api.storage. return $response.handler: A service that is used by the session. $response->setStatusCode(401.save_path ini setting yourself in that case.html PDF brought to you by generated on November 18. } return "Welcome {$user['username']}!".'). function () use ($app) { $username = $app['request']->server->get('PHP_AUTH_USER'. array('username' => $username)). $app->get('/account'. false). You will have to configure the session.storage for data access.storage. Registering Listing 21-1 1 $app->register(new Silex\Provider\SessionServiceProvider()). sprintf('Basic realm="%s"'.handler'] = null.storage.symfony.handler to null. Custom Session Configurations If your system is using a custom session configuration (such as a redis handler from a PHP extension) then you need to disable the NativeFileSessionHandler by setting session.com/master/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler. } $response = new Response(). }). return $app->redirect('/account'). 4.• session. Listing 21-3 1 $app['session. 2015 Chapter 21: SessionServiceProvider | 64 . Defaults to a NativeFileSessionHandler4 storage handler.storage. Here is an example that authenticates a user and creates a session for them: Listing 21-2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 use Symfony\Component\HttpFoundation\Response. $response->headers->set('WWW-Authenticate'. http://api. $password = $app['request']->server->get('PHP_AUTH_PW'). Usage The Session provider provides a session service. 'Please sign in. }). 'site_login')). function () use ($app) { if (null === $user = $app['session']->get('user')) { return $app->redirect('/login'). if ('igor' === $username && 'password' === $password) { $app['session']->set('user'. $app->get('/login'. • auth_mode: SMTP authentication mode. password: SMTP password. 'cram-md5'. 7 'auth_mode' => null 8 ). defaults to an empty string. Valid values are 'plain'. encryption: SMTP encryption. Valid values are 'tls'. 'ssl'. 'login'. Parameters • swiftmailer.Chapter 22 SwiftmailerServiceProvider The SwiftmailerServiceProvider provides a service for sending email through the Swift Mailer1 library. The following options can be set: • • • • • host: SMTP hostname. defaults to an empty string. port: SMTP port. 3 'port' => '25'. defaults to 25. 1. defaults to null. 5 'password' => 'password'. defaults to true. defaults to null. 4 'username' => 'username'. or null. You can use the mailer service to send messages easily. By default. it will attempt to send emails through SMTP. 2015 Chapter 22: SwiftmailerServiceProvider | 65 . • swiftmailer.use_spool: A boolean to specify whether or not to use the memory spool.options: An array of options for the default SMTP-based configuration. username: SMTP username.org PDF brought to you by generated on November 18. defaults to 'localhost'. 6 'encryption' => null. or null (indicating no encryption). http://swiftmailer. Example usage: Listing 22-1 1 $app['swiftmailer.options'] = array( 2 'host' => 'host'. SwiftMailer comes with the "fat" Silex archive but not with the regular one. function () use ($app) { 2 $request = $app['request']. If you are using Composer.com')) 7 ->setTo(array('[email protected]')) 8 ->setBody($request->get('message')).buffer: StreamBuffer used by the transport.transport: The transport Swift_Transport_EsmtpTransport.. Example usage: Listing 22-2 1 $message = \Swift_Message::newInstance(). plaintext.eventdispatcher: Internal event dispatcher used by Swiftmailer. 4 5 $app['mailer']->send($message). PDF brought to you by generated on November 18.transport. 9 10 $app['mailer']->send($message). Will try the following by default: CRAM-MD5. Registering Listing 22-3 1 $app->register(new Silex\Provider\SwiftmailerServiceProvider()). 2015 Chapter 22: SwiftmailerServiceProvider | 66 . 13 }). 11 12 return new Response('Thank you for your feedback!'.authhandler: Authentication handler used by the transport.Services • mailer: The mailer instance. 2 3 // . • swiftmailer.. 3 4 $message = \Swift_Message::newInstance() 5 ->setSubject('[YourSite] Feedback') 6 ->setFrom(array('noreply@yoursite. • swiftmailer. 201). Defaults to a • swiftmailer. used for e-mail delivery.transport. add it as a dependency: Listing 22-4 1 composer require swiftmailer/swiftmailer Usage The Swiftmailer provider provides a mailer service: Listing 22-5 1 $app->post('/feedback'. • swiftmailer. login.transport. you can just make sure to flush the message spool by hand before ending the command execution. For more information. use the following code: Listing 22-7 1 $app['swiftmailer.com')) 5 ->setBody($request->get('message'))). Alternatively. it is recommended that you disable the use of the memory spool (before accessing $app['mailer']): Listing 22-6 1 $app['swiftmailer. 2015 Chapter 22: SwiftmailerServiceProvider | 67 . To do so. Listing 22-8 1 $app->mail(\Swift_Message::newInstance() 2 ->setSubject('[YourSite] Feedback') 3 ->setFrom(array('noreply@yoursite. your emails won't be sent.transport']) 4 .Usage in commands By default. Traits Silex\Application\SwiftmailerTrait adds the following shortcuts: • mail: Sends an email.com')) 4 ->setTo(array('feedback@yoursite. if you send emails using a command console. as this event isn't fired for console commands.use_spool'] = false. http://swiftmailer. check out the Swift Mailer documentation2.org PDF brought to you by generated on November 18.spooltransport'] 2 ->getSpool() 3 ->flushQueue($app['swiftmailer. However. the Swiftmailer provider sends the emails using the KernelEvents::TERMINATE event. which is fired after the response has been sent. For that reason. 2. This parameter contains the translation data for all languages and domains. http://api. Services • translator: An instance of Translator1.symfony.message_selector: An instance of MessageSelector4. • locale (optional): The locale for the translator.symfony.html 4. http://api. • translator.loader: An instance of an implementation of the translation LoaderInterface2.Chapter 23 TranslationServiceProvider The TranslationServiceProvider provides a service for translating your application into different languages. defaults to an ArrayLoader3. • locale_fallbacks (optional): Fallback locales for the translator. Defaults to en. that is used for translation. Registering Listing 23-1 1. http://api.html 3.symfony.com/master/Symfony/Component/Translation/Translator.html 2.symfony. 2015 Chapter 23: TranslationServiceProvider | 68 . • translator.com/master/Symfony/Component/Translation/Loader/ArrayLoader.domains (optional): A mapping of domains/locales/messages. It will be used when the current locale has no messages set.com/master/Symfony/Component/Translation/MessageSelector. You will most likely want to set this based on some request parameter. Parameters • translator.html PDF brought to you by generated on November 18. Defaults to en.com/master/Symfony/Component/Translation/Loader/LoaderInterface. http://api. 1 $app->register(new Silex\Provider\TranslationServiceProvider(). ). 'fr' => array( 'hello' => 'Bonjour %name%'. ).domains'] = array( 'messages' => array( 'en' => array( 'hello' => 'Hello %name%'. /de/hello/igor will return Hallo igor. ). /it/hello/igor will return Hello igor (because of the fallback). add it as a dependency: Listing 23-2 1 composer require symfony/translation Usage The Translation provider provides a translator service and makes use of the translator. $name) use ($app) { return $app['translator']->trans($message. 'goodbye' => 'Tschüss %name%'. 'goodbye' => 'Goodbye %name%'. 'goodbye' => 'Au revoir %name%'.' => 'Cette valeur doit être un nombre. }). ). If you are using Composer. The Symfony Translation Component comes with the "fat" Silex archive but not with the regular one. 2015 Chapter 23: TranslationServiceProvider | 69 . Using Resources When translations are stored in a file. array( 2 'locale_fallbacks' => array('en'). function ($message. 'de' => array( 'hello' => 'Hallo %name%'.domains parameter: Listing 23-3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $app['translator. The above example will result in following routes: • • • • /en/hello/igor will return Hello igor. ). 3 )). 'validators' => array( 'fr' => array( 'This value should be a valid number. ). /fr/hello/igor will return Bonjour igor. ).'. array('%name%' => $name)). $app->get('/{_locale}/{message}/{name}'. you can load them as follows: PDF brought to you by generated on November 18. Listing 23-4 1 2 3 4 5 6 7 8 9 10 $app = new Application(); $app->register(new TranslationServiceProvider()); $app->extend('translator.resources', function ($resources, $app) { $resources = array_merge($resources, array( array('array', array('This value should be a valid number.' => 'Cette valeur doit être un nombre.'), 'fr', 'validators'), )); return $resources; }); 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. Listing 23-5 1 $app->trans('Hello World'); 2 3 $app->transChoice('Hello World'); 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: Listing 23-6 1 composer require symfony/config symfony/yaml 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 23-7 1 hello: Hello %name% 2 goodbye: Goodbye %name% Then, register the YamlFileLoader on the translator and add all your translation files: Listing 23-8 1 use Symfony\Component\Translation\Loader\YamlFileLoader; 2 3 $app['translator'] = $app->share($app->extend('translator', function($translator, $app) { 4 $translator->addLoader('yaml', new YamlFileLoader()); 5 6 $translator->addResource('yaml', __DIR__.'/locales/en.yml', 'en'); 7 $translator->addResource('yaml', __DIR__.'/locales/de.yml', 'de'); PDF brought to you by generated on November 18, 2015 Chapter 23: TranslationServiceProvider | 70 8 $translator->addResource('yaml', __DIR__.'/locales/fr.yml', 'fr'); 9 10 return $translator; 11 })); XLIFF-based language files Just as you would do with YAML translation files, you first need to add the Symfony Config component as a dependency (see above for details). Then, similarly, create XLIFF files in your locales directory and add them to the translator: Listing 23-9 1 $translator->addResource('xliff', __DIR__.'/locales/en.xlf', 'en'); 2 $translator->addResource('xliff', __DIR__.'/locales/de.xlf', 'de'); 3 $translator->addResource('xliff', __DIR__.'/locales/fr.xlf', 'fr'); The XLIFF loader is already pre-configured by the extension. Accessing translations in Twig templates Once loaded, the translation service provider is available from within Twig templates: Listing 23-10 1 {{ app.translator.trans('translation_key') }} Moreover, when using the Twig bridge provided by Symfony (see TwigServiceProvider), you will be allowed to translate strings in the Twig way: Listing 23-11 1 {{ 'translation_key'|trans }} 2 {{ 'translation_key'|transchoice }} 3 {% trans %}translation_key{% endtrans %} PDF brought to you by generated on November 18, 2015 Chapter 23: TranslationServiceProvider | 71 Chapter 24 TwigServiceProvider The TwigServiceProvider provides integration with the Twig1 template engine. 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). 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 24-1 1 $app->register(new Silex\Provider\TwigServiceProvider(), array( 2 'twig.path' => __DIR__.'/views', 3 )); 1. http://twig.sensiolabs.org/ 2. http://twig.sensiolabs.org/doc/api.html#environment-options PDF brought to you by generated on November 18, 2015 Chapter 24: TwigServiceProvider | 72 http://symfony.twig'.host }} A render function is also registered to help you render another controller from a template: Listing 24-6 3. 5 }). just put this in your template: Listing 24-5 1 {{ app. • FormServiceProvider: If you are using the FormServiceProvider.twig. you will have access to the path() and url() functions. You can find more information in the Symfony Translation documentation3. • SecurityServiceProvider: If you are using the SecurityServiceProvider. the TwigServiceProvider will provide you with the following additional capabilities: • UrlGeneratorServiceProvider: If you are using the UrlGeneratorServiceProvider.html PDF brought to you by generated on November 18. Usage The Twig provider provides a twig service: Listing 24-4 1 $app->get('/hello/{name}'.request. So you can access any service from within your view. http://symfony. If you are using Composer. you will get a set of helpers for working with forms in templates.com/doc/current/reference/forms/twig_reference.Twig comes with the "fat" Silex archive but not with the regular one. This will render a file named views/hello. you will have access to the is_granted() function in templates. 2015 Chapter 24: TwigServiceProvider | 73 . you will get the trans() and transchoice() functions for translation in Twig templates. the app variable refers to the Application object. • TranslationServiceProvider: If you are using the TranslationServiceProvider.html#twig-templates 4. Add it as a dependency: Listing 24-3 1 composer require symfony/twig-bridge When present. For example to access $app['request']->getHost().com/doc/current/book/translation. 4 )). You can find more information in the Symfony Routing documentation. In any Twig template. function ($name) use ($app) { 2 return $app['twig']->render('hello. You can find more information in the Symfony Forms reference4. array( 3 'name' => $name. add it as a dependency: Listing 24-2 1 composer require twig/twig Symfony Components Integration Symfony provides a Twig bridge that provides additional integration between some Symfony components and Twig. You can find more information in the Symfony Security documentation. ['name' => 'Fabien']. check out the official Twig documentation5. http://twig.baseUrl ~ '/sidebar') }} 2 3 {# or if you are also using the UrlGeneratorServiceProvider #} 4 {{ render(url('sidebar')) }} You must prepend the app.request. ['name' => 'Fabien']).sensiolabs. ['name' => 'Fabien']. 3 4 return $app->render('index.1 {{ render(app. function($twig. 1 // stream a view 2 use Symfony\Component\HttpFoundation\StreamedResponse. 4 5 return $twig. 6 })). 5. $response). 3 $twig->addFilter('levenshtein'. Listing 24-7 Listing 24-8 1 2 3 4 5 6 return $app->render('index. 3. $app) { 2 $twig->addGlobal('pi'. $response = new Response().org PDF brought to you by generated on November 18.request. For more information.html'. Traits Silex\Application\TwigTrait adds the following shortcuts: • render: Renders a view with the given parameters and returns a Response object. $response->setTtl(10). 2015 Chapter 24: TwigServiceProvider | 74 . new \Twig_Filter_Function('levenshtein')).html'.14). Customization You can configure the Twig environment before using it by extending the twig service: Listing 24-9 1 $app['twig'] = $app->share($app->extend('twig'.html'.baseUrl to render calls to ensure that the render works when deployed into a sub-directory of the docroot. return $app->render('index. new StreamedResponse()). which takes the route name as an argument. http://api. using the RouteCollection2 that is provided through the routes service.html 2. 3 }) 1. function () { 2 return 'welcome to the homepage'. It has a generate method. Parameters None. http://api. 2015 Chapter 25: UrlGeneratorServiceProvider | 75 .com/master/Symfony/Component/Routing/RouteCollection.symfony.symfony. Usage The UrlGenerator provider provides a url_generator service: Listing 25-2 1 $app->get('/'.html PDF brought to you by generated on November 18. Services • url_generator: An instance of UrlGenerator1.com/master/Symfony/Component/Routing/Generator/UrlGenerator.Chapter 25 UrlGeneratorServiceProvider The UrlGeneratorServiceProvider provides a service for generating URLs for named routes. Registering Listing 25-1 1 $app->register(new Silex\Provider\UrlGeneratorServiceProvider()). followed by an array of route parameters. PDF brought to you by generated on November 18. When using Twig. }) ->bind('hello').url_generator. '<a href="'.$app['url_generator']->generate('hello'.generate('homepage') }} Moreover. 2015 Chapter 25: UrlGeneratorServiceProvider | 76 . $app->get('/hello/{name}'.org/hello/ Fabien #} Traits Silex\Application\UrlGeneratorTrait adds the following shortcuts: • path: Generates a path.org/ #} {{ path('hello'. $app->get('/navigation'. function ($name) { return "Hello $name!". {name: 'Fabien'}) }} {# generates the absolute url http://example.$app['url_generator']->generate('homepage'). if you have twig-bridge as a Composer dep.'">Hello Igor</a>'. {name: 'Fabien'}) }} {{ url('hello'. Listing 25-5 1 $app->path('homepage'). you will have access to the path() and url() functions: Listing 25-4 1 2 3 4 {{ path('homepage') }} {{ url('homepage') }} {# generates the absolute url http://example. • url: Generates an absolute URL.'">Home</a>'. }). array('name' => 'Igor')). function () use ($app) { return '<a href="'. ' | '. the service can be used like this: Listing 25-3 1 {{ app.4 5 6 7 8 9 10 11 12 13 14 15 ->bind('homepage'). 2 $app->url('homepage'). which takes a ClassMetadata argument. Registering Listing 26-1 1 $app->register(new Silex\Provider\ValidatorServiceProvider()).class_metadata_factory: Factory for metadata loaders. Parameters • validator.Chapter 26 ValidatorServiceProvider The ValidatorServiceProvider provides a service for validating data. Services • validator: An instance of Validator1. • validator. 1.symfony.validator_service_ids: An array of service names representing validators.html PDF brought to you by generated on November 18. http://api.com/master/Symfony/Component/Validator/ValidatorInterface. Then you can set constraints on this ClassMetadata instance. 2015 Chapter 26: ValidatorServiceProvider | 77 . Defaults to StaticMethodLoader-ClassMetadataFactory. This means you can define a static loadValidatorMetadata method on your data class. but can also be used standalone. It is most useful when used with the FormServiceProvider.mapping. which can read validation constraint information from classes. ). class Book { public $title. function ($email) use ($app) { 4 $errors = $app['validator']->validateValue($email. 2 3 $app->get('/validate/{email}'. 8 } else { 9 return 'The email is valid'. ). 'last_name' => 'Potencier'. public $author. If you are using Composer. with a collection of constraints: Listing 26-4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 use Symfony\Component\Validator\Constraints as Assert. } class Author { public $first_name. add it as a dependency: Listing 26-2 1 composer require symfony/validator Usage The Validator provider provides a validator service. 'author' => array( 'first_name' => 'Fabien'. } $book = array( 'title' => 'My Book'. public $last_name. Validating Values You can validate values directly using the validateValue validator method: Listing 26-3 1 use Symfony\Component\Validator\Constraints as Assert. Validating Associative Arrays Validating associative arrays is like validating simple values. 10 } 11 }). $constraint = new Assert\Collection(array( PDF brought to you by generated on November 18. 2015 Chapter 26: ValidatorServiceProvider | 78 . new Assert\Email()).The Symfony Validator Component comes with the "fat" Silex archive but not with the regular one. 5 6 if (count($errors) > 0) { 7 return (string) $errors. )). new Assert\Length(array('min' => 10))). } You can also declare the class constraint by adding a static loadValidatorMetadata method to your classes: Listing 26-6 1 use Symfony\Component\Validator\Mapping\ClassMetadata.class_metadata_factory']->getMetadataFor('Book').$error->getMessage(). $metadata->addPropertyConstraint('first_name'. $metadata->addPropertyConstraint('first_name'. $book = new Book(). if (count($errors) > 0) { foreach ($errors as $error) { echo $error->getPropertyPath(). )). $author->first_name = 'Fabien'. $metadata->addPropertyConstraint('last_name'. new Assert\Length(array('min' => 10))). $metadata = $app['validator. 2015 Chapter 26: ValidatorServiceProvider | 79 ."\n". and then call the validate method: Listing 26-5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 use Symfony\Component\Validator\Constraints as Assert.mapping. } } else { echo 'The author is valid'.' '. $constraint). 'last_name' => new Assert\Length(array('min' => 10)). you can define the constraint for the class properties and getters.' '. $errors = $app['validator']->validateValue($book. 'author' => new Assert\Collection(array( 'first_name' => array(new Assert\NotBlank(). $metadata = $app['validator. $author = new Author(). $book->author = $author. $author->last_name = 'Potencier'. PDF brought to you by generated on November 18.24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 'title' => new Assert\Length(array('min' => 10)). } } else { echo 'The book is valid'. new Assert\Length(array('min' => 10))). new Assert\NotBlank()).class_metadata_factory']->getMetadataFor('Author')."\n". if (count($errors) > 0) { foreach ($errors as $error) { echo $error->getPropertyPath(). $errors = $app['validator']->validate($book). $metadata->addPropertyConstraint('title'. new Assert\Length(array('min' => 10))). 2 use Symfony\Component\Validator\Constraints as Assert.mapping. $book->title = 'My Book'. } Validating Objects If you want to add validations to a class. $metadata->addPropertyConstraint('author'.$error->getMessage(). new Assert\Valid()). 2015 Chapter 26: ValidatorServiceProvider | 80 . if (count($errors) > 0) { foreach ($errors as $error) { echo $error->getPropertyPath(). new Assert\Length(array('min' => 10))). $metadata->addPropertyConstraint('first_name'. public $last_name.' '. } } class Author { public $first_name. Use addGetterConstraint() to add constraints on getter methods and addConstraint() to add constraints on the class itself. $metadata->addPropertyConstraint('last_name'. } } $app->get('/validate/{email}'. new Assert\Length(array('min' => 10))). static public function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('first_name'.3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Book { public $title. $metadata->addPropertyConstraint('author'. function ($email) use ($app) { $author = new Author(). new Assert\Length(array('min' => 10))). $book = new Book(). $errors = $app['validator']->validate($book). static public function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('title'. $book->author = $author.$error->getMessage()."\n". $author->first_name = 'Fabien'. you can use the translator provider and register the messages under the validators domain: PDF brought to you by generated on November 18. Translation To be able to translate the error messages. public $author. } }). new Assert\Valid()). $book->title = 'My Book'. new Assert\NotBlank()). } } else { echo 'The author is valid'. $author->last_name = 'Potencier'. 5 ). 2015 Chapter 26: ValidatorServiceProvider | 81 . 6 ).html PDF brought to you by generated on November 18. 2.com/doc/master/book/validation.' => 'Cette valeur doit être un nombre. For more information. consult the Symfony Validation documentation2.'. http://symfony. 7 ).domains'] = array( 2 'validators' => array( 3 'fr' => array( 4 'This value should be a valid number.Listing 26-7 1 $app['translator. Chapter 27 FormServiceProvider The FormServiceProvider provides a service for building forms in your application with the Symfony Form component. • form. Defaults to md5(__DIR__).4+.symfony. Services • form. http://api. to prevent hijacking of your forms. Registering Listing 27-1 1 use Silex\Provider\FormServiceProvider. 2 3 $app->register(new FormServiceProvider()).csrf_provider: An instance of an implementation of CsrfProviderInterface2 for Symfony 2.html 2.com/master/Symfony/Component/Form/FormFactory. 1. 2015 Chapter 27: FormServiceProvider | 82 .secret: This secret value is used for generating and validating the CSRF token for a specific page. http://api.3 or CsrfTokenManagerInterface3 for Symfony 2.symfony.com/2.com/2.factory: An instance of FormFactory1. Parameters • form.html 3. that is used to build a form.html PDF brought to you by generated on November 18. http://api.3/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.symfony. It is very important for you to set this value to a static randomly generated value.7/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface. 6 ). function (Request $request) use ($app) { 2 // some default data for when the form is displayed the first time 3 $data = array( 4 'name' => 'Your name'. 7 8 $form = $app['form. do not forget to register the Validator provider. If you are using Composer.factory service. 14 )) 15 ->getForm(). if you didn't do that already. If you want to use validation with forms. The Symfony Form Component and all its dependencies (optional or not) comes with the "fat" Silex archive but not with the regular one. it's fine: a default one will be used.If you don't want to create your own form layout. $data) 9 ->add('name') 10 ->add('email') 11 ->add('gender'. 16 17 $form->handleRequest($request). 21 22 // do something with the data PDF brought to you by generated on November 18. 2015 Chapter 27: FormServiceProvider | 83 . the Translation component in order for the bridge to work: Listing 27-5 1 composer require symfony/twig-bridge symfony/translation Usage The FormServiceProvider provides a form. 5 'email' => 'Your email'. array( 12 'choices' => array(1 => 'male'. 18 19 if ($form->isValid()) { 20 $data = $form->getData(). you must also add a dependency to the symfony/config and symfony/translation components: Listing 27-3 1 composer require symfony/validator symfony/config symfony/translation The Symfony Security CSRF component is used to protect forms against CSRF attacks (as of Symfony 2. Make sure to install. 'choice'. 2 => 'female').4+): Listing 27-4 1 composer require symfony/security-csrf If you want to use forms in your Twig templates. Here is a usage example: Listing 27-6 1 $app->match('/form'.factory']->createBuilder('form'. you can also install the Symfony Twig Bridge. 13 'expanded' => true. But you will have to register the translation provider as the default form layout requires it. add it as a dependency: Listing 27-2 1 composer require symfony/form If you are going to use the validation extension with forms. array( 'choices' => array(1 => 'male'. 30 }).types: Listing 27-9 1 $app['form. $app->register(new Silex\Provider\TranslationServiceProvider(). function 2 ($extensions) use ($app) { 3 $extensions[] = new YourTopFormExtension().23 24 // redirect somewhere 25 return $app->redirect('. array( 'translator. 2 => 'female'). function ($types) use ($app) { 2 $types[] = new YourFormType(). new Assert\Length(array('min' => 5))) )) ->add('email'. )). 'choice'. $app->register(new Silex\Provider\ValidatorServiceProvider()). You can register form types by extending form. 5 })). 'constraints' => new Assert\Choice(array(1.twig form template (requires symfony/twig-bridge): Listing 27-7 1 <form action="#" method="post"> 2 {{ form_widget(form) }} 3 4 <input type="submit" name="submit" /> 5 </form> If you are using the validator provider.types'] = $app->share($app->extend('form.extensions'.. 'expanded' => true.. 4 5 PDF brought to you by generated on November 18. 'text'.types'.factory']->createBuilder('form') ->add('name'. 2015 Chapter 27: FormServiceProvider | 84 . you can also add validation to your form by adding constraints on the fields: Listing 27-8 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 use Symfony\Component\Validator\Constraints as Assert. array( 'constraints' => new Assert\Email() )) ->add('gender'. 2)). 3 4 return $types. 'text'. )) ->getForm(). You can register form extensions by extending form. And here is the index.extensions'] = $app->share($app->extend('form.extensions: Listing 27-10 1 $app['form. $form = $app['form. array('form' => $form->createView())).domains' => array().').twig'. 26 } 27 28 // display the form 29 return $app['twig']->render('index. array( 'constraints' => array(new Assert\NotBlank(). consult the Symfony Forms documentation4.com/doc/2.type.type. http://symfony. })). 4 5 return $extensions. 2015 Chapter 27: FormServiceProvider | 85 . function 2 ($extensions) use ($app) { 3 $extensions[] = new YourFormTypeExtension(). 4. You can register form type guessers by extending form.return $extensions.extensions'.guessers: Listing 27-12 1 $app['form.3/book/forms. Listing 27-13 1 $app->form($data). })).type.type.guessers'] = $app->share($app->extend('form.guessers'.extensions'] = $app->share($app->extend('form.type. For more information. Traits Silex\Application\FormTrait adds the following shortcuts: • form: Creates a FormBuilder instance. 4 5 return $guessers. function 2 ($guessers) use ($app) { 3 $guessers[] = new YourFormTypeGuesser().html PDF brought to you by generated on November 18.type.extensions: Listing 27-11 1 $app['form. You can register form type extensions by extending form. })). http://api.store: An instance of Store4.symfony.symfony.symfony. http://api.options (optional): An array of options for the HttpCache1 constructor. http://api. array( 2 'http_cache.html 3. that implements all the logic for storing cache metadata (Request and Response headers). Registering Listing 28-1 1 $app->register(new Silex\Provider\HttpCacheServiceProvider().com/master/Symfony/Component/HttpKernel/HttpCache/Store.com/master/Symfony/Component/HttpKernel/HttpCache/HttpCache. 3 )). 1.cache_dir: The cache directory to store the HTTP cache data.Chapter 28 HttpCacheServiceProvider The HttpCacheServiceProvider provides support for the Symfony Reverse Proxy.'/cache/'. • http_cache. that implements the ESI capabilities to Request and Response instances. http://api. Services • http_cache: An instance of HttpCache2. • http_cache.html PDF brought to you by generated on November 18.symfony.com/master/Symfony/Component/HttpKernel/HttpCache/Esi. • http_cache.esi: An instance of Esi3.html 4. Parameters • http_cache.cache_dir' => __DIR__. 2015 Chapter 28: HttpCacheServiceProvider | 86 .com/master/Symfony/Component/HttpKernel/HttpCache/HttpCache.html 2. 1')). 7 }). array( 5 'Cache-Control' => 's-maxage=5'. you will need to whitelist it as documented in Trusting Proxies5. function() { 4 return new Response('Foo'. The Symfony reverse proxy acts much like any other proxy would. 2015 Chapter 28: HttpCacheServiceProvider | 87 .com/doc/current/components/http_foundation/trusting_proxies. function() { $response = new Response(<<<EOF <html> <body> Hello <esi:include src="/included" /> </body> </html> EOF . http://symfony.Usage Silex already supports any reverse proxy like Varnish out of the box by setting Response HTTP cache headers: Listing 28-2 1 use Symfony\Component\HttpFoundation\Response. return $response. This provider allows you to use the Symfony reverse proxy natively with Silex applications by using the http_cache service. )). 2 3 $app->get('/'. '::1')). 2 3 Request::setTrustedProxies(array('127.0. array( 'Surrogate-Control' => 'content="ESI/1. }). 4 $app['http_cache']->run(). 200.0. 5. 6 )). so you will want to whitelist it: Listing 28-4 1 use Symfony\Component\HttpFoundation\Request. The provider also provides ESI support: Listing 28-5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $app->get('/'. 4 $app->run().1'.0. If you would be running Varnish in front of your application on the same machine: Listing 28-3 1 use Symfony\Component\HttpFoundation\Request. $response->setTtl(20). 2 3 Request::setTrustedProxies(array('127.0. If you want Silex to trust the X-Forwarded-For* headers from your reverse proxy at address $ip.0"'. 200.html PDF brought to you by generated on November 18. If you are not using the Symfony Session provider. set your application debug to true. If your application doesn't use ESI. For more information. 2015 Chapter 28: HttpCacheServiceProvider | 88 .cache_limiter setting to an empty value to avoid the default PHP behavior. 3 'http_cache.esi' => null.20 $app->get('/included'. 4 )). 23 24 return $response. check that your Web server does not override your caching strategy. consult the Symfony HTTP Cache documentation6. you can disable it to slightly improve the overall performance: Listing 28-6 1 $app->register(new Silex\Provider\HttpCacheServiceProvider(). 25 }). http://symfony. Symfony automatically adds a X-Symfony-Cache header to each response with useful information about cache hits and misses.'/cache/'. array( 2 'http_cache. Finally. 22 $response->setTtl(5). 6.cache_dir' => __DIR__.com/doc/current/book/http_cache. you might want to set the PHP session.html PDF brought to you by generated on November 18. To help you debug caching issues. function() { 21 $response = new Response('Foo'). 26 27 $app['http_cache']->run(). Chapter 29 HttpFragmentServiceProvider The HttpFragmentServiceProvider provides support for the Symfony fragment sub-framework, which allows you to embed fragments of HTML in a template. This service provider only work with Symfony 2.4+. 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 29-1 1 $app->register(new Silex\Provider\HttpFragmentServiceProvider()); 1. http://api.symfony.com/master/Symfony/Component/HttpKernel/Fragment/FragmentHandler.html PDF brought to you by generated on November 18, 2015 Chapter 29: HttpFragmentServiceProvider | 89 Usage This section assumes that you are using Twig for your templates. 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: Listing 29-2 1 The main page content. 2 3 {{ render('/foo') }} 4 5 The main page content resumes here. 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): Listing 29-3 1 {{ render(url('route_name')) }} 2 3 {{ render_esi(url('route_name')) }} 4 5 {{ render_hinclude(url('route_name')) }} PDF brought to you by generated on November 18, 2015 Chapter 29: HttpFragmentServiceProvider | 90 Chapter 30 SecurityServiceProvider The SecurityServiceProvider manages authentication and authorization for your applications. Parameters • security.hide_user_not_found (optional): Defines whether to hide user not found exception or not. Defaults to true. Services • security: The main entry point for the security provider. Use it to get the current user token (only for Symfony up to 2.5). • security.token_storage: Gives access to the user token (Symfony 2.6+). • security.authorization_checker: Allows to check authorizations for the users (Symfony 2.6+). • security.authentication_manager: An instance of AuthenticationProviderManager1, responsible for authentication. • security.access_manager: An instance of AccessDecisionManager2, 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 errors when given a Request object. • security.encoder_factory: Defines the encoding strategies for user passwords (default to use a digest algorithm for all users). • security.encoder.digest: The encoder to use by default for all users. • user: Returns the current user 1. http://api.symfony.com/master/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.html 2. http://api.symfony.com/master/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.html PDF brought to you by generated on November 18, 2015 Chapter 30: SecurityServiceProvider | 91 The Symfony Security Component comes with the "fat" Silex archive but not with the regular one. Usage The Symfony Security component is powerful.html PDF brought to you by generated on November 18.The service provider defines many other services that are used internally but rarely need to be customized. 2015 Chapter 30: SecurityServiceProvider | 92 . If you are using Composer.firewalls' => // see below 3 )). http://symfony. read the Symfony Security documentation3.com/doc/2. To learn more about it. you need to enable SessionServiceProvider.3/book/security. don't forget to call boot() first: Listing 30-3 1 $app->boot(). When a security configuration does not behave as expected. if you want to use it outside of the handling of a request. add it as a dependency: Listing 30-2 1 composer require symfony/security The security features are only available after the Application has been booted. array( 2 'security. If you're using a form to authenticate users. Below is a list of recipes that cover some common use cases. Accessing the current User The current user information is stored in a token that is accessible via the security service: Listing 30-4 3. Registering Listing 30-1 1 $app->register(new Silex\Provider\SecurityServiceProvider(). So. enable logging (with the Monolog extension for instance) as the Security Component logs a lot of interesting information about what it does and why. symfony. If the user is known. 8 '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='). the client IP. Securing a Path with HTTP Authentication The following configuration uses HTTP basic authentication to secure URLs under /admin/: Listing 30-6 1 $app['security. 5 'users' => array( 6 // raw password is foo 7 'admin' => array('ROLE_ADMIN'.com/master/Symfony/Component/Security/Core/User/UserInterface.html PDF brought to you by generated on November 18. • The user encoded password. If you want to restrict the firewall by more than the URL pattern (like the HTTP method.token_storage']->getToken(). 10 ). or any Request attributes). use an instance of a RequestMatcher5 for the pattern option: Listing 30-7 1 use Symfony/Component/HttpFoundation/RequestMatcher.firewalls'] = array( 2 'admin' => array( 3 'pattern' => '^/admin'. 4 'http' => true. http://api.1 2 3 4 5 // Symfony 2. Each user is defined with the following information: • The role or an array of roles for the user (roles are strings beginning with ROLE_ and ending with anything you want). you can get it with a call to getUser(): Listing 30-5 1 if (null !== $token) { 2 $user = $token->getUser().firewalls'] = array( 4 'admin' => array( 5 'pattern' => new RequestMatcher('^/admin'. or an instance of UserInterface4.html 5. http://api. ).5 $token = $app['security']->getToken(). the token is null. 7 ).com/master/Symfony/Component/HttpFoundation/RequestMatcher. the hostname. 3 } The user can be a string. 'POST'). 4. The pattern is a regular expression on the URL path.symfony. // Symfony 2. the http setting tells the security layer to use HTTP basic authentication and the users entry defines valid users. 'example. 6 // ... 2015 Chapter 30: SecurityServiceProvider | 93 . 2 3 $app['security. 9 ). If there is no information about the user.6+ $token = $app['security. 8 ).3/2. an object with a __toString() method.com'. http://api. Always keep in mind the following two golden rules: • The login_path path must always be defined outside the secured area (or if it is in the secured area. To generate a valid encoded password from a raw password.+)$ RewriteRule . 9 ).* .encoder_factory']->getEncoder($user).html PDF brought to you by generated on November 18.see below). The default configuration of the extension enforces encoded passwords.L] Securing a Path with a Form Using a form to authenticate users is very similar to the above configuration. 5 'users' => array( 6 'admin' => array('ROLE_ADMIN'.symfony.*)$ app. ). When the user is authenticated. 'check_path' => '/admin/login_check'). the anonymous authentication mechanism must be enabled -. 8 ).[E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.php [QSA.firewalls'] = array( 2 'admin' => array( 3 'pattern' => '^/admin/'. • check_path: The check URL used by Symfony to validate the credentials of the user. 6.com/master/Symfony/Component/Security/Core/User/User. 7 '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='). Instead of using the http setting. 2015 Chapter 30: SecurityServiceProvider | 94 . use the security. the user stored in the token is an instance of User6 If you are using php-cgi under Apache. use the form one and define these two parameters: • 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. you need to add this configuration to make things work correctly: Listing 30-9 1 2 3 4 5 RewriteEngine On RewriteCond %{HTTP:Authorization} ^(.encoder_factory service: Listing 30-8 1 2 3 4 5 // find the encoder for a UserInterface instance $encoder = $app['security. $user->getSalt()). 4 'form' => array('login_path' => '/login'. // compute the encoded password for foo $password = $encoder->encodePassword('foo'.All users must at least have one role associated with them. • The check_path path must always be defined inside the secured area. Here is how to secure all URLs under /admin/ with a form: Listing 30-10 1 $app['security. *$'. 2015 Chapter 30: SecurityServiceProvider | 95 . 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. It's also useful when you want to secure all URLs except the login form: Listing 30-13 1 $app['security. ).html'.firewalls'] = array( 2 'login' => array( 3 'pattern' => '^/login$'. 7 'form' => array('login_path' => '/login'. 8 'users' => array( 9 'admin' => array('ROLE_ADMIN'. Create the associated template: Listing 30-12 1 <form action="{{ path('admin_login_check') }}" method="post"> 2 {{ error }} 3 <input type="text" name="_username" value="{{ last_username }}" /> 4 <input type="password" name="_password" value="" /> 5 <input type="submit" /> 6 </form> 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). and then it secures all other URLs. Configuring several firewalls is useful when you want to secure different parts of your website with different authentication strategies or for different users (like using an HTTP basic authentication for the website API and a form to secure your website administration area).last_username'). PDF brought to you by generated on November 18. The above configuration first ensures that the /login URL is not secured (no authentication settings). 5 'secured' => array( 6 'pattern' => '^.last_error']($request). 11 ). 6 'last_username' => $app['session']->get('_security. 8 }). 'check_path' => '/login_check'). 4 ).For the login form to work. The order of the firewall configurations is significant as the first one to match wins. 12 ). 7 )). create a controller like the following: Listing 30-11 1 use Symfony\Component\HttpFoundation\Request. function(Request $request) use ($app) { 4 return $app['twig']->render('login. 2 3 $app->get('/login'. 10 '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='). Defining more than one Firewall You are not limited to define one firewall per project. array( 5 'error' => $app['security. 5 'wsse' => true. enabled the anonymous authentication mechanism: Listing 30-17 1 $app['security. 6 ). 4 'form' => array('login_path' => '/login'. the user information are not available in non-secured areas. Adding a Logout When using a form for authentication. 6 7 // . 9 ). 'check_path' => '/admin/login_check').. if the user is not authenticated. you can let users log out if you add the logout setting. a user will always be accessible from the security context. 8 ). Checking User Roles To check if a user is granted some role.You can toggle all registered authentication mechanisms for a particular area on and off with the security flag: Listing 30-14 1 $app['security. When enabling the anonymous setting. string.firewalls'] = array( 2 'api' => array( 3 'pattern' => '^/api'. 2015 Chapter 30: SecurityServiceProvider | 96 .. A route is automatically generated. 7 ).. 4 5 // . 9 ). based on the configured path (all / are replaced with _ and the leading / is stripped): Listing 30-16 1 <a href="{{ path('admin_logout') }}">Logout</a> Allowing Anonymous Users When securing only some parts of your website. it returns the anon. 8 ). 6 7 // .. 5 'logout' => array('logout_path' => '/admin/logout'. use the isGranted() method on the security context: PDF brought to you by generated on November 18.. 4 'security' => $app['debug'] ? false : true. To make the user accessible in such areas. where logout_path must match the main firewall pattern: Listing 30-15 1 $app['security.. 'invalidate_session' => true).firewalls'] = array( 2 'secured' => array( 3 'pattern' => '^/admin/'.firewalls'] = array( 2 'unsecured' => array( 3 'anonymous' => true. . 'role' => 4 'ROLE_ALLOWED_TO_SWITCH').6+ if ($app['security... 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: Listing 30-22 PDF brought to you by generated on November 18.authorization_checker']->isGranted('ROLE_ADMIN')) { // .. Don't use the getRoles() method to check user roles.3/2. isGranted() throws an exception when no authentication information is available (which is the case on non-secured area). enable the switch_user authentication strategy: Listing 30-21 1 $app['security. Impersonating a User If you want to be able to switch to another user (without knowing the user credentials).5 if ($app['security']->isGranted('ROLE_ADMIN')) { // .firewalls'] = array( 2 'unsecured' => array( 3 'switch_user' => array('parameter' => '_switch_user'.. } You can check roles in Twig templates too: Listing 30-19 1 {% if is_granted('ROLE_ADMIN') %} 2 <a href="/secured?_switch_user=fabien">Switch to Fabien</a> 3 {% endif %} You can check if a user is "fully authenticated" (not an anonymous user for instance) with the special IS_AUTHENTICATED_FULLY role: Listing 30-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. ). 7 ). 5 6 // . 2015 Chapter 30: SecurityServiceProvider | 97 . } // Symfony 2.Listing 30-18 1 2 3 4 5 6 7 8 9 // Symfony 2.. 3 <a href="?_switch_user=_exit"> exit</a> the switch. 'ROLE_ALLOWED_TO_SWITCH'). but you can override this default mechanism with you own. This is useful for instance to allow the user to switch back to their primary account: Listing 30-23 1 {% if is_granted('ROLE_PREVIOUS_ADMIN') %} 2 You are an admin but you've switched to another user. 'https'). Furthermore.symfony. 'ROLE_USER'). The first argument can also be a RequestMatcher7 instance. 2015 Chapter 30: SecurityServiceProvider | 98 .html PDF brought to you by generated on November 18. 3 array('^.1 {% if is_granted('ROLE_ALLOWED_TO_SWITCH') %} 2 <a href="?_switch_user=fabien">Switch to user Fabien</a> 3 {% endif %} You can check that you are impersonating a user by checking the special ROLE_PREVIOUS_ADMIN. 'ROLE_ADMIN'. users must have the ROLE_ADMIN to access the /admin section of the website. 3 ). The users setting can be defined as a service that returns an instance of UserProviderInterface8: Listing 30-26 7. 4 {% endif %} Defining a Role Hierarchy Defining a role hierarchy allows to automatically grant users some additional roles: Listing 30-24 1 $app['security. 4 ).access_rules'] = array( 2 array('^/admin'.role_hierarchy'] = array( 2 'ROLE_ADMIN' => array('ROLE_USER'. http://api.com/master/Symfony/Component/Security/Core/User/UserProviderInterface.com/master/Symfony/Component/HttpFoundation/RequestMatcher. With this configuration. but they can also be used to further secure some areas by defining access rules: Listing 30-25 1 $app['security. all users with the ROLE_ADMIN role also automatically have the ROLE_USER and ROLE_ALLOWED_TO_SWITCH roles. http://api.html 8. the user will be automatically redirected). Defining a custom User Provider Using an array of users is simple and useful when securing an admin section of a personal website. Defining Access Rules Roles are a great way to adapt the behavior of your website depending on groups of users. With the above configuration.symfony.*$'. the admin section can only be accessible via HTTPS (if that's not the case. and ROLE_USER for everything else. the only requirement is that the class must implement UserInterface9 And here is the code that you can use to create the database schema and some sample users: 9. true.symfony. explode('. array(strtolower($username))). true). where Doctrine DBAL is used to store the users: Listing 30-27 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 use use use use use use Symfony\Component\Security\Core\User\UserProviderInterface. instances of the default User class are created for the users. $username)). get_class($user))). Here is a simple example of a user provider. $user['roles']). Symfony\Component\Security\Core\Exception\UnsupportedUserException.'.html PDF brought to you by generated on November 18. public function __construct(Connection $conn) { $this->conn = $conn. http://api. true. true.1 'users' => $app->share(function () use ($app) { 2 return new UserProvider($app['db']). 2015 Chapter 30: SecurityServiceProvider | 99 .'. Doctrine\DBAL\Connection.com/master/Symfony/Component/Security/Core/User/UserInterface. class UserProvider implements UserProviderInterface { private $conn. } public function refreshUser(UserInterface $user) { if (!$user instanceof User) { throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported. $user['password']. } public function supportsClass($class) { return $class === 'Symfony\Component\Security\Core\User\User'. 3 }). Symfony\Component\Security\Core\User\User. Symfony\Component\Security\Core\User\UserInterface. } } In this example.'. } return $this->loadUserByUsername($user->getUsername()). Symfony\Component\Security\Core\Exception\UsernameNotFoundException. } public function loadUserByUsername($username) { $stmt = $this->conn->executeQuery('SELECT * FROM users WHERE username = ?'. but you can define your own class. if (!$user = $stmt->fetch()) { throw new UsernameNotFoundException(sprintf('Username "%s" does not exist. } return new User($user['username']. array( 'username' => 'fabien'. 'autoincrement' => true)).). 'integer'. $users->addColumn('password'. $users->addColumn('id'.encoder. Silex uses the sha512 algorithm to encode passwords. array('length' $users->addColumn('roles'. HTTP. => 32)). array( 'username' => 'admin'. $app['db']->insert('users'.encoder. array('length' => true. remember me.digest'] = $app->share(function ($app) { 4 // use the sha1 algorithm 5 // don't base64 encode the password 6 // use only 1 iteration 7 return new MessageDigestPasswordEncoder('sha1'. You can change these defaults by overriding the security. 'roles' => 'ROLE_ADMIN' )).factory. 8 }). Additionally. $schema = $app['db']->getSchemaManager(). 255)). 1).XXX where XXX is the name you want to use in your configuration: PDF brought to you by generated on November 18. array('unsigned' => $users->setPrimaryKey(array('id')). 2 3 $app['security. array('length' $users->addUniqueIndex(array('username')). the password is encoded multiple times and converted to base64.. false. the Symfony bridge for Doctrine provides a user provider class that is able to load users from your entities. $users->addColumn('username'. Defining a custom Authentication Provider The Symfony Security component provides a lot of ready-to-use authentication providers (form. if (!$schema->tablesExist('users')) { $users = new Table('users'). 'string'. $app['db']->insert('users'.authentication_listener. 'roles' => 'ROLE_USER' )). 'password' => '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='. but you can add new ones easily. 'string'.digest service: Listing 30-29 1 use Symfony\Component\Security\Core\Encoder\MessageDigestPasswordEncoder.Listing 30-28 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 use Doctrine\DBAL\Schema\Table. X509. 'string'. 'password' => '5FZ2Z8QIkA7UTZ4BYkoC+GsReLf569mSKDsfods6LYQ8t+a8EW9oaircfMpmaLbPBh4FOBiiFyLfuZmTSUwzZg=='. .. To register a new authentication provider. create a service named security. $schema->createTable($users). } If you are using the Doctrine ORM. Defining a custom Encoder By default. 2015 Chapter 30: SecurityServiceProvider | 100 . => 255)). token_storage' on Symfony <2. However.authentication_provider.$name..'. $app['security. __DIR__. // the authentication listener id 'security. you can also define an array of options that customize the behavior of your authentication factory. Instead of true.factory. HTTP authentication.$name.'.Listing 30-30 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $app['security.wsse'] = $app->share(function () use ($app) { return new WsseProvider($app['security.token_storage'].firewalls'] = array( 2 'default' => array( 10. $options) use ($app) { // define the authentication provider object $app['security. array( 2 'security. return array( // the authentication provider id 'security.'. You can now use it in your configuration like any other built-in authentication provider: Listing 30-31 1 $app->register(new Silex\Provider\SecurityServiceProvider().wsse'] = $app->protect(function ($name. the credentials are sent for each request. This example uses the authentication provider classes as described in the Symfony cookbook10. WSSE and so on. a session cookie is created to persist the security context of the user.wsse'] = $app->share(function () use ($app) { // use 'security' instead of 'security. }).firewalls' => array( 3 'default' => array( 4 'wsse' => true.authentication_manager']).com/doc/current/cookbook/security/custom_authentication_provider.default']. 7 ).wsse'.'/security_cache'). 5 6 // . http://symfony.user_provider. 8 ). if you use certificates.authentication_provider.authentication_listener. }). }). // the entry point id null.$name.authentication_listener. // the position of the listener in the stack 'pre_auth' ).'. it will be passed as the second argument of your authentication factory (see above).authentication_listener.$name.html PDF brought to you by generated on November 18..'. 9 )).6 return new WsseListener($app['security. Stateless Authentication By default.wsse'. // define the authentication listener object $app['security. In that case. 2015 Chapter 30: SecurityServiceProvider | 101 .'. you can turn off persistence by activating the stateless authentication flag: Listing 30-32 1 $app['security.'.'. The Silex\Route\SecurityTrait must be used with a user defined Route class.3 4 5 6 7 8 ). class MyRoute extends Route { use Route\SecurityTrait. Silex\Route\SecurityTrait adds the following methods to the controllers: • secure: Secures a controller for the given roles. PDF brought to you by generated on November 18. not the application. 2015 Chapter 30: SecurityServiceProvider | 102 . Listing 30-33 1 $user = $app->user(). } 1 $app['route_class'] = 'MyRoute'.. Traits Silex\Application\SecurityTrait adds the following shortcuts: • encodePassword: Encode a given password. function () { 2 // do something but only for admins 3 })->secure('ROLE_ADMIN'). 'foo'). 2 3 $encoded = $app->encodePassword($user.. Listing 30-35 Listing 30-36 1 2 3 4 5 6 use Silex\Route. Listing 30-34 1 $app->get('/'. 'wsse' => true. ). // . 'stateless' => true. 7 'form' => true. Parameters n/a Services n/a The service provider defines many other services that are used internally but rarely need to be customized.Chapter 31 RememberMeServiceProvider The RememberMeServiceProvider adds "Remember-Me" authentication to the SecurityServiceProvider. 11 'always_remember_me' => true. Registering Before registering this service provider. 9 'remember_me' => array( 10 'key' => 'Choose_A_Unique_Random_Key'. 3 4 $app['security. 2015 Chapter 31: RememberMeServiceProvider | 103 . 2 $app->register(new Silex\Provider\RememberMeServiceProvider()).firewalls'] = array( 5 'my-firewall' => array( 6 'pattern' => '^/secure$'. 8 'logout' => true. you must register the SecurityServiceProvider: Listing 31-1 1 $app->register(new Silex\Provider\SecurityServiceProvider()). PDF brought to you by generated on November 18. You can find more information in the Symfony cookbook1 (default: _remember_me).com/doc/current/cookbook/security/remember_me. To add the checkbox to the login form. name: Cookie name (default: REMEMBERME). path: Cookie path (default: /). remember_me_parameter: Name of the request parameter enabling remember_me on login. */ ).12 13 14 15 16 ). 2015 Chapter 31: RememberMeServiceProvider | 104 .. httponly: Cookie is HTTP only (default: true). lifetime: Cookie lifetime (default: 31536000 ~ 1 year).html PDF brought to you by generated on November 18.. /* Other options */ ). secure: Cookie is secure (default: false). 1. 'users' => array( /* . ). Options • • • • • • • • • key: A secret key to generate tokens (you should generate a random string). domain: Cookie domain (default: null = request domain). always_remember_me: Enable remember me (default: false). http://symfony. symfony. • serializer.com/master/Symfony/Component/Serializer/Encoder/JsonEncoder.html 5.com/master/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.symfony.com/master/Symfony/Component/Serializer/Serializer. 2015 Chapter 32: SerializerServiceProvider | 105 .symfony. Services • serializer: An instance of Symfony\Component\Serializer\Serializer1.com/master/Symfony/Component/Serializer/Normalizer/CustomNormalizer.com/master/Symfony/Component/Serializer/Encoder/XmlEncoder. http://api.symfony.encoders: Symfony\Component\Serializer\Encoder\JsonEncoder2 and Symfony\Component\Serializer\Encoder\XmlEncoder3. • serializer. http://api. 1.Chapter 32 SerializerServiceProvider The SerializerServiceProvider provides a service for serializing objects.symfony. http://api.html 2.normalizers: Symfony\Component\Serializer\Normalizer\CustomNormalizer4 and Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer5.html 4. Registering Listing 32-1 1 $app->register(new Silex\Provider\SerializerServiceProvider()). http://api.html 3. http://api. Parameters None.html PDF brought to you by generated on November 18. // only accept content types supported by the serializer via the assert method. $app = new Application(). http://symfony. "\d+"). $app->register(new SerializerServiceProvider()).com/doc/current/components/serializer. function ($id) use ($app) { // assume a page_repository service exists that returns Page objects. $page = $app['page_repository']->find($id). $format). })->assert("_format". add it as a dependency: Listing 32-2 1 composer require symfony/serializer Usage The SerializerServiceProvider provider provides a serializer service: Listing 32-3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 use Silex\Application. } return new Response($app['serializer']->serialize($page. If you are using Composer.The SerializerServiceProvider relies on Symfony's Serializer Component6. The // object returned has getters and setters exposing the state. which comes with the "fat" Silex archive but not with the regular one. use Silex\Provider\SerializerServiceProvider. 2015 Chapter 32: SerializerServiceProvider | 106 . $app->get("/pages/{id}. 200. $format = $app['request']->getRequestFormat(). array( "Content-Type" => $app['request']->getMimeType($format) )). if (!$page instanceof Page) { $app->abort("No page found for id: $id"). "xml|json") ->assert("id". use Symfony\Component\HttpFoundation\Response.html PDF brought to you by generated on November 18. 6.{_format}". but with a bit of work. Because your dependencies are clearly defined. the ServiceControllerServiceProvider simply extends the existing resolver service. they are easily mocked. By keeping careful control of your dependencies. your controllers start to become more independent of the framework you are using. Why would I want to do this? • Dependency Injection over Service Location Using this method. Services There are no extra services provided. Parameters There are currently no parameters for the ServiceControllerServiceProvider. Carefully crafted. allowing you to test your controllers in isolation. PDF brought to you by generated on November 18. you can inject the actual dependencies required by your controller and gain total inversion of control.Chapter 33 ServiceControllerServiceProvider As your Silex application grows. your controllers could easily become compatible with Silex. Symfony (full stack) and Drupal. • Framework Independence Using this method. Silex can use controller classes out of the box. while still maintaining the lazy loading of your controllers and its dependencies. your controllers can be created as services. giving you the full power of dependency injection and lazy loading. your controllers will become reusable with multiple frameworks. you may wish to begin organizing your controllers in a more formal fashion. 2015 Chapter 33: ServiceControllerServiceProvider | 107 . to name just a few. json route to use a controller. } public function indexJsonAction() { return new JsonResponse($this->repo->findAll()). } } 1.repository']->findAll()). that is defined as a service. $app->get('/posts. you can use type hinting and parameter naming to get the parameters you need. You may also notice that the only external dependency is on Symfony\Component\HttpFoundation\JsonResponse. use Demo\Repository\PostRepository.repository'] = $app->share(function() { return new PostRepository. meaning this controller could easily be used in a Symfony (full stack) application.com/doc/master/components/http_foundation/introduction. and is easily tested/specced. }). }). we're going to change the /posts. along with an indexJsonAction method to handle the request. Usage In this slightly contrived example of a blog API. 2015 Chapter 33: ServiceControllerServiceProvider | 108 . Rewriting your controller as a service is pretty simple. public function __construct(PostRepository $repo) { $this->repo = $repo. create a Plain Ol' PHP Object with your PostRepository as a dependency. Listing 33-3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 namespace Demo\Controller. http://symfony. class PostController { protected $repo.html PDF brought to you by generated on November 18. just like with standard Silex routes. $app['posts. $app = new Application(). you may notice that this controller has well defined responsibilities and dependencies.Registering Listing 33-1 1 $app->register(new Silex\Provider\ServiceControllerServiceProvider()).json'. use Symfony\Component\HttpFoundation\JsonResponse. If you are a TDD/BDD fan (and you should be). Although not shown in the example below. use Demo\Repository\PostRepository. Listing 33-2 1 2 3 4 5 6 7 8 9 10 11 12 use Silex\Application. or potentially with other applications or frameworks that know how to handle a Symfony/HttpFoundation1 Response object. function() use ($app) { return $app->json($app['posts. followed by the method name. followed by a single colon (:). Listing 33-4 1 $app['posts.And lastly. 4 5 $app->get('/posts.controller'] = $app->share(function() use ($app) { 2 return new PostController($app['posts. "posts.json'.repository']). define your controller as a service in the application. along with your route. PDF brought to you by generated on November 18. The syntax in the route definition is the name of the service. 2015 Chapter 33: ServiceControllerServiceProvider | 109 . 3 }).controller:indexJsonAction"). 2015 Chapter 34: Webserver Configuration | 110 . Alternatively.php [QSA.L] 8 </IfModule> 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.adayinthelifeof. relative from the webroot. relative from the webroot. if you use Apache 2. make sure mod_rewrite is enabled and use the following .16 or higher.2.Chapter 34 Webserver Configuration Apache If you are using Apache.htaccess even easier: Listing 34-2 1 FallbackResource /index.htaccess file: Listing 34-1 1 <IfModule mod_rewrite. http://www.nl/2012/01/21/apaches-fallbackresource-your-new-htaccess-command/ PDF brought to you by generated on November 18.php If your site is not at the webroot level you will have to adjust the path to point to your directory.c> 2 Options -MultiViews 3 4 RewriteEngine On 5 #RewriteBase /path/to/app 6 RewriteCond %{REQUEST_FILENAME} !-f 7 RewriteRule ^ index. you can use the FallbackResource directive1 to make your . 1. php(/|$) { 13 # the ubuntu default 14 fastcgi_pass unix:/var/run/php5-fpm.php$is_args$args.php/some-path 25 # Enable the internal directive to disable URIs like this 26 # internal.log. 2015 Chapter 34: Webserver Configuration | 111 .webServer> 4 <defaultDocument> 5 <files> 6 <clear /> 7 <add value="index. 27 } 28 29 #return 404 for all php files as we do have a front controller 30 location ~ \. This will 404: 24 # http://domain.config file: Listing 34-4 1 <?xml version="1. 8 } 9 10 # If you have 2 front controllers for dev|prod use the following line instead 11 # location ~ ^/(index|index_dev)\. fallback to front controller 7 try_files $uri /index.php(/|$) { 12 location ~ ^/index\.php$ { 31 return 404. 35 access_log /var/log/nginx/project_access.php)(/. 36 } IIS If you are using the Internet Information Services from Windows.log.0"?> 2 <configuration> 3 <system. 15 # for running on centos 16 #fastcgi_pass unix:/var/run/php-fpm/www.sock.php" /> 8 </files> 9 </defaultDocument> 10 <rewrite> PDF brought to you by generated on November 18. you can use this sample web.nginx The minimum configuration to get your application running under Nginx is: Listing 34-3 1 server { 2 server_name domain.tld/index.+\.tld.domain. 21 fastcgi_param HTTPS off.tld www. 4 5 location / { 6 # try to serve file directly. 17 18 fastcgi_split_path_info ^(. 22 23 # Prevents URIs that include the front controller. 20 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name.*)$. 3 root /var/www/project/web. 19 include fastcgi_params. 32 } 33 34 error_log /var/log/nginx/project_error.sock. 7 8 "^(/[^\?]*)(\?. if (php_sapi_name() === 'cli-server' && is_file($filename)) { return false. use this sample simple-vhost as a starting point: Listing 34-5 1 server.ico$" => "$0".rewrite-once = ( 4 # configure some static files 5 "^/assets/.*)$#'.. 2015 Chapter 34: Webserver Configuration | 112 .4 PHP 5.11 <rules> 12 <rule name="Silex Front Controller" stopProcessing="true"> 13 <match url="^(.4 ships with a built-in webserver for development. 6 "^/favicon\. Assuming your front controller is at web/index. in order to serve static files.webServer> </configuration> Lighttpd If you are using lighttpd. However. you can start the server from the command-line with this command: Listing 34-7 1 $ php -S localhost:8080 -t web web/index.+" => "$0".php$1$2" 9 ) PHP 5. This server allows you to run silex without any configuration. $_SERVER['REQUEST_URI']). you'll have to make sure your front controller returns false in that case: Listing 34-6 1 2 3 4 5 6 7 8 9 // web/index.*)?" => "/index. } $app = require __DIR__. $app->run().php. ''.document-root = "/path/to/app" 2 3 url.php Now the application should be running at http://localhost:8080.php" appendQueryString="true" /> 19 </rule> 20 </rules> 21 </rewrite> 22 </system.preg_replace('#(\?.php'./src/app.*)$" ignoreCase="false" /> 14 <conditions logicalGrouping="MatchAll"> 15 <add input="{REQUEST_FILENAME}" matchType="IsFile" 16 ignoreCase="false" negate="true" /> 17 </conditions> 18 <action type="Rewrite" url="index.'/. PDF brought to you by generated on November 18.php $filename = __DIR__. PDF brought to you by generated on November 18.This server is for development only. 2015 Chapter 34: Webserver Configuration | 113 . It is not recommended to use it in production. 4 (2015-09-15) • fixed some new deprecations • fixed translation registration for the validators 1.3.3.0 fixed some Form deprecations removed deprecated method call in the exception handler fixed Swiftmailer spool flushing when spool is not enabled 1.0 and Twig 2.3.3 (2015-09-08) • • • • added support for Symfony 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 PDF brought to you by generated on November 18.3.Chapter 35 Changelog 1.2 (2015-08-24) • no changes 1.3. 2015 Chapter 35: Changelog | 114 .5 (2015-XX-XX) • n/a 1. 1.7 an 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.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 is not compatible with Symfony 2.5 (2015-06-04) • no code changes (last version of the 1.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.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.2.types to the Form provider 1.4 (2015-04-11) • fixed the exception message when mounting a collection that doesn't return a ControllerCollection • fixed Symfony dependencies (Silex 1.5.2. 2015 Chapter 35: Changelog | 115 .9 1.0 (2014-03-29) • Allowed disabling the boot logic of MonologServiceProvider • Reverted "convert attributes on the request that actually exist" PDF brought to you by generated on November 18. and 2.6) • removed deprecated TwigCoreExtension class (register the new HttpFragmentServiceProvider instead) • bumped minimum version of PHP to 5.7) 1.2 branch) 1.8 (and keep compatibility with Symfony 2.2.2.2.2.3.3.3. before.hide_user_not_found" support in SecurityServiceProvider • Fixed event listeners that are registered after the boot via the on() method 1. • Dropped support for 2. • 2013-04-01: Added support for host matching with symfony 2. 1. convert. after on Controller) 1.0. after.1 and 2.2 versions of Symfony.1. before.2 (2013-10-30) • Added missing "security. so that they will not instantiate the dispatcher early. finish. on Application.1.1 (2013-10-11) • Removed or replaced deprecated Symfony code • Updated code to take advantages of 2.0.1.2 (2013-10-30) • Fixed SecurityServiceProvider to use null as a fake controller so that routes can be dumped 1.1. error.1 (2013-07-04) • Fixed RedirectableUrlMatcher::redirect() when Silex is configured to use a logger • Make DoctrineServiceProvider multi-db support lazy.0 (2013-05-03) • 2013-04-12: Added support for validators as services.3 new features • Only convert attributes on the request that actually exist.0.2: Listing 35-1 PDF brought to you by generated on November 18. 1.• [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.0 (2013-07-04) • Support for any Psr\Log\LoggerInterface as opposed to the monolog-bridge one.3 (2013-XX-XX) • Fixed translator locale management 1. after and error lazy. 2015 Chapter 35: Changelog | 116 . 1. • Made dispatcher proxy methods on. error().configure service. • 2012-07-15: removed the monolog. • 2013-02-07: Added Application::sendFile() to ease sending BinaryFileResponse. 2 $app) { 3 // do something 4 5 return $monolog.example. function($monolog. PDF brought to you by generated on November 18. })).configure'] = $app->protect(function ($monolog) use ($app) { 2 // do something 3 }). After: Listing 35-3 1 $app['monolog'] = $app->share($app->extend('monolog'. • 2013-03-08: Added support for remember-me via the RememberMeServiceProvider. • 2012-11-05: The before(). function ($user) { // user-specific action })->host('{user}. Use the extend method instead: Before: Listing 35-2 1 $app['monolog. After: $app['exception_handler']->disable(). 2015 Chapter 35: Changelog | 117 . • 2012-06-17: ControllerCollection now takes a required route instance as a constructor argument. • 2012-11-05: Filters have been renamed to application middlewares in the documentation.1 2 3 4 5 6 7 $app->match('/'. function() { // app-specific action })->host('example. • 2012-11-05: Removing the default exception handler should now be done via its disable() method: Before: unset($app['exception_handler']).com'). • 2013-03-08: Added support for form type extensions and guessers as services. after(). $app->match('/'. and finish() listener priorities now set the priority of the underlying Symfony event instead of a custom one before.com'). PDF brought to you by generated on November 18.default_locale to locale • 2012-06-16: Removed the translator. • 2012-05-26: Removed SymfonyBridgesServiceProvider. • 2012-06-17: added application traits for PHP 5. After: Listing 35-7 1 $app['twig'] = $app->share($app->extend('twig'.loader service. CssSelector. • 2012-05-20: Added a way to define settings on a controller collection. Use the extend method instead: Before: Listing 35-6 1 $app['twig.g. Finder and Process components optional dependencies. • 2012-05-21: Changed error() to allow handling specific exceptions. • 2012-06-13: Added a route before middleware • 2012-06-13: Renamed the route middleware to before • 2012-06-13: Added an extension for the Symfony Security component • 2012-05-31: Made the BrowserKit. 2015 Chapter 35: Changelog | 118 .messages parameter (use translator. • 2012-05-24: Removed the autoloader service (use composer instead). 5 })).class_path settings on all the built-in providers have also been removed in favor of Composer. function($twig.4 • 2012-06-16: renamed request. DomCrawler. • 2012-05-26: Removed the translator. • 2012-05-26: added boot() to ServiceProviderInterface.json. Projects that depend on them (e. It is now implicit by checking the existence of the bridge. 2 3 // or even better 4 $controllers = $app['controllers_factory']. through functional tests) should add those dependencies to their composer.domains instead). See documentation for how to use XLIFF or YAML-based translation files.configure'] = $app->protect(function ($twig) use ($app) { 2 // do something 3 }). • 2012-06-15: removed the twig.Before: Listing 35-4 1 $controllers = new ControllerCollection(). $app) { 2 // do something 3 4 return $twig. The *. After: Listing 35-5 1 $controllers = new ControllerCollection(new Route()).configure service. 3 4 return $app. • 2012-01-02: Introduced support for streaming responses. 2015 Chapter 35: Changelog | 119 . • 2011-09-22: The way reusable applications work has changed. • 2012-02-27: Updated to Symfony 2. After: Listing 35-10 1 $app = new ControllerCollection(). • 2011-08-08: The controller method configuration is now done on the Controller itself Before: Listing 35-11 1 $app->match('/'. • 2011-09-22: ExtensionInterface has been renamed to ServiceProviderInterface. function () { echo 'foo'. • 2012-03-02: Switched to use Composer for dependency management. 2 $app->get('/bar'. function() { return 'foo'. })->method('GET|POST'). 2 $app->get('/bar'. All built-in extensions have been renamed accordingly (for instance. • 2012-03-11: Added route middlewares.• 2012-05-20: The Request instance is not available anymore from the Application after it has been handled. function() { return 'foo'. • 2012-03-20: Added json helper: Listing 35-8 1 $data = array('some' => 'data'). The mount() method now takes an instance of ControllerCollection instead of an Application one. }). }. • 2012-04-01: Added finish filters. Before: Listing 35-9 1 $app = new Application(). function () { echo 'foo'. }). 2 $response = $app->json($data). 3 4 return $app. Silex\Extension\TwigExtension has been renamed to Silex\Provider\TwigServiceProvider). 'GET|POST'). PDF brought to you by generated on November 18.1 session handling. After: Listing 35-12 1 $app->match('/'.
Copyright © 2024 DOKUMEN.SITE Inc.