From c01bc536ef3c35be9f0a28c5d1d4573c695ecaa6 Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Thu, 18 Oct 2012 21:23:09 +0200 Subject: [PATCH 001/245] Update reference/configuration/framework.rst This PR is related to this one https://github.com/symfony/symfony/pull/5347. --- reference/configuration/framework.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index ce6216bb916..7ad7bd53968 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -26,6 +26,8 @@ Configuration * field_name * `session`_ * `lifetime`_ +* `serializer`_ + * enabled * `templating`_ * `assets_base_urls`_ * `assets_version`_ @@ -112,6 +114,23 @@ lifetime This determines the lifetime of the session - in seconds. By default it will use ``0``, which means the cookie is valid for the length of the browser session. +serializer +~~~~~~~~~~ + +enabled +....... + +**type**: ``boolean`` **default**: ``false`` + +Whether to enable or not the serializer service in the service container. If enabled, +the serializer will be loaded along with two encoders (:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` +and :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder) +and one normalizer (:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`). + +You can add more normalizers and/or encoders by tagging them as `serializer.encoder` and +`serializer.normalizer`. It's also possible to set the priority of the tag in order to decide the +matching order. + templating ~~~~~~~~~~ @@ -319,6 +338,10 @@ Full Default Configuration # DEPRECATED! Please use: cookie_httponly httponly: ~ + # serializer configuration + serializer: + enabled: false + # templating configuration templating: assets_version: ~ From 460406edf272e61a1826279d23d391b64d6f1355 Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Sun, 21 Oct 2012 19:34:05 +0200 Subject: [PATCH 002/245] Update reference/dic_tags.rst Adding tags to the tag list reference --- reference/dic_tags.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index fa7ea84e043..27d681653f1 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -42,6 +42,10 @@ the AsseticBundle has several tags that aren't listed here. +-----------------------------------+---------------------------------------------------------------------------+ | `security.listener.factory`_ | Necessary when creating a custom authentication system | +-----------------------------------+---------------------------------------------------------------------------+ +| `serializer.encoder`_ | Register a new encoder in the Serializer service | ++-----------------------------------+---------------------------------------------------------------------------+ +| `serializer.normalizer`_ | Register a new normalizer in the Serializer service | ++-----------------------------------+---------------------------------------------------------------------------+ | `swiftmailer.plugin`_ | Register a custom SwiftMailer Plugin | +-----------------------------------+---------------------------------------------------------------------------+ | `templating.helper`_ | Make your service available in PHP templates | @@ -558,6 +562,24 @@ is used behind the scenes to determine if the user should have access. The For more information, read the cookbook article: :doc:`/cookbook/security/voters`. +serializer.encoder +------------------ + +**Purpose**: Register a new encoder in the Serializer service + +You have to enable the Serializer service in order to use this tag. The class to +be tagged should extend the :class:`Symfony\\Component\\Serializer\\Encoder\\EncoderInterface` +and :class:`Symfony\\Component\\Serializer\\Encoder\\DecoderInterface` + +serializer.normalizer +--------------------- + +**Purpose**: Register a new normalizer in the Serializer service + +You have to enable the Serializer service in order to use this tag. The class to +be tagged should extend the :class:`Symfony\\Component\\Serializer\\Normalizer\\NormalizerInterface` +and :class:`Symfony\\Component\\Serializer\\Normalizer\\DenormalizerInterface` + swiftmailer.plugin ------------------ From f723940a8309221e016b852699ec6d2ff423db5d Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Sun, 21 Oct 2012 19:38:12 +0200 Subject: [PATCH 003/245] Update reference/configuration/framework.rst Changing backticks --- reference/configuration/framework.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 7ad7bd53968..e5c122aeecc 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -127,8 +127,8 @@ the serializer will be loaded along with two encoders (:class:`Symfony\\Componen and :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder) and one normalizer (:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`). -You can add more normalizers and/or encoders by tagging them as `serializer.encoder` and -`serializer.normalizer`. It's also possible to set the priority of the tag in order to decide the +You can add more normalizers and/or encoders by tagging them as ``serializer.encoder`` and +``serializer.normalizer``. It's also possible to set the priority of the tag in order to decide the matching order. templating From 70ca20c337f1b90f09415312fef03b7c126ee435 Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Mon, 29 Oct 2012 20:05:33 +0000 Subject: [PATCH 004/245] Update reference/configuration/framework.rst The GetSetMethodNormalizer is not loaded by default --- reference/configuration/framework.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index e5c122aeecc..aede5c5867a 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -124,8 +124,9 @@ enabled Whether to enable or not the serializer service in the service container. If enabled, the serializer will be loaded along with two encoders (:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` -and :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder) -and one normalizer (:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`). +and :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) +but none normalizer. The :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` is broken by design +so you are required to load it under your responsability. You can do creating a new service and tagging it appropiately. You can add more normalizers and/or encoders by tagging them as ``serializer.encoder`` and ``serializer.normalizer``. It's also possible to set the priority of the tag in order to decide the From 65332683b72abbb9e024746bbeaa5f2c03eafd71 Mon Sep 17 00:00:00 2001 From: Lee McDermott Date: Thu, 20 Dec 2012 18:22:34 +0000 Subject: [PATCH 005/245] Add note about console autocompletion --- components/console/introduction.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 997d14c1172..458ade73874 100755 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -312,6 +312,22 @@ convenient for passwords:: false ); +.. 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:: + + $dialog = $this->getHelperSet()->get('dialog'); + $bundleNames = array('AcmeDemoBundle', 'AcmeBlogBundle', 'AcmeStoreBundle'); + $name = $dialog->ask( + $output, + 'Please enter the name of a bundle', + 'FooBundle', + $bundleNames + ); + + .. caution:: When you ask for a hidden response, Symfony will use either a binary, change From dbfcaffa1bf10f658e9c49384b848e56e73d4b5f Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Sat, 5 Jan 2013 15:26:20 +0100 Subject: [PATCH 006/245] Adapted the docs to the rename of the "virtual" form option to "inherit_data" --- cookbook/form/index.rst | 2 +- ...uals_forms.rst => inherit_data_option.rst} | 68 ++++++++----------- cookbook/map.rst.inc | 2 +- 3 files changed, 32 insertions(+), 40 deletions(-) rename cookbook/form/{use_virtuals_forms.rst => inherit_data_option.rst} (59%) diff --git a/cookbook/form/index.rst b/cookbook/form/index.rst index e60f02c9a68..9ec1c8ea260 100644 --- a/cookbook/form/index.rst +++ b/cookbook/form/index.rst @@ -10,4 +10,4 @@ Form form_collections create_custom_field_type create_form_type_extension - use_virtuals_forms + inherit_data_option diff --git a/cookbook/form/use_virtuals_forms.rst b/cookbook/form/inherit_data_option.rst similarity index 59% rename from cookbook/form/use_virtuals_forms.rst rename to cookbook/form/inherit_data_option.rst index 41b05355ac8..6241927f0ff 100644 --- a/cookbook/form/use_virtuals_forms.rst +++ b/cookbook/form/inherit_data_option.rst @@ -1,13 +1,12 @@ .. index:: - single: Form; Virtual forms + single: Form; The "inherit_data" option -How to use the Virtual Form Field Option -======================================== +Reducing Code Duplication with "inherit_data" +============================================= -The ``virtual`` form field option can be very useful when you have some -duplicated fields in different entities. - -For example, imagine you have two entities, a ``Company`` and a ``Customer``:: +The ``inherit_data`` form field option can be very useful when you have some +duplicated fields in different entities. For example, imagine you have two +entities, a ``Company`` and a ``Customer``:: // src/Acme/HelloBundle/Entity/Company.php namespace Acme\HelloBundle\Entity; @@ -39,13 +38,10 @@ For example, imagine you have two entities, a ``Company`` and a ``Customer``:: private $country; } -Like you can see, each entity shares a few of the same fields: ``address``, +As you can see, each entity shares a few of the same fields: ``address``, ``zipcode``, ``city``, ``country``. -Now, you want to build two forms: one for a ``Company`` and the second for -a ``Customer``. - -Start by creating a very simple ``CompanyType`` and ``CustomerType``:: +Let's build two forms for these entities, ``CompanyType`` and ``CustomerType``:: // src/Acme/HelloBundle/Form/Type/CompanyType.php namespace Acme\HelloBundle\Form\Type; @@ -79,8 +75,9 @@ Start by creating a very simple ``CompanyType`` and ``CustomerType``:: } } -Now, to deal with the four duplicated fields. Here is a (simple) -location form type:: +Instead of including the duplicated fields ``address``, ``zipcode``, ``city`` +and ``country``in both of these forms, we will create a third form for that. +We will call this form simply ``LocationType``:: // src/Acme/HelloBundle/Form/Type/LocationType.php namespace Acme\HelloBundle\Form\Type; @@ -102,7 +99,7 @@ location form type:: public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'virtual' => true + 'inherit_data' => true )); } @@ -112,20 +109,25 @@ location form type:: } } -You don't *actually* have a location field in each of your entities, so you -can't directly link ``LocationType`` to ``CompanyType`` or ``CustomerType``. -But you absolutely want to have a dedicated form type to deal with location (remember, DRY!). +The location form has an interesting option set, namely ``inherit_data``. This +option lets the form inherit its data from its parent form. If embedded in +the company form, the fields of the location form will access the properties of +the ``Company`` instance. If embedded in the customer form, the fields will +access the properties of the ``Customer`` instance instead. Easy, eh? -The ``virtual`` form field option is the solution. +.. note:: -You can set the option ``'virtual' => true`` in the ``setDefaultOptions()`` method -of ``LocationType`` and directly start using it in the two original form types. + Instead of setting the ``inherit_data`` option inside ``LocationType``, you + can also (just like with any option) pass it in the third argument of + ``$builder->add()``. -Look at the result:: +Let's make this work by adding the location form to our two original forms:: - // CompanyType + // src/Acme/HelloBundle/Form/Type/CompanyType.php public function buildForm(FormBuilderInterface $builder, array $options) { + // ... + $builder->add('foo', new LocationType(), array( 'data_class' => 'Acme\HelloBundle\Entity\Company' )); @@ -133,25 +135,15 @@ Look at the result:: .. code-block:: php - // CustomerType + // src/Acme/HelloBundle/Form/Type/CustomerType.php public function buildForm(FormBuilderInterface $builder, array $options) { + // ... + $builder->add('bar', new LocationType(), array( 'data_class' => 'Acme\HelloBundle\Entity\Customer' )); } -With the virtual option set to false (default behavior), the Form Component -expects each underlying object to have a ``foo`` (or ``bar``) property that -is either some object or array which contains the four location fields. -Of course, you don't have this object/array in your entities and you don't want it! - -With the virtual option set to true, the Form component skips the ``foo`` (or ``bar``) -property, and instead "gets" and "sets" the 4 location fields directly -on the underlying object! - -.. note:: - - Instead of setting the ``virtual`` option inside ``LocationType``, you - can (just like with any options) also pass it in as an array option to - the third argument of ``$builder->add()``. +That's it! You have extracted duplicated field definitions to a separate +location form that you can reuse wherever you need it. diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index e2ee0fcf118..c1df5af3dd4 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -79,7 +79,7 @@ * :doc:`/cookbook/form/form_collections` * :doc:`/cookbook/form/create_custom_field_type` * :doc:`/cookbook/form/create_form_type_extension` - * :doc:`/cookbook/form/use_virtuals_forms` + * :doc:`/cookbook/form/inherit_data_option` * (validation) :doc:`/cookbook/validation/custom_constraint` * (doctrine) :doc:`/cookbook/doctrine/file_uploads` From 700f5291e512311d10970465da4513d60ec74d9d Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Sun, 20 Jan 2013 13:25:48 +0000 Subject: [PATCH 007/245] Update reference/configuration/framework.rst --- reference/configuration/framework.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index aede5c5867a..61c9ca12a1a 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -126,12 +126,22 @@ Whether to enable or not the serializer service in the service container. If ena the serializer will be loaded along with two encoders (:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` and :class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) but none normalizer. The :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` is broken by design -so you are required to load it under your responsability. You can do creating a new service and tagging it appropiately. +so you are required to load it under your responsability. -You can add more normalizers and/or encoders by tagging them as ``serializer.encoder`` and +You can load more normalizers and/or encoders by tagging them as ``serializer.encoder`` and ``serializer.normalizer``. It's also possible to set the priority of the tag in order to decide the matching order. +Here an example on how to load the load +the :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`: + + # app/config/config.yml + services: + get_set_method_normalizer: + class: Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer + tags: + - { name: serializer.normalizer } + templating ~~~~~~~~~~ From 63fae5b639a0cde4b9fe1630a2579bb6e10473c9 Mon Sep 17 00:00:00 2001 From: Javier Lopez Date: Sun, 20 Jan 2013 13:28:33 +0000 Subject: [PATCH 008/245] Update reference/configuration/framework.rst --- reference/configuration/framework.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 61c9ca12a1a..8781dff2bc9 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -135,6 +135,8 @@ matching order. Here an example on how to load the load the :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`: +.. code-block:: yaml + # app/config/config.yml services: get_set_method_normalizer: From f3c431fc9dc6ab9a85846639c2cfa9d4ce3e6901 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 14 Mar 2013 21:00:00 -0500 Subject: [PATCH 009/245] [#2283] Updating URL from 2.1 to master on master branch --- reference/forms/twig_reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/twig_reference.rst b/reference/forms/twig_reference.rst index d30c01015b7..845df8ee5be 100644 --- a/reference/forms/twig_reference.rst +++ b/reference/forms/twig_reference.rst @@ -277,4 +277,4 @@ object: | | (for example, a ``choice`` field, which is actually a group of checkboxes | +-----------------+-----------------------------------------------------------------------------------------+ -.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/2.1/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 From eae1566ba5930179b2c69384c87cfac0560ad7c4 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 08:18:11 +0100 Subject: [PATCH 010/245] added a note about the new setCurrent method on the progress helper --- components/console/helpers/progresshelper.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/components/console/helpers/progresshelper.rst b/components/console/helpers/progresshelper.rst index 757091ba20d..f77c52490cf 100644 --- a/components/console/helpers/progresshelper.rst +++ b/components/console/helpers/progresshelper.rst @@ -7,6 +7,9 @@ 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. + When executing longer-running commands, it may be helpful to show progress information, which updates as your command runs: @@ -28,6 +31,12 @@ pass it a total number of units, and advance the progress as your command execut $progress->finish(); +.. tip:: + + You can also set the current progress by calling the + :method:`Symfony\\Component\\Console\\Helper\\ProgressHelper::setCurrent` + method. + 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 @@ -70,4 +79,4 @@ To see other available options, check the API documentation for if ($i % 100 == 0) { $progress->advance(); } - } \ No newline at end of file + } From 441e766e4a1ff7392d23e187d4792237bbdf1909 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 11:52:04 +0100 Subject: [PATCH 011/245] removed deprecated trust_proxy_headers docs --- reference/configuration/framework.rst | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index fe70e1f121c..4e2ac86965f 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -18,7 +18,7 @@ Configuration * `secret`_ * `ide`_ * `test`_ -* `trust_proxy_headers`_ +* `trusted_proxies`_ * `form`_ * enabled * `csrf_protection`_ @@ -117,23 +117,6 @@ see :doc:`/components/http_foundation/trusting_proxies`. 'trusted_proxies' => array('192.0.0.1'), )); -trust_proxy_headers -~~~~~~~~~~~~~~~~~~~ - -.. caution:: - - The ``trust_proxy_headers`` option is deprecated and will be removed in - Symfony 2.3. See `trusted_proxies`_ and :doc:`/components/http_foundation/trusting_proxies` - for details on how to properly trust proxy data. - -**type**: ``Boolean`` - -Configures if HTTP headers (like ``HTTP_X_FORWARDED_FOR``, ``X_FORWARDED_PROTO``, and -``X_FORWARDED_HOST``) are trusted as an indication for an SSL connection. By default, it is -set to ``false`` and only SSL_HTTPS connections are indicated as secure. - -You should enable this setting if your application is behind a reverse proxy. - .. _reference-framework-form: form @@ -379,7 +362,6 @@ Full Default Configuration framework: charset: ~ secret: ~ - trust_proxy_headers: false trusted_proxies: [] ide: ~ test: ~ From 74f9a1f338122505ce56618107fa8e21affb5eb4 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 11:55:19 +0100 Subject: [PATCH 012/245] removed docs on PHP parsing in YAML files --- components/yaml/introduction.rst | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/components/yaml/introduction.rst b/components/yaml/introduction.rst index bd30aea820a..37bbf6eaeec 100644 --- a/components/yaml/introduction.rst +++ b/components/yaml/introduction.rst @@ -134,20 +134,6 @@ string or a file containing YAML. Internally, it calls the :method:`Symfony\\Component\\Yaml\\Parser::parse` method, but enhances the error if something goes wrong by adding the filename to the message. -Executing PHP Inside YAML Files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 2.1 - The ``Yaml::enablePhpParsing()`` method is new to Symfony 2.1. Prior to 2.1, - PHP was *always* executed when calling the ``parse()`` function. - -By default, if you include PHP inside a YAML file, it will not be parsed. -If you do want PHP to be parsed, you must call ``Yaml::enablePhpParsing()`` -before parsing the file to activate this mode. If you only want to allow -PHP code for a single YAML file, be sure to disable PHP parsing after parsing -the single file by calling ``Yaml::$enablePhpParsing = false;`` (``$enablePhpParsing`` -is a public property). - Writing YAML Files ~~~~~~~~~~~~~~~~~~ From f886afcf668fd1a7ea30fdcd698922a1396aed3a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 11:57:55 +0100 Subject: [PATCH 013/245] updated deprecated usage of flash bags --- book/controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/controller.rst b/book/controller.rst index 1f51d5ed4c5..eb028247aa9 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -686,7 +686,7 @@ the ``notice`` message: .. code-block:: php - getFlash('notice') as $message): ?> + getFlashBag()->get('notice') as $message): ?>
$message
" ?> From 6e7c0cdf51fa6a5a31d09d1fc8390f814790f70d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 11:59:58 +0100 Subject: [PATCH 014/245] removed deprecated min/max/minlength/maxlength constraints --- reference/constraints.rst | 4 - reference/constraints/Max.rst | 111 --------------------------- reference/constraints/MaxLength.rst | 107 -------------------------- reference/constraints/Min.rst | 111 --------------------------- reference/constraints/MinLength.rst | 112 ---------------------------- reference/constraints/Valid.rst | 8 +- reference/constraints/map.rst.inc | 4 - 7 files changed, 6 insertions(+), 451 deletions(-) delete mode 100644 reference/constraints/Max.rst delete mode 100644 reference/constraints/MaxLength.rst delete mode 100644 reference/constraints/Min.rst delete mode 100644 reference/constraints/MinLength.rst diff --git a/reference/constraints.rst b/reference/constraints.rst index 1069b0ea3e0..1ed9b35649b 100644 --- a/reference/constraints.rst +++ b/reference/constraints.rst @@ -14,15 +14,11 @@ Validation Constraints Reference constraints/Type constraints/Email - constraints/MinLength - constraints/MaxLength constraints/Length constraints/Url constraints/Regex constraints/Ip - constraints/Max - constraints/Min constraints/Range constraints/Date diff --git a/reference/constraints/Max.rst b/reference/constraints/Max.rst deleted file mode 100644 index 5b19fe038c7..00000000000 --- a/reference/constraints/Max.rst +++ /dev/null @@ -1,111 +0,0 @@ -Max -=== - -.. caution:: - - The Max constraint is deprecated since version 2.1 and will be removed - in Symfony 2.3. Use :doc:`/reference/constraints/Range` with the ``max`` - option instead. - -Validates that a given number is *less* than some maximum number. - -+----------------+--------------------------------------------------------------------+ -| Applies to | :ref:`property or method` | -+----------------+--------------------------------------------------------------------+ -| Options | - `limit`_ | -| | - `message`_ | -| | - `invalidMessage`_ | -+----------------+--------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Max` | -+----------------+--------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\MaxValidator` | -+----------------+--------------------------------------------------------------------+ - -Basic Usage ------------ - -To verify that the "age" field of a class is not greater than "50", you might -add the following: - -.. configuration-block:: - - .. code-block:: yaml - - # src/Acme/EventBundle/Resources/config/validation.yml - Acme\EventBundle\Entity\Participant: - properties: - age: - - Max: { limit: 50, message: You must be 50 or under to enter. } - - .. code-block:: php-annotations - - // src/Acme/EventBundle/Entity/Participant.php - namespace Acme\EventBundle\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Participant - { - /** - * @Assert\Max(limit = 50, message = "You must be 50 or under to enter.") - */ - protected $age; - } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // src/Acme/EventBundle/Entity/Participant.php - namespace Acme\EventBundle\Entity; - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Participant - { - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('age', new Assert\Max(array( - 'limit' => 50, - 'message' => "You must be 50 or under to enter.", - ))); - } - } - -Options -------- - -limit -~~~~~ - -**type**: ``integer`` [:ref:`default option`] - -This required option is the "max" value. Validation will fail if the given -value is **greater** than this max value. - -message -~~~~~~~ - -**type**: ``string`` **default**: ``This value should be {{ limit }} or less`` - -The message that will be shown if the underlying value is greater than the -`limit`_ option. - -invalidMessage -~~~~~~~~~~~~~~ - -**type**: ``string`` **default**: ``This value should be a valid number`` - -The message that will be shown if the underlying value is not a number (per -the :phpfunction:`is_numeric` PHP function). diff --git a/reference/constraints/MaxLength.rst b/reference/constraints/MaxLength.rst deleted file mode 100644 index 54c6fd4d95d..00000000000 --- a/reference/constraints/MaxLength.rst +++ /dev/null @@ -1,107 +0,0 @@ -MaxLength -========= - -.. caution:: - - The MaxLength constraint is deprecated since version 2.1 and will be removed - in Symfony 2.3. Use :doc:`/reference/constraints/Length` with the ``max`` - option instead. - -Validates that the length of a string is not larger than the given limit. - -+----------------+-------------------------------------------------------------------------+ -| Applies to | :ref:`property or method` | -+----------------+-------------------------------------------------------------------------+ -| Options | - `limit`_ | -| | - `message`_ | -| | - `charset`_ | -+----------------+-------------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\MaxLength` | -+----------------+-------------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\MaxLengthValidator` | -+----------------+-------------------------------------------------------------------------+ - -Basic Usage ------------ - -.. configuration-block:: - - .. code-block:: yaml - - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Blog: - properties: - summary: - - MaxLength: 100 - - .. code-block:: php-annotations - - // src/Acme/BlogBundle/Entity/Blog.php - namespace Acme\BlogBundle\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Blog - { - /** - * @Assert\MaxLength(100) - */ - protected $summary; - } - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // src/Acme/BlogBundle/Entity/Blog.php - namespace Acme\BlogBundle\Entity; - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Blog - { - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('summary', new Assert\MaxLength(array( - 'limit' => 100, - ))); - } - } - -Options -------- - -limit -~~~~~ - -**type**: ``integer`` [:ref:`default option`] - -This required option is the "max" value. Validation will fail if the length -of the give string is **greater** than this number. - -message -~~~~~~~ - -**type**: ``string`` **default**: ``This value is too long. It should have {{ limit }} characters or less`` - -The message that will be shown if the underlying string has a length that -is longer than the `limit`_ option. - -charset -~~~~~~~ - -**type**: ``charset`` **default**: ``UTF-8`` - -If the PHP extension "mbstring" is installed, then the PHP function :phpfunction:`mb_strlen` -will be used to calculate the length of the string. The value of the ``charset`` -option is passed as the second argument to that function. diff --git a/reference/constraints/Min.rst b/reference/constraints/Min.rst deleted file mode 100644 index 06f0ba2d332..00000000000 --- a/reference/constraints/Min.rst +++ /dev/null @@ -1,111 +0,0 @@ -Min -=== - -.. caution:: - - The Min constraint is deprecated since version 2.1 and will be removed - in Symfony 2.3. Use :doc:`/reference/constraints/Range` with the ``min`` - option instead. - -Validates that a given number is *greater* than some minimum number. - -+----------------+--------------------------------------------------------------------+ -| Applies to | :ref:`property or method` | -+----------------+--------------------------------------------------------------------+ -| Options | - `limit`_ | -| | - `message`_ | -| | - `invalidMessage`_ | -+----------------+--------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Min` | -+----------------+--------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\MinValidator` | -+----------------+--------------------------------------------------------------------+ - -Basic Usage ------------ - -To verify that the "age" field of a class is "18" or greater, you might add -the following: - -.. configuration-block:: - - .. code-block:: yaml - - # src/Acme/EventBundle/Resources/config/validation.yml - Acme\EventBundle\Entity\Participant: - properties: - age: - - Min: { limit: 18, message: You must be 18 or older to enter. } - - .. code-block:: php-annotations - - // src/Acme/EventBundle/Entity/Participant.php - namespace Acme\EventBundle\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Participant - { - /** - * @Assert\Min(limit = "18", message = "You must be 18 or older to enter.") - */ - protected $age; - } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // src/Acme/EventBundle/Entity/Participant.php - namespace Acme\EventBundle\Entity\Participant; - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Participant - { - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('age', new Assert\Min(array( - 'limit' => '18', - 'message' => 'You must be 18 or older to enter.', - )); - } - } - -Options -------- - -limit -~~~~~ - -**type**: ``integer`` [:ref:`default option`] - -This required option is the "min" value. Validation will fail if the given -value is **less** than this min value. - -message -~~~~~~~ - -**type**: ``string`` **default**: ``This value should be {{ limit }} or more`` - -The message that will be shown if the underlying value is less than the `limit`_ -option. - -invalidMessage -~~~~~~~~~~~~~~ - -**type**: ``string`` **default**: ``This value should be a valid number`` - -The message that will be shown if the underlying value is not a number (per -the :phpfunction:`is_numeric` PHP function). diff --git a/reference/constraints/MinLength.rst b/reference/constraints/MinLength.rst deleted file mode 100644 index b9ad5afd075..00000000000 --- a/reference/constraints/MinLength.rst +++ /dev/null @@ -1,112 +0,0 @@ -MinLength -========= - -.. caution:: - - The MinLength constraint is deprecated since version 2.1 and will be removed - in Symfony 2.3. Use :doc:`/reference/constraints/Length` with the ``min`` - option instead. - -Validates that the length of a string is at least as long as the given limit. - -+----------------+-------------------------------------------------------------------------+ -| Applies to | :ref:`property or method` | -+----------------+-------------------------------------------------------------------------+ -| Options | - `limit`_ | -| | - `message`_ | -| | - `charset`_ | -+----------------+-------------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\MinLength` | -+----------------+-------------------------------------------------------------------------+ -| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\MinLengthValidator` | -+----------------+-------------------------------------------------------------------------+ - -Basic Usage ------------ - -.. configuration-block:: - - .. code-block:: yaml - - # src/Acme/BlogBundle/Resources/config/validation.yml - Acme\BlogBundle\Entity\Blog: - properties: - firstName: - - MinLength: { limit: 3, message: "Your name must have at least {{ limit }} characters." } - - .. code-block:: php-annotations - - // src/Acme/BlogBundle/Entity/Blog.php - namespace Acme\BlogBundle\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Blog - { - /** - * @Assert\MinLength( - * limit=3, - * message="Your name must have at least {{ limit }} characters." - * ) - */ - protected $summary; - } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // src/Acme/BlogBundle/Entity/Blog.php - namespace Acme\BlogBundle\Entity; - - use Symfony\Component\Validator\Mapping\ClassMetadata; - use Symfony\Component\Validator\Constraints as Assert; - - class Blog - { - public static function loadValidatorMetadata(ClassMetadata $metadata) - { - $metadata->addPropertyConstraint('summary', new Assert\MinLength(array( - 'limit' => 3, - 'message' => 'Your name must have at least {{ limit }} characters.', - ))); - } - } - -Options -------- - -limit -~~~~~ - -**type**: ``integer`` [:ref:`default option`] - -This required option is the "min" value. Validation will fail if the length -of the give string is **less** than this number. - -message -~~~~~~~ - -**type**: ``string`` **default**: ``This value is too short. It should have {{ limit }} characters or more`` - -The message that will be shown if the underlying string has a length that -is shorter than the `limit`_ option. - -charset -~~~~~~~ - -**type**: ``charset`` **default**: ``UTF-8`` - -If the PHP extension "mbstring" is installed, then the PHP function :phpfunction:`mb_strlen` -will be used to calculate the length of the string. The value of the ``charset`` -option is passed as the second argument to that function. diff --git a/reference/constraints/Valid.rst b/reference/constraints/Valid.rst index b8a6f9978c6..f19942c0e46 100644 --- a/reference/constraints/Valid.rst +++ b/reference/constraints/Valid.rst @@ -115,14 +115,18 @@ an ``Address`` instance in the ``$address`` property. - 5 + + + - 4 + + + diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 0482c4c5447..3d10d637ccf 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -16,8 +16,6 @@ String Constraints ~~~~~~~~~~~~~~~~~~ * :doc:`Email ` -* :doc:`MinLength ` -* :doc:`MaxLength ` * :doc:`Length ` * :doc:`Url ` * :doc:`Regex ` @@ -26,8 +24,6 @@ String Constraints Number Constraints ~~~~~~~~~~~~~~~~~~ -* :doc:`Max ` -* :doc:`Min ` * :doc:`Range ` Date Constraints From ac9fbcd13fb1939c727613f342f8e015cc73802b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 12:12:04 +0100 Subject: [PATCH 015/245] updated the way form errors are customized --- cookbook/form/form_customization.rst | 27 ++++------------------- cookbook/validation/custom_constraint.rst | 5 ----- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 000ba292585..eb41d37ec1f 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -718,13 +718,9 @@ and customize the ``form_errors`` fragment. {% block form_errors %} {% spaceless %} {% if errors|length > 0 %} -
    +
      {% for error in errors %} -
    • {{ - error.messagePluralization is null - ? error.messageTemplate|trans(error.messageParameters, 'validators') - : error.messageTemplate|transchoice(error.messagePluralization, error.messageParameters, 'validators') - }}
    • +
    • {{ error.message }}
    • {% endfor %}
    {% endif %} @@ -735,28 +731,13 @@ and customize the ``form_errors`` fragment. -
      +
        -
      • getMessagePluralization()) { - echo $view['translator']->trans( - $error->getMessageTemplate(), - $error->getMessageParameters(), - 'validators' - ); - } else { - echo $view['translator']->transChoice( - $error->getMessageTemplate(), - $error->getMessagePluralization(), - $error->getMessageParameters(), - 'validators' - ); - }?>
      • +
      • getMessage() ?>
      - .. tip:: See :ref:`cookbook-form-theming-methods` for how to apply this customization. diff --git a/cookbook/validation/custom_constraint.rst b/cookbook/validation/custom_constraint.rst index f74063d7c2d..74859a3884b 100644 --- a/cookbook/validation/custom_constraint.rst +++ b/cookbook/validation/custom_constraint.rst @@ -79,11 +79,6 @@ The validator class is also simple, and only has one required method: ``validate The first parameter of the ``addViolation`` call is the error message to use for that violation. -.. versionadded:: 2.1 - The ``isValid`` method was renamed to ``validate`` in Symfony 2.1. The - ``setMessage`` method was also deprecated, in favor of calling ``addViolation`` - on the context. - Using the new Validator ----------------------- From 2521b05111bd5af839b4b1267225d72ef38caf62 Mon Sep 17 00:00:00 2001 From: marcosQuesada Date: Tue, 26 Feb 2013 21:07:01 +0100 Subject: [PATCH 016/245] Added documentation to cover PR #6951 on serializer component. --- components/serializer.rst | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/components/serializer.rst b/components/serializer.rst index e502845a126..6ab98e0da69 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -96,6 +96,22 @@ The first parameter of the :method:`Symfony\\Component\\Serializer\\Serializer:: is the object to be serialized and the second is used to choose the proper encoder, in this case :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder`. +As an option, there's a way to ignore attributes from the origin object to be +serialized, to remove those attributes use +:method:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer::setIgnoredAttributes` +method on normalizer definition:: + + use Symfony\Component\Serializer\Serializer; + use Symfony\Component\Serializer\Encoder\JsonEncoder; + use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; + + $normalizer = new GetSetMethodNormalizer(); + $normalizer->setIgnoredAttributes(array('age')); + $encoder = new JsonEncoder(); + + $serializer = new Serializer(array($normalizer), array($encoder)); + $serializer->serialize($person, 'json'); // Output: {"name":"foo"} + Deserializing an Object ~~~~~~~~~~~~~~~~~~~~~~~ @@ -118,6 +134,32 @@ needs three parameters: 2. The name of the class this information will be decoded to 3. The encoder used to convert that information into an array +Sometimes property names from the serialized content are underscored, in a +regular configuration those attributes will use get/set methods as +``getCamel_case``, when ``getCamelCase`` method is preferable. To change that +behavior use +:method:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer::setCamelizedAttributes` +on normalizer definition:: + + $encoder = new JsonEncoder(); + $normalizer = new GetSetMethodNormalizer(); + $normalizer->setCamelizedAttributes(array('camel_case')); + + $serializer = new Serializer(array($normalizer), array($encoder)); + + $json = <<deserialize($json, 'Acme\Person', 'json'); + +As a final result, Person object uses ``camelCase`` attribute for +``camel_case`` json parameter, the same applies on getters and setters. + JMSSerializer ------------- From d879786996b149845af872406a6627398324aed4 Mon Sep 17 00:00:00 2001 From: Philipp Wahala Date: Mon, 25 Mar 2013 07:30:16 +0100 Subject: [PATCH 017/245] Document new framework option http_method_override --- reference/configuration/framework.rst | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index fe70e1f121c..79f0d6c6e27 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -16,6 +16,7 @@ Configuration ------------- * `secret`_ +* `http_method_override`_ * `ide`_ * `test`_ * `trust_proxy_headers`_ @@ -49,6 +50,21 @@ it's used for generating the CSRF tokens, but it could be used in any other context where having a unique string is useful. It becomes the service container parameter named ``kernel.secret``. +http_method_override +~~~~~~ + +.. versionadded:: 2.3 + The ``http_method_override`` option is new in version 2.3 + +**type**: ``Boolean`` **default**: ``true`` + +This determines whether the '_method' request parameter is used as the intended +HTTP method on POST requests. If enabled, the +:method:`Request::enableHttpMethodParameterOverride ` +gets called automatically. It becomes the service container parameter named +``kernel.http_method_override``. For more information, see +:doc:`/cookbook/routing/method_parameters`. + ide ~~~ @@ -379,6 +395,7 @@ Full Default Configuration framework: charset: ~ secret: ~ + http_method_override: true trust_proxy_headers: false trusted_proxies: [] ide: ~ @@ -405,7 +422,7 @@ Full Default Configuration profiler: enabled: false only_exceptions: false - only_master_requests: false + only_master_requests: false dsn: file:%kernel.cache_dir%/profiler username: password: From 784b61153e3162ca9a891dbf6406326ba5a8c886 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 24 Mar 2013 11:39:46 +0100 Subject: [PATCH 018/245] added documentation for the dispatchable console application --- components/console/events.rst | 116 ++++++++++++++++++++++++++++++++++ components/console/index.rst | 2 +- 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 components/console/events.rst diff --git a/components/console/events.rst b/components/console/events.rst new file mode 100644 index 00000000000..af55b7f6a2e --- /dev/null +++ b/components/console/events.rst @@ -0,0 +1,116 @@ +.. index:: + single: Console; Events + +.. versionadded:: 2.3 + The feature described in this chapter was added in 2.3. + +Using Events +============ + +The Application class of the Console component allows you to optionally hook +into the lifecycle of a console application via events. Instead of reinventing +the wheel, it uses the Symfony EventDispatcher component to do the work:: + + use Symfony\Component\Console\Application; + use Symfony\Component\EventDispatcher\EventDispatcher; + + $application = new Application(); + $application->setDispatcher($dispatcher); + $application->run(); + +The ``ConsoleEvents::COMMAND`` event +------------------------------------ + +**Typical Purposes**: Doing something before any command is run (like logging +which command is going to be executed), or displaying something about the event +to be executed. + +Just before executing any command, the ``ConsoleEvents::COMMAND`` event is +dispatched. Listeners receive a +:class:`Symfony\\Component\\Console\\Event\\ConsoleCommandEvent` event:: + + use Symfony\Component\Console\Event\ConsoleCommandEvent; + use Symfony\Component\Console\ConsoleEvents; + + $dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) { + // get the input instance + $input = $event->getInput(); + + // get the output instance + $output = $event->getOutput(); + + // get the command to be executed + $command = $event->getCommand(); + + // write something about the command + $output->writeln(sprintf('Before running command %s', $command->getName())); + + // get the application + $application = $command->getApplication(); + }); + +The ``ConsoleEvents::TERMINATE`` event +-------------------------------------- + +**Typical Purposes**: To perform some cleanup actions after the command has +been executed. + +After the command has been executed, the ``ConsoleEvents::TERMINATE`` event is +dispatched. It can be used to do any actions that need to be executed for all +commands or to cleanup what you initiated in the ``ConsoleEvents::COMMAND`` +command (like sending logs, closing a database connection, sending emails, +...). A listener might also change the exit code. + +Listeners receive a +:class:`Symfony\\Component\\Console\\Event\\ConsoleTerminateEvent` event:: + + use Symfony\Component\Console\Event\ConsoleTerminateEvent; + use Symfony\Component\Console\ConsoleEvents; + + $dispatcher->addListener(ConsoleEvents::TERMINATE, function (ConsoleTerminateEvent $event) { + // get the output + $output = $event->getOutput(); + + // get the command that has been executed + $command = $event->getCommand(); + + // display something + $output->writeln(sprintf('After running command %s', $command->getName())); + + // change the exit code + $event->setExitCode(128); + }); + +.. tip:: + + This event is also dispatched when an exception is thrown by the command. + It is then dispatched just before the ``ConsoleEvents::EXCEPTION`` event. + The exit code received in this case is the exception code. + +The ``ConsoleEvents::EXCEPTION`` event +-------------------------------------- + +**Typical Purposes**: Handle exceptions thrown during the execution of a +command. + +Whenever an exception is thrown by a command, the ``ConsoleEvents::EXCEPTION`` +command is dispatched. A listener can wrap or change the exception or do +anything useful before the exception is thrown by the application. + +Listeners receive a +:class:`Symfony\\Component\\Console\\Event\\ConsoleForExceptionEvent` event:: + + use Symfony\Component\Console\Event\ConsoleForExceptionEvent; + use Symfony\Component\Console\ConsoleEvents; + + $dispatcher->addListener(ConsoleEvents::EXCEPTION, function (ConsoleForExceptionEvent $event) { + $output = $event->getOutput(); + + $output->writeln(sprintf('Oops, exception thrown while running command %s', $command->getName())); + + // get the current exit code (the exception code or the exit code set by a ConsoleEvents::TERMINATE event) + $exitCode = $event->getExitCode(); + + // change the exception to another one + $event->setException(new \LogicException('Caught exception', $exitCode, $event->getException())); + }); diff --git a/components/console/index.rst b/components/console/index.rst index 4d0a12e4d70..9b025d3a0a1 100644 --- a/components/console/index.rst +++ b/components/console/index.rst @@ -7,5 +7,5 @@ Console introduction usage single_command_tool - helpers/index + events From f9bbfcdee289df495f9fbb9b9ce73cd1d99abd64 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 10:51:27 +0100 Subject: [PATCH 019/245] updated documentation for synchronized services --- cookbook/service_container/scopes.rst | 204 +++++++++++++++++++++----- 1 file changed, 171 insertions(+), 33 deletions(-) diff --git a/cookbook/service_container/scopes.rst b/cookbook/service_container/scopes.rst index aee6710636f..48e760ca1c6 100644 --- a/cookbook/service_container/scopes.rst +++ b/cookbook/service_container/scopes.rst @@ -21,13 +21,15 @@ scopes: - ``prototype``: A new instance is created each time you request the service. -The FrameworkBundle 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). +The +:class:`Symfony\\Component\\HttpKernel\\DependencyInjection\\ContainerAwareHttpKernel` +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). 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`` component, you'll receive +``my_foo`` service, but try to inject the ``request`` service, you will receive a :class:`Symfony\\Component\\DependencyInjection\\Exception\\ScopeWideningInjectionException` when compiling the container. Read the sidebar below for more details. @@ -69,10 +71,71 @@ 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. -Setting the Scope in the Definition ------------------------------------ +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: + +* 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 + :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`); + +* 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`); + +Each scenario is detailed in the following sections. + +.. _using-synchronized-service: + +Using a synchronized Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The scope of a service is set in the definition of the service: +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:: + + // src/Acme/HelloBundle/Mail/Mailer.php + namespace Acme\HelloBundle\Mail; + + use Symfony\Component\HttpFoundation\Request; + + class Mailer + { + protected $request; + + public function setRequest(Request $request = null) + { + $this->request = $request; + } + + public function sendEmail() + { + if (null === $this->request) { + // throw an error? + } + + // ... do something using the request here + } + } + +Whenever the ``request`` is entered or leaved, the service container will +automatically call the ``setRequest()`` method with the current ``request`` +instance. + +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 +your code. This should also be taken into account when declaring your service: .. configuration-block:: @@ -82,42 +145,117 @@ The scope of a service is set in the definition of the service: services: greeting_card_manager: class: Acme\HelloBundle\Mail\GreetingCardManager - scope: request + calls: + - [setRequest, ['@?request']] .. code-block:: xml - + + + + .. code-block:: php // src/Acme/HelloBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; + use Symfony\Component\DependencyInjection\ContainerInterface; - $container->setDefinition( + $definition = $container->setDefinition( 'greeting_card_manager', new Definition('Acme\HelloBundle\Mail\GreetingCardManager') - )->setScope('request'); + ) + ->addMethodCall('setRequest', array( + new Reference('request', ContainerInterface::NULL_ON_INVALID_REFERENCE, false) + )); -If you don't specify the scope, it defaults to ``container``, which is what -you want most of the time. Unless your service depends on another service -that's scoped to a narrower scope (most commonly, the ``request`` service), -you probably don't need to set the scope. +.. tip:: -Using a Service from a narrower Scope -------------------------------------- + You can declare your own ``synchronized`` services very easily; here is + the declaration of the ``request`` service for reference: + + .. configuration-block:: + + .. code-block:: yaml -If your service depends on a scoped service, the best solution is to put -it in the same scope (or a narrower one). Usually, this means putting your -new service in the ``request`` scope. + services: + request: + scope: request + synthetic: true + synchronized: true -But this is not always possible (for instance, a twig extension must be in -the ``container`` scope as the Twig environment needs it as a dependency). -In these cases, you should pass the entire container into your service and -retrieve your dependency from the container each time you need it to be sure -you have the right instance:: + .. 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); + +.. _changing-service-scope: + +Changing the Scope of your Service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Changing the scope of a service should be done set in its definition: + +.. configuration-block:: + + .. code-block:: yaml + + # src/Acme/HelloBundle/Resources/config/services.yml + services: + greeting_card_manager: + class: Acme\HelloBundle\Mail\GreetingCardManager + scope: request + arguments: [@request] + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // src/Acme/HelloBundle/Resources/config/services.php + use Symfony\Component\DependencyInjection\Definition; + + $definition = $container->setDefinition( + 'greeting_card_manager', + new Definition( + 'Acme\HelloBundle\Mail\GreetingCardManager', + array(new Reference('request'), + )) + )->setScope('request'); + +.. _passing-container: + +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 +needs it as a dependency). In these cases, you can pass the entire container +into your service:: // src/Acme/HelloBundle/Mail/Mailer.php namespace Acme\HelloBundle\Mail; @@ -160,8 +298,7 @@ The service config for this class would look something like this: services: my_mailer: class: "%my_mailer.class%" - arguments: - - "@service_container" + arguments: ["@service_container"] # scope: container can be omitted as it is the default .. code-block:: xml @@ -195,10 +332,11 @@ The service config for this class would look something like this: .. note:: Injecting the whole container into a service is generally not a good - idea (only inject what you need). In some rare cases, it's necessary - when you have a service in the ``container`` scope that needs a service - in the ``request`` scope. + 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. + 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. From 35fff77b0d6719ae1854de12d65b7884c5f8af8a Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 26 Mar 2013 12:52:37 -0500 Subject: [PATCH 020/245] [#2343] Minor tweaks to synchronized services section --- cookbook/service_container/scopes.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cookbook/service_container/scopes.rst b/cookbook/service_container/scopes.rst index 48e760ca1c6..e779bc933b9 100644 --- a/cookbook/service_container/scopes.rst +++ b/cookbook/service_container/scopes.rst @@ -98,6 +98,9 @@ Each scenario is detailed in the following sections. 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 @@ -127,7 +130,7 @@ without any special code in your service or in your definition:: } } -Whenever the ``request`` is entered or leaved, the service container will +Whenever the ``request`` scope is entered or left, the service container will automatically call the ``setRequest()`` method with the current ``request`` instance. @@ -210,7 +213,7 @@ your code. This should also be taken into account when declaring your service: Changing the Scope of your Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Changing the scope of a service should be done set in its definition: +Changing the scope of a service should be done in its definition: .. configuration-block:: From bf4ce18dc658f4f02bcad20e4f42ee9802f6da38 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 12:46:59 +0100 Subject: [PATCH 021/245] removed deprecated cookie config options --- .../configuration/pdo_session_storage.rst | 24 +++++++-------- reference/configuration/framework.rst | 30 ------------------- 2 files changed, 12 insertions(+), 42 deletions(-) diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index 6db15b4f520..9272b8c2d4b 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -54,7 +54,7 @@ configuration format of your choice): - + @@ -196,16 +196,16 @@ For MSSQL, the statement might look like the following: .. code-block:: sql CREATE TABLE [dbo].[session]( - [session_id] [nvarchar](255) NOT NULL, - [session_value] [ntext] NOT NULL, + [session_id] [nvarchar](255) NOT NULL, + [session_value] [ntext] NOT NULL, [session_time] [int] NOT NULL, - PRIMARY KEY CLUSTERED( - [session_id] ASC - ) WITH ( - PAD_INDEX = OFF, - STATISTICS_NORECOMPUTE = OFF, - IGNORE_DUP_KEY = OFF, - ALLOW_ROW_LOCKS = ON, - ALLOW_PAGE_LOCKS = ON - ) ON [PRIMARY] + PRIMARY KEY CLUSTERED( + [session_id] ASC + ) WITH ( + PAD_INDEX = OFF, + STATISTICS_NORECOMPUTE = OFF, + IGNORE_DUP_KEY = OFF, + ALLOW_ROW_LOCKS = ON, + ALLOW_PAGE_LOCKS = ON + ) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 4e2ac86965f..aef1bd6d053 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -131,9 +131,6 @@ session cookie_lifetime ............... -.. versionadded:: 2.1 - This option was formerly known as ``lifetime`` - **type**: ``integer`` **default**: ``0`` This determines the lifetime of the session - in seconds. By default it will use @@ -142,9 +139,6 @@ This determines the lifetime of the session - in seconds. By default it will use cookie_path ........... -.. versionadded:: 2.1 - This option was formerly known as ``path`` - **type**: ``string`` **default**: ``/`` This determines the path to set in the session cookie. By default it will use ``/``. @@ -152,9 +146,6 @@ This determines the path to set in the session cookie. By default it will use `` cookie_domain ............. -.. versionadded:: 2.1 - This option was formerly known as ``domain`` - **type**: ``string`` **default**: ``''`` This determines the domain to set in the session cookie. By default it's blank, @@ -164,9 +155,6 @@ to the cookie specification. cookie_secure ............. -.. versionadded:: 2.1 - This option was formerly known as ``secure`` - **type**: ``Boolean`` **default**: ``false`` This determines whether cookies should only be sent over secure connections. @@ -174,9 +162,6 @@ This determines whether cookies should only be sent over secure connections. cookie_httponly ............... -.. versionadded:: 2.1 - This option was formerly known as ``httponly`` - **type**: ``Boolean`` **default**: ``false`` This determines whether cookies should only accesible through the HTTP protocol. @@ -429,21 +414,6 @@ Full Default Configuration gc_maxlifetime: ~ save_path: %kernel.cache_dir%/sessions - # DEPRECATED! Please use: cookie_lifetime - lifetime: ~ - - # DEPRECATED! Please use: cookie_path - path: ~ - - # DEPRECATED! Please use: cookie_domain - domain: ~ - - # DEPRECATED! Please use: cookie_secure - secure: ~ - - # DEPRECATED! Please use: cookie_httponly - httponly: ~ - # templating configuration templating: assets_version: ~ From a78694102c8b0aa83cf760277d7686ff16219b75 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 12:48:54 +0100 Subject: [PATCH 022/245] removed deprecated auto_start setting --- reference/configuration/framework.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index aef1bd6d053..a7ac54a321b 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -399,8 +399,6 @@ Full Default Configuration # session configuration session: - # DEPRECATED! Session starts on demand - auto_start: false storage_id: session.storage.native handler_id: session.handler.native_file name: ~ @@ -461,9 +459,4 @@ Full Default Configuration file_cache_dir: %kernel.cache_dir%/annotations debug: %kernel.debug% - -.. versionadded:: 2.1 - The ```framework.session.auto_start`` setting has been removed in Symfony2.1, - it will start on demand now. - .. _`protocol-relative`: http://tools.ietf.org/html/rfc3986#section-4.2 From 934531c41f5eea03bc7324c1faba3e3d49077d15 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 23 Mar 2013 12:49:47 +0100 Subject: [PATCH 023/245] removed deprecated charset setting --- reference/configuration/framework.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index a7ac54a321b..76e0429e357 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -345,7 +345,6 @@ Full Default Configuration .. code-block:: yaml framework: - charset: ~ secret: ~ trusted_proxies: [] ide: ~ From a9c8ed63303e05552adbc398eeb4bfd8ef3ff255 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 30 Mar 2013 08:40:23 -0500 Subject: [PATCH 024/245] [#1829] Moving serializer framework docs around - added a new cookbook and linked to that from everywhere else --- cookbook/index.rst | 1 + cookbook/map.rst.inc | 4 + cookbook/serializer.rst | 111 ++++++++++++++++++++++++++ reference/configuration/framework.rst | 52 +----------- reference/dic_tags.rst | 6 +- 5 files changed, 119 insertions(+), 55 deletions(-) create mode 100644 cookbook/serializer.rst diff --git a/cookbook/index.rst b/cookbook/index.rst index b2e6f87de0f..8131c58898a 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -12,6 +12,7 @@ The Cookbook form/index validation/index configuration/index + serializer service_container/index bundles/index email/index diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 0ec091b14ca..569d3615900 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -122,6 +122,10 @@ * :doc:`/cookbook/security/custom_authentication_provider` * :doc:`/cookbook/security/target_path` +* **Serializer** + + * :doc:`/cookbook/serializer` + * :doc:`/cookbook/service_container/index` * :doc:`/cookbook/service_container/event_listener` diff --git a/cookbook/serializer.rst b/cookbook/serializer.rst new file mode 100644 index 00000000000..d4e9baeab00 --- /dev/null +++ b/cookbook/serializer.rst @@ -0,0 +1,111 @@ +.. index:: + single: Serializer + +How to use the Serializer +========================= + +Serializing and deserializing to and from objects and different formats (e.g. +JSON or XML) is a very complex topic. Symfony comes with a +:doc:`Serializer Component`, which gives you some +tools that you can leverage for your solution. + +In fact, before you start, get familiar with the serializer, normalizers +and encoders by reading the :doc:`Serializer Component`. +You should also check out the `JMSSerializerBundle`_, which expands on the +functionality offered by Symfony's core serializer. + +Activating the Serializer +------------------------- + +.. versionadded:: 2.3 + The Serializer has always existed in Symfony, but prior to Symfony 2.3, + you needed to build the ``serializer`` service yourself. + +The ``serializer`` service is not available by default. To turn it on, activate +it in your configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + # ... + serializer: + enabled: true + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('framework', array( + // ... + 'serializer' => array( + 'enabled' => true + ), + )); + +Adding Normalizers and Encoders +------------------------------- + +Once enabled, the ``serializer`` service will be available in the container +and will be loaded with two :ref:`encoders` +(:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` and +:class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) +but no :ref:`normalizers`, meaning you'll +need to load your own. + +You can load normalizers and/or encoders by tagging them as +:ref:`serializer.normalizer` and +:ref:`serializer.encoder`. It's also +possible to set the priority of the tag in order to decide the matching order. + +Here an example on how to load the load +the :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + get_set_method_normalizer: + class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer + tags: + - { name: serializer.normalizer } + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/config.php + use Symfony\Component\DependencyInjection\Definition; + + $definition = new Definition( + 'Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer' + )); + $definition->addTag('serializer.normalizer'); + $container->setDefinition('get_set_method_normalizer', $definition); + +.. note:: + + The :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` + is broken by design. As soon as you have a circular object graph, an + infinite loop is created when calling the getters. You're encouraged + to add your own normalizers that fit your use-case. + +.. _JMSSerializerBundle: http://jmsyst.com/bundles/JMSSerializerBundle \ No newline at end of file diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 77b4a2c8acb..76a8a33ad94 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -257,58 +257,8 @@ enabled **type**: ``boolean`` **default**: ``false`` Whether to enable the ``serializer`` service or not in the service container. -If enabled, the ``serializer`` service will be available in the container -and will be loaded with two :ref:`encoders` -(:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` and -:class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder`) -but no :ref:`normalizers`, meaning you'll -need to load your own. -You can load normalizers and/or encoders by tagging them as -:ref:`serializer.normalizer` and -:ref:`serializer.encoder`. It's also -possible to set the priority of the tag in order to decide the matching order. - -Here an example on how to load the load -the :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/config.yml - services: - get_set_method_normalizer: - class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer - tags: - - { name: serializer.normalizer } - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // app/config/config.php - use Symfony\Component\DependencyInjection\Definition; - - $definition = new Definition( - 'Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer' - )); - $definition->addTag('serializer.normalizer'); - $container->setDefinition('get_set_method_normalizer', $definition); - -.. note:: - - The :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` - is broken by design. As soon as you have a circular object graph, an - infinite loop is created when calling the getters. You're encouraged - to add your own normalizers that fit your use-case. +For more details, see :doc:`/cookbook/serializer`. templating ~~~~~~~~~~ diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index a7c3670c845..79d44d66d8f 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -574,8 +574,7 @@ serializer.encoder The class that's tagged should implement the :class:`Symfony\\Component\\Serializer\\Encoder\\EncoderInterface` and :class:`Symfony\\Component\\Serializer\\Encoder\\DecoderInterface`. -You have to :ref:`enable the serializer service` -in order to use this tag. +For more details, see :doc:`/cookbook/serializer`. .. _reference-dic-tags-serializer-normalizer: @@ -587,8 +586,7 @@ serializer.normalizer The class that's tagged should implement the :class:`Symfony\\Component\\Serializer\\Normalizer\\NormalizerInterface` and :class:`Symfony\\Component\\Serializer\\Normalizer\\DenormalizerInterface`. -You have to :ref:`enable the serializer service` -in order to use this tag. +For more details, see :doc:`/cookbook/serializer`. swiftmailer.plugin ------------------ From 5e502ea68d0cc4af16e526f50878f839881ad0af Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 30 Mar 2013 16:56:12 -0500 Subject: [PATCH 025/245] [#2352] Minor tweaks to new console events section --- components/console/events.rst | 14 +++++++------- components/console/index.rst | 2 +- components/map.rst.inc | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/components/console/events.rst b/components/console/events.rst index af55b7f6a2e..7306aa50d06 100644 --- a/components/console/events.rst +++ b/components/console/events.rst @@ -1,12 +1,12 @@ .. index:: single: Console; Events -.. versionadded:: 2.3 - The feature described in this chapter was added in 2.3. - Using Events ============ +.. versionadded:: 2.3 + Console events were added in Symfony 2.3. + The Application class of the Console component allows you to optionally hook into the lifecycle of a console application via events. Instead of reinventing the wheel, it uses the Symfony EventDispatcher component to do the work:: @@ -18,7 +18,7 @@ the wheel, it uses the Symfony EventDispatcher component to do the work:: $application->setDispatcher($dispatcher); $application->run(); -The ``ConsoleEvents::COMMAND`` event +The ``ConsoleEvents::COMMAND`` Event ------------------------------------ **Typical Purposes**: Doing something before any command is run (like logging @@ -57,8 +57,8 @@ been executed. After the command has been executed, the ``ConsoleEvents::TERMINATE`` event is dispatched. It can be used to do any actions that need to be executed for all -commands or to cleanup what you initiated in the ``ConsoleEvents::COMMAND`` -command (like sending logs, closing a database connection, sending emails, +commands or to cleanup what you initiated in a ``ConsoleEvents::COMMAND`` +listener (like sending logs, closing a database connection, sending emails, ...). A listener might also change the exit code. Listeners receive a @@ -94,7 +94,7 @@ The ``ConsoleEvents::EXCEPTION`` event command. Whenever an exception is thrown by a command, the ``ConsoleEvents::EXCEPTION`` -command is dispatched. A listener can wrap or change the exception or do +event is dispatched. A listener can wrap or change the exception or do anything useful before the exception is thrown by the application. Listeners receive a diff --git a/components/console/index.rst b/components/console/index.rst index 9b025d3a0a1..c814942d018 100644 --- a/components/console/index.rst +++ b/components/console/index.rst @@ -7,5 +7,5 @@ Console introduction usage single_command_tool - helpers/index events + helpers/index diff --git a/components/map.rst.inc b/components/map.rst.inc index cb32c7bd7fa..c9084b6f897 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -16,6 +16,7 @@ * :doc:`/components/console/introduction` * :doc:`/components/console/usage` * :doc:`/components/console/single_command_tool` + * :doc:`/components/console/events` * :doc:`/components/console/helpers/index` * **CSS Selector** From 5f0aba7acd2ec51220bdb4e812fa0171ac5a1641 Mon Sep 17 00:00:00 2001 From: Dan Finnie Date: Sun, 31 Mar 2013 15:33:49 -0400 Subject: [PATCH 026/245] [SecurityBundle] Document multiple IP matching in rules --- book/security.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/book/security.rst b/book/security.rst index 12c7dfd998e..7d9ecf0ef63 100644 --- a/book/security.rst +++ b/book/security.rst @@ -770,7 +770,7 @@ access control should be used on this request. The following ``access_control`` options are used for matching: * ``path`` -* ``ip`` +* ``ip`` or ``ips`` * ``host`` * ``methods`` @@ -877,6 +877,11 @@ prevent any direct access to these resources from a web browser (by guessing the ESI URL pattern), the ESI route **must** be secured to be only visible from the trusted reverse proxy cache. +.. versionadded:: 2.3 + Version 2.3 allows multiple IP addresses in a single rule with the ``ips: [a, b]`` + construct. Prior to 2.3, users should create one rule per IP address to match and + use the ``ip`` key instead of ``ips``. + Here is an example of how you might secure all ESI routes that start with a given prefix, ``/esi``, from outside access: @@ -888,20 +893,20 @@ given prefix, ``/esi``, from outside access: security: # ... access_control: - - { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 } + - { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } - { path: ^/esi, roles: ROLE_NO_ACCESS } .. code-block:: xml - + .. code-block:: php 'access_control' => array( - array('path' => '^/esi', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ip' => '127.0.0.1'), + array('path' => '^/esi', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', 'ips' => '127.0.0.1, ::1'), array('path' => '^/esi', 'role' => 'ROLE_NO_ACCESS'), ), @@ -909,7 +914,7 @@ Here is how it works when the path is ``/esi/something`` coming from the ``10.0.0.1`` IP: * The first access control rule is ignored as the ``path`` matches but the - ``ip`` does not; + ``ip`` does not match either of the IPs listed; * The second access control rule is enabled (the only restriction being the ``path`` and it matches): as the user cannot have the ``ROLE_NO_ACCESS`` @@ -917,7 +922,8 @@ Here is how it works when the path is ``/esi/something`` coming from the be anything that does not match an existing role, it just serves as a trick to always deny access). -Now, if the same request comes from ``127.0.0.1``: +Now, if the same request comes from ``127.0.0.1`` or ``::1`` (the IPv6 loopback +address): * Now, the first access control rule is enabled as both the ``path`` and the ``ip`` match: access is allowed as the user always has the From 647fd97bee4e20620e818ed1e8de560cbd633852 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 31 Mar 2013 16:49:43 -0500 Subject: [PATCH 027/245] [#2355] Updating note about _method activation for 2.3 --- cookbook/routing/method_parameters.rst | 8 +++++--- reference/configuration/framework.rst | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cookbook/routing/method_parameters.rst b/cookbook/routing/method_parameters.rst index 5446b051f68..f0360e7e87f 100644 --- a/cookbook/routing/method_parameters.rst +++ b/cookbook/routing/method_parameters.rst @@ -76,9 +76,11 @@ Faking the Method with _method .. note:: - The ``_method`` functionality shown here is disabled by default in Symfony 2.2. - To enable it, you must call :method:`Request::enableHttpMethodParameterOverride ` - before you handle the request (e.g. in your front controller). + The ``_method`` functionality shown here is disabled by default in Symfony 2.2 + and enabled by default in Symfony 2.3. To control it in Symfony 2.2, you + must call :method:`Request::enableHttpMethodParameterOverride ` + before you handle the request (e.g. in your front controller). In Symfony + 2.3, use the :ref:`configuration-framework-http_method_override` option. Unfortunately, life isn't quite this simple, since most browsers do not support sending PUT and DELETE requests. Fortunately Symfony2 provides you diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 30f5f88c4ce..79ced25beba 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -52,6 +52,8 @@ it's used for generating the CSRF tokens, but it could be used in any other context where having a unique string is useful. It becomes the service container parameter named ``kernel.secret``. +.. _configuration-framework-http_method_override: + http_method_override ~~~~~~~~~~~~~~~~~~~~ From cf5683acfebc2223f9115cf44ecf5436c6b6a24b Mon Sep 17 00:00:00 2001 From: Max Beutel Date: Wed, 3 Apr 2013 10:25:23 +0200 Subject: [PATCH 028/245] [TwigBundle] document new autoescape features --- reference/configuration/twig.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index 5977ce092fb..13378f14e7a 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -31,14 +31,16 @@ TwigBundle Configuration Reference # set to service or leave blank type: ~ value: ~ - autoescape: ~ - base_template_class: ~ # Example: Twig_Template - cache: "%kernel.cache_dir%/twig" - charset: "%kernel.charset%" - debug: "%kernel.debug%" - strict_variables: ~ - auto_reload: ~ - optimizations: ~ + autoescape: ~ + autoescape_service: ~ # Example: @my_service + autoescape_service_method: ~ # use in combination with autoescape_service option + base_template_class: ~ # Example: Twig_Template + cache: "%kernel.cache_dir%/twig" + charset: "%kernel.charset%" + debug: "%kernel.debug%" + strict_variables: ~ + auto_reload: ~ + optimizations: ~ .. code-block:: xml From 4c9b68940214eb09cd7195787757bf5488598d99 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Fri, 15 Mar 2013 18:28:15 +0100 Subject: [PATCH 029/245] Added documentation for the Intl component --- components/index.rst | 2 +- components/intl.rst | 517 +++++++++++++++++++++++++++++++++++++++++ components/locale.rst | 72 ------ components/map.rst.inc | 4 +- 4 files changed, 520 insertions(+), 75 deletions(-) create mode 100644 components/intl.rst delete mode 100644 components/locale.rst diff --git a/components/index.rst b/components/index.rst index 8b4abe24f8d..7eed4559c83 100644 --- a/components/index.rst +++ b/components/index.rst @@ -16,7 +16,7 @@ The Components finder http_foundation/index http_kernel/index - locale + intl process property_access/index routing/index diff --git a/components/intl.rst b/components/intl.rst new file mode 100644 index 00000000000..d5ceb76b6cb --- /dev/null +++ b/components/intl.rst @@ -0,0 +1,517 @@ +.. index:: + single: Intl + single: Components; Intl + +The Intl Component +================== + + A PHP replacement layer for the C `intl extension`_ that includes additional + data from the ICU library. + +.. note:: + + The replacement layer is limited to the locale "en". If you want to use + other locales, you should `install the intl extension`_ instead. + +Installation +------------ + +You can install the component in two different ways: + +* Using the official Git repository (https://github.com/symfony/Intl); +* :doc:`Install it via Composer` (``symfony/intl`` on `Packagist`_). + +If you install the component via Composer, the following classes and functions +of the intl extension will be automatically provided if the intl extension is +not loaded: + +* :phpclass:``Collator`` +* :phpclass:``IntlDateFormatter`` +* :phpclass:``Locale`` +* :phpclass:``NumberFormatter`` +* :phpfunction:``intl_error_name`` +* :phpfunction:``intl_is_failure`` +* :phpfunction:``intl_get_error_code`` +* :phpfunction:``intl_get_error_message`` + +If you don't use Composer but the Symfony ClassLoader component, you need to +load them manually by adding the following lines to your autoload code:: + + if (!function_exists('intl_is_failure')) { + require '/path/to/Icu/Resources/stubs/functions.php'; + + $loader->registerPrefixFallback('/path/to/Icu/Resources/stubs'); + } + +The component provides replacements for the following functions and classes: + +.. note:: + + The stub implementation only supports the locale ``en``. + +Stubbed Classes +--------------- + +The stubbed classes of the intl extension are limited to the locale "en" and +will throw an exception if you try to use a different locale. For using other +locales, `install the intl extension`_ instead. + +Locale +~~~~~~ + +The only method supported in the :phpclass:``Locale`` class is +:phpmethod:`Locale::getDefault`. This method will always return "en". All other +methods will throw an exception when used. + +NumberFormatter +~~~~~~~~~~~~~~~ + +Numbers can be formatted with the :phpclass:``NumberFormatter`` class. +The following methods are supported. All other methods are not supported and +will throw an exception when used. + +.. _`NumberFormatter::__construct()`: + +\__construct($locale = $style = null, $pattern = null) +...................................................... + +The only supported locale is "en". The supported styles are +``NumberFormatter::DECIMAL`` and ``NumberFormatter::CURRENCY``. The argument +``$pattern`` may not be used. + +::create($locale = $style = null, $pattern = null) +.................................................. + +See `NumberFormatter::__construct()`_. + +formatCurrency($value, $currency) +................................. + +Fully supported. + +format($value, $type = NumberFormatter::TYPE_DEFAULT) +..................................................... + +Only type ``NumberFormatter::TYPE_DEFAULT`` is supported. + +getAttribute($attr) +................... + +Fully supported. + +getErrorCode() +.............. + +Fully supported. + +getErrorMessage() +................. + +Fully supported. + +getLocale($type = Locale::ACTUAL_LOCALE) +........................................ + +The parameter ``$type`` is ignored. + +parse($value, $type = NumberFormatter::TYPE_DOUBLE, &$position = null) +...................................................................... + +The supported types are ``NumberFormatter::TYPE_DOUBLE``, +``NumberFormatter::TYPE_INT32`` and ``NumberFormatter::TYPE_INT64``. The +parameter ``$position`` must always be ``null``. + +setAttribute($attr, $value) +........................... + +The only supported attributes are ``NumberFormatter::FRACTION_DIGITS``, +``NumberFormatter::GROUPING_USED`` and ``NumberFormatter::ROUNDING_MODE``. + +The only supported rounding modes are ``NumberFormatter::ROUND_HALFEVEN``, +``NumberFormatter::ROUND_HALFDOWN`` and ``NumberFormatter::ROUND_HALFUP``. + +IntlDateFormatter +~~~~~~~~~~~~~~~~~ + +Dates can be formatted with the :phpclass:`IntlDateFormatter` class. The +following methods are supported. All other methods are not supported and will +throw an exception when used. + +.. _`IntlDateFormatter::__construct()`: + +\__construct($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null) +........................................................................................................................ + +The only supported locale is "en". The parameter ``$calendar`` can only be +``IntlDateFormatter::GREGORIAN``. + +::create($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null) +.................................................................................................................... + +See `IntlDateFormatter::__construct()`_. + +format($timestamp) +.................. + +Fully supported. + +getCalendar() +............. + +Fully supported. + +getDateType() +............. + +Fully supported. + +getErrorCode() +.............. + +Fully supported. + +getErrorMessage() +................. + +Fully supported. + +getLocale($type = Locale::ACTUAL_LOCALE) +........................................ + +The parameter ``$type`` is ignored. + +getPattern() +............ + +Fully supported. + +getTimeType() +............. + +Fully supported. + +getTimeZoneId() +............... + +Fully supported. + +isLenient() +........... + +Always returns ``false``. + +parse($value, &$position = null) +................................ + +The parameter ``$position`` must always be ``null``. + +setLenient($lenient) +.................... + +Only accepts ``false``. + +setPattern($pattern) +.................... + +Fully supported. + +setTimeZoneId($timeZoneId) +.......................... + +Fully supported. + +setTimeZone($timeZone) +...................... + +Fully supported. + +Collator +~~~~~~~~ + +Localized strings can be sorted with the :phpclass:`\Collator` class. The +following methods are supported. All other methods are not supported and will +throw an exception when used. + +.. _`Collator::__construct()`: + +\__construct($locale) +..................... + +The only supported locale is "en". + +create($locale) +............... + +See `Collator::__construct()`_. + +asort(&$array, $sortFlag = Collator::SORT_REGULAR) +.................................................. + +Fully supported. + +getErrorCode() +.............. + +Fully supported. + +getErrorMessage() +................. + +Fully supported. + +getLocale($type = Locale::ACTUAL_LOCALE) +........................................ + +The parameter ``$type`` is ignored. + +ResourceBundle +~~~~~~~~~~~~~~ + +The :phpclass:`ResourceBundle` class is not and will not be supported. Instead, +this component ships a set of readers and writers for reading and writing arrays +(or array-like objects) from/to resource bundle files. The following classes +are supported: + +TextBundleWriter +................ + +Writes an array or an array-like object to a plain text resource bundle. The +resulting .txt file can be converted to a binary .res file with the +:class:`Symfony\\Component\\Intl\\ResourceBundle\\Compiler\\BundleCompiler` +class:: + + use Symfony\Component\Intl\ResourceBundle\Writer\TextBundleWriter; + use Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompiler; + + $writer = new TextBundleWriter(); + $writer->write('/path/to/bundle', 'en', array( + 'Data' => array( + 'entry1', + 'entry2', + ... + ), + )); + + $compiler = new BundleCompiler(); + $compiler->compile('/path/to/bundle', '/path/to/binary/bundle'); + +The command "genrb" must be available for the +:class:`Symfony\\Component\\Intl\\ResourceBundle\\Compiler\\BundleCompiler` to +work. If the command is located in a non-standard location, you can pass its +path to the +:class:`Symfony\\Component\\Intl\\ResourceBundle\\Compiler\\BundleCompiler` +constructor. + +PhpBundleWriter +~~~~~~~~~~~~~~~ + +Writes an array or an array-like object to a .php resource bundle:: + + use Symfony\Component\Intl\ResourceBundle\Writer\PhpBundleWriter; + + $writer = new PhpBundleWriter(); + $writer->write('/path/to/bundle', 'en', array( + 'Data' => array( + 'entry1', + 'entry2', + ... + ), + )); + +BinaryBundleReader +~~~~~~~~~~~~~~~~~~ + +Reads binary resource bundle files and returns an array or an array-like object. +This class currently only works with the `intl extension`_ installed:: + + use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader; + + $reader = new BinaryBundleReader(); + $data = $reader->read('/path/to/bundle', 'en'); + + echo $data['Data']['entry1']; + +PhpBundleReader +~~~~~~~~~~~~~~~ + +Reads resource bundles from .php files and returns an array or an array-like +object:: + + use Symfony\Component\Intl\ResourceBundle\Reader\PhpBundleReader; + + $reader = new PhpBundleReader(); + $data = $reader->read('/path/to/bundle', 'en'); + + echo $data['Data']['entry1']; + +BufferedBundleReader +~~~~~~~~~~~~~~~~~~~~ + +Wraps another reader, but keeps the last N reads in a buffer, where N is a +buffer size passed to the constructor:: + + use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader; + use Symfony\Component\Intl\ResourceBundle\Reader\BufferedBundleReader; + + $reader = new BufferedBundleReader(new BinaryBundleReader(), 10); + + // actually reads the file + $data = $reader->read('/path/to/bundle', 'en'); + + // returns data from the buffer + $data = $reader->read('/path/to/bundle', 'en'); + + // actually reads the file + $data = $reader->read('/path/to/bundle', 'fr'); + +StructuredBundleReader +~~~~~~~~~~~~~~~~~~~~~~ + +Wraps another reader and offers a +:method:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\StructuredBundleReaderInterface::readEntry` +method for reading an entry of the resource bundle without having to worry +whether array keys are set or not. If a path cannot be resolved, ``null`` is +returned:: + + use Symfony\Component\Intl\ResourceBundle\Reader\BinaryBundleReader; + use Symfony\Component\Intl\ResourceBundle\Reader\StructuredBundleReader; + + $reader = new StructuredBundleReader(new BinaryBundleReader()); + + $data = $reader->read('/path/to/bundle', 'en'); + + // Produces an error if the key "Data" does not exist + echo $data['Data']['entry1']; + + // Returns null if the key "Data" does not exist + echo $reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1')); + +Additionally, the +:method:`Symfony\\Component\\Intl\\ResourceBundle\\Reader\\StructuredBundleReaderInterface::readEntry` +method resolves fallback locales. For example, the fallback locale of "en_GB" is +"en". For single-valued entries (strings, numbers etc.), the entry will be read +from the fallback locale if it cannot be found in the more specific locale. For +multi-valued entries (arrays), the values of the more specific and the fallback +locale will be merged. In order to suppress this behavior, the last parameter +``$fallback`` can be set to ``false``:: + + echo $reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1'), false); + +Included Resource Bundles +------------------------- + +The ICU data is located in several "resource bundles". You can access a PHP +wrapper of these bundles through the static +:class:`Symfony\\Component\\Intl\\Intl` class. + +Languages and Scripts +~~~~~~~~~~~~~~~~~~~~~ + +The translations of language and script names can be found in the language +bundle:: + + use Symfony\Component\Intl\Intl; + + \Locale::setDefault('en'); + + $languages = Intl::getLanguageBundle()->getLanguageNames(); + // => array('ab' => 'Abkhazian', ...) + + $language = Intl::getLanguageBundle()->getLanguageName('de'); + // => 'German' + + $language = Intl::getLanguageBundle()->getLanguageName('de', 'AT'); + // => 'Austrian German' + + $scripts = Intl::getLanguageBundle()->getScriptNames(); + // => array('Arab' => 'Arabic', ...) + + $script = Intl::getLanguageBundle()->getScriptName('Hans'); + // => 'Simplified' + +All methods accept the translation locale as last, optional parameter, which +defaults to the current default locale:: + + $languages = Intl::getLanguageBundle()->getLanguageNames('de'); + // => array('ab' => 'Abchasisch', ...) + +Countries +~~~~~~~~~ + +The translations of country names can be found in the region bundle:: + + use Symfony\Component\Intl\Intl; + + \Locale::setDefault('en'); + + $countries = Intl::getRegionBundle()->getCountryNames(); + // => array('AF' => 'Afghanistan', ...) + + $country = Intl::getRegionBundle()->getCountryName('GB'); + // => 'United Kingdom' + +All methods accept the translation locale as last, optional parameter, which +defaults to the current default locale:: + + $countries = Intl::getRegionBundle()->getCountryNames('de'); + // => array('AF' => 'Afghanistan', ...) + +Locales +~~~~~~~ + +The translations of locale names can be found in the locale bundle:: + + use Symfony\Component\Intl\Intl; + + \Locale::setDefault('en'); + + $locales = Intl::getLocaleBundle()->getLocaleNames(); + // => array('af' => 'Afrikaans', ...) + + $locale = Intl::getLocaleBundle()->getLocaleName('zh_Hans_MO'); + // => 'Chinese (Simplified, Macau SAR China)' + +All methods accept the translation locale as last, optional parameter, which +defaults to the current default locale:: + + $locales = Intl::getLocaleBundle()->getLocaleNames('de'); + // => array('af' => 'Afrikaans', ...) + +Currencies +~~~~~~~~~~ + +The translations of currency names and other currency-related information can +be found in the currency bundle:: + + use Symfony\Component\Intl\Intl; + + \Locale::setDefault('en'); + + $currencies = Intl::getCurrencyBundle()->getCurrencyNames(); + // => array('AFN' => 'Afghan Afghani', ...) + + $currency = Intl::getCurrencyBundle()->getCurrencyName('INR'); + // => 'Indian Rupee' + + $symbol = Intl::getCurrencyBundle()->getCurrencySymbol('INR'); + // => '₹' + + $fractionDigits = Intl::getCurrencyBundle()->getFractionDigits('INR'); + // => 2 + + $roundingIncrement = Intl::getCurrencyBundle()->getRoundingIncrement('INR'); + // => 0 + +All methods (except for +:method:`Symfony\\Component\\Intl\\ResourceBundle\\CurrencyBundleInterface::getFractionDigits` +and +:method:`Symfony\\Component\\Intl\\ResourceBundle\\CurrencyBundleInterface::getRoundingIncrement()`) +accept the translation locale as last, optional parameter, which defaults to the +current default locale:: + + $currencies = Intl::getCurrencyBundle()->getCurrencyNames('de'); + // => array('AFN' => 'Afghanische Afghani', ...) + +.. _Packagist: https://packagist.org/packages/symfony/locale +.. _intl extension: http://www.php.net/manual/en/book.intl.php +.. _install the intl extension: http://www.php.net/manual/en/intl.setup.php diff --git a/components/locale.rst b/components/locale.rst deleted file mode 100644 index 526a97221c8..00000000000 --- a/components/locale.rst +++ /dev/null @@ -1,72 +0,0 @@ -.. index:: - single: Locale - single: Components; Locale - -The Locale Component -==================== - - Locale component provides fallback code to handle cases when the ``intl`` extension is missing. - Additionally it extends the implementation of a native :phpclass:`Locale` class with several handy methods. - -Replacement for the following functions and classes is provided: - -* :phpfunction:`intl_is_failure` -* :phpfunction:`intl_get_error_code` -* :phpfunction:`intl_get_error_message` -* :phpclass:`Collator` -* :phpclass:`IntlDateFormatter` -* :phpclass:`Locale` -* :phpclass:`NumberFormatter` - -.. note:: - - Stub implementation only supports the ``en`` locale. - -Installation ------------- - -You can install the component in many different ways: - -* Use the official Git repository (https://github.com/symfony/Locale); -* :doc:`Install it via Composer` (``symfony/locale`` on `Packagist`_). - -Usage ------ - -Taking advantage of the fallback code includes requiring function stubs and adding class stubs to the autoloader. - -When using the ClassLoader component following code is sufficient to supplement missing ``intl`` extension: - -.. code-block:: php - - if (!function_exists('intl_get_error_code')) { - require __DIR__.'/path/to/src/Symfony/Component/Locale/Resources/stubs/functions.php'; - - $loader->registerPrefixFallbacks( - array(__DIR__.'/path/to/src/Symfony/Component/Locale/Resources/stubs') - ); - } - -:class:`Symfony\\Component\\Locale\\Locale` class enriches native :phpclass:`Locale` class with additional features: - -.. code-block:: php - - use Symfony\Component\Locale\Locale; - - // Get the country names for a locale or get all country codes - $countries = Locale::getDisplayCountries('pl'); - $countryCodes = Locale::getCountries(); - - // Get the language names for a locale or get all language codes - $languages = Locale::getDisplayLanguages('fr'); - $languageCodes = Locale::getLanguages(); - - // Get the locale names for a given code or get all locale codes - $locales = Locale::getDisplayLocales('en'); - $localeCodes = Locale::getLocales(); - - // Get ICU versions - $icuVersion = Locale::getIntlIcuVersion(); - $icuDataVersion = Locale::getIcuDataVersion(); - -.. _Packagist: https://packagist.org/packages/symfony/locale diff --git a/components/map.rst.inc b/components/map.rst.inc index f16a8e874b2..120831c3336 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -67,9 +67,9 @@ * :doc:`/components/http_kernel/introduction` -* **Locale** +* **Intl** - * :doc:`/components/locale` + * :doc:`/components/intl` * **Process** From cfea9507552cd1ac173c5cc98ad313566b6652b7 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Sun, 17 Mar 2013 13:26:05 +0100 Subject: [PATCH 030/245] Fixed quirks in the Intl documentation --- components/intl.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/components/intl.rst b/components/intl.rst index d5ceb76b6cb..51a25a3ffa2 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -25,14 +25,14 @@ If you install the component via Composer, the following classes and functions of the intl extension will be automatically provided if the intl extension is not loaded: -* :phpclass:``Collator`` -* :phpclass:``IntlDateFormatter`` -* :phpclass:``Locale`` -* :phpclass:``NumberFormatter`` -* :phpfunction:``intl_error_name`` -* :phpfunction:``intl_is_failure`` -* :phpfunction:``intl_get_error_code`` -* :phpfunction:``intl_get_error_message`` +* :phpclass:`Collator` +* :phpclass:`IntlDateFormatter` +* :phpclass:`Locale` +* :phpclass:`NumberFormatter` +* :phpfunction:`intl_error_name` +* :phpfunction:`intl_is_failure` +* :phpfunction:`intl_get_error_code` +* :phpfunction:`intl_get_error_message` If you don't use Composer but the Symfony ClassLoader component, you need to load them manually by adding the following lines to your autoload code:: @@ -59,14 +59,14 @@ locales, `install the intl extension`_ instead. Locale ~~~~~~ -The only method supported in the :phpclass:``Locale`` class is +The only method supported in the :phpclass:`Locale` class is :phpmethod:`Locale::getDefault`. This method will always return "en". All other methods will throw an exception when used. NumberFormatter ~~~~~~~~~~~~~~~ -Numbers can be formatted with the :phpclass:``NumberFormatter`` class. +Numbers can be formatted with the :phpclass:`NumberFormatter` class. The following methods are supported. All other methods are not supported and will throw an exception when used. @@ -288,7 +288,7 @@ class:: 'Data' => array( 'entry1', 'entry2', - ... + // ... ), )); @@ -314,7 +314,7 @@ Writes an array or an array-like object to a .php resource bundle:: 'Data' => array( 'entry1', 'entry2', - ... + // ... ), )); From 5921a7b8783a5c3d99af90ed5a10d5f8f77ea07e Mon Sep 17 00:00:00 2001 From: Drak Date: Sat, 6 Apr 2013 18:59:10 +0100 Subject: [PATCH 031/245] Document PhpSessionStorage --- .../http_foundation/session_php_legacy.rst | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 components/http_foundation/session_php_legacy.rst diff --git a/components/http_foundation/session_php_legacy.rst b/components/http_foundation/session_php_legacy.rst new file mode 100644 index 00000000000..2cb19c7d2be --- /dev/null +++ b/components/http_foundation/session_php_legacy.rst @@ -0,0 +1,40 @@ +.. index:: + single: HTTP + single: HttpFoundation, Sessions + +Integrating with legacy sessions +================================ + +Sometimes it may be necessary to integrate Symfony into a legacy application +where you do not initially have the level of control you require. + +As stated elsewhere, Symfony Sessions are designed to replace the use of +PHP's native `session_*()` functions and use of the `$_SESSION` +superglobal. Additionally, it is mandatory for Symfony to start the session. + +However when there really are circumstances where this is not possible, it is possible +to use a special storage bridge +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpSessionStorage` +which is designed to allow Symfony to work with a session started outside of +the Symfony Session framework. You are warned that things can interrupt this +use case unless you are careful: for example legacy application erases `$_SESSION`. + +Typical use of this might look as follows:: + + start(); + +This will allow you to start using the Symfony Session API and allow +migration of your application to Symfony Sessions. \ No newline at end of file From 043773381c07b7be0879c87cff8c0c24cc5a0fb4 Mon Sep 17 00:00:00 2001 From: Drak Date: Sat, 6 Apr 2013 19:13:33 +0100 Subject: [PATCH 032/245] Update index --- components/http_foundation/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/http_foundation/index.rst b/components/http_foundation/index.rst index 9937c960776..c638e784903 100644 --- a/components/http_foundation/index.rst +++ b/components/http_foundation/index.rst @@ -8,4 +8,5 @@ HTTP Foundation sessions session_configuration session_testing + session_php_legacy trusting_proxies From 5f415702c99c945a21810f8b36431874897babec Mon Sep 17 00:00:00 2001 From: Drak Date: Sat, 6 Apr 2013 19:50:03 +0100 Subject: [PATCH 033/245] Document start on demand feature. --- .../http_foundation/session_configuration.rst | 37 ++++++++++++++++++- reference/configuration/framework.rst | 4 ++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index f445f740b76..df0ab84ed6e 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -79,7 +79,7 @@ examples if you wish to write your own. Example usage:: use Symfony\Component\HttpFoundation\Session\Session; - use Symfony\Component\HttpFoundation\Session\Storage\SessionStorage; + use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; $storage = new NativeSessionStorage(array(), new PdoSessionHandler()); @@ -217,6 +217,41 @@ particular cookie by reading the ``getLifetime()`` method:: The expiry time of the cookie can be determined by adding the created timestamp and the lifetime. +Session start-on-demand +~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + Control over session "start-on-demand" was added in Symfony 2.3. + +In versions 2.1-2.2, Symfony Sessions automatically invoked `$session->start()` when +any attempt was made to access session data (effectively 'start on demand'). +From Symfony 2.3 this behaviour can be controlled. + +There are three modes defined by :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface` + +The settings are as follows: + + - `SessionStorageInterface::NO_START_ON_DEMAND_STRICT` - The session will not be started on demand + and any attempt to read or write session data will result in a `\RuntimeException` + - `SessionStorageInterface::START_ON_DEMAND` - The session will be started if it hasn't already been + when any attempt is made to read ro write session data. + - `SessionStorageInterface::NO_START_ON_DEMAND_LAX` - The sessions will not be started on demand + when session data is read or written to. It will allow access to the unitialized `BagInterface`. + If this session is subsequently started manually after data is written to a `BagInterface` will + be overwritten (by the session data read from persistence). + +You can configure these by injecting a configured storage engine into the session:: + + Date: Sat, 6 Apr 2013 20:02:49 +0100 Subject: [PATCH 034/245] Correct markup --- .../http_foundation/session_configuration.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index df0ab84ed6e..d92bd8fe300 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -223,7 +223,7 @@ Session start-on-demand .. versionadded:: 2.3 Control over session "start-on-demand" was added in Symfony 2.3. -In versions 2.1-2.2, Symfony Sessions automatically invoked `$session->start()` when +In versions 2.1-2.2, Symfony Sessions automatically invoked ``$session->start()`` when any attempt was made to access session data (effectively 'start on demand'). From Symfony 2.3 this behaviour can be controlled. @@ -231,13 +231,13 @@ There are three modes defined by :class:`Symfony\\Component\\HttpFoundation\\Ses The settings are as follows: - - `SessionStorageInterface::NO_START_ON_DEMAND_STRICT` - The session will not be started on demand - and any attempt to read or write session data will result in a `\RuntimeException` - - `SessionStorageInterface::START_ON_DEMAND` - The session will be started if it hasn't already been + - ``SessionStorageInterface::NO_START_ON_DEMAND_STRICT`` - The session will not be started on demand + and any attempt to read or write session data will result in a ``\RuntimeException`` + - ``SessionStorageInterface::START_ON_DEMAND`` - The session will be started if it hasn't already been when any attempt is made to read ro write session data. - - `SessionStorageInterface::NO_START_ON_DEMAND_LAX` - The sessions will not be started on demand - when session data is read or written to. It will allow access to the unitialized `BagInterface`. - If this session is subsequently started manually after data is written to a `BagInterface` will + - ``SessionStorageInterface::NO_START_ON_DEMAND_LAX`` - The sessions will not be started on demand + when session data is read or written to. It will allow access to the unitialized ``BagInterface``. + If this session is subsequently started manually after data is written to a ``BagInterface`` will be overwritten (by the session data read from persistence). You can configure these by injecting a configured storage engine into the session:: From 67a5ecde4ba541f82a222cd64bcf1a1a6629433e Mon Sep 17 00:00:00 2001 From: Drak Date: Sat, 6 Apr 2013 20:08:35 +0100 Subject: [PATCH 035/245] Added nortes as per @stof's recommendation. --- components/http_foundation/session_php_legacy.rst | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/components/http_foundation/session_php_legacy.rst b/components/http_foundation/session_php_legacy.rst index 2cb19c7d2be..1950785ab53 100644 --- a/components/http_foundation/session_php_legacy.rst +++ b/components/http_foundation/session_php_legacy.rst @@ -9,7 +9,7 @@ Sometimes it may be necessary to integrate Symfony into a legacy application where you do not initially have the level of control you require. As stated elsewhere, Symfony Sessions are designed to replace the use of -PHP's native `session_*()` functions and use of the `$_SESSION` +PHP's native ``session_*()`` functions and use of the ``$_SESSION`` superglobal. Additionally, it is mandatory for Symfony to start the session. However when there really are circumstances where this is not possible, it is possible @@ -17,7 +17,7 @@ to use a special storage bridge :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpSessionStorage` which is designed to allow Symfony to work with a session started outside of the Symfony Session framework. You are warned that things can interrupt this -use case unless you are careful: for example legacy application erases `$_SESSION`. +use case unless you are careful: for example legacy application erases ``$_SESSION``. Typical use of this might look as follows:: @@ -37,4 +37,13 @@ Typical use of this might look as follows:: $session->start(); This will allow you to start using the Symfony Session API and allow -migration of your application to Symfony Sessions. \ No newline at end of file +migration of your application to Symfony Sessions. + +.. note:: + +Symfony Sessions store data like attributes in special 'Bags' which use a +key in the ``$_SESSION`` superglobal. This means that a Symfony Session +cannot access arbitary keys in ``$_SESSION`` that may be set by the legacy +application, although all the ``$_SESSION`` contents will be saved when +the session is saved. + From 793e5703ebf000353ee04bbb5f486bb3b18ddd6c Mon Sep 17 00:00:00 2001 From: Drak Date: Sat, 6 Apr 2013 22:20:21 +0100 Subject: [PATCH 036/245] Style fixes --- components/http_foundation/session_php_legacy.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/http_foundation/session_php_legacy.rst b/components/http_foundation/session_php_legacy.rst index 1950785ab53..69903ef7b0b 100644 --- a/components/http_foundation/session_php_legacy.rst +++ b/components/http_foundation/session_php_legacy.rst @@ -2,7 +2,7 @@ single: HTTP single: HttpFoundation, Sessions -Integrating with legacy sessions +Integrating with Legacy Sessions ================================ Sometimes it may be necessary to integrate Symfony into a legacy application @@ -41,9 +41,9 @@ migration of your application to Symfony Sessions. .. note:: -Symfony Sessions store data like attributes in special 'Bags' which use a -key in the ``$_SESSION`` superglobal. This means that a Symfony Session -cannot access arbitary keys in ``$_SESSION`` that may be set by the legacy -application, although all the ``$_SESSION`` contents will be saved when -the session is saved. + Symfony Sessions store data like attributes in special 'Bags' which use a + key in the ``$_SESSION`` superglobal. This means that a Symfony Session + cannot access arbitary keys in ``$_SESSION`` that may be set by the legacy + application, although all the ``$_SESSION`` contents will be saved when + the session is saved. From 6374b08e60bb48aa9e62da387713333a94ba15cc Mon Sep 17 00:00:00 2001 From: umpirsky Date: Sat, 6 Apr 2013 19:04:03 +0200 Subject: [PATCH 037/245] Console table helper --- components/console/helpers/index.rst | 1 + components/console/helpers/map.rst.inc | 1 + components/console/helpers/tablehelper.rst | 55 +++++++++++++++++++++ images/components/console/table.png | Bin 0 -> 67527 bytes 4 files changed, 57 insertions(+) create mode 100644 components/console/helpers/tablehelper.rst create mode 100644 images/components/console/table.png diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index adb2a4bbc8a..c922e732e64 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -10,6 +10,7 @@ The Console Helpers dialoghelper formatterhelper progresshelper + tablehelper The Console Components comes with some useful helpers. These helpers contain function to ease some common tasks. diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index cbc819ad832..60b32c03975 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,3 +1,4 @@ * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` * :doc:`/components/console/helpers/progresshelper` +* :doc:`/components/console/helpers/tablehelper` diff --git a/components/console/helpers/tablehelper.rst b/components/console/helpers/tablehelper.rst new file mode 100644 index 00000000000..a41ad357ee7 --- /dev/null +++ b/components/console/helpers/tablehelper.rst @@ -0,0 +1,55 @@ +.. index:: + single: Console Helpers; Table Helper + +Table Helper +============ + +.. versionadded:: 2.3 + The ``table`` helper was added in Symfony 2.3. + +When building a console application it may be useful to display tabular data: + +.. image:: /images/components/console/table.png + +To display table, use the :class:`Symfony\\Component\\Console\\Helper\\TableHelper`, +set headers, rows and render:: + + $table = $app->getHelperSet()->get('table'); + $table + ->setHeaders(array('ISBN', 'Title', 'Author')) + ->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + array('960-425-059-0', 'The Lord of the Rings', 'J. R. R. Tolkien'), + array('80-902734-1-6', 'And Then There Were None', 'Agatha Christie'), + )) + ; + $table->render($output); + +Table layout can be customized as well. There are two ways to customize table rendering: +using named layouts or by customizing rendering options. + +Customize Table Layout using Named Layouts +------------------------------------------ + +Table helper is shipped with two preconfigured table layouts: + +* ``TableHelper::LAYOUT_DEFAULT`` + +* ``TableHelper::LAYOUT_BORDERLESS`` + +Layout can be set using :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setLayout` method. + +Customize Table Layout using Rendering Options +---------------------------------------------- + +You can control table rendering by setting custom rendering option values: + +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setPaddingChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setHorizontalBorderChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVerticalBorderChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVrossingChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVellHeaderFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setVellRowFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setBorderFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableHelper::setPadType` diff --git a/images/components/console/table.png b/images/components/console/table.png new file mode 100644 index 0000000000000000000000000000000000000000..ba1e3ae79b9e4522818d0a44f7d356cdaf6c7528 GIT binary patch literal 67527 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 Date: Sat, 6 Apr 2013 22:31:27 +0100 Subject: [PATCH 038/245] Update values. --- reference/configuration/framework.rst | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 3d15cf7cb53..461f0a1bcdf 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -26,6 +26,8 @@ Configuration * enabled * field_name * `session`_ + * `name`_ + * `mock_name`_ * `auto_start`_ * `on_demand`_ * `cookie_lifetime`_ @@ -150,6 +152,43 @@ csrf_protection session ~~~~~~~ +on_demand +......... + +**type**: ``string`` **default**: ``on`` + +Can be values + + - ``on`` - start automatically if not started upon session read/write + - ``off`` - do not start session automatically on data read/write, if an attempt is + make to do so, throw a ``\RuntimeException`` + - ``off_lax`` - do not start session automatically on data read/write, but if an attempt + is made to read or write to the session, allow access to the relevent bag. + If data is written to the bags and a session is subsequently started, it will be + overwritten. + +auto_start +.......... + +**type**: ``Boolean`` **default**: ``false`` + +This controls the ``SessionListener`` which will automatically start the session +during the Request cycle. + +name +.... + +**type**: ``string`` + +Sets the session cookie name + +mock_name +......... + +**type**: ``string`` + +Sets the mock session cookie name + cookie_lifetime ............... From bdc83783e88c5809cffad6dfe3608dced42f4567 Mon Sep 17 00:00:00 2001 From: Drak Date: Sat, 6 Apr 2013 22:32:46 +0100 Subject: [PATCH 039/245] formatting --- components/http_foundation/session_configuration.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index d92bd8fe300..9cae338e55a 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -248,7 +248,9 @@ You can configure these by injecting a configured storage engine into the sessio use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - $storage = new NativeSessionStorage(array(), new PdoSessionHandler(), SessionStorageInterface::NO_START_ON_DEMAND_STRICT); + $storage = new NativeSessionStorage(array(), + new PdoSessionHandler(), + SessionStorageInterface::NO_START_ON_DEMAND_STRICT); $session = new Session($storage); From 852a205bdcf82c3f3697759469a844ae5caa006a Mon Sep 17 00:00:00 2001 From: Drak Date: Sat, 6 Apr 2013 22:33:34 +0100 Subject: [PATCH 040/245] formatting --- components/http_foundation/session_configuration.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index 9cae338e55a..ec542980aed 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -227,7 +227,8 @@ In versions 2.1-2.2, Symfony Sessions automatically invoked ``$session->start()` any attempt was made to access session data (effectively 'start on demand'). From Symfony 2.3 this behaviour can be controlled. -There are three modes defined by :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface` +There are three modes defined by +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface` The settings are as follows: From df35697260d77bc6910818aea7654bf1923b6126 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 7 Apr 2013 18:08:21 +0200 Subject: [PATCH 041/245] added documentation for the new Debug component --- components/debug.rst | 75 ++++++++++++++++++++++++++++++++++++++++++ components/index.rst | 1 + components/map.rst.inc | 4 +++ 3 files changed, 80 insertions(+) create mode 100644 components/debug.rst diff --git a/components/debug.rst b/components/debug.rst new file mode 100644 index 00000000000..94f21514550 --- /dev/null +++ b/components/debug.rst @@ -0,0 +1,75 @@ +.. index:: + single: Debug + single: Components; Debug + +The Debug Component +=================== + + The Debug Component provides tools to ease debugging PHP code. + +.. versionadded:: 2.3 + The Debug Component is new to Symfony 2.3. Previously, the classes were + located in the ``HttpKernel`` component. + +Installation +------------ + +You can install the component in many different ways: + +* Use the official Git repository (https://github.com/symfony/Debug); +* :doc:`Install it via Composer ` (``symfony/debug`` on `Packagist`_). + +Usage +----- + +The Debug component provides several tools to help you debug PHP code. +Enabling them all is as easy as it can get:: + + use Symfony\Component\Debug\Debug; + + Debug::enable(); + +The :method:`Symfony\\Component\\Debug\\Debug::enable` method registers an +error handler and an exception handler. If the :doc:`ClassLoader component +` is available, a special class loader is also +registered. + +Read the following sections for more information about the different available +tools. + +.. caution:: + + You should never enable the debug tools in a production environment as + they might disclose sensitive information to the user. + +Enabling the Error Handler +-------------------------- + +The :class:`Symfony\\Component\\Debug\\ErrorHandler` class catches PHP errors +and converts them to exceptions (of class :phpclass:`ErrorException` or +:class:`Symfony\\Component\\Debug\\Exception\\FatalErrorException` for PHP +fatal errors):: + + use Symfony\Component\Debug\ErrorHandler; + + ErrorHandler::register(); + +Enabling the Exception Handler +------------------------------ + +The :class:`Symfony\\Component\\Debug\\ExceptionHandler` class catches +uncaught PHP exceptions and converts them to a nice PHP response. It is useful +in debug mode to replace the default PHP/XDebug output with something prettier +and more useful:: + + use Symfony\Component\Debug\ExceptionHandler; + + ExceptionHandler::register(); + +.. note:: + + If the :doc:`HttpFoundation component ` is + available, the handler uses a Symfony Response object; if not, it falls + back to a regular PHP response. + +.. _Packagist: https://packagist.org/packages/symfony/debug diff --git a/components/index.rst b/components/index.rst index 8b4abe24f8d..8f9b989cd11 100644 --- a/components/index.rst +++ b/components/index.rst @@ -9,6 +9,7 @@ The Components config/index console/index css_selector + debug dom_crawler dependency_injection/index event_dispatcher/index diff --git a/components/map.rst.inc b/components/map.rst.inc index f16a8e874b2..e2a0d317eb2 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -23,6 +23,10 @@ * :doc:`/components/css_selector` +* **Debug** + + * :doc:`/components/debug` + * :doc:`/components/dependency_injection/index` * :doc:`/components/dependency_injection/introduction` From 35513bc777f51ea66284b575fd9799d0d2aa9ede Mon Sep 17 00:00:00 2001 From: Drak Date: Thu, 11 Apr 2013 08:52:09 +0100 Subject: [PATCH 042/245] Fix type and clarify behaviour --- components/http_foundation/session_configuration.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index ec542980aed..e094ddd0b4f 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -235,7 +235,8 @@ The settings are as follows: - ``SessionStorageInterface::NO_START_ON_DEMAND_STRICT`` - The session will not be started on demand and any attempt to read or write session data will result in a ``\RuntimeException`` - ``SessionStorageInterface::START_ON_DEMAND`` - The session will be started if it hasn't already been - when any attempt is made to read ro write session data. + when any attempt is made to read or write session data. This setting reflects the default behaviour + since Symfony 2.1 - ``SessionStorageInterface::NO_START_ON_DEMAND_LAX`` - The sessions will not be started on demand when session data is read or written to. It will allow access to the unitialized ``BagInterface``. If this session is subsequently started manually after data is written to a ``BagInterface`` will From 64da9d77efbe808477f2fdc59581d7a2ef8a4f58 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 11 Apr 2013 11:32:22 +0200 Subject: [PATCH 043/245] Improved Intl documentation based on the PR comments --- components/intl.rst | 241 ++++---------------------------------------- 1 file changed, 18 insertions(+), 223 deletions(-) diff --git a/components/intl.rst b/components/intl.rst index 51a25a3ffa2..46fe5c7e8bb 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -34,8 +34,20 @@ not loaded: * :phpfunction:`intl_get_error_code` * :phpfunction:`intl_get_error_message` -If you don't use Composer but the Symfony ClassLoader component, you need to -load them manually by adding the following lines to your autoload code:: +When the intl extension is not available, the following classes are used to +replace the intl classes: + +* :class:`Symfony\\Component\\Intl\\Collator\\Collator` +* :class:`Symfony\\Component\\Intl\\DateFormatter\\IntlDateFormatter` +* :class:`Symfony\\Component\\Intl\\Locale\\Locale` +* :class:`Symfony\\Component\\Intl\\NumberFormatter\\NumberFormatter` +* :class:`Symfony\\Component\\Intl\\Globals\\IntlGlobals` + +Composer automatically exposes these classes in the global namespace. + +If you don't use Composer but the +:doc:`Symfony ClassLoader component`, you need to +expose them manually by adding the following lines to your autoload code:: if (!function_exists('intl_is_failure')) { require '/path/to/Icu/Resources/stubs/functions.php'; @@ -43,229 +55,12 @@ load them manually by adding the following lines to your autoload code:: $loader->registerPrefixFallback('/path/to/Icu/Resources/stubs'); } -The component provides replacements for the following functions and classes: - .. note:: The stub implementation only supports the locale ``en``. -Stubbed Classes ---------------- - -The stubbed classes of the intl extension are limited to the locale "en" and -will throw an exception if you try to use a different locale. For using other -locales, `install the intl extension`_ instead. - -Locale -~~~~~~ - -The only method supported in the :phpclass:`Locale` class is -:phpmethod:`Locale::getDefault`. This method will always return "en". All other -methods will throw an exception when used. - -NumberFormatter -~~~~~~~~~~~~~~~ - -Numbers can be formatted with the :phpclass:`NumberFormatter` class. -The following methods are supported. All other methods are not supported and -will throw an exception when used. - -.. _`NumberFormatter::__construct()`: - -\__construct($locale = $style = null, $pattern = null) -...................................................... - -The only supported locale is "en". The supported styles are -``NumberFormatter::DECIMAL`` and ``NumberFormatter::CURRENCY``. The argument -``$pattern`` may not be used. - -::create($locale = $style = null, $pattern = null) -.................................................. - -See `NumberFormatter::__construct()`_. - -formatCurrency($value, $currency) -................................. - -Fully supported. - -format($value, $type = NumberFormatter::TYPE_DEFAULT) -..................................................... - -Only type ``NumberFormatter::TYPE_DEFAULT`` is supported. - -getAttribute($attr) -................... - -Fully supported. - -getErrorCode() -.............. - -Fully supported. - -getErrorMessage() -................. - -Fully supported. - -getLocale($type = Locale::ACTUAL_LOCALE) -........................................ - -The parameter ``$type`` is ignored. - -parse($value, $type = NumberFormatter::TYPE_DOUBLE, &$position = null) -...................................................................... - -The supported types are ``NumberFormatter::TYPE_DOUBLE``, -``NumberFormatter::TYPE_INT32`` and ``NumberFormatter::TYPE_INT64``. The -parameter ``$position`` must always be ``null``. - -setAttribute($attr, $value) -........................... - -The only supported attributes are ``NumberFormatter::FRACTION_DIGITS``, -``NumberFormatter::GROUPING_USED`` and ``NumberFormatter::ROUNDING_MODE``. - -The only supported rounding modes are ``NumberFormatter::ROUND_HALFEVEN``, -``NumberFormatter::ROUND_HALFDOWN`` and ``NumberFormatter::ROUND_HALFUP``. - -IntlDateFormatter -~~~~~~~~~~~~~~~~~ - -Dates can be formatted with the :phpclass:`IntlDateFormatter` class. The -following methods are supported. All other methods are not supported and will -throw an exception when used. - -.. _`IntlDateFormatter::__construct()`: - -\__construct($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null) -........................................................................................................................ - -The only supported locale is "en". The parameter ``$calendar`` can only be -``IntlDateFormatter::GREGORIAN``. - -::create($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null) -.................................................................................................................... - -See `IntlDateFormatter::__construct()`_. - -format($timestamp) -.................. - -Fully supported. - -getCalendar() -............. - -Fully supported. - -getDateType() -............. - -Fully supported. - -getErrorCode() -.............. - -Fully supported. - -getErrorMessage() -................. - -Fully supported. - -getLocale($type = Locale::ACTUAL_LOCALE) -........................................ - -The parameter ``$type`` is ignored. - -getPattern() -............ - -Fully supported. - -getTimeType() -............. - -Fully supported. - -getTimeZoneId() -............... - -Fully supported. - -isLenient() -........... - -Always returns ``false``. - -parse($value, &$position = null) -................................ - -The parameter ``$position`` must always be ``null``. - -setLenient($lenient) -.................... - -Only accepts ``false``. - -setPattern($pattern) -.................... - -Fully supported. - -setTimeZoneId($timeZoneId) -.......................... - -Fully supported. - -setTimeZone($timeZone) -...................... - -Fully supported. - -Collator -~~~~~~~~ - -Localized strings can be sorted with the :phpclass:`\Collator` class. The -following methods are supported. All other methods are not supported and will -throw an exception when used. - -.. _`Collator::__construct()`: - -\__construct($locale) -..................... - -The only supported locale is "en". - -create($locale) -............... - -See `Collator::__construct()`_. - -asort(&$array, $sortFlag = Collator::SORT_REGULAR) -.................................................. - -Fully supported. - -getErrorCode() -.............. - -Fully supported. - -getErrorMessage() -................. - -Fully supported. - -getLocale($type = Locale::ACTUAL_LOCALE) -........................................ - -The parameter ``$type`` is ignored. - -ResourceBundle -~~~~~~~~~~~~~~ +Writing and Reading Resource Bundles +------------------------------------ The :phpclass:`ResourceBundle` class is not and will not be supported. Instead, this component ships a set of readers and writers for reading and writing arrays @@ -273,7 +68,7 @@ this component ships a set of readers and writers for reading and writing arrays are supported: TextBundleWriter -................ +~~~~~~~~~~~~~~~~ Writes an array or an array-like object to a plain text resource bundle. The resulting .txt file can be converted to a binary .res file with the @@ -397,7 +192,7 @@ locale will be merged. In order to suppress this behavior, the last parameter echo $reader->readEntry('/path/to/bundle', 'en', array('Data', 'entry1'), false); -Included Resource Bundles +Provided Resource Bundles ------------------------- The ICU data is located in several "resource bundles". You can access a PHP From 6c883642848c9aaf2a425774b8ddf13737159248 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 11 Apr 2013 17:32:18 +0200 Subject: [PATCH 044/245] Added documentation for buttons in forms --- book/forms.rst | 128 ++++++++++++++++-- reference/forms/types.rst | 3 + reference/forms/types/button.rst | 34 +++++ reference/forms/types/map.rst.inc | 9 +- reference/forms/types/options/attr.rst.inc | 2 +- .../forms/types/options/button_attr.rst.inc | 15 ++ .../types/options/button_disabled.rst.inc | 9 ++ .../forms/types/options/button_label.rst.inc | 11 ++ .../options/button_translation_domain.rst.inc | 7 + reference/forms/types/reset.rst | 34 +++++ reference/forms/types/submit.rst | 43 ++++++ 11 files changed, 284 insertions(+), 11 deletions(-) create mode 100644 reference/forms/types/button.rst create mode 100644 reference/forms/types/options/button_attr.rst.inc create mode 100644 reference/forms/types/options/button_disabled.rst.inc create mode 100644 reference/forms/types/options/button_label.rst.inc create mode 100644 reference/forms/types/options/button_translation_domain.rst.inc create mode 100644 reference/forms/types/reset.rst create mode 100644 reference/forms/types/submit.rst diff --git a/book/forms.rst b/book/forms.rst index 272eae5e86e..e37423d9d56 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -101,6 +101,7 @@ from inside a controller:: $form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') + ->add('save', 'submit') ->getForm(); return $this->render('AcmeTaskBundle:Default:new.html.twig', array( @@ -125,6 +126,11 @@ In this example, you've added two fields to your form - ``task`` and ``dueDate`` corresponding to the ``task`` and ``dueDate`` properties of the ``Task`` class. You've also assigned each a "type" (e.g. ``text``, ``date``), which, among other things, determines which HTML form tag(s) is rendered for that field. +At last, you added a submit button for submitting the form to the server. + +.. versionadded:: 2.3 + Support for submit buttons was added in Symfony 2.3. Before that, you had + to add buttons to the form's HTML manually. Symfony2 comes with many built-in types that will be discussed shortly (see :ref:`book-forms-type-reference`). @@ -147,8 +153,6 @@ helper functions: {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #}
      {{ form_widget(form) }} - -
      .. code-block:: html+php @@ -156,8 +160,6 @@ helper functions:
      enctype($form) ?> > widget($form) ?> - -
      .. image:: /images/book/form-simple.png @@ -194,7 +196,7 @@ it into a format that's suitable for being rendered in an HTML form. Support for "hasser" methods was added in Symfony 2.1. .. index:: - single: Forms; Handling form submission + single: Forms; Handling form submissions Handling Form Submissions ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -215,6 +217,7 @@ controller:: $form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') + ->add('save', 'submit') ->getForm(); if ($request->isMethod('POST')) { @@ -265,6 +268,42 @@ possible paths: Redirecting a user after a successful form submission prevents the user from being able to hit "refresh" and re-post the data. +.. index:: + single: Forms; Multiple Submit Buttons + +.. _book-form-submitting-multiple-buttons: + +Submitting Forms with Multiple Buttons +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + Support for buttons in forms was added in Symfony 2.3. + +When your form contains more than one submit button, you will want to check +which of the buttons was clicked to adapt the program flow in your controller. +Let's add a second button with the caption "Save and add" to our form:: + + $form = $this->createFormBuilder($task) + ->add('task', 'text') + ->add('dueDate', 'date') + ->add('save', 'submit') + ->add('saveAndAdd', 'submit') + ->getForm(); + +In your controller, use the button's +:method:`Symfony\\Component\\Form\\ClickableInterface\\isClicked` method for +querying if the "Save and add" button was clicked:: + + if ($form->isValid()) { + // perform some action, such as saving the task to the database + + $nextAction = $form->get('saveAndAdd')->isClicked() + ? 'task_new' + : 'task_success'; + + return $this->redirect($this->generateUrl($nextAction)); + } + .. index:: single: Forms; Validation @@ -408,8 +447,41 @@ method:: In both of these cases, *only* the ``registration`` validation group will be used to validate the underlying object. -Groups based on Submitted Data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. index:: + single: Forms; Disabling validation + +Disabling Validation +~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + The ability to set ``validation_groups`` to false is new to version 2.3. + Setting it to an empty array was already supported before. + +Sometimes it is useful to suppress the validation of a form altogether. For +these cases, you can skip the call to :method:`Symfony\\Component\\Form\\FormInterface::isValid` +in your controller. If this is not possible, you can alternatively set the +``validation_groups`` option to ``false`` or an empty array:: + + use Symfony\Component\OptionsResolver\OptionsResolverInterface; + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'validation_groups' => false, + )); + } + +Note that when you do that, the form will still run basic integrity checks, +for example whether an uploaded file was too large or whether non-existing +fields were submitted. If you want to suppress validation completely, remove +the :method:`Symfony\\Component\\Form\\FormInterface::isValid` call from your +controller. + +.. index:: + single: Forms; Validation groups based on submitted data + +Groups based on the Submitted Data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2.1 The ability to specify a callback or Closure in ``validation_groups`` @@ -417,7 +489,7 @@ Groups based on Submitted Data If you need some advanced logic to determine the validation groups (e.g. based on submitted data), you can set the ``validation_groups`` option -to an array callback, or a ``Closure``:: +to an array callback:: use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -431,7 +503,7 @@ to an array callback, or a ``Closure``:: This will call the static method ``determineValidationGroups()`` on the ``Client`` class after the form is bound, but before validation is executed. The Form object is passed as an argument to that method (see next example). -You can also define whole logic inline by using a Closure:: +You can also define whole logic inline by using a ``Closure``:: use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -450,6 +522,44 @@ You can also define whole logic inline by using a Closure:: )); } +.. index:: + single: Forms; Validation groups based on clicked button + +Groups based on the Clicked Button +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + Support for buttons in forms was added in Symfony 2.3. + +When your form contains multiple submit buttons, you can change the validation +group depending on which button is used to submit the form. For example, +consider a form in a wizard that lets you advance to the next step or go back +to the previous step. Let's assume also that when returning to the previous +step, the data of the form should be saved, but not validated. + +First, we need to add the two buttons to the form:: + + $form = $this->createFormBuilder($task) + // ... + ->add('nextStep', 'submit') + ->add('previousStep', 'submit') + ->getForm(); + +Then, we configure the button for returning to the previous step to run +specific validation groups. In this example, we want it to suppress validation, +so we set its ``validation_groups`` options to false:: + + $form = $this->createFormBuilder($task) + // ... + ->add('previousStep', 'submit', array( + 'validation_groups' => false, + )) + ->getForm(); + +Now the form will skip your validation constraints. It will still validate +basic integrity constraints, such as checking whether an uploaded file was too +large or whether you tried to submit text in a number field. + .. index:: single: Forms; Built-in field types diff --git a/reference/forms/types.rst b/reference/forms/types.rst index 685c9007a7b..00d20926ada 100644 --- a/reference/forms/types.rst +++ b/reference/forms/types.rst @@ -9,6 +9,7 @@ Form Types Reference :hidden: types/birthday + types/button types/checkbox types/choice types/collection @@ -31,7 +32,9 @@ Form Types Reference types/percent types/radio types/repeated + types/reset types/search + types/submit types/text types/textarea types/time diff --git a/reference/forms/types/button.rst b/reference/forms/types/button.rst new file mode 100644 index 00000000000..ef755e1d4b5 --- /dev/null +++ b/reference/forms/types/button.rst @@ -0,0 +1,34 @@ +.. index:: + single: Forms; Fields; button + +button Field Type +================= + +.. versionadded:: 2.3 + The ``button`` type is new in version 2.3 + +A simple, non-responsive button. + ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Rendered as | ``button`` tag | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Options | - `attr`_ | +| | - `disabled`_ | +| | - `label`_ | +| | - `translation_domain`_ | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Parent type | none | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\ButtonType` | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ + +Options +------- + +.. include:: /reference/forms/types/options/button_attr.rst.inc + +.. include:: /reference/forms/types/options/button_disabled.rst.inc + +.. include:: /reference/forms/types/options/button_label.rst.inc + +.. include:: /reference/forms/types/options/button_translation_domain.rst.inc diff --git a/reference/forms/types/map.rst.inc b/reference/forms/types/map.rst.inc index fdf51a6a554..38690a7df04 100644 --- a/reference/forms/types/map.rst.inc +++ b/reference/forms/types/map.rst.inc @@ -49,8 +49,15 @@ Hidden Fields * :doc:`hidden` * :doc:`csrf` +Buttons +~~~~~~~ + +* :doc:`button` +* :doc:`reset` +* :doc:`submit` + Base Fields ~~~~~~~~~~~ * :doc:`field` -* :doc:`form` \ No newline at end of file +* :doc:`form` diff --git a/reference/forms/types/options/attr.rst.inc b/reference/forms/types/options/attr.rst.inc index 21b040843d5..7fcf2603e7c 100644 --- a/reference/forms/types/options/attr.rst.inc +++ b/reference/forms/types/options/attr.rst.inc @@ -3,7 +3,7 @@ attr **type**: array **default**: Empty array -If you want to add extra attributes to HTML field representation +If you want to add extra attributes to the HTML field representation, you can use ``attr`` option. It's an associative array with HTML attribute as a key. This can be useful when you need to set a custom class for some widget:: diff --git a/reference/forms/types/options/button_attr.rst.inc b/reference/forms/types/options/button_attr.rst.inc new file mode 100644 index 00000000000..39a2f6c35f4 --- /dev/null +++ b/reference/forms/types/options/button_attr.rst.inc @@ -0,0 +1,15 @@ +attr +~~~~ + +**type**: array **default**: Empty array + +If you want to add extra attributes to the HTML representation of the button, +you can use ``attr`` option. It's an associative array with HTML attribute +as a key. This can be useful when you need to set a custom class for the button:: + + $builder->add('save', 'button', array( + 'attr' => array('class' => 'save'), + )); + + + diff --git a/reference/forms/types/options/button_disabled.rst.inc b/reference/forms/types/options/button_disabled.rst.inc new file mode 100644 index 00000000000..3fee652ff68 --- /dev/null +++ b/reference/forms/types/options/button_disabled.rst.inc @@ -0,0 +1,9 @@ +disabled +~~~~~~~~ + +**type**: ``boolean`` **default**: ``false`` + +If you don't want a user to be able to click a button, you can set the disabled +option to true. It will not be possible to submit the form with this button, +not even when bypassing the browser and sending an request manually, for +example with cURL. diff --git a/reference/forms/types/options/button_label.rst.inc b/reference/forms/types/options/button_label.rst.inc new file mode 100644 index 00000000000..9926016a0f3 --- /dev/null +++ b/reference/forms/types/options/button_label.rst.inc @@ -0,0 +1,11 @@ +label +~~~~~ + +**type**: ``string`` **default**: The label is "guessed" from the field name + +Sets the label that will be displayed on the button. The label can also be +directly set inside the template: + +.. code-block:: jinja + + {{ form_widget(form.save, { 'label': 'Click me' }) }} diff --git a/reference/forms/types/options/button_translation_domain.rst.inc b/reference/forms/types/options/button_translation_domain.rst.inc new file mode 100644 index 00000000000..56c3453f5c1 --- /dev/null +++ b/reference/forms/types/options/button_translation_domain.rst.inc @@ -0,0 +1,7 @@ +translation_domain +~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``messages`` + +This is the translation domain that will be used for any labels or options +that are rendered for this button. diff --git a/reference/forms/types/reset.rst b/reference/forms/types/reset.rst new file mode 100644 index 00000000000..418122fad24 --- /dev/null +++ b/reference/forms/types/reset.rst @@ -0,0 +1,34 @@ +.. index:: + single: Forms; Fields; reset + +reset Field Type +================ + +.. versionadded:: 2.3 + The ``submit`` type is new in version 2.3 + +A button that resets all fields to their original values. + ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Rendered as | ``input`` ``reset`` tag | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Inherited | - `attr`_ | +| options | - `disabled`_ | +| | - `label`_ | +| | - `translation_domain`_ | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Parent type | :doc:`button` | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\ResetType` | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ + +Inherited options +----------------- + +.. include:: /reference/forms/types/options/button_attr.rst.inc + +.. include:: /reference/forms/types/options/button_disabled.rst.inc + +.. include:: /reference/forms/types/options/button_label.rst.inc + +.. include:: /reference/forms/types/options/button_translation_domain.rst.inc diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst new file mode 100644 index 00000000000..5b2dc8e370a --- /dev/null +++ b/reference/forms/types/submit.rst @@ -0,0 +1,43 @@ +.. index:: + single: Forms; Fields; submit + +submit Field Type +================= + +.. versionadded:: 2.3 + The ``submit`` type is new in version 2.3 + +A submit button. + ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Rendered as | ``input`` ``submit`` tag | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Inherited | - `attr`_ | +| options | - `disabled`_ | +| | - `label`_ | +| | - `translation_domain`_ | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Parent type | :doc:`button` | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\SubmitType` | ++----------------------+------------------------------------------------------------------------------------------------------------------------+ + +Submit buttons feature an additional method +:method:`Symfony\\Component\\Form\\ClickableInterface\\isClicked` that lets you +check whether this button was used to submit the forms. This is especially +useful when :ref:`a form has multiple submit buttons `:: + + if ($form->get('save')->isClicked()) { + // ... + } + +Inherited options +----------------- + +.. include:: /reference/forms/types/options/button_attr.rst.inc + +.. include:: /reference/forms/types/options/button_disabled.rst.inc + +.. include:: /reference/forms/types/options/button_label.rst.inc + +.. include:: /reference/forms/types/options/button_translation_domain.rst.inc From 1dcfe8df2c3a0edd28bc39968ce531e7e378c728 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 11 Apr 2013 16:31:27 -0500 Subject: [PATCH 045/245] [#2479] Minor tweak for new debug article --- components/debug.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/debug.rst b/components/debug.rst index 94f21514550..a718ba6ee74 100644 --- a/components/debug.rst +++ b/components/debug.rst @@ -68,7 +68,7 @@ and more useful:: .. note:: - If the :doc:`HttpFoundation component ` is + If the :doc:`HttpFoundation component ` is available, the handler uses a Symfony Response object; if not, it falls back to a regular PHP response. From 8899b9285c5373b1f89b65a15ee95203a4362646 Mon Sep 17 00:00:00 2001 From: Fred Jiles Date: Fri, 12 Apr 2013 12:20:11 -0400 Subject: [PATCH 046/245] Previous session options to security configuration Add documentation for option to set require_previous_session in form login. --- reference/configuration/security.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 9d208d605e2..1b6039dfe7d 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -147,6 +147,10 @@ Each part will be explained in the next section. # by default, the login form *must* be a POST, not a GET post_only: true remember_me: false + + #by default, a session must exist before submitting an authentication request + require_previous_session: true + remember_me: token_provider: name key: someS3cretKey From beaf27c093a15dad502169d2d5cb56f7ead087b2 Mon Sep 17 00:00:00 2001 From: Fred Jiles Date: Fri, 12 Apr 2013 17:11:18 -0400 Subject: [PATCH 047/245] Add space after # --- reference/configuration/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 1b6039dfe7d..dcff7d276e1 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -148,7 +148,7 @@ Each part will be explained in the next section. post_only: true remember_me: false - #by default, a session must exist before submitting an authentication request + # by default, a session must exist before submitting an authentication request require_previous_session: true remember_me: From 88b88e5bb47b0f39a634c6efc409902db3359a57 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Sat, 13 Apr 2013 15:33:43 +0200 Subject: [PATCH 048/245] Fixed spelling and grammar errors --- book/forms.rst | 9 +++--- reference/forms/types/button.rst | 26 ++++++++--------- .../types/options/button_disabled.rst.inc | 2 +- .../forms/types/options/button_label.rst.inc | 12 ++++++-- reference/forms/types/reset.rst | 26 ++++++++--------- reference/forms/types/submit.rst | 28 +++++++++---------- 6 files changed, 55 insertions(+), 48 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index e37423d9d56..568c4d52ad5 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -291,11 +291,11 @@ Let's add a second button with the caption "Save and add" to our form:: ->getForm(); In your controller, use the button's -:method:`Symfony\\Component\\Form\\ClickableInterface\\isClicked` method for +:method:`Symfony\\Component\\Form\\ClickableInterface::isClicked` method for querying if the "Save and add" button was clicked:: if ($form->isValid()) { - // perform some action, such as saving the task to the database + // ... perform some action, such as saving the task to the database $nextAction = $form->get('saveAndAdd')->isClicked() ? 'task_new' @@ -454,8 +454,9 @@ Disabling Validation ~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2.3 - The ability to set ``validation_groups`` to false is new to version 2.3. - Setting it to an empty array was already supported before. + The ability to set ``validation_groups`` to false was added in Symfony 2.3, + although setting it to an empty array achieved the same result in previous + versions. Sometimes it is useful to suppress the validation of a form altogether. For these cases, you can skip the call to :method:`Symfony\\Component\\Form\\FormInterface::isValid` diff --git a/reference/forms/types/button.rst b/reference/forms/types/button.rst index ef755e1d4b5..6e8cce6ed57 100644 --- a/reference/forms/types/button.rst +++ b/reference/forms/types/button.rst @@ -5,22 +5,22 @@ button Field Type ================= .. versionadded:: 2.3 - The ``button`` type is new in version 2.3 + The ``button`` type was added in Symfony 2.3 A simple, non-responsive button. -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Rendered as | ``button`` tag | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Options | - `attr`_ | -| | - `disabled`_ | -| | - `label`_ | -| | - `translation_domain`_ | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Parent type | none | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\ButtonType` | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ ++----------------------+----------------------------------------------------------------------+ +| Rendered as | ``button`` tag | ++----------------------+----------------------------------------------------------------------+ +| Options | - `attr`_ | +| | - `disabled`_ | +| | - `label`_ | +| | - `translation_domain`_ | ++----------------------+----------------------------------------------------------------------+ +| Parent type | none | ++----------------------+----------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\ButtonType` | ++----------------------+----------------------------------------------------------------------+ Options ------- diff --git a/reference/forms/types/options/button_disabled.rst.inc b/reference/forms/types/options/button_disabled.rst.inc index 3fee652ff68..b08ebf3f35f 100644 --- a/reference/forms/types/options/button_disabled.rst.inc +++ b/reference/forms/types/options/button_disabled.rst.inc @@ -5,5 +5,5 @@ disabled If you don't want a user to be able to click a button, you can set the disabled option to true. It will not be possible to submit the form with this button, -not even when bypassing the browser and sending an request manually, for +not even when bypassing the browser and sending a request manually, for example with cURL. diff --git a/reference/forms/types/options/button_label.rst.inc b/reference/forms/types/options/button_label.rst.inc index 9926016a0f3..9cafe2fc8e4 100644 --- a/reference/forms/types/options/button_label.rst.inc +++ b/reference/forms/types/options/button_label.rst.inc @@ -5,7 +5,13 @@ label Sets the label that will be displayed on the button. The label can also be directly set inside the template: - -.. code-block:: jinja - {{ form_widget(form.save, { 'label': 'Click me' }) }} +.. configuration-block:: + + .. code-block:: html+jinja + + {{ form_widget(form.save, { 'label': 'Click me' }) }} + + .. code-block:: html+php + + widget($form['save'], array('label' => 'Click me')) ?> diff --git a/reference/forms/types/reset.rst b/reference/forms/types/reset.rst index 418122fad24..004657aaf55 100644 --- a/reference/forms/types/reset.rst +++ b/reference/forms/types/reset.rst @@ -5,22 +5,22 @@ reset Field Type ================ .. versionadded:: 2.3 - The ``submit`` type is new in version 2.3 + The ``reset`` type was added in Symfony 2.3 A button that resets all fields to their original values. -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Rendered as | ``input`` ``reset`` tag | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Inherited | - `attr`_ | -| options | - `disabled`_ | -| | - `label`_ | -| | - `translation_domain`_ | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Parent type | :doc:`button` | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\ResetType` | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ ++----------------------+---------------------------------------------------------------------+ +| Rendered as | ``input`` ``reset`` tag | ++----------------------+---------------------------------------------------------------------+ +| Inherited | - `attr`_ | +| options | - `disabled`_ | +| | - `label`_ | +| | - `translation_domain`_ | ++----------------------+---------------------------------------------------------------------+ +| Parent type | :doc:`button` | ++----------------------+---------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\ResetType` | ++----------------------+---------------------------------------------------------------------+ Inherited options ----------------- diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst index 5b2dc8e370a..ce75fa3b116 100644 --- a/reference/forms/types/submit.rst +++ b/reference/forms/types/submit.rst @@ -5,25 +5,25 @@ submit Field Type ================= .. versionadded:: 2.3 - The ``submit`` type is new in version 2.3 + The ``submit`` type was added in Symfony 2.3 A submit button. -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Rendered as | ``input`` ``submit`` tag | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Inherited | - `attr`_ | -| options | - `disabled`_ | -| | - `label`_ | -| | - `translation_domain`_ | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Parent type | :doc:`button` | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\SubmitType` | -+----------------------+------------------------------------------------------------------------------------------------------------------------+ ++----------------------+----------------------------------------------------------------------+ +| Rendered as | ``input`` ``submit`` tag | ++----------------------+----------------------------------------------------------------------+ +| Inherited | - `attr`_ | +| options | - `disabled`_ | +| | - `label`_ | +| | - `translation_domain`_ | ++----------------------+----------------------------------------------------------------------+ +| Parent type | :doc:`button` | ++----------------------+----------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Form\\Extension\\Core\\Type\\SubmitType` | ++----------------------+----------------------------------------------------------------------+ Submit buttons feature an additional method -:method:`Symfony\\Component\\Form\\ClickableInterface\\isClicked` that lets you +:method:`Symfony\\Component\\Form\\ClickableInterface::isClicked` that lets you check whether this button was used to submit the forms. This is especially useful when :ref:`a form has multiple submit buttons `:: From 318ba888af1db81a2eb950c7270f3443afe76bfe Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Wed, 2 Jan 2013 15:51:02 +0100 Subject: [PATCH 049/245] Added information about the new form(), form_start() and form_end() helpers --- book/forms.rst | 126 +++++++++++++++++------- cookbook/doctrine/file_uploads.rst | 26 ----- cookbook/doctrine/registration_form.rst | 14 +-- cookbook/form/form_collections.rst | 16 +-- cookbook/routing/method_parameters.rst | 29 +----- reference/forms/twig_reference.rst | 74 ++++++++++++++ reference/forms/types/collection.rst | 4 +- reference/forms/types/file.rst | 7 +- reference/twig_reference.rst | 10 ++ 9 files changed, 194 insertions(+), 112 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index 272eae5e86e..cc288096151 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -145,35 +145,27 @@ helper functions: .. code-block:: html+jinja {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} -
      - {{ form_widget(form) }} - - -
      + {{ form(form) }} .. code-block:: html+php -
      enctype($form) ?> > - widget($form) ?> - - -
      + form($form) ?> .. image:: /images/book/form-simple.png :align: center .. note:: - This example assumes that you've created a route called ``task_new`` - that points to the ``AcmeTaskBundle:Default:new`` controller that - was created earlier. + This example assumes that you submit the form in a "POST" request and to + the same URL that it was displayed in. You will learn later how to + change the request method and the target URL of the form. -That's it! By printing ``form_widget(form)``, each field in the form is -rendered, along with a label and error message (if there is one). As easy -as this is, it's not very flexible (yet). Usually, you'll want to render each -form field individually so you can control how the form looks. You'll learn how -to do that in the ":ref:`form-rendering-template`" section. +That's it! By printing ``form(form)``, each field in the form is rendered, along +with a label and error message (if there is one). As easy as this is, it's not +very flexible (yet). Usually, you'll want to render each form field individually +so you can control how the form looks. You'll learn how to do that in the +":ref:`form-rendering-template`" section. Before moving on, notice how the rendered ``task`` input field has the value of the ``task`` property from the ``$task`` object (i.e. "Write a blog post"). @@ -605,35 +597,30 @@ of code. Of course, you'll usually need much more flexibility when rendering: .. code-block:: html+jinja {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} -
      + {{ form_start(form) }} {{ form_errors(form) }} {{ form_row(form.task) }} {{ form_row(form.dueDate) }} - {{ form_rest(form) }} - -
      + {{ form_end(form) }} .. code-block:: html+php -
      enctype($form) ?>> + start($form) ?> errors($form) ?> row($form['task']) ?> row($form['dueDate']) ?> - rest($form) ?> - -
      + end($form) ?> Take a look at each part: -* ``form_enctype(form)`` - If at least one field is a file upload field, this - renders the obligatory ``enctype="multipart/form-data"``; +* ``form_start(form)`` - Renders the start tag of the form. * ``form_errors(form)`` - Renders any errors global to the whole form (field-specific errors are displayed next to each field); @@ -642,10 +629,8 @@ Take a look at each part: form widget for the given field (e.g. ``dueDate``) inside, by default, a ``div`` element; -* ``form_rest(form)`` - Renders any fields that have not yet been rendered. - It's usually a good idea to place a call to this helper at the bottom of - each form (in case you forgot to output a field or don't want to bother - manually rendering hidden fields). This helper is also useful for taking +* ``form_end()`` - Renders the end tag of the form and any fields that have not + yet been rendered. This is useful for rendering hidden fields and taking advantage of the automatic :ref:`CSRF Protection`. The majority of the work is done by the ``form_row`` helper, which renders @@ -740,7 +725,7 @@ field: .. code-block:: html+jinja - {{ form_widget(form.task, { 'attr': {'class': 'task_field'} }) }} + {{ form_widget(form.task, {'attr': {'class': 'task_field'}}) }} .. code-block:: html+php @@ -783,6 +768,75 @@ available in the :doc:`reference manual`. Read this to know everything about the helpers available and the options that can be used with each. +.. index:: + single: Forms; Changing the action and method + +.. _book-forms-changing-action-and-method: + +Changing the Action and Method of a Form +---------------------------------------- + +So far, we have used the ``form_start()`` helper to render the form's start tag +and assumed that each form is submitted to the same URL in a POST request. +Sometimes you want to change these parameters. You can do so in a few different +ways. If you build your form in the controller, you can use ``setAction()`` and +``setMethod()``:: + + $form = $this->createFormBuilder($task) + ->setAction($this->generateUrl('target_route')) + ->setMethod('GET') + ->add('task', 'text') + ->add('dueDate', 'date') + ->getForm(); + +.. note:: + + This example assumes that you've created a route called ``target_route`` + that points to the controller that processes the form. + +In :ref:`book-form-creating-form-classes` you will learn how to outsource the +form building code into separate classes. When using such a form class in the +controller, you can pass the action and method as form options:: + + $form = $this->createForm(new TaskType(), $task, array( + 'action' => $this->generateUrl('target_route'), + 'method' => 'GET', + )); + +At last, you can override the action and method in the template by passing them +to the ``form()`` or the ``form_start()`` helper: + +.. configuration-block:: + + .. code-block:: html+jinja + + {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} + {{ form(form, {'action': path('target_route'), 'method': 'GET'}) }} + + {{ form_start(form, {'action': path('target_route'), 'method': 'GET'}) }} + + .. code-block:: html+php + + + form($form, array( + 'action' => $view['router']->generate('target_route'), + 'method' => 'GET', + )) ?> + + start($form, array( + 'action' => $view['router']->generate('target_route'), + 'method' => 'GET', + )) ?> + +.. note:: + + If the form's method is not GET or POST, but PUT, PATCH or DELETE, Symfony2 + will insert a hidden field with the name "_method" that stores this method. + The form will be submitted in a normal POST request, but Symfony2's router + is capable of detecting the "_method" parameter and will interpret the + request as PUT, PATCH or DELETE request. Read the cookbook chapter + ":doc:`/cookbook/routing/method_parameters`" for more information. + .. index:: single: Forms; Creating form classes @@ -1143,7 +1197,7 @@ renders the form: {% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' 'AcmeTaskBundle:Form:fields2.html.twig' %} -
      + {{ form(form) }} .. code-block:: html+php @@ -1152,7 +1206,7 @@ renders the form: setTheme($form, array('AcmeTaskBundle:Form', 'AcmeTaskBundle:Form')) ?> - + form($form) ?> The ``form_theme`` tag (in Twig) "imports" the fragments defined in the given template and uses them when rendering the form. In other words, when the @@ -1231,7 +1285,7 @@ are 4 possible *parts* of a form that can be rendered: .. note:: - There are actually 3 other *parts* - ``rows``, ``rest``, and ``enctype`` - + There are actually 2 other *parts* - ``rows`` and ``rest`` - but you should rarely if ever need to worry about overriding them. By knowing the field type (e.g. ``textarea``) and which part you want to diff --git a/cookbook/doctrine/file_uploads.rst b/cookbook/doctrine/file_uploads.rst index 34778c3c0aa..f3ca3a05624 100644 --- a/cookbook/doctrine/file_uploads.rst +++ b/cookbook/doctrine/file_uploads.rst @@ -251,32 +251,6 @@ The following controller shows you how to handle the entire process:: return array('form' => $form->createView()); } -.. note:: - - When writing the template, don't forget to set the ``enctype`` attribute: - - .. configuration-block:: - - .. code-block:: html+jinja - -

      Upload File

      - - - {{ form_widget(form) }} - - -
      - - .. code-block:: html+php - -

      Upload File

      - -
      enctype($form) ?>> - widget($form) ?> - - -
      - The previous controller will automatically persist the ``Document`` entity with the submitted name, but it will do nothing about the file and the ``path`` property will be blank. diff --git a/cookbook/doctrine/registration_form.rst b/cookbook/doctrine/registration_form.rst index 9e735ee877a..a0665cba206 100644 --- a/cookbook/doctrine/registration_form.rst +++ b/cookbook/doctrine/registration_form.rst @@ -232,10 +232,10 @@ controller for displaying the registration form:: { public function registerAction() { - $form = $this->createForm( - new RegistrationType(), - new Registration() - ); + $registration = new Registration(); + $form = $this->createForm(new RegistrationType(), $registration, array( + 'action' => $this->generateUrl('create'), + )); return $this->render( 'AcmeAccountBundle:Account:register.html.twig', @@ -249,11 +249,7 @@ and its template: .. code-block:: html+jinja {# src/Acme/AccountBundle/Resources/views/Account/register.html.twig #} -
      - {{ form_widget(form) }} - - -
      + {{ form(form) }} Finally, create the controller which handles the form submission. This performs the validation and saves the data into the database:: diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index be64bb969b7..92dfbabf04a 100755 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -205,7 +205,7 @@ zero tags when first created). {# ... #} -
      + {{ form_start(form) }} {# render the task's only field: description #} {{ form_row(form.description) }} @@ -216,10 +216,9 @@ zero tags when first created).
    • {{ form_row(tag.name) }}
    • {% endfor %}
    + {{ form_end(form) }} - {{ form_rest(form) }} - {# ... #} - + {# ... #} .. code-block:: html+php @@ -227,16 +226,17 @@ zero tags when first created). -
    + start($form) ?> + + row($form['description']) ?> +

    Tags

    • row($tag['name']) ?>
    - - rest($form) ?> -
    + end($form) ?> diff --git a/cookbook/routing/method_parameters.rst b/cookbook/routing/method_parameters.rst index f0360e7e87f..ecf152ecc47 100644 --- a/cookbook/routing/method_parameters.rst +++ b/cookbook/routing/method_parameters.rst @@ -86,28 +86,7 @@ Unfortunately, life isn't quite this simple, since most browsers do not support sending PUT and DELETE requests. Fortunately Symfony2 provides you with a simple way of working around this limitation. By including a ``_method`` parameter in the query string or parameters of an HTTP request, Symfony2 will -use this as the method when matching routes. This can be done easily in forms -with a hidden field. Suppose you have a form for editing a blog post: - -.. code-block:: html+jinja - -
    - - {{ form_widget(form) }} - -
    - -The submitted request will now match the ``blog_update`` route and the ``updateAction`` -will be used to process the form. - -Likewise the delete form could be changed to look like this: - -.. code-block:: html+jinja - -
    - - {{ form_widget(delete_form) }} - -
    - -It will then match the ``blog_delete`` route. +use this as the method when matching routes. Forms automatically include a +hidden field for this parameter if their submission method is not GET or POST. +See :ref:`the related chapter in the forms documentation` +for more information. diff --git a/reference/forms/twig_reference.rst b/reference/forms/twig_reference.rst index 7022dbf19be..6ad0aa55347 100644 --- a/reference/forms/twig_reference.rst +++ b/reference/forms/twig_reference.rst @@ -24,6 +24,75 @@ rendering forms. There are several different functions available, and each is responsible for rendering a different part of a form (e.g. labels, errors, widgets, etc). +.. _reference-forms-twig-form: + +form(view, variables) +--------------------- + +Renders the HTML of a complete form. + +.. code-block:: jinja + + {# render the form and change the submission method #} + {{ form(form, {'method': 'GET'}) }} + +You will mostly use this helper for prototyping and if you use custom form +themes. If you need more flexibility in rendering the form, you should use +the other helpers to render individual parts of the form instead: + +.. code-block:: jinja + + {{ form_start(form) }} + {{ form_errors(form) }} + + {{ form_row(form.name) }} + {{ form_row(form.dueDate) }} + + + {{ form_end(form) }} + +See ":ref:`twig-reference-form-variables`" to learn more about the ``variables`` +argument. + +.. _reference-forms-twig-start: + +form_start(view, variables) +--------------------------- + +Renders the start tag of a form. This helper takes care of printing the +configured method and target action of the form. It will also include the +correct ``enctype`` property if the form contains upload fields. + +.. code-block:: jinja + + {# render the start tag and change the submission method #} + {{ form_start(form, {'method': 'GET'}) }} + +See ":ref:`twig-reference-form-variables`" to learn more about the ``variables`` +argument. + +.. _reference-forms-twig-end: + +form_end(view, variables) +------------------------- + +Renders the end tag of a form. + +.. code-block:: jinja + + {{ form_end(form) }} + +This helper also outputs ``form_rest()`` unless you set ``render_rest`` to +false: + +.. code-block:: jinja + + {# don't render unrendered fields #} + {{ form_end(form, {'render_rest': false}) }} + +See ":ref:`twig-reference-form-variables`" to learn more about the ``variables`` +argument. + .. _reference-forms-twig-label: form_label(view, label, variables) @@ -119,6 +188,11 @@ obvious (since it'll render the field for you). form_enctype(view) ------------------ +.. note:: + + This helper was deprecated in Symfony 2.3 and will be removed in Symfony 3.0. + You should use ``form_start()`` instead. + If the form contains at least one file upload field, this will render the required ``enctype="multipart/form-data"`` form attribute. It's always a good idea to include this in your form tag: diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index ce4e438001f..b394e8bbb5a 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -151,7 +151,7 @@ you need is the JavaScript: .. code-block:: html+jinja -
    + {{ form_start(form) }} {# ... #} {# store the prototype on the data-prototype attribute #} @@ -167,7 +167,7 @@ you need is the JavaScript: Add another email {# ... #} -
    + {{ form_end(form) }}