diff --git a/book/controller.rst b/book/controller.rst index c4d6d031900..167ffbecaaa 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -590,8 +590,6 @@ Accessing other Services When extending the base controller class, you can access any Symfony2 service via the ``get()`` method. Here are several common services you might need:: - $request = $this->getRequest(); - $templating = $this->get('templating'); $router = $this->get('router'); @@ -660,16 +658,21 @@ by using the native PHP sessions. Storing and retrieving information from the session can be easily achieved from any controller:: - $session = $this->getRequest()->getSession(); + use Symfony\Component\HttpFoundation\Request; + + public function indexAction(Request $request) + { + $session = $request->getSession(); - // store an attribute for reuse during a later user request - $session->set('foo', 'bar'); + // store an attribute for reuse during a later user request + $session->set('foo', 'bar'); - // in another controller for another request - $foo = $session->get('foo'); + // in another controller for another request + $foo = $session->get('foo'); - // use a default value if the key doesn't exist - $filters = $session->get('filters', array()); + // use a default value if the key doesn't exist + $filters = $session->get('filters', array()); + } These attributes will remain on the user for the remainder of that user's session. @@ -687,11 +690,13 @@ These types of messages are called "flash" messages. For example, imagine you're processing a form submit:: - public function updateAction() + use Symfony\Component\HttpFoundation\Request; + + public function updateAction(Request $request) { $form = $this->createForm(...); - $form->handleRequest($this->getRequest()); + $form->handleRequest($request); if ($form->isValid()) { // do some sort of processing @@ -783,17 +788,22 @@ The Request Object ------------------ Besides the values of the routing placeholders, the controller also has access -to the ``Request`` object when extending the base ``Controller`` class:: +to the ``Request`` object. The framework injects the ``Request`` object in the +controller if a variable is type-hinted with +`Symfony\Component\HttpFoundation\Request`:: - $request = $this->getRequest(); + use Symfony\Component\HttpFoundation\Request; - $request->isXmlHttpRequest(); // is it an Ajax request? + public function indexAction(Request $request) + { + $request->isXmlHttpRequest(); // is it an Ajax request? - $request->getPreferredLanguage(array('en', 'fr')); + $request->getPreferredLanguage(array('en', 'fr')); - $request->query->get('page'); // get a $_GET parameter + $request->query->get('page'); // get a $_GET parameter - $request->request->get('page'); // get a $_POST parameter + $request->request->get('page'); // get a $_POST parameter + } Like the ``Response`` object, the request headers are stored in a ``HeaderBag`` object and are easily accessible. diff --git a/book/http_cache.rst b/book/http_cache.rst index 8079bd3571d..4133ee0d2f9 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -557,12 +557,14 @@ each ``ETag`` must be unique across all representations of the same resource. To see a simple implementation, generate the ETag as the md5 of the content:: - public function indexAction() + use Symfony\Component\HttpFoundation\Request; + + public function indexAction(Request $request) { $response = $this->render('MyBundle:Main:index.html.twig'); $response->setETag(md5($response->getContent())); $response->setPublic(); // make sure the response is public/cacheable - $response->isNotModified($this->getRequest()); + $response->isNotModified($request); return $response; } @@ -604,7 +606,9 @@ For instance, you can use the latest update date for all the objects needed to compute the resource representation as the value for the ``Last-Modified`` header value:: - public function showAction($articleSlug) + use Symfony\Component\HttpFoundation\Request; + + public function showAction($articleSlug, Request $request) { // ... @@ -617,7 +621,7 @@ header value:: // Set response as public. Otherwise it will be private by default. $response->setPublic(); - if ($response->isNotModified($this->getRequest())) { + if ($response->isNotModified($request)) { return $response; } @@ -653,8 +657,9 @@ the better. The ``Response::isNotModified()`` method does exactly that by exposing a simple and efficient pattern:: use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpFoundation\Request; - public function showAction($articleSlug) + public function showAction($articleSlug, Request $request) { // Get the minimum information to compute // the ETag or the Last-Modified value @@ -671,7 +676,7 @@ exposing a simple and efficient pattern:: $response->setPublic(); // Check that the Response is not modified for the given Request - if ($response->isNotModified($this->getRequest())) { + if ($response->isNotModified($request)) { // return the 304 Response immediately return $response; } else { diff --git a/book/security.rst b/book/security.rst index dfcbfbad729..edf3172febd 100644 --- a/book/security.rst +++ b/book/security.rst @@ -319,7 +319,7 @@ First, enable form login under your firewall: - + @@ -425,13 +425,13 @@ Next, create the controller that will display the login form:: namespace Acme\SecurityBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\SecurityContext; class SecurityController extends Controller { - public function loginAction() + public function loginAction(Request $request) { - $request = $this->getRequest(); $session = $request->getSession(); // get the login error if there is one @@ -515,6 +515,11 @@ Finally, create the corresponding template: +.. caution:: + + This login form is currently not protected against CSRF attacks. Read + :doc:`/cookbook/security/csrf_in_login_form` on how to protect your login form. + .. tip:: The ``error`` variable passed into the template is an instance of @@ -673,6 +678,13 @@ see :doc:`/cookbook/security/form_login`. for different firewalls. But usually for most applications, having one main firewall is enough. + **5. Routing error pages are not covered by firewalls** + + As Routing is done *before* security, Routing error pages are not covered + by any firewall. This means you can't check for security or even access + the user object on these pages. See :doc:`/cookbook/controller/error_pages` + for more details. + Authorization ------------- @@ -1050,73 +1062,6 @@ the user will be redirected to ``https``: ), ), -.. _book-security-securing-controller: - -Securing a Controller -~~~~~~~~~~~~~~~~~~~~~ - -Protecting your application based on URL patterns is easy, but may not be -fine-grained enough in certain cases. When necessary, you can easily force -authorization from inside a controller:: - - // ... - use Symfony\Component\Security\Core\Exception\AccessDeniedException; - - public function helloAction($name) - { - if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { - throw new AccessDeniedException(); - } - - // ... - } - -.. _book-security-securing-controller-annotations: - -You can also choose to install and use the optional JMSSecurityExtraBundle, -which can secure your controller using annotations:: - - // ... - use JMS\SecurityExtraBundle\Annotation\Secure; - - /** - * @Secure(roles="ROLE_ADMIN") - */ - public function helloAction($name) - { - // ... - } - -For more information, see the `JMSSecurityExtraBundle`_ documentation. - -Securing other Services -~~~~~~~~~~~~~~~~~~~~~~~ - -In fact, anything in Symfony can be protected using a strategy similar to -the one seen in the previous section. For example, suppose you have a service -(i.e. a PHP class) whose job is to send emails from one user to another. -You can restrict use of this class - no matter where it's being used from - -to users that have a specific role. - -For more information on how you can use the Security component to secure -different services and methods in your application, see :doc:`/cookbook/security/securing_services`. - -Access Control Lists (ACLs): Securing Individual Database Objects -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Imagine you are designing a blog system where your users can comment on your -posts. Now, you want a user to be able to edit their own comments, but not -those of other users. Also, as the admin user, you yourself want to be able -to edit *all* comments. - -The Security component comes with an optional access control list (ACL) system -that you can use when you need to control access to individual instances -of an object in your system. *Without* ACL, you can secure your system so that -only certain users can edit blog comments in general. But *with* ACL, you -can restrict or allow access on a comment-by-comment basis. - -For more information, see the cookbook article: :doc:`/cookbook/security/acl`. - Users ----- @@ -1406,6 +1351,11 @@ the password is simply run through the ``sha1`` algorithm one time and without any extra encoding. You can now calculate the hashed password either programmatically (e.g. ``hash('sha1', 'ryanpass')``) or via some online tool like `functions-online.com`_ +.. tip:: + + Supported algorithms for this method depend on your PHP version. + A full list is available calling the PHP function :phpfunction:`hash_algos`. + If you're creating your users dynamically (and storing them in a database), you can use even tougher hashing algorithms and then rely on an actual password encoder object to help you encode passwords. For example, suppose your User @@ -1706,191 +1656,38 @@ In the above configuration, users with ``ROLE_ADMIN`` role will also have the ``ROLE_USER`` role. The ``ROLE_SUPER_ADMIN`` role has ``ROLE_ADMIN``, ``ROLE_ALLOWED_TO_SWITCH`` and ``ROLE_USER`` (inherited from ``ROLE_ADMIN``). -.. _book-security-logging-out: - -Logging Out ------------ - -Usually, you'll also want your users to be able to log out. Fortunately, -the firewall can handle this automatically for you when you activate the -``logout`` config parameter: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - firewalls: - secured_area: - # ... - logout: - path: /logout - target: / - # ... - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - 'firewalls' => array( - 'secured_area' => array( - // ... - 'logout' => array('path' => 'logout', 'target' => '/'), - ), - ), - // ... - )); - -Once this is configured under your firewall, sending a user to ``/logout`` -(or whatever you configure the ``path`` to be), will un-authenticate the -current user. The user will then be sent to the homepage (the value defined -by the ``target`` parameter). Both the ``path`` and ``target`` config parameters -default to what's specified here. In other words, unless you need to customize -them, you can omit them entirely and shorten your configuration: - -.. configuration-block:: - - .. code-block:: yaml - - logout: ~ - - .. code-block:: xml - - - - .. code-block:: php - - 'logout' => array(), - -Note that you will *not* need to implement a controller for the ``/logout`` -URL as the firewall takes care of everything. You *do*, however, need to create -a route so that you can use it to generate the URL: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/routing.yml - logout: - path: /logout - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // app/config/routing.php - use Symfony\Component\Routing\RouteCollection; - use Symfony\Component\Routing\Route; - - $collection = new RouteCollection(); - $collection->add('logout', new Route('/logout', array())); - - return $collection; - -Once the user has been logged out, they will be redirected to whatever path -is defined by the ``target`` parameter above (e.g. the ``homepage``). For -more information on configuring the logout, see the -:doc:`Security Configuration Reference `. - -.. _book-security-template: - -Access Control in Templates ---------------------------- - -If you want to check if the current user has a role inside a template, use -the built-in helper function: - -.. configuration-block:: - - .. code-block:: html+jinja - - {% if is_granted('ROLE_ADMIN') %} - Delete - {% endif %} - - .. code-block:: html+php - - isGranted('ROLE_ADMIN')): ?> - Delete - - -.. note:: - - If you use this function and are *not* at a URL where there is a firewall - active, an exception will be thrown. Again, it's almost always a good - idea to have a main firewall that covers all URLs (as has been shown - in this chapter). - -.. _book-security-template-expression: +Access Control +-------------- -.. versionadded:: 2.4 - The ``expression`` functionality was introduced in Symfony 2.4. - -You can also use expressions inside your templates: - -.. configuration-block:: - - .. code-block:: html+jinja - - {% if is_granted(expression( - '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())' - )) %} - Delete - {% endif %} - - .. code-block:: html+php - - isGranted(new Expression( - '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())' - ))): ?> - Delete - +Now that you have a User and Roles, you can go further than URL-pattern based +authorization. -For more details on expressions and security, see :ref:`book-security-expressions`. +.. _book-security-securing-controller: Access Control in Controllers ------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you want to check if the current user has a role in your controller, use -the :method:`Symfony\\Component\\Security\\Core\\SecurityContext::isGranted` -method of the security context:: +Protecting your application based on URL patterns is easy, but may not be +fine-grained enough in certain cases. When necessary, you can easily force +authorization from inside a controller:: - public function indexAction() + // ... + use Symfony\Component\Security\Core\Exception\AccessDeniedException; + + public function helloAction($name) { - // show different content to admin users - if ($this->get('security.context')->isGranted('ROLE_ADMIN')) { - // ... load admin content here + if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { + throw new AccessDeniedException(); } - // ... load other regular content here + // ... } -.. note:: +.. caution:: - A firewall must be active or an exception will be thrown when the ``isGranted`` - method is called. See the note above about templates for more details. + A firewall must be active or an exception will be thrown when the ``isGranted()`` + method is called. It's almost always a good idea to have a main firewall that + covers all URLs (as is shown in this chapter). .. _book-security-expressions: @@ -1977,89 +1774,98 @@ Additionally, you have access to a number of functions inside the expression: true if the user has actually logged in during this session (i.e. is full-fledged). -Impersonating a User --------------------- +Access Control in Other Services +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Sometimes, it's useful to be able to switch from one user to another without -having to log out and log in again (for instance when you are debugging or trying -to understand a bug a user sees that you can't reproduce). This can be easily -done by activating the ``switch_user`` firewall listener: +In fact, anything in Symfony can be protected using a strategy similar to +the one seen in the previous section. For example, suppose you have a service +(i.e. a PHP class) whose job is to send emails from one user to another. +You can restrict use of this class - no matter where it's being used from - +to users that have a specific role. -.. configuration-block:: +For more information on how you can use the Security component to secure +different services and methods in your application, see :doc:`/cookbook/security/securing_services`. - .. code-block:: yaml +.. _book-security-template: - # app/config/security.yml - security: - firewalls: - main: - # ... - switch_user: true +Access Control in Templates +~~~~~~~~~~~~~~~~~~~~~~~~~~~ - .. code-block:: xml +If you want to check if the current user has a role inside a template, use +the built-in helper function: - - - - - - - +.. configuration-block:: - .. code-block:: php + .. code-block:: html+jinja - // app/config/security.php - $container->loadFromExtension('security', array( - 'firewalls' => array( - 'main'=> array( - // ... - 'switch_user' => true - ), - ), - )); + {% if is_granted('ROLE_ADMIN') %} + Delete + {% endif %} -To switch to another user, just add a query string with the ``_switch_user`` -parameter and the username as the value to the current URL: + .. code-block:: html+php -.. code-block:: text + isGranted('ROLE_ADMIN')): ?> + Delete + - http://example.com/somewhere?_switch_user=thomas +.. note:: -To switch back to the original user, use the special ``_exit`` username: + If you use this function and are *not* at a URL behind a firewall + active, an exception will be thrown. Again, it's almost always a good + idea to have a main firewall that covers all URLs (as has been shown + in this chapter). -.. code-block:: text +.. _book-security-template-expression: - http://example.com/somewhere?_switch_user=_exit +.. versionadded:: 2.4 + The ``expression`` functionality was introduced in Symfony 2.4. -During impersonation, the user is provided with a special role called -``ROLE_PREVIOUS_ADMIN``. In a template, for instance, this role can be used -to show a link to exit impersonation: +You can also use expressions inside your templates: .. configuration-block:: .. code-block:: html+jinja - {% if is_granted('ROLE_PREVIOUS_ADMIN') %} - Exit impersonation + {% if is_granted(expression( + '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())' + )) %} + Delete {% endif %} .. code-block:: html+php - isGranted('ROLE_PREVIOUS_ADMIN')): ?> - - Exit impersonation - + isGranted(new Expression( + '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())' + ))): ?> + Delete -Of course, this feature needs to be made available to a small group of users. -By default, access is restricted to users having the ``ROLE_ALLOWED_TO_SWITCH`` -role. The name of this role can be modified via the ``role`` setting. For -extra security, you can also change the query parameter name via the ``parameter`` -setting: +For more details on expressions and security, see :ref:`book-security-expressions`. + +Access Control Lists (ACLs): Securing Individual Database Objects +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Imagine you are designing a blog system where your users can comment on your +posts. Now, you want a user to be able to edit their own comments, but not +those of other users. Also, as the admin user, you yourself want to be able +to edit *all* comments. + +The Security component comes with an optional access control list (ACL) system +that you can use when you need to control access to individual instances +of an object in your system. *Without* ACL, you can secure your system so that +only certain users can edit blog comments in general. But *with* ACL, you +can restrict or allow access on a comment-by-comment basis. + +For more information, see the cookbook article: :doc:`/cookbook/security/acl`. + +.. _book-security-logging-out: + +Logging Out +----------- + +Usually, you'll also want your users to be able to log out. Fortunately, +the firewall can handle this automatically for you when you activate the +``logout`` config parameter: .. configuration-block:: @@ -2068,18 +1874,22 @@ setting: # app/config/security.yml security: firewalls: - main: + secured_area: # ... - switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user } + logout: + path: /logout + target: / + # ... .. code-block:: xml - + - + + .. code-block:: php @@ -2087,16 +1897,75 @@ setting: // app/config/security.php $container->loadFromExtension('security', array( 'firewalls' => array( - 'main'=> array( + 'secured_area' => array( // ... - 'switch_user' => array( - 'role' => 'ROLE_ADMIN', - 'parameter' => '_want_to_be_this_user', - ), + 'logout' => array('path' => 'logout', 'target' => '/'), ), ), + // ... )); +Once this is configured under your firewall, sending a user to ``/logout`` +(or whatever you configure the ``path`` to be), will un-authenticate the +current user. The user will then be sent to the homepage (the value defined +by the ``target`` parameter). Both the ``path`` and ``target`` config parameters +default to what's specified here. In other words, unless you need to customize +them, you can omit them entirely and shorten your configuration: + +.. configuration-block:: + + .. code-block:: yaml + + logout: ~ + + .. code-block:: xml + + + + .. code-block:: php + + 'logout' => array(), + +Note that you will *not* need to implement a controller for the ``/logout`` +URL as the firewall takes care of everything. You *do*, however, need to create +a route so that you can use it to generate the URL: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/routing.yml + logout: + path: /logout + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/routing.php + use Symfony\Component\Routing\RouteCollection; + use Symfony\Component\Routing\Route; + + $collection = new RouteCollection(); + $collection->add('logout', new Route('/logout', array())); + + return $collection; + +Once the user has been logged out, they will be redirected to whatever path +is defined by the ``target`` parameter above (e.g. the ``homepage``). For +more information on configuring the logout, see the +:doc:`Security Configuration Reference `. + Stateless Authentication ------------------------ @@ -2217,6 +2086,7 @@ Learn more from the Cookbook ---------------------------- * :doc:`Forcing HTTP/HTTPS ` +* :doc:`Impersonating a User ` * :doc:`Blacklist users by IP address with a custom voter ` * :doc:`Access Control Lists (ACLs) ` * :doc:`/cookbook/security/remember_me` diff --git a/book/translation.rst b/book/translation.rst index 930e5641187..193945da32a 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -408,12 +408,14 @@ Handling the User's Locale The locale of the current user is stored in the request and is accessible via the ``request`` object:: - // access the request object in a standard controller - $request = $this->getRequest(); + use Symfony\Component\HttpFoundation\Request; - $locale = $request->getLocale(); + public function indexAction(Request $request) + { + $locale = $request->getLocale(); - $request->setLocale('en_US'); + $request->setLocale('en_US'); + } .. tip:: diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 6af03289796..ea60102dcd6 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -160,6 +160,8 @@ You can also set these colors and options inside the tagname:: // bold text on a yellow background $output->writeln('foo'); +.. verbosity-levels: + Verbosity Levels ~~~~~~~~~~~~~~~~ @@ -226,6 +228,13 @@ When the quiet level is used, all output is suppressed as the default :method:`Symfony\Component\Console\Output::write ` method returns without actually printing. +.. tip:: + + The MonologBridge provides a :class:`Symfony\\Bridge\\Monolog\\Handler\\ConsoleHandler` + class that allows you to display messages on the console. This is cleaner + than wrapping your output calls in conditions. For an example use in + the Symfony Framework, see :doc:/cookbook/logging/monolog_console. + Using Command Arguments ----------------------- diff --git a/contributing/code/patches.rst b/contributing/code/patches.rst index c3c3247cbf8..d2dae74d2fd 100644 --- a/contributing/code/patches.rst +++ b/contributing/code/patches.rst @@ -177,14 +177,12 @@ in mind the following: .. tip:: - You can check the coding standards of your patch by running the following - `script `_ - (`source `_): + When submitting pull requests, `fabbot`_ checks your code + for common typos and verifies that you are using the PHP coding standards + as defined in PSR-1 and PSR-2. - .. code-block:: bash - - $ cd /path/to/symfony/src - $ php symfony-cs-fixer.phar fix . Symfony20Finder + A status is posted below the pull request description with a summary + of any problems it detects or any Travis CI build failures. .. tip:: @@ -410,3 +408,4 @@ of all the commits. When you finish, execute the push command. .. _`travis-ci.org status icon`: http://about.travis-ci.org/docs/user/status-images/ .. _`travis-ci.org Getting Started Guide`: http://about.travis-ci.org/docs/user/getting-started/ .. _`documentation repository`: https://github.com/symfony/symfony-docs +.. _`fabbot`: http://fabbot.io diff --git a/cookbook/console/logging.rst b/cookbook/console/logging.rst index 5352fb581ee..b4c9ea9f872 100644 --- a/cookbook/console/logging.rst +++ b/cookbook/console/logging.rst @@ -34,7 +34,7 @@ container and use it to do the logging:: use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; - use Symfony\Component\HttpKernel\Log\LoggerInterface; + use \Psr\Log\LoggerInterface; class GreetCommand extends ContainerAwareCommand { @@ -54,7 +54,7 @@ container and use it to do the logging:: if ($input->getOption('yell')) { $text = strtoupper($text); - $logger->warn('Yelled: '.$text); + $logger->warning('Yelled: '.$text); } else { $logger->info('Greeted: '.$text); } @@ -69,146 +69,109 @@ setup), you should see the logged entries in ``app/logs/dev.log`` or ``app/logs/ Enabling automatic Exceptions logging ------------------------------------- -To get your console application to automatically log uncaught exceptions -for all of your commands, you'll need to do a little bit more work. - -First, create a new sub-class of :class:`Symfony\\Bundle\\FrameworkBundle\\Console\\Application` -and override its :method:`Symfony\\Bundle\\FrameworkBundle\\Console\\Application::run` -method, where exception handling should happen: - -.. caution:: - - Due to the nature of the core :class:`Symfony\\Component\\Console\\Application` - class, much of the :method:`run ` - method has to be duplicated and even a private property ``originalAutoExit`` - re-implemented. This serves as an example of what you *could* do in your - code, though there is a high risk that something may break when upgrading - to future versions of Symfony. - -.. code-block:: php - - // src/Acme/DemoBundle/Console/Application.php - namespace Acme\DemoBundle\Console; - - use Symfony\Bundle\FrameworkBundle\Console\Application as BaseApplication; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; - use Symfony\Component\Console\Output\ConsoleOutputInterface; - use Symfony\Component\HttpKernel\Log\LoggerInterface; - use Symfony\Component\HttpKernel\KernelInterface; - use Symfony\Component\Console\Output\ConsoleOutput; - use Symfony\Component\Console\Input\ArgvInput; - - class Application extends BaseApplication +To get your console application to automatically log uncaught exceptions for +all of your commands, you can use :doc:`console events`. + +.. versionadded:: 2.3 + Console events were added in Symfony 2.3. + +First configure a listener for console exception events in the service container: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + kernel.listener.command_dispatch: + class: Acme\DemoBundle\EventListener\ConsoleExceptionListener + arguments: + logger: "@logger" + tags: + - { name: kernel.event_listener, event: console.exception } + + .. code-block:: xml + + + + + + + Acme\DemoBundle\EventListener\ConsoleExceptionListener + + + + + + + + + + + .. code-block:: php + + // app/config/services.php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + + $container->setParameter( + 'console_exception_listener.class', + 'Acme\DemoBundle\EventListener\ConsoleExceptionListener' + ); + $definitionConsoleExceptionListener = new Definition( + '%console_exception_listener.class%', + array(new Reference('logger')) + ); + $definitionConsoleExceptionListener->addTag( + 'kernel.event_listener', + array('event' => 'console.exception') + ); + $container->setDefinition( + 'kernel.listener.command_dispatch', + $definitionConsoleExceptionListener + ); + +Then implement the actual listener:: + + // src/Acme/DemoBundle/EventListener/ConsoleExceptionListener.php + namespace Acme\DemoBundle\EventListener; + + use Symfony\Component\Console\Event\ConsoleExceptionEvent; + use Psr\Log\LoggerInterface; + + class ConsoleExceptionListener { - private $originalAutoExit; + private $logger; - public function __construct(KernelInterface $kernel) + public function __construct(LoggerInterface $logger) { - parent::__construct($kernel); - $this->originalAutoExit = true; + $this->logger = $logger; } - /** - * Runs the current application. - * - * @param InputInterface $input An Input instance - * @param OutputInterface $output An Output instance - * - * @return integer 0 if everything went fine, or an error code - * - * @throws \Exception When doRun returns Exception - * - * @api - */ - public function run(InputInterface $input = null, OutputInterface $output = null) - { - // make the parent method throw exceptions, so you can log it - $this->setCatchExceptions(false); - - if (null === $input) { - $input = new ArgvInput(); - } - - if (null === $output) { - $output = new ConsoleOutput(); - } - - try { - $statusCode = parent::run($input, $output); - } catch (\Exception $e) { - - /** @var $logger LoggerInterface */ - $logger = $this->getKernel()->getContainer()->get('logger'); - - $message = sprintf( - '%s: %s (uncaught exception) at %s line %s while running console command `%s`', - get_class($e), - $e->getMessage(), - $e->getFile(), - $e->getLine(), - $this->getCommandName($input) - ); - $logger->crit($message); - - if ($output instanceof ConsoleOutputInterface) { - $this->renderException($e, $output->getErrorOutput()); - } else { - $this->renderException($e, $output); - } - $statusCode = $e->getCode(); - - $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1; - } - - if ($this->originalAutoExit) { - if ($statusCode > 255) { - $statusCode = 255; - } - // @codeCoverageIgnoreStart - exit($statusCode); - // @codeCoverageIgnoreEnd - } + public function onConsoleException(ConsoleExceptionEvent $event) { + $command = $event->getCommand(); + $exception = $event->getException(); - return $statusCode; - } + $message = sprintf( + '%s: %s (uncaught exception) at %s line %s while running console command `%s`', + get_class($exception), + $exception->getMessage(), + $exception->getFile(), + $exception->getLine(), + $command->getName() + ); - public function setAutoExit($bool) - { - // parent property is private, so we need to intercept it in a setter - $this->originalAutoExit = (Boolean) $bool; - parent::setAutoExit($bool); + $this->logger->error($message); } - } -In the code above, you disable exception catching so the parent ``run`` method -will throw all exceptions. When an exception is caught, you simply log it by -accessing the ``logger`` service from the service container and then handle -the rest of the logic in the same way that the parent ``run`` method does -(specifically, since the parent :method:`run ` -method will not handle exceptions rendering and status code handling when -``catchExceptions`` is set to false, it has to be done in the overridden -method). - -For the extended ``Application`` class to work properly with in console shell mode, -you have to do a small trick to intercept the ``autoExit`` setter and store the -setting in a different property, since the parent property is private. - -Now to be able to use your extended ``Application`` class you need to adjust -the ``app/console`` script to use the new class instead of the default:: - - // app/console - - // ... - // replace the following line: - // use Symfony\Bundle\FrameworkBundle\Console\Application; - use Acme\DemoBundle\Console\Application; - - // ... - -That's it! Thanks to autoloader, your class will now be used instead of original -one. +In the code above, when any command throws an exception, the listener will +receive an event. You can simply log it by passing the logger service via the +service configuration. Your method receives a +:class:`Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent`` object, +which has methods to get information about the event and the exception. Logging non-0 exit statuses --------------------------- @@ -217,36 +180,98 @@ The logging capabilities of the console can be further extended by logging non-0 exit statuses. This way you will know if a command had any errors, even if no exceptions were thrown. -In order to do that, you'd have to modify the ``run()`` method of your extended -``Application`` class in the following way:: - - public function run(InputInterface $input = null, OutputInterface $output = null) +First configure a listener for console terminate events in the service container: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + kernel.listener.command_dispatch: + class: Acme\DemoBundle\EventListener\ConsoleTerminateListener + arguments: + logger: "@logger" + tags: + - { name: kernel.event_listener, event: console.terminate } + + .. code-block:: xml + + + + + + + Acme\DemoBundle\EventListener\ConsoleExceptionListener + + + + + + + + + + + .. code-block:: php + + // app/config/services.php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + + $container->setParameter( + 'console_terminate_listener.class', + 'Acme\DemoBundle\EventListener\ConsoleExceptionListener' + ); + $definitionConsoleExceptionListener = new Definition( + '%console_terminate_listener.class%', + array(new Reference('logger')) + ); + $definitionConsoleExceptionListener->addTag( + 'kernel.event_listener', + array('event' => 'console.terminate') + ); + $container->setDefinition( + 'kernel.listener.command_dispatch', + $definitionConsoleExceptionListener + ); + +Then implement the actual listener:: + + // src/Acme/DemoBundle/EventListener/ConsoleExceptionListener.php + namespace Acme/DemoBundle\EventListener; + + use Symfony\Component\Console\Event\ConsoleTerminateEvent; + use Psr\Log\LoggerInterface; + + class ConsoleTerminateListener { - // make the parent method throw exceptions, so you can log it - $this->setCatchExceptions(false); + private $logger; - // store the autoExit value before resetting it - you'll need it later - $autoExit = $this->originalAutoExit; - $this->setAutoExit(false); + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } - // ... + public function onConsoleTerminate(ConsoleTerminateEvent $event) { + $statusCode = $event->getExitCode(); + $command = $event->getCommand(); - if ($autoExit) { - if ($statusCode > 255) { - $statusCode = 255; + if ($statusCode === 0) { + return; } - // log non-0 exit codes along with command name - if ($statusCode !== 0) { - /** @var $logger LoggerInterface */ - $logger = $this->getKernel()->getContainer()->get('logger'); - $logger->warn(sprintf('Command `%s` exited with status code %d', $this->getCommandName($input), $statusCode)); + if ($statusCode > 255) { + $statusCode = 255; + $event->setExitCode($statusCode); } - // @codeCoverageIgnoreStart - exit($statusCode); - // @codeCoverageIgnoreEnd + $this->logger->warning(sprintf( + 'Command `%s` exited with status code %d', + $command->getName(), + $statusCode + )); } - - return $statusCode; } diff --git a/cookbook/doctrine/custom_dql_functions.rst b/cookbook/doctrine/custom_dql_functions.rst index 9c863055c08..35141309b5e 100644 --- a/cookbook/doctrine/custom_dql_functions.rst +++ b/cookbook/doctrine/custom_dql_functions.rst @@ -17,17 +17,14 @@ In Symfony, you can register your custom DQL functions as follows: doctrine: orm: # ... - entity_managers: - default: - # ... - dql: - string_functions: - test_string: Acme\HelloBundle\DQL\StringFunction - second_string: Acme\HelloBundle\DQL\SecondStringFunction - numeric_functions: - test_numeric: Acme\HelloBundle\DQL\NumericFunction - datetime_functions: - test_datetime: Acme\HelloBundle\DQL\DatetimeFunction + dql: + string_functions: + test_string: Acme\HelloBundle\DQL\StringFunction + second_string: Acme\HelloBundle\DQL\SecondStringFunction + numeric_functions: + test_numeric: Acme\HelloBundle\DQL\NumericFunction + datetime_functions: + test_datetime: Acme\HelloBundle\DQL\DatetimeFunction .. code-block:: xml @@ -41,15 +38,12 @@ In Symfony, you can register your custom DQL functions as follows: - - - - Acme\HelloBundle\DQL\SecondStringFunction - Acme\HelloBundle\DQL\DatetimeFunction - - + + Acme\HelloBundle\DQL\SecondStringFunction + Acme\HelloBundle\DQL\DatetimeFunction + @@ -60,23 +54,16 @@ In Symfony, you can register your custom DQL functions as follows: $container->loadFromExtension('doctrine', array( 'orm' => array( // ... - - 'entity_managers' => array( - 'default' => array( - // ... - - 'dql' => array( - 'string_functions' => array( - 'test_string' => 'Acme\HelloBundle\DQL\StringFunction', - 'second_string' => 'Acme\HelloBundle\DQL\SecondStringFunction', - ), - 'numeric_functions' => array( - 'test_numeric' => 'Acme\HelloBundle\DQL\NumericFunction', - ), - 'datetime_functions' => array( - 'test_datetime' => 'Acme\HelloBundle\DQL\DatetimeFunction', - ), - ), + 'dql' => array( + 'string_functions' => array( + 'test_string' => 'Acme\HelloBundle\DQL\StringFunction', + 'second_string' => 'Acme\HelloBundle\DQL\SecondStringFunction', + ), + 'numeric_functions' => array( + 'test_numeric' => 'Acme\HelloBundle\DQL\NumericFunction', + ), + 'datetime_functions' => array( + 'test_datetime' => 'Acme\HelloBundle\DQL\DatetimeFunction', ), ), ), diff --git a/cookbook/logging/index.rst b/cookbook/logging/index.rst index dda7d7cafdd..2570b3d5627 100644 --- a/cookbook/logging/index.rst +++ b/cookbook/logging/index.rst @@ -6,5 +6,6 @@ Logging monolog monolog_email + monolog_console monolog_regex_based_excludes channels_handlers diff --git a/cookbook/logging/monolog.rst b/cookbook/logging/monolog.rst index 32e064598e1..c2c6559dd75 100644 --- a/cookbook/logging/monolog.rst +++ b/cookbook/logging/monolog.rst @@ -23,8 +23,7 @@ your controller:: } The ``logger`` service has different methods for different logging levels. -See :class:`Symfony\\Component\\HttpKernel\\Log\\LoggerInterface` for details -on which methods are available. +See LoggerInterface_ for details on which methods are available. Handlers and Channels: Writing logs to different Locations ---------------------------------------------------------- @@ -351,3 +350,4 @@ using a processor. handler level instead of globally. .. _Monolog: https://github.com/Seldaek/monolog +.. _LoggerInterface: https://github.com/php-fig/log/blob/master/Psr/Log/LoggerInterface.php diff --git a/cookbook/logging/monolog_console.rst b/cookbook/logging/monolog_console.rst new file mode 100644 index 00000000000..3a22b7dcfbc --- /dev/null +++ b/cookbook/logging/monolog_console.rst @@ -0,0 +1,183 @@ +.. index:: + single: Logging; Console messages + +How to Configure Monolog to Display Console Messages +==================================================== + +.. versionadded:: 2.3 + This feature was introduced to the MonologBundle in version 2.4, which + was first packaged with Symfony at version 2.4 (but compatible with Symfony 2.3). + +It is possible to use the console to print messages for certain :ref:`verbosity-levels` +using the :class:`Symfony\\Component\\Console\\Output\\OutputInterface` +instance that is passed when a command gets executed. + +When a lot of logging has to happen, it's cumbersome to print information +depending on the verbosity settings (``-v``, ``-vv``, ``-vvv``) because the +calls need to be wrapped in conditions. The code quickly gets verbose or dirty. +For example:: + + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Output\OutputInterface; + + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG) { + $output->writeln('Some info'); + } + + if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { + $output->writeln('Some more info'); + } + } + +Instead of using these semantic methods to test for each of the verbosity +levels, `MonologBundle`_ 2.4 provides a `ConsoleHandler`_ that listens to +console events and writes log messages to the console output depending on the +current log level and the console verbosity. + +The example above could then be rewritten as:: + + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Output\OutputInterface; + + protected function execute(InputInterface $input, OutputInterface $output) + { + // assuming the Command extends ContainerAwareCommand... + $logger = $this->getContainer()->get('logger'); + $logger->debug('Some info'); + + $logger->notice('Some more info'); + } + +Depending on the verbosity level that the command is run in and the user's +configuration (see below), these messages may or may not be displayed to +the console. If they are displayed, they are timestamped and colored appropriately. +Additionally, error logs are written to the error output (php://stderr). +There is no need to conditionally handle the verbosity settings anymore. + +The Monolog console handler is enabled in the Monolog configuration. This is +the default in Symfony Standard Edition 2.4 too. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + monolog: + handlers: + console: + type: console + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('monolog', array( + 'handlers' => array( + 'console' => array( + 'type' => 'console', + ), + ), + )); + +With the ``verbosity_levels`` option you can adapt the mapping between +verbosity and log level. In the given example it will also show notices in +normal verbosity mode (instead of warnings only). Additionally, it will only +use messages logged with the custom ``my_channel`` channel and it changes the +display style via a custom formatter. See also the :doc:`reference/configuration/monolog` +for more information: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + monolog: + handlers: + console: + type: console + verbosity_levels: + VERBOSITY_NORMAL: NOTICE + channels: my_channel + formatter: my_formatter + + .. code-block:: xml + + + + + + + + + my_channel + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('monolog', array( + 'handlers' => array( + 'console' => array( + 'type' => 'console', + 'verbosity_levels' => array( + 'VERBOSITY_NORMAL' => 'NOTICE', + ), + 'channels' => 'my_channel', + 'formatter' => 'my_formatter', + ), + ), + )); + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/services.yml + services: + my_formatter: + class: Symfony\Bridge\Monolog\Formatter\ConsoleFormatter + arguments: + - "[%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n" + + .. code-block:: xml + + + + + + + + [%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n + + + + + + .. code-block:: php + + // app/config/services.php + $container + ->register('my_formatter', 'Symfony\Bridge\Monolog\Formatter\ConsoleFormatter') + ->addArgument('[%%datetime%%] %%start_tag%%%%message%%%%end_tag%% (%%level_name%%) %%context%% %%extra%%\n') + ; + +.. _ConsoleHandler: https://github.com/symfony/MonologBridge/blob/master/Handler/ConsoleHandler.php +.. _MonologBundle: https://github.com/symfony/MonologBundle diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 92c7bdc8957..64590e38e1d 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -101,6 +101,7 @@ * :doc:`/cookbook/logging/monolog` * :doc:`/cookbook/logging/monolog_email` + * :doc:`/cookbook/logging/monolog_console` * :doc:`/cookbook/logging/monolog_regex_based_excludes` * :doc:`/cookbook/logging/channels_handlers` @@ -128,6 +129,7 @@ * :doc:`/cookbook/security/entity_provider` * :doc:`/cookbook/security/remember_me` + * :doc:`/cookbook/security/impersonating_user` * :doc:`/cookbook/security/voters` * :doc:`/cookbook/security/acl` * :doc:`/cookbook/security/acl_advanced` @@ -140,6 +142,7 @@ * :doc:`/cookbook/security/api_key_authentication` * :doc:`/cookbook/security/custom_authentication_provider` * :doc:`/cookbook/security/target_path` + * :doc:`/cookbook/security/csrf_in_login_form` * **Serializer** diff --git a/cookbook/security/csrf_in_login_form.rst b/cookbook/security/csrf_in_login_form.rst new file mode 100644 index 00000000000..edc63ce1acb --- /dev/null +++ b/cookbook/security/csrf_in_login_form.rst @@ -0,0 +1,171 @@ +.. index:: + single: Security; CSRF in the Login Form + +Using CSRF in the Login Form +============================ + +When using a login form, you should make sure that you are protected against CSRF +(`Cross-site request forgery`_). The Security component already has built-in support +for CSRF. In this article you'll learn how you can use it in your login form. + +.. note:: + + Login CSRF attacks are a bit less well-known. See `Forging Login Requests`_ + if you're curious about more details. + +Configuring CSRF +---------------- + +First, configure the Security component so it can use CSRF protection. +The Security component needs a CSRF provider. You can set this to use the default +provider available in the Form component: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + firewalls: + secured_area: + # ... + form_login: + # ... + csrf_provider: form.csrf_provider + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + // ... + 'form_login' => array( + // ... + 'csrf_provider' => 'form.csrf_provider', + ) + ) + ) + )); + +The Security component can be configured further, but this is all information +it needs to be able to use CSRF in the login form. + +Rendering the CSRF field +------------------------ + +Now that Security component will check for the CSRF token, you have to add +a *hidden* field to the login form containing the CSRF token. By default, +this field is named ``_csrf_token``. That hidden field must contain the CSRF +token, which can be generated by using the ``csrf_token`` function. That +function requires a token ID, which must be set to ``authenticate`` when +using the login form: + +.. configuration-block:: + + .. code-block:: html+twig + + {# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #} + + {# ... #} +
+ {# ... the login fields #} + + + + +
+ + .. code-block:: html+php + + + + +
+ + + + + +
+ +After this, you have protected your login form against CSRF attacks. + +.. tip:: + + You can change the name of the field by setting ``csrf_parameter`` and change + the token ID by setting ``intention`` in your configuration: + + .. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + firewalls: + secured_area: + # ... + form_login: + # ... + csrf_parameter: _csrf_security_token + intention: a_private_string + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + // ... + 'form_login' => array( + // ... + 'csrf_parameter' => '_csrf_security_token', + 'intention' => 'a_private_string', + ) + ) + ) + )); + +.. _`Cross-site request forgery`: http://en.wikipedia.org/wiki/Cross-site_request_forgery +.. _`Forging Login Requests`: http://en.wikipedia.org/wiki/Cross-site_request_forgery#Forging_login_requests \ No newline at end of file diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index 53142551e5a..a2ef0f2a6d2 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -171,7 +171,7 @@ focus on the most important methods that come from the .. note:: - When implementing the + If you choose to implement :class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface`, you determine yourself which properties need to be compared to distinguish your user objects. @@ -198,14 +198,27 @@ interface forces the class to implement the five following methods: For more details on each of these, see :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface`. -.. note:: +.. sidebar:: What is the importance of serialize and unserialize? The :phpclass:`Serializable` interface and its ``serialize`` and ``unserialize`` methods have been added to allow the ``User`` class to be serialized to the session. This may or may not be needed depending on your setup, - but it's probably a good idea. Only the ``id`` needs to be serialized, - because the :method:`Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider::refreshUser` - method reloads the user on each request by using the ``id``. + but it's probably a good idea. The ``id`` is the most important value + that needs to be serialized because the + :method:`Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider::refreshUser` + method reloads the user on each request by using the ``id``. In practice, + this means that the User object is reloaded from the database on each + request using the ``id`` from the serialized object. This makes sure + all of the User's data is fresh. + + Symfony also uses the ``username``, ``salt``, and ``password`` to verify + that the User has not changed between requests. Failing to serialize + these may cause you to be logged out on each request. If your User implements + :class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface`, + then instead of these properties being checked, your ``isEqualTo`` method + is simply called, and you can check whatever properties you want. Unless + you understand this, you probably *won't* need to implement this interface + or worry about it. Below is an export of the ``User`` table from MySQL with user ``admin`` and password ``admin`` (which has been encoded). For details on how to create diff --git a/cookbook/security/impersonating_user.rst b/cookbook/security/impersonating_user.rst new file mode 100644 index 00000000000..c3bd0b708c6 --- /dev/null +++ b/cookbook/security/impersonating_user.rst @@ -0,0 +1,136 @@ +.. index:: + single: Security; Impersonating User + +How to Impersonate a User +========================= + +Sometimes, it's useful to be able to switch from one user to another without +having to log out and log in again (for instance when you are debugging or trying +to understand a bug a user sees that you can't reproduce). This can be easily +done by activating the ``switch_user`` firewall listener: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + firewalls: + main: + # ... + switch_user: true + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'main'=> array( + // ... + 'switch_user' => true + ), + ), + )); + +To switch to another user, just add a query string with the ``_switch_user`` +parameter and the username as the value to the current URL: + +.. code-block:: text + + http://example.com/somewhere?_switch_user=thomas + +To switch back to the original user, use the special ``_exit`` username: + +.. code-block:: text + + http://example.com/somewhere?_switch_user=_exit + +During impersonation, the user is provided with a special role called +``ROLE_PREVIOUS_ADMIN``. In a template, for instance, this role can be used +to show a link to exit impersonation: + +.. configuration-block:: + + .. code-block:: html+jinja + + {% if is_granted('ROLE_PREVIOUS_ADMIN') %} + Exit impersonation + {% endif %} + + .. code-block:: html+php + + isGranted('ROLE_PREVIOUS_ADMIN')): ?> + + Exit impersonation + + + +Of course, this feature needs to be made available to a small group of users. +By default, access is restricted to users having the ``ROLE_ALLOWED_TO_SWITCH`` +role. The name of this role can be modified via the ``role`` setting. For +extra security, you can also change the query parameter name via the ``parameter`` +setting: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + firewalls: + main: + # ... + switch_user: { role: ROLE_ADMIN, parameter: _want_to_be_this_user } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'main'=> array( + // ... + 'switch_user' => array( + 'role' => 'ROLE_ADMIN', + 'parameter' => '_want_to_be_this_user', + ), + ), + ), + )); diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index e9cad89cb50..63bd29520b3 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -6,6 +6,7 @@ Security entity_provider remember_me + impersonating_user voters acl acl_advanced @@ -18,3 +19,4 @@ Security api_key_authentication custom_authentication_provider target_path + csrf_in_login_form diff --git a/cookbook/security/voters.rst b/cookbook/security/voters.rst index 0d4e526c742..8798bcefa06 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -5,10 +5,10 @@ How to implement your own Voter to blacklist IP Addresses ========================================================= The Symfony2 Security component provides several layers to authorize users. -One of the layers is called a `voter`. A voter is a dedicated class that checks -if the user has the rights to be connected to the application. For instance, -Symfony2 provides a layer that checks if the user is fully authorized or if -it has some expected roles. +One of the layers is called a "voter". A voter is a dedicated class that checks +if the user has the rights to connect to the application or access a specific +resource/URL. For instance, Symfony2 provides a layer that checks if the user +is fully authorized or if it has some expected roles. It is sometimes useful to create a custom voter to handle a specific case not handled by the framework. In this section, you'll learn how to create a voter @@ -34,18 +34,18 @@ The ``supportsAttribute()`` method is used to check if the voter supports the given user attribute (i.e: a role, an ACL, etc.). The ``supportsClass()`` method is used to check if the voter supports the -current user token class. +class of the object whose access is being checked (doesn't apply to this entry). The ``vote()`` method must implement the business logic that verifies whether or not the user is granted access. This method must return one of the following values: -* ``VoterInterface::ACCESS_GRANTED``: The user is allowed to access the application -* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if the user is granted or not -* ``VoterInterface::ACCESS_DENIED``: The user is not allowed to access the application +* ``VoterInterface::ACCESS_GRANTED``: The authorization will be granted by this voter; +* ``VoterInterface::ACCESS_ABSTAIN``: The voter cannot decide if authorization should be granted; +* ``VoterInterface::ACCESS_DENIED``: The authorization will be denied by this voter. In this example, you'll check if the user's IP address matches against a list of -blacklisted addresses. If the user's IP is blacklisted, you'll return +blacklisted addresses and "something" will be the application. If the user's IP is blacklisted, you'll return ``VoterInterface::ACCESS_DENIED``, otherwise you'll return ``VoterInterface::ACCESS_ABSTAIN`` as this voter's purpose is only to deny access, not to grant access. diff --git a/cookbook/session/locale_sticky_session.rst b/cookbook/session/locale_sticky_session.rst index 5f3da5a5977..172930869d7 100644 --- a/cookbook/session/locale_sticky_session.rst +++ b/cookbook/session/locale_sticky_session.rst @@ -100,4 +100,9 @@ use the :method:`Request::getLocale getRequest()->getLocale(); + use Symfony\Component\HttpFoundation\Request; + + public function indexAction(Request $request) + { + $locale = $request->getLocale(); + } diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index ce5f1331657..65e8e9e4da0 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -90,17 +90,22 @@ Getting information from the Request ------------------------------------ Besides the values of the routing placeholders, the controller also has access -to the ``Request`` object:: +to the ``Request`` object. The framework injects the ``Request`` object in the +controller if a variable is type hinted with +`Symfony\Component\HttpFoundation\Request`:: - $request = $this->getRequest(); + use Symfony\Component\HttpFoundation\Request; - $request->isXmlHttpRequest(); // is it an Ajax request? + public function indexAction(Request $request) + { + $request->isXmlHttpRequest(); // is it an Ajax request? - $request->getPreferredLanguage(array('en', 'fr')); + $request->getPreferredLanguage(array('en', 'fr')); - $request->query->get('page'); // get a $_GET parameter + $request->query->get('page'); // get a $_GET parameter - $request->request->get('page'); // get a $_POST parameter + $request->request->get('page'); // get a $_POST parameter + } In a template, you can also access the ``Request`` object via the ``app.request`` variable: @@ -122,16 +127,21 @@ by using native PHP sessions. Storing and retrieving information from the session can be easily achieved from any controller:: - $session = $this->getRequest()->getSession(); + use Symfony\Component\HttpFoundation\Request; - // store an attribute for reuse during a later user request - $session->set('foo', 'bar'); + public function indexAction(Request $request) + { + $session = $this->request->getSession(); - // in another controller for another request - $foo = $session->get('foo'); + // store an attribute for reuse during a later user request + $session->set('foo', 'bar'); - // use a default value if the key doesn't exist - $filters = $session->get('filters', array()); + // in another controller for another request + $foo = $session->get('foo'); + + // use a default value if the key doesn't exist + $filters = $session->get('filters', array()); + } You can also store small messages that will only be available for the very next request:: diff --git a/reference/configuration/monolog.rst b/reference/configuration/monolog.rst index 24365fbd682..1b09cbc020c 100644 --- a/reference/configuration/monolog.rst +++ b/reference/configuration/monolog.rst @@ -25,6 +25,13 @@ MonologBundle Configuration ("monolog") action_level: WARNING buffer_size: 30 handler: custom + console: + type: console + verbosity_levels: + VERBOSITY_NORMAL: WARNING + VERBOSITY_VERBOSE: NOTICE + VERBOSITY_VERY_VERBOSE: INFO + VERBOSITY_DEBUG: DEBUG custom: type: service id: my_handler @@ -84,6 +91,10 @@ MonologBundle Configuration ("monolog") action-level="warning" handler="custom" /> + ` type: These options inherit from the :doc:`form ` type: +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/invalid_message.rst.inc .. include:: /reference/forms/types/options/invalid_message_parameters.rst.inc diff --git a/reference/forms/types/button.rst b/reference/forms/types/button.rst index 6e8cce6ed57..5c73b5f34e1 100644 --- a/reference/forms/types/button.rst +++ b/reference/forms/types/button.rst @@ -15,6 +15,7 @@ A simple, non-responsive button. | Options | - `attr`_ | | | - `disabled`_ | | | - `label`_ | +| | - `label_attr`_ | | | - `translation_domain`_ | +----------------------+----------------------------------------------------------------------+ | Parent type | none | @@ -31,4 +32,6 @@ Options .. include:: /reference/forms/types/options/button_label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + .. include:: /reference/forms/types/options/button_translation_domain.rst.inc diff --git a/reference/forms/types/checkbox.rst b/reference/forms/types/checkbox.rst index ddefd70ee06..adb6f8d1b24 100644 --- a/reference/forms/types/checkbox.rst +++ b/reference/forms/types/checkbox.rst @@ -16,6 +16,7 @@ if the box is unchecked, the value will be set to false. | Inherited | - `data`_ | | options | - `required`_ | | | - `label`_ | +| | - `label_attr`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `error_bubbling`_ | @@ -50,7 +51,7 @@ not affect the value that's set on your object. .. caution:: - To make a checkbox checked by default, use the `data`_ option. + To make a checkbox checked by default, set the `data`_ option to ``true``. Inherited options ----------------- @@ -63,6 +64,8 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index 551db27c551..b69e48c32ed 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -22,6 +22,8 @@ option. +-------------+------------------------------------------------------------------------------+ | Inherited | - `required`_ | | options | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `error_bubbling`_ | @@ -116,6 +118,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index e5df85ec0f9..8c5adb11a1f 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -21,6 +21,7 @@ forms, which is useful when creating forms that expose one-to-many relationships | | - `prototype_name`_ | +-------------+-----------------------------------------------------------------------------+ | Inherited | - `label`_ | +| | - `label_attr`_ | | options | - `error_bubbling`_ | | | - `error_mapping`_ | | | - `by_reference`_ | @@ -341,9 +342,7 @@ Not all options are listed here - only the most applicable to this type: .. include:: /reference/forms/types/options/label.rst.inc -.. include:: /reference/forms/types/options/mapped.rst.inc - -.. include:: /reference/forms/types/options/error_mapping.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc error_bubbling ~~~~~~~~~~~~~~ @@ -352,8 +351,12 @@ error_bubbling .. include:: /reference/forms/types/options/_error_bubbling_body.rst.inc +.. include:: /reference/forms/types/options/error_mapping.rst.inc + .. _reference-form-types-by-reference: .. include:: /reference/forms/types/options/by_reference.rst.inc .. include:: /reference/forms/types/options/empty_data.rst.inc + +.. include:: /reference/forms/types/options/mapped.rst.inc diff --git a/reference/forms/types/country.rst b/reference/forms/types/country.rst index 8d60cb91d98..ceb1f8385d8 100644 --- a/reference/forms/types/country.rst +++ b/reference/forms/types/country.rst @@ -33,6 +33,8 @@ you should just use the ``choice`` type directly. | | - `error_mapping`_ | | | - `required`_ | | | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `mapped`_ | @@ -76,6 +78,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/currency.rst b/reference/forms/types/currency.rst index d921ce09a1b..b92d1a3e4d5 100644 --- a/reference/forms/types/currency.rst +++ b/reference/forms/types/currency.rst @@ -26,6 +26,8 @@ should just use the ``choice`` type directly. | | - `error_bubbling`_ | | | - `required`_ | | | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `mapped`_ | @@ -60,12 +62,16 @@ These options inherit from the :doc:`choice` type .. include:: /reference/forms/types/options/error_bubbling.rst.inc -These options inherit from the :doc:`date` type: +These options inherit from the :doc:`form` type: .. include:: /reference/forms/types/options/required.rst.inc .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index c2fe15c3c07..4e5d284e7cd 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -32,8 +32,9 @@ day, and year) or three select boxes (see the `widget`_ option). | Overridden Options | - `by_reference`_ | | | - `error_bubbling`_ | +----------------------+-----------------------------------------------------------------------------+ -| Inherited | - `invalid_message`_ | -| options | - `invalid_message_parameters`_ | +| Inherited | - `data`_ | +| options | - `invalid_message`_ | +| | - `invalid_message_parameters`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `mapped`_ | @@ -138,6 +139,8 @@ Inherited options These options inherit from the :doc:`form ` type: +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/invalid_message.rst.inc .. include:: /reference/forms/types/options/invalid_message_parameters.rst.inc diff --git a/reference/forms/types/datetime.rst b/reference/forms/types/datetime.rst index 1b59ddff52e..e607655f03e 100644 --- a/reference/forms/types/datetime.rst +++ b/reference/forms/types/datetime.rst @@ -31,8 +31,9 @@ data can be a ``DateTime`` object, a string, a timestamp or an array. | | - `view_timezone`_ | | | - `empty_value`_ | +----------------------+-----------------------------------------------------------------------------+ -| Inherited | - `invalid_message`_ | -| options | - `invalid_message_parameters`_ | +| Inherited | - `data`_ | +| options | - `invalid_message`_ | +| | - `invalid_message_parameters`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `mapped`_ | @@ -121,6 +122,8 @@ Inherited options These options inherit from the :doc:`form ` type: +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/invalid_message.rst.inc .. include:: /reference/forms/types/options/invalid_message_parameters.rst.inc diff --git a/reference/forms/types/email.rst b/reference/forms/types/email.rst index fb6ca9a2868..1ba1ec68d95 100644 --- a/reference/forms/types/email.rst +++ b/reference/forms/types/email.rst @@ -13,6 +13,8 @@ The ``email`` field is a text field that is rendered using the HTML5 | Inherited | - `max_length`_ | | options | - `required`_ | | | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `trim`_ | | | - `read_only`_ | | | - `disabled`_ | @@ -36,6 +38,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/trim.rst.inc .. include:: /reference/forms/types/options/read_only.rst.inc diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index ec1b2075320..69c2537e977 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -21,12 +21,14 @@ objects from the database. | Overridden | - `choices`_ | | Options | - `choice_list`_ | +-------------+------------------------------------------------------------------+ -| Inherited | - `required`_ | -| options | - `label`_ | -| | - `multiple`_ | -| | - `expanded`_ | +| Inherited | - `multiple`_ | +| options | - `expanded`_ | | | - `preferred_choices`_ | | | - `empty_value`_ | +| | - `required`_ | +| | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `error_bubbling`_ | @@ -195,6 +197,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index a33dae8d992..19abd57d0ae 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -11,6 +11,7 @@ The ``file`` type represents a file input in your form. +-------------+---------------------------------------------------------------------+ | Inherited | - `required`_ | | options | - `label`_ | +| | - `label_attr`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `error_bubbling`_ | @@ -85,6 +86,8 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/form.rst b/reference/forms/types/form.rst index b51fe597078..1357650086f 100644 --- a/reference/forms/types/form.rst +++ b/reference/forms/types/form.rst @@ -9,10 +9,16 @@ See :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\FormType`. The ``form`` type predefines a couple of options that are then available on all fields. +.. include:: /reference/forms/types/options/compound.rst.inc + .. include:: /reference/forms/types/options/data.rst.inc .. include:: /reference/forms/types/options/required.rst.inc +.. include:: /reference/forms/types/options/label.rst.inc + +.. include:: /reference/forms/types/options/label_attr.rst.inc + .. include:: /reference/forms/types/options/constraints.rst.inc .. include:: /reference/forms/types/options/cascade_validation.rst.inc diff --git a/reference/forms/types/integer.rst b/reference/forms/types/integer.rst index fef0802fb7b..ba4a0760b25 100644 --- a/reference/forms/types/integer.rst +++ b/reference/forms/types/integer.rst @@ -21,6 +21,8 @@ integers. By default, all non-integer values (e.g. 6.78) will round down (e.g. 6 +-------------+-----------------------------------------------------------------------+ | Inherited | - `required`_ | | options | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `error_bubbling`_ | @@ -79,6 +81,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/language.rst b/reference/forms/types/language.rst index dc83dd0d903..8205045e6e4 100644 --- a/reference/forms/types/language.rst +++ b/reference/forms/types/language.rst @@ -34,6 +34,8 @@ you should just use the ``choice`` type directly. | | - `error_mapping`_ | | | - `required`_ | | | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `mapped`_ | @@ -77,6 +79,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/locale.rst b/reference/forms/types/locale.rst index 6a5378c87dc..4d5dd32d621 100644 --- a/reference/forms/types/locale.rst +++ b/reference/forms/types/locale.rst @@ -36,6 +36,8 @@ you should just use the ``choice`` type directly. | | - `error_mapping`_ | | | - `required`_ | | | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `mapped`_ | @@ -79,6 +81,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/money.rst b/reference/forms/types/money.rst index 06dfdb7c2c1..5e143c93570 100644 --- a/reference/forms/types/money.rst +++ b/reference/forms/types/money.rst @@ -21,6 +21,8 @@ how the input and output of the data is handled. +-------------+---------------------------------------------------------------------+ | Inherited | - `required`_ | | options | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `error_bubbling`_ | @@ -89,6 +91,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/number.rst b/reference/forms/types/number.rst index dc5a113db10..74d35a4f61a 100644 --- a/reference/forms/types/number.rst +++ b/reference/forms/types/number.rst @@ -17,6 +17,8 @@ you want to use for your number. +-------------+----------------------------------------------------------------------+ | Inherited | - `required`_ | | options | - `label`_ | +| | - `label_attr`_ | +| | - `data`_ | | | - `read_only`_ | | | - `disabled`_ | | | - `error_bubbling`_ | @@ -75,6 +77,10 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_attr.rst.inc + +.. include:: /reference/forms/types/options/data.rst.inc + .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/disabled.rst.inc diff --git a/reference/forms/types/options/_error_bubbling_hint.rst b/reference/forms/types/options/_error_bubbling_hint.rst new file mode 100644 index 00000000000..743da6f8d0e --- /dev/null +++ b/reference/forms/types/options/_error_bubbling_hint.rst @@ -0,0 +1,7 @@ +.. tip:: + + By default the ``error_bubbling`` option is enabled for the + :doc:`collection Field Type `, + which passes the errors to the parent form. If you want to attach + the errors to the locations where they actually occur you have to + set ``error_bubbling`` to ``false``. diff --git a/reference/forms/types/options/cascade_validation.rst.inc b/reference/forms/types/options/cascade_validation.rst.inc index 60d1bd50593..f110e9087b2 100644 --- a/reference/forms/types/options/cascade_validation.rst.inc +++ b/reference/forms/types/options/cascade_validation.rst.inc @@ -11,6 +11,5 @@ the data from ``CategoryType`` to also be validated. Instead of using this option, you can also use the ``Valid`` constraint in your model to force validation on a child object stored on a property. - - +.. include:: /reference/forms/types/options/_error_bubbling_hint.rst.inc diff --git a/reference/forms/types/options/compound.rst.inc b/reference/forms/types/options/compound.rst.inc new file mode 100644 index 00000000000..d8b07f65315 --- /dev/null +++ b/reference/forms/types/options/compound.rst.inc @@ -0,0 +1,8 @@ +compound +~~~~~~~~ + +**type**: ``boolean`` + +This option specifies if a form is compound. This is independent of whether the +form actually has children. A form can be compound but not have any children +at all (e.g. an empty collection form). diff --git a/reference/forms/types/options/data.rst.inc b/reference/forms/types/options/data.rst.inc index 8b4b32c44be..ee92e82a2d3 100644 --- a/reference/forms/types/options/data.rst.inc +++ b/reference/forms/types/options/data.rst.inc @@ -12,4 +12,8 @@ an individual field, you can set it in the data option:: 'data' => 'abcdef', )); +.. note:: + The default values for form fields are taken directly from the + underlying data structure (e.g. an entity or an array). + The ``data`` option overrides this default value. diff --git a/reference/forms/types/options/label_attr.rst.inc b/reference/forms/types/options/label_attr.rst.inc new file mode 100644 index 00000000000..de834c4ae1b --- /dev/null +++ b/reference/forms/types/options/label_attr.rst.inc @@ -0,0 +1,22 @@ +label_attr +~~~~~ + +**type**: ``array`` **default**: ``array()`` + +Sets the html attributes for the ``