diff --git a/book/controller.rst b/book/controller.rst index 661d7838dab..167ffbecaaa 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -392,8 +392,13 @@ itself. Extending the base class is *optional* in Symfony; it contains useful shortcuts but nothing mandatory. You can also extend - :class:`Symfony\\Component\\DependencyInjection\\ContainerAware`. The service - container object will then be accessible via the ``container`` property. + :class:`Symfony\\Component\\DependencyInjection\\ContainerAware` or use + the class:`Symfony\\Component\\DependencyInjection\\ContainerAwareTrait` trait + (if you have PHP 5.4). The service container object will then be accessible + via the ``container`` property. + +.. versionadded:: 2.4 + The ``ContainerAwareTrait`` is new in Symfony 2.4. .. note:: @@ -750,12 +755,15 @@ headers and content that's sent back to the client:: use Symfony\Component\HttpFoundation\Response; // create a simple Response with a 200 status code (the default) - $response = new Response('Hello '.$name, 200); + $response = new Response('Hello '.$name, Response::HTTP_OK); // create a JSON-response with a 200 status code $response = new Response(json_encode(array('name' => $name))); $response->headers->set('Content-Type', 'application/json'); +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + .. tip:: The ``headers`` property is a diff --git a/book/doctrine.rst b/book/doctrine.rst index 026a3b4f977..8fc6e72dc6e 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -728,65 +728,18 @@ a controller, do the following:: $products = $query->getResult(); +The ``getResult()`` method returns an array of results. + If you're comfortable with SQL, then DQL should feel very natural. The biggest difference is that you need to think in terms of "objects" instead of rows -in a database. For this reason, you select *from* ``AcmeStoreBundle:Product`` -and then alias it as ``p``. - -The ``getResult()`` method returns an array of results. If you're querying -for just one object, you can use the ``getSingleResult()`` method instead:: - - $product = $query->getSingleResult(); - -.. caution:: - - The ``getSingleResult()`` method throws a ``Doctrine\ORM\NoResultException`` - exception if no results are returned and a ``Doctrine\ORM\NonUniqueResultException`` - if *more* than one result is returned. If you use this method, you may - need to wrap it in a try-catch block and ensure that only one result is - returned (if you're querying on something that could feasibly return - more than one result):: - - $query = $em->createQuery('SELECT ...') - ->setMaxResults(1); - - try { - $product = $query->getSingleResult(); - } catch (\Doctrine\Orm\NoResultException $e) { - $product = null; - } - // ... +in a database. For this reason, you select *from* the ``AcmeStoreBundle:Product`` +*object* and then alias it as ``p``. The DQL syntax is incredibly powerful, allowing you to easily join between entities (the topic of :ref:`relations ` will be covered later), group, etc. For more information, see the official Doctrine `Doctrine Query Language`_ documentation. -.. sidebar:: Setting Parameters - - Take note of the ``setParameter()`` method. When working with Doctrine, - it's always a good idea to set any external values as "placeholders", - which was done in the above query: - - .. code-block:: text - - ... WHERE p.price > :price ... - - You can then set the value of the ``price`` placeholder by calling the - ``setParameter()`` method:: - - ->setParameter('price', '19.99') - - Using parameters instead of placing values directly in the query string - is done to prevent SQL injection attacks and should *always* be done. - If you're using multiple parameters, you can set their values at once - using the ``setParameters()`` method:: - - ->setParameters(array( - 'price' => '19.99', - 'name' => 'Foo', - )) - Using Doctrine's Query Builder ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -819,10 +772,10 @@ Custom Repository Classes In the previous sections, you began constructing and using more complex queries from inside a controller. In order to isolate, test and reuse these queries, -it's a good idea to create a custom repository class for your entity and +it's a good practise to create a custom repository class for your entity and add methods with your query logic there. -To do this, add the name of the repository class to your mapping definition. +To do this, add the name of the repository class to your mapping definition: .. configuration-block:: @@ -1300,7 +1253,7 @@ Configuration Doctrine is highly configurable, though you probably won't ever need to worry about most of its options. To find out more about configuring Doctrine, see -the Doctrine section of the :doc:`reference manual `. +the Doctrine section of the :doc:`config reference `. Lifecycle Callbacks ------------------- @@ -1312,7 +1265,7 @@ stages of the lifecycle of an entity (e.g. the entity is inserted, updated, deleted, etc). If you're using annotations for your metadata, start by enabling the lifecycle -callbacks. This is not necessary if you're using YAML or XML for your mapping: +callbacks. This is not necessary if you're using YAML or XML for your mapping. .. code-block:: php-annotations @@ -1375,20 +1328,8 @@ the current date, only when the entity is first persisted (i.e. inserted): Now, right before the entity is first persisted, Doctrine will automatically call this method and the ``createdAt`` field will be set to the current date. - -This can be repeated for any of the other lifecycle events, which include: - -* ``preRemove`` -* ``postRemove`` -* ``prePersist`` -* ``postPersist`` -* ``preUpdate`` -* ``postUpdate`` -* ``postLoad`` -* ``loadClassMetadata`` - -For more information on what these lifecycle events mean and lifecycle callbacks -in general, see Doctrine's `Lifecycle Events documentation`_ +For more information on other lifecycle events and lifecycle callbacks in +general, see Doctrine's `Lifecycle Events documentation`_. .. sidebar:: Lifecycle Callbacks and Event Listeners @@ -1403,17 +1344,6 @@ in general, see Doctrine's `Lifecycle Events documentation`_ or subscriber and give it access to whatever resources you need. For more information, see :doc:`/cookbook/doctrine/event_listeners_subscribers`. -Doctrine Extensions: Timestampable, Sluggable, etc. ---------------------------------------------------- - -Doctrine is quite flexible, and a number of third-party extensions are available -that allow you to easily perform repeated and common tasks on your entities. -These include thing such as *Sluggable*, *Timestampable*, *Loggable*, *Translatable*, -and *Tree*. - -For more information on how to find and use these extensions, see the cookbook -article about :doc:`using common Doctrine extensions `. - .. _book-doctrine-field-types: Doctrine Field Types Reference @@ -1421,167 +1351,8 @@ Doctrine Field Types Reference Doctrine comes with a large number of field types available. Each of these maps a PHP data type to a specific column type in whatever database you're -using. The following types are supported in Doctrine: - -* **Strings** - - * ``string`` (used for shorter strings) - * ``text`` (used for larger strings) - -* **Numbers** - - * ``integer`` - * ``smallint`` - * ``bigint`` - * ``decimal`` - * ``float`` - -* **Dates and Times** (use a `DateTime`_ object for these fields in PHP) - - * ``date`` - * ``time`` - * ``datetime`` - * ``datetimetz`` - -* **Other Types** - - * ``boolean`` - * ``object`` (serialized and stored in a ``CLOB`` field) - * ``array`` (serialized and stored in a ``CLOB`` field) - * ``blob`` (mapped to a resource stream) - * ``simple_array`` (serialized using :phpfunction:`implode()` and :phpfunction:`explode()`, - with a comma as delimiter, and stored in a ``CLOB`` field) - * ``json_array`` (serialized using :phpfunction:`json_encode()` and :phpfunction:`json_decode()`, - and stored in a ``CLOB`` field) - * ``guid`` - -For more information, see Doctrine's `Mapping Types documentation`_. - -Field Options -~~~~~~~~~~~~~ - -Each field can have a set of options applied to it. The available options -include ``type`` (defaults to ``string``), ``name``, ``length``, ``unique`` -and ``nullable``. Take a few examples: - -.. configuration-block:: - - .. code-block:: php-annotations - - /** - * A string field with length 255 that cannot be null - * (reflecting the default values for the "type", "length" - * and *nullable* options) - * - * @ORM\Column() - */ - protected $name; - - /** - * A string field of length 150 that persists to an "email_address" column - * and has a unique index. - * - * @ORM\Column(name="email_address", unique=true, length=150) - */ - protected $email; - - .. code-block:: yaml - - fields: - # A string field length 255 that cannot be null - # (reflecting the default values for the "length" and *nullable* options) - # type attribute is necessary in YAML definitions - name: - type: string - - # A string field of length 150 that persists to an "email_address" column - # and has a unique index. - email: - type: string - column: email_address - length: 150 - unique: true - - .. code-block:: xml - - - - - -.. note:: - - There are a few more options not listed here. For more details, see - Doctrine's `Property Mapping documentation`_ - -.. index:: - single: Doctrine; ORM console commands - single: CLI; Doctrine ORM - -Console Commands ----------------- - -The Doctrine2 ORM integration offers several console commands under the -``doctrine`` namespace. To view the command list you can run the console -without any arguments: - -.. code-block:: bash - - $ php app/console - -A list of available commands will print out, many of which start with the -``doctrine:`` prefix. You can find out more information about any of these -commands (or any Symfony command) by running the ``help`` command. For example, -to get details about the ``doctrine:database:create`` task, run: - -.. code-block:: bash - - $ php app/console help doctrine:database:create - -Some notable or interesting tasks include: - -* ``doctrine:ensure-production-settings`` - checks to see if the current - environment is configured efficiently for production. This should always - be run in the ``prod`` environment: - - .. code-block:: bash - - $ php app/console doctrine:ensure-production-settings --env=prod - -* ``doctrine:mapping:import`` - allows Doctrine to introspect an existing - database and create mapping information. For more information, see - :doc:`/cookbook/doctrine/reverse_engineering`. - -* ``doctrine:mapping:info`` - tells you all of the entities that Doctrine - is aware of and whether or not there are any basic errors with the mapping. - -* ``doctrine:query:dql`` and ``doctrine:query:sql`` - allow you to execute - DQL or SQL queries directly from the command line. - -.. note:: - - To be able to load data fixtures to your database, you will need to have - the DoctrineFixturesBundle bundle installed. To learn how to do it, - read the ":doc:`/bundles/DoctrineFixturesBundle/index`" entry of the - documentation. - -.. tip:: - - This page shows working with Doctrine within a controller. You may also - want to work with Doctrine elsewhere in your application. The - :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::getDoctrine` - method of the controller returns the ``doctrine`` service, you can work with - this in the same way elsewhere by injecting this into your own - services. See :doc:`/book/service_container` for more on creating - your own services. +using. To see a list of all available types and more information, see +Doctrine's `Mapping Types documentation`_. Summary ------- @@ -1598,10 +1369,12 @@ that allow you to take different actions as objects go through their persistence lifecycle. For more information about Doctrine, see the *Doctrine* section of the -:doc:`cookbook `, which includes the following articles: +:doc:`cookbook `. Some usefull articles might be: -* :doc:`/bundles/DoctrineFixturesBundle/index` * :doc:`/cookbook/doctrine/common_extensions` +* :doc:`/cookbook/doctrine/console` +* :doc:`/bundles/DoctrineFixturesBundle/index` +* :doc:`/bundles/DoctrineMongoDBBundle/index` .. _`Doctrine`: http://www.doctrine-project.org/ .. _`MongoDB`: http://www.mongodb.org/ @@ -1609,10 +1382,8 @@ For more information about Doctrine, see the *Doctrine* section of the .. _`Query Builder`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html .. _`Doctrine Query Language`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html .. _`Association Mapping Documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html -.. _`DateTime`: http://php.net/manual/en/class.datetime.php .. _`Mapping Types Documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#doctrine-mapping-types -.. _`Property Mapping documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#property-mapping +.. _`Property Mapping`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#property-mapping .. _`Lifecycle Events documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events .. _`Reserved SQL keywords documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words .. _`Persistent classes`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#persistent-classes -.. _`Property Mapping`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#property-mapping diff --git a/book/forms.rst b/book/forms.rst index 2bdef6801a4..5fc28cc1368 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -1831,11 +1831,6 @@ The answer is to setup the constraints yourself, and attach them to the individu fields. The overall approach is covered a bit more in the :ref:`validation chapter `, but here's a short example: -.. versionadded:: 2.1 - The ``constraints`` option, which accepts a single constraint or an array - of constraints (before 2.1, the option was called ``validation_constraint``, - and only accepted a single constraint) is new to Symfony 2.1. - .. code-block:: php use Symfony\Component\Validator\Constraints\Length; @@ -1893,7 +1888,7 @@ Learn more from the Cookbook .. _`Symfony2 Form component`: https://github.com/symfony/Form .. _`DateTime`: http://php.net/manual/en/class.datetime.php -.. _`Twig Bridge`: https://github.com/symfony/symfony/tree/2.2/src/Symfony/Bridge/Twig -.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/2.2/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +.. _`Twig Bridge`: https://github.com/symfony/symfony/tree/master/src/Symfony/Bridge/Twig +.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig .. _`Cross-site request forgery`: http://en.wikipedia.org/wiki/Cross-site_request_forgery -.. _`view on GitHub`: https://github.com/symfony/symfony/tree/2.2/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form +.. _`view on GitHub`: https://github.com/symfony/symfony/tree/master/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index f593f81b3cc..756bf91ac75 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -433,7 +433,7 @@ content: { "require": { - "symfony/symfony": "2.3.*" + "symfony/symfony": "2.4.*" }, "autoload": { "files": ["model.php","controllers.php"] @@ -476,12 +476,15 @@ the HTTP response being returned. Use them to improve the blog: $response = show_action($request->query->get('id')); } else { $html = '

Page Not Found

'; - $response = new Response($html, 404); + $response = new Response($html, Response::HTTP_NOT_FOUND); } // echo the headers and send the response $response->send(); +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + The controllers are now responsible for returning a ``Response`` object. To make this easier, you can add a new ``render_template()`` function, which, incidentally, acts quite a bit like the Symfony2 templating engine: diff --git a/book/http_cache.rst b/book/http_cache.rst index ffb8a1f6636..eae2857dc9f 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -1067,15 +1067,18 @@ Here is how you can configure the Symfony2 reverse proxy to support the $response = new Response(); if ($this->getStore()->purge($request->getUri())) { - $response->setStatusCode(200, 'Purged'); + $response->setStatusCode(Response::HTTP_OK, 'Purged'); } else { - $response->setStatusCode(404, 'Not purged'); + $response->setStatusCode(Response::HTTP_NOT_FOUND, 'Not purged'); } return $response; } } +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + .. caution:: You must protect the ``PURGE`` HTTP method somehow to avoid random people diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index c0420ba0081..72657151dd5 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -278,12 +278,15 @@ interface to construct the response that needs to be returned to the client:: $response = new Response(); $response->setContent('

Hello world!

'); - $response->setStatusCode(200); + $response->setStatusCode(Response::HTTP_OK); $response->headers->set('Content-Type', 'text/html'); // prints the HTTP headers followed by the content $response->send(); +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + If Symfony offered nothing else, you would already have a toolkit for easily accessing request information and an object-oriented interface for creating the response. Even as you learn the many powerful features in Symfony, keep @@ -364,6 +367,7 @@ on that value. This can get ugly quickly:: // index.php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; + $request = Request::createFromGlobals(); $path = $request->getPathInfo(); // the URI path being requested @@ -372,7 +376,7 @@ on that value. This can get ugly quickly:: } elseif ($path == '/contact') { $response = new Response('Contact us'); } else { - $response = new Response('Page not found.', 404); + $response = new Response('Page not found.', Response::HTTP_NOT_FOUND); } $response->send(); diff --git a/book/includes/_service_container_my_mailer.rst.inc b/book/includes/_service_container_my_mailer.rst.inc new file mode 100644 index 00000000000..efc2eaeac26 --- /dev/null +++ b/book/includes/_service_container_my_mailer.rst.inc @@ -0,0 +1,36 @@ +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + my_mailer: + class: Acme\HelloBundle\Mailer + arguments: [sendmail] + + .. code-block:: xml + + + + + + + + sendmail + + + + + .. code-block:: php + + // app/config/config.php + use Symfony\Component\DependencyInjection\Definition; + + $container->setDefinition('my_mailer', new Definition( + 'Acme\HelloBundle\Mailer', + array('sendmail') + )); \ No newline at end of file diff --git a/book/installation.rst b/book/installation.rst index 52a5f89ed01..3604f1236c5 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -57,7 +57,7 @@ Distribution: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony 2.3.* + $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony 2.4.* .. tip:: @@ -104,10 +104,10 @@ one of the following commands (replacing ``###`` with your actual filename): .. code-block:: bash # for .tgz file - $ tar zxvf Symfony_Standard_Vendors_2.3.###.tgz + $ tar zxvf Symfony_Standard_Vendors_2.4.###.tgz # for a .zip file - $ unzip Symfony_Standard_Vendors_2.3.###.zip + $ unzip Symfony_Standard_Vendors_2.4.###.zip If you've downloaded "without vendors", you'll definitely need to read the next section. diff --git a/book/internals.rst b/book/internals.rst index f7467a58d7e..40ecc7bba07 100644 --- a/book/internals.rst +++ b/book/internals.rst @@ -417,10 +417,13 @@ and set a new ``Exception`` object, or do nothing:: return new Response( 'Error', - 404 // ignored, - array('X-Status-Code' => 200) + Response::HTTP_NOT_FOUND, // ignored + array('X-Status-Code' => Response::HTTP_OK) ); + .. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + .. seealso:: Read more on the :ref:`kernel.exception event `. diff --git a/book/routing.rst b/book/routing.rst index b280bec1303..8091d3e668a 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -68,10 +68,6 @@ The route is simple: return $collection; -.. versionadded:: 2.2 - The ``path`` option is new in Symfony2.2, ``pattern`` is used in older - versions. - The path defined by the ``blog_show`` route acts like ``/blog/*`` where the wildcard is given the name ``slug``. For the URL ``/blog/my-blog-post``, the ``slug`` variable gets a value of ``my-blog-post``, which is available @@ -507,10 +503,11 @@ to the ``{page}`` parameter. | /blog/my-blog-post | blog | {page} = my-blog-post | +--------------------+-------+-----------------------+ -The answer to the problem is to add route *requirements*. The routes in this -example would work perfectly if the ``/blog/{page}`` path *only* matched -URLs where the ``{page}`` portion is an integer. Fortunately, regular expression -requirements can easily be added for each parameter. For example: +The answer to the problem is to add route *requirements* or route *conditions* +(see :ref:`book-routing-conditions`). The routes in this example would work +perfectly if the ``/blog/{page}`` path *only* matched URLs where the ``{page}`` +portion is an integer. Fortunately, regular expression requirements can easily +be added for each parameter. For example: .. configuration-block:: @@ -694,10 +691,6 @@ be accomplished with the following route configuration: return $collection; -.. versionadded:: 2.2 - The ``methods`` option is added in Symfony2.2. Use the ``_method`` - requirement in older versions. - Despite the fact that these two routes have identical paths (``/contact``), the first route will match only GET requests and the second route will match only POST requests. This means that you can display the form and submit the @@ -710,13 +703,99 @@ form via the same URL, while using distinct controllers for the two actions. Adding a Host Requirement ~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - Host matching support was added in Symfony 2.2 - You can also match on the HTTP *host* of the incoming request. For more information, see :doc:`/components/routing/hostname_pattern` in the Routing component documentation. +.. _book-routing-conditions: + +Completely Customized Route Matching with Conditions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.4 + Route conditions were introduced in Symfony 2.4. + +As you've seen, a route can be made to match only certain routing wildcards +(via regular expressions), HTTP methods, or host names. But the routing system +can be extended to have an almost infinite flexibility using ``conditions``: + +.. configuration-block:: + + .. code-block:: yaml + + contact: + path: /contact + defaults: { _controller: AcmeDemoBundle:Main:contact } + condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'" + + .. code-block:: xml + + + + + + AcmeDemoBundle:Main:contact + + + + .. code-block:: php + + use Symfony\Component\Routing\RouteCollection; + use Symfony\Component\Routing\Route; + + $collection = new RouteCollection(); + $collection->add('contact', new Route( + '/contact', array( + '_controller' => 'AcmeDemoBundle:Main:contact', + ), + array(), + array(), + '', + array(), + array(), + 'context.getMethod() in ["GET", "HEAD"] and request.headers.get("User-Agent") matches "/firefox/i"' + )); + + return $collection; + +The ``condition`` is an expression, and you can learn more about its syntax +here: :doc:`/components/expression_language/syntax`. With this, the route +won't match unless the HTTP method is either GET or HEAD *and* if the ``User-Agent`` +header matches ``firefox``. + +You can do any complex logic you need in the expression by leveraging two +variables that are passed into the expression: + +* ``context``: An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, + which holds the most fundamental information about the route being matched; +* ``request``: The Symfony :class:`Symfony\\Component\\HttpFoundation\\Request` + object (see :ref:`component-http-foundation-request`). + +.. caution:: + + Conditions are *not* taken into account when generating a URL. + +.. sidebar:: Expressions are Compiled to PHP + + Behind the scenes, expressions are compiled down to raw PHP. Our example + would generate the following PHP in the cache directory:: + + if (rtrim($pathinfo, '/contact') === '' && ( + in_array($context->getMethod(), array(0 => "GET", 1 => "HEAD")) + && preg_match("/firefox/i", $request->headers->get("User-Agent")) + )) { + // ... + } + + Because of this, using the ``condition`` key causes no extra overhead + beyond the time it takes for the underlying PHP to execute. + .. index:: single: Routing; Advanced example single: Routing; _format parameter @@ -1070,9 +1149,6 @@ from the new routing resource. Adding a Host requirement to Imported Routes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - Host matching support was added in Symfony 2.2 - You can set the host regex on imported routes. For more information, see :ref:`component-routing-host-imported`. diff --git a/book/security.rst b/book/security.rst index 12a50e35e63..edf3172febd 100644 --- a/book/security.rst +++ b/book/security.rst @@ -412,12 +412,8 @@ submission (i.e. ``/login_check``): You will *not* need to implement a controller for the ``/login_check`` URL as the firewall will automatically catch and process any form submitted - to this URL. - -.. versionadded:: 2.1 - As of Symfony 2.1, you *must* have routes configured for your ``login_path`` - and ``check_path``. These keys can be route names (as shown in this example) - or URLs that have routes configured for them. + to this URL. However, you *must* have a route (as shown here) for this + URL, as well as one for your logout path (see :ref:`book-security-logging-out`). Notice that the name of the ``login`` route matches the ``login_path`` config value, as that's where the security system will redirect users that need @@ -880,6 +876,8 @@ options: (internally, an :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` is thrown); +* ``allow_if`` If the expression returns false, then access is denied; + * ``requires_channel`` If the incoming request's channel (e.g. ``http``) does not match this value (e.g. ``https``), the user will be redirected (e.g. redirected from ``http`` to ``https``, or vice versa). @@ -975,6 +973,58 @@ address): * The second access rule is not examined as the first rule matched. +.. _book-security-allow-if: + +Securing by an Expression +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.4 + The ``allow_if`` functionality was introduced in Symfony 2.4. + +Once an ``access_control`` entry is matched, you can deny access via the +``roles`` key or use more complex logic with an expression in the ``allow_if`` +key: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + access_control: + - + path: ^/_internal/secure + allow_if: "'127.0.0.1' == request.getClientIp() or has_role('ROLE_ADMIN')" + + .. code-block:: xml + + + + + + .. code-block:: php + + 'access_control' => array( + array( + 'path' => '^/_internal/secure', + 'allow_if' => '"127.0.0.1" == request.getClientIp() or has_role("ROLE_ADMIN")', + ), + ), + +In this case, when the user tries to access any URL starting with ``/_internal/secure``, +they will only be granted access if the IP address is ``127.0.0.1`` or if +the user has the ``ROLE_ADMIN`` role. + +Inside the expression, you have access to a number of different variables +and functions including ``request``, which is the Symfony +:class:`Symfony\\Component\\HttpFoundation\\Request` object (see +:ref:`component-http-foundation-request`). + +For a list of the other functions and variables, see +:ref:`functions and variables `. + .. _book-security-securing-channel: Forcing a Channel (http, https) @@ -1349,10 +1399,6 @@ it as base64. In other words, the password has been greatly obfuscated so that the hashed password can't be decoded (i.e. you can't determine the password from the hashed password). -.. versionadded:: 2.2 - As of Symfony 2.2 you can also use the :ref:`PBKDF2 ` - and :ref:`BCrypt ` password encoders. - Determining the Hashed Password ............................... @@ -1567,6 +1613,8 @@ doesn't need to be defined anywhere - you can just start using it. Symfony2. If you define your own roles with a dedicated ``Role`` class (more advanced), don't use the ``ROLE_`` prefix. +.. _book-security-role-hierarchy: + Hierarchical Roles ~~~~~~~~~~~~~~~~~~ @@ -1641,22 +1689,91 @@ authorization from inside a controller:: 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-securing-controller-annotations: +.. _book-security-expressions: + +Complex Access Controls with Expressions +---------------------------------------- -You can also choose to install and use the optional `JMSSecurityExtraBundle`_, -which can secure your controller using annotations:: +.. versionadded:: 2.4 + The expression functionality was introduced in Symfony 2.4. +In addition to a role like ``ROLE_ADMIN``, the ``isGranted`` method also +accepts an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` object:: + + use Symfony\Component\Security\Core\Exception\AccessDeniedException; + use Symfony\Component\ExpressionLanguage\Expression; // ... - use JMS\SecurityExtraBundle\Annotation\Secure; - /** - * @Secure(roles="ROLE_ADMIN") - */ - public function helloAction($name) + public function indexAction() { + if (!$this->get('security.context')->isGranted(new Expression( + '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())' + ))) { + throw new AccessDeniedException(); + } + // ... } +In this example, if the current user has ``ROLE_ADMIN`` or if the current +user object's ``isSuperAdmin()`` method returns ``true``, then access will +be granted (note: your User object may not have an ``isSuperAdmin`` method, +that method is invented for this example). + +This uses an expression and you can learn more about the expression language +syntax, see :doc:`/components/expression_language/syntax`. + +.. _book-security-expression-variables: + +Inside the expression, you have access to a number of variables: + +* ``user`` The user object (or the string ``anon`` if you're not authenticated); +* ``roles`` The array of roles the user has, including from the + :ref:`role hierarchy ` but not including + the ``IS_AUTHENTICATED_*`` attributes (see the functions below); +* ``object``: The object (if any) that's passed as the second argument to + ``isGranted`` ; +* ``token`` The token object; +* ``trust_resolver``: The :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationTrustResolverInterface`, + object: you'll probably use the ``is_*`` functions below instead. + +Additionally, you have access to a number of functions inside the expression: + +* ``is_authenticated``: Returns ``true`` if the user is authenticated via "remember-me" + or authenticated "fully" - i.e. returns true if the user is "logged in"; +* ``is_anonymous``: Equal to using ``IS_AUTHENTICATED_ANONYMOUSLY`` with + the ``isGranted`` function; +* ``is_remember_me``: Similar, but not equal to ``IS_AUTHENTICATED_REMEMBERED``, + see below; +* ``is_fully_authenticated``: Similar, but not equal to ``IS_AUTHENTICATED_FULLY``, + see below; +* ``has_role``: Checks to see if the user has the given role - equivalent + to an expression like ``'ROLE_ADMIN' in roles``. + +.. sidebar:: ``is_remember_me`` is different than checking ``IS_AUTHENTICATED_REMEMBERED`` + + The ``is_remember_me`` and ``is_authenticated_fully`` functions are *similar* + to using ``IS_AUTHENTICATED_REMEMBERED`` and ``IS_AUTHENTICATED_FULLY`` + with the ``isGranted`` function - but they are **not** the same. The + following shows the difference:: + + use Symfony\Component\ExpressionLanguage\Expression; + // ... + + $sc = $this->get('security.context'); + $access1 = $sc->isGranted('IS_AUTHENTICATED_REMEMBERED'); + + $access2 = $sc->isGranted(new Expression( + 'is_remember_me() or is_fully_authenticated()' + )); + + Here, ``$access1`` and ``$access2`` will be the same value. Unlike the + behavior of ``IS_AUTHENTICATED_REMEMBERED`` and ``IS_AUTHENTICATED_FULLY``, + the ``is_remember_me`` function *only* returns true if the user is authenticated + via a remember-me cookie and ``is_fully_authenticated`` *only* returns + true if the user has actually logged in during this session (i.e. is + full-fledged). + Access Control in Other Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1698,6 +1815,33 @@ the built-in helper function: idea to have a main firewall that covers all URLs (as has been shown in this chapter). +.. _book-security-template-expression: + +.. 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 + + +For more details on expressions and security, see :ref:`book-security-expressions`. + Access Control Lists (ACLs): Securing Individual Database Objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1714,6 +1858,8 @@ 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 ----------- @@ -1815,11 +1961,6 @@ a route so that you can use it to generate the URL: return $collection; -.. caution:: - - As of Symfony 2.1, you *must* have a route that corresponds to your logout - path. Without this route, logging out will not work. - 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 @@ -1872,9 +2013,6 @@ cookie will be ever created by Symfony2): Utilities --------- -.. versionadded:: 2.2 - The ``StringUtils`` and ``SecureRandom`` classes were added in Symfony 2.2 - The Symfony Security component comes with a collection of nice utilities related to security. These utilities are used by Symfony, but you should also use them if you want to solve the problem they address. diff --git a/book/service_container.rst b/book/service_container.rst index 14a9bff96e6..3dc5698c853 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -103,40 +103,7 @@ for you. In order for this to work, you must *teach* the container how to create the ``Mailer`` service. This is done via configuration, which can be specified in YAML, XML or PHP: -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - services: - my_mailer: - class: Acme\HelloBundle\Mailer - arguments: [sendmail] - - .. code-block:: xml - - - - - - - - sendmail - - - - - .. code-block:: php - - // app/config/config.php - use Symfony\Component\DependencyInjection\Definition; - - $container->setDefinition('my_mailer', new Definition( - 'Acme\HelloBundle\Mailer', - array('sendmail') - )); +.. include includes/_service_container_my_mailer.rst.inc .. note:: @@ -270,14 +237,6 @@ looks up the value of each parameter and uses it in the service definition. http://symfony.com/?foo=%%s&bar=%%d -.. caution:: - - You may receive a - :class:`Symfony\\Component\\DependencyInjection\\Exception\\ScopeWideningInjectionException` - when passing the ``request`` service as an argument. To understand this - problem better and learn how to solve it, refer to the cookbook article - :doc:`/cookbook/service_container/scopes`. - The purpose of parameters is to feed information into services. Of course there was nothing wrong with defining the service without using any parameters. Parameters, however, have several advantages: @@ -668,6 +627,115 @@ service needs the ``my_mailer`` service in order to function. When you define this dependency in the service container, the container takes care of all the work of instantiating the classes. +.. _book-services-expressions: + +Using the Expression Language +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.4 + The Expression Language functionality was introduced in Symfony 2.4. + +The service container also supports an "expression" that allows you to inject +very specific values into a service. + +For example, suppose you have a third service (not shown here), called ``mailer_configuration``, +which has a ``getMailerMethod()`` method on it, which will return a string +like ``sendmail`` based on some configuration. Remember that the first argument +to the ``my_mailer`` service is the simple string ``sendmail``: + +.. include includes/_service_container_my_mailer.rst.inc + +But instead of hardcoding this, how could we get this value from the ``getMailerMethod()`` +of the new ``mailer_configuration`` service? One way is to use an expression: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + my_mailer: + class: Acme\HelloBundle\Mailer + arguments: ["@=service('mailer_configuration').getMailerMethod()"] + + .. code-block:: xml + + + + + + + + service('mailer_configuration').getMailerMethod() + + + + + .. code-block:: php + + // app/config/config.php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\ExpressionLanguage\Expression; + + $container->setDefinition('my_mailer', new Definition( + 'Acme\HelloBundle\Mailer', + array(new Expression('service("mailer_configuration").getMailerMethod()')) + )); + +To learn more about the expression language syntax, see :doc:`/components/expression_language/syntax`. + +In this context, you have access to 2 functions: + +* ``service`` - returns a given service (see the example above); +* ``parameter`` - returns a specific parameter value (syntax is just like ``service``) + +You also have access to the :class:`Symfony\\Component\\DependencyInjection\\ContainerBuilder` +via a ``container`` variable. Here's another example: + +.. configuration-block:: + + .. code-block:: yaml + + services: + my_mailer: + class: Acme\HelloBundle\Mailer + arguments: ["@=container.hasParameter('some_param') ? parameter('some_param') : 'default_value'"] + + .. code-block:: xml + + + + + + + @=container.hasParameter('some_param') ? parameter('some_param') : 'default_value' + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\ExpressionLanguage\Expression; + + $container->setDefinition('my_mailer', new Definition( + 'Acme\HelloBundle\Mailer', + array(new Expression( + "@=container.hasParameter('some_param') ? parameter('some_param') : 'default_value'" + )) + )); + +Expressions can be used in ``arguments``, ``properties``, as arguments with +``configurator`` and as arguments to ``calls`` (method calls). + Optional Dependencies: Setter Injection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -762,6 +830,104 @@ Injecting the dependency by the setter method just needs a change of syntax: and "setter injection". The Symfony2 service container also supports "property injection". +.. _book-container-request-stack: + +Injecting the Request +~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.4 + The ``request_stack`` service was introduced in version 2.4. + +As of Symfony 2.4, instead of injecting the ``request`` service, you should +inject the ``request_stack`` service and access the ``Request`` by calling +the :method:`Symfony\\Component\\HttpFoundation\\RequestStack::getCurrentRequest` +method:: + + namespace Acme\HelloBundle\Newsletter; + + use Symfony\Component\HttpFoundation\RequestStack; + + class NewsletterManager + { + protected $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + public function anyMethod() + { + $request = $this->requestStack->getCurrentRequest(); + // ... do something with the request + } + + // ... + } + +Now, just inject the ``request_stack``, which behaves like any normal service: + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/HelloBundle/Resources/config/services.yml + services: + newsletter_manager: + class: "Acme\HelloBundle\Newsletter\NewsletterManager" + arguments: ["@request_stack"] + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/HelloBundle/Resources/config/services.php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + + // ... + $container->setDefinition('newsletter_manager', new Definition( + 'Acme\HelloBundle\Newsletter\NewsletterManager', + array(new Reference('request_stack')) + )); + +.. sidebar:: Why not Inject the ``request`` Service? + + Almost all Symfony2 built-in services behave in the same way: a single + instance is created by the container which it returns whenever you get it or + when it is injected into another service. There is one exception in a standard + Symfony2 application: the ``request`` service. + + If you try to inject the ``request`` into a service, you will probably receive + a + :class:`Symfony\\Component\\DependencyInjection\\Exception\\ScopeWideningInjectionException` + exception. That's because the ``request`` can **change** during the life-time + of a container (when a sub-request is created for instance). + + +.. tip:: + + If you define a controller as a service then you can get the ``Request`` + object without injecting the container by having it passed in as an + argument of your action method. See + :ref:`book-controller-request-argument` for details. + Making References Optional -------------------------- diff --git a/book/stable_api.rst b/book/stable_api.rst index 0d92777bdd0..8980f0274dc 100644 --- a/book/stable_api.rst +++ b/book/stable_api.rst @@ -22,7 +22,8 @@ everything not tagged explicitly is not part of the stable API. Any third party bundle should also publish its own stable API. -As of Symfony 2.0, the following components have a public tagged API: +As of the latest stable release of Symfony, the following components have +a public tagged API: * BrowserKit * ClassLoader @@ -31,7 +32,7 @@ As of Symfony 2.0, the following components have a public tagged API: * DependencyInjection * DomCrawler * EventDispatcher -* Filesystem (as of Symfony 2.1) +* Filesystem * Finder * HttpFoundation * HttpKernel diff --git a/book/templating.rst b/book/templating.rst index e9a5aa53072..cfa9c1088c1 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -375,11 +375,6 @@ When working with template inheritance, here are some tips to keep in mind: Template Naming and Locations ----------------------------- -.. versionadded:: 2.2 - Namespaced path support was added in 2.2, allowing for template names - like ``@AcmeDemo/layout.html.twig``. See :doc:`/cookbook/templating/namespaced_paths` - for more details. - By default, templates can live in two different locations: * ``app/Resources/views/``: The applications ``views`` directory can contain @@ -572,10 +567,6 @@ you set `with_context`_ to false). maps (i.e. an array with named keys). If you needed to pass in multiple elements, it would look like this: ``{'foo': foo, 'bar': bar}``. -.. versionadded:: 2.2 - The `include() function`_ is a new Twig feature that's available in Symfony - 2.2. Prior, the `{% include %} tag`_ tag was used. - .. index:: single: Templating; Embedding action @@ -676,9 +667,6 @@ that as much code as possible lives in reusable :doc:`services assertGreaterThan( @@ -295,7 +299,7 @@ document:: $this->assertTrue($client->getResponse()->isNotFound()); // Assert a specific 200 status code $this->assertEquals( - 200, + Response::HTTP_OK, $client->getResponse()->getStatusCode() ); @@ -306,6 +310,9 @@ document:: // or simply check that the response is a redirect to any URL $this->assertTrue($client->getResponse()->isRedirect()); + .. versionadded:: 2.4 + Support for HTTP status code constants was added with Symfony 2.4. + .. index:: single: Tests; Client @@ -670,6 +677,11 @@ their type:: // Upload a file $form['photo']->upload('/path/to/lucas.jpg'); +.. tip:: + + If you purposefully want to select "invalid" select/radio values, see + :ref:`components-dom-crawler-invalid`. + .. tip:: You can get the values that will be submitted by calling the ``getValues()`` diff --git a/book/translation.rst b/book/translation.rst index 73d771032b0..193945da32a 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -307,9 +307,6 @@ texts* and complex expressions: Note that this only influences the current template, not any "included" template (in order to avoid side effects). -.. versionadded:: 2.1 - The ``trans_default_domain`` tag is new in Symfony 2.1 - PHP Templates ~~~~~~~~~~~~~ @@ -526,11 +523,6 @@ the framework: 'default_locale' => 'en', )); -.. versionadded:: 2.1 - The ``default_locale`` parameter was defined under the session key - originally, however, as of 2.1 this has been moved. This is because the - locale is now set on the request instead of the session. - .. _book-translation-constraint-messages: Translating Constraint Messages diff --git a/components/class_loader/cache_class_loader.rst b/components/class_loader/cache_class_loader.rst index d9ef74ca207..1d122fd9fef 100644 --- a/components/class_loader/cache_class_loader.rst +++ b/components/class_loader/cache_class_loader.rst @@ -26,9 +26,6 @@ for a class. ApcClassLoader -------------- -.. versionadded:: 2.1 - The ``ApcClassLoader`` class was added in Symfony 2.1. - ``ApcClassLoader`` wraps an existing class loader and caches calls to its ``findFile()`` method using `APC`_:: @@ -49,9 +46,6 @@ ApcClassLoader XcacheClassLoader ----------------- -.. versionadded:: 2.1 - The ``XcacheClassLoader`` class was added in Symfony 2.1. - ``XcacheClassLoader`` uses `XCache`_ to cache a class loader. Registering it is straightforward:: diff --git a/components/class_loader/class_loader.rst b/components/class_loader/class_loader.rst index a05b03b0b83..198f25d168a 100644 --- a/components/class_loader/class_loader.rst +++ b/components/class_loader/class_loader.rst @@ -4,9 +4,6 @@ The PSR-0 Class Loader ====================== -.. versionadded:: 2.1 - The ``ClassLoader`` class was added in Symfony 2.1. - If your classes and third-party libraries follow the `PSR-0`_ standard, you can use the :class:`Symfony\\Component\\ClassLoader\\ClassLoader` class to load all of your project's classes. diff --git a/components/class_loader/debug_class_loader.rst b/components/class_loader/debug_class_loader.rst index 0cd7fa9e5cb..7c724fb6a01 100644 --- a/components/class_loader/debug_class_loader.rst +++ b/components/class_loader/debug_class_loader.rst @@ -4,9 +4,6 @@ Debugging a Class Loader ======================== -.. versionadded:: 2.1 - The ``DebugClassLoader`` class was added in Symfony 2.1. - The :class:`Symfony\\Component\\ClassLoader\\DebugClassLoader` attempts to throw more helpful exceptions when a class isn't found by the registered autoloaders. All autoloaders that implement a ``findFile()`` method are replaced diff --git a/components/config/definition.rst b/components/config/definition.rst index 78961575c19..c42285c8eb0 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -99,9 +99,9 @@ node definition. Node type are available for: * scalar * boolean -* integer (new in 2.2) -* float (new in 2.2) -* enum (new in 2.1) +* integer +* float +* enum * array * variable (no validation) @@ -111,9 +111,6 @@ and are created with ``node($name, $type)`` or their associated shortcut Numeric node constraints ~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - The numeric (float and integer) nodes are new in 2.2 - Numeric nodes (float and integer) provide two extra constraints - :method:`Symfony\\Component\\Config\\Definition\\Builder::min` and :method:`Symfony\\Component\\Config\\Definition\\Builder::max` - @@ -136,9 +133,6 @@ allowing to validate the value:: Enum nodes ~~~~~~~~~~ -.. versionadded:: 2.1 - The enum node is new in Symfony 2.1 - Enum nodes provide a constraint to match the given input against a set of values:: @@ -290,9 +284,6 @@ has a certain value: Optional Sections ----------------- -.. versionadded:: 2.2 - The ``canBeEnabled`` and ``canBeDisabled`` methods are new in Symfony 2.2 - If you have entire sections which are optional and can be enabled/disabled, you can take advantage of the shortcut :method:`Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition::canBeEnabled` and diff --git a/components/console/helpers/dialoghelper.rst b/components/console/helpers/dialoghelper.rst index 57a8c40208b..3e46334fab1 100644 --- a/components/console/helpers/dialoghelper.rst +++ b/components/console/helpers/dialoghelper.rst @@ -58,9 +58,6 @@ the default value (``AcmeDemoBundle`` here) is returned. Autocompletion ~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - Autocompletion for questions was added in Symfony 2.2. - You can also specify an array of potential answers for a given question. These will be autocompleted as the user types:: @@ -76,9 +73,6 @@ will be autocompleted as the user types:: Hiding the User's Response ~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - The ``askHiddenResponse`` method was added in Symfony 2.2. - You can also ask a question and hide the response. This is particularly convenient for passwords:: @@ -146,9 +140,6 @@ be able to proceed if their input is valid. Validating a Hidden Response ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - The ``askHiddenResponseAndValidate`` method was added in Symfony 2.2. - You can also ask and validate a hidden response:: $dialog = $this->getHelperSet()->get('dialog'); @@ -173,10 +164,6 @@ some reason, pass true as the fifth argument. Let the user choose from a list of Answers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - The :method:`Symfony\\Component\\Console\\Helper\\DialogHelper::select` method - was added in Symfony 2.2. - If you have a predefined set of answers the user can choose from, you could use the ``ask`` method described above or, to make sure the user provided a correct answer, the ``askAndValidate`` method. Both have diff --git a/components/console/helpers/progresshelper.rst b/components/console/helpers/progresshelper.rst index b56c1d0a377..9ee76b694cb 100644 --- a/components/console/helpers/progresshelper.rst +++ b/components/console/helpers/progresshelper.rst @@ -4,12 +4,12 @@ Progress Helper =============== -.. versionadded:: 2.2 - The ``progress`` helper was added in Symfony 2.2. - .. versionadded:: 2.3 The ``setCurrent`` method was added in Symfony 2.3. +.. versionadded:: 2.4 + The ``clear`` method was added in Symfony 2.4. + When executing longer-running commands, it may be helpful to show progress information, which updates as your command runs: @@ -37,6 +37,12 @@ pass it a total number of units, and advance the progress as your command execut :method:`Symfony\\Component\\Console\\Helper\\ProgressHelper::setCurrent` method. +If you want to output something while the progress bar is running, +call :method:`Symfony\\Component\\Console\\Helper\\ProgressHelper::clear` first. +After you're done, call +:method:`Symfony\\Component\\Console\\Helper\\ProgressHelper::display` +to show the progress bar again. + The appearance of the progress output can be customized as well, with a number of different levels of verbosity. Each of these displays different possible items - like percentage completion, a moving progress bar, or current/total diff --git a/components/console/helpers/tablehelper.rst b/components/console/helpers/tablehelper.rst index 1145dd7b603..4bc3323ebe6 100644 --- a/components/console/helpers/tablehelper.rst +++ b/components/console/helpers/tablehelper.rst @@ -32,12 +32,17 @@ table rendering: using named layouts or by customizing rendering options. Customize Table Layout using Named Layouts ------------------------------------------ -The Table helper ships with two preconfigured table layouts: +.. versionadded:: 2.4 + The ``TableHelper::LAYOUT_COMPACT`` layout was added in Symfony 2.4. + +The Table helper ships with three preconfigured table layouts: * ``TableHelper::LAYOUT_DEFAULT`` * ``TableHelper::LAYOUT_BORDERLESS`` +* ``TableHelper::LAYOUT_COMPACT`` + Layout can be set using :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setLayout` method. Customize Table Layout using Rendering Options diff --git a/components/console/introduction.rst b/components/console/introduction.rst index bc709a2abc9..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 ~~~~~~~~~~~~~~~~ @@ -196,10 +198,43 @@ level. For example:: $output->writeln(...); } +.. versionadded:: 2.4 + The :method:`Symfony\\Component\Console\\Output\\Output::isQuiet`, + :method:`Symfony\\Component\Console\\Output\\Output::isVerbose`, + :method:`Symfony\\Component\Console\\Output\\Output::isVeryVerbose` and + :method:`Symfony\\Component\Console\\Output\\Output::isDebug` + methods were introduced in version 2.4 + +There are also more semantic methods you can use to test for each of the +verbosity levels:: + + if ($output->isQuiet()) { + // ... + } + + if ($output->isVerbose()) { + // ... + } + + if ($output->isVeryVerbose()) { + // ... + } + + if ($output->isDebug()) { + // ... + } + 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/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst index a90110e4e65..d9413aa7e75 100644 --- a/components/dependency_injection/compilation.rst +++ b/components/dependency_injection/compilation.rst @@ -276,9 +276,6 @@ but also load a secondary one only if a certain parameter is set:: Prepending Configuration passed to the Extension ------------------------------------------------ -.. versionadded:: 2.2 - The ability to prepend the configuration of a bundle is new in Symfony 2.2. - An Extension can prepend the configuration of any Bundle before the ``load()`` method is called by implementing :class:`Symfony\\Component\\DependencyInjection\\Extension\\PrependExtensionInterface`:: diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst index 8f93fbcae75..b232de064f0 100644 --- a/components/dependency_injection/lazy_services.rst +++ b/components/dependency_injection/lazy_services.rst @@ -40,7 +40,7 @@ the `ProxyManager bridge`_: .. code-block:: json "require": { - "ocramius/proxy-manager": "0.4.*" + "ocramius/proxy-manager": "0.5.*" } to your ``composer.json``. Afterwards compile your container and check diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index 4ad121ba061..568f0202417 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -95,6 +95,65 @@ To remove a node the anonymous function must return false. All filter methods return a new :class:`Symfony\\Component\\DomCrawler\\Crawler` instance with filtered content. +Both the :method:`Symfony\\Component\\DomCrawler\\Crawler::filterXPath` and +:method:`Symfony\\Component\\DomCrawler\\Crawler::filter` methods work with +XML namespaces, which can be either automatically discovered or registered +explicitly. + +.. versionadded:: 2.4 + Auto discovery and explicit registration of namespaces was introduced + in Symfony 2.4. + +Consider the XML below: + + + + tag:youtube.com,2008:video:kgZRZmEc9j4 + + + + Chordates - CrashCourse Biology #24 + widescreen + + + +This can be filtered with the ``Crawler`` without needing to register namespace +aliases both with :method:`Symfony\\Component\\DomCrawler\\Crawler::filterXPath`:: + + $crawler = $crawler->filterXPath('//default:entry/media:group//yt:aspectRatio'); + +and :method:`Symfony\\Component\\DomCrawler\\Crawler::filter`:: + + use Symfony\Component\CssSelector\CssSelector; + + CssSelector::disableHtmlExtension(); + $crawler = $crawler->filter('default|entry media|group yt|aspectRatio'); + +.. note:: + + The default namespace is registered with a prefix "default". It can be + changed with the + :method:`Symfony\\Component\\DomCrawler\\Crawler::setDefaultNamespacePrefix` + method. + + The default namespace is removed when loading the content if it's the only + namespace in the document. It's done to simplify the xpath queries. + +Namespaces can be explicitly registered with the +:method:`Symfony\\Component\\DomCrawler\\Crawler::registerNamespace` method:: + + $crawler->registerNamespace('m', 'http://search.yahoo.com/mrss/'); + $crawler = $crawler->filterXPath('//m:group//yt:aspectRatio'); + +.. caution:: + + To query XML with a CSS selector, the HTML extension needs to be disabled with + :method:`CssSelector::disableHtmlExtension ` + to avoid converting the selector to lowercase. + Node Traversing ~~~~~~~~~~~~~~~ @@ -340,6 +399,9 @@ and uploading files:: // even fake a file upload $form['registration[photo]']->upload('/path/to/lucas.jpg'); +Using the Form Data +................... + What's the point of doing all of this? If you're testing internally, you can grab the information off of your form as if it had just been submitted by using the PHP values:: @@ -375,5 +437,26 @@ directly:: // submit that form $crawler = $client->submit($form); +.. _components-dom-crawler-invalid: + +Selecting Invalid Choice Values +............................... + +.. versionadded:: 2.4 + The :method:`Symfony\\Component\\DomCrawler\\Form::disableValidation` + method was added in Symfony 2.4. + +By default, choice fields (select, radio) have internal validation activated +to prevent you from setting invalid values. If you want to be able to set +invalid values, you can use the ``disableValidation()`` method on either +the whole form or specific field(s):: + + // Disable validation for a specific field + $form['country']->disableValidation()->select('Invalid value'); + + // Disable validation for the whole form + $form->disableValidation(); + $form['country']->select('Invalid value'); + .. _`Goutte`: https://github.com/fabpot/goutte .. _Packagist: https://packagist.org/packages/symfony/dom-crawler diff --git a/components/event_dispatcher/immutable_dispatcher.rst b/components/event_dispatcher/immutable_dispatcher.rst index ee4f85004f9..0732f7597dd 100644 --- a/components/event_dispatcher/immutable_dispatcher.rst +++ b/components/event_dispatcher/immutable_dispatcher.rst @@ -4,9 +4,6 @@ The Immutable Event Dispatcher ============================== -.. versionadded:: 2.1 - This feature was added in Symfony 2.1. - The :class:`Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher` is a locked or frozen event dispatcher. The dispatcher cannot register new listeners or subscribers. diff --git a/components/expression_language/caching.rst b/components/expression_language/caching.rst new file mode 100644 index 00000000000..41c9d2a1c26 --- /dev/null +++ b/components/expression_language/caching.rst @@ -0,0 +1,71 @@ +.. index:: + single: Caching; ExpressionLanguage + +Caching Expressions Using Parser Caches +======================================= + +The ExpressionLanguage component already provides a +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::compile` +method to be able to cache the expressions in plain PHP. But internally, the +component also caches the parsed expressions, so duplicated expressions can be +compiled/evaluated quicker. + +The Workflow +------------ + +Both :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::evaluate` +and ``compile()`` need to do some things before each can provide the return +values. For ``evaluate()``, this overhead is even bigger. + +Both methods need to tokenize and parse the expression. This is done by the +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::parse` +method. It returns a :class:`Symfony\\Component\\ExpressionLanguage\\ParsedExpression`. +Now, the ``compile()`` method just returns the string conversion of this object. +The ``evaluate()`` method needs to loop through the "nodes" (pieces of an +expression saved in the ``ParsedExpression``) and evaluate them on the fly. + +To save time, the ``ExpressionLanguage`` caches the ``ParsedExpression`` so +it can skip the tokenize and parse steps with duplicate expressions. +The caching is done by a +:class:`Symfony\\Component\\ExpressionLanguage\\ParserCache\\ParserCacheInterface` +instance (by default, it uses an +:class:`Symfony\\Component\\ExpressionLanguage\\ParserCache\\ArrayParserCache`). +You can customize this by creating a custom ``ParserCache`` and injecting this +in the object using the constructor:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + use Acme\ExpressionLanguage\ParserCache\MyDatabaseParserCache; + + $cache = new MyDatabaseParserCache(...); + $language = new ExpressionLanguage($cache); + +.. note:: + + The `DoctrineBridge`_ provides a Parser Cache implementation using the + `doctrine cache library`_, which gives you caching for all sorts of cache + strategies, like Apc, Filesystem and Memcached. + +Using Parsed and Serialized Expressions +--------------------------------------- + +Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and +``SerializedParsedExpression``:: + + use Symfony\Component\ExpressionLanguage\ParsedExpression; + // ... + + $expression = new ParsedExpression($language->parse('1 + 4')); + + echo $language->evaluate($expression); // prints 5 + +.. code-block:: php + + use Symfony\Component\ExpressionLanguage\SerializedParsedExpression; + // ... + + $expression = new SerializedParsedExpression(serialize($language->parse('1 + 4'))); + + echo $language->evaluate($expression); // prints 5 + +.. _DoctrineBridge: https://github.com/symfony/DoctrineBridge +.. _`doctrine cache library`: http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/caching.html diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst new file mode 100644 index 00000000000..e2678236d17 --- /dev/null +++ b/components/expression_language/extending.rst @@ -0,0 +1,89 @@ +.. index:: + single: Extending; ExpressionLanguage + +Extending the ExpressionLanguage +================================ + +The ExpressionLanguage can be extended by adding custom functions. For +instance, in the Symfony Framework, the security has custom functions to check +the user's role. + +.. note:: + + If you want to learn how to use functions in an expression, read + ":ref:`component-expression-functions`". + +Registering Functions +--------------------- + +Functions are registered on each specific ``ExpressionLanguage`` instance. +That means the functions can be used in any expression executed by that +instance. + +To register a function, use +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::register`. +This method has 3 arguments: + +* **name** - The name of the function in an expression; +* **compiler** - A function executed when compiling an expression using the + function; +* **evaluator** - A function executed when the expression is evaluated. + +.. code-block:: php + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $language = new ExpressionLanguage(); + $language->register('lowercase', function ($str) { + if (!is_string($str)) { + return $str; + } + + return sprintf('strtolower(%s)', $str); + }, function ($arguments, $str) { + if (!is_string($str)) { + return $str; + } + + return strtolower($str); + }); + + echo $language->evaluate('lowercase("HELLO")'); + +This will print ``hello``. Both the **compiler** and **evaluator** are passed +an ``arguments`` variable as their first argument, which is equal to the +second argument to ``evaluate()`` or ``compile()`` (e.g. the "values" when +evaluating or the "names" if compiling). + +Creating a new ExpressionLanguage Class +--------------------------------------- + +When you use the ``ExpressionLanguage`` class in your library, it's recommend +to create a new ``ExpressionLanguage`` class and register the functions there. +Override ``registerFunctions`` to add your own functions:: + + namespace Acme\AwesomeLib\ExpressionLanguage; + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; + + class ExpressionLanguage extends BaseExpressionLanguage + { + protected function registerFunctions() + { + parent::registerFunctions(); // do not forget to also register core functions + + $this->register('lowercase', function ($str) { + if (!is_string($str)) { + return $str; + } + + return sprintf('strtolower(%s)', $str); + }, function ($arguments, $str) { + if (!is_string($str)) { + return $str; + } + + return strtolower($str); + }); + } + } diff --git a/components/expression_language/index.rst b/components/expression_language/index.rst new file mode 100644 index 00000000000..aa63907d921 --- /dev/null +++ b/components/expression_language/index.rst @@ -0,0 +1,10 @@ +Expression Language +=================== + +.. toctree:: + :maxdepth: 2 + + introduction + syntax + extending + caching diff --git a/components/expression_language/introduction.rst b/components/expression_language/introduction.rst new file mode 100644 index 00000000000..a53c8aa960d --- /dev/null +++ b/components/expression_language/introduction.rst @@ -0,0 +1,118 @@ +.. index:: + single: Expressions + Single: Components; Expression Language + +The ExpressionLanguage Component +================================ + + The ExpressionLanguage component provides an engine that can compile and + evaluate expressions. An expression is a one-liner that returns a value + (mostly, but not limited to, Booleans). + +.. versionadded:: 2.4 + The ExpressionLanguage component was introduced in Symfony 2.4. + +Installation +------------ + +You can install the component in 2 different ways: + +* :doc:`Install it via Composer ` (``symfony/expression-language`` on `Packagist`_); +* Use the official Git repository (https://github.com/symfony/expression-language). + +How can the Expression Engine Help Me? +-------------------------------------- + +The purpose of the component is to allow users to use expressions inside +configuration for more complex logic. For some examples, the Symfony2 Framework +uses expressions in security, for validation rules and in route matching. + +Besides using the component in the framework itself, the ExpressionLanguage +component is a perfect candidate for the foundation of a *business rule engine*. +The idea is to let the webmaster of a website configure things in a dynamic +way without using PHP and without introducing security problems: + +.. _component-expression-language-examples: + +.. code-block:: text + + # Get the special price if + user.getGroup() in ['good_customers', 'collaborator'] + + # Promote article to the homepage when + article.commentCount > 100 and article.category not in ["misc"] + + # Send an alert when + product.stock < 15 + +Expressions can be seen as a very restricted PHP sandbox and are immune to +external injections as you must explicitly declare which variables are available +in an expression. + +Usage +----- + +The ExpressionLanguage component can compile and evaluate expressions. +Expressions are one-liners that often return a Boolean, which can be used +by the code executing the expression in an ``if`` statement. A simple example +of an expression is ``1 + 2``. You can also use more complicated expressions, +such as ``someArray[3].someMethod('bar')``. + +The component provides 2 ways to work with expressions: + +* **evaluation**: the expression is evaluated without being compiled to PHP; +* **compile**: the expression is compiled to PHP, so it can be cached and + evaluated. + +The main class of the component is +:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage`:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $language = new ExpressionLanguage(); + + echo $language->evaluate('1 + 2'); // displays 3 + + echo $language->compile('1 + 2'); // displays (1 + 2) + +Expression Syntax +----------------- + +See :doc:`/components/expression_language/syntax` to learn the syntax of the +ExpressionLanguage component. + +Passing in Variables +-------------------- + +You can also pass variables into the expression, which can be of any valid +PHP type (including objects):: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $language = new ExpressionLanguage(); + + class Apple + { + public $variety; + } + + $apple = new Apple(); + $apple->variety = 'Honeycrisp'; + + echo $language->evaluate( + 'fruit.variety', + array( + 'fruit' => $apple, + ) + ); + +This will print "Honeycrisp". For more information, see the :doc:`/components/expression_language/syntax` +entry, especially :ref:`component-expression-objects` and :ref:`component-expression-arrays`. + +Caching +------- + +The component provides some different caching strategies, read more about them +in :doc:`/components/expression_language/caching`. + +.. _Packagist: https://packagist.org/packages/symfony/expression-language diff --git a/components/expression_language/syntax.rst b/components/expression_language/syntax.rst new file mode 100644 index 00000000000..4a45f9fbe55 --- /dev/null +++ b/components/expression_language/syntax.rst @@ -0,0 +1,295 @@ +.. index:: + single: Syntax; ExpressionLanguage + +The Expression Syntax +===================== + +The ExpressionLanguage component uses a specific syntax which is based on the +expression syntax of Twig. In this document, you can find all supported +syntaxes. + +Supported Literals +------------------ + +The component supports: + +* **strings** - single and double quotes (e.g. ``'hello'``) +* **numbers** - e.g. ``103`` +* **arrays** - using JSON-like notation (e.g. ``[1, 2]``) +* **hashes** - using JSON-like notation (e.g. ``{ foo: 'bar' }``) +* **booleans** - ``true`` and ``false`` +* **null** - ``null`` + +.. _component-expression-objects: + +Working with Objects +-------------------- + +When passing objects into an expression, you can use different syntaxes to +access properties and call methods on the object. + +Accessing Public Methods +~~~~~~~~~~~~~~~~~~~~~~~~ + +Public properties on objects can be accessed by using the ``.`` syntax, similar +to JavaScript:: + + class Apple + { + public $variety; + } + + $apple = new Apple(); + $apple->variety = 'Honeycrisp'; + + echo $language->evaluate( + 'fruit.variety', + array( + 'fruit' => $apple, + ) + ); + +This will print ``Honeycrisp``. + +Calling Methods +~~~~~~~~~~~~~~~ + +The ``.`` syntax can also be used to call methods on an object, similar to +JavaScript:: + + class Robot + { + public function sayHi($times) + { + $greetings = array(); + for ($i = 0; $i < $times; $i++) { + $greetings[] = 'Hi'; + } + + return implode(' ', $greetings).'!'; + } + } + + $robot = new Robot(); + + echo $language->evaluate( + 'robot.sayHi(3)', + array( + 'robot' => $robot, + ) + ); + +This will print ``Hi Hi Hi!``. + +.. _component-expression-functions: + +Working with Functions +---------------------- + +You can also use registered functions in the expression by using the same +syntax as PHP and JavaScript. The ExpressionLanguage component comes with one +function by default: ``constant()``, which will return the value of the PHP +constant:: + + define('DB_USER', 'root'); + + echo $language->evaluate( + 'constant("DB_USER")' + ); + +This will print ``root``. + +.. tip:: + + To read how to register your own functions to use in an expression, see + ":doc:`/components/expression_language/extending`". + +.. _component-expression-arrays: + +Working with Arrays +------------------- + +If you pass an array into an expression, use the ``[]`` syntax to access +array keys, similar to JavaScript:: + + $data = array('life' => 10, 'universe' => 10, 'everything' => 22); + + echo $language->evaluate( + 'data["life"] + data["universe"] + data["everything"]', + array( + 'data' => $data, + ) + ); + +This will print ``42``. + +Supported Operators +------------------- + +The component comes with a lot of operators: + +Arithmetic Operators +~~~~~~~~~~~~~~~~~~~~ + +* ``+`` (addition) +* ``-`` (subtraction) +* ``*`` (multiplication) +* ``/`` (division) +* ``%`` (modulus) +* ``**`` (pow) + +For example:: + + echo $language->evaluate( + 'life + universe + everything', + array( + 'life' => 10, + 'universe' => 10, + 'everything' => 22, + ) + ); + +This will print out ``42``. + +Bitwise Operators +~~~~~~~~~~~~~~~~~ + +* ``&`` (and) +* ``|`` (or) +* ``^`` (xor) + +Comparison Operators +~~~~~~~~~~~~~~~~~~~~ + +* ``==`` (equal) +* ``===`` (identical) +* ``!=`` (not equal) +* ``!==`` (not identical) +* ``<`` (less than) +* ``>`` (greater than) +* ``<=`` (less than or equal to) +* ``>=`` (greater than or equal to) +* ``matches`` (regex match) + +.. tip:: + + To test if a string does *not* match a regex, use the logical ``not`` + operator in combination with the ``matches`` operator:: + + $language->evaluate('not "foo" matches "/bar/"'); // returns true + +Examples:: + + $ret1 = $language->evaluate( + 'life == everything', + array( + 'life' => 10, + 'universe' => 10, + 'everything' => 22, + ) + ); + + $ret2 = $language->evaluate( + 'life > everything', + array( + 'life' => 10, + 'universe' => 10, + 'everything' => 22, + ) + ); + +Both variables would be set to ``false``. + +Logical Operators +~~~~~~~~~~~~~~~~~ + +* ``not`` or ``!`` +* ``and`` or ``&&`` +* ``or`` or ``||`` + +For example:: + + $ret = $language->evaluate( + 'life < universe or life < everything', + array( + 'life' => 10, + 'universe' => 10, + 'everything' => 22, + ) + ); + +This ``$ret`` variable will be set to ``true``. + +String Operators +~~~~~~~~~~~~~~~~ + +* ``~`` (concatenation) + +For example:: + + echo $language->evaluate( + 'firstName~" "~lastName', + array( + 'firstName' => 'Arthur', + 'lastName' => 'Dent', + ) + ); + +This would print out ``Arthur Dent``. + +Array Operators +~~~~~~~~~~~~~~~ + +* ``in`` (contain) +* ``not in`` (does not contain) + +For example:: + + class User + { + public $group; + } + + $user = new User(); + $user->group = 'human_resources'; + + $inGroup = $language->evaluate( + 'user.group in ["human_resources", "marketing"]', + array( + 'user' => $user + ) + ); + +The ``$inGroup`` would evaluate to ``true``. + +Numeric Operators +~~~~~~~~~~~~~~~~~ + +* ``..`` (range) + +For example:: + + class User + { + public $age; + } + + $user = new User(); + $user->age = 34; + + $language->evaluate( + 'user.age in 18..45', + array( + 'user' => $user, + ) + ); + +This will evaluate to ``true``, because ``user.age`` is in the range from +``18`` to ``45``. + +Ternary Operators +~~~~~~~~~~~~~~~~~ + +* ``foo ? 'yes' : 'no'`` +* ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``) +* ``foo ? 'yes'`` (equal to ``foo ? 'yes' : ''``) diff --git a/components/filesystem.rst b/components/filesystem.rst index 1860d10bd56..357cfb0146b 100644 --- a/components/filesystem.rst +++ b/components/filesystem.rst @@ -6,10 +6,6 @@ The Filesystem Component The Filesystem component provides basic utilities for the filesystem. -.. versionadded:: 2.1 - The Filesystem component is new to Symfony 2.1. Previously, the ``Filesystem`` - class was located in the HttpKernel component. - Installation ------------ @@ -25,16 +21,20 @@ The :class:`Symfony\\Component\\Filesystem\\Filesystem` class is the unique endpoint for filesystem operations:: use Symfony\Component\Filesystem\Filesystem; - use Symfony\Component\Filesystem\Exception\IOException; + use Symfony\Component\Filesystem\Exception\IOExceptionInterface; $fs = new Filesystem(); try { $fs->mkdir('/tmp/random/dir/' . mt_rand()); - } catch (IOException $e) { - echo "An error occurred while creating your directory"; + } catch (IOExceptionInterface $e) { + echo "An error occurred while creating your directory at ".$e->getPath(); } +.. versionadded:: 2.4 + The ``IOExceptionInterface`` and its ``getPath`` method are new in Symfony + 2.4. Prior to 2.4, you would catch the ``IOException`` class. + .. note:: Methods :method:`Symfony\\Component\\Filesystem\\Filesystem::mkdir`, @@ -255,14 +255,12 @@ Error Handling -------------- Whenever something wrong happens, an exception implementing -:class:`Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface` is -thrown. +:class:`Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface` or +:class:`Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface` is thrown. .. note:: - Prior to version 2.1, ``mkdir`` returned a boolean and did not throw - exceptions. As of 2.1, a - :class:`Symfony\\Component\\Filesystem\\Exception\\IOException` is thrown - if a directory creation fails. + An :class:`Symfony\\Component\\Filesystem\\Exception\\IOException` is + thrown if directory creation fails. .. _`Packagist`: https://packagist.org/packages/symfony/filesystem diff --git a/components/finder.rst b/components/finder.rst index d9b6822a99d..08392476cbf 100644 --- a/components/finder.rst +++ b/components/finder.rst @@ -82,9 +82,6 @@ Search in several locations by chaining calls to $finder->files()->in(__DIR__)->in('/elsewhere'); -.. versionadded:: 2.2 - Wildcard support was added in version 2.2. - Use wildcard characters to search in the directories matching a pattern:: $finder->in('src/Symfony/*/*/Resources'); @@ -206,9 +203,6 @@ The ``notContains()`` method excludes files containing given pattern:: Path ~~~~ -.. versionadded:: 2.2 - The ``path()`` and ``notPath()`` methods were added in version 2.2. - Restrict files and directories by path with the :method:`Symfony\\Component\\Finder\\Finder::path` method:: diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index 8497a09e530..902a8d93890 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -240,10 +240,13 @@ by using the following methods: returns the list of accepted languages ordered by descending quality; * :method:`Symfony\\Component\\HttpFoundation\\Request::getCharsets`: - returns the list of accepted charsets ordered by descending quality. + returns the list of accepted charsets ordered by descending quality; -.. versionadded:: 2.2 - The :class:`Symfony\\Component\\HttpFoundation\\AcceptHeader` class is new in Symfony 2.2. +* :method:`Symfony\\Component\\HttpFoundation\\Request::getEncodings`: + returns the list of accepted charsets ordered by descending quality; + + .. versionadded:: 2.4 + The ``getEncodings()`` method was added in Symfony 2.4. If you need to get full access to parsed data from ``Accept``, ``Accept-Language``, ``Accept-Charset`` or ``Accept-Encoding``, you can use @@ -269,6 +272,26 @@ request information. Have a look at :class:`the Request API ` for more information about them. +Overriding the Request +~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.4 + The :method:`Symfony\\Component\\HttpFoundation\\Request::setFactory` + method was added in Symfony 2.4. + +The ``Request`` class should not be overridden as it is a data object that +represents an HTTP message. But when moving from a legacy system, adding +methods or changing some default behavior might help. In that case, register a +PHP callable that is able to create an instance of your ``Request`` class:: + + use Symfony\Component\HttpFoundation\Request; + + Request::setFactory(function (array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) { + return SpecialRequest::create($query, $request, $attributes, $cookies, $files, $server, $content); + }); + + $request = Request::createFromGlobals(); + .. _component-http-foundation-response: Response @@ -283,10 +306,13 @@ code, and an array of HTTP headers:: $response = new Response( 'Content', - 200, + Response::HTTP_OK, array('content-type' => 'text/html') ); +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + These information can also be manipulated after the Response object creation:: $response->setContent('Hello World'); @@ -294,7 +320,7 @@ These information can also be manipulated after the Response object creation:: // the headers public attribute is a ResponseHeaderBag $response->headers->set('Content-Type', 'text/plain'); - $response->setStatusCode(404); + $response->setStatusCode(Response::HTTP_NOT_FOUND); When setting the ``Content-Type`` of the Response, you can set the charset, but it is better to set it via the @@ -436,10 +462,6 @@ abstracts the hard work behind a simple API:: $response->headers->set('Content-Disposition', $d); -.. versionadded:: 2.2 - The :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse` - class was added in Symfony 2.2. - Alternatively, if you are serving a static file, you can use a :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse`:: diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index d9e56df99e4..70b049e1a3f 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -569,21 +569,23 @@ each event has their own event object: .. _component-http-kernel-event-table: -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| **Name** | ``KernelEvents`` **Constant** | **Argument passed to the listener** | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.request | ``KernelEvents::REQUEST`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.controller | ``KernelEvents::CONTROLLER`` | :class:`Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.view | ``KernelEvents::VIEW`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.response | ``KernelEvents::RESPONSE`` | :class:`Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.terminate | ``KernelEvents::TERMINATE`` | :class:`Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.exception | ``KernelEvents::EXCEPTION`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ +| **Name** | ``KernelEvents`` **Constant** | **Argument passed to the listener** | ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ +| kernel.request | ``KernelEvents::REQUEST`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent` | ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ +| kernel.controller | ``KernelEvents::CONTROLLER`` | :class:`Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent` | ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ +| kernel.view | ``KernelEvents::VIEW`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent` | ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ +| kernel.response | ``KernelEvents::RESPONSE`` | :class:`Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent` | ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ +| kernel.finish_request | ``KernelEvents::FINISH_REQUEST`` | :class:`Symfony\\Component\\HttpKernel\\Event\\FinishRequestEvent` | ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ +| kernel.terminate | ``KernelEvents::TERMINATE`` | :class:`Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent` | ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ +| kernel.exception | ``KernelEvents::EXCEPTION`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent` | ++-----------------------+----------------------------------+-------------------------------------------------------------------------------------+ .. _http-kernel-working-example: diff --git a/components/index.rst b/components/index.rst index 739b9e84f9c..b53785bb642 100644 --- a/components/index.rst +++ b/components/index.rst @@ -13,6 +13,7 @@ The Components dependency_injection/index dom_crawler event_dispatcher/index + expression_language/index filesystem finder form/index diff --git a/components/intl.rst b/components/intl.rst index e65ba77dbf5..4dbcb7d5a79 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -73,7 +73,7 @@ code:: but usually Composer does this for you automatically: * 1.0.*: when the intl extension is not available - * 1.1.*: when intl is compiled with ICU 4.0 or higher + * 1.1.*: when intl is compiled with ICU 3.8 or higher * 1.2.*: when intl is compiled with ICU 4.4 or higher These versions are important when you deploy your application to a **server with diff --git a/components/map.rst.inc b/components/map.rst.inc index 867f04568ca..a5f2d0f8db7 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -57,6 +57,13 @@ * :doc:`/components/event_dispatcher/generic_event` * :doc:`/components/event_dispatcher/immutable_dispatcher` +* :doc:`/components/expression_language/index` + + * :doc:`/components/expression_language/introduction` + * :doc:`/components/expression_language/syntax` + * :doc:`/components/expression_language/extending` + * :doc:`/components/expression_language/caching` + * **Filesystem** * :doc:`/components/filesystem` diff --git a/components/process.rst b/components/process.rst index dc3ed868825..f2e61fdabbe 100644 --- a/components/process.rst +++ b/components/process.rst @@ -36,15 +36,20 @@ a command in a sub-process:: The component takes care of the subtle differences between the different platforms when executing the command. -.. versionadded:: 2.2 - The ``getIncrementalOutput()`` and ``getIncrementalErrorOutput()`` methods were added in Symfony 2.2. - The ``getOutput()`` method always return the whole content of the standard output of the command and ``getErrorOutput()`` the content of the error output. Alternatively, the :method:`Symfony\\Component\\Process\\Process::getIncrementalOutput` and :method:`Symfony\\Component\\Process\\Process::getIncrementalErrorOutput` methods returns the new outputs since the last call. +.. versionadded:: 2.4 + The ``flushOutput()`` and ``flushErrorOutput()`` methods were added in Symfony 2.4. + +The :method:`Symfony\\Component\\Process\\Process::flushOutput` method flushes +the contents of the output and +:method:`Symfony\\Component\\Process\\Process::flushErrorOutput` flushes +the contents of the error output. + Getting real-time Process Output -------------------------------- @@ -64,9 +69,6 @@ anonymous function to the } }); -.. versionadded:: 2.1 - The non-blocking feature was added in 2.1. - Running Processes Asynchronously -------------------------------- @@ -210,6 +212,25 @@ check regularly:: .. _reference-process-signal: +Process Idle Timeout +-------------------- + +.. versionadded:: 2.4 + The :method:`Symfony\\Component\\Process\\Process::setIdleTimeout` method was added in Symfony 2.4. + +In contrast to the timeout of the previous paragraph, the idle timeout only +considers the time since the last output was produced by the process:: + + use Symfony\Component\Process\Process; + + $process = new Process('something-with-variable-runtime'); + $process->setTimeout(3600); + $process->setIdleTimeout(60); + $process->run(); + +In the case above, a process is considered timed out, when either the total runtime +exceeds 3600 seconds, or the process does not produce any output for 60 seconds. + Process Signals --------------- diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index bc9e6d14cb3..5f635c7d0da 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -8,10 +8,6 @@ The PropertyAccess Component The PropertyAccess component provides function to read and write from/to an object or array using a simple string notation. -.. versionadded:: 2.2 - The PropertyAccess component is new to Symfony 2.2. Previously, the - ``PropertyPath`` class was located in the Form component. - Installation ------------ diff --git a/components/routing/hostname_pattern.rst b/components/routing/hostname_pattern.rst index d2ac2bfa8e1..f4f2b5358cc 100644 --- a/components/routing/hostname_pattern.rst +++ b/components/routing/hostname_pattern.rst @@ -4,9 +4,6 @@ How to match a route based on the Host ====================================== -.. versionadded:: 2.2 - Host matching support was added in Symfony 2.2 - You can also match on the HTTP *host* of the incoming request. .. configuration-block:: diff --git a/components/routing/introduction.rst b/components/routing/introduction.rst index a3ab1b59800..6176c3ca850 100644 --- a/components/routing/introduction.rst +++ b/components/routing/introduction.rst @@ -93,9 +93,6 @@ are the least commonly needed. 7. An array of methods. These enforce a certain HTTP request method (``HEAD``, ``GET``, ``POST``, ...). -.. versionadded:: 2.2 - Host matching support was added in Symfony 2.2 - Take the following route, which combines several of these ideas:: $route = new Route( @@ -160,10 +157,6 @@ the :method:`Symfony\\Component\\Routing\\RouteCollection::addPrefix` method:: $rootCollection->addCollection($subCollection); -.. versionadded:: 2.2 - The ``addPrefix`` method is added in Symfony2.2. This was part of the - ``addCollection`` method in older versions. - Set the Request Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/components/security/authentication.rst b/components/security/authentication.rst index b06e0e24032..95c3b27c08b 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -198,10 +198,8 @@ own, it just needs to follow these rules: #. The class must implement :class:`Symfony\\Component\\Security\\Core\\Encoder\\PasswordEncoderInterface`; -#. The first line in ``encodePassword`` and ``isPasswordValid`` must check - to make sure the password is not too long (e.g. 4096). This is for security - (see `CVE-2013-5750`_), and you can copy the `BasePasswordEncoder::checkPasswordLength`_ - implementation from Symfony 2.4. +#. ``$this->checkPasswordLength($raw);`` must be the first code executed in + ``encodePassword()`` and ``isPasswordValid()`` (see `CVE-2013-5750`_). Using Password Encoders ~~~~~~~~~~~~~~~~~~~~~~~ @@ -228,4 +226,3 @@ which should be used to encode this user's password:: $user->getSalt()); .. _`CVE-2013-5750`: http://symfony.com/blog/cve-2013-5750-security-issue-in-fosuserbundle-login-form -.. _`BasePasswordEncoder::checkPasswordLength`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Core/Encoder/BasePasswordEncoder.php diff --git a/components/stopwatch.rst b/components/stopwatch.rst index 3ac7f760a66..2598bdb3cd0 100644 --- a/components/stopwatch.rst +++ b/components/stopwatch.rst @@ -7,10 +7,6 @@ The Stopwatch Component Stopwatch component provides a way to profile code. -.. versionadded:: 2.2 - The Stopwatch component is new to Symfony 2.2. Previously, the ``Stopwatch`` - class was located in the HttpKernel component (and was new in 2.1). - Installation ------------ diff --git a/components/translation/introduction.rst b/components/translation/introduction.rst index 9ccc34c74ed..2151bebd15e 100644 --- a/components/translation/introduction.rst +++ b/components/translation/introduction.rst @@ -88,11 +88,6 @@ Loader too. The default loaders are: * :class:`Symfony\\Component\\Translation\\Loader\\YamlFileLoader` - to load catalogs from Yaml files (requires the :doc:`Yaml component`). -.. versionadded:: 2.1 - The ``IcuDatFileLoader``, ``IcuResFileLoader``, ``IniFileLoader``, - ``MofileLoader``, ``PoFileLoader`` and ``QtFileLoader`` were added in - Symfony 2.1 - All file loaders require the :doc:`Config component `. At first, you should add one or more loaders to the ``Translator``:: diff --git a/cookbook/bundles/installation.rst b/cookbook/bundles/installation.rst index d0ea5d677fb..3d6ccb57a1e 100644 --- a/cookbook/bundles/installation.rst +++ b/cookbook/bundles/installation.rst @@ -10,8 +10,8 @@ basic steps for installing a bundle are the same. Add Composer Dependencies ------------------------- -Starting from Symfony 2.1, dependencies are managed with Composer. It's -a good idea to learn some basics of Composer in `their documentation`_. +In Symfony, dependencies are managed with Composer. It's a good idea to learn +some basics of Composer in `their documentation`_. Before you can use Composer to install a bundle, you should look for a `Packagist`_ package of that bundle. For example, if you search for the popular @@ -33,11 +33,6 @@ file. If it isn't, you can use the version you want. If you choose an incompatib version, Composer will throw dependency errors when you try to install. If this happens, you can try a different version. -In the case of the FOSUserBundle, the ``README`` file has a caution that version -1.2.0 must be used for Symfony 2.0 and 1.3+ for Symfony 2.1+. Packagist displays -example ``require`` statements for all existing versions of a package. The -current development version of FOSUserBundle is ``"friendsofsymfony/user-bundle": "2.0.*@dev"``. - Now you can add the bundle to your ``composer.json`` file and update the dependencies. You can do this manually: diff --git a/cookbook/configuration/override_dir_structure.rst b/cookbook/configuration/override_dir_structure.rst index 31aeb973847..6112b67edde 100644 --- a/cookbook/configuration/override_dir_structure.rst +++ b/cookbook/configuration/override_dir_structure.rst @@ -91,8 +91,8 @@ may need to modify the paths inside these files:: require_once __DIR__.'/../Symfony/app/bootstrap.php.cache'; require_once __DIR__.'/../Symfony/app/AppKernel.php'; -Since Symfony 2.1 (in which Composer is introduced), you also need to change -the ``extra.symfony-web-dir`` option in the ``composer.json`` file: +You also need to change the ``extra.symfony-web-dir`` option in the ``composer.json`` +file: .. code-block:: json diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index 7e811d205f7..6684731a4c5 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -14,13 +14,6 @@ Symfony2 has a built-in solution for database session storage called To use it, you just need to change some parameters in ``config.yml`` (or the configuration format of your choice): -.. versionadded:: 2.1 - In Symfony 2.1 the class and namespace are slightly modified. You can now - find the session storage classes in the ``Session\Storage`` namespace: - ``Symfony\Component\HttpFoundation\Session\Storage``. Also - note that in Symfony 2.1 you should configure ``handler_id`` not ``storage_id`` like in Symfony 2.0. - Below, you'll notice that ``%session.storage.options%`` is not used anymore. - .. configuration-block:: .. code-block:: yaml diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index a9f4bac91ed..6e3721a24ef 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -62,6 +62,59 @@ This command will now automatically be available to run: $ app/console demo:greet Fabien +.. _cookbook-console-dic: + +Register Commands in the Service Container +------------------------------------------ + +.. versionadded:: 2.4 + Support for registering commands in the service container was added in + version 2.4. + +Instead of putting your command in the ``Command`` directory and having Symfony +auto-discover it for you, you can register commands in the service container +using the ``console.command`` tag: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + acme_hello.command.my_command: + class: Acme\HelloBundle\Command\MyCommand + tags: + - { name: console.command } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + + $container + ->register('acme_hello.command.my_command', 'Acme\HelloBundle\Command\MyCommand') + ->addTag('console.command') + ; + +.. tip:: + + Registering your command as a service gives you more control over its + location and the services that are injected into it. But, there are no + functional advantages, so you don't need to register your command as a service. + Getting Services from the Service Container ------------------------------------------- @@ -106,7 +159,6 @@ instead of $commandTester = new CommandTester($command); $commandTester->execute( array( - 'command' => $command->getName(), 'name' => 'Fabien', '--yell' => true, ) @@ -118,6 +170,11 @@ instead of } } +.. versionadded:: 2.4 + Since Symfony 2.4, the ``CommandTester`` automatically detects the name of + the command to execute. Thus, you don't need to pass it via the ``command`` + key anymore. + .. note:: In the specific case above, the ``name`` parameter and the ``--yell`` option @@ -147,7 +204,6 @@ you can extend your test from $commandTester = new CommandTester($command); $commandTester->execute( array( - 'command' => $command->getName(), 'name' => 'Fabien', '--yell' => true, ) diff --git a/cookbook/console/sending_emails.rst b/cookbook/console/sending_emails.rst index ccc1d1947d5..450e415b22c 100644 --- a/cookbook/console/sending_emails.rst +++ b/cookbook/console/sending_emails.rst @@ -17,16 +17,13 @@ what URL it should use when generating URLs. There are two ways of configuring the request context: at the application level and per Command. -Configuring the Request Context globally +Configuring the Request Context Globally ---------------------------------------- -.. versionadded: 2.2 - The ``base_url`` parameter is available since Symfony 2.2 - To configure the Request Context - which is used by the URL Generator - you can redefine the parameters it uses as default values to change the default host -(localhost) and scheme (http). Starting with Symfony 2.2 you can also configure -the base path if Symfony is not running in the root directory. +(localhost) and scheme (http). You can also configure the base path if Symfony +is not running in the root directory. Note that this does not impact URLs generated via normal web requests, since those will override the defaults. diff --git a/cookbook/doctrine/console.rst b/cookbook/doctrine/console.rst new file mode 100644 index 00000000000..b48cd3e3f3c --- /dev/null +++ b/cookbook/doctrine/console.rst @@ -0,0 +1,60 @@ +.. index:: + single: Doctrine; ORM console commands + single: CLI; Doctrine ORM + +Console Commands +---------------- + +The Doctrine2 ORM integration offers several console commands under the +``doctrine`` namespace. To view the command list you can run the console +without any arguments: + +.. code-block:: bash + + $ php app/console + +A list of available commands will print out, many of which start with the +``doctrine:`` prefix. You can find out more information about any of these +commands (or any Symfony command) by running the ``help`` command. For example, +to get details about the ``doctrine:database:create`` task, run: + +.. code-block:: bash + + $ php app/console help doctrine:database:create + +Some notable or interesting tasks include: + +* ``doctrine:ensure-production-settings`` - checks to see if the current + environment is configured efficiently for production. This should always + be run in the ``prod`` environment: + + .. code-block:: bash + + $ php app/console doctrine:ensure-production-settings --env=prod + +* ``doctrine:mapping:import`` - allows Doctrine to introspect an existing + database and create mapping information. For more information, see + :doc:`/cookbook/doctrine/reverse_engineering`. + +* ``doctrine:mapping:info`` - tells you all of the entities that Doctrine + is aware of and whether or not there are any basic errors with the mapping. + +* ``doctrine:query:dql`` and ``doctrine:query:sql`` - allow you to execute + DQL or SQL queries directly from the command line. + +.. note:: + + To be able to load data fixtures to your database, you will need to have + the DoctrineFixturesBundle bundle installed. To learn how to do it, + read the ":doc:`/bundles/DoctrineFixturesBundle/index`" entry of the + documentation. + +.. tip:: + + This page shows working with Doctrine within a controller. You may also + want to work with Doctrine elsewhere in your application. The + :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::getDoctrine` + method of the controller returns the ``doctrine`` service, you can work with + this in the same way elsewhere by injecting this into your own + services. See :doc:`/book/service_container` for more on creating + your own services. diff --git a/cookbook/doctrine/index.rst b/cookbook/doctrine/index.rst index 7f19ef7d4fe..a62c736db11 100644 --- a/cookbook/doctrine/index.rst +++ b/cookbook/doctrine/index.rst @@ -14,3 +14,4 @@ Doctrine resolve_target_entity mapping_model_classes registration_form + console diff --git a/cookbook/expression/expressions.rst b/cookbook/expression/expressions.rst new file mode 100644 index 00000000000..aba6431edfc --- /dev/null +++ b/cookbook/expression/expressions.rst @@ -0,0 +1,24 @@ +.. index:: + single: Expressions in the Framework + +How to use Expressions in Security, Routing, Services, and Validation +===================================================================== + +.. versionadded:: 2.4 + The expression functionality was introduced in Symfony 2.4. + +In Symfony 2.4, a powerful :doc:`ExpressionLanguage ` +component was added to Symfony. This allows us to add highly customized +logic inside configuration. + +The Symfony Framework leverages expressions out of the box in the following +ways: + +* :ref:`Configuring services `; +* :ref:`Route matching conditions `; +* :ref:`Checking security ` and + :ref:`access controls with allow_if `; +* :doc:`Validation `. + +For more information about how to create and work with expressions, see +:doc:`/components/expression_language/syntax`. diff --git a/cookbook/expression/index.rst b/cookbook/expression/index.rst new file mode 100644 index 00000000000..909ecc72224 --- /dev/null +++ b/cookbook/expression/index.rst @@ -0,0 +1,7 @@ +Expressions +=========== + +.. toctree:: + :maxdepth: 2 + + expressions diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index 7af64ecd09f..d05dc32fe59 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -129,11 +129,6 @@ the event listener might look like the following:: }); } -.. versionadded:: 2.2 - The ability to pass a string into - :method:`FormInterface::add ` - was added in Symfony 2.2. - .. note:: You can of course use any callback type instead of a closure, e.g. a method call on the ``ProductType`` object itself for better readability:: @@ -526,10 +521,6 @@ On a form, we can usually listen to the following events: The events ``PRE_SUBMIT``, ``SUBMIT`` and ``POST_SUBMIT`` were added in Symfony 2.3. Before, they were named ``PRE_BIND``, ``BIND`` and ``POST_BIND``. -.. versionadded:: 2.2.6 - The behavior of the ``POST_SUBMIT`` event changed slightly in 2.2.6, which the - below example uses. - The key is to add a ``POST_SUBMIT`` listener to the field that your new field depends on. If you add a ``POST_SUBMIT`` listener to a form child (e.g. ``sport``), and add new children to the parent form, the Form component will detect the diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 6a80c068df7..3b4f616947d 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -989,4 +989,4 @@ customizations directly. Look at the following example: The array passed as the second argument contains form "variables". For more details about this concept in Twig, see :ref:`twig-reference-form-variables`. -.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/2.3/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig diff --git a/cookbook/index.rst b/cookbook/index.rst index 07869cad14a..428661bc374 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -15,6 +15,7 @@ The Cookbook doctrine/index email/index event_dispatcher/index + expression/index form/index logging/index profiler/index diff --git a/cookbook/logging/channels_handlers.rst b/cookbook/logging/channels_handlers.rst index 566b8a9a338..f4065d22352 100644 --- a/cookbook/logging/channels_handlers.rst +++ b/cookbook/logging/channels_handlers.rst @@ -115,9 +115,8 @@ Configure Additional Channels without Tagged Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2.3 - This feature was introduced to the MonologBundle in version 2.4. This - version is compatible with Symfony 2.3, but only MonologBundle 2.3 is - installed by default. To use this feature, upgrade your bundle manually. + This feature was introduced to the MonologBundle in version 2.4, which + was first packaged with Symfony at version 2.4. With MonologBundle 2.4 you can configure additional channels without the need to tag your services: 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/logging/monolog_email.rst b/cookbook/logging/monolog_email.rst index edcd787b08c..3dc89f4e254 100644 --- a/cookbook/logging/monolog_email.rst +++ b/cookbook/logging/monolog_email.rst @@ -107,14 +107,6 @@ to and from addresses and the subject. You can combine these handlers with other handlers so that the errors still get logged on the server as well as the emails being sent: -.. caution:: - - The default spool setting for swiftmailer is set to ``memory``, which - means that emails are sent at the very end of the request. However, this - does not work with buffered logs at the moment. In order to enable emailing - logs per the example below, you must comment out the ``spool: { type: memory }`` - line in the ``config.yml`` file. - .. configuration-block:: .. code-block:: yaml diff --git a/cookbook/logging/monolog_regex_based_excludes.rst b/cookbook/logging/monolog_regex_based_excludes.rst index f7f02d51131..71bc77a2a12 100644 --- a/cookbook/logging/monolog_regex_based_excludes.rst +++ b/cookbook/logging/monolog_regex_based_excludes.rst @@ -6,10 +6,9 @@ How to Configure Monolog to Exclude 404 Errors from the Log =========================================================== -.. versionadded:: 2.3 - This feature was introduced to the MonologBundle in version 2.4. This - version is compatible with Symfony 2.3, but only MonologBundle 2.3 is - installed by default. To use this feature, upgrade your bundle manually. +.. versionadded:: 2.4 + This feature was introduced to the MonologBundle in version 2.4, which + was first packaged with Symfony at version 2.4. Sometimes your logs become flooded with unwanted 404 HTTP errors, for example, when an attacker scans your app for some well-known application paths (e.g. diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index b2777f42f5e..b4833557d7a 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -62,6 +62,7 @@ * :doc:`/cookbook/doctrine/resolve_target_entity` * :doc:`/cookbook/doctrine/mapping_model_classes` * :doc:`/cookbook/doctrine/registration_form` + * :doc:`/cookbook/doctrine/console` * :doc:`/cookbook/email/index` @@ -78,6 +79,10 @@ * :doc:`/cookbook/event_dispatcher/method_behavior` * (service container) :doc:`/cookbook/service_container/event_listener` +* :doc:`/cookbook/expression/index` + + * :doc:`/cookbook/expression/expressions` + * :doc:`/cookbook/form/index` * :doc:`/cookbook/form/form_customization` @@ -97,6 +102,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` @@ -130,9 +136,12 @@ * :doc:`/cookbook/security/acl` * :doc:`/cookbook/security/acl_advanced` * :doc:`/cookbook/security/force_https` + * :doc:`/cookbook/security/host_restriction` * :doc:`/cookbook/security/form_login` * :doc:`/cookbook/security/securing_services` * :doc:`/cookbook/security/custom_provider` + * :doc:`/cookbook/security/custom_password_authenticator` + * :doc:`/cookbook/security/api_key_authentication` * :doc:`/cookbook/security/custom_authentication_provider` * :doc:`/cookbook/security/target_path` * :doc:`/cookbook/security/csrf_in_login_form` @@ -153,6 +162,7 @@ * :doc:`/cookbook/session/locale_sticky_session` * :doc:`/cookbook/session/sessions_directory` * :doc:`/cookbook/session/php_bridge` + * :doc:`/cookbook/session/limit_metadata_writes` * **symfony1** diff --git a/cookbook/security/acl_advanced.rst b/cookbook/security/acl_advanced.rst index 1bc217d8e6e..01228220bb6 100644 --- a/cookbook/security/acl_advanced.rst +++ b/cookbook/security/acl_advanced.rst @@ -45,6 +45,13 @@ Security Identities This is analog to the object identity, but represents a user, or a role in your application. Each role, or user has its own security identity. +.. versionadded:: 2.5 + For users, the security identity is based on the username. This means that, + if for any reason, a user's username was to change, you must ensure its + security identity is updated too. The + :method:`MutableAclProvider::updateUserSecurityIdentity() ` + method is there to handle the update. + Database Table Structure ------------------------ diff --git a/cookbook/security/api_key_authentication.rst b/cookbook/security/api_key_authentication.rst new file mode 100644 index 00000000000..48aa3f0aa20 --- /dev/null +++ b/cookbook/security/api_key_authentication.rst @@ -0,0 +1,208 @@ +.. index:: + single: Security; Custom Request Authenticator + +How to Authenticate Users with API Keys +======================================= + +Nowadays, it's quite usual to authenticate the user via an API key (when developing +a web service for instance). The API key is provided for every request and is +passed as a query string parameter or via a HTTP header. + +The API Key Authenticator +------------------------- + +.. versionadded:: 2.4 + The ``SimplePreAuthenticatorInterface`` interface was added in Symfony 2.4. + +Authenticating a user based on the Request information should be done via a +pre-authentication mechanism. The :class:`Symfony\\Component\\Security\\Core\\Authentication\\SimplePreAuthenticatorInterface` +interface allows to implement such a scheme really easily:: + + // src/Acme/HelloBundle/Security/ApiKeyAuthenticator.php + namespace Acme\HelloBundle\Security; + + use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Exception\AuthenticationException; + use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\Security\Core\User\User; + use Symfony\Component\Security\Core\User\UserProviderInterface; + use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; + use Symfony\Component\Security\Core\Exception\BadCredentialsException; + + class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface + { + protected $userProvider; + + public function __construct(ApiKeyUserProviderInterface $userProvider) + { + $this->userProvider = $userProvider; + } + + public function createToken(Request $request, $providerKey) + { + if (!$request->query->has('apikey')) { + throw new BadCredentialsException('No API key found'); + } + + return new PreAuthenticatedToken( + 'anon.', + $request->query->get('apikey'), + $providerKey + ); + } + + public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) + { + $apikey = $token->getCredentials(); + if (!$this->userProvider->getUsernameForApiKey($apikey)) { + throw new AuthenticationException( + sprintf('API Key "%s" does not exist.', $apikey) + ); + } + + $user = new User( + $this->userProvider->getUsernameForApiKey($apikey), + $apikey, + array('ROLE_USER') + ); + + return new PreAuthenticatedToken( + $user, + $apikey, + $providerKey, + $user->getRoles() + ); + } + + public function supportsToken(TokenInterface $token, $providerKey) + { + return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey; + } + } + +``$userProvider`` can be any user provider implementing an interface similar to +this:: + + // src/Acme/HelloBundle/Security/ApiKeyUserProviderInterface.php + namespace Acme\HelloBundle\Security; + + use Symfony\Component\Security\Core\User\UserProviderInterface; + + interface ApiKeyUserProviderInterface extends UserProviderInterface + { + public function getUsernameForApiKey($apikey); + } + +.. note:: + + Read the dedicated article to learn + :doc:`how to create a custom user provider `. + +To access a resource protected by such an authenticator, you need to add an apikey +parameter to the query string, like in ``http://example.com/admin/foo?apikey=37b51d194a7513e45b56f6524f2d51f2``. + +Configuration +------------- + +Configure your ``ApiKeyAuthenticator`` as a service: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + # ... + + apikey_authenticator: + class: Acme\HelloBundle\Security\ApiKeyAuthenticator + arguments: [@your_api_key_user_provider] + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + + // ... + + $container->setDefinition('apikey_authenticator', new Definition( + 'Acme\HelloBundle\Security\ApiKeyAuthenticator', + array(new Reference('your_api_key_user_provider')) + )); + +Then, activate it in your firewalls section using the ``simple-preauth`` key +like this: + +.. configuration-block:: + + .. code-block:: yaml + + security: + firewalls: + secured_area: + pattern: ^/admin + simple-preauth: + provider: ... + authenticator: apikey_authenticator + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // .. + + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + 'pattern' => '^/admin', + 'provider' => 'authenticator', + 'simple-preauth' => array( + 'provider' => ..., + 'authenticator' => 'apikey_authenticator', + ), + ), + ), + )); diff --git a/cookbook/security/custom_authentication_provider.rst b/cookbook/security/custom_authentication_provider.rst index 7bdf7f5e213..03ac57d0f9c 100644 --- a/cookbook/security/custom_authentication_provider.rst +++ b/cookbook/security/custom_authentication_provider.rst @@ -153,18 +153,21 @@ set an authenticated token in the security context if successful. // Deny authentication with a '403 Forbidden' HTTP response $response = new Response(); - $response->setStatusCode(403); + $response->setStatusCode(Response::HTTP_FORBIDDEN); $event->setResponse($response); } // By default deny authorization $response = new Response(); - $response->setStatusCode(403); + $response->setStatusCode(Response::HTTP_FORBIDDEN); $event->setResponse($response); } } +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + This listener checks the request for the expected ``X-WSSE`` header, matches the value returned for the expected WSSE information, creates a token using that information, and passes the token on to the authentication manager. If diff --git a/cookbook/security/custom_password_authenticator.rst b/cookbook/security/custom_password_authenticator.rst new file mode 100644 index 00000000000..2560bd52223 --- /dev/null +++ b/cookbook/security/custom_password_authenticator.rst @@ -0,0 +1,225 @@ +.. index:: + single: Security; Custom Password Authenticator + +How to create a Custom Password Authenticator +============================================= + +Imagine you want to allow access to your website only between 2pm and 4pm (for +the UTC timezone). Before Symfony 2.4, you had to create a custom token, factory, +listener and provider. + +The Password Authenticator +-------------------------- + +.. versionadded:: 2.4 + The ``SimpleFormAuthenticatorInterface`` interface was added in Symfony 2.4. + +But now, thanks to new simplified authentication customization options in +Symfony 2.4, you don't need to create a whole bunch of new classes, but use the +:class:`Symfony\\Component\\Security\\Core\\Authentication\\SimpleFormAuthenticatorInterface` +interface instead:: + + // src/Acme/HelloBundle/Security/TimeAuthenticator.php + namespace Acme\HelloBundle\Security; + + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface; + use Symfony\Component\Security\Core\Authentication\TokenInterface; + use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; + use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; + use Symfony\Component\Security\Core\Exception\AuthenticationException; + use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; + use Symfony\Component\Security\Core\User\UserProviderInterface; + + class TimeAuthenticator implements SimpleFormAuthenticatorInterface + { + private $encoderFactory; + + public function __construct(EncoderFactoryInterface $encoderFactory) + { + $this->encoderFactory = $encoderFactory; + } + + public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) + { + try { + $user = $userProvider->loadUserByUsername($token->getUsername()); + } catch (UsernameNotFoundException $e) { + throw new AuthenticationException('Invalid username or password'); + } + + $encoder = $this->encoderFactory->getEncoder($user); + $passwordValid = $encoder->isPasswordValid( + $user->getPassword(), + $token->getCredentials(), + $user->getSalt() + ); + + if ($passwordValid) { + $currentHour = date('G'); + if ($currentHour < 14 || $currentHour > 16) { + throw new AuthenticationException( + 'You can only log in between 2 and 4!', + 100 + ); + } + + return new UsernamePasswordToken( + $user->getUsername(), + $user->getPassword(), + $providerKey, + $user->getRoles() + ); + } + + throw new AuthenticationException('Invalid username or password'); + } + + public function supportsToken(TokenInterface $token, $providerKey) + { + return $token instanceof UsernamePasswordToken + && $token->getProviderKey() === $providerKey; + } + + public function createToken(Request $request, $username, $password, $providerKey) + { + return new UsernamePasswordToken($username, $password, $providerKey); + } + } + +How it Works +------------ + +There are a lot of things going on: + +* ``createToken()`` creates a Token that will be used to authenticate the user; +* ``authenticateToken()`` checks that the Token is allowed to log in by first + getting the User via the user provider and then, by checking the password + and the current time (a Token with roles is authenticated); +* ``supportsToken()`` is just a way to allow several authentication mechanisms to + be used for the same firewall (that way, you can for instance first try to + authenticate the user via a certificate or an API key and fall back to a + form login); +* An encoder is needed to check the user password's validity; this is a + service provided by default:: + + $encoder = $this->encoderFactory->getEncoder($user); + $passwordValid = $encoder->isPasswordValid( + $user->getPassword(), + $token->getCredentials(), + $user->getSalt() + ); + +Configuration +------------- + +Now, configure your ``TimeAuthenticator`` as a service: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + # ... + + time_authenticator: + class: Acme\HelloBundle\Security\TimeAuthenticator + arguments: [@security.encoder_factory] + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\Reference; + + // ... + + $container->setDefinition('time_authenticator', new Definition( + 'Acme\HelloBundle\Security\TimeAuthenticator', + array(new Reference('security.encoder_factory')) + )); + +Then, activate it in your ``firewalls`` section using the ``simple-form`` key +like this: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + + firewalls: + secured_area: + pattern: ^/admin + provider: authenticator + simple-form: + provider: ... + authenticator: time_authenticator + check_path: login_check + login_path: login + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // .. + + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + 'pattern' => '^/admin', + 'provider' => 'authenticator', + 'simple-form' => array( + 'provider' => ..., + 'authenticator' => 'time_authenticator', + 'check_path' => 'login_check', + 'login_path' => 'login', + ), + ), + ), + )); diff --git a/cookbook/security/host_restriction.rst b/cookbook/security/host_restriction.rst new file mode 100644 index 00000000000..232f1cd5ff6 --- /dev/null +++ b/cookbook/security/host_restriction.rst @@ -0,0 +1,70 @@ +.. index:: + single: Security; Restrict Security Firewalls to a Host + +How to Restrict Firewalls to a Specific Host +============================================ + +.. versionadded:: 2.4 + Support for restricting security firewalls to a specific host was added in + Symfony 2.4. + +When using the Security component, you can create firewalls that match certain +URL patterns and therefore are activated for all pages whose URL matches +that pattern. Additionally, you can restrict the initialization of a firewall +to a host using the ``host`` key: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + + # ... + + security: + firewalls: + secured_area: + pattern: ^/ + host: ^admin\.example\.com$ + http_basic: true + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // ... + + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + 'pattern' => '^/', + 'host' => '^admin\.example\.com$', + 'http_basic' => true, + ), + ), + )); + +The ``host`` (like the ``pattern``) is a regular expression. In this example, +the firewall will only be activated if the host is equal exactly (due to +the ``^`` and ``$`` regex characters) to the hostname ``admin.example.com``. +If the hostname does not match this pattern, the firewall will not be activated +and subsequent firewalls will have the opportunity to be matched for this +request. diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 4ba14308abb..63bd29520b3 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -11,9 +11,12 @@ Security acl acl_advanced force_https + host_restriction form_login securing_services custom_provider + custom_password_authenticator + 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 2ab50f5d639..8798bcefa06 100644 --- a/cookbook/security/voters.rst +++ b/cookbook/security/voters.rst @@ -61,19 +61,18 @@ and compare the IP address against a set of blacklisted IP addresses: // src/Acme/DemoBundle/Security/Authorization/Voter/ClientIpVoter.php namespace Acme\DemoBundle\Security\Authorization\Voter; - use Symfony\Component\DependencyInjection\ContainerInterface; + use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; class ClientIpVoter implements VoterInterface { - private $container; - + protected $requestStack; private $blacklistedIp; - - public function __construct(ContainerInterface $container, array $blacklistedIp = array()) + + public function __construct(RequestStack $requestStack, array $blacklistedIp = array()) { - $this->container = $container; + $this->requestStack = $requestStack; $this->blacklistedIp = $blacklistedIp; } @@ -91,7 +90,7 @@ and compare the IP address against a set of blacklisted IP addresses: public function vote(TokenInterface $token, $object, array $attributes) { - $request = $this->container->get('request'); + $request = $this->requestStack->getCurrentRequest(); if (in_array($request->getClientIp(), $this->blacklistedIp)) { return VoterInterface::ACCESS_DENIED; } @@ -128,7 +127,7 @@ and tag it as a ``security.voter``: services: security.access.blacklist_voter: class: Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter - arguments: ["@service_container", [123.123.123.123, 171.171.171.171]] + arguments: ["@request_stack", [123.123.123.123, 171.171.171.171]] public: false tags: - { name: security.voter } @@ -138,7 +137,7 @@ and tag it as a ``security.voter``: - + 123.123.123.123 171.171.171.171 @@ -155,7 +154,7 @@ and tag it as a ``security.voter``: $definition = new Definition( 'Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter', array( - new Reference('service_container'), + new Reference('request_stack'), array('123.123.123.123', '171.171.171.171'), ), ); diff --git a/cookbook/service_container/event_listener.rst b/cookbook/service_container/event_listener.rst index d0e7ae08831..2e22ca66ecc 100644 --- a/cookbook/service_container/event_listener.rst +++ b/cookbook/service_container/event_listener.rst @@ -43,7 +43,7 @@ event is just one of the core kernel events:: $response->setStatusCode($exception->getStatusCode()); $response->headers->replace($exception->getHeaders()); } else { - $response->setStatusCode(500); + $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR); } // Send the modified response object to the event @@ -51,6 +51,9 @@ event is just one of the core kernel events:: } } +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + .. tip:: Each event receives a slightly different type of ``$event`` object. For diff --git a/cookbook/service_container/scopes.rst b/cookbook/service_container/scopes.rst index 1780195e3c0..59d4cd425a4 100644 --- a/cookbook/service_container/scopes.rst +++ b/cookbook/service_container/scopes.rst @@ -6,8 +6,17 @@ How to work with Scopes This entry is all about scopes, a somewhat advanced topic related to the :doc:`/book/service_container`. If you've ever gotten an error mentioning -"scopes" when creating services, or need to create a service that depends -on the ``request`` service, then this entry is for you. +"scopes" when creating services, then this entry is for you. + +.. note:: + + If you are trying to inject the ``request`` service, the simple solution + is to inject the ``request_stack`` service instead and access the current + Request by calling the + :method:`Symfony\\Component\\HttpFoundation\\RequestStack::getCurrentRequest` + method (see :ref:`book-container-request-stack`). The rest of this entry + talks about scopes in a theoretical and more advanced way. If you're + dealing with scopes for the ``request`` service, simply inject ``request_stack``. Understanding Scopes -------------------- @@ -27,10 +36,22 @@ also defines a third scope: ``request``. This scope is tied to the request, meaning a new instance is created for each subrequest and is unavailable outside the request (for instance in the CLI). +An Example: Client Scope +~~~~~~~~~~~~~~~~~~~~~~~~ + +Other than the ``request`` service (which has a simple solution, see the +above note), no services in the default Symfony2 container belong to any +scope other than ``container`` and ``prototype``. But for the purposes of +this entry, imagine there is another scope ``client`` and a service ``client_configuration`` +that belongs to it. This is not a common situation, but the idea is that +you may enter and exit multiple ``client`` scopes during a request, and each +has its own ``client_configuration`` service. + Scopes add a constraint on the dependencies of a service: a service cannot depend on services from a narrower scope. For example, if you create a generic -``my_foo`` service, but try to inject the ``request`` service, you will receive -a :class:`Symfony\\Component\\DependencyInjection\\Exception\\ScopeWideningInjectionException` +``my_foo`` service, but try to inject the ``client_configuration`` service, +you will receive a +:class:`Symfony\\Component\\DependencyInjection\\Exception\\ScopeWideningInjectionException` when compiling the container. Read the sidebar below for more details. .. sidebar:: Scopes and Dependencies @@ -40,28 +61,29 @@ when compiling the container. Read the sidebar below for more details. every time you ask the container for the ``my_mailer`` service, you get the same object back. This is usually how you want your services to work. - Imagine, however, that you need the ``request`` service in your ``my_mailer`` - service, maybe because you're reading the URL of the current request. - So, you add it as a constructor argument. Let's look at why this presents - a problem: + Imagine, however, that you need the ``client_configuration`` service + in your ``my_mailer`` service, maybe because you're reading some details + from it, such as what the "sender" address should be. You add it as a + constructor argument. Let's look at why this presents a problem: - * When requesting ``my_mailer``, an instance of ``my_mailer`` (let's call - it *MailerA*) is created and the ``request`` service (let's call it - *RequestA*) is passed to it. Life is good! + * When requesting ``my_mailer``, an instance of ``my_mailer`` (called + *MailerA* here) is created and the ``client_configuration`` service ( + called *ConfigurationA* here) is passed to it. Life is good! - * You've now made a subrequest in Symfony, which is a fancy way of saying - that you've called, for example, the ``{{ render(...) }}`` Twig function, - which executes another controller. Internally, the old ``request`` service - (*RequestA*) is actually replaced by a new request instance (*RequestB*). - This happens in the background, and it's totally normal. + * Your application now needs to do something with another client, and + you've architected your application in such a way that you handle this + by entering a new ``client_configuration`` scope and setting a new + ``client_configuration`` service into the container. Call this + *ConfigurationB*. - * In your embedded controller, you once again ask for the ``my_mailer`` + * Somewhere in your application, you once again ask for the ``my_mailer`` service. Since your service is in the ``container`` scope, the same instance (*MailerA*) is just re-used. But here's the problem: the - *MailerA* instance still contains the old *RequestA* object, which - is now **not** the correct request object to have (*RequestB* is now - the current ``request`` service). This is subtle, but the mis-match could - cause major problems, which is why it's not allowed. + *MailerA* instance still contains the old *ConfigurationA* object, which + is now **not** the correct configuration object to have (*ConfigurationB* + is now the current ``client_configuration`` service). This is subtle, + but the mis-match could cause major problems, which is why it's not + allowed. So, that's the reason *why* scopes exist, and how they can cause problems. Keep reading to find out the common solutions. @@ -71,22 +93,19 @@ when compiling the container. Read the sidebar below for more details. A service can of course depend on a service from a wider scope without any issue. -Using a Service from a narrower Scope +Using a Service from a Narrower Scope ------------------------------------- -If your service has a dependency on a scoped service (like the ``request``), -you have three ways to deal with it: +There are several solutions to the scope problem: -* Use setter injection if the dependency is "synchronized"; this is the - recommended way and the best solution for the ``request`` instance as it is - synchronized with the ``request`` scope (see +* A) Use setter injection if the dependency is ``synchronized`` (see :ref:`using-synchronized-service`); -* Put your service in the same scope as the dependency (or a narrower one). If - you depend on the ``request`` service, this means putting your new service - in the ``request`` scope (see :ref:`changing-service-scope`); +* B) Put your service in the same scope as the dependency (or a narrower one). If + you depend on the ``client_configuration`` service, this means putting your + new service in the ``client`` scope (see :ref:`changing-service-scope`); -* Pass the entire container to your service and retrieve your dependency from +* C) Pass the entire container to your service and retrieve your dependency from the container each time you need it to be sure you have the right instance -- your service can live in the default ``container`` scope (see :ref:`passing-container`). @@ -95,49 +114,99 @@ Each scenario is detailed in the following sections. .. _using-synchronized-service: -Using a synchronized Service -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +A) Using a Synchronized Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2.3 Synchronized services are new in Symfony 2.3. -Injecting the container or setting your service to a narrower scope have -drawbacks. For synchronized services (like the ``request``), using setter -injection is the best option as it has no drawbacks and everything works -without any special code in your service or in your definition:: +Both injecting the container and setting your service to a narrower scope have +drawbacks. Assume first that the ``client_configuration`` service has been +marked as ``synchronized``: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + client_configuration: + class: Acme\HelloBundle\Client\ClientConfiguration + scope: client + synchronized: true + synthetic: true + # ... + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + use Symfony\Component\DependencyInjection\Definition; + + $definition = new Definition( + 'Acme\HelloBundle\Client\ClientConfiguration', + array() + ); + $definition->setScope('client'); + $definition->setSynchronized(true); + $definition->setSynthetic(true); + $container->setDefinition('client_configuration', $definition); + +Now, if you inject this service using setter injection, there are no drawbacks +and everything works without any special code in your service or in your definition:: // src/Acme/HelloBundle/Mail/Mailer.php namespace Acme\HelloBundle\Mail; - use Symfony\Component\HttpFoundation\Request; + use Acme\HelloBundle\Client\ClientConfiguration; class Mailer { - protected $request; + protected $clientConfiguration; - public function setRequest(Request $request = null) + public function setClientConfiguration(ClientConfiguration $clientConfiguration = null) { - $this->request = $request; + $this->clientConfiguration = $clientConfiguration; } public function sendEmail() { - if (null === $this->request) { + if (null === $this->clientConfiguration) { // throw an error? } - // ... do something using the request here + // ... do something using the client configuration here } } -Whenever the ``request`` scope is entered or left, the service container will -automatically call the ``setRequest()`` method with the current ``request`` -instance. +Whenever the ``client`` scope is active, the service container will +automatically call the ``setClientConfiguration()`` method when the +``client_configuration`` service is set in the container. -You might have noticed that the ``setRequest()`` method accepts ``null`` as a -valid value for the ``request`` argument. That's because when leaving the -``request`` scope, the ``request`` instance can be ``null`` (for the master -request for instance). Of course, you should take care of this possibility in +You might have noticed that the ``setClientConfiguration()`` method accepts +``null`` as a valid value for the ``client_configuration`` argument. That's +because when leaving the ``client`` scope, the ``client_configuration`` instance +can be ``null``. Of course, you should take care of this possibility in your code. This should also be taken into account when declaring your service: .. configuration-block:: @@ -146,20 +215,25 @@ your code. This should also be taken into account when declaring your service: # src/Acme/HelloBundle/Resources/config/services.yml services: - greeting_card_manager: - class: Acme\HelloBundle\Mail\GreetingCardManager + my_mailer: + class: Acme\HelloBundle\Mail\Mailer calls: - - [setRequest, ['@?request=']] + - [setClientConfiguration, ['@?client_configuration=']] .. code-block:: xml - - - + + @@ -171,55 +245,25 @@ your code. This should also be taken into account when declaring your service: use Symfony\Component\DependencyInjection\ContainerInterface; $definition = $container->setDefinition( - 'greeting_card_manager', - new Definition('Acme\HelloBundle\Mail\GreetingCardManager') + 'my_mailer', + new Definition('Acme\HelloBundle\Mail\Mailer') ) - ->addMethodCall('setRequest', array( - new Reference('request', ContainerInterface::NULL_ON_INVALID_REFERENCE, false) + ->addMethodCall('setClientConfiguration', array( + new Reference( + 'client_configuration', + ContainerInterface::NULL_ON_INVALID_REFERENCE, + false + ) )); -.. tip:: - - You can declare your own ``synchronized`` services very easily; here is - the declaration of the ``request`` service for reference: - - .. configuration-block:: - - .. code-block:: yaml - - services: - request: - scope: request - synthetic: true - synchronized: true - - .. code-block:: xml - - - - - - .. code-block:: php - - use Symfony\Component\DependencyInjection\Definition; - use Symfony\Component\DependencyInjection\ContainerInterface; - - $definition = $container->setDefinition('request') - ->setScope('request') - ->setSynthetic(true) - ->setSynchronized(true); - -.. caution:: - - The service using the synchronized service will need to be public in order - to have its setter called when the scope changes. - .. _changing-service-scope: -Changing the Scope of your Service -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +B) Changing the Scope of your Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Changing the scope of a service should be done in its definition: +Changing the scope of a service should be done in its definition. This example +assumes that the ``Mailer`` class has a ``__construct`` function whose first +argument is the ``ClientConfiguration`` object: .. configuration-block:: @@ -227,20 +271,20 @@ Changing the scope of a service should be done in its definition: # src/Acme/HelloBundle/Resources/config/services.yml services: - greeting_card_manager: - class: Acme\HelloBundle\Mail\GreetingCardManager - scope: request - arguments: ["@request"] + my_mailer: + class: Acme\HelloBundle\Mail\Mailer + scope: client + arguments: [@client_configuration] .. code-block:: xml - - + .. code-block:: php @@ -249,17 +293,17 @@ Changing the scope of a service should be done in its definition: use Symfony\Component\DependencyInjection\Definition; $definition = $container->setDefinition( - 'greeting_card_manager', + 'my_mailer', new Definition( - 'Acme\HelloBundle\Mail\GreetingCardManager', - array(new Reference('request'), + 'Acme\HelloBundle\Mail\Mailer', + array(new Reference('client_configuration'), )) - )->setScope('request'); + )->setScope('client'); .. _passing-container: -Passing the Container as a Dependency of your Service -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +C) Passing the Container as a Dependency of your Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Setting the scope to a narrower one is not always possible (for instance, a twig extension must be in the ``container`` scope as the Twig environment @@ -282,15 +326,15 @@ into your service:: public function sendEmail() { - $request = $this->container->get('request'); - // ... do something using the request here + $request = $this->container->get('client_configuration'); + // ... do something using the client configuration here } } .. caution:: - Take care not to store the request in a property of the object for a - future call of the service as it would cause the same issue described + Take care not to store the client configuration in a property of the object + for a future call of the service as it would cause the same issue described in the first section (except that Symfony cannot detect that you are wrong). @@ -342,10 +386,3 @@ The service config for this class would look something like this: Injecting the whole container into a service is generally not a good idea (only inject what you need). - -.. tip:: - - If you define a controller as a service then you can get the ``Request`` - object without injecting the container by having it passed in as an - argument of your action method. See - :ref:`book-controller-request-argument` for details. diff --git a/cookbook/session/index.rst b/cookbook/session/index.rst index 536ad02c3d8..dede3362434 100644 --- a/cookbook/session/index.rst +++ b/cookbook/session/index.rst @@ -7,4 +7,5 @@ Sessions proxy_examples locale_sticky_session sessions_directory - php_bridge \ No newline at end of file + php_bridge + limit_metadata_writes diff --git a/cookbook/session/limit_metadata_writes.rst b/cookbook/session/limit_metadata_writes.rst new file mode 100644 index 00000000000..78d3923c69b --- /dev/null +++ b/cookbook/session/limit_metadata_writes.rst @@ -0,0 +1,68 @@ +.. index:: + single: Limit Metadata Writes; Session + +Limit Session Metadata Writes +============================= + +.. versionadded:: 2.4 + The ability to limit session metadata writes was added in Symfony 2.4. + +The default behavior of PHP session is to persist the session regardless of +whether the session data has changed or not. In Symfony, each time the session +is accessed, metadata is recorded (session created/last used) which can be used +to determine session age and idle time. + +If for performance reasons you wish to limit the frequency at which the session +persists, this feature can adjust the granularity of the metadata updates and +persist the session less often while still maintaining relatively accurate +metadata. If other session data is changed, the session will always persist. + +You can tell Symfony not to update the metadata "session last updated" time +until a certain amount of time has passed, by setting +``framework.session.metadata_update_threshold`` to a value in seconds greater +than zero: + +.. configuration-block:: + + .. code-block:: yaml + + framework: + session: + metadata_update_threshold: 120 + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + $container->loadFromExtension('framework', array( + 'session' => array( + 'metadata_update_threshold' => 120, + ), + )); + +.. note:: + + PHP default's behavior is to save the session whether it has been changed or + not. When using ``framework.session.metadata_update_threshold`` Symfony + will wrap the session handler (configured at + ``framework.session.handler_id``) into the WriteCheckSessionHandler. This + will prevent any session write if the session was not modified. + +.. caution:: + + Be aware that if the session is not written at every request, it may be + garbage collected sooner than usual. This means that your users may be + logged out sooner than expected. diff --git a/cookbook/templating/PHP.rst b/cookbook/templating/PHP.rst index bd3dba7ff26..556e05ac528 100644 --- a/cookbook/templating/PHP.rst +++ b/cookbook/templating/PHP.rst @@ -310,6 +310,21 @@ portable. Thanks to this helper, you can move the application root directory anywhere under your web root directory without changing anything in your template's code. +Profiling Templates +~~~~~~~~~~~~~~~~~~~ + +By using the ``stopwatch`` helper, you are able to time parts of your template +and display it on the timeline of the WebProfilerBundle:: + + start('foo') ?> + ... things that get timed + stop('foo') ?> + +.. tip:: + + If you use the same name more than once in your template, the times are + grouped on the same line in the timeline. + Output Escaping --------------- diff --git a/cookbook/templating/namespaced_paths.rst b/cookbook/templating/namespaced_paths.rst index 55a73b69573..97168b74ab5 100644 --- a/cookbook/templating/namespaced_paths.rst +++ b/cookbook/templating/namespaced_paths.rst @@ -4,9 +4,6 @@ How to use and Register namespaced Twig Paths ============================================= -.. versionadded:: 2.2 - Namespaced path support was added in 2.2. - Usually, when you refer to a template, you'll use the ``MyBundle:Subdir:filename.html.twig`` format (see :ref:`template-naming-locations`). diff --git a/cookbook/templating/render_without_controller.rst b/cookbook/templating/render_without_controller.rst index f2a44f7e4e1..a00c9089f1a 100644 --- a/cookbook/templating/render_without_controller.rst +++ b/cookbook/templating/render_without_controller.rst @@ -77,10 +77,6 @@ this is probably only useful if you'd like to cache this page partial (see Caching the static Template --------------------------- -.. versionadded:: 2.2 - The ability to cache templates rendered via ``FrameworkBundle:Template:template`` - is new in Symfony 2.2. - Since templates that are rendered in this way are typically static, it might make sense to cache them. Fortunately, this is easy! By configuring a few other variables in your route, you can control exactly how your page is cached: @@ -134,4 +130,4 @@ object created in the controller. For more information on caching, see There is also a ``private`` variable (not shown here). By default, the Response will be made public, as long as ``maxAge`` or ``sharedMaxAge`` are passed. -If set to ``true``, the Response will be marked as private. \ No newline at end of file +If set to ``true``, the Response will be marked as private. diff --git a/cookbook/testing/insulating_clients.rst b/cookbook/testing/insulating_clients.rst index fa91e591813..5e908e2e98c 100644 --- a/cookbook/testing/insulating_clients.rst +++ b/cookbook/testing/insulating_clients.rst @@ -7,19 +7,26 @@ How to test the Interaction of several Clients If you need to simulate an interaction between different clients (think of a chat for instance), create several clients:: + // ... + $harry = static::createClient(); $sally = static::createClient(); $harry->request('POST', '/say/sally/Hello'); $sally->request('GET', '/messages'); - $this->assertEquals(201, $harry->getResponse()->getStatusCode()); + $this->assertEquals(Response::HTTP_CREATED, $harry->getResponse()->getStatusCode()); $this->assertRegExp('/Hello/', $sally->getResponse()->getContent()); +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. + This works except when your code maintains a global state or if it depends on a third-party library that has some kind of global state. In such a case, you can insulate your clients:: + // ... + $harry = static::createClient(); $sally = static::createClient(); @@ -29,7 +36,7 @@ can insulate your clients:: $harry->request('POST', '/say/sally/Hello'); $sally->request('GET', '/messages'); - $this->assertEquals(201, $harry->getResponse()->getStatusCode()); + $this->assertEquals(Response::HTTP_CREATED, $harry->getResponse()->getStatusCode()); $this->assertRegExp('/Hello/', $sally->getResponse()->getContent()); Insulated clients transparently execute their requests in a dedicated and diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 290767552b3..2305a55990e 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -61,12 +61,11 @@ have a ``Symfony/`` directory that looks like this: .. note:: If you are familiar with `Composer`_, you can download Composer and then - run the following command instead of downloading the archive (replacing - ``2.3.0`` with the latest Symfony release like ``2.3.1``): + run the following command instead of downloading the archive: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition Symfony 2.3.0 + $ php composer.phar create-project symfony/framework-standard-edition Symfony 2.4.* .. _`quick-tour-big-picture-built-in-server`: @@ -232,7 +231,10 @@ controller might create the response by hand, based on the request:: $name = $request->query->get('name'); - return new Response('Hello '.$name, 200, array('Content-Type' => 'text/plain')); + return new Response('Hello '.$name, Response::HTTP_OK, array('Content-Type' => 'text/plain')); + +.. versionadded:: 2.4 + Support for HTTP status code constants was added in Symfony 2.4. .. note:: @@ -421,7 +423,7 @@ When loaded and enabled (by default in the ``dev`` :ref:`environment + ` | +----------------+------------------------------------------------------------------------+ -| Options | - `methods`_ | +| Options | - :ref:`callback ` | +----------------+------------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\Callback` | +----------------+------------------------------------------------------------------------+ | Validator | :class:`Symfony\\Component\\Validator\\Constraints\\CallbackValidator` | +----------------+------------------------------------------------------------------------+ -Setup ------ +Configuration +------------- .. configuration-block:: @@ -37,8 +42,7 @@ Setup # src/Acme/BlogBundle/Resources/config/validation.yml Acme\BlogBundle\Entity\Author: constraints: - - Callback: - methods: [isAuthorValid] + - Callback: [validate] .. code-block:: php-annotations @@ -46,12 +50,17 @@ Setup namespace Acme\BlogBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\ExecutionContextInterface; - /** - * @Assert\Callback(methods={"isAuthorValid"}) - */ class Author { + /** + * @Assert\Callback + */ + public function validate(ExecutionContextInterface $context) + { + // ... + } } .. code-block:: xml @@ -63,11 +72,7 @@ Setup xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd"> - - - + validate @@ -83,9 +88,7 @@ Setup { public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addConstraint(new Assert\Callback(array( - 'methods' => array('isAuthorValid'), - ))); + $metadata->addConstraint(new Assert\Callback('validate')); } } @@ -98,133 +101,182 @@ those errors should be attributed:: // ... use Symfony\Component\Validator\ExecutionContextInterface; - + class Author { // ... private $firstName; - - public function isAuthorValid(ExecutionContextInterface $context) + + public function validate(ExecutionContextInterface $context) { // somehow you have an array of "fake names" - $fakeNames = array(); - + $fakeNames = array(/* ... */); + // check if the name is actually a fake name if (in_array($this->getFirstName(), $fakeNames)) { - $context->addViolationAt('firstname', 'This name sounds totally fake!', array(), null); + $context->addViolationAt( + 'firstName', + 'This name sounds totally fake!', + array(), + null + ); } } } -Options -------- +Static Callbacks +---------------- -methods -~~~~~~~ +You can also use the constraint with static methods. Since static methods don't +have access to the object instance, they receive the object as the first argument:: -**type**: ``array`` **default**: ``array()`` [:ref:`default option `] + public static function validate($object, ExecutionContextInterface $context) + { + // somehow you have an array of "fake names" + $fakeNames = array(/* ... */); + + // check if the name is actually a fake name + if (in_array($object->getFirstName(), $fakeNames)) { + $context->addViolationAt( + 'firstName', + 'This name sounds totally fake!', + array(), + null + ); + } + } -This is an array of the methods that should be executed during the validation -process. Each method can be one of the following formats: +External Callbacks and Closures +------------------------------- -1) **String method name** +If you want to execute a static callback method that is not located in the class +of the validated object, you can configure the constraint to invoke an array +callable as supported by PHP's :phpfunction:`call_user_func` function. Suppose +your validation function is ``Vendor\Package\Validator::validate()``:: - If the name of a method is a simple string (e.g. ``isAuthorValid``), that - method will be called on the same object that's being validated and the - ``ExecutionContextInterface`` will be the only argument (see the above example). + namespace Vendor\Package; -2) **Static array callback** + use Symfony\Component\Validator\ExecutionContextInterface; - Each method can also be specified as a standard array callback: + class Validator + { + public function validate($object, ExecutionContextInterface $context) + { + // ... + } + } - .. configuration-block:: +You can then use the following configuration to invoke this validator: - .. code-block:: yaml +.. configuration-block:: - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Author: - constraints: - - Callback: - methods: - - [Acme\BlogBundle\MyStaticValidatorClass, isAuthorValid] + .. code-block:: yaml - .. code-block:: php-annotations + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author: + constraints: + - Callback: [Vendor\Package\Validator, validate] - // src/Acme/BlogBundle/Entity/Author.php - use Symfony\Component\Validator\Constraints as Assert; + .. code-block:: php-annotations - /** - * @Assert\Callback(methods={ - * { "Acme\BlogBundle\MyStaticValidatorClass", "isAuthorValid"} - * }) - */ - class Author - { - } + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + /** + * @Assert\Callback({"Vendor\Package\Validator", "validate"}) + */ + class Author + { + } - .. code-block:: xml + .. code-block:: xml - - - + + + - - - - - - + + + Vendor\Package\Validator + validate + + + - .. code-block:: php + .. code-block:: php - // src/Acme/BlogBundle/Entity/Author.php + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints\Callback; + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; - class Author + class Author + { + public static function loadValidatorMetadata(ClassMetadata $metadata) { - public $name; - - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addConstraint(new Callback(array( - 'methods' => array( - array('Acme\BlogBundle\MyStaticValidatorClass', 'isAuthorValid'), - ), - ))); - } + $metadata->addConstraint(new Assert\Callback(array( + 'Vendor\Package\Validator', + 'validate', + ))); } + } - In this case, the static method ``isAuthorValid`` will be called on the - ``Acme\BlogBundle\MyStaticValidatorClass`` class. It's passed both the original - object being validated (e.g. ``Author``) as well as the ``ExecutionContextInterface``:: +.. note:: - namespace Acme\BlogBundle; - - use Symfony\Component\Validator\ExecutionContextInterface; - use Acme\BlogBundle\Entity\Author; - - class MyStaticValidatorClass + The Callback constraint does *not* support global callback functions nor + is it possible to specify a global function or a :term:`service` method + as callback. To validate using a service, you should + :doc:`create a custom validation constraint ` + and add that new constraint to your class. + +When configuring the constraint via PHP, you can also pass a closure to the +constructor of the Callback constraint:: + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + public static function loadValidatorMetadata(ClassMetadata $metadata) { - public static function isAuthorValid(Author $author, ExecutionContextInterface $context) - { + $callback = function ($object, ExecutionContextInterface $context) { // ... - } + }; + + $metadata->addConstraint(new Assert\Callback($callback)); } + } + +Options +------- + +.. _callback-option: + +callback +~~~~~~~~ + +**type**: ``string``, ``array`` or ``Closure`` [:ref:`default option `] + +The callback option accepts three different formats for specifying the +callback method: + +* A **string** containing the name of a concrete or static method; + +* An array callable with the format ``array('', '')``; + +* A closure. - .. tip:: +Concrete callbacks receive an :class:`Symfony\\Component\\Validator\\ExecutionContextInterface` +instance as only argument. - If you specify your ``Callback`` constraint via PHP, then you also have - the option to make your callback either a PHP closure or a non-static - callback. It is *not* currently possible, however, to specify a :term:`service` - as a constraint. To validate using a service, you should - :doc:`create a custom validation constraint ` - and add that new constraint to your class. +Static or closure callbacks receive the validated object as the first argument +and the :class:`Symfony\\Component\\Validator\\ExecutionContextInterface` +instance as the second argument. diff --git a/reference/constraints/CardScheme.rst b/reference/constraints/CardScheme.rst index 7c4109e418b..e133ce10e70 100644 --- a/reference/constraints/CardScheme.rst +++ b/reference/constraints/CardScheme.rst @@ -1,9 +1,6 @@ CardScheme ========== -.. versionadded:: 2.2 - The CardScheme validation is new in Symfony 2.2. - This constraint ensures that a credit card number is valid for a given credit card company. It can be used to validate the number before trying to initiate a payment through a payment gateway. diff --git a/reference/constraints/Expression.rst b/reference/constraints/Expression.rst new file mode 100644 index 00000000000..430e53ecba1 --- /dev/null +++ b/reference/constraints/Expression.rst @@ -0,0 +1,167 @@ +Expression +========== + +.. versionadded:: 2.4 + The Expression constraint was introduced in Symfony 2.4. + +This constraint allows you to use an :ref:`expression ` +for more complex, dynamic validation. See `Basic Usage`_ for an example. +See :doc:`/reference/constraints/Callback` for a different constraint that +gives you similar flexibility. + ++----------------+-----------------------------------------------------------------------------------------------+ +| Applies to | :ref:`class ` or :ref:`property/method ` | ++----------------+-----------------------------------------------------------------------------------------------+ +| Options | - :ref:`expression ` | +| | - `message`_ | ++----------------+-----------------------------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Expression` | ++----------------+-----------------------------------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\ExpressionValidator` | ++----------------+-----------------------------------------------------------------------------------------------+ + +Basic Usage +----------- + +Imagine you have a class ``BlogPost`` with ``category`` and ``isTechnicalPost`` +properties:: + + namespace Acme\DemoBundle\Model; + + use Symfony\Component\Validator\Constraints as Assert; + + class BlogPost + { + private $category; + + private $isTechnicalPost; + + // ... + + public function getCategory() + { + return $this->category; + } + + public function setIsTechnicalPost($isTechnicalPost) + { + $this->isTechnicalPost = $isTechnicalPost; + } + + // ... + } + +To validate the object, you have some special requirements: + +* A) If ``isTechnicalPost`` is true, then ``category`` must be either ``php`` + or ``symfony``; + +* B) If ``isTechnicalPost`` is false, then ``category`` can be anything. + +One way to accomplish this is with the Expression constraint: + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/DemoBundle/Resources/config/validation.yml + Acme\DemoBundle\Model\BlogPost: + constraints: + - Expression: + expression: "this.getCategory() in ['php', 'symfony'] or !this.isTechnicalPost()" + message: "If this is a tech post, the category should be either php or symfony!" + + .. code-block:: php-annotations + + // src/Acme/DemoBundle/Model/BlogPost.php + namespace Acme\DemoBundle\Model\BlogPost; + + use Symfony\Component\Validator\Constraints as Assert; + + /** + * @Assert\Expression( + * "this.getCategory() in ['php', 'symfony'] or !this.isTechnicalPost()", + * message="If this is a tech post, the category should be either php or symfony!" + * ) + */ + class BlogPost + { + // ... + } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // src/Acme/DemoBundle/Model/BlogPost.php + namespace Acme\DemoBundle\Model\BlogPost; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class BlogPost + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addConstraint(new Assert\Expression(array( + 'expression' => 'this.getCategory() in ["php", "symfony"] or !this.isTechnicalPost()', + 'message' => 'If this is a tech post, the category should be either php or symfony!', + ))); + } + + // ... + } + +The :ref:`expression ` option is the +expression that must return true in order for validation to pass. To learn +more about the expression language syntax, see +:doc:`/components/expression_language/syntax`. + +For more information about the expression and what variables are available +to you, see the :ref:`expression ` +option details below. + +Available Options +----------------- + +.. _reference-constraint-expression-option: + +expression +~~~~~~~~~~ + +**type**: ``string`` [:ref:`default option `] + +The expression that will be evaluated. If the expression evaluates to a false +value (using ``==``, not ``===``), validation will fail. + +To learn more about the expression language syntax, see +:doc:`/components/expression_language/syntax`. + +Inside of the expression, you have access to up to 2 variables: + +Depending on how you use the constraint, you have access to 1 or 2 variables +in your expression: + +* ``this``: The object being validated (e.g. an instance of BlogPost); +* ``value``: The value of the property being validated (only available when + the constraint is applied directly to a property); + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value is not valid.`` + +The default message supplied when the expression evaluates to false. diff --git a/reference/constraints/Image.rst b/reference/constraints/Image.rst index 1a9caf906c0..ee970da891a 100644 --- a/reference/constraints/Image.rst +++ b/reference/constraints/Image.rst @@ -5,8 +5,13 @@ The Image constraint works exactly like the :doc:`File ` constraint for the bulk of the documentation on this constraint. @@ -19,12 +24,22 @@ the documentation on this constraint. | | - `maxWidth`_ | | | - `maxHeight`_ | | | - `minHeight`_ | +| | - `maxRatio`_ | +| | - `minRatio`_ | +| | - `allowSquare`_ | +| | - `allowLandscape`_ | +| | - `allowPortrait`_ | | | - `mimeTypesMessage`_ | | | - `sizeNotDetectedMessage`_ | | | - `maxWidthMessage`_ | | | - `minWidthMessage`_ | | | - `maxHeightMessage`_ | | | - `minHeightMessage`_ | +| | - `maxRatioMessage`_ | +| | - `minRatioMessage`_ | +| | - `allowSquareMessage`_ | +| | - `allowLandscapeMessage`_ | +| | - `allowPortraitMessage`_ | | | - See :doc:`File ` for inherited options | +----------------+-----------------------------------------------------------------------+ | Class | :class:`Symfony\\Component\\Validator\\Constraints\\File` | @@ -82,6 +97,8 @@ it is between a certain size, add the following: .. code-block:: php-annotations // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + use Symfony\Component\Validator\Constraints as Assert; class Author @@ -120,10 +137,10 @@ it is between a certain size, add the following: .. code-block:: php // src/Acme/BlogBundle/Entity/Author.php - // ... + namespace Acme/BlogBundle/Entity use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints\Image; + use Symfony\Component\Validator\Constraints as Assert; class Author { @@ -131,7 +148,7 @@ it is between a certain size, add the following: public static function loadValidatorMetadata(ClassMetadata $metadata) { - $metadata->addPropertyConstraint('headshot', new Image(array( + $metadata->addPropertyConstraint('headshot', new Assert\Image(array( 'minWidth' => 200, 'maxWidth' => 400, 'minHeight' => 200, @@ -143,6 +160,76 @@ it is between a certain size, add the following: The ``headshot`` property is validated to guarantee that it is a real image and that it is between a certain width and height. +You may also want to guarantee the ``headshot`` image to be square. In this +case you can disable portrait and landscape orientations as shown in the +following code: + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/BlogBundle/Resources/config/validation.yml + Acme\BlogBundle\Entity\Author + properties: + headshot: + - Image: + allowLandscape: false + allowPortrait: false + + + .. code-block:: php-annotations + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + /** + * @Assert\Image( + * allowLandscape = false + * allowPortrait = false + * ) + */ + protected $headshot; + } + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // src/Acme/BlogBundle/Entity/Author.php + namespace Acme\BlogBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + // ... + + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('headshot', new Assert\Image(array( + 'allowLandscape' => false, + 'allowPortrait' => false, + ))); + } + } + +You can mix all the constraint options to create powerful validation rules. + Options ------- @@ -194,41 +281,124 @@ maxHeight If set, the height of the image file must be less than or equal to this value in pixels. +maxRatio +~~~~~~~~ + +**type**: ``float`` + +If set, the aspect ratio (``width / height``) of the image file must be less +than or equal to this value. + +minRatio +~~~~~~~~ + +**type**: ``float`` + +If set, the aspect ratio (``width / height``) of the image file must be greater +than or equal to this value. + +allowSquare +~~~~~~~~~~~ + +**type**: ``Boolean`` **default**: ``true`` + +If this option is false, the image cannot be a square. If you want to force +a square image, then set leave this option as its default ``true`` value +and set `allowLandscape`_ and `allowPortrait`_ both to ``false``. + +allowLandscape +~~~~~~~~~~~~~~ + +**type**: ``Boolean`` **default**: ``true`` + +If this option is false, the image cannot be landscape oriented. + +allowPortrait +~~~~~~~~~~~~~ + +**type**: ``Boolean`` **default**: ``true`` + +If this option is false, the image cannot be portrait oriented. + sizeNotDetectedMessage ~~~~~~~~~~~~~~~~~~~~~~ **type**: ``string`` **default**: ``The size of the image could not be detected.`` If the system is unable to determine the size of the image, this error will -be displayed. This will only occur when at least one of the four size constraint +be displayed. This will only occur when at least one of the size constraint options has been set. maxWidthMessage ~~~~~~~~~~~~~~~ -**type**: ``string`` **default**: ``The image width is too big ({{ width }}px). Allowed maximum width is {{ max_width }}px.`` +**type**: ``string`` **default**: ``The image width is too big ({{ width }}px). +Allowed maximum width is {{ max_width }}px.`` The error message if the width of the image exceeds `maxWidth`_. minWidthMessage ~~~~~~~~~~~~~~~ -**type**: ``string`` **default**: ``The image width is too small ({{ width }}px). Minimum width expected is {{ min_width }}px.`` +**type**: ``string`` **default**: ``The image width is too small ({{ width }}px). +Minimum width expected is {{ min_width }}px.`` The error message if the width of the image is less than `minWidth`_. maxHeightMessage ~~~~~~~~~~~~~~~~ -**type**: ``string`` **default**: ``The image height is too big ({{ height }}px). Allowed maximum height is {{ max_height }}px.`` +**type**: ``string`` **default**: ``The image height is too big ({{ height }}px). +Allowed maximum height is {{ max_height }}px.`` The error message if the height of the image exceeds `maxHeight`_. minHeightMessage ~~~~~~~~~~~~~~~~ -**type**: ``string`` **default**: ``The image height is too small ({{ height }}px). Minimum height expected is {{ min_height }}px.`` +**type**: ``string`` **default**: ``The image height is too small ({{ height }}px). +Minimum height expected is {{ min_height }}px.`` The error message if the height of the image is less than `minHeight`_. +maxRatioMessage +~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The image ratio is too big ({{ ratio }}). +Allowed maximum ratio is {{ max_ratio }}`` + +The error message if the aspect ratio of the image exceeds `maxRatio`_. + +minRatioMessage +~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The image ratio is too small ({{ ratio }}). +Minimum ratio expected is {{ min_ratio }}`` + +The error message if the aspect ratio of the image is less than `minRatio`_. + +allowSquareMessage +~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The image is square ({{ width }}x{{ height }}px). +Square images are not allowed`` + +The error message if the image is square and you set `allowSquare`_ to ``false``. + +allowLandscapeMessage +~~~~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The image is landscape oriented ({{ width }}x{{ height }}px). +Landscape oriented images are not allowed`` + +The error message if the image is landscape oriented and you set `allowLandscape`_ to ``false``. + +allowPortraitMessage +~~~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The image is portrait oriented ({{ width }}x{{ height }}px). +Portrait oriented images are not allowed`` + +The error message if the image is portrait oriented and you set `allowPortrait`_ to ``false``. + .. _`IANA website`: http://www.iana.org/assignments/media-types/image/index.html diff --git a/reference/constraints/Luhn.rst b/reference/constraints/Luhn.rst index 07bd8bd2dda..2329d4c0ff2 100644 --- a/reference/constraints/Luhn.rst +++ b/reference/constraints/Luhn.rst @@ -1,9 +1,6 @@ Luhn ==== -.. versionadded:: 2.2 - The Luhn validation is new in Symfony 2.2. - This constraint is used to ensure that a credit card number passes the `Luhn algorithm`_. It is useful as a first step to validating a credit card: before communicating with a payment gateway. diff --git a/reference/constraints/Regex.rst b/reference/constraints/Regex.rst index c0e480b3682..4f1e588ab17 100644 --- a/reference/constraints/Regex.rst +++ b/reference/constraints/Regex.rst @@ -177,9 +177,6 @@ string *does* match this pattern. htmlPattern ~~~~~~~~~~~ -.. versionadded:: 2.1 - The ``htmlPattern`` option was added in Symfony 2.1 - **type**: ``string|Boolean`` **default**: null This option specifies the pattern to use in the HTML5 ``pattern`` attribute. diff --git a/reference/constraints/UniqueEntity.rst b/reference/constraints/UniqueEntity.rst index f5a7ffa69f7..7bc38e3ca45 100644 --- a/reference/constraints/UniqueEntity.rst +++ b/reference/constraints/UniqueEntity.rst @@ -160,9 +160,6 @@ errorPath **type**: ``string`` **default**: The name of the first field in `fields`_ -.. versionadded:: 2.1 - The ``errorPath`` option was added in Symfony 2.1. - If the entity violates the constraint the error message is bound to the first field in `fields`_. If there is more than one field, you may want to map the error message to another field. @@ -261,9 +258,6 @@ ignoreNull **type**: ``Boolean`` **default**: ``true`` -.. versionadded:: 2.1 - The ``ignoreNull`` option was added in Symfony 2.1. - If this option is set to ``true``, then the constraint will allow multiple entities to have a ``null`` value for a field without failing validation. If set to ``false``, only one ``null`` value is allowed - if a second entity diff --git a/reference/constraints/UserPassword.rst b/reference/constraints/UserPassword.rst index d153c89401f..1383172aaed 100644 --- a/reference/constraints/UserPassword.rst +++ b/reference/constraints/UserPassword.rst @@ -1,14 +1,6 @@ UserPassword ============ -.. note:: - - Since Symfony 2.2, the ``UserPassword*`` classes in the - ``Symfony\\Component\\Security\\Core\\Validator\\Constraint`` namespace are - deprecated and will be removed in Symfony 2.3. Please use the - ``UserPassword*`` classes in the - ``Symfony\\Component\\Security\\Core\\Validator\\Constraints`` namespace instead. - This validates that an input value is equal to the current authenticated user's password. This is useful in a form where a user can change their password, but needs to enter their old password for security. diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 84186b01d25..237329866d4 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -76,6 +76,7 @@ Other Constraints ~~~~~~~~~~~~~~~~~ * :doc:`Callback ` +* :doc:`Expression ` * :doc:`All ` * :doc:`UserPassword ` * :doc:`Valid ` diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 05d600afaa7..f5ee7c9afba 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -29,6 +29,8 @@ may also be tags in other bundles you use that aren't listed here. +-----------------------------------+---------------------------------------------------------------------------+ | `assetic.templating.twig`_ | Remove this service if Twig templating is disabled | +-----------------------------------+---------------------------------------------------------------------------+ +| `console.command`_ | Add a command | ++-----------------------------------+---------------------------------------------------------------------------+ | `data_collector`_ | Create a class that collects custom data for the profiler | +-----------------------------------+---------------------------------------------------------------------------+ | `doctrine.event_listener`_ | Add a Doctrine event listener | @@ -241,6 +243,18 @@ assetic.templating.twig The tagged service will be removed from the container if ``framework.templating.engines`` config section does not contain ``twig``. +console.command +--------------- + +.. versionadded:: 2.4 + Support for registering commands in the service container was added in + version 2.4. + +**Purpose**: Add a command to the application + +For details on registering your own commands in the service container, read +:ref:`the cookbook article`. + data_collector -------------- @@ -973,9 +987,6 @@ translation.extractor **Purpose**: To register a custom service that extracts messages from a file -.. versionadded:: 2.1 - The ability to add message extractors is new in Symfony 2.1. - When executing the ``translation:update`` command, it uses extractors to extract translation messages from a file. By default, the Symfony2 framework has a :class:`Symfony\\Bridge\\Twig\\Translation\\TwigExtractor` and a @@ -1044,9 +1055,6 @@ translation.dumper **Purpose**: To register a custom service that dumps messages to a file -.. versionadded:: 2.1 - The ability to add message dumpers is new in Symfony 2.1. - After an `Extractor `_ has extracted all messages from the templates, the dumpers are executed to dump the messages to a translation file in a specific format. @@ -1220,5 +1228,6 @@ For an example, see the ``EntityInitializer`` class inside the Doctrine Bridge. .. _`Twig's documentation`: http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension .. _`Twig official extension repository`: https://github.com/fabpot/Twig-extensions +.. _`KernelEvents`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/KernelEvents.php .. _`SwiftMailer's Plugin Documentation`: http://swiftmailer.org/docs/plugins.html .. _`Twig Loader`: http://twig.sensiolabs.org/doc/api.html#loaders diff --git a/reference/forms/twig_reference.rst b/reference/forms/twig_reference.rst index e33b6b5f1c0..be71a9d7557 100644 --- a/reference/forms/twig_reference.rst +++ b/reference/forms/twig_reference.rst @@ -295,6 +295,9 @@ object: get('name')->vars['label'] ?> +.. versionadded:: 2.4 + The ``submitted`` variable was added in Symfony 2.4. + +-----------------+-----------------------------------------------------------------------------------------+ | Variable | Usage | +=================+=========================================================================================+ @@ -310,6 +313,8 @@ object: | | since this only returns "global" errors: some individual fields may have errors | | | Instead, use the ``valid`` option | +-----------------+-----------------------------------------------------------------------------------------+ +| ``submitted`` | Returns ``true`` or ``false`` depending on whether the whole form is submitted | ++-----------------+-----------------------------------------------------------------------------------------+ | ``valid`` | Returns ``true`` or ``false`` depending on whether the whole form is valid | +-----------------+-----------------------------------------------------------------------------------------+ | ``value`` | The value that will be used when rendering (commonly the ``value`` HTML attribute) | diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index 19abd57d0ae..e941b7abfbb 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -9,6 +9,8 @@ The ``file`` type represents a file input in your form. +-------------+---------------------------------------------------------------------+ | Rendered as | ``input`` ``file`` field | +-------------+---------------------------------------------------------------------+ +| Options | - `multiple`_ | ++-------------+---------------------------------------------------------------------+ | Inherited | - `required`_ | | options | - `label`_ | | | - `label_attr`_ | @@ -77,6 +79,16 @@ before using it directly. Read the :doc:`cookbook ` for an example of how to manage a file upload associated with a Doctrine entity. +Field Options +------------- + +multiple +~~~~~~~~ + +**type**: ``Boolean`` **default**: ``false`` + +When set to true, the user will be able to upload multiple files at the same time. + Inherited options ----------------- diff --git a/reference/forms/types/integer.rst b/reference/forms/types/integer.rst index cda5cb276ca..ba4a0760b25 100644 --- a/reference/forms/types/integer.rst +++ b/reference/forms/types/integer.rst @@ -50,17 +50,25 @@ By default, if the user enters a non-integer number, it will be rounded down. There are several other rounding methods, and each is a constant on the :class:`Symfony\\Component\\Form\\Extension\\Core\\DataTransformer\\IntegerToLocalizedStringTransformer`: -* ``IntegerToLocalizedStringTransformer::ROUND_DOWN`` Rounding mode to - round towards zero. +* ``IntegerToLocalizedStringTransformer::ROUND_DOWN`` Round towards zero. -* ``IntegerToLocalizedStringTransformer::ROUND_FLOOR`` Rounding mode to - round towards negative infinity. +* ``IntegerToLocalizedStringTransformer::ROUND_FLOOR`` Round towards negative + infinity. -* ``IntegerToLocalizedStringTransformer::ROUND_UP`` Rounding mode to round - away from zero. +* ``IntegerToLocalizedStringTransformer::ROUND_UP`` Round away from zero. -* ``IntegerToLocalizedStringTransformer::ROUND_CEILING`` Rounding mode - to round towards positive infinity. +* ``IntegerToLocalizedStringTransformer::ROUND_CEILING`` Round towards + positive infinity. + +* ``IntegerToLocalizedStringTransformer::ROUND_HALF_DOWN`` Round towards the + "nearest neighbor". If both neighbors are equidistant, round down. + +* ``IntegerToLocalizedStringTransformer::ROUND_HALF_EVEN`` Round towards the + "nearest neighbor". If both neighbors are equidistant, round towards the + even neighbor. + +* ``IntegerToLocalizedStringTransformer::ROUND_HALF_UP`` Round towards the + "nearest neighbor". If both neighbors are equidistant, round up. .. include:: /reference/forms/types/options/grouping.rst.inc diff --git a/reference/forms/types/number.rst b/reference/forms/types/number.rst index 1becc320c7f..74d35a4f61a 100644 --- a/reference/forms/types/number.rst +++ b/reference/forms/types/number.rst @@ -40,35 +40,31 @@ Field Options rounding_mode ~~~~~~~~~~~~~ -**type**: ``integer`` **default**: ``IntegerToLocalizedStringTransformer::ROUND_HALFUP`` +**type**: ``integer`` **default**: ``NumberToLocalizedStringTransformer::ROUND_HALFUP`` If a submitted number needs to be rounded (based on the ``precision`` option), you have several configurable options for that rounding. Each -option is a constant on the :class:`Symfony\\Component\\Form\\Extension\\Core\\DataTransformer\\IntegerToLocalizedStringTransformer`: +option is a constant on the :class:`Symfony\\Component\\Form\\Extension\\Core\\DataTransformer\\NumberToLocalizedStringTransformer`: -* ``IntegerToLocalizedStringTransformer::ROUND_DOWN`` Rounding mode to - round towards zero. +* ``NumberToLocalizedStringTransformer::ROUND_DOWN`` Round towards zero. -* ``IntegerToLocalizedStringTransformer::ROUND_FLOOR`` Rounding mode to - round towards negative infinity. +* ``NumberToLocalizedStringTransformer::ROUND_FLOOR`` Round towards negative + infinity. -* ``IntegerToLocalizedStringTransformer::ROUND_UP`` Rounding mode to round - away from zero. +* ``NumberToLocalizedStringTransformer::ROUND_UP`` Round away from zero. -* ``IntegerToLocalizedStringTransformer::ROUND_CEILING`` Rounding mode - to round towards positive infinity. +* ``NumberToLocalizedStringTransformer::ROUND_CEILING`` Round towards + positive infinity. -* ``IntegerToLocalizedStringTransformer::ROUND_HALFDOWN`` Rounding mode - to round towards "nearest neighbor" unless both neighbors are equidistant, - in which case round down. +* ``NumberToLocalizedStringTransformer::ROUND_HALF_DOWN`` Round towards the + "nearest neighbor". If both neighbors are equidistant, round down. -* ``IntegerToLocalizedStringTransformer::ROUND_HALFEVEN`` Rounding mode - to round towards the "nearest neighbor" unless both neighbors are equidistant, - in which case, round towards the even neighbor. +* ``NumberToLocalizedStringTransformer::ROUND_HALF_EVEN`` Round towards the + "nearest neighbor". If both neighbors are equidistant, round towards the + even neighbor. -* ``IntegerToLocalizedStringTransformer::ROUND_HALFUP`` Rounding mode to - round towards "nearest neighbor" unless both neighbors are equidistant, - in which case round up. +* ``NumberToLocalizedStringTransformer::ROUND_HALF_UP`` Round towards the + "nearest neighbor". If both neighbors are equidistant, round up. .. include:: /reference/forms/types/options/grouping.rst.inc diff --git a/reference/forms/types/options/disabled.rst.inc b/reference/forms/types/options/disabled.rst.inc index 89e8177a6e3..130b7d7f6b3 100644 --- a/reference/forms/types/options/disabled.rst.inc +++ b/reference/forms/types/options/disabled.rst.inc @@ -1,9 +1,6 @@ disabled ~~~~~~~~ -.. versionadded:: 2.1 - The ``disabled`` option is new in version 2.1 - **type**: ``boolean`` **default**: ``false`` If you don't want a user to modify the value of a field, you can set diff --git a/reference/forms/types/options/error_mapping.rst.inc b/reference/forms/types/options/error_mapping.rst.inc index ba67e1d5cdb..d570147a931 100644 --- a/reference/forms/types/options/error_mapping.rst.inc +++ b/reference/forms/types/options/error_mapping.rst.inc @@ -3,9 +3,6 @@ error_mapping **type**: ``array`` **default**: ``empty`` -.. versionadded:: 2.1 - The ``error_mapping`` option is new to Symfony 2.1. - This option allows you to modify the target of a validation error. Imagine you have a custom method named ``matchingCityAndZipCode`` that validates diff --git a/reference/forms/types/options/property_path.rst.inc b/reference/forms/types/options/property_path.rst.inc index 797ea2a5784..fb402b1cbcb 100644 --- a/reference/forms/types/options/property_path.rst.inc +++ b/reference/forms/types/options/property_path.rst.inc @@ -14,6 +14,3 @@ If you wish the field to be ignored when reading or writing to the object you can set the ``property_path`` option to ``false``, but using ``property_path`` for this purpose is deprecated, you should use the ``mapped`` option. - -.. versionadded:: 2.1 - Since 2.1, the ``mapped`` option has been added for this use-case. diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 17b2c399da0..a791f8c8d21 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -17,9 +17,8 @@ There may also be tags in bundles you use that aren't listed here. Functions --------- -.. versionadded:: 2.2 - The ``render`` and ``controller`` functions are new in Symfony 2.2. Prior, - the ``{% render %}`` tag was used and had a different signature. +.. versionadded:: 2.4 + The ``expression`` function was introduced in Symfony 2.4. +----------------------------------------------------+--------------------------------------------------------------------------------------------+ | Function Syntax | Usage | @@ -89,13 +88,13 @@ Functions +----------------------------------------------------+--------------------------------------------------------------------------------------------+ | ``url(name, parameters = {})`` | Equal to ``path(...)`` but it generates an absolute URL | +----------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``expression(expression)`` | Creates an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` in Twig. See | +| | ":ref:`Template Expressions `". | ++----------------------------------------------------+--------------------------------------------------------------------------------------------+ Filters ------- -.. versionadded:: 2.1 - The ``humanize`` filter was added in Symfony 2.1 - +---------------------------------------------------------------------------------+-------------------------------------------------------------------+ | Filter Syntax | Usage | +=================================================================================+===================================================================+ @@ -137,6 +136,9 @@ Filters Tags ---- +.. versionadded:: 2.4 + The stopwatch tag was added in Symfony 2.4. + +---------------------------------------------------+--------------------------------------------------------------------+ | Tag Syntax | Usage | +===================================================+====================================================================+ @@ -153,6 +155,9 @@ Tags | ``{% trans_default_domain language %}`` | This will set the default domain for message catalogues in the | | | current template | +---------------------------------------------------+--------------------------------------------------------------------+ +| ``{% stopwatch 'name' %}...{% endstopwatch %}`` | This will time the run time of the code inside it and put that on | +| | the timeline of the WebProfilerBundle. | ++---------------------------------------------------+--------------------------------------------------------------------+ Tests -----