From da02c1b6c8a0835446321e59464d7eda4e122380 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Sat, 9 Jul 2016 23:44:54 -0700 Subject: [PATCH] Removed doc directory --- doc/changelog.rst | 353 ----------- doc/conf.py | 17 - doc/contributing.rst | 34 -- doc/cookbook/error_handler.rst | 38 -- doc/cookbook/form_no_csrf.rst | 36 -- doc/cookbook/guard_authentication.rst | 182 ------ doc/cookbook/index.rst | 40 -- doc/cookbook/json_request_body.rst | 95 --- doc/cookbook/multiple_loggers.rst | 69 --- doc/cookbook/session_storage.rst | 93 --- doc/cookbook/sub_requests.rst | 137 ----- doc/cookbook/validator_yaml.rst | 35 -- doc/index.rst | 19 - doc/internals.rst | 84 --- doc/intro.rst | 50 -- doc/middlewares.rst | 162 ------ doc/organizing_controllers.rst | 84 --- doc/providers.rst | 256 -------- doc/providers/asset.rst | 60 -- doc/providers/csrf.rst | 52 -- doc/providers/doctrine.rst | 137 ----- doc/providers/form.rst | 204 ------- doc/providers/http_cache.rst | 128 ---- doc/providers/http_fragment.rst | 70 --- doc/providers/index.rst | 24 - doc/providers/locale.rst | 24 - doc/providers/monolog.rst | 115 ---- doc/providers/remember_me.rst | 69 --- doc/providers/security.rst | 711 ----------------------- doc/providers/serializer.rst | 73 --- doc/providers/service_controller.rst | 142 ----- doc/providers/session.rst | 103 ---- doc/providers/swiftmailer.rst | 146 ----- doc/providers/translation.rst | 193 ------ doc/providers/twig.rst | 200 ------- doc/providers/validator.rst | 217 ------- doc/providers/var_dumper.rst | 44 -- doc/services.rst | 264 --------- doc/testing.rst | 221 ------- doc/usage.rst | 805 -------------------------- doc/web_servers.rst | 165 ------ 41 files changed, 5951 deletions(-) delete mode 100644 doc/changelog.rst delete mode 100644 doc/conf.py delete mode 100644 doc/contributing.rst delete mode 100644 doc/cookbook/error_handler.rst delete mode 100644 doc/cookbook/form_no_csrf.rst delete mode 100644 doc/cookbook/guard_authentication.rst delete mode 100644 doc/cookbook/index.rst delete mode 100644 doc/cookbook/json_request_body.rst delete mode 100644 doc/cookbook/multiple_loggers.rst delete mode 100644 doc/cookbook/session_storage.rst delete mode 100644 doc/cookbook/sub_requests.rst delete mode 100644 doc/cookbook/validator_yaml.rst delete mode 100644 doc/index.rst delete mode 100644 doc/internals.rst delete mode 100644 doc/intro.rst delete mode 100644 doc/middlewares.rst delete mode 100644 doc/organizing_controllers.rst delete mode 100644 doc/providers.rst delete mode 100644 doc/providers/asset.rst delete mode 100644 doc/providers/csrf.rst delete mode 100644 doc/providers/doctrine.rst delete mode 100644 doc/providers/form.rst delete mode 100644 doc/providers/http_cache.rst delete mode 100644 doc/providers/http_fragment.rst delete mode 100644 doc/providers/index.rst delete mode 100644 doc/providers/locale.rst delete mode 100644 doc/providers/monolog.rst delete mode 100644 doc/providers/remember_me.rst delete mode 100644 doc/providers/security.rst delete mode 100644 doc/providers/serializer.rst delete mode 100644 doc/providers/service_controller.rst delete mode 100644 doc/providers/session.rst delete mode 100644 doc/providers/swiftmailer.rst delete mode 100644 doc/providers/translation.rst delete mode 100644 doc/providers/twig.rst delete mode 100644 doc/providers/validator.rst delete mode 100644 doc/providers/var_dumper.rst delete mode 100644 doc/services.rst delete mode 100644 doc/testing.rst delete mode 100644 doc/usage.rst delete mode 100644 doc/web_servers.rst diff --git a/doc/changelog.rst b/doc/changelog.rst deleted file mode 100644 index 0c9cdd4..0000000 --- a/doc/changelog.rst +++ /dev/null @@ -1,353 +0,0 @@ -Changelog -========= - -2.0.2 (2016-06-14) ------------------- - -* fixed Symfony 3.1 deprecations - -2.0.1 (2016-05-27) ------------------- - -* fixed the silex form extension registration to allow overriding default ones -* removed support for the obsolete Locale Symfony component (uses the Intl one now) -* added support for Symfony 3.1 - -2.0.0 (2016-05-18) ------------------- - -* decoupled the exception handler from HttpKernelServiceProvider -* Switched to BCrypt as the default encoder in the security provider -* added full support for RequestMatcher -* added support for Symfony Guard -* added support for callables in CallbackResolver -* added FormTrait::namedForm() -* added support for delivery_addresses, delivery_whitelist, and sender_address -* added support to register form types / form types extensions / form types guessers as services -* added support for callable in mounts (allow nested route collection to be built easily) -* added support for conditions on routes -* added support for the Symfony VarDumper Component -* added a global Twig variable (an AppVariable instance) -* [BC BREAK] CSRF has been moved to a standalone provider (``form.secret`` is not available anymore) -* added support for the Symfony HttpFoundation Twig bridge extension -* added support for the Symfony Asset Component -* bumped minimum version of Symfony to 2.8 -* bumped minimum version of PHP to 5.5.0 -* Updated Pimple to 3.0 -* Updated session listeners to extends HttpKernel ones -* [BC BREAK] Locale management has been moved to LocaleServiceProvider which must be registered - if you want Silex to manage your locale (must also be registered for the translation service provider) -* [BC BREAK] Provider interfaces moved to Silex\Api namespace, published as - separate package via subtree split -* [BC BREAK] ServiceProviderInterface split in to EventListenerProviderInterface - and BootableProviderInterface -* [BC BREAK] Service Provider support files moved under Silex\Provider - namespace, allowing publishing as separate package via sub-tree split -* ``monolog.exception.logger_filter`` option added to Monolog service provider -* [BC BREAK] ``$app['request']`` service removed, use ``$app['request_stack']`` instead - -1.3.6 (2016-XX-XX) ------------------- - -* n/a - -1.3.5 (2016-01-06) ------------------- - -* fixed typo in SecurityServiceProvider - -1.3.4 (2015-09-15) ------------------- - -* fixed some new deprecations -* fixed translation registration for the validators - -1.3.3 (2015-09-08) ------------------- - -* added support for Symfony 3.0 and Twig 2.0 -* fixed some Form deprecations -* removed deprecated method call in the exception handler -* fixed Swiftmailer spool flushing when spool is not enabled - -1.3.2 (2015-08-24) ------------------- - -* no changes - -1.3.1 (2015-08-04) ------------------- - -* added missing support for the Expression constraint -* fixed the possibility to override translations for validator error messages -* fixed sub-mounts with same name clash -* fixed session logout handler when a firewall is stateless - -1.3.0 (2015-06-05) ------------------- - -* added a `$app['user']` to get the current user (security provider) -* added view handlers -* added support for the OPTIONS HTTP method -* added caching for the Translator provider -* deprecated `$app['exception_handler']->disable()` in favor of `unset($app['exception_handler'])` -* made Silex compatible with Symfony 2.7 an 2.8 (and keep compatibility with Symfony 2.3, 2.5, and 2.6) -* removed deprecated TwigCoreExtension class (register the new HttpFragmentServiceProvider instead) -* bumped minimum version of PHP to 5.3.9 - -1.2.5 (2015-06-04) ------------------- - -* no code changes (last version of the 1.2 branch) - -1.2.4 (2015-04-11) ------------------- - -* fixed the exception message when mounting a collection that doesn't return a ControllerCollection -* fixed Symfony dependencies (Silex 1.2 is not compatible with Symfony 2.7) - -1.2.3 (2015-01-20) ------------------- - -* fixed remember me listener -* fixed translation files loading when they do not exist -* allowed global after middlewares to return responses like route specific ones - -1.2.2 (2014-09-26) ------------------- - -* fixed Translator locale management -* added support for the $app argument in application middlewares (to make it consistent with route middlewares) -* added form.types to the Form provider - -1.2.1 (2014-07-01) ------------------- - -* added support permissions in the Monolog provider -* fixed Switfmailer spool where the event dispatcher is different from the other ones -* fixed locale when changing it on the translator itself - -1.2.0 (2014-03-29) ------------------- - -* Allowed disabling the boot logic of MonologServiceProvider -* Reverted "convert attributes on the request that actually exist" -* [BC BREAK] Routes are now always added in the order of their registration (even for mounted routes) -* Added run() on Route to be able to define the controller code -* Deprecated TwigCoreExtension (register the new HttpFragmentServiceProvider instead) -* Added HttpFragmentServiceProvider -* Allowed a callback to be a method call on a service (before, after, finish, error, on Application; convert, before, after on Controller) - -1.1.3 (2013-XX-XX) ------------------- - -* Fixed translator locale management - -1.1.2 (2013-10-30) ------------------- - -* Added missing "security.hide_user_not_found" support in SecurityServiceProvider -* Fixed event listeners that are registered after the boot via the on() method - -1.0.2 (2013-10-30) ------------------- - -* Fixed SecurityServiceProvider to use null as a fake controller so that routes can be dumped - -1.1.1 (2013-10-11) ------------------- - -* Removed or replaced deprecated Symfony code -* Updated code to take advantages of 2.3 new features -* Only convert attributes on the request that actually exist. - -1.1.0 (2013-07-04) ------------------- - -* Support for any ``Psr\Log\LoggerInterface`` as opposed to the monolog-bridge - one. -* Made dispatcher proxy methods ``on``, ``before``, ``after`` and ``error`` - lazy, so that they will not instantiate the dispatcher early. -* Dropped support for 2.1 and 2.2 versions of Symfony. - -1.0.1 (2013-07-04) ------------------- - -* Fixed RedirectableUrlMatcher::redirect() when Silex is configured to use a logger -* Make ``DoctrineServiceProvider`` multi-db support lazy. - -1.0.0 (2013-05-03) ------------------- - -* **2013-04-12**: Added support for validators as services. - -* **2013-04-01**: Added support for host matching with symfony 2.2:: - - $app->match('/', function() { - // app-specific action - })->host('example.com'); - - $app->match('/', function ($user) { - // user-specific action - })->host('{user}.example.com'); - -* **2013-03-08**: Added support for form type extensions and guessers as - services. - -* **2013-03-08**: Added support for remember-me via the - ``RememberMeServiceProvider``. - -* **2013-02-07**: Added ``Application::sendFile()`` to ease sending - ``BinaryFileResponse``. - -* **2012-11-05**: Filters have been renamed to application middlewares in the - documentation. - -* **2012-11-05**: The ``before()``, ``after()``, ``error()``, and ``finish()`` - listener priorities now set the priority of the underlying Symfony event - instead of a custom one before. - -* **2012-11-05**: Removing the default exception handler should now be done - via its ``disable()`` method: - - Before: - - unset($app['exception_handler']); - - After: - - $app['exception_handler']->disable(); - -* **2012-07-15**: removed the ``monolog.configure`` service. Use the - ``extend`` method instead: - - Before:: - - $app['monolog.configure'] = $app->protect(function ($monolog) use ($app) { - // do something - }); - - After:: - - $app['monolog'] = $app->share($app->extend('monolog', function($monolog, $app) { - // do something - - return $monolog; - })); - - -* **2012-06-17**: ``ControllerCollection`` now takes a required route instance - as a constructor argument. - - Before:: - - $controllers = new ControllerCollection(); - - After:: - - $controllers = new ControllerCollection(new Route()); - - // or even better - $controllers = $app['controllers_factory']; - -* **2012-06-17**: added application traits for PHP 5.4 - -* **2012-06-16**: renamed ``request.default_locale`` to ``locale`` - -* **2012-06-16**: Removed the ``translator.loader`` service. See documentation - for how to use XLIFF or YAML-based translation files. - -* **2012-06-15**: removed the ``twig.configure`` service. Use the ``extend`` - method instead: - - Before:: - - $app['twig.configure'] = $app->protect(function ($twig) use ($app) { - // do something - }); - - After:: - - $app['twig'] = $app->share($app->extend('twig', function($twig, $app) { - // do something - - return $twig; - })); - -* **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``, ``CssSelector``, ``DomCrawler``, - ``Finder`` and ``Process`` components optional dependencies. Projects that - depend on them (e.g. through functional tests) should add those dependencies - to their ``composer.json``. - -* **2012-05-26**: added ``boot()`` to ``ServiceProviderInterface``. - -* **2012-05-26**: Removed ``SymfonyBridgesServiceProvider``. It is now implicit - by checking the existence of the bridge. - -* **2012-05-26**: Removed the ``translator.messages`` parameter (use - ``translator.domains`` instead). - -* **2012-05-24**: Removed the ``autoloader`` service (use composer instead). - The ``*.class_path`` settings on all the built-in providers have also been - removed in favor of Composer. - -* **2012-05-21**: Changed error() to allow handling specific exceptions. - -* **2012-05-20**: Added a way to define settings on a controller collection. - -* **2012-05-20**: The Request instance is not available anymore from the - Application after it has been handled. - -* **2012-04-01**: Added ``finish`` filters. - -* **2012-03-20**: Added ``json`` helper:: - - $data = array('some' => 'data'); - $response = $app->json($data); - -* **2012-03-11**: Added route middlewares. - -* **2012-03-02**: Switched to use Composer for dependency management. - -* **2012-02-27**: Updated to Symfony 2.1 session handling. - -* **2012-01-02**: Introduced support for streaming responses. - -* **2011-09-22**: ``ExtensionInterface`` has been renamed to - ``ServiceProviderInterface``. All built-in extensions have been renamed - accordingly (for instance, ``Silex\Extension\TwigExtension`` has been - renamed to ``Silex\Provider\TwigServiceProvider``). - -* **2011-09-22**: The way reusable applications work has changed. The - ``mount()`` method now takes an instance of ``ControllerCollection`` instead - of an ``Application`` one. - - Before:: - - $app = new Application(); - $app->get('/bar', function() { return 'foo'; }); - - return $app; - - After:: - - $app = new ControllerCollection(); - $app->get('/bar', function() { return 'foo'; }); - - return $app; - -* **2011-08-08**: The controller method configuration is now done on the Controller itself - - Before:: - - $app->match('/', function () { echo 'foo'; }, 'GET|POST'); - - After:: - - $app->match('/', function () { echo 'foo'; })->method('GET|POST'); diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index dfe355c..0000000 --- a/doc/conf.py +++ /dev/null @@ -1,17 +0,0 @@ -import sys, os -from sphinx.highlighting import lexers -from pygments.lexers.web import PhpLexer - -sys.path.append(os.path.abspath('_exts')) - -extensions = [] -master_doc = 'index' -highlight_language = 'php' - -project = u'Silex' -copyright = u'2010 Fabien Potencier' - -version = '0' -release = '0.0.0' - -lexers['php'] = PhpLexer(startinline=True) diff --git a/doc/contributing.rst b/doc/contributing.rst deleted file mode 100644 index 34a339d..0000000 --- a/doc/contributing.rst +++ /dev/null @@ -1,34 +0,0 @@ -Contributing -============ - -We are open to contributions to the Silex code. If you find a bug or want to -contribute a provider, just follow these steps: - -* Fork `the Silex repository `_; - -* Make your feature addition or bug fix; - -* Add tests for it; - -* Optionally, add some documentation; - -* `Send a pull request - `_, to the correct - target branch (1.3 for bug fixes, master for new features). - -.. note:: - - Any code you contribute must be licensed under the MIT - License. - -Writing Documentation -===================== - -The documentation is written in `reStructuredText -`_ and can be generated using `sphinx -`_. - -.. code-block:: bash - - $ cd doc - $ sphinx-build -b html . build diff --git a/doc/cookbook/error_handler.rst b/doc/cookbook/error_handler.rst deleted file mode 100644 index 235c263..0000000 --- a/doc/cookbook/error_handler.rst +++ /dev/null @@ -1,38 +0,0 @@ -Converting Errors to Exceptions -=============================== - -Silex catches exceptions that are thrown from within a request/response cycle. -However, it does *not* catch PHP errors and notices. This recipe tells you how -to catch them by converting them to exceptions. - -Registering the ErrorHandler ----------------------------- - -The ``Symfony/Debug`` package has an ``ErrorHandler`` class that solves this -problem. It converts all errors to exceptions, and exceptions are then caught -by Silex. - -Register it by calling the static ``register`` method:: - - use Symfony\Component\Debug\ErrorHandler; - - ErrorHandler::register(); - -It is recommended that you do this as early as possible. - -Handling fatal errors ---------------------- - -To handle fatal errors, you can additionally register a global -``ExceptionHandler``:: - - use Symfony\Component\Debug\ExceptionHandler; - - ExceptionHandler::register(); - -In production you may want to disable the debug output by passing ``false`` as -the ``$debug`` argument:: - - use Symfony\Component\Debug\ExceptionHandler; - - ExceptionHandler::register(false); diff --git a/doc/cookbook/form_no_csrf.rst b/doc/cookbook/form_no_csrf.rst deleted file mode 100644 index e9bf595..0000000 --- a/doc/cookbook/form_no_csrf.rst +++ /dev/null @@ -1,36 +0,0 @@ -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. When the :doc:`CSRF Service Provider -` is registered, the *FormExtension* uses the CSRF Protection -avoiding Cross-site request forgery, a method by which a malicious user -attempts to make your legitimate users unknowingly submit data that they don't -intend to submit. - -You can find more details about CSRF Protection and CSRF token in the -`Symfony Book -`_. - -In some cases (for example, when embedding a form in an html email) you might -want not to use this protection. The easiest way to avoid this is to -understand that it is possible to give specific options to your form builder -through the ``createBuilder()`` function. - -Example -------- - -.. code-block:: php - - $form = $app['form.factory']->createBuilder('form', null, array('csrf_protection' => false)); - -That's it, your form could be submitted from everywhere without CSRF Protection. - -Going further -------------- - -This specific example showed how to change the ``csrf_protection`` in the -``$options`` parameter of the ``createBuilder()`` function. More of them could -be passed through this parameter, it is as simple as using the Symfony -``getDefaultOptions()`` method in your form classes. `See more here -`_. diff --git a/doc/cookbook/guard_authentication.rst b/doc/cookbook/guard_authentication.rst deleted file mode 100644 index f7f736c..0000000 --- a/doc/cookbook/guard_authentication.rst +++ /dev/null @@ -1,182 +0,0 @@ -How to Create a Custom Authentication System with Guard -======================================================= - -Whether you need to build a traditional login form, an API token -authentication system or you need to integrate with some proprietary -single-sign-on system, the Guard component can make it easy... and fun! - -In this example, you'll build an API token authentication system and -learn how to work with Guard. - -Step 1) Create the Authenticator Class --------------------------------------- - -Suppose you have an API where your clients will send an X-AUTH-TOKEN -header on each request. This token is composed of the username followed -by a password, separated by a colon (e.g. ``X-AUTH-TOKEN: coolguy:awesomepassword``). -Your job is to read this, find theassociated user (if any) and check -the password. - -To create a custom authentication system, just create a class and make -it implement GuardAuthenticatorInterface. Or, extend the simpler -AbstractGuardAuthenticator. This requires you to implement six methods: - -.. code-block:: php - - encoderFactory = $encoderFactory; - } - - public function getCredentials(Request $request) - { - // Checks if the credential header is provided - if (!$token = $request->headers->get('X-AUTH-TOKEN')) { - return; - } - - // Parse the header or ignore it if the format is incorrect. - if (false === strpos(':', $token)) { - return; - } - list($username, $secret) = explode(':', $token, 2); - - return array( - 'username' => $username, - 'secret' => $secret, - ); - } - - public function getUser($credentials, UserProviderInterface $userProvider) - { - return $userProvider->loadUserByUsername($credentials['username']); - } - - public function checkCredentials($credentials, UserInterface $user) - { - // check credentials - e.g. make sure the password is valid - // return true to cause authentication success - - $encoder = $this->encoderFactory->getEncoder($user); - - return $encoder->isPasswordValid( - $user->getPassword(), - $credentials['secret'], - $user->getSalt() - ); - } - - public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) - { - // on success, let the request continue - return; - } - - public function onAuthenticationFailure(Request $request, AuthenticationException $exception) - { - $data = array( - 'message' => strtr($exception->getMessageKey(), $exception->getMessageData()), - - // or to translate this message - // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData()) - ); - - return new JsonResponse($data, 403); - } - - /** - * Called when authentication is needed, but it's not sent - */ - public function start(Request $request, AuthenticationException $authException = null) - { - $data = array( - // you might translate this message - 'message' => 'Authentication Required', - ); - - return new JsonResponse($data, 401); - } - - public function supportsRememberMe() - { - return false; - } - } - - -Step 2) Configure the Authenticator ------------------------------------ - -To finish this, register the class as a service: - -.. code-block:: php - - $app['app.token_authenticator'] = function ($app) { - return new App\Security\TokenAuthenticator($app['security.encoder_factory']); - }; - - -Finally, configure your `security.firewalls` key to use this authenticator: - -.. code-block:: php - - $app['security.firewalls'] => array( - 'main' => array( - 'guard' => array( - 'authenticators' => array( - 'app.token_authenticator' - ), - - // Using more than 1 authenticator, you must specify - // which one is used as entry point. - // 'entry_point' => 'app.token_authenticator', - ), - // configure where your users come from. Hardcode them, or load them from somewhere - // http://silex.sensiolabs.org/doc/providers/security.html#defining-a-custom-user-provider - 'users' => array( - 'victoria' => array('ROLE_USER', 'randomsecret'), - ), - // 'anonymous' => true - ), - ); - -.. note:: - You can use many authenticators, they are executed by the order - they are configured. - -You did it! You now have a fully-working API token authentication -system. If your homepage required ROLE_USER, then you could test it -under different conditions: - -.. code-block:: bash - - # test with no token - curl http://localhost:8000/ - # {"message":"Authentication Required"} - - # test with a bad token - curl -H "X-AUTH-TOKEN: alan" http://localhost:8000/ - # {"message":"Username could not be found."} - - # test with a working token - curl -H "X-AUTH-TOKEN: victoria:randomsecret" http://localhost:8000/ - # the homepage controller is executed: the page loads normally - -For more details read the Symfony cookbook entry on -`How to Create a Custom Authentication System with Guard `_. diff --git a/doc/cookbook/index.rst b/doc/cookbook/index.rst deleted file mode 100644 index 53b10fe..0000000 --- a/doc/cookbook/index.rst +++ /dev/null @@ -1,40 +0,0 @@ -Cookbook -======== - -The cookbook section contains recipes for solving specific problems. - -.. toctree:: - :maxdepth: 1 - :hidden: - - json_request_body - session_storage - form_no_csrf - validator_yaml - sub_requests - error_handler - multiple_loggers - guard_authentication - -Recipes -------- - -* :doc:`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. - -* :doc:`Using PdoSessionStorage to store Sessions in the Database - `. - -* :doc:`Disabling the CSRF Protection on a Form using the FormExtension - `. - -* :doc:`Using YAML to configure Validation `. - -* :doc:`Making sub-Requests `. - -* :doc:`Converting Errors to Exceptions `. - -* :doc:`Using multiple Monolog Loggers `. - -* :doc:`How to Create a Custom Authentication System with Guard `. diff --git a/doc/cookbook/json_request_body.rst b/doc/cookbook/json_request_body.rst deleted file mode 100644 index 4715900..0000000 --- a/doc/cookbook/json_request_body.rst +++ /dev/null @@ -1,95 +0,0 @@ -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. - -An example for such an API could be a blog post creation. - -Example API ------------ - -In this example we will create an API for creating a blog post. The following -is a spec of how we want it to work. - -Request -~~~~~~~ - -In the request we send the data for the blog post as a JSON object. We also -indicate that using the ``Content-Type`` header: - -.. code-block:: text - - POST /blog/posts - Accept: application/json - Content-Type: application/json - Content-Length: 57 - - {"title":"Hello World!","body":"This is my first post!"} - -Response -~~~~~~~~ - -The server responds with a 201 status code, telling us that the post was -created. It tells us the ``Content-Type`` of the response, which is also -JSON: - -.. code-block:: text - - HTTP/1.1 201 Created - Content-Type: application/json - Content-Length: 65 - Connection: close - - {"id":"1","title":"Hello World!","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``. Since we want to do this for every request, -the easiest solution is to use an application before middleware. - -We simply use ``json_decode`` to parse the content of the request and then -replace the request data on the ``$request`` object:: - - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpFoundation\ParameterBag; - - $app->before(function (Request $request) { - if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) { - $data = json_decode($request->getContent(), true); - $request->request->replace(is_array($data) ? $data : array()); - } - }); - -Controller implementation -------------------------- - -Our controller will create a new blog post from the data provided and will -return the post object, including its ``id``, as JSON:: - - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpFoundation\Response; - - $app->post('/blog/posts', function (Request $request) use ($app) { - $post = array( - 'title' => $request->request->get('title'), - 'body' => $request->request->get('body'), - ); - - $post['id'] = createPost($post); - - return $app->json($post, 201); - }); - -Manual testing --------------- - -In order to manually test our API, we can use the ``curl`` command line -utility, which allows sending HTTP requests: - -.. code-block:: bash - - $ curl http://blog.lo/blog/posts -d '{"title":"Hello World!","body":"This is my first post!"}' -H 'Content-Type: application/json' - {"id":"1","title":"Hello World!","body":"This is my first post!"} diff --git a/doc/cookbook/multiple_loggers.rst b/doc/cookbook/multiple_loggers.rst deleted file mode 100644 index 9bc33e7..0000000 --- a/doc/cookbook/multiple_loggers.rst +++ /dev/null @@ -1,69 +0,0 @@ -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, allowing for fine -grained control of where your logging goes and in what detail. - -This simple example allows you to quickly configure several monolog instances, -using the bundled handler, but each with a different channel. - -.. code-block:: php - - $app['monolog.factory'] = $app->protect(function ($name) use ($app) { - $log = new $app['monolog.logger.class']($name); - $log->pushHandler($app['monolog.handler']); - - return $log; - }); - - foreach (array('auth', 'payments', 'stats') as $channel) { - $app['monolog.'.$channel] = function ($app) use ($channel) { - return $app['monolog.factory']($channel); - }; - } - -As your application grows, or your logging needs for certain areas of the -system become apparent, it should be straightforward to then configure that -particular service separately, including your customizations. - -.. code-block:: php - - use Monolog\Handler\StreamHandler; - - $app['monolog.payments'] = function ($app) { - $log = new $app['monolog.logger.class']('payments'); - $handler = new StreamHandler($app['monolog.payments.logfile'], $app['monolog.payment.level']); - $log->pushHandler($handler); - - return $log; - }; - -Alternatively, you could attempt to make the factory more complicated, and rely -on some conventions, such as checking for an array of handlers registered with -the container with the channel name, defaulting to the bundled handler. - -.. code-block:: php - - use Monolog\Handler\StreamHandler; - use Monolog\Logger; - - $app['monolog.factory'] = $app->protect(function ($name) use ($app) { - $log = new $app['monolog.logger.class']($name); - - $handlers = isset($app['monolog.'.$name.'.handlers']) - ? $app['monolog.'.$name.'.handlers'] - : array($app['monolog.handler']); - - foreach ($handlers as $handler) { - $log->pushHandler($handler); - } - - return $log; - }); - - $app['monolog.payments.handlers'] = function ($app) { - return array( - new StreamHandler(__DIR__.'/../payments.log', Logger::DEBUG), - ); - }; diff --git a/doc/cookbook/session_storage.rst b/doc/cookbook/session_storage.rst deleted file mode 100644 index 8741ad5..0000000 --- a/doc/cookbook/session_storage.rst +++ /dev/null @@ -1,93 +0,0 @@ -Using PdoSessionStorage to store Sessions in the Database -========================================================= - -By default, the :doc:`SessionServiceProvider ` writes -session information in files using Symfony NativeFileSessionStorage. Most -medium to large websites use a database to store sessions instead of files, -because databases are easier to use and scale in a multi-webserver environment. - -Symfony's `NativeSessionStorage -`_ -has multiple storage handlers and one of them uses PDO to store sessions, -`PdoSessionHandler -`_. -To use it, replace the ``session.storage.handler`` service in your application -like explained below. - -With a dedicated PDO service ----------------------------- - -.. code-block:: php - - use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - - $app->register(new Silex\Provider\SessionServiceProvider()); - - $app['pdo.dsn'] = 'mysql:dbname=mydatabase'; - $app['pdo.user'] = 'myuser'; - $app['pdo.password'] = 'mypassword'; - - $app['session.db_options'] = array( - 'db_table' => 'session', - 'db_id_col' => 'session_id', - 'db_data_col' => 'session_value', - 'db_time_col' => 'session_time', - ); - - $app['pdo'] = function () use ($app) { - return new PDO( - $app['pdo.dsn'], - $app['pdo.user'], - $app['pdo.password'] - ); - }; - - $app['session.storage.handler'] = function () use ($app) { - return new PdoSessionHandler( - $app['pdo'], - $app['session.db_options'], - $app['session.storage.options'] - ); - }; - -Using the DoctrineServiceProvider ---------------------------------- - -When using the :doc:`DoctrineServiceProvider ` You don't -have to make another database connection, simply pass the getWrappedConnection method. - -.. code-block:: php - - use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - - $app->register(new Silex\Provider\SessionServiceProvider()); - - $app['session.db_options'] = array( - 'db_table' => 'session', - 'db_id_col' => 'session_id', - 'db_data_col' => 'session_value', - 'db_lifetime_col' => 'session_lifetime', - 'db_time_col' => 'session_time', - ); - - $app['session.storage.handler'] = function () use ($app) { - return new PdoSessionHandler( - $app['db']->getWrappedConnection(), - $app['session.db_options'], - $app['session.storage.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_lifetime``: Lifetime column (INTEGER) -* ``session_time``: Time column (INTEGER) - -You can find examples of SQL statements to create the session table in the -`Symfony cookbook -`_ diff --git a/doc/cookbook/sub_requests.rst b/doc/cookbook/sub_requests.rst deleted file mode 100644 index 95d3913..0000000 --- a/doc/cookbook/sub_requests.rst +++ /dev/null @@ -1,137 +0,0 @@ -Making sub-Requests -=================== - -Since Silex is based on the ``HttpKernelInterface``, it allows you to simulate -requests against your application. This means that you can embed a page within -another, it also allows you to forward a request which is essentially an -internal redirect that does not change the URL. - -Basics ------- - -You can make a sub-request by calling the ``handle`` method on the -``Application``. This method takes three arguments: - -* ``$request``: An instance of the ``Request`` class which represents the - HTTP request. - -* ``$type``: Must be either ``HttpKernelInterface::MASTER_REQUEST`` or - ``HttpKernelInterface::SUB_REQUEST``. Certain listeners are only executed for - the master request, so it's important that this is set to ``SUB_REQUEST``. - -* ``$catch``: Catches exceptions and turns them into a response with status code - ``500``. This argument defaults to ``true``. For sub-requests you will most - likely want to set it to ``false``. - -By calling ``handle``, you can make a sub-request manually. Here's an example:: - - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpKernel\HttpKernelInterface; - - $subRequest = Request::create('/'); - $response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false); - -There's some more things that you need to keep in mind though. In most cases -you will want to forward some parts of the current master request to the -sub-request like cookies, server information, or the session. - -Here is a more advanced example that forwards said information (``$request`` -holds the master request):: - - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpKernel\HttpKernelInterface; - - $subRequest = Request::create('/', 'GET', array(), $request->cookies->all(), array(), $request->server->all()); - if ($request->getSession()) { - $subRequest->setSession($request->getSession()); - } - - $response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false); - -To forward this response to the client, you can simply return it from a -controller:: - - use Silex\Application; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpKernel\HttpKernelInterface; - - $app->get('/foo', function (Application $app, Request $request) { - $subRequest = Request::create('/', ...); - $response = $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false); - - return $response; - }); - -If you want to embed the response as part of a larger page you can call -``Response::getContent``:: - - $header = ...; - $footer = ...; - $body = $response->getContent(); - - return $header.$body.$footer; - -Rendering pages in Twig templates ---------------------------------- - -The :doc:`TwigServiceProvider ` provides a ``render`` -function that you can use in Twig templates. It gives you a convenient way to -embed pages. - -.. code-block:: jinja - - {{ render('/sidebar') }} - -For details, refer to the :doc:`TwigServiceProvider ` docs. - -Edge Side Includes ------------------- - -You can use ESI either through the :doc:`HttpCacheServiceProvider -` or a reverse proxy cache such as Varnish. This also -allows you to embed pages, however it also gives you the benefit of caching -parts of the page. - -Here is an example of how you would embed a page via ESI: - -.. code-block:: jinja - - - -For details, refer to the :doc:`HttpCacheServiceProvider -` docs. - -Dealing with the request base URL ---------------------------------- - -One thing to watch out for is the base URL. If your application is not -hosted at the webroot of your web server, then you may have an URL like -``http://example.org/foo/index.php/articles/42``. - -In this case, ``/foo/index.php`` is your request base path. Silex accounts for -this path prefix in the routing process, it reads it from -``$request->server``. In the context of sub-requests this can lead to issues, -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. - -You can prevent that from happening by always prepending the base path when -constructing a request:: - - $url = $request->getUriForPath('/'); - $subRequest = Request::create($url, 'GET', array(), $request->cookies->all(), array(), $request->server->all()); - -This is something to be aware of when making sub-requests by hand. - -Services depending on the Request ---------------------------------- - -The container is a concept that is global to a Silex application, since the -application object **is** the container. Any request that is run against an -application will re-use the same set of services. Since these services are -mutable, code in a master request can affect the sub-requests and vice versa. -Any services depending on the ``request`` service will store the first request -that they get (could be master or sub-request), and keep using it, even if -that request is already over. - -Instead of injecting the ``request`` service, you should always inject the -``request_stack`` one instead. diff --git a/doc/cookbook/validator_yaml.rst b/doc/cookbook/validator_yaml.rst deleted file mode 100644 index 2d478ff..0000000 --- a/doc/cookbook/validator_yaml.rst +++ /dev/null @@ -1,35 +0,0 @@ -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. But this doesn't mean that this is not -possible. Let's see how to do it. - -First, you need to install the YAML Component: - -.. code-block:: bash - - composer require symfony/yaml - -Next, you need to tell the Validation Service that you are not using -``StaticMethodLoader`` to load your class metadata but a YAML file:: - - $app->register(new ValidatorServiceProvider()); - - $app['validator.mapping.class_metadata_factory'] = new Symfony\Component\Validator\Mapping\ClassMetadataFactory( - new Symfony\Component\Validator\Mapping\Loader\YamlFileLoader(__DIR__.'/validation.yml') - ); - -Now, we can replace the usage of the static method and move all the validation -rules to ``validation.yml``: - -.. code-block:: yaml - - # validation.yml - Post: - properties: - title: - - NotNull: ~ - - NotBlank: ~ - body: - - Min: 100 diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index d1a851d..0000000 --- a/doc/index.rst +++ /dev/null @@ -1,19 +0,0 @@ -The Book -======== - -.. toctree:: - :maxdepth: 1 - - intro - usage - middlewares - organizing_controllers - services - providers - testing - cookbook/index - internals - contributing - providers/index - web_servers - changelog diff --git a/doc/internals.rst b/doc/internals.rst deleted file mode 100644 index c7ffac8..0000000 --- a/doc/internals.rst +++ /dev/null @@ -1,84 +0,0 @@ -Internals -========= - -This chapter will tell you how Silex works internally. - -Silex ------ - -Application -~~~~~~~~~~~ - -The application is the main interface to Silex. It implements Symfony's -`HttpKernelInterface -`_, -so you can pass a `Request -`_ -to the ``handle`` method and it will return a `Response -`_. - -It extends the ``Pimple`` service container, allowing for flexibility on the -outside as well as the inside. You could replace any service, and you are also -able to read them. - -The application makes strong use of the `EventDispatcher -`_ to hook into the Symfony `HttpKernel -`_ -events. This allows fetching the ``Request``, converting string responses into -``Response`` objects and handling Exceptions. We also use it to dispatch some -custom events like before/after middlewares and errors. - -Controller -~~~~~~~~~~ - -The Symfony `Route -`_ is -actually quite powerful. Routes can be named, which allows for URL generation. -They can also have requirements for the variable parts. In order to allow -setting these through a nice interface, the ``match`` method (which is used by -``get``, ``post``, etc.) returns an instance of the ``Controller``, which -wraps a route. - -ControllerCollection -~~~~~~~~~~~~~~~~~~~~ - -One of the goals of exposing the `RouteCollection -`_ -was to make it mutable, so providers could add stuff to it. 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. - -To solve this challenge we came up with a staging area for routes. The -``ControllerCollection`` holds the controllers until ``flush`` is called, at -which point the routes are added to the ``RouteCollection``. Also, the -controllers are then frozen. This means that they can no longer be modified -and will throw an Exception if you try to do so. - -Unfortunately no good way for flushing implicitly could be found, which is why -flushing is now always explicit. The Application will flush, but if you want -to read the ``ControllerCollection`` before the request takes place, you will -have to call flush yourself. - -The ``Application`` provides a shortcut ``flush`` method for flushing the -``ControllerCollection``. - -.. tip:: - - Instead of creating an instance of ``RouteCollection`` yourself, use the - ``$app['controllers_factory']`` factory instead. - -Symfony -------- - -Following Symfony components are used by Silex: - -* **HttpFoundation**: For ``Request`` and ``Response``. - -* **HttpKernel**: Because we need a heart. - -* **Routing**: For matching defined routes. - -* **EventDispatcher**: For hooking into the HttpKernel. - -For more information, `check out the Symfony website `_. diff --git a/doc/intro.rst b/doc/intro.rst deleted file mode 100644 index 2ab2bc3..0000000 --- a/doc/intro.rst +++ /dev/null @@ -1,50 +0,0 @@ -Introduction -============ - -Silex is a PHP microframework. It is built on the shoulders of `Symfony`_ and -`Pimple`_ and also inspired by `Sinatra`_. - -Silex aims to be: - -* *Concise*: Silex exposes an intuitive and concise API. - -* *Extensible*: Silex has an extension system based around the Pimple - service-container that makes it easy to tie in third party libraries. - -* *Testable*: Silex uses Symfony's HttpKernel which abstracts request and - response. This makes it very easy to test apps and the framework itself. It - also respects the HTTP specification and encourages its proper use. - -In a nutshell, you define controllers and map them to routes, all in one step. - -Usage ------ - -.. code-block:: php - - get('/hello/{name}', function ($name) use ($app) { - return 'Hello '.$app->escape($name); - }); - - $app->run(); - -All that is needed to get access to the Framework is to include the -autoloader. - -Next, a route for ``/hello/{name}`` that matches for ``GET`` requests is -defined. When the route matches, the function is executed and the return value -is sent back to the client. - -Finally, the app is run. Visit ``/hello/world`` to see the result. It's really -that easy! - -.. _Symfony: http://symfony.com/ -.. _Pimple: http://pimple.sensiolabs.org/ -.. _Sinatra: http://www.sinatrarb.com/ diff --git a/doc/middlewares.rst b/doc/middlewares.rst deleted file mode 100644 index c5c17cf..0000000 --- a/doc/middlewares.rst +++ /dev/null @@ -1,162 +0,0 @@ -Middleware -========== - -Silex allows you to run code, that changes the default Silex behavior, at -different stages during the handling of a request through *middleware*: - -* *Application middleware* is triggered independently of the current handled - request; - -* *Route middleware* is triggered when its associated route is matched. - -Application Middleware ----------------------- - -Application middleware is only run for the "master" Request. - -Before Middleware -~~~~~~~~~~~~~~~~~ - -A *before* application middleware allows you to tweak the Request before the -controller is executed:: - - $app->before(function (Request $request, Application $app) { - // ... - }); - -By default, the middleware is run after the routing and the security. - -If you want your middleware to be run even if an exception is thrown early on -(on a 404 or 403 error for instance), then, you need to register it as an -early event:: - - $app->before(function (Request $request, Application $app) { - // ... - }, Application::EARLY_EVENT); - -In this case, the routing and the security won't have been executed, and so you -won't have access to the locale, the current route, or the security user. - -.. note:: - - The before middleware is an event registered on the Symfony *request* - event. - -After Middleware -~~~~~~~~~~~~~~~~ - -An *after* application middleware allows you to tweak the Response before it -is sent to the client:: - - $app->after(function (Request $request, Response $response) { - // ... - }); - -.. note:: - - The after middleware is an event registered on the Symfony *response* - event. - -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):: - - $app->finish(function (Request $request, Response $response) { - // ... - // Warning: modifications to the Request or Response will be ignored - }); - -.. note:: - - The finish middleware is an event registered on the Symfony *terminate* - event. - -Route Middleware ----------------- - -Route middleware is added to routes or route collections and it is only -triggered when the corresponding route is matched. You can also stack them:: - - $app->get('/somewhere', function () { - // ... - }) - ->before($before1) - ->before($before2) - ->after($after1) - ->after($after2) - ; - -Before Middleware -~~~~~~~~~~~~~~~~~ - -A *before* route middleware is fired just before the route callback, but after -the *before* application middleware:: - - $before = function (Request $request, Application $app) { - // ... - }; - - $app->get('/somewhere', function () { - // ... - }) - ->before($before); - -After Middleware -~~~~~~~~~~~~~~~~ - -An *after* route middleware is fired just after the route callback, but before -the application *after* application middleware:: - - $after = function (Request $request, Response $response, Application $app) { - // ... - }; - - $app->get('/somewhere', function () { - // ... - }) - ->after($after); - -Middleware Priority -------------------- - -You can add as much middleware as you want, in which case they are triggered -in the same order as you added them. - -You can explicitly control the priority of your middleware by passing an -additional argument to the registration methods:: - - $app->before(function (Request $request) { - // ... - }, 32); - -As a convenience, two constants allow you to register an event as early as -possible or as late as possible:: - - $app->before(function (Request $request) { - // ... - }, Application::EARLY_EVENT); - - $app->before(function (Request $request) { - // ... - }, Application::LATE_EVENT); - -Short-circuiting the Controller -------------------------------- - -If a *before* middleware returns a ``Response`` object, the request handling is -short-circuited (the next middleware won't be run, nor the route -callback), and the Response is passed to the *after* middleware right away:: - - $app->before(function (Request $request) { - // redirect the user to the login screen if access to the Resource is protected - if (...) { - return new RedirectResponse('/login'); - } - }); - -.. note:: - - A ``RuntimeException`` is thrown if a before middleware does not return a - Response or ``null``. diff --git a/doc/organizing_controllers.rst b/doc/organizing_controllers.rst deleted file mode 100644 index 50558cb..0000000 --- a/doc/organizing_controllers.rst +++ /dev/null @@ -1,84 +0,0 @@ -Organizing Controllers -====================== - -When your application starts to define too many controllers, you might want to -group them logically:: - - // define controllers for a blog - $blog = $app['controllers_factory']; - $blog->get('/', function () { - return 'Blog home page'; - }); - // ... - - // define controllers for a forum - $forum = $app['controllers_factory']; - $forum->get('/', function () { - return 'Forum home page'; - }); - - // define "global" controllers - $app->get('/', function () { - return 'Main home page'; - }); - - $app->mount('/blog', $blog); - $app->mount('/forum', $forum); - - // define controllers for a admin - $app->mount('/admin', function ($admin) { - // recursively mount - $admin->mount('/blog', function ($user) { - $user->get('/', function () { - return 'Admin Blog home page'; - }); - }); - }); - -.. note:: - - ``$app['controllers_factory']`` is a factory that returns a new instance - of ``ControllerCollection`` when used. - -``mount()`` prefixes all routes with the given prefix and merges them into the -main Application. So, ``/`` will map to the main home page, ``/blog/`` to the -blog home page, ``/forum/`` to the forum home page, and ``/admin/blog/`` to the -admin blog home page. - -.. caution:: - - When mounting a route collection under ``/blog``, it is not possible to - define a route for the ``/blog`` URL. The shortest possible URL is - ``/blog/``. - -.. note:: - - When calling ``get()``, ``match()``, or any other HTTP methods on the - Application, you are in fact calling them on a default instance of - ``ControllerCollection`` (stored in ``$app['controllers']``). - -Another benefit is the ability to apply settings on a set of controllers very -easily. Building on the example from the middleware section, here is how you -would secure all controllers for the backend collection:: - - $backend = $app['controllers_factory']; - - // ensure that all controllers require logged-in users - $backend->before($mustBeLogged); - -.. tip:: - - For a better readability, you can split each controller collection into a - separate file:: - - // blog.php - $blog = $app['controllers_factory']; - $blog->get('/', function () { return 'Blog home page'; }); - - return $blog; - - // app.php - $app->mount('/blog', include 'blog.php'); - - Instead of requiring a file, you can also create a :ref:`Controller - provider `. diff --git a/doc/providers.rst b/doc/providers.rst deleted file mode 100644 index a53fdc9..0000000 --- a/doc/providers.rst +++ /dev/null @@ -1,256 +0,0 @@ -Providers -========= - -Providers allow the developer to reuse parts of an application into another -one. Silex provides two types of providers defined by two interfaces: -``ServiceProviderInterface`` for services and ``ControllerProviderInterface`` -for controllers. - -Service Providers ------------------ - -Loading providers -~~~~~~~~~~~~~~~~~ - -In order to load and use a service provider, you must register it on the -application:: - - $app = new Silex\Application(); - - $app->register(new Acme\DatabaseServiceProvider()); - -You can also provide some parameters as a second argument. These will be set -**after** the provider is registered, but **before** it is booted:: - - $app->register(new Acme\DatabaseServiceProvider(), array( - 'database.dsn' => 'mysql:host=localhost;dbname=myapp', - 'database.user' => 'root', - 'database.password' => 'secret_root_password', - )); - -Conventions -~~~~~~~~~~~ - -You need to watch out in what order you do certain things when interacting -with providers. Just keep these rules in mind: - -* Overriding existing services must occur **after** the provider is - registered. - - *Reason: If the service already exists, the provider will overwrite it.* - -* You can set parameters any time **after** the provider is registered, but - **before** the service is accessed. - - *Reason: Providers can set default values for parameters. Just like with - services, the provider will overwrite existing values.* - -Included providers -~~~~~~~~~~~~~~~~~~ - -There are a few providers that you get out of the box. All of these are within -the ``Silex\Provider`` namespace: - -* :doc:`DoctrineServiceProvider ` -* :doc:`FormServiceProvider ` -* :doc:`HttpCacheServiceProvider ` -* :doc:`MonologServiceProvider ` -* :doc:`RememberMeServiceProvider ` -* :doc:`SecurityServiceProvider ` -* :doc:`SerializerServiceProvider ` -* :doc:`ServiceControllerServiceProvider ` -* :doc:`SessionServiceProvider ` -* :doc:`SwiftmailerServiceProvider ` -* :doc:`TranslationServiceProvider ` -* :doc:`TwigServiceProvider ` -* :doc:`ValidatorServiceProvider ` - -.. note:: - - The Silex core team maintains a `WebProfiler - `_ provider that helps debug - code in the development environment thanks to the Symfony web debug toolbar - and the Symfony profiler. - -Third party providers -~~~~~~~~~~~~~~~~~~~~~ - -Some service providers are developed by the community. Those third-party -providers are listed on `Silex' repository wiki -`_. - -You are encouraged to share yours. - -Creating a provider -~~~~~~~~~~~~~~~~~~~ - -Providers must implement the ``Pimple\ServiceProviderInterface``:: - - interface ServiceProviderInterface - { - public function register(Container $container); - } - -This is very straight forward, just create a new class that implements the -register method. In the ``register()`` method, you can define services on the -application which then may make use of other services and parameters. - -.. tip:: - - The ``Pimple\ServiceProviderInterface`` belongs to the Pimple package, so - take care to only use the API of ``Pimple\Container`` within your - ``register`` method. Not only is this a good practice due to the way Pimple - and Silex work, but may allow your provider to be used outside of Silex. - -Optionally, your service provider can implement the -``Silex\Api\BootableProviderInterface``. A bootable provider must -implement the ``boot()`` method, with which you can configure the application, just -before it handles a request:: - - interface BootableProviderInterface - { - function boot(Application $app); - } - -Another optional interface, is the ``Silex\Api\EventListenerProviderInterface``. -This interface contains the ``subscribe()`` method, which allows your provider to -subscribe event listener with Silex's EventDispatcher, just before it handles a -request:: - - interface EventListenerProviderInterface - { - function subscribe(Container $app, EventDispatcherInterface $dispatcher); - } - -Here is an example of such a provider:: - - namespace Acme; - - use Pimple\Container; - use Pimple\ServiceProviderInterface; - use Silex\Application; - use Silex\Api\BootableProviderInterface; - use Silex\Api\EventListenerProviderInterface; - use Symfony\Component\HttpKernel\KernelEvents; - use Symfony\Component\HttpKernel\Event\FilterResponseEvent; - - class HelloServiceProvider implements ServiceProviderInterface, BootableProviderInterface, EventListenerProviderInterface - { - public function register(Container $app) - { - $app['hello'] = $app->protect(function ($name) use ($app) { - $default = $app['hello.default_name'] ? $app['hello.default_name'] : ''; - $name = $name ?: $default; - - return 'Hello '.$app->escape($name); - }); - } - - public function boot(Application $app) - { - // do something - } - - public function subscribe(Container $app, EventDispatcherInterface $dispatcher) - { - $dispatcher->addListener(KernelEvents::REQUEST, function(FilterResponseEvent $event) use ($app) { - // do something - }); - } - } - -This class provides a ``hello`` service which is a protected closure. It takes -a ``name`` argument and will return ``hello.default_name`` if no name is -given. If the default is also missing, it will use an empty string. - -You can now use this provider as follows:: - - use Symfony\Component\HttpFoundation\Request; - - $app = new Silex\Application(); - - $app->register(new Acme\HelloServiceProvider(), array( - 'hello.default_name' => 'Igor', - )); - - $app->get('/hello', function (Request $request) use ($app) { - $name = $request->get('name'); - - return $app['hello']($name); - }); - -In this example we are getting the ``name`` parameter from the query string, -so the request path would have to be ``/hello?name=Fabien``. - -.. _controller-providers: - -Controller Providers --------------------- - -Loading providers -~~~~~~~~~~~~~~~~~ - -In order to load and use a controller provider, you must "mount" its -controllers under a path:: - - $app = new Silex\Application(); - - $app->mount('/blog', new Acme\BlogControllerProvider()); - -All controllers defined by the provider will now be available under the -``/blog`` path. - -Creating a provider -~~~~~~~~~~~~~~~~~~~ - -Providers must implement the ``Silex\Api\ControllerProviderInterface``:: - - interface ControllerProviderInterface - { - public function connect(Application $app); - } - -Here is an example of such a provider:: - - namespace Acme; - - use Silex\Application; - use Silex\Api\ControllerProviderInterface; - - class HelloControllerProvider implements ControllerProviderInterface - { - public function connect(Application $app) - { - // creates a new controller based on the default route - $controllers = $app['controllers_factory']; - - $controllers->get('/', function (Application $app) { - return $app->redirect('/hello'); - }); - - return $controllers; - } - } - -The ``connect`` method must return an instance of ``ControllerCollection``. -``ControllerCollection`` is the class where all controller related methods are -defined (like ``get``, ``post``, ``match``, ...). - -.. tip:: - - The ``Application`` class acts in fact as a proxy for these methods. - -You can use this provider as follows:: - - $app = new Silex\Application(); - - $app->mount('/blog', new Acme\HelloControllerProvider()); - -In this example, the ``/blog/`` path now references the controller defined in -the provider. - -.. tip:: - - 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. diff --git a/doc/providers/asset.rst b/doc/providers/asset.rst deleted file mode 100644 index ef084e5..0000000 --- a/doc/providers/asset.rst +++ /dev/null @@ -1,60 +0,0 @@ -Asset -===== - -The *AssetServiceProvider* provides a way to manage URL generation and -versioning of web assets such as CSS stylesheets, JavaScript files and image -files. - -Parameters ----------- - -* **assets.version**: Default version for assets. - -* **assets.format_version** (optional): Default format for assets. - -* **assets.named_packages** (optional): Named packages. Keys are the package - names and values the configuration (supported keys are ``version``, - ``version_format``, ``base_urls``, and ``base_path``). - -Services --------- - -* **assets.packages**: The asset service. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\AssetServiceProvider(), array( - 'assets.version' => 'v1', - 'assets.version_format' => '%s?version=%s', - 'assets.named_packages' => array( - 'css' => array('version' => 'css2', 'base_path' => '/whatever-makes-sense'), - 'images' => array('base_urls' => array('https://img.example.com')), - ), - )); - -.. note:: - - Add the Symfony Asset Component as a dependency: - - .. code-block:: bash - - composer require symfony/asset - -Usage ------ - -The AssetServiceProvider is mostly useful with the Twig provider: - -.. code-block:: jinja - - {{ asset('/css/foo.png') }} - {{ asset('/css/foo.css', 'css') }} - {{ asset('/img/foo.png', 'images') }} - - {{ asset_version('/css/foo.png') }} - -For more information, check out the `Asset Component documentation -`_. diff --git a/doc/providers/csrf.rst b/doc/providers/csrf.rst deleted file mode 100644 index 3bd35f4..0000000 --- a/doc/providers/csrf.rst +++ /dev/null @@ -1,52 +0,0 @@ -CSRF -==== - -The *CsrfServiceProvider* provides a service for building forms in your -application with the Symfony Form component. - -Parameters ----------- - -* none - -Services --------- - -* **csrf.token_manager**: An instance of an implementation of the - `CsrfProviderInterface - `_, - defaults to a `DefaultCsrfProvider - `_. - -Registering ------------ - -.. code-block:: php - - use Silex\Provider\CsrfServiceProvider; - - $app->register(new CsrfServiceProvider()); - -.. note:: - - Add the Symfony's `Security CSRF Component - `_ as a - dependency: - - .. code-block:: bash - - composer require symfony/security-csrf - -Usage ------ - -When the CSRF Service Provider is registered, all forms created via the Form -Service Provider are protected against CSRF by default. - -You can also use the CSRF protection even without using the Symfony Form -component. If, for example, you're doing a DELETE action, you can check the -CSRF token:: - - use Symfony\Component\Security\Csrf\CsrfToken; - - $app['csrf.token_manager']->isTokenValid(new CsrfToken('token_id', 'TOKEN')); diff --git a/doc/providers/doctrine.rst b/doc/providers/doctrine.rst deleted file mode 100644 index 0ef167b..0000000 --- a/doc/providers/doctrine.rst +++ /dev/null @@ -1,137 +0,0 @@ -Doctrine -======== - -The *DoctrineServiceProvider* provides integration with the `Doctrine DBAL -`_ for easy database access -(Doctrine ORM integration is **not** supplied). - -Parameters ----------- - -* **db.options**: Array of Doctrine DBAL options. - - These options are available: - - * **driver**: The database driver to use, defaults to ``pdo_mysql``. - Can be any of: ``pdo_mysql``, ``pdo_sqlite``, ``pdo_pgsql``, - ``pdo_oci``, ``oci8``, ``ibm_db2``, ``pdo_ibm``, ``pdo_sqlsrv``. - - * **dbname**: The name of the database to connect to. - - * **host**: The host of the database to connect to. Defaults to - localhost. - - * **user**: The user of the database to connect to. Defaults to - root. - - * **password**: The password of the database to connect to. - - * **charset**: Only relevant for ``pdo_mysql``, and ``pdo_oci/oci8``, - specifies the charset used when connecting to the database. - - * **path**: Only relevant for ``pdo_sqlite``, specifies the path to - the SQLite database. - - * **port**: Only relevant for ``pdo_mysql``, ``pdo_pgsql``, and ``pdo_oci/oci8``, - specifies the port of the database to connect to. - - These and additional options are described in detail in the `Doctrine DBAL - configuration documentation `_. - -Services --------- - -* **db**: The database connection, instance of - ``Doctrine\DBAL\Connection``. - -* **db.config**: Configuration object for Doctrine. Defaults to - an empty ``Doctrine\DBAL\Configuration``. - -* **db.event_manager**: Event Manager for Doctrine. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\DoctrineServiceProvider(), array( - 'db.options' => array( - 'driver' => 'pdo_sqlite', - 'path' => __DIR__.'/app.db', - ), - )); - -.. note:: - - Add the Doctrine DBAL as a dependency: - - .. code-block:: bash - - composer require "doctrine/dbal:~2.2" - -Usage ------ - -The Doctrine provider provides a ``db`` service. Here is a usage -example:: - - $app->get('/blog/{id}', function ($id) use ($app) { - $sql = "SELECT * FROM posts WHERE id = ?"; - $post = $app['db']->fetchAssoc($sql, array((int) $id)); - - return "

{$post['title']}

". - "

{$post['body']}

"; - }); - -Using multiple databases ------------------------- - -The Doctrine provider can allow access to multiple databases. In order to -configure the data sources, replace the **db.options** with **dbs.options**. -**dbs.options** is an array of configurations where keys are connection names -and values are options:: - - $app->register(new Silex\Provider\DoctrineServiceProvider(), array( - 'dbs.options' => array ( - 'mysql_read' => array( - 'driver' => 'pdo_mysql', - 'host' => 'mysql_read.someplace.tld', - 'dbname' => 'my_database', - 'user' => 'my_username', - 'password' => 'my_password', - 'charset' => 'utf8mb4', - ), - 'mysql_write' => array( - 'driver' => 'pdo_mysql', - 'host' => 'mysql_write.someplace.tld', - 'dbname' => 'my_database', - 'user' => 'my_username', - 'password' => 'my_password', - 'charset' => 'utf8mb4', - ), - ), - )); - -The first registered connection is the default and can simply be accessed as -you would if there was only one connection. Given the above configuration, -these two lines are equivalent:: - - $app['db']->fetchAll('SELECT * FROM table'); - - $app['dbs']['mysql_read']->fetchAll('SELECT * FROM table'); - -Using multiple connections:: - - $app->get('/blog/{id}', function ($id) use ($app) { - $sql = "SELECT * FROM posts WHERE id = ?"; - $post = $app['dbs']['mysql_read']->fetchAssoc($sql, array((int) $id)); - - $sql = "UPDATE posts SET value = ? WHERE id = ?"; - $app['dbs']['mysql_write']->executeUpdate($sql, array('newValue', (int) $id)); - - return "

{$post['title']}

". - "

{$post['body']}

"; - }); - -For more information, consult the `Doctrine DBAL documentation -`_. diff --git a/doc/providers/form.rst b/doc/providers/form.rst deleted file mode 100644 index 6360007..0000000 --- a/doc/providers/form.rst +++ /dev/null @@ -1,204 +0,0 @@ -Form -==== - -The *FormServiceProvider* provides a service for building forms in -your application with the Symfony Form component. - -Parameters ----------- - -* none - -Services --------- - -* **form.factory**: An instance of `FormFactory - `_, - that is used to build a form. - -Registering ------------ - -.. code-block:: php - - use Silex\Provider\FormServiceProvider; - - $app->register(new FormServiceProvider()); - -.. note:: - - If you don't want to create your own form layout, it's fine: a default one - will be used. But you will have to register the :doc:`translation provider - ` as the default form layout requires it:: - - $app->register(new Silex\Provider\TranslationServiceProvider(), array( - 'translator.domains' => array(), - )); - - If you want to use validation with forms, do not forget to register the - :doc:`Validator provider `. - -.. note:: - - Add the Symfony Form Component as a dependency: - - .. code-block:: bash - - composer require symfony/form - - If you are going to use the validation extension with forms, you must also - add a dependency to the ``symfony/config`` and ``symfony/translation`` - components: - - .. code-block:: bash - - composer require symfony/validator symfony/config symfony/translation - - If you want to use forms in your Twig templates, you can also install the - Symfony Twig Bridge. Make sure to install, if you didn't do that already, - the Translation component in order for the bridge to work: - - .. code-block:: bash - - composer require symfony/twig-bridge symfony/translation - -Usage ------ - -The FormServiceProvider provides a ``form.factory`` service. Here is a usage -example:: - - use Symfony\Component\Form\Extension\Core\Type\FormType; - use Symfony\Component\Form\Extension\Core\Type\ChoiceType; - - $app->match('/form', function (Request $request) use ($app) { - // some default data for when the form is displayed the first time - $data = array( - 'name' => 'Your name', - 'email' => 'Your email', - ); - - $form = $app['form.factory']->createBuilder(FormType::class, $data) - ->add('name') - ->add('email') - ->add('billing_plan', ChoiceType::class, array( - 'choices' => array(1 => 'free', 2 => 'small_business', 3 => 'corporate'), - 'expanded' => true, - )) - ->getForm(); - - $form->handleRequest($request); - - if ($form->isValid()) { - $data = $form->getData(); - - // do something with the data - - // redirect somewhere - return $app->redirect('...'); - } - - // display the form - return $app['twig']->render('index.twig', array('form' => $form->createView())); - }); - -And here is the ``index.twig`` form template (requires ``symfony/twig-bridge``): - -.. code-block:: jinja - -
- {{ form_widget(form) }} - - -
- -If you are using the validator provider, you can also add validation to your -form by adding constraints on the fields:: - - use Symfony\Component\Form\Extension\Core\Type\FormType; - use Symfony\Component\Form\Extension\Core\Type\TextType; - use Symfony\Component\Form\Extension\Core\Type\ChoiceType; - use Symfony\Component\Validator\Constraints as Assert; - - $app->register(new Silex\Provider\ValidatorServiceProvider()); - $app->register(new Silex\Provider\TranslationServiceProvider(), array( - 'translator.domains' => array(), - )); - - $form = $app['form.factory']->createBuilder(FormType::class) - ->add('name', TextType::class, array( - 'constraints' => array(new Assert\NotBlank(), new Assert\Length(array('min' => 5))) - )) - ->add('email', TextType::class, array( - 'constraints' => new Assert\Email() - )) - ->add('billing_plan', ChoiceType::class, array( - 'choices' => array(1 => 'free', 2 => 'small_business', 3 => 'corporate'), - 'expanded' => true, - 'constraints' => new Assert\Choice(array(1, 2, 3)), - )) - ->getForm(); - -You can register form types by extending ``form.types``:: - - $app['your.type.service'] = function ($app) { - return new YourServiceFormType(); - }; - $app->extend('form.types', function ($types) use ($app) { - $types[] = new YourFormType(); - $types[] = 'your.type.service'; - - return $types; - })); - -You can register form extensions by extending ``form.extensions``:: - - $app->extend('form.extensions', function ($extensions) use ($app) { - $extensions[] = new YourTopFormExtension(); - - return $extensions; - }); - - -You can register form type extensions by extending ``form.type.extensions``:: - - $app['your.type.extension.service'] = function ($app) { - return new YourServiceFormTypeExtension(); - }; - $app->extend('form.type.extensions', function ($extensions) use ($app) { - $extensions[] = new YourFormTypeExtension(); - $extensions[] = 'your.type.extension.service'; - - return $extensions; - }); - -You can register form type guessers by extending ``form.type.guessers``:: - - $app['your.type.guesser.service'] = function ($app) { - return new YourServiceFormTypeGuesser(); - }; - $app->extend('form.type.guessers', function ($guessers) use ($app) { - $guessers[] = new YourFormTypeGuesser(); - $guessers[] = 'your.type.guesser.service'; - - return $guessers; - }); - -.. warning:: - - CSRF protection is only available and automatically enabled when the - :doc:`CSRF Service Provider
` is registered. - -Traits ------- - -``Silex\Application\FormTrait`` adds the following shortcuts: - -* **form**: Creates a FormBuilder instance. - -.. code-block:: php - - $app->form($data); - -For more information, consult the `Symfony Forms documentation -`_. diff --git a/doc/providers/http_cache.rst b/doc/providers/http_cache.rst deleted file mode 100644 index 8bc98f6..0000000 --- a/doc/providers/http_cache.rst +++ /dev/null @@ -1,128 +0,0 @@ -HTTP Cache -========== - -The *HttpCacheServiceProvider* provides support for the Symfony Reverse -Proxy. - -Parameters ----------- - -* **http_cache.cache_dir**: The cache directory to store the HTTP cache data. - -* **http_cache.options** (optional): An array of options for the `HttpCache - `_ - constructor. - -Services --------- - -* **http_cache**: An instance of `HttpCache - `_. - -* **http_cache.esi**: An instance of `Esi - `_, - that implements the ESI capabilities to Request and Response instances. - -* **http_cache.store**: An instance of `Store - `_, - that implements all the logic for storing cache metadata (Request and Response - headers). - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\HttpCacheServiceProvider(), array( - 'http_cache.cache_dir' => __DIR__.'/cache/', - )); - -Usage ------ - -Silex already supports any reverse proxy like Varnish out of the box by -setting Response HTTP cache headers:: - - use Symfony\Component\HttpFoundation\Response; - - $app->get('/', function() { - return new Response('Foo', 200, array( - 'Cache-Control' => 's-maxage=5', - )); - }); - -.. tip:: - - If you want Silex to trust the ``X-Forwarded-For*`` headers from your - reverse proxy at address $ip, you will need to whitelist it as documented - in `Trusting Proxies - `_. - - If you would be running Varnish in front of your application on the same machine:: - - use Symfony\Component\HttpFoundation\Request; - - Request::setTrustedProxies(array('127.0.0.1', '::1')); - $app->run(); - -This provider allows you to use the Symfony reverse proxy natively with -Silex applications by using the ``http_cache`` service. The Symfony reverse proxy -acts much like any other proxy would, so you will want to whitelist it:: - - use Symfony\Component\HttpFoundation\Request; - - Request::setTrustedProxies(array('127.0.0.1')); - $app['http_cache']->run(); - -The provider also provides ESI support:: - - $app->get('/', function() { - $response = new Response(<< - - Hello - - - - - EOF - , 200, array( - 'Surrogate-Control' => 'content="ESI/1.0"', - )); - - $response->setTtl(20); - - return $response; - }); - - $app->get('/included', function() { - $response = new Response('Foo'); - $response->setTtl(5); - - return $response; - }); - - $app['http_cache']->run(); - -If your application doesn't use ESI, you can disable it to slightly improve the -overall performance:: - - $app->register(new Silex\Provider\HttpCacheServiceProvider(), array( - 'http_cache.cache_dir' => __DIR__.'/cache/', - 'http_cache.esi' => null, - )); - -.. tip:: - - To help you debug caching issues, set your application ``debug`` to true. - Symfony automatically adds a ``X-Symfony-Cache`` header to each response - with useful information about cache hits and misses. - - If you are *not* using the Symfony Session provider, you might want to set - the PHP ``session.cache_limiter`` setting to an empty value to avoid the - default PHP behavior. - - Finally, check that your Web server does not override your caching strategy. - -For more information, consult the `Symfony HTTP Cache documentation -`_. diff --git a/doc/providers/http_fragment.rst b/doc/providers/http_fragment.rst deleted file mode 100644 index 8e68185..0000000 --- a/doc/providers/http_fragment.rst +++ /dev/null @@ -1,70 +0,0 @@ -HTTP Fragment -============= - -The *HttpFragmentServiceProvider* provides support for the Symfony fragment -sub-framework, which allows you to embed fragments of HTML in a template. - -Parameters ----------- - -* **fragment.path**: The path to use for the URL generated for ESI and - HInclude URLs (``/_fragment`` by default). - -* **uri_signer.secret**: The secret to use for the URI signer service (used - for the HInclude renderer). - -* **fragment.renderers.hinclude.global_template**: The content or Twig - template to use for the default content when using the HInclude renderer. - -Services --------- - -* **fragment.handler**: An instance of `FragmentHandler - `_. - -* **fragment.renderers**: An array of fragment renderers (by default, the - inline, ESI, and HInclude renderers are pre-configured). - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\HttpFragmentServiceProvider()); - -Usage ------ - -.. note:: - - 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: - -.. code-block:: jinja - - The main page content. - - {{ render('/foo') }} - - 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): - -.. code-block:: jinja - - {{ render(url('route_name')) }} - - {{ render_esi(url('route_name')) }} - - {{ render_hinclude(url('route_name')) }} diff --git a/doc/providers/index.rst b/doc/providers/index.rst deleted file mode 100644 index 8c5a175..0000000 --- a/doc/providers/index.rst +++ /dev/null @@ -1,24 +0,0 @@ -Built-in Service Providers -========================== - -.. toctree:: - :maxdepth: 1 - - twig - asset - monolog - session - swiftmailer - locale - translation - validator - form - csrf - http_cache - http_fragment - security - remember_me - serializer - service_controller - var_dumper - doctrine diff --git a/doc/providers/locale.rst b/doc/providers/locale.rst deleted file mode 100644 index 8f6cd67..0000000 --- a/doc/providers/locale.rst +++ /dev/null @@ -1,24 +0,0 @@ -Locale -====== - -The *LocaleServiceProvider* manages the locale of an application. - -Parameters ----------- - -* **locale**: The locale of the user. When set before any request handling, it - defines the default locale (``en`` by default). When a request is being - handled, it is automatically set according to the ``_locale`` request - attribute of the current route. - -Services --------- - -* n/a - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\LocaleServiceProvider()); diff --git a/doc/providers/monolog.rst b/doc/providers/monolog.rst deleted file mode 100644 index 8a83127..0000000 --- a/doc/providers/monolog.rst +++ /dev/null @@ -1,115 +0,0 @@ -Monolog -======= - -The *MonologServiceProvider* provides a default logging mechanism through -Jordi Boggiano's `Monolog `_ library. - -It will log requests and errors and allow you to add logging to your -application. This allows you to debug and monitor the behaviour, -even in production. - -Parameters ----------- - -* **monolog.logfile**: File where logs are written to. -* **monolog.bubble**: (optional) Whether the messages that are handled can bubble up the stack or not. -* **monolog.permission**: (optional) File permissions default (null), nothing change. - -* **monolog.level** (optional): Level of logging, defaults - to ``DEBUG``. Must be one of ``Logger::DEBUG``, ``Logger::INFO``, - ``Logger::WARNING``, ``Logger::ERROR``. ``DEBUG`` will log - everything, ``INFO`` will log everything except ``DEBUG``, - etc. - - In addition to the ``Logger::`` constants, it is also possible to supply the - level in string form, for example: ``"DEBUG"``, ``"INFO"``, ``"WARNING"``, - ``"ERROR"``. - -* **monolog.name** (optional): Name of the monolog channel, - defaults to ``myapp``. - -* **monolog.exception.logger_filter** (optional): An anonymous function that - filters which exceptions should be logged. - -* **monolog.use_error_handler** (optional): Whether errors and uncaught exceptions - should be handled by the Monolog ``ErrorHandler`` class and added to the log. - By default the error handler is enabled unless the application ``debug`` parameter - is set to true. - - Please note that enabling the error handler may silence some errors, - ignoring the PHP ``display_errors`` configuration setting. - -Services --------- - -* **monolog**: The monolog logger instance. - - Example usage:: - - $app['monolog']->addDebug('Testing the Monolog logging.'); - -* **monolog.listener**: An event listener to log requests, responses and errors. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\MonologServiceProvider(), array( - 'monolog.logfile' => __DIR__.'/development.log', - )); - -.. note:: - - Add Monolog as a dependency: - - .. code-block:: bash - - composer require monolog/monolog - -Usage ------ - -The MonologServiceProvider provides a ``monolog`` service. You can use it to -add log entries for any logging level through ``addDebug()``, ``addInfo()``, -``addWarning()`` and ``addError()``:: - - use Symfony\Component\HttpFoundation\Response; - - $app->post('/user', function () use ($app) { - // ... - - $app['monolog']->addInfo(sprintf("User '%s' registered.", $username)); - - return new Response('', 201); - }); - -Customization -------------- - -You can configure Monolog (like adding or changing the handlers) before using -it by extending the ``monolog`` service:: - - $app->extend('monolog', function($monolog, $app) { - $monolog->pushHandler(...); - - return $monolog; - }); - -By default, all requests, responses and errors are logged by an event listener -registered as a service called `monolog.listener`. You can replace or remove -this service if you want to modify or disable the logged information. - -Traits ------- - -``Silex\Application\MonologTrait`` adds the following shortcuts: - -* **log**: Logs a message. - -.. code-block:: php - - $app->log(sprintf("User '%s' registered.", $username)); - -For more information, check out the `Monolog documentation -`_. diff --git a/doc/providers/remember_me.rst b/doc/providers/remember_me.rst deleted file mode 100644 index 7fdaaab..0000000 --- a/doc/providers/remember_me.rst +++ /dev/null @@ -1,69 +0,0 @@ -Remember Me -=========== - -The *RememberMeServiceProvider* adds "Remember-Me" authentication to the -*SecurityServiceProvider*. - -Parameters ----------- - -n/a - -Services --------- - -n/a - -.. note:: - - The service provider defines many other services that are used internally - but rarely need to be customized. - -Registering ------------ - -Before registering this service provider, you must register the -*SecurityServiceProvider*:: - - $app->register(new Silex\Provider\SecurityServiceProvider()); - $app->register(new Silex\Provider\RememberMeServiceProvider()); - - $app['security.firewalls'] = array( - 'my-firewall' => array( - 'pattern' => '^/secure$', - 'form' => true, - 'logout' => true, - 'remember_me' => array( - 'key' => 'Choose_A_Unique_Random_Key', - 'always_remember_me' => true, - /* Other options */ - ), - 'users' => array( /* ... */ ), - ), - ); - -Options -------- - -* **key**: A secret key to generate tokens (you should generate a random - string). - -* **name**: Cookie name (default: ``REMEMBERME``). - -* **lifetime**: Cookie lifetime (default: ``31536000`` ~ 1 year). - -* **path**: Cookie path (default: ``/``). - -* **domain**: Cookie domain (default: ``null`` = request domain). - -* **secure**: Cookie is secure (default: ``false``). - -* **httponly**: Cookie is HTTP only (default: ``true``). - -* **always_remember_me**: Enable remember me (default: ``false``). - -* **remember_me_parameter**: Name of the request parameter enabling remember_me - on login. To add the checkbox to the login form. You can find more - information in the `Symfony cookbook - `_ - (default: ``_remember_me``). diff --git a/doc/providers/security.rst b/doc/providers/security.rst deleted file mode 100644 index f84d318..0000000 --- a/doc/providers/security.rst +++ /dev/null @@ -1,711 +0,0 @@ -Security -======== - -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``. - -* **security.encoder.bcrypt.cost** (optional): Defines BCrypt password encoder cost. Defaults to 13. - -Services --------- - -* **security.token_storage**: Gives access to the user token. - -* **security.authorization_checker**: Allows to check authorizations for the - users. - -* **security.authentication_manager**: An instance of - `AuthenticationProviderManager - `_, - responsible for authentication. - -* **security.access_manager**: An instance of `AccessDecisionManager - `_, - 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 (uses ``security.default_encoder``). - -* **security.default_encoder**: The encoder to use by default for all users (BCrypt). - -* **security.encoder.digest**: Digest password encoder. - -* **security.encoder.bcrypt**: BCrypt password encoder. - -* **security.encoder.pbkdf2**: Pbkdf2 password encoder. - -* **user**: Returns the current user - -.. note:: - - The service provider defines many other services that are used internally - but rarely need to be customized. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\SecurityServiceProvider(), array( - 'security.firewalls' => // see below - )); - -.. note:: - - Add the Symfony Security Component as a dependency: - - .. code-block:: bash - - composer require symfony/security - -.. caution:: - - If you're using a form to authenticate users, you need to enable - ``SessionServiceProvider``. - -.. caution:: - - The security features are only available after the Application has been - booted. So, if you want to use it outside of the handling of a request, - don't forget to call ``boot()`` first:: - - $app->boot(); - -Usage ------ - -The Symfony Security component is powerful. To learn more about it, read the -`Symfony Security documentation -`_. - -.. tip:: - - When a security configuration does not behave as expected, enable logging - (with the Monolog extension for instance) as the Security Component logs a - lot of interesting information about what it does and why. - -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:: - - $token = $app['security.token_storage']->getToken(); - -If there is no information about the user, the token is ``null``. If the user -is known, you can get it with a call to ``getUser()``:: - - if (null !== $token) { - $user = $token->getUser(); - } - -The user can be a string, an object with a ``__toString()`` method, or an -instance of `UserInterface -`_. - -Securing a Path with HTTP Authentication -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following configuration uses HTTP basic authentication to secure URLs -under ``/admin/``:: - - $app['security.firewalls'] = array( - 'admin' => array( - 'pattern' => '^/admin', - 'http' => true, - 'users' => array( - // raw password is foo - 'admin' => array('ROLE_ADMIN', '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a'), - ), - ), - ); - -The ``pattern`` is a regular expression on the URL path; the ``http`` setting -tells the security layer to use HTTP basic authentication and the ``users`` -entry defines valid users. - -If you want to restrict the firewall by more than the URL pattern (like the -HTTP method, the client IP, the hostname, or any Request attributes), use an -instance of a `RequestMatcher -`_ -for the ``pattern`` option:: - - use Symfony/Component/HttpFoundation/RequestMatcher; - - $app['security.firewalls'] = array( - 'admin' => array( - 'pattern' => new RequestMatcher('^/admin', 'example.com', 'POST'), - // ... - ), - ); - -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); - -* The user encoded password. - -.. caution:: - - All users must at least have one role associated with them. - -The default configuration of the extension enforces encoded passwords. To -generate a valid encoded password from a raw password, use the -``security.encoder_factory`` service:: - - // find the encoder for a UserInterface instance - $encoder = $app['security.encoder_factory']->getEncoder($user); - - // compute the encoded password for foo - $password = $encoder->encodePassword('foo', $user->getSalt()); - -When the user is authenticated, the user stored in the token is an instance of -`User -`_ - -.. caution:: - - If you are using php-cgi under Apache, you need to add this configuration - to make things work correctly: - - .. code-block:: apache - - RewriteEngine On - RewriteCond %{HTTP:Authorization} ^(.+)$ - RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ app.php [QSA,L] - -Securing a Path with a Form -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Using a form to authenticate users is very similar to the above configuration. -Instead of using the ``http`` setting, 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; - -* **check_path**: The check URL used by Symfony to validate the credentials of - the user. - -Here is how to secure all URLs under ``/admin/`` with a form:: - - $app['security.firewalls'] = array( - 'admin' => array( - 'pattern' => '^/admin/', - 'form' => array('login_path' => '/login', 'check_path' => '/admin/login_check'), - 'users' => array( - 'admin' => array('ROLE_ADMIN', '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a'), - ), - ), - ); - -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, the ``anonymous`` authentication mechanism - must be enabled -- see below); - -* The ``check_path`` path must always be defined **inside** the secured area. - -For the login form to work, create a controller like the following:: - - use Symfony\Component\HttpFoundation\Request; - - $app->get('/login', function(Request $request) use ($app) { - return $app['twig']->render('login.html', array( - 'error' => $app['security.last_error']($request), - 'last_username' => $app['session']->get('_security.last_username'), - )); - }); - -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. - -Create the associated template: - -.. code-block:: jinja - -
- {{ error }} - - - -
- -.. note:: - - 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). - -Defining more than one Firewall -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You are not limited to define one firewall per project. - -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). - -It's also useful when you want to secure all URLs except the login form:: - - $app['security.firewalls'] = array( - 'login' => array( - 'pattern' => '^/login$', - ), - 'secured' => array( - 'pattern' => '^.*$', - 'form' => array('login_path' => '/login', 'check_path' => '/login_check'), - 'users' => array( - 'admin' => array('ROLE_ADMIN', '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a'), - ), - ), - ); - -The order of the firewall configurations is significant as the first one to -match wins. The above configuration first ensures that the ``/login`` URL is -not secured (no authentication settings), and then it secures all other URLs. - -.. tip:: - - You can toggle all registered authentication mechanisms for a particular - area on and off with the ``security`` flag:: - - $app['security.firewalls'] = array( - 'api' => array( - 'pattern' => '^/api', - 'security' => $app['debug'] ? false : true, - 'wsse' => true, - - // ... - ), - ); - -Adding a Logout -~~~~~~~~~~~~~~~ - -When using a form for authentication, you can let users log out if you add the -``logout`` setting, where ``logout_path`` must match the main firewall -pattern:: - - $app['security.firewalls'] = array( - 'secured' => array( - 'pattern' => '^/admin/', - 'form' => array('login_path' => '/login', 'check_path' => '/admin/login_check'), - 'logout' => array('logout_path' => '/admin/logout', 'invalidate_session' => true), - - // ... - ), - ); - -A route is automatically generated, based on the configured path (all ``/`` -are replaced with ``_`` and the leading ``/`` is stripped): - -.. code-block:: jinja - - Logout - -Allowing Anonymous Users -~~~~~~~~~~~~~~~~~~~~~~~~ - -When securing only some parts of your website, the user information are not -available in non-secured areas. To make the user accessible in such areas, -enabled the ``anonymous`` authentication mechanism:: - - $app['security.firewalls'] = array( - 'unsecured' => array( - 'anonymous' => true, - - // ... - ), - ); - -When enabling the anonymous setting, a user will always be accessible from the -security context; if the user is not authenticated, it returns the ``anon.`` -string. - -Checking User Roles -~~~~~~~~~~~~~~~~~~~ - -To check if a user is granted some role, use the ``isGranted()`` method on the -security context:: - - if ($app['security.authorization_checker']->isGranted('ROLE_ADMIN')) { - // ... - } - -You can check roles in Twig templates too: - -.. code-block:: jinja - - {% if is_granted('ROLE_ADMIN') %} - Switch to Fabien - {% endif %} - -You can check if a user is "fully authenticated" (not an anonymous user for -instance) with the special ``IS_AUTHENTICATED_FULLY`` role: - -.. code-block:: jinja - - {% if is_granted('IS_AUTHENTICATED_FULLY') %} - Logout - {% else %} - Login - {% endif %} - -Of course you will need to define a ``login`` route for this to work. - -.. tip:: - - Don't use the ``getRoles()`` method to check user roles. - -.. caution:: - - ``isGranted()`` throws an exception when no authentication information is - available (which is the case on non-secured area). - -Impersonating a User -~~~~~~~~~~~~~~~~~~~~ - -If you want to be able to switch to another user (without knowing the user -credentials), enable the ``switch_user`` authentication strategy:: - - $app['security.firewalls'] = array( - 'unsecured' => array( - 'switch_user' => array('parameter' => '_switch_user', 'role' => 'ROLE_ALLOWED_TO_SWITCH'), - - // ... - ), - ); - -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: - -.. code-block:: jinja - - {% if is_granted('ROLE_ALLOWED_TO_SWITCH') %} - Switch to user Fabien - {% endif %} - -You can check that you are impersonating a user by checking the special -``ROLE_PREVIOUS_ADMIN``. This is useful for instance to allow the user to -switch back to their primary account: - -.. code-block:: jinja - - {% if is_granted('ROLE_PREVIOUS_ADMIN') %} - You are an admin but you've switched to another user, - exit the switch. - {% endif %} - -Defining a Role Hierarchy -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Defining a role hierarchy allows to automatically grant users some additional -roles:: - - $app['security.role_hierarchy'] = array( - 'ROLE_ADMIN' => array('ROLE_USER', 'ROLE_ALLOWED_TO_SWITCH'), - ); - -With this configuration, all users with the ``ROLE_ADMIN`` role also -automatically have the ``ROLE_USER`` and ``ROLE_ALLOWED_TO_SWITCH`` roles. - -Defining Access Rules -~~~~~~~~~~~~~~~~~~~~~ - -Roles are a great way to adapt the behavior of your website depending on -groups of users, but they can also be used to further secure some areas by -defining access rules:: - - $app['security.access_rules'] = array( - array('^/admin', 'ROLE_ADMIN', 'https'), - array('^.*$', 'ROLE_USER'), - ); - -With the above configuration, users must have the ``ROLE_ADMIN`` to access the -``/admin`` section of the website, and ``ROLE_USER`` for everything else. -Furthermore, the admin section can only be accessible via HTTPS (if that's not -the case, the user will be automatically redirected). - -.. note:: - - The first argument can also be a `RequestMatcher - `_ - instance. - -Defining a custom User Provider -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Using an array of users is simple and useful when securing an admin section of -a personal website, but you can override this default mechanism with you own. - -The ``users`` setting can be defined as a service that returns an instance of -`UserProviderInterface -`_:: - - 'users' => function () use ($app) { - return new UserProvider($app['db']); - }, - -Here is a simple example of a user provider, where Doctrine DBAL is used to -store the users:: - - use Symfony\Component\Security\Core\User\UserProviderInterface; - use Symfony\Component\Security\Core\User\UserInterface; - use Symfony\Component\Security\Core\User\User; - use Symfony\Component\Security\Core\Exception\UnsupportedUserException; - use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; - use Doctrine\DBAL\Connection; - - class UserProvider implements UserProviderInterface - { - private $conn; - - public function __construct(Connection $conn) - { - $this->conn = $conn; - } - - public function loadUserByUsername($username) - { - $stmt = $this->conn->executeQuery('SELECT * FROM users WHERE username = ?', array(strtolower($username))); - - if (!$user = $stmt->fetch()) { - throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username)); - } - - return new User($user['username'], $user['password'], explode(',', $user['roles']), true, true, true, true); - } - - public function refreshUser(UserInterface $user) - { - if (!$user instanceof User) { - throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); - } - - return $this->loadUserByUsername($user->getUsername()); - } - - public function supportsClass($class) - { - return $class === 'Symfony\Component\Security\Core\User\User'; - } - } - -In this example, instances of the default ``User`` class are created for the -users, but you can define your own class; the only requirement is that the -class must implement `UserInterface -`_ - -And here is the code that you can use to create the database schema and some -sample users:: - - use Doctrine\DBAL\Schema\Table; - - $schema = $app['db']->getSchemaManager(); - if (!$schema->tablesExist('users')) { - $users = new Table('users'); - $users->addColumn('id', 'integer', array('unsigned' => true, 'autoincrement' => true)); - $users->setPrimaryKey(array('id')); - $users->addColumn('username', 'string', array('length' => 32)); - $users->addUniqueIndex(array('username')); - $users->addColumn('password', 'string', array('length' => 255)); - $users->addColumn('roles', 'string', array('length' => 255)); - - $schema->createTable($users); - - $app['db']->insert('users', array( - 'username' => 'fabien', - 'password' => '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a', - 'roles' => 'ROLE_USER' - )); - - $app['db']->insert('users', array( - 'username' => 'admin', - 'password' => '$2y$10$3i9/lVd8UOFIJ6PAMFt8gu3/r5g0qeCJvoSlLCsvMTythye19F77a', - 'roles' => 'ROLE_ADMIN' - )); - } - -.. tip:: - - If you are using the Doctrine ORM, the Symfony bridge for Doctrine - provides a user provider class that is able to load users from your - entities. - -Defining a custom Encoder -~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, Silex uses the ``BCrypt`` algorithm to encode passwords. -Additionally, the password is encoded multiple times. -You can change these defaults by overriding ``security.default_encoder`` -service to return one of the predefined encoders: - -* **security.encoder.digest**: Digest password encoder. - -* **security.encoder.bcrypt**: BCrypt password encoder. - -* **security.encoder.pbkdf2**: Pbkdf2 password encoder. - -.. code-block:: php - - $app['security.default_encoder'] = function ($app) { - return $app['security.encoder.pbkdf2']; - }; - -Or you can define you own, fully customizable encoder:: - - use Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder; - - $app['security.default_encoder'] = function ($app) { - // Plain text (e.g. for debugging) - return new PlaintextPasswordEncoder(); - }; - -.. tip:: - - You can change the default BCrypt encoding cost by overriding ``security.encoder.bcrypt.cost`` - -Defining a custom Authentication Provider -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The Symfony Security component provides a lot of ready-to-use authentication -providers (form, HTTP, X509, remember me, ...), but you can add new ones -easily. To register a new authentication provider, create a service named -``security.authentication_listener.factory.XXX`` where ``XXX`` is the name you want to -use in your configuration:: - - $app['security.authentication_listener.factory.wsse'] = $app->protect(function ($name, $options) use ($app) { - // define the authentication provider object - $app['security.authentication_provider.'.$name.'.wsse'] = function () use ($app) { - return new WsseProvider($app['security.user_provider.default'], __DIR__.'/security_cache'); - }; - - // define the authentication listener object - $app['security.authentication_listener.'.$name.'.wsse'] = function () use ($app) { - return new WsseListener($app['security.token_storage'], $app['security.authentication_manager']); - }; - - return array( - // the authentication provider id - 'security.authentication_provider.'.$name.'.wsse', - // the authentication listener id - 'security.authentication_listener.'.$name.'.wsse', - // the entry point id - null, - // the position of the listener in the stack - 'pre_auth' - ); - }); - -You can now use it in your configuration like any other built-in -authentication provider:: - - $app->register(new Silex\Provider\SecurityServiceProvider(), array( - 'security.firewalls' => array( - 'default' => array( - 'wsse' => true, - - // ... - ), - ), - )); - -Instead of ``true``, you can also define an array of options that customize -the behavior of your authentication factory; it will be passed as the second -argument of your authentication factory (see above). - -This example uses the authentication provider classes as described in the -Symfony `cookbook`_. - - -.. note:: - - The Guard component simplifies the creation of custom authentication - providers. :doc:`How to Create a Custom Authentication System with Guard - ` - -Stateless Authentication -~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, a session cookie is created to persist the security context of -the user. However, if you use certificates, HTTP authentication, WSSE and so -on, the credentials are sent for each request. In that case, you can turn off -persistence by activating the ``stateless`` authentication flag:: - - $app['security.firewalls'] = array( - 'default' => array( - 'stateless' => true, - 'wsse' => true, - - // ... - ), - ); - -Traits ------- - -``Silex\Application\SecurityTrait`` adds the following shortcuts: - -* **encodePassword**: Encode a given password. - -.. code-block:: php - - $user = $app->user(); - - $encoded = $app->encodePassword($user, 'foo'); - -``Silex\Route\SecurityTrait`` adds the following methods to the controllers: - -* **secure**: Secures a controller for the given roles. - -.. code-block:: php - - $app->get('/', function () { - // do something but only for admins - })->secure('ROLE_ADMIN'); - -.. caution:: - - The ``Silex\Route\SecurityTrait`` must be used with a user defined - ``Route`` class, not the application. - - .. code-block:: php - - use Silex\Route; - - class MyRoute extends Route - { - use Route\SecurityTrait; - } - - .. code-block:: php - - $app['route_class'] = 'MyRoute'; - - -.. _cookbook: http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html diff --git a/doc/providers/serializer.rst b/doc/providers/serializer.rst deleted file mode 100644 index 162dbab..0000000 --- a/doc/providers/serializer.rst +++ /dev/null @@ -1,73 +0,0 @@ -Serializer -========== - -The *SerializerServiceProvider* provides a service for serializing objects. - -Parameters ----------- - -None. - -Services --------- - -* **serializer**: An instance of `Symfony\\Component\\Serializer\\Serializer - `_. - -* **serializer.encoders**: `Symfony\\Component\\Serializer\\Encoder\\JsonEncoder - `_ - and `Symfony\\Component\\Serializer\\Encoder\\XmlEncoder - `_. - -* **serializer.normalizers**: `Symfony\\Component\\Serializer\\Normalizer\\CustomNormalizer - `_ - and `Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer - `_. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\SerializerServiceProvider()); - -.. note:: - - Add the Symfony's `Serializer Component - `_ as a - dependency: - - .. code-block:: bash - - composer require symfony/serializer - -Usage ------ - -The ``SerializerServiceProvider`` provider provides a ``serializer`` service:: - - use Silex\Application; - use Silex\Provider\SerializerServiceProvider; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpFoundation\Response; - - $app = new Application(); - - $app->register(new SerializerServiceProvider()); - - // only accept content types supported by the serializer via the assert method. - $app->get("/pages/{id}.{_format}", function (Request $request, $id) use ($app) { - // assume a page_repository service exists that returns Page objects. The - // object returned has getters and setters exposing the state. - $page = $app['page_repository']->find($id); - $format = $request->getRequestFormat(); - - if (!$page instanceof Page) { - $app->abort("No page found for id: $id"); - } - - return new Response($app['serializer']->serialize($page, $format), 200, array( - "Content-Type" => $request->getMimeType($format) - )); - })->assert("_format", "xml|json") - ->assert("id", "\d+"); diff --git a/doc/providers/service_controller.rst b/doc/providers/service_controller.rst deleted file mode 100644 index 15bca28..0000000 --- a/doc/providers/service_controller.rst +++ /dev/null @@ -1,142 +0,0 @@ -Service Controllers -=================== - -As your Silex application grows, you may wish to begin organizing your -controllers in a more formal fashion. Silex can use controller classes out of -the box, but with a bit of work, your controllers can be created as services, -giving you the full power of dependency injection and lazy loading. - -.. ::todo Link above to controller classes cookbook - -Why would I want to do this? ----------------------------- - -- Dependency Injection over Service Location - - Using this method, you can inject the actual dependencies required by your - controller and gain total inversion of control, while still maintaining the - lazy loading of your controllers and its dependencies. Because your - dependencies are clearly defined, they are easily mocked, allowing you to test - your controllers in isolation. - -- Framework Independence - - Using this method, your controllers start to become more independent of the - framework you are using. Carefully crafted, your controllers will become - reusable with multiple frameworks. By keeping careful control of your - dependencies, your controllers could easily become compatible with Silex, - Symfony (full stack) and Drupal, to name just a few. - -Parameters ----------- - -There are currently no parameters for the ``ServiceControllerServiceProvider``. - -Services --------- - -There are no extra services provided, the ``ServiceControllerServiceProvider`` -simply extends the existing **resolver** service. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\ServiceControllerServiceProvider()); - -Usage ------ - -In this slightly contrived example of a blog API, we're going to change the -``/posts.json`` route to use a controller, that is defined as a service. - -.. code-block:: php - - use Silex\Application; - use Demo\Repository\PostRepository; - - $app = new Application(); - - $app['posts.repository'] = function() { - return new PostRepository; - }; - - $app->get('/posts.json', function() use ($app) { - return $app->json($app['posts.repository']->findAll()); - }); - -Rewriting your controller as a service is pretty simple, create a Plain Ol' PHP -Object with your ``PostRepository`` as a dependency, along with an -``indexJsonAction`` method to handle the request. Although not shown in the -example below, you can use type hinting and parameter naming to get the -parameters you need, just like with standard Silex routes. - -If you are a TDD/BDD fan (and you should be), you may notice that this -controller has well defined responsibilities and dependencies, and is easily -tested/specced. You may also notice that the only external dependency is on -``Symfony\Component\HttpFoundation\JsonResponse``, meaning this controller could -easily be used in a Symfony (full stack) application, or potentially with other -applications or frameworks that know how to handle a `Symfony/HttpFoundation -`_ -``Response`` object. - -.. code-block:: php - - namespace Demo\Controller; - - use Demo\Repository\PostRepository; - use Symfony\Component\HttpFoundation\JsonResponse; - - class PostController - { - protected $repo; - - public function __construct(PostRepository $repo) - { - $this->repo = $repo; - } - - public function indexJsonAction() - { - return new JsonResponse($this->repo->findAll()); - } - } - -And lastly, define your controller as a service in the application, along with -your route. The syntax in the route definition is the name of the service, -followed by a single colon (:), followed by the method name. - -.. code-block:: php - - $app['posts.controller'] = function() use ($app) { - return new PostController($app['posts.repository']); - }; - - $app->get('/posts.json', "posts.controller:indexJsonAction"); - -In addition to using classes for service controllers, you can define any -callable as a service in the application to be used for a route. - -.. code-block:: php - - namespace Demo\Controller; - - use Demo\Repository\PostRepository; - use Symfony\Component\HttpFoundation\JsonResponse; - - function postIndexJson(PostRepository $repo) { - return function() use ($repo) { - return new JsonResponse($repo->findAll()); - }; - } - -And when defining your route, the code would look like the following: - -.. code-block:: php - - $app['posts.controller'] = function($app) { - return Demo\Controller\postIndexJson($app['posts.repository']); - }; - - $app->get('/posts.json', 'posts.controller'); diff --git a/doc/providers/session.rst b/doc/providers/session.rst deleted file mode 100644 index 301385d..0000000 --- a/doc/providers/session.rst +++ /dev/null @@ -1,103 +0,0 @@ -Session -======= - -The *SessionServiceProvider* provides a service for storing data persistently -between requests. - -Parameters ----------- - -* **session.storage.save_path** (optional): The path for the - ``NativeFileSessionHandler``, defaults to the value of - ``sys_get_temp_dir()``. - -* **session.storage.options**: An array of options that is passed to the - constructor of the ``session.storage`` service. - - In case of the default `NativeSessionStorage - `_, - 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, all of these are optional. Default Sessions life time is 1800 - seconds (30 minutes). To override this, set the ``lifetime`` option. - - For a full list of available options, read the `PHP - `_ official documentation. - -* **session.test**: Whether to simulate sessions or not (useful when writing - functional tests). - -Services --------- - -* **session**: An instance of Symfony's `Session - `_. - -* **session.storage**: A service that is used for persistence of the session - data. - -* **session.storage.handler**: A service that is used by the - ``session.storage`` for data access. Defaults to a `NativeFileSessionHandler - `_ - storage handler. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\SessionServiceProvider()); - -Usage ------ - -The Session provider provides a ``session`` service. Here is an example that -authenticates a user and creates a session for them:: - - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpFoundation\Response; - - $app->get('/login', function (Request $request) use ($app) { - $username = $request->server->get('PHP_AUTH_USER', false); - $password = $request->server->get('PHP_AUTH_PW'); - - if ('igor' === $username && 'password' === $password) { - $app['session']->set('user', array('username' => $username)); - return $app->redirect('/account'); - } - - $response = new Response(); - $response->headers->set('WWW-Authenticate', sprintf('Basic realm="%s"', 'site_login')); - $response->setStatusCode(401, 'Please sign in.'); - return $response; - }); - - $app->get('/account', function () use ($app) { - if (null === $user = $app['session']->get('user')) { - return $app->redirect('/login'); - } - - return "Welcome {$user['username']}!"; - }); - - -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.storage.handler`` to null. You will have to configure the -``session.save_path`` ini setting yourself in that case. - -.. code-block:: php - - $app['session.storage.handler'] = null; - diff --git a/doc/providers/swiftmailer.rst b/doc/providers/swiftmailer.rst deleted file mode 100644 index f3153f1..0000000 --- a/doc/providers/swiftmailer.rst +++ /dev/null @@ -1,146 +0,0 @@ -Swiftmailer -=========== - -The *SwiftmailerServiceProvider* provides a service for sending email through -the `Swift Mailer `_ library. - -You can use the ``mailer`` service to send messages easily. By default, it -will attempt to send emails through SMTP. - -Parameters ----------- - -* **swiftmailer.use_spool**: A boolean to specify whether or not to use the - memory spool, defaults to true. - -* **swiftmailer.options**: An array of options for the default SMTP-based - configuration. - - The following options can be set: - - * **host**: SMTP hostname, defaults to 'localhost'. - * **port**: SMTP port, defaults to 25. - * **username**: SMTP username, defaults to an empty string. - * **password**: SMTP password, defaults to an empty string. - * **encryption**: SMTP encryption, defaults to null. Valid values are 'tls', 'ssl', or null (indicating no encryption). - * **auth_mode**: SMTP authentication mode, defaults to null. Valid values are 'plain', 'login', 'cram-md5', or null. - - Example usage:: - - $app['swiftmailer.options'] = array( - 'host' => 'host', - 'port' => '25', - 'username' => 'username', - 'password' => 'password', - 'encryption' => null, - 'auth_mode' => null - ); - -* **swiftmailer.sender_address**: If set, all messages will be delivered with - this address as the "return path" address. - -* **swiftmailer.delivery_addresses**: If not empty, all email messages will be - sent to those addresses instead of being sent to their actual recipients. This - is often useful when developing. - -* **swiftmailer.delivery_whitelist**: Used in combination with - ``delivery_addresses``. If set, emails matching any of these patterns will be - delivered like normal, as well as being sent to ``delivery_addresses``. - -Services --------- - -* **mailer**: The mailer instance. - - Example usage:: - - $message = \Swift_Message::newInstance(); - - // ... - - $app['mailer']->send($message); - -* **swiftmailer.transport**: The transport used for e-mail - delivery. Defaults to a ``Swift_Transport_EsmtpTransport``. - -* **swiftmailer.transport.buffer**: StreamBuffer used by - the transport. - -* **swiftmailer.transport.authhandler**: Authentication - handler used by the transport. Will try the following - by default: CRAM-MD5, login, plaintext. - -* **swiftmailer.transport.eventdispatcher**: Internal event - dispatcher used by Swiftmailer. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\SwiftmailerServiceProvider()); - -.. note:: - - Add SwiftMailer as a dependency: - - .. code-block:: bash - - composer require swiftmailer/swiftmailer - -Usage ------ - -The Swiftmailer provider provides a ``mailer`` service:: - - use Symfony\Component\HttpFoundation\Request; - - $app->post('/feedback', function (Request $request) use ($app) { - $message = \Swift_Message::newInstance() - ->setSubject('[YourSite] Feedback') - ->setFrom(array('noreply@yoursite.com')) - ->setTo(array('feedback@yoursite.com')) - ->setBody($request->get('message')); - - $app['mailer']->send($message); - - return new Response('Thank you for your feedback!', 201); - }); - -Usage in commands -~~~~~~~~~~~~~~~~~ - -By default, the Swiftmailer provider sends the emails using the ``KernelEvents::TERMINATE`` -event, which is fired after the response has been sent. However, as this event -isn't fired for console commands, your emails won't be sent. - -For that reason, if you send emails using a command console, it is recommended -that you disable the use of the memory spool (before accessing ``$app['mailer']``):: - - $app['swiftmailer.use_spool'] = false; - -Alternatively, you can just make sure to flush the message spool by hand before -ending the command execution. To do so, use the following code:: - - $app['swiftmailer.spooltransport'] - ->getSpool() - ->flushQueue($app['swiftmailer.transport']) - ; - -Traits ------- - -``Silex\Application\SwiftmailerTrait`` adds the following shortcuts: - -* **mail**: Sends an email. - -.. code-block:: php - - $app->mail(\Swift_Message::newInstance() - ->setSubject('[YourSite] Feedback') - ->setFrom(array('noreply@yoursite.com')) - ->setTo(array('feedback@yoursite.com')) - ->setBody($request->get('message'))); - -For more information, check out the `Swift Mailer documentation -`_. diff --git a/doc/providers/translation.rst b/doc/providers/translation.rst deleted file mode 100644 index 145fc18..0000000 --- a/doc/providers/translation.rst +++ /dev/null @@ -1,193 +0,0 @@ -Translation -=========== - -The *TranslationServiceProvider* provides a service for translating your -application into different languages. - -Parameters ----------- - -* **translator.domains** (optional): A mapping of domains/locales/messages. - This parameter contains the translation data for all languages and domains. - -* **locale** (optional): The locale for the translator. You will most likely - want to set this based on some request parameter. Defaults to ``en``. - -* **locale_fallbacks** (optional): Fallback locales for the translator. It will - be used when the current locale has no messages set. Defaults to ``en``. - -Services --------- - -* **translator**: An instance of `Translator - `_, - that is used for translation. - -* **translator.loader**: An instance of an implementation of the translation - `LoaderInterface - `_, - defaults to an `ArrayLoader - `_. - -* **translator.message_selector**: An instance of `MessageSelector - `_. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\LocaleServiceProvider()); - $app->register(new Silex\Provider\TranslationServiceProvider(), array( - 'locale_fallbacks' => array('en'), - )); - -.. note:: - - Add the Symfony Translation Component as a dependency: - - .. code-block:: bash - - composer require symfony/translation - -Usage ------ - -The Translation provider provides a ``translator`` service and makes use of -the ``translator.domains`` parameter:: - - $app['translator.domains'] = array( - 'messages' => array( - 'en' => array( - 'hello' => 'Hello %name%', - 'goodbye' => 'Goodbye %name%', - ), - 'de' => array( - 'hello' => 'Hallo %name%', - 'goodbye' => 'Tschüss %name%', - ), - 'fr' => array( - 'hello' => 'Bonjour %name%', - 'goodbye' => 'Au revoir %name%', - ), - ), - 'validators' => array( - 'fr' => array( - 'This value should be a valid number.' => 'Cette valeur doit être un nombre.', - ), - ), - ); - - $app->get('/{_locale}/{message}/{name}', function ($message, $name) use ($app) { - return $app['translator']->trans($message, array('%name%' => $name)); - }); - -The above example will result in following routes: - -* ``/en/hello/igor`` will return ``Hello igor``. - -* ``/de/hello/igor`` will return ``Hallo igor``. - -* ``/fr/hello/igor`` will return ``Bonjour igor``. - -* ``/it/hello/igor`` will return ``Hello igor`` (because of the fallback). - -Using Resources ---------------- - -When translations are stored in a file, you can load them as follows:: - - $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. - -.. code-block:: php - - $app->trans('Hello World'); - - $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: - -.. code-block:: bash - - 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: - -.. code-block:: yaml - - hello: Hello %name% - goodbye: Goodbye %name% - -Then, register the ``YamlFileLoader`` on the ``translator`` and add all your -translation files:: - - use Symfony\Component\Translation\Loader\YamlFileLoader; - - $app->extend('translator', function($translator, $app) { - $translator->addLoader('yaml', new YamlFileLoader()); - - $translator->addResource('yaml', __DIR__.'/locales/en.yml', 'en'); - $translator->addResource('yaml', __DIR__.'/locales/de.yml', 'de'); - $translator->addResource('yaml', __DIR__.'/locales/fr.yml', 'fr'); - - return $translator; - }); - -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:: - - $translator->addResource('xliff', __DIR__.'/locales/en.xlf', 'en'); - $translator->addResource('xliff', __DIR__.'/locales/de.xlf', 'de'); - $translator->addResource('xliff', __DIR__.'/locales/fr.xlf', 'fr'); - -.. note:: - - 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 when using the Twig bridge provided by Symfony (see -:doc:`TwigServiceProvider
`): - -.. code-block:: jinja - - {{ 'translation_key'|trans }} - {{ 'translation_key'|transchoice }} - {% trans %}translation_key{% endtrans %} diff --git a/doc/providers/twig.rst b/doc/providers/twig.rst deleted file mode 100644 index dd8a85b..0000000 --- a/doc/providers/twig.rst +++ /dev/null @@ -1,200 +0,0 @@ -Twig -==== - -The *TwigServiceProvider* provides integration with the `Twig -`_ 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 documentation `_ - for more information. - -* **twig.form.templates** (optional): An array of templates used to render - forms (only available when the ``FormServiceProvider`` is enabled). The - default theme is ``form_div_layout.html.twig``, but you can use the other - built-in themes: ``form_table_layout.html.twig``, - ``bootstrap_3_layout.html.twig``, and - ``bootstrap_3_horizontal_layout.html.twig``. - -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 ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\TwigServiceProvider(), array( - 'twig.path' => __DIR__.'/views', - )); - -.. note:: - - Add Twig as a dependency: - - .. code-block:: bash - - composer require twig/twig - -Usage ------ - -The Twig provider provides a ``twig`` service that can render templates:: - - $app->get('/hello/{name}', function ($name) use ($app) { - return $app['twig']->render('hello.twig', array( - 'name' => $name, - )); - }); - -Symfony Components Integration ------------------------------- - -Symfony provides a Twig bridge that provides additional integration between -some Symfony components and Twig. Add it as a dependency: - -.. code-block:: bash - - composer require symfony/twig-bridge - -When present, the ``TwigServiceProvider`` will provide you with the following -additional capabilities. - -* Access to the ``path()`` and ``url()`` functions. You can find more - information in the `Symfony Routing documentation - `_: - - .. code-block:: jinja - - {{ path('homepage') }} - {{ url('homepage') }} {# generates the absolute url http://example.org/ #} - {{ path('hello', {name: 'Fabien'}) }} - {{ url('hello', {name: 'Fabien'}) }} {# generates the absolute url http://example.org/hello/Fabien #} - -* Access to the ``absolute_url()`` and ``relative_path()`` Twig functions. - -Translations Support -~~~~~~~~~~~~~~~~~~~~ - -If you are using the ``TranslationServiceProvider``, you will get the -``trans()`` and ``transchoice()`` functions for translation in Twig templates. -You can find more information in the `Symfony Translation documentation -`_. - -Form Support -~~~~~~~~~~~~ - -If you are using the ``FormServiceProvider``, you will get a set of helpers for -working with forms in templates. You can find more information in the `Symfony -Forms reference -`_. - -Security Support -~~~~~~~~~~~~~~~~ - -If you are using the ``SecurityServiceProvider``, you will have access to the -``is_granted()`` function in templates. You can find more information in the -`Symfony Security documentation -`_. - -Global Variable -~~~~~~~~~~~~~~~ - -When the Twig bridge is available, the ``global`` variable refers to an -instance of `AppVariable `_. -It gives access to the following methods: - -.. code-block:: jinja - - {# The current Request #} - {{ global.request }} - - {# The current User (when security is enabled) #} - {{ global.user }} - - {# The current Session #} - {{ global.session }} - - {# The debug flag #} - {{ global.debug }} - -Rendering a Controller -~~~~~~~~~~~~~~~~~~~~~~ - -A ``render`` function is also registered to help you render another controller -from a template (available when the :doc:`HttpFragment Service Provider -
` is registered): - -.. code-block:: jinja - - {{ render(url('sidebar')) }} - - {# or you can reference a controller directly without defining a route for it #} - {{ render(controller(controller)) }} - -.. note:: - - You must prepend the ``app.request.baseUrl`` to render calls to ensure - that the render works when deployed into a sub-directory of the docroot. - -.. note:: - - Read the Twig `reference`_ for Symfony document to learn more about the - various Twig functions. - -Traits ------- - -``Silex\Application\TwigTrait`` adds the following shortcuts: - -* **render**: Renders a view with the given parameters and returns a Response - object. - -.. code-block:: php - - return $app->render('index.html', ['name' => 'Fabien']); - - $response = new Response(); - $response->setTtl(10); - - return $app->render('index.html', ['name' => 'Fabien'], $response); - -.. code-block:: php - - // stream a view - use Symfony\Component\HttpFoundation\StreamedResponse; - - return $app->render('index.html', ['name' => 'Fabien'], new StreamedResponse()); - -Customization -------------- - -You can configure the Twig environment before using it by extending the -``twig`` service:: - - $app->extend('twig', function($twig, $app) { - $twig->addGlobal('pi', 3.14); - $twig->addFilter('levenshtein', new \Twig_Filter_Function('levenshtein')); - - return $twig; - }); - -For more information, check out the `official Twig documentation -`_. - -.. _reference: https://symfony.com/doc/current/reference/twig_reference.html#controller diff --git a/doc/providers/validator.rst b/doc/providers/validator.rst deleted file mode 100644 index bd4e998..0000000 --- a/doc/providers/validator.rst +++ /dev/null @@ -1,217 +0,0 @@ -Validator -========= - -The *ValidatorServiceProvider* provides a service for validating data. It is -most useful when used with the *FormServiceProvider*, but can also be used -standalone. - -Parameters ----------- - -* **validator.validator_service_ids**: An array of service names representing - validators. - -Services --------- - -* **validator**: An instance of `Validator - `_. - -* **validator.mapping.class_metadata_factory**: Factory for metadata loaders, - which can read validation constraint information from classes. Defaults to - StaticMethodLoader--ClassMetadataFactory. - - This means you can define a static ``loadValidatorMetadata`` method on your - data class, which takes a ClassMetadata argument. Then you can set - constraints on this ClassMetadata instance. - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\ValidatorServiceProvider()); - -.. note:: - - Add the Symfony Validator Component as a dependency: - - .. code-block:: bash - - composer require symfony/validator - -Usage ------ - -The Validator provider provides a ``validator`` service. - -Validating Values -~~~~~~~~~~~~~~~~~ - -You can validate values directly using the ``validate`` validator -method:: - - use Symfony\Component\Validator\Constraints as Assert; - - $app->get('/validate/{email}', function ($email) use ($app) { - $errors = $app['validator']->validate($email, new Assert\Email()); - - if (count($errors) > 0) { - return (string) $errors; - } else { - return 'The email is valid'; - } - }); - -Validating Associative Arrays -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Validating associative arrays is like validating simple values, with a -collection of constraints:: - - use Symfony\Component\Validator\Constraints as Assert; - - $book = array( - 'title' => 'My Book', - 'author' => array( - 'first_name' => 'Fabien', - 'last_name' => 'Potencier', - ), - ); - - $constraint = new Assert\Collection(array( - 'title' => new Assert\Length(array('min' => 10)), - 'author' => new Assert\Collection(array( - 'first_name' => array(new Assert\NotBlank(), new Assert\Length(array('min' => 10))), - 'last_name' => new Assert\Length(array('min' => 10)), - )), - )); - $errors = $app['validator']->validate($book, $constraint); - - if (count($errors) > 0) { - foreach ($errors as $error) { - echo $error->getPropertyPath().' '.$error->getMessage()."\n"; - } - } else { - echo 'The book is valid'; - } - -Validating Objects -~~~~~~~~~~~~~~~~~~ - -If you want to add validations to a class, you can define the constraint for -the class properties and getters, and then call the ``validate`` method:: - - use Symfony\Component\Validator\Constraints as Assert; - - class Book - { - public $title; - public $author; - } - - class Author - { - public $first_name; - public $last_name; - } - - $author = new Author(); - $author->first_name = 'Fabien'; - $author->last_name = 'Potencier'; - - $book = new Book(); - $book->title = 'My Book'; - $book->author = $author; - - $metadata = $app['validator.mapping.class_metadata_factory']->getMetadataFor('Author'); - $metadata->addPropertyConstraint('first_name', new Assert\NotBlank()); - $metadata->addPropertyConstraint('first_name', new Assert\Length(array('min' => 10))); - $metadata->addPropertyConstraint('last_name', new Assert\Length(array('min' => 10))); - - $metadata = $app['validator.mapping.class_metadata_factory']->getMetadataFor('Book'); - $metadata->addPropertyConstraint('title', new Assert\Length(array('min' => 10))); - $metadata->addPropertyConstraint('author', new Assert\Valid()); - - $errors = $app['validator']->validate($book); - - if (count($errors) > 0) { - foreach ($errors as $error) { - echo $error->getPropertyPath().' '.$error->getMessage()."\n"; - } - } else { - echo 'The author is valid'; - } - -You can also declare the class constraint by adding a static -``loadValidatorMetadata`` method to your classes:: - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Book - { - public $title; - public $author; - - static public function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('title', new Assert\Length(array('min' => 10))); - $metadata->addPropertyConstraint('author', new Assert\Valid()); - } - } - - class Author - { - public $first_name; - public $last_name; - - static public function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('first_name', new Assert\NotBlank()); - $metadata->addPropertyConstraint('first_name', new Assert\Length(array('min' => 10))); - $metadata->addPropertyConstraint('last_name', new Assert\Length(array('min' => 10))); - } - } - - $app->get('/validate/{email}', function ($email) use ($app) { - $author = new Author(); - $author->first_name = 'Fabien'; - $author->last_name = 'Potencier'; - - $book = new Book(); - $book->title = 'My Book'; - $book->author = $author; - - $errors = $app['validator']->validate($book); - - if (count($errors) > 0) { - foreach ($errors as $error) { - echo $error->getPropertyPath().' '.$error->getMessage()."\n"; - } - } else { - echo 'The author is valid'; - } - }); - -.. note:: - - Use ``addGetterConstraint()`` to add constraints on getter methods and - ``addConstraint()`` to add constraints on the class itself. - -Translation -~~~~~~~~~~~ - -To be able to translate the error messages, you can use the translator -provider and register the messages under the ``validators`` domain:: - - $app['translator.domains'] = array( - 'validators' => array( - 'fr' => array( - 'This value should be a valid number.' => 'Cette valeur doit être un nombre.', - ), - ), - ); - -For more information, consult the `Symfony Validation documentation -`_. diff --git a/doc/providers/var_dumper.rst b/doc/providers/var_dumper.rst deleted file mode 100644 index ea4dd19..0000000 --- a/doc/providers/var_dumper.rst +++ /dev/null @@ -1,44 +0,0 @@ -Var Dumper -========== - -The *VarDumperServiceProvider* provides a mechanism that allows exploring then -dumping any PHP variable. - -Parameters ----------- - -* **var_dumper.dump_destination**: A stream URL where dumps should be written - to (defaults to ``null``). - -Services --------- - -* n/a - -Registering ------------ - -.. code-block:: php - - $app->register(new Silex\Provider\VarDumperServiceProvider()); - -.. note:: - - Add the Symfony VarDumper Component as a dependency: - - .. code-block:: bash - - composer require symfony/var-dumper - -Usage ------ - -Adding the VarDumper component as a Composer dependency gives you access to the -``dump()`` PHP function anywhere in your code. - -If you are using Twig, it also provides a ``dump()`` Twig function and a -``dump`` Twig tag. - -The VarDumperServiceProvider is also useful when used with the Silex -WebProfiler as the dumps are made available in the web debug toolbar and in the -web profiler. diff --git a/doc/services.rst b/doc/services.rst deleted file mode 100644 index fd35ec1..0000000 --- a/doc/services.rst +++ /dev/null @@ -1,264 +0,0 @@ -Services -======== - -Silex is not only a framework, it is also a service container. It does this by -extending `Pimple `_ which provides a very simple -service container. - -Dependency Injection --------------------- - -.. note:: - - You can skip this if you already know what Dependency Injection is. - -Dependency Injection is a design pattern where you pass dependencies to -services instead of creating them from within the service or relying on -globals. This generally leads to code that is decoupled, re-usable, flexible -and testable. - -Here is an example of a class that takes a ``User`` object and stores it as a -file in JSON format:: - - class JsonUserPersister - { - private $basePath; - - public function __construct($basePath) - { - $this->basePath = $basePath; - } - - public function persist(User $user) - { - $data = $user->getAttributes(); - $json = json_encode($data); - $filename = $this->basePath.'/'.$user->id.'.json'; - file_put_contents($filename, $json, LOCK_EX); - } - } - -In this simple example the dependency is the ``basePath`` property. It is -passed to the constructor. This means you can create several independent -instances with different base paths. Of course dependencies do not have to be -simple strings. More often they are in fact other services. - -A service container is responsible for creating and storing services. It can -recursively create dependencies of the requested services and inject them. It -does so lazily, which means a service is only created when you actually need it. - -Pimple ------- - -Pimple makes strong use of closures and implements the ArrayAccess interface. - -We will start off by creating a new instance of Pimple -- and because -``Silex\Application`` extends ``Pimple\Container`` all of this applies to Silex -as well:: - - $container = new Pimple\Container(); - -or:: - - $app = new Silex\Application(); - -Parameters -~~~~~~~~~~ - -You can set parameters (which are usually strings) by setting an array key on -the container:: - - $app['some_parameter'] = 'value'; - -The array key can be any value. By convention dots are used for namespacing:: - - $app['asset.host'] = 'http://cdn.mysite.com/'; - -Reading parameter values is possible with the same syntax:: - - echo $app['some_parameter']; - -Service definitions -~~~~~~~~~~~~~~~~~~~ - -Defining services is no different than defining parameters. You just set an -array key on the container to be a closure. However, when you retrieve the -service, the closure is executed. This allows for lazy service creation:: - - $app['some_service'] = function () { - return new Service(); - }; - -And to retrieve the service, use:: - - $service = $app['some_service']; - -On first invocation, this will create the service; the same instance will then -be returned on any subsequent access. - -Factory services -~~~~~~~~~~~~~~~~ - -If you want a different instance to be returned for each service access, wrap -the service definition with the ``factory()`` method:: - - $app['some_service'] = $app->factory(function () { - return new Service(); - }); - -Every time you call ``$app['some_service']``, a new instance of the service is -created. - -Access container from closure -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In many cases you will want to access the service container from within a -service definition closure. For example when fetching services the current -service depends on. - -Because of this, the container is passed to the closure as an argument:: - - $app['some_service'] = function ($app) { - return new Service($app['some_other_service'], $app['some_service.config']); - }; - -Here you can see an example of Dependency Injection. ``some_service`` depends -on ``some_other_service`` and takes ``some_service.config`` as configuration -options. The dependency is only created when ``some_service`` is accessed, and -it is possible to replace either of the dependencies by simply overriding -those definitions. - -Going back to our initial example, here's how we could use the container -to manage its dependencies:: - - $app['user.persist_path'] = '/tmp/users'; - $app['user.persister'] = function ($app) { - return new JsonUserPersister($app['user.persist_path']); - }; - - -Protected closures -~~~~~~~~~~~~~~~~~~ - -Because the container sees closures as factories for services, it will always -execute them when reading them. - -In some cases you will however want to store a closure as a parameter, so that -you can fetch it and execute it yourself -- with your own arguments. - -This is why Pimple allows you to protect your closures from being executed, by -using the ``protect`` method:: - - $app['closure_parameter'] = $app->protect(function ($a, $b) { - return $a + $b; - }); - - // will not execute the closure - $add = $app['closure_parameter']; - - // calling it now - echo $add(2, 3); - -Note that protected closures do not get access to the container. - -Core services -------------- - -Silex defines a range of services. - -* **request_stack**: Controls the lifecycle of requests, an instance of - `RequestStack ` _. - It gives you access to ``GET``, ``POST`` parameters and lots more! - - Example usage:: - - $id = $app['request_stack']->getCurrentRequest()->get('id'); - - A request is only available when a request is being served; you can only - access it from within a controller, an application before/after middlewares, - or an error handler. - -* **routes**: The `RouteCollection - `_ - that is used internally. You can add, modify, read routes. - -* **url_generator**: An instance of `UrlGenerator - `_, - using the `RouteCollection - `_ - that is provided through the ``routes`` service. It has a ``generate`` - method, which takes the route name as an argument, followed by an array of - route parameters. - -* **controllers**: The ``Silex\ControllerCollection`` that is used internally. - Check the :doc:`Internals chapter ` for more information. - -* **dispatcher**: The `EventDispatcher - `_ - that is used internally. It is the core of the Symfony system and is used - quite a bit by Silex. - -* **resolver**: The `ControllerResolver - `_ - that is used internally. It takes care of executing the controller with the - right arguments. - -* **kernel**: The `HttpKernel - `_ - that is used internally. The HttpKernel is the heart of Symfony, it takes a - Request as input and returns a Response as output. - -* **request_context**: The request context is a simplified representation of - the request that is used by the router and the URL generator. - -* **exception_handler**: The Exception handler is the default handler that is - used when you don't register one via the ``error()`` method or if your - handler does not return a Response. Disable it with - ``unset($app['exception_handler'])``. - -* **logger**: A `LoggerInterface `_ instance. By default, logging is - disabled as the value is set to ``null``. To enable logging you can either use - the :doc:`MonologServiceProvider ` or define your own ``logger`` service that - conforms to the PSR logger interface. - -Core traits ------------ - -* ``Silex\Application\UrlGeneratorTrait`` adds the following shortcuts: - - * **path**: Generates a path. - - * **url**: Generates an absolute URL. - - .. code-block:: php - - $app->path('homepage'); - $app->url('homepage'); - -Core parameters ---------------- - -* **request.http_port** (optional): Allows you to override the default port - for non-HTTPS URLs. If the current request is HTTP, it will always use the - current port. - - Defaults to 80. - - This parameter can be used when generating URLs. - -* **request.https_port** (optional): Allows you to override the default port - for HTTPS URLs. If the current request is HTTPS, it will always use the - current port. - - Defaults to 443. - - This parameter can be used when generating URLs. - -* **debug** (optional): Returns whether or not the application is running in - debug mode. - - Defaults to false. - -* **charset** (optional): The charset to use for Responses. - - Defaults to UTF-8. diff --git a/doc/testing.rst b/doc/testing.rst deleted file mode 100644 index 41054c9..0000000 --- a/doc/testing.rst +++ /dev/null @@ -1,221 +0,0 @@ -Testing -======= - -Because Silex is built on top of Symfony, it is very easy to write functional -tests for your application. Functional tests are automated software tests that -ensure that your code is working correctly. They go through the user interface, -using a fake browser, and mimic the actions a user would do. - -Why ---- - -If you are not familiar with software tests, you may be wondering why you would -need this. Every time you make a change to your application, you have to test -it. This means going through all the pages and making sure they are still -working. Functional tests save you a lot of time, because they enable you to -test your application in usually under a second by running a single command. - -For more information on functional testing, unit testing, and automated -software tests in general, check out `PHPUnit -`_ and `Bulat Shakirzyanov's talk -on Clean Code `_. - -PHPUnit -------- - -`PHPUnit `_ is the de-facto -standard testing framework for PHP. It was built for writing unit tests, but it -can be used for functional tests too. You write tests by creating a new class, -that extends the ``PHPUnit_Framework_TestCase``. Your test cases are methods -prefixed with ``test``:: - - class ContactFormTest extends \PHPUnit_Framework_TestCase - { - public function testInitialPage() - { - ... - } - } - -In your test cases, you do assertions on the state of what you are testing. In -this case we are testing a contact form, so we would want to assert that the -page loaded correctly and contains our form:: - - public function testInitialPage() - { - $statusCode = ... - $pageContent = ... - - $this->assertEquals(200, $statusCode); - $this->assertContains('Contact us', $pageContent); - $this->assertContains('`_ -section of the PHPUnit documentation. - -WebTestCase ------------ - -Symfony provides a WebTestCase class that can be used to write functional -tests. The Silex version of this class is ``Silex\WebTestCase``, and you can -use it by making your test extend it:: - - use Silex\WebTestCase; - - class ContactFormTest extends WebTestCase - { - ... - } - -.. caution:: - - If you need to override the ``setUp()`` method, don't forget to call the - parent (``parent::setUp()``) to call the Silex default setup. - -.. note:: - - If you want to use the Symfony ``WebTestCase`` class you will need to - explicitly install its dependencies for your project: - - .. code-block:: bash - - composer require --dev symfony/browser-kit symfony/css-selector - -For your WebTestCase, you will have to implement a ``createApplication`` -method, which returns your application instance:: - - public function createApplication() - { - // app.php must return an Application instance - return require __DIR__.'/path/to/app.php'; - } - -Make sure you do **not** use ``require_once`` here, as this method will be -executed before every test. - -.. tip:: - - By default, the application behaves in the same way as when using it from a - browser. But when an error occurs, it is sometimes easier to get raw - exceptions instead of HTML pages. It is rather simple if you tweak the - application configuration in the ``createApplication()`` method like - follows:: - - public function createApplication() - { - $app = require __DIR__.'/path/to/app.php'; - $app['debug'] = true; - unset($app['exception_handler']); - - return $app; - } - -.. tip:: - - If your application use sessions, set ``session.test`` to ``true`` to - simulate sessions:: - - public function createApplication() - { - // ... - - $app['session.test'] = true; - - // ... - } - -The WebTestCase provides a ``createClient`` method. A client acts as a browser, -and allows you to interact with your application. Here's how it works:: - - public function testInitialPage() - { - $client = $this->createClient(); - $crawler = $client->request('GET', '/'); - - $this->assertTrue($client->getResponse()->isOk()); - $this->assertCount(1, $crawler->filter('h1:contains("Contact us")')); - $this->assertCount(1, $crawler->filter('form')); - ... - } - -There are several things going on here. You have both a ``Client`` and a -``Crawler``. - -You can also access the application through ``$this->app``. - -Client -~~~~~~ - -The client represents a browser. It holds your browsing history, cookies and -more. The ``request`` method allows you to make a request to a page on your -application. - -.. note:: - - You can find some documentation for it in `the client section of the - testing chapter of the Symfony documentation - `_. - -Crawler -~~~~~~~ - -The crawler allows you to inspect the content of a page. You can filter it -using CSS expressions and lots more. - -.. note:: - - You can find some documentation for it in `the crawler section of the testing - chapter of the Symfony documentation - `_. - -Configuration -------------- - -The suggested way to configure PHPUnit is to create a ``phpunit.xml.dist`` -file, a ``tests`` folder and your tests in -``tests/YourApp/Tests/YourTest.php``. The ``phpunit.xml.dist`` file should -look like this: - -.. code-block:: xml - - - - - - ./tests/ - - - - -Your ``tests/YourApp/Tests/YourTest.php`` should look like this:: - - namespace YourApp\Tests; - - use Silex\WebTestCase; - - class YourTest extends WebTestCase - { - public function createApplication() - { - return require __DIR__.'/../../../app.php'; - } - - public function testFooBar() - { - ... - } - } - -Now, when running ``phpunit`` on the command line, tests should run. diff --git a/doc/usage.rst b/doc/usage.rst deleted file mode 100644 index e6a3635..0000000 --- a/doc/usage.rst +++ /dev/null @@ -1,805 +0,0 @@ -Usage -===== - -Installation ------------- - -If you want to get started fast, `download`_ Silex as an archive and extract -it, you should have the following directory structure: - -.. code-block:: text - - ├── composer.json - ├── composer.lock - ├── vendor - │ └── ... - └── web - └── index.php - -If you want more flexibility, use Composer_ instead: - -.. code-block:: bash - - composer require silex/silex:~2.0 - -Web Server ----------- - -All examples in the documentation rely on a well-configured web server; read -the :doc:`webserver documentation` to check yours. - -Bootstrap ---------- - -To bootstrap Silex, all you need to do is require the ``vendor/autoload.php`` -file and create an instance of ``Silex\Application``. After your controller -definitions, call the ``run`` method on your application:: - - // web/index.php - require_once __DIR__.'/../vendor/autoload.php'; - - $app = new Silex\Application(); - - // ... definitions - - $app->run(); - -.. tip:: - - When developing a website, you might want to turn on the debug mode to - ease debugging:: - - $app['debug'] = true; - -.. tip:: - - If your application is hosted behind a reverse proxy at address ``$ip``, - and you want Silex to trust the ``X-Forwarded-For*`` headers, you will - need to run your application like this:: - - use Symfony\Component\HttpFoundation\Request; - - Request::setTrustedProxies(array($ip)); - $app->run(); - -Routing -------- - -In Silex you define a route and the controller that is called when that -route is matched. A route pattern consists of: - -* *Pattern*: The route pattern defines a path that points to a resource. The - pattern can include variable parts and you are able to set RegExp - requirements for them. - -* *Method*: One of the following HTTP methods: ``GET``, ``POST``, ``PUT``, - ``DELETE``, ``PATCH``, or ``OPTIONS``. This describes the interaction with - the resource. - -The controller is defined using a closure like this:: - - function () { - // ... do something - } - -The return value of the closure becomes the content of the page. - -Example GET Route -~~~~~~~~~~~~~~~~~ - -Here is an example definition of a ``GET`` route:: - - $blogPosts = array( - 1 => array( - 'date' => '2011-03-29', - 'author' => 'igorw', - 'title' => 'Using Silex', - 'body' => '...', - ), - ); - - $app->get('/blog', function () use ($blogPosts) { - $output = ''; - foreach ($blogPosts as $post) { - $output .= $post['title']; - $output .= '
'; - } - - return $output; - }); - -Visiting ``/blog`` will return a list of blog post titles. The ``use`` -statement means something different in this context. It tells the closure to -import the ``$blogPosts`` variable from the outer scope. This allows you to use -it from within the closure. - -Dynamic Routing -~~~~~~~~~~~~~~~ - -Now, you can create another controller for viewing individual blog posts:: - - $app->get('/blog/{id}', function (Silex\Application $app, $id) use ($blogPosts) { - if (!isset($blogPosts[$id])) { - $app->abort(404, "Post $id does not exist."); - } - - $post = $blogPosts[$id]; - - return "

{$post['title']}

". - "

{$post['body']}

"; - }); - -This route definition has a variable ``{id}`` part which is passed to the -closure. - -The current ``Application`` is automatically injected by Silex to the Closure -thanks to the type hinting. - -When the post does not exist, you are using ``abort()`` to stop the request -early. It actually throws an exception, which you will see how to handle later -on. - -Example POST Route -~~~~~~~~~~~~~~~~~~ - -POST routes signify the creation of a resource. An example for this is a -feedback form. You will use the ``mail`` function to send an e-mail:: - - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpFoundation\Response; - - $app->post('/feedback', function (Request $request) { - $message = $request->get('message'); - mail('feedback@yoursite.com', '[YourSite] Feedback', $message); - - return new Response('Thank you for your feedback!', 201); - }); - -It is pretty straightforward. - -.. note:: - - There is a :doc:`SwiftmailerServiceProvider ` - included that you can use instead of ``mail()``. - -The current ``request`` is automatically injected by Silex to the Closure -thanks to the type hinting. It is an instance of -Request_, so you can fetch variables using the request ``get`` method. - -Instead of returning a string you are returning an instance of Response_. -This allows setting an HTTP status code, in this case it is set to -``201 Created``. - -.. note:: - - Silex always uses a ``Response`` internally, it converts strings to - responses with status code ``200``. - -Other methods -~~~~~~~~~~~~~ - -You can create controllers for most HTTP methods. Just call one of these -methods on your application: ``get``, ``post``, ``put``, ``delete``, ``patch``, ``options``:: - - $app->put('/blog/{id}', function ($id) { - // ... - }); - - $app->delete('/blog/{id}', function ($id) { - // ... - }); - - $app->patch('/blog/{id}', function ($id) { - // ... - }); - -.. tip:: - - Forms in most web browsers do not directly support the use of other HTTP - methods. To use methods other than GET and POST you can utilize a special - form field with a name of ``_method``. The form's ``method`` attribute must - be set to POST when using this field: - - .. code-block:: html - -
- - -
- - You need to explicitly enable this method override:: - - use Symfony\Component\HttpFoundation\Request; - - Request::enableHttpMethodParameterOverride(); - $app->run(); - -You can also call ``match``, which will match all methods. This can be -restricted via the ``method`` method:: - - $app->match('/blog', function () { - // ... - }); - - $app->match('/blog', function () { - // ... - }) - ->method('PATCH'); - - $app->match('/blog', function () { - // ... - }) - ->method('PUT|POST'); - -.. note:: - - The order in which the routes are defined is significant. The first - matching route will be used, so place more generic routes at the bottom. - -Route Variables -~~~~~~~~~~~~~~~ - -As it has been shown before you can define variable parts in a route like -this:: - - $app->get('/blog/{id}', function ($id) { - // ... - }); - -It is also possible to have more than one variable part, just make sure the -closure arguments match the names of the variable parts:: - - $app->get('/blog/{postId}/{commentId}', function ($postId, $commentId) { - // ... - }); - -While it's not recommended, you could also do this (note the switched -arguments):: - - $app->get('/blog/{postId}/{commentId}', function ($commentId, $postId) { - // ... - }); - -You can also ask for the current Request and Application objects:: - - $app->get('/blog/{id}', function (Application $app, Request $request, $id) { - // ... - }); - -.. note:: - - Note for the Application and Request objects, Silex does the injection - based on the type hinting and not on the variable name:: - - $app->get('/blog/{id}', function (Application $foo, Request $bar, $id) { - // ... - }); - -Route Variable Converters -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Before injecting the route variables into the controller, you can apply some -converters:: - - $app->get('/user/{id}', function ($id) { - // ... - })->convert('id', function ($id) { return (int) $id; }); - -This is useful when you want to convert route variables to objects as it -allows to reuse the conversion code across different controllers:: - - $userProvider = function ($id) { - return new User($id); - }; - - $app->get('/user/{user}', function (User $user) { - // ... - })->convert('user', $userProvider); - - $app->get('/user/{user}/edit', function (User $user) { - // ... - })->convert('user', $userProvider); - -The converter callback also receives the ``Request`` as its second argument:: - - $callback = function ($post, Request $request) { - return new Post($request->attributes->get('slug')); - }; - - $app->get('/blog/{id}/{slug}', function (Post $post) { - // ... - })->convert('post', $callback); - -A converter can also be defined as a service. For example, here is a user -converter based on Doctrine ObjectManager:: - - use Doctrine\Common\Persistence\ObjectManager; - use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - - class UserConverter - { - private $om; - - public function __construct(ObjectManager $om) - { - $this->om = $om; - } - - 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``):: - - $app['converter.user'] = 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:: - - $app->get('/blog/{id}', function ($id) { - // ... - }) - ->assert('id', '\d+'); - -You can also chain these calls:: - - $app->get('/blog/{postId}/{commentId}', function ($postId, $commentId) { - // ... - }) - ->assert('postId', '\d+') - ->assert('commentId', '\d+'); - -Conditions -~~~~~~~~~~ - -Besides restricting route matching based on the HTTP method or parameter -requirements, you can set conditions on any part of the request by calling -``when`` on the ``Controller`` object, which is returned by the routing -methods:: - - $app->get('/blog/{id}', function ($id) { - // ... - }) - ->when("request.headers.get('User-Agent') matches '/firefox/i'"); - -The ``when`` argument is a Symfony Expression_ , which means that you need to -add ``symfony/expression-language`` as a dependency of your project. - -Default Values -~~~~~~~~~~~~~~ - -You can define a default value for any route variable by calling ``value`` on -the ``Controller`` object:: - - $app->get('/{pageName}', function ($pageName) { - // ... - }) - ->value('pageName', 'index'); - -This will allow matching ``/``, in which case the ``pageName`` variable will -have the value ``index``. - -Named Routes -~~~~~~~~~~~~ - -Some providers can make use of named routes. By default Silex will generate an -internal route name for you but you can give an explicit route name by calling -``bind``:: - - $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:: - - $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 :doc:`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:: - - $app['controllers'] - ->value('id', '1') - ->assert('id', '\d+') - ->requireHttps() - ->method('get') - ->convert('id', function () { /* ... */ }) - ->before(function () { /* ... */ }) - ->when('request.isSecure() == true') - ; - -These settings are applied to already registered controllers and they become -the defaults for new controllers. - -.. note:: - - The global configuration does not apply to controller providers you might - mount as they have their own global configuration (read the - :doc:`dedicated chapter` for more information). - -Error Handlers --------------- - -When an exception is thrown, error handlers allow you to display a custom -error page to the user. They can also be used to do additional things, such as -logging. - -To register an error handler, pass a closure to the ``error`` method which -takes an ``Exception`` argument and returns a response:: - - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\HttpFoundation\Request; - - $app->error(function (\Exception $e, Request $request, $code) { - return new Response('We are sorry, but something went terribly wrong.'); - }); - -You can also check for specific errors by using the ``$code`` argument, and -handle them differently:: - - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\HttpFoundation\Request; - - $app->error(function (\Exception $e, Request $request, $code) { - switch ($code) { - case 404: - $message = 'The requested page could not be found.'; - break; - default: - $message = 'We are sorry, but something went terribly wrong.'; - } - - return new Response($message); - }); - -You can restrict an error handler to only handle some Exception classes by -setting a more specific type hint for the Closure argument:: - - use Symfony\Component\HttpFoundation\Request; - - $app->error(function (\LogicException $e, Request $request, $code) { - // this handler will only handle \LogicException exceptions - // and exceptions that extend \LogicException - }); - -.. note:: - - 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:: - - return new Response('Error', 404 /* ignored */, array('X-Status-Code' => 200)); - -If you want to use a separate error handler for logging, make sure you register -it with a higher priority than response error handlers, because once a response -is returned, the following handlers are ignored. - -.. note:: - - Silex ships with a provider for Monolog_ which handles logging of errors. - Check out the *Providers* :doc:`chapter ` for details. - -.. tip:: - - Silex comes with a default error handler that displays a detailed error - message with the stack trace when **debug** is true, and a simple error - message otherwise. Error handlers registered via the ``error()`` method - always take precedence but you can keep the nice error messages when debug - is turned on like this:: - - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\HttpFoundation\Request; - - $app->error(function (\Exception $e, Request $request, $code) use ($app) { - if ($app['debug']) { - return; - } - - // ... logic to handle the error and return a Response - }); - -The error handlers are also called when you use ``abort`` to abort a request -early:: - - $app->get('/blog/{id}', function (Silex\Application $app, $id) use ($blogPosts) { - if (!isset($blogPosts[$id])) { - $app->abort(404, "Post $id does not exist."); - } - - return new Response(...); - }); - -You can convert errors to ``Exceptions``, check out the cookbook :doc:`chapter ` for details. - -View Handlers -------------- - -View Handlers allow you to intercept a controller result that is not a -``Response`` and transform it before it gets returned to the kernel. - -To register a view handler, pass a callable (or string that can be resolved to a -callable) to the ``view()`` method. The callable should accept some sort of result -from the controller:: - - $app->view(function (array $controllerResult) use ($app) { - return $app->json($controllerResult); - }); - -View Handlers also receive the ``Request`` as their second argument, -making them a good candidate for basic content negotiation:: - - $app->view(function (array $controllerResult, Request $request) use ($app) { - $acceptHeader = $request->headers->get('Accept'); - $bestFormat = $app['negotiator']->getBestFormat($acceptHeader, array('json', 'xml')); - - if ('json' === $bestFormat) { - return new JsonResponse($controllerResult); - } - - if ('xml' === $bestFormat) { - return $app['serializer.xml']->renderResponse($controllerResult); - } - - return $controllerResult; - }); - -View Handlers will be examined in the order they are added to the application -and Silex will use type hints to determine if a view handler should be used for -the current result, continuously using the return value of the last view handler -as the input for the next. - -.. note:: - - You must ensure that Silex receives a ``Response`` or a string as the result of - the last view handler (or controller) to be run. - -Redirects ---------- - -You can redirect to another page by returning a ``RedirectResponse`` response, -which you can create by calling the ``redirect`` method:: - - $app->get('/', function () use ($app) { - return $app->redirect('/hello'); - }); - -This will redirect from ``/`` to ``/hello``. - -Forwards --------- - -When you want to delegate the rendering to another controller, without a -round-trip to the browser (as for a redirect), use an internal sub-request:: - - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpKernel\HttpKernelInterface; - - $app->get('/', function () use ($app) { - // forward to /hello - $subRequest = Request::create('/hello', 'GET'); - - return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST); - }); - -.. tip:: - - You can also generate the URI via the built-in URL generator:: - - $request = Request::create($app['url_generator']->generate('hello'), 'GET'); - -There's some more things that you need to keep in mind though. In most cases you -will want to forward some parts of the current master request to the sub-request. -That includes: Cookies, server information, session. -Read more on :doc:`how to make sub-requests `. - -JSON ----- - -If you want to return JSON data, you can use the ``json`` helper method. -Simply pass it your data, status code and headers, and it will create a JSON -response for you:: - - $app->get('/users/{id}', function ($id) use ($app) { - $user = getUser($id); - - if (!$user) { - $error = array('message' => 'The user was not found.'); - - return $app->json($error, 404); - } - - return $app->json($user); - }); - -Streaming ---------- - -It's possible to stream a response, which is important in cases when you don't -want to buffer the data being sent:: - - $app->get('/images/{file}', function ($file) use ($app) { - if (!file_exists(__DIR__.'/images/'.$file)) { - return $app->abort(404, 'The image was not found.'); - } - - $stream = function () use ($file) { - readfile($file); - }; - - return $app->stream($stream, 200, array('Content-Type' => 'image/png')); - }); - -If you need to send chunks, make sure you call ``ob_flush`` and ``flush`` -after every chunk:: - - $stream = function () { - $fh = fopen('http://www.example.com/', 'rb'); - while (!feof($fh)) { - echo fread($fh, 1024); - ob_flush(); - flush(); - } - fclose($fh); - }; - -Sending a file --------------- - -If you want to return a file, you can use the ``sendFile`` helper method. -It eases returning files that would otherwise not be publicly available. Simply -pass it your file path, status code, headers and the content disposition and it -will create a ``BinaryFileResponse`` response for you:: - - $app->get('/files/{path}', function ($path) use ($app) { - if (!file_exists('/base/path/' . $path)) { - $app->abort(404); - } - - return $app->sendFile('/base/path/' . $path); - }); - -To further customize the response before returning it, check the API doc for -`Symfony\Component\HttpFoundation\BinaryFileResponse -`_:: - - return $app - ->sendFile('/base/path/' . $path) - ->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'pic.jpg') - ; - -Traits ------- - -Silex comes with PHP traits that define shortcut methods. - -Almost all built-in service providers have some corresponding PHP traits. To -use them, define your own Application class and include the traits you want:: - - use Silex\Application; - - class MyApplication extends Application - { - use Application\TwigTrait; - use Application\SecurityTrait; - use Application\FormTrait; - use Application\UrlGeneratorTrait; - use Application\SwiftmailerTrait; - use Application\MonologTrait; - use Application\TranslationTrait; - } - -You can also define your own Route class and use some traits:: - - use Silex\Route; - - class MyRoute extends Route - { - use Route\SecurityTrait; - } - -To use your newly defined route, override the ``$app['route_class']`` -setting:: - - $app['route_class'] = 'MyRoute'; - -Read each provider chapter to learn more about the added methods. - -Security --------- - -Make sure to protect your application against attacks. - -Escaping -~~~~~~~~ - -When outputting any user input, make sure to escape it correctly to prevent -Cross-Site-Scripting attacks. - -* **Escaping HTML**: PHP provides the ``htmlspecialchars`` function for this. - Silex provides a shortcut ``escape`` method:: - - use Symfony\Component\HttpFoundation\Request; - - $app->get('/name', function (Request $request, Silex\Application $app) { - $name = $request->get('name'); - - return "You provided the name {$app->escape($name)}."; - }); - - If you use the Twig template engine, you should use its escaping or even - auto-escaping mechanisms. Check out the *Providers* :doc:`chapter ` for details. - -* **Escaping JSON**: If you want to provide data in JSON format you should - use the Silex ``json`` function:: - - use Symfony\Component\HttpFoundation\Request; - - $app->get('/name.json', function (Request $request, Silex\Application $app) { - $name = $request->get('name'); - - return $app->json(array('name' => $name)); - }); - -.. _download: http://silex.sensiolabs.org/download -.. _Composer: http://getcomposer.org/ -.. _Request: http://api.symfony.com/master/Symfony/Component/HttpFoundation/Request.html -.. _Response: http://api.symfony.com/master/Symfony/Component/HttpFoundation/Response.html -.. _Monolog: https://github.com/Seldaek/monolog -.. _Expression: https://symfony.com/doc/current/book/routing.html#completely-customized-route-matching-with-conditions diff --git a/doc/web_servers.rst b/doc/web_servers.rst deleted file mode 100644 index cd99faa..0000000 --- a/doc/web_servers.rst +++ /dev/null @@ -1,165 +0,0 @@ -Webserver Configuration -======================= - -Apache ------- - -If you are using Apache, make sure ``mod_rewrite`` is enabled and use the -following ``.htaccess`` file: - -.. code-block:: apache - - - Options -MultiViews - - RewriteEngine On - #RewriteBase /path/to/app - RewriteCond %{REQUEST_FILENAME} !-d - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^ index.php [QSA,L] - - -.. note:: - - If your site is not at the webroot level you will have to uncomment the - ``RewriteBase`` statement and adjust the path to point to your directory, - relative from the webroot. - -Alternatively, if you use Apache 2.2.16 or higher, you can use the -`FallbackResource directive`_ to make your .htaccess even easier: - -.. code-block:: apache - - FallbackResource index.php - -.. note:: - - If your site is not at the webroot level you will have to adjust the path to - point to your directory, relative from the webroot. - -nginx ------ - -The **minimum configuration** to get your application running under Nginx is: - -.. code-block:: nginx - - server { - server_name domain.tld www.domain.tld; - root /var/www/project/web; - - location / { - # try to serve file directly, fallback to front controller - try_files $uri /index.php$is_args$args; - } - - # If you have 2 front controllers for dev|prod use the following line instead - # location ~ ^/(index|index_dev)\.php(/|$) { - location ~ ^/index\.php(/|$) { - # the ubuntu default - fastcgi_pass unix:/var/run/php5-fpm.sock; - # for running on centos - #fastcgi_pass unix:/var/run/php-fpm/www.sock; - - fastcgi_split_path_info ^(.+\.php)(/.*)$; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param HTTPS off; - - # Prevents URIs that include the front controller. This will 404: - # http://domain.tld/index.php/some-path - # Enable the internal directive to disable URIs like this - # internal; - } - - #return 404 for all php files as we do have a front controller - location ~ \.php$ { - return 404; - } - - error_log /var/log/nginx/project_error.log; - access_log /var/log/nginx/project_access.log; - } - -IIS ---- - -If you are using the Internet Information Services from Windows, you can use -this sample ``web.config`` file: - -.. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - -Lighttpd --------- - -If you are using lighttpd, use this sample ``simple-vhost`` as a starting -point: - -.. code-block:: lighttpd - - server.document-root = "/path/to/app" - - url.rewrite-once = ( - # configure some static files - "^/assets/.+" => "$0", - "^/favicon\.ico$" => "$0", - - "^(/[^\?]*)(\?.*)?" => "/index.php$1$2" - ) - -.. _FallbackResource directive: http://www.adayinthelifeof.nl/2012/01/21/apaches-fallbackresource-your-new-htaccess-command/ - -PHP ---- - -PHP ships with a built-in webserver for development. This server allows you to -run silex without any configuration. However, in order to serve static files, -you'll have to make sure your front controller returns false in that case:: - - // web/index.php - - $filename = __DIR__.preg_replace('#(\?.*)$#', '', $_SERVER['REQUEST_URI']); - if (php_sapi_name() === 'cli-server' && is_file($filename)) { - return false; - } - - $app = require __DIR__.'/../src/app.php'; - $app->run(); - - -Assuming your front controller is at ``web/index.php``, you can start the -server from the command-line with this command: - -.. code-block:: text - - $ php -S localhost:8080 -t web web/index.php - -Now the application should be running at ``http://localhost:8080``. - -.. note:: - - This server is for development only. It is **not** recommended to use it - in production.