1Usage 2===== 3 4Installation 5------------ 6 7If you want to get started fast, use the `Silex Skeleton`_: 8 9.. code-block:: bash 10 11 composer create-project fabpot/silex-skeleton path/to/install "~2.0" 12 13If you want more flexibility, use Composer_ instead: 14 15.. code-block:: bash 16 17 composer require silex/silex:~2.0 18 19Web Server 20---------- 21 22All examples in the documentation rely on a well-configured web server; read 23the :doc:`webserver documentation<web_servers>` to check yours. 24 25Bootstrap 26--------- 27 28To bootstrap Silex, all you need to do is require the ``vendor/autoload.php`` 29file and create an instance of ``Silex\Application``. After your controller 30definitions, call the ``run`` method on your application:: 31 32 // web/index.php 33 require_once __DIR__.'/../vendor/autoload.php'; 34 35 $app = new Silex\Application(); 36 37 // ... definitions 38 39 $app->run(); 40 41.. tip:: 42 43 When developing a website, you might want to turn on the debug mode to 44 ease debugging:: 45 46 $app['debug'] = true; 47 48.. tip:: 49 50 If your application is hosted behind a reverse proxy at address ``$ip``, 51 and you want Silex to trust the ``X-Forwarded-For*`` headers, you will 52 need to run your application like this:: 53 54 use Symfony\Component\HttpFoundation\Request; 55 56 Request::setTrustedProxies(array($ip)); 57 $app->run(); 58 59Routing 60------- 61 62In Silex you define a route and the controller that is called when that 63route is matched. A route pattern consists of: 64 65* *Pattern*: The route pattern defines a path that points to a resource. The 66 pattern can include variable parts and you are able to set RegExp 67 requirements for them. 68 69* *Method*: One of the following HTTP methods: ``GET``, ``POST``, ``PUT``, 70 ``DELETE``, ``PATCH``, or ``OPTIONS``. This describes the interaction with 71 the resource. 72 73The controller is defined using a closure like this:: 74 75 function () { 76 // ... do something 77 } 78 79The return value of the closure becomes the content of the page. 80 81Example GET Route 82~~~~~~~~~~~~~~~~~ 83 84Here is an example definition of a ``GET`` route:: 85 86 $blogPosts = array( 87 1 => array( 88 'date' => '2011-03-29', 89 'author' => 'igorw', 90 'title' => 'Using Silex', 91 'body' => '...', 92 ), 93 ); 94 95 $app->get('/blog', function () use ($blogPosts) { 96 $output = ''; 97 foreach ($blogPosts as $post) { 98 $output .= $post['title']; 99 $output .= '<br />'; 100 } 101 102 return $output; 103 }); 104 105Visiting ``/blog`` will return a list of blog post titles. The ``use`` 106statement means something different in this context. It tells the closure to 107import the ``$blogPosts`` variable from the outer scope. This allows you to use 108it from within the closure. 109 110Dynamic Routing 111~~~~~~~~~~~~~~~ 112 113Now, you can create another controller for viewing individual blog posts:: 114 115 $app->get('/blog/{id}', function (Silex\Application $app, $id) use ($blogPosts) { 116 if (!isset($blogPosts[$id])) { 117 $app->abort(404, "Post $id does not exist."); 118 } 119 120 $post = $blogPosts[$id]; 121 122 return "<h1>{$post['title']}</h1>". 123 "<p>{$post['body']}</p>"; 124 }); 125 126This route definition has a variable ``{id}`` part which is passed to the 127closure. 128 129The current ``Application`` is automatically injected by Silex to the Closure 130thanks to the type hinting. 131 132When the post does not exist, you are using ``abort()`` to stop the request 133early. It actually throws an exception, which you will see how to handle later 134on. 135 136Example POST Route 137~~~~~~~~~~~~~~~~~~ 138 139POST routes signify the creation of a resource. An example for this is a 140feedback form. You will use the ``mail`` function to send an e-mail:: 141 142 use Symfony\Component\HttpFoundation\Request; 143 use Symfony\Component\HttpFoundation\Response; 144 145 $app->post('/feedback', function (Request $request) { 146 $message = $request->get('message'); 147 mail('feedback@yoursite.com', '[YourSite] Feedback', $message); 148 149 return new Response('Thank you for your feedback!', 201); 150 }); 151 152It is pretty straightforward. 153 154.. note:: 155 156 There is a :doc:`SwiftmailerServiceProvider <providers/swiftmailer>` 157 included that you can use instead of ``mail()``. 158 159The current ``request`` is automatically injected by Silex to the Closure 160thanks to the type hinting. It is an instance of 161Request_, so you can fetch variables using the request ``get`` method. 162 163Instead of returning a string you are returning an instance of Response_. 164This allows setting an HTTP status code, in this case it is set to 165``201 Created``. 166 167.. note:: 168 169 Silex always uses a ``Response`` internally, it converts strings to 170 responses with status code ``200``. 171 172Other methods 173~~~~~~~~~~~~~ 174 175You can create controllers for most HTTP methods. Just call one of these 176methods on your application: ``get``, ``post``, ``put``, ``delete``, ``patch``, ``options``:: 177 178 $app->put('/blog/{id}', function ($id) { 179 // ... 180 }); 181 182 $app->delete('/blog/{id}', function ($id) { 183 // ... 184 }); 185 186 $app->patch('/blog/{id}', function ($id) { 187 // ... 188 }); 189 190.. tip:: 191 192 Forms in most web browsers do not directly support the use of other HTTP 193 methods. To use methods other than GET and POST you can utilize a special 194 form field with a name of ``_method``. The form's ``method`` attribute must 195 be set to POST when using this field: 196 197 .. code-block:: html 198 199 <form action="/my/target/route/" method="post"> 200 <!-- ... --> 201 <input type="hidden" id="_method" name="_method" value="PUT" /> 202 </form> 203 204 You need to explicitly enable this method override:: 205 206 use Symfony\Component\HttpFoundation\Request; 207 208 Request::enableHttpMethodParameterOverride(); 209 $app->run(); 210 211You can also call ``match``, which will match all methods. This can be 212restricted via the ``method`` method:: 213 214 $app->match('/blog', function () { 215 // ... 216 }); 217 218 $app->match('/blog', function () { 219 // ... 220 }) 221 ->method('PATCH'); 222 223 $app->match('/blog', function () { 224 // ... 225 }) 226 ->method('PUT|POST'); 227 228.. note:: 229 230 The order in which the routes are defined is significant. The first 231 matching route will be used, so place more generic routes at the bottom. 232 233Route Variables 234~~~~~~~~~~~~~~~ 235 236As it has been shown before you can define variable parts in a route like 237this:: 238 239 $app->get('/blog/{id}', function ($id) { 240 // ... 241 }); 242 243It is also possible to have more than one variable part, just make sure the 244closure arguments match the names of the variable parts:: 245 246 $app->get('/blog/{postId}/{commentId}', function ($postId, $commentId) { 247 // ... 248 }); 249 250While it's not recommended, you could also do this (note the switched 251arguments):: 252 253 $app->get('/blog/{postId}/{commentId}', function ($commentId, $postId) { 254 // ... 255 }); 256 257You can also ask for the current Request and Application objects:: 258 259 $app->get('/blog/{id}', function (Application $app, Request $request, $id) { 260 // ... 261 }); 262 263.. note:: 264 265 Note for the Application and Request objects, Silex does the injection 266 based on the type hinting and not on the variable name:: 267 268 $app->get('/blog/{id}', function (Application $foo, Request $bar, $id) { 269 // ... 270 }); 271 272Route Variable Converters 273~~~~~~~~~~~~~~~~~~~~~~~~~ 274 275Before injecting the route variables into the controller, you can apply some 276converters:: 277 278 $app->get('/user/{id}', function ($id) { 279 // ... 280 })->convert('id', function ($id) { return (int) $id; }); 281 282This is useful when you want to convert route variables to objects as it 283allows to reuse the conversion code across different controllers:: 284 285 $userProvider = function ($id) { 286 return new User($id); 287 }; 288 289 $app->get('/user/{user}', function (User $user) { 290 // ... 291 })->convert('user', $userProvider); 292 293 $app->get('/user/{user}/edit', function (User $user) { 294 // ... 295 })->convert('user', $userProvider); 296 297The converter callback also receives the ``Request`` as its second argument:: 298 299 $callback = function ($post, Request $request) { 300 return new Post($request->attributes->get('slug')); 301 }; 302 303 $app->get('/blog/{id}/{slug}', function (Post $post) { 304 // ... 305 })->convert('post', $callback); 306 307A converter can also be defined as a service. For example, here is a user 308converter based on Doctrine ObjectManager:: 309 310 use Doctrine\Common\Persistence\ObjectManager; 311 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 312 313 class UserConverter 314 { 315 private $om; 316 317 public function __construct(ObjectManager $om) 318 { 319 $this->om = $om; 320 } 321 322 public function convert($id) 323 { 324 if (null === $user = $this->om->find('User', (int) $id)) { 325 throw new NotFoundHttpException(sprintf('User %d does not exist', $id)); 326 } 327 328 return $user; 329 } 330 } 331 332The service will now be registered in the application, and the 333``convert()`` method will be used as converter (using the syntax 334``service_name:method_name``):: 335 336 $app['converter.user'] = function () { 337 return new UserConverter(); 338 }; 339 340 $app->get('/user/{user}', function (User $user) { 341 // ... 342 })->convert('user', 'converter.user:convert'); 343 344Requirements 345~~~~~~~~~~~~ 346 347In some cases you may want to only match certain expressions. You can define 348requirements using regular expressions by calling ``assert`` on the 349``Controller`` object, which is returned by the routing methods. 350 351The following will make sure the ``id`` argument is a positive integer, since 352``\d+`` matches any amount of digits:: 353 354 $app->get('/blog/{id}', function ($id) { 355 // ... 356 }) 357 ->assert('id', '\d+'); 358 359You can also chain these calls:: 360 361 $app->get('/blog/{postId}/{commentId}', function ($postId, $commentId) { 362 // ... 363 }) 364 ->assert('postId', '\d+') 365 ->assert('commentId', '\d+'); 366 367Conditions 368~~~~~~~~~~ 369 370Besides restricting route matching based on the HTTP method or parameter 371requirements, you can set conditions on any part of the request by calling 372``when`` on the ``Controller`` object, which is returned by the routing 373methods:: 374 375 $app->get('/blog/{id}', function ($id) { 376 // ... 377 }) 378 ->when("request.headers.get('User-Agent') matches '/firefox/i'"); 379 380The ``when`` argument is a Symfony Expression_ , which means that you need to 381add ``symfony/expression-language`` as a dependency of your project. 382 383Default Values 384~~~~~~~~~~~~~~ 385 386You can define a default value for any route variable by calling ``value`` on 387the ``Controller`` object:: 388 389 $app->get('/{pageName}', function ($pageName) { 390 // ... 391 }) 392 ->value('pageName', 'index'); 393 394This will allow matching ``/``, in which case the ``pageName`` variable will 395have the value ``index``. 396 397Named Routes 398~~~~~~~~~~~~ 399 400Some providers can make use of named routes. By default Silex will generate an 401internal route name for you but you can give an explicit route name by calling 402``bind``:: 403 404 $app->get('/', function () { 405 // ... 406 }) 407 ->bind('homepage'); 408 409 $app->get('/blog/{id}', function ($id) { 410 // ... 411 }) 412 ->bind('blog_post'); 413 414Controllers as Classes 415~~~~~~~~~~~~~~~~~~~~~~ 416 417Instead of anonymous functions, you can also define your controllers as 418methods. By using the ``ControllerClass::methodName`` syntax, you can tell 419Silex to lazily create the controller object for you:: 420 421 $app->get('/', 'Acme\\Foo::bar'); 422 423 use Silex\Application; 424 use Symfony\Component\HttpFoundation\Request; 425 426 namespace Acme 427 { 428 class Foo 429 { 430 public function bar(Request $request, Application $app) 431 { 432 // ... 433 } 434 } 435 } 436 437This will load the ``Acme\Foo`` class on demand, create an instance and call 438the ``bar`` method to get the response. You can use ``Request`` and 439``Silex\Application`` type hints to get ``$request`` and ``$app`` injected. 440 441It is also possible to :doc:`define your controllers as services 442<providers/service_controller>`. 443 444Global Configuration 445-------------------- 446 447If a controller setting must be applied to **all** controllers (a converter, a 448middleware, a requirement, or a default value), configure it on 449``$app['controllers']``, which holds all application controllers:: 450 451 $app['controllers'] 452 ->value('id', '1') 453 ->assert('id', '\d+') 454 ->requireHttps() 455 ->method('get') 456 ->convert('id', function () { /* ... */ }) 457 ->before(function () { /* ... */ }) 458 ->when('request.isSecure() == true') 459 ; 460 461These settings are applied to already registered controllers and they become 462the defaults for new controllers. 463 464.. note:: 465 466 The global configuration does not apply to controller providers you might 467 mount as they have their own global configuration (read the 468 :doc:`dedicated chapter<organizing_controllers>` for more information). 469 470Error Handlers 471-------------- 472 473When an exception is thrown, error handlers allow you to display a custom 474error page to the user. They can also be used to do additional things, such as 475logging. 476 477To register an error handler, pass a closure to the ``error`` method which 478takes an ``Exception`` argument and returns a response:: 479 480 use Symfony\Component\HttpFoundation\Response; 481 use Symfony\Component\HttpFoundation\Request; 482 483 $app->error(function (\Exception $e, Request $request, $code) { 484 return new Response('We are sorry, but something went terribly wrong.'); 485 }); 486 487You can also check for specific errors by using the ``$code`` argument, and 488handle them differently:: 489 490 use Symfony\Component\HttpFoundation\Response; 491 use Symfony\Component\HttpFoundation\Request; 492 493 $app->error(function (\Exception $e, Request $request, $code) { 494 switch ($code) { 495 case 404: 496 $message = 'The requested page could not be found.'; 497 break; 498 default: 499 $message = 'We are sorry, but something went terribly wrong.'; 500 } 501 502 return new Response($message); 503 }); 504 505You can restrict an error handler to only handle some Exception classes by 506setting a more specific type hint for the Closure argument:: 507 508 use Symfony\Component\HttpFoundation\Request; 509 510 $app->error(function (\LogicException $e, Request $request, $code) { 511 // this handler will only handle \LogicException exceptions 512 // and exceptions that extend \LogicException 513 }); 514 515.. note:: 516 517 As Silex ensures that the Response status code is set to the most 518 appropriate one depending on the exception, setting the status on the 519 response won't work. If you want to overwrite the status code, set the 520 ``X-Status-Code`` header:: 521 522 return new Response('Error', 404 /* ignored */, array('X-Status-Code' => 200)); 523 524If you want to use a separate error handler for logging, make sure you register 525it with a higher priority than response error handlers, because once a response 526is returned, the following handlers are ignored. 527 528.. note:: 529 530 Silex ships with a provider for Monolog_ which handles logging of errors. 531 Check out the *Providers* :doc:`chapter <providers/monolog>` for details. 532 533.. tip:: 534 535 Silex comes with a default error handler that displays a detailed error 536 message with the stack trace when **debug** is true, and a simple error 537 message otherwise. Error handlers registered via the ``error()`` method 538 always take precedence but you can keep the nice error messages when debug 539 is turned on like this:: 540 541 use Symfony\Component\HttpFoundation\Response; 542 use Symfony\Component\HttpFoundation\Request; 543 544 $app->error(function (\Exception $e, Request $request, $code) use ($app) { 545 if ($app['debug']) { 546 return; 547 } 548 549 // ... logic to handle the error and return a Response 550 }); 551 552The error handlers are also called when you use ``abort`` to abort a request 553early:: 554 555 $app->get('/blog/{id}', function (Silex\Application $app, $id) use ($blogPosts) { 556 if (!isset($blogPosts[$id])) { 557 $app->abort(404, "Post $id does not exist."); 558 } 559 560 return new Response(...); 561 }); 562 563You can convert errors to ``Exceptions``, check out the cookbook :doc:`chapter <cookbook/error_handler>` for details. 564 565View Handlers 566------------- 567 568View Handlers allow you to intercept a controller result that is not a 569``Response`` and transform it before it gets returned to the kernel. 570 571To register a view handler, pass a callable (or string that can be resolved to a 572callable) to the ``view()`` method. The callable should accept some sort of result 573from the controller:: 574 575 $app->view(function (array $controllerResult) use ($app) { 576 return $app->json($controllerResult); 577 }); 578 579View Handlers also receive the ``Request`` as their second argument, 580making them a good candidate for basic content negotiation:: 581 582 $app->view(function (array $controllerResult, Request $request) use ($app) { 583 $acceptHeader = $request->headers->get('Accept'); 584 $bestFormat = $app['negotiator']->getBestFormat($acceptHeader, array('json', 'xml')); 585 586 if ('json' === $bestFormat) { 587 return new JsonResponse($controllerResult); 588 } 589 590 if ('xml' === $bestFormat) { 591 return $app['serializer.xml']->renderResponse($controllerResult); 592 } 593 594 return $controllerResult; 595 }); 596 597View Handlers will be examined in the order they are added to the application 598and Silex will use type hints to determine if a view handler should be used for 599the current result, continuously using the return value of the last view handler 600as the input for the next. 601 602.. note:: 603 604 You must ensure that Silex receives a ``Response`` or a string as the result of 605 the last view handler (or controller) to be run. 606 607Redirects 608--------- 609 610You can redirect to another page by returning a ``RedirectResponse`` response, 611which you can create by calling the ``redirect`` method:: 612 613 $app->get('/', function () use ($app) { 614 return $app->redirect('/hello'); 615 }); 616 617This will redirect from ``/`` to ``/hello``. 618 619Forwards 620-------- 621 622When you want to delegate the rendering to another controller, without a 623round-trip to the browser (as for a redirect), use an internal sub-request:: 624 625 use Symfony\Component\HttpFoundation\Request; 626 use Symfony\Component\HttpKernel\HttpKernelInterface; 627 628 $app->get('/', function () use ($app) { 629 // forward to /hello 630 $subRequest = Request::create('/hello', 'GET'); 631 632 return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST); 633 }); 634 635.. tip:: 636 637 You can also generate the URI via the built-in URL generator:: 638 639 $request = Request::create($app['url_generator']->generate('hello'), 'GET'); 640 641There's some more things that you need to keep in mind though. In most cases you 642will want to forward some parts of the current master request to the sub-request. 643That includes: Cookies, server information, session. 644Read more on :doc:`how to make sub-requests <cookbook/sub_requests>`. 645 646JSON 647---- 648 649If you want to return JSON data, you can use the ``json`` helper method. 650Simply pass it your data, status code and headers, and it will create a JSON 651response for you:: 652 653 $app->get('/users/{id}', function ($id) use ($app) { 654 $user = getUser($id); 655 656 if (!$user) { 657 $error = array('message' => 'The user was not found.'); 658 659 return $app->json($error, 404); 660 } 661 662 return $app->json($user); 663 }); 664 665Streaming 666--------- 667 668It's possible to stream a response, which is important in cases when you don't 669want to buffer the data being sent:: 670 671 $app->get('/images/{file}', function ($file) use ($app) { 672 if (!file_exists(__DIR__.'/images/'.$file)) { 673 return $app->abort(404, 'The image was not found.'); 674 } 675 676 $stream = function () use ($file) { 677 readfile($file); 678 }; 679 680 return $app->stream($stream, 200, array('Content-Type' => 'image/png')); 681 }); 682 683If you need to send chunks, make sure you call ``ob_flush`` and ``flush`` 684after every chunk:: 685 686 $stream = function () { 687 $fh = fopen('http://www.example.com/', 'rb'); 688 while (!feof($fh)) { 689 echo fread($fh, 1024); 690 ob_flush(); 691 flush(); 692 } 693 fclose($fh); 694 }; 695 696Sending a file 697-------------- 698 699If you want to return a file, you can use the ``sendFile`` helper method. 700It eases returning files that would otherwise not be publicly available. Simply 701pass it your file path, status code, headers and the content disposition and it 702will create a ``BinaryFileResponse`` response for you:: 703 704 $app->get('/files/{path}', function ($path) use ($app) { 705 if (!file_exists('/base/path/' . $path)) { 706 $app->abort(404); 707 } 708 709 return $app->sendFile('/base/path/' . $path); 710 }); 711 712To further customize the response before returning it, check the API doc for 713`Symfony\Component\HttpFoundation\BinaryFileResponse 714<http://api.symfony.com/master/Symfony/Component/HttpFoundation/BinaryFileResponse.html>`_:: 715 716 return $app 717 ->sendFile('/base/path/' . $path) 718 ->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'pic.jpg') 719 ; 720 721Traits 722------ 723 724Silex comes with PHP traits that define shortcut methods. 725 726Almost all built-in service providers have some corresponding PHP traits. To 727use them, define your own Application class and include the traits you want:: 728 729 use Silex\Application; 730 731 class MyApplication extends Application 732 { 733 use Application\TwigTrait; 734 use Application\SecurityTrait; 735 use Application\FormTrait; 736 use Application\UrlGeneratorTrait; 737 use Application\SwiftmailerTrait; 738 use Application\MonologTrait; 739 use Application\TranslationTrait; 740 } 741 742You can also define your own Route class and use some traits:: 743 744 use Silex\Route; 745 746 class MyRoute extends Route 747 { 748 use Route\SecurityTrait; 749 } 750 751To use your newly defined route, override the ``$app['route_class']`` 752setting:: 753 754 $app['route_class'] = 'MyRoute'; 755 756Read each provider chapter to learn more about the added methods. 757 758Security 759-------- 760 761Make sure to protect your application against attacks. 762 763Escaping 764~~~~~~~~ 765 766When outputting any user input, make sure to escape it correctly to prevent 767Cross-Site-Scripting attacks. 768 769* **Escaping HTML**: PHP provides the ``htmlspecialchars`` function for this. 770 Silex provides a shortcut ``escape`` method:: 771 772 use Symfony\Component\HttpFoundation\Request; 773 774 $app->get('/name', function (Request $request, Silex\Application $app) { 775 $name = $request->get('name'); 776 777 return "You provided the name {$app->escape($name)}."; 778 }); 779 780 If you use the Twig template engine, you should use its escaping or even 781 auto-escaping mechanisms. Check out the *Providers* :doc:`chapter <providers/twig>` for details. 782 783* **Escaping JSON**: If you want to provide data in JSON format you should 784 use the Silex ``json`` function:: 785 786 use Symfony\Component\HttpFoundation\Request; 787 788 $app->get('/name.json', function (Request $request, Silex\Application $app) { 789 $name = $request->get('name'); 790 791 return $app->json(array('name' => $name)); 792 }); 793 794.. _Silex Skeleton: http://github.com/silexphp/Silex-Skeleton 795.. _Composer: http://getcomposer.org/ 796.. _Request: http://api.symfony.com/master/Symfony/Component/HttpFoundation/Request.html 797.. _Response: http://api.symfony.com/master/Symfony/Component/HttpFoundation/Response.html 798.. _Monolog: https://github.com/Seldaek/monolog 799.. _Expression: https://symfony.com/doc/current/book/routing.html#completely-customized-route-matching-with-conditions 800