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