From d03f39dc184ccb152cff120f1526606b8ba1a62d Mon Sep 17 00:00:00 2001 From: daniele Date: Fri, 6 Sep 2013 11:04:13 +0200 Subject: [PATCH 001/835] Added configuration to enable profiler in test --- book/testing.rst | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/book/testing.rst b/book/testing.rst index 5ec4ebaf683..65104368a11 100644 --- a/book/testing.rst +++ b/book/testing.rst @@ -473,6 +473,52 @@ To get the Profiler for the last request, do the following:: For specific details on using the profiler inside a test, see the :doc:`/cookbook/testing/profiling` cookbook entry. +To avoid collecting data in each test you can set the ``collect`` parameter +in the configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config_test.yml + + # ... + framework: + profiler: + enabled: true + collect: false + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // app/config/config.php + + // ... + $container->loadFromExtension('framework', array( + 'profiler' => array( + 'enabled' => true, + 'collect' => false, + ), + )); + +In this way only tests that call ``enableProfiler()`` will collect data. + Redirecting ~~~~~~~~~~~ From 10fe8a4d16be777c12ad84eaaa7429011d60dbdc Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sat, 2 Nov 2013 11:35:59 +0100 Subject: [PATCH 002/835] Removed 2.2 references as it reached eom --- book/forms.rst | 6 +++--- book/routing.rst | 14 -------------- book/security.rst | 7 ------- book/templating.rst | 12 ------------ components/config/definition.rst | 8 +------- components/console/helpers/dialoghelper.rst | 13 ------------- components/console/helpers/progresshelper.rst | 3 --- components/dependency_injection/compilation.rst | 3 --- components/finder.rst | 6 ------ components/http_foundation/introduction.rst | 7 ------- components/process.rst | 3 --- components/property_access/introduction.rst | 4 ---- components/routing/hostname_pattern.rst | 3 --- components/routing/introduction.rst | 7 ------- components/stopwatch.rst | 4 ---- cookbook/console/sending_emails.rst | 9 +++------ cookbook/form/dynamic_form_modification.rst | 8 -------- cookbook/templating/namespaced_paths.rst | 5 +---- cookbook/templating/render_without_controller.rst | 6 +----- reference/configuration/framework.rst | 5 ----- reference/configuration/security.rst | 6 ------ reference/constraints/CardScheme.rst | 3 --- reference/constraints/Luhn.rst | 3 --- reference/constraints/UserPassword.rst | 8 -------- reference/dic_tags.rst | 2 +- reference/twig_reference.rst | 4 ---- 26 files changed, 10 insertions(+), 149 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index f5621c51a5b..a835cd2cb16 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -1873,7 +1873,7 @@ Learn more from the Cookbook .. _`Symfony2 Form Component`: https://github.com/symfony/Form .. _`DateTime`: http://php.net/manual/en/class.datetime.php -.. _`Twig Bridge`: https://github.com/symfony/symfony/tree/2.2/src/Symfony/Bridge/Twig -.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/2.2/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +.. _`Twig Bridge`: https://github.com/symfony/symfony/tree/master/src/Symfony/Bridge/Twig +.. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig .. _`Cross-site request forgery`: http://en.wikipedia.org/wiki/Cross-site_request_forgery -.. _`view on GitHub`: https://github.com/symfony/symfony/tree/2.2/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form +.. _`view on GitHub`: https://github.com/symfony/symfony/tree/master/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form diff --git a/book/routing.rst b/book/routing.rst index 68a9e4c7ce6..fc594aaeaff 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -68,10 +68,6 @@ The route is simple: return $collection; -.. versionadded:: 2.2 - The ``path`` option is new in Symfony2.2, ``pattern`` is used in older - versions. - The path defined by the ``blog_show`` route acts like ``/blog/*`` where the wildcard is given the name ``slug``. For the URL ``/blog/my-blog-post``, the ``slug`` variable gets a value of ``my-blog-post``, which is available @@ -694,10 +690,6 @@ be accomplished with the following route configuration: return $collection; -.. versionadded:: 2.2 - The ``methods`` option is added in Symfony2.2. Use the ``_method`` - requirement in older versions. - Despite the fact that these two routes have identical paths (``/contact``), the first route will match only GET requests and the second route will match only POST requests. This means that you can display the form and submit the @@ -710,9 +702,6 @@ form via the same URL, while using distinct controllers for the two actions. Adding a Host ~~~~~~~~~~~~~ -.. versionadded:: 2.2 - Host matching support was added in Symfony 2.2 - You can also match on the HTTP *host* of the incoming request. For more information, see :doc:`/components/routing/hostname_pattern` in the Routing component documentation. @@ -1070,9 +1059,6 @@ from the new routing resource. Adding a Host regex to Imported Routes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - Host matching support was added in Symfony 2.2 - You can set the host regex on imported routes. For more information, see :ref:`component-routing-host-imported`. diff --git a/book/security.rst b/book/security.rst index e21e32cc438..6a82b4ecfd1 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1385,10 +1385,6 @@ it as base64. In other words, the password has been greatly obfuscated so that the hashed password can't be decoded (i.e. you can't determine the password from the hashed password). -.. versionadded:: 2.2 - As of Symfony 2.2 you can also use the :ref:`PBKDF2 ` - and :ref:`BCrypt ` password encoders. - Determining the Hashed Password ............................... @@ -2023,9 +2019,6 @@ cookie will be ever created by Symfony2): Utilities --------- -.. versionadded:: 2.2 - The ``StringUtils`` and ``SecureRandom`` classes were added in Symfony 2.2 - The Symfony Security Component comes with a collection of nice utilities related to security. These utilities are used by Symfony, but you should also use them if you want to solve the problem they address. diff --git a/book/templating.rst b/book/templating.rst index 2e01d869196..d6e75fa0f75 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -375,11 +375,6 @@ When working with template inheritance, here are some tips to keep in mind: Template Naming and Locations ----------------------------- -.. versionadded:: 2.2 - Namespaced path support was added in 2.2, allowing for template names - like ``@AcmeDemo/layout.html.twig``. See :doc:`/cookbook/templating/namespaced_paths` - for more details. - By default, templates can live in two different locations: * ``app/Resources/views/``: The applications ``views`` directory can contain @@ -572,10 +567,6 @@ you set `with_context`_ to false). maps (i.e. an array with named keys). If you needed to pass in multiple elements, it would look like this: ``{'foo': foo, 'bar': bar}``. -.. versionadded:: 2.2 - The `include() function`_ is a new Twig feature that's available in Symfony - 2.2. Prior, the `{% include %} tag`_ tag was used. - .. index:: single: Templating; Embedding action @@ -782,9 +773,6 @@ in your application configuration: ), )); -.. versionadded:: 2.2 - Default templates per render function was added in Symfony 2.2 - You can define default templates per ``render`` function (which will override any global default template that is defined): diff --git a/components/config/definition.rst b/components/config/definition.rst index 0bd25d19a03..9a5f22d7125 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -99,7 +99,7 @@ node definition. Node type are available for: * scalar * boolean -* integer (new in 2.2) +* integer * float * enum * array @@ -111,9 +111,6 @@ and are created with ``node($name, $type)`` or their associated shortcut Numeric node constraints ~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - The numeric (float and integer) nodes are new in 2.2 - Numeric nodes (float and integer) provide two extra constraints - :method:`Symfony\\Component\\Config\\Definition\\Builder::min` and :method:`Symfony\\Component\\Config\\Definition\\Builder::max` - @@ -287,9 +284,6 @@ has a certain value: Optional Sections ----------------- -.. versionadded:: 2.2 - The ``canBeEnabled`` and ``canBeDisabled`` methods are new in Symfony 2.2 - If you have entire sections which are optional and can be enabled/disabled, you can take advantage of the shortcut :method:`Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition::canBeEnabled` and diff --git a/components/console/helpers/dialoghelper.rst b/components/console/helpers/dialoghelper.rst index 1d1215236f1..584a080164d 100644 --- a/components/console/helpers/dialoghelper.rst +++ b/components/console/helpers/dialoghelper.rst @@ -56,9 +56,6 @@ the default value (``AcmeDemoBundle`` here) is returned. Autocompletion ~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - Autocompletion for questions was added in Symfony 2.2. - You can also specify an array of potential answers for a given question. These will be autocompleted as the user types:: @@ -74,9 +71,6 @@ will be autocompleted as the user types:: Hiding the User's Response ~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - The ``askHiddenResponse`` method was added in Symfony 2.2. - You can also ask a question and hide the response. This is particularly convenient for passwords:: @@ -144,9 +138,6 @@ be able to proceed if her input is valid. Hiding the User's Response ~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - The ``askHiddenResponseAndValidate`` method was added in Symfony 2.2. - You can also ask and validate a hidden response:: $dialog = $this->getHelperSet()->get('dialog'); @@ -171,10 +162,6 @@ some reason, pass true as the fifth argument. Let the user choose from a list of Answers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.2 - The :method:`Symfony\\Component\\Console\\Helper\\DialogHelper::select` method - was added in Symfony 2.2. - If you have a predefined set of answers the user can choose from, you could use the ``ask`` method described above or, to make sure the user provided a correct answer, the ``askAndValidate`` method. Both have diff --git a/components/console/helpers/progresshelper.rst b/components/console/helpers/progresshelper.rst index ccd5d381afd..9ee76b694cb 100644 --- a/components/console/helpers/progresshelper.rst +++ b/components/console/helpers/progresshelper.rst @@ -4,9 +4,6 @@ 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. diff --git a/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst index af1fd1444f5..07c8a737c6a 100644 --- a/components/dependency_injection/compilation.rst +++ b/components/dependency_injection/compilation.rst @@ -276,9 +276,6 @@ but also load a secondary one only if a certain parameter is set:: Prepending Configuration passed to the Extension ------------------------------------------------ -.. versionadded:: 2.2 - The ability to prepend the configuration of a bundle is new in Symfony 2.2. - An Extension can prepend the configuration of any Bundle before the ``load()`` method is called by implementing :class:`Symfony\\Component\\DependencyInjection\\Extension\\PrependExtensionInterface`:: diff --git a/components/finder.rst b/components/finder.rst index c63d624e6c6..2f985ba0af9 100644 --- a/components/finder.rst +++ b/components/finder.rst @@ -82,9 +82,6 @@ Search in several locations by chaining calls to $finder->files()->in(__DIR__)->in('/elsewhere'); -.. versionadded:: 2.2 - Wildcard support was added in version 2.2. - Use wildcard characters to search in the directories matching a pattern:: $finder->in('src/Symfony/*/*/Resources'); @@ -206,9 +203,6 @@ The ``notContains()`` method excludes files containing given pattern:: Path ~~~~ -.. versionadded:: 2.2 - The ``path()`` and ``notPath()`` methods were added in version 2.2. - Restrict files and directories by path with the :method:`Symfony\\Component\\Finder\\Finder::path` method:: diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index f7cb342037e..f7880d15e5f 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -248,9 +248,6 @@ by using the following methods: .. versionadded:: 2.4 The ``getEncodings()`` method was added in Symfony 2.4. -.. versionadded:: 2.2 - The :class:`Symfony\\Component\\HttpFoundation\\AcceptHeader` class is new in Symfony 2.2. - If you need to get full access to parsed data from ``Accept``, ``Accept-Language``, ``Accept-Charset`` or ``Accept-Encoding``, you can use :class:`Symfony\\Component\\HttpFoundation\\AcceptHeader` utility class:: @@ -462,10 +459,6 @@ abstracts the hard work behind a simple API:: $response->headers->set('Content-Disposition', $d); -.. versionadded:: 2.2 - The :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse` - class was added in Symfony 2.2. - Alternatively, if you are serving a static file, you can use a :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse`:: diff --git a/components/process.rst b/components/process.rst index 981ec69e493..a9d21d1187f 100644 --- a/components/process.rst +++ b/components/process.rst @@ -36,9 +36,6 @@ a command in a sub-process:: The component takes care of the subtle differences between the different platforms when executing the command. -.. versionadded:: 2.2 - The ``getIncrementalOutput()`` and ``getIncrementalErrorOutput()`` methods were added in Symfony 2.2. - The ``getOutput()`` method always return the whole content of the standard output of the command and ``getErrorOutput()`` the content of the error output. Alternatively, the :method:`Symfony\\Component\\Process\\Process::getIncrementalOutput` diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 767097f8dfd..3c316726142 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -8,10 +8,6 @@ The PropertyAccess Component The PropertyAccess component provides function to read and write from/to an object or array using a simple string notation. -.. versionadded:: 2.2 - The PropertyAccess Component is new to Symfony 2.2. Previously, the - ``PropertyPath`` class was located in the ``Form`` component. - Installation ------------ diff --git a/components/routing/hostname_pattern.rst b/components/routing/hostname_pattern.rst index dfe5df8a445..fd872bb65a8 100644 --- a/components/routing/hostname_pattern.rst +++ b/components/routing/hostname_pattern.rst @@ -4,9 +4,6 @@ How to match a route based on the Host ====================================== -.. versionadded:: 2.2 - Host matching support was added in Symfony 2.2 - You can also match on the HTTP *host* of the incoming request. .. configuration-block:: diff --git a/components/routing/introduction.rst b/components/routing/introduction.rst index 12a032eb9d4..dc4ddcdd85d 100644 --- a/components/routing/introduction.rst +++ b/components/routing/introduction.rst @@ -93,9 +93,6 @@ are the least commonly needed. 7. An array of methods. These enforce a certain HTTP request method (``HEAD``, ``GET``, ``POST``, ...). -.. versionadded:: 2.2 - Host matching support was added in Symfony 2.2 - Take the following route, which combines several of these ideas:: $route = new Route( @@ -160,10 +157,6 @@ the :method:`Symfony\\Component\\Routing\\RouteCollection::addPrefix` method:: $rootCollection->addCollection($subCollection); -.. versionadded:: 2.2 - The ``addPrefix`` method is added in Symfony2.2. This was part of the - ``addCollection`` method in older versions. - Set the Request Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/components/stopwatch.rst b/components/stopwatch.rst index c7347a4ad5c..6b0004441c0 100644 --- a/components/stopwatch.rst +++ b/components/stopwatch.rst @@ -7,10 +7,6 @@ The Stopwatch Component Stopwatch component provides a way to profile code. -.. versionadded:: 2.2 - The Stopwatch Component is new to Symfony 2.2. Previously, the ``Stopwatch`` - class was located in the ``HttpKernel`` component. - Installation ------------ diff --git a/cookbook/console/sending_emails.rst b/cookbook/console/sending_emails.rst index 2d72da3cfd4..e7dbcbca03d 100644 --- a/cookbook/console/sending_emails.rst +++ b/cookbook/console/sending_emails.rst @@ -17,16 +17,13 @@ what URL it should use when generating URLs. There are two ways of configuring the request context: at the application level and per Command. -Configuring the Request Context globally +Configuring the Request Context Globally ---------------------------------------- -.. versionadded: 2.2 - The ``base_url`` parameter is available since Symfony 2.2 - To configure the Request Context - which is used by the URL Generator - you can redefine the parameters it uses as default values to change the default host -(localhost) and scheme (http). Starting with Symfony 2.2 you can also configure -the base path if Symfony is not running in the root directory. +(localhost) and scheme (http). You can also configure the base path if Symfony +is not running in the root directory. Note that this does not impact URLs generated via normal web requests, since those will override the defaults. diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index e358d39bb96..a53998ef168 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -114,10 +114,6 @@ The goal is to create a "name" field *only* if the underlying Product object is new (e.g. hasn't been persisted to the database). Based on that, the subscriber might look like the following: -.. versionadded:: 2.2 - The ability to pass a string into :method:`FormInterface::add ` - was added in Symfony 2.2. - .. code-block:: php // src/Acme/DemoBundle/Form/EventListener/AddNameFieldSubscriber.php @@ -469,10 +465,6 @@ On a form, we can usually listen to the following events: The events ``PRE_SUBMIT``, ``SUBMIT`` and ``POST_SUBMIT`` were added in Symfony 2.3. Before, they were named ``PRE_BIND``, ``BIND`` and ``POST_BIND``. -.. versionadded:: 2.2.6 - The behavior of the ``POST_SUBMIT`` event changed slightly in 2.2.6, which the - below example uses. - The key is to add a ``POST_SUBMIT`` listener to the field that your new field depends on. If you add a ``POST_SUBMIT`` listener to a form child (e.g. ``sport``), and add new children to the parent form, the Form component will detect the diff --git a/cookbook/templating/namespaced_paths.rst b/cookbook/templating/namespaced_paths.rst index 8e33b38ef66..ff41d10e7c0 100644 --- a/cookbook/templating/namespaced_paths.rst +++ b/cookbook/templating/namespaced_paths.rst @@ -4,9 +4,6 @@ How to use and Register namespaced Twig Paths ============================================= -.. versionadded:: 2.2 - Namespaced path support was added in 2.2. - Usually, when you refer to a template, you'll use the ``MyBundle:Subdir:filename.html.twig`` format (see :ref:`template-naming-locations`). @@ -80,4 +77,4 @@ called ``sidebar.twig`` in that directory, you can use it easily: .. code-block:: jinja - {% include '@foo_bar/side.bar.twig` %} \ No newline at end of file + {% include '@foo_bar/side.bar.twig` %} diff --git a/cookbook/templating/render_without_controller.rst b/cookbook/templating/render_without_controller.rst index f2a44f7e4e1..a00c9089f1a 100644 --- a/cookbook/templating/render_without_controller.rst +++ b/cookbook/templating/render_without_controller.rst @@ -77,10 +77,6 @@ this is probably only useful if you'd like to cache this page partial (see Caching the static Template --------------------------- -.. versionadded:: 2.2 - The ability to cache templates rendered via ``FrameworkBundle:Template:template`` - is new in Symfony 2.2. - Since templates that are rendered in this way are typically static, it might make sense to cache them. Fortunately, this is easy! By configuring a few other variables in your route, you can control exactly how your page is cached: @@ -134,4 +130,4 @@ object created in the controller. For more information on caching, see There is also a ``private`` variable (not shown here). By default, the Response will be made public, as long as ``maxAge`` or ``sharedMaxAge`` are passed. -If set to ``true``, the Response will be marked as private. \ No newline at end of file +If set to ``true``, the Response will be marked as private. diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 05d707e370d..2e91dd07e2c 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -405,11 +405,6 @@ would be ``/images/logo.png?version=5``. profiler ~~~~~~~~ -.. versionadded:: 2.2 - The ``enabled`` option was added in Symfony 2.2. Previously, the profiler - could only be disabled by omitting the ``framework.profiler`` configuration - entirely. - .. _profiler.enabled: enabled diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index dce75e06451..fd33ffc94aa 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -284,9 +284,6 @@ Redirecting after Login Using the PBKDF2 encoder: Security and Speed -------------------------------------------- -.. versionadded:: 2.2 - The PBKDF2 password encoder was added in Symfony 2.2. - The `PBKDF2`_ encoder provides a high level of Cryptographic security, as recommended by the National Institute of Standards and Technology (NIST). @@ -309,9 +306,6 @@ Using the BCrypt Password Encoder To use this encoder, you either need to use PHP Version 5.5 or install the `ircmaxell/password-compat`_ library via Composer. -.. versionadded:: 2.2 - The BCrypt password encoder was added in Symfony 2.2. - .. configuration-block:: .. code-block:: yaml diff --git a/reference/constraints/CardScheme.rst b/reference/constraints/CardScheme.rst index 172ddbfdf70..7e9309c1ea7 100644 --- a/reference/constraints/CardScheme.rst +++ b/reference/constraints/CardScheme.rst @@ -1,9 +1,6 @@ CardScheme ========== -.. versionadded:: 2.2 - The CardScheme validation is new in Symfony 2.2. - This constraint ensures that a credit card number is valid for a given credit card company. It can be used to validate the number before trying to initiate a payment through a payment gateway. diff --git a/reference/constraints/Luhn.rst b/reference/constraints/Luhn.rst index 58697e94184..0907456266b 100644 --- a/reference/constraints/Luhn.rst +++ b/reference/constraints/Luhn.rst @@ -1,9 +1,6 @@ Luhn ==== -.. versionadded:: 2.2 - The Luhn validation is new in Symfony 2.2. - This constraint is used to ensure that a credit card number passes the `Luhn algorithm`_. It is useful as a first step to validating a credit card: before communicating with a payment gateway. diff --git a/reference/constraints/UserPassword.rst b/reference/constraints/UserPassword.rst index e5e1baea42d..fdd9371b40c 100644 --- a/reference/constraints/UserPassword.rst +++ b/reference/constraints/UserPassword.rst @@ -1,14 +1,6 @@ UserPassword ============ -.. note:: - - Since Symfony 2.2, the ``UserPassword*`` classes in the - ``Symfony\\Component\\Security\\Core\\Validator\\Constraint`` namespace are - deprecated and will be removed in Symfony 2.3. Please use the - ``UserPassword*`` classes in the - ``Symfony\\Component\\Security\\Core\\Validator\\Constraints`` namespace instead. - This validates that an input value is equal to the current authenticated user's password. This is useful in a form where a user can change his password, but needs to enter his old password for security. diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index f81362c8dbc..35ed24521e6 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -1216,6 +1216,6 @@ For an example, see the ``EntityInitializer`` class inside the Doctrine Bridge. .. _`Twig's documentation`: http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension .. _`Twig official extension repository`: https://github.com/fabpot/Twig-extensions -.. _`KernelEvents`: https://github.com/symfony/symfony/blob/2.2/src/Symfony/Component/HttpKernel/KernelEvents.php +.. _`KernelEvents`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/KernelEvents.php .. _`SwiftMailer's Plugin Documentation`: http://swiftmailer.org/docs/plugins.html .. _`Twig Loader`: http://twig.sensiolabs.org/doc/api.html#loaders diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index af020967192..568e21ac668 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -17,10 +17,6 @@ There may also be tags in bundles you use that aren't listed here. Functions --------- -.. versionadded:: 2.2 - The ``render`` and ``controller`` functions are new in Symfony 2.2. Prior, - the ``{% render %}`` tag was used and had a different signature. - +----------------------------------------------------+--------------------------------------------------------------------------------------------+ | Function Syntax | Usage | +====================================================+============================================================================================+ From 2f486ce60aa1eb9466fe4527bd6c53a6e1343ded Mon Sep 17 00:00:00 2001 From: Mathieu Lemoine Date: Thu, 12 Dec 2013 14:18:43 -0500 Subject: [PATCH 003/835] [Security][Acl] Documentation for the new updateUserSecurityIdentity method in Dbal\MutableAclProvider --- cookbook/security/acl_advanced.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cookbook/security/acl_advanced.rst b/cookbook/security/acl_advanced.rst index 1bc217d8e6e..45d164aa29c 100644 --- a/cookbook/security/acl_advanced.rst +++ b/cookbook/security/acl_advanced.rst @@ -45,6 +45,13 @@ Security Identities This is analog to the object identity, but represents a user, or a role in your application. Each role, or user has its own security identity. +.. versionadded:: 2.5 + For users, the security identity is based on the username. This means that, + if for any reason, a user's username was to change, you must ensure its + security identity is updated too. The method ``updateUserSecurityIdentity`` + of the :class:`Symfony\\Component\\Security\\Acl\\Dbal\\MutableAclProvider` + class is there to handle the update. + Database Table Structure ------------------------ From a7b80a6bdb1a2a042d61a97d7b9c8adf0021590f Mon Sep 17 00:00:00 2001 From: Bilal Amarni Date: Tue, 17 Dec 2013 12:49:33 +0100 Subject: [PATCH 004/835] [Form] added multiple option to file type doc --- reference/forms/types/file.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index a33dae8d992..03da93ffa78 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -9,6 +9,8 @@ The ``file`` type represents a file input in your form. +-------------+---------------------------------------------------------------------+ | Rendered as | ``input`` ``file`` field | +-------------+---------------------------------------------------------------------+ +| Options | - `multiple`_ | ++-------------+---------------------------------------------------------------------+ | Inherited | - `required`_ | | options | - `label`_ | | | - `read_only`_ | @@ -76,6 +78,16 @@ before using it directly. Read the :doc:`cookbook ` for an example of how to manage a file upload associated with a Doctrine entity. +Field Options +------------- + +multiple +~~~~~~~~ + +**type**: ``Boolean`` **default**: ``false`` + +When set to true, the user will be able to upload multiple files at the same time. + Inherited options ----------------- From bf98517e92118e97a53e96fbbd6ec5259cbabc1e Mon Sep 17 00:00:00 2001 From: Bilal Amarni Date: Tue, 17 Dec 2013 12:52:22 +0100 Subject: [PATCH 005/835] fixed spaces --- reference/forms/types/file.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index 03da93ffa78..78d08fba3e4 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -9,7 +9,7 @@ The ``file`` type represents a file input in your form. +-------------+---------------------------------------------------------------------+ | Rendered as | ``input`` ``file`` field | +-------------+---------------------------------------------------------------------+ -| Options | - `multiple`_ | +| Options | - `multiple`_ | +-------------+---------------------------------------------------------------------+ | Inherited | - `required`_ | | options | - `label`_ | From 2a10a46c9dc1709fd926d2d79490b365bbaf00df Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 23 Dec 2013 17:37:35 -0500 Subject: [PATCH 006/835] [#3319] Linking to the whole method --- cookbook/security/acl_advanced.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/security/acl_advanced.rst b/cookbook/security/acl_advanced.rst index 45d164aa29c..163760c1a80 100644 --- a/cookbook/security/acl_advanced.rst +++ b/cookbook/security/acl_advanced.rst @@ -48,9 +48,9 @@ your application. Each role, or user has its own security identity. .. versionadded:: 2.5 For users, the security identity is based on the username. This means that, if for any reason, a user's username was to change, you must ensure its - security identity is updated too. The method ``updateUserSecurityIdentity`` - of the :class:`Symfony\\Component\\Security\\Acl\\Dbal\\MutableAclProvider` - class is there to handle the update. + security identity is updated too. The + :method:`MutableAclProvider::updateUserSecurityIdentity ` + ``updateUserSecurityIdentity`` method is there to handle the update. Database Table Structure ------------------------ From 34551d2cd380d8cf32988bea0e6ed4000607884d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 26 Dec 2013 10:34:20 -0500 Subject: [PATCH 007/835] [#3319] Fixing typos thanks to @xabbuh --- cookbook/security/acl_advanced.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/security/acl_advanced.rst b/cookbook/security/acl_advanced.rst index 163760c1a80..01228220bb6 100644 --- a/cookbook/security/acl_advanced.rst +++ b/cookbook/security/acl_advanced.rst @@ -49,8 +49,8 @@ your application. Each role, or user has its own security identity. For users, the security identity is based on the username. This means that, if for any reason, a user's username was to change, you must ensure its security identity is updated too. The - :method:`MutableAclProvider::updateUserSecurityIdentity ` - ``updateUserSecurityIdentity`` method is there to handle the update. + :method:`MutableAclProvider::updateUserSecurityIdentity() ` + method is there to handle the update. Database Table Structure ------------------------ From bd1e53e84058decccd141580d256a9013be401b2 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Jan 2014 16:51:39 -0600 Subject: [PATCH 008/835] Revert "[#3338] Removing delete_empty details because this is first being committed to 2.3" This re-introduces the delete_empty documentation to the master (2.5) branch, since it should live there. This reverts commit 72fa7b4767b5b86053ca92f4052de2dc7c2dac09. --- reference/forms/types/collection.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index b6c014784b5..0decd3c547e 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -19,6 +19,7 @@ forms, which is useful when creating forms that expose one-to-many relationships | | - `allow_delete`_ | | | - `prototype`_ | | | - `prototype_name`_ | +| | - `delete_empty`_ | +-------------+-----------------------------------------------------------------------------+ | Inherited | - `label`_ | | | - `label_attr`_ | @@ -335,6 +336,16 @@ If you have several collections in your form, or worse, nested collections you may want to change the placeholder so that unrelated placeholders are not replaced with the same value. +delete_empty +~~~~~~~~~~~~ + +**type**: ``Boolean`` **default**: ``false`` + +If you want to explicitly remove entirely empty collection entries from your +form you have to set this option to true. However, existing collection entries +will only be deleted if you have the allow_delete_ option enabled. Otherwise +the empty values will be kept. + Inherited options ----------------- From 42084dd5bcd9109ea65429ccd5ae7837813125c6 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Jan 2014 16:53:05 -0600 Subject: [PATCH 009/835] [#3338] Adding versionadded --- reference/forms/types/collection.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index 0decd3c547e..fe1e6ee8cb5 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -339,6 +339,9 @@ replaced with the same value. delete_empty ~~~~~~~~~~~~ +.. versionadded:: 2.5 + The delete_empty option was introduced in Symfony 2.5. + **type**: ``Boolean`` **default**: ``false`` If you want to explicitly remove entirely empty collection entries from your From 33d27cc14dd55c36247397522a90a6a1456fff89 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Jan 2014 19:32:16 -0600 Subject: [PATCH 010/835] [#3362] Updating composer install version on the master branch --- book/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index 3604f1236c5..c5a7ed955ff 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -57,7 +57,7 @@ Distribution: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony 2.4.* + $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony dev-master .. tip:: From bfe3a203e33a53f5b581bcafa9be67a261067a97 Mon Sep 17 00:00:00 2001 From: Philipp Rieber Date: Sat, 4 Jan 2014 11:47:25 +0100 Subject: [PATCH 011/835] [Validation] Add "hasser" support --- book/validation.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/book/validation.rst b/book/validation.rst index ea5315afaae..ca2880f5881 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -583,8 +583,8 @@ Getters Constraints can also be applied to the return value of a method. Symfony2 allows you to add a constraint to any public method whose name starts with -"get" or "is". In this guide, both of these types of methods are referred -to as "getters". +"get", "is" or "has". In this guide, these types of methods are referred to +as "getters". The benefit of this technique is that it allows you to validate your object dynamically. For example, suppose you want to make sure that a password field @@ -665,9 +665,9 @@ Now, create the ``isPasswordLegal()`` method, and include the logic you need:: .. note:: The keen-eyed among you will have noticed that the prefix of the getter - ("get" or "is") is omitted in the mapping. This allows you to move the - constraint to a property with the same name later (or vice versa) without - changing your validation logic. + ("get", "is" or "has") is omitted in the mapping. This allows you to move + the constraint to a property with the same name later (or vice versa) + without changing your validation logic. .. _validation-class-target: From bb9004ef6cd181fd7fba1f6d7dae1e2c5e129b3f Mon Sep 17 00:00:00 2001 From: Philipp Rieber Date: Sat, 4 Jan 2014 19:09:24 +0100 Subject: [PATCH 012/835] Add "versionadded" directive for 2.5 --- book/validation.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/book/validation.rst b/book/validation.rst index ca2880f5881..4168ae079bb 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -586,6 +586,9 @@ allows you to add a constraint to any public method whose name starts with "get", "is" or "has". In this guide, these types of methods are referred to as "getters". +.. versionadded:: 2.5 + Support for methods starting with ``has`` is new in Symfony 2.5. + The benefit of this technique is that it allows you to validate your object dynamically. For example, suppose you want to make sure that a password field doesn't match the first name of the user (for security reasons). You can From b29ab899a445a2686189a9266e6ec8fea4b3cae4 Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Sat, 4 Jan 2014 23:32:19 +0000 Subject: [PATCH 013/835] Documented the Change the Default Command in the Console component --- ...tool.rst => changing_default_behavior.rst} | 45 ++++++++++++++++++- components/console/index.rst | 2 +- 2 files changed, 44 insertions(+), 3 deletions(-) rename components/console/{single_command_tool.rst => changing_default_behavior.rst} (62%) diff --git a/components/console/single_command_tool.rst b/components/console/changing_default_behavior.rst similarity index 62% rename from components/console/single_command_tool.rst rename to components/console/changing_default_behavior.rst index 27942df40f2..33ffc6ee1e6 100644 --- a/components/console/single_command_tool.rst +++ b/components/console/changing_default_behavior.rst @@ -1,8 +1,49 @@ .. index:: - single: Console; Single command application + single: Console; Changing the Default Behavior + +Changing the Default Behavior +============================= + +When building a command line tool, you may need to customize it to fit your needs. +Probably you want to change the Default Command that the Application runs or +maybe you just want to run a Single Command instead of have to pass the command +name each time. Fortunately it is possible to do both. + +Changing the Default Command +---------------------------- + +By default the Application will always run the ListCommand. In order to change +the default command you just need to pass the command name you want to run by +default to the :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` +method:: + + #!/usr/bin/env php + add($command); + $application->setDefaultCommand($command->getName()); + $application->run() + +Test the new console command by running the following + +.. code-block:: bash + + $ app/console Fabien + +This will print the following to the command line: + +.. code-block:: text + + Hello Fabien Building a Single Command Application -===================================== +------------------------------------- When building a command line tool, you may not need to provide several commands. In such case, having to pass the command name each time is tedious. Fortunately, diff --git a/components/console/index.rst b/components/console/index.rst index c814942d018..ba15bd2c176 100644 --- a/components/console/index.rst +++ b/components/console/index.rst @@ -6,6 +6,6 @@ Console introduction usage - single_command_tool + changing_default_behavior events helpers/index From b9f8b8d5cbb603ca538889ac29e2e122e78ccfdc Mon Sep 17 00:00:00 2001 From: Philipp Rieber Date: Sun, 5 Jan 2014 13:13:10 +0100 Subject: [PATCH 014/835] fix "versionadded" wording --- book/validation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/validation.rst b/book/validation.rst index 4168ae079bb..9d253930917 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -587,7 +587,7 @@ allows you to add a constraint to any public method whose name starts with as "getters". .. versionadded:: 2.5 - Support for methods starting with ``has`` is new in Symfony 2.5. + Support for methods starting with ``has`` was introduced in Symfony 2.5. The benefit of this technique is that it allows you to validate your object dynamically. For example, suppose you want to make sure that a password field From 11c7174059df940a1a5e75584727c452304355f8 Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Sun, 5 Jan 2014 14:42:59 +0000 Subject: [PATCH 015/835] Added the version number where the setDefaultCommand was introduced --- components/console/changing_default_behavior.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/console/changing_default_behavior.rst b/components/console/changing_default_behavior.rst index 33ffc6ee1e6..eb44d128a12 100644 --- a/components/console/changing_default_behavior.rst +++ b/components/console/changing_default_behavior.rst @@ -4,6 +4,10 @@ Changing the Default Behavior ============================= +.. versionadded:: 2.5, + The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` + method was introduced in version 2.5 + When building a command line tool, you may need to customize it to fit your needs. Probably you want to change the Default Command that the Application runs or maybe you just want to run a Single Command instead of have to pass the command From 60e2b3ec4466b3db6450bb76da1d4f6cb0f8c368 Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Sun, 5 Jan 2014 14:47:30 +0000 Subject: [PATCH 016/835] Added the delete document to avoid broken urls and added a notice that the document was moved to another location --- components/console/single_command_tool.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 components/console/single_command_tool.rst diff --git a/components/console/single_command_tool.rst b/components/console/single_command_tool.rst new file mode 100644 index 00000000000..c4a477ded13 --- /dev/null +++ b/components/console/single_command_tool.rst @@ -0,0 +1,4 @@ +Building a Single Command Application +===================================== + +This Document was moved to :doc:`/components/console/changing_default_behaviour` From af9eac4f97b1918031c02d3dc395997012682e7a Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Sun, 5 Jan 2014 14:55:02 +0000 Subject: [PATCH 017/835] Changed the code to remove references to Symfony Framework since it's the standalone component --- components/console/changing_default_behavior.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/console/changing_default_behavior.rst b/components/console/changing_default_behavior.rst index eb44d128a12..0dbbb64ed60 100644 --- a/components/console/changing_default_behavior.rst +++ b/components/console/changing_default_behavior.rst @@ -23,9 +23,9 @@ method:: #!/usr/bin/env php Date: Sun, 5 Jan 2014 15:12:09 +0000 Subject: [PATCH 018/835] Updated references to the new document --- components/console/introduction.rst | 2 +- components/map.rst.inc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 4ca2bcfe8c1..d7217176400 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -525,7 +525,7 @@ Learn More! ----------- * :doc:`/components/console/usage` -* :doc:`/components/console/single_command_tool` +* :doc:`/components/console/changing_default_behavior` .. _Packagist: https://packagist.org/packages/symfony/console .. _ANSICON: https://github.com/adoxa/ansicon/downloads diff --git a/components/map.rst.inc b/components/map.rst.inc index a5f2d0f8db7..842097c5ef2 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -19,7 +19,7 @@ * :doc:`/components/console/introduction` * :doc:`/components/console/usage` - * :doc:`/components/console/single_command_tool` + * :doc:`/components/console/changing_default_behavior` * :doc:`/components/console/events` * :doc:`/components/console/helpers/index` From 012456d9f5a13455c4271d1fdc888600c415ebef Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Sun, 5 Jan 2014 15:20:41 +0000 Subject: [PATCH 019/835] Moved `versionadded` to the right section --- components/console/changing_default_behavior.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/console/changing_default_behavior.rst b/components/console/changing_default_behavior.rst index 0dbbb64ed60..e89332b4c9d 100644 --- a/components/console/changing_default_behavior.rst +++ b/components/console/changing_default_behavior.rst @@ -4,10 +4,6 @@ Changing the Default Behavior ============================= -.. versionadded:: 2.5, - The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` - method was introduced in version 2.5 - When building a command line tool, you may need to customize it to fit your needs. Probably you want to change the Default Command that the Application runs or maybe you just want to run a Single Command instead of have to pass the command @@ -16,6 +12,10 @@ name each time. Fortunately it is possible to do both. Changing the Default Command ---------------------------- +.. versionadded:: 2.5, +The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` + method was introduced in version 2.5 + By default the Application will always run the ListCommand. In order to change the default command you just need to pass the command name you want to run by default to the :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` From c23f34e0f5f9bd6984a2e01aed5d768e1ed82753 Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Sun, 5 Jan 2014 22:39:12 +0000 Subject: [PATCH 020/835] Applied some suggestions --- .../console/changing_default_behavior.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/components/console/changing_default_behavior.rst b/components/console/changing_default_behavior.rst index e89332b4c9d..244d9ed6af3 100644 --- a/components/console/changing_default_behavior.rst +++ b/components/console/changing_default_behavior.rst @@ -5,21 +5,20 @@ Changing the Default Behavior ============================= When building a command line tool, you may need to customize it to fit your needs. -Probably you want to change the Default Command that the Application runs or -maybe you just want to run a Single Command instead of have to pass the command -name each time. Fortunately it is possible to do both. +Probably you want to change the default command that the Application runs or +maybe you just want to run a single command instead of have to pass the command +name each time. Fortunately, it is possible to do both. Changing the Default Command ---------------------------- .. versionadded:: 2.5, -The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` - method was introduced in version 2.5 + The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` + method was introduced in version 2.5. By default the Application will always run the ListCommand. In order to change the default command you just need to pass the command name you want to run by -default to the :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` -method:: +default to the ``setDefaultCommand`` method:: #!/usr/bin/env php Date: Mon, 6 Jan 2014 23:14:48 +0100 Subject: [PATCH 021/835] Add info about callback in options resolver --- components/options_resolver.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 9a62f8581fa..d14ee678278 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -305,6 +305,26 @@ There is also an method, which you can use if you want to add an allowed value to the previously set allowed values. +If you need to add some more logic to the value validation process you can pass a callable +as an allowed value:: + + // ... + protected function setDefaultOptions(OptionsResolverInterface $resolver) + { + // ... + + $resolver->setAllowedValues(array( + 'transport' => function($value) { + return strpos($value, 'mail') !== false; + } + )); + } + +Note that using this together with addAllowedValues will not work. + +.. versionadded:: 2.5 + The callback support for allowed values was added in Symfony 2.5. + Configure allowed Types ~~~~~~~~~~~~~~~~~~~~~~~ From 5e97202fcf5d974964bb540d85bc6e15c2395cc0 Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Tue, 7 Jan 2014 15:16:45 +0000 Subject: [PATCH 022/835] Applyed suggestions from @fabpot and @stof --- .../console/changing_default_behavior.rst | 116 ------------------ .../console/changing_default_command.rst | 67 ++++++++++ components/console/index.rst | 3 +- components/console/introduction.rst | 3 +- components/console/single_command_tool.rst | 70 ++++++++++- 5 files changed, 140 insertions(+), 119 deletions(-) delete mode 100644 components/console/changing_default_behavior.rst create mode 100644 components/console/changing_default_command.rst diff --git a/components/console/changing_default_behavior.rst b/components/console/changing_default_behavior.rst deleted file mode 100644 index 244d9ed6af3..00000000000 --- a/components/console/changing_default_behavior.rst +++ /dev/null @@ -1,116 +0,0 @@ -.. index:: - single: Console; Changing the Default Behavior - -Changing the Default Behavior -============================= - -When building a command line tool, you may need to customize it to fit your needs. -Probably you want to change the default command that the Application runs or -maybe you just want to run a single command instead of have to pass the command -name each time. Fortunately, it is possible to do both. - -Changing the Default Command ----------------------------- - -.. versionadded:: 2.5, - The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` - method was introduced in version 2.5. - -By default the Application will always run the ListCommand. In order to change -the default command you just need to pass the command name you want to run by -default to the ``setDefaultCommand`` method:: - - #!/usr/bin/env php - add($command); - $application->setDefaultCommand($command->getName()); - $application->run() - -Test the new console command by running the following - -.. code-block:: bash - - $ app/console Fabien - -This will print the following to the command line: - -.. code-block:: text - - Hello Fabien - -Building a Single Command Application -------------------------------------- - -When building a command line tool, you may not need to provide several commands. -In such case, having to pass the command name each time is tedious. Fortunately, -it is possible to remove this need by extending the application:: - - namespace Acme\Tool; - - use Symfony\Component\Console\Application; - use Symfony\Component\Console\Input\InputInterface; - - class MyApplication extends Application - { - /** - * Gets the name of the command based on input. - * - * @param InputInterface $input The input interface - * - * @return string The command name - */ - protected function getCommandName(InputInterface $input) - { - // This should return the name of your command. - return 'my_command'; - } - - /** - * Gets the default commands that should always be available. - * - * @return array An array of default Command instances - */ - protected function getDefaultCommands() - { - // Keep the core default commands to have the HelpCommand - // which is used when using the --help option - $defaultCommands = parent::getDefaultCommands(); - - $defaultCommands[] = new MyCommand(); - - return $defaultCommands; - } - - /** - * Overridden so that the application doesn't expect the command - * name to be the first argument. - */ - public function getDefinition() - { - $inputDefinition = parent::getDefinition(); - // clear out the normal first argument, which is the command name - $inputDefinition->setArguments(); - - return $inputDefinition; - } - } - -When calling your console script, the ``MyCommand`` command will then always -be used, without having to pass its name. - -Executing the application can also be simplified:: - - #!/usr/bin/env php - run(); diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst new file mode 100644 index 00000000000..a90e298223a --- /dev/null +++ b/components/console/changing_default_command.rst @@ -0,0 +1,67 @@ +.. index:: + single: Console; Changing the Default Command + +Changing the Default Command +============================ + +.. versionadded:: 2.5, + The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` + method was introduced in version 2.5. + +By default the Application will always run the ``ListCommand``. In order to change +the default command you just need to pass the command name you want to run by +default to the ``setDefaultCommand`` method:: + + namespace Acme\Command; + + use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Output\OutputInterface; + + class HelloWorldCommand extends Command + { + protected function configure() + { + $this->setName('hello:world') + ->setDescription('Outputs \'Hello World\''); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $output->writeln('Hello World'); + } + } + +Executing the application and changing the default Command:: + + // application.php + + use Acme\Command\HelloWorldCommand; + use Symfony\Component\Console\Application; + + $command = new HelloWorldCommand(); + $application = new Application(); + $application->add($command); + $application->setDefaultCommand($command->getName()); + $application->run(); + +Test the new default console command by running the following + +.. code-block:: bash + + $ php application.php + +This will print the following to the command line: + +.. code-block:: text + + Hello Fabien + +.. tip:: + + The feature was a limitation since you cannot use the Command ``arguments``. + +Learn More! +----------- + +* :doc:`/components/console/single_command_tool` diff --git a/components/console/index.rst b/components/console/index.rst index ba15bd2c176..f974291a8e0 100644 --- a/components/console/index.rst +++ b/components/console/index.rst @@ -6,6 +6,7 @@ Console introduction usage - changing_default_behavior + changing_default_command + single_command_tool events helpers/index diff --git a/components/console/introduction.rst b/components/console/introduction.rst index d7217176400..c742c333f89 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -525,7 +525,8 @@ Learn More! ----------- * :doc:`/components/console/usage` -* :doc:`/components/console/changing_default_behavior` +* :doc:`/components/console/single_command_tool` +* :doc:`/components/console/changing_default_command` .. _Packagist: https://packagist.org/packages/symfony/console .. _ANSICON: https://github.com/adoxa/ansicon/downloads diff --git a/components/console/single_command_tool.rst b/components/console/single_command_tool.rst index c4a477ded13..a5ba82ceca5 100644 --- a/components/console/single_command_tool.rst +++ b/components/console/single_command_tool.rst @@ -1,4 +1,72 @@ +.. index:: +single: Console; Single command application + Building a Single Command Application ===================================== -This Document was moved to :doc:`/components/console/changing_default_behaviour` +When building a command line tool, you may not need to provide several commands. +In such case, having to pass the command name each time is tedious. Fortunately, +it is possible to remove this need by extending the application:: + + namespace Acme\Tool; + + use Symfony\Component\Console\Application; + use Symfony\Component\Console\Input\InputInterface; + + class MyApplication extends Application + { + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + // This should return the name of your command. + return 'my_command'; + } + + /** + * Gets the default commands that should always be available. + * + * @return array An array of default Command instances + */ + protected function getDefaultCommands() + { + // Keep the core default commands to have the HelpCommand + // which is used when using the --help option + $defaultCommands = parent::getDefaultCommands(); + + $defaultCommands[] = new MyCommand(); + + return $defaultCommands; + } + + /** + * Overridden so that the application doesn't expect the command + * name to be the first argument. + */ + public function getDefinition() + { + $inputDefinition = parent::getDefinition(); + // clear out the normal first argument, which is the command name + $inputDefinition->setArguments(); + + return $inputDefinition; + } + } + +When calling your console script, the command ``MyCommand`` will then always +be used, without having to pass its name. + +You can also simplify how you execute the application:: + + #!/usr/bin/env php + run(); From fdf48913ed078ac5f232bdbb07d2c6b2e1009b82 Mon Sep 17 00:00:00 2001 From: Jakub Zalas Date: Wed, 8 Jan 2014 22:39:58 +0000 Subject: [PATCH 023/835] Documented deprecation of the apache router. --- cookbook/configuration/apache_router.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cookbook/configuration/apache_router.rst b/cookbook/configuration/apache_router.rst index c740979534a..4eca8afe3b3 100644 --- a/cookbook/configuration/apache_router.rst +++ b/cookbook/configuration/apache_router.rst @@ -7,6 +7,12 @@ How to use the Apache Router Symfony2, while fast out of the box, also provides various ways to increase that speed with a little bit of tweaking. One of these ways is by letting Apache handle routes directly, rather than using Symfony2 for this task. +.. note:: + + Apache router was deprecated in Symfony 2.5 and will be removed in Symfony 3.0. + Since the PHP implementation of the Router was improved, performance gains were no longer + significant (while it's very hard to replicate the same behaviour). + Change Router Configuration Parameters -------------------------------------- From c1b2aad00e47ddd9c02732f24ed2ce3960210e03 Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Thu, 9 Jan 2014 09:17:19 +0000 Subject: [PATCH 024/835] Applied suggestions from Ryan --- components/console/changing_default_command.rst | 8 ++++---- components/console/single_command_tool.rst | 2 +- components/map.rst.inc | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst index a90e298223a..a2072c795ba 100644 --- a/components/console/changing_default_command.rst +++ b/components/console/changing_default_command.rst @@ -1,5 +1,5 @@ .. index:: - single: Console; Changing the Default Command + single: Console; Changing the Default Command Changing the Default Command ============================ @@ -8,7 +8,7 @@ Changing the Default Command The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` method was introduced in version 2.5. -By default the Application will always run the ``ListCommand``. In order to change +will always run the ``ListCommand`` when no command name is passed. In order to change the default command you just need to pass the command name you want to run by default to the ``setDefaultCommand`` method:: @@ -45,7 +45,7 @@ Executing the application and changing the default Command:: $application->setDefaultCommand($command->getName()); $application->run(); -Test the new default console command by running the following +Test the new default console command by running the following: .. code-block:: bash @@ -59,7 +59,7 @@ This will print the following to the command line: .. tip:: - The feature was a limitation since you cannot use the Command ``arguments``. + This feature has a limitation: you cannot use it with any Command arguments. Learn More! ----------- diff --git a/components/console/single_command_tool.rst b/components/console/single_command_tool.rst index a5ba82ceca5..02fc24a565f 100644 --- a/components/console/single_command_tool.rst +++ b/components/console/single_command_tool.rst @@ -1,5 +1,5 @@ .. index:: -single: Console; Single command application + single: Console; Single command application Building a Single Command Application ===================================== diff --git a/components/map.rst.inc b/components/map.rst.inc index 842097c5ef2..7a5c74eeff6 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -19,7 +19,8 @@ * :doc:`/components/console/introduction` * :doc:`/components/console/usage` - * :doc:`/components/console/changing_default_behavior` + * :doc:`/components/console/single_command_tool` + * :doc:`/components/console/changing_default_command` * :doc:`/components/console/events` * :doc:`/components/console/helpers/index` From faa034b3e678dde3d6401c36258fa953f4cb5cb4 Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Sun, 12 Jan 2014 18:58:23 +0100 Subject: [PATCH 025/835] [WIP][Valiadtor] - EmailConstraint reference --- reference/constraints/Email.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index 5eacfbda04b..48cf138b9a9 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -7,7 +7,8 @@ cast to a string before being validated. +----------------+---------------------------------------------------------------------+ | Applies to | :ref:`property or method ` | +----------------+---------------------------------------------------------------------+ -| Options | - `message`_ | +| Options | - `strict`_ | +| | - `message`_ | | | - `checkMX`_ | | | - `checkHost`_ | +----------------+---------------------------------------------------------------------+ @@ -89,6 +90,15 @@ Basic Usage Options ------- +strict +~~~~~~~ + +**type**: ``boolean`` **default**: ``false`` + +Will validate the email against a simple RegularExpression. +If true, then the library (`egulias/email-validator`)[https://packagist.org/packages/egulias/email-validator] +is required to perform an RFC compilant validation. + message ~~~~~~~ From e4a0e2f0ff892a3ec5ca9a7d45554b57d6b11fa5 Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Sun, 12 Jan 2014 19:07:33 +0100 Subject: [PATCH 026/835] Use of Sphinx markup --- reference/constraints/Email.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index 48cf138b9a9..caba8cd7fcc 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -96,7 +96,7 @@ strict **type**: ``boolean`` **default**: ``false`` Will validate the email against a simple RegularExpression. -If true, then the library (`egulias/email-validator`)[https://packagist.org/packages/egulias/email-validator] +If true, then the `EmailValidator `_ library is required to perform an RFC compilant validation. message From f0b3b85567f0865a983e1ece7a0780416239597e Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Sun, 12 Jan 2014 19:53:58 +0100 Subject: [PATCH 027/835] Version added and clarifications --- reference/constraints/Email.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index caba8cd7fcc..e459115dfa8 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -91,11 +91,13 @@ Options ------- strict -~~~~~~~ +~~~~~~ +.. versionadded:: 2.5 + The ``strict`` option was introduced in Symfony 2.5. **type**: ``boolean`` **default**: ``false`` -Will validate the email against a simple RegularExpression. +When false, the email will be validated against a simple RegularExpression. If true, then the `EmailValidator `_ library is required to perform an RFC compilant validation. From 517e4c549372cc81138ca590bf53506817c3402d Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Sun, 12 Jan 2014 20:24:11 +0100 Subject: [PATCH 028/835] Link moved and heading updated --- reference/constraints/Email.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index e459115dfa8..518503f583e 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -90,16 +90,16 @@ Basic Usage Options ------- -strict -~~~~~~ .. versionadded:: 2.5 The ``strict`` option was introduced in Symfony 2.5. +strict +~~~~~~ + **type**: ``boolean`` **default**: ``false`` -When false, the email will be validated against a simple RegularExpression. -If true, then the `EmailValidator `_ library -is required to perform an RFC compilant validation. +When false, the email will be validated against a simple Regular Expression. +If true, then the `egulias/email-validator`_ library is required to perform an RFC compilant validation. message ~~~~~~~ @@ -124,3 +124,6 @@ checkHost If true, then the :phpfunction:`checkdnsrr` PHP function will be used to check the validity of the MX *or* the A *or* the AAAA record of the host of the given email. + + +.. _EmailValidator: https://packagist.org/packages/egulias/email-validator From cce3b40b6b6be563827010bf991a4c0bdc702ffc Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Sun, 12 Jan 2014 20:41:15 +0000 Subject: [PATCH 029/835] fixed typo --- components/console/changing_default_command.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst index a2072c795ba..d00e60a3d7f 100644 --- a/components/console/changing_default_command.rst +++ b/components/console/changing_default_command.rst @@ -4,7 +4,7 @@ Changing the Default Command ============================ -.. versionadded:: 2.5, +.. versionadded:: 2.5 The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` method was introduced in version 2.5. From 8beb5704adc38a24d1d9e4581b6740cf8f8f87bd Mon Sep 17 00:00:00 2001 From: Daniel Gomes Date: Sun, 12 Jan 2014 21:50:05 +0000 Subject: [PATCH 030/835] Fixed `versionadded` inconsistencies --- cookbook/security/acl_advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/acl_advanced.rst b/cookbook/security/acl_advanced.rst index 01228220bb6..208f72d3b6e 100644 --- a/cookbook/security/acl_advanced.rst +++ b/cookbook/security/acl_advanced.rst @@ -50,7 +50,7 @@ your application. Each role, or user has its own security identity. if for any reason, a user's username was to change, you must ensure its security identity is updated too. The :method:`MutableAclProvider::updateUserSecurityIdentity() ` - method is there to handle the update. + method is there to handle the update, it was introduced in Symfony 2.5. Database Table Structure ------------------------ From aa5aa61593465b80838dd7cc34d5b8d7843fc6e9 Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Sun, 12 Jan 2014 23:47:32 +0100 Subject: [PATCH 031/835] Typo and link label --- reference/constraints/Email.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index 518503f583e..d1be0e43dad 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -99,7 +99,7 @@ strict **type**: ``boolean`` **default**: ``false`` When false, the email will be validated against a simple Regular Expression. -If true, then the `egulias/email-validator`_ library is required to perform an RFC compilant validation. +If true, then the `EmailValidator`_ library is required to perform an RFC compliant validation. message ~~~~~~~ From 8231230f426e95ff0f2661b33a2555c0b88b27cd Mon Sep 17 00:00:00 2001 From: Marek Kalnik Date: Mon, 13 Jan 2014 20:15:38 +0100 Subject: [PATCH 032/835] Fix according to PR comments --- components/options_resolver.rst | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index d14ee678278..56cfa62d4b1 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -305,7 +305,11 @@ There is also an method, which you can use if you want to add an allowed value to the previously set allowed values. -If you need to add some more logic to the value validation process you can pass a callable +.. versionadded:: 2.5 + + The callback support for allowed values was introduced in Symfony 2.5. + +If you need to add some more logic to the value validation process, you can pass a callable as an allowed value:: // ... @@ -315,15 +319,14 @@ as an allowed value:: $resolver->setAllowedValues(array( 'transport' => function($value) { - return strpos($value, 'mail') !== false; - } + return false !== strpos($value, 'mail'); + }, )); } -Note that using this together with addAllowedValues will not work. +.. caution:: -.. versionadded:: 2.5 - The callback support for allowed values was added in Symfony 2.5. + Note that using this together with ``addAllowedValues`` will not work. Configure allowed Types ~~~~~~~~~~~~~~~~~~~~~~~ From a0dd460633842ef7f3e028786d7f503b18613f91 Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Mon, 13 Jan 2014 23:12:35 +0100 Subject: [PATCH 033/835] Lowercase and link label --- reference/constraints/Email.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index d1be0e43dad..4b64e3c9632 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -98,8 +98,8 @@ strict **type**: ``boolean`` **default**: ``false`` -When false, the email will be validated against a simple Regular Expression. -If true, then the `EmailValidator`_ library is required to perform an RFC compliant validation. +When false, the email will be validated against a simple regular expression. +If true, then the `egulias/email-validator`_ library is required to perform an RFC compliant validation. message ~~~~~~~ @@ -126,4 +126,4 @@ check the validity of the MX *or* the A *or* the AAAA record of the host of the given email. -.. _EmailValidator: https://packagist.org/packages/egulias/email-validator +.. _egulias/email-validator: https://packagist.org/packages/egulias/email-validator From c50f0413782ea8d76a785e7aafac90049439a1a9 Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Fri, 17 Jan 2014 00:11:14 +0100 Subject: [PATCH 034/835] CS --- reference/constraints/Email.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index 4b64e3c9632..02c736bc6e5 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -99,7 +99,8 @@ strict **type**: ``boolean`` **default**: ``false`` When false, the email will be validated against a simple regular expression. -If true, then the `egulias/email-validator`_ library is required to perform an RFC compliant validation. +If true, then the `egulias/email-validator`_ library is required to perform +an RFC compliant validation. message ~~~~~~~ @@ -124,6 +125,4 @@ checkHost If true, then the :phpfunction:`checkdnsrr` PHP function will be used to check the validity of the MX *or* the A *or* the AAAA record of the host of the given email. - - .. _egulias/email-validator: https://packagist.org/packages/egulias/email-validator From 6fd3f93201ec093d20b12f13f035373ac5a0bec0 Mon Sep 17 00:00:00 2001 From: Klaus Silveira Date: Fri, 17 Jan 2014 10:51:53 -0200 Subject: [PATCH 035/835] Documenting createAccessDeniedException() method Updating documentation to reflect the changes added in https://github.com/symfony/symfony/pull/9405. --- book/security.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index fa9958dca1e..f9a8e97e887 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1072,12 +1072,11 @@ fine-grained enough in certain cases. When necessary, you can easily force authorization from inside a controller:: // ... - use Symfony\Component\Security\Core\Exception\AccessDeniedException; public function helloAction($name) { if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { - throw new AccessDeniedException(); + throw $this->createAccessDeniedException('Unable to access this page!'); } // ... @@ -1085,6 +1084,9 @@ authorization from inside a controller:: .. _book-security-securing-controller-annotations: +The ``createAccessDeniedException()`` method creates a special ``AccessDeniedException`` +object, which ultimately triggers a 403 HTTP response inside Symfony. + Thanks to the SensioFrameworkExtraBundle, you can also secure your controller using annotations:: // ... From 53f156c6fd104cb0d0719fcd4d66d5f32b522906 Mon Sep 17 00:00:00 2001 From: Klaus Silveira Date: Fri, 17 Jan 2014 15:07:11 -0200 Subject: [PATCH 036/835] Updating references to match doc format --- book/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/security.rst b/book/security.rst index f9a8e97e887..8cff1560a0a 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1084,7 +1084,7 @@ authorization from inside a controller:: .. _book-security-securing-controller-annotations: -The ``createAccessDeniedException()`` method creates a special ``AccessDeniedException`` +The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException()` method creates a special :class:`Symfony\\Component\\Security\\Core\Exception\\AccessDeniedException` object, which ultimately triggers a 403 HTTP response inside Symfony. Thanks to the SensioFrameworkExtraBundle, you can also secure your controller using annotations:: From b1b9090452a6141fa7763f711fde1a99f9e79e71 Mon Sep 17 00:00:00 2001 From: Klaus Silveira Date: Fri, 17 Jan 2014 16:06:35 -0200 Subject: [PATCH 037/835] Adding versionadded detail --- book/security.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/book/security.rst b/book/security.rst index 8cff1560a0a..3d0a26dc3b1 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1084,6 +1084,9 @@ authorization from inside a controller:: .. _book-security-securing-controller-annotations: +.. versionadded:: 2.5 + The ``createAccessDeniedException`` method was introduced in Symfony 2.5. + The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException()` method creates a special :class:`Symfony\\Component\\Security\\Core\Exception\\AccessDeniedException` object, which ultimately triggers a 403 HTTP response inside Symfony. From da6002d65ea8468875306c10eec6e5a115543c1a Mon Sep 17 00:00:00 2001 From: Klaus Silveira Date: Fri, 17 Jan 2014 17:01:57 -0200 Subject: [PATCH 038/835] Wrapping line --- book/security.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/security.rst b/book/security.rst index 3d0a26dc3b1..9603fb92a15 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1087,7 +1087,8 @@ authorization from inside a controller:: .. versionadded:: 2.5 The ``createAccessDeniedException`` method was introduced in Symfony 2.5. -The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException()` method creates a special :class:`Symfony\\Component\\Security\\Core\Exception\\AccessDeniedException` +The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException()` +method creates a special :class:`Symfony\\Component\\Security\\Core\Exception\\AccessDeniedException` object, which ultimately triggers a 403 HTTP response inside Symfony. Thanks to the SensioFrameworkExtraBundle, you can also secure your controller using annotations:: From 257c48348c51a9b5361bb4ea800d643afa3f140f Mon Sep 17 00:00:00 2001 From: Eduardo Gulias Davis Date: Sat, 18 Jan 2014 20:03:36 +0100 Subject: [PATCH 039/835] Blank line restored --- reference/constraints/Email.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index 02c736bc6e5..112620da68b 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -125,4 +125,5 @@ checkHost If true, then the :phpfunction:`checkdnsrr` PHP function will be used to check the validity of the MX *or* the A *or* the AAAA record of the host of the given email. + .. _egulias/email-validator: https://packagist.org/packages/egulias/email-validator From 8cd63d06953434b2ba9186f800ab4905b1ac6bca Mon Sep 17 00:00:00 2001 From: tamirvs Date: Sat, 18 Jan 2014 22:46:03 +0200 Subject: [PATCH 040/835] Added feature doc for named encoders --- book/security.rst | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/book/security.rst b/book/security.rst index fa9958dca1e..48112ff4325 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1466,6 +1466,79 @@ it as base64. In other words, the password has been greatly obfuscated so that the hashed password can't be decoded (i.e. you can't determine the password from the hashed password). +Named encoders +.............. + +.. versionadded:: 2.5 + Named encoders were introduced in Symfony 2.5 + +Another option is to set the encoder dynamically on an instance basis. +In the previous example, you've set the ``sha512`` algorithm for ``Acme\UserBundle\Entity\User``. +This may be secure enough for a regular user, but what if you want your admins to have +a stronger algorithm? Let's say ``bcrypt``. This can be done with named encoders: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + encoders: + harsh: + algorithm: bcrypt + cost: 15 + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'encoders' => array( + 'harsh' => array( + 'algorithm' => 'bcrypt', + 'cost' => '15' + ), + ), + )); + +Now you've created an encoder named ``harsh``. In order for a ``User`` instance to use it, +It must implement ``EncoderAwareInterface`` and have a method ``getEncoderName`` which returns the +name of the encoder to use:: + + // src/Acme/UserBundle/Entity/User.php + namespace Acme\UserBundle\Entity; + + use Symfony\Component\Security\Core\User\UserInterface; + use Symfony\Component\Security\Core\Encoder\EncoderAwareInterface; + + class User implements UserInterface, EncoderAwareInterface + { + public function getEncoderName() + { + if ($this->isAdmin()) { + return 'harsh'; + } + + return null; // use the default encoder + } + } + Determining the Hashed Password ............................... From b6abafbf9d1a2e3bc76417e7f7886cd6a98f5dc9 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 20 Jan 2014 21:11:15 -0600 Subject: [PATCH 041/835] [#3446] Minor tweaks as suggested by @xabbuh --- cookbook/configuration/apache_router.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cookbook/configuration/apache_router.rst b/cookbook/configuration/apache_router.rst index 4eca8afe3b3..c0d0fc9578d 100644 --- a/cookbook/configuration/apache_router.rst +++ b/cookbook/configuration/apache_router.rst @@ -7,11 +7,12 @@ How to use the Apache Router Symfony2, while fast out of the box, also provides various ways to increase that speed with a little bit of tweaking. One of these ways is by letting Apache handle routes directly, rather than using Symfony2 for this task. -.. note:: +.. caution:: - Apache router was deprecated in Symfony 2.5 and will be removed in Symfony 3.0. - Since the PHP implementation of the Router was improved, performance gains were no longer - significant (while it's very hard to replicate the same behaviour). + Apache router was deprecated in Symfony 2.5 and will be removed in Symfony + 3.0. Since the PHP implementation of the Router was improved, performance + gains were no longer significant (while it's very hard to replicate the + same behavior). Change Router Configuration Parameters -------------------------------------- From 2fbf17c69cfbfc3afcfb968b77fbfdc363c1fb47 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 2 Feb 2014 21:16:23 -0600 Subject: [PATCH 042/835] [#3491] Moving the new named algorithms into their own cookbook entry and making some minor tweaks --- book/security.rst | 74 +----------------- cookbook/map.rst.inc | 1 + cookbook/security/index.rst | 1 + cookbook/security/named_encoders.rst | 111 +++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 71 deletions(-) create mode 100644 cookbook/security/named_encoders.rst diff --git a/book/security.rst b/book/security.rst index 7e40fe7c3be..391856bb3ae 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1434,78 +1434,10 @@ or via some online tool. Supported algorithms for this method depend on your PHP version. A full list is available by calling the PHP function :phpfunction:`hash_algos`. -Named encoders -.............. - -.. versionadded:: 2.5 - Named encoders were introduced in Symfony 2.5 - -Another option is to set the encoder dynamically on an instance basis. -In the previous example, you've set the ``sha512`` algorithm for ``Acme\UserBundle\Entity\User``. -This may be secure enough for a regular user, but what if you want your admins to have -a stronger algorithm? Let's say ``bcrypt``. This can be done with named encoders: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - # ... - encoders: - harsh: - algorithm: bcrypt - cost: 15 - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - // ... - 'encoders' => array( - 'harsh' => array( - 'algorithm' => 'bcrypt', - 'cost' => '15' - ), - ), - )); - -Now you've created an encoder named ``harsh``. In order for a ``User`` instance to use it, -It must implement ``EncoderAwareInterface`` and have a method ``getEncoderName`` which returns the -name of the encoder to use:: - - // src/Acme/UserBundle/Entity/User.php - namespace Acme\UserBundle\Entity; - - use Symfony\Component\Security\Core\User\UserInterface; - use Symfony\Component\Security\Core\Encoder\EncoderAwareInterface; +.. tip:: - class User implements UserInterface, EncoderAwareInterface - { - public function getEncoderName() - { - if ($this->isAdmin()) { - return 'harsh'; - } - - return null; // use the default encoder - } - } + It's also possible to use different hashing algorithms on a user-by-user + basis. See :doc:`/cookbook/security/named-encoders` for more details. Determining the Hashed Password ............................... diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 6986e4b1ca0..380498f59e0 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -144,6 +144,7 @@ * :doc:`/cookbook/security/custom_authentication_provider` * :doc:`/cookbook/security/target_path` * :doc:`/cookbook/security/csrf_in_login_form` + * :doc:`/cookbook/security/named_encoders` * **Serializer** diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 63bd29520b3..59f9af787be 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -20,3 +20,4 @@ Security custom_authentication_provider target_path csrf_in_login_form + named_encoders diff --git a/cookbook/security/named_encoders.rst b/cookbook/security/named_encoders.rst new file mode 100644 index 00000000000..cf881563363 --- /dev/null +++ b/cookbook/security/named_encoders.rst @@ -0,0 +1,111 @@ +.. index:: + single: Security; Named Encoders + +How to Choose the Password Encoder Algorithm Dynamically +======================================================== + +.. versionadded:: 2.5 + Named encoders were introduced in Symfony 2.5 + +Usually, the same password encoder is used for all users by configuring it +to apply to all instances of a specific class: + + # app/config/security.yml + security: + # ... + encoders: + Symfony\Component\Security\Core\User\User: sha512 + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'encoders' => array( + 'Symfony\Component\Security\Core\User\User' => array( + 'algorithm' => 'sha512', + ), + ), + )); + +Another option is to use a "named" encoder, and then select which encoder +you want to use dynamically. + +In the previous example, you've set the ``sha512`` algorithm for ``Acme\UserBundle\Entity\User``. +This may be secure enough for a regular user, but what if you want your admins +to have a stronger algorithm, for example ``bcrypt``. This can be done with +named encoders: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + encoders: + harsh: + algorithm: bcrypt + cost: 15 + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'encoders' => array( + 'harsh' => array( + 'algorithm' => 'bcrypt', + 'cost' => '15' + ), + ), + )); + +This creates an encoder named ``harsh``. In order for a ``User`` instance +to use it, the class must implement +:class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderAwareInterface`. +The interface requires one method - ``getEncoderName`` - which should reutrn +the name of the encoder to use:: + + // src/Acme/UserBundle/Entity/User.php + namespace Acme\UserBundle\Entity; + + use Symfony\Component\Security\Core\User\UserInterface; + use Symfony\Component\Security\Core\Encoder\EncoderAwareInterface; + + class User implements UserInterface, EncoderAwareInterface + { + public function getEncoderName() + { + if ($this->isAdmin()) { + return 'harsh'; + } + + return null; // use the default encoder + } + } From 34e69de95ed997846e9c755144105fcc2ff48519 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 3 Feb 2014 21:33:12 -0600 Subject: [PATCH 043/835] [#3533] Lots of nice changes thanks to @xabbuh --- cookbook/security/named_encoders.rst | 32 +++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/cookbook/security/named_encoders.rst b/cookbook/security/named_encoders.rst index cf881563363..fe54738f670 100644 --- a/cookbook/security/named_encoders.rst +++ b/cookbook/security/named_encoders.rst @@ -5,7 +5,7 @@ How to Choose the Password Encoder Algorithm Dynamically ======================================================== .. versionadded:: 2.5 - Named encoders were introduced in Symfony 2.5 + Named encoders were introduced in Symfony 2.5. Usually, the same password encoder is used for all users by configuring it to apply to all instances of a specific class: @@ -19,12 +19,20 @@ to apply to all instances of a specific class: .. code-block:: xml - - - - + + + + + + + .. code-block:: php @@ -33,12 +41,12 @@ to apply to all instances of a specific class: // ... 'encoders' => array( 'Symfony\Component\Security\Core\User\User' => array( - 'algorithm' => 'sha512', + 'algorithm' => 'sha512', ), ), )); -Another option is to use a "named" encoder, and then select which encoder +Another option is to use a "named" encoder and then select which encoder you want to use dynamically. In the previous example, you've set the ``sha512`` algorithm for ``Acme\UserBundle\Entity\User``. @@ -63,7 +71,11 @@ named encoders: + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:srv="http://symfony.com/schema/dic/services" + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd" + > From 2ca9bd5e1c6ad44114f6d400853c119cb2b418af Mon Sep 17 00:00:00 2001 From: jochenvdv Date: Tue, 4 Feb 2014 13:53:20 +0100 Subject: [PATCH 044/835] Describe retrieval of StopwatchEvent --- components/stopwatch.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/stopwatch.rst b/components/stopwatch.rst index 2598bdb3cd0..034a6d3e928 100644 --- a/components/stopwatch.rst +++ b/components/stopwatch.rst @@ -31,6 +31,8 @@ microtime by yourself. Instead, use the simple // ... some code goes here $event = $stopwatch->stop('eventName'); +The :class:`Symfony\\Component\\Stopwatch\StopwatchEvent` object can be retrieved from :method:`Symfony\\Component\\Stopwatch\\Stopwatch::start`, :method:`Symfony\\Component\\Stopwatch\\Stopwatch::stop`, :method:`Symfony\\Component\\Stopwatch\\Stopwatch::lap` and :method:`Symfony\\Component\\Stopwatch\\Stopwatch::getEvent` + You can also provide a category name to an event:: $stopwatch->start('eventName', 'categoryName'); From 64602c83849b29088bfbf60b46a8fb802bcc9860 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 5 Feb 2014 17:26:24 +0100 Subject: [PATCH 045/835] fix referenced documents names --- book/security.rst | 2 +- reference/forms/types/collection.rst | 2 +- reference/forms/types/options/checkbox_empty_data.rst.inc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/book/security.rst b/book/security.rst index 12a98e7bd1f..758bd7b3878 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1437,7 +1437,7 @@ is available by calling the PHP function :phpfunction:`hash_algos`. .. tip:: It's also possible to use different hashing algorithms on a user-by-user - basis. See :doc:`/cookbook/security/named-encoders` for more details. + basis. See :doc:`/cookbook/security/named_encoders` for more details. Determining the Hashed Password ............................... diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index 76f91580dc8..f918c6cbf80 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -379,4 +379,4 @@ error_bubbling .. include:: /reference/forms/types/options/mapped.rst.inc -.. include:: /reference/forms/type/options/cascade_validation.rst.inc +.. include:: /reference/forms/types/options/cascade_validation.rst.inc diff --git a/reference/forms/types/options/checkbox_empty_data.rst.inc b/reference/forms/types/options/checkbox_empty_data.rst.inc index 8143ad364ce..d7a09ba3b7b 100644 --- a/reference/forms/types/options/checkbox_empty_data.rst.inc +++ b/reference/forms/types/options/checkbox_empty_data.rst.inc @@ -6,4 +6,4 @@ empty_data This option determines what value the field will return when the ``empty_value`` choice is selected. In checkbox, the value of ``empty_data`` is overriden by the value returned by -the data transformer (see :doc:`/cookbook/form/data_transformers.rst`). +the data transformer (see :doc:`/cookbook/form/data_transformers`). From 7e3c0e3af0bc730a4ccc732ee6c46fd9d82a99a9 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 5 Feb 2014 17:27:38 +0100 Subject: [PATCH 046/835] readd configuration block --- cookbook/security/named_encoders.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cookbook/security/named_encoders.rst b/cookbook/security/named_encoders.rst index fe54738f670..cf9ea3770c2 100644 --- a/cookbook/security/named_encoders.rst +++ b/cookbook/security/named_encoders.rst @@ -10,6 +10,10 @@ How to Choose the Password Encoder Algorithm Dynamically Usually, the same password encoder is used for all users by configuring it to apply to all instances of a specific class: +.. configuration-block:: + + .. code-block:: yaml + # app/config/security.yml security: # ... @@ -117,7 +121,7 @@ the name of the encoder to use:: if ($this->isAdmin()) { return 'harsh'; } - + return null; // use the default encoder } } From 46e0b52f66cb2bebbf769c1590c27108a1e257a0 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 5 Feb 2014 17:28:15 +0100 Subject: [PATCH 047/835] remove empty_data from the list of inherited options, it's documented in the overriden options section --- reference/forms/types/checkbox.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/reference/forms/types/checkbox.rst b/reference/forms/types/checkbox.rst index aeefb915ccf..e39c393dae7 100644 --- a/reference/forms/types/checkbox.rst +++ b/reference/forms/types/checkbox.rst @@ -60,8 +60,6 @@ These options inherit from the :doc:`form ` type: .. include:: /reference/forms/types/options/data.rst.inc -.. include:: /reference/forms/types/options/empty_data.rst.inc - .. include:: /reference/forms/types/options/required.rst.inc .. include:: /reference/forms/types/options/label.rst.inc From eefd0ab58faad4bb0284b3859ef5743e0df7b4eb Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 25 Feb 2014 23:24:25 +0100 Subject: [PATCH 048/835] add versionadded directive for multiple option --- reference/forms/types/file.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index fa79a02e18e..ed148358262 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -86,6 +86,9 @@ Field Options multiple ~~~~~~~~ +.. versionadded:: 2.5 + The ``multiple`` option was introduced in Symfony 2.5. + **type**: ``Boolean`` **default**: ``false`` When set to true, the user will be able to upload multiple files at the same time. From f4afaab4abf5f191e9d2fdd415eccf7c9be75bb8 Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Fri, 28 Feb 2014 15:21:23 -0500 Subject: [PATCH 049/835] Documentation for the new Uuid constraint --- reference/constraints/Uuid.rst | 126 +++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 reference/constraints/Uuid.rst diff --git a/reference/constraints/Uuid.rst b/reference/constraints/Uuid.rst new file mode 100644 index 00000000000..1d315bdd2a8 --- /dev/null +++ b/reference/constraints/Uuid.rst @@ -0,0 +1,126 @@ +Uuid +== + +.. versionadded:: 2.5 + The Uuid constraint was added in Symfony 2.5. + +Validates that a value is a valid `Universally unique identifier (UUID)`_ per `RFC 4122`_. +By default, this will validate the format according to the RFC's guidelines, but this can +be relaxed to accept non-standard UUIDs that other systems (like PostgreSQL) accept. +UUID versions can also be restricted using a whitelist. + ++----------------+---------------------------------------------------------------------+ +| Applies to | :ref:`property or method ` | ++----------------+---------------------------------------------------------------------+ +| Options | - `message`_ | +| | - `strict`_ | +| | - `versions`_ | ++----------------+---------------------------------------------------------------------+ +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Uuid` | ++----------------+---------------------------------------------------------------------+ +| Validator | :class:`Symfony\\Component\\Validator\\Constraints\\UuidValidator` | ++----------------+---------------------------------------------------------------------+ + +Basic Usage +----------- + +.. configuration-block:: + + .. code-block:: yaml + + # src/UploadsBundle/Resources/config/validation.yml + Acme\UploadsBundle\Entity\File: + properties: + identifier: + - Uuid: ~ + + .. code-block:: php-annotations + + // src/Acme/UploadsBundle/Entity/File.php + namespace Acme\UploadsBundle\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class File + { + /** + * @Assert\Uuid + */ + protected $identifier; + } + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // src/Acme/UploadsBundle/Entity/File.php + namespace Acme\UploadsBundle\Entity; + + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Validator\Constraints as Assert; + + class File + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('identifier', new Assert\Uuid()); + } + } + + +Options +------- + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This is not a valid UUID.`` + +This message is shown if the string is not a valid UUID. + +strict +~~~~~~ + +**type**: ``boolean`` **default**: ``true`` + +If this option is set to ``true`` the constraint will check if the UUID is formatted per the +RFC's input format rules: ``216fff40-98d9-11e3-a5e2-0800200c9a66``. Setting this to ``false`` +will allow alternate input formats like:: + + 216f-ff40-98d9-11e3-a5e2-0800-200c-9a66 + {216fff40-98d9-11e3-a5e2-0800200c9a66} + 216fff4098d911e3a5e20800200c9a66 + +versions +~~~~~~~~ + +**type**: ``int[]`` **default**: ``[1,2,3,4,5]`` + +This option can be used to only allow specific `UUID versions`_. Valid versions are 1 - 5. +The following PHP constants can also be used: + +* ``Uuid::V1_MAC`` +* ``Uuid::V2_DCE`` +* ``Uuid::V3_MD5`` +* ``Uuid::V4_RANDOM`` +* ``Uuid::V5_SHA1`` + +All five versions are allowed by default. + +.. _`Universally unique identifier (UUID)`: http://en.wikipedia.org/wiki/Universally_unique_identifier +.. _`RFC 4122`: http://tools.ietf.org/html/rfc4122 +.. _`UUID versions`: http://en.wikipedia.org/wiki/Universally_unique_identifier#Variants_and_versions + From 3ea91372096bd7486fa04e85ad706e6302f0a506 Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Fri, 28 Feb 2014 15:22:50 -0500 Subject: [PATCH 050/835] List the UUID constraint in the reference section --- reference/constraints.rst | 1 + reference/constraints/map.rst.inc | 1 + 2 files changed, 2 insertions(+) diff --git a/reference/constraints.rst b/reference/constraints.rst index 0b209bbf012..ac8ef5ae33c 100644 --- a/reference/constraints.rst +++ b/reference/constraints.rst @@ -18,6 +18,7 @@ Validation Constraints Reference constraints/Url constraints/Regex constraints/Ip + constraints/Uuid constraints/Range diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 237329866d4..686ad22bca5 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -20,6 +20,7 @@ String Constraints * :doc:`Url ` * :doc:`Regex ` * :doc:`Ip ` +* :doc:`Uuid` Number Constraints ~~~~~~~~~~~~~~~~~~ From 6f6c03fe6c28994909f7eafebbaaa46f1de07fa2 Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Fri, 28 Feb 2014 15:43:14 -0500 Subject: [PATCH 051/835] Fix formatting issues --- reference/constraints/Uuid.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/reference/constraints/Uuid.rst b/reference/constraints/Uuid.rst index 1d315bdd2a8..4db4fc8ceab 100644 --- a/reference/constraints/Uuid.rst +++ b/reference/constraints/Uuid.rst @@ -1,8 +1,8 @@ Uuid -== +==== .. versionadded:: 2.5 - The Uuid constraint was added in Symfony 2.5. + The Uuid constraint was introduced in Symfony 2.5. Validates that a value is a valid `Universally unique identifier (UUID)`_ per `RFC 4122`_. By default, this will validate the format according to the RFC's guidelines, but this can @@ -97,12 +97,12 @@ strict **type**: ``boolean`` **default**: ``true`` If this option is set to ``true`` the constraint will check if the UUID is formatted per the -RFC's input format rules: ``216fff40-98d9-11e3-a5e2-0800200c9a66``. Setting this to ``false`` -will allow alternate input formats like:: +RFC's input format rules: ``216fff40-98d9-11e3-a5e2-0800200c9a66``. Setting this to ``false`` +will allow alternate input formats like: - 216f-ff40-98d9-11e3-a5e2-0800-200c-9a66 - {216fff40-98d9-11e3-a5e2-0800200c9a66} - 216fff4098d911e3a5e20800200c9a66 +* ``216f-ff40-98d9-11e3-a5e2-0800-200c-9a66`` +* ``{216fff40-98d9-11e3-a5e2-0800200c9a66}`` +* ``216fff4098d911e3a5e20800200c9a66`` versions ~~~~~~~~ @@ -123,4 +123,3 @@ All five versions are allowed by default. .. _`Universally unique identifier (UUID)`: http://en.wikipedia.org/wiki/Universally_unique_identifier .. _`RFC 4122`: http://tools.ietf.org/html/rfc4122 .. _`UUID versions`: http://en.wikipedia.org/wiki/Universally_unique_identifier#Variants_and_versions - From bd9cd2694ee562f858b969d4382780f992faadc8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 3 Mar 2014 12:24:53 +0100 Subject: [PATCH 052/835] added docs for the new Table console helper --- components/console/helpers/index.rst | 1 + components/console/helpers/map.rst.inc | 1 + components/console/helpers/table.rst | 144 +++++++++++++++++++++ components/console/helpers/tablehelper.rst | 7 + 4 files changed, 153 insertions(+) create mode 100644 components/console/helpers/table.rst diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index 1c95bc47057..417ec6caf12 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -10,6 +10,7 @@ The Console Helpers dialoghelper formatterhelper progresshelper + table tablehelper The Console component comes with some useful helpers. These helpers contain diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index 60b32c03975..cb046bfb592 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,4 +1,5 @@ * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` * :doc:`/components/console/helpers/progresshelper` +* :doc:`/components/console/helpers/table` * :doc:`/components/console/helpers/tablehelper` diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst new file mode 100644 index 00000000000..6fe6848a8e1 --- /dev/null +++ b/components/console/helpers/table.rst @@ -0,0 +1,144 @@ +.. index:: + single: Console Helpers; Table + +Table +===== + +.. versionadded:: 2.5 + The ``Table`` class was introduced in Symfony 2.5 as a replacement for the + :doc:`Table Helper `. + +When building a console application it may be useful to display tabular data: + +.. code-block:: text + + +---------------+--------------------------+------------------+ + | ISBN | Title | Author | + +---------------+--------------------------+------------------+ + | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + | 80-902734-1-6 | And Then There Were None | Agatha Christie | + +---------------+--------------------------+------------------+ + +To display a table, use :class:`Symfony\\Component\\Console\\Helper\\Table`, +set the headers, set the rows and then render the table:: + + use Symfony\Component\Helper\Table; + + $table = new Table($output); + $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(); + +You can add a table separator anywhere in the output by passing an instance of +:class:`Symfony\\Component\\Console\\Helper\\TableSeparator` as a row:: + + use Symfony\Component\Helper\TableSeparator; + + $table->setRows(array( + array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), + array('9971-5-0210-0', 'A Tale of Two Cities', 'Charles Dickens'), + new TableSeparator(), + 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'), + )); + +.. code-block:: text + + +---------------+--------------------------+------------------+ + | ISBN | Title | Author | + +---------------+--------------------------+------------------+ + | 99921-58-10-7 | Divine Comedy | Dante Alighieri | + | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens | + +---------------+--------------------------+------------------+ + | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien | + | 80-902734-1-6 | And Then There Were None | Agatha Christie | + +---------------+--------------------------+------------------+ + +The table style can be changed to any built-in styles via +:method:`Symfony\\Component\\Console\\Helper\\Table::setStyle`:: + + // same as calling nothing + $table->setStyle('default'); + + // changes the default style to compact + $table->setStyle('compact'); + $table->render(); + +This code results in: + +.. code-block:: text + + ISBN Title Author + 99921-58-10-7 Divine Comedy Dante Alighieri + 9971-5-0210-0 A Tale of Two Cities Charles Dickens + 960-425-059-0 The Lord of the Rings J. R. R. Tolkien + 80-902734-1-6 And Then There Were None Agatha Christie + +You can also set the style to ``borderless``:: + + $table->setStyle('borderless'); + $table->render(); + +which outputs: + +.. code-block:: text + + =============== ========================== ================== + ISBN Title Author + =============== ========================== ================== + 99921-58-10-7 Divine Comedy Dante Alighieri + 9971-5-0210-0 A Tale of Two Cities Charles Dickens + 960-425-059-0 The Lord of the Rings J. R. R. Tolkien + 80-902734-1-6 And Then There Were None Agatha Christie + =============== ========================== ================== + +If the built-in styles do not fit your need, define your own:: + +.. code-block:: php + + use Symfony\Component\Helper\TableStyle; + + // by default, this is based on the default style + $style = new TableStyle(); + + // customize the style + $style + ->setHorizontalBorderChar('|') + ->setVerticalBorderChar('-') + ->setCrossingChar(' ') + ; + + // use the style for this table + $table->setStyle($style); + +Here is a full list of things you can customize: + +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setPaddingChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setHorizontalBorderChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setVerticalBorderChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setCrossingChar` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setCellHeaderFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setCellRowFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setBorderFormat` +* :method:`Symfony\\Component\\Console\\Helper\\TableStyle::setPadType` + +.. tip:: + + You can also register a style globally:: + + // register the style under the colorful name + Table::setStyleDefinition('colorful', $style); + + // use it for a table + $table->setStyle('colorful'); + + This method can also be used to override a built-in style. diff --git a/components/console/helpers/tablehelper.rst b/components/console/helpers/tablehelper.rst index 4bc3323ebe6..e42b151b845 100644 --- a/components/console/helpers/tablehelper.rst +++ b/components/console/helpers/tablehelper.rst @@ -7,6 +7,13 @@ Table Helper .. versionadded:: 2.3 The ``table`` helper was added in Symfony 2.3. +.. caution:: + + The Table Helper was deprecated in Symfony 2.5 and will be removed in + Symfony 3.0. You should now use the + :doc:`Table ` class instead which is + more powerful. + When building a console application it may be useful to display tabular data: .. image:: /images/components/console/table.png From 9e2727d628be64d5a1d8a730d2c3c7be9608cf34 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 3 Mar 2014 10:38:50 +0100 Subject: [PATCH 053/835] added documentation for the new Symfony 2.5 progress bar --- components/console/helpers/index.rst | 1 + components/console/helpers/map.rst.inc | 1 + components/console/helpers/progressbar.rst | 322 ++++++++++++++++++ components/console/helpers/progresshelper.rst | 11 +- images/components/console/progressbar.gif | Bin 0 -> 29016 bytes 5 files changed, 333 insertions(+), 2 deletions(-) create mode 100644 components/console/helpers/progressbar.rst create mode 100644 images/components/console/progressbar.gif diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index 1c95bc47057..35e9e56d4e3 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -9,6 +9,7 @@ The Console Helpers dialoghelper formatterhelper + progressbar progresshelper tablehelper diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index 60b32c03975..034af6fd43b 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,4 +1,5 @@ * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` +* :doc:`/components/console/helpers/progressbar` * :doc:`/components/console/helpers/progresshelper` * :doc:`/components/console/helpers/tablehelper` diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst new file mode 100644 index 00000000000..0cd119a0a9b --- /dev/null +++ b/components/console/helpers/progressbar.rst @@ -0,0 +1,322 @@ +.. index:: + single: Console Helpers; Progress Bar + +Progress Bar +============ + +.. versionadded:: 2.5 + The Progress Bar feature was introduced in Symfony 2.5 as a replacement for + the :doc:`Progress Helper `. + +When executing longer-running commands, it may be helpful to show progress +information, which updates as your command runs: + +.. image:: /images/components/console/progressbar.gif + +To display progress details, use the +:class:`Symfony\\Component\\Console\\Helper\\ProgressBar`, pass it a total +number of units, and advance the progress as the command executes:: + + use Symfony\Component\Console\Helper\ProgressBar; + + // create a new progress bar (50 units) + $progress = new ProgressBar($output, 50); + + // start and displays the progress bar + $progress->start(); + + $i = 0; + while ($i++ < 50) { + // ... do some work + + // advance the progress bar 1 unit + $progress->advance(); + + // you can also advance the progress bar by more than 1 unit + // $progress->advance(3); + } + + // ensure that the progress bar is at 100% + $progress->finish(); + +Instead of advancing the bar by a number of steps (with the +:method:`Symfony\\Component\\Console\\Helper\\ProgressBar::advance` method), +you can also set the current progress by calling the +:method:`Symfony\\Component\\Console\\Helper\\ProgressBar::setCurrent` method. + +.. caution:: + + The progress bar only works if your platform supports ANSI codes; on other + platforms, no output is generated. + +If you don't know the number of steps in advance, just omit the steps argument +when creating the :class:`Symfony\\Component\\Console\\Helper\\ProgressBar` +instance:: + + $progress = new ProgressBar($output); + +The progress will then be displayed as a throbber:: + +.. code-block:: text + + # no max steps (displays it like a throbber) + 0 [>---------------------------] + 5 [----->----------------------] + 5 [============================] + + # max steps defined + 0/3 [>---------------------------] 0% + 1/3 [=========>------------------] 33% + 3/3 [============================] 100% + +Whenever your task is finished, don't forget to call +:method:`Symfony\\Component\\Console\\Helper\\ProgressBar::finish` to ensure +that the progress bar display is refreshed with a 100% completion. + +.. note:: + + If you want to output something while the progress bar is running, + call :method:`Symfony\\Component\\Console\\Helper\\ProgressBar::clear` first. + After you're done, call + :method:`Symfony\\Component\\Console\\Helper\\ProgressBar::display` + to show the progress bar again. + +Customizing the Progress Bar +---------------------------- + +Built-in Formats +~~~~~~~~~~~~~~~~ + +By default, the information rendered on a progress bar depends on the current +level of verbosity of the ``OutputInterface`` instance: + +.. code-block:: text + + # OutputInterface::VERBOSITY_NORMAL (CLI with no verbosity flag) + 0/3 [>---------------------------] 0% + 1/3 [=========>------------------] 33% + 3/3 [============================] 100% + + # OutputInterface::VERBOSITY_VERBOSE (-v) + 0/3 [>---------------------------] 0% 1 sec + 1/3 [=========>------------------] 33% 1 sec + 3/3 [============================] 100% 1 sec + + # OutputInterface::VERBOSITY_VERY_VERBOSE (-vv) + 0/3 [>---------------------------] 0% 1 sec + 1/3 [=========>------------------] 33% 1 sec + 3/3 [============================] 100% 1 sec + + # OutputInterface::VERBOSITY_DEBUG (-vvv) + 0/3 [>---------------------------] 0% 1 sec/1 sec 1.0 MB + 1/3 [=========>------------------] 33% 1 sec/1 sec 1.0 MB + 3/3 [============================] 100% 1 sec/1 sec 1.0 MB + +.. note:: + + If you call a command with the quiet flag (``-q``), the progress bar won't + be displayed. + +Instead of relying on the verbosity mode of the current command, you can also +force a format via ``setFormat()``:: + + $bar->setFormat('verbose'); + +The built-in formats are the following: + +* ``normal`` +* ``verbose`` +* ``very_verbose`` +* ``debug`` + +If you don't set the number of steps for your progress bar, use the ``_nomax`` +variants: + +* ``normal_nomax`` +* ``verbose_nomax`` +* ``very_verbose_nomax`` +* ``debug_nomax`` + +Custom Formats +~~~~~~~~~~~~~~ + +Instead of using the built-in formats, you can also set your own:: + + $bar->setFormat('%bar%'); + +This sets the format to only display the progress bar itself: + +.. code-block:: text + + >--------------------------- + =========>------------------ + ============================ + +A progress bar format is a string that contains specific placeholders (a name +enclosed with the ``%`` character); the placeholders are replaced based on the +current progress of the bar. Here is a list of the built-in placeholders: + +* ``current``: The current step; +* ``max``: The maximum number of steps (or 0 if no max is defined); +* ``bar``: The bar itself; +* ``percent``: The percentage of completion (not available if no max is defined); +* ``elapsed``: The time elapsed since the start of the progress bar; +* ``remaining``: The remaining time to complete the task (not available if no max is defined); +* ``estimated``: The estimated time to complete the task (not available if no max is defined); +* ``memory``: The current memory usage; +* ``message``: The current message attached to the progress bar. + +For instance, here is how you could set the format to be the same as the +``debug`` one:: + + $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s% %memory:6s%'); + +Notice the ``:6s`` part added to some placeholders? That's how you can tweak +the appearance of the bar (formatting and alignment). The part after the colon +(``:``) is used to set the ``sprintf`` format of the string. + +The ``message`` placeholder is a bit special as you must set the value +yourself:: + + $bar->setMessage('Task starts'); + $bar->start(); + + $bar->setMessage('Task in progress...'); + $bar->advance(); + + // ... + + $bar->setMessage('Task is finished'); + $bar->finish(); + +Instead of setting the format for a given instance of a progress bar, you can +also define global formats:: + + ProgressBar::setFormatDefinition('minimal', 'Progress: %percent%%'); + + $bar = new ProgressBar($output, 3); + $bar->setFormat('minimal'); + +This code defines a new ``minimal`` format that you can then use for your +progress bars: + +.. code-block:: text + + Progress: 0% + Progress: 33% + Progress: 100% + +.. tip:: + + It is almost always better to redefine built-in formats instead of creating + new ones as that allows the display to automatically vary based on the + verbosity flag of the command. + +When defining a new style that contains placeholders that are only available +when the maximum number of steps is known, you should create a ``_nomax`` +variant:: + + ProgressBar::setFormatDefinition('minimal', '%percent%% %remaining%'); + ProgressBar::setFormatDefinition('minimal_nomax', '%percent%%'); + + $bar = new ProgressBar($output); + $bar->setFormat('minimal'); + +When displaying the progress bar, the format will automatically be set to +``minimal_nomax`` if the bar does not have a maximum number of steps like in +the example above. + +.. tip:: + + A format can contain any valid ANSI codes and can also use the + Symfony-specific way to set colors:: + + ProgressBar::setFormatDefinition( + 'minimal', + '%percent%\033[32m%\033[0m %remaining%' + ); + +.. note:: + + A format can span more than one line; that's very useful when you want to + display more contextual information alongside the progress bar (see the + example at the beginning of this article). + +Bar Settings +~~~~~~~~~~~~ + +Amongst the placeholders, ``bar`` is a bit special as all the characters used +to display it can be customized:: + + // the finished part of the bar + $progress->setBarCharacter('='); + + // the unfinished part of the bar + $progress->setEmptyBarCharacter(' '); + + // the progress character + $progress->setProgressCharacter('|'); + + // the bar width + $progress->setBarWidth(50); + +.. caution:: + + For performance reasons, be careful if you set the total number of steps + to a high number. For example, if you're iterating over a large number of + items, consider setting the redraw frequency to a higher value by calling + :method:`Symfony\\Component\\Console\\Helper\\ProgressHelper::setRedrawFrequency`, + so it updates on only some iterations:: + + $progress->start($output, 50000); + + // update every 100 iterations + $progress->setRedrawFrequency(100); + + $i = 0; + while ($i++ < 50000) { + // ... do some work + + $progress->advance(); + } + +Custom Placeholders +~~~~~~~~~~~~~~~~~~~ + +If you want to display some information that depends on the progress bar +display that are not available in the list of built-in placeholders, you can +create your own. Let's see how you can create a ``remaining_steps`` placeholder +that displays the number of remaining steps:: + + ProgressBar::setPlaceholderFormatter( + '%remaining_steps%', + function (ProgressBar $bar, OutputInterface $output) { + return $bar->getMaxSteps() - $bar->getStep(); + } + ); + +Custom Messages +~~~~~~~~~~~~~~~ + +The ``%message%`` placeholder allows you to specify a custom message to be +displayed with the progress bar. But if you need more than one, just define +your own:: + + $bar->setMessage('Task starts'); + $bar->setMessage('', 'filename'); + $bar->start(); + + $bar->setMessage('Task is in progress...'); + while ($file = array_pop($files)) { + $bar->setMessage($filename, 'filename'); + $bar->advance(); + } + + $bar->setMessage('Task is finished'); + $bar->setMessage('', 'filename'); + $bar->finish(); + +For the ``filename`` to be part of the progress bar, just add the +``%filename%`` placeholder in your format:: + + $bar->setFormat(" %message%\n %step%/%max%\n Working on %filename%"); diff --git a/components/console/helpers/progresshelper.rst b/components/console/helpers/progresshelper.rst index 9ee76b694cb..3722b3fe213 100644 --- a/components/console/helpers/progresshelper.rst +++ b/components/console/helpers/progresshelper.rst @@ -10,6 +10,13 @@ Progress Helper .. versionadded:: 2.4 The ``clear`` method was added in Symfony 2.4. +.. caution:: + + The Progress Helper was deprecated in Symfony 2.5 and will be removed in + Symfony 3.0. You should now use the + :doc:`Progress Bar ` instead which + is more powerful. + When executing longer-running commands, it may be helpful to show progress information, which updates as your command runs: @@ -25,7 +32,7 @@ pass it a total number of units, and advance the progress as your command execut while ($i++ < 50) { // ... do some work - // advance the progress bar 1 unit + // advances the progress bar 1 unit $progress->advance(); } @@ -79,7 +86,7 @@ To see other available options, check the API documentation for $progress->start($output, 50000); - // update every 100 iterations + // updates every 100 iterations $progress->setRedrawFrequency(100); $i = 0; diff --git a/images/components/console/progressbar.gif b/images/components/console/progressbar.gif new file mode 100644 index 0000000000000000000000000000000000000000..6c80e6e897f670d0a53a1dc115bbe34a71fa1568 GIT binary patch literal 29016 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
ZeTPcbORBHACKVq0VF$~>Ex^4p#$>AvyurY zAfPS-h#l zDl)&xcvEusa7ra3^(hljLqIu(-HZX$&~F`-(=!ZoV)E-7@|!yVK~$KN01$GZ7E?@P z77wu4%BYc1(%QooN53-->^4jzgI$FgV6dw&jny^9DZi^QopoL8{u%(pfi_IzAcKv? z89*Cmn5~0=_yTLVt&Q0>l+8xr_4z;{<}VNjIxzqc?>r53VtTrOAVeMj#QQp#f7>t& z*1!OBaAx#BI%s~HF*(cv3Nj-M*8K2rU(KHq3>%BTnHirQ21+n&GR|a9j^zkg=u z1729kISGF|5L5^S)2R1x?J^1$>`Pujog|zrg}&OkjFqu`5yYoU*z9Z%3avjmEA54o zQYGd4Imcd)Tg?7L1q9OZn=ULkA$Mf=iA%zMrP zw1b~t_t(N@6Iq*&TUA@El`|hq`09z*D?6uOX|PwDkIpgQHo5IobZXbl?SMOn4}I#^ z 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 literal 0 HcmV?d00001 From 2d9647b9e953dbd85fc1d0873eaa163505b97e82 Mon Sep 17 00:00:00 2001 From: florianv Date: Mon, 3 Mar 2014 18:49:17 +0100 Subject: [PATCH 054/835] Added documentation for translation:debug --- book/translation.rst | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/book/translation.rst b/book/translation.rst index c27ac70c933..b8c0600b932 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -649,6 +649,39 @@ The translation of database content should be handled by Doctrine through the `Translatable Extension`_. For more information, see the documentation for that library. +Debugging Translations +---------------------- + +.. versionadded:: 2.5 + The ``translation:debug`` command was introduced in Symfony 2.5. + +When maintaining a bundle, you may use or remove the usage of a translation +message without updating all message catalogues. The ``translation:debug`` +command helps you finding these missing or unused translation messages for a +given locale. It shows you a table with the result when translating the +message in the given locale and the result when the fallback would be used. +On top of that, it also shows you when the translation is the same as the +fallback translation (this could indicate that the message was not correctly +translated). To inspect all messages in the ``en`` locale for the AcmeDemoBundle, run: + +.. code-block:: bash + + $ php app/console translation:debug en AcmeDemoBundle + +By default all domains are inspected, but it is possible to specify a single domain: + +.. code-block:: bash + + $ php app/console translation:debug en AcmeDemoBundle --domain=messages + +You can also display only the unused or only the missing messages, by using +the ``--only-unused`` or ``--only-missing`` switches: + +.. code-block:: bash + + $ php app/console translation:debug en AcmeDemoBundle --only-unused + $ php app/console translation:debug en AcmeDemoBundle --only-missing + Summary ------- From 6adf1603933dded0fe264c4a0f03907250a4f311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gildas=20Qu=C3=A9m=C3=A9ner?= Date: Tue, 31 Dec 2013 11:41:39 +0100 Subject: [PATCH 055/835] Added documentation about new requests formats configuration --- cookbook/request/mime_type.rst | 136 ++++++++++++++++++++------------- 1 file changed, 81 insertions(+), 55 deletions(-) diff --git a/cookbook/request/mime_type.rst b/cookbook/request/mime_type.rst index fb57352c189..6879407da8c 100644 --- a/cookbook/request/mime_type.rst +++ b/cookbook/request/mime_type.rst @@ -15,81 +15,107 @@ object. Internally, Symfony contains a map of the most common formats (e.g. easily be added. This document will show how you can add the ``jsonp`` format and corresponding MIME type. -Create a ``kernel.request`` Listener -------------------------------------- +.. versionadded:: 2.5 + The possibility to configure request formats was introduced in Symfony 2.5. -The key to defining a new MIME type is to create a class that will "listen" to -the ``kernel.request`` event dispatched by the Symfony kernel. The -``kernel.request`` event is dispatched early in Symfony's request handling -process and allows you to modify the request object. - -Create the following class, replacing the path with a path to a bundle in your -project:: - - // src/Acme/DemoBundle/RequestListener.php - namespace Acme\DemoBundle; - - use Symfony\Component\HttpKernel\HttpKernelInterface; - use Symfony\Component\HttpKernel\Event\GetResponseEvent; - - class RequestListener - { - public function onKernelRequest(GetResponseEvent $event) - { - $event->getRequest()->setFormat('jsonp', 'application/javascript'); - } - } - -Registering your Listener +Configure your New Format ------------------------- -As with any other listener, you need to add it in one of your configuration -files and register it as a listener by adding the ``kernel.event_listener`` tag: +The FrameworkBundle registers a subscriber that will add formats to incomming requests. + +All you have to do is to configure the ``jsonp`` format: .. configuration-block:: .. code-block:: yaml # app/config/config.yml - services: - acme.demobundle.listener.request: - class: Acme\DemoBundle\RequestListener - tags: - - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest } + framework: + request: + formats: + jsonp: 'application/javascript' .. code-block:: xml - + + - - - - - + xmlns:framework="http://symfony.com/schema/dic/symfony" + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/symfony + http://symfony.com/schema/dic/symfony/symfony-1.0.xsd" + > + + + + application/javascript + + + .. code-block:: php - # app/config/config.php - $definition = new Definition('Acme\DemoBundle\RequestListener'); - $definition->addTag('kernel.event_listener', array( - 'event' => 'kernel.request', - 'method' => 'onKernelRequest', + // app/config/config.php + $container->loadFromExtension('framework', array( + 'request' => array( + 'formats' => array( + 'jsonp' => 'application/javascript', + ), + ), )); - $container->setDefinition('acme.demobundle.listener.request', $definition); - -At this point, the ``acme.demobundle.listener.request`` service has been -configured and will be notified when the Symfony kernel dispatches the -``kernel.request`` event. .. tip:: - You can also register the listener in a configuration extension class (see - :ref:`service-container-extension-configuration` for more information). + You can also associate multiple mime types to a format, but please note that + the preferred one must be the first as it will be used as the content type: + + .. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + framework: + request: + formats: + csv: ['text/csv', 'text/plain'] + + .. code-block:: xml + + + + + + + + + text/csv + text/plain + + + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('framework', array( + 'request' => array( + 'formats' => array( + 'jsonp' => array( + 'text/csv', + 'text/plain', + ), + ), + ), + )); From 6c52b92a1eba95a7cedb62309d20ddb2c29bcbeb Mon Sep 17 00:00:00 2001 From: John Kary Date: Sat, 30 Nov 2013 00:16:42 -0600 Subject: [PATCH 056/835] [#3311] Use KernelTestCase instead of WebTestCase for tests needing only a Container --- cookbook/console/console_command.rst | 15 ++++++++++++--- cookbook/testing/doctrine.rst | 9 ++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index 6e3721a24ef..fc903b12650 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -183,14 +183,14 @@ instead of To be able to use the fully set up service container for your console tests you can extend your test from -:class:`Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase`:: +:class:`Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase`:: use Symfony\Component\Console\Tester\CommandTester; use Symfony\Bundle\FrameworkBundle\Console\Application; - use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Acme\DemoBundle\Command\GreetCommand; - class ListCommandTest extends WebTestCase + class ListCommandTest extends KernelTestCase { public function testExecute() { @@ -214,3 +214,12 @@ you can extend your test from // ... } } + +.. versionadded:: 2.5 + :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase` was + extracted from :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase` + in Symfony 2.5, where WebTestCase was made to inherit from KernelTestCase. + The difference being that WebTestCase makes available an instance of + :class:`Symfony\\Bundle\\FrameworkBundle\\Client` via `createClient()`, + while KernelTestCase makes available an instance of + :class:`Symfony\\Component\\HttpKernel\\KernelInterface` via `createKernel()`. diff --git a/cookbook/testing/doctrine.rst b/cookbook/testing/doctrine.rst index ef9467861a3..87779fcda41 100644 --- a/cookbook/testing/doctrine.rst +++ b/cookbook/testing/doctrine.rst @@ -17,15 +17,15 @@ Functional Testing ------------------ If you need to actually execute a query, you will need to boot the kernel -to get a valid connection. In this case, you'll extend the ``WebTestCase``, +to get a valid connection. In this case, you'll extend the ``KernelTestCase``, which makes all of this quite easy:: // src/Acme/StoreBundle/Tests/Entity/ProductRepositoryFunctionalTest.php namespace Acme\StoreBundle\Tests\Entity; - use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; - class ProductRepositoryFunctionalTest extends WebTestCase + class ProductRepositoryFunctionalTest extends KernelTestCase { /** * @var \Doctrine\ORM\EntityManager @@ -37,8 +37,7 @@ which makes all of this quite easy:: */ public function setUp() { - static::$kernel = static::createKernel(); - static::$kernel->boot(); + self::bootKernel(); $this->em = static::$kernel->getContainer() ->get('doctrine') ->getManager() From 153f97f768e256a97cf0017edfcb31ac038d6393 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Mon, 10 Mar 2014 18:02:10 +0100 Subject: [PATCH 057/835] Added 2.5 CHANGELOG --- changelog.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/changelog.rst b/changelog.rst index 6264bee4693..ca2995a01cc 100644 --- a/changelog.rst +++ b/changelog.rst @@ -21,12 +21,16 @@ New Documentation ~~~~~~~~~~~~~~~~~ - `d52f3f8 `_ #3454 [Security] Add host option (ghostika) +- `11e079b `_ #3446 [WCM] Documented deprecation of the apache router. (jakzal) +- `0a0bf4c `_ #3437 Add info about callback in options resolver (marekkalnik) +- `6db5f23 `_ #3426 New Feature: Change the Default Command in the Console component (danielcsgomes) - `6b3c424 `_ #3428 Translation - Added info about JsonFileLoader added in 2.4 (singles) Fixed Documentation ~~~~~~~~~~~~~~~~~~~ - `fb22fa0 `_ #3456 remove duplicate label (xabbuh) +- `a87fe18 `_ #3470 Fixed typo (danielcsgomes) - `c205bc6 `_ #3468 enclose YAML string with double quotes to fix syntax highlighting (xabbuh) - `89963cc `_ #3463 Fix typos in cookbook/testing/database (ifdattic) - `e0a52ec `_ #3460 remove confusing outdated note on interactive rebasing (xabbuh) @@ -50,6 +54,7 @@ Minor Documentation Changes - `f285d93 `_ #3451 some language tweaks (AE, third-person perspective) (xabbuh) - `b9bbe5d `_ #3499 Fix YAML syntax highlight + remove trailing whitespace (ifdattic) - `2b7e0f6 `_ #3497 Fix highlighting (WouterJ) +- `2746067 `_ #3472 Fixed `````versionadded````` inconsistencies in Symfony 2.5+ (danielcsgomes) - `a535ae0 `_ #3471 Fixed `````versionadded````` inconsistencies in Symfony 2.3 (danielcsgomes) - `f077a8e `_ #3465 change wording in versionadded example to be consistent with what we use... (xabbuh) - `f9f7548 `_ #3462 Replace ... with etc (ifdattic) @@ -76,6 +81,8 @@ New Documentation - `9676f2c `_ #3523 [Components][EventDispatcher] describe that the event name and the event dispatcher are passed to even... (xabbuh) - `5c367b4 `_ #3517 Fixed OptionsResolver component docs (WouterJ) - `527c8b6 `_ #3496 Added a section about using named assets (vmattila) +- `8ccfe85 `_ #3491 Added doc for named encoders (tamirvs) +- `46377b2 `_ #3486 Documenting createAccessDeniedException() method (klaussilveira) Fixed Documentation ~~~~~~~~~~~~~~~~~~~ @@ -86,6 +93,7 @@ Fixed Documentation - `de71a51 `_ #3551 [Cookbook][Dynamic Form Modification] Fix sample code (rybakit) - `143db2f `_ #3550 Update introduction.rst (taavit) - `384538b `_ #3549 Fixed createPropertyAccessorBuilder usage (antonbabenko) +- `642e776 `_ #3544 Fix build errors (xabbuh) - `d275302 `_ #3541 Update generic_event.rst (Lumbendil) - `819949c `_ #3537 Add missing variable assignment (colinodell) - `d7e8262 `_ #3535 fix form type name. (yositani2002) @@ -119,6 +127,7 @@ Minor Documentation Changes - `6a2a55b `_ #3579 Fix build errors (xabbuh) - `dce2e23 `_ #3532 Added tip for Entity Listeners (slavafomin) - `73adf8b `_ #3528 Clarify service parameters usages (WouterJ) +- `7e75b64 `_ #3533 Moving the new named algorithms into their own cookbook entry (weaverryan) - `f634600 `_ #3531 Remove horizontal scrolling in code block (ifdattic) - `9ba4fa7 `_ #3527 Changes to components domcrawler (ifdattic) - `8973c81 `_ #3526 Changes for Console component (ifdattic) From 7eb30516357f36b02e9f8c5c18c62f7f8ac62036 Mon Sep 17 00:00:00 2001 From: Rick van Laarhoven Date: Wed, 12 Mar 2014 17:31:36 +0100 Subject: [PATCH 058/835] Fixed syntax highlighting --- cookbook/security/voters_data_permission.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 0e5055261e1..0afab373a4b 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -54,7 +54,9 @@ Creating the Custom Voter ------------------------- The goal is to create a voter that checks if a user has access to view or -edit a particular object. Here's an example implementation:: +edit a particular object. Here's an example implementation + +.. code-block:: php // src/Acme/DemoBundle/Security/Authorization/Voter/PostVoter.php namespace Acme\DemoBundle\Security\Authorization\Voter; From 484b7b95f2f309df8f4c00afc2042ae8aaacb424 Mon Sep 17 00:00:00 2001 From: Rick van Laarhoven Date: Wed, 12 Mar 2014 17:42:11 +0100 Subject: [PATCH 059/835] Added : --- cookbook/security/voters_data_permission.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 0afab373a4b..e27a86669be 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -54,7 +54,7 @@ Creating the Custom Voter ------------------------- The goal is to create a voter that checks if a user has access to view or -edit a particular object. Here's an example implementation +edit a particular object. Here's an example implementation: .. code-block:: php From 38b2955966776d2836106bd1650e820b87e4b748 Mon Sep 17 00:00:00 2001 From: florianv Date: Thu, 13 Mar 2014 03:08:32 +0100 Subject: [PATCH 060/835] Improved the documentation with examples and images --- book/translation.rst | 134 ++++++++++++++++++++++++++-- images/book/translation/debug_1.png | Bin 0 -> 22689 bytes images/book/translation/debug_2.png | Bin 0 -> 22534 bytes images/book/translation/debug_3.png | Bin 0 -> 22381 bytes images/book/translation/debug_4.png | Bin 0 -> 22236 bytes 5 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 images/book/translation/debug_1.png create mode 100644 images/book/translation/debug_2.png create mode 100644 images/book/translation/debug_3.png create mode 100644 images/book/translation/debug_4.png diff --git a/book/translation.rst b/book/translation.rst index d3a6e010ff6..2356f239f6c 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -657,16 +657,140 @@ Debugging Translations When maintaining a bundle, you may use or remove the usage of a translation message without updating all message catalogues. The ``translation:debug`` -command helps you finding these missing or unused translation messages for a +command helps you to find these missing or unused translation messages for a given locale. It shows you a table with the result when translating the message in the given locale and the result when the fallback would be used. On top of that, it also shows you when the translation is the same as the fallback translation (this could indicate that the message was not correctly -translated). To inspect all messages in the ``en`` locale for the AcmeDemoBundle, run: +translated). + +Thanks to the messages extractors, the command will detect the translation +tag or filter usages in Twig templates: + +.. code-block:: jinja + + {% trans %}Symfony2 is great{% endtrans %} + + {{ 'Symfony2 is great'|trans }} + +It will also detect the following translator usages in PHP templates: + +.. code-block:: php + + $view['translator']->trans("Symfony2 is great"); + + $view['translator']->trans(‘Symfony2 is great’); + +Supposing your application default_locale is French ``fr`` and you have enabled +the translator in your configuration with English ``en`` as fallback locale. + +See :ref:`book-translation-configuration` and :ref:`book-translation-fallback` for details +about how to configure these. + +You are working on the AcmeDemoBundle and the translation file for the ``messages`` domain +in the ``fr`` locale contains: + +.. configuration-block:: + + .. code-block:: xml + + + + + + + + Symfony2 is great + J'aime Symfony2 + + + + + + .. code-block:: php + + // messages.fr.php + return array( + 'Symfony2 is great' => 'J\'aime Symfony2', + ); + + .. code-block:: yaml + + # messages.fr.yml + Symfony2 is great: J'aime Symfony2 + +and for the ``en`` locale: + +.. configuration-block:: + + .. code-block:: xml + + + + + + + + Symfony2 is great + Symfony2 is great + + + + + + .. code-block:: php + + // messages.en.php + return array( + 'Symfony2 is great' => 'Symfony2 is great', + ); + + .. code-block:: yaml + + # messages.en.yml + Symfony2 is great: Symfony2 is great + +To inspect all messages in the ``fr`` locale for the AcmeDemoBundle, run: .. code-block:: bash - $ php app/console translation:debug en AcmeDemoBundle + $ php app/console translation:debug fr AcmeDemoBundle + +You will get this output: + +.. image:: /images/book/translation/debug_1.png + :align: center + +It indicates the message with id ``Symfony2 is great`` is unused because it is translated +but we don’t use it in any template yet. + +Now, if you translate the message in one of your templates, you will get this output: + +.. image:: /images/book/translation/debug_2.png + :align: center + +The state is empty which means the message is translated in the ``fr`` locale and used in one or more templates. +Moreover, we see the translation is different than the ``en`` one. + +If you delete the message ``Symfony2 is great`` from your translation file for the ``fr`` locale +and run the command, you will get: + +.. image:: /images/book/translation/debug_3.png + :align: center + +The state indicates the message is missing because it is not translated in the ``fr`` locale +but it is still used in the template. +Moreover, we see the message in the ``fr`` locale equals to the message in the ``en`` locale. +This is a special case because the untranslated message id equals its translation in the ``en`` locale. + +If you copy the content of the translation file in the ``en`` locale, to the translation file +in the ``fr`` locale and run the command, you will get: + +.. image:: /images/book/translation/debug_4.png + :align: center + +We observe the translations of the message are identical in the ``fr`` and ``en`` locales +which means this message was probably copied from French to English or vice versa (which is the case). By default all domains are inspected, but it is possible to specify a single domain: @@ -674,8 +798,8 @@ By default all domains are inspected, but it is possible to specify a single dom $ php app/console translation:debug en AcmeDemoBundle --domain=messages -You can also display only the unused or only the missing messages, by using -the ``--only-unused`` or ``--only-missing`` switches: +When bundles have a lot of messages, it is useful to display only the unused +or only the missing messages, by using the ``--only-unused`` or ``--only-missing`` switches: .. code-block:: bash diff --git a/images/book/translation/debug_1.png b/images/book/translation/debug_1.png new file mode 100644 index 0000000000000000000000000000000000000000..8f175f4d7fffb76a5834a22b37580c5b8213f2d0 GIT binary patch literal 22689 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 literal 0 HcmV?d00001 diff --git a/images/book/translation/debug_2.png b/images/book/translation/debug_2.png new file mode 100644 index 0000000000000000000000000000000000000000..04a57fa41d45ef6bfa7ed755518b5ee1a2178a71 GIT binary patch literal 22534 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 D9@U|- literal 0 HcmV?d00001 diff --git a/images/book/translation/debug_3.png b/images/book/translation/debug_3.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed595e097b7b714b93be70d134cef71ac8b7c9a GIT binary patch literal 22381 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�D7=5gk)_W3TkefbFJrdOqir!n0*vrq9t+X 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 literal 0 HcmV?d00001 From b6ac4540c0b95f1bcdd47c26db126336cd0535f2 Mon Sep 17 00:00:00 2001 From: florianv Date: Thu, 13 Mar 2014 16:21:09 +0100 Subject: [PATCH 061/835] Fixed the issues --- book/translation.rst | 47 ++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/book/translation.rst b/book/translation.rst index 2356f239f6c..0c7e13e77ae 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -673,28 +673,38 @@ tag or filter usages in Twig templates: {{ 'Symfony2 is great'|trans }} + {{ 'Symfony2 is great'|transchoice(1) }} + + {% transchoice 1 %}Symfony2 is great{% endtranschoice %} + It will also detect the following translator usages in PHP templates: .. code-block:: php $view['translator']->trans("Symfony2 is great"); - $view['translator']->trans(‘Symfony2 is great’); + $view['translator']->trans('Symfony2 is great'); + +.. caution:: -Supposing your application default_locale is French ``fr`` and you have enabled -the translator in your configuration with English ``en`` as fallback locale. + The extractors are not able to inspect the messages translated outside templates which means + that translator usages in form labels or inside your controllers won't be detected. + Dynamic translations involving variables or expressions are not detected in templates, + which means this example won't be analyzed: -See :ref:`book-translation-configuration` and :ref:`book-translation-fallback` for details -about how to configure these. + .. code-block:: jinja + {% set message = 'Symfony2 is great' %} + {{ message|trans }} -You are working on the AcmeDemoBundle and the translation file for the ``messages`` domain -in the ``fr`` locale contains: +Suppose your application's default_locale is ``fr`` and you have configured ``en`` as the fallback locale +(see :ref:`book-translation-configuration` and :ref:`book-translation-fallback` for how to configure these). +And suppose you've already setup some translations for the ``fr`` locale inside an AcmeDemoBundle: .. configuration-block:: .. code-block:: xml - + @@ -709,14 +719,14 @@ in the ``fr`` locale contains: .. code-block:: php - // messages.fr.php + // src/Acme/AcmeDemoBundle/Resources/translations/messages.fr.php return array( 'Symfony2 is great' => 'J\'aime Symfony2', ); .. code-block:: yaml - # messages.fr.yml + # src/Acme/AcmeDemoBundle/Resources/translations/messages.fr.yml Symfony2 is great: J'aime Symfony2 and for the ``en`` locale: @@ -725,7 +735,7 @@ and for the ``en`` locale: .. code-block:: xml - + @@ -740,14 +750,14 @@ and for the ``en`` locale: .. code-block:: php - // messages.en.php + // src/Acme/AcmeDemoBundle/Resources/translations/messages.en.php return array( 'Symfony2 is great' => 'Symfony2 is great', ); .. code-block:: yaml - # messages.en.yml + # src/Acme/AcmeDemoBundle/Resources/translations/messages.en.yml Symfony2 is great: Symfony2 is great To inspect all messages in the ``fr`` locale for the AcmeDemoBundle, run: @@ -761,8 +771,8 @@ You will get this output: .. image:: /images/book/translation/debug_1.png :align: center -It indicates the message with id ``Symfony2 is great`` is unused because it is translated -but we don’t use it in any template yet. +It indicates that the message ``Symfony2 is great`` is unused because it is translated, +but you haven't used it anywhere yet. Now, if you translate the message in one of your templates, you will get this output: @@ -770,7 +780,6 @@ Now, if you translate the message in one of your templates, you will get this ou :align: center The state is empty which means the message is translated in the ``fr`` locale and used in one or more templates. -Moreover, we see the translation is different than the ``en`` one. If you delete the message ``Symfony2 is great`` from your translation file for the ``fr`` locale and run the command, you will get: @@ -780,7 +789,7 @@ and run the command, you will get: The state indicates the message is missing because it is not translated in the ``fr`` locale but it is still used in the template. -Moreover, we see the message in the ``fr`` locale equals to the message in the ``en`` locale. +Moreover, the message in the ``fr`` locale equals to the message in the ``en`` locale. This is a special case because the untranslated message id equals its translation in the ``en`` locale. If you copy the content of the translation file in the ``en`` locale, to the translation file @@ -789,8 +798,8 @@ in the ``fr`` locale and run the command, you will get: .. image:: /images/book/translation/debug_4.png :align: center -We observe the translations of the message are identical in the ``fr`` and ``en`` locales -which means this message was probably copied from French to English or vice versa (which is the case). +You can see that the translations of the message are identical in the ``fr`` and ``en`` locales +which means this message was probably copied from French to English and maybe you forgot to translate it. By default all domains are inspected, but it is possible to specify a single domain: From 37278d8b693408ab603a5070f73b67fb594b4b3e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 13 Mar 2014 22:40:18 +0100 Subject: [PATCH 062/835] remove unnecessary code block directive --- components/console/helpers/table.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst index 6fe6848a8e1..7729ecce6eb 100644 --- a/components/console/helpers/table.rst +++ b/components/console/helpers/table.rst @@ -103,8 +103,6 @@ which outputs: If the built-in styles do not fit your need, define your own:: -.. code-block:: php - use Symfony\Component\Helper\TableStyle; // by default, this is based on the default style From 17166bd4107bdf64a5a4c177751b9deb004c9d6a Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Wed, 12 Mar 2014 12:27:02 +0100 Subject: [PATCH 063/835] First shot of a documentation of the new PSR-4 class loader. --- components/class_loader/index.rst | 1 + components/class_loader/psr4_class_loader.rst | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 components/class_loader/psr4_class_loader.rst diff --git a/components/class_loader/index.rst b/components/class_loader/index.rst index 4916933d6fa..de8da86c7c6 100644 --- a/components/class_loader/index.rst +++ b/components/class_loader/index.rst @@ -6,6 +6,7 @@ ClassLoader introduction class_loader + psr4_class_loader map_class_loader cache_class_loader debug_class_loader diff --git a/components/class_loader/psr4_class_loader.rst b/components/class_loader/psr4_class_loader.rst new file mode 100644 index 00000000000..d98362267c1 --- /dev/null +++ b/components/class_loader/psr4_class_loader.rst @@ -0,0 +1,63 @@ +.. index:: + single: ClassLoader; PSR-4 Class Loader + +The PSR-4 Class Loader +====================== + +Libraries that follow the `PSR-4`_ standard can be loaded with the ``Psr4ClassLoader``. + +.. note:: + + If you manage your dependencies via Composer, you get a PSR-4 compatible + autoloader out of the box. Use this loader in environments where Composer + is not available. + +.. tip:: + All Symfony Components follow PSR-4. + +Usage +----- + +The following example demonstrates, how you can use the +:class:`Symfony\\Component\\ClassLoader\\Psr4ClassLoader` autoloader to use +Symfony's Yaml component. Let's imagine, you downloaded both components – +ClassLoader and Yaml – as ZIP packages and unpacked them to a libs directory. + +The directory structure will look like this: + +.. code-block:: text + + / + +- libs + | +- ClassLoader + | | +- Psr4ClassLoader.php + | | +- … + | +- Yaml + | +- Yaml.php + | +- … + +- config.yml + +- test.php + +In ``demo.php``, to parse the file ``config.yml``, you can use the following +code. + +.. code-block:: php + + use Symfony\Component\ClassLoader\Psr4ClassLoader; + use Symfony\Component\Yaml\Yaml; + + require __DIR__ . '/lib/ClassLoader/Psr4ClassLoader.php'; + + $loader = new Psr4ClassLoader(); + $loader->addPrefix('Symfony\\Component\\Yaml\\', __DIR__ . '/lib/Yaml'); + $loader->register(); + + $data = Yaml::parse(__DIR__ . '/demo.yml'); + +First of all, we've loaded our class loader manually using ``require`` since we +don't have an autoload mechanism, yet. With the ``addPrefix()`` call, we told +the class loader where to look for classes with the namespace prefix +``Symfony\Component\Yaml\``. After registering the autoloader, the Yaml +component is ready to use. + +.. _PSR-4: http://www.php-fig.org/psr/psr-4/ From 6f2a1a34f01b9338f9f302dff83a4a3eb3e54ff6 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Sun, 16 Mar 2014 23:50:58 +0100 Subject: [PATCH 064/835] Adjustments from comments be @WouterJ --- components/class_loader/psr4_class_loader.rst | 52 ++++++++++--------- components/map.rst.inc | 1 + 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/components/class_loader/psr4_class_loader.rst b/components/class_loader/psr4_class_loader.rst index d98362267c1..f7ada1df22c 100644 --- a/components/class_loader/psr4_class_loader.rst +++ b/components/class_loader/psr4_class_loader.rst @@ -6,6 +6,10 @@ The PSR-4 Class Loader Libraries that follow the `PSR-4`_ standard can be loaded with the ``Psr4ClassLoader``. +.. versionadded:: 2.5 + The :class:`Symfony\\Component\\ClassLoader\\Psr4ClassLoader` was + introduced in Symfony 2.5. + .. note:: If you manage your dependencies via Composer, you get a PSR-4 compatible @@ -13,51 +17,51 @@ Libraries that follow the `PSR-4`_ standard can be loaded with the ``Psr4ClassLo is not available. .. tip:: + All Symfony Components follow PSR-4. Usage ----- -The following example demonstrates, how you can use the +The following example demonstrates how you can use the :class:`Symfony\\Component\\ClassLoader\\Psr4ClassLoader` autoloader to use -Symfony's Yaml component. Let's imagine, you downloaded both components – -ClassLoader and Yaml – as ZIP packages and unpacked them to a libs directory. - +Symfony's Yaml component. Imagine, you downloaded both the ``ClassLoader`` and +``Yaml`` component as ZIP packages and unpacked them to a ``libs`` directory. The directory structure will look like this: .. code-block:: text - / - +- libs - | +- ClassLoader - | | +- Psr4ClassLoader.php - | | +- … - | +- Yaml - | +- Yaml.php - | +- … - +- config.yml - +- test.php + libs/ + ClassLoader/ + Psr4ClassLoader.php + ... + Yaml/ + Yaml.php + ... + config.yml + test.php -In ``demo.php``, to parse the file ``config.yml``, you can use the following -code. +In ``demo.php`` you are going to parse the ``config.yml`` file. To do that, you +first need to configure the ``Psr4ClassLoader``: .. code-block:: php use Symfony\Component\ClassLoader\Psr4ClassLoader; use Symfony\Component\Yaml\Yaml; - require __DIR__ . '/lib/ClassLoader/Psr4ClassLoader.php'; + require __DIR__.'/lib/ClassLoader/Psr4ClassLoader.php'; $loader = new Psr4ClassLoader(); - $loader->addPrefix('Symfony\\Component\\Yaml\\', __DIR__ . '/lib/Yaml'); + $loader->addPrefix('Symfony\\Component\\Yaml\\', __DIR__.'/lib/Yaml'); $loader->register(); - $data = Yaml::parse(__DIR__ . '/demo.yml'); + $data = Yaml::parse(__DIR__.'/demo.yml'); -First of all, we've loaded our class loader manually using ``require`` since we -don't have an autoload mechanism, yet. With the ``addPrefix()`` call, we told -the class loader where to look for classes with the namespace prefix -``Symfony\Component\Yaml\``. After registering the autoloader, the Yaml -component is ready to use. +First of all, the class loader is loaded manually using a ``require`` +statement, since there is no autoload mechanism yet. With the +:method:`Symfony\Component\ClassLoader\Psr4ClassLoader::addPrefix` call, you +tell the class loader where to look for classes with the +``Symfony\Component\Yaml\`` namespace prefix. After registering the autoloader, +the Yaml component is ready to use. .. _PSR-4: http://www.php-fig.org/psr/psr-4/ diff --git a/components/map.rst.inc b/components/map.rst.inc index 17e6146f2d8..c3b88b548a9 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -4,6 +4,7 @@ * :doc:`/components/class_loader/introduction` * :doc:`/components/class_loader/class_loader` + * :doc:`/components/class_loader/psr4_class_loader` * :doc:`/components/class_loader/map_class_loader` * :doc:`/components/class_loader/cache_class_loader` * :doc:`/components/class_loader/debug_class_loader` From a05da41b86f713f4acf5dd8f9b721ea160c7fce8 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 17 Mar 2014 09:05:44 +0100 Subject: [PATCH 065/835] Minor corrections. --- components/class_loader/psr4_class_loader.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/class_loader/psr4_class_loader.rst b/components/class_loader/psr4_class_loader.rst index f7ada1df22c..f10799c5a98 100644 --- a/components/class_loader/psr4_class_loader.rst +++ b/components/class_loader/psr4_class_loader.rst @@ -39,7 +39,7 @@ The directory structure will look like this: Yaml.php ... config.yml - test.php + demo.php In ``demo.php`` you are going to parse the ``config.yml`` file. To do that, you first need to configure the ``Psr4ClassLoader``: @@ -55,13 +55,13 @@ first need to configure the ``Psr4ClassLoader``: $loader->addPrefix('Symfony\\Component\\Yaml\\', __DIR__.'/lib/Yaml'); $loader->register(); - $data = Yaml::parse(__DIR__.'/demo.yml'); + $data = Yaml::parse(__DIR__.'/config.yml'); First of all, the class loader is loaded manually using a ``require`` statement, since there is no autoload mechanism yet. With the :method:`Symfony\Component\ClassLoader\Psr4ClassLoader::addPrefix` call, you tell the class loader where to look for classes with the ``Symfony\Component\Yaml\`` namespace prefix. After registering the autoloader, -the Yaml component is ready to use. +the Yaml component is ready to be used. .. _PSR-4: http://www.php-fig.org/psr/psr-4/ From 16fead486a71751ceefeb78237a05dc0dda7fdec Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 17 Mar 2014 09:35:36 +0100 Subject: [PATCH 066/835] Adjustments from comments by @bicpi --- components/class_loader/psr4_class_loader.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/class_loader/psr4_class_loader.rst b/components/class_loader/psr4_class_loader.rst index f10799c5a98..973c8ec3656 100644 --- a/components/class_loader/psr4_class_loader.rst +++ b/components/class_loader/psr4_class_loader.rst @@ -18,15 +18,15 @@ Libraries that follow the `PSR-4`_ standard can be loaded with the ``Psr4ClassLo .. tip:: - All Symfony Components follow PSR-4. + All Symfony components follow PSR-4. Usage ----- The following example demonstrates how you can use the :class:`Symfony\\Component\\ClassLoader\\Psr4ClassLoader` autoloader to use -Symfony's Yaml component. Imagine, you downloaded both the ``ClassLoader`` and -``Yaml`` component as ZIP packages and unpacked them to a ``libs`` directory. +Symfony's Yaml component. Imagine, you downloaded both the ClassLoader and +Yaml component as ZIP packages and unpacked them to a ``libs`` directory. The directory structure will look like this: .. code-block:: text From cb2be4ab4df34a6aa02726c665e566d9ed31a849 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 17 Mar 2014 13:29:40 +0100 Subject: [PATCH 067/835] Moved versionadded block to the top. --- components/class_loader/psr4_class_loader.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/class_loader/psr4_class_loader.rst b/components/class_loader/psr4_class_loader.rst index 973c8ec3656..489db8a351d 100644 --- a/components/class_loader/psr4_class_loader.rst +++ b/components/class_loader/psr4_class_loader.rst @@ -4,12 +4,12 @@ The PSR-4 Class Loader ====================== -Libraries that follow the `PSR-4`_ standard can be loaded with the ``Psr4ClassLoader``. - .. versionadded:: 2.5 The :class:`Symfony\\Component\\ClassLoader\\Psr4ClassLoader` was introduced in Symfony 2.5. +Libraries that follow the `PSR-4`_ standard can be loaded with the ``Psr4ClassLoader``. + .. note:: If you manage your dependencies via Composer, you get a PSR-4 compatible From 4dde2caa50d6877fc2e49ad83e29d172e6ae5f8e Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Wed, 12 Mar 2014 10:01:36 +0100 Subject: [PATCH 068/835] [Process] Add doc for Process::disableOutput and Process::enableOutput --- components/process.rst | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/components/process.rst b/components/process.rst index 5340f21d1fd..6ad44519b0c 100644 --- a/components/process.rst +++ b/components/process.rst @@ -281,6 +281,34 @@ You can access the `pid`_ of a running process with the you may have to prefix your commands with `exec`_. Please read `Symfony Issue#5759`_ to understand why this is happening. +Disabling Output +---------------- + +.. versionadded:: 2.5 + The :method:`Symfony\\Component\\Process\\Process::disableOutput` and + :method:`Symfony\\Component\\Process\\Process::enableOutput` methods were + introduced in Symfony 2.5. + +As standard output and error output are always fetched from the underlying process, +it might be convenient to disable output in some cases to save memory. +Use :method:`Symfony\\Component\\Process\\Process::disableOutput` and +:method:`Symfony\\Component\\Process\\Process::enableOutput` to toggle this feature:: + + use Symfony\Component\Process\Process; + + $process = new Process('/usr/bin/php worker.php'); + $process->disableOutput(); + $process->run(); + +.. caution:: + + You can not enable or disable the output while the process is running. + + If you disable the output, you cannot access ``getOutput``, + ``getIncrementalOutput``, ``getErrorOutput`` or ``getIncrementalErrorOutput``. + Moreover, you could not pass a callback to the ``start``, ``run`` or ``mustRun`` + methods or use ``setIdleTimeout``. + .. _`Symfony Issue#5759`: https://github.com/symfony/symfony/issues/5759 .. _`PHP Bug#39992`: https://bugs.php.net/bug.php?id=39992 .. _`exec`: http://en.wikipedia.org/wiki/Exec_(operating_system) From bd947c7fb104596295ce7ad628768b48e94bca96 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Fri, 14 Mar 2014 14:54:16 +0100 Subject: [PATCH 069/835] [TwigBundle] Add documentation about generating absolute URL with the asset function --- book/templating.rst | 16 ++ .../templating/helpers/assetshelper.rst | 17 +++ reference/twig_reference.rst | 142 +++++++++--------- 3 files changed, 104 insertions(+), 71 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index 6e1090f7d71..e9cfdfaf086 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -991,6 +991,22 @@ assets won't be cached when deployed. For example, ``/images/logo.png`` might look like ``/images/logo.png?v2``. For more information, see the :ref:`ref-framework-assets-version` configuration option. +.. versionadded:: 2.5 + Absolute URLs for assets were introduced in Symfony 2.5. + +If you need absolute URLs for assets, you can set the third argument (or the +``absolute`` argument) to ``true``: + +.. configuration-block:: + + .. code-block:: html+jinja + + Symfony! + + .. code-block:: html+php + + Symfony! + .. index:: single: Templating; Including stylesheets and JavaScripts single: Stylesheets; Including stylesheets diff --git a/components/templating/helpers/assetshelper.rst b/components/templating/helpers/assetshelper.rst index 57ef49f0802..6151d2b4784 100644 --- a/components/templating/helpers/assetshelper.rst +++ b/components/templating/helpers/assetshelper.rst @@ -47,6 +47,23 @@ You can also specify a URL to use in the second parameter of the constructor:: Now URLs are rendered like ``http://cdn.example.com/images/logo.png``. +.. versionadded:: 2.5 + Absolute URLs for assets were introduced in Symfony 2.5. + +You can also use the third argument of the helper to force an absolute URL: + +.. code-block:: html+php + + + + +.. note:: + + If you already set a URL in the constructor, using the third argument of + ``getUrl`` will not affect the generated URL. + Versioning ---------- diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index a791f8c8d21..02e48fb8bfa 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -20,77 +20,77 @@ Functions .. versionadded:: 2.4 The ``expression`` function was introduced in Symfony 2.4. -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| Function Syntax | Usage | -+====================================================+============================================================================================+ -| ``render(uri, options = {})`` | This will render the fragment for the given controller or URL | -| ``render(controller('B:C:a', {params}))`` | For more information, see :ref:`templating-embedding-controller`. | -| ``render(path('route', {params}))`` | | -| ``render(url('route', {params}))`` | | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``render_esi(controller('B:C:a', {params}))`` | This will generate an ESI tag when possible or fallback to the ``render`` | -| ``render_esi(url('route', {params}))`` | behavior otherwise. For more information, see :ref:`templating-embedding-controller`. | -| ``render_esi(path('route', {params}))`` | | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``render_hinclude(controller(...))`` | This will generates an Hinclude tag for the given controller or URL. | -| ``render_hinclude(url('route', {params}))`` | For more information, see :ref:`templating-embedding-controller`. | -| ``render_hinclude(path('route', {params}))`` | | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``controller(attributes = {}, query = {})`` | Used along with the ``render`` tag to refer to the controller that you want to render. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``asset(path, packageName = null)`` | Get the public path of the asset, more information in | -| | ":ref:`book-templating-assets`". | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``asset_version(packageName = null)`` | Get the current version of the package, more information in | -| | ":ref:`book-templating-assets`". | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form(view, variables = {})`` | This will render the HTML of a complete form, more information in | -| | in :ref:`the Twig Form reference`. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_start(view, variables = {})`` | This will render the HTML start tag of a form, more information in | -| | in :ref:`the Twig Form reference`. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_end(view, variables = {})`` | This will render the HTML end tag of a form together with all fields that | -| | have not been rendered yet, more information | -| | in :ref:`the Twig Form reference`. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_enctype(view)`` | This will render the required ``enctype="multipart/form-data"`` attribute | -| | if the form contains at least one file upload field, more information in | -| | in :ref:`the Twig Form reference `. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_widget(view, variables = {})`` | This will render a complete form or a specific HTML widget of a field, | -| | more information in :ref:`the Twig Form reference `. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_errors(view)`` | This will render any errors for the given field or the "global" errors, | -| | more information in :ref:`the Twig Form reference `. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_label(view, label = null, variables = {})`` | This will render the label for the given field, more information in | -| | :ref:`the Twig Form reference `. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_row(view, variables = {})`` | This will render the row (the field's label, errors and widget) of the given | -| | field, more information in :ref:`the Twig Form reference `. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_rest(view, variables = {})`` | This will render all fields that have not yet been rendered, more | -| | information in :ref:`the Twig Form reference `. | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``csrf_token(intention)`` | This will render a CSRF token. Use this function if you want CSRF protection without | -| | creating a form | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``is_granted(role, object = null, field = null)`` | This will return ``true`` if the current user has the required role, more | -| | information in ":ref:`book-security-template`" | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``logout_path(key)`` | This will generate the relative logout URL for the given firewall | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``logout_url(key)`` | Equal to ``logout_path(...)`` but this will generate an absolute URL | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``path(name, parameters = {})`` | Get a relative URL for the given route, more information in | -| | ":ref:`book-templating-pages`". | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``url(name, parameters = {})`` | Equal to ``path(...)`` but it generates an absolute URL | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``expression(expression)`` | Creates an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` in Twig. See | -| | ":ref:`Template Expressions `". | -+----------------------------------------------------+--------------------------------------------------------------------------------------------+ ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| Function Syntax | Usage | ++=======================================================+============================================================================================+ +| ``render(uri, options = {})`` | This will render the fragment for the given controller or URL | +| ``render(controller('B:C:a', {params}))`` | For more information, see :ref:`templating-embedding-controller`. | +| ``render(path('route', {params}))`` | | +| ``render(url('route', {params}))`` | | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``render_esi(controller('B:C:a', {params}))`` | This will generate an ESI tag when possible or fallback to the ``render`` | +| ``render_esi(url('route', {params}))`` | behavior otherwise. For more information, see :ref:`templating-embedding-controller`. | +| ``render_esi(path('route', {params}))`` | | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``render_hinclude(controller(...))`` | This will generates an Hinclude tag for the given controller or URL. | +| ``render_hinclude(url('route', {params}))`` | For more information, see :ref:`templating-embedding-controller`. | +| ``render_hinclude(path('route', {params}))`` | | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``controller(attributes = {}, query = {})`` | Used along with the ``render`` tag to refer to the controller that you want to render. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``asset(path, packageName = null, absolute = false)`` | Get the public path of the asset, more information in | +| | ":ref:`book-templating-assets`". | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``asset_version(packageName = null)`` | Get the current version of the package, more information in | +| | ":ref:`book-templating-assets`". | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form(view, variables = {})`` | This will render the HTML of a complete form, more information in | +| | in :ref:`the Twig Form reference`. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_start(view, variables = {})`` | This will render the HTML start tag of a form, more information in | +| | in :ref:`the Twig Form reference`. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_end(view, variables = {})`` | This will render the HTML end tag of a form together with all fields that | +| | have not been rendered yet, more information | +| | in :ref:`the Twig Form reference`. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_enctype(view)`` | This will render the required ``enctype="multipart/form-data"`` attribute | +| | if the form contains at least one file upload field, more information in | +| | in :ref:`the Twig Form reference `. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_widget(view, variables = {})`` | This will render a complete form or a specific HTML widget of a field, | +| | more information in :ref:`the Twig Form reference `. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_errors(view)`` | This will render any errors for the given field or the "global" errors, | +| | more information in :ref:`the Twig Form reference `. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_label(view, label = null, variables = {})`` | This will render the label for the given field, more information in | +| | :ref:`the Twig Form reference `. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_row(view, variables = {})`` | This will render the row (the field's label, errors and widget) of the given | +| | field, more information in :ref:`the Twig Form reference `. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_rest(view, variables = {})`` | This will render all fields that have not yet been rendered, more | +| | information in :ref:`the Twig Form reference `. | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``csrf_token(intention)`` | This will render a CSRF token. Use this function if you want CSRF protection without | +| | creating a form | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``is_granted(role, object = null, field = null)`` | This will return ``true`` if the current user has the required role, more | +| | information in ":ref:`book-security-template`" | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``logout_path(key)`` | This will generate the relative logout URL for the given firewall | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``logout_url(key)`` | Equal to ``logout_path(...)`` but this will generate an absolute URL | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``path(name, parameters = {})`` | Get a relative URL for the given route, more information in | +| | ":ref:`book-templating-pages`". | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``url(name, parameters = {})`` | Equal to ``path(...)`` but it generates an absolute URL | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``expression(expression)`` | Creates an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` in Twig. See | +| | ":ref:`Template Expressions `". | ++-------------------------------------------------------+--------------------------------------------------------------------------------------------+ Filters ------- From a6a074c8378cbf01c1162357bce32ad3522dcefd Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 18 Mar 2014 19:35:36 +0100 Subject: [PATCH 070/835] fixing a code block --- book/translation.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/book/translation.rst b/book/translation.rst index 0c7e13e77ae..d398a7d9cfa 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -693,8 +693,9 @@ It will also detect the following translator usages in PHP templates: which means this example won't be analyzed: .. code-block:: jinja - {% set message = 'Symfony2 is great' %} - {{ message|trans }} + + {% set message = 'Symfony2 is great' %} + {{ message|trans }} Suppose your application's default_locale is ``fr`` and you have configured ``en`` as the fallback locale (see :ref:`book-translation-configuration` and :ref:`book-translation-fallback` for how to configure these). From eafbab1a5f29bbd865fd9e0ab54d9e8a1f705591 Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Tue, 18 Mar 2014 22:48:17 +0100 Subject: [PATCH 071/835] Enhanced Firewall Restrictions docs Squashed commits: [2081fc4] Enhanced Firewall Restrictions docs for PR symfony/symfony#10404 [8f065bc] Add redirect to new page [b42e80f] fixed typos [cc6b07d] tiny update --- book/security.rst | 7 +- cookbook/map.rst.inc | 2 +- cookbook/security/firewall_restriction.rst | 193 +++++++++++++++++++++ cookbook/security/host_restriction.rst | 70 +------- reference/configuration/security.rst | 6 + 5 files changed, 209 insertions(+), 69 deletions(-) create mode 100644 cookbook/security/firewall_restriction.rst diff --git a/book/security.rst b/book/security.rst index a2fea0592b9..07310c0225f 100644 --- a/book/security.rst +++ b/book/security.rst @@ -181,6 +181,11 @@ firewall is activated does *not* mean, however, that the HTTP authentication username and password box is displayed for every URL. For example, any user can access ``/foo`` without being prompted to authenticate. +.. tip:: + + You can also match a request against other details of the request (e.g. host, method). For more + information and examples read :doc:`/cookbook/security/firewall_restriction`. + .. image:: /images/book/security_anonymous_user_access.png :align: center @@ -2139,7 +2144,7 @@ Learn more from the Cookbook * :doc:`Blacklist users by IP address with a custom voter ` * :doc:`Access Control Lists (ACLs) ` * :doc:`/cookbook/security/remember_me` -* :doc:`How to Restrict Firewalls to a Specific Host ` +* :doc:`How to Restrict Firewalls to a Specific Request ` .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle .. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 6c23641e2e3..dd9295c9515 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -138,7 +138,7 @@ * :doc:`/cookbook/security/acl` * :doc:`/cookbook/security/acl_advanced` * :doc:`/cookbook/security/force_https` - * :doc:`/cookbook/security/host_restriction` + * :doc:`/cookbook/security/firewall_restriction` * :doc:`/cookbook/security/form_login` * :doc:`/cookbook/security/securing_services` * :doc:`/cookbook/security/custom_provider` diff --git a/cookbook/security/firewall_restriction.rst b/cookbook/security/firewall_restriction.rst new file mode 100644 index 00000000000..db3568e5f2b --- /dev/null +++ b/cookbook/security/firewall_restriction.rst @@ -0,0 +1,193 @@ +.. index:: + single: Security; Restrict Security Firewalls to a Request + +How to Restrict Firewalls to a Specific Request +=============================================== + +When using the Security component, you can create firewalls that match certain request options. +In most cases, matching against the URL is sufficient, but in special cases you can further +restrict the initialization of a firewall against other options of the request. + +.. note:: + + You can use any of these restrictions individually or mix them together to get + your desired firewall configuration. + +Restricting by Pattern +---------------------- + +This is the default restriction and restricts a firewall to only be initialized if the request URL +matches the configured ``pattern``. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + + # ... + security: + firewalls: + secured_area: + pattern: ^/admin + # ... + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // ... + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + 'pattern' => '^/admin', + // ... + ), + ), + )); + +The ``pattern`` is a regular expression. In this example, the firewall will only be +activated if the URL starts (due to the ``^`` regex character) with ``/admin`. If +the URL does not match this pattern, the firewall will not be activated and subsequent +firewalls will have the opportunity to be matched for this request. + +Restricting by Host +------------------- + +.. versionadded:: 2.4 + Support for restricting security firewalls to a specific host was introduced in + Symfony 2.4. + +If matching against the ``pattern`` only is not enough, the request can also be matched against +``host``. When the configuration option ``host`` is set, the firewall will be restricted to +only initialize if the host from the request matches against the configuration. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + + # ... + security: + firewalls: + secured_area: + host: ^admin\.example\.com$ + # ... + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // ... + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + 'host' => '^admin\.example\.com$', + // ... + ), + ), + )); + +The ``host`` (like the ``pattern``) is a regular expression. In this example, +the firewall will only be activated if the host is equal exactly (due to +the ``^`` and ``$`` regex characters) to the hostname ``admin.example.com``. +If the hostname does not match this pattern, the firewall will not be activated +and subsequent firewalls will have the opportunity to be matched for this +request. + +Restricting by HTTP Methods +--------------------------- + +.. versionadded:: 2.5 + Support for restricting security firewalls to specific HTTP methods was introduced in + Symfony 2.5. + +The configuration option ``methods`` restricts the initialization of the firewall to +the provided HTTP methods. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + + # ... + security: + firewalls: + secured_area: + methods: [GET, POST] + # ... + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // ... + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + 'methods' => array('GET', 'POST'), + // ... + ), + ), + )); + +In this example, the firewall will only be activated if the HTTP method of the +request is either ``GET`` or ``POST``. If the method is not in the array of the +allowed methods, the firewall will not be activated and subsequent firewalls will again +have the opportunity to be matched for this request. diff --git a/cookbook/security/host_restriction.rst b/cookbook/security/host_restriction.rst index b5b2e529d95..0ee0d334e30 100644 --- a/cookbook/security/host_restriction.rst +++ b/cookbook/security/host_restriction.rst @@ -1,70 +1,6 @@ -.. index:: - single: Security; Restrict Security Firewalls to a Host - How to Restrict Firewalls to a Specific Host ============================================ -.. versionadded:: 2.4 - Support for restricting security firewalls to a specific host was introduced in - Symfony 2.4. - -When using the Security component, you can create firewalls that match certain -URL patterns and therefore are activated for all pages whose URL matches -that pattern. Additionally, you can restrict the initialization of a firewall -to a host using the ``host`` key: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - - # ... - - security: - firewalls: - secured_area: - pattern: ^/ - host: ^admin\.example\.com$ - http_basic: true - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - - // ... - - $container->loadFromExtension('security', array( - 'firewalls' => array( - 'secured_area' => array( - 'pattern' => '^/', - 'host' => '^admin\.example\.com$', - 'http_basic' => true, - ), - ), - )); - -The ``host`` (like the ``pattern``) is a regular expression. In this example, -the firewall will only be activated if the host is equal exactly (due to -the ``^`` and ``$`` regex characters) to the hostname ``admin.example.com``. -If the hostname does not match this pattern, the firewall will not be activated -and subsequent firewalls will have the opportunity to be matched for this -request. +As of Symfony 2.5, more possibilities to restrict firewalls have been added. +You can read everything about all the possibilities (including ``host``) +in ":doc:`/cookbook/security/firewall_restriction`". diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index accbbd97cbd..a5a643d334c 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -17,6 +17,10 @@ Each part will be explained in the next section. Support for restricting security firewalls to a specific host was introduced in Symfony 2.4. +.. versionadded:: 2.5 + Support for restricting security firewalls to specific http methods was introduced in + Symfony 2.5. + .. configuration-block:: .. code-block:: yaml @@ -104,6 +108,8 @@ Each part will be explained in the next section. pattern: .* # restrict the firewall to a specific host host: admin\.example\.com + # restrict the firewall to specific http methods + methods: [GET, POST] request_matcher: some.service.id access_denied_url: /foo/error403 access_denied_handler: some.service.id From 0816a077ef60ec625132e1465cf6c08c9c8b9d19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 19 Mar 2014 17:26:51 +0100 Subject: [PATCH 072/835] [Console] Change Command namespaces --- components/console/changing_default_command.rst | 4 ++-- components/console/introduction.rst | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst index d00e60a3d7f..6c8d2689a97 100644 --- a/components/console/changing_default_command.rst +++ b/components/console/changing_default_command.rst @@ -12,7 +12,7 @@ will always run the ``ListCommand`` when no command name is passed. In order to the default command you just need to pass the command name you want to run by default to the ``setDefaultCommand`` method:: - namespace Acme\Command; + namespace Acme\Console\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -36,7 +36,7 @@ Executing the application and changing the default Command:: // application.php - use Acme\Command\HelloWorldCommand; + use Acme\Console\Command\HelloWorldCommand; use Symfony\Component\Console\Application; $command = new HelloWorldCommand(); diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 3801b5d7ebb..6f2d43a196b 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -35,7 +35,7 @@ Creating a basic Command To make a console command that greets you from the command line, create ``GreetCommand.php`` and add the following to it:: - namespace Acme\Command; + namespace Acme\Console\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -88,7 +88,7 @@ an ``Application`` and adds commands to it:: Date: Thu, 20 Mar 2014 22:54:24 +0100 Subject: [PATCH 073/835] add firewall restriction document to security toctree --- cookbook/security/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 9cd886b686a..15a2668d1fe 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -13,6 +13,7 @@ Security acl_advanced force_https host_restriction + firewall_restriction form_login securing_services custom_provider From 70b55b54abb0a3865fc9f175fffef0750573f701 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 20 Mar 2014 22:55:00 +0100 Subject: [PATCH 074/835] fix literal --- cookbook/security/firewall_restriction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/firewall_restriction.rst b/cookbook/security/firewall_restriction.rst index db3568e5f2b..4cc7e0f7617 100644 --- a/cookbook/security/firewall_restriction.rst +++ b/cookbook/security/firewall_restriction.rst @@ -65,7 +65,7 @@ matches the configured ``pattern``. )); The ``pattern`` is a regular expression. In this example, the firewall will only be -activated if the URL starts (due to the ``^`` regex character) with ``/admin`. If +activated if the URL starts (due to the ``^`` regex character) with ``/admin``. If the URL does not match this pattern, the firewall will not be activated and subsequent firewalls will have the opportunity to be matched for this request. From 15628e64971b776cfdc3c3113ac619e4b4a16058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 19 Mar 2014 16:00:26 +0100 Subject: [PATCH 075/835] [Console] Added standalone PSR-3 compliant logger --- components/console/index.rst | 1 + components/console/logger.rst | 108 +++++++++++++++++++++++++++ cookbook/logging/monolog_console.rst | 5 ++ 3 files changed, 114 insertions(+) create mode 100644 components/console/logger.rst diff --git a/components/console/index.rst b/components/console/index.rst index f974291a8e0..b449eff094b 100644 --- a/components/console/index.rst +++ b/components/console/index.rst @@ -9,4 +9,5 @@ Console changing_default_command single_command_tool events + logger helpers/index diff --git a/components/console/logger.rst b/components/console/logger.rst new file mode 100644 index 00000000000..84a41ad2b6c --- /dev/null +++ b/components/console/logger.rst @@ -0,0 +1,108 @@ +.. index:: + single: Console; Logger + +Using the Logger +================ + +.. versionadded:: 2.5 + The :class:`Symfony\\Component\\Console\\Logger\\ConsoleLogger` was + introduced in Symfony 2.5. + +The Console component comes with a standalone logger complying with the +`PSR-3_` standard. +Depending of the verbosity setting, log messages will be sent to the +:class:`Symfony\\Component\\Console\\Output\\OutputInterface` instance +passed as a parameter to the constructor. + +The logger does not have any external dependency except ``php-fig/log``. +This is useful for console applications and commands needing a lightweight +PSR-3 compliant logger:: + + namespace Acme; + + use Psr\Log\LoggerInterface; + + class MyDependency + { + private $logger; + + public function __construct(LoggerInterface $logger) + { + $this->logger = $logger; + } + + public function doStuff() + { + $this->logger->info('I love Tony Vairelles\' hairdresser.'); + } + } + +You can rely on the logger to use this dependency inside a command:: + + namespace Acme\Console\Command; + + use Acme\MyDependency; + use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Output\OutputInterface; + use Symfony\Component\Console\Logger\ConsoleLogger; + + class MyCommand extends Command + { + protected function configure() + { + $this + ->setName('my:command') + ->setDescription( + 'Use an external dependency requiring a PSR-3 logger' + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $logger = new ConsoleLogger($output); + + $myDependency = MyDependency($logger); + $myDependency->doStuff(); + } + } + +The dependency will use the instance of +``Symfony\\Component\\Console\\Logger\\ConsoleLogger`` as logger. +Log messages emitted will be displayed on the console output. + +Verbosity +--------- + +Depending on the verbosity level that the command is run, messages may or +may not be sent to the ``Symfony\\Component\\Console\\Output\\OutputInterface`` instance. + +By default, the console logger behaves like the +:doc:`Monolog's Console Handler `. +The association between the log level and the verbosity can be configured +through the second parameter of the :class:`Symfony\\Component\\Console\\ConsoleLogger` +constructor:: + + // ... + $verbosityLevelMap = array( + LogLevel::NOTICE => OutputInterface::VERBOSITY_NORMAL, + LogLevel::INFO => OutputInterface::VERBOSITY_NORMAL, + ); + $logger = new ConsoleLogger($output, $verbosityLevelMap); + +Color +----- + +The logger outputs the log messages formatted with a color reflecting their +level. This behavior is configurable through the third parameter of the +constructor:: + + // ... + private $formatLevelMap = array( + LogLevel::CRITICAL => self::INFO, + LogLevel::DEBUG => self::ERROR, + ); + $logger = new ConsoleLogger($output, array(), $formatLevelMap); + +.. _PSR-3: http://www.php-fig.org/psr/psr-3/ \ No newline at end of file diff --git a/cookbook/logging/monolog_console.rst b/cookbook/logging/monolog_console.rst index 7945193022e..ec72197c4c9 100644 --- a/cookbook/logging/monolog_console.rst +++ b/cookbook/logging/monolog_console.rst @@ -12,6 +12,11 @@ It is possible to use the console to print messages for certain :class:`Symfony\\Component\\Console\\Output\\OutputInterface` instance that is passed when a command gets executed. +.. seealso:: + Alternatively, you can use the + :doc:`standalone PSR-3 logger ` provided with + the console component. + When a lot of logging has to happen, it's cumbersome to print information depending on the verbosity settings (``-v``, ``-vv``, ``-vvv``) because the calls need to be wrapped in conditions. The code quickly gets verbose or dirty. From ac484f41e609177d8f3db0358096c2731e527e16 Mon Sep 17 00:00:00 2001 From: jochenvdv Date: Tue, 25 Mar 2014 16:09:59 +0100 Subject: [PATCH 076/835] Made suggested tweaks --- components/stopwatch.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/stopwatch.rst b/components/stopwatch.rst index 034a6d3e928..b056af59e6e 100644 --- a/components/stopwatch.rst +++ b/components/stopwatch.rst @@ -31,7 +31,14 @@ microtime by yourself. Instead, use the simple // ... some code goes here $event = $stopwatch->stop('eventName'); -The :class:`Symfony\\Component\\Stopwatch\StopwatchEvent` object can be retrieved from :method:`Symfony\\Component\\Stopwatch\\Stopwatch::start`, :method:`Symfony\\Component\\Stopwatch\\Stopwatch::stop`, :method:`Symfony\\Component\\Stopwatch\\Stopwatch::lap` and :method:`Symfony\\Component\\Stopwatch\\Stopwatch::getEvent` +.. versionadded:: 2.5 + The ``getEvent()`` method was introduced + +The :class:`Symfony\\Component\\Stopwatch\StopwatchEvent` object can be retrieved from the +:method:`Symfony\\Component\\Stopwatch\\Stopwatch::start`, +:method:`Symfony\\Component\\Stopwatch\\Stopwatch::stop`, +:method:`Symfony\\Component\\Stopwatch\\Stopwatch::lap` and +:method:`Symfony\\Component\\Stopwatch\\Stopwatch::getEvent` methods. You can also provide a category name to an event:: From 295f10993545c3b9a33e0eb383fe7ade4713538d Mon Sep 17 00:00:00 2001 From: jochenvdv Date: Tue, 25 Mar 2014 16:18:54 +0100 Subject: [PATCH 077/835] Add reason for getEvent() --- components/stopwatch.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/stopwatch.rst b/components/stopwatch.rst index b056af59e6e..4bc306b8338 100644 --- a/components/stopwatch.rst +++ b/components/stopwatch.rst @@ -38,7 +38,8 @@ The :class:`Symfony\\Component\\Stopwatch\StopwatchEvent` object can be retrieve :method:`Symfony\\Component\\Stopwatch\\Stopwatch::start`, :method:`Symfony\\Component\\Stopwatch\\Stopwatch::stop`, :method:`Symfony\\Component\\Stopwatch\\Stopwatch::lap` and -:method:`Symfony\\Component\\Stopwatch\\Stopwatch::getEvent` methods. +:method:`Symfony\\Component\\Stopwatch\\Stopwatch::getEvent` methods. +The latter should be used when you need to retrieve the duration of an event while it is still running. You can also provide a category name to an event:: From 0ee15d3ad6821a4e1eacdd397d18d77505e78700 Mon Sep 17 00:00:00 2001 From: jochenvdv Date: Tue, 25 Mar 2014 16:46:49 +0100 Subject: [PATCH 078/835] Fix versionadded --- components/stopwatch.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/stopwatch.rst b/components/stopwatch.rst index 4bc306b8338..324539f3cf6 100644 --- a/components/stopwatch.rst +++ b/components/stopwatch.rst @@ -32,7 +32,7 @@ microtime by yourself. Instead, use the simple $event = $stopwatch->stop('eventName'); .. versionadded:: 2.5 - The ``getEvent()`` method was introduced + The ``getEvent()`` method was introduced in Symfony 2.5 The :class:`Symfony\\Component\\Stopwatch\StopwatchEvent` object can be retrieved from the :method:`Symfony\\Component\\Stopwatch\\Stopwatch::start`, From 239b1b686d16624c7404fe8375dbcfd564938c54 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 25 Mar 2014 16:53:59 +0100 Subject: [PATCH 079/835] Some markup fixes --- components/options_resolver.rst | 1 - cookbook/console/console_command.rst | 11 ++++++----- reference/forms/types/collection.rst | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index ced1cb0c2b0..af240abaefd 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -328,7 +328,6 @@ method, which you can use if you want to add an allowed value to the previously configured allowed values. .. versionadded:: 2.5 - The callback support for allowed values was introduced in Symfony 2.5. If you need to add some more logic to the value validation process, you can pass a callable diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index 30ea6f0d9cf..da5c18e01eb 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -174,8 +174,9 @@ you can extend your test from .. versionadded:: 2.5 :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase` was extracted from :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase` - in Symfony 2.5, where WebTestCase was made to inherit from KernelTestCase. - The difference being that WebTestCase makes available an instance of - :class:`Symfony\\Bundle\\FrameworkBundle\\Client` via `createClient()`, - while KernelTestCase makes available an instance of - :class:`Symfony\\Component\\HttpKernel\\KernelInterface` via `createKernel()`. + in Symfony 2.5. ``WebTestCase`` inherits from ``KernelTestCase``. The + ``WebTestCase`` creates an instance of + :class:`Symfony\\Bundle\\FrameworkBundle\\Client` via ``createClient()``, + while ``KernelTestCase`` creates an instance of + :class:`Symfony\\Component\\HttpKernel\\KernelInterface` via + ``createKernel()``. diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index 42018725f2d..ad487018e20 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -341,7 +341,7 @@ delete_empty ~~~~~~~~~~~~ .. versionadded:: 2.5 - The delete_empty option was introduced in Symfony 2.5. + The ``delete_empty`` option was introduced in Symfony 2.5. **type**: ``Boolean`` **default**: ``false`` From b3372cf73ac0c17d0ca5cee2f317bb0d362b06b5 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 25 Mar 2014 16:54:45 +0100 Subject: [PATCH 080/835] Made versionadded consistent --- components/console/changing_default_command.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst index 6c8d2689a97..1c191ba8571 100644 --- a/components/console/changing_default_command.rst +++ b/components/console/changing_default_command.rst @@ -6,7 +6,7 @@ Changing the Default Command .. versionadded:: 2.5 The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` - method was introduced in version 2.5. + method was introduced in Symfony 2.5. will always run the ``ListCommand`` when no command name is passed. In order to change the default command you just need to pass the command name you want to run by From dd165898d2ea89718ee6fc1232f3eb44243f003f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 26 Mar 2014 10:35:55 +0100 Subject: [PATCH 081/835] add logger chapter to the component's map --- components/map.rst.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/components/map.rst.inc b/components/map.rst.inc index c3b88b548a9..9eb981425e3 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -23,6 +23,7 @@ * :doc:`/components/console/single_command_tool` * :doc:`/components/console/changing_default_command` * :doc:`/components/console/events` + * :doc:`/components/console/logger` * :doc:`/components/console/helpers/index` * **CssSelector** From 9b0911b2a4b44950d030d8039210a7c3d2c57392 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 26 Mar 2014 10:39:42 +0100 Subject: [PATCH 082/835] fix grammer mistake --- components/console/logger.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/console/logger.rst b/components/console/logger.rst index 84a41ad2b6c..6c54564963e 100644 --- a/components/console/logger.rst +++ b/components/console/logger.rst @@ -9,10 +9,9 @@ Using the Logger introduced in Symfony 2.5. The Console component comes with a standalone logger complying with the -`PSR-3_` standard. -Depending of the verbosity setting, log messages will be sent to the -:class:`Symfony\\Component\\Console\\Output\\OutputInterface` instance -passed as a parameter to the constructor. +`PSR-3_` standard. Depending on the verbosity setting, log messages will +be sent to the :class:`Symfony\\Component\\Console\\Output\\OutputInterface` +instance passed as a parameter to the constructor. The logger does not have any external dependency except ``php-fig/log``. This is useful for console applications and commands needing a lightweight @@ -76,7 +75,8 @@ Verbosity --------- Depending on the verbosity level that the command is run, messages may or -may not be sent to the ``Symfony\\Component\\Console\\Output\\OutputInterface`` instance. +may not be sent to the ``Symfony\\Component\\Console\\Output\\OutputInterface`` +instance. By default, the console logger behaves like the :doc:`Monolog's Console Handler `. @@ -105,4 +105,4 @@ constructor:: ); $logger = new ConsoleLogger($output, array(), $formatLevelMap); -.. _PSR-3: http://www.php-fig.org/psr/psr-3/ \ No newline at end of file +.. _PSR-3: http://www.php-fig.org/psr/psr-3/ From 3bbccf70bf27d0f32fa00fb395ff06a1b151450e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 26 Mar 2014 10:40:03 +0100 Subject: [PATCH 083/835] fix PSR-3 link as mentioned by @max-voloshin --- components/console/logger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/logger.rst b/components/console/logger.rst index 6c54564963e..35b72704044 100644 --- a/components/console/logger.rst +++ b/components/console/logger.rst @@ -9,7 +9,7 @@ Using the Logger introduced in Symfony 2.5. The Console component comes with a standalone logger complying with the -`PSR-3_` standard. Depending on the verbosity setting, log messages will +`PSR-3`_ standard. Depending on the verbosity setting, log messages will be sent to the :class:`Symfony\\Component\\Console\\Output\\OutputInterface` instance passed as a parameter to the constructor. From 83adc87e176ed9b194c06b727d4092ffb51737b5 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 26 Mar 2014 10:43:37 +0100 Subject: [PATCH 084/835] fix class roles --- components/console/logger.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/console/logger.rst b/components/console/logger.rst index 35b72704044..71b8452b1d3 100644 --- a/components/console/logger.rst +++ b/components/console/logger.rst @@ -68,14 +68,14 @@ You can rely on the logger to use this dependency inside a command:: } The dependency will use the instance of -``Symfony\\Component\\Console\\Logger\\ConsoleLogger`` as logger. +:class:`Symfony\\Component\\Console\\Logger\\ConsoleLogger` as logger. Log messages emitted will be displayed on the console output. Verbosity --------- Depending on the verbosity level that the command is run, messages may or -may not be sent to the ``Symfony\\Component\\Console\\Output\\OutputInterface`` +may not be sent to the :class:`Symfony\\Component\\Console\\Output\\OutputInterface` instance. By default, the console logger behaves like the From 610eb6b3910d6f7db3ecf787b2c86ef7a4d8e9b0 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 26 Mar 2014 10:46:38 +0100 Subject: [PATCH 085/835] code fixes as mentioned by @dirkluijk --- components/console/logger.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/console/logger.rst b/components/console/logger.rst index 71b8452b1d3..43951c63130 100644 --- a/components/console/logger.rst +++ b/components/console/logger.rst @@ -62,7 +62,7 @@ You can rely on the logger to use this dependency inside a command:: { $logger = new ConsoleLogger($output); - $myDependency = MyDependency($logger); + $myDependency = new MyDependency($logger); $myDependency->doStuff(); } } @@ -99,7 +99,7 @@ level. This behavior is configurable through the third parameter of the constructor:: // ... - private $formatLevelMap = array( + $formatLevelMap = array( LogLevel::CRITICAL => self::INFO, LogLevel::DEBUG => self::ERROR, ); From bb8e3ed3ee2ebd141acb5c572efcdfbecf5d5b2d Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Fri, 28 Mar 2014 17:33:52 +0100 Subject: [PATCH 086/835] Added documentation for the new PropertyAccessor::isReadable() and isWritable() methods --- components/property_access/introduction.rst | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 07177d15b5f..2fabbf2119b 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -309,6 +309,39 @@ see `Enable other Features`_. echo $person->getWouter(); // array(...) +Checking Property Paths +----------------------- + +.. versionadded:: 2.5 + The methods + :method:`PropertyAccessor::isReadable` + and + :method:`PropertyAccessor::isWritable` + methods were added in Symfony 2.5. + +When you want to check whether :method:`PropertyAccessor::getValue` +can safely be called without actually calling that method, you can use +:method:`PropertyAccessor::isReadable` +instead:: + + $person = new Person(); + + if ($accessor->isReadable($person, 'firstName') { + // ... + } + +The same is possible for :method:`PropertyAccessor::setValue`: +Call the +:method:`PropertyAccessor::isWritable` +method to find out whether a property path can be updated. In the third +argument, you should pass the value that you want to write:: + + $person = new Person(); + + if ($accessor->isWritable($person, 'firstName', 'Wouter') { + // ... + } + Mixing Objects and Arrays ------------------------- From f6af2510347c02aaffab6877ca1ed8e566aa862d Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Mon, 31 Mar 2014 15:46:59 +0200 Subject: [PATCH 087/835] [Templating] Add documentation about generating versioned URLs --- book/templating.rst | 19 +++ .../templating/helpers/assetshelper.rst | 12 ++ reference/twig_reference.rst | 142 +++++++++--------- 3 files changed, 102 insertions(+), 71 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index e9cfdfaf086..a91c4b5ba81 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -1007,6 +1007,25 @@ If you need absolute URLs for assets, you can set the third argument (or the Symfony! +.. versionadded:: 2.5 + Versioned URLs for assets were introduced in Symfony 2.5. + +If you need versioned URLs for assets, you can set the fourth argument (or the +``version`` argument) to the desired version: + +.. configuration-block:: + + .. code-block:: html+jinja + + Symfony! + + .. code-block:: html+php + + Symfony! + +If you dont give a version or pass ``null``, the default package version will +be used. If you pass ``false``, versioned URL will be deactivated. + .. index:: single: Templating; Including stylesheets and JavaScripts single: Stylesheets; Including stylesheets diff --git a/components/templating/helpers/assetshelper.rst b/components/templating/helpers/assetshelper.rst index 6151d2b4784..6f096d35c52 100644 --- a/components/templating/helpers/assetshelper.rst +++ b/components/templating/helpers/assetshelper.rst @@ -80,6 +80,18 @@ is used in :phpfunction:`sprintf`. The first argument is the path and the second is the version. For instance, ``%s?v=%s`` will be rendered as ``/images/logo.png?v=328rad75``. +.. versionadded:: 2.5 + On-demand versioned URLs for assets were introduced in Symfony 2.5. + +You can also generate a versioned URL using the fourth argument of the helper: + +.. code-block:: html+php + + + + Multiple Packages ----------------- diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 23c39471555..00e709fd335 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -20,77 +20,77 @@ Functions .. versionadded:: 2.4 The ``expression`` function was introduced in Symfony 2.4. -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| Function Syntax | Usage | -+=======================================================+============================================================================================+ -| ``render(uri, options = {})`` | This will render the fragment for the given controller or URL | -| ``render(controller('B:C:a', {params}))`` | For more information, see :ref:`templating-embedding-controller`. | -| ``render(path('route', {params}))`` | | -| ``render(url('route', {params}))`` | | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``render_esi(controller('B:C:a', {params}))`` | This will generate an ESI tag when possible or fallback to the ``render`` | -| ``render_esi(url('route', {params}))`` | behavior otherwise. For more information, see :ref:`templating-embedding-controller`. | -| ``render_esi(path('route', {params}))`` | | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``render_hinclude(controller(...))`` | This will generates an Hinclude tag for the given controller or URL. | -| ``render_hinclude(url('route', {params}))`` | For more information, see :ref:`templating-embedding-controller`. | -| ``render_hinclude(path('route', {params}))`` | | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``controller(attributes = {}, query = {})`` | Used along with the ``render`` tag to refer to the controller that you want to render. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``asset(path, packageName = null, absolute = false)`` | Get the public path of the asset, more information in | -| | ":ref:`book-templating-assets`". | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``asset_version(packageName = null)`` | Get the current version of the package, more information in | -| | ":ref:`book-templating-assets`". | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form(view, variables = {})`` | This will render the HTML of a complete form, more information in | -| | in :ref:`the Twig Form reference`. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_start(view, variables = {})`` | This will render the HTML start tag of a form, more information in | -| | in :ref:`the Twig Form reference`. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_end(view, variables = {})`` | This will render the HTML end tag of a form together with all fields that | -| | have not been rendered yet, more information | -| | in :ref:`the Twig Form reference`. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_enctype(view)`` | This will render the required ``enctype="multipart/form-data"`` attribute | -| | if the form contains at least one file upload field, more information in | -| | in :ref:`the Twig Form reference `. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_widget(view, variables = {})`` | This will render a complete form or a specific HTML widget of a field, | -| | more information in :ref:`the Twig Form reference `. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_errors(view)`` | This will render any errors for the given field or the "global" errors, | -| | more information in :ref:`the Twig Form reference `. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_label(view, label = null, variables = {})`` | This will render the label for the given field, more information in | -| | :ref:`the Twig Form reference `. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_row(view, variables = {})`` | This will render the row (the field's label, errors and widget) of the given | -| | field, more information in :ref:`the Twig Form reference `. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``form_rest(view, variables = {})`` | This will render all fields that have not yet been rendered, more | -| | information in :ref:`the Twig Form reference `. | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``csrf_token(intention)`` | This will render a CSRF token. Use this function if you want CSRF protection without | -| | creating a form | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``is_granted(role, object = null, field = null)`` | This will return ``true`` if the current user has the required role, more | -| | information in ":ref:`book-security-template`" | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``logout_path(key)`` | This will generate the relative logout URL for the given firewall | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``logout_url(key)`` | Equal to ``logout_path(...)`` but this will generate an absolute URL | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``path(name, parameters = {})`` | Get a relative URL for the given route, more information in | -| | ":ref:`book-templating-pages`". | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``url(name, parameters = {})`` | Equal to ``path(...)`` but it generates an absolute URL | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ -| ``expression(expression)`` | Creates an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` in Twig. See | -| | ":ref:`Template Expressions `". | -+-------------------------------------------------------+--------------------------------------------------------------------------------------------+ ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| Function Syntax | Usage | ++=======================================================================+============================================================================================+ +| ``render(uri, options = {})`` | This will render the fragment for the given controller or URL | +| ``render(controller('B:C:a', {params}))`` | For more information, see :ref:`templating-embedding-controller`. | +| ``render(path('route', {params}))`` | | +| ``render(url('route', {params}))`` | | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``render_esi(controller('B:C:a', {params}))`` | This will generate an ESI tag when possible or fallback to the ``render`` | +| ``render_esi(url('route', {params}))`` | behavior otherwise. For more information, see :ref:`templating-embedding-controller`. | +| ``render_esi(path('route', {params}))`` | | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``render_hinclude(controller(...))`` | This will generates an Hinclude tag for the given controller or URL. | +| ``render_hinclude(url('route', {params}))`` | For more information, see :ref:`templating-embedding-controller`. | +| ``render_hinclude(path('route', {params}))`` | | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``controller(attributes = {}, query = {})`` | Used along with the ``render`` tag to refer to the controller that you want to render. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``asset(path, packageName = null, absolute = false, version = null)`` | Get the public path of the asset, more information in | +| | ":ref:`book-templating-assets`". | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``asset_version(packageName = null)`` | Get the current version of the package, more information in | +| | ":ref:`book-templating-assets`". | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form(view, variables = {})`` | This will render the HTML of a complete form, more information in | +| | in :ref:`the Twig Form reference`. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_start(view, variables = {})`` | This will render the HTML start tag of a form, more information in | +| | in :ref:`the Twig Form reference`. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_end(view, variables = {})`` | This will render the HTML end tag of a form together with all fields that | +| | have not been rendered yet, more information | +| | in :ref:`the Twig Form reference`. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_enctype(view)`` | This will render the required ``enctype="multipart/form-data"`` attribute | +| | if the form contains at least one file upload field, more information in | +| | in :ref:`the Twig Form reference `. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_widget(view, variables = {})`` | This will render a complete form or a specific HTML widget of a field, | +| | more information in :ref:`the Twig Form reference `. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_errors(view)`` | This will render any errors for the given field or the "global" errors, | +| | more information in :ref:`the Twig Form reference `. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_label(view, label = null, variables = {})`` | This will render the label for the given field, more information in | +| | :ref:`the Twig Form reference `. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_row(view, variables = {})`` | This will render the row (the field's label, errors and widget) of the given | +| | field, more information in :ref:`the Twig Form reference `. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``form_rest(view, variables = {})`` | This will render all fields that have not yet been rendered, more | +| | information in :ref:`the Twig Form reference `. | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``csrf_token(intention)`` | This will render a CSRF token. Use this function if you want CSRF protection without | +| | creating a form | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``is_granted(role, object = null, field = null)`` | This will return ``true`` if the current user has the required role, more | +| | information in ":ref:`book-security-template`" | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``logout_path(key)`` | This will generate the relative logout URL for the given firewall | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``logout_url(key)`` | Equal to ``logout_path(...)`` but this will generate an absolute URL | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``path(name, parameters = {})`` | Get a relative URL for the given route, more information in | +| | ":ref:`book-templating-pages`". | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``url(name, parameters = {})`` | Equal to ``path(...)`` but it generates an absolute URL | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``expression(expression)`` | Creates an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` in Twig. See | +| | ":ref:`Template Expressions `". | ++-----------------------------------------------------------------------+--------------------------------------------------------------------------------------------+ Filters ------- From 456da2802373c6a6be0f719210ee791c5cf9e3ed Mon Sep 17 00:00:00 2001 From: Stefano Sala Date: Sat, 11 Jan 2014 10:41:56 +0100 Subject: [PATCH 088/835] [Form] Deprecated max_length and pattern options --- book/forms.rst | 6 +++--- reference/forms/twig_reference.rst | 6 ++++-- reference/forms/types/email.rst | 2 +- reference/forms/types/form.rst | 4 ++-- reference/forms/types/options/max_length.rst.inc | 12 +++++++++--- reference/forms/types/options/pattern.rst.inc | 5 +++++ reference/forms/types/password.rst | 2 +- reference/forms/types/search.rst | 2 +- reference/forms/types/text.rst | 2 +- reference/forms/types/textarea.rst | 2 +- reference/forms/types/url.rst | 2 +- 11 files changed, 29 insertions(+), 16 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index c21c44f6d68..0f318b3be0c 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -707,8 +707,8 @@ the correct values of a number of field options. (i.e. is the field ``nullable``). This is very useful, as your client-side validation will automatically match your validation rules. -* ``max_length``: If the field is some sort of text field, then the ``max_length`` - option can be guessed from the validation constraints (if ``Length`` or +* ``maxlength``: If the field is some sort of text field, then the ``maxlength`` + option attribute can be guessed from the validation constraints (if ``Length`` or ``Range`` is used) or from the Doctrine metadata (via the field's length). .. note:: @@ -719,7 +719,7 @@ the correct values of a number of field options. If you'd like to change one of the guessed values, you can override it by passing the option in the options field array:: - ->add('task', null, array('max_length' => 4)) + ->add('task', null, array('attr' => array('maxlength' => 4))) .. index:: single: Forms; Rendering in a template diff --git a/reference/forms/twig_reference.rst b/reference/forms/twig_reference.rst index 0bd5d8d6771..fc19dc08b11 100644 --- a/reference/forms/twig_reference.rst +++ b/reference/forms/twig_reference.rst @@ -350,9 +350,11 @@ object: | ``required`` | If ``true``, a ``required`` attribute is added to the field to activate HTML5 | | | validation. Additionally, a ``required`` class is added to the label. | +------------------------+-------------------------------------------------------------------------------------+ -| ``max_length`` | Adds a ``maxlength`` HTML attribute to the element. | +| ``max_length`` | Adds a ``maxlength`` HTML attribute to the element. (deprecated as of 2.5, to be | +| | removed in 3.0, use ``attr["maxlength"]`` instead) | +------------------------+-------------------------------------------------------------------------------------+ -| ``pattern`` | Adds a ``pattern`` HTML attribute to the element. | +| ``pattern`` | Adds a ``pattern`` HTML attribute to the element. (deprecated as of 2.5, to be | +| | removed in 3.0, use ``attr["pattern"]`` instead) | +------------------------+-------------------------------------------------------------------------------------+ | ``label`` | The string label that will be rendered. | +------------------------+-------------------------------------------------------------------------------------+ diff --git a/reference/forms/types/email.rst b/reference/forms/types/email.rst index 33908563a9a..e03177a47e1 100644 --- a/reference/forms/types/email.rst +++ b/reference/forms/types/email.rst @@ -10,7 +10,7 @@ The ``email`` field is a text field that is rendered using the HTML5 +-------------+---------------------------------------------------------------------+ | Rendered as | ``input`` ``email`` field (a text box) | +-------------+---------------------------------------------------------------------+ -| Inherited | - `max_length`_ | +| Inherited | - `max_length`_ (deprecated as of 2.5) | | options | - `empty_data`_ | | | - `required`_ | | | - `label`_ | diff --git a/reference/forms/types/form.rst b/reference/forms/types/form.rst index 38416d12aa9..8aedffb86f7 100644 --- a/reference/forms/types/form.rst +++ b/reference/forms/types/form.rst @@ -20,7 +20,7 @@ on all types for which ``form`` is the parent type. | | - `trim`_ | | | - `mapped`_ | | | - `property_path`_ | -| | - `max_length`_ | +| | - `max_length`_ (deprecated as of 2.5) | | | - `by_reference`_ | | | - `error_bubbling`_ | | | - `inherit_data`_ | @@ -29,7 +29,7 @@ on all types for which ``form`` is the parent type. | | - `invalid_message_parameters`_ | | | - `extra_fields_message`_ | | | - `post_max_size_message`_ | -| | - `pattern`_ | +| | - `pattern`_ (deprecated as of 2.5) | +-----------+--------------------------------------------------------------------+ | Inherited | - `block_name`_ | | options | - `disabled`_ | diff --git a/reference/forms/types/options/max_length.rst.inc b/reference/forms/types/options/max_length.rst.inc index d20d44e1199..85874698d15 100644 --- a/reference/forms/types/options/max_length.rst.inc +++ b/reference/forms/types/options/max_length.rst.inc @@ -1,10 +1,16 @@ +.. caution:: + + The ``max_length`` option has been deprecated and will be removed in 3.0. + Instead, use the ``attr`` option by setting it to an array with a ``maxlength`` key. + max_length ~~~~~~~~~~ **type**: ``integer`` **default**: ``null`` -If this option is not null, an attribute ``maxlength`` is added, which -is used by some browsers to limit the amount of text in a field. +If this option is not null, an attribute ``maxlength`` is added, which +is used by some browsers to limit the amount of text in a field. -This is just a browser validation, so data must still be validated +This is just a browser validation, so data must still be validated server-side. + diff --git a/reference/forms/types/options/pattern.rst.inc b/reference/forms/types/options/pattern.rst.inc index d1ff3aea732..65c3870961a 100644 --- a/reference/forms/types/options/pattern.rst.inc +++ b/reference/forms/types/options/pattern.rst.inc @@ -1,3 +1,8 @@ +.. caution:: + + The ``pattern`` option has been deprecated and will be removed in 3.0. + Instead, use the ``attr`` option by setting it to an array with a ``pattern`` key. + pattern ~~~~~~~ diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index 12ed8b17d77..fa9df3f3b12 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -11,7 +11,7 @@ The ``password`` field renders an input password text box. +-------------+------------------------------------------------------------------------+ | Options | - `always_empty`_ | +-------------+------------------------------------------------------------------------+ -| Inherited | - `max_length`_ | +| Inherited | - `max_length`_ (deprecated as of 2.5) | | options | - `empty_data`_ | | | - `required`_ | | | - `label`_ | diff --git a/reference/forms/types/search.rst b/reference/forms/types/search.rst index 9ff7b9d8393..cc1ceeccb8d 100644 --- a/reference/forms/types/search.rst +++ b/reference/forms/types/search.rst @@ -12,7 +12,7 @@ Read about the input search field at `DiveIntoHTML5.info`_ +-------------+----------------------------------------------------------------------+ | Rendered as | ``input search`` field | +-------------+----------------------------------------------------------------------+ -| Inherited | - `max_length`_ | +| Inherited | - `max_length`_ (deprecated as of 2.5) | | options | - `empty_data`_ | | | - `required`_ | | | - `label`_ | diff --git a/reference/forms/types/text.rst b/reference/forms/types/text.rst index 675b1f800ae..c61de693ab9 100644 --- a/reference/forms/types/text.rst +++ b/reference/forms/types/text.rst @@ -9,7 +9,7 @@ The text field represents the most basic input text field. +-------------+--------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+--------------------------------------------------------------------+ -| Inherited | - `max_length`_ | +| Inherited | - `max_length`_ (deprecated as of 2.5) | | options | - `empty_data`_ | | | - `required`_ | | | - `label`_ | diff --git a/reference/forms/types/textarea.rst b/reference/forms/types/textarea.rst index da27d4e9efb..0963abadbad 100644 --- a/reference/forms/types/textarea.rst +++ b/reference/forms/types/textarea.rst @@ -9,7 +9,7 @@ Renders a ``textarea`` HTML element. +-------------+------------------------------------------------------------------------+ | Rendered as | ``textarea`` tag | +-------------+------------------------------------------------------------------------+ -| Inherited | - `max_length`_ | +| Inherited | - `max_length`_ (deprecated as of 2.5) | | options | - `empty_data`_ | | | - `required`_ | | | - `label`_ | diff --git a/reference/forms/types/url.rst b/reference/forms/types/url.rst index 44a68d68218..ecd29752cb0 100644 --- a/reference/forms/types/url.rst +++ b/reference/forms/types/url.rst @@ -13,7 +13,7 @@ have a protocol. +-------------+-------------------------------------------------------------------+ | Options | - `default_protocol`_ | +-------------+-------------------------------------------------------------------+ -| Inherited | - `max_length`_ | +| Inherited | - `max_length`_ (deprecated as of 2.5) | | options | - `empty_data`_ | | | - `required`_ | | | - `label`_ | From efbe0f3b04de19385f7e50d0a63f0a25c8e22e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gildas=20Qu=C3=A9m=C3=A9ner?= Date: Thu, 3 Apr 2014 16:43:52 +0200 Subject: [PATCH 089/835] Fixed a typo --- cookbook/request/mime_type.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/request/mime_type.rst b/cookbook/request/mime_type.rst index 6879407da8c..11851c02031 100644 --- a/cookbook/request/mime_type.rst +++ b/cookbook/request/mime_type.rst @@ -21,7 +21,7 @@ and corresponding MIME type. Configure your New Format ------------------------- -The FrameworkBundle registers a subscriber that will add formats to incomming requests. +The FrameworkBundle registers a subscriber that will add formats to incoming requests. All you have to do is to configure the ``jsonp`` format: From f4ee337449ca7c19d1ebd09083e99e1a91bd5e38 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Thu, 3 Apr 2014 14:41:33 +0200 Subject: [PATCH 090/835] [Console] Add documentation for QuestionHelper --- components/console/helpers/dialoghelper.rst | 9 + components/console/helpers/index.rst | 1 + components/console/helpers/map.rst.inc | 1 + components/console/helpers/questionhelper.rst | 263 ++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 components/console/helpers/questionhelper.rst diff --git a/components/console/helpers/dialoghelper.rst b/components/console/helpers/dialoghelper.rst index 334b53ef86f..c84724d66b2 100644 --- a/components/console/helpers/dialoghelper.rst +++ b/components/console/helpers/dialoghelper.rst @@ -4,6 +4,13 @@ Dialog Helper ============= +.. caution:: + + The Dialog Helper was deprecated in Symfony 2.5 and will be removed in + Symfony 3.0. You should now use the + :doc:`Question Helper ` instead, + which is simpler to use. + The :class:`Symfony\\Component\\Console\\Helper\\DialogHelper` provides functions to ask the user for more information. It is included in the default helper set, which you can get by calling @@ -149,6 +156,8 @@ You can also ask and validate a hidden response:: if (trim($value) == '') { throw new \Exception('The password can not be empty'); } + + return $value; }; $password = $dialog->askHiddenResponseAndValidate( diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index 357b4bc071d..d632eeeb66a 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -11,6 +11,7 @@ The Console Helpers formatterhelper progressbar progresshelper + questionhelper table tablehelper diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index ff746b14a78..b9a1114cfc2 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -2,5 +2,6 @@ * :doc:`/components/console/helpers/formatterhelper` * :doc:`/components/console/helpers/progressbar` * :doc:`/components/console/helpers/progresshelper` +* :doc:`/components/console/helpers/questionhelper` * :doc:`/components/console/helpers/table` * :doc:`/components/console/helpers/tablehelper` diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst new file mode 100644 index 00000000000..0f1031392c1 --- /dev/null +++ b/components/console/helpers/questionhelper.rst @@ -0,0 +1,263 @@ +.. index:: + single: Console Helpers; Question Helper + +Question Helper +=============== + +.. versionadded:: 2.5 + The Question Helper was introduced in Symfony 2.5. + +The :class:`Symfony\\Component\\Console\\Helper\\QuestionHelper` provides +functions to ask the user for more information. It is included in the default +helper set, which you can get by calling +:method:`Symfony\\Component\\Console\\Command\\Command::getHelperSet`:: + + $helper = $this->getHelperSet()->get('question'); + +The Question Helper has a single method +:method:`Symfony\\Component\\Console\\Command\\Command::ask` that needs an +:class:`Symfony\\Component\\Console\\Output\\InputInterface` instance as the +first argument, an :class:`Symfony\\Component\\Console\\Output\\OutputInterface` +instance as the second argument and a +:class:`Symfony\\Component\\Console\\Question\\Question` as last argument. + +Asking the User for Confirmation +-------------------------------- + +Suppose you want to confirm an action before actually executing it. Add +the following to your command:: + + use Symfony\Component\Console\Question\ConfirmationQuestion; + // ... + + $helper = $this->getHelperSet()->get('question'); + $question = new ConfirmationQuestion('Continue with this action?', false); + + if (!$helper->ask($input, $output, $question)) { + return; + } + +In this case, the user will be asked "Continue with this action?". If the user +answers with ``y`` it returns ``true`` or ``false`` if they answer with ``n``. +The second argument to +:method:`Symfony\\Component\\Console\\Question\\ConfirmationQuestion::__construct` +is the default value to return if the user doesn't enter any input. Any other +input will ask the same question again. + +Asking the User for Information +------------------------------- + +You can also ask a question with more than a simple yes/no answer. For instance, +if you want to know a bundle name, you can add this to your command:: + + use Symfony\Component\Console\Question\Question; + // ... + + $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle'); + + $bundle = $helper->ask($input, $output, $question); + +The user will be asked "Please enter the name of the bundle". They can type +some name which will be returned by the +:method:`Symfony\\Component\\Console\\Helper\\QuestionHelper::ask` method. +If they leave it empty, the default value (``AcmeDemoBundle`` here) is returned. + +Let the User Choose from a List of Answers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you have a predefined set of answers the user can choose from, you +could use a :class:`Symfony\\Component\\Console\\Question\\ChoiceQuestion` +which makes sure that the user can only enter a valid string +from a predefined list:: + + use Symfony\Component\Console\Question\ChoiceQuestion; + // ... + + $helper = $app->getHelperSet()->get('question'); + $question = new ChoiceQuestion( + 'Please select your favorite color (defaults to red)', + array('red', 'blue', 'yellow'), + 'red' + ); + $question->setErrorMessage('Color %s is invalid.'); + + $color = $helper->ask($input, $output, $question); + $output->writeln('You have just selected: '.$color); + + // ... do something with the color + +The option which should be selected by default is provided with the third +argument of the constructor. The default is ``null``, which means that no +option is the default one. + +If the user enters an invalid string, an error message is shown and the user +is asked to provide the answer another time, until they enter a valid string +or reach the maximum number of attempts. The default value for the maximum number +of attempts is ``null``, which means infinite number attempts. You can define +your own error message using +:method:`Symfony\\Component\\Console\\Question\\ChoiceQuestion::setErrorMessage`. + +Multiple Choices +................ + +Sometimes, multiple answers can be given. The ``ChoiceQuestion`` provides this +feature using comma separated values. This is disabled by default, to enable +this use :method:`Symfony\\Component\\Console\\Question\\ChoiceQuestion::setMultiselect`:: + + use Symfony\Component\Console\Question\ChoiceQuestion; + // ... + + $helper = $app->getHelperSet()->get('question'); + $question = new ChoiceQuestion( + 'Please select your favorite color (defaults to red)', + array('red', 'blue', 'yellow'), + 'red' + ); + $question->setMultiselect(true); + + $colors = $helper->ask($input, $output, $question); + $output->writeln('You have just selected: ' . implode(', ', $colors)); + +Now, when the user enters ``1,2``, the result will be: +``You have just selected: blue, yellow``. + +Autocompletion +~~~~~~~~~~~~~~ + +You can also specify an array of potential answers for a given question. These +will be autocompleted as the user types:: + + use Symfony\Component\Console\Question\Question; + // ... + + $bundles = array('AcmeDemoBundle', 'AcmeBlogBundle', 'AcmeStoreBundle'); + $question = new Question('Please enter the name of a bundle', 'FooBundle'); + $question->setAutocompleterValues($bundles); + + $name = $helper->ask($input, $output, $question); + +Hiding the User's Response +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also ask a question and hide the response. This is particularly +convenient for passwords:: + + use Symfony\Component\Console\Question\Question; + // ... + + $question = new Question('What is the database password?'); + $question->setHidden(true); + $question->setHiddenFallback(false); + + $password = $helper->ask($input, $output, $question); + +.. caution:: + + When you ask for a hidden response, Symfony will use either a binary, change + stty mode or use another trick to hide the response. If none is available, + it will fallback and allow the response to be visible unless you set this + behavior to ``false`` using + :method:`Symfony\\Component\\Console\\Question\\Question::setHiddenFallback` + like in the example above. In this case, a ``RuntimeException`` + would be thrown. + +Validating the Answer +--------------------- + +You can even validate the answer. For instance, in a previous example you asked +for the bundle name. Following the Symfony naming conventions, it should +be suffixed with ``Bundle``. You can validate that by using the +:method:`Symfony\\Component\\Console\\Question\\Question::setValidator` +method:: + + use Symfony\Component\Console\Question\Question; + // ... + + $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle'); + $question->setValidator(function ($answer) { + if ('Bundle' !== substr($answer, -6)) { + throw new \RuntimeException( + 'The name of the bundle should be suffixed with \'Bundle\'' + ); + } + return $answer; + }); + $question->setMaxAttempts(2); + + $name = $helper->ask($input, $output, $question); + +The ``$validator`` is a callback which handles the validation. It should +throw an exception if there is something wrong. The exception message is displayed +in the console, so it is a good practice to put some useful information in it. The +callback function should also return the value of the user's input if the validation +was successful. + +You can set the max number of times to ask with the +:method:`Symfony\\Component\\Console\\Question\\Question::setMaxAttempts` method. +If you reach this max number it will use the default value. Using ``null`` means +the amount of attempts is infinite. The user will be asked as long as they provide an +invalid answer and will only be able to proceed if their input is valid. + +Validating a Hidden Response +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also use a validator with a hidden question:: + + use Symfony\Component\Console\Question\Question; + // ... + + $helper = $this->getHelperSet()->get('question'); + + $question = new Question('Please enter your password'); + $question->setValidator(function ($value) { + if (trim($value) == '') { + throw new \Exception('The password can not be empty'); + } + + return $value; + }); + $question->setHidden(true); + $question->setMaxAttempts(20); + + $password = $helper->ask($input, $output, $question); + + +Testing a Command that Expects Input +------------------------------------ + +If you want to write a unit test for a command which expects some kind of input +from the command line, you need to set the helper input stream:: + + use Symfony\Component\Console\Helper\QuestionHelper; + use Symfony\Component\Console\Helper\HelperSet; + use Symfony\Component\Console\Tester\CommandTester; + + // ... + public function testExecute() + { + // ... + $commandTester = new CommandTester($command); + + $helper = $command->getHelper('question'); + $helper->setInputStream($this->getInputStream('Test\\n')); + // Equals to a user inputting "Test" and hitting ENTER + // If you need to enter a confirmation, "yes\n" will work + + $commandTester->execute(array('command' => $command->getName())); + + // $this->assertRegExp('/.../', $commandTester->getDisplay()); + } + + protected function getInputStream($input) + { + $stream = fopen('php://memory', 'r+', false); + fputs($stream, $input); + rewind($stream); + + return $stream; + } + +By setting the input stream of the ``QuestionHelper``, you imitate what the +console would do internally with all user input through the cli. This way +you can test any user interaction (even complex ones) by passing an appropriate +input stream. From 1705231f31b0120ab40f0a7dfb1cb1fc834dc7cc Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Thu, 3 Apr 2014 15:53:10 +0200 Subject: [PATCH 091/835] [Console] Add Process Helper documentation --- components/console/helpers/index.rst | 1 + components/console/helpers/map.rst.inc | 1 + components/console/helpers/processhelper.rst | 56 ++++++++++++++++++ .../console/process-helper-debug.png | Bin 0 -> 28822 bytes .../console/process-helper-error-debug.png | Bin 0 -> 19108 bytes .../console/process-helper-verbose.png | Bin 0 -> 14289 bytes 6 files changed, 58 insertions(+) create mode 100644 components/console/helpers/processhelper.rst create mode 100644 images/components/console/process-helper-debug.png create mode 100644 images/components/console/process-helper-error-debug.png create mode 100644 images/components/console/process-helper-verbose.png diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index 357b4bc071d..7fcfe78fd4b 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -9,6 +9,7 @@ The Console Helpers dialoghelper formatterhelper + processhelper progressbar progresshelper table diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index ff746b14a78..fbf2e8e8602 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,5 +1,6 @@ * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` +* :doc:`/components/console/helpers/processhelper` * :doc:`/components/console/helpers/progressbar` * :doc:`/components/console/helpers/progresshelper` * :doc:`/components/console/helpers/table` diff --git a/components/console/helpers/processhelper.rst b/components/console/helpers/processhelper.rst new file mode 100644 index 00000000000..f9fd141ffcd --- /dev/null +++ b/components/console/helpers/processhelper.rst @@ -0,0 +1,56 @@ +.. index:: + single: Console Helpers; Process Helper + +Process Helper +============== + +.. versionadded:: 2.5 + The Process Helper was introduced in Symfony 2.5. + +The Process Helper shows processes as they're running and reports +useful information about process status. + +To display process details, use the :class:`Symfony\\Component\\Console\\Helper\\ProcessHelper` +and run your command with verbosity. For example, running the following code with +a very verbose verbosity (e.g. -vv):: + + use Symfony\Component\Process\ProcessBuilder; + + $helper = $this->getHelperSet()->get('process'); + $process = ProcessBuilder::create(array('figlet', 'Symfony'))->getProcess(); + + $helper->run($output, $process); + +will result in this output: + +.. image:: /images/components/console/process-helper-verbose.png + +It will result in more detailed output with debug verbosity (e.g. -vvv): + +.. image:: /images/components/console/process-helper-debug.png + +In case the process fails, debugging is easier: + +.. image:: /images/components/console/process-helper-error-debug.png + +There are three ways to use the process helper: using a command line string, an array +of arguments that would be escaped or a :class:`Symfony\\Component\\Process\\Process` +object. + +You can display a customized error message using the third argument of the +:method:`Symfony\\Component\\Console\\Helper\\ProcessHelper::run` method:: + + $helper->run($output, $process, 'The process failed :('); + +A custom process callback can be passed as fourth argument, refer to the +:doc:`Process Component ` for callback documentation:: + + use Symfony\Component\Process\Process; + + $helper->run($output, $process, 'The process failed :(', function ($type, $data) { + if (Process::ERR === $type) { + // do something with the stderr output + } else { + // do something with the stdout + } + }); diff --git a/images/components/console/process-helper-debug.png b/images/components/console/process-helper-debug.png new file mode 100644 index 0000000000000000000000000000000000000000..282e1336389762bb88f5fa6d61d4669066c483de GIT binary patch literal 28822 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 literal 0 HcmV?d00001 diff --git a/images/components/console/process-helper-error-debug.png b/images/components/console/process-helper-error-debug.png new file mode 100644 index 0000000000000000000000000000000000000000..8d1145478f2e3a0d6c0ee2074ea50883bd7c5107 GIT binary patch literal 19108 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 QS79}f`_V2ur+}ot0sK-87XSbN literal 0 HcmV?d00001 diff --git a/images/components/console/process-helper-verbose.png b/images/components/console/process-helper-verbose.png new file mode 100644 index 0000000000000000000000000000000000000000..c4c912e14331dc53482e25571d1f16ad4018765a GIT binary patch literal 14289 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 literal 0 HcmV?d00001 From 319bf2935113d28840007e49b5702b0c2f683156 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 12 Apr 2014 10:38:34 -0400 Subject: [PATCH 092/835] [#3729] Making minor changes thansk to @WouterJ and @xabbuh. --- components/property_access/introduction.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 2fabbf2119b..7b1193cb9fa 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -314,12 +314,13 @@ Checking Property Paths .. versionadded:: 2.5 The methods - :method:`PropertyAccessor::isReadable` + :method:`PropertyAccessor::isReadable ` and - :method:`PropertyAccessor::isWritable` - methods were added in Symfony 2.5. + :method:`PropertyAccessor::isWritable ` + methods were introduced in Symfony 2.5. -When you want to check whether :method:`PropertyAccessor::getValue` +When you want to check whether +:method:`PropertyAccessor::getValue` can safely be called without actually calling that method, you can use :method:`PropertyAccessor::isReadable` instead:: From fb9fe9971ebcf414a819f129c53c6ff7407967b5 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 12 Apr 2014 10:38:43 -0400 Subject: [PATCH 093/835] [#3729] Removing 3rd argument to isWritable - this doesn't exist in the final merged item --- components/property_access/introduction.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 7b1193cb9fa..dfd457f7d4d 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -334,12 +334,11 @@ instead:: The same is possible for :method:`PropertyAccessor::setValue`: Call the :method:`PropertyAccessor::isWritable` -method to find out whether a property path can be updated. In the third -argument, you should pass the value that you want to write:: +method to find out whether a property path can be updated:: $person = new Person(); - if ($accessor->isWritable($person, 'firstName', 'Wouter') { + if ($accessor->isWritable($person, 'firstName') { // ... } From 552c780925cbc36a1896813250e1f6057be69f4f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 12 Apr 2014 11:35:28 -0400 Subject: [PATCH 094/835] Revert "[Console] Add Process Helper documentation" This reverts commit 1705231f31b0120ab40f0a7dfb1cb1fc834dc7cc. --- components/console/helpers/index.rst | 1 - components/console/helpers/map.rst.inc | 1 - components/console/helpers/processhelper.rst | 56 ------------------ .../console/process-helper-debug.png | Bin 28822 -> 0 bytes .../console/process-helper-error-debug.png | Bin 19108 -> 0 bytes .../console/process-helper-verbose.png | Bin 14289 -> 0 bytes 6 files changed, 58 deletions(-) delete mode 100644 components/console/helpers/processhelper.rst delete mode 100644 images/components/console/process-helper-debug.png delete mode 100644 images/components/console/process-helper-error-debug.png delete mode 100644 images/components/console/process-helper-verbose.png diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index 7fcfe78fd4b..357b4bc071d 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -9,7 +9,6 @@ The Console Helpers dialoghelper formatterhelper - processhelper progressbar progresshelper table diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index fbf2e8e8602..ff746b14a78 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,6 +1,5 @@ * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` -* :doc:`/components/console/helpers/processhelper` * :doc:`/components/console/helpers/progressbar` * :doc:`/components/console/helpers/progresshelper` * :doc:`/components/console/helpers/table` diff --git a/components/console/helpers/processhelper.rst b/components/console/helpers/processhelper.rst deleted file mode 100644 index f9fd141ffcd..00000000000 --- a/components/console/helpers/processhelper.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. index:: - single: Console Helpers; Process Helper - -Process Helper -============== - -.. versionadded:: 2.5 - The Process Helper was introduced in Symfony 2.5. - -The Process Helper shows processes as they're running and reports -useful information about process status. - -To display process details, use the :class:`Symfony\\Component\\Console\\Helper\\ProcessHelper` -and run your command with verbosity. For example, running the following code with -a very verbose verbosity (e.g. -vv):: - - use Symfony\Component\Process\ProcessBuilder; - - $helper = $this->getHelperSet()->get('process'); - $process = ProcessBuilder::create(array('figlet', 'Symfony'))->getProcess(); - - $helper->run($output, $process); - -will result in this output: - -.. image:: /images/components/console/process-helper-verbose.png - -It will result in more detailed output with debug verbosity (e.g. -vvv): - -.. image:: /images/components/console/process-helper-debug.png - -In case the process fails, debugging is easier: - -.. image:: /images/components/console/process-helper-error-debug.png - -There are three ways to use the process helper: using a command line string, an array -of arguments that would be escaped or a :class:`Symfony\\Component\\Process\\Process` -object. - -You can display a customized error message using the third argument of the -:method:`Symfony\\Component\\Console\\Helper\\ProcessHelper::run` method:: - - $helper->run($output, $process, 'The process failed :('); - -A custom process callback can be passed as fourth argument, refer to the -:doc:`Process Component ` for callback documentation:: - - use Symfony\Component\Process\Process; - - $helper->run($output, $process, 'The process failed :(', function ($type, $data) { - if (Process::ERR === $type) { - // do something with the stderr output - } else { - // do something with the stdout - } - }); diff --git a/images/components/console/process-helper-debug.png b/images/components/console/process-helper-debug.png deleted file mode 100644 index 282e1336389762bb88f5fa6d61d4669066c483de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28822 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 diff --git a/images/components/console/process-helper-error-debug.png b/images/components/console/process-helper-error-debug.png deleted file mode 100644 index 8d1145478f2e3a0d6c0ee2074ea50883bd7c5107..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19108 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 QS79}f`_V2ur+}ot0sK-87XSbN diff --git a/images/components/console/process-helper-verbose.png b/images/components/console/process-helper-verbose.png deleted file mode 100644 index c4c912e14331dc53482e25571d1f16ad4018765a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14289 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 From eef3313d69a5ce5f231c91cf3cfedd9cac0d8e7c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 12 Apr 2014 11:47:27 -0400 Subject: [PATCH 095/835] [#3752] Adding the question helper to the helper list and marking dialog as deprecated --- components/console/introduction.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 801ac379344..963c07bb30f 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -402,10 +402,11 @@ Console Helpers The console component also contains a set of "helpers" - different small tools capable of helping you with different tasks: -* :doc:`/components/console/helpers/dialoghelper`: interactively ask the user for information +* :doc:`/components/console/helpers/questionhelper`: interactively ask the user for information * :doc:`/components/console/helpers/formatterhelper`: customize the output colorization * :doc:`/components/console/helpers/progresshelper`: shows a progress bar * :doc:`/components/console/helpers/tablehelper`: displays tabular data as a table +* :doc:`/components/console/helpers/dialoghelper`: (deprecated) interactively ask the user for information Testing Commands ---------------- From 98288fcf789197b5c7a26a6a44ccbdabc911690f Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 13 Apr 2014 12:43:31 +0200 Subject: [PATCH 096/835] Updated changelog for March --- changelog.rst | 118 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/changelog.rst b/changelog.rst index ca2995a01cc..0187663a180 100644 --- a/changelog.rst +++ b/changelog.rst @@ -141,4 +141,120 @@ Minor Documentation Changes - `80a90ba `_ #3500 Minimize horizontal scrolling in code blocks (improve readability) (ifdattic) - `e5bc4ea `_ #3498 Remove second empty data (xabbuh) - `d084d87 `_ #3485 [Cookbook][Assetic] Fix "javascripts" tag name typo (bicpi) -- `3250aba `_ #3481 Fix code block (minimise horizontal scrolling), typo in yaml (ifdattic) \ No newline at end of file +- `3250aba `_ #3481 Fix code block (minimise horizontal scrolling), typo in yaml (ifdattic) + +March, 2014 +----------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `3b640aa `_ #3644 made some small addition about our BC promise and semantic versioning (fabpot) +- `2d1ecd9 `_ #3525 Update file_uploads.rst (juanmf) +- `b1e8f56 `_ #3368 The host parameter has to be in defaults, not requirements (MarieMinasyan) +- `b34fb64 `_ #3619 [Validator] Uuid constraint reference (colinodell) +- `d7027c0 `_ #3418 [Validation] Add "hasser" support (bicpi) +- `4fd5fc1 `_ #3539 [Stopwatch] Describe retrieval of StopwatchEvent (jochenvdv) +- `1908a15 `_ #3696 [Console] Added standalone PSR-3 compliant logger (dunglas) +- `c75b1a7 `_ #3621 [Console] Command as service (gnugat) +- `00a462a `_ minor #3658 Fix PSR coding standards error (ifdattic) +- `acf255d `_ #3328 [WIP] Travis integration (WouterJ) +- `450146e `_ #3681 Enhanced Firewall Restrictions docs (danez) +- `3e7028d `_ #3659 [Internals] Complete notification description for kernel.terminate (bicpi) +- `db3cde7 `_ #3124 Add note about the property attribute (Property Accessor) (raziel057) +- `5965ec8 `_ #3420 [Cookbook][Configuration] add configuration cookbook handlig parameters in Configurator class (cordoval) +- `dcf8e6e `_ #3402 Added documentation about new requests formats configuration (gquemener) +- `a1050eb `_ #3411 [Cookbook][Dynamic Form Modification] Add AJAX sample (bicpi) +- `842fd30 `_ #3683 [TwigBundle] Add documentation about generating absolute URL with the asset function (romainneutron) +- `fc1576a `_ #3664 [Process] Add doc for ``Process::disableOutput`` and ``Process::enableOutput`` (romainneutron) +- `3731e2e `_ #3686 Documentation of the new PSR-4 class loader. (derrabus) +- `5b915c2 `_ #3629 Added documentation for translation:debug (florianv) +- `6951460 `_ #3601 Added documentation for missing ctype extension (slavafomin) +- `df63740 `_ #3627 added docs for the new Table console helper (fabpot) +- `96bd81b `_ #3626 added documentation for the new Symfony 2.5 progress bar (fabpot) +- `b02c16a `_ #3565 added information on AuthenticationFailureHandlerInterface (samsamm777) +- `2657ee7 `_ #3597 Document how to create a custom type guesser (WouterJ) +- `5ad1599 `_ #3577 Development of custom error pages is impractical if you need to set kernel.debug=false (mpdude) +- `3f4b319 `_ #3610 [HttpFoundation] Add doc for ``Request::getContent()`` method (bicpi) +- `56bc266 `_ #3589 Finishing the Templating component docs (WouterJ) +- `d881181 `_ #3588 Documented all form variables (WouterJ) +- `5cda1c7 `_ #3311 Use KernelTestCase instead of WebTestCase for testing code only requiring the Container (johnkary) +- `e96e12d `_ #3234 [Cookbook] New cookbok: How to use the Cloud to send Emails (bicpi) +- `d5d64ce `_ #3436 [Reference][Form Types] Add missing docs for "action" and "method" option (bicpi) +- `3df34af `_ #3490 Tweaking Doctrine book chapter (WouterJ) +- `b9608a7 `_ #3594 New Data Voter Article (continuation) (weaverryan) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `cad38ae `_ #3721 tweaks to the Console logger (xabbuh) +- `06c56c1 `_ #3709 [Components][Security] Fix #3708 (bicpi) +- `aadc61d `_ #3707 make method supportsClass() in custom voter compatible with the interface's documentation (xabbuh) +- `65150f9 `_ #3637 Update render_without_controller.rst (94noni) +- `9fcccc7 `_ #3634 Fix goal of “framework.profiler.only_exceptions“ option which profile on each exceptions on controller (not only 500) (stephpy) +- `9dd8d96 `_ #3689 Fix cache warmer description (WouterJ) +- `6221f35 `_ #3671 miss extends keyword in define BlogController class (ghanbari) +- `4ce7a15 `_ #3543 Fix the definition of customizing form's global errors. (mtrojanowski) +- `5d4a3a4 `_ #3343 [Testing] Fix phpunit test dir paths (bicpi) +- `badaae7 `_ #3622 [Components][Routing] Fix addPrefix() sample code (bicpi) +- `de0a5e1 `_ #3665 [Cookbook][Test] fix sample code (inalgnu) +- `4ef746a `_ #3614 [Internals] Fix Profiler:find() arguments (bicpi) +- `0c41762 `_ #3600 [Security][Authentication] Fix instructions for creating password encoders (bicpi) +- `0ab1f24 `_ #3593 Clarified Default and ClassName groups (WouterJ) +- `178984b `_ #3648 [Routing] Remove outdated tip about sticky locale (bicpi) +- `fc28453 `_ #3039 use DebugClassLoader class from Decomponent instead of the one from ... (xabbuh) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `abca098 `_ #3726 Minor tweaks after merging #3644 by @stof and @xabbuh (weaverryan) +- `d16be31 `_ #3725 Minor tweaks related to #3368 (weaverryan) +- `aa9bb25 `_ #3636 Update security.rst (nomack84) +- `78425c6 `_ #3722 add "Commands as Services" chapter to the cookbook's map (xabbuh) +- `9f26da8 `_ #3720 [#3539] A backport of a sentence - the parts that apply to 2.3 (weaverryan) +- `4b611d6 `_ #3717 [master] Fixed versionadded blocks (WouterJ) +- `5a3ba1b `_ #3715 change variable name to a better fitting one (xabbuh) +- `499eb6c `_ #3714 [2.4] Versionadded consistency (WouterJ) +- `e7580c0 `_ #3713 Updated versionadded directives to use "introduced" (WouterJ) +- `e15afe0 `_ #3711 Simplified the Travis configuration (stof) +- `db1cda5 `_ #3700 [Cookbook][Security] Firewall restrictions tweaks (xabbuh) +- `5035837 `_ #3706 Add support for nginx (guiditoito) +- `00a462a `_ #3658 Fix PSR coding standards error (ifdattic) +- `868de1e `_ #3698 Dynamic form modification cookbook: Fix inclusion of code (michaelperrin) +- `15a9d25 `_ #3697 [Console] Change Command namespaces (dunglas) +- `41b2eb8 `_ #3693 Tweak to Absolute URL generation (weaverryan) +- `bd473db `_ #3563 Add another tip to setup permissions (tony-co) +- `67129b1 `_ #3611 [Reference][Forms] add an introductory table containing all options of the basic form type (xabbuh) +- `fd8f7ae `_ #3694 fix the referenced documents names (xabbuh) +- `d617011 `_ #3657 Fix typos, remove trailing whitespace. (ifdattic) +- `1b4f6a6 `_ #3656 Minimize horizontal scrolling, add missing characters, remove trailing whitespace. (ifdattic) +- `7c0c5d1 `_ #3653 Http cache validation rewording (weaverryan) +- `0fb2c5f `_ #3651 [Reference][Forms] remove the label_attr option which is not available in the button type (xabbuh) +- `69ac21b `_ #3642 Fixed some typos and formatting issues (javiereguiluz) +- `93c35d0 `_ #3641 Added some examples to the "services as parameters" section (javiereguiluz) +- `12a6676 `_ #3640 [minor] fixed one typo and one formatting issue (javiereguiluz) +- `9967b0c `_ #3638 [#3116] Fixing wrong table name - singular is used elsewhere (weaverryan) +- `4fbf1cd `_ #3635 [QuickTour] close opened literals (xabbuh) +- `27b3410 `_ #3692 [Book][Translations] fixing a code block (xabbuh) +- `2192c32 `_ #3650 Fixing some build errors (xabbuh) +- `fa3f531 `_ #3677 [Reference][Forms] Remove variables section from tables (xabbuh) +- `cd6d1de `_ #3676 remove unnecessary code block directive (xabbuh) +- `07822b8 `_ #3675 add missing code block directive (xabbuh) +- `739f43f `_ #3669 Fixed syntax highlighting (rvanlaarhoven) +- `1f384bc `_ #3631 Added documentation for message option of the ``True`` constraint (naitsirch) +- `f6a41b9 `_ #3630 Minor tweaks to form action/method (weaverryan) +- `ae755e0 `_ #3628 Added anchor for permissions (WouterJ) +- `6380113 `_ #3667 Update index.rst (NAYZO) +- `97ef2f7 `_ #3566 Changes ACL permission setting hints (MicheleOnGit) +- `9f7d742 `_ #3654 [Cookbook][Security] Fix VoterInterface signature (bicpi) +- `0a65b6f `_ #3608 [Reference][Forms] add versionadded directive for multiple option of file type (xabbuh) +- `e34204e `_ #3605 Fixed a plural issue (benjaminpaap) +- `e7d5a45 `_ #3599 [CHANGELOG] fix reference to contributing docs (xabbuh) +- `3582bf1 `_ #3598 add changelog to hidden toctree (xabbuh) +- `58b7f96 `_ #3596 [HTTP Cache] Validation model: Fix header name (bicpi) +- `6d1378e `_ #3592 Added a tip about hardcoding URLs in functional tests (javiereguiluz) +- `04cf9f8 `_ #3595 Collection of fixes and improvements (bicpi) +- `2ed0943 `_ #3645 Adjusted the BC rules to be consistent (stof) +- `664a0be `_ #3633 Added missing PHP syntax coloration (DerekRoth) +- `1714a31 `_ #3585 Use consistent method chaining in BlogBundle sample application (ockcyp) +- `cb61f4f `_ #3581 Add missing hyphen in HTTP Fundamentals page (ockcyp) From 560e010de144488f308a2b17f804ff833e8252bc Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 6 May 2014 07:56:24 -0500 Subject: [PATCH 097/835] Adding more details to be clear that you can set asset version globally or locally on an asset --- book/templating.rst | 29 ++++++++++--------- .../templating/helpers/assetshelper.rst | 3 +- reference/configuration/framework.rst | 4 +++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index d2a3301a8b8..7c3c31aa7a8 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -991,40 +991,43 @@ assets won't be cached when deployed. For example, ``/images/logo.png`` might look like ``/images/logo.png?v2``. For more information, see the :ref:`ref-framework-assets-version` configuration option. +.. _` + .. versionadded:: 2.5 - Absolute URLs for assets were introduced in Symfony 2.5. + Setting versioned URLs on an asset-by-asset basis were introduced in Symfony 2.5. -If you need absolute URLs for assets, you can set the third argument (or the -``absolute`` argument) to ``true``: +If you need to set a version for a specific asset, you can set the fourth +argument (or the ``version`` argument) to the desired version: .. configuration-block:: .. code-block:: html+jinja - Symfony! + Symfony! .. code-block:: html+php - Symfony! + Symfony! + +If you dont give a version or pass ``null``, the default package version +(from :ref:`ref-framework-assets-version`) wil be used. If you pass ``false``, +versioned URL will be deactivated for this asset. .. versionadded:: 2.5 - Versioned URLs for assets were introduced in Symfony 2.5. + Absolute URLs for assets were introduced in Symfony 2.5. -If you need versioned URLs for assets, you can set the fourth argument (or the -``version`` argument) to the desired version: +If you need absolute URLs for assets, you can set the third argument (or the +``absolute`` argument) to ``true``: .. configuration-block:: .. code-block:: html+jinja - Symfony! + Symfony! .. code-block:: html+php - Symfony! - -If you dont give a version or pass ``null``, the default package version will -be used. If you pass ``false``, versioned URL will be deactivated. + Symfony! .. index:: single: Templating; Including stylesheets and JavaScripts diff --git a/components/templating/helpers/assetshelper.rst b/components/templating/helpers/assetshelper.rst index 6f096d35c52..8e8dbf857bc 100644 --- a/components/templating/helpers/assetshelper.rst +++ b/components/templating/helpers/assetshelper.rst @@ -83,7 +83,8 @@ second is the version. For instance, ``%s?v=%s`` will be rendered as .. versionadded:: 2.5 On-demand versioned URLs for assets were introduced in Symfony 2.5. -You can also generate a versioned URL using the fourth argument of the helper: +You can also generate a versioned URL on an asset-by-asset basis using the +fourth argument of the helper: .. code-block:: html+php diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 79c62088a62..a57f578887d 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -369,6 +369,10 @@ Now, the same asset will be rendered as ``/images/logo.png?v2`` If you use this feature, you **must** manually increment the ``assets_version`` value before each deployment so that the query parameters change. +It's also possible to set the version value on an asset-by-asset basis (instead +of using the global version - e.g. ``v2`` - set here). See +:ref:`Versioning by Asset ` for details. + You can also control how the query string works via the `assets_version_format`_ option. From c01e87c9d6ad98f1acd245c2d53b6337d45c09d8 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 6 May 2014 16:40:38 +0200 Subject: [PATCH 098/835] Added April changelog --- changelog.rst | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/changelog.rst b/changelog.rst index 0187663a180..f18908513c4 100644 --- a/changelog.rst +++ b/changelog.rst @@ -258,3 +258,62 @@ Minor Documentation Changes - `664a0be `_ #3633 Added missing PHP syntax coloration (DerekRoth) - `1714a31 `_ #3585 Use consistent method chaining in BlogBundle sample application (ockcyp) - `cb61f4f `_ #3581 Add missing hyphen in HTTP Fundamentals page (ockcyp) + + +April, 2014 +----------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `322972e `_ #3803 [Book][Validation] configuration examples for the GroupSequenceProvider (xabbuh) +- `9e129bc `_ #3752 [Console] Add documentation for QuestionHelper (romainneutron) +- `64a924d `_ #3756 [WCM][Console] Add Process Helper documentation (romainneutron) +- `d4ca16a `_ #3743 Improve examples in parent services (WouterJ) +- `be4b9d3 `_ #3729 Added documentation for the new PropertyAccessor``::``isReadable() and isWritable() methods (webmozart) +- `70a3893 `_ #3774 [Book][Internals] add description for the kernel.finish_request event (xabbuh) +- `1934720 `_ #3461 [Form] Deprecated max_length and pattern options (stefanosala) +- `d611e77 `_ #3701 [Serializer] add documentation for serializer callbacks (cordoval) +- `80c645c `_ #3719 Fixed event listeners priority (tony-co) +- `c062d81 `_ #3469 [Validator] - EmailConstraint reference (egulias) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `f801e2e `_ #3805 Add missing autocomplete argument in askAndValidate method (ifdattic) +- `a81d367 `_ #3786 replaceArguments should be setArguments (RobinvdVleuten) +- `33b64e1 `_ #3788 Fix link for StopwatchEvent class (rpg600) +- `2ebabfb `_ #3792 Update commands_as_services.rst (mimol91) +- `529d4ce `_ #3761 buildViewBottomUp has been renamed to finishView (Nyholm) +- `d743139 `_ #3768 the Locale component does not have elements tagged with @api (xabbuh) +- `2b8e44d `_ #3747 Fix Image constraint class and validator link (weaverryan) +- `fa362ca `_ #3741 correct RuntimeException reference (shieldo) +- `d92545e `_ #3734 [book] [testing] fixed the path of the phpunit.xml file (javiereguiluz) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `136f98c `_ #3784 [Expression Langage] be consistent in "print/print out" uses (mickaelandrieu) +- `1094a13 `_ #3807 Added some exceptions to the method order in CS (stof) +- `55442b5 `_ #3800 Fixed another blockquote rendering issue (WouterJ) +- `969fd71 `_ #3785 ensure that destination directories don't exist before creating them (xabbuh) +- `79322ff `_ #3799 Fix list to not render in a block quote (WouterJ) +- `1a6f730 `_ #3793 language tweak for the tip introduced in #3743 (xabbuh) +- `dda9e88 `_ #3778 Adding information on internal reverse proxy (tcz) +- `d36bbd9 `_ #3765 [WIP] make headlines consistent with our standards (xabbuh) +- `daa81a0 `_ #3766 [Book] add note about services and the service container in the form cha... (xabbuh) +- `4529858 `_ #3767 [Book] link to the bc promise in the stable API description (xabbuh) +- `a5471b3 `_ #3775 Fixed variable naming (peterrehm) +- `703c2a6 `_ #3772 [Cookbook][Sessions] some language improvements (xabbuh) +- `3d30b56 `_ #3773 modify Symfony CMF configuration values in the build process so that the... (xabbuh) +- `cfd6d7c `_ #3758 [Book][Routing] Fixed typo on PHP version of a route definition (saro0h) +- `cedfdce `_ #3757 Fixed a typo in the request formats configuration page (gquemener) +- `6bd134c `_ #3754 ignore more files and directories which are created when building the documentation (xabbuh) +- `610462e `_ #3755 [Cookbook][Security] Firewall resitrction tweaks, fix markup, add to toc (xabbuh) +- `0a21718 `_ #3695 Firewall backport (weaverryan) +- `54d6a9e `_ #3736 [book] Misc. routing fixes (javiereguiluz) +- `f149dcf `_ #3739 [book] [forms] misc. fixes and tweaks (javiereguiluz) +- `ce582ec `_ #3735 [book] [controller] fixed the code of a session sample code (javiereguiluz) +- `499ba5c `_ #3733 [book] [validation] fixed typos (javiereguiluz) +- `4d0ff8f `_ #3732 Update routing.rst. Explain using url() v. path(). (ackerman) +- `44c6273 `_ #3727 Added a note about inlined private services (javiereguiluz) \ No newline at end of file From 6d00c3d330d2f6eff4a74452b8ea3a48fc7d0740 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 6 May 2014 16:40:51 +0200 Subject: [PATCH 099/835] [#3797] Reordered changelog --- changelog.rst | 322 +++++++++++++++++++++++++------------------------- 1 file changed, 160 insertions(+), 162 deletions(-) diff --git a/changelog.rst b/changelog.rst index f18908513c4..e7a6d40c2b2 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,135 +13,63 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. - -January, 2014 -------------- - -New Documentation -~~~~~~~~~~~~~~~~~ - -- `d52f3f8 `_ #3454 [Security] Add host option (ghostika) -- `11e079b `_ #3446 [WCM] Documented deprecation of the apache router. (jakzal) -- `0a0bf4c `_ #3437 Add info about callback in options resolver (marekkalnik) -- `6db5f23 `_ #3426 New Feature: Change the Default Command in the Console component (danielcsgomes) -- `6b3c424 `_ #3428 Translation - Added info about JsonFileLoader added in 2.4 (singles) - -Fixed Documentation -~~~~~~~~~~~~~~~~~~~ - -- `fb22fa0 `_ #3456 remove duplicate label (xabbuh) -- `a87fe18 `_ #3470 Fixed typo (danielcsgomes) -- `c205bc6 `_ #3468 enclose YAML string with double quotes to fix syntax highlighting (xabbuh) -- `89963cc `_ #3463 Fix typos in cookbook/testing/database (ifdattic) -- `e0a52ec `_ #3460 remove confusing outdated note on interactive rebasing (xabbuh) -- `6831b13 `_ #3455 [Contributing][Code] fix indentation so that the text is rendered properly (xabbuh) -- `ea5816f `_ #3433 [WIP][Reference][Form Types] Update "radio" form type (bicpi) -- `42c80d1 `_ #3448 Overridden tweak (weaverryan) -- `bede4c3 `_ #3447 Fix error in namespace when use TokenInterface (joanteixi) -- `d9d7c58 `_ #3444 Fix issue #3442 (ifdattic) -- `a6ad607 `_ #3441 [Expression]Change title 'Accessing Public Methods' (Pyrech) -- `9e2e64b `_ #3427 Removed code references to Symfony Standard Distribution (danielcsgomes) -- `3c2c5fc `_ #3435 Update custom_password_authenticator.rst (boardyuk) -- `26b8146 `_ #3415 [#3334] the data_class option was not introduced in 2.4 (xabbuh) -- `0b2a491 `_ #3414 add missing code-block directive (xabbuh) -- `4988118 `_ #3432 [Reference][Form Types] Add "max_length" option in form type (nykopol) -- `26a7b1b `_ #3423 [Session Configuration] add clarifying notes on session save handler proxies (cordoval) -- `f2f5e9a `_ #3421 [Contributing] Cleaning the "contributing patch" page a bit (lemoinem) - -Minor Documentation Changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- `f285d93 `_ #3451 some language tweaks (AE, third-person perspective) (xabbuh) -- `b9bbe5d `_ #3499 Fix YAML syntax highlight + remove trailing whitespace (ifdattic) -- `2b7e0f6 `_ #3497 Fix highlighting (WouterJ) -- `2746067 `_ #3472 Fixed `````versionadded````` inconsistencies in Symfony 2.5+ (danielcsgomes) -- `a535ae0 `_ #3471 Fixed `````versionadded````` inconsistencies in Symfony 2.3 (danielcsgomes) -- `f077a8e `_ #3465 change wording in versionadded example to be consistent with what we use... (xabbuh) -- `f9f7548 `_ #3462 Replace ... with etc (ifdattic) -- `65efcc4 `_ #3445 [Reference][Form Types] Add missing (but existing) options to "form" type (bicpi) -- `1d1b91d `_ #3431 [Config] add cautionary note on ini file loader limitation (cordoval) -- `f2eaf9b `_ #3419 doctrine file upload example uses dir -- caution added (cordoval) -- `72b53ad `_ #3404 [#3276] Trying to further clarify the session storage directory details (weaverryan) -- `67b7bbd `_ #3413 [Cookbook][Bundles] improve explanation of code block for bundle removal (cordoval) -- `7c5a914 `_ #3369 Indicate that Group Sequence Providers can use YAML (karptonite) -- `1e0311e `_ #3416 add empty_data option where required option is used (xabbuh) -- `2be3f52 `_ #3422 [Cookbook][Custom Authentication Provider] add a note of warning for when forbidding anonymous users (cordoval) -- `e255de9 `_ #3429 [Reference][Form Types] Document "with_minutes" time/datetime option (bicpi) - -February, 2014 --------------- +April, 2014 +----------- New Documentation ~~~~~~~~~~~~~~~~~ -- `9dcf467 `_ #3613 Javiereguiluz revamped quick tour (weaverryan) -- `89c6f1d `_ #3439 [Review] Added detailed Backwards Compatibility Promise text (webmozart) -- `0029408 `_ #3558 Created Documentation CHANGELOG (WouterJ) -- `f6dd678 `_ #3548 Update forms.rst (atmosf3ar) -- `9676f2c `_ #3523 [Components][EventDispatcher] describe that the event name and the event dispatcher are passed to even... (xabbuh) -- `5c367b4 `_ #3517 Fixed OptionsResolver component docs (WouterJ) -- `527c8b6 `_ #3496 Added a section about using named assets (vmattila) -- `8ccfe85 `_ #3491 Added doc for named encoders (tamirvs) -- `46377b2 `_ #3486 Documenting createAccessDeniedException() method (klaussilveira) +- `322972e `_ #3803 [Book][Validation] configuration examples for the GroupSequenceProvider (xabbuh) +- `9e129bc `_ #3752 [Console] Add documentation for QuestionHelper (romainneutron) +- `64a924d `_ #3756 [WCM][Console] Add Process Helper documentation (romainneutron) +- `d4ca16a `_ #3743 Improve examples in parent services (WouterJ) +- `be4b9d3 `_ #3729 Added documentation for the new PropertyAccessor``::``isReadable() and isWritable() methods (webmozart) +- `70a3893 `_ #3774 [Book][Internals] add description for the kernel.finish_request event (xabbuh) +- `1934720 `_ #3461 [Form] Deprecated max_length and pattern options (stefanosala) +- `d611e77 `_ #3701 [Serializer] add documentation for serializer callbacks (cordoval) +- `80c645c `_ #3719 Fixed event listeners priority (tony-co) +- `c062d81 `_ #3469 [Validator] - EmailConstraint reference (egulias) Fixed Documentation ~~~~~~~~~~~~~~~~~~~ -- `adcbb5d `_ #3615 Fixes to cookbook/doctrine/registration_form.rst (Crushnaut) -- `5c4336a `_ #3570 Callback: [Validator, validate] expects validate to be static (nixilla) -- `a21fb26 `_ #3559 Remove reference to copying parameters.yml from Git cookbook (pwaring) -- `de71a51 `_ #3551 [Cookbook][Dynamic Form Modification] Fix sample code (rybakit) -- `143db2f `_ #3550 Update introduction.rst (taavit) -- `384538b `_ #3549 Fixed createPropertyAccessorBuilder usage (antonbabenko) -- `642e776 `_ #3544 Fix build errors (xabbuh) -- `d275302 `_ #3541 Update generic_event.rst (Lumbendil) -- `819949c `_ #3537 Add missing variable assignment (colinodell) -- `d7e8262 `_ #3535 fix form type name. (yositani2002) -- `821af3b `_ #3493 Type fix in remove.rst (weaverryan) -- `003230f `_ #3530 Update form_customization.rst (dczech) -- `a43f15a `_ #3519 [Book][Service Container] Fix syntax highlighting (iamdto) -- `86e02c6 `_ #3514 Fixed some small typos in code example (RobinvdVleuten) -- `696313c `_ #3513 [Component-DI] Fixed typo (saro0h) -- `27dcebd `_ #3509 Fix typo: side.bar.twig => sidebar.twig (ifdattic) -- `0dc8c26 `_ #3507 Fix a typo (missing `````) in ``:doc:`` link (ifdattic) -- `272197b `_ #3504 fix include directive so that the contents are really included (xabbuh) -- `e385d28 `_ #3503 file extension correction xfliff to xliff (nixilla) -- `6d34aa6 `_ #3478 Update custom_password_authenticator.rst (piotras-s) -- `a171700 `_ #3477 Api key user provider should use "implements" instead of "extends" (skowi) -- `7fe0de3 `_ #3475 Fixed doc for framework.session.cookie_lifetime refrence. (tyomo4ka) -- `8155e4c `_ #3473 Update proxy_examples.rst (AZielinski) +- `f801e2e `_ #3805 Add missing autocomplete argument in askAndValidate method (ifdattic) +- `a81d367 `_ #3786 replaceArguments should be setArguments (RobinvdVleuten) +- `33b64e1 `_ #3788 Fix link for StopwatchEvent class (rpg600) +- `2ebabfb `_ #3792 Update commands_as_services.rst (mimol91) +- `529d4ce `_ #3761 buildViewBottomUp has been renamed to finishView (Nyholm) +- `d743139 `_ #3768 the Locale component does not have elements tagged with @api (xabbuh) +- `2b8e44d `_ #3747 Fix Image constraint class and validator link (weaverryan) +- `fa362ca `_ #3741 correct RuntimeException reference (shieldo) +- `d92545e `_ #3734 [book] [testing] fixed the path of the phpunit.xml file (javiereguiluz) Minor Documentation Changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- `0928249 `_ #3568 Update checkbox_compound.rst.inc (joshuaadickerson) -- `38def3b `_ #3567 Update checkbox_compound.rst.inc (joshuaadickerson) -- `15d8ab8 `_ #3553 Minimize horizontal scrolling in code blocks to improve readability (ifdattic) -- `5120863 `_ #3547 Update acl.rst (iqfoundry) -- `b7ac326 `_ #3557 Minimize horizontal scrolling in code block to improve readability (ifdattic) -- `d974c77 `_ #3556 Fix PSR error (ifdattic) -- `f4bb017 `_ #3555 Wrap variables in {} for safer interpolation (ifdattic) -- `5f02bca `_ #3552 Fix typos (ifdattic) -- `6e32c47 `_ #3546 Fix README: contributions should be based off 2.3 or higher (colinodell) -- `ffa8f76 `_ #3545 Example of getting entity managers directly from the container (colinodell) -- `6a2a55b `_ #3579 Fix build errors (xabbuh) -- `dce2e23 `_ #3532 Added tip for Entity Listeners (slavafomin) -- `73adf8b `_ #3528 Clarify service parameters usages (WouterJ) -- `7e75b64 `_ #3533 Moving the new named algorithms into their own cookbook entry (weaverryan) -- `f634600 `_ #3531 Remove horizontal scrolling in code block (ifdattic) -- `9ba4fa7 `_ #3527 Changes to components domcrawler (ifdattic) -- `8973c81 `_ #3526 Changes for Console component (ifdattic) -- `6848bed `_ #3538 Rebasing #3518 (weaverryan) -- `c838df8 `_ #3511 [Component-DI] Removed useless else statement in code example (saro0h) -- `1af6742 `_ #3510 add empty line (lazyants) -- `1131247 `_ #3508 Add 'in XML' for additional clarity (ifdattic) -- `a650b93 `_ #3506 Nykopol overriden options (weaverryan) -- `ab10035 `_ #3505 replace Akamaï with Akamai (xabbuh) -- `7f56c20 `_ #3501 [Security] Fix markup (tyx) -- `80a90ba `_ #3500 Minimize horizontal scrolling in code blocks (improve readability) (ifdattic) -- `e5bc4ea `_ #3498 Remove second empty data (xabbuh) -- `d084d87 `_ #3485 [Cookbook][Assetic] Fix "javascripts" tag name typo (bicpi) -- `3250aba `_ #3481 Fix code block (minimise horizontal scrolling), typo in yaml (ifdattic) +- `136f98c `_ #3784 [Expression Langage] be consistent in "print/print out" uses (mickaelandrieu) +- `1094a13 `_ #3807 Added some exceptions to the method order in CS (stof) +- `55442b5 `_ #3800 Fixed another blockquote rendering issue (WouterJ) +- `969fd71 `_ #3785 ensure that destination directories don't exist before creating them (xabbuh) +- `79322ff `_ #3799 Fix list to not render in a block quote (WouterJ) +- `1a6f730 `_ #3793 language tweak for the tip introduced in #3743 (xabbuh) +- `dda9e88 `_ #3778 Adding information on internal reverse proxy (tcz) +- `d36bbd9 `_ #3765 [WIP] make headlines consistent with our standards (xabbuh) +- `daa81a0 `_ #3766 [Book] add note about services and the service container in the form cha... (xabbuh) +- `4529858 `_ #3767 [Book] link to the bc promise in the stable API description (xabbuh) +- `a5471b3 `_ #3775 Fixed variable naming (peterrehm) +- `703c2a6 `_ #3772 [Cookbook][Sessions] some language improvements (xabbuh) +- `3d30b56 `_ #3773 modify Symfony CMF configuration values in the build process so that the... (xabbuh) +- `cfd6d7c `_ #3758 [Book][Routing] Fixed typo on PHP version of a route definition (saro0h) +- `cedfdce `_ #3757 Fixed a typo in the request formats configuration page (gquemener) +- `6bd134c `_ #3754 ignore more files and directories which are created when building the documentation (xabbuh) +- `610462e `_ #3755 [Cookbook][Security] Firewall resitrction tweaks, fix markup, add to toc (xabbuh) +- `0a21718 `_ #3695 Firewall backport (weaverryan) +- `54d6a9e `_ #3736 [book] Misc. routing fixes (javiereguiluz) +- `f149dcf `_ #3739 [book] [forms] misc. fixes and tweaks (javiereguiluz) +- `ce582ec `_ #3735 [book] [controller] fixed the code of a session sample code (javiereguiluz) +- `499ba5c `_ #3733 [book] [validation] fixed typos (javiereguiluz) +- `4d0ff8f `_ #3732 Update routing.rst. Explain using url() v. path(). (ackerman) +- `44c6273 `_ #3727 Added a note about inlined private services (javiereguiluz) March, 2014 ----------- @@ -259,61 +187,131 @@ Minor Documentation Changes - `1714a31 `_ #3585 Use consistent method chaining in BlogBundle sample application (ockcyp) - `cb61f4f `_ #3581 Add missing hyphen in HTTP Fundamentals page (ockcyp) +February, 2014 +-------------- -April, 2014 ------------ +New Documentation +~~~~~~~~~~~~~~~~~ + +- `9dcf467 `_ #3613 Javiereguiluz revamped quick tour (weaverryan) +- `89c6f1d `_ #3439 [Review] Added detailed Backwards Compatibility Promise text (webmozart) +- `0029408 `_ #3558 Created Documentation CHANGELOG (WouterJ) +- `f6dd678 `_ #3548 Update forms.rst (atmosf3ar) +- `9676f2c `_ #3523 [Components][EventDispatcher] describe that the event name and the event dispatcher are passed to even... (xabbuh) +- `5c367b4 `_ #3517 Fixed OptionsResolver component docs (WouterJ) +- `527c8b6 `_ #3496 Added a section about using named assets (vmattila) +- `8ccfe85 `_ #3491 Added doc for named encoders (tamirvs) +- `46377b2 `_ #3486 Documenting createAccessDeniedException() method (klaussilveira) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `adcbb5d `_ #3615 Fixes to cookbook/doctrine/registration_form.rst (Crushnaut) +- `5c4336a `_ #3570 Callback: [Validator, validate] expects validate to be static (nixilla) +- `a21fb26 `_ #3559 Remove reference to copying parameters.yml from Git cookbook (pwaring) +- `de71a51 `_ #3551 [Cookbook][Dynamic Form Modification] Fix sample code (rybakit) +- `143db2f `_ #3550 Update introduction.rst (taavit) +- `384538b `_ #3549 Fixed createPropertyAccessorBuilder usage (antonbabenko) +- `642e776 `_ #3544 Fix build errors (xabbuh) +- `d275302 `_ #3541 Update generic_event.rst (Lumbendil) +- `819949c `_ #3537 Add missing variable assignment (colinodell) +- `d7e8262 `_ #3535 fix form type name. (yositani2002) +- `821af3b `_ #3493 Type fix in remove.rst (weaverryan) +- `003230f `_ #3530 Update form_customization.rst (dczech) +- `a43f15a `_ #3519 [Book][Service Container] Fix syntax highlighting (iamdto) +- `86e02c6 `_ #3514 Fixed some small typos in code example (RobinvdVleuten) +- `696313c `_ #3513 [Component-DI] Fixed typo (saro0h) +- `27dcebd `_ #3509 Fix typo: side.bar.twig => sidebar.twig (ifdattic) +- `0dc8c26 `_ #3507 Fix a typo (missing `````) in ``:doc:`` link (ifdattic) +- `272197b `_ #3504 fix include directive so that the contents are really included (xabbuh) +- `e385d28 `_ #3503 file extension correction xfliff to xliff (nixilla) +- `6d34aa6 `_ #3478 Update custom_password_authenticator.rst (piotras-s) +- `a171700 `_ #3477 Api key user provider should use "implements" instead of "extends" (skowi) +- `7fe0de3 `_ #3475 Fixed doc for framework.session.cookie_lifetime refrence. (tyomo4ka) +- `8155e4c `_ #3473 Update proxy_examples.rst (AZielinski) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `0928249 `_ #3568 Update checkbox_compound.rst.inc (joshuaadickerson) +- `38def3b `_ #3567 Update checkbox_compound.rst.inc (joshuaadickerson) +- `15d8ab8 `_ #3553 Minimize horizontal scrolling in code blocks to improve readability (ifdattic) +- `5120863 `_ #3547 Update acl.rst (iqfoundry) +- `b7ac326 `_ #3557 Minimize horizontal scrolling in code block to improve readability (ifdattic) +- `d974c77 `_ #3556 Fix PSR error (ifdattic) +- `f4bb017 `_ #3555 Wrap variables in {} for safer interpolation (ifdattic) +- `5f02bca `_ #3552 Fix typos (ifdattic) +- `6e32c47 `_ #3546 Fix README: contributions should be based off 2.3 or higher (colinodell) +- `ffa8f76 `_ #3545 Example of getting entity managers directly from the container (colinodell) +- `6a2a55b `_ #3579 Fix build errors (xabbuh) +- `dce2e23 `_ #3532 Added tip for Entity Listeners (slavafomin) +- `73adf8b `_ #3528 Clarify service parameters usages (WouterJ) +- `7e75b64 `_ #3533 Moving the new named algorithms into their own cookbook entry (weaverryan) +- `f634600 `_ #3531 Remove horizontal scrolling in code block (ifdattic) +- `9ba4fa7 `_ #3527 Changes to components domcrawler (ifdattic) +- `8973c81 `_ #3526 Changes for Console component (ifdattic) +- `6848bed `_ #3538 Rebasing #3518 (weaverryan) +- `c838df8 `_ #3511 [Component-DI] Removed useless else statement in code example (saro0h) +- `1af6742 `_ #3510 add empty line (lazyants) +- `1131247 `_ #3508 Add 'in XML' for additional clarity (ifdattic) +- `a650b93 `_ #3506 Nykopol overriden options (weaverryan) +- `ab10035 `_ #3505 replace Akamaï with Akamai (xabbuh) +- `7f56c20 `_ #3501 [Security] Fix markup (tyx) +- `80a90ba `_ #3500 Minimize horizontal scrolling in code blocks (improve readability) (ifdattic) +- `e5bc4ea `_ #3498 Remove second empty data (xabbuh) +- `d084d87 `_ #3485 [Cookbook][Assetic] Fix "javascripts" tag name typo (bicpi) +- `3250aba `_ #3481 Fix code block (minimise horizontal scrolling), typo in yaml (ifdattic) + +January, 2014 +------------- New Documentation ~~~~~~~~~~~~~~~~~ -- `322972e `_ #3803 [Book][Validation] configuration examples for the GroupSequenceProvider (xabbuh) -- `9e129bc `_ #3752 [Console] Add documentation for QuestionHelper (romainneutron) -- `64a924d `_ #3756 [WCM][Console] Add Process Helper documentation (romainneutron) -- `d4ca16a `_ #3743 Improve examples in parent services (WouterJ) -- `be4b9d3 `_ #3729 Added documentation for the new PropertyAccessor``::``isReadable() and isWritable() methods (webmozart) -- `70a3893 `_ #3774 [Book][Internals] add description for the kernel.finish_request event (xabbuh) -- `1934720 `_ #3461 [Form] Deprecated max_length and pattern options (stefanosala) -- `d611e77 `_ #3701 [Serializer] add documentation for serializer callbacks (cordoval) -- `80c645c `_ #3719 Fixed event listeners priority (tony-co) -- `c062d81 `_ #3469 [Validator] - EmailConstraint reference (egulias) +- `d52f3f8 `_ #3454 [Security] Add host option (ghostika) +- `11e079b `_ #3446 [WCM] Documented deprecation of the apache router. (jakzal) +- `0a0bf4c `_ #3437 Add info about callback in options resolver (marekkalnik) +- `6db5f23 `_ #3426 New Feature: Change the Default Command in the Console component (danielcsgomes) +- `6b3c424 `_ #3428 Translation - Added info about JsonFileLoader added in 2.4 (singles) Fixed Documentation ~~~~~~~~~~~~~~~~~~~ -- `f801e2e `_ #3805 Add missing autocomplete argument in askAndValidate method (ifdattic) -- `a81d367 `_ #3786 replaceArguments should be setArguments (RobinvdVleuten) -- `33b64e1 `_ #3788 Fix link for StopwatchEvent class (rpg600) -- `2ebabfb `_ #3792 Update commands_as_services.rst (mimol91) -- `529d4ce `_ #3761 buildViewBottomUp has been renamed to finishView (Nyholm) -- `d743139 `_ #3768 the Locale component does not have elements tagged with @api (xabbuh) -- `2b8e44d `_ #3747 Fix Image constraint class and validator link (weaverryan) -- `fa362ca `_ #3741 correct RuntimeException reference (shieldo) -- `d92545e `_ #3734 [book] [testing] fixed the path of the phpunit.xml file (javiereguiluz) +- `fb22fa0 `_ #3456 remove duplicate label (xabbuh) +- `a87fe18 `_ #3470 Fixed typo (danielcsgomes) +- `c205bc6 `_ #3468 enclose YAML string with double quotes to fix syntax highlighting (xabbuh) +- `89963cc `_ #3463 Fix typos in cookbook/testing/database (ifdattic) +- `e0a52ec `_ #3460 remove confusing outdated note on interactive rebasing (xabbuh) +- `6831b13 `_ #3455 [Contributing][Code] fix indentation so that the text is rendered properly (xabbuh) +- `ea5816f `_ #3433 [WIP][Reference][Form Types] Update "radio" form type (bicpi) +- `42c80d1 `_ #3448 Overridden tweak (weaverryan) +- `bede4c3 `_ #3447 Fix error in namespace when use TokenInterface (joanteixi) +- `d9d7c58 `_ #3444 Fix issue #3442 (ifdattic) +- `a6ad607 `_ #3441 [Expression]Change title 'Accessing Public Methods' (Pyrech) +- `9e2e64b `_ #3427 Removed code references to Symfony Standard Distribution (danielcsgomes) +- `3c2c5fc `_ #3435 Update custom_password_authenticator.rst (boardyuk) +- `26b8146 `_ #3415 [#3334] the data_class option was not introduced in 2.4 (xabbuh) +- `0b2a491 `_ #3414 add missing code-block directive (xabbuh) +- `4988118 `_ #3432 [Reference][Form Types] Add "max_length" option in form type (nykopol) +- `26a7b1b `_ #3423 [Session Configuration] add clarifying notes on session save handler proxies (cordoval) +- `f2f5e9a `_ #3421 [Contributing] Cleaning the "contributing patch" page a bit (lemoinem) Minor Documentation Changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- `136f98c `_ #3784 [Expression Langage] be consistent in "print/print out" uses (mickaelandrieu) -- `1094a13 `_ #3807 Added some exceptions to the method order in CS (stof) -- `55442b5 `_ #3800 Fixed another blockquote rendering issue (WouterJ) -- `969fd71 `_ #3785 ensure that destination directories don't exist before creating them (xabbuh) -- `79322ff `_ #3799 Fix list to not render in a block quote (WouterJ) -- `1a6f730 `_ #3793 language tweak for the tip introduced in #3743 (xabbuh) -- `dda9e88 `_ #3778 Adding information on internal reverse proxy (tcz) -- `d36bbd9 `_ #3765 [WIP] make headlines consistent with our standards (xabbuh) -- `daa81a0 `_ #3766 [Book] add note about services and the service container in the form cha... (xabbuh) -- `4529858 `_ #3767 [Book] link to the bc promise in the stable API description (xabbuh) -- `a5471b3 `_ #3775 Fixed variable naming (peterrehm) -- `703c2a6 `_ #3772 [Cookbook][Sessions] some language improvements (xabbuh) -- `3d30b56 `_ #3773 modify Symfony CMF configuration values in the build process so that the... (xabbuh) -- `cfd6d7c `_ #3758 [Book][Routing] Fixed typo on PHP version of a route definition (saro0h) -- `cedfdce `_ #3757 Fixed a typo in the request formats configuration page (gquemener) -- `6bd134c `_ #3754 ignore more files and directories which are created when building the documentation (xabbuh) -- `610462e `_ #3755 [Cookbook][Security] Firewall resitrction tweaks, fix markup, add to toc (xabbuh) -- `0a21718 `_ #3695 Firewall backport (weaverryan) -- `54d6a9e `_ #3736 [book] Misc. routing fixes (javiereguiluz) -- `f149dcf `_ #3739 [book] [forms] misc. fixes and tweaks (javiereguiluz) -- `ce582ec `_ #3735 [book] [controller] fixed the code of a session sample code (javiereguiluz) -- `499ba5c `_ #3733 [book] [validation] fixed typos (javiereguiluz) -- `4d0ff8f `_ #3732 Update routing.rst. Explain using url() v. path(). (ackerman) -- `44c6273 `_ #3727 Added a note about inlined private services (javiereguiluz) \ No newline at end of file +- `f285d93 `_ #3451 some language tweaks (AE, third-person perspective) (xabbuh) +- `b9bbe5d `_ #3499 Fix YAML syntax highlight + remove trailing whitespace (ifdattic) +- `2b7e0f6 `_ #3497 Fix highlighting (WouterJ) +- `2746067 `_ #3472 Fixed `````versionadded````` inconsistencies in Symfony 2.5+ (danielcsgomes) +- `a535ae0 `_ #3471 Fixed `````versionadded````` inconsistencies in Symfony 2.3 (danielcsgomes) +- `f077a8e `_ #3465 change wording in versionadded example to be consistent with what we use... (xabbuh) +- `f9f7548 `_ #3462 Replace ... with etc (ifdattic) +- `65efcc4 `_ #3445 [Reference][Form Types] Add missing (but existing) options to "form" type (bicpi) +- `1d1b91d `_ #3431 [Config] add cautionary note on ini file loader limitation (cordoval) +- `f2eaf9b `_ #3419 doctrine file upload example uses dir -- caution added (cordoval) +- `72b53ad `_ #3404 [#3276] Trying to further clarify the session storage directory details (weaverryan) +- `67b7bbd `_ #3413 [Cookbook][Bundles] improve explanation of code block for bundle removal (cordoval) +- `7c5a914 `_ #3369 Indicate that Group Sequence Providers can use YAML (karptonite) +- `1e0311e `_ #3416 add empty_data option where required option is used (xabbuh) +- `2be3f52 `_ #3422 [Cookbook][Custom Authentication Provider] add a note of warning for when forbidding anonymous users (cordoval) +- `e255de9 `_ #3429 [Reference][Form Types] Document "with_minutes" time/datetime option (bicpi) From fe5a39f79ed85c86a216d286efe61e2905e220ec Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 9 May 2014 16:54:10 +0200 Subject: [PATCH 100/835] fix literal positions --- changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.rst b/changelog.rst index e7a6d40c2b2..5399a286543 100644 --- a/changelog.rst +++ b/changelog.rst @@ -23,7 +23,7 @@ New Documentation - `9e129bc `_ #3752 [Console] Add documentation for QuestionHelper (romainneutron) - `64a924d `_ #3756 [WCM][Console] Add Process Helper documentation (romainneutron) - `d4ca16a `_ #3743 Improve examples in parent services (WouterJ) -- `be4b9d3 `_ #3729 Added documentation for the new PropertyAccessor``::``isReadable() and isWritable() methods (webmozart) +- `be4b9d3 `_ #3729 Added documentation for the new ``PropertyAccessor::isReadable()`` and ``isWritable()`` methods (webmozart) - `70a3893 `_ #3774 [Book][Internals] add description for the kernel.finish_request event (xabbuh) - `1934720 `_ #3461 [Form] Deprecated max_length and pattern options (stefanosala) - `d611e77 `_ #3701 [Serializer] add documentation for serializer callbacks (cordoval) From 36f68b6758c18eb305b4a4a7bbdf21b6c4258532 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 5 May 2014 18:40:47 +0200 Subject: [PATCH 101/835] fix the wording in versionadded directives --- components/property_access/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 3a55ce8bb82..0a938117923 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -313,7 +313,7 @@ Checking Property Paths ----------------------- .. versionadded:: 2.5 - The methods + The :method:`PropertyAccessor::isReadable ` and :method:`PropertyAccessor::isWritable ` From 4eafdbde933b4dc850531ec94a373e440e7ee7bf Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 10 May 2014 17:02:41 -0500 Subject: [PATCH 102/835] [#3837] Fixes thanks to stof and WouterJ --- book/templating.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index 7c3c31aa7a8..545e1c24531 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -991,10 +991,10 @@ assets won't be cached when deployed. For example, ``/images/logo.png`` might look like ``/images/logo.png?v2``. For more information, see the :ref:`ref-framework-assets-version` configuration option. -.. _` +.. _`book-templating-version-by-asset`: .. versionadded:: 2.5 - Setting versioned URLs on an asset-by-asset basis were introduced in Symfony 2.5. + Setting versioned URLs on an asset-by-asset basis was introduced in Symfony 2.5. If you need to set a version for a specific asset, you can set the fourth argument (or the ``version`` argument) to the desired version: @@ -1003,14 +1003,14 @@ argument (or the ``version`` argument) to the desired version: .. code-block:: html+jinja - Symfony! + Symfony! .. code-block:: html+php Symfony! If you dont give a version or pass ``null``, the default package version -(from :ref:`ref-framework-assets-version`) wil be used. If you pass ``false``, +(from :ref:`ref-framework-assets-version`) will be used. If you pass ``false``, versioned URL will be deactivated for this asset. .. versionadded:: 2.5 From 83bb7237461c9cd6028ed01f062a5a31b76ed29f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 26 May 2014 13:08:52 +0200 Subject: [PATCH 103/835] Updated the installation instructions to Symfony 2.5+ --- quick_tour/the_big_picture.rst | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index eb98595f979..aafbc41cb29 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -18,7 +18,7 @@ directory: .. code-block:: bash - $ composer create-project symfony/framework-standard-edition myproject/ ~2.4 + $ composer create-project symfony/framework-standard-edition myproject/ ~2.5 .. note:: @@ -35,9 +35,25 @@ directory: Beware that the first time you install Symfony2, it may take a few minutes to download all its components. At the end of the installation process, the -installer will ask you to provide some configuration options for the Symfony2 -project. For this first project you can safely ignore this configuration by -pressing the ```` key repeatedly. +installer will ask you four questions: + +1. **Would you like to use Symfony 3 directory structure? [y/N]** The upcoming + Symfony 3 version will modify the default directory structure for Symfony + applications. If you want to test drive this new structure, type ``y``. + In order to follow this tutorial, press the ```` key to accept the + default ``N`` value and to keep using the default Symfony2 structure. +2. **Would you like to install Acme demo bundle? [y/N]** Symfony versions prior + to 2.5 included a demo application to test drive some features of the + framework. However, as this demo application is only useful for newcomers, + installing it is now optional. In order to follow this tutorial, type the + ``y`` key to install the demo application. +3. **Some parameters are missing. Please provide them.** Symfony2 asks you for + the value of all the configuration parameters. For this first project, + you can safely ignore this configuration by pressing the ```` key + repeatedly. +4. **Do you want to remove the existing VCS (.git, .svn..) history? [Y,n]?** + The development history of large projects such as Symfony can take a lot of + disk space. Press the ```` key to safely remove all this history data. Running Symfony2 ---------------- From 1f5d980fedb8007bc775542b61e16749c1dd90b6 Mon Sep 17 00:00:00 2001 From: Marcin Chwedziak Date: Mon, 24 Feb 2014 18:01:05 +0100 Subject: [PATCH 104/835] added a note about is* getters support with GetSetMethodNormalizer --- components/serializer.rst | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/components/serializer.rst b/components/serializer.rst index aeaabeb6067..872c5936e8b 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -62,6 +62,7 @@ exists in your project:: { private $age; private $name; + private $sportsman; // Getters public function getName() @@ -74,6 +75,12 @@ exists in your project:: return $this->age; } + // Issers + public function isSportsman() + { + return $this->sportsman; + } + // Setters public function setName($name) { @@ -84,6 +91,11 @@ exists in your project:: { $this->age = $age; } + + public function setSportsman($sportsman) + { + $this->sportsman = $sportsman; + } } Now, if you want to serialize this object into JSON, you only need to @@ -92,10 +104,11 @@ use the Serializer service created before:: $person = new Acme\Person(); $person->setName('foo'); $person->setAge(99); + $person->setSportsman(false); $jsonContent = $serializer->serialize($person, 'json'); - // $jsonContent contains {"name":"foo","age":99} + // $jsonContent contains {"name":"foo","age":99,"sportsman":false} echo $jsonContent; // or return it in a Response @@ -124,7 +137,7 @@ method on the normalizer definition:: $encoder = new JsonEncoder(); $serializer = new Serializer(array($normalizer), array($encoder)); - $serializer->serialize($person, 'json'); // Output: {"name":"foo"} + $serializer->serialize($person, 'json'); // Output: {"name":"foo","sportsman":false} Deserializing an Object ----------------------- @@ -136,6 +149,7 @@ of the ``Person`` class would be encoded in XML format:: foo 99 + false EOF; @@ -181,6 +195,18 @@ method on the normalizer definition:: As a final result, the deserializer uses the ``first_name`` attribute as if it were ``firstName`` and uses the ``getFirstName`` and ``setFirstName`` methods. +Serializing Boolean Attributes +------------------------------ + +.. versionadded:: 2.5 + Support for ``is*`` accessors in + :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` + was introduced in Symfony 2.5. + +If you are using isser methods (methods prefixed by ``is``, like +``Acme\Person::isSportsman()``), the Serializer component will automatically +detect and use it to serialize related attributes. + Using Callbacks to Serialize Properties with Object Instances ------------------------------------------------------------- From e6bc038e2a61550c4bb75fd890976f60bf372a37 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Thu, 3 Apr 2014 15:07:28 +0200 Subject: [PATCH 105/835] Add documentation about service decoration --- components/dependency_injection/advanced.rst | 92 ++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/components/dependency_injection/advanced.rst b/components/dependency_injection/advanced.rst index 77056c296ce..95d7e5b2697 100644 --- a/components/dependency_injection/advanced.rst +++ b/components/dependency_injection/advanced.rst @@ -185,3 +185,95 @@ the service itself gets loaded. To do so, you can use the ``file`` directive. Notice that Symfony will internally call the PHP statement ``require_once``, which means that your file will be included only once per request. + +Decorating Services +------------------- + +.. versionadded:: 2.5 + Decorated services were introduced in Symfony 2.5. + +When overriding an existing definition, the old service is lost: + +.. code-block:: php + + $container->register('foo', 'FooService'); + + // this is going to replace the old definition with the new one + // old definition is lost + $container->register('foo', 'CustomFooService'); + +Most of the time, that's exactly what you want to do. But sometimes, +you might want to decorate the old one instead. In this case, the +old service should be kept around to be able to reference it in the +new one. This configuration replaces ``foo`` with a new one, but keeps +a reference of the old one as ``bar.inner``: + +.. configuration-block:: + + .. code-block:: yaml + + bar: + public: false + class: stdClass + decorates: foo + arguments: ["@bar.inner"] + + .. code-block:: xml + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Reference; + + $container->register('bar', 'stdClass') + ->addArgument(new Reference('bar.inner')) + ->setPublic(false) + ->setDecoratedService('foo'); + +Here is what's going on here: the ``setDecoratedService()` method tells +the container that the ``bar`` service should replace the ``foo`` service, +renaming ``foo`` to ``bar.inner``. +By convention, the old ``foo`` service is going to be renamed ``bar.inner``, +so you can inject it into your new service. + +.. note:: + The generated inner id is based on the id of the decorator service + (``bar`` here), not of the decorated service (``foo`` here). This is + mandatory to allow several decorators on the same service (they need to have + different generated inner ids). + + Most of the time, the decorator should be declared private, as you will not + need to retrieve it as ``bar`` from the container. The visibility of the + decorated ``foo`` service (which is an alias for ``bar``) will still be the + same as the original ``foo`` visibility. + +You can change the inner service name if you want to: + +.. configuration-block:: + + .. code-block:: yaml + + bar: + class: stdClass + public: false + decorates: foo + decoration_inner_name: bar.wooz + arguments: ["@bar.wooz"] + + .. code-block:: xml + + + + + + .. code-block:: php + + use Symfony\Component\DependencyInjection\Reference; + + $container->register('bar', 'stdClass') + ->addArgument(new Reference('bar.wooz')) + ->setPublic(false) + ->setDecoratedService('foo', 'bar.wooz'); From 564f60d68943b7c870cc82a5dde196827e0c2318 Mon Sep 17 00:00:00 2001 From: Abdellatif AitBoudad Date: Sun, 25 May 2014 20:33:58 +0100 Subject: [PATCH 106/835] Fixed link. --- book/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/security.rst b/book/security.rst index c42a7f49934..3a9bf341674 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1099,7 +1099,7 @@ authorization from inside a controller:: .. versionadded:: 2.5 The ``createAccessDeniedException`` method was introduced in Symfony 2.5. -The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException()` +The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException` method creates a special :class:`Symfony\\Component\\Security\\Core\Exception\\AccessDeniedException` object, which ultimately triggers a 403 HTTP response inside Symfony. From 47ce3f1a09c63bfb0ebfd6341f47c74bb1568389 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Fri, 6 Jun 2014 14:48:18 +0200 Subject: [PATCH 107/835] Fixed syntax --- components/console/helpers/progressbar.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index 0cd119a0a9b..2ae9e64844a 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -55,7 +55,7 @@ instance:: $progress = new ProgressBar($output); -The progress will then be displayed as a throbber:: +The progress will then be displayed as a throbber: .. code-block:: text From c5ca4978cb98f378c92cdf9d5c40611b8349a132 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 8 Jun 2014 17:22:57 +0200 Subject: [PATCH 108/835] Added May changelog --- changelog.rst | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/changelog.rst b/changelog.rst index f5b997e4cea..46868fdda58 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,80 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +May, 2014 +--------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `4fd1b49 `_ #3753 [DependencyInjection] Add documentation about service decoration (romainneutron) +- `f913dd7 `_ #3603 [Serializer] Support for is.* getters in GetSetMethodNormalizer (tiraeth) +- `e8511cb `_ #3776 Updated event_listener.rst (bfgasparin) +- `af8c20f `_ #3818 [Form customization] added block_name example. (aitboudad) +- `c788325 `_ #3841 [Cookbook][Logging] register processor per handler and per channel (xabbuh) +- `979533a `_ #3839 document how to test actions (greg0ire) +- `d8aaac3 `_ #3835 Updated framework.ide configuration (WouterJ) +- `a9648e8 `_ #3742 [2.5][Templating] Add documentation about generating versioned URLs (romainneutron) +- `f665e14 `_ #3704 [Form] Added documentation for Form Events (csarrazi) +- `14b9f14 `_ #3777 added docs for the core team (fabpot) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `0649c21 `_ #3869 Add a missing argument to the PdoSessionHandler (jakzal) +- `259a2b7 `_ #3866 [Book][Security]fixed Login when there is no session. (aitboudad) +- `9b7584f `_ #3863 Error in XML (tvlooy) +- `0cb9c3b `_ #3827 Update 'How to Create and store a Symfony2 Project in Git' (nicwortel) +- `4ed9a08 `_ #3830 Generate an APC prefix based on __FILE__ (trsteel88) +- `9a65412 `_ #3840 Update dialoghelper.rst (jdecoster) +- `1853fea `_ #3716 Fix issue #3712 (umpirsky) +- `baa9759 `_ #3791 Property access tweaks (weaverryan) +- `80d70a4 `_ #3779 [Book][Security] constants are defined in the SecurityContextInterface (xabbuh) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `302fa82 `_ #3872 Update hostname_pattern.rst (sofany) +- `50672f7 `_ #3867 fixed missing info about FosUserBundle. (aitboudad) +- `3e3004f `_ #3865 Fixed link. (aitboudad) +- `b32ec15 `_ #3856 Update voters_data_permission.rst (MarcomTeam) +- `bffe163 `_ #3859 Add filter cssrewrite (DOEO) +- `f617ff8 `_ #3764 Update testing.rst (NAYZO) +- `3792fee `_ #3858 Clarified Password Encoders example (WouterJ) +- `663d68c `_ #3857 Added little bit information about the route name (WouterJ) +- `797cbd5 `_ #3794 Adds link to new QuestionHelper (weaverryan) +- `4211bff `_ #3852 Fixed link and typo in type_guesser.rst (rpg600) +- `78ae7ec `_ #3845 added link to /cookbook/security/force_https. (aitboudad) +- `6c69362 `_ #3846 [Routing][Loader] added JMSI18nRoutingBundle (aitboudad) +- `136864b `_ #3844 [Components] Fixed some typos. (ahsio) +- `b0710bc `_ #3842 Update dialoghelper.rst (bijsterdee) +- `9f1a354 `_ #3804 [Components][DependencyInjection] add note about a use case that requires to compile the container (xabbuh) +- `d92c522 `_ #3769 Updated references to new Session() (scottwarren) +- `00f60a8 `_ #3837 More asset version details (weaverryan) +- `681ddc8 `_ #3843 [Changelog] fix literal positions (xabbuh) +- `1aa79d5 `_ #3834 fix the wording in versionadded directives (for the master branch) (xabbuh) +- `7288a33 `_ #3789 [Reference][Forms] Improvements to the form type (xabbuh) +- `72fae25 `_ #3790 [Reference][Forms] move versionadded directives for form options directly below the option's headline (xabbuh) +- `b4d4ac3 `_ #3838 fix filename typo in cookbook/form/unit_testing.rst (hice3000) +- `0b06287 `_ #3836 remove unnecessary rewrite from nginx conf (Burgov) +- `89d0dae `_ #3833 fix the wording in versionadded directives (for the 2.4 branch) (xabbuh) +- `e58e39f `_ #3832 fix the wording in versionadded directives (for the 2.3 branch) (xabbuh) +- `09d6ca1 `_ #3829 [Components] consistent headlines (xabbuh) +- `54e0882 `_ #3828 [Contributing] consistent headlines (xabbuh) +- `b1336d7 `_ #3823 Added empty line after if statements (zomberg) +- `79b9fdc `_ #3822 Update voters_data_permission.rst (mimol91) +- `69cb7b8 `_ #3821 Update custom_authentication_provider.rst (leberknecht) +- `9f602c4 `_ #3820 Update page_creation.rst (adreeun) +- `52518c0 `_ #3819 Update csrf_in_login_form.rst (micheal) +- `1adfd9b `_ #3802 Add a note about which types can be used in Symfony (fabpot) +- `fa27ded `_ #3801 [Cookbook][Form] Fixed Typo & missing word. (ahsio) +- `127beed `_ #3770 Update factories.rst (AlaaAttya) +- `822d985 `_ #3817 Update translation.rst (richardpi) +- `241d923 `_ #3813 [Reference][Forms]fix time field count. (yositani2002) +- `bc96f55 `_ #3812 [Cookbook][Configuration] Fixed broken link. (ahsio) +- `5867327 `_ #3809 Fixed typo (WouterJ) +- `678224e `_ #3808 Fixed broken link in "Handling Authentication Failure" (stacyhorton) + April, 2014 ----------- From 43ae4f132cc9fa125fee8e0374bd7ab00451b6d1 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 8 Jun 2014 17:23:21 +0200 Subject: [PATCH 109/835] Added May changelog --- changelog.rst | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/changelog.rst b/changelog.rst index f5b997e4cea..46868fdda58 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,80 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +May, 2014 +--------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `4fd1b49 `_ #3753 [DependencyInjection] Add documentation about service decoration (romainneutron) +- `f913dd7 `_ #3603 [Serializer] Support for is.* getters in GetSetMethodNormalizer (tiraeth) +- `e8511cb `_ #3776 Updated event_listener.rst (bfgasparin) +- `af8c20f `_ #3818 [Form customization] added block_name example. (aitboudad) +- `c788325 `_ #3841 [Cookbook][Logging] register processor per handler and per channel (xabbuh) +- `979533a `_ #3839 document how to test actions (greg0ire) +- `d8aaac3 `_ #3835 Updated framework.ide configuration (WouterJ) +- `a9648e8 `_ #3742 [2.5][Templating] Add documentation about generating versioned URLs (romainneutron) +- `f665e14 `_ #3704 [Form] Added documentation for Form Events (csarrazi) +- `14b9f14 `_ #3777 added docs for the core team (fabpot) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `0649c21 `_ #3869 Add a missing argument to the PdoSessionHandler (jakzal) +- `259a2b7 `_ #3866 [Book][Security]fixed Login when there is no session. (aitboudad) +- `9b7584f `_ #3863 Error in XML (tvlooy) +- `0cb9c3b `_ #3827 Update 'How to Create and store a Symfony2 Project in Git' (nicwortel) +- `4ed9a08 `_ #3830 Generate an APC prefix based on __FILE__ (trsteel88) +- `9a65412 `_ #3840 Update dialoghelper.rst (jdecoster) +- `1853fea `_ #3716 Fix issue #3712 (umpirsky) +- `baa9759 `_ #3791 Property access tweaks (weaverryan) +- `80d70a4 `_ #3779 [Book][Security] constants are defined in the SecurityContextInterface (xabbuh) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `302fa82 `_ #3872 Update hostname_pattern.rst (sofany) +- `50672f7 `_ #3867 fixed missing info about FosUserBundle. (aitboudad) +- `3e3004f `_ #3865 Fixed link. (aitboudad) +- `b32ec15 `_ #3856 Update voters_data_permission.rst (MarcomTeam) +- `bffe163 `_ #3859 Add filter cssrewrite (DOEO) +- `f617ff8 `_ #3764 Update testing.rst (NAYZO) +- `3792fee `_ #3858 Clarified Password Encoders example (WouterJ) +- `663d68c `_ #3857 Added little bit information about the route name (WouterJ) +- `797cbd5 `_ #3794 Adds link to new QuestionHelper (weaverryan) +- `4211bff `_ #3852 Fixed link and typo in type_guesser.rst (rpg600) +- `78ae7ec `_ #3845 added link to /cookbook/security/force_https. (aitboudad) +- `6c69362 `_ #3846 [Routing][Loader] added JMSI18nRoutingBundle (aitboudad) +- `136864b `_ #3844 [Components] Fixed some typos. (ahsio) +- `b0710bc `_ #3842 Update dialoghelper.rst (bijsterdee) +- `9f1a354 `_ #3804 [Components][DependencyInjection] add note about a use case that requires to compile the container (xabbuh) +- `d92c522 `_ #3769 Updated references to new Session() (scottwarren) +- `00f60a8 `_ #3837 More asset version details (weaverryan) +- `681ddc8 `_ #3843 [Changelog] fix literal positions (xabbuh) +- `1aa79d5 `_ #3834 fix the wording in versionadded directives (for the master branch) (xabbuh) +- `7288a33 `_ #3789 [Reference][Forms] Improvements to the form type (xabbuh) +- `72fae25 `_ #3790 [Reference][Forms] move versionadded directives for form options directly below the option's headline (xabbuh) +- `b4d4ac3 `_ #3838 fix filename typo in cookbook/form/unit_testing.rst (hice3000) +- `0b06287 `_ #3836 remove unnecessary rewrite from nginx conf (Burgov) +- `89d0dae `_ #3833 fix the wording in versionadded directives (for the 2.4 branch) (xabbuh) +- `e58e39f `_ #3832 fix the wording in versionadded directives (for the 2.3 branch) (xabbuh) +- `09d6ca1 `_ #3829 [Components] consistent headlines (xabbuh) +- `54e0882 `_ #3828 [Contributing] consistent headlines (xabbuh) +- `b1336d7 `_ #3823 Added empty line after if statements (zomberg) +- `79b9fdc `_ #3822 Update voters_data_permission.rst (mimol91) +- `69cb7b8 `_ #3821 Update custom_authentication_provider.rst (leberknecht) +- `9f602c4 `_ #3820 Update page_creation.rst (adreeun) +- `52518c0 `_ #3819 Update csrf_in_login_form.rst (micheal) +- `1adfd9b `_ #3802 Add a note about which types can be used in Symfony (fabpot) +- `fa27ded `_ #3801 [Cookbook][Form] Fixed Typo & missing word. (ahsio) +- `127beed `_ #3770 Update factories.rst (AlaaAttya) +- `822d985 `_ #3817 Update translation.rst (richardpi) +- `241d923 `_ #3813 [Reference][Forms]fix time field count. (yositani2002) +- `bc96f55 `_ #3812 [Cookbook][Configuration] Fixed broken link. (ahsio) +- `5867327 `_ #3809 Fixed typo (WouterJ) +- `678224e `_ #3808 Fixed broken link in "Handling Authentication Failure" (stacyhorton) + April, 2014 ----------- From c24d262a848de52a55f2aae9336319c06e340d86 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 27 May 2014 22:43:49 +0200 Subject: [PATCH 110/835] change version numbers in installation notes to be in line with the documented Symfony version --- book/installation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index a929a0a9692..c9e24f32721 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -57,7 +57,7 @@ Distribution: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony dev-master + $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony 2.5.* .. tip:: @@ -104,10 +104,10 @@ one of the following commands (replacing ``###`` with your actual filename): .. code-block:: bash # for .tgz file - $ tar zxvf Symfony_Standard_Vendors_2.4.###.tgz + $ tar zxvf Symfony_Standard_Vendors_2.5.###.tgz # for a .zip file - $ unzip Symfony_Standard_Vendors_2.4.###.zip + $ unzip Symfony_Standard_Vendors_2.5.###.zip If you've downloaded "without vendors", you'll definitely need to read the next section. From cf667c94daa0d29e797d9d3c0031feeec87ac51e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 11 Jun 2014 00:15:12 +0200 Subject: [PATCH 111/835] update the TraceableEventDispatcher to reflect its movement to the EventDispatcher component in Symfony 2.5 --- components/event_dispatcher/traceable_dispatcher.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/event_dispatcher/traceable_dispatcher.rst b/components/event_dispatcher/traceable_dispatcher.rst index 6b1cb19db86..22404865fbf 100644 --- a/components/event_dispatcher/traceable_dispatcher.rst +++ b/components/event_dispatcher/traceable_dispatcher.rst @@ -5,13 +5,17 @@ The Traceable Event Dispatcher ============================== -The :class:`Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher` +.. versionadded:: 2.5 + The ``TraceableEventDispatcher`` class was moved to the EventDispatcher + component in Symfony 2.5. Before, it was located in the HttpKernel component. + +The :class:`Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher` is an event dispatcher that wraps any other event dispatcher and can then be used to determine which event listeners have been called by the dispatcher. Pass the event dispatcher to be wrapped and an instance of the :class:`Symfony\\Component\\Stopwatch\\Stopwatch` to its constructor:: - use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher; + use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; use Symfony\Component\Stopwatch\Stopwatch; // the event dispatcher to debug From c7c48b86e84d03df2b08daec12ceda9d550fae5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Wed, 11 Jun 2014 14:05:59 +0200 Subject: [PATCH 112/835] fix typo --- book/translation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/translation.rst b/book/translation.rst index 0f46aa03923..ce10b796644 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -647,7 +647,7 @@ Translating Database Content The translation of database content should be handled by Doctrine through the `Translatable Extension`_ or the `Translatable Behavior`_ (PHP 5.4+). -For more information, see the documentation for thes libraries. +For more information, see the documentation for these libraries. Debugging Translations ---------------------- From 6c4421b2a30806fabd8b6e5001882aedc9d3dcb4 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 11 Jun 2014 18:20:17 +0200 Subject: [PATCH 113/835] properly escape backslashes in class and method directives --- book/security.rst | 2 +- components/class_loader/psr4_class_loader.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index fb0f7884dfb..0572bc13330 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1105,7 +1105,7 @@ authorization from inside a controller:: The ``createAccessDeniedException`` method was introduced in Symfony 2.5. The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException` -method creates a special :class:`Symfony\\Component\\Security\\Core\Exception\\AccessDeniedException` +method creates a special :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` object, which ultimately triggers a 403 HTTP response inside Symfony. Thanks to the SensioFrameworkExtraBundle, you can also secure your controller using annotations:: diff --git a/components/class_loader/psr4_class_loader.rst b/components/class_loader/psr4_class_loader.rst index 489db8a351d..e388590310d 100644 --- a/components/class_loader/psr4_class_loader.rst +++ b/components/class_loader/psr4_class_loader.rst @@ -59,7 +59,7 @@ first need to configure the ``Psr4ClassLoader``: First of all, the class loader is loaded manually using a ``require`` statement, since there is no autoload mechanism yet. With the -:method:`Symfony\Component\ClassLoader\Psr4ClassLoader::addPrefix` call, you +:method:`Symfony\\Component\\ClassLoader\\Psr4ClassLoader::addPrefix` call, you tell the class loader where to look for classes with the ``Symfony\Component\Yaml\`` namespace prefix. After registering the autoloader, the Yaml component is ready to be used. From 34ad1b5773820bc4c80b17d3984aedae1691a8c1 Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Fri, 6 Jun 2014 14:28:59 +0200 Subject: [PATCH 114/835] [Security] Added remote_user firewall info and documentation for pre authenticated firewalls --- reference/configuration/security.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 2eda8410c86..340d9af8c3a 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -121,6 +121,9 @@ Each part will be explained in the next section. stateless: false x509: provider: some_key_from_above + # new in Symfony 2.6 + remote_user: + provider: some_key_from_above http_basic: provider: some_key_from_above http_digest: From 8465d465f8cde4a60d35a776313bee363f38e577 Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Fri, 6 Jun 2014 14:56:44 +0200 Subject: [PATCH 115/835] [Reference][Configuration] Removed version added for remote_user --- reference/configuration/security.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 340d9af8c3a..66c15ae0916 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -121,7 +121,6 @@ Each part will be explained in the next section. stateless: false x509: provider: some_key_from_above - # new in Symfony 2.6 remote_user: provider: some_key_from_above http_basic: From 86ba188bbe274d9e1d9b3d392e2c57a737eb63cd Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Thu, 12 Jun 2014 17:34:04 +0200 Subject: [PATCH 116/835] rebased using x509 pr, added remote_user pre authenticated part --- cookbook/security/pre_authenticated.rst | 66 ++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/cookbook/security/pre_authenticated.rst b/cookbook/security/pre_authenticated.rst index fe77000422c..d5490fde519 100644 --- a/cookbook/security/pre_authenticated.rst +++ b/cookbook/security/pre_authenticated.rst @@ -66,6 +66,8 @@ the user provider, and sets the ``SSL_CLIENT_S_DN`` as credentials in the You can override these by setting the ``user`` and the ``credentials`` keys in the x509 firewall configuration respectively. +.. _cookbook-security-pre-authenticated-user-provider-note: + .. note:: An authentication provider will only inform the user provider of the username @@ -76,4 +78,66 @@ in the x509 firewall configuration respectively. provider, see: * :doc:`/cookbook/security/custom_provider` - * :doc:`/cookbook/security/entity_provider` \ No newline at end of file + * :doc:`/cookbook/security/entity_provider` + +REMOTE_USER based Authentication +-------------------------------- + +.. versionadded:: 2.6 + REMOTE_USER pre authenticated firewall was introduced in Symfony 2.6. + +A lot of authentication modules, like ``auth_kerb` for Apache provide the username +using the ``REMOTE_USER`` environment variable. This variable can be trusted by +the application since the authentication happened before the request reached it. + +To configure Symfony using the ``REMOTE_USER` environment variable, simply enable the +corresponding firewall in your security configuration: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + firewalls: + secured_area: + pattern: ^/ + remote_user: + provider: your_user_provider + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + 'pattern' => '^/' + 'remote_user' => array( + 'provider' => 'your_user_provider', + ), + ), + ), + )); + +The firewall will then provide the ``REMOTE_USER`` environment variable to +your user provider. You can change the variable name used by setting the ``user`` +key in the ``remote_user`` firewall configuration. + +.. note:: + + Just like for X509 authentication, you will need to configure a "user provider". + See :ref:`the note about it `. From be0d866de3fe6392d1dceddbff08d0e31025dc9e Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Thu, 12 Jun 2014 19:08:02 +0200 Subject: [PATCH 117/835] fix missing backtick, rephrased bottom note --- cookbook/security/pre_authenticated.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cookbook/security/pre_authenticated.rst b/cookbook/security/pre_authenticated.rst index d5490fde519..640fc9f5cf7 100644 --- a/cookbook/security/pre_authenticated.rst +++ b/cookbook/security/pre_authenticated.rst @@ -73,8 +73,8 @@ in the x509 firewall configuration respectively. An authentication provider will only inform the user provider of the username that made the request. You will need to create (or use) a "user provider" that is referenced by the ``provider`` configuration parameter (``your_user_provider`` - in the configuration example). This provider will turn the username into a User - object of your choice. For more information on creating or configuring a user + in the configuration example). This provider will turn the username into a User + object of your choice. For more information on creating or configuring a user provider, see: * :doc:`/cookbook/security/custom_provider` @@ -86,11 +86,11 @@ REMOTE_USER based Authentication .. versionadded:: 2.6 REMOTE_USER pre authenticated firewall was introduced in Symfony 2.6. -A lot of authentication modules, like ``auth_kerb` for Apache provide the username -using the ``REMOTE_USER`` environment variable. This variable can be trusted by +A lot of authentication modules, like ``auth_kerb` for Apache provide the username +using the ``REMOTE_USER`` environment variable. This variable can be trusted by the application since the authentication happened before the request reached it. -To configure Symfony using the ``REMOTE_USER` environment variable, simply enable the +To configure Symfony using the ``REMOTE_USER`` environment variable, simply enable the corresponding firewall in your security configuration: .. configuration-block:: @@ -140,4 +140,5 @@ key in the ``remote_user`` firewall configuration. .. note:: Just like for X509 authentication, you will need to configure a "user provider". - See :ref:`the note about it `. + See :ref:`the note previous note ` + for more information. From 9dfc088545d453ae938e40dd7621b32af58edb95 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Fri, 13 Jun 2014 13:28:38 +0200 Subject: [PATCH 118/835] Fixed missing component name in namespaces --- components/console/helpers/table.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst index 7729ecce6eb..0f3511fe040 100644 --- a/components/console/helpers/table.rst +++ b/components/console/helpers/table.rst @@ -24,7 +24,7 @@ When building a console application it may be useful to display tabular data: To display a table, use :class:`Symfony\\Component\\Console\\Helper\\Table`, set the headers, set the rows and then render the table:: - use Symfony\Component\Helper\Table; + use Symfony\Component\Console\Helper\Table; $table = new Table($output); $table @@ -41,7 +41,7 @@ set the headers, set the rows and then render the table:: You can add a table separator anywhere in the output by passing an instance of :class:`Symfony\\Component\\Console\\Helper\\TableSeparator` as a row:: - use Symfony\Component\Helper\TableSeparator; + use Symfony\Component\Console\Helper\TableSeparator; $table->setRows(array( array('99921-58-10-7', 'Divine Comedy', 'Dante Alighieri'), @@ -103,7 +103,7 @@ which outputs: If the built-in styles do not fit your need, define your own:: - use Symfony\Component\Helper\TableStyle; + use Symfony\Component\Console\Helper\TableStyle; // by default, this is based on the default style $style = new TableStyle(); From 80673d58627435aeab3d0daa0891205c2306b344 Mon Sep 17 00:00:00 2001 From: Vyacheslav Salakhutdinov Date: Mon, 23 Jun 2014 05:23:37 +0000 Subject: [PATCH 119/835] Disallow empty file in FileValidator --- reference/constraints/File.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 683d0fe6a49..c06a20fd71b 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -23,6 +23,7 @@ form type. | | - `mimeTypes`_ | | | - `maxSizeMessage`_ | | | - `mimeTypesMessage`_ | +| | - `disallowEmptyMessage`_ | | | - `notFoundMessage`_ | | | - `notReadableMessage`_ | | | - `uploadIniSizeErrorMessage`_ | @@ -194,6 +195,18 @@ mimeTypesMessage The message displayed if the mime type of the file is not a valid mime type per the `mimeTypes`_ option. +disallowEmptyMessage +~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.6 + The ``disallowEmptyMessage`` option was introduced in Symfony 2.6. Prior to 2.6, + if the user uploaded an empty file, no validation error occurred. + +**type**: ``string`` **default**: ``An empty file is not allowed.`` + +This constraint checks if the uploaded file is empty (i.e. 0 bytes). If it is, +this message is displayed. + notFoundMessage ~~~~~~~~~~~~~~~ From d1c95ceeb2722ff8935f3b40aef4b0fbf6b6ba27 Mon Sep 17 00:00:00 2001 From: Diego Saint Esteben Date: Tue, 24 Jun 2014 00:14:32 -0300 Subject: [PATCH 120/835] Added PSR-4 ClassLoader to list --- components/class_loader/introduction.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/components/class_loader/introduction.rst b/components/class_loader/introduction.rst index 2eff7ee69e8..8e529c0ac52 100644 --- a/components/class_loader/introduction.rst +++ b/components/class_loader/introduction.rst @@ -12,11 +12,14 @@ Usage Whenever you reference a class that has not been required or included yet, PHP uses the `autoloading mechanism`_ to delegate the loading of a file defining -the class. Symfony2 provides two autoloaders, which are able to load your classes: +the class. Symfony2 provides three autoloaders, which are able to load your classes: * :doc:`/components/class_loader/class_loader`: loads classes that follow the `PSR-0` class naming standard; +* :doc:`/components/class_loader/psr4_class_loader`: loads classes that follow + the `PSR-4` class naming standard; + * :doc:`/components/class_loader/map_class_loader`: loads classes using a static map from class name to file path. From e3acdc5dcfcf6419c0a784a8151b90067d01452e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sat, 31 May 2014 23:26:15 +0200 Subject: [PATCH 121/835] Support MaxSize in KiB and MiB --- reference/constraints/File.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 683d0fe6a49..7d200e4f36b 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -163,10 +163,16 @@ to be valid. The size of the file can be given in one of the following formats: numeric (e.g. ``4096``); * **kilobytes**: To specify the ``maxSize`` in kilobytes, pass a number and - suffix it with a lowercase "k" (e.g. ``200k``); + suffix it with a "k" (e.g. ``200k``); * **megabytes**: To specify the ``maxSize`` in megabytes, pass a number and - suffix it with a capital "M" (e.g. ``4M``). + suffix it with a "M" (e.g. ``4M``). + +* **kibibytes**: To specify the ``maxSize`` in kibibytes, pass a number and + suffix it with a "Ki" (e.g. ``600Ki``); + +* **mebibytes**: To specify the ``maxSize`` in mebibytes, pass a number and + suffix it with a "Mi" (e.g. ``8Mi``). mimeTypes ~~~~~~~~~ From deac0c3a6fd13c61eeca59d715c7fab014836f73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sun, 1 Jun 2014 15:33:56 +0200 Subject: [PATCH 122/835] Add option binaryFormat in constraint file --- reference/constraints/File.rst | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 7d200e4f36b..a09c98a70d8 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -20,6 +20,7 @@ form type. | Applies to | :ref:`property or method ` | +----------------+---------------------------------------------------------------------+ | Options | - `maxSize`_ | +| | - `binaryFormat`_ | | | - `mimeTypes`_ | | | - `maxSizeMessage`_ | | | - `mimeTypesMessage`_ | @@ -78,7 +79,7 @@ below a certain file size and a valid PDF, add the following: maxSize: 1024k mimeTypes: [application/pdf, application/x-pdf] mimeTypesMessage: Please upload a valid PDF - + .. code-block:: php-annotations @@ -151,6 +152,18 @@ have been specified. Options ------- +.. versionadded:: 2.6 + The ``binaryFormat`` option was introduced in Symfony 2.6. + +binaryFormat +~~~~~~~~~~~~ + +**type**: ``boolean`` **default**: ``null`` + +When true, the sizes will be displayed in messages with binary suffixes (KiB, MiB). +When false, the sizes will be displayed with SI suffixes (kB, MB). +When null, then the binaryFormat will be guessed from the suffix defined in the maxSize option. + maxSize ~~~~~~~ From 3f3f4e0292a80194e6c54d048feabef81a75c610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sun, 1 Jun 2014 16:05:54 +0200 Subject: [PATCH 123/835] Provide information about SI and Binary prefixes --- reference/constraints/File.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index a09c98a70d8..0c02b191097 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -164,6 +164,8 @@ When true, the sizes will be displayed in messages with binary suffixes (KiB, Mi When false, the sizes will be displayed with SI suffixes (kB, MB). When null, then the binaryFormat will be guessed from the suffix defined in the maxSize option. +For more information about the difference between binary and SI suffixes, see `Wikipedia: Binary prefix`_. + maxSize ~~~~~~~ @@ -173,19 +175,21 @@ If set, the size of the underlying file must be below this file size in order to be valid. The size of the file can be given in one of the following formats: * **bytes**: To specify the ``maxSize`` in bytes, pass a value that is entirely - numeric (e.g. ``4096``); + numeric (e.g. ``4096``). * **kilobytes**: To specify the ``maxSize`` in kilobytes, pass a number and - suffix it with a "k" (e.g. ``200k``); + suffix it with a "k" (e.g. ``200k``); 1k = 1 000 bytes. * **megabytes**: To specify the ``maxSize`` in megabytes, pass a number and - suffix it with a "M" (e.g. ``4M``). + suffix it with a "M" (e.g. ``4M``); 1M = 1 000 000 bytes. * **kibibytes**: To specify the ``maxSize`` in kibibytes, pass a number and - suffix it with a "Ki" (e.g. ``600Ki``); + suffix it with a "Ki" (e.g. ``600Ki``); 1Ki = 1 024 bytes. * **mebibytes**: To specify the ``maxSize`` in mebibytes, pass a number and - suffix it with a "Mi" (e.g. ``8Mi``). + suffix it with a "Mi" (e.g. ``8Mi``); 1Mi = 1 048 576 bytes. + +For more information about the difference between binary and SI suffixes, see `Wikipedia: Binary prefix`_. mimeTypes ~~~~~~~~~ @@ -257,3 +261,4 @@ to disk. .. _`IANA website`: http://www.iana.org/assignments/media-types/index.html +.. _`Wikipedia: Binary prefix`: http://en.wikipedia.org/wiki/Binary_prefix From 71fdd60f3c28521b4588f0908dedb1ce06f0d36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sun, 1 Jun 2014 16:53:08 +0200 Subject: [PATCH 124/835] Fix line wrap --- reference/constraints/File.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 0c02b191097..cd573c3a7e3 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -160,11 +160,13 @@ binaryFormat **type**: ``boolean`` **default**: ``null`` -When true, the sizes will be displayed in messages with binary suffixes (KiB, MiB). -When false, the sizes will be displayed with SI suffixes (kB, MB). -When null, then the binaryFormat will be guessed from the suffix defined in the maxSize option. +When ``true``, the sizes will be displayed in messages with binary suffixes +(KiB, MiB). When ``false``, the sizes will be displayed with SI suffixes (kB, +MB). When ``null``, then the binaryFormat will be guessed from the suffix +defined in the maxSize option. -For more information about the difference between binary and SI suffixes, see `Wikipedia: Binary prefix`_. +For more information about the difference between binary and SI suffixes, +see `Wikipedia: Binary prefix`_. maxSize ~~~~~~~ @@ -189,7 +191,8 @@ to be valid. The size of the file can be given in one of the following formats: * **mebibytes**: To specify the ``maxSize`` in mebibytes, pass a number and suffix it with a "Mi" (e.g. ``8Mi``); 1Mi = 1 048 576 bytes. -For more information about the difference between binary and SI suffixes, see `Wikipedia: Binary prefix`_. +For more information about the difference between binary and SI suffixes, +see `Wikipedia: Binary prefix`_. mimeTypes ~~~~~~~~~ From 7dcd7c3b9b0d85be92a074f0dce8d28ae6fd7629 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sun, 1 Jun 2014 20:51:03 +0200 Subject: [PATCH 125/835] Move secion "binaryFormat" to the right place --- reference/constraints/File.rst | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index cd573c3a7e3..355901362b1 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -152,22 +152,6 @@ have been specified. Options ------- -.. versionadded:: 2.6 - The ``binaryFormat`` option was introduced in Symfony 2.6. - -binaryFormat -~~~~~~~~~~~~ - -**type**: ``boolean`` **default**: ``null`` - -When ``true``, the sizes will be displayed in messages with binary suffixes -(KiB, MiB). When ``false``, the sizes will be displayed with SI suffixes (kB, -MB). When ``null``, then the binaryFormat will be guessed from the suffix -defined in the maxSize option. - -For more information about the difference between binary and SI suffixes, -see `Wikipedia: Binary prefix`_. - maxSize ~~~~~~~ @@ -194,6 +178,22 @@ to be valid. The size of the file can be given in one of the following formats: For more information about the difference between binary and SI suffixes, see `Wikipedia: Binary prefix`_. +binaryFormat +~~~~~~~~~~~~ + +.. versionadded:: 2.6 + The ``binaryFormat`` option was introduced in Symfony 2.6. + +**type**: ``boolean`` **default**: ``null`` + +When ``true``, the sizes will be displayed in messages with binary suffixes +(KiB, MiB). When ``false``, the sizes will be displayed with SI suffixes (kB, +MB). When ``null``, then the binaryFormat will be guessed from the suffix +defined in the maxSize option. + +For more information about the difference between binary and SI suffixes, +see `Wikipedia: Binary prefix`_. + mimeTypes ~~~~~~~~~ From b414f5c6e69cbad6a6c6a4d860f8144d0644650a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Thu, 19 Jun 2014 23:16:21 +0200 Subject: [PATCH 126/835] Use comma as thousands separator --- reference/constraints/File.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 355901362b1..6eb19e232af 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -80,7 +80,6 @@ below a certain file size and a valid PDF, add the following: mimeTypes: [application/pdf, application/x-pdf] mimeTypesMessage: Please upload a valid PDF - .. code-block:: php-annotations // src/Acme/BlogBundle/Entity/Author.php @@ -161,19 +160,19 @@ If set, the size of the underlying file must be below this file size in order to be valid. The size of the file can be given in one of the following formats: * **bytes**: To specify the ``maxSize`` in bytes, pass a value that is entirely - numeric (e.g. ``4096``). + numeric (e.g. ``4096``); * **kilobytes**: To specify the ``maxSize`` in kilobytes, pass a number and - suffix it with a "k" (e.g. ``200k``); 1k = 1 000 bytes. + suffix it with a "k" (e.g. ``200k``); 1k = 1,000 bytes; * **megabytes**: To specify the ``maxSize`` in megabytes, pass a number and - suffix it with a "M" (e.g. ``4M``); 1M = 1 000 000 bytes. + suffix it with a "M" (e.g. ``4M``); 1M = 1,000,000 bytes; * **kibibytes**: To specify the ``maxSize`` in kibibytes, pass a number and - suffix it with a "Ki" (e.g. ``600Ki``); 1Ki = 1 024 bytes. + suffix it with a "Ki" (e.g. ``600Ki``); 1Ki = 1,024 bytes; * **mebibytes**: To specify the ``maxSize`` in mebibytes, pass a number and - suffix it with a "Mi" (e.g. ``8Mi``); 1Mi = 1 048 576 bytes. + suffix it with a "Mi" (e.g. ``8Mi``); 1Mi = 1,048,576 bytes. For more information about the difference between binary and SI suffixes, see `Wikipedia: Binary prefix`_. From 77a0687f3f2ca2a9aed57984d0fc0d73801317b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Tue, 24 Jun 2014 22:15:47 +0200 Subject: [PATCH 127/835] Add versionAdded on binary suffix --- reference/constraints/File.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 6eb19e232af..25edb10c8da 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -154,6 +154,9 @@ Options maxSize ~~~~~~~ +.. versionadded:: 2.6 + The suffixes ``Ki`` and ``Mi`` were introduced in Symfony 2.6. + **type**: ``mixed`` If set, the size of the underlying file must be below this file size in order @@ -188,7 +191,7 @@ binaryFormat When ``true``, the sizes will be displayed in messages with binary suffixes (KiB, MiB). When ``false``, the sizes will be displayed with SI suffixes (kB, MB). When ``null``, then the binaryFormat will be guessed from the suffix -defined in the maxSize option. +defined in the ``maxSize`` option. For more information about the difference between binary and SI suffixes, see `Wikipedia: Binary prefix`_. From 1e8fa48566091c10eb2ce40611e1909e6f73c9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Tue, 24 Jun 2014 22:17:51 +0200 Subject: [PATCH 128/835] Replace bullet list by a table --- reference/constraints/File.rst | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 25edb10c8da..686442cb365 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -162,20 +162,19 @@ maxSize If set, the size of the underlying file must be below this file size in order to be valid. The size of the file can be given in one of the following formats: -* **bytes**: To specify the ``maxSize`` in bytes, pass a value that is entirely - numeric (e.g. ``4096``); - -* **kilobytes**: To specify the ``maxSize`` in kilobytes, pass a number and - suffix it with a "k" (e.g. ``200k``); 1k = 1,000 bytes; - -* **megabytes**: To specify the ``maxSize`` in megabytes, pass a number and - suffix it with a "M" (e.g. ``4M``); 1M = 1,000,000 bytes; - -* **kibibytes**: To specify the ``maxSize`` in kibibytes, pass a number and - suffix it with a "Ki" (e.g. ``600Ki``); 1Ki = 1,024 bytes; - -* **mebibytes**: To specify the ``maxSize`` in mebibytes, pass a number and - suffix it with a "Mi" (e.g. ``8Mi``); 1Mi = 1,048,576 bytes. ++--------+-----------+-----------------+------+ +| Suffix | Unit Name | value | e.g. | ++========+===========+=================+======+ +| | byte | 1 byte | 4096 | ++--------+-----------+-----------------+------+ +| k | kilobyte | 1,000 bytes | 200k | ++--------+-----------+-----------------+------+ +| M | megabyte | 1,000,000 bytes | 2M | ++--------+-----------+-----------------+------+ +| Ki | kibibyte | 1,024 bytes | 32Ki | ++--------+-----------+-----------------+------+ +| Mi | mebibyte | 1,048,576 bytes | 8Mi | ++--------+-----------+-----------------+------+ For more information about the difference between binary and SI suffixes, see `Wikipedia: Binary prefix`_. From b8a0eb2660c073c4742c85f47cfa3109314106af Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Wed, 25 Jun 2014 14:19:25 +0200 Subject: [PATCH 129/835] fixes missing backtick --- cookbook/security/pre_authenticated.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/pre_authenticated.rst b/cookbook/security/pre_authenticated.rst index 640fc9f5cf7..394ae4f739e 100644 --- a/cookbook/security/pre_authenticated.rst +++ b/cookbook/security/pre_authenticated.rst @@ -86,7 +86,7 @@ REMOTE_USER based Authentication .. versionadded:: 2.6 REMOTE_USER pre authenticated firewall was introduced in Symfony 2.6. -A lot of authentication modules, like ``auth_kerb` for Apache provide the username +A lot of authentication modules, like ``auth_kerb`` for Apache provide the username using the ``REMOTE_USER`` environment variable. This variable can be trusted by the application since the authentication happened before the request reached it. From f34eaed712931efa116d588793ec07f40b102bb2 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 1 Jul 2014 19:18:58 +0200 Subject: [PATCH 130/835] Added June changelog --- changelog.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog.rst b/changelog.rst index 61eecf7732b..8e8f7f27c7f 100644 --- a/changelog.rst +++ b/changelog.rst @@ -28,11 +28,13 @@ New Documentation - `23b51c8 `_ #3901 Bootstraped the standards for "Files and Directories" (javiereguiluz) - `8931c36 `_ #3889 Fixed the section about getting services from a command (javiereguiluz) - `9fddab6 `_ #3877 Added a note about configuring several paths under the same namespace (javiereguiluz) +- `eadf281 `_ #3874 Updated the installation instructions for Symfony 2.5+ (javiereguiluz) Fixed Documentation ~~~~~~~~~~~~~~~~~~~ - `aeffd12 `_ #3961 Fixing php coding (mvhirsch) +- `84332ff `_ #3945 Fixed missing component name in namespaces (WouterJ) - `d8329dc `_ #3943 Fixing simple quotes in double quotes (ptitlazy) - `04f4318 `_ #3934 Move __construct after the repository assignment (cmodijk) - `0626f2b `_ #3897 Collection constraint (hhamon) @@ -48,13 +50,17 @@ Minor Documentation Changes - `fba083e `_ #3957 [Cookbook][Bundles] fix typos in the prepend extension chapter (xabbuh) - `c444b5d `_ #3948 update the Sphinx extensions to raise warnings when backslashes are not ... (xabbuh) - `8fef7b7 `_ #3938 [Contributing][Documentation] don't render the list inside a blockquote (xabbuh) +- `b7a03f8 `_ #3937 properly escape backslashes in class and method directives (xabbuh) +- `882471f `_ #3935 Typo (greg0ire) - `222a014 `_ #3933 render directory inside a code block (xabbuh) +- `0c2a9b3 `_ #3931 [Component][EventDispatcher] 2.5 specific documentation for the TraceableEventDispatcher (xabbuh) - `b31ea51 `_ #3929 Update custom_authentication_provider.rst (verschoof) - `7937864 `_ #3927 [Cookbook][Security] Explicit 'your_user_provider' configuration parameter (zefrog) - `26d00d0 `_ #3925 Fixed the indentation of two code blocks (javiereguiluz) - `351b2cf `_ #3922 update fabpot Sphinx extensions version (xabbuh) - `3ddbe1b `_ #3923 Fixed the headers of one table (javiereguiluz) - `35cbffc `_ #3920 [Components][Form] remove blank line to render the versionadded directive properly (xabbuh) +- `df9f31a `_ #3882 change version numbers in installation notes to be in line with the docu... (xabbuh) - `ed496ae `_ #3887 [Components][Form] add versionadded for the data collector form extension (xabbuh) - `36337e7 `_ #3906 Blockquote introductions (xabbuh) - `5e0e119 `_ #3899 [RFR] Misc. fixes mostly related to formatting issues (javiereguiluz) From fcae3cd8faee35aba5077b8754e7874d3c52fdff Mon Sep 17 00:00:00 2001 From: Manuel Reinhard Date: Thu, 27 Mar 2014 08:00:15 +0100 Subject: [PATCH 131/835] Updated ISBN validator --- reference/constraints/Isbn.rst | 63 ++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/reference/constraints/Isbn.rst b/reference/constraints/Isbn.rst index 9518c527042..fb0be64a49b 100644 --- a/reference/constraints/Isbn.rst +++ b/reference/constraints/Isbn.rst @@ -4,14 +4,21 @@ Isbn .. versionadded:: 2.3 The Isbn constraint was introduced in Symfony 2.3. +.. caution:: + + The ``isbn10`` and ``isbn13`` options are deprecated since Symfony 2.5 + and will be removed in Symfony 3.0. Use the ``type`` option instead. + Furthermore, when using the ``type`` option, lowercase characters are no + longer supported starting in Symfony 2.5, as they are not allowed in ISBNs. + This constraint validates that an `International Standard Book Number (ISBN)`_ -is either a valid ISBN-10, a valid ISBN-13 or both. +is either a valid ISBN-10 or a valid ISBN-13. +----------------+----------------------------------------------------------------------+ | Applies to | :ref:`property or method` | +----------------+----------------------------------------------------------------------+ -| Options | - `isbn10`_ | -| | - `isbn13`_ | +| Options | - `type`_ | +| | - `message`_ | | | - `isbn10Message`_ | | | - `isbn13Message`_ | | | - `bothIsbnMessage`_ | @@ -25,7 +32,7 @@ Basic Usage ----------- To use the ``Isbn`` validator, simply apply it to a property or method -on an object that will contain a ISBN number. +on an object that will contain an ISBN. .. configuration-block:: @@ -36,9 +43,8 @@ on an object that will contain a ISBN number. properties: isbn: - Isbn: - isbn10: true - isbn13: true - bothIsbnMessage: This value is neither a valid ISBN-10 nor a valid ISBN-13. + type: isbn10 + message: This value is not valid. .. code-block:: php-annotations @@ -49,9 +55,8 @@ on an object that will contain a ISBN number. { /** * @Assert\Isbn( - * isbn10 = true, - * isbn13 = true, - * bothIsbnMessage = "This value is neither a valid ISBN-10 nor a valid ISBN-13." + * type = isbn10, + * message: This value is not valid. * ) */ protected $isbn; @@ -63,9 +68,8 @@ on an object that will contain a ISBN number. - - - + + @@ -85,9 +89,8 @@ on an object that will contain a ISBN number. public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('isbn', new Assert\Isbn(array( - 'isbn10' => true, - 'isbn13' => true, - 'bothIsbnMessage' => 'This value is neither a valid ISBN-10 nor a valid ISBN-13.' + 'type' => isbn10, + 'message' => 'This value is not valid.' ))); } } @@ -95,28 +98,28 @@ on an object that will contain a ISBN number. Available Options ----------------- -isbn10 -~~~~~~ +type +~~~~ -**type**: ``boolean`` +**type**: ``string`` **default**: ``null`` -If this required option is set to ``true`` the constraint will check if the -code is a valid ISBN-10 code. +The type of ISBN to validate against. +Valid values are ``isbn10``, ``isbn13`` and ``null`` to accept any kind of ISBN. -isbn13 -~~~~~~ +message +~~~~~~~ -**type**: ``boolean`` +**type**: ``string`` **default**: ``null`` -If this required option is set to ``true`` the constraint will check if the -code is a valid ISBN-13 code. +The message that will be shown if the value is not valid. +If not ``null``, this message has priority over all the other messages. isbn10Message ~~~~~~~~~~~~~ **type**: ``string`` **default**: ``This value is not a valid ISBN-10.`` -The message that will be shown if the `isbn10`_ option is true and the given +The message that will be shown if the `type`_ option is ``isbn10`` and the given value does not pass the ISBN-10 check. isbn13Message @@ -124,7 +127,7 @@ isbn13Message **type**: ``string`` **default**: ``This value is not a valid ISBN-13.`` -The message that will be shown if the `isbn13`_ option is true and the given +The message that will be shown if the `type`_ option is ``isbn13`` and the given value does not pass the ISBN-13 check. bothIsbnMessage @@ -132,7 +135,7 @@ bothIsbnMessage **type**: ``string`` **default**: ``This value is neither a valid ISBN-10 nor a valid ISBN-13.`` -The message that will be shown if both the `isbn10`_ and `isbn13`_ options -are true and the given value does not pass the ISBN-13 nor the ISBN-13 check. +The message that will be shown if the `type`_ option is ``null`` and the given +value does not pass any of the ISBN checks. .. _`International Standard Book Number (ISBN)`: http://en.wikipedia.org/wiki/Isbn From abda29b0a6957bbb1418fa87254db0a080a7741d Mon Sep 17 00:00:00 2001 From: WybrenKoelmans Date: Fri, 4 Jul 2014 10:31:37 +0200 Subject: [PATCH 132/835] #11212 [HttpFoundation] Added a switch to delete file after the response is send --- components/http_foundation/introduction.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index 4db19238609..92034f4efe6 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -515,6 +515,13 @@ You can still set the ``Content-Type`` of the sent file, or change its ``Content 'filename.txt' ); +.. versionadded:: 2.6 + The ``deleteFileAfterSend()`` method was introduced in Symfony 2.6. + +It is possible to delete the file after the request is send with the +:method:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse::deleteFileAfterSend` method. +Please note that this will not work when the ``X-Sendfile`` header is set. + .. _component-http-foundation-json-response: Creating a JSON Response From 1f4dc760c8283c656f1ae157391ed240108f61b3 Mon Sep 17 00:00:00 2001 From: Vincent Composieux Date: Sat, 5 Jul 2014 13:06:07 +0200 Subject: [PATCH 133/835] [Console] Fix Console some $app to $this and getHelperSet()->get() to getHelper() --- components/console/helpers/dialoghelper.rst | 10 +++++----- components/console/helpers/formatterhelper.rst | 2 +- components/console/helpers/progresshelper.rst | 2 +- components/console/helpers/questionhelper.rst | 10 +++++----- components/console/helpers/tablehelper.rst | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/components/console/helpers/dialoghelper.rst b/components/console/helpers/dialoghelper.rst index 3e7c016886e..cda8d8e934f 100644 --- a/components/console/helpers/dialoghelper.rst +++ b/components/console/helpers/dialoghelper.rst @@ -16,7 +16,7 @@ functions to ask the user for more information. It is included in the default helper set, which you can get by calling :method:`Symfony\\Component\\Console\\Command\\Command::getHelperSet`:: - $dialog = $this->getHelperSet()->get('dialog'); + $dialog = $this->getHelper('dialog'); All the methods inside the Dialog Helper have an :class:`Symfony\\Component\\Console\\Output\\OutputInterface` as the first @@ -69,7 +69,7 @@ Autocompletion 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'); + $dialog = $this->getHelper('dialog'); $bundleNames = array('AcmeDemoBundle', 'AcmeBlogBundle', 'AcmeStoreBundle'); $name = $dialog->ask( $output, @@ -84,7 +84,7 @@ Hiding the User's Response You can also ask a question and hide the response. This is particularly convenient for passwords:: - $dialog = $this->getHelperSet()->get('dialog'); + $dialog = $this->getHelper('dialog'); $password = $dialog->askHiddenResponse( $output, 'What is the database password?', @@ -152,7 +152,7 @@ Validating a Hidden Response You can also ask and validate a hidden response:: - $dialog = $this->getHelperSet()->get('dialog'); + $dialog = $this->getHelper('dialog'); $validator = function ($value) { if ('' === trim($value)) { @@ -186,7 +186,7 @@ Instead, you can use the method, which makes sure that the user can only enter a valid string from a predefined list:: - $dialog = $this->getHelperSet()->get('dialog'); + $dialog = $this->getHelper('dialog'); $colors = array('red', 'blue', 'yellow'); $color = $dialog->select( diff --git a/components/console/helpers/formatterhelper.rst b/components/console/helpers/formatterhelper.rst index 565be6e6bab..12386d04c3f 100644 --- a/components/console/helpers/formatterhelper.rst +++ b/components/console/helpers/formatterhelper.rst @@ -12,7 +12,7 @@ The :class:`Symfony\\Component\\Console\\Helper\\FormatterHelper` is included in the default helper set, which you can get by calling :method:`Symfony\\Component\\Console\\Command\\Command::getHelperSet`:: - $formatter = $this->getHelperSet()->get('formatter'); + $formatter = $this->getHelper('formatter'); The methods return a string, which you'll usually render to the console by passing it to the diff --git a/components/console/helpers/progresshelper.rst b/components/console/helpers/progresshelper.rst index 3662fff8800..78e09e0eb25 100644 --- a/components/console/helpers/progresshelper.rst +++ b/components/console/helpers/progresshelper.rst @@ -25,7 +25,7 @@ information, which updates as your command runs: To display progress details, use the :class:`Symfony\\Component\\Console\\Helper\\ProgressHelper`, pass it a total number of units, and advance the progress as your command executes:: - $progress = $this->getHelperSet()->get('progress'); + $progress = $this->getHelper('progress'); $progress->start($output, 50); $i = 0; diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst index 0f1031392c1..3305cb3776e 100644 --- a/components/console/helpers/questionhelper.rst +++ b/components/console/helpers/questionhelper.rst @@ -12,7 +12,7 @@ functions to ask the user for more information. It is included in the default helper set, which you can get by calling :method:`Symfony\\Component\\Console\\Command\\Command::getHelperSet`:: - $helper = $this->getHelperSet()->get('question'); + $helper = $this->getHelper('question'); The Question Helper has a single method :method:`Symfony\\Component\\Console\\Command\\Command::ask` that needs an @@ -30,7 +30,7 @@ the following to your command:: use Symfony\Component\Console\Question\ConfirmationQuestion; // ... - $helper = $this->getHelperSet()->get('question'); + $helper = $this->getHelper('question'); $question = new ConfirmationQuestion('Continue with this action?', false); if (!$helper->ask($input, $output, $question)) { @@ -73,7 +73,7 @@ from a predefined list:: use Symfony\Component\Console\Question\ChoiceQuestion; // ... - $helper = $app->getHelperSet()->get('question'); + $helper = $this->getHelper('question'); $question = new ChoiceQuestion( 'Please select your favorite color (defaults to red)', array('red', 'blue', 'yellow'), @@ -107,7 +107,7 @@ this use :method:`Symfony\\Component\\Console\\Question\\ChoiceQuestion::setMult use Symfony\Component\Console\Question\ChoiceQuestion; // ... - $helper = $app->getHelperSet()->get('question'); + $helper = $this->getHelper('question'); $question = new ChoiceQuestion( 'Please select your favorite color (defaults to red)', array('red', 'blue', 'yellow'), @@ -206,7 +206,7 @@ You can also use a validator with a hidden question:: use Symfony\Component\Console\Question\Question; // ... - $helper = $this->getHelperSet()->get('question'); + $helper = $this->getHelper('question'); $question = new Question('Please enter your password'); $question->setValidator(function ($value) { diff --git a/components/console/helpers/tablehelper.rst b/components/console/helpers/tablehelper.rst index 93643837181..d1c094938cf 100644 --- a/components/console/helpers/tablehelper.rst +++ b/components/console/helpers/tablehelper.rst @@ -21,7 +21,7 @@ When building a console application it may be useful to display tabular data: To display a table, use the :class:`Symfony\\Component\\Console\\Helper\\TableHelper`, set headers, rows and render:: - $table = $this->getHelperSet()->get('table'); + $table = $this->getHelper('table'); $table ->setHeaders(array('ISBN', 'Title', 'Author')) ->setRows(array( From 97c3894dd801a02f8af79925ea90b5fdfa1e6640 Mon Sep 17 00:00:00 2001 From: florianv Date: Sun, 6 Jul 2014 09:51:26 +0200 Subject: [PATCH 134/835] [Console] Fixed QuestionHelper examples --- components/console/helpers/questionhelper.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst index 0f1031392c1..2f756434edc 100644 --- a/components/console/helpers/questionhelper.rst +++ b/components/console/helpers/questionhelper.rst @@ -77,7 +77,7 @@ from a predefined list:: $question = new ChoiceQuestion( 'Please select your favorite color (defaults to red)', array('red', 'blue', 'yellow'), - 'red' + 0 ); $question->setErrorMessage('Color %s is invalid.'); @@ -109,9 +109,9 @@ this use :method:`Symfony\\Component\\Console\\Question\\ChoiceQuestion::setMult $helper = $app->getHelperSet()->get('question'); $question = new ChoiceQuestion( - 'Please select your favorite color (defaults to red)', + 'Please select your favorite colors (defaults to red and blue)', array('red', 'blue', 'yellow'), - 'red' + '0,1' ); $question->setMultiselect(true); @@ -121,6 +121,9 @@ this use :method:`Symfony\\Component\\Console\\Question\\ChoiceQuestion::setMult Now, when the user enters ``1,2``, the result will be: ``You have just selected: blue, yellow``. +If the user does not enter anything, the result will be: +``You have just selected: red, blue``. + Autocompletion ~~~~~~~~~~~~~~ From b8f27ef7c4246133c75764026d5086d4ade1d639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gildas=20Qu=C3=A9m=C3=A9ner?= Date: Wed, 9 Jul 2014 17:37:48 +0200 Subject: [PATCH 135/835] Removed wrong reference to cookbook The /cookbook/request/mime_type article does not contain an example to register a kernel listener since 2.5 --- reference/dic_tags.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 7ed9632e57b..18fce432bff 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -508,9 +508,6 @@ points. For a full example of this listener, read the :doc:`/cookbook/service_container/event_listener` cookbook entry. -For another practical example of a kernel listener, see the cookbook -article: :doc:`/cookbook/request/mime_type`. - Core Event Listener Reference ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 4bc00df2dd1413adf0f80821184f369e55aa2cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Va=C5=A1ek=20Purchart?= Date: Wed, 9 Jul 2014 10:57:13 +0200 Subject: [PATCH 136/835] Fixed documentation for ProgressBar This place is using the old ProgressHelper API and can be confusing when migrating. --- components/console/helpers/progressbar.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index 2ae9e64844a..46e993c08c0 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -265,10 +265,11 @@ to display it can be customized:: For performance reasons, be careful if you set the total number of steps to a high number. For example, if you're iterating over a large number of items, consider setting the redraw frequency to a higher value by calling - :method:`Symfony\\Component\\Console\\Helper\\ProgressHelper::setRedrawFrequency`, + :method:`Symfony\\Component\\Console\\Helper\\ProgressBar::setRedrawFrequency`, so it updates on only some iterations:: - $progress->start($output, 50000); + $progress = new ProgressBar($output, 50000); + $progress->start(); // update every 100 iterations $progress->setRedrawFrequency(100); From df8849136c94b1214cd4cec13a8111bb9174f49c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 15 Jul 2014 16:59:49 +0200 Subject: [PATCH 137/835] added CVE 2014-4931 --- contributing/code/security.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/contributing/code/security.rst b/contributing/code/security.rst index de7b63f8930..49a6ff35e30 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -96,6 +96,7 @@ Security Advisories This section indexes security vulnerabilities that were fixed in Symfony releases, starting from Symfony 1.0.0: +* July 15, 2014: `Security releases: Symfony 2.3.18, 2.4.8, and 2.5.2 released `_ (`CVE-2014-4931 `_) * October 10, 2013: `Security releases: Symfony 2.0.25, 2.1.13, 2.2.9, and 2.3.6 released `_ (`CVE-2013-5958 `_) * August 7, 2013: `Security releases: Symfony 2.0.24, 2.1.12, 2.2.5, and 2.3.3 released `_ (`CVE-2013-4751 `_ and `CVE-2013-4752 `_) * January 17, 2013: `Security release: Symfony 2.0.22 and 2.1.7 released `_ (`CVE-2013-1348 `_ and `CVE-2013-1397 `_) From 2299df0d643be336d277e01c92721b7d18f0b8ca Mon Sep 17 00:00:00 2001 From: Daniel Santana Date: Tue, 15 Jul 2014 13:25:28 -0400 Subject: [PATCH 138/835] Updating Symfony version from 2.4 to 2.5 The descriptions are still displaying the old version --- book/from_flat_php_to_symfony2.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index 0a333f4b7d3..e094d8b3716 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -431,7 +431,7 @@ content: { "require": { - "symfony/symfony": "2.4.*" + "symfony/symfony": "2.5.*" }, "autoload": { "files": ["model.php","controllers.php"] @@ -480,8 +480,8 @@ the HTTP response being returned. Use them to improve the blog: // echo the headers and send the response $response->send(); -.. versionadded:: 2.4 - Support for HTTP status code constants was introduced in Symfony 2.4. +.. versionadded:: 2.5 + Support for HTTP status code constants was introduced in Symfony 2.5. The controllers are now responsible for returning a ``Response`` object. To make this easier, you can add a new ``render_template()`` function, which, From 81d962f26ea9673a8d61cd0c562bc07f74f8cb9a Mon Sep 17 00:00:00 2001 From: Daniel Santana Date: Tue, 15 Jul 2014 16:30:39 -0400 Subject: [PATCH 139/835] Configuring composer.json to use the 2.5 version Configuring composer.json to use the 2.5 version Rather than 2.4 --- book/from_flat_php_to_symfony2.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index e094d8b3716..565a57fd565 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -480,8 +480,8 @@ the HTTP response being returned. Use them to improve the blog: // echo the headers and send the response $response->send(); -.. versionadded:: 2.5 - Support for HTTP status code constants was introduced in Symfony 2.5. +.. versionadded:: 2.4 + Support for HTTP status code constants was introduced in Symfony 2.4. The controllers are now responsible for returning a ``Response`` object. To make this easier, you can add a new ``render_template()`` function, which, From cb508f7a17dd1eb106649a7d32ecd5e3e853ff74 Mon Sep 17 00:00:00 2001 From: Thomas Ploch Date: Fri, 25 Jul 2014 12:57:58 +0200 Subject: [PATCH 140/835] [WCM] Skip console commands from event listeners | Q | A | ------------- | --- | Doc fix? | no | New docs? | yes (symfony/symfony#9235) | Applies to | master | Fixed tickets | none --- components/console/events.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/components/console/events.rst b/components/console/events.rst index ff7048138e8..210f445d4ef 100644 --- a/components/console/events.rst +++ b/components/console/events.rst @@ -51,6 +51,40 @@ dispatched. Listeners receive a $application = $command->getApplication(); }); +Disable Commands inside Listeners +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.6 + Disabling commands inside listeners was introduced in Symfony 2.6. + +Using the +:method:`Symfony\\Component\\Console\\Event\\ConsoleCommandEvent::disableCommand` +method, you can disable a command inside a listener. The application +will then not execute the command but return the code `113` (defined in +``ConsoleCommandEvent::RETURN_CODE_DISABLED``), which is one of the +`reserved exit codes`_ for console commands to conform with the C/C++ standard.:: + + use Symfony\Component\Console\Event\ConsoleCommandEvent; + use Symfony\Component\Console\ConsoleEvents; + + $dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) { + // get the command to be executed + $command = $event->getCommand(); + + // ... check if the command can be executed + + // disable the command, this will result in the command being skipped + // and code 113 being returned from the Application + $event->disableCommand(); + + // it is possible to enable the command in a later listener + if (!$event->commandShouldRun()) { + $event->enableCommand(); + } + }); + +.. _`reserved exit codes`: http://www.tldp.org/LDP/abs/html/exitcodes.html + The ``ConsoleEvents::TERMINATE`` Event -------------------------------------- @@ -118,3 +152,4 @@ Listeners receive a // change the exception to another one $event->setException(new \LogicException('Caught exception', $exitCode, $event->getException())); }); + From 9879ee6601b97d3a473592a26079ccf4a819c4d3 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Sat, 21 Jun 2014 12:59:31 -0400 Subject: [PATCH 141/835] Add note about invokable controller services --- cookbook/controller/service.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cookbook/controller/service.rst b/cookbook/controller/service.rst index 602b14aead3..da39e672401 100644 --- a/cookbook/controller/service.rst +++ b/cookbook/controller/service.rst @@ -136,6 +136,10 @@ the route ``_controller`` value: :doc:`FrameworkExtraBundle documentation ` for details. +.. versionadded:: 2.6 + If your controller service implements the ``__invoke`` method, you can simply refer to the service id + (``acme.hello.controller``). + Alternatives to base Controller Methods --------------------------------------- From 09c9af499d64247e3658edfad44a6b7577432fae Mon Sep 17 00:00:00 2001 From: Endre Fejes Date: Wed, 30 Jul 2014 19:34:32 +0200 Subject: [PATCH 142/835] [DomCrawler] Added node name getter --- components/dom_crawler.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index 2d17126b89f..37ef6e24827 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -193,6 +193,15 @@ Get all the child or parent nodes:: Accessing Node Values ~~~~~~~~~~~~~~~~~~~~~ +.. versionadded:: 2.6 + The :method:`Symfony\\Component\\DomCrawler\\Crawler::nodeName` + method was introduced in Symfony 2.6. + +Access the node name (HTML tag name) of the first node of the current selection (eg. "p" or "div"):: + + // will return the node name (HTML tag name) of the first child element under + $tag = $crawler->filterXPath('//body/*')->nodeName(); + Access the value of the first node of the current selection:: $message = $crawler->filterXPath('//body/p')->text(); From 6dc62a80e02a45d125d91b32af00ced420588b17 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Tue, 5 Aug 2014 16:21:50 +0200 Subject: [PATCH 143/835] Put version into quotes, otherwise it fails When using the `zsh` shell, the command fails because of: ``` $ composer create-project symfony/framework-standard-edition myproject/ ~2.5 zsh: no such user or named directory: 2.5 ``` This problem does not occur in Bash. Quoting the version number does not effect Bash, and fixes ZSH. --- quick_tour/the_big_picture.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index bd6297b1c38..08bc43c77b8 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -18,7 +18,7 @@ directory: .. code-block:: bash - $ composer create-project symfony/framework-standard-edition myproject/ ~2.5 + $ composer create-project symfony/framework-standard-edition myproject/ '~2.5' .. note:: From e586e6c13f10964bb736034e2f10065980f6a4bf Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Tue, 5 Aug 2014 16:49:10 +0200 Subject: [PATCH 144/835] See #4091 --- book/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index c9e24f32721..2ad27b8d8ac 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -57,7 +57,7 @@ Distribution: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony 2.5.* + $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony '~2.5' .. tip:: From b2a70d16a75de6d4a76af815fe4d60d862d2994a Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 10 Aug 2014 15:01:44 +0200 Subject: [PATCH 145/835] Added July changelog --- changelog.rst | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/changelog.rst b/changelog.rst index 8e8f7f27c7f..b3dd93e571a 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,65 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +July, 2014 +---------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `1b4c1c8 `_ #4045 Added a new "Deploying to Heroku Cloud" cookbook article (javiereguiluz) +- `f943eee `_ #4009 Remove "Controllers extends ContainerAware" best practice (tgalopin) +- `eae9ad0 `_ #3875 Added a note about customizing a form with more than one template (javiereguiluz) +- `1938c2f `_ #3724 Updated ISBN validator docs (sprain) +- `d6787b7 `_ #3989 adde stof as a merger (fabpot) +- `4a9e49e `_ #3946 DQL custom functions on doctrine reference page (healdropper) +- `2b2d9d3 `_ #3972 Added PSR-4 to Class Loaders list (dosten) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `1b695b5 `_ #4063 fix parent form types (xabbuh) +- `7901005 `_ #4048 $this->request replaced by $request (danielsan) +- `f6123f1 `_ #4031 Update form_events.rst (redstar504) +- `99932cf `_ #4010 [Console] Fixed documentation for ProgressBar (VasekPurchart) +- `06f8c31 `_ #4012 Fix xml route configuration for routing condition (xavierbriand) +- `a1435e5 `_ #3998 [Console] Fixed QuestionHelper examples (florianv) +- `b32f9f2 `_ #3771 Fix function example in expression language component (raulfraile) +- `eb813a5 `_ #3979 removed invalid processors option (ricoli) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `a4bdb97 `_ #4070 Added a note about permissions in the Quick Tour (javiereguiluz) +- `a7fe00f `_ #4068 Remove diff info from cookbook/security/voters.rst (pmartelletti) +- `b3f15b2 `_ #4059 eraseCredentials method typo (danielsan) +- `44091b1 `_ #4053 Update doctrine.rst (sr972) +- `b06ad60 `_ #4052 [Security] [Custom Provider] Use properties on WebserviceUser (entering) +- `a834a7e `_ #4042 [Cookbook] apply headline guidelines to the cookbook articles (xabbuh) +- `f25faf3 `_ #4046 Fixed a syntax error (javiereguiluz) +- `3c660d1 `_ #4044 Added editorconfig (WouterJ) +- `ae3ec04 `_ #4041 [Cookbook][Deployment] link to the deployment index (xabbuh) +- `2e4fc7f `_ #4030 enclose YAML strings containing % with quotes (xabbuh) +- `9520d92 `_ #4038 Update rendered tag (kirill-oficerov) +- `f5c2602 `_ #4036 Update page_creation.rst (redstar504) +- `c2eda93 `_ #4034 Update internals.rst (redstar504) +- `a5ad0df `_ #4035 Update version in Rework your Patch section (yguedidi) +- `eed8d64 `_ #4026 Updating Symfony version from 2.4 to 2.5 (danielsan) +- `d8b037a `_ #4019 Update twig_reference.rst (redstar504) +- `7ea87e6 `_ #4016 Fixed the format of one letter-based list (javiereguiluz) +- `579a873 `_ #4015 Fixed bad indenting (the list was treated as a blockquote) (javiereguiluz) +- `12752c1 `_ #4013 Removed wrong reference to cookbook (gquemener) +- `4669620 `_ #4004 use GitHub instead of Github (xabbuh) +- `ec832dc `_ #3994 [Console] Fix Console component $app to $this and use of getHelper() method (eko) +- `a3fe74f `_ #3993 [Console] Fix Console component getHelperSet()->get() to getHelper() (eko) +- `a41af7e `_ #3880 document the mysterious abc part of the header (greg0ire) +- `90773b0 `_ #3990 Move the section about collect: false to the cookbook entry (weaverryan) +- `2ae8281 `_ #3864 plug rules for static methods (cordoval) +- `d882cc0 `_ #3988 fix typos. (yositani2002) +- `b67a059 `_ #3986 Rebased #3982 - Some fixes (WouterJ) +- `801c756 `_ #3977 [WCM] removed call to deprecated getRequest() method (Baptouuuu) +- `4c1d4ae `_ #3968 Proofreading the new Azure deployment article (weaverryan) + June, 2014 ---------- From 47c7fe20ca558131af6a55ae96022fc00447916b Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 10 Aug 2014 15:02:18 +0200 Subject: [PATCH 146/835] Added July changelog --- changelog.rst | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/changelog.rst b/changelog.rst index 8e8f7f27c7f..e9def3e513f 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,68 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +July, 2014 +---------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `1b4c1c8 `_ #4045 Added a new "Deploying to Heroku Cloud" cookbook article (javiereguiluz) +- `f943eee `_ #4009 Remove "Controllers extends ContainerAware" best practice (tgalopin) +- `eae9ad0 `_ #3875 Added a note about customizing a form with more than one template (javiereguiluz) +- `2ae4f34 `_ #3746 [Validator] Disallow empty file in FileValidator (megazoll) +- `1938c2f `_ #3724 Updated ISBN validator docs (sprain) +- `7c71b18 `_ #2952 Enabling profiler in test (danieledangeli) +- `d6787b7 `_ #3989 adde stof as a merger (fabpot) +- `4a9e49e `_ #3946 DQL custom functions on doctrine reference page (healdropper) +- `2b2d9d3 `_ #3972 Added PSR-4 to Class Loaders list (dosten) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `1b695b5 `_ #4063 fix parent form types (xabbuh) +- `7901005 `_ #4048 $this->request replaced by $request (danielsan) +- `f6123f1 `_ #4031 Update form_events.rst (redstar504) +- `99932cf `_ #4010 [Console] Fixed documentation for ProgressBar (VasekPurchart) +- `06f8c31 `_ #4012 Fix xml route configuration for routing condition (xavierbriand) +- `a2a628f `_ #4025 added CVE 2014-4931 (fabpot) +- `a1435e5 `_ #3998 [Console] Fixed QuestionHelper examples (florianv) +- `b32f9f2 `_ #3771 Fix function example in expression language component (raulfraile) +- `eb813a5 `_ #3979 removed invalid processors option (ricoli) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `a4bdb97 `_ #4070 Added a note about permissions in the Quick Tour (javiereguiluz) +- `a7fe00f `_ #4068 Remove diff info from cookbook/security/voters.rst (pmartelletti) +- `b3f15b2 `_ #4059 eraseCredentials method typo (danielsan) +- `44091b1 `_ #4053 Update doctrine.rst (sr972) +- `b06ad60 `_ #4052 [Security] [Custom Provider] Use properties on WebserviceUser (entering) +- `a834a7e `_ #4042 [Cookbook] apply headline guidelines to the cookbook articles (xabbuh) +- `f25faf3 `_ #4046 Fixed a syntax error (javiereguiluz) +- `3c660d1 `_ #4044 Added editorconfig (WouterJ) +- `ae3ec04 `_ #4041 [Cookbook][Deployment] link to the deployment index (xabbuh) +- `2e4fc7f `_ #4030 enclose YAML strings containing % with quotes (xabbuh) +- `9520d92 `_ #4038 Update rendered tag (kirill-oficerov) +- `f5c2602 `_ #4036 Update page_creation.rst (redstar504) +- `c2eda93 `_ #4034 Update internals.rst (redstar504) +- `a5ad0df `_ #4035 Update version in Rework your Patch section (yguedidi) +- `eed8d64 `_ #4026 Updating Symfony version from 2.4 to 2.5 (danielsan) +- `d8b037a `_ #4019 Update twig_reference.rst (redstar504) +- `7ea87e6 `_ #4016 Fixed the format of one letter-based list (javiereguiluz) +- `579a873 `_ #4015 Fixed bad indenting (the list was treated as a blockquote) (javiereguiluz) +- `12752c1 `_ #4013 Removed wrong reference to cookbook (gquemener) +- `4669620 `_ #4004 use GitHub instead of Github (xabbuh) +- `ec832dc `_ #3994 [Console] Fix Console component $app to $this and use of getHelper() method (eko) +- `a3fe74f `_ #3993 [Console] Fix Console component getHelperSet()->get() to getHelper() (eko) +- `a41af7e `_ #3880 document the mysterious abc part of the header (greg0ire) +- `90773b0 `_ #3990 Move the section about collect: false to the cookbook entry (weaverryan) +- `2ae8281 `_ #3864 plug rules for static methods (cordoval) +- `d882cc0 `_ #3988 fix typos. (yositani2002) +- `b67a059 `_ #3986 Rebased #3982 - Some fixes (WouterJ) +- `801c756 `_ #3977 [WCM] removed call to deprecated getRequest() method (Baptouuuu) +- `4c1d4ae `_ #3968 Proofreading the new Azure deployment article (weaverryan) + June, 2014 ---------- From f701f73838223885f48dc6674fb07b96e2f2dadc Mon Sep 17 00:00:00 2001 From: Hryhorii Hrebiniuk Date: Thu, 26 Dec 2013 12:28:36 +0200 Subject: [PATCH 147/835] [Translation] added method to expose collected message --- components/translation/usage.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/components/translation/usage.rst b/components/translation/usage.rst index fbb6664c607..63daff8d7a8 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -371,3 +371,25 @@ use for translation:: .. _`L10n`: http://en.wikipedia.org/wiki/Internationalization_and_localization .. _`ISO 31-11`: http://en.wikipedia.org/wiki/Interval_(mathematics)#Notations_for_intervals + +Retrieving the Message Catalogue +-------------------------------- + +In case you want to use the same translation catalogue outside your application +(e.g. use translation on a client side), it's possible to fetch raw translation messages. +You just need to specify required locale:: + + $messages = $translator->getMessages('fr_FR'); + +``$messages`` will have the following structure:: + + array( + 'messages' => array( + 'Hello world' => 'Bonjour tout le monde', + ), + 'validators' => array( + 'Value should not be empty' => 'Valeur ne doit pas être vide', + 'Value is too long' => 'Valeur est trop long', + ), + ); + From c7d17912c557c1c79544597d6112aeec5336b585 Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Sun, 11 May 2014 01:54:57 +0200 Subject: [PATCH 148/835] Fixed typos --- cookbook/security/named_encoders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/named_encoders.rst b/cookbook/security/named_encoders.rst index cf9ea3770c2..3b22a2d9343 100644 --- a/cookbook/security/named_encoders.rst +++ b/cookbook/security/named_encoders.rst @@ -105,7 +105,7 @@ named encoders: This creates an encoder named ``harsh``. In order for a ``User`` instance to use it, the class must implement :class:`Symfony\\Component\\Security\\Core\\Encoder\\EncoderAwareInterface`. -The interface requires one method - ``getEncoderName`` - which should reutrn +The interface requires one method - ``getEncoderName`` - which should return the name of the encoder to use:: // src/Acme/UserBundle/Entity/User.php From d753d0e3acb9ab313b73b829c2d862d36011829a Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Wed, 30 Jul 2014 00:06:58 +0200 Subject: [PATCH 149/835] document the namespace alias feature --- cookbook/doctrine/mapping_model_classes.rst | 61 ++++++++++++--------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/cookbook/doctrine/mapping_model_classes.rst b/cookbook/doctrine/mapping_model_classes.rst index 975d95cd0be..9894777a339 100644 --- a/cookbook/doctrine/mapping_model_classes.rst +++ b/cookbook/doctrine/mapping_model_classes.rst @@ -14,23 +14,23 @@ register the mappings for your model classes. For non-reusable bundles, the easiest option is to put your model classes in the default locations: ``Entity`` for the Doctrine ORM or ``Document`` for one of the ODMs. For reusable bundles, rather than duplicate model classes - just to get the auto mapping, use the compiler pass. + just to get the auto-mapping, use the compiler pass. .. versionadded:: 2.3 The base mapping compiler pass was introduced in Symfony 2.3. The Doctrine bundles support it from DoctrineBundle >= 1.2.1, MongoDBBundle >= 3.0.0, - PHPCRBundle >= 1.0.0-alpha2 and the (unversioned) CouchDBBundle supports the + PHPCRBundle >= 1.0.0 and the (unversioned) CouchDBBundle supports the compiler pass since the `CouchDB Mapping Compiler Pass pull request`_ was merged. - If you want your bundle to support older versions of Symfony and - Doctrine, you can provide a copy of the compiler pass in your bundle. - See for example the `FOSUserBundle mapping configuration`_ - ``addRegisterMappingsPass``. - +.. versionadded:: 2.6 + Support for defining namespace aliases was introduced in Symfony 2.6. + It is safe to define the aliases with older versions of Symfony as + the aliases are the last argument to ``createXmlMappingDriver`` and + are ignored by PHP if that argument doesn't exist. In your bundle class, write the following code to register the compiler pass. -This one is written for the FOSUserBundle, so parts of it will need to +This one is written for the CmfRoutingBundle, so parts of it will need to be adapted for your case:: use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass; @@ -38,7 +38,7 @@ be adapted for your case:: use Doctrine\Bundle\CouchDBBundle\DependencyInjection\Compiler\DoctrineCouchDBMappingsPass; use Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass; - class FOSUserBundle extends Bundle + class CmfRoutingBundle extends Bundle { public function build(ContainerBuilder $container) { @@ -47,7 +47,7 @@ be adapted for your case:: $modelDir = realpath(__DIR__.'/Resources/config/doctrine/model'); $mappings = array( - $modelDir => 'FOS\UserBundle\Model', + $modelDir => 'Symfony\Cmf\RoutingBundle\Model', ); $ormCompilerClass = 'Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\DoctrineOrmMappingsPass'; @@ -55,8 +55,9 @@ be adapted for your case:: $container->addCompilerPass( DoctrineOrmMappingsPass::createXmlMappingDriver( $mappings, - array('fos_user.model_manager_name'), - 'fos_user.backend_type_orm' + array('cmf_routing.model_manager_name'), + 'cmf_routing.backend_type_orm', + array('CmfRoutingBundle' => 'Symfony\Cmf\RoutingBundle\Model') )); } @@ -65,8 +66,9 @@ be adapted for your case:: $container->addCompilerPass( DoctrineMongoDBMappingsPass::createXmlMappingDriver( $mappings, - array('fos_user.model_manager_name'), - 'fos_user.backend_type_mongodb' + array('cmf_routing.model_manager_name'), + 'cmf_routing.backend_type_mongodb', + array('CmfRoutingBundle' => 'Symfony\Cmf\RoutingBundle\Model') )); } @@ -75,8 +77,9 @@ be adapted for your case:: $container->addCompilerPass( DoctrineCouchDBMappingsPass::createXmlMappingDriver( $mappings, - array('fos_user.model_manager_name'), - 'fos_user.backend_type_couchdb' + array('cmf_routing.model_manager_name'), + 'cmf_routing.backend_type_couchdb', + array('CmfRoutingBundle' => 'Symfony\Cmf\RoutingBundle\Model') )); } @@ -85,8 +88,9 @@ be adapted for your case:: $container->addCompilerPass( DoctrinePhpcrMappingsPass::createXmlMappingDriver( $mappings, - array('fos_user.model_manager_name'), - 'fos_user.backend_type_phpcr' + array('cmf_routing.model_manager_name'), + 'cmf_routing.backend_type_phpcr', + array('CmfRoutingBundle' => 'Symfony\Cmf\RoutingBundle\Model') )); } } @@ -99,17 +103,20 @@ decide which to use. The compiler pass provides factory methods for all drivers provided by Doctrine: Annotations, XML, Yaml, PHP and StaticPHP. The arguments are: -* a map/hash of absolute directory path to namespace; -* an array of container parameters that your bundle uses to specify the name of - the Doctrine manager that it is using. In the above example, the FOSUserBundle - stores the manager name that's being used under the ``fos_user.model_manager_name`` +* A map/hash of absolute directory path to namespace; +* An array of container parameters that your bundle uses to specify the name of + the Doctrine manager that it is using. In the example above, the CmfRoutingBundle + stores the manager name that's being used under the ``cmf_routing.model_manager_name`` parameter. The compiler pass will append the parameter Doctrine is using to specify the name of the default manager. The first parameter found is used and the mappings are registered with that manager; -* an optional container parameter name that will be used by the compiler +* An optional container parameter name that will be used by the compiler pass to determine if this Doctrine type is used at all. This is relevant if your user has more than one type of Doctrine bundle installed, but your - bundle is only used with one type of Doctrine. + bundle is only used with one type of Doctrine; +* A map/hash of aliases to namespace. This should be the same convention used + by Doctrine auto-mapping. In the example above, this allows the user to call + ``$om->getRepository('CmfRoutingBundle:Route')``. .. note:: @@ -120,7 +127,7 @@ Annotations, XML, Yaml, PHP and StaticPHP. The arguments are: of the class as their filename (e.g. ``BlogPost.orm.xml``) If you also need to map a base class, you can register a compiler pass - with the ``DefaultFileLocator`` like this. This code is simply taken from the + with the ``DefaultFileLocator`` like this. This code is taken from the ``DoctrineOrmMappingsPass`` and adapted to use the ``DefaultFileLocator`` instead of the ``SymfonyFileLocator``:: @@ -138,6 +145,9 @@ Annotations, XML, Yaml, PHP and StaticPHP. The arguments are: ); } + Note that you do not need to provide a namespace alias unless your users are + expected to ask Doctrine for the base classes. + Now place your mapping file into ``/Resources/config/doctrine-base`` with the fully qualified class name, separated by ``.`` instead of ``\``, for example ``Other.Namespace.Model.Name.orm.xml``. You may not mix the two as otherwise @@ -146,4 +156,3 @@ Annotations, XML, Yaml, PHP and StaticPHP. The arguments are: Adjust accordingly for the other Doctrine implementations. .. _`CouchDB Mapping Compiler Pass pull request`: https://github.com/doctrine/DoctrineCouchDBBundle/pull/27 -.. _`FOSUserBundle mapping configuration`: https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/FOSUserBundle.php From aa70a98fd3fa2eaff454c8f87569001a5906e998 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 16 Aug 2014 12:31:35 -0400 Subject: [PATCH 150/835] [#3975] Minor typo --- components/http_foundation/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index 92034f4efe6..60bdb7c9ffc 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -518,7 +518,7 @@ You can still set the ``Content-Type`` of the sent file, or change its ``Content .. versionadded:: 2.6 The ``deleteFileAfterSend()`` method was introduced in Symfony 2.6. -It is possible to delete the file after the request is send with the +It is possible to delete the file after the request is sent with the :method:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse::deleteFileAfterSend` method. Please note that this will not work when the ``X-Sendfile`` header is set. From d185f40b4c7e337f31797f279b2831df9d163484 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 16 Aug 2014 19:37:04 +0200 Subject: [PATCH 151/835] fix from suffix to prefix --- reference/constraints/File.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 1759cf374cd..eb45c931475 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -177,7 +177,7 @@ to be valid. The size of the file can be given in one of the following formats: | Mi | mebibyte | 1,048,576 bytes | 8Mi | +--------+-----------+-----------------+------+ -For more information about the difference between binary and SI suffixes, +For more information about the difference between binary and SI prefixes, see `Wikipedia: Binary prefix`_. binaryFormat @@ -188,12 +188,12 @@ binaryFormat **type**: ``boolean`` **default**: ``null`` -When ``true``, the sizes will be displayed in messages with binary suffixes -(KiB, MiB). When ``false``, the sizes will be displayed with SI suffixes (kB, -MB). When ``null``, then the binaryFormat will be guessed from the suffix -defined in the ``maxSize`` option. +When ``true``, the sizes will be displayed in messages with binary-prefixed +units (KiB, MiB). When ``false``, the sizes will be displayed with SI-prefixed +units (kB, MB). When ``null``, then the binaryFormat will be guessed from +the value defined in the ``maxSize`` option. -For more information about the difference between binary and SI suffixes, +For more information about the difference between binary and SI prefixes, see `Wikipedia: Binary prefix`_. mimeTypes From 43ae1d8b1bae03577018027a229b3bacce0c8c69 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 21 Aug 2014 19:42:57 +0200 Subject: [PATCH 152/835] [OptionsResolver] Adjusted the OptionsResolver documentation to describe the 2.6 API --- components/options_resolver.rst | 665 +++++++++++++++++++++----------- 1 file changed, 445 insertions(+), 220 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index eaad4bc7eb1..cf7ac0953cb 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -5,8 +5,7 @@ The OptionsResolver Component ============================= - The OptionsResolver component helps you configure objects with option - arrays. It supports default values, option constraints and lazy options. + The OptionsResolver component helps you to easily process option arrays. Installation ------------ @@ -19,11 +18,13 @@ You can install the component in 2 different ways: Usage ----- -Imagine you have a ``Mailer`` class which has 2 options: ``host`` and -``password``. These options are going to be handled by the OptionsResolver -Component. +.. versionadded:: 2.6 + This documentation was written for Symfony 2.6 and later. If you use an older + version, please read the corresponding documentation using the version + drop-down on the upper right. -First, create the ``Mailer`` class:: +Imagine you have a ``Mailer`` class which has four options: ``host``, +``username``, ``password`` and ``port``:: class Mailer { @@ -31,27 +32,63 @@ First, create the ``Mailer`` class:: public function __construct(array $options = array()) { + $this->options = $options; } } -You could of course set the ``$options`` value directly on the property. Instead, -use the :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` class -and let it resolve the options by calling -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::resolve`. -The advantages of doing this will become more obvious as you continue:: +When accessing the ``$options``, you need to add a lot of boilerplate code to +check which options are set:: - use Symfony\Component\OptionsResolver\OptionsResolver; + // ... + public function sendMail($from, $to) + { + $mail = ...; + $mail->setHost(isset($this->options['host']) + ? $this->options['host'] + : 'smtp.example.org'); + $mail->setUsername(isset($this->options['username']) + ? $this->options['username'] + : 'user'); + $mail->setPassword(isset($this->options['password']) + ? $this->options['password'] + : 'pa$$word'); + $mail->setPort(isset($this->options['port']) + ? $this->options['port'] + : 25); + // ... + } + +This boilerplate is hard to read and repetitive. Also, the default values of the +options are buried in the business logic of your code. Let's use +:method:`Symfony\\Component\\OptionsResolver\\Options::resolve` to fix that:: + + use Symfony\Component\OptionsResolver\Options; // ... public function __construct(array $options = array()) { - $resolver = new OptionsResolver(); - - $this->options = $resolver->resolve($options); + $this->options = Options::resolve($options, array( + 'host' => 'smtp.example.org', + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, + )); } -The options property now is a well defined array with all resolved options -readily available:: +Now all options are guaranteed to be set. Any option that wasn't passed through +``$options`` will be set to the specified default value. Additionally, an +:class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` +is thrown if an unknown option is passed:: + + $mailer = new Mailer(array( + 'usernme' => 'johndoe', + )); + + // InvalidOptionsException: The option "usernme" does not exist. Known + // options are: "host", "password", "username" + +The rest of your code can now access the values of the options without +boilerplate code:: // ... public function sendMail($from, $to) @@ -60,154 +97,148 @@ readily available:: $mail->setHost($this->options['host']); $mail->setUsername($this->options['username']); $mail->setPassword($this->options['password']); + $mail->setPort($this->options['port']); // ... } -Configuring the OptionsResolver -------------------------------- +Required Options +~~~~~~~~~~~~~~~~ -Now, try to actually use the class:: +If an option must be set by the caller, pass that option to +:method:`Symfony\\Component\\OptionsResolver\\Options::validateRequired`. +For example, let's make the ``host`` option required:: - $mailer = new Mailer(array( - 'host' => 'smtp.example.org', - 'username' => 'user', - 'password' => 'pa$$word', - )); + // ... + public function __construct(array $options = array()) + { + Options::validateRequired($options, 'host'); -Right now, you'll receive a -:class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException`, -which tells you that the options ``host`` and ``password`` do not exist. -This is because you need to configure the ``OptionsResolver`` first, so it -knows which options should be resolved. + $this->options = Options::resolve($options, array( + 'host' => null, + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, + )); + } -.. tip:: +If you omit a required option, a +:class:`Symfony\\Component\\OptionsResolver\\Exception\\MissingOptionsException` +will be thrown:: - To check if an option exists, you can use the - :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isKnown` - function. + $mailer = new Mailer(); -A best practice is to put the configuration in a method (e.g. -``configureOptions``). You call this method in the constructor to configure -the ``OptionsResolver`` class:: + // MissingOptionsException: The required option "host" is missing. - use Symfony\Component\OptionsResolver\OptionsResolver; - use Symfony\Component\OptionsResolver\OptionsResolverInterface; +The :method:`Symfony\\Component\\OptionsResolver\\Options::validateRequired` +method accepts a single name or an array of option names if you have more than +one required option. - class Mailer - { - protected $options; - - public function __construct(array $options = array()) - { - $resolver = new OptionsResolver(); - $this->configureOptions($resolver); - - $this->options = $resolver->resolve($options); - } +.. note:: - protected function configureOptions(OptionsResolverInterface $resolver) - { - // ... configure the resolver, you will learn this - // in the sections below - } - } + As you can see, the ``host`` option must still be passed to + :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`, + otherwise the method will not accept that option. The default value, + however, can be omitted as the option must be set by the caller. -Set default Values -~~~~~~~~~~~~~~~~~~ +Type Validation +~~~~~~~~~~~~~~~ -Most of the options have a default value. You can configure these options by -calling :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefaults`:: +You can run additional checks on the options to make sure they were passed +correctly. To validate the types of the options, call +:method:`Symfony\\Component\\OptionsResolver\\Options::validateTypes`:: // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) + public function __construct(array $options = array()) { // ... + Options::validateTypes($options, array( + 'host' => 'string', + 'port' => array('null', 'int'), + )); - $resolver->setDefaults(array( - 'username' => 'root', + $this->options = Options::resolve($options, array( + 'host' => null, + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, )); } -This would add an option - ``username`` - and give it a default value of -``root``. If the user passes in a ``username`` option, that value will -override this default. You don't need to configure ``username`` as an optional -option. - -Required Options -~~~~~~~~~~~~~~~~ - -The ``host`` option is required: the class can't work without it. You can set -the required options by calling -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setRequired`:: +For each option, you can define either just one type or an array of acceptable +types. You can pass any type for which an ``is_()`` method is defined. +Additionally, you may pass fully qualified class or interface names. - // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) - { - $resolver->setRequired(array('host')); - } - -You are now able to use the class without errors:: +If you pass an invalid option now, an :class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` +is thrown:: $mailer = new Mailer(array( - 'host' => 'smtp.example.org', + 'host' => 25, )); - echo $mailer->getHost(); // 'smtp.example.org' - -If you don't pass a required option, a -:class:`Symfony\\Component\\OptionsResolver\\Exception\\MissingOptionsException` -will be thrown. - -.. tip:: - - To determine if an option is required, you can use the - :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isRequired` - method. + // InvalidOptionsException: The option "host" with value "25" is expected to + // be of type "string" -Optional Options +Value Validation ~~~~~~~~~~~~~~~~ -Sometimes, an option can be optional (e.g. the ``password`` option in the -``Mailer`` class), but it doesn't have a default value. You can configure -these options by calling -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setOptional`:: +Some options can only take one of a fixed list of predefined values. For +example, suppose the ``Mailer`` class has a ``transport`` option which can be +one of ``sendmail``, ``mail`` and ``smtp``. Use the method +:method:`Symfony\\Component\\OptionsResolver\\Options::validateValues` to verify +that the passed option contains one of these values:: // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) + public function __construct(array $options = array()) { // ... + Options::validateValues($options, array( + 'transport' => array('sendmail', 'mail', 'smtp'), + )); - $resolver->setOptional(array('password')); + $this->options = Options::resolve($options, array( + // ... + 'transport' => 'sendmail', + )); } -Options with defaults are already marked as optional. +If you pass an invalid transport, an :class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` +is thrown:: -.. tip:: + $mailer = new Mailer(array( + 'transport' => 'send-mail', + )); + + // InvalidOptionsException: The option "transport" has the value "send-mail", + // but is expected to be one of "sendmail", "mail", "smtp" - When setting an option as optional, you can't be sure if it's in the array - or not. You have to check if the option exists before using it. +For options with more complicated validation schemes, pass a callback which +returns ``true`` for acceptable values and ``false`` for invalid values:: - To avoid checking if it exists everytime, you can also set a default of - ``null`` to an option using the ``setDefaults()`` method (see `Set Default Values`_), - this means the element always exists in the array, but with a default of - ``null``. + Options::validateValues($options, array( + // ... + 'transport' => function ($value) { + // return true or false + }, + )); Default Values that Depend on another Option ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Suppose you add a ``port`` option to the ``Mailer`` class, whose default -value you guess based on the encryption. You can do that easily by using a -closure as the default value:: +Suppose you want to set the default value of the ``port`` option based on the +encryption chosen by the user of the ``Mailer`` class. More precisely, we want +to set the port to ``465`` if SSL is used and to ``25`` otherwise. - use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsResolverInterface; +You can implement this feature by passing a closure as default value of the +``port`` option. The closure receives the options as argument. Based on these +options, you can return the desired default value:: // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) + public function __construct(array $options = array()) { // ... - $resolver->setDefaults(array( + $this->options = Options::resolve($options, new Options(array( + // ... 'encryption' => null, 'port' => function (Options $options) { if ('ssl' === $options['encryption']) { @@ -216,45 +247,288 @@ closure as the default value:: return 25; }, - )); + ))); + } + +Instead of a simple array, we now pass the default options as +:class:`Symfony\\Component\\OptionsResolver\\Options` instance to +:method:`Symfony\\Component\\OptionsResolver\\Options::resolve`. This class +makes sure that the closure stored in the default value of the ``port`` option +is called. In the closure, you can use the +:class:`Symfony\\Component\\OptionsResolver\\Options` instance just like a +normal option array. + +.. caution:: + + The first argument of the closure must be type hinted as ``Options``. + Otherwise, the closure is considered as the default value of the option. + If the closure is still not called, double check that you passed the default + options as :class:`Symfony\\Component\\OptionsResolver\\Options` instance. + +.. note:: + + The closure is only executed if the ``port`` option isn't set by the user. + +Coding Patterns +~~~~~~~~~~~~~~~ + +If you have a large list of options, the option processing code can take up a +lot of space of your method. To make your code easier to read and maintain, it +is a good practice to put the option definitions into static class properties:: + + class Mailer + { + private static $defaultOptions = array( + 'host' => null, + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, + 'encryption' => null, + ); + + private static $requiredOptions = array( + 'host', + ); + + private static $optionTypes = array( + 'host' => 'string', + 'username' => 'string', + 'password' => 'string', + 'port' => 'int', + ); + + private static $optionValues = array( + 'encryption' => array(null, 'ssl', 'tls'), + ); + + protected $options; + + public function __construct(array $options = array()) + { + Options::validateRequired($options, static::$requiredOptions); + Options::validateTypes($options, static::$optionTypes); + Options::validateValues($options, static::$optionValues); + + $this->options = Options::resolve($options, static::$defaultOptions); + } } -The :class:`Symfony\\Component\\OptionsResolver\\Options` class implements -:phpclass:`ArrayAccess`, :phpclass:`Iterator` and :phpclass:`Countable`. That -means you can handle it just like a normal array containing the options. +In this way, the class remains easy to read and maintain even with a lot of +options being processed and validated. .. caution:: - The first argument of the closure must be typehinted as ``Options``, - otherwise it is considered as the value. + PHP does not support closures in property definitions. In such cases, you + must move your closure to a static method:: + + private static $defaultOptions = array( + // ... + 'port' => array(__CLASS__, 'getDefaultPort'), + ); + + public static function getDefaultPort(Options $options) + { + if ('ssl' === $options['encryption']) { + return 465; + } + + return 25; + } + +Decoupling the Option Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +So far, the configuration of the options, their allowed types etc. was very +tightly coupled to the code that resolves the options. This is fine in most cases. +In some cases, however, the configuration of options must be distributed across +multiple classes. An example is a class hierarchy that supports the addition of +options by subclasses. In those cases, you can create an +:class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` object and pass that +object everywhere that you want to adjust the option configuration. Then, call +:method:`Symfony\\Component\\OptionsResolver\\Options::resolve` with the +configuration object to resolve the options. + +The following code demonstrates how to write our previous ``Mailer`` class with +an :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` object:: + + use Symfony\Component\OptionsResolver\Options; + use Symfony\Component\OptionsResolver\OptionsConfig; + + class Mailer + { + protected $options; + + public function __construct(array $options = array()) + { + $config = new OptionsConfig(); + $this->configureOptions($config); + + $this->options = Options::resolve($options, $config); + } + + protected function configureOptions(OptionsConfig $config) + { + $config->setDefaults(array( + 'host' => null, + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, + 'encryption' => null, + )); + + $config->setRequired(array( + 'host', + )); + + $config->setAllowedTypes(array( + 'host' => 'string', + 'username' => 'string', + 'password' => 'string', + 'port' => 'int', + )); + + $config->setAllowedValues(array( + 'encryption' => array(null, 'ssl', 'tls'), + )); + } + } + +As you can see, the code is very similar as before. However, the performance +is marginally worse, since the creation of an additional object is required: +the :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` instance. + +Nevertheless, this design also has a benefit: We can extend the ``Mailer`` +class and adjust the options of the parent class in the subclass:: + + use Symfony\Component\OptionsResolver\Options; + use Symfony\Component\OptionsResolver\OptionsConfig; + + class GoogleMailer extends Mailer + { + protected function configureOptions(OptionsConfig $config) + { + $config->setDefaults(array( + 'host' => 'smtp.google.com', + 'port' => 25, + 'encryption' => 'ssl', + )); + + $config->setRequired(array( + 'username', + 'password', + )); + } + } + +The ``host`` option is no longer required now, but defaults to "smtp.google.com". +The ``username`` and ``password`` options, however, are required in the +subclass. + +The :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` has various +useful methods to find out which options are set or required. Check out the +API documentation to find out more about these methods. + +.. note:: + + The :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` class used + by the Form Component inherits from + :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig`. All the + documentation for ``OptionsConfig`` applies to ``OptionsResolver`` as well. + +Optional Options +~~~~~~~~~~~~~~~~ + +The :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` has one feature +that is not available when not using this class: You can specify optional +options. Optional options will be accepted and validated when set. When not set, +however, *no default value* will be added to the options array. Pass the names +of the optional options to +:method:`Symfony\\Component\\OptionsResolver\\OptionsConfig::setOptional`:: + + // ... + protected function configureOptions(OptionsConfig $config) + { + // ... + + $config->setOptional(array('port')); + } + +This is useful if you need to know whether an option was explicitly passed. If +not, it will be missing from the options array:: + + class Mailer + { + // ... + public function __construct(array $options = array()) + { + // ... + + if (array_key_exists('port', $this->options)) { + echo "Set!"; + } else { + echo "Not Set!"; + } + } + } + + $mailer = new Mailer(array( + 'port' => 25, + )); + // Set! + + $mailer = new Mailer(); + // Not Set! + +.. tip:: -Overwriting default Values + If you need this functionality when not using an + :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` object, check + the options before calling + :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`:: + + // ... + public function __construct(array $options = array()) + { + // ... + + if (array_key_exists('port', $options)) { + echo "Set!"; + } else { + echo "Not Set!"; + } + + $this->options = Options::resolve($options, array( + // ... + )); + } + +Overwriting Default Values ~~~~~~~~~~~~~~~~~~~~~~~~~~ A previously set default value can be overwritten by invoking -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefaults` +:method:`Symfony\\Component\\OptionsResolver\\OptionsConfig::setDefaults` again. When using a closure as the new value it is passed 2 arguments: * ``$options``: an :class:`Symfony\\Component\\OptionsResolver\\Options` instance with all the other default options -* ``$previousValue``: the previous set default value +* ``$previousValue``: the previously set default value .. code-block:: php use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsResolverInterface; + use Symfony\Component\OptionsResolver\OptionsConfig; // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) + protected function configureOptions(OptionsConfig $config) { // ... - $resolver->setDefaults(array( + $config->setDefaults(array( 'encryption' => 'ssl', 'host' => 'localhost', )); // ... - $resolver->setDefaults(array( + $config->setDefaults(array( 'encryption' => 'tls', // simple overwrite 'host' => function (Options $options, $previousValue) { return 'localhost' == $previousValue @@ -267,20 +541,20 @@ again. When using a closure as the new value it is passed 2 arguments: .. tip:: If the previous default value is calculated by an expensive closure and - you don't need access to it, you can use the - :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::replaceDefaults` - method instead. It acts like ``setDefaults`` but simply erases the - previous value to improve performance. This means that the previous - default value is not available when overwriting with another closure:: + you don't need access to it, use the + :method:`Symfony\\Component\\OptionsResolver\\OptionsConfig::replaceDefaults` + method instead. It acts like ``setDefaults`` but erases the previous value + to improve performance. This means that the previous default value is not + available when overwriting with another closure:: use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsResolverInterface; + use Symfony\Component\OptionsResolver\OptionsConfig; // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) + protected function configureOptions(OptionsConfig $config) { // ... - $resolver->setDefaults(array( + $config->setDefaults(array( 'encryption' => 'ssl', 'heavy' => function (Options $options) { // Some heavy calculations to create the $result @@ -289,7 +563,7 @@ again. When using a closure as the new value it is passed 2 arguments: }, )); - $resolver->replaceDefaults(array( + $config->replaceDefaults(array( 'encryption' => 'tls', // simple overwrite 'heavy' => function (Options $options) { // $previousValue not available @@ -304,92 +578,21 @@ again. When using a closure as the new value it is passed 2 arguments: Existing option keys that you do not mention when overwriting are preserved. -Configure Allowed Values -~~~~~~~~~~~~~~~~~~~~~~~~ - -Not all values are valid values for options. Suppose the ``Mailer`` class has -a ``transport`` option, it can only be one of ``sendmail``, ``mail`` or -``smtp``. You can configure these allowed values by calling -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setAllowedValues`:: - - // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) - { - // ... - - $resolver->setAllowedValues(array( - 'encryption' => array(null, 'ssl', 'tls'), - )); - } - -There is also an -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::addAllowedValues` -method, which you can use if you want to add an allowed value to the previously -configured allowed values. - -.. versionadded:: 2.5 - The callback support for allowed values was introduced in Symfony 2.5. - -If you need to add some more logic to the value validation process, you can pass a callable -as an allowed value:: - - // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) - { - // ... - - $resolver->setAllowedValues(array( - 'transport' => function($value) { - return false !== strpos($value, 'mail'); - }, - )); - } - -.. caution:: - - Note that using this together with ``addAllowedValues`` will not work. - -Configure Allowed Types -~~~~~~~~~~~~~~~~~~~~~~~ - -You can also specify allowed types. For instance, the ``port`` option can -be anything, but it must be an integer. You can configure these types by calling -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setAllowedTypes`:: - - // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) - { - // ... - - $resolver->setAllowedTypes(array( - 'port' => 'integer', - )); - } - -Possible types are the ones associated with the ``is_*`` PHP functions or a -class name. You can also pass an array of types as the value. For instance, -``array('null', 'string')`` allows ``port`` to be ``null`` or a ``string``. - -There is also an -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::addAllowedTypes` -method, which you can use to add an allowed type to the previous allowed types. - -Normalize the Options -~~~~~~~~~~~~~~~~~~~~~ +Option Normalization +~~~~~~~~~~~~~~~~~~~~ Some values need to be normalized before you can use them. For instance, -pretend that the ``host`` should always start with ``http://``. To do that, -you can write normalizers. These closures will be executed after all options -are passed and should return the normalized value. You can configure these -normalizers by calling -:method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizers`:: +assume that the ``host`` should always start with ``http://``. To do that, +you can write normalizers. Normalizers are executed after all options were +processed. You can configure these normalizers by calling +:method:`Symfony\\Components\\OptionsResolver\\OptionsConfig::setNormalizers`:: // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) + protected function configureOptions(OptionsConfig $config) { // ... - $resolver->setNormalizers(array( + $config->setNormalizers(array( 'host' => function (Options $options, $value) { if ('http://' !== substr($value, 0, 7)) { $value = 'http://'.$value; @@ -400,15 +603,16 @@ normalizers by calling )); } -You see that the closure also gets an ``$options`` parameter. Sometimes, you -need to use the other options for normalizing:: +The normalizer receives the actual ``$value`` and returns the normalized form. +You see that the closure also takes an ``$options`` parameter. This is useful +if you need to use other options for the normalization:: // ... - protected function setDefaultOptions(OptionsResolverInterface $resolver) + protected function configureOptions(OptionsConfig $config) { // ... - $resolver->setNormalizers(array( + $config->setNormalizers(array( 'host' => function (Options $options, $value) { if (!in_array(substr($value, 0, 7), array('http://', 'https://'))) { if ($options['ssl']) { @@ -423,4 +627,25 @@ need to use the other options for normalizing:: )); } +.. tip:: + + When not using an :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` + object, perform normalization after the call to + :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`:: + + // ... + public function __construct(array $options = array()) + { + $this->options = Options::resolve($options, array( + // ... + )); + + if ('http://' !== substr($this->options['host'], 0, 7)) { + $this->options['host'] = 'http://'.$this->options['host']; + } + } + +That's it! You now have all the tools and knowledge needed to easily process +options in your code. + .. _Packagist: https://packagist.org/packages/symfony/options-resolver From ae668939a713442ebeb57bebd4a468b416fcc8f2 Mon Sep 17 00:00:00 2001 From: Peter Rehm Date: Sat, 23 Aug 2014 16:17:51 +0200 Subject: [PATCH 153/835] Added missing mailer class and use statements --- components/options_resolver.rst | 369 +++++++++++++++++++------------- 1 file changed, 217 insertions(+), 152 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index cf7ac0953cb..11899762de0 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -39,23 +39,26 @@ Imagine you have a ``Mailer`` class which has four options: ``host``, When accessing the ``$options``, you need to add a lot of boilerplate code to check which options are set:: - // ... - public function sendMail($from, $to) + class Mailer { - $mail = ...; - $mail->setHost(isset($this->options['host']) - ? $this->options['host'] - : 'smtp.example.org'); - $mail->setUsername(isset($this->options['username']) - ? $this->options['username'] - : 'user'); - $mail->setPassword(isset($this->options['password']) - ? $this->options['password'] - : 'pa$$word'); - $mail->setPort(isset($this->options['port']) - ? $this->options['port'] - : 25); // ... + public function sendMail($from, $to) + { + $mail = ...; + $mail->setHost(isset($this->options['host']) + ? $this->options['host'] + : 'smtp.example.org'); + $mail->setUsername(isset($this->options['username']) + ? $this->options['username'] + : 'user'); + $mail->setPassword(isset($this->options['password']) + ? $this->options['password'] + : 'pa$$word'); + $mail->setPort(isset($this->options['port']) + ? $this->options['port'] + : 25); + // ... + } } This boilerplate is hard to read and repetitive. Also, the default values of the @@ -64,15 +67,18 @@ options are buried in the business logic of your code. Let's use use Symfony\Component\OptionsResolver\Options; - // ... - public function __construct(array $options = array()) + class Mailer { - $this->options = Options::resolve($options, array( - 'host' => 'smtp.example.org', - 'username' => 'user', - 'password' => 'pa$$word', - 'port' => 25, - )); + // ... + public function __construct(array $options = array()) + { + $this->options = Options::resolve($options, array( + 'host' => 'smtp.example.org', + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, + )); + } } Now all options are guaranteed to be set. Any option that wasn't passed through @@ -90,15 +96,18 @@ is thrown if an unknown option is passed:: The rest of your code can now access the values of the options without boilerplate code:: - // ... - public function sendMail($from, $to) + class Mailer { - $mail = ...; - $mail->setHost($this->options['host']); - $mail->setUsername($this->options['username']); - $mail->setPassword($this->options['password']); - $mail->setPort($this->options['port']); // ... + public function sendMail($from, $to) + { + $mail = ...; + $mail->setHost($this->options['host']); + $mail->setUsername($this->options['username']); + $mail->setPassword($this->options['password']); + $mail->setPort($this->options['port']); + // ... + } } Required Options @@ -108,17 +117,22 @@ If an option must be set by the caller, pass that option to :method:`Symfony\\Component\\OptionsResolver\\Options::validateRequired`. For example, let's make the ``host`` option required:: - // ... - public function __construct(array $options = array()) + use Symfony\Component\OptionsResolver\Options; + + class Mailer { - Options::validateRequired($options, 'host'); - - $this->options = Options::resolve($options, array( - 'host' => null, - 'username' => 'user', - 'password' => 'pa$$word', - 'port' => 25, - )); + // ... + public function __construct(array $options = array()) + { + Options::validateRequired($options, 'host'); + + $this->options = Options::resolve($options, array( + 'host' => null, + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, + )); + } } If you omit a required option, a @@ -147,21 +161,26 @@ You can run additional checks on the options to make sure they were passed correctly. To validate the types of the options, call :method:`Symfony\\Component\\OptionsResolver\\Options::validateTypes`:: - // ... - public function __construct(array $options = array()) + use Symfony\Component\OptionsResolver\Options; + + class Mailer { // ... - Options::validateTypes($options, array( - 'host' => 'string', - 'port' => array('null', 'int'), - )); - - $this->options = Options::resolve($options, array( - 'host' => null, - 'username' => 'user', - 'password' => 'pa$$word', - 'port' => 25, - )); + public function __construct(array $options = array()) + { + // ... + Options::validateTypes($options, array( + 'host' => 'string', + 'port' => array('null', 'int'), + )); + + $this->options = Options::resolve($options, array( + 'host' => null, + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, + )); + } } For each option, you can define either just one type or an array of acceptable @@ -187,18 +206,23 @@ one of ``sendmail``, ``mail`` and ``smtp``. Use the method :method:`Symfony\\Component\\OptionsResolver\\Options::validateValues` to verify that the passed option contains one of these values:: - // ... - public function __construct(array $options = array()) + use Symfony\Component\OptionsResolver\Options; + + class Mailer { // ... - Options::validateValues($options, array( - 'transport' => array('sendmail', 'mail', 'smtp'), - )); - - $this->options = Options::resolve($options, array( + public function __construct(array $options = array()) + { // ... - 'transport' => 'sendmail', - )); + Options::validateValues($options, array( + 'transport' => array('sendmail', 'mail', 'smtp'), + )); + + $this->options = Options::resolve($options, array( + // ... + 'transport' => 'sendmail', + )); + } } If you pass an invalid transport, an :class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` @@ -232,22 +256,27 @@ You can implement this feature by passing a closure as default value of the ``port`` option. The closure receives the options as argument. Based on these options, you can return the desired default value:: - // ... - public function __construct(array $options = array()) + use Symfony\Component\OptionsResolver\Options; + + class Mailer { // ... - - $this->options = Options::resolve($options, new Options(array( + public function __construct(array $options = array()) + { // ... - 'encryption' => null, - 'port' => function (Options $options) { - if ('ssl' === $options['encryption']) { - return 465; - } - return 25; - }, - ))); + $this->options = Options::resolve($options, new Options(array( + // ... + 'encryption' => null, + 'port' => function (Options $options) { + if ('ssl' === $options['encryption']) { + return 465; + } + + return 25; + }, + ))); + } } Instead of a simple array, we now pass the default options as @@ -276,6 +305,8 @@ If you have a large list of options, the option processing code can take up a lot of space of your method. To make your code easier to read and maintain, it is a good practice to put the option definitions into static class properties:: + use Symfony\Component\OptionsResolver\Options; + class Mailer { private static $defaultOptions = array( @@ -445,12 +476,18 @@ however, *no default value* will be added to the options array. Pass the names of the optional options to :method:`Symfony\\Component\\OptionsResolver\\OptionsConfig::setOptional`:: - // ... - protected function configureOptions(OptionsConfig $config) + use Symfony\Component\OptionsResolver\Options; + use Symfony\Component\OptionsResolver\OptionsConfig; + + class Mailer { // ... + protected function configureOptions(OptionsConfig $config) + { + // ... - $config->setOptional(array('port')); + $config->setOptional(array('port')); + } } This is useful if you need to know whether an option was explicitly passed. If @@ -486,20 +523,25 @@ not, it will be missing from the options array:: the options before calling :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`:: - // ... - public function __construct(array $options = array()) + use Symfony\Component\OptionsResolver\Options; + + class Mailer { // ... + public function __construct(array $options = array()) + { + // ... - if (array_key_exists('port', $options)) { - echo "Set!"; - } else { - echo "Not Set!"; - } + if (array_key_exists('port', $options)) { + echo "Set!"; + } else { + echo "Not Set!"; + } - $this->options = Options::resolve($options, array( - // ... - )); + $this->options = Options::resolve($options, array( + // ... + )); + } } Overwriting Default Values @@ -518,24 +560,27 @@ again. When using a closure as the new value it is passed 2 arguments: use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsConfig; - // ... - protected function configureOptions(OptionsConfig $config) + class Mailer { // ... - $config->setDefaults(array( - 'encryption' => 'ssl', - 'host' => 'localhost', - )); + protected function configureOptions(OptionsConfig $config) + { + // ... + $config->setDefaults(array( + 'encryption' => 'ssl', + 'host' => 'localhost', + )); - // ... - $config->setDefaults(array( - 'encryption' => 'tls', // simple overwrite - 'host' => function (Options $options, $previousValue) { - return 'localhost' == $previousValue - ? '127.0.0.1' - : $previousValue; - }, - )); + // ... + $config->setDefaults(array( + 'encryption' => 'tls', // simple overwrite + 'host' => function (Options $options, $previousValue) { + return 'localhost' == $previousValue + ? '127.0.0.1' + : $previousValue; + }, + )); + } } .. tip:: @@ -550,28 +595,31 @@ again. When using a closure as the new value it is passed 2 arguments: use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsConfig; - // ... - protected function configureOptions(OptionsConfig $config) + class Mailer { // ... - $config->setDefaults(array( - 'encryption' => 'ssl', - 'heavy' => function (Options $options) { - // Some heavy calculations to create the $result - - return $result; - }, - )); - - $config->replaceDefaults(array( - 'encryption' => 'tls', // simple overwrite - 'heavy' => function (Options $options) { - // $previousValue not available - // ... - - return $someOtherResult; - }, - )); + protected function configureOptions(OptionsConfig $config) + { + // ... + $config->setDefaults(array( + 'encryption' => 'ssl', + 'heavy' => function (Options $options) { + // Some heavy calculations to create the $result + + return $result; + }, + )); + + $config->replaceDefaults(array( + 'encryption' => 'tls', // simple overwrite + 'heavy' => function (Options $options) { + // $previousValue not available + // ... + + return $someOtherResult; + }, + )); + } } .. note:: @@ -587,44 +635,56 @@ you can write normalizers. Normalizers are executed after all options were processed. You can configure these normalizers by calling :method:`Symfony\\Components\\OptionsResolver\\OptionsConfig::setNormalizers`:: - // ... - protected function configureOptions(OptionsConfig $config) + use Symfony\Component\OptionsResolver\Options; + use Symfony\Component\OptionsResolver\OptionsConfig; + + class Mailer { // ... + protected function configureOptions(OptionsConfig $config) + { + // ... - $config->setNormalizers(array( - 'host' => function (Options $options, $value) { - if ('http://' !== substr($value, 0, 7)) { - $value = 'http://'.$value; - } + $config->setNormalizers(array( + 'host' => function (Options $options, $value) { + if ('http://' !== substr($value, 0, 7)) { + $value = 'http://'.$value; + } - return $value; - }, - )); + return $value; + }, + )); + } } The normalizer receives the actual ``$value`` and returns the normalized form. You see that the closure also takes an ``$options`` parameter. This is useful if you need to use other options for the normalization:: - // ... - protected function configureOptions(OptionsConfig $config) + use Symfony\Component\OptionsResolver\Options; + use Symfony\Component\OptionsResolver\OptionsConfig; + + class Mailer { // ... + protected function configureOptions(OptionsConfig $config) + { + // ... - $config->setNormalizers(array( - 'host' => function (Options $options, $value) { - if (!in_array(substr($value, 0, 7), array('http://', 'https://'))) { - if ($options['ssl']) { - $value = 'https://'.$value; - } else { - $value = 'http://'.$value; + $config->setNormalizers(array( + 'host' => function (Options $options, $value) { + if (!in_array(substr($value, 0, 7), array('http://', 'https://'))) { + if ($options['ssl']) { + $value = 'https://'.$value; + } else { + $value = 'http://'.$value; + } } - } - return $value; - }, - )); + return $value; + }, + )); + } } .. tip:: @@ -633,15 +693,20 @@ if you need to use other options for the normalization:: object, perform normalization after the call to :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`:: - // ... - public function __construct(array $options = array()) + use Symfony\Component\OptionsResolver\Options; + + class Mailer { - $this->options = Options::resolve($options, array( - // ... - )); + // ... + public function __construct(array $options = array()) + { + $this->options = Options::resolve($options, array( + // ... + )); - if ('http://' !== substr($this->options['host'], 0, 7)) { - $this->options['host'] = 'http://'.$this->options['host']; + if ('http://' !== substr($this->options['host'], 0, 7)) { + $this->options['host'] = 'http://'.$this->options['host']; + } } } From df640b2ba0c31c928b14b6aaae9f640a44f973af Mon Sep 17 00:00:00 2001 From: WouterJ Date: Mon, 25 Aug 2014 12:14:01 +0200 Subject: [PATCH 154/835] Updated for CMF doc refactoring --- redirection_map | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/redirection_map b/redirection_map index e632d829662..1a6758a5560 100644 --- a/redirection_map +++ b/redirection_map @@ -22,3 +22,25 @@ /cookbook/console/generating_urls /cookbook/console/sending_emails /components/yaml /components/yaml/introduction /components/templating /components/templating/introduction +/cmf/reference/configuration/block /cmf/bundles/block/configuration +/cmf/reference/configuration/content /cmf/bundles/content/configuration +/cmf/reference/configuration/core /cmf/bundles/core/configuration +/cmf/reference/configuration/create /cmf/bundles/create/configuration +/cmf/reference/configuration/media /cmf/bundles/media/configuration +/cmf/reference/configuration/menu /cmf/bundles/menu/configuration +/cmf/reference/configuration/phpcr_odm /cmf/bundles/phpcr_odm/configuration +/cmf/reference/configuration/routing /cmf/bundles/routing/configuration +/cmf/reference/configuration/search /cmf/bundles/search/configuration +/cmf/reference/configuration/seo /cmf/bundles/seo/configuration +/cmf/reference/configuration/simple_cms /cmf/bundles/simple_cms/configuration +/cmf/reference/configuration/tree_browser /cmf/bundles/tree_browser/configuration +/cmf/cookbook/exposing_content_via_rest /cmf/bundles/content/exposing_content_via_rest +/cmf/cookbook/creating_a_cms/auto-routing /cmf/tutorial/auto-routing +/cmf/cookbook/creating_a_cms/conclusion /cmf/tutorial/conclusion +/cmf/cookbook/creating_a_cms/content-to-controllers /cmf/tutorial/content-to-controllers +/cmf/cookbook/creating_a_cms/getting-started /cmf/tutorial/getting-started +/cmf/cookbook/creating_a_cms/index /cmf/tutorial/index +/cmf/cookbook/creating_a_cms/introduction /cmf/tutorial/introduction +/cmf/cookbook/creating_a_cms/make-homepage /cmf/tutorial/make-homepage +/cmf/cookbook/creating_a_cms/sonata-admin /cmf/tutorial/sonata-admin +/cmf/cookbook/creating_a_cms/the-frontend /cmf/tutorial/the-frontend From 785827fda1a15c46ae63842e5b1534ea06afae27 Mon Sep 17 00:00:00 2001 From: Ariel Ferrandini Date: Tue, 26 Aug 2014 12:04:34 +0200 Subject: [PATCH 155/835] New service to simplify password encoding --- book/security.rst | 35 +++++++++++++++-- .../custom_password_authenticator.rst | 38 ++++++++----------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/book/security.rst b/book/security.rst index 0572bc13330..ab1ce30c862 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1459,23 +1459,36 @@ is available by calling the PHP function :phpfunction:`hash_algos`. Determining the Hashed Password ............................... +.. versionadded:: 2.6 + The ``security.password_encoder`` service was introduced in Symfony 2.6. + If you're storing users in the database and you have some sort of registration form for users, you'll need to be able to determine the hashed password so that you can set it on your user before inserting it. No matter what algorithm you configure for your user object, the hashed password can always be determined in the following way from a controller:: - $factory = $this->get('security.encoder_factory'); $user = new Acme\UserBundle\Entity\User(); + $plainPassword = 'ryanpass'; + $encoded = $this->container->get('security.password_encoder') + ->encodePassword($user, $plainPassword); - $encoder = $factory->getEncoder($user); - $password = $encoder->encodePassword('ryanpass', $user->getSalt()); - $user->setPassword($password); + $user->setPassword($encoded); In order for this to work, just make sure that you have the encoder for your user class (e.g. ``Acme\UserBundle\Entity\User``) configured under the ``encoders`` key in ``app/config/security.yml``. +.. sidebar:: Get the User Encoder + + In some cases, you need a specific encoder for a given user (e.g. ``Acme\UserBundle\Entity\User``). + You can use the ``EncoderFactory`` to get this encoder:: + + $factory = $this->get('security.encoder_factory'); + $user = new Acme\UserBundle\Entity\User(); + + $encoder = $factory->getEncoder($user); + .. caution:: When you allow a user to submit a plaintext password (e.g. registration @@ -1483,6 +1496,20 @@ key in ``app/config/security.yml``. that the password is 4096 characters or less. Read more details in :ref:`How to implement a simple Registration Form `. +Validating a Plaintext Password +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you want to check if a plain password is valid for a given user:: + + // a user instance of some class which implements Symfony\Component\Security\Core\User\UserInterface + $user = ...; + + // the password that should be checked + $plainPassword = ...; + + $isValidPassword = $this->container->get('security.password_encoder') + ->isPasswordValid($user, $plainPassword); + Retrieving the User Object ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/cookbook/security/custom_password_authenticator.rst b/cookbook/security/custom_password_authenticator.rst index 288d0e65a6f..6894a5043c2 100644 --- a/cookbook/security/custom_password_authenticator.rst +++ b/cookbook/security/custom_password_authenticator.rst @@ -8,6 +8,7 @@ Imagine you want to allow access to your website only between 2pm and 4pm UTC. Before Symfony 2.4, you had to create a custom token, factory, listener and provider. In this entry, you'll learn how to do this for a login form (i.e. where your user submits their username and password). +Before Symfony 2.6, you had to use the password encoder to authenticate the user password. The Password Authenticator -------------------------- @@ -15,6 +16,9 @@ The Password Authenticator .. versionadded:: 2.4 The ``SimpleFormAuthenticatorInterface`` interface was introduced in Symfony 2.4. +.. versionadded:: 2.6 + The ``UserPasswordEncoderInterface`` interface was introduced in Symfony 2.6. + First, create a new class that implements :class:`Symfony\\Component\\Security\\Core\\Authentication\\SimpleFormAuthenticatorInterface`. Eventually, this will allow you to create custom logic for authenticating @@ -27,18 +31,18 @@ the user:: use Symfony\Component\Security\Core\Authentication\SimpleFormAuthenticatorInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; - use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; + use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\UserProviderInterface; class TimeAuthenticator implements SimpleFormAuthenticatorInterface { - private $encoderFactory; + private $encoder; - public function __construct(EncoderFactoryInterface $encoderFactory) + public function __construct(UserPasswordEncoderInterface $encoder) { - $this->encoderFactory = $encoderFactory; + $this->encoder = $encoder; } public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) @@ -49,12 +53,7 @@ the user:: throw new AuthenticationException('Invalid username or password'); } - $encoder = $this->encoderFactory->getEncoder($user); - $passwordValid = $encoder->isPasswordValid( - $user->getPassword(), - $token->getCredentials(), - $user->getSalt() - ); + $passwordValid = $this->encoder->isPasswordValid($user, $token->getCredentials()); if ($passwordValid) { $currentHour = date('G'); @@ -127,17 +126,12 @@ Ultimately, your job is to return a *new* token object that is "authenticated" (i.e. it has at least 1 role set on it) and which has the ``User`` object inside of it. -Inside this method, an encoder is needed to check the password's validity:: +Inside this method, the password encoder is needed to check the password's validity:: - $encoder = $this->encoderFactory->getEncoder($user); - $passwordValid = $encoder->isPasswordValid( - $user->getPassword(), - $token->getCredentials(), - $user->getSalt() - ); + $passwordValid = $this->encoder->isPasswordValid($user, $token->getCredentials()); -This is a service that is already available in Symfony and the password algorithm -is configured in the security configuration (e.g. ``security.yml``) under +This is a service that is already available in Symfony and it uses the password algorithm +that is configured in the security configuration (e.g. ``security.yml``) under the ``encoders`` key. Below, you'll see how to inject that into the ``TimeAuthenticator``. .. _cookbook-security-password-authenticator-config: @@ -157,7 +151,7 @@ Now, configure your ``TimeAuthenticator`` as a service: time_authenticator: class: Acme\HelloBundle\Security\TimeAuthenticator - arguments: ["@security.encoder_factory"] + arguments: ["@security.password_encoder"] .. code-block:: xml @@ -173,7 +167,7 @@ Now, configure your ``TimeAuthenticator`` as a service: - + @@ -188,7 +182,7 @@ Now, configure your ``TimeAuthenticator`` as a service: $container->setDefinition('time_authenticator', new Definition( 'Acme\HelloBundle\Security\TimeAuthenticator', - array(new Reference('security.encoder_factory')) + array(new Reference('security.password_encoder')) )); Then, activate it in the ``firewalls`` section of the security configuration From 0a4511345a82313cb6cc5fc0dae1351073f50c6a Mon Sep 17 00:00:00 2001 From: Bruno Vitorino Date: Wed, 27 Aug 2014 16:16:55 +0200 Subject: [PATCH 156/835] Route description correction. The actual route is '/random/{limit}' and not '/number/{limit}'. --- book/page_creation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/page_creation.rst b/book/page_creation.rst index da550fa60e4..fd6889868d0 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -231,7 +231,7 @@ the new route that defines the URL of the page that you're about to create: The routing consists of two basic pieces: the ``path``, which is the URL that this route will match, and a ``defaults`` array, which specifies the controller that should be executed. The placeholder syntax in the path -(``{limit}``) is a wildcard. It means that ``/number/10``, ``/number/327`` +(``{limit}``) is a wildcard. It means that ``/random/10``, ``/random/327`` or any other similar URL will match this route. The ``{limit}`` placeholder parameter will also be passed to the controller so that you can use its value to generate the proper random number. From fb0e79cfa21c733763855c8ca4486fe8503aa0b0 Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 6 Sep 2014 19:37:30 +0200 Subject: [PATCH 157/835] Fix method for adding placholders in progressBar The method is called setPlaceholderFormatterDefinition and not setPlaceholderFormatter --- components/console/helpers/progressbar.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index 46e993c08c0..a242791eda3 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -289,7 +289,7 @@ display that are not available in the list of built-in placeholders, you can create your own. Let's see how you can create a ``remaining_steps`` placeholder that displays the number of remaining steps:: - ProgressBar::setPlaceholderFormatter( + ProgressBar::setPlaceholderFormatterDefinition( '%remaining_steps%', function (ProgressBar $bar, OutputInterface $output) { return $bar->getMaxSteps() - $bar->getStep(); From f3ef9dc5aa3c0f5bbd70de0556f941dbdf100180 Mon Sep 17 00:00:00 2001 From: Daniel Tschinder Date: Sat, 6 Sep 2014 19:55:20 +0200 Subject: [PATCH 158/835] The name of the placeholder must not be encapsulated in % --- components/console/helpers/progressbar.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index a242791eda3..fec1b8c127f 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -290,7 +290,7 @@ create your own. Let's see how you can create a ``remaining_steps`` placeholder that displays the number of remaining steps:: ProgressBar::setPlaceholderFormatterDefinition( - '%remaining_steps%', + 'remaining_steps', function (ProgressBar $bar, OutputInterface $output) { return $bar->getMaxSteps() - $bar->getStep(); } From c723d8dd546412b08ffaf3846b5574f4f4021418 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Mon, 8 Sep 2014 17:38:40 +0200 Subject: [PATCH 159/835] Missing backtick, thanks to @Baptouuuu --- components/dependency_injection/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/advanced.rst b/components/dependency_injection/advanced.rst index f3fde08777c..babd7cfd0fc 100644 --- a/components/dependency_injection/advanced.rst +++ b/components/dependency_injection/advanced.rst @@ -269,7 +269,7 @@ a reference of the old one as ``bar.inner``: ->setPublic(false) ->setDecoratedService('foo'); -Here is what's going on here: the ``setDecoratedService()` method tells +Here is what's going on here: the ``setDecoratedService()`` method tells the container that the ``bar`` service should replace the ``foo`` service, renaming ``foo`` to ``bar.inner``. By convention, the old ``foo`` service is going to be renamed ``bar.inner``, From be4ec85e76f4982342d66cdd72db6da7c5a9bf9a Mon Sep 17 00:00:00 2001 From: Wouter J Date: Mon, 8 Sep 2014 17:40:11 +0200 Subject: [PATCH 160/835] Reverts c723d8dd546412b08ffaf3846b5574f4f4021418 --- components/dependency_injection/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/advanced.rst b/components/dependency_injection/advanced.rst index babd7cfd0fc..f3fde08777c 100644 --- a/components/dependency_injection/advanced.rst +++ b/components/dependency_injection/advanced.rst @@ -269,7 +269,7 @@ a reference of the old one as ``bar.inner``: ->setPublic(false) ->setDecoratedService('foo'); -Here is what's going on here: the ``setDecoratedService()`` method tells +Here is what's going on here: the ``setDecoratedService()` method tells the container that the ``bar`` service should replace the ``foo`` service, renaming ``foo`` to ``bar.inner``. By convention, the old ``foo`` service is going to be renamed ``bar.inner``, From dadaea3d9d82a1d9bb7e29ccf26036d74ccf7e1f Mon Sep 17 00:00:00 2001 From: Wouter J Date: Mon, 8 Sep 2014 17:41:33 +0200 Subject: [PATCH 161/835] Missing backtick, thanks to @Baptouuuu --- components/dependency_injection/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/advanced.rst b/components/dependency_injection/advanced.rst index f3fde08777c..babd7cfd0fc 100644 --- a/components/dependency_injection/advanced.rst +++ b/components/dependency_injection/advanced.rst @@ -269,7 +269,7 @@ a reference of the old one as ``bar.inner``: ->setPublic(false) ->setDecoratedService('foo'); -Here is what's going on here: the ``setDecoratedService()` method tells +Here is what's going on here: the ``setDecoratedService()`` method tells the container that the ``bar`` service should replace the ``foo`` service, renaming ``foo`` to ``bar.inner``. By convention, the old ``foo`` service is going to be renamed ``bar.inner``, From d6dd54087f9f3589fd691e23c852beae8354b8e4 Mon Sep 17 00:00:00 2001 From: Matthieu Auger Date: Mon, 15 Sep 2014 23:46:38 +0200 Subject: [PATCH 162/835] Move debug command to the debug namespace --- book/controller.rst | 7 +++++-- book/routing.rst | 9 ++++++--- book/service_container.rst | 11 +++++++---- book/testing.rst | 5 ++++- book/translation.rst | 15 +++++++++------ 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/book/controller.rst b/book/controller.rst index 17902efcf9e..f8b8cb0b41c 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -613,12 +613,15 @@ via the ``get()`` method. Here are several common services you might need:: $mailer = $this->get('mailer'); There are countless other services available and you are encouraged to define -your own. To list all available services, use the ``container:debug`` console +your own. To list all available services, use the ``debug:container`` console command: .. code-block:: bash - $ php app/console container:debug + $ php app/console debug:container + +.. versionadded:: 2.6 + Prior to Symfony 2.6, this command was called ``container:debug``. For more information, see the :doc:`/book/service_container` chapter. diff --git a/book/routing.rst b/book/routing.rst index 3b82cb3033a..69ce87f6429 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -1192,12 +1192,15 @@ Visualizing & Debugging Routes While adding and customizing routes, it's helpful to be able to visualize and get detailed information about your routes. A great way to see every route -in your application is via the ``router:debug`` console command. Execute +in your application is via the ``debug:router`` console command. Execute the command by running the following from the root of your project. .. code-block:: bash - $ php app/console router:debug + $ php app/console debug:router + +.. versionadded:: 2.6 + Prior to Symfony 2.6, this command was called ``router:debug``. This command will print a helpful list of *all* the configured routes in your application: @@ -1216,7 +1219,7 @@ the route name after the command: .. code-block:: bash - $ php app/console router:debug article_show + $ php app/console debug:router article_show Likewise, if you want to test whether a URL matches a given route, you can use the ``router:match`` console command: diff --git a/book/service_container.rst b/book/service_container.rst index 34120f60ee7..f7b4808d7c7 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -1169,18 +1169,21 @@ console. To show all services and the class for each service, run: .. code-block:: bash - $ php app/console container:debug + $ php app/console debug:container + +.. versionadded:: 2.6 + Prior to Symfony 2.6, this command was called ``container:debug``. By default only public services are shown, but you can also view private services: .. code-block:: bash - $ php app/console container:debug --show-private + $ php app/console debug:container --show-private .. note:: If a private service is only used as an argument to just *one* other service, - it won't be displayed by the ``container:debug`` command, even when using + it won't be displayed by the ``debug:container`` command, even when using the ``--show-private`` option. See :ref:`Inline Private Services ` for more details. @@ -1189,7 +1192,7 @@ its id: .. code-block:: bash - $ php app/console container:debug my_mailer + $ php app/console debug:container my_mailer Learn more ---------- diff --git a/book/testing.rst b/book/testing.rst index f870ac58272..67c5f798f2b 100644 --- a/book/testing.rst +++ b/book/testing.rst @@ -459,7 +459,10 @@ injection container:: Be warned that this does not work if you insulate the client or if you use an HTTP layer. For a list of services available in your application, use the -``container:debug`` console task. +``debug:container`` console task. + +.. versionadded:: 2.6 + Prior to Symfony 2.6, this command was called ``container:debug``. .. tip:: diff --git a/book/translation.rst b/book/translation.rst index 8abe3c8da2c..63a98e8309b 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -662,10 +662,13 @@ Debugging Translations ---------------------- .. versionadded:: 2.5 - The ``translation:debug`` command was introduced in Symfony 2.5. + The ``debug:translation`` command was introduced in Symfony 2.5. + +.. versionadded:: 2.6 + Prior to Symfony 2.6, this command was called ``translation:debug``. When maintaining a bundle, you may use or remove the usage of a translation -message without updating all message catalogues. The ``translation:debug`` +message without updating all message catalogues. The ``debug:translation`` command helps you to find these missing or unused translation messages for a given locale. It shows you a table with the result when translating the message in the given locale and the result when the fallback would be used. @@ -774,7 +777,7 @@ To inspect all messages in the ``fr`` locale for the AcmeDemoBundle, run: .. code-block:: bash - $ php app/console translation:debug fr AcmeDemoBundle + $ php app/console debug:translation fr AcmeDemoBundle You will get this output: @@ -815,15 +818,15 @@ By default all domains are inspected, but it is possible to specify a single dom .. code-block:: bash - $ php app/console translation:debug en AcmeDemoBundle --domain=messages + $ php app/console debug:translation en AcmeDemoBundle --domain=messages When bundles have a lot of messages, it is useful to display only the unused or only the missing messages, by using the ``--only-unused`` or ``--only-missing`` switches: .. code-block:: bash - $ php app/console translation:debug en AcmeDemoBundle --only-unused - $ php app/console translation:debug en AcmeDemoBundle --only-missing + $ php app/console debug:translation en AcmeDemoBundle --only-unused + $ php app/console debug:translation en AcmeDemoBundle --only-missing Summary ------- From f202fb83e60254b9938aefb00acaef1aa1fdf704 Mon Sep 17 00:00:00 2001 From: Peter Rehm Date: Wed, 17 Sep 2014 07:52:36 +0200 Subject: [PATCH 163/835] Removed duplicate use statements --- components/options_resolver.rst | 51 +++++++++++---------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 11899762de0..22b1943f9e9 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -96,6 +96,7 @@ is thrown if an unknown option is passed:: The rest of your code can now access the values of the options without boilerplate code:: + // ... class Mailer { // ... @@ -117,8 +118,7 @@ If an option must be set by the caller, pass that option to :method:`Symfony\\Component\\OptionsResolver\\Options::validateRequired`. For example, let's make the ``host`` option required:: - use Symfony\Component\OptionsResolver\Options; - + // ... class Mailer { // ... @@ -160,9 +160,8 @@ Type Validation You can run additional checks on the options to make sure they were passed correctly. To validate the types of the options, call :method:`Symfony\\Component\\OptionsResolver\\Options::validateTypes`:: - - use Symfony\Component\OptionsResolver\Options; - + + // ... class Mailer { // ... @@ -206,8 +205,7 @@ one of ``sendmail``, ``mail`` and ``smtp``. Use the method :method:`Symfony\\Component\\OptionsResolver\\Options::validateValues` to verify that the passed option contains one of these values:: - use Symfony\Component\OptionsResolver\Options; - + // ... class Mailer { // ... @@ -256,8 +254,7 @@ You can implement this feature by passing a closure as default value of the ``port`` option. The closure receives the options as argument. Based on these options, you can return the desired default value:: - use Symfony\Component\OptionsResolver\Options; - + // ... class Mailer { // ... @@ -305,8 +302,7 @@ If you have a large list of options, the option processing code can take up a lot of space of your method. To make your code easier to read and maintain, it is a good practice to put the option definitions into static class properties:: - use Symfony\Component\OptionsResolver\Options; - + // ... class Mailer { private static $defaultOptions = array( @@ -382,7 +378,7 @@ configuration object to resolve the options. The following code demonstrates how to write our previous ``Mailer`` class with an :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` object:: - use Symfony\Component\OptionsResolver\Options; + // ... use Symfony\Component\OptionsResolver\OptionsConfig; class Mailer @@ -431,9 +427,7 @@ the :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` instance. Nevertheless, this design also has a benefit: We can extend the ``Mailer`` class and adjust the options of the parent class in the subclass:: - use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsConfig; - + // ... class GoogleMailer extends Mailer { protected function configureOptions(OptionsConfig $config) @@ -476,9 +470,7 @@ however, *no default value* will be added to the options array. Pass the names of the optional options to :method:`Symfony\\Component\\OptionsResolver\\OptionsConfig::setOptional`:: - use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsConfig; - + // ... class Mailer { // ... @@ -493,6 +485,7 @@ of the optional options to This is useful if you need to know whether an option was explicitly passed. If not, it will be missing from the options array:: + // ... class Mailer { // ... @@ -523,8 +516,7 @@ not, it will be missing from the options array:: the options before calling :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`:: - use Symfony\Component\OptionsResolver\Options; - + // ... class Mailer { // ... @@ -557,9 +549,7 @@ again. When using a closure as the new value it is passed 2 arguments: .. code-block:: php - use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsConfig; - + // ... class Mailer { // ... @@ -592,9 +582,7 @@ again. When using a closure as the new value it is passed 2 arguments: to improve performance. This means that the previous default value is not available when overwriting with another closure:: - use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsConfig; - + // ... class Mailer { // ... @@ -635,9 +623,7 @@ you can write normalizers. Normalizers are executed after all options were processed. You can configure these normalizers by calling :method:`Symfony\\Components\\OptionsResolver\\OptionsConfig::setNormalizers`:: - use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsConfig; - + // ... class Mailer { // ... @@ -661,9 +647,7 @@ The normalizer receives the actual ``$value`` and returns the normalized form. You see that the closure also takes an ``$options`` parameter. This is useful if you need to use other options for the normalization:: - use Symfony\Component\OptionsResolver\Options; - use Symfony\Component\OptionsResolver\OptionsConfig; - + // ... class Mailer { // ... @@ -693,8 +677,7 @@ if you need to use other options for the normalization:: object, perform normalization after the call to :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`:: - use Symfony\Component\OptionsResolver\Options; - + // ... class Mailer { // ... From bdd8325180021675015cb16f6803ff39cbff5e00 Mon Sep 17 00:00:00 2001 From: kshishkin Date: Sat, 20 Sep 2014 14:46:32 +0200 Subject: [PATCH 164/835] Adder and remover sidenote | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | all | Fixed tickets | none There are cases when you only want either adder or remover, important to know both of them need to be found if any is to work. --- cookbook/form/form_collections.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index 6871be7cdab..58383fd9f7f 100644 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -462,7 +462,8 @@ these new ``Tag`` objects easier (especially if you're using Doctrine, which we talk about next!). .. caution:: - + Create **both** methods, otherwise none of them will be used. + If no ``addTag`` **and** ``removeTag`` method is found, the form will still use ``setTag`` even if ``by_reference`` is ``false``. You'll learn more about the ``removeTag`` method later in this article. From 59b932dbedf841466dbde85f9d8a39a75c1fe9ea Mon Sep 17 00:00:00 2001 From: kshishkin Date: Sun, 21 Sep 2014 20:23:11 +0200 Subject: [PATCH 165/835] Merging emphasized notes Merged both notes into one, in accordance to discussion feedback. --- cookbook/form/form_collections.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index 58383fd9f7f..c848387f035 100644 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -462,11 +462,10 @@ these new ``Tag`` objects easier (especially if you're using Doctrine, which we talk about next!). .. caution:: - Create **both** methods, otherwise none of them will be used. - - If no ``addTag`` **and** ``removeTag`` method is found, the form will - still use ``setTag`` even if ``by_reference`` is ``false``. You'll learn - more about the ``removeTag`` method later in this article. + + You have to create **both** ``addTag`` and ``removeTag`` methods, + otherwise the form will still use ``setTag`` even if ``by_reference`` is ``false``. + You'll learn more about the ``removeTag`` method later in this article. .. sidebar:: Doctrine: Cascading Relations and saving the "Inverse" side From 26edf7ad4f2ada551c145cf81e7a3c9fccf5b567 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 17 Sep 2014 18:55:15 +0200 Subject: [PATCH 166/835] intro the var-dumper component --- components/index.rst | 1 + components/map.rst.inc | 4 ++ components/var_dumper.rst | 134 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 components/var_dumper.rst diff --git a/components/index.rst b/components/index.rst index 8e167cd2ac9..be74544ce43 100644 --- a/components/index.rst +++ b/components/index.rst @@ -29,6 +29,7 @@ The Components stopwatch templating/index translation/index + var_dumper yaml/index .. include:: /components/map.rst.inc diff --git a/components/map.rst.inc b/components/map.rst.inc index f9eab711e07..d3ce97a167f 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -143,6 +143,10 @@ * :doc:`/components/translation/usage` * :doc:`/components/translation/custom_formats` +* **VarDumper** + + * :doc:`/components/var_dumper` + * :doc:`/components/yaml/index` * :doc:`/components/yaml/introduction` diff --git a/components/var_dumper.rst b/components/var_dumper.rst new file mode 100644 index 00000000000..01d8f237673 --- /dev/null +++ b/components/var_dumper.rst @@ -0,0 +1,134 @@ +.. index:: + single: VarDumper + single: Components; VarDumper + +The VarDumper Component +======================= + + The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. + Built on top, it provides a better ``dump()`` function, that you can use instead of ``var_dump()``, + *better* meaning: + + - per object and resource types specialized view to e.g. filter out Doctrine noise + while dumping a single proxy entity, or get more insight on opened files with + ``stream_get_meta_data()``. + - configurable output format: HTML, command line with colors or JSON. + - ability to dump internal references, either soft ones (objects or resources) + or hard ones (``=&`` on arrays or objects properties). Repeated occurrences of + the same object/array/resource won't appear again and again anymore. Moreover, + you'll be able to inspected the reference structure of your data. + - ability to operate in the context of an output buffering handler. + +.. versionadded:: 2.6 + The VarDumper component was introduced in Symfony 2.6. + +Installation +------------ + +You can install the component in 2 different ways: + +* :doc:`Install it via Composer ` (``symfony/var-dumper`` on `Packagist`_); +* Use the official Git repository (https://github.com/symfony/VarDumper). + +The dump() function +------------------- + +The VarDumper component creates a global ``dump()`` function that is auto-configured out of the box: +HTML or CLI output is automatically selected based on the current PHP SAPI. + +``dump()`` is just a thin wrapper for ``\Symfony\Component\VarDumper\VarDumper::dump()`` so can you also use it directly. +You can change the behavior of this function by calling ``\Symfony\Component\VarDumper\VarDumper::setHandler($callable)``: +calls to ``dump()`` will then be forwarded to the ``$callable`` given as first argument. + +Advanced usage +-------------- + +Cloners +~~~~~~~ + +A cloner is used to create an intermediate representation of any PHP variable. +Its output is a Data object that wraps this representation. +A cloner also applies limits when creating the representation, so that the corresponding +Data object could represent only a subset of the cloned variable. + +You can create a Data object this way:: + + $cloner = new PhpCloner(); + $data = $cloner->cloneVar($myVar); + +Before cloning, you can configure the limits with:: + + $cloner->setMaxItems($number); + $cloner->setMaxString($number); + +These limits will be applied when calling ``->cloneVar()`` afterwise. + +Casters +~~~~~~~ + +Objects and resources nested in a PHP variable are casted to arrays in the intermediate Data representation. +You can tweak the array representation for each object/resource by hooking a Caster into this process. +The component already has a many casters for base PHP classes and other common classes. + +If you want to build your how Caster, you can register one before cloning a PHP variable. +Casters are registered using either a Cloner's constructor or its ``addCasters()`` method:: + + $myCasters = array(...); + $cloner = new PhpCloner($myCasters); + +or:: + + $cloner->addCasters($myCasters); + +The provided ``$myCasters`` argument is an array that maps a class, an interface or a resource type to a callable:: + + $myCasters = array( + 'FooClass' => $myFooClassCallableCaster, + ':bar resource' => $myBarResourceCallableCaster, + ); + +As you can notice, resource types are prefixed by a ``:`` to prevent colliding with a class name. + +Because an object has one main class and potentially many parent classes or interfaces, +many casters can be applied to one object. In this case, casters are called one after the other, +starting from casters bound to the interfaces, the parents classes and then the main class. +Several casters can also be registered for the same resource type/class/interface. +They are called in registration order. + +Casters are responsible for returning the properties of the object or resource being cloned in an array. +They are callables that accept four arguments:: + + /** + * A caster not doing anything. + * + * @param object|resource $object The object or resource being casted. + * @param array $array An array modelled for objects after PHP's native `(array)` cast operator. + * @param Stub $stub A Cloner\Stub object representing the main properties of $object (class, type, etc.). + * @param bool $isNested True/false when the caster is called nested is a structure or not. + * + * @return array The properties of $object casted in an array. + */ + function myCaster($origValue, $array, $stub, $isNested) + { + // Here, populate/alter $array to your needs. + + return $array; + } + +For objects, the ``$array`` parameter comes pre-populated with PHP's native ``(array)`` casting operator, +or with the return value of ``$object->__debugInfo()`` if the magic method exists. +Then, the return value of one Caster is given as argument to the next Caster in the chain. + +When casting with the ``(array)`` operator, PHP prefixes protected properties with a ``\0*\0`` +and private ones with the class owning the property: e.g. ``\0Foobar\0`` prefixes all private properties +of objects of type Foobar. Casters follow this convention and add two more prefixes: ``\0~\0`` is used for +virtual properties and ``\0+\0`` for dynamic ones (runtime added properties not in the class declaration). + +.. note:: + + Although you can, it is best advised not to alter the state of an object while casting it in a Caster. + +Dumpers +~~~~~~~ + +.. _Packagist: https://packagist.org/packages/symfony/var-dumper From 4e2142d44e5a35feee34333cd9bb7e233fc5bbf4 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 5 Sep 2014 14:02:00 -0400 Subject: [PATCH 167/835] Added note about ProgressBar changes --- components/console/helpers/progressbar.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index 46e993c08c0..eb13c69b587 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -46,8 +46,16 @@ you can also set the current progress by calling the .. caution:: - The progress bar only works if your platform supports ANSI codes; on other - platforms, no output is generated. + Prior to version 2.6, the progress bar only works if your platform + supports ANSI codes; on other platforms, no output is generated. + +.. versionadded:: 2.6 + If your platform doesn't support ANSI codes, updates to the progress + bar are added as new lines. To prevent the output from being flooded, + adjust the + :method:`Symfony\\Component\\Console\\Helper\\ProgressBar::setRedrawFrequency` + accordingly. By default, when using a ``max``, the redraw frequency + is set to *10%* of your ``max``. If you don't know the number of steps in advance, just omit the steps argument when creating the :class:`Symfony\\Component\\Console\\Helper\\ProgressBar` From c41b17cf0d97702679fe25b7cff07a8b0358539a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 17 Sep 2014 23:57:10 +0200 Subject: [PATCH 168/835] add description for the `validation_groups` option --- book/forms.rst | 2 ++ reference/forms/types/submit.rst | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/book/forms.rst b/book/forms.rst index a861d232997..0998408312e 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -504,6 +504,8 @@ fields were submitted. If you want to suppress validation, you can use the .. index:: single: Forms; Validation groups based on submitted data +.. _book-form-validation-groups: + Groups based on the Submitted Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst index 72faa779ae1..01847034d84 100644 --- a/reference/forms/types/submit.rst +++ b/reference/forms/types/submit.rst @@ -17,6 +17,7 @@ A submit button. | | - `label`_ | | | - `label_attr`_ | | | - `translation_domain`_ | +| | - `validation_groups`_ | +----------------------+----------------------------------------------------------------------+ | Parent type | :doc:`button` | +----------------------+----------------------------------------------------------------------+ @@ -45,6 +46,33 @@ Inherited Options .. include:: /reference/forms/types/options/button_translation_domain.rst.inc +validation_groups +~~~~~~~~~~~~~~~~~ + +**type**: ``array`` **default**: ``null`` + +When your form contains multiple submit buttons, you can change the validation +group based on the button which was used to submit the form. Imagine a registration +form wizard with buttons to go to the previous or the next step:: + + $form = $this->createFormBuilder($user) + ->add('previousStep', 'submit', array( + 'validation_groups' => false, + )) + ->add('nextStep', 'submit', array( + 'validation_groups' => array('Registration'), + )) + ->getForm(); + +The special ``false`` ensures that no validation is performed when the previous +step button is clicked. When the second button is clicked, all constraints +from the "Registration" are validated. + +.. seealso:: + + You can read more about this in :ref:`the Form chapter ` + of the book. + Form Variables -------------- From 63d8657a1b6a456bfe69c6f3de99813c151acab2 Mon Sep 17 00:00:00 2001 From: Stefano Sala Date: Mon, 7 Jul 2014 14:47:02 +0200 Subject: [PATCH 169/835] [Twig][Form] Moved twig.form.resources to a higher level --- cookbook/form/form_customization.rst | 28 +++++++++------------------ reference/configuration/twig.rst | 29 +++++++++++++++------------- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index fb36e4fc01f..358fab95a31 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -439,18 +439,15 @@ form is rendered. # app/config/config.yml twig: - form: - resources: - - 'AcmeDemoBundle:Form:fields.html.twig' + form_themes: + - 'AcmeDemoBundle:Form:fields.html.twig' # ... .. code-block:: xml - - AcmeDemoBundle:Form:fields.html.twig - + AcmeDemoBundle:Form:fields.html.twig @@ -458,10 +455,8 @@ form is rendered. // app/config/config.php $container->loadFromExtension('twig', array( - 'form' => array( - 'resources' => array( - 'AcmeDemoBundle:Form:fields.html.twig', - ), + 'form_themes' => array( + 'AcmeDemoBundle:Form:fields.html.twig', ), // ... @@ -477,17 +472,14 @@ resource to use such a layout: # app/config/config.yml twig: - form: - resources: ['form_table_layout.html.twig'] + form_themes: ['form_table_layout.html.twig'] # ... .. code-block:: xml - - form_table_layout.html.twig - + form_table_layout.html.twig @@ -495,10 +487,8 @@ resource to use such a layout: // app/config/config.php $container->loadFromExtension('twig', array( - 'form' => array( - 'resources' => array( - 'form_table_layout.html.twig', - ), + 'form_themes' => array( + 'form_table_layout.html.twig', ), // ... diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index cdb3de3bd00..cd121710277 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -10,14 +10,13 @@ TwigBundle Configuration ("twig") twig: exception_controller: twig.controller.exception:showAction - form: - resources: + form_themes: - # Default: - - form_div_layout.html.twig + # Default: + - form_div_layout.html.twig - # Example: - - MyBundle::form.html.twig + # Example: + - MyBundle::form.html.twig globals: # Examples: @@ -54,9 +53,8 @@ TwigBundle Configuration ("twig") http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/doctrine/twig-1.0.xsd"> - - MyBundle::form.html.twig - + form_div_layout.html.twig + MyBundle::form.html.twig 3.14 @@ -65,10 +63,9 @@ TwigBundle Configuration ("twig") .. code-block:: php $container->loadFromExtension('twig', array( - 'form' => array( - 'resources' => array( - 'MyBundle::form.html.twig', - ) + 'form_themes' => array( + 'form_div_layout.html.twig', // Default + 'MyBundle::form.html.twig', ), 'globals' => array( 'foo' => '@bar', @@ -83,6 +80,12 @@ TwigBundle Configuration ("twig") 'strict_variables' => false, )); +.. caution:: + + The ``twig.form`` (```` tag for xml) configuration key + has been deprecated and will be removed in 3.0. Instead, use the ``twig.form_themes`` + option. + Configuration ------------- From e6aa73314d5e092bedd2d4b468c3e278d4feabcd Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Wed, 24 Sep 2014 16:58:43 +0200 Subject: [PATCH 170/835] swapped comment and opening in xml configuration example --- cookbook/security/pre_authenticated.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/security/pre_authenticated.rst b/cookbook/security/pre_authenticated.rst index 394ae4f739e..63405fb7f53 100644 --- a/cookbook/security/pre_authenticated.rst +++ b/cookbook/security/pre_authenticated.rst @@ -34,8 +34,8 @@ Enable the x509 authentication for a particular firewall in the security configu .. code-block:: xml - + @@ -107,8 +107,8 @@ corresponding firewall in your security configuration: .. code-block:: xml - + From 695cb2056e966d48e71108b41ba3999a2fa91dc8 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 24 Sep 2014 11:00:32 -0500 Subject: [PATCH 171/835] [#4003] A few more form_themes config changes --- book/forms.rst | 15 +++++---------- cookbook/form/create_custom_field_type.rst | 15 +++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index b6f4ddbfd4d..b66dab4a1de 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -1578,9 +1578,8 @@ file: # app/config/config.yml twig: - form: - resources: - - 'AcmeTaskBundle:Form:fields.html.twig' + form_themes: + - 'AcmeTaskBundle:Form:fields.html.twig' # ... .. code-block:: xml @@ -1594,9 +1593,7 @@ file: http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - - AcmeTaskBundle:Form:fields.html.twig - + AcmeTaskBundle:Form:fields.html.twig @@ -1605,10 +1602,8 @@ file: // app/config/config.php $container->loadFromExtension('twig', array( - 'form' => array( - 'resources' => array( - 'AcmeTaskBundle:Form:fields.html.twig', - ), + 'form_themes' => array( + 'AcmeTaskBundle:Form:fields.html.twig', ), // ... )); diff --git a/cookbook/form/create_custom_field_type.rst b/cookbook/form/create_custom_field_type.rst index 3cb5f33c265..aa5420d32a6 100644 --- a/cookbook/form/create_custom_field_type.rst +++ b/cookbook/form/create_custom_field_type.rst @@ -159,27 +159,22 @@ link for details), create a ``gender_widget`` block to handle this: # app/config/config.yml twig: - form: - resources: - - 'AcmeDemoBundle:Form:fields.html.twig' + form_themes: + - 'AcmeDemoBundle:Form:fields.html.twig' .. code-block:: xml - - AcmeDemoBundle:Form:fields.html.twig - + AcmeDemoBundle:Form:fields.html.twig .. code-block:: php // app/config/config.php $container->loadFromExtension('twig', array( - 'form' => array( - 'resources' => array( - 'AcmeDemoBundle:Form:fields.html.twig', - ), + 'form_themes' => array( + 'AcmeDemoBundle:Form:fields.html.twig', ), )); From 881f8d883178065905d5ef7bde94a7bdda25f45a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 26 Sep 2014 15:10:20 +0200 Subject: [PATCH 172/835] added Nicolas to the list of mergers for the new var dumper component --- contributing/code/core_team.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/contributing/code/core_team.rst b/contributing/code/core_team.rst index 03f866f2e3e..5ec7ac23fd6 100644 --- a/contributing/code/core_team.rst +++ b/contributing/code/core_team.rst @@ -51,8 +51,8 @@ Active Core Members * **Romain Neutron** (:merger:`romainneutron`) can merge into the Process_ component; - * **Nicolas Grekas** (:merger:`nicolas-grekas`) can merge into the Debug_ - component; + * **Nicolas Grekas** (:merger:`nicolas-grekas`) can merge into the Debug_ and + VarDumper_ components, and into the DebugBundle_ bundle; * **Christophe Coevoet** (:merger:`stof`) can merge into the BrowserKit_, Config_, Console_, DependencyInjection_, DomCrawler_, EventDispatcher_, @@ -164,3 +164,5 @@ discretion of the **Project Leader**. .. _Stopwatch: https://github.com/symfony/Stopwatch .. _TwigBridge: https://github.com/symfony/TwigBridge .. _Validator: https://github.com/symfony/Validator +.. _VarDumper: https://github.com/symfony/var-dumper +.. _DebugBundle: https://github.com/symfony/debug-bundle From 2a14bf30e2ff9847eb46d1db711528345f743c0d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 26 Sep 2014 14:26:53 -0500 Subject: [PATCH 173/835] Clarifying some language --- contributing/code/core_team.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contributing/code/core_team.rst b/contributing/code/core_team.rst index 5ec7ac23fd6..32390121dc3 100644 --- a/contributing/code/core_team.rst +++ b/contributing/code/core_team.rst @@ -51,8 +51,8 @@ Active Core Members * **Romain Neutron** (:merger:`romainneutron`) can merge into the Process_ component; - * **Nicolas Grekas** (:merger:`nicolas-grekas`) can merge into the Debug_ and - VarDumper_ components, and into the DebugBundle_ bundle; + * **Nicolas Grekas** (:merger:`nicolas-grekas`) can merge into the Debug_ + component, the VarDumper_ component and the DebugBundle_; * **Christophe Coevoet** (:merger:`stof`) can merge into the BrowserKit_, Config_, Console_, DependencyInjection_, DomCrawler_, EventDispatcher_, From 3ad13a140da2a96fa748f335d5a5ffe2ad1539ed Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Sat, 27 Sep 2014 11:46:05 +0200 Subject: [PATCH 174/835] Make a small grammatical adjustment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original form of the phrase - «The other option is to specifically checking if a session has expired after the session is started.» - is not entirely grammatically correct, as the infinitive form of «to check» shall normally be used, or the gerund form, which is the form used in this commit. --- components/http_foundation/session_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index 07413e7f162..91775856a9d 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -181,7 +181,7 @@ which runs reasonably frequently. The ``cookie_lifetime`` would be set to a relatively high value, and the garbage collection ``gc_maxlifetime`` would be set to destroy sessions at whatever the desired idle period is. -The other option is to specifically checking if a session has expired after the +The other option is specifically checking if a session has expired after the session is started. The session can be destroyed as required. This method of processing can allow the expiry of sessions to be integrated into the user experience, for example, by displaying a message. From 9dd3a772c5dead301838e397f50b8f65c4392d3f Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 28 Sep 2014 14:26:10 +0200 Subject: [PATCH 175/835] Created August CHANGELOG --- changelog.rst | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/changelog.rst b/changelog.rst index 62cb6670efe..1fa891aeb67 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,86 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +August, 2014 +------------ + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `bccb080 `_ #4140 [Cookbook][Logging] document multiple recipients in XML configs (xabbuh) +- `7a6e3d1 `_ #4150 Added the schema_filter option to the reference (peterrehm) +- `be90d8a `_ #4142 [Cookbook][Configuration] tweaks for the web server configuration chapter (xabbuh) +- `5379f54 `_ #4086 [Reference][Constraints] Added hint about attaching the expression constraint to a form field (peterrehm) +- `041105c `_ #3883 Removed redundant POST request exclusion info (ryancastle) +- `4f9fef6 `_ #4000 [Cookbook] add cookbook article for the server:run command (xabbuh) +- `4ea4dfe `_ #3915 [Cookbook][Configuration] documentation of Apache + PHP-FPM (xabbuh) +- `08bed5f `_ #4128 Finished #3759 (WouterJ) +- `4d5adaa `_ #4125 Added link to JSFiddle example (WouterJ) +- `75bda4b `_ #4124 Rebased #3965 (WouterJ) +- `fdb8a32 `_ #3950 [Components][EventDispatcher] describe the usage of the RegisterListenersPass (xabbuh) +- `7e09383 `_ #3940 Updated docs for Monolog "swift" handler in cookbook. (phansys) +- `8adfe98 `_ #3894 Rewrote Extension & Configuration docs (WouterJ) +- `cafea43 `_ #3888 Updated the example used to explain page creation (javiereguiluz) +- `df0cf68 `_ #3885 [RFR] Added "How to Organize Configuration Files" cookbook (javiereguiluz) +- `41116da `_ #4081 [Components][ClassLoader] documentation for the ClassMapGenerator class (xabbuh) +- `2b9cb7c `_ #4076 Fixed description of session storage of the ApiKeyAuthenticator (peterrehm) +- `35a0f66 `_ #4102 Adding a new entry about reverse proxies in the framework (weaverryan) +- `95c2066 `_ #4096 labels in submit buttons + new screenshot (ricardclau) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `5fac303 `_ #4165 Update voters.rst (gerryvdm) +- `4882b99 `_ #4164 Fixed minor typos. (ahsio) +- `eaaa35a `_ #4145 Fix documentation for group_sequence_provider (giosh94mhz) +- `155c3e8 `_ #4153 [Reference] fix namespace in Expression constraint (xabbuh) +- `2c93aa5 `_ #4147 [Cookbook][Logging] add missing Monolog handler type in XML config (xabbuh) +- `53b2c2b `_ #4139 cleaned up the code example (gondo) +- `b5c9f2a `_ #4138 fixed wrongly linked dependency (gondo) +- `b486b22 `_ #4131 Replaced old way of specifying http method by the new one (Baptouuuu) +- `93481d7 `_ #4120 Fix use mistakes (mbutkereit) +- `c0a0120 `_ #4119 Fix class name in ConsoleTerminateListener example (alOneh) +- `4629d8b `_ #4116 Fixed the code snippets for the expression language functions (stof) +- `d699255 `_ #4083 [Reference] field dependent empty_data option description (xabbuh) +- `3ffc20f `_ #4103 [Cookbook][Forms] fix PHP template file name (xabbuh) +- `234fa36 `_ #4095 Fix php template (piotrantosik) +- `01fb9f2 `_ #4093 See #4091 (dannykopping) +- `7d39b03 `_ #4079 Fixed typo in filesystem component (kohkimakimoto) +- `f0bde03 `_ #4075 Fixed typo in the yml validation (timothymctim) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `e9d317a `_ #4160 [Reference] consistent & complete config examples (xabbuh) +- `3e68ee7 `_ #4152 Adding 'attr' option to the Textarea options list (ronanguilloux) +- `c4eb628 `_ #4130 A set of small typos (Baptouuuu) +- `236d8e0 `_ #4137 fixed directive syntax (WouterJ) +- `6e90520 `_ #4135 [#3940] Adding php example for an array of emails (weaverryan) +- `b37ee61 `_ #4132 Use proper way to reference a doc page for legacy sessions (Baptouuuu) +- `189a123 `_ #4129 [Components] consistent & complete config examples (xabbuh) +- `5ab5246 `_ #4127 Second part of #3848 (WouterJ) +- `46f3108 `_ #4126 Rebased #3848 (WouterJ) +- `84e6e7f `_ #4114 [Book] consistent and complete config examples (xabbuh) +- `03fcab1 `_ #4112 [Contributing][Documentation] add order of translation formats (xabbuh) +- `650120a `_ #4002 added Github teams for the core team (fabpot) +- `10792c3 `_ #3959 [book][cache][tip] added cache annotations. (aitboudad) +- `ebaed21 `_ #3944 Update dbal.rst (bpiepiora) +- `16e346a `_ #3890 [Components][HttpFoundation] use a placeholder for the constructor arguments (xabbuh) +- `7bb4f34 `_ #4115 [Documentation] [Minor] Changes foobar.net in example.com (magnetik) +- `12d0b82 `_ #4113 tweaks to the new reverse proxy/load balancer chapter (xabbuh) +- `4cce133 `_ #4057 Update introduction.rst (carltondickson) +- `26141d6 `_ #4080 [Reference] order form type options alphabetically (xabbuh) +- `7806aa7 `_ #4117 Added a note about the automatic handling of the memory spool in the CLI (stof) +- `5959b6c `_ #4101 [Contributing] extended Symfony 2.4 maintenance (xabbuh) +- `e2056ad `_ #4072 [Contributing][Code] add note on Symfony SE forks for bug reports (xabbuh) +- `b8687dd `_ #4091 Put version into quotes, otherwise it fails in ZSH (dannykopping) +- `665c091 `_ #4087 Typo (tvlooy) +- `f95bbf3 `_ #4023 [Cookbook][Security] usage of a non-default entity manager in an entity user provider (xabbuh) +- `27b1003 `_ #4074 Fixed (again) a typo: Toolbet --> Toolbelt (javiereguiluz) +- `c97418f `_ #4073 Reworded bundle requirement (WouterJ) +- `e5d5eb8 `_ #4066 Update inherit_data_option.rst (Oylex) +- `9c08572 `_ #4064 Fixed typo on tag service (saro0h) + July, 2014 ---------- From f4d308a28450cdbebca0873fd4050ec275b5882a Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 28 Sep 2014 14:26:41 +0200 Subject: [PATCH 176/835] Created August CHANGELOG --- changelog.rst | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/changelog.rst b/changelog.rst index 057384a6dfe..a9996af5728 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,92 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +August, 2014 +------------ + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `bccb080 `_ #4140 [Cookbook][Logging] document multiple recipients in XML configs (xabbuh) +- `7a6e3d1 `_ #4150 Added the schema_filter option to the reference (peterrehm) +- `be90d8a `_ #4142 [Cookbook][Configuration] tweaks for the web server configuration chapter (xabbuh) +- `5379f54 `_ #4086 [Reference][Constraints] Added hint about attaching the expression constraint to a form field (peterrehm) +- `041105c `_ #3883 Removed redundant POST request exclusion info (ryancastle) +- `4f9fef6 `_ #4000 [Cookbook] add cookbook article for the server:run command (xabbuh) +- `4ea4dfe `_ #3915 [Cookbook][Configuration] documentation of Apache + PHP-FPM (xabbuh) +- `79cb4f1 `_ #4069 document the namespace alias (dbu) +- `08bed5f `_ #4128 Finished #3759 (WouterJ) +- `4d5adaa `_ #4125 Added link to JSFiddle example (WouterJ) +- `75bda4b `_ #4124 Rebased #3965 (WouterJ) +- `e2f13a4 `_ #4039 [DomCrawler] Added node name getter (fejese) +- `3f92d5f `_ #3966 [Cookbook][Controller] Add note about invokable controller services (kbond) +- `fdb8a32 `_ #3950 [Components][EventDispatcher] describe the usage of the RegisterListenersPass (xabbuh) +- `7e09383 `_ #3940 Updated docs for Monolog "swift" handler in cookbook. (phansys) +- `9d7c999 `_ #3895 [Validator] Support "maxSize" given in KiB (jeremy-derusse) +- `8adfe98 `_ #3894 Rewrote Extension & Configuration docs (WouterJ) +- `cafea43 `_ #3888 Updated the example used to explain page creation (javiereguiluz) +- `df0cf68 `_ #3885 [RFR] Added "How to Organize Configuration Files" cookbook (javiereguiluz) +- `41116da `_ #4081 [Components][ClassLoader] documentation for the ClassMapGenerator class (xabbuh) +- `2b9cb7c `_ #4076 Fixed description of session storage of the ApiKeyAuthenticator (peterrehm) +- `35a0f66 `_ #4102 Adding a new entry about reverse proxies in the framework (weaverryan) +- `95c2066 `_ #4096 labels in submit buttons + new screenshot (ricardclau) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `5fac303 `_ #4165 Update voters.rst (gerryvdm) +- `4882b99 `_ #4164 Fixed minor typos. (ahsio) +- `eaaa35a `_ #4145 Fix documentation for group_sequence_provider (giosh94mhz) +- `155c3e8 `_ #4153 [Reference] fix namespace in Expression constraint (xabbuh) +- `2c93aa5 `_ #4147 [Cookbook][Logging] add missing Monolog handler type in XML config (xabbuh) +- `53b2c2b `_ #4139 cleaned up the code example (gondo) +- `b5c9f2a `_ #4138 fixed wrongly linked dependency (gondo) +- `b486b22 `_ #4131 Replaced old way of specifying http method by the new one (Baptouuuu) +- `93481d7 `_ #4120 Fix use mistakes (mbutkereit) +- `c0a0120 `_ #4119 Fix class name in ConsoleTerminateListener example (alOneh) +- `4629d8b `_ #4116 Fixed the code snippets for the expression language functions (stof) +- `d699255 `_ #4083 [Reference] field dependent empty_data option description (xabbuh) +- `3ffc20f `_ #4103 [Cookbook][Forms] fix PHP template file name (xabbuh) +- `234fa36 `_ #4095 Fix php template (piotrantosik) +- `01fb9f2 `_ #4093 See #4091 (dannykopping) +- `8f3a261 `_ #4092 See #4091 (dannykopping) +- `7d39b03 `_ #4079 Fixed typo in filesystem component (kohkimakimoto) +- `f0bde03 `_ #4075 Fixed typo in the yml validation (timothymctim) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `e9d317a `_ #4160 [Reference] consistent & complete config examples (xabbuh) +- `3e68ee7 `_ #4152 Adding 'attr' option to the Textarea options list (ronanguilloux) +- `a7f3297 `_ #4136 [Reference] fix from suffix to prefix (xabbuh) +- `c4eb628 `_ #4130 A set of small typos (Baptouuuu) +- `236d8e0 `_ #4137 fixed directive syntax (WouterJ) +- `6e90520 `_ #4135 [#3940] Adding php example for an array of emails (weaverryan) +- `b37ee61 `_ #4132 Use proper way to reference a doc page for legacy sessions (Baptouuuu) +- `189a123 `_ #4129 [Components] consistent & complete config examples (xabbuh) +- `5ab5246 `_ #4127 Second part of #3848 (WouterJ) +- `46f3108 `_ #4126 Rebased #3848 (WouterJ) +- `84e6e7f `_ #4114 [Book] consistent and complete config examples (xabbuh) +- `03fcab1 `_ #4112 [Contributing][Documentation] add order of translation formats (xabbuh) +- `650120a `_ #4002 added Github teams for the core team (fabpot) +- `10792c3 `_ #3959 [book][cache][tip] added cache annotations. (aitboudad) +- `ebaed21 `_ #3944 Update dbal.rst (bpiepiora) +- `16e346a `_ #3890 [Components][HttpFoundation] use a placeholder for the constructor arguments (xabbuh) +- `7bb4f34 `_ #4115 [Documentation] [Minor] Changes foobar.net in example.com (magnetik) +- `12d0b82 `_ #4113 tweaks to the new reverse proxy/load balancer chapter (xabbuh) +- `4cce133 `_ #4057 Update introduction.rst (carltondickson) +- `26141d6 `_ #4080 [Reference] order form type options alphabetically (xabbuh) +- `7806aa7 `_ #4117 Added a note about the automatic handling of the memory spool in the CLI (stof) +- `5959b6c `_ #4101 [Contributing] extended Symfony 2.4 maintenance (xabbuh) +- `e2056ad `_ #4072 [Contributing][Code] add note on Symfony SE forks for bug reports (xabbuh) +- `b8687dd `_ #4091 Put version into quotes, otherwise it fails in ZSH (dannykopping) +- `665c091 `_ #4087 Typo (tvlooy) +- `f95bbf3 `_ #4023 [Cookbook][Security] usage of a non-default entity manager in an entity user provider (xabbuh) +- `27b1003 `_ #4074 Fixed (again) a typo: Toolbet --> Toolbelt (javiereguiluz) +- `c97418f `_ #4073 Reworded bundle requirement (WouterJ) +- `e5d5eb8 `_ #4066 Update inherit_data_option.rst (Oylex) +- `9c08572 `_ #4064 Fixed typo on tag service (saro0h) + July, 2014 ---------- From 93684e2c290bda41e4ec507df61872ca6f72c892 Mon Sep 17 00:00:00 2001 From: Philipp Rieber Date: Thu, 25 Sep 2014 15:18:50 +0200 Subject: [PATCH 177/835] [Cookbook][External Parameters] Enhance description of environment variables --- cookbook/configuration/external_parameters.rst | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/cookbook/configuration/external_parameters.rst b/cookbook/configuration/external_parameters.rst index 0f9c56501c5..58a72fd898c 100644 --- a/cookbook/configuration/external_parameters.rst +++ b/cookbook/configuration/external_parameters.rst @@ -14,9 +14,13 @@ Environment Variables --------------------- Symfony will grab any environment variable prefixed with ``SYMFONY__`` and -set it as a parameter in the service container. Double underscores are replaced -with a period, as a period is not a valid character in an environment variable -name. +set it as a parameter in the service container. Some transformations are +applied to the resulting parameter name: + +* ``SYMFONY__`` prefix is removed; +* Parameter name is lowercased; +* Double underscores are replaced with a period, as a period is not + a valid character in an environment variable name. For example, if you're using Apache, environment variables can be set using the following ``VirtualHost`` configuration: @@ -94,6 +98,14 @@ You can now reference these parameters wherever you need them. ) )); +.. note:: + + Even in debug mode, setting or changing an environment variable + requires your cache to be cleared to make the parameter available. + In debug mode, this is required since only a change to a configuration + file that is loaded by Symfony triggers your configuration to be + re-evaluated. + Constants --------- From 5e0b9bdc34ee60d1bb00e20d6949c03e38564371 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 30 Sep 2014 09:14:20 +0200 Subject: [PATCH 178/835] review effect --- components/var_dumper.rst | 76 ++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/components/var_dumper.rst b/components/var_dumper.rst index 01d8f237673..bcc1d66f139 100644 --- a/components/var_dumper.rst +++ b/components/var_dumper.rst @@ -6,18 +6,8 @@ The VarDumper Component ======================= The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. - Built on top, it provides a better ``dump()`` function, that you can use instead of ``var_dump()``, - *better* meaning: + Built on top, it provides a better ``dump()`` function that you can use instead of :phpfunction:`var_dump`. - - per object and resource types specialized view to e.g. filter out Doctrine noise - while dumping a single proxy entity, or get more insight on opened files with - ``stream_get_meta_data()``. - - configurable output format: HTML, command line with colors or JSON. - - ability to dump internal references, either soft ones (objects or resources) - or hard ones (``=&`` on arrays or objects properties). Repeated occurrences of - the same object/array/resource won't appear again and again anymore. Moreover, - you'll be able to inspected the reference structure of your data. - - ability to operate in the context of an output buffering handler. .. versionadded:: 2.6 The VarDumper component was introduced in Symfony 2.6. @@ -33,21 +23,34 @@ You can install the component in 2 different ways: The dump() function ------------------- -The VarDumper component creates a global ``dump()`` function that is auto-configured out of the box: +The VarDumper component creates a global ``dump()`` function that is configured out of the box: HTML or CLI output is automatically selected based on the current PHP SAPI. -``dump()`` is just a thin wrapper for ``\Symfony\Component\VarDumper\VarDumper::dump()`` so can you also use it directly. -You can change the behavior of this function by calling ``\Symfony\Component\VarDumper\VarDumper::setHandler($callable)``: -calls to ``dump()`` will then be forwarded to the ``$callable`` given as first argument. +The advantages of this function are: -Advanced usage + - per object and resource types specialized view to e.g. filter out Doctrine internals + while dumping a single proxy entity, or get more insight on opened files with + :phpfunction:`stream_get_meta_data()`. + - configurable output formats: HTML or colored command line output. + - ability to dump internal references, either soft ones (objects or resources) + or hard ones (``=&`` on arrays or objects properties). Repeated occurrences of + the same object/array/resource won't appear again and again anymore. Moreover, + you'll be able to inspect the reference structure of your data. + - ability to operate in the context of an output buffering handler. + +``dump()`` is just a thin wrapper for :method:`VarDumper::dump() ` +so can you also use it directly. You can change the behavior of this function by calling +:method:`VarDumper::setHandler($callable) `: +calls to ``dump()`` will then be forwarded to ``$callable``, given as first argument. + +Advanced Usage -------------- Cloners ~~~~~~~ A cloner is used to create an intermediate representation of any PHP variable. -Its output is a Data object that wraps this representation. +Its output is a :class:`Data ` object that wraps this representation. A cloner also applies limits when creating the representation, so that the corresponding Data object could represent only a subset of the cloned variable. @@ -61,26 +64,27 @@ Before cloning, you can configure the limits with:: $cloner->setMaxItems($number); $cloner->setMaxString($number); -These limits will be applied when calling ``->cloneVar()`` afterwise. +They will be applied when calling ``->cloneVar()`` afterwards. Casters ~~~~~~~ Objects and resources nested in a PHP variable are casted to arrays in the intermediate Data representation. You can tweak the array representation for each object/resource by hooking a Caster into this process. -The component already has a many casters for base PHP classes and other common classes. +The component already includes many casters for base PHP classes and other common classes. -If you want to build your how Caster, you can register one before cloning a PHP variable. +If you want to build your own Caster, you can register one before cloning a PHP variable. Casters are registered using either a Cloner's constructor or its ``addCasters()`` method:: $myCasters = array(...); $cloner = new PhpCloner($myCasters); -or:: + // or $cloner->addCasters($myCasters); -The provided ``$myCasters`` argument is an array that maps a class, an interface or a resource type to a callable:: +The provided ``$myCasters`` argument is an array that maps a class, +an interface or a resource type to a callable:: $myCasters = array( 'FooClass' => $myFooClassCallableCaster, @@ -96,26 +100,24 @@ Several casters can also be registered for the same resource type/class/interfac They are called in registration order. Casters are responsible for returning the properties of the object or resource being cloned in an array. -They are callables that accept four arguments:: - - /** - * A caster not doing anything. - * - * @param object|resource $object The object or resource being casted. - * @param array $array An array modelled for objects after PHP's native `(array)` cast operator. - * @param Stub $stub A Cloner\Stub object representing the main properties of $object (class, type, etc.). - * @param bool $isNested True/false when the caster is called nested is a structure or not. - * - * @return array The properties of $object casted in an array. - */ - function myCaster($origValue, $array, $stub, $isNested) +They are callables that accept four arguments: + + - the object or resource being casted, + - an array modelled for objects after PHP's native ``(array)`` cast operator, + - a :class:`Stub ` object representing + the main properties of the object (class, type, etc.), + - true/false when the caster is called nested is a structure or not. + +Here is a simple caster not doing anything:: + + function myCaster($object, $array, $stub, $isNested) { - // Here, populate/alter $array to your needs. + // ... populate/alter $array to your needs return $array; } -For objects, the ``$array`` parameter comes pre-populated with PHP's native ``(array)`` casting operator, +For objects, the ``$array`` parameter comes pre-populated with PHP's native ``(array)`` casting operator or with the return value of ``$object->__debugInfo()`` if the magic method exists. Then, the return value of one Caster is given as argument to the next Caster in the chain. From 7dc6e5cc24c522cab26cdcf38d954c26cc7a5c54 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 30 Sep 2014 12:05:55 +0200 Subject: [PATCH 179/835] Reduce line length --- components/var_dumper.rst | 90 ++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/components/var_dumper.rst b/components/var_dumper.rst index bcc1d66f139..db025c7cbfa 100644 --- a/components/var_dumper.rst +++ b/components/var_dumper.rst @@ -5,9 +5,9 @@ The VarDumper Component ======================= - The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. - Built on top, it provides a better ``dump()`` function that you can use instead of :phpfunction:`var_dump`. - + The VarDumper component provides mechanisms for walking through any + arbitrary PHP variable. Built on top, it provides a better ``dump()`` + function that you can use instead of :phpfunction:`var_dump`. .. versionadded:: 2.6 The VarDumper component was introduced in Symfony 2.6. @@ -23,23 +23,26 @@ You can install the component in 2 different ways: The dump() function ------------------- -The VarDumper component creates a global ``dump()`` function that is configured out of the box: -HTML or CLI output is automatically selected based on the current PHP SAPI. +The VarDumper component creates a global ``dump()`` function that is +configured out of the box: HTML or CLI output is automatically selected based +on the current PHP SAPI. The advantages of this function are: - - per object and resource types specialized view to e.g. filter out Doctrine internals - while dumping a single proxy entity, or get more insight on opened files with - :phpfunction:`stream_get_meta_data()`. + - per object and resource types specialized view to e.g. filter out + Doctrine internals while dumping a single proxy entity, or get more + insight on opened files with :phpfunction:`stream_get_meta_data()`. - configurable output formats: HTML or colored command line output. - - ability to dump internal references, either soft ones (objects or resources) - or hard ones (``=&`` on arrays or objects properties). Repeated occurrences of - the same object/array/resource won't appear again and again anymore. Moreover, - you'll be able to inspect the reference structure of your data. + - ability to dump internal references, either soft ones (objects or + resources) or hard ones (``=&`` on arrays or objects properties). + Repeated occurrences of the same object/array/resource won't appear + again and again anymore. Moreover, you'll be able to inspect the + reference structure of your data. - ability to operate in the context of an output buffering handler. -``dump()`` is just a thin wrapper for :method:`VarDumper::dump() ` -so can you also use it directly. You can change the behavior of this function by calling +``dump()`` is just a thin wrapper for :method:`VarDumper::dump() +` so can you also use it directly. +You can change the behavior of this function by calling :method:`VarDumper::setHandler($callable) `: calls to ``dump()`` will then be forwarded to ``$callable``, given as first argument. @@ -50,9 +53,10 @@ Cloners ~~~~~~~ A cloner is used to create an intermediate representation of any PHP variable. -Its output is a :class:`Data ` object that wraps this representation. -A cloner also applies limits when creating the representation, so that the corresponding -Data object could represent only a subset of the cloned variable. +Its output is a :class:`Data ` +object that wraps this representation. A cloner also applies limits when +creating the representation, so that the corresponding Data object could +represent only a subset of the cloned variable. You can create a Data object this way:: @@ -69,12 +73,14 @@ They will be applied when calling ``->cloneVar()`` afterwards. Casters ~~~~~~~ -Objects and resources nested in a PHP variable are casted to arrays in the intermediate Data representation. -You can tweak the array representation for each object/resource by hooking a Caster into this process. -The component already includes many casters for base PHP classes and other common classes. +Objects and resources nested in a PHP variable are casted to arrays in the +intermediate Data representation. You can tweak the array representation for +each object/resource by hooking a Caster into this process. The component +already includes many casters for base PHP classes and other common classes. -If you want to build your own Caster, you can register one before cloning a PHP variable. -Casters are registered using either a Cloner's constructor or its ``addCasters()`` method:: +If you want to build your own Caster, you can register one before cloning +a PHP variable. Casters are registered using either a Cloner's constructor +or its ``addCasters()`` method:: $myCasters = array(...); $cloner = new PhpCloner($myCasters); @@ -91,21 +97,23 @@ an interface or a resource type to a callable:: ':bar resource' => $myBarResourceCallableCaster, ); -As you can notice, resource types are prefixed by a ``:`` to prevent colliding with a class name. +As you can notice, resource types are prefixed by a ``:`` to prevent +colliding with a class name. -Because an object has one main class and potentially many parent classes or interfaces, -many casters can be applied to one object. In this case, casters are called one after the other, -starting from casters bound to the interfaces, the parents classes and then the main class. -Several casters can also be registered for the same resource type/class/interface. +Because an object has one main class and potentially many parent classes +or interfaces, many casters can be applied to one object. In this case, +casters are called one after the other, starting from casters bound to the +interfaces, the parents classes and then the main class. Several casters +can also be registered for the same resource type/class/interface. They are called in registration order. -Casters are responsible for returning the properties of the object or resource being cloned in an array. -They are callables that accept four arguments: +Casters are responsible for returning the properties of the object orresource +being cloned in an array. They are callables that accept four arguments: - the object or resource being casted, - an array modelled for objects after PHP's native ``(array)`` cast operator, - - a :class:`Stub ` object representing - the main properties of the object (class, type, etc.), + - a :class:`Stub ` object + representing the main properties of the object (class, type, etc.), - true/false when the caster is called nested is a structure or not. Here is a simple caster not doing anything:: @@ -117,18 +125,22 @@ Here is a simple caster not doing anything:: return $array; } -For objects, the ``$array`` parameter comes pre-populated with PHP's native ``(array)`` casting operator -or with the return value of ``$object->__debugInfo()`` if the magic method exists. -Then, the return value of one Caster is given as argument to the next Caster in the chain. +For objects, the ``$array`` parameter comes pre-populated with PHP's native +``(array)`` casting operator or with the return value of ``$object->__debugInfo()`` +if the magic method exists. Then, the return value of one Caster is given +as argument to the next Caster in the chain. -When casting with the ``(array)`` operator, PHP prefixes protected properties with a ``\0*\0`` -and private ones with the class owning the property: e.g. ``\0Foobar\0`` prefixes all private properties -of objects of type Foobar. Casters follow this convention and add two more prefixes: ``\0~\0`` is used for -virtual properties and ``\0+\0`` for dynamic ones (runtime added properties not in the class declaration). +When casting with the ``(array)`` operator, PHP prefixes protected properties +with a ``\0*\0`` and private ones with the class owning the property: +e.g. ``\0Foobar\0`` prefixes all private properties of objects of type Foobar. +Casters follow this convention and add two more prefixes: ``\0~\0`` is used +for virtual properties and ``\0+\0`` for dynamic ones (runtime added +properties not in the class declaration). .. note:: - Although you can, it is best advised not to alter the state of an object while casting it in a Caster. + Although you can, it is best advised not to alter the state of an object + while casting it in a Caster. Dumpers ~~~~~~~ From ac4d9cd3ed24bd209a56555a42d44c00cb04d5a9 Mon Sep 17 00:00:00 2001 From: Claudio Galdiolo Date: Mon, 29 Sep 2014 00:37:43 -0400 Subject: [PATCH 180/835] Double-quotes instead of single quotes (UnexpectedValueException in Windows 8) --- cookbook/workflow/new_project_git.rst | 2 +- quick_tour/the_big_picture.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/workflow/new_project_git.rst b/cookbook/workflow/new_project_git.rst index c40cba6d53f..7551e87622d 100644 --- a/cookbook/workflow/new_project_git.rst +++ b/cookbook/workflow/new_project_git.rst @@ -26,7 +26,7 @@ git repository: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition path/ '~2.3' + $ php composer.phar create-project symfony/framework-standard-edition path/ "~2.3" Composer will now download the Standard Distribution along with all of the required vendor libraries. For more information about downloading Symfony using diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 7d4fd3f4406..7d6728775fa 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -20,7 +20,7 @@ directory: .. code-block:: bash - $ composer create-project symfony/framework-standard-edition myproject/ '~2.3' + $ composer create-project symfony/framework-standard-edition myproject/ "~2.3" .. note:: From e5d432c28c5c68f60dab5d28e1f2e68d26b560e8 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 1 Oct 2014 09:37:30 -0400 Subject: [PATCH 181/835] [#4047] Re-adding the versionadded for the info() method --- components/config/definition.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/config/definition.rst b/components/config/definition.rst index 3b703559f1d..f0f45f4e585 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -290,6 +290,10 @@ method. The info will be printed as a comment when dumping the configuration tree. +.. versionadded:: 2.6 + Since Symfony 2.6, the info will also be added to the exception message + when an invalid type is given. + Optional Sections ----------------- From ad6a340e8e8eacce9fe2f18769b5100299b93d3c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 5 Sep 2014 12:05:09 +0200 Subject: [PATCH 182/835] [Components][Process] `mustRun()` documentation --- components/process.rst | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/components/process.rst b/components/process.rst index a2d8dc67e4d..ef77f6078da 100644 --- a/components/process.rst +++ b/components/process.rst @@ -31,7 +31,7 @@ a command in a sub-process:: throw new \RuntimeException($process->getErrorOutput()); } - print $process->getOutput(); + echo $process->getOutput(); The component takes care of the subtle differences between the different platforms when executing the command. @@ -50,6 +50,27 @@ the contents of the output and :method:`Symfony\\Component\\Process\\Process::clearErrorOutput` clears the contents of the error output. +.. versionadded:: 2.5 + The ``mustRun()`` method was introduced in Symfony 2.5. + +The ``mustRun()`` method is identical to ``run()``, except that it will throw +a :class:`Symfony\\Component\\Process\\Exception\\ProcessFailedException` +if the process couldn't be executed successfully (i.e. the process exited +with a non-zero code):: + + use Symfony\Component\Process\Exception\ProcessFailedException; + use Symfony\Component\Process\Process; + + $process = new Process('ls -lsa'); + + try { + $process->mustRun(); + + echo $process->getOutput(); + } catch (ProcessFailedException $e) { + echo $e->getMessage(); + } + Getting real-time Process Output -------------------------------- @@ -218,17 +239,17 @@ Process Idle Timeout .. versionadded:: 2.4 The :method:`Symfony\\Component\\Process\\Process::setIdleTimeout` method was introduced in Symfony 2.4. - + In contrast to the timeout of the previous paragraph, the idle timeout only considers the time since the last output was produced by the process:: use Symfony\Component\Process\Process; - + $process = new Process('something-with-variable-runtime'); $process->setTimeout(3600); $process->setIdleTimeout(60); $process->run(); - + In the case above, a process is considered timed out, when either the total runtime exceeds 3600 seconds, or the process does not produce any output for 60 seconds. From d68cd093815782fb034f3df4afe4e8ec80c01e6a Mon Sep 17 00:00:00 2001 From: Kristof Van Cauwenbergh Date: Thu, 2 Oct 2014 11:19:05 +0200 Subject: [PATCH 183/835] change misleading language identifier --- reference/forms/types/language.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reference/forms/types/language.rst b/reference/forms/types/language.rst index 7e18fcbcf39..0a03cfdd8c4 100644 --- a/reference/forms/types/language.rst +++ b/reference/forms/types/language.rst @@ -8,7 +8,7 @@ The ``language`` type is a subset of the ``ChoiceType`` that allows the user to select from a large list of languages. As an added bonus, the language names are displayed in the language of the user. -The "value" for each language is the *Unicode language identifier* +The "value" for each language is the *Unicode language identifier* used in the `International Components for Unicode`_ (e.g. ``fr`` or ``zh-Hant``). .. note:: @@ -106,3 +106,5 @@ The actual default value of this option depends on other field options: .. include:: /reference/forms/types/options/read_only.rst.inc .. include:: /reference/forms/types/options/required.rst.inc + +.. _`International Components for Unicode`: http://site.icu-project.org \ No newline at end of file From 12cccc80c0a352fa7e32ceb2a3b3ac18c27c7233 Mon Sep 17 00:00:00 2001 From: Kristof Van Cauwenbergh Date: Thu, 2 Oct 2014 11:22:38 +0200 Subject: [PATCH 184/835] changed dash to underscore --- reference/forms/types/language.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/forms/types/language.rst b/reference/forms/types/language.rst index 0a03cfdd8c4..c4875857eb5 100644 --- a/reference/forms/types/language.rst +++ b/reference/forms/types/language.rst @@ -9,7 +9,7 @@ to select from a large list of languages. As an added bonus, the language names are displayed in the language of the user. The "value" for each language is the *Unicode language identifier* used in the `International Components for Unicode`_ -(e.g. ``fr`` or ``zh-Hant``). +(e.g. ``fr`` or ``zh_Hant``). .. note:: @@ -107,4 +107,4 @@ The actual default value of this option depends on other field options: .. include:: /reference/forms/types/options/required.rst.inc -.. _`International Components for Unicode`: http://site.icu-project.org \ No newline at end of file +.. _`International Components for Unicode`: http://site.icu-project.org From db85c67f7b7da1eb8fcd0171a7a40dbfc3fc5803 Mon Sep 17 00:00:00 2001 From: Kristof Van Cauwenbergh Date: Thu, 2 Oct 2014 13:19:40 +0200 Subject: [PATCH 185/835] formatting to follow 72 characters line limit --- reference/forms/types/language.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/forms/types/language.rst b/reference/forms/types/language.rst index c4875857eb5..120cb37b11c 100644 --- a/reference/forms/types/language.rst +++ b/reference/forms/types/language.rst @@ -8,8 +8,8 @@ The ``language`` type is a subset of the ``ChoiceType`` that allows the user to select from a large list of languages. As an added bonus, the language names are displayed in the language of the user. -The "value" for each language is the *Unicode language identifier* used in the `International Components for Unicode`_ -(e.g. ``fr`` or ``zh_Hant``). +The "value" for each language is the *Unicode language identifier* used in +the `International Components for Unicode`_ (e.g. ``fr`` or ``zh_Hant``). .. note:: From 042dcf9c67396224ffa400f9264500428ab819d8 Mon Sep 17 00:00:00 2001 From: Nicolas Assing Date: Fri, 25 Jul 2014 10:10:57 +0200 Subject: [PATCH 186/835] Replace addViolationAt (deprecated) by buildViolation --- reference/constraints/Callback.rst | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/reference/constraints/Callback.rst b/reference/constraints/Callback.rst index 36b55e97910..43a29e4eb3e 100644 --- a/reference/constraints/Callback.rst +++ b/reference/constraints/Callback.rst @@ -114,12 +114,10 @@ those errors should be attributed:: // check if the name is actually a fake name if (in_array($this->getFirstName(), $fakeNames)) { - $context->addViolationAt( - 'firstName', - 'This name sounds totally fake!', - array(), - null - ); + $context->buildViolation('This name sounds totally fake!') + ->atPath('firstName') + ->addViolation() + ; } } } @@ -137,12 +135,10 @@ have access to the object instance, they receive the object as the first argumen // check if the name is actually a fake name if (in_array($object->getFirstName(), $fakeNames)) { - $context->addViolationAt( - 'firstName', - 'This name sounds totally fake!', - array(), - null - ); + $context->buildViolation('This name sounds totally fake!') + ->atPath('firstName') + ->addViolation() + ; } } From f4380ed2a973a7e198949e9400057ff994f53323 Mon Sep 17 00:00:00 2001 From: lashae Date: Fri, 22 Aug 2014 11:25:07 +0300 Subject: [PATCH 187/835] Update Callback.rst "Symfony\Component\Validator\ExecutionContextInterface" namespace is deprecated, new namespace should be "Symfony\Component\Validator\Context\ExecutionContextInterface" --- reference/constraints/Callback.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/reference/constraints/Callback.rst b/reference/constraints/Callback.rst index 43a29e4eb3e..1947a37089a 100644 --- a/reference/constraints/Callback.rst +++ b/reference/constraints/Callback.rst @@ -50,7 +50,7 @@ Configuration namespace Acme\BlogBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; - use Symfony\Component\Validator\ExecutionContextInterface; + use Symfony\Component\Validator\Context\ExecutionContextInterface; class Author { @@ -100,7 +100,7 @@ can set "violations" directly on this object and determine to which field those errors should be attributed:: // ... - use Symfony\Component\Validator\ExecutionContextInterface; + use Symfony\Component\Validator\Context\ExecutionContextInterface; class Author { @@ -152,7 +152,7 @@ your validation function is ``Vendor\Package\Validator::validate()``:: namespace Vendor\Package; - use Symfony\Component\Validator\ExecutionContextInterface; + use Symfony\Component\Validator\Context\ExecutionContextInterface; class Validator { @@ -270,7 +270,7 @@ callback method: * A closure. -Concrete callbacks receive an :class:`Symfony\\Component\\Validator\\ExecutionContextInterface` +Concrete callbacks receive an :class:`Symfony\\Component\\Validator\\Context\\ExecutionContextInterface` instance as only argument. Static or closure callbacks receive the validated object as the first argument From 5dfe499c0dbeaf9673551e75b3ff1eedb1542722 Mon Sep 17 00:00:00 2001 From: Rootie Date: Fri, 8 Aug 2014 07:54:47 +0200 Subject: [PATCH 188/835] Don't use deprecated functions in Callback.rst The addViolationAt function is marked as deprecated and should be replaced by buildViolation according to the Symfony update guides. --- reference/constraints/Callback.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/reference/constraints/Callback.rst b/reference/constraints/Callback.rst index 1947a37089a..7e41e4bda9c 100644 --- a/reference/constraints/Callback.rst +++ b/reference/constraints/Callback.rst @@ -116,8 +116,7 @@ those errors should be attributed:: if (in_array($this->getFirstName(), $fakeNames)) { $context->buildViolation('This name sounds totally fake!') ->atPath('firstName') - ->addViolation() - ; + ->addViolation(); } } } From 70c5ca12afb5e5acc0c611c5e6bd65216479b144 Mon Sep 17 00:00:00 2001 From: Rootie Date: Sat, 9 Aug 2014 00:02:22 +0200 Subject: [PATCH 189/835] Update custom_contraint.rst to meet the new 2.5 api --- cookbook/validation/custom_constraint.rst | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/cookbook/validation/custom_constraint.rst b/cookbook/validation/custom_constraint.rst index 5cfd2312759..bf4ecc2a410 100644 --- a/cookbook/validation/custom_constraint.rst +++ b/cookbook/validation/custom_constraint.rst @@ -65,9 +65,9 @@ The validator class is also simple, and only has one required method ``validate( public function validate($value, Constraint $constraint) { if (!preg_match('/^[a-zA-Za0-9]+$/', $value, $matches)) { - $this->context->addViolation( - $constraint->message, - array('%string%' => $value) + $this->context->buildViolation($constraint->message) + ->setParameter('%string%', $value) + ->addViolation(); ); } } @@ -76,11 +76,17 @@ The validator class is also simple, and only has one required method ``validate( .. note:: The ``validate`` method does not return a value; instead, it adds violations - to the validator's ``context`` property with an ``addViolation`` method - call if there are validation failures. Therefore, a value could be considered - as being valid if it causes no violations to be added to the context. - The first parameter of the ``addViolation`` call is the error message to - use for that violation. + to the validator's ``context`` property. Therefore, a value could be considered + as being valid if it causes no violations to be added to the context.The + violation is constructed and added to the context using a + ``ConstraintViolationBuilder`` returned by the ``buildViolation`` method + call. The parameter given to the ``buildViolation`` call is the error message + to use for that violation. The ``addViolation`` method call finally adds the + violation to the context. + +.. versionadded:: 2.5 + The ``buildViolation`` method was added in Symfony 2.5. For usage examples with + older Symfony versions, see the corresponding versions of this documentation page. Using the new Validator ----------------------- From e658b568ceb667aeb762f0e73fbeeafc4fa7c70e Mon Sep 17 00:00:00 2001 From: Rootie Date: Sat, 9 Aug 2014 00:05:33 +0200 Subject: [PATCH 190/835] added a versionadded comment to Callback.rst --- reference/constraints/Callback.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reference/constraints/Callback.rst b/reference/constraints/Callback.rst index 7e41e4bda9c..a046095831a 100644 --- a/reference/constraints/Callback.rst +++ b/reference/constraints/Callback.rst @@ -121,6 +121,10 @@ those errors should be attributed:: } } +.. versionadded:: 2.5 + The ``buildViolation`` method was added in Symfony 2.5. For usage examples with + older Symfony versions, see the corresponding versions of this documentation page. + Static Callbacks ---------------- From 280440ea734e77cfe7ccfc1b100ad81940cd402c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 15 Sep 2014 20:36:35 -0400 Subject: [PATCH 191/835] Adding details about the 2.4 API as comments --- cookbook/validation/custom_constraint.rst | 30 ++++++++++++++--------- reference/constraints/Callback.rst | 27 ++++++++++++++++++-- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/cookbook/validation/custom_constraint.rst b/cookbook/validation/custom_constraint.rst index bf4ecc2a410..fe46e79d01d 100644 --- a/cookbook/validation/custom_constraint.rst +++ b/cookbook/validation/custom_constraint.rst @@ -65,28 +65,34 @@ The validator class is also simple, and only has one required method ``validate( public function validate($value, Constraint $constraint) { if (!preg_match('/^[a-zA-Za0-9]+$/', $value, $matches)) { + // If you're using the new 2.5 validation API (you probably are!) $this->context->buildViolation($constraint->message) ->setParameter('%string%', $value) ->addViolation(); ); + + // If you're using the old 2.4 validation API + /* + $this->context->addViolation( + $constraint->message, + array('%string%' => $value) + ); + */ } } } -.. note:: - - The ``validate`` method does not return a value; instead, it adds violations - to the validator's ``context`` property. Therefore, a value could be considered - as being valid if it causes no violations to be added to the context.The - violation is constructed and added to the context using a - ``ConstraintViolationBuilder`` returned by the ``buildViolation`` method - call. The parameter given to the ``buildViolation`` call is the error message - to use for that violation. The ``addViolation`` method call finally adds the - violation to the context. +Inside ``validate``, you don't need to return a value. Instead, you add violations +to the validator's ``context`` property and a value will be considered valid +if it causes no violations. The ``buildViolation`` takes the error message +as its argument and returns an instance of +:class:`Symfony\\Component\\Validator\\Violation\\ConstraintViolationBuilder` +The ``addViolation`` method call finally adds the violation to the context. .. versionadded:: 2.5 - The ``buildViolation`` method was added in Symfony 2.5. For usage examples with - older Symfony versions, see the corresponding versions of this documentation page. + The ``buildViolation`` method was added in Symfony 2.5. For usage examples + with older Symfony versions, see the corresponding versions of this documentation + page. Using the new Validator ----------------------- diff --git a/reference/constraints/Callback.rst b/reference/constraints/Callback.rst index a046095831a..e0a21e4b2e0 100644 --- a/reference/constraints/Callback.rst +++ b/reference/constraints/Callback.rst @@ -51,6 +51,8 @@ Configuration use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Context\ExecutionContextInterface; + // if you're using the older 2.4 validation API, you'll need this instead + // use Symfony\Component\Validator\ExecutionContextInterface; class Author { @@ -101,6 +103,8 @@ those errors should be attributed:: // ... use Symfony\Component\Validator\Context\ExecutionContextInterface; + // if you're using the older 2.4 validation API, you'll need this instead + // use Symfony\Component\Validator\ExecutionContextInterface; class Author { @@ -114,16 +118,26 @@ those errors should be attributed:: // check if the name is actually a fake name if (in_array($this->getFirstName(), $fakeNames)) { + // If you're using the new 2.5 validation API (you probably are!) $context->buildViolation('This name sounds totally fake!') ->atPath('firstName') ->addViolation(); + + // If you're using the old 2.4 validation API + /* + $context->addViolationAt( + 'firstName', + 'This name sounds totally fake!' + ); + */ } } } .. versionadded:: 2.5 - The ``buildViolation`` method was added in Symfony 2.5. For usage examples with - older Symfony versions, see the corresponding versions of this documentation page. + The ``buildViolation`` method was added in Symfony 2.5. For usage examples + with older Symfony versions, see the corresponding versions of this documentation + page. Static Callbacks ---------------- @@ -138,10 +152,17 @@ have access to the object instance, they receive the object as the first argumen // check if the name is actually a fake name if (in_array($object->getFirstName(), $fakeNames)) { + // If you're using the new 2.5 validation API (you probably are!) $context->buildViolation('This name sounds totally fake!') ->atPath('firstName') ->addViolation() ; + + // If you're using the old 2.4 validation API + $context->addViolationAt( + 'firstName', + 'This name sounds totally fake!' + ); } } @@ -156,6 +177,8 @@ your validation function is ``Vendor\Package\Validator::validate()``:: namespace Vendor\Package; use Symfony\Component\Validator\Context\ExecutionContextInterface; + // if you're using the older 2.4 validation API, you'll need this instead + // use Symfony\Component\Validator\ExecutionContextInterface; class Validator { From 279d8d6bcf1a971e64a8af0d5021b7dba881cf37 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 18 Sep 2014 10:19:26 -0400 Subject: [PATCH 192/835] Adding a section about keeping BC in a re-usable bundle --- cookbook/bundles/best_practices.rst | 37 +++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index d9c5fa88801..54e719db215 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -328,6 +328,43 @@ semantic configuration described in the cookbook. If you are defining services, they should also be prefixed with the bundle alias. +Custom Validation Constraints +----------------------------- + +Starting with Symfony 2.5, a new Validation API was introduced. In fact, +there are 3 modes, which the user can configure in their project: + +* 2.4: the original 2.4 and earlier validation API; +* 2.5: the new 2.5 and later validation API; +* 2.5-BC: the new 2.5 API with a backwards-compatible layer so that the + 2.4 API still works. This is only available in PHP 5.3.9+. + +As a bundle author, you'll want to support *both* API's, since some users +may still be using the 2.4 API. Specifically, if your bundle adds a violation +directly to the :class:`Symfony\Component\Validator\Context\ExecutionContext` +(e.g. like in a custom validation constraint), you'll need to check for which +API is being used. The following code, would work for *all* users:: + + class ContainsAlphanumericValidator extends ConstraintValidator + { + public function validate($value, Constraint $constraint) + { + if ($this->context instanceof ExecutionContextInterface) { + // the 2.5 API + $this->context->buildViolation($constraint->message) + ->setParameter('%string%', $value) + ->addViolation(); + ); + } else { + // the 2.4 API + $this->context->addViolation( + $constraint->message, + array('%string%' => $value) + ); + } + } + } + Learn more from the Cookbook ---------------------------- From f97ba7ae29933e4487cbeeabb3143c1e4e6fc528 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 18 Sep 2014 10:19:44 -0400 Subject: [PATCH 193/835] Fixes thanks to @xabbuh --- cookbook/validation/custom_constraint.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/validation/custom_constraint.rst b/cookbook/validation/custom_constraint.rst index fe46e79d01d..caf582a4eee 100644 --- a/cookbook/validation/custom_constraint.rst +++ b/cookbook/validation/custom_constraint.rst @@ -84,9 +84,9 @@ The validator class is also simple, and only has one required method ``validate( Inside ``validate``, you don't need to return a value. Instead, you add violations to the validator's ``context`` property and a value will be considered valid -if it causes no violations. The ``buildViolation`` takes the error message -as its argument and returns an instance of -:class:`Symfony\\Component\\Validator\\Violation\\ConstraintViolationBuilder` +if it causes no violations. The ``buildViolation`` method takes the error +message as its argument and returns an instance of +:class:`Symfony\\Component\\Validator\\Violation\\ConstraintViolationBuilderInterface`. The ``addViolation`` method call finally adds the violation to the context. .. versionadded:: 2.5 From 94fc520c79839c9dfa3b06abcd8f5bf703ace561 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 2 Oct 2014 08:48:38 -0400 Subject: [PATCH 194/835] Minor tweaks and a missing location thanks to xabbuh and WouterJ --- cookbook/bundles/best_practices.rst | 7 ++++++- cookbook/form/unit_testing.rst | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 54e719db215..bc97194336d 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -341,10 +341,15 @@ there are 3 modes, which the user can configure in their project: As a bundle author, you'll want to support *both* API's, since some users may still be using the 2.4 API. Specifically, if your bundle adds a violation -directly to the :class:`Symfony\Component\Validator\Context\ExecutionContext` +directly to the :class:`Symfony\\Component\\Validator\\Context\\ExecutionContext` (e.g. like in a custom validation constraint), you'll need to check for which API is being used. The following code, would work for *all* users:: + use Symfony\Component\Validator\ConstraintValidator; + use Symfony\Component\Validator\Constraint; + use Symfony\Component\Validator\Context\ExecutionContextInterface; + // ... + class ContainsAlphanumericValidator extends ConstraintValidator { public function validate($value, Constraint $constraint) diff --git a/cookbook/form/unit_testing.rst b/cookbook/form/unit_testing.rst index 944ed25b372..a6d2cd458ce 100644 --- a/cookbook/form/unit_testing.rst +++ b/cookbook/form/unit_testing.rst @@ -185,7 +185,7 @@ on other extensions. You need add those extensions to the factory object:: { parent::setUp(); - $validator = $this->getMock('\Symfony\Component\Validator\ValidatorInterface'); + $validator = $this->getMock('\Symfony\Component\Validator\Validator\ValidatorInterface'); $validator->method('validate')->will($this->returnValue(new ConstraintViolationList())); $this->factory = Forms::createFormFactoryBuilder() From 9874d8e5d533cf40bd2e182fa53ee4271e641f18 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 2 Oct 2014 08:53:32 -0400 Subject: [PATCH 195/835] [#4233][#4094] Making validateValue and validate changes --- book/validation.rst | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/book/validation.rst b/book/validation.rst index 4ccb264fe8a..73415ee8d97 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -822,9 +822,13 @@ With this configuration, there are three validation groups: fields only. To tell the validator to use a specific group, pass one or more group names -as the second argument to the ``validate()`` method:: +as the third argument to the ``validate()`` method:: - $errors = $validator->validate($author, array('registration')); + // If you're using the new 2.5 validation API (you probably are!) + $errors = $validator->validate($author, null, array('registration')); + + // If you're using the old 2.4 validation API + // $errors = $validator->validate($author, array('registration')); If no groups are specified, all constraints that belong in group ``Default`` will be applied. @@ -1189,10 +1193,19 @@ it looks like this:: $emailConstraint->message = 'Invalid email address'; // use the validator to validate the value + // If you're using the new 2.5 validation API (you probably are!) + $errorList = $this->get('validator')->validate( + $email, + $emailConstraint + ); + + // If you're using the old 2.4 validation API + /* $errorList = $this->get('validator')->validateValue( $email, $emailConstraint ); + */ if (count($errorList) == 0) { // this IS a valid email address, do something @@ -1206,13 +1219,13 @@ it looks like this:: // ... } -By calling ``validateValue`` on the validator, you can pass in a raw value and +By calling ``validate`` on the validator, you can pass in a raw value and the constraint object that you want to validate that value against. A full list of the available constraints - as well as the full class name for each constraint - is available in the :doc:`constraints reference ` section . -The ``validateValue`` method returns a :class:`Symfony\\Component\\Validator\\ConstraintViolationList` +The ``validate`` method returns a :class:`Symfony\\Component\\Validator\\ConstraintViolationList` object, which acts just like an array of errors. Each error in the collection is a :class:`Symfony\\Component\\Validator\\ConstraintViolation` object, which holds the error message on its ``getMessage`` method. From 036edcbcdcf9c6b48cc9c30dcff12377707874c6 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 1 Oct 2014 09:48:39 +0200 Subject: [PATCH 196/835] doc for Dumpers --- components/var_dumper.rst | 111 ++++++++++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 27 deletions(-) diff --git a/components/var_dumper.rst b/components/var_dumper.rst index db025c7cbfa..b001162ed8a 100644 --- a/components/var_dumper.rst +++ b/components/var_dumper.rst @@ -17,8 +17,8 @@ Installation You can install the component in 2 different ways: -* :doc:`Install it via Composer ` (``symfony/var-dumper`` on `Packagist`_); -* Use the official Git repository (https://github.com/symfony/VarDumper). +- :doc:`Install it via Composer ` (``symfony/var-dumper`` on `Packagist`_); +- Use the official Git repository (https://github.com/symfony/var-dumper). The dump() function ------------------- @@ -29,19 +29,20 @@ on the current PHP SAPI. The advantages of this function are: - - per object and resource types specialized view to e.g. filter out - Doctrine internals while dumping a single proxy entity, or get more - insight on opened files with :phpfunction:`stream_get_meta_data()`. - - configurable output formats: HTML or colored command line output. - - ability to dump internal references, either soft ones (objects or - resources) or hard ones (``=&`` on arrays or objects properties). - Repeated occurrences of the same object/array/resource won't appear - again and again anymore. Moreover, you'll be able to inspect the - reference structure of your data. - - ability to operate in the context of an output buffering handler. - -``dump()`` is just a thin wrapper for :method:`VarDumper::dump() -` so can you also use it directly. +- per object and resource types specialized view to e.g. filter out + Doctrine internals while dumping a single proxy entity, or get more + insight on opened files with :phpfunction:`stream_get_meta_data()`. +- configurable output formats: HTML or colored command line output. +- ability to dump internal references, either soft ones (objects or + resources) or hard ones (``=&`` on arrays or objects properties). + Repeated occurrences of the same object/array/resource won't appear + again and again anymore. Moreover, you'll be able to inspect the + reference structure of your data. +- ability to operate in the context of an output buffering handler. + +``dump()`` is just a thin wrapper for +:method:`VarDumper::dump() ` +so can you also use it directly. You can change the behavior of this function by calling :method:`VarDumper::setHandler($callable) `: calls to ``dump()`` will then be forwarded to ``$callable``, given as first argument. @@ -53,12 +54,13 @@ Cloners ~~~~~~~ A cloner is used to create an intermediate representation of any PHP variable. -Its output is a :class:`Data ` +Its output is a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object that wraps this representation. A cloner also applies limits when creating the representation, so that the corresponding Data object could represent only a subset of the cloned variable. -You can create a Data object this way:: +You can create a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` +object this way:: $cloner = new PhpCloner(); $data = $cloner->cloneVar($myVar); @@ -74,9 +76,10 @@ Casters ~~~~~~~ Objects and resources nested in a PHP variable are casted to arrays in the -intermediate Data representation. You can tweak the array representation for -each object/resource by hooking a Caster into this process. The component -already includes many casters for base PHP classes and other common classes. +intermediate :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` +representation. You can tweak the array representation for each object/resource +by hooking a Caster into this process. The component already includes many +casters for base PHP classes and other common classes. If you want to build your own Caster, you can register one before cloning a PHP variable. Casters are registered using either a Cloner's constructor @@ -107,14 +110,14 @@ interfaces, the parents classes and then the main class. Several casters can also be registered for the same resource type/class/interface. They are called in registration order. -Casters are responsible for returning the properties of the object orresource +Casters are responsible for returning the properties of the object or resource being cloned in an array. They are callables that accept four arguments: - - the object or resource being casted, - - an array modelled for objects after PHP's native ``(array)`` cast operator, - - a :class:`Stub ` object - representing the main properties of the object (class, type, etc.), - - true/false when the caster is called nested is a structure or not. +- the object or resource being casted, +- an array modelled for objects after PHP's native ``(array)`` cast operator, +- a :class:`Symfony\\Component\\VarDumper\\Cloner\\Stub` object + representing the main properties of the object (class, type, etc.), +- true/false when the caster is called nested is a structure or not. Here is a simple caster not doing anything:: @@ -138,11 +141,65 @@ for virtual properties and ``\0+\0`` for dynamic ones (runtime added properties not in the class declaration). .. note:: - Although you can, it is best advised not to alter the state of an object while casting it in a Caster. Dumpers ~~~~~~~ +A dumper is responsible for outputting a string representation of a PHP variable, +using a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object as input. +The destination and the formatting of this output vary with dumpers. + +This component comes with an :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` +for HTML output and a :class:`Symfony\\Component\\VarDumper\\Dumper\\CliDumper` +for optionally colored command line output. + +For example, if you want to dump some ``$variable``, just do:: + + $cloner = new PhpCloner(); + $dumper = new CliDumper(); + + $dumper->dump($cloner->cloneVar($variable)); + +By using the first argument of the constructor, you can select the output +stream where the dump will be written. By default, the ``CliDumper`` writes +on ``php://stdout`` and the ``HtmlDumper`` on ``php://output``, but any PHP +stream (resource or URL) is acceptable. + +Instead of a stream destination, you can also pass it a ``callable`` that +will be called repeatedly for each line generated by a dumper. This +callable can be configured using the first argument of a dumper's constructor, +but also using the +:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::setLineDumper` +method or using the second argument of the +:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::dump` method. + +For example, to get a dump in a variable, you can do:: + + $cloner = new PhpCloner(); + $dumper = new CliDumper(); + $output = ''; + + $dumper->dump( + $cloner->cloneVar($variable), + function ($line, $depth) use (&$output) { + // A negative depth means "end of dump" + if ($depth >= 0) { + // Adds a two spaces indentation to the line + $output .= str_repeat(' ', $depth).$line."\n"; + } + } + ); + + // $output is now populated with the dump representation of $variable + +Dumpers implement the :class:`Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface` +interface that specifies the +:method:`dump(Data $data) ` +method. They also typically implement the +:class:`Symfony\\Component\\VarDumper\\Cloner\\DumperInterface` that frees +them from re-implementing the logic required to walk through a +:class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object's internal structure. + .. _Packagist: https://packagist.org/packages/symfony/var-dumper From 350e0c7871bf05796954295757b981297676a4e1 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 3 Oct 2014 09:30:07 +0200 Subject: [PATCH 197/835] split intro/advanced sections --- components/index.rst | 2 +- components/map.rst.inc | 5 +- .../advanced.rst} | 51 +------------------ components/var_dumper/index.rst | 8 +++ components/var_dumper/introduction.rst | 50 ++++++++++++++++++ 5 files changed, 64 insertions(+), 52 deletions(-) rename components/{var_dumper.rst => var_dumper/advanced.rst} (75%) create mode 100644 components/var_dumper/index.rst create mode 100644 components/var_dumper/introduction.rst diff --git a/components/index.rst b/components/index.rst index be74544ce43..6386caab5df 100644 --- a/components/index.rst +++ b/components/index.rst @@ -29,7 +29,7 @@ The Components stopwatch templating/index translation/index - var_dumper + var_dumper/index yaml/index .. include:: /components/map.rst.inc diff --git a/components/map.rst.inc b/components/map.rst.inc index d3ce97a167f..93797c2e5d3 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -143,9 +143,10 @@ * :doc:`/components/translation/usage` * :doc:`/components/translation/custom_formats` -* **VarDumper** +* :doc:`/components/var_dumper/index` - * :doc:`/components/var_dumper` + * :doc:`/components/var_dumper/introduction` + * :doc:`/components/var_dumper/advanced` * :doc:`/components/yaml/index` diff --git a/components/var_dumper.rst b/components/var_dumper/advanced.rst similarity index 75% rename from components/var_dumper.rst rename to components/var_dumper/advanced.rst index b001162ed8a..ac384d8f181 100644 --- a/components/var_dumper.rst +++ b/components/var_dumper/advanced.rst @@ -2,53 +2,8 @@ single: VarDumper single: Components; VarDumper -The VarDumper Component -======================= - - The VarDumper component provides mechanisms for walking through any - arbitrary PHP variable. Built on top, it provides a better ``dump()`` - function that you can use instead of :phpfunction:`var_dump`. - -.. versionadded:: 2.6 - The VarDumper component was introduced in Symfony 2.6. - -Installation ------------- - -You can install the component in 2 different ways: - -- :doc:`Install it via Composer ` (``symfony/var-dumper`` on `Packagist`_); -- Use the official Git repository (https://github.com/symfony/var-dumper). - -The dump() function -------------------- - -The VarDumper component creates a global ``dump()`` function that is -configured out of the box: HTML or CLI output is automatically selected based -on the current PHP SAPI. - -The advantages of this function are: - -- per object and resource types specialized view to e.g. filter out - Doctrine internals while dumping a single proxy entity, or get more - insight on opened files with :phpfunction:`stream_get_meta_data()`. -- configurable output formats: HTML or colored command line output. -- ability to dump internal references, either soft ones (objects or - resources) or hard ones (``=&`` on arrays or objects properties). - Repeated occurrences of the same object/array/resource won't appear - again and again anymore. Moreover, you'll be able to inspect the - reference structure of your data. -- ability to operate in the context of an output buffering handler. - -``dump()`` is just a thin wrapper for -:method:`VarDumper::dump() ` -so can you also use it directly. -You can change the behavior of this function by calling -:method:`VarDumper::setHandler($callable) `: -calls to ``dump()`` will then be forwarded to ``$callable``, given as first argument. - -Advanced Usage --------------- +Advanced Usage of the VarDumper Component +========================================= Cloners ~~~~~~~ @@ -201,5 +156,3 @@ method. They also typically implement the :class:`Symfony\\Component\\VarDumper\\Cloner\\DumperInterface` that frees them from re-implementing the logic required to walk through a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object's internal structure. - -.. _Packagist: https://packagist.org/packages/symfony/var-dumper diff --git a/components/var_dumper/index.rst b/components/var_dumper/index.rst new file mode 100644 index 00000000000..2c67f2aa53f --- /dev/null +++ b/components/var_dumper/index.rst @@ -0,0 +1,8 @@ +VarDumper +========= + +.. toctree:: + :maxdepth: 2 + + introduction + advanced diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst new file mode 100644 index 00000000000..1b4669cc83c --- /dev/null +++ b/components/var_dumper/introduction.rst @@ -0,0 +1,50 @@ +.. index:: + single: VarDumper + single: Components; VarDumper + +The VarDumper Component +======================= + + The VarDumper component provides mechanisms for walking through any + arbitrary PHP variable. Built on top, it provides a better ``dump()`` + function that you can use instead of :phpfunction:`var_dump`. + +.. versionadded:: 2.6 + The VarDumper component was introduced in Symfony 2.6. + +Installation +------------ + +You can install the component in 2 different ways: + +- :doc:`Install it via Composer ` (``symfony/var-dumper`` on `Packagist`_); +- Use the official Git repository (https://github.com/symfony/var-dumper). + +The dump() function +------------------- + +The VarDumper component creates a global ``dump()`` function that is +configured out of the box: HTML or CLI output is automatically selected based +on the current PHP SAPI. + +The advantages of this function are: + +- per object and resource types specialized view to e.g. filter out + Doctrine internals while dumping a single proxy entity, or get more + insight on opened files with :phpfunction:`stream_get_meta_data()`. +- configurable output formats: HTML or colored command line output. +- ability to dump internal references, either soft ones (objects or + resources) or hard ones (``=&`` on arrays or objects properties). + Repeated occurrences of the same object/array/resource won't appear + again and again anymore. Moreover, you'll be able to inspect the + reference structure of your data. +- ability to operate in the context of an output buffering handler. + +``dump()`` is just a thin wrapper for +:method:`VarDumper::dump() ` +so can you also use it directly. +You can change the behavior of this function by calling +:method:`VarDumper::setHandler($callable) `: +calls to ``dump()`` will then be forwarded to ``$callable``, given as first argument. + +.. _Packagist: https://packagist.org/packages/symfony/var-dumper From fb18056924f6462ec55bbb5dba6a98f8b021be51 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 5 Sep 2014 12:27:47 +0200 Subject: [PATCH 198/835] validate `null` (Expression constraint in 2.6) Since Symfony 2.6, the Expression constraint doesn't skip validating `null` values. --- reference/constraints/Expression.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/reference/constraints/Expression.rst b/reference/constraints/Expression.rst index 68526372837..01eb69c3493 100644 --- a/reference/constraints/Expression.rst +++ b/reference/constraints/Expression.rst @@ -217,12 +217,11 @@ more about the expression language syntax, see // ... } - .. caution:: - - In Symfony 2.4 and Symfony 2.5, if the property (e.g. ``isTechnicalPost``) - were ``null``, the expression would never be called and the value - would be seen as valid. To ensure that the value is not ``null``, - use the :doc:`NotNull constraint `. + .. versionadded:: 2.6 + In Symfony 2.6, the Expression constraint *is* executed if the value + is ``null``. Before 2.6, if the value was ``null``, the expression + was never executed and the value was considered valid (unless you + also had a constraint like `NotBlank` on the property). For more information about the expression and what variables are available to you, see the :ref:`expression ` From f2bf9805b10f8ab3af243e6d5829eca53b13b481 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 3 Oct 2014 14:28:37 +0200 Subject: [PATCH 199/835] support Varnish in configuration blocks --- _exts | 2 +- conf.py | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/_exts b/_exts index 03bc1c60172..bb540b67288 160000 --- a/_exts +++ b/_exts @@ -1 +1 @@ -Subproject commit 03bc1c60172a280619e3476f22b111b4a187895d +Subproject commit bb540b6728898b48d7ec61e52065a18c391951fe diff --git a/conf.py b/conf.py index b8ffe20fc86..fe7fd835847 100644 --- a/conf.py +++ b/conf.py @@ -23,6 +23,7 @@ # adding PhpLexer from sphinx.highlighting import lexers from pygments.lexers.web import PhpLexer +from pygments.lexers.agile import RubyLexer # -- General configuration ----------------------------------------------------- @@ -100,6 +101,15 @@ lexers['php-annotations'] = PhpLexer(startinline=True) lexers['php-standalone'] = PhpLexer(startinline=True) lexers['php-symfony'] = PhpLexer(startinline=True) +lexers['varnish2'] = RubyLexer() +lexers['varnish3'] = RubyLexer() +lexers['varnish4'] = RubyLexer() + +config_block = { + 'varnish2': 'Varnish 2', + 'varnish3': 'Varnish 3', + 'varnish4': 'Varnish 4' +} # use PHP as the primary domain primary_domain = 'php' @@ -264,3 +274,4 @@ # Use PHP syntax highlighting in code examples by default highlight_language='php' + From 1a7c4b9c7d243b1908566f4b0394bb2113cd435c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 3 Oct 2014 10:32:16 -0400 Subject: [PATCH 200/835] Updating library/bundle installation docs to use the new composer require (no version) functionality --- .../dependency_injection/lazy_services.rst | 14 ++- cookbook/bundles/best_practices.rst | 5 +- cookbook/bundles/installation.rst | 98 +++++++------------ cookbook/workflow/_vendor_deps.rst.inc | 23 +---- 4 files changed, 45 insertions(+), 95 deletions(-) diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst index 57226d9e463..497e45720ce 100644 --- a/components/dependency_injection/lazy_services.rst +++ b/components/dependency_injection/lazy_services.rst @@ -30,21 +30,19 @@ the `ProxyManager bridge`_: .. code-block:: bash - $ php composer.phar require symfony/proxy-manager-bridge:2.3.* + $ php composer.phar require symfony/proxy-manager-bridge .. note:: If you're using the full-stack framework, the proxy manager bridge is already - included but the actual proxy manager needs to be included. Therefore add + included but the actual proxy manager needs to be included. So, run: - .. code-block:: json + .. code-block:: bash - "require": { - "ocramius/proxy-manager": "0.4.*" - } + $ php composer.phar require ocramius/proxy-manager - to your ``composer.json``. Afterwards compile your container and check - to make sure that you get a proxy for your lazy services. + Afterwards compile your container and check to make sure that you get + a proxy for your lazy services. Configuration ------------- diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index d9c5fa88801..169746fe2b3 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -212,7 +212,7 @@ following standardized instructions in your ``README.md`` file. following command to download the latest stable version of this bundle: ```bash - $ composer require "~1" + $ composer require ``` This command requires you to have Composer installed globally, as explained @@ -247,9 +247,6 @@ following standardized instructions in your ``README.md`` file. } ``` -This template assumes that your bundle is in its ``1.x`` version. If not, change -the ``"~1"`` installation version accordingly (``"~2"``, ``"~3"``, etc.) - Optionally, you can add more installation steps (*Step 3*, *Step 4*, etc.) to explain other required installation tasks, such as registering routes or dumping assets. diff --git a/cookbook/bundles/installation.rst b/cookbook/bundles/installation.rst index a9150291642..8f3978bb06b 100644 --- a/cookbook/bundles/installation.rst +++ b/cookbook/bundles/installation.rst @@ -5,74 +5,46 @@ How to Install 3rd Party Bundles ================================ Most bundles provide their own installation instructions. However, the -basic steps for installing a bundle are the same. +basic steps for installing a bundle are the same: -Add Composer Dependencies -------------------------- +* `A) Add Composer Dependencies`_ +* `B) Enable the Bundle`_ +* `C) Configure the Bundle`_ -Starting from Symfony 2.1, dependencies are managed with Composer. It's -a good idea to learn some basics of Composer in `their documentation`_. +A) Add Composer Dependencies +---------------------------- -Before you can use Composer to install a bundle, you should look for a -`Packagist`_ package of that bundle. For example, if you search for the popular -`FOSUserBundle`_ you will find a package called `friendsofsymfony/user-bundle`_. +Dependencies are managed with Composer, so if Composer is new to you, learn +some basics in `their documentation`_. This has 2 steps: -.. note:: - - Packagist is the main archive for Composer. If you are searching - for a bundle, the best thing you can do is check out - `KnpBundles`_, it is the unofficial archive of Symfony Bundles. If - a bundle contains a ``README`` file, it is displayed there and if it - has a Packagist package it shows a link to the package. It's a - really useful site to begin searching for bundles. - -Now that you have the package name, you should determine the version -you want to use. Usually different versions of a bundle correspond to -a particular version of Symfony. This information should be in the ``README`` -file. If it isn't, you can use the version you want. If you choose an incompatible -version, Composer will throw dependency errors when you try to install. If -this happens, you can try a different version. - -In the case of the FOSUserBundle, the ``README`` file has a caution that version -1.2.0 must be used for Symfony 2.0 and 1.3+ for Symfony 2.1+. Packagist displays -example ``require`` statements for all existing versions of a package. The -current development version of FOSUserBundle is ``"friendsofsymfony/user-bundle": "2.0.*@dev"``. - -Now you can add the bundle to your ``composer.json`` file and update the -dependencies. You can do this manually: - -1. **Add it to the ``composer.json`` file:** - - .. code-block:: json +1) Find out the Name of the Bundle on Packagist +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - { - ..., - "require": { - ..., - "friendsofsymfony/user-bundle": "2.0.*@dev" - } - } +The README for a bundle (e.g. `FOSUserBundle`_) usually tells you its name +(e.g. ``friendsofsymfony/user-bundle``). If it doesn't, you can search for +the library on the `Packagist.org`_ site. -2. **Update the dependency:** +.. note:: - .. code-block:: bash + Looking for bundles? Try searching at `KnpBundles.com`_: the unofficial + archive of Symfony Bundles. - $ php composer.phar update friendsofsymfony/user-bundle +2) Install the Bundle via Composer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - or update all dependencies +Now that you know the package name, you can install it via Composer: .. code-block:: bash - $ php composer.phar update + $ php composer.phar require friendsofsymfony/user-bundle -Or you can do this in one command: - -.. code-block:: bash +This will choose the best version for your project, add it to ``composer.json`` +and download the library into the ``vendor/`` directory. If you need a specific +version, add a ``:`` and the version right after the library name (see +`composer require`_). - $ php composer.phar require friendsofsymfony/user-bundle:2.0.*@dev - -Enable the Bundle ------------------ +B) Enable the Bundle +-------------------- At this point, the bundle is installed in your Symfony project (in ``vendor/friendsofsymfony/``) and the autoloader recognizes its classes. @@ -96,13 +68,13 @@ The only thing you need to do now is register the bundle in ``AppKernel``:: } } -Configure the Bundle --------------------- +C) Configure the Bundle +----------------------- -Usually a bundle requires some configuration to be added to app's -``app/config/config.yml`` file. The bundle's documentation will likely -describe that configuration. But you can also get a reference of the -bundle's config via the ``config:dump-reference`` command. +It's pretty common for a bundle to need some additional setup or configuration +in ``app/config/config.yml``. The bundle's documentation will tell you about +the configuration, but you can also get a reference of the bundle's config +via the ``config:dump-reference`` command. For instance, in order to look the reference of the ``assetic`` config you can use this: @@ -137,10 +109,10 @@ Other Setup ----------- At this point, check the ``README`` file of your brand new bundle to see -what to do next. +what to do next. Have fun! .. _their documentation: http://getcomposer.org/doc/00-intro.md -.. _Packagist: https://packagist.org +.. _Packagist.org: https://packagist.org .. _FOSUserBundle: https://github.com/FriendsOfSymfony/FOSUserBundle -.. _`friendsofsymfony/user-bundle`: https://packagist.org/packages/friendsofsymfony/user-bundle .. _KnpBundles: http://knpbundles.com/ +.. _`composer require`: https://getcomposer.org/doc/03-cli.md#require diff --git a/cookbook/workflow/_vendor_deps.rst.inc b/cookbook/workflow/_vendor_deps.rst.inc index 23ab3c3e073..93d009550b4 100644 --- a/cookbook/workflow/_vendor_deps.rst.inc +++ b/cookbook/workflow/_vendor_deps.rst.inc @@ -24,29 +24,12 @@ To upgrade your libraries to new versions, run ``php composer.phar update``. .. tip:: - If you want to add a new package to your application, modify the ``composer.json`` - file: - - .. code-block:: json - - { - "require": { - ... - "doctrine/doctrine-fixtures-bundle": "@dev" - } - } - - and then execute the ``update`` command for this specific package, i.e.: - - .. code-block:: bash - - $ php composer.phar update doctrine/doctrine-fixtures-bundle - - You can also combine both steps into a single command: + If you want to add a new package to your application, run the composer + ``require`` command: .. code-block:: bash - $ php composer.phar require doctrine/doctrine-fixtures-bundle:@dev + $ php composer.phar require doctrine/doctrine-fixtures-bundle To learn more about Composer, see `GetComposer.org`_: From a174a2b91257cd877bc10be080e6bf2c59f5ab78 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 3 Oct 2014 10:54:23 -0400 Subject: [PATCH 201/835] Fixing bad link --- cookbook/bundles/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/bundles/installation.rst b/cookbook/bundles/installation.rst index 8f3978bb06b..c546ffad58c 100644 --- a/cookbook/bundles/installation.rst +++ b/cookbook/bundles/installation.rst @@ -114,5 +114,5 @@ what to do next. Have fun! .. _their documentation: http://getcomposer.org/doc/00-intro.md .. _Packagist.org: https://packagist.org .. _FOSUserBundle: https://github.com/FriendsOfSymfony/FOSUserBundle -.. _KnpBundles: http://knpbundles.com/ +.. _KnpBundles.com: http://knpbundles.com/ .. _`composer require`: https://getcomposer.org/doc/03-cli.md#require From 6cba2db8ebd2cd67dad0b43474ec0952bb5a8ceb Mon Sep 17 00:00:00 2001 From: Daniel Santana Date: Fri, 3 Oct 2014 12:53:31 -0400 Subject: [PATCH 202/835] Fixed broken external link to DemoController Test --- book/testing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/testing.rst b/book/testing.rst index e003b4a650a..9876bce5e5d 100644 --- a/book/testing.rst +++ b/book/testing.rst @@ -852,6 +852,6 @@ Learn more * :doc:`/cookbook/testing/profiling` * :doc:`/cookbook/testing/bootstrap` -.. _`DemoControllerTest`: https://github.com/symfony/symfony-standard/blob/master/src/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php +.. _`DemoControllerTest`: https://github.com/sensiolabs/SensioDistributionBundle/blob/master/Resources/skeleton/acme-demo-bundle/Acme/DemoBundle/Tests/Controller/DemoControllerTest.php .. _`$_SERVER`: http://php.net/manual/en/reserved.variables.server.php .. _`documentation`: http://phpunit.de/manual/current/en/ From 2cb85a16b9928930dbd6b5a3c0507c92b4d3d39c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20DECOOL?= Date: Sun, 5 Oct 2014 18:09:18 +0200 Subject: [PATCH 203/835] [Cookbook][Doctrine] Fix typo in XML configuration for custom SQL function --- cookbook/doctrine/custom_dql_functions.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/doctrine/custom_dql_functions.rst b/cookbook/doctrine/custom_dql_functions.rst index b15877ffa2a..dcdfc6bc492 100644 --- a/cookbook/doctrine/custom_dql_functions.rst +++ b/cookbook/doctrine/custom_dql_functions.rst @@ -39,10 +39,10 @@ In Symfony, you can register your custom DQL functions as follows: - Acme\HelloBundle\DQL\SecondStringFunction - Acme\HelloBundle\DQL\DatetimeFunction + Acme\HelloBundle\DQL\StringFunction + Acme\HelloBundle\DQL\SecondStringFunction + Acme\HelloBundle\DQL\NumericFunction + Acme\HelloBundle\DQL\DatetimeFunction From 8c05dabd81012a94df8e450be3e09957b47e8501 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Sun, 5 Oct 2014 23:29:51 +0200 Subject: [PATCH 204/835] routing example fixed with routing.xml file --- book/http_fundamentals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index b297bab5783..f89666b4c69 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -431,7 +431,7 @@ by adding an entry for ``/contact`` to your routing configuration file: .. code-block:: xml - + Date: Thu, 18 Sep 2014 10:26:19 -0400 Subject: [PATCH 205/835] Clarifying that the bundles best practices entry is for reusable bundles, not for app bundles --- cookbook/bundles/best_practices.rst | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index d9c5fa88801..1b2ce0c6468 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -1,12 +1,19 @@ .. index:: single: Bundle; Best practices -How to Use best Practices for Structuring Bundles -================================================= +Best Practices for Reusable Bundles +=================================== -A bundle is a directory that has a well-defined structure and can host anything -from classes to controllers and web resources. Even if bundles are very -flexible, you should follow some best practices if you want to distribute them. +There are 2 types of bundles: + +* Application bundles: only use to build your application; +* Reusable bundle: meant to be shared across many projects. + +This article is all about how to structure your **reusable bundles** so that +they're easy to configure and extend. Many of these recommendations do not +apply to application bundles because you'll want to keep those as simple +as possible. For application bundles, just follow the practices shown throughout +the book and cookbook. .. index:: pair: Bundle; Naming conventions From 1caa1fd1bd85d513c9e6cd7b05174eee7a9956ea Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 6 Oct 2014 10:06:18 -0400 Subject: [PATCH 206/835] Some fixes thanks to @stof --- cookbook/bundles/best_practices.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 1b2ce0c6468..91168b9635a 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -6,8 +6,8 @@ Best Practices for Reusable Bundles There are 2 types of bundles: -* Application bundles: only use to build your application; -* Reusable bundle: meant to be shared across many projects. +* Application-specific bundles: only used to build your application; +* Reusable bundles: meant to be shared across many projects. This article is all about how to structure your **reusable bundles** so that they're easy to configure and extend. Many of these recommendations do not From f36c45e3a81fb9ec21b67474a66d9ed6709928ba Mon Sep 17 00:00:00 2001 From: Maxime Douailin Date: Tue, 7 Oct 2014 15:35:35 +0200 Subject: [PATCH 207/835] uppercase title --- cookbook/security/pre_authenticated.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/pre_authenticated.rst b/cookbook/security/pre_authenticated.rst index 63405fb7f53..7a0775a8ab8 100644 --- a/cookbook/security/pre_authenticated.rst +++ b/cookbook/security/pre_authenticated.rst @@ -80,7 +80,7 @@ in the x509 firewall configuration respectively. * :doc:`/cookbook/security/custom_provider` * :doc:`/cookbook/security/entity_provider` -REMOTE_USER based Authentication +REMOTE_USER Based Authentication -------------------------------- .. versionadded:: 2.6 From c0167daa4a5b6f63e51452d86964f55cfae9410e Mon Sep 17 00:00:00 2001 From: Thierry Geindre Date: Tue, 7 Oct 2014 16:22:43 +0200 Subject: [PATCH 208/835] Fix PropertyAccessorBuilder usage --- components/property_access/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index a9eb4083fd9..8d6492c8d8e 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -209,7 +209,7 @@ enable this feature by using :class:`Symfony\\Component\\PropertyAccess\\Propert $person = new Person(); // Enable magic __call - $accessor = PropertyAccess::getPropertyAccessorBuilder() + $accessor = PropertyAccess::createPropertyAccessorBuilder() ->enableMagicCall() ->getPropertyAccessor(); @@ -305,7 +305,7 @@ see `Enable other Features`_. $person = new Person(); // Enable magic __call - $accessor = PropertyAccess::getPropertyAccessorBuilder() + $accessor = PropertyAccess::createPropertyAccessorBuilder() ->enableMagicCall() ->getPropertyAccessor(); From 2fc3811688f556946837df423a135c717fd267e2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 7 Oct 2014 16:32:29 +0200 Subject: [PATCH 209/835] use VarCloner instead of Php/ExtCloner --- components/var_dumper/advanced.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index ac384d8f181..d1c1609a229 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -17,7 +17,7 @@ represent only a subset of the cloned variable. You can create a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object this way:: - $cloner = new PhpCloner(); + $cloner = new VarCloner(); $data = $cloner->cloneVar($myVar); Before cloning, you can configure the limits with:: @@ -41,7 +41,7 @@ a PHP variable. Casters are registered using either a Cloner's constructor or its ``addCasters()`` method:: $myCasters = array(...); - $cloner = new PhpCloner($myCasters); + $cloner = new VarCloner($myCasters); // or @@ -112,7 +112,7 @@ for optionally colored command line output. For example, if you want to dump some ``$variable``, just do:: - $cloner = new PhpCloner(); + $cloner = new VarCloner(); $dumper = new CliDumper(); $dumper->dump($cloner->cloneVar($variable)); @@ -132,7 +132,7 @@ method or using the second argument of the For example, to get a dump in a variable, you can do:: - $cloner = new PhpCloner(); + $cloner = new VarCloner(); $dumper = new CliDumper(); $output = ''; From 8bb1c9a68204e362bb377e4e754b6ff305c0aec4 Mon Sep 17 00:00:00 2001 From: Evan Owens Date: Tue, 7 Oct 2014 16:34:04 -0400 Subject: [PATCH 210/835] Fix spelling "so-called" is a traditionally hyphenated compound word. --- cookbook/configuration/web_server_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/configuration/web_server_configuration.rst b/cookbook/configuration/web_server_configuration.rst index e93e873307c..3e248ddf205 100644 --- a/cookbook/configuration/web_server_configuration.rst +++ b/cookbook/configuration/web_server_configuration.rst @@ -89,7 +89,7 @@ the FastCGI process manager ``php-fpm`` binary and Apache's FastCGI module installed (for example, on a Debian based system you have to install the ``libapache2-mod-fastcgi`` and ``php5-fpm`` packages). -PHP-FPM uses so called *pools* to handle incoming FastCGI requests. You can +PHP-FPM uses so-called *pools* to handle incoming FastCGI requests. You can configure an arbitrary number of pools in the FPM configuration. In a pool you configure either a TCP socket (IP and port) or a unix domain socket to listen on. Each pool can also be run under a different UID and GID: From d420491e36da04350fe09de445d4a3aff6e767e9 Mon Sep 17 00:00:00 2001 From: Milan Magudia Date: Tue, 7 Oct 2014 19:20:02 +0100 Subject: [PATCH 211/835] Changed to reference the correct twig template --- quick_tour/the_view.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_view.rst b/quick_tour/the_view.rst index c641eb6db10..470cd51226e 100644 --- a/quick_tour/the_view.rst +++ b/quick_tour/the_view.rst @@ -157,7 +157,7 @@ First, create an ``embedded.html.twig`` template: {# src/Acme/DemoBundle/Resources/views/Demo/embedded.html.twig #} Hello {{ name }} -And change the ``index.html.twig`` template to include it: +And change the ``hello.html.twig`` template to include it: .. code-block:: jinja From 60c0c82c06583fbe14c3bca331d81e789b255c87 Mon Sep 17 00:00:00 2001 From: Evan Owens Date: Tue, 7 Oct 2014 16:34:04 -0400 Subject: [PATCH 212/835] Fix spelling "so-called" is a traditionally hyphenated compound word. --- cookbook/configuration/web_server_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/configuration/web_server_configuration.rst b/cookbook/configuration/web_server_configuration.rst index e93e873307c..3e248ddf205 100644 --- a/cookbook/configuration/web_server_configuration.rst +++ b/cookbook/configuration/web_server_configuration.rst @@ -89,7 +89,7 @@ the FastCGI process manager ``php-fpm`` binary and Apache's FastCGI module installed (for example, on a Debian based system you have to install the ``libapache2-mod-fastcgi`` and ``php5-fpm`` packages). -PHP-FPM uses so called *pools* to handle incoming FastCGI requests. You can +PHP-FPM uses so-called *pools* to handle incoming FastCGI requests. You can configure an arbitrary number of pools in the FPM configuration. In a pool you configure either a TCP socket (IP and port) or a unix domain socket to listen on. Each pool can also be run under a different UID and GID: From a715baf9264b454ef590b96556c7686f79b0a1cf Mon Sep 17 00:00:00 2001 From: Andrew M Date: Thu, 9 Oct 2014 08:13:18 +0300 Subject: [PATCH 213/835] Improve readability --- cookbook/event_dispatcher/before_after_filters.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cookbook/event_dispatcher/before_after_filters.rst b/cookbook/event_dispatcher/before_after_filters.rst index 569f0c7cce5..7fe93410713 100644 --- a/cookbook/event_dispatcher/before_after_filters.rst +++ b/cookbook/event_dispatcher/before_after_filters.rst @@ -125,7 +125,8 @@ event listeners, you can learn more about them at :doc:`/cookbook/service_contai $controller = $event->getController(); /* - * $controller passed can be either a class or a Closure. This is not usual in Symfony but it may happen. + * $controller passed can be either a class or a Closure. + * This is not usual in Symfony but it may happen. * If it is a class, it comes in array format */ if (!is_array($controller)) { @@ -189,7 +190,7 @@ want. After Filters with the ``kernel.response`` Event ------------------------------------------------ -In addition to having a "hook" that's executed before your controller, you +In addition to having a "hook" that's executed *before* your controller, you can also add a hook that's executed *after* your controller. For this example, imagine that you want to add a sha1 hash (with a salt using that token) to all responses that have passed this token authentication. From d01460741678fcd6a7b75154e498db46a6ffc4ec Mon Sep 17 00:00:00 2001 From: Olivier Dolbeau Date: Sat, 11 Oct 2014 13:02:27 +0200 Subject: [PATCH 214/835] Update choice.rst Add missing parenthesis --- reference/forms/types/choice.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index 9b2ef49971c..95c91e8442b 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -119,7 +119,7 @@ With this option you can also allow float values to be selected as data. // ... $builder->add('status', 'choice', array( - 'choice_list' => new ChoiceList(array(1, 0.5), array('Full', 'Half') + 'choice_list' => new ChoiceList(array(1, 0.5), array('Full', 'Half')) )); .. include:: /reference/forms/types/options/empty_value.rst.inc From 7fe8a36fcb96562b0d041a956ae8b8545cfd394a Mon Sep 17 00:00:00 2001 From: Andrew M Date: Sun, 12 Oct 2014 09:08:44 +0300 Subject: [PATCH 215/835] Remove horizontal scrollbar and change event name to follow conventions --- components/event_dispatcher/traceable_dispatcher.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/components/event_dispatcher/traceable_dispatcher.rst b/components/event_dispatcher/traceable_dispatcher.rst index 6b1cb19db86..3e850b71894 100644 --- a/components/event_dispatcher/traceable_dispatcher.rst +++ b/components/event_dispatcher/traceable_dispatcher.rst @@ -17,7 +17,10 @@ Pass the event dispatcher to be wrapped and an instance of the // the event dispatcher to debug $eventDispatcher = ...; - $traceableEventDispatcher = new TraceableEventDispatcher($eventDispatcher, new Stopwatch()); + $traceableEventDispatcher = new TraceableEventDispatcher( + $eventDispatcher, + new Stopwatch() + ); Now, the ``TraceableEventDispatcher`` can be used like any other event dispatcher to register event listeners and dispatch events:: @@ -27,11 +30,11 @@ to register event listeners and dispatch events:: // register an event listener $eventListener = ...; $priority = ...; - $traceableEventDispatcher->addListener('the-event-name', $eventListener, $priority); + $traceableEventDispatcher->addListener('event.the_name', $eventListener, $priority); // dispatch an event $event = ...; - $traceableEventDispatcher->dispatch('the-event-name', $event); + $traceableEventDispatcher->dispatch('event.the_name', $event); After your application has been processed, you can use the :method:`Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface::getCalledListeners` From a9d6a8b966ba6122d61e028a38d8a456c6e3b27c Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Thu, 3 Apr 2014 15:53:10 +0200 Subject: [PATCH 216/835] [Console] Add Process Helper documentation --- components/console/helpers/index.rst | 1 + components/console/helpers/map.rst.inc | 1 + components/console/helpers/processhelper.rst | 84 ++++++++++++++++++ .../console/process-helper-debug.png | Bin 0 -> 28822 bytes .../console/process-helper-error-debug.png | Bin 0 -> 19108 bytes .../console/process-helper-verbose.png | Bin 0 -> 14289 bytes 6 files changed, 86 insertions(+) create mode 100644 components/console/helpers/processhelper.rst create mode 100644 images/components/console/process-helper-debug.png create mode 100644 images/components/console/process-helper-error-debug.png create mode 100644 images/components/console/process-helper-verbose.png diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index d632eeeb66a..909671fb08c 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -9,6 +9,7 @@ The Console Helpers dialoghelper formatterhelper + processhelper progressbar progresshelper questionhelper diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index b9a1114cfc2..15e6e9f8da9 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,5 +1,6 @@ * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` +* :doc:`/components/console/helpers/processhelper` * :doc:`/components/console/helpers/progressbar` * :doc:`/components/console/helpers/progresshelper` * :doc:`/components/console/helpers/questionhelper` diff --git a/components/console/helpers/processhelper.rst b/components/console/helpers/processhelper.rst new file mode 100644 index 00000000000..414724c7f87 --- /dev/null +++ b/components/console/helpers/processhelper.rst @@ -0,0 +1,84 @@ +.. index:: + single: Console Helpers; Process Helper + +Process Helper +============== + +.. versionadded:: 2.6 + The Process Helper was introduced in Symfony 2.6. + +The Process Helper shows processes as they're running and reports +useful information about process status. + +To display process details, use the :class:`Symfony\\Component\\Console\\Helper\\ProcessHelper` +and run your command with verbosity. For example, running the following code with +a very verbose verbosity (e.g. -vv):: + + use Symfony\Component\Process\ProcessBuilder; + + $helper = $this->getHelper('process'); + $process = ProcessBuilder::create(array('figlet', 'Symfony'))->getProcess(); + + $helper->run($output, $process); + +will result in this output: + +.. image:: /images/components/console/process-helper-verbose.png + +It will result in more detailed output with debug verbosity (e.g. ``-vvv``): + +.. image:: /images/components/console/process-helper-debug.png + +In case the process fails, debugging is easier: + +.. image:: /images/components/console/process-helper-error-debug.png + +Arguments +--------- + +There are three ways to use the process helper: + +* Either using a command line string:: + + // ... + $helper->run($output, 'figlet Symfony'); + +* An array of arguments:: + + // ... + $helper->run($output, array('figlet', 'Symfony')); + + .. note:: + + When running the helper against an array of arguments, be aware that + these ones will be automatically escaped. + +* Or a :class:`Symfony\\Component\\Process\\Process` instance:: + + use Symfony\Component\Process\ProcessBuilder; + + // ... + $process = ProcessBuilder::create(array('figlet', 'Symfony'))->getProcess(); + + $helper->run($output, $process); + +Customized Display +------------------ + +You can display a customized error message using the third argument of the +:method:`Symfony\\Component\\Console\\Helper\\ProcessHelper::run` method:: + + $helper->run($output, $process, 'The process failed :('); + +A custom process callback can be passed as fourth argument, refer to the +:doc:`Process Component ` for callback documentation:: + + use Symfony\Component\Process\Process; + + $helper->run($output, $process, 'The process failed :(', function ($type, $data) { + if (Process::ERR === $type) { + // ... do something with the stderr output + } else { + // ... do something with the stdout + } + }); diff --git a/images/components/console/process-helper-debug.png b/images/components/console/process-helper-debug.png new file mode 100644 index 0000000000000000000000000000000000000000..282e1336389762bb88f5fa6d61d4669066c483de GIT binary patch literal 28822 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 literal 0 HcmV?d00001 diff --git a/images/components/console/process-helper-error-debug.png b/images/components/console/process-helper-error-debug.png new file mode 100644 index 0000000000000000000000000000000000000000..8d1145478f2e3a0d6c0ee2074ea50883bd7c5107 GIT binary patch literal 19108 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 QS79}f`_V2ur+}ot0sK-87XSbN literal 0 HcmV?d00001 diff --git a/images/components/console/process-helper-verbose.png b/images/components/console/process-helper-verbose.png new file mode 100644 index 0000000000000000000000000000000000000000..c4c912e14331dc53482e25571d1f16ad4018765a GIT binary patch literal 14289 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 literal 0 HcmV?d00001 From 0d8e3c29b764b1bf7aa0bd37b580c748f8a83583 Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Sun, 12 Oct 2014 23:39:42 +0200 Subject: [PATCH 217/835] Correct a typo: remove unnecessary "the" word. --- cookbook/bundles/extension.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/bundles/extension.rst b/cookbook/bundles/extension.rst index cc4ce322399..d335b8a3f26 100644 --- a/cookbook/bundles/extension.rst +++ b/cookbook/bundles/extension.rst @@ -84,7 +84,7 @@ container, to ensure all services and parameters are also added to the actual container. In the ``load()`` method, you can use PHP code to register service definitions, -but it is more common if you put the these definitions in a configuration file +but it is more common if you put these definitions in a configuration file (using the Yaml, XML or PHP format). Luckily, you can use the file loaders in the extension! From 10776bb66fd0c834ab5d0c597987c8945fd46d81 Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Mon, 13 Oct 2014 12:52:25 +0200 Subject: [PATCH 218/835] Correct a typo: replace "then" by "the". --- components/dependency_injection/definitions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/definitions.rst b/components/dependency_injection/definitions.rst index 46ed34ab36a..c9d1e72cb3d 100644 --- a/components/dependency_injection/definitions.rst +++ b/components/dependency_injection/definitions.rst @@ -40,7 +40,7 @@ Creating a new Definition ~~~~~~~~~~~~~~~~~~~~~~~~~ If you need to create a new definition rather than manipulate one retrieved -from then container then the definition class is :class:`Symfony\\Component\\DependencyInjection\\Definition`. +from the container then the definition class is :class:`Symfony\\Component\\DependencyInjection\\Definition`. Class ~~~~~ From 3d3388d606e33216ad3abe778765756dff65d06a Mon Sep 17 00:00:00 2001 From: Fabien Schurter Date: Wed, 15 Oct 2014 00:30:59 +0200 Subject: [PATCH 219/835] Correct a typo. Removed uncorrect "load use" phrase part, and replaced it with "you can use", which I guess conveys the original meaning of the phrase better. --- components/form/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/form/introduction.rst b/components/form/introduction.rst index 21f2d669eb3..c5b47f8fa87 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -137,7 +137,7 @@ and validated when binding the form. .. tip:: - If you're not using the HttpFoundation component, load use + If you're not using the HttpFoundation component, you can use :class:`Symfony\\Component\\Form\\Extension\\Csrf\\CsrfProvider\\DefaultCsrfProvider` instead, which relies on PHP's native session handling:: From 2c2000a0274b182cbf1a429badb567ee65432c54 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 15 Oct 2014 14:57:07 +0200 Subject: [PATCH 220/835] first import of the "Official Best Practices" book --- best_practices/business-logic.rst | 344 ++++++++++++++++++++++ best_practices/configuration.rst | 183 ++++++++++++ best_practices/controllers.rst | 212 ++++++++++++++ best_practices/creating-the-project.rst | 252 ++++++++++++++++ best_practices/forms.rst | 231 +++++++++++++++ best_practices/i18n.rst | 96 +++++++ best_practices/index.rst | 17 ++ best_practices/introduction.rst | 98 +++++++ best_practices/security.rst | 363 ++++++++++++++++++++++++ best_practices/templates.rst | 164 +++++++++++ best_practices/tests.rst | 114 ++++++++ best_practices/web-assets.rst | 97 +++++++ 12 files changed, 2171 insertions(+) create mode 100644 best_practices/business-logic.rst create mode 100644 best_practices/configuration.rst create mode 100644 best_practices/controllers.rst create mode 100644 best_practices/creating-the-project.rst create mode 100644 best_practices/forms.rst create mode 100644 best_practices/i18n.rst create mode 100644 best_practices/index.rst create mode 100644 best_practices/introduction.rst create mode 100644 best_practices/security.rst create mode 100644 best_practices/templates.rst create mode 100644 best_practices/tests.rst create mode 100644 best_practices/web-assets.rst diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst new file mode 100644 index 00000000000..fe7364ec2ec --- /dev/null +++ b/best_practices/business-logic.rst @@ -0,0 +1,344 @@ +Organizing Your Business Logic +============================== + +In computer software, **business logic** or domain logic is "the part of the +program that encodes the real-world business rules that determine how data can +be created, displayed, stored, and changed" (read `full definition`_). + +In Symfony applications, business logic is all the custom code you write for +your app that's not specific to the framework (e.g. routing and controllers). +Domain classes, Doctrine entities and regular PHP classes that are used as +services are good examples of business logic. + +For most projects, you should store everything inside the ``AppBundle``. +Inside here, you can create whatever directories you want to organize things: + +.. code-block:: text + + symfoy2-project/ + ├─ app/ + ├─ src/ + │ └─ AppBundle/ + │ └─ Utils/ + │ └─ MyClass.php + ├─ vendor/ + └─ web/ + +Storing Classes Outside of the Bundle? +-------------------------------------- + +But there's no technical reason for putting business logic inside of a bundle. +If you like, you can create your own namespace inside the ``src/`` directory +and put things there: + +.. code-block:: text + + symfoy2-project/ + ├─ app/ + ├─ src/ + │ ├─ Acme/ + │ │ └─ Utils/ + │ │ └─ MyClass.php + │ └─ AppBundle/ + ├─ vendor/ + └─ web/ + +.. tip:: + + The recommended approach of using the ``AppBundle`` directory is for + simplicity. If you're advanced enough to know what needs to live in + a bundle and what can live outside of one, then feel free to do that. + +Services: Naming and Format +--------------------------- + +The blog application needs a utility that can transform a post title (e.g. +"Hello World") into a slug (e.g. "hello-world"). The slug will be used as +part of the post URL. + +Let's, create a new ``Slugger`` class inside ``src/AppBundle/Utils/`` and +add the following ``slugify()`` method: + +.. code-block:: php + + // src/AppBundle/Utils/Slugger.php + namespace AppBundle\Utils; + + class Slugger + { + public function slugify($string) + { + return preg_replace( + '/[^a-z0-9]/', '-', strtolower(trim(strip_tags($string))) + ); + } + } + +Next, define a new service for that class. + +.. code-block:: yaml + + # app/config/services.yml + services: + # keep your service names short + slugger: + class: AppBundle\Utils\Slugger + +Traditionally, the naming convention for a service involved following the +class name and location to avoid name collisions. Thus, the service +*would have been* called ``app.utils.slugger``. But by using short service names, +your code will be easier to read and use. + +.. best-practice:: + + The name of your application's services should be as short as possible, + ideally just one simple word. + +Now you can use the custom slugger in any controller class, such as the +``AdminController``: + +.. code-block:: php + + public function createAction(Request $request) + { + // ... + + if ($form->isSubmitted() && $form->isValid()) { + $slug = $this->get('slugger')->slugify($post->getTitle())); + $post->setSlug($slug); + + // ... + } + } + +Service Format: YAML +-------------------- + +In the previous section, YAML was used to define the service. + +.. best-practice:: + + Use the YAML format to define your own services. + +This is controversial, and in our experience, YAML and XML usage is evenly +distributed among developers, with a slight preference towards YAML. +Both formats have the same performance, so this is ultimately a matter of +personal taste. + +We recommend YAML because it's friendly to newcomers and concise. You can +of course use whatever format you like. + +Service: No Class Parameter +--------------------------- + +You may have noticed that the previous service definition doesn't configure +the class namespace as a parameter: + +.. code-block:: yaml + + # app/config/services.yml + + # service definition with class namespace as parameter + parameters: + slugger.class: AppBundle\Utils\Slugger + + services: + slugger: + class: "%slugger.class%" + +This practice is cumbersome and completely unnecessary for your own services: + +.. best-practice:: + + Don't define parameters for the classes of your services. + +This practice was wrongly adopted from third-party bundles. When Symfony +introduced its service container, some developers used this technique to easily +allow overriding services. However, overriding a service by just changing its +class name is a very rare use case because, frequently, the new service has +different constructor arguments. + +Using a Persistence Layer +------------------------- + +Symfony is an HTTP framework that only cares about generating an HTTP response +for each HTTP request. That's why Symfony doesn't provide a way to talk to +a persistence layer (e.g. database, external API). You can choose whatever +library of strategy you want for this. + +In practice, many Symfony applications rely on the independent +`Doctrine project`_ to define their model using entities and repositories. +Just like with business logic, we recommend storing Doctrine entities in +the ``AppBundle`` + +The three entities defined by our sample blog application are a good example: + +.. code-block:: text + + symfony2-project/ + ├─ ... + └─ src/ + └─ AppBundle/ + └─ Entity/ + ├─ Comment.php + ├─ Post.php + └─ User.php + +.. tip:: + + If you're more advanced, you can of course store them under your own + namespace in ``src/``. + +Doctrine Mapping Information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Doctrine Entities are plain PHP objects that you store in some "database". +Doctrine only knows about your entities through the mapping metadata configured +for your model classes. Doctrine supports four metadata formats: YAML, XML, +PHP and annotations. + +.. best-practice:: + + Use annotations to define the mapping information of the Doctrine entities. + +Annotations are by far the most convenient and agile way of setting up and +looking for mapping information: + +.. code-block:: php + + namespace AppBundle\Entity; + + use Doctrine\ORM\Mapping as ORM; + use Doctrine\Common\Collections\ArrayCollection; + + /** + * @ORM\Entity + */ + class Post + { + const NUM_ITEMS = 10; + + /** + * @ORM\Id + * @ORM\GeneratedValue + * @ORM\Column(type="integer") + */ + private $id; + + /** + * @ORM\Column(type="string") + */ + private $title; + + /** + * @ORM\Column(type="string") + */ + private $slug; + + /** + * @ORM\Column(type="text") + */ + private $content; + + /** + * @ORM\Column(type="string") + */ + private $authorEmail; + + /** + * @ORM\Column(type="datetime") + */ + private $publishedAt; + + /** + * @ORM\OneToMany( + * targetEntity="Comment", + * mappedBy="post", + * orphanRemoval=true + * ) + * @ORM\OrderBy({"publishedAt" = "ASC"}) + */ + private $comments; + + public function __construct() + { + $this->publishedAt = new \DateTime(); + $this->comments = new ArrayCollection(); + } + + // getters and setters ... + } + +All formats have the same performance, so this is once again ultimately a +matter of taste. + +Data Fixtures +~~~~~~~~~~~~~ + +As fixtures support is not enabled by default in Symfony, you should execute +the following command to install the Doctrine fixtures bundle: + +.. code-block:: bash + + $ composer require "doctrine/doctrine-fixtures-bundle" + +Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and +``test`` environments: + +.. code-block:: php + + use Symfony\Component\HttpKernel\Kernel; + + class AppKernel extends Kernel + { + public function registerBundles() + { + $bundles = array( + // ... + ); + + if (in_array($this->getEnvironment(), array('dev', 'test'))) { + // ... + $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(), + } + + return $bundles; + } + + // ... + } + +We recommend creating just *one* `fixture class`_ for simplicity, though +you're welcome to have more if that class gets quite large. + +Assuming you have at least one fixtures class and that the database access +is configured properly, you can load your fixtures by executing the following +command: + +.. code-block:: bash + + $ php app/console doctrine:fixtures:load + + Careful, database will be purged. Do you want to continue Y/N ? Y + > purging database + > loading AppBundle\DataFixtures\ORM\LoadFixtures + +Coding Standards +---------------- + +The Symfony source code follows the `PSR-1`_ and `PSR-2`_ coding standards that +were defined by the PHP community. You can learn more about +`the Symfony Code Standards`_ and even use the `PHP-CS-Fixer`_, which is +a command-line utility that can fix the coding standards of an entire codebase +in a matter of seconds. + +.. _`full definition`: http://en.wikipedia.org/wiki/Business_logic +.. _`Toran Proxy`: https://toranproxy.com/ +.. _`Composer`: https://getcomposer.org/ +.. _`MVC architecture`: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller +.. _`Doctrine project`: http://www.doctrine-project.org/ +.. _`fixture class`: http://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures +.. _`PSR-1`: http://www.php-fig.org/psr/psr-1/ +.. _`PSR-2`: http://www.php-fig.org/psr/psr-2/ +.. _`the Symfony Code Standards`: http://symfony.com/doc/current/contributing/code/standards.html +.. _`PHP-CS-Fixer`: https://github.com/fabpot/PHP-CS-Fixer diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst new file mode 100644 index 00000000000..0c463239784 --- /dev/null +++ b/best_practices/configuration.rst @@ -0,0 +1,183 @@ +Configuration +============= + +Configuration usually involves different application parts (such as infrastructure +and security credentials) and different environments (development, production). +That's why Symfony recommends that you split the application configuration into +three parts. + +Infrastructure-Related Configuration +------------------------------------ + +.. best-practice:: + + Define the infrastructure-related configuration options in the + ``app/config/parameters.yml`` file. + +The default ``parameters.yml`` file follows this recommendation and defines the +options related to the database and mail server infrastructure: + +.. code-block:: yaml + + # app/config/parameters.yml + parameters: + database_driver: pdo_mysql + database_host: 127.0.0.1 + database_port: ~ + database_name: symfony + database_user: root + database_password: ~ + + mailer_transport: smtp + mailer_host: 127.0.0.1 + mailer_user: ~ + mailer_password: ~ + + # ... + +These options aren't defined inside the ``app/config/config.yml`` file because +they have nothing to do with the application's behavior. In other words, your +application doesn't care about the location of your database or the credentials +to access to it, as long as the database is correctly configured. + +Canonical Parameters +~~~~~~~~~~~~~~~~~~~~ + +.. best-practice:: + + Define all your application's parameters in the + ``app/config/parameters.yml.dist`` file. + +Since version 2.3, Symfony includes a configuration file called ``parameters.yml.dist``, +which stores the canonical list of configuration parameters for the application. + +Whenever a new configuration parameter is defined for the application, you +should also add it to this file and submit the changes to your version control +system. Then, whenever a developer updates the project or deploys it to a server, +Symfony will check if there is any difference between the canonical +``parameters.yml.dist`` file and your local ``parameters.yml`` file. If there +is a difference, Symfony will ask you to provide a value for the new parameter +and it will add it to your local ``parameters.yml`` file. + +Application-Related Configuration +--------------------------------- + +.. best-practice:: + + Define the application behavior related configuration options in the + ``app/config/config.yml`` file. + +The ``config.yml`` file contains the options used by the application to modify +its behavior, such as the sender of email notifications, or the enabled +`feature toggles`_. Defining these values in ``parameters.yml`` file would +add an extra layer of configuration that's not needed because you don't need +or want these configuration values to change on each server. + +The configuration options defined in the ``config.yml`` file usually vary from +one `execution environment`_ to another. That's why Symfony already includes +``app/config/config_dev.yml`` and ``app/config/config_prod.yml`` files so +that you can override specific values for each environment. + +Constants vs Configuration Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +One of the most common errors when defining application configuration is to +create new options for values that never change, such as the number of items for +paginated results. + +.. best-practice:: + + Use constants to define configuration options that rarely change. + +The traditional approach for defining configuration options has caused many +Symfony apps to include an option like the following, which would be used +to control the number of posts to display on the blog homepage: + +.. code-block:: yaml + + # app/config/config.yml + parameters: + homepage.num_items: 10 + +If you ask yourself when the last time was that you changed the value of +*any* option like this, odds are that you *never* have. Creating a configuration +option for a value that you are never going to configure just isn't necessary. +Our recommendation is to define these values as constants in your application. +You could, for example, define a ``NUM_ITEMS`` constant in the ``Post`` entity: + +.. code-block:: php + + // src/AppBundle/Entity/Post.php + namespace AppBundle\Entity; + + class Post + { + const NUM_ITEMS = 10; + + // ... + } + +The main advantage of defining constants is that you can use their values +everywhere in your application. When using parameters, they are only available +from places wih access to the Symfony container. + +Constants can be used for example in your Twig templates thanks to the +``constant()`` function: + +.. code-block:: html+jinja + +

+ Displaying the {{ constant('NUM_ITEMS', post) }} most recent results. +

+ +And Doctrine entities and repositories can now easily access these values, +whereas they cannot access the container parameters: + +.. code-block:: php + + namespace AppBundle\Repository; + + use Doctrine\ORM\EntityRepository; + use AppBundle\Entity\Post; + + class PostRepository extends EntityRepository + { + public function findLatest($limit = Post::NUM_ITEMS) + { + // ... + } + } + +The only notable disadvantage of using constants for this kind of configuration +values is that you cannot redefine them easily in your tests. + +Semantic Configuration: Don't Do It +----------------------------------- + +.. best-practice:: + + Don't define a semantic dependency injection configuration for your bundles. + +As explained in `How to Expose a semantic Configuration for a Bundle`_ article, +Symfony bundles have two choices on how to handle configuration: normal service +configuration through the ``services.yml`` file and semantic configuration +through a special ``*Extension`` class. + +Although semantic configuration is much more powerful and provides nice features +such as configuration validation, the amount of work needed to define that +configuration isn't worth it for bundles that aren't meant to be shared as +third-party bundles. + +Moving Sensitive Options Outside of Symfony Entirely +---------------------------------------------------- + +When dealing with sensitive options, like database credentials, we also recommend +that you store them outside the Symfony project and make them available +through environment variables. Learn how to do it in the following article: +`How to Set external Parameters in the Service Container`_ + +.. _`feature toggles`: http://en.wikipedia.org/wiki/Feature_toggle +.. _`execution environment`: http://symfony.com/doc/current/cookbook/configuration/environments.html +.. _`constant() function`: http://twig.sensiolabs.org/doc/functions/constant.html +.. _`How to Expose a semantic Configuration for a Bundle`: http://symfony.com/doc/current/cookbook/bundles/extension.html +.. _`How to Set external Parameters in the Service Container`: http://symfony.com/doc/current/cookbook/configuration/external_parameters.html diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst new file mode 100644 index 00000000000..05cd7a1af9b --- /dev/null +++ b/best_practices/controllers.rst @@ -0,0 +1,212 @@ +Controllers +=========== + +Symfony follows the philosophy of *"thin controllers and fat models"*. This +means that controllers should hold just the thin layer of *glue-code* +needed to coordinate the different parts of the application. + +As a rule of thumb, you should follow the 5-10-20 rule, where controllers should +only define 5 variables or less, contain 10 actions or less and include 20 lines +of code or less in each action. This isn't an exact science, but it should +help you realize when code should be refactored out of the controller and +into a service. + +.. best-practice:: + + Make your controller extend the ``FrameworkBundle`` base Controller and + use annotations to configure routing, caching and security whenever possible. + +Coupling the controllers to the underlying framework allows you to leverage +all of its features and increases your productivity. + +And since your controllers should be thin and contain nothing more than a +few lines of *glue-code*, spending hours trying to decouple them from your +framework doesn't benefit you in the long run. The amount of time *wasted* +isn't worth the benefit. + +In addition, using annotations for routing, caching and security simplifies +configuration. You don't need to browse tens of files created with different +formats (YAML, XML, PHP): all the configuration is just where you need it +and it only uses one format. + +Overall, this means you should aggressively decouple your business logic +from the framework while, at the same time, aggressively coupling your controllers +and routing *to* the framework in order to get the most out of it. + +Routing Configuration +--------------------- + +To load routes defined as annotations in your controllers, add the following +configuration to the main routing configuration file: + +.. code-block:: yaml + + # app/config/routing.yml + app: + resource: "@AppBundle/Controller/" + type: annotation + +This configuration will load annotations from any controller stored inside the +``src/AppBundle/Controller/`` directory and even from its subdirectories. +So if your application defines lots of controllers, it's perfectly ok to +reorganize them into subdirectories: + +.. code-block:: text + + / + ├─ ... + └─ src/ + └─ AppBundle/ + ├─ ... + └─ Controller/ + ├─ DefaultController.php + ├─ ... + ├─ Api/ + │ ├─ ... + │ └─ ... + └─ Backend/ + ├─ ... + └─ ... + +Template Configuration +---------------------- + +.. best-practice:: + + Don't use the ``@Template()`` annotation to configure the template used by + the controller. + +The ``@Template`` annotation is useful, but also involves some magic. For +that reason, we don't recommend using it. + +Most of the time, ``@Template`` is used without any parameters, which makes +it more difficult to know which template is being rendered. It also makes +it less obvious to beginners that a controller should always return a Response +object (unless you're using a view layer). + +Lastly, the ``@Template`` annotation uses a ``TemplateListener`` class that hooks +into the ``kernel.view`` event dispatched by the framework. This listener introduces +a measurable performance impact. In the sample blog application, rendering the +homepage took 5 milliseconds using the ``$this->render()`` method and 26 milliseconds +using the ``@Template`` annotation. + +How the Controller Looks +------------------------ + +Considering all this, here is an example of how the controller should look +for the homepage of our app: + +.. code-block:: php + + namespace AppBundle\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + + class DefaultController extends Controller + { + /** + * @Route("/", name="homepage") + */ + public function indexAction() + { + $em = $this->getDoctrine()->getManager(); + $posts = $em->getRepository('App:Post')->findLatest(); + + return $this->render('default/index.html.twig', array( + 'posts' => $posts + )); + } + } + +.. _best-practices-paramconverter: + +Using the ParamConverter +------------------------ + +If you're using Doctrine, then you can *optionally* use the `ParamConverter`_ +to automatically query for an entity and pass it as an argument to your controller. + +.. best-practice:: + + Use the ParamConverter trick to automatically query for Doctrine entities + when it's simple and convenient. + +For example: + +.. code-block:: php + + /** + * @Route("/{id}", name="admin_post_show") + */ + public function showAction(Post $post) + { + $deleteForm = $this->createDeleteForm($post); + + return $this->render('admin/post/show.html.twig', array( + 'post' => $post, + 'delete_form' => $deleteForm->createView(), + )); + } + +Normally, you'd expect a ``$id`` argument to ``showAction``. Instead, by +creating a new argument (``$post``) and type-hinting it with the ``Post`` +class (which is a Doctrine entity), the ParamConverter automatically queries +for an object whose ``$id`` property matches the ``{id}`` value. It will +also show a 404 page if no ``Post`` can be found. + +When Things Get More Advanced +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This works without any configuration because the wildcard name ``{id}`` matches +the name of the property on the entity. If this isn't true, or if you have +even more complex logic, the easiest thing to do is just query for the entity +manually. In our application, we have this situation in ``CommentController``: + +.. code-block:: php + + /** + * @Route("/comment/{postSlug}/new", name = "comment_new") + */ + public function newAction(Request $request, $postSlug) + { + $post = $this->getDoctrine() + ->getRepository('AppBundle:Post') + ->findOneBy(array('slug' => $postSlug)); + + if (!$post) { + throw $this->createNotFoundException(); + } + + // ... + } + +You can also use the ``@ParamConverter`` configuration, which is infinitely +flexible: + +.. code-block:: php + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; + + /** + * @Route("/comment/{postSlug}/new", name = "comment_new") + * @ParamConverter("post", options={"mapping": {"postSlug": "slug"}}) + */ + public function newAction(Request $request, Post $post) + { + // ... + } + +The point is this: the ParamConverter shortcut is great for simple situations. +But you shouldn't forget that querying for entities directly is still very +easy. + +Pre and Post Hooks +------------------ + +If you need to execute some code before or after the execution of your controllers, +you can use the EventDispatcher component to `set up before/after filters`_. + +.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`set up before/after filters`: http://symfony.com/doc/current/cookbook/event_dispatcher/before_after_filters.html diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst new file mode 100644 index 00000000000..244cb6741b4 --- /dev/null +++ b/best_practices/creating-the-project.rst @@ -0,0 +1,252 @@ +Creating the Project +==================== + +Installing Symfony +------------------ + +There is only one recommended way to install Symfony: + +.. best-practice:: + + Always use `Composer`_ to install Symfony. + +Composer is the dependency manager used by modern PHP applications. Adding or +removing requirements for your project and updating the third-party libraries +used by your code is a breeze thanks to Composer. + +Dependency Management with Composer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before installing Symfony, you need to make sure that you have Composer installed +globally. Open your terminal (also called *command console*) and run the following +command: + +.. code-block:: bash + + $ composer --version + Composer version 1e27ff5e22df81e3cd0cd36e5fdd4a3c5a031f4a 2014-08-11 15:46:48 + +You'll probably see a different version identifier. Never mind because Composer +is updated on a continuous basis and its specific version doesn't matter. + +Installing Composer Globally +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In case you don't have Composer installed globally, execute the following two +commands if you use Linux or Mac OS X (the second command will ask for your +user password): + +.. code-block:: bash + + $ curl -sS https://getcomposer.org/installer | php + $ sudo mv composer.phar /usr/local/bin/composer + +.. note:: + + Depending on your Linux distribution, you may need to execute ``su`` command + instead of ``sudo``. + +If you use a Windows system, download the executable installer from the +`Composer download page`_ and follow the steps to install it. + +Creating the Blog Application +----------------------------- + +Now that everything is correctly set up, you can create a new project based on +Symfony. In your command console, browse to a directory where you have permission +to create files and execute the following commands: + +.. code-block:: bash + + $ cd projects/ + $ composer create-project symfony/framework-standard-edition blog/ + +This command will create a new directory called ``blog`` that will contain +a fresh new project based on the most recent stable Symfony version available. + +Checking the Symfony Installation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once the installation is finished, enter the ``blog/`` directory and check that +Symfony is correctly installed by executing the following command: + +.. code-block:: bash + + $ cd blog/ + $ php app/console --version + + Symfony version 2.6.* - app/dev/debug + +If you see the installed Symfony version, everything worked as expected. If not, +you can execute the following *script* to check what does prevent your system +from correctly executing Symfony applications: + +.. code-block:: bash + + $ php app/check.php + +Depending on your system, you can see up to two different lists when executing the +`check.php` script. The first one shows the mandatory requirements which your +system must meet to execute Symfony applications. The second list shows the +optional requirements suggested for an optimal execution of Symfony applications: + +.. code-block:: bash + + Symfony2 Requirements Checker + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + > PHP is using the following php.ini file: + /usr/local/zend/etc/php.ini + + > Checking Symfony requirements: + .....E.........................W..... + + [ERROR] + Your system is not ready to run Symfony2 projects + + Fix the following mandatory requirements + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * date.timezone setting must be set + > Set the "date.timezone" setting in php.ini* (like Europe/Paris). + + Optional recommendations to improve your setup + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + * short_open_tag should be disabled in php.ini + > Set short_open_tag to off in php.ini*. + + +.. tip:: + + Symfony releases are digitally signed for security reasons. If you want to + verify the integrity of your Symfony installation, take a look at the + `public checksums repository`_ and follow `these steps`_ to verify the + signatures. + +Structuring the Application +--------------------------- + +After creating the application, enter the ``blog/`` directory and you'll see a +number of files and directories generated automatically: + +.. code-block:: text + + blog/ + ├─ app/ + │ ├─ console + │ ├─ cache/ + │ ├─ config/ + │ ├─ logs/ + │ └─ Resources/ + ├─ src/ + │ └─ AppBundle/ + ├─ vendor/ + └─ web/ + +This file and directory hierarchy is the convention proposed by Symfony to +structure your applications. The recommended purpose of each directory is the +following: + +* ``app/cache/``, stores all the cache files generated by the application; +* ``app/config/``, stores all the configuration defined for any environment; +* ``app/logs/``, stores all the log files generated by the application; +* ``app/Resources/``, stores all the templates and the translation files for the + application; +* ``src/AppBundle/``, stores the Symfony specific code (controllers and routes), + your domain code (e.g. Doctrine classes) and all your business logic; +* ``vendor/``, this is the directory where Composer installs the application's + dependencies and you should never modify any of its contents; +* ``web/``, stores all the front controller files and all the web assets, such + as stylesheets, JavaScript files and images. + +Application Bundles +~~~~~~~~~~~~~~~~~~~ + +When Symfony 2.0 was released, most developers naturally adopted the symfony +1.x way of dividing applications into logical modules. That's why many Symfony +apps use bundles to divide their code into logical features: ``UserBundle``, +``ProductBundle``, ``InvoiceBundle``, etc. + +But a bundle is *meant* to be something that can be reused as a stand-alone +piece of software. If ``UserBundle`` cannot be used *"as is"* in other Symfony +apps, then it shouldn't be its own bundle. Moreover ``InvoiceBundle`` depends +on ``ProductBundle``, then there's no advantage to having two separate bundles. + +.. best-practice:: + + Create only one bundle called ``AppBundle`` for your application logic + +Implementing a single ``AppBundle`` bundle in your projects will make your code +more concise and easier to understand. Starting in Symfony 2.6, the official +Symfony documentation uses the ``AppBundle`` name. + +.. note:: + + There is no need to prefix the ``AppBundle`` with your own vendor (e.g. + ``AcmeAppBundle``), because this application bundle is never going to be + shared. + +All in all, this is the typical directory structure of a Symfony application +that follows these best practices: + +.. code-block:: text + + blog/ + ├─ app/ + │ ├─ console + │ ├─ cache/ + │ ├─ config/ + │ ├─ logs/ + │ └─ Resources/ + ├─ src/ + │ └─ AppBundle/ + ├─ vendor/ + └─ web/ + ├─ app.php + └─ app_dev.php + +.. tip:: + + If you are using Symfony 2.6 or a newer version, the ``AppBundle`` bundle + is already generated for you. If you are using an older Symfony version, + you can generate it by hand executing this command: + + .. code-block:: bash + + $ php app/console generate:bundle --namespace=AppBundle --dir=src --format=annotation --no-interaction + +Extending the Directory Structure +--------------------------------- + +If your project or infrastructure requires some changes to the default directory +structure of Symfony, you can `override the location of the main directories`_: +``cache/``, ``logs/`` and ``web/``. + +In addition, Symfony3 will use a slightly different directory structure when +it's released: + +.. code-block:: text + + blog-symfony3/ + ├─ app/ + │ ├─ config/ + │ └─ Resources/ + ├─ bin/ + │ └─ console + ├─ src/ + ├─ var/ + │ ├─ cache/ + │ └─ logs/ + ├─ vendor/ + └─ web/ + +The changes are pretty superficial, but for now, we recommend that you use +the Symfony2 directory structure. + +.. _`Composer`: https://getcomposer.org/ +.. _`Get Started`: https://getcomposer.org/doc/00-intro.md +.. _`Composer download page`: https://getcomposer.org/download/ +.. _`override the location of the main directories`: http://symfony.com/doc/current/cookbook/configuration/override_dir_structure.html +.. _`public checksums repository`: https://github.com/sensiolabs/checksums +.. _`these steps`: http://fabien.potencier.org/article/73/signing-project-releases diff --git a/best_practices/forms.rst b/best_practices/forms.rst new file mode 100644 index 00000000000..6d70561e914 --- /dev/null +++ b/best_practices/forms.rst @@ -0,0 +1,231 @@ +Forms +===== + +Forms are one of the most misused Symfony components due to its vast scope and +endless list of features. In this chapter we'll show you some of the best +practices so you can leverage forms but get work done quickly. + +Building Forms +-------------- + +.. best-practice:: + + Define your forms as PHP classes. + +The Form component allows you to build forms right inside your controller +code. Honestly, unless you need to reuse the form somewhere else, that's +totally fine. But for organize and reuse, we recommend that you define each +form in its own PHP class: + +.. code-block:: php + + namespace AppBundle\Form; + + use Symfony\Component\Form\AbstractType; + use Symfony\Component\Form\FormBuilderInterface; + use Symfony\Component\OptionsResolver\OptionsResolverInterface; + + class PostType extends AbstractType + { + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + ->add('title') + ->add('summary', 'textarea') + ->add('content', 'textarea') + ->add('authorEmail', 'email') + ->add('publishedAt', 'datetime') + ; + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => 'AppBundle\Entity\Post' + )); + } + + public function getName() + { + return 'post'; + } + } + +To use the class, use ``createForm`` and instantiate the new class: + +.. code-block:: php + + use AppBundle\Form\PostType; + // ... + + public function newAction(Request $request) + { + $post = new Post(); + $form = $this->createForm(new PostType(), $post); + + // ... + } + +Registering Forms as Services +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can also `register your form type as a service`_. But this is *not* recommended +unless you plan to reuse the new form type in many places or embed it in +other forms directly or via the `collection type`_. + +For most forms that are used only to edit or create something, registering +the form as a service is over-kill, and makes it more difficult to figure +out exactly which form class is being used in a controller. + +Form Button Configuration +------------------------- + +Form classes should try to be agnostic to *where* they will be used. This +makes them easier to re-use later. + +.. best-practice:: + + Add buttons in the templates, not in the form classes or the controllers. + +Since Symfony 2.5, you can add buttons as fields on your form. This is a nice +way to simplify the template that renders your form. But if you add the buttons +directly in your form class, this would effectively limit the scope of that form: + +.. code-block:: php + + class PostType extends AbstractType + { + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder + // ... + ->add('save', 'submit', array('label' => 'Create Post')) + ; + } + + // ... + } + +This form *may* have been designed for creating posts, but if you wanted +to reuse it for editing posts, the button label would be wrong. Instead, +some developers configure form buttons in the controller: + +.. code-block:: php + + namespace AppBundle\Controller\Admin; + + use Symfony\Component\HttpFoundation\Request; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use AppBundle\Entity\Post; + use AppBundle\Form\PostType; + + class PostController extends Controller + { + // ... + + public function newAction(Request $request) + { + $post = new Post(); + $form = $this->createForm(new PostType(), $post); + $form->add('submit', 'submit', array( + 'label' => 'Create', + 'attr' => array('class' => 'btn btn-default pull-right') + )); + + // ... + } + } + +This is also an important error, because you are mixing presentation markup +(labels, CSS classes, etc.) with pure PHP code. Separation of concerns is +always a good practice to follow, so put all the view-related things in the +view layer: + +.. code-block:: html+jinja + +
+ {{ form_widget(form) }} + + +
+ +Rendering the Form +------------------ + +There are a lot of ways to render your form, ranging from rendering the entire +thing in one line to rendering each part of each field independently. The +best way depends on how much customization you need. + +The simplest way - which is especially useful during development - is to render +the form tags manually and then use ``form_widget()`` to render all of the fields: + +.. code-block:: html+jinja + +
+ {{ form_widget(form) }} +
+ +.. best-practice:: + + Don't use the ``form()`` or ``form_start()`` functions to render the + starting and ending form tags. + +Experienced Symfony developers will recognize that we're rendering the ``
`` +tags manually instead of using the ``form_start()`` or ``form()`` functions. +While those are convenient, they take away from some clarity with little +benefit. + +.. tip:: + + The exception is a delete form because it's really just one button and + so benefits from some of these extra shortcuts. + +If you need more control over how your fields are rendered, then you should +remove the ``form_widget(form)`` function and render your fields individually. +See `How to Customize Form Rendering`_ for more information on this and how +you can control *how* the form renders at a global level using form theming. + +Handling Form Submits +--------------------- + +Handling a form submit usually follows a similar template: + +.. code-block:: php + + public function newAction(Request $request) + { + // build the form ... + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($post); + $em->flush(); + + return $this->redirect($this->generateUrl( + 'admin_post_show', + array('id' => $post->getId()) + )); + } + + // render the template + } + +There are really only two notable things here. First, we recommend that you +use a single action for both rendering the form and handling the form submit. +For example, you *could* have a ``newAction`` that *only* renders the form +and a ``createAction`` that *only* processes the form submit. Both those +actions will be almost identical. So it's much simpler to let ``newAction`` +handle everything. + +Second, we recommend using ``$form->isSubmitted()`` in the ``if`` statement +for clarity. This isn't technically needed, since ``isValid()`` first calls +``isSubmitted()``. But without this, the flow doesn't read well as it *looks* +like the form is *always* processed (even on the GET request). + +.. _`register your form type as a service`: http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html#creating-your-field-type-as-a-service +.. _`collection type`: http://symfony.com/doc/current/reference/forms/types/collection.html +.. _`How to Customize Form Rendering`: http://symfony.com/doc/current/cookbook/form/form_customization.html +.. _`form event system`: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html diff --git a/best_practices/i18n.rst b/best_practices/i18n.rst new file mode 100644 index 00000000000..1e7a86f3196 --- /dev/null +++ b/best_practices/i18n.rst @@ -0,0 +1,96 @@ +Internationalization +==================== + +Internationalization and localization adapt the applications and their contents +to the specific region or language of the users. In Symfony this is an opt-in +feature that needs to be enabled before using it. To do this, uncomment the +following ``translator`` configuration option and set your application locale: + +.. code-block:: yaml + + # app/config/config.yml + framework: + # ... + translator: { fallback: "%locale%" } + + # app/config/parameters.yml + parameters: + # ... + locale: en + +Translation Source File Format +------------------------------ + +The Symfony Translation component supports lots of different translation +formats: PHP, Qt, ``.po``, ``.mo``, JSON, CSV, INI, etc. + +.. best-practice:: + + Use the XLIFF format for your translation files. + +Of all the available translation formats, only XLIFF and gettext have broad +support in the tools used by professional translators. And since it's based +on XML, you can validate XLIFF file contents as you write them. + +Symfony 2.6 added support for notes inside XLIFF files, making them more +user-friendly for translators. At the end, good translations are all about +context, and these XLIFF notes allow you to define that context. + +.. tip:: + + The Apache-licensed `JMSTranslationBundle`_ offers you a web interface for + viewing and editing these translation files. It also has advanced extractors + that can read your project and automatically update the XLIFF files. + +Translation Source File Location +-------------------------------- + +.. best-practice:: + + Store the translation files in the ``app/Resources/translations/`` directory. + +Traditionally, Symfony developers have created these files in the +``Resources/translations/`` directory of each bundle. + +But since the ``app/Resources/`` directory is considered the global location +for the application's resources, storing translations in ``app/Resources/translations/`` +centralizes them *and* gives them priority over any other translation file. +This lets you override translations defined in third-party bundles. + +Translation Keys +---------------- + +.. best-practice:: + + Always use keys for translations instead of content strings. + +Using keys simplifies the management of the translation files because you +can change the original contents without having to update all of the translation +files. + +Keys should always describe their *purpose* and *not* their location. For +example, if a form has a field with the label "Username", then a nice key +would be ``label.username``, *not* ``edit_form.label.username``. + +Example Translation File +------------------------ + +Applying all the previous best practices, the sample translation file for +English in the application would be: + +.. code-block:: xml + + + + + + + + title.post_list + Post List + + + + + +.. _`JMSTranslationBundle`: https://github.com/schmittjoh/JMSTranslationBundle diff --git a/best_practices/index.rst b/best_practices/index.rst new file mode 100644 index 00000000000..b2790280998 --- /dev/null +++ b/best_practices/index.rst @@ -0,0 +1,17 @@ +Official Symfony Best Practices +=============================== + +.. toctree:: + :hidden: + + introduction + creating-the-project + configuration + business-logic + controllers + templates + forms + i18n + security + web-assets + tests diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst new file mode 100644 index 00000000000..1c5cb3055dd --- /dev/null +++ b/best_practices/introduction.rst @@ -0,0 +1,98 @@ +The Symfony Framework Best Practices +==================================== + +The Symfony framework is well-known for being *really* flexible and is used +to build micro-sites, enterprise applications that handle billions of connections +and even as the basis for *other* frameworks. Since its release in July 2011, +the community has learned a lot about what's possible and how to do things *best*. + +These community resources - like blog posts or presentations - have created +an unofficial set of recommendations for developing Symfony applications. +Unfortunately, a lot of these recommendations are unneeded for web applications. +Much of the time, they unnecessarily overcomplicate things and don't follow the +original pragmatic philosophy of Symfony. + +What is this Guide About? +------------------------- + +This guide aims to fix that by describing the **official best practices for +developing web apps with the Symfony full-stack framework**. These are best- +practices that fit the philosophy of the framework as envisioned by its original +creator `Fabien Potencier`_. + +.. note:: + + **Best practice** is a noun that means *"a well defined procedure that is + known to produce near-optimum results"*. And that's exactly what this + guide aims to provide. Even if you don't agree with every recommendation, + we believe these will help you build great applications with less complexity. + +This guide is **specially suited** for: + +* Websites and web applications developed with the full-stack Symfony framework. + +For other situations, this guide might be a good **starting point** that you can +then **extend and fit to your specific needs**: + +* Bundles shared publicly to the Symfony community; +* Advanced developers or teams who have created their own standards; +* Some complex applications that have highly customized requirements; +* Bundles that may be shared internally within a company. + +We know that old habits die hard and some of you will be shocked by some +of these best practices. But by following these, you'll be able to develop +apps faster, with less complexity and with the same or even higher quality. +It's also a moving target that will continue to improve. + +Keep in mind that these are **optional recommendations** that you and your +team may or may not follow to develop Symfony applications. If you want to +continue using your own best practices and methodologies, you can of course +do it. Symfony is flexible enough to adapt to your needs. That will never +change. + +Who this Book Is for (Hint: It's not a Tutorial) +------------------------------------------------ + +Any Symfony developer, whether you are an expert or a newcomer, can read this +guide. But since this isn't a tutorial, you'll need some basic knowledge of +Symfony to follow everything. If you are totally new to Symfony, welcome! +Start with `The Quick Tour`_ tutorial first. + +We've deliberately kept this guide short. We won't repeat explanations that +you can find in the vast Symfony documentation, like discussions about dependency +injection or front controllers. We'll solely focus on explaining how to do +what you already know. + +The Application +--------------- + +In addition to this guide, you'll find a sample application developed with +all these best practices in mind. **The application is a simple blog engine**, +because that will allow us to focus on the Symfony concepts and features without +getting buried in difficult details. + +Instead of developing the application step by step in this guide, you'll find +selected snippets of code through the chapters. Please refer to the last chapter +of this guide to find more details about this application and the instructions +to install it. + +Don't Update Your Existing Applications +--------------------------------------- + +After reading this handbook, some of you may be considering refactoring your +existing Symfony applications. Our recommendation is sound and clear: **you +should not refactor your existing applications to comply with these best +practices**. The reasons for not doing it are various: + +* Your existing applications are not wrong, they just follow another set of + guidelines; +* A full codebase refactorization is prone to introduce errors in your + applications; +* The amount of work spent on this could be better dedicated to improving + your tests or adding features that provide real value to the end users. + +.. _`Fabien Potencier`: https://connect.sensiolabs.com/profile/fabpot +.. _`The Quick Tour`: http://symfony.com/doc/current/quick_tour/the_big_picture.html +.. _`The Official Symfony Book`: http://symfony.com/doc/current/book/index.html +.. _`The Symfony Cookbook`: http://symfony.com/doc/current/cookbook/index.html +.. _`github.com/.../...`: http://github.com/.../... diff --git a/best_practices/security.rst b/best_practices/security.rst new file mode 100644 index 00000000000..026c672bcaa --- /dev/null +++ b/best_practices/security.rst @@ -0,0 +1,363 @@ +Security +======== + +Authentication and Firewalls (i.e. Getting the User's Credentials) +------------------------------------------------------------------ + +You can configure Symfony to authenticate your users using any method you +want and to load user information from any source. This is a complex topic, +but the `Security Cookbook Section`_ has a lot of information about this. + +Regardless of your needs, authentication is configured in ``security.yml``, +primarily under the ``firewalls`` key. + +.. best-practice:: + + Unless you have two legitimately different authentication systems and + users (e.g. form login for the main site and a token system for your + API only), we recommend having only *one* firewall entry with the ``anonymous`` + key enabled. + +Most applications only have one authentication system and one set of users. +For this reason, you only need *one* firewall entry. There are exceptions +of course, especially if you have separated web and API sections on your +site. But the point is to keep things simple. + +Additionally, you should use the ``anonymous`` key under your firewall. If +you need to require users to be logged in for different sections of your +site (or maybe nearly *all* sections), use the ``access_control`` area. + +.. best-practice:: + + Use the ``bcrypt`` encoder for encoding your users' passwords. + +If your users have a password, then we recommend encoding it using the ``bcrypt`` +encoder, instead of the traditional SHA-512 hashing encoder. The main advantages +of ``bcrypt`` are the inclusion of a *salt* value to protect against rainbow +table attacks, and its adaptive nature, which allows to make it slower to +remain resistant to brute-force search attacks. + +With this in mind, here is the authentication setup from our application, +which uses a login form to load users from the database: + +.. code-block:: yaml + + security: + encoders: + AppBundle\Entity\User: bcrypt + + providers: + database_users: + entity: { class: AppBundle:User, property: username } + + firewalls: + secured_area: + pattern: ^/ + anonymous: true + form_login: + check_path: security_login_check + login_path: security_login_form + + logout: + path: security_logout + target: homepage + + # ... access_control exists, but is not shown here + +.. tip:: + + The source code for our project contains comments that explain each part. + +Authorization (i.e. Denying Access) +----------------------------------- + +Symfony gives you several ways to enforce authorization, including the ``access_control`` +configuration in `security.yml`_, the :ref:`@Security annotation ` +and using :ref:`isGranted ` on the ``security.context`` +service directly. + +.. best-practice:: + + * For protecting broad URL patterns, use ``access_control``; + * Whenever possible, use the ``@Security`` annotation; + * Check security directly on the ``security.context`` service whenever + you have a more complex situation. + +There are also different ways to centralize your authorization logic, like +with a custom security voter or with ACL. + +.. best-practice:: + + * For fine-grained restrictions, define a custom security voter; + * For restricting access to *any* object by *any* user via an admin + interface, use the Symfony ACL. + +.. _best-practices-security-annotation: + +The @Security Annotation +------------------------ + +For controlling access on a controller-by-controller basis, use the ``@Security`` +annotation whenever possible. It's easy to read and is placed consistently +above each action. + +In our application, you need the ``ROLE_ADMIN`` in order to create a new post. +Using ``@Security``, this looks like: + +.. code-block:: php + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; + // ... + + /** + * Displays a form to create a new Post entity. + * + * @Route("/new", name="admin_post_new") + * @Security("has_role('ROLE_ADMIN')") + */ + public function newAction() + { + // ... + } + +Using Expressions for Complex Security Restrictions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your security logic is a little bit more complex, you can use an `expression`_ +inside ``@Security``. In the following example, a user can only access the +controller if their email matches the value returned by the ``getAuthorEmail`` +method on the ``Post`` object: + +.. code-block:: php + + use AppBundle\Entity\Post; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; + + /** + * @Route("/{id}/edit", name="admin_post_edit") + * @Security("user.getEmail() == post.getAuthorEmail()") + */ + public function editAction(Post $post) + { + // ... + } + +Notice that this requires the use of the `ParamConverter`_, which automatically +queries for the ``Post`` object and puts it on the ``$post`` argument. This +is what makes it possible to use the ``post`` variable in the expression. + +This has one major drawback: an expression in an annotation cannot easily +be reused in other parts of the application. Imagine that you want to add +a link in a template that will only be seen by authors. Right now you'll +need to repeat the expression code using Twig syntax: + +.. code-block:: html+jinja + + {% if app.user and app.user.email == post.authorEmail %} + ... + {% endif %} + +The easiest solution - if your logic is simple enough - is to add a new method +to the ``Post`` entity that checks if a given user is its author: + +.. code-block:: php + + // src/AppBundle/Entity/Post.php + // ... + + class Post + { + // ... + + /** + * Is the given User the author of this Post? + * + * @return bool + */ + public function isAuthor(User $user = null) + { + return $user && $user->getEmail() == $this->getAuthorEmail(); + } + } + +Now you can reuse this method both in the template and in the security expression: + +.. code-block:: php + + use AppBundle\Entity\Post; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; + + /** + * @Route("/{id}/edit", name="admin_post_edit") + * @Security("post.isAuthor(user)") + */ + public function editAction(Post $post) + { + // ... + } + +.. code-block:: html+jinja + + {% if post.isAuthor(app.user) %} + ... + {% endif %} + +.. _best-practices-directy-isGranted: + +Checking Permissions without @Security +-------------------------------------- + +The above example with ``@Security`` only works because we're using the +:ref:`ParamConverter `, which gives the expression +access to the a ``post`` variable. If you don't use this, or have some other +more advanced use-case, you can always do the same security check in PHP: + +.. code-block:: php + + /** + * @Route("/{id}/edit", name="admin_post_edit") + */ + public function editAction($id) + { + $post = $this->getDoctrine()->getRepository('AppBundle:Post') + ->find($id); + + if (!$post) { + throw $this->createNotFoundException(); + } + + if (!$post->isAuthor($this->getUser())) { + throw $this->createAccessDeniedException(); + } + + // ... + } + +Security Voters +--------------- + +If your security logic is complex and can't be centralized into a method +like ``isAuthor()``, you should leverage custom voters. These are an order +of magnitude easier than `ACL's`_ and will give you the flexibility you need +in almost all cases. + +First, create a voter class. The following example shows a voter that implements +the same ``getAuthorEmail`` logic you used above: + +.. code-block:: php + + namespace AppBundle\Security; + + use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; + use Symfony\Component\Security\Core\User\UserInterface; + + // AbstractVoter class requires Symfony 2.6 or higher version + class PostVoter extends AbstractVoter + { + const CREATE = 'create'; + const EDIT = 'edit'; + + protected function getSupportedAttributes() + { + return array(self::CREATE, self::EDIT); + } + + protected function getSupportedClasses() + { + return array('AppBundle\Entity\Post'); + } + + protected function isGranted($attribute, $post, $user = null) + { + if (!$user instanceof UserInterface) { + return false; + } + + if ($attribute === self::CREATE && in_array('ROLE_ADMIN', $user->getRoles(), true)) { + return true; + } + + if ($attribute === self::EDIT && $user->getEmail() === $post->getAuthorEmail()) { + return true; + } + + return false; + } + } + +To enable the security voter in the application, define a new service: + +.. code-block:: yaml + + # app/config/services.yml + services: + # ... + post_voter: + class: AppBundle\Security\PostVoter + public: false + tags: + - { name: security.voter } + +Now, you can use the voter with the ``@Security`` annotation: + +.. code-block:: php + + /** + * @Route("/{id}/edit", name="admin_post_edit") + * @Security("is_granted('edit', post)") + */ + public function editAction(Post $post) + { + // ... + } + +You can also use this directly with the ``security.context`` service, or +via the even easier shortcut in a controller: + +.. code-block:: php + + /** + * @Route("/{id}/edit", name="admin_post_edit") + */ + public function editAction($id) + { + $post = // query for the post ... + + if (!$this->get('security.context')->isGranted('edit', $post)) { + throw $this->createAccessDeniedException(); + } + } + +Learn More +---------- + +The `FOSUserBundle`_, developed by the Symfony community, adds support for a +database-backed user system in Symfony2. It also handles common tasks like +user registration and forgotten password functionality. + +Enable the `Remember Me feature`_ to allow your users to stay logged in for +a long period of time. + +When providing customer support, sometimes it's necessary to access the application +as some *other* user so that you can reproduce the problem. Symfony provides +the ability to `impersonate users`_. + +If your company uses a user login method not supported by Symfony, you can +develop `your own user provider`_ and `your own authentication provider`_. + +.. _`Security Cookbook Section`: http://symfony.com/doc/current/cookbook/security/index.html +.. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html +.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html +.. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html +.. _`security voter`: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html +.. _`Acces Control List`: http://symfony.com/doc/current/cookbook/security/acl.html +.. _`ACL's`: http://symfony.com/doc/current/cookbook/security/acl.html +.. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html +.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle +.. _`Remember Me feature`: http://symfony.com/doc/current/cookbook/security/remember_me.html +.. _`impersonate users`: http://symfony.com/doc/current/cookbook/security/impersonating_user.html +.. _`your own user provider`: http://symfony.com/doc/current/cookbook/security/custom_provider.html +.. _`your own authentication provider`: http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html diff --git a/best_practices/templates.rst b/best_practices/templates.rst new file mode 100644 index 00000000000..a53b0a3046f --- /dev/null +++ b/best_practices/templates.rst @@ -0,0 +1,164 @@ +Templates +========= + +When PHP was created 20 years ago, developers loved its simplicity and how +well it blended HTML and dynamic code. But as time passed, other template +languages - like `Twig`_ - were created to make templating even better. + +.. best-practice:: + + Use Twig templating format for your templates. + +Generally speaking, PHP templates are much more verbose than in Twig because +they lack native support for lots of modern features needed by templates, +like inheritance, automatic escaping and named arguments for filters and +functions. + +Twig is the default templating format in Symfony and has the largest community +support of all non-PHP template engines (it's used in high profile projects +such as Drupal 8). + +In addition, Twig is the only template format with guaranteed support in Symfony +3.0. As a matter of fact, PHP may be removed from the officially supported +template engines. + +Template Locations +------------------ + +.. best-practice:: + + Store all your application's templates in ``app/Resources/views/`` directory. + +Traditionally, Symfony developers stored the application templates in the +``Resources/views/`` directory of each bundle. Then they used the logical name +to refer to them (e.g. ``AcmeDemoBundle:Default:index.html.twig``). + +But for the templates used in your application, it's much more convenient +to store them in the ``app/Resources/views/`` directory. For starters, this +drastically simplifies their logical names: + +================================================== ================================== +Templates stored inside bundles Templates stored in ``app/`` +================================================== ================================== +``AcmeDemoBunde:Default:index.html.twig`` ``default/index.html.twig`` +``::layout.html.twig`` ``layout.html.twig`` +``AcmeDemoBundle::index.html.twig`` ``index.html.twig`` +``AcmeDemoBundle:Default:subdir/index.html.twig`` ``default/subdir/index.html.twig`` +``AcmeDemoBundle:Default/subdir:index.html.twig`` ``default/subdir/index.html.twig`` +================================================== ================================== + +Another advantage is that centralizing your templates simplifies the work +of your designers. They don't need to look for templates in lots of directories +scattered through lots of bundles. + +Twig Extensions +--------------- + +.. best-practice:: + + Define your Twig extensions in the ``AppBundle/Twig/`` directory and + configure them using the ``app/config/services.yml`` file. + +Our application needs a custom ``md2html`` Twig filter so that we can transform +the Markdown contents of each post into HTML. + +To do this, first, install the excellent `Parsedown`_ Markdown parser as +a new dependency of the project: + +.. code-block:: bash + + $ composer require erusev/parsedown + +Then, create a new ``Markdown`` service that will be used later by the Twig +extension. The service definition only requires the path to the class: + +.. code-block:: yaml + + # app/config/services.yml + services: + # ... + markdown: + class: AppBundle\Utils\Markdown + +And the ``Markdown`` class just needs to define one single method to transform +Markdown content into HTML:: + + namespace AppBundle\Utils; + + class Markdown + { + private $parser; + + public function __construct() + { + $this->parser = new \Parsedown(); + } + + public function toHtml($text) + { + $html = $this->parser->text($text); + + return $html; + } + } + +Next, create a new Twig extension and define a new filter called ``md2html`` +using the ``Twig_SimpleFilter`` class. Inject the newly defined ``markdown`` +service in the constructor of the Twig extension: + +.. code-block:: php + + namespace AppBundle\Twig; + + use AppBundle\Utils\Markdown; + + class AppExtension extends \Twig_Extension + { + private $parser; + + public function __construct(Markdown $parser) + { + $this->parser = $parser; + } + + public function getFilters() + { + return array( + new \Twig_SimpleFilter( + 'md2html', + array($this, 'markdownToHtml'), + array('is_safe' => array('html')) + ), + ); + } + + public function markdownToHtml($content) + { + return $this->parser->toHtml($content); + } + + public function getName() + { + return 'app_extension'; + } + } + +Lastly define a new service to enable this Twig extension in the app (the service +name is irrelevant because you never use it in your own code): + +.. code-block:: yaml + + # app/config/services.yml + services: + app.twig.app_extension: + class: AppBundle\Twig\AppExtension + arguments: ["@markdown"] + tags: + - { name: twig.extension } + + +.. _`Twig`: http://twig.sensiolabs.org/ +.. _`Parsedown`: http://parsedown.org/ +.. _`Twig global variables`: http://symfony.com/doc/master/cookbook/templating/global_variables.html +.. _`override error pages`: http://symfony.com/doc/current/cookbook/controller/error_pages.html +.. _`render a template without using a controller`: http://symfony.com/doc/current/cookbook/templating/render_without_controller.html diff --git a/best_practices/tests.rst b/best_practices/tests.rst new file mode 100644 index 00000000000..0bbcbd665de --- /dev/null +++ b/best_practices/tests.rst @@ -0,0 +1,114 @@ +Tests +===== + +Roughly speaking, there are two types of test. Unit testing allows you to +test the input and output of specific functions. Functional testing allows +you to command a "browser" where you browse to pages on your site, click +links, fill out forms and assert that you see certain things on the page. + +Unit Tests +---------- + +Unit tests are used to test your "business logic", which should live in classes +that are independent of Symfony. For that reason, Symfony doesn't really +have an opinion on what tools you use for unit testing. However, the most +popular tools are `PhpUnit`_ and `PhpSpec`_. + +Functional Tests +---------------- + +Creating really good functional tests can be tough so some developers skip +these completely. Don't skip the functional tests! By defining some *simple* +functional tests, you can quickly spot any big errors before you deploy them: + +.. best-practice:: + + Define a functional test that at least checks if your application pages + are successfully loading. + +A functional test can be as easy as this: + +.. code-block:: php + + /** @dataProvider provideUrls */ + public function testPageIsSuccessful($url) + { + $client = self::createClient(); + $client->request('GET', $url); + + $this->assertTrue($client->getResponse()->isSuccessful()); + } + + public function provideUrls() + { + return array( + array('/'), + array('/posts'), + array('/post/fixture-post-1'), + array('/blog/category/fixture-category'), + array('/archives'), + // ... + ); + } + +This code checks that all the given URLs load successfully, which means that +their HTTP response status code is between ``200`` and ``299``. This may +not look that useful, but given how little effort this took, it's worth +having it in your application. + +In computer software, this kind of test is called `smoke testing`_ and consists +of *"preliminary testing to reveal simple failures severe enough to reject a +prospective software release"*. + +Hardcode URLs in a Functional Test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some of you may be asking why the previous functional test doesn't use the URL +generator service: + +.. best-practice:: + + Hardcode the URLs used in the functional tests instead of using the URL + generator. + +Consider the following functional test that uses the ``router`` service to +generate the URL of the tested page: + +.. code-block:: php + + public function testBlogArchives() + { + $client = self::createClient(); + $url = $client->getContainer()->get('router')->generate('blog_archives'); + $client->request('GET', $url); + + // ... + } + +This will work, but it has one *huge* drawback. If a developer mistakenly +changes the path of the ``blog_archives`` route, the test will still pass, +but the original (old) URL won't work! This means that any bookmarks for +that URL will be broken and you'll lose any search engine page ranking. + +Testing JavaScript Functionality +-------------------------------- + +The built-in functional testing client is great, but it can't be used to +test any JavaScript behavior on your pages. If you need to test this, consider +using the `Mink`_ library from within PHPUnit. + +Of course, if you have a heavy JavaScript frontend, you should consider using +pure JavaScript-based testing tools. + +Learn More about Functional Tests +--------------------------------- + +Consider using `Faker`_ and `Alice`_ libraries to generate real-looking data +for your test fixtures. + +.. _`Faker`: https://github.com/fzaninotto/Faker +.. _`Alice`: https://github.com/nelmio/alice +.. _`PhpUnit`: https://phpunit.de/ +.. _`PhpSpec`: http://www.phpspec.net/ +.. _`Mink`: http://mink.behat.org +.. _`smoke testing`: http://en.wikipedia.org/wiki/Smoke_testing_(software) diff --git a/best_practices/web-assets.rst b/best_practices/web-assets.rst new file mode 100644 index 00000000000..e77e3db7721 --- /dev/null +++ b/best_practices/web-assets.rst @@ -0,0 +1,97 @@ +Web Assets +========== + +Web assets are things like CSS, JavaScript and image files that make the +frontend of your site look and work great. Symfony developers have traditionally +stored these assets in the ``Resources/public/`` directory of each bundle. + +.. best-practice:: + + Store your assets in the ``web/`` directory. + +Scattering your web assets across tens of different bundles makes it more +difficult to manage them. Your designers' lives will be much easier if all +the application assets are in one location. + +Templates also benefit from centralizing your assets, because the links are +much more concise: + +.. code-block:: html+jinja + + + + + {# ... #} + + + + +.. note:: + + Keep in mind that ``web/`` is a public directory and that anything stored + here will be publicly accessible. For that reason, you should put your + compiled web assets here, but not their source files (e.g. SASS files). + +Using Assetic +------------- + +These days, you probably can't simply create static CSS and JavaScript files +and include them in your template. Instead, you'll probably want to combine +and minify these to improve client-side performance. You may also want to +use LESS or Sass (for example), which means you'll need some way to process +these into CSS files. + +A lot of tools exist to solve these problems, including pure-frontend (non-PHP) +tools like GruntJS. + +.. best-practice:: + + Use Assetic to compile, combine and minimize web assets, unless you're + comfortable with frontend tools like GruntJS. + +`Assetic`_ is an asset manager capable of compiling assets developed with +a lot of different frontend technologies like LESS, Sass and CoffeScript. +Combining all your assets with Assetic is a matter of wrapping all the assets +with a single Twig tag: + +.. code-block:: html+jinja + + {% stylesheets + 'css/bootstrap.min.css' + 'css/main.css' + filter='cssrewrite' output='css/compiled/all.css' %} + + {% endstylesheets %} + + {# ... #} + + {% javascripts + 'js/jquery.min.js' + 'js/bootstrap.min.js' + output='js/compiled/all.js' %} + + {% endjavascripts %} + +Frontend-Based Applications +--------------------------- + +Recently, frontend technologies like AngularJS have become pretty popular +for developing frontend web applications that talk to an API. + +If you are developing an application like this, you should use the tools +that are recommended by the technology, such as Bower and GruntJS. You should +develop your frontend application separately from your Symfony backend (even +separating the repositories if you want). + +Learn More about Assetic +------------------------ + +Assetic can also minimize CSS and JavaScript assets `using UglifyCSS/UglifyJS`_ +to speed up your websites. You can even `compress images`_ with Assetic to +reduce their size before serving them to the user. Check out the +`official Assetic documentation`_ to learn more about all the available features. + +.. _`Assetic`: http://symfony.com/doc/current/cookbook/assetic/asset_management.html +.. _`using UglifyCSS/UglifyJS`: http://symfony.com/doc/current/cookbook/assetic/uglifyjs.html +.. _`compress images`: http://symfony.com/doc/current/cookbook/assetic/jpeg_optimize.html +.. _`official Assetic documentation`: https://github.com/kriswallsmith/assetic From d317925909f9e1c8aae5b5a27ebb1d8773cb0ea1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 15 Oct 2014 15:17:05 +0200 Subject: [PATCH 221/835] Fixed some technical problems as suggested by Wouter --- best_practices/index.rst | 1 - index.rst | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/best_practices/index.rst b/best_practices/index.rst index b2790280998..e6e712f5852 100644 --- a/best_practices/index.rst +++ b/best_practices/index.rst @@ -2,7 +2,6 @@ Official Symfony Best Practices =============================== .. toctree:: - :hidden: introduction creating-the-project diff --git a/index.rst b/index.rst index 23959281381..cefdfe8c506 100644 --- a/index.rst +++ b/index.rst @@ -45,6 +45,16 @@ Cookbook Read the :doc:`Cookbook `. +Best Practices +-------------- + +.. toctree:: + :hidden: + + best_practices/index + +Read the :doc:`Official Best Practices `. + Components ---------- From f100fc0afe831ba66a52974066a34fb89ccad20f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 15 Oct 2014 15:40:40 +0200 Subject: [PATCH 222/835] Added an index directive to the first chapter --- best_practices/introduction.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst index 1c5cb3055dd..4700bb4559f 100644 --- a/best_practices/introduction.rst +++ b/best_practices/introduction.rst @@ -1,3 +1,6 @@ +.. index:: + single: Symfony Framework Best Practices + The Symfony Framework Best Practices ==================================== From 30b9c454a6f832d28b45b2dd736fe3d4768ab36e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 15 Oct 2014 16:17:47 +0200 Subject: [PATCH 223/835] updated fabpot/sphinx-php module reference --- _exts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_exts b/_exts index bb540b67288..734d7353b5f 160000 --- a/_exts +++ b/_exts @@ -1 +1 @@ -Subproject commit bb540b6728898b48d7ec61e52065a18c391951fe +Subproject commit 734d7353b5fe8cc7b8edf80c68d9c0f754697fad From af6b70b48343151130370d0e569aca993d8187e5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 15 Oct 2014 16:24:11 +0200 Subject: [PATCH 224/835] added the missing "best-practice" directive import in Sphinx configuration --- conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf.py b/conf.py index fe7fd835847..d99007617ae 100644 --- a/conf.py +++ b/conf.py @@ -33,7 +33,7 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', - 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode'] + 'sensio.sphinx.refinclude', 'sensio.sphinx.configurationblock', 'sensio.sphinx.phpcode', 'sensio.sphinx.bestpractice'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] From 4bada7c491c097d35e03bc96b942918e8d7facad Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 15 Oct 2014 11:54:33 -0400 Subject: [PATCH 225/835] [#4276] Making language correction we agreed on --- components/http_foundation/session_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index 91775856a9d..4a28190e795 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -181,7 +181,7 @@ which runs reasonably frequently. The ``cookie_lifetime`` would be set to a relatively high value, and the garbage collection ``gc_maxlifetime`` would be set to destroy sessions at whatever the desired idle period is. -The other option is specifically checking if a session has expired after the +The other option is specifically check if a session has expired after the session is started. The session can be destroyed as required. This method of processing can allow the expiry of sessions to be integrated into the user experience, for example, by displaying a message. From d12cfe535a39244553654a4d91f16c6a313cc337 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 16 Oct 2014 16:18:50 +0200 Subject: [PATCH 226/835] Renamed "official best practices" to "best practices" --- best_practices/introduction.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst index 4700bb4559f..6a83cd2ae6b 100644 --- a/best_practices/introduction.rst +++ b/best_practices/introduction.rst @@ -18,10 +18,10 @@ original pragmatic philosophy of Symfony. What is this Guide About? ------------------------- -This guide aims to fix that by describing the **official best practices for -developing web apps with the Symfony full-stack framework**. These are best- -practices that fit the philosophy of the framework as envisioned by its original -creator `Fabien Potencier`_. +This guide aims to fix that by describing the **best practices for developing +web apps with the Symfony full-stack framework**. These are best practices that +fit the philosophy of the framework as envisioned by its original creator +`Fabien Potencier`_. .. note:: From d001da85786f634c284df6aef6e4bf2b5928b9ec Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 16 Oct 2014 16:27:25 +0200 Subject: [PATCH 227/835] Fixed some typos reported by @henrikbjorn --- best_practices/business-logic.rst | 2 +- best_practices/templates.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index fe7364ec2ec..e844eaec2cc 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -164,7 +164,7 @@ Using a Persistence Layer Symfony is an HTTP framework that only cares about generating an HTTP response for each HTTP request. That's why Symfony doesn't provide a way to talk to a persistence layer (e.g. database, external API). You can choose whatever -library of strategy you want for this. +library or strategy you want for this. In practice, many Symfony applications rely on the independent `Doctrine project`_ to define their model using entities and repositories. diff --git a/best_practices/templates.rst b/best_practices/templates.rst index a53b0a3046f..4783cb37aa7 100644 --- a/best_practices/templates.rst +++ b/best_practices/templates.rst @@ -40,7 +40,7 @@ drastically simplifies their logical names: ================================================== ================================== Templates stored inside bundles Templates stored in ``app/`` ================================================== ================================== -``AcmeDemoBunde:Default:index.html.twig`` ``default/index.html.twig`` +``AcmeDemoBundle:Default:index.html.twig`` ``default/index.html.twig`` ``::layout.html.twig`` ``layout.html.twig`` ``AcmeDemoBundle::index.html.twig`` ``index.html.twig`` ``AcmeDemoBundle:Default:subdir/index.html.twig`` ``default/subdir/index.html.twig`` From f74b6f24ec2c0d92d8f3685a33a9e4799542f1f6 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Sat, 4 Oct 2014 13:52:47 +0200 Subject: [PATCH 228/835] Improve cookbook entry for error pages in 2.3~ --- cookbook/controller/error_pages.rst | 285 ++++++++++++++++++++-------- 1 file changed, 209 insertions(+), 76 deletions(-) diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index 00cb1b420f8..fa8efef21fc 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -5,32 +5,47 @@ How to Customize Error Pages ============================ -When any exception is thrown in Symfony, the exception is caught inside the -``Kernel`` class and eventually forwarded to a special controller, -``TwigBundle:Exception:show`` for handling. This controller, which lives -inside the core TwigBundle, determines which error template to display and -the status code that should be set for the given exception. +When an exception is thrown, the core ``HttpKernel`` class catches it and +dispatches a ``kernel.exception`` event. This gives you the power to convert +the exception into a ``Response`` in a few different ways. -Error pages can be customized in two different ways, depending on how much -control you need: +The core TwigBundle sets up a listener for this event which will run +a configurable (but otherwise arbitrary) controller to generate the +response. The default controller used has a sensible way of +picking one out of the available set of error templates. -1. Customize the error templates of the different error pages; +Thus, error pages can be customized in different ways, depending on how +much control you need: -2. Replace the default exception controller ``twig.controller.exception:showAction``. +#. :ref:`Use the default ExceptionController and create a few + templates that allow you to customize how your different error + pages look (easy); ` -The default ExceptionController -------------------------------- +#. :ref:`Replace the default exception controller with your own + (intermediate). ` -The default ``ExceptionController`` will either display an +#. :ref:`Use the kernel.exception event to come up with your own + handling (advanced). ` + +.. _use-default-exception-controller: + +Using the Default ExceptionController +------------------------------------- + +By default, the ``showAction()`` method of the +:class:`Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController` +will be called when an exception occurs. + +This controller will either display an *exception* or *error* page, depending on the setting of the ``kernel.debug`` flag. While *exception* pages give you a lot of helpful information during development, *error* pages are meant to be -shown to the end-user. +shown to the user in production. .. sidebar:: Testing Error Pages during Development You should not set ``kernel.debug`` to ``false`` in order to see your - error pages during development. This will also stop + *error* pages during development. This will also stop Symfony from recompiling your twig templates, among other things. The third-party `WebfactoryExceptionsBundle`_ provides a special @@ -38,13 +53,52 @@ shown to the end-user. pages for arbitrary HTTP status codes even with ``kernel.debug`` set to ``true``. -Override Error Templates ------------------------- +.. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle + +.. _cookbook-error-pages-by-status-code: + +How the Template for the Error and Exception Pages Is Selected +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -All of the error templates live inside the TwigBundle. To override the -templates, simply rely on the standard method for overriding templates that -live inside a bundle. For more information, see -:ref:`overriding-bundle-templates`. +The TwigBundle contains some default templates for error and +exception pages in its ``Resources/views/Exception`` directory. + +.. tip:: + + In a standard Symfony installation, the TwigBundle can be found at + ``vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle``. In addition + to the standard HTML error page, it also provides a default + error page for many of the most common response formats, including + JSON (``error.json.twig``), XML (``error.xml.twig``) and even + JavaScript (``error.js.twig``), to name a few. + +Here is how the ``ExceptionController`` will pick one of the +available templates based on the HTTP status code and request format: + +* For *error* pages, it first looks for a template for the given format + and status code (like ``error404.json.twig``); + +* If that does not exist or apply, it looks for a general template for + the given format (like ``error.json.twig`` or + ``exception.json.twig``); + +* Finally, it ignores the format and falls back to the HTML template + (like ``error.html.twig`` or ``exception.html.twig``). + +.. tip:: + + If the exception being handled implements the + :class:`Symfony\\Component\\HttpKernel\\Exception\\HttpExceptionInterface`, + the ``getStatusCode()`` method will be + called to obtain the HTTP status code to use. Otherwise, + the status code will be "500". + +Overriding or Adding Templates +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To override these templates, simply rely on the standard method for +overriding templates that live inside a bundle. For more information, +see :ref:`overriding-bundle-templates`. For example, to override the default error template, create a new template located at @@ -74,82 +128,161 @@ template located at .. tip:: - If you're not familiar with Twig, don't worry. Twig is a simple, powerful - and optional templating engine that integrates with Symfony. For more - information about Twig see :doc:`/book/templating`. + If you're not familiar with Twig, don't worry. Twig is a simple, + powerful and optional templating engine that integrates with + Symfony. For more information about Twig see :doc:`/book/templating`. -In addition to the standard HTML error page, Symfony provides a default error -page for many of the most common response formats, including JSON -(``error.json.twig``), XML (``error.xml.twig``) and even JavaScript -(``error.js.twig``), to name a few. To override any of these templates, just -create a new file with the same name in the -``app/Resources/TwigBundle/views/Exception`` directory. This is the standard -way of overriding any template that lives inside a bundle. +This works not only to replace the default templates, but also to add +new ones. -.. _cookbook-error-pages-by-status-code: +For instance, create an ``app/Resources/TwigBundle/views/Exception/error404.html.twig`` +template to display a special page for 404 (page not found) errors. +Refer to the previous section for the order in which the +``ExceptionController`` tries different template names. -Customizing the 404 Page and other Error Pages ----------------------------------------------- +.. tip:: -You can also customize specific error templates according to the HTTP status -code. For instance, create a -``app/Resources/TwigBundle/views/Exception/error404.html.twig`` template to -display a special page for 404 (page not found) errors. + Often, the easiest way to customize an error page is to copy it from + the TwigBundle into ``app/Resources/TwigBundle/views/Exception`` and + then modify it. -Symfony uses the following algorithm to determine which template to use: +.. note:: -* First, it looks for a template for the given format and status code (like - ``error404.json.twig``); + The debug-friendly exception pages shown to the developer can even be + customized in the same way by creating templates such as + ``exception.html.twig`` for the standard HTML exception page or + ``exception.json.twig`` for the JSON exception page. + +.. _custom-exception-controller: -* If it does not exist, it looks for a template for the given format (like - ``error.json.twig``); +Replacing the Default ExceptionController +------------------------------------------ + +If you need a little more flexibility beyond just overriding the +template, then you can change the controller that renders the error +page. For example, you might need to pass some additional variables into +your template. + +.. caution:: -* If it does not exist, it falls back to the HTML template (like - ``error.html.twig``). + Make sure you don't lose the exception pages that render the helpful + error messages during development. + +To do this, simply create a new controller and set the +:ref:`twig.exception_controller ` option +to point to it. + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + twig: + exception_controller: AcmeFooBundle:Exception:showException + + .. code-block:: xml + + + + + + + AcmeFooBundle:Exception:showException + + + + .. code-block:: php + + // app/config/config.php + $container->loadFromExtension('twig', array( + 'exception_controller' => 'AcmeFooBundle:Exception:showException', + // ... + )); .. tip:: - To see the full list of default error templates, see the - ``Resources/views/Exception`` directory of the TwigBundle. In a - standard Symfony installation, the TwigBundle can be found at - ``vendor/symfony/symfony/src/Symfony/Bundle/TwigBundle``. Often, the easiest way - to customize an error page is to copy it from the TwigBundle into - ``app/Resources/TwigBundle/views/Exception`` and then modify it. + You can also set up your controller as a service. -.. note:: + The default value of ``twig.controller.exception:showAction`` refers + to the ``showAction`` method of the ``ExceptionController`` + described previously, which is registered in the DIC as the + ``twig.controller.exception`` service. - The debug-friendly exception pages shown to the developer can even be - customized in the same way by creating templates such as - ``exception.html.twig`` for the standard HTML exception page or - ``exception.json.twig`` for the JSON exception page. +Your controller will be passed two parameters: ``exception``, +which is a :class:`\\Symfony\\Component\\Debug\\Exception\\FlattenException` +instance created from the exception being handled, and ``logger``, +an instance of :class:`\\Symfony\\Component\\HttpKernel\\Log\\DebugLoggerInterface` +(which may be ``null``). -.. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle +.. tip:: -Replace the default Exception Controller ----------------------------------------- + The Request that will be dispatched to your controller is created + in the :class:`Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener`. + This event listener is set up by the TwigBundle. -If you need a little more flexibility beyond just overriding the template -(e.g. you need to pass some additional variables into your template), -then you can override the controller that renders the error page. +You can, of course, also extend the previously described +:class:`Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController`. +In that case, you might want to override one or both of the +``showAction`` and ``findTemplate`` methods. The latter one locates the +template to be used. -The default exception controller is registered as a service - the actual -class is ``Symfony\Bundle\TwigBundle\Controller\ExceptionController``. +.. caution:: -To do this, create a new controller class and make it extend Symfony's default -``Symfony\Bundle\TwigBundle\Controller\ExceptionController`` class. + As of writing, the ``ExceptionController`` is *not* part of the + Symfony API, so be aware that it might change in following releases. -There are several methods you can override to customize different parts of how -the error page is rendered. You could, for example, override the entire -``showAction`` or just the ``findTemplate`` method, which locates which -template should be rendered. +.. _use-kernel-exception-event: -To make Symfony use your exception controller instead of the default, set the -:ref:`twig.exception_controller ` option -in app/config/config.yml. +Working with the kernel.exception Event +----------------------------------------- + +As mentioned in the beginning, the ``kernel.exception`` event is +dispatched whenever the Symfony Kernel needs to +handle an exception. For more information on that, see :ref:`kernel-kernel.exception`. + +Working with this event is actually much more powerful than what has +been explained before but also requires a thorough understanding of +Symfony internals. + +To give one example, assume your application throws +specialized exceptions with a particular meaning to your domain. + +In that case, all the default ``ExceptionListener`` and +``ExceptionController`` could do for you was trying to figure out the +right HTTP status code and display your nice-looking error page. + +:doc:`Writing your own event listener ` +for the ``kernel.exception`` event allows you to have a closer look +at the exception and take different actions depending on it. Those +actions might include logging the exception, redirecting the user to +another page or rendering specialized error pages. + +.. note:: + + If your listener calls ``setResponse()`` on the + :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent`, + event propagation will be stopped and the response will be sent to + the client. + +This approach allows you to create centralized and layered error +handling: Instead of catching (and handling) the same exceptions +in various controllers again and again, you can have just one (or +several) listeners deal with them. .. tip:: - The customization of exception handling is actually much more powerful - than what's written here. An internal event, ``kernel.exception``, is thrown - which allows complete control over exception handling. For more - information, see :ref:`kernel-kernel.exception`. + To see an example, have a look at the `ExceptionListener`_ in the + Security Component. + + It handles various security-related exceptions that are thrown in + your application (like :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`) + and takes measures like redirecting the user to the login page, + logging them out and other things. + +Good luck! + +.. _`ExceptionListener`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php From 39b44300a30b34deb13a85776b276e19dac949a5 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Fri, 17 Oct 2014 16:12:33 -0400 Subject: [PATCH 229/835] Minor language tweaks --- components/console/helpers/processhelper.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/console/helpers/processhelper.rst b/components/console/helpers/processhelper.rst index 414724c7f87..5494ca0137f 100644 --- a/components/console/helpers/processhelper.rst +++ b/components/console/helpers/processhelper.rst @@ -38,7 +38,7 @@ Arguments There are three ways to use the process helper: -* Either using a command line string:: +* Using a command line string:: // ... $helper->run($output, 'figlet Symfony'); @@ -51,9 +51,9 @@ There are three ways to use the process helper: .. note:: When running the helper against an array of arguments, be aware that - these ones will be automatically escaped. + these will be automatically escaped. -* Or a :class:`Symfony\\Component\\Process\\Process` instance:: +* Passing a :class:`Symfony\\Component\\Process\\Process` instance:: use Symfony\Component\Process\ProcessBuilder; @@ -70,7 +70,7 @@ You can display a customized error message using the third argument of the $helper->run($output, $process, 'The process failed :('); -A custom process callback can be passed as fourth argument, refer to the +A custom process callback can be passed as the fourth argument. Refer to the :doc:`Process Component ` for callback documentation:: use Symfony\Component\Process\Process; From 8e7037357082048a5d754e6004e5b0f36df6570b Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Fri, 17 Oct 2014 23:48:17 +0200 Subject: [PATCH 230/835] Document error page preview in Symfony ~2.6 (#4293) Feature added in symfony/symfony#12096. --- cookbook/controller/error_pages.rst | 66 ++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index fa8efef21fc..b087bd69fab 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -42,18 +42,10 @@ flag. While *exception* pages give you a lot of helpful information during development, *error* pages are meant to be shown to the user in production. -.. sidebar:: Testing Error Pages during Development - - You should not set ``kernel.debug`` to ``false`` in order to see your - *error* pages during development. This will also stop - Symfony from recompiling your twig templates, among other things. - - The third-party `WebfactoryExceptionsBundle`_ provides a special - test controller that allows you to display your custom error - pages for arbitrary HTTP status codes even with - ``kernel.debug`` set to ``true``. +.. tip:: -.. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle + You can also :ref:`preview your error pages ` + in ``kernel.debug`` mode. .. _cookbook-error-pages-by-status-code: @@ -153,6 +145,53 @@ Refer to the previous section for the order in which the ``exception.html.twig`` for the standard HTML exception page or ``exception.json.twig`` for the JSON exception page. +.. _testing-error-pages: + +Testing Error Pages during Development +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default ``ExceptionController`` also allows you to preview your +*error* pages during development. + +.. versionadded:: 2.6 + This feature was introduced in Symfony 2.6. Before, the third-party + `WebfactoryExceptionsBundle`_ could be used for the same purpose. + +To use this feature, you need to have a definition in your +``routing_dev.yml`` file like so: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/routing_dev.yml + _errors: + resource: "@TwigBundle/Resources/config/routing/errors.xml" + prefix: /_error + +If you're coming from an older version of Symfony, you might need to +add this to your ``routing_dev.yml`` file. If you're starting from +scratch, the `Symfony Standard Edition`_ already contains it for you. + +With this route added, you can use URLs like + +.. code-block:: text + + http://localhost/app_dev.php/_error/{statusCode} + http://localhost/app_dev.php/_error/{statusCode}.{format} + +to preview the *error* page for a given status code as HTML or for a +given status code and format. + +.. tip:: + + You should not set ``kernel.debug`` to ``false`` in order to see your + error pages during development. This will also stop + Symfony from recompiling your twig templates, among other things. + +.. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle +.. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard/ + .. _custom-exception-controller: Replacing the Default ExceptionController @@ -235,6 +274,11 @@ template to be used. As of writing, the ``ExceptionController`` is *not* part of the Symfony API, so be aware that it might change in following releases. +.. tip:: + + The :ref:`error page preview ` also works for + your own controllers set up this way. + .. _use-kernel-exception-event: Working with the kernel.exception Event From 2bf8c55a79474a3750c92891797969c706e59528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Mon, 22 Sep 2014 19:36:36 +0200 Subject: [PATCH 231/835] [Filesystem] Moved current document to a dedicated folder --- components/{ => filesystem}/filesystem.rst | 21 ++------------------- components/filesystem/index.rst | 9 +++++++++ components/filesystem/introduction.rst | 16 ++++++++++++++++ components/filesystem/lockhandler.rst | 2 ++ components/index.rst | 2 +- redirection_map | 1 + 6 files changed, 31 insertions(+), 20 deletions(-) rename components/{ => filesystem}/filesystem.rst (92%) create mode 100644 components/filesystem/index.rst create mode 100644 components/filesystem/introduction.rst create mode 100644 components/filesystem/lockhandler.rst diff --git a/components/filesystem.rst b/components/filesystem/filesystem.rst similarity index 92% rename from components/filesystem.rst rename to components/filesystem/filesystem.rst index 076d7c38d1f..e41dfd0b98b 100644 --- a/components/filesystem.rst +++ b/components/filesystem/filesystem.rst @@ -1,22 +1,5 @@ -.. index:: - single: Filesystem - -The Filesystem Component -======================== - - The Filesystem component provides basic utilities for the filesystem. - -.. versionadded:: 2.1 - The Filesystem component was introduced in Symfony 2.1. Previously, the - ``Filesystem`` class was located in the HttpKernel component. - -Installation ------------- - -You can install the component in 2 different ways: - -* :doc:`Install it via Composer ` (``symfony/filesystem`` on `Packagist`_); -* Use the official Git repository (https://github.com/symfony/Filesystem). +Filesystem +========== Usage ----- diff --git a/components/filesystem/index.rst b/components/filesystem/index.rst new file mode 100644 index 00000000000..b3629ef0af9 --- /dev/null +++ b/components/filesystem/index.rst @@ -0,0 +1,9 @@ +Filesystem +========== + +.. toctree:: + :maxdepth: 2 + + introduction + filesystem + lockhandler diff --git a/components/filesystem/introduction.rst b/components/filesystem/introduction.rst new file mode 100644 index 00000000000..15c8d46628a --- /dev/null +++ b/components/filesystem/introduction.rst @@ -0,0 +1,16 @@ +The Filesystem Component +======================== + + The Filesystem component provides basic utilities for the filesystem. + +.. versionadded:: 2.1 + The Filesystem component was introduced in Symfony 2.1. Previously, the + ``Filesystem`` class was located in the HttpKernel component. + +Installation +------------ + +You can install the component in 2 different ways: + +* :doc:`Install it via Composer ` (``symfony/filesystem`` on `Packagist`_); +* Use the official Git repository (https://github.com/symfony/Filesystem). diff --git a/components/filesystem/lockhandler.rst b/components/filesystem/lockhandler.rst new file mode 100644 index 00000000000..30aa687bd9b --- /dev/null +++ b/components/filesystem/lockhandler.rst @@ -0,0 +1,2 @@ +Lockhandler +=========== diff --git a/components/index.rst b/components/index.rst index 8e167cd2ac9..91b3dfc466a 100644 --- a/components/index.rst +++ b/components/index.rst @@ -14,7 +14,7 @@ The Components dom_crawler event_dispatcher/index expression_language/index - filesystem + filesystem/index finder form/index http_foundation/index diff --git a/redirection_map b/redirection_map index 1a6758a5560..33d8293f670 100644 --- a/redirection_map +++ b/redirection_map @@ -22,6 +22,7 @@ /cookbook/console/generating_urls /cookbook/console/sending_emails /components/yaml /components/yaml/introduction /components/templating /components/templating/introduction +/components/filesystem /components/filesystem/introduction /cmf/reference/configuration/block /cmf/bundles/block/configuration /cmf/reference/configuration/content /cmf/bundles/content/configuration /cmf/reference/configuration/core /cmf/bundles/core/configuration From 909a2940c86110752c7401bf9998aac479712cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Mon, 22 Sep 2014 20:00:46 +0200 Subject: [PATCH 232/835] [Filesystem] Added documentation for the new LockHandler --- components/filesystem/filesystem.rst | 253 ------------------------ components/filesystem/index.rst | 3 +- components/filesystem/introduction.rst | 260 +++++++++++++++++++++++++ components/filesystem/lock_handler.rst | 69 +++++++ components/filesystem/lockhandler.rst | 2 - components/map.rst.inc | 5 +- 6 files changed, 333 insertions(+), 259 deletions(-) delete mode 100644 components/filesystem/filesystem.rst create mode 100644 components/filesystem/lock_handler.rst delete mode 100644 components/filesystem/lockhandler.rst diff --git a/components/filesystem/filesystem.rst b/components/filesystem/filesystem.rst deleted file mode 100644 index e41dfd0b98b..00000000000 --- a/components/filesystem/filesystem.rst +++ /dev/null @@ -1,253 +0,0 @@ -Filesystem -========== - -Usage ------ - -The :class:`Symfony\\Component\\Filesystem\\Filesystem` class is the unique -endpoint for filesystem operations:: - - use Symfony\Component\Filesystem\Filesystem; - use Symfony\Component\Filesystem\Exception\IOExceptionInterface; - - $fs = new Filesystem(); - - try { - $fs->mkdir('/tmp/random/dir/' . mt_rand()); - } catch (IOExceptionInterface $e) { - echo "An error occurred while creating your directory at ".$e->getPath(); - } - -.. versionadded:: 2.4 - The ``IOExceptionInterface`` and its ``getPath`` method were introduced in - Symfony 2.4. Prior to 2.4, you would catch the ``IOException`` class. - -.. note:: - - Methods :method:`Symfony\\Component\\Filesystem\\Filesystem::mkdir`, - :method:`Symfony\\Component\\Filesystem\\Filesystem::exists`, - :method:`Symfony\\Component\\Filesystem\\Filesystem::touch`, - :method:`Symfony\\Component\\Filesystem\\Filesystem::remove`, - :method:`Symfony\\Component\\Filesystem\\Filesystem::chmod`, - :method:`Symfony\\Component\\Filesystem\\Filesystem::chown` and - :method:`Symfony\\Component\\Filesystem\\Filesystem::chgrp` can receive a - string, an array or any object implementing :phpclass:`Traversable` as - the target argument. - -Mkdir -~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::mkdir` creates a directory. -On posix filesystems, directories are created with a default mode value -`0777`. You can use the second argument to set your own mode:: - - $fs->mkdir('/tmp/photos', 0700); - -.. note:: - - You can pass an array or any :phpclass:`Traversable` object as the first - argument. - -Exists -~~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::exists` checks for the -presence of all files or directories and returns ``false`` if a file is missing:: - - // this directory exists, return true - $fs->exists('/tmp/photos'); - - // rabbit.jpg exists, bottle.png does not exists, return false - $fs->exists(array('rabbit.jpg', 'bottle.png')); - -.. note:: - - You can pass an array or any :phpclass:`Traversable` object as the first - argument. - -Copy -~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::copy` is used to copy -files. If the target already exists, the file is copied only if the source -modification date is later than the target. This behavior can be overridden by -the third boolean argument:: - - // works only if image-ICC has been modified after image.jpg - $fs->copy('image-ICC.jpg', 'image.jpg'); - - // image.jpg will be overridden - $fs->copy('image-ICC.jpg', 'image.jpg', true); - -Touch -~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::touch` sets access and -modification time for a file. The current time is used by default. You can set -your own with the second argument. The third argument is the access time:: - - // set modification time to the current timestamp - $fs->touch('file.txt'); - // set modification time 10 seconds in the future - $fs->touch('file.txt', time() + 10); - // set access time 10 seconds in the past - $fs->touch('file.txt', time(), time() - 10); - -.. note:: - - You can pass an array or any :phpclass:`Traversable` object as the first - argument. - -Chown -~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::chown` is used to change -the owner of a file. The third argument is a boolean recursive option:: - - // set the owner of the lolcat video to www-data - $fs->chown('lolcat.mp4', 'www-data'); - // change the owner of the video directory recursively - $fs->chown('/video', 'www-data', true); - -.. note:: - - You can pass an array or any :phpclass:`Traversable` object as the first - argument. - -Chgrp -~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::chgrp` is used to change -the group of a file. The third argument is a boolean recursive option:: - - // set the group of the lolcat video to nginx - $fs->chgrp('lolcat.mp4', 'nginx'); - // change the group of the video directory recursively - $fs->chgrp('/video', 'nginx', true); - -.. note:: - - You can pass an array or any :phpclass:`Traversable` object as the first - argument. - -Chmod -~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::chmod` is used to change -the mode of a file. The fourth argument is a boolean recursive option:: - - // set the mode of the video to 0600 - $fs->chmod('video.ogg', 0600); - // change the mod of the src directory recursively - $fs->chmod('src', 0700, 0000, true); - -.. note:: - - You can pass an array or any :phpclass:`Traversable` object as the first - argument. - -Remove -~~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::remove` is used to remove -files, symlinks, directories easily:: - - $fs->remove(array('symlink', '/path/to/directory', 'activity.log')); - -.. note:: - - You can pass an array or any :phpclass:`Traversable` object as the first - argument. - -Rename -~~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::rename` is used to rename -files and directories:: - - // rename a file - $fs->rename('/tmp/processed_video.ogg', '/path/to/store/video_647.ogg'); - // rename a directory - $fs->rename('/tmp/files', '/path/to/store/files'); - -symlink -~~~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::symlink` creates a -symbolic link from the target to the destination. If the filesystem does not -support symbolic links, a third boolean argument is available:: - - // create a symbolic link - $fs->symlink('/path/to/source', '/path/to/destination'); - // duplicate the source directory if the filesystem - // does not support symbolic links - $fs->symlink('/path/to/source', '/path/to/destination', true); - -makePathRelative -~~~~~~~~~~~~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::makePathRelative` returns -the relative path of a directory given another one:: - - // returns '../' - $fs->makePathRelative( - '/var/lib/symfony/src/Symfony/', - '/var/lib/symfony/src/Symfony/Component' - ); - // returns 'videos/' - $fs->makePathRelative('/tmp/videos', '/tmp') - -mirror -~~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::mirror` mirrors a -directory:: - - $fs->mirror('/path/to/source', '/path/to/target'); - -isAbsolutePath -~~~~~~~~~~~~~~ - -:method:`Symfony\\Component\\Filesystem\\Filesystem::isAbsolutePath` returns -``true`` if the given path is absolute, ``false`` otherwise:: - - // return true - $fs->isAbsolutePath('/tmp'); - // return true - $fs->isAbsolutePath('c:\\Windows'); - // return false - $fs->isAbsolutePath('tmp'); - // return false - $fs->isAbsolutePath('../dir'); - -dumpFile -~~~~~~~~ - -.. versionadded:: 2.3 - The ``dumpFile()`` was introduced in Symfony 2.3. - -:method:`Symfony\\Component\\Filesystem\\Filesystem::dumpFile` allows you to -dump contents to a file. It does this in an atomic manner: it writes a temporary -file first and then moves it to the new file location when it's finished. -This means that the user will always see either the complete old file or -complete new file (but never a partially-written file):: - - $fs->dumpFile('file.txt', 'Hello World'); - -The ``file.txt`` file contains ``Hello World`` now. - -A desired file mode can be passed as the third argument. - -Error Handling --------------- - -Whenever something wrong happens, an exception implementing -:class:`Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface` or -:class:`Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface` is thrown. - -.. note:: - - An :class:`Symfony\\Component\\Filesystem\\Exception\\IOException` is - thrown if directory creation fails. - -.. _`Packagist`: https://packagist.org/packages/symfony/filesystem diff --git a/components/filesystem/index.rst b/components/filesystem/index.rst index b3629ef0af9..e643f5a90f4 100644 --- a/components/filesystem/index.rst +++ b/components/filesystem/index.rst @@ -5,5 +5,4 @@ Filesystem :maxdepth: 2 introduction - filesystem - lockhandler + lock_handler diff --git a/components/filesystem/introduction.rst b/components/filesystem/introduction.rst index 15c8d46628a..8990198aa41 100644 --- a/components/filesystem/introduction.rst +++ b/components/filesystem/introduction.rst @@ -1,3 +1,6 @@ +.. index:: + single: Filesystem + The Filesystem Component ======================== @@ -7,6 +10,12 @@ The Filesystem Component The Filesystem component was introduced in Symfony 2.1. Previously, the ``Filesystem`` class was located in the HttpKernel component. + +.. tip:: + + A lock handler feature was introduce in symfony 2.6. + :doc:`See the documentation for more information `. + Installation ------------ @@ -14,3 +23,254 @@ You can install the component in 2 different ways: * :doc:`Install it via Composer ` (``symfony/filesystem`` on `Packagist`_); * Use the official Git repository (https://github.com/symfony/Filesystem). + +Usage +----- + +The :class:`Symfony\\Component\\Filesystem\\Filesystem` class is the unique +endpoint for filesystem operations:: + + use Symfony\Component\Filesystem\Filesystem; + use Symfony\Component\Filesystem\Exception\IOExceptionInterface; + + $fs = new Filesystem(); + + try { + $fs->mkdir('/tmp/random/dir/' . mt_rand()); + } catch (IOExceptionInterface $e) { + echo "An error occurred while creating your directory at ".$e->getPath(); + } + +.. versionadded:: 2.4 + The ``IOExceptionInterface`` and its ``getPath`` method were introduced in + Symfony 2.4. Prior to 2.4, you would catch the ``IOException`` class. + +.. note:: + + Methods :method:`Symfony\\Component\\Filesystem\\Filesystem::mkdir`, + :method:`Symfony\\Component\\Filesystem\\Filesystem::exists`, + :method:`Symfony\\Component\\Filesystem\\Filesystem::touch`, + :method:`Symfony\\Component\\Filesystem\\Filesystem::remove`, + :method:`Symfony\\Component\\Filesystem\\Filesystem::chmod`, + :method:`Symfony\\Component\\Filesystem\\Filesystem::chown` and + :method:`Symfony\\Component\\Filesystem\\Filesystem::chgrp` can receive a + string, an array or any object implementing :phpclass:`Traversable` as + the target argument. + +Mkdir +~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::mkdir` creates a directory. +On posix filesystems, directories are created with a default mode value +`0777`. You can use the second argument to set your own mode:: + + $fs->mkdir('/tmp/photos', 0700); + +.. note:: + + You can pass an array or any :phpclass:`Traversable` object as the first + argument. + +Exists +~~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::exists` checks for the +presence of all files or directories and returns ``false`` if a file is missing:: + + // this directory exists, return true + $fs->exists('/tmp/photos'); + + // rabbit.jpg exists, bottle.png does not exists, return false + $fs->exists(array('rabbit.jpg', 'bottle.png')); + +.. note:: + + You can pass an array or any :phpclass:`Traversable` object as the first + argument. + +Copy +~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::copy` is used to copy +files. If the target already exists, the file is copied only if the source +modification date is later than the target. This behavior can be overridden by +the third boolean argument:: + + // works only if image-ICC has been modified after image.jpg + $fs->copy('image-ICC.jpg', 'image.jpg'); + + // image.jpg will be overridden + $fs->copy('image-ICC.jpg', 'image.jpg', true); + +Touch +~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::touch` sets access and +modification time for a file. The current time is used by default. You can set +your own with the second argument. The third argument is the access time:: + + // set modification time to the current timestamp + $fs->touch('file.txt'); + // set modification time 10 seconds in the future + $fs->touch('file.txt', time() + 10); + // set access time 10 seconds in the past + $fs->touch('file.txt', time(), time() - 10); + +.. note:: + + You can pass an array or any :phpclass:`Traversable` object as the first + argument. + +Chown +~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::chown` is used to change +the owner of a file. The third argument is a boolean recursive option:: + + // set the owner of the lolcat video to www-data + $fs->chown('lolcat.mp4', 'www-data'); + // change the owner of the video directory recursively + $fs->chown('/video', 'www-data', true); + +.. note:: + + You can pass an array or any :phpclass:`Traversable` object as the first + argument. + +Chgrp +~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::chgrp` is used to change +the group of a file. The third argument is a boolean recursive option:: + + // set the group of the lolcat video to nginx + $fs->chgrp('lolcat.mp4', 'nginx'); + // change the group of the video directory recursively + $fs->chgrp('/video', 'nginx', true); + +.. note:: + + You can pass an array or any :phpclass:`Traversable` object as the first + argument. + +Chmod +~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::chmod` is used to change +the mode of a file. The fourth argument is a boolean recursive option:: + + // set the mode of the video to 0600 + $fs->chmod('video.ogg', 0600); + // change the mod of the src directory recursively + $fs->chmod('src', 0700, 0000, true); + +.. note:: + + You can pass an array or any :phpclass:`Traversable` object as the first + argument. + +Remove +~~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::remove` is used to remove +files, symlinks, directories easily:: + + $fs->remove(array('symlink', '/path/to/directory', 'activity.log')); + +.. note:: + + You can pass an array or any :phpclass:`Traversable` object as the first + argument. + +Rename +~~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::rename` is used to rename +files and directories:: + + // rename a file + $fs->rename('/tmp/processed_video.ogg', '/path/to/store/video_647.ogg'); + // rename a directory + $fs->rename('/tmp/files', '/path/to/store/files'); + +symlink +~~~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::symlink` creates a +symbolic link from the target to the destination. If the filesystem does not +support symbolic links, a third boolean argument is available:: + + // create a symbolic link + $fs->symlink('/path/to/source', '/path/to/destination'); + // duplicate the source directory if the filesystem + // does not support symbolic links + $fs->symlink('/path/to/source', '/path/to/destination', true); + +makePathRelative +~~~~~~~~~~~~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::makePathRelative` returns +the relative path of a directory given another one:: + + // returns '../' + $fs->makePathRelative( + '/var/lib/symfony/src/Symfony/', + '/var/lib/symfony/src/Symfony/Component' + ); + // returns 'videos/' + $fs->makePathRelative('/tmp/videos', '/tmp') + +mirror +~~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::mirror` mirrors a +directory:: + + $fs->mirror('/path/to/source', '/path/to/target'); + +isAbsolutePath +~~~~~~~~~~~~~~ + +:method:`Symfony\\Component\\Filesystem\\Filesystem::isAbsolutePath` returns +``true`` if the given path is absolute, ``false`` otherwise:: + + // return true + $fs->isAbsolutePath('/tmp'); + // return true + $fs->isAbsolutePath('c:\\Windows'); + // return false + $fs->isAbsolutePath('tmp'); + // return false + $fs->isAbsolutePath('../dir'); + +dumpFile +~~~~~~~~ + +.. versionadded:: 2.3 + The ``dumpFile()`` was introduced in Symfony 2.3. + +:method:`Symfony\\Component\\Filesystem\\Filesystem::dumpFile` allows you to +dump contents to a file. It does this in an atomic manner: it writes a temporary +file first and then moves it to the new file location when it's finished. +This means that the user will always see either the complete old file or +complete new file (but never a partially-written file):: + + $fs->dumpFile('file.txt', 'Hello World'); + +The ``file.txt`` file contains ``Hello World`` now. + +A desired file mode can be passed as the third argument. + +Error Handling +-------------- + +Whenever something wrong happens, an exception implementing +:class:`Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface` or +:class:`Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface` is thrown. + +.. note:: + + An :class:`Symfony\\Component\\Filesystem\\Exception\\IOException` is + thrown if directory creation fails. + +.. _`Packagist`: https://packagist.org/packages/symfony/filesystem diff --git a/components/filesystem/lock_handler.rst b/components/filesystem/lock_handler.rst new file mode 100644 index 00000000000..d57889190ef --- /dev/null +++ b/components/filesystem/lock_handler.rst @@ -0,0 +1,69 @@ +LockHandler +=========== + +.. versionadded:: 2.6 + The lock handler feature was introduced in Symfony 2.6 + +What is a Lock? +--------------- + +File locking is a mechanism that restricts access to a computer file by allowing +only one user or process access at any specific time. This mechanism was +introduced a few decades ago for mainframes, but continues being useful for +modern applications. + +Symfony provides a LockHelper to help you use locks in your project. + +Usage +----- + +.. caution:: + + The lock handler only works if you're using just one server. If you have + several hosts, you must not use this helper. + +A lock can be used, for example, to allow only one instance of a command to run. + +.. code-block:: php + + use Symfony\Component\Filesystem\LockHandler; + + $lockHandler = new LockHandler('hello.lock'); + if (!$lockHandler->lock()) { + // the resource "hello" is already locked by another process + + return 0; + } + +The first argument of the constructor is a string that it will use as part of +the name of the file used to create the lock on the local filesystem. A best +practice for Symfony commands is to use the command name, such as ``acme:my-command``. +``LockHandler`` sanitizes the contents of the string before creating +the file, so you can pass any value for this argument. + +.. tip:: + + The ``.lock`` extension is optional, but it's a common practice to include + it. This will make it easier to find lock files on the filesystem. Moreover, + to avoid name collisions, ``LockHandler`` also appends a hash to the name of + the lock file. + +By default, the lock will be created in the temporary directory, but you can +optionally select the directory where locks are created by passing it as the +second argument of the constructor. + +The :method:`Symfony\\Component\\Filesystem\\LockHandler::lock` method tries to +acquire the lock. If the lock is acquired, the method returns ``true``, +``false`` otherwise. If the ``lock`` method is called several times on the same +instance it will always return ``true`` if the lock was acquired on the first +call. + +You can pass an optional blocking argument as the first argument to the +``lock()`` method, which defaults to ``false``. If this is set to ``true``, your +PHP code will wait indefinitely until the lock is released by another process. + +The resource is automatically released by PHP at the end of the script. In +addition, you can invoke the +:method:`Symfony\\Component\\Filesystem\\LockHandler::release` method to release +the lock explicitly. Once it's released, any other process can lock the +resource. diff --git a/components/filesystem/lockhandler.rst b/components/filesystem/lockhandler.rst deleted file mode 100644 index 30aa687bd9b..00000000000 --- a/components/filesystem/lockhandler.rst +++ /dev/null @@ -1,2 +0,0 @@ -Lockhandler -=========== diff --git a/components/map.rst.inc b/components/map.rst.inc index f9eab711e07..1bccb80fb61 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -70,9 +70,10 @@ * :doc:`/components/expression_language/extending` * :doc:`/components/expression_language/caching` -* **Filesystem** +* :doc:`/components/filesystem/index` - * :doc:`/components/filesystem` + * :doc:`/components/filesystem/introduction` + * :doc:`/components/filesystem/lock_handler` * **Finder** From ab2a688efc4653c12326a84deefdc26954dcd3c9 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 18 Oct 2014 15:39:05 -0400 Subject: [PATCH 233/835] [#4290] Re-adding version back --- components/dependency_injection/lazy_services.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst index 497e45720ce..5c8447fb579 100644 --- a/components/dependency_injection/lazy_services.rst +++ b/components/dependency_injection/lazy_services.rst @@ -39,7 +39,7 @@ the `ProxyManager bridge`_: .. code-block:: bash - $ php composer.phar require ocramius/proxy-manager + $ php composer.phar require ocramius/proxy-manager:~0.5 Afterwards compile your container and check to make sure that you get a proxy for your lazy services. From d7ccbf3e2c0360252605c202906c853a9d182713 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 18 Oct 2014 15:41:03 -0400 Subject: [PATCH 234/835] Re-adding one more version --- components/dependency_injection/lazy_services.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst index 5c8447fb579..2d3eafc1785 100644 --- a/components/dependency_injection/lazy_services.rst +++ b/components/dependency_injection/lazy_services.rst @@ -30,7 +30,7 @@ the `ProxyManager bridge`_: .. code-block:: bash - $ php composer.phar require symfony/proxy-manager-bridge + $ php composer.phar require symfony/proxy-manager-bridge:~2.3 .. note:: From 5877d02d5eb5b7d3eba28ed26a84a2277bf560f4 Mon Sep 17 00:00:00 2001 From: Alan Farquharson Date: Wed, 6 Aug 2014 14:44:37 +0100 Subject: [PATCH 235/835] Update http_cache.rst Explained how the parameters are passed to the controller - named parameters --- book/http_cache.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/book/http_cache.rst b/book/http_cache.rst index 190a64fc5fb..5fdeb57ea96 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -938,6 +938,19 @@ wondering why you would want to use a helper instead of just writing the ESI tag yourself. That's because using a helper makes your application work even if there is no gateway cache installed. +.. tip:: + + Using render_esi with parameters doesn't use request variables - they are + passed in as named parameters. They must be named the same. + + e.g. ``render_esi(controller('...:content', { max_per_page: 5, type: 'news' }`` + +.. code-block:: php + + public function content($max_per_page = 10, $type = "news") + { + + When using the default ``render`` function (or setting the renderer to ``inline``), Symfony merges the included page content into the main one before sending the response to the client. But if you use the ``esi`` renderer From ded75771db5b36b7263b07db7ba13757e83762f8 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 18 Oct 2014 15:54:50 -0400 Subject: [PATCH 236/835] Clarifying a bit more that you're passing variables into your controller --- book/http_cache.rst | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/book/http_cache.rst b/book/http_cache.rst index 5fdeb57ea96..5d2d2b21059 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -915,20 +915,20 @@ matter), Symfony uses the standard ``render`` helper to configure ESI tags: .. code-block:: jinja {# you can use a controller reference #} - {{ render_esi(controller('...:news', { 'max': 5 })) }} + {{ render_esi(controller('...:news', { 'maxPerPage': 5 })) }} {# ... or a URL #} - {{ render_esi(url('latest_news', { 'max': 5 })) }} + {{ render_esi(url('latest_news', { 'maxPerPage': 5 })) }} .. code-block:: html+php render( - new \Symfony\Component\HttpKernel\Controller\ControllerReference('...:news', array('max' => 5)), + new \Symfony\Component\HttpKernel\Controller\ControllerReference('...:news', array('maxPerPage' => 5)), array('strategy' => 'esi')) ?> render( - $view['router']->generate('latest_news', array('max' => 5), true), + $view['router']->generate('latest_news', array('maxPerPage' => 5), true), array('strategy' => 'esi'), ) ?> @@ -940,16 +940,10 @@ if there is no gateway cache installed. .. tip:: - Using render_esi with parameters doesn't use request variables - they are - passed in as named parameters. They must be named the same. - - e.g. ``render_esi(controller('...:content', { max_per_page: 5, type: 'news' }`` - -.. code-block:: php - - public function content($max_per_page = 10, $type = "news") - { - + As you'll see below, the ``maxPerPage`` variable you pass is available + as an argument to your controller (i.e. ``$maxPerPage``). The variables + passed through ``render_esi`` also become part of the cache key so that + you have unique caches for each combination of variables and values. When using the default ``render`` function (or setting the renderer to ``inline``), Symfony merges the included page content into the main one @@ -971,7 +965,7 @@ of the master page. .. code-block:: php - public function newsAction($max) + public function newsAction($maxPerPage) { // ... From e8964f4952d783b6b69ad2120eeefdc86960f4f7 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 18 Oct 2014 15:58:58 -0400 Subject: [PATCH 237/835] Putting #pull-request-format anchor link back, which we link to in our contrib notes --- contributing/documentation/overview.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contributing/documentation/overview.rst b/contributing/documentation/overview.rst index 1ad45ef7b4f..2bf7b9a7a29 100644 --- a/contributing/documentation/overview.rst +++ b/contributing/documentation/overview.rst @@ -94,6 +94,8 @@ to base your changes on. The **compare repository** should be your forked copy of ``symfony-docs`` and the **compare branch** should be ``improve_install_chapter``, which is the name of the branch you created and where you made your changes. +.. _pull-request-format: + **Step 8.** The last step is to prepare the **description** of the pull request. To ensure that your work is reviewed quickly, please add the following table at the beginning of your pull request description: From f3b73949f87120aee9f8ff4b0cdeb262376184ef Mon Sep 17 00:00:00 2001 From: Thierry Marianne Date: Tue, 30 Sep 2014 00:40:09 +0200 Subject: [PATCH 238/835] Added config example for Varnish 4.0 --- cookbook/cache/varnish.rst | 72 +++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index cb6ba5b8174..5bc9c841e86 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -54,30 +54,62 @@ Then, optimize Varnish so that it only parses the Response contents when there is at least one ESI tag by checking the ``Surrogate-Control`` header that Symfony adds automatically: -.. code-block:: text +.. configuration-block:: - sub vcl_fetch { - /* - Check for ESI acknowledgement - and remove Surrogate-Control header - */ - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; + .. code-block:: varnish4 - // For Varnish >= 3.0 - set beresp.do_esi = true; - // For Varnish < 3.0 - // esi; + /* (https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#req-not-available-in-vcl-backend-response) */ + sub vcl_backend_response { + // Check for ESI acknowledgement and remove Surrogate-Control header + if (beresp.http.Surrogate-Control ~ "ESI/1.0") { + unset beresp.http.Surrogate-Control; + set beresp.do_esi = true; + } + /* By default Varnish ignores Pragma: nocache + (https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cache-control) + so in order avoid caching it has to be done explicitly */ + if (beresp.http.Pragma ~ "no-cache") { + // https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#hit-for-pass-objects-are-created-using-beresp-uncacheable + set beresp.uncacheable = true; + set beresp.ttl = 120s; + return (deliver); + } } - /* By default Varnish ignores Cache-Control: nocache - (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache" || - beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private") { - return (hit_for_pass); + + .. code-block:: varnish3 + + sub vcl_fetch { + // Check for ESI acknowledgement and remove Surrogate-Control header + if (beresp.http.Surrogate-Control ~ "ESI/1.0") { + unset beresp.http.Surrogate-Control; + set beresp.do_esi = true; + } + /* By default Varnish ignores Cache-Control: nocache + (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), + so in order avoid caching it has to be done explicitly */ + if (beresp.http.Pragma ~ "no-cache" || + beresp.http.Cache-Control ~ "no-cache" || + beresp.http.Cache-Control ~ "private") { + return (hit_for_pass); + } + } + + .. code-block:: varnish2 + + sub vcl_fetch { + // Check for ESI acknowledgement and remove Surrogate-Control header + if (beresp.http.Surrogate-Control ~ "ESI/1.0") { + unset beresp.http.Surrogate-Control; + esi; + } + /* By default Varnish ignores Cache-Control: nocache + so in order avoid caching it has to be done explicitly */ + if (beresp.http.Pragma ~ "no-cache" || + beresp.http.Cache-Control ~ "no-cache" || + beresp.http.Cache-Control ~ "private") { + return (hit_for_pass); + } } - } .. caution:: From 126496331e59319842dece5ac5287e9ff07b8771 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 19 Oct 2014 11:19:16 -0400 Subject: [PATCH 239/835] [#4204] Fixing slight markup issue --- reference/constraints/Expression.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/constraints/Expression.rst b/reference/constraints/Expression.rst index 01eb69c3493..b3d8f386fa1 100644 --- a/reference/constraints/Expression.rst +++ b/reference/constraints/Expression.rst @@ -221,7 +221,7 @@ more about the expression language syntax, see In Symfony 2.6, the Expression constraint *is* executed if the value is ``null``. Before 2.6, if the value was ``null``, the expression was never executed and the value was considered valid (unless you - also had a constraint like `NotBlank` on the property). + also had a constraint like ``NotBlank`` on the property). For more information about the expression and what variables are available to you, see the :ref:`expression ` From f8d69d48c3ff7850f60b1be819b84ed542b0bfa0 Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Wed, 6 Aug 2014 17:09:54 +0200 Subject: [PATCH 240/835] Added Composer installation tip --- book/installation.rst | 4 ++++ cookbook/workflow/new_project_git.rst | 8 +++++++- quick_tour/the_big_picture.rst | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index b4dcf4e8cd4..5037a18ce9d 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -66,6 +66,10 @@ Distribution: To download the vendor files faster, add the ``--prefer-dist`` option at the end of any Composer command. + Add the ``-vvv`` flag to see everything that Composer is doing - this is + especially useful on a slow connection where it may seem that nothing is + happening. + This command may take several minutes to run as Composer downloads the Standard Distribution along with all of the vendor libraries that it needs. When it finishes, you should have a directory that looks something like this: diff --git a/cookbook/workflow/new_project_git.rst b/cookbook/workflow/new_project_git.rst index c40cba6d53f..485c249f0ce 100644 --- a/cookbook/workflow/new_project_git.rst +++ b/cookbook/workflow/new_project_git.rst @@ -26,7 +26,13 @@ git repository: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition path/ '~2.3' + $ php composer.phar create-project symfony/framework-standard-edition path/ '~2.5' + +.. tip:: + + Add the ``-vvv`` flag to see everything that Composer is doing - this is + especially useful on a slow connection where it may seem that nothing is + happening. Composer will now download the Standard Distribution along with all of the required vendor libraries. For more information about downloading Symfony using diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 3cfc648f2d5..212f406e62c 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -22,6 +22,12 @@ directory: $ composer create-project symfony/framework-standard-edition myproject/ '~2.3' +.. tip:: + + Add the ``-vvv`` flag to see everything that Composer is doing - this is + especially useful on a slow connection where it may seem that nothing is + happening. + .. note:: `Composer`_ is the package manager used by modern PHP applications and the From 866d2a80e4f05b7e237861dace54d43c6150b393 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 19 Oct 2014 11:30:24 -0400 Subject: [PATCH 241/835] [#4099] Adding an extra tip --- book/installation.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/installation.rst b/book/installation.rst index 5037a18ce9d..d83cf153ea7 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -66,6 +66,8 @@ Distribution: To download the vendor files faster, add the ``--prefer-dist`` option at the end of any Composer command. +.. tip:: + Add the ``-vvv`` flag to see everything that Composer is doing - this is especially useful on a slow connection where it may seem that nothing is happening. From 5b3f68e1d80d69d3d3ee46770ad8a8ddc772cb9f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 19 Oct 2014 11:32:32 -0400 Subject: [PATCH 242/835] [#4099] Trying to get the markup just right --- cookbook/workflow/new_project_git.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/workflow/new_project_git.rst b/cookbook/workflow/new_project_git.rst index 485c249f0ce..4f355591288 100644 --- a/cookbook/workflow/new_project_git.rst +++ b/cookbook/workflow/new_project_git.rst @@ -28,11 +28,11 @@ git repository: $ php composer.phar create-project symfony/framework-standard-edition path/ '~2.5' -.. tip:: + .. tip:: - Add the ``-vvv`` flag to see everything that Composer is doing - this is - especially useful on a slow connection where it may seem that nothing is - happening. + Add the ``-vvv`` flag to see everything that Composer is doing - this is + especially useful on a slow connection where it may seem that nothing is + happening. Composer will now download the Standard Distribution along with all of the required vendor libraries. For more information about downloading Symfony using From 8753f372e792a1ca3b94676db075cf4fecfb4d19 Mon Sep 17 00:00:00 2001 From: xamgreen Date: Thu, 7 Aug 2014 23:24:37 +0200 Subject: [PATCH 243/835] Use ${APACHE_LOG_DIR} instead of /var/log/apache2 --- cookbook/configuration/web_server_configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/configuration/web_server_configuration.rst b/cookbook/configuration/web_server_configuration.rst index 3e248ddf205..032a069e2d3 100644 --- a/cookbook/configuration/web_server_configuration.rst +++ b/cookbook/configuration/web_server_configuration.rst @@ -48,8 +48,8 @@ are: Allow from All - ErrorLog /var/log/apache2/project_error.log - CustomLog /var/log/apache2/project_access.log combined + ErrorLog ${APACHE_LOG_DIR}/project_error.log + CustomLog ${APACHE_LOG_DIR}/project_access.log combined .. note:: From b8328c53669cacc7a278c87e703417c7b87a08be Mon Sep 17 00:00:00 2001 From: xamgreen Date: Mon, 25 Aug 2014 13:39:02 +0200 Subject: [PATCH 244/835] APACHE_LOG_DIR note added Added a note, that for systems, which support the APACHE_LOG_DIR env, the user can use it for the logging-path. --- cookbook/configuration/web_server_configuration.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/cookbook/configuration/web_server_configuration.rst b/cookbook/configuration/web_server_configuration.rst index 032a069e2d3..a8d3ac4693f 100644 --- a/cookbook/configuration/web_server_configuration.rst +++ b/cookbook/configuration/web_server_configuration.rst @@ -48,10 +48,13 @@ are: Allow from All - ErrorLog ${APACHE_LOG_DIR}/project_error.log - CustomLog ${APACHE_LOG_DIR}/project_access.log combined + ErrorLog /var/log/apache2/project_error.log + CustomLog /var/log/apache2/project_access.log combined - +.. note:: + You probably want to use ``${APACHE_LOG_DIR}/`` instead of ``/var/log/apache2/`` + for the logging-paths, if your system supports the ``APACHE_LOG_DIR`` variable. + .. note:: For performance reasons, you will probably want to set From c8a32c5bad2ef88a2ff0e592aa3863d9b2349778 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 19 Oct 2014 11:35:56 -0400 Subject: [PATCH 245/835] [#4104] Re-organizing language in one sentence --- cookbook/configuration/web_server_configuration.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cookbook/configuration/web_server_configuration.rst b/cookbook/configuration/web_server_configuration.rst index a8d3ac4693f..ccf99d50c86 100644 --- a/cookbook/configuration/web_server_configuration.rst +++ b/cookbook/configuration/web_server_configuration.rst @@ -51,10 +51,12 @@ are: ErrorLog /var/log/apache2/project_error.log CustomLog /var/log/apache2/project_access.log combined + .. note:: - You probably want to use ``${APACHE_LOG_DIR}/`` instead of ``/var/log/apache2/`` - for the logging-paths, if your system supports the ``APACHE_LOG_DIR`` variable. - + + If your system supports the ``APACHE_LOG_DIR`` variable, you may want + to use ``${APACHE_LOG_DIR}/`` instead of ``/var/log/apache2/``. + .. note:: For performance reasons, you will probably want to set From 33f6422dbcafcceb362f39582611ec970ac928ae Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 3 Aug 2014 10:25:49 +0200 Subject: [PATCH 246/835] [Forms] add missing features being new in 2.3 These features are: * Use the submit() (instead of bind()) method to manually submit a form. * The ability to change a form's action and method. --- book/forms.rst | 6 +++++ components/form/introduction.rst | 43 ++++++++++++++++++++++++++++++++ cookbook/form/direct_submit.rst | 2 ++ reference/forms/types/form.rst | 4 +++ 4 files changed, 55 insertions(+) diff --git a/book/forms.rst b/book/forms.rst index 0998408312e..80d19185e94 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -273,6 +273,12 @@ possible paths: from being able to hit the "Refresh" button of their browser and re-post the data. +.. seealso:: + + If you need more control over exactly when your form is submitted or which + data is passed to it, you can use the :method:`Symfony\\Component\\Form\\FormInterface::submit` + for this. Read more about it :ref:`in the cookbook `. + .. index:: single: Forms; Multiple Submit Buttons diff --git a/components/form/introduction.rst b/components/form/introduction.rst index c5b47f8fa87..74b47461b65 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -78,6 +78,12 @@ Behind the scenes, this uses a :class:`Symfony\\Component\\Form\\NativeRequestHa object to read data off of the correct PHP superglobals (i.e. ``$_POST`` or ``$_GET``) based on the HTTP method configured on the form (POST is default). +.. seealso:: + + If you need more control over exactly when your form is submitted or which + data is passed to it, you can use the :method:`Symfony\\Component\\Form\\FormInterface::submit` + for this. Read more about it :ref:`in the cookbook `. + .. sidebar:: Integration with the HttpFoundation Component If you use the HttpFoundation component, then you should add the @@ -489,6 +495,43 @@ 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. +Changing a Form's Method and Action +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.3 + The ability to configure the form method and action was introduced in + Symfony 2.3. + +By default, a form is submitted to the same URI that rendered the form with +an HTTP POST request. This behavior can be changed using the :ref:`form-option-action` +and :ref:`form-option-method` options (the ``method`` option is also used +by ``handleRequest()`` to determine whether a form has been submitted): + +.. configuration-block:: + + .. code-block:: php-standalone + + $formBuilder = $formFactory->createBuilder('form', null, array( + 'action' => '/search', + 'method' => 'GET', + ); + + // ... + + .. code-block:: php-symfony + + // ... + + public function searchAction() + { + $formBuilder = $this->createFormBuilder('form', null, array( + 'action' => '/search', + 'method' => 'GET', + )); + + // ... + } + .. _component-form-intro-handling-submission: Handling Form Submissions diff --git a/cookbook/form/direct_submit.rst b/cookbook/form/direct_submit.rst index 5a40a0aec3f..221f7f534d8 100644 --- a/cookbook/form/direct_submit.rst +++ b/cookbook/form/direct_submit.rst @@ -37,6 +37,8 @@ submissions:: To see more about this method, read :ref:`book-form-handling-form-submissions`. +.. _cookbook-form-call-submit-directly: + Calling Form::submit() manually ------------------------------- diff --git a/reference/forms/types/form.rst b/reference/forms/types/form.rst index 976f3c3e5d1..7bf4d09df76 100644 --- a/reference/forms/types/form.rst +++ b/reference/forms/types/form.rst @@ -48,6 +48,8 @@ on all types for which ``form`` is the parent type. Field Options ------------- +.. _form-option-action: + .. include:: /reference/forms/types/options/action.rst.inc .. include:: /reference/forms/types/options/by_reference.rst.inc @@ -96,6 +98,8 @@ The actual default value of this option depends on other field options: .. include:: /reference/forms/types/options/max_length.rst.inc +.. _form-option-method: + .. include:: /reference/forms/types/options/method.rst.inc .. _reference-form-option-pattern: From c4f45888dc08b789e2e651e299b94d1fb47048d2 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 13 Sep 2014 13:20:21 +0200 Subject: [PATCH 247/835] outline implications of the kernel.terminate event --- components/http_kernel/introduction.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index 10084d5ccc0..2f4230543f0 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -472,6 +472,15 @@ you will trigger the ``kernel.terminate`` event where you can perform certain actions that you may have delayed in order to return the response as quickly as possible to the client (e.g. sending emails). +.. caution:: + + Internally, the HttpKernel makes use of the :phpfunction:`fastcgi_finish_request` + PHP function. This means that at the moment, only the `PHP FPM`_ server + API is able to send a response to the client while the server's PHP process + still performs some tasks. With all other server APIs, listeners to ``kernel.terminate`` + are still executed, but the response is not sent to the client until they + are all completed. + .. note:: Using the ``kernel.terminate`` event is optional, and should only be @@ -689,3 +698,4 @@ look like this:: .. _reflection: http://php.net/manual/en/book.reflection.php .. _FOSRestBundle: https://github.com/friendsofsymfony/FOSRestBundle .. _`Create your own framework... on top of the Symfony2 Components`: http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1 +.. _`PHP FPM`: http://php.net/manual/en/install.fpm.php From 28645d9e929af186e12aa2bdc597a45c3bb8e4a2 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 19 Oct 2014 20:29:39 +0200 Subject: [PATCH 248/835] add sidebar for the built-in server in VMs --- cookbook/web_server/built_in.rst | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cookbook/web_server/built_in.rst b/cookbook/web_server/built_in.rst index f7ef8d1f039..d584ab806e3 100644 --- a/cookbook/web_server/built_in.rst +++ b/cookbook/web_server/built_in.rst @@ -30,12 +30,29 @@ The command will wait and will respond to incoming HTTP requests until you terminate it (this is usually done by pressing Ctrl and C). By default, the web server listens on port 8000 on the loopback device. You -can change the socket passing an ip address and a port as a command-line argument: +can change the socket passing an IP address and a port as a command-line argument: .. code-block:: bash $ php app/console server:run 192.168.0.1:8080 +.. sidebar:: Using the built-in Web Server from inside a Virtual Machine + + If you want to use the built-in web server from inside a virtual machine + and then load the site from a browser on your host machine, you'll need + to listen on the ``0.0.0.0:8000`` address (i.e. on all IP addresses that + are assigned to the virtual machine): + + .. code-block:: bash + + $ php app/console server:run 0.0.0.0:8000 + + .. caution:: + + You should **NEVER** listen to all interfaces on a computer that is + directly accessible from the Internet. The built-in web server is + not designed to be used on public networks. + Command Options --------------- From 3caec2169ab5791fd6483ed7939fc093816f21d6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 7 Jul 2014 16:24:39 +0200 Subject: [PATCH 249/835] description for running PHP's built-in web server in the background --- book/installation.rst | 16 ++++++++- cookbook/web_server/built_in.rst | 61 +++++++++++++++++++++++++++----- quick_tour/the_big_picture.rst | 8 ++++- 3 files changed, 74 insertions(+), 11 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index 560c4852bb0..837751a87ad 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -274,7 +274,21 @@ If there are any issues, correct them now before moving on. Note that using the ACL is recommended when you have access to them on your server because changing the umask is not thread-safe. - **4. Use the same user for the CLI and the web server** + **4. Use the built-in web server in development environments** + + The built-in PHP web server - which can be used during development - allows + your web server user and CLI user to be the same. This removes any permissions + issues: + + .. code-block:: bash + + $ php app/console server:start + + .. seealso:: + + Read more about the internal server :doc:`in the cookbook `. + + **5. Use the same user for the CLI and the web server** In development environments, it is a common practice to use the same unix user for the CLI and the web server because it avoids any of these permissions diff --git a/cookbook/web_server/built_in.rst b/cookbook/web_server/built_in.rst index f7ef8d1f039..154ab192c36 100644 --- a/cookbook/web_server/built_in.rst +++ b/cookbook/web_server/built_in.rst @@ -4,6 +4,10 @@ How to Use PHP's built-in Web Server ==================================== +.. versionadded:: 2.6 + The ability to run the server as a background process was introduced + in Symfony 2.6. + Since PHP 5.4 the CLI SAPI comes with a `built-in web server`_. It can be used to run your PHP applications locally during development, for testing or for application demonstrations. This way, you don't have to bother configuring @@ -19,25 +23,46 @@ Starting the Web Server ----------------------- Running a Symfony application using PHP's built-in web server is as easy as -executing the ``server:run`` command: +executing the ``server:start`` command: .. code-block:: bash - $ php app/console server:run + $ php app/console server:start -This starts a server at ``localhost:8000`` that executes your Symfony application. -The command will wait and will respond to incoming HTTP requests until you -terminate it (this is usually done by pressing Ctrl and C). +This starts the web server at ``localhost:8000`` in the background that serves +your Symfony application. By default, the web server listens on port 8000 on the loopback device. You -can change the socket passing an ip address and a port as a command-line argument: +can change the socket passing an IP address and a port as a command-line argument: .. code-block:: bash $ php app/console server:run 192.168.0.1:8080 +.. note:: + + You can use the ``server:status`` command to check if a web server is + listening on a certain socket: + + .. code-block:: bash + + $ php app/console server:status + + $ php app/console server:status 192.168.0.1:8080 + + The first command shows if your Symfony application will be server through + ``localhost:8000``, the second one does the same for ``192.168.0.1:8080``. + +.. note:: + + Before Symfony 2.6, the ``server:run`` command was used to start the built-in + web server. This command is still available and behaves slightly different. + Instead of starting the server in the background, it will block the current + terminal until you terminate it (this is usually done by pressing Ctrl + and C). + Command Options ---------------- +~~~~~~~~~~~~~~~ The built-in web server expects a "router" script (read about the "router" script on `php.net`_) as an argument. Symfony already passes such a router @@ -47,14 +72,32 @@ script: .. code-block:: bash - $ php app/console server:run --env=test --router=app/config/router_test.php + $ php app/console server:start --env=test --router=app/config/router_test.php If your application's document root differs from the standard directory layout, you have to pass the correct location using the ``--docroot`` option: .. code-block:: bash - $ php app/console server:run --docroot=public_html + $ php app/console server:start --docroot=public_html + +Stopping the Server +------------------- + +When you are finished, you can simply stop the web server using the ``server:stop`` +command: + +.. code-block:: bash + + $ php app/console server:stop + +Like with the start command, if you omit the socket information, Symfony will +stop the web server bound to ``localhost:8000``. Just pass the socket information +when the web server listens to another IP address or to another port: + +.. code-block:: bash + + $ php app/console server:stop 192.168.0.1:8080 .. _`built-in web server`: http://www.php.net/manual/en/features.commandline.webserver.php .. _`php.net`: http://php.net/manual/en/features.commandline.webserver.php#example-401 diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 7d6728775fa..28704ef5633 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -70,7 +70,13 @@ to run Symfony: .. code-block:: bash - $ php app/console server:run + $ php app/console server:start + +When you are finished, you can stop it with the ``server:stop`` command: + +.. code-block:: bash + + $ php app/console server:stop .. seealso:: From fef57d5497d2f5ad81969e1d6725f869260b0af4 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 19 Oct 2014 22:38:08 +0200 Subject: [PATCH 250/835] Reworded a misleading Doctrine explanation --- book/doctrine.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 39a07663ad0..f8d9867bbc6 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -542,13 +542,12 @@ Take a look at the previous example in more detail: .. note:: - In fact, since Doctrine is aware of all your managed entities, when you - call the ``flush()`` method, it calculates an overall changeset and executes - the most efficient query/queries possible. For example, if you persist a - total of 100 ``Product`` objects and then subsequently call ``flush()``, - Doctrine will create a *single* prepared statement and re-use it for each - insert. This pattern is called *Unit of Work*, and it's used because it's - fast and efficient. + In fact, since Doctrine is aware of all your managed entities, when you call + the ``flush()`` method, it calculates an overall changeset and executes + the queries in the correct order. It utilizes cached prepare statement to + slightly improve the performance. For example, if you persist a total of 100 + ``Product`` objects and then subsequently call ``flush()``, Doctrine will + execute 100 ``INSERT`` queries using a single prepared statement object. When creating or updating objects, the workflow is always the same. In the next section, you'll see how Doctrine is smart enough to automatically issue From d02c7c4663b82bd34683be828b0f12f74de88e5f Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Sun, 19 Oct 2014 22:59:02 +0200 Subject: [PATCH 251/835] Updates according to GH feedback --- cookbook/controller/error_pages.rst | 37 ++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index b087bd69fab..83b8c0b4d0d 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -169,6 +169,32 @@ To use this feature, you need to have a definition in your resource: "@TwigBundle/Resources/config/routing/errors.xml" prefix: /_error + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/routing_dev.php + use Symfony\Component\Routing\RouteCollection; + + $collection = new RouteCollection(); + $collection->addCollection( + $loader->import("@AcmeHelloBundle/Resources/config/routing.php") + ); + $collection->addPrefix("/error"); + + return $collection; + If you're coming from an older version of Symfony, you might need to add this to your ``routing_dev.yml`` file. If you're starting from scratch, the `Symfony Standard Edition`_ already contains it for you. @@ -183,15 +209,6 @@ With this route added, you can use URLs like to preview the *error* page for a given status code as HTML or for a given status code and format. -.. tip:: - - You should not set ``kernel.debug`` to ``false`` in order to see your - error pages during development. This will also stop - Symfony from recompiling your twig templates, among other things. - -.. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle -.. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard/ - .. _custom-exception-controller: Replacing the Default ExceptionController @@ -329,4 +346,6 @@ several) listeners deal with them. Good luck! +.. _`WebfactoryExceptionsBundle`: https://github.com/webfactory/exceptions-bundle +.. _`Symfony Standard Edition`: https://github.com/symfony/symfony-standard/ .. _`ExceptionListener`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php From 9838438bdf6908cbb67cb5fda20e026202da9912 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 19 Oct 2014 19:22:04 -0400 Subject: [PATCH 252/835] [#4293] Fixing bad path --- cookbook/controller/error_pages.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index 83b8c0b4d0d..951a02fa823 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -189,7 +189,7 @@ To use this feature, you need to have a definition in your $collection = new RouteCollection(); $collection->addCollection( - $loader->import("@AcmeHelloBundle/Resources/config/routing.php") + $loader->import('@TwigBundle/Resources/config/routing/errors.xml') ); $collection->addPrefix("/error"); From 7eb936771194be16743637e4579fc94f0fedf2cd Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 20 Oct 2014 12:44:32 +0200 Subject: [PATCH 253/835] Updated the sphinx submodule to ad support for best-practice directive --- _exts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_exts b/_exts index 734d7353b5f..e58edd22d16 160000 --- a/_exts +++ b/_exts @@ -1 +1 @@ -Subproject commit 734d7353b5fe8cc7b8edf80c68d9c0f754697fad +Subproject commit e58edd22d16cb247267025d557410dcbfa5fa959 From 74dc6f81d83c10220b9472f6fa09c36c359f95d4 Mon Sep 17 00:00:00 2001 From: Geert De Deckere Date: Mon, 20 Oct 2014 10:59:26 +0200 Subject: [PATCH 254/835] Correct capitalization for the Content-Type header --- book/http_fundamentals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index f89666b4c69..85b3f165d50 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -186,7 +186,7 @@ PHP? In reality, PHP abstracts you a bit from the whole process:: $uri = $_SERVER['REQUEST_URI']; $foo = $_GET['foo']; - header('Content-type: text/html'); + header('Content-Type: text/html'); echo 'The URI requested is: '.$uri; echo 'The value of the "foo" parameter is: '.$foo; From eae1a03cf71ef804cb294f5432ead4ca6e646311 Mon Sep 17 00:00:00 2001 From: Veltar Date: Wed, 3 Sep 2014 12:45:23 +0200 Subject: [PATCH 255/835] Clarified the bundle base template bit. --- book/templating.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/templating.rst b/book/templating.rst index e0f519be009..f4e642f2318 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -413,6 +413,8 @@ lives in a specific location: that's specific to the ``AcmeBlogBundle``. Since the middle, "controller", portion is missing (e.g. ``Blog``), the template lives at ``Resources/views/layout.html.twig`` inside ``AcmeBlogBundle``. + Note that the colon is not removed with the "controller", and two colons exist + between the "bundle"name and template file name. * ``::base.html.twig``: This syntax refers to an application-wide base template or layout. Notice that the string begins with two colons (``::``), meaning From c8f96c02f450593edbb1849ec39d0c85ab181eee Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 20 Oct 2014 09:34:18 -0400 Subject: [PATCH 256/835] [#4196] Updating language --- book/templating.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index f4e642f2318..8dd65e51267 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -413,8 +413,8 @@ lives in a specific location: that's specific to the ``AcmeBlogBundle``. Since the middle, "controller", portion is missing (e.g. ``Blog``), the template lives at ``Resources/views/layout.html.twig`` inside ``AcmeBlogBundle``. - Note that the colon is not removed with the "controller", and two colons exist - between the "bundle"name and template file name. + Yes, there are 2 colons in the middle of the string when the "controller" + subdirectory part is missing. * ``::base.html.twig``: This syntax refers to an application-wide base template or layout. Notice that the string begins with two colons (``::``), meaning From 6e9e1e6dd50bdd791c5db8e2fecf796f42098042 Mon Sep 17 00:00:00 2001 From: Mike Sivolobov Date: Tue, 21 Oct 2014 01:33:09 +0700 Subject: [PATCH 257/835] Removed extra parenthesis --- best_practices/business-logic.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index e844eaec2cc..aed61983a45 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -104,7 +104,7 @@ Now you can use the custom slugger in any controller class, such as the // ... if ($form->isSubmitted() && $form->isValid()) { - $slug = $this->get('slugger')->slugify($post->getTitle())); + $slug = $this->get('slugger')->slugify($post->getTitle()); $post->setSlug($slug); // ... From 16daca4ec053cb6ddcd038a3b71767f1f54ee538 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 20 Oct 2014 21:11:07 +0200 Subject: [PATCH 258/835] fix route prefix in PHP code example --- cookbook/controller/error_pages.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index 951a02fa823..35199483430 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -191,7 +191,7 @@ To use this feature, you need to have a definition in your $collection->addCollection( $loader->import('@TwigBundle/Resources/config/routing/errors.xml') ); - $collection->addPrefix("/error"); + $collection->addPrefix("/_error"); return $collection; @@ -326,7 +326,7 @@ another page or rendering specialized error pages. If your listener calls ``setResponse()`` on the :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent`, - event propagation will be stopped and the response will be sent to + event propagation will be stopped and the response will be sent to the client. This approach allows you to create centralized and layered error @@ -338,7 +338,7 @@ several) listeners deal with them. To see an example, have a look at the `ExceptionListener`_ in the Security Component. - + It handles various security-related exceptions that are thrown in your application (like :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`) and takes measures like redirecting the user to the login page, From 1a1476ecad140af03e5145b9c8f6391eea32f792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 6 Oct 2014 18:13:48 +0200 Subject: [PATCH 259/835] [Serializer] Handle circular references. symfony/symfony#12098. --- components/serializer.rst | 97 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/components/serializer.rst b/components/serializer.rst index 872c5936e8b..4508482f88f 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -238,6 +238,101 @@ When serializing, you can set a callback to format a specific object property:: $serializer->serialize($person, 'json'); // Output: {"name":"cordoval", "age": 34, "createdAt": "2014-03-22T09:43:12-0500"} +Handling Circular References +---------------------------- + +.. versionadded:: 2.6 + Handling of circular references was introduced in Symfony 2.6. In previous + versions of Symfony, circular references led to infinite loops. + +Circular references are common when dealing with entity relations:: + + class Organization + { + private $name; + private $members; + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setMembers(array $members) + { + $this->members = $members; + } + + public function getMembers() + { + return $this->members; + } + } + + class Member + { + private $name; + private $organization; + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setOrganization(Organization $organization) + { + $this->organization = $organization; + } + + public function getOrganization() + { + return $this->organization; + } + } + +To avoid infinite loops, :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` +throws a :class:`Symfony\\Component\\Serializer\\Exception\\CircularReferenceException` +when such case is encountered:: + + $member = new Member(); + $member->setName('Kévin'); + + $org = new Organization(); + $org->setName('Les-Tilleuls.coop'); + $org->setMembers(array($member)); + + $member->setOrganization($kevin); + + echo $serializer->serialize($org, 'json'); // Throws a CircularReferenceException + +The ``setCircularReferenceLimit()`` method of this normalizer sets the number +of times serializing the same object must occur before considering being +in a circular reference. Its default value is ``1``. + +Instead of throwing an exception, circular references can also be handled +by custom callables. This is especially useful when serializing entities +having unique identifiers:: + + $encoder = new JsonEncoder(); + $normalizer = new GetSetMethodNormalizer(); + + $normalizer->setCircularReferenceHandler(function ($object) { + return $object->getName(); + }); + + $serializer = new Serializer(array($normalizer), array($encoder)); + echo $serializer->serialize($org, 'json'); + // {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"]} + JMSSerializer ------------- @@ -245,7 +340,7 @@ A popular third-party library, `JMS serializer`_, provides a more sophisticated albeit more complex solution. This library includes the ability to configure how your objects should be serialized/deserialized via annotations (as well as YAML, XML and PHP), integration with the Doctrine ORM, -and handling of other complex cases (e.g. circular references). +and handling of other complex cases. .. _`JMS serializer`: https://github.com/schmittjoh/serializer .. _Packagist: https://packagist.org/packages/symfony/serializer From b5b45bccfe74d3c7a13660f01d4388240bd0f114 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 21 Oct 2014 08:52:28 +0200 Subject: [PATCH 260/835] move Dumper section above the Caster one --- components/var_dumper/advanced.rst | 116 ++++++++++++++--------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index d1c1609a229..bf15c1bfe79 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -27,6 +27,64 @@ Before cloning, you can configure the limits with:: They will be applied when calling ``->cloneVar()`` afterwards. +Dumpers +~~~~~~~ + +A dumper is responsible for outputting a string representation of a PHP variable, +using a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object as input. +The destination and the formatting of this output vary with dumpers. + +This component comes with an :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` +for HTML output and a :class:`Symfony\\Component\\VarDumper\\Dumper\\CliDumper` +for optionally colored command line output. + +For example, if you want to dump some ``$variable``, just do:: + + $cloner = new VarCloner(); + $dumper = new CliDumper(); + + $dumper->dump($cloner->cloneVar($variable)); + +By using the first argument of the constructor, you can select the output +stream where the dump will be written. By default, the ``CliDumper`` writes +on ``php://stdout`` and the ``HtmlDumper`` on ``php://output``, but any PHP +stream (resource or URL) is acceptable. + +Instead of a stream destination, you can also pass it a ``callable`` that +will be called repeatedly for each line generated by a dumper. This +callable can be configured using the first argument of a dumper's constructor, +but also using the +:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::setLineDumper` +method or the second argument of the +:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::dump` method. + +For example, to get a dump as a string in a variable, you can do:: + + $cloner = new VarCloner(); + $dumper = new CliDumper(); + $output = ''; + + $dumper->dump( + $cloner->cloneVar($variable), + function ($line, $depth) use (&$output) { + // A negative depth means "end of dump" + if ($depth >= 0) { + // Adds a two spaces indentation to the line + $output .= str_repeat(' ', $depth).$line."\n"; + } + } + ); + + // $output is now populated with the dump representation of $variable + +Dumpers implement the :class:`Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface` +interface that specifies the +:method:`dump(Data $data) ` +method. They also typically implement the +:class:`Symfony\\Component\\VarDumper\\Cloner\\DumperInterface` that frees +them from re-implementing the logic required to walk through a +:class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object's internal structure. + Casters ~~~~~~~ @@ -98,61 +156,3 @@ properties not in the class declaration). .. note:: Although you can, it is best advised not to alter the state of an object while casting it in a Caster. - -Dumpers -~~~~~~~ - -A dumper is responsible for outputting a string representation of a PHP variable, -using a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object as input. -The destination and the formatting of this output vary with dumpers. - -This component comes with an :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` -for HTML output and a :class:`Symfony\\Component\\VarDumper\\Dumper\\CliDumper` -for optionally colored command line output. - -For example, if you want to dump some ``$variable``, just do:: - - $cloner = new VarCloner(); - $dumper = new CliDumper(); - - $dumper->dump($cloner->cloneVar($variable)); - -By using the first argument of the constructor, you can select the output -stream where the dump will be written. By default, the ``CliDumper`` writes -on ``php://stdout`` and the ``HtmlDumper`` on ``php://output``, but any PHP -stream (resource or URL) is acceptable. - -Instead of a stream destination, you can also pass it a ``callable`` that -will be called repeatedly for each line generated by a dumper. This -callable can be configured using the first argument of a dumper's constructor, -but also using the -:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::setLineDumper` -method or using the second argument of the -:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::dump` method. - -For example, to get a dump in a variable, you can do:: - - $cloner = new VarCloner(); - $dumper = new CliDumper(); - $output = ''; - - $dumper->dump( - $cloner->cloneVar($variable), - function ($line, $depth) use (&$output) { - // A negative depth means "end of dump" - if ($depth >= 0) { - // Adds a two spaces indentation to the line - $output .= str_repeat(' ', $depth).$line."\n"; - } - } - ); - - // $output is now populated with the dump representation of $variable - -Dumpers implement the :class:`Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface` -interface that specifies the -:method:`dump(Data $data) ` -method. They also typically implement the -:class:`Symfony\\Component\\VarDumper\\Cloner\\DumperInterface` that frees -them from re-implementing the logic required to walk through a -:class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object's internal structure. From 7ae82858119c1a6de056b7c63a88f3bd83ea88ec Mon Sep 17 00:00:00 2001 From: Erik Saunier Date: Tue, 21 Oct 2014 11:31:13 +0200 Subject: [PATCH 261/835] Missing space --- cookbook/security/voters_data_permission.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 071f3733176..7359b0b27cb 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -96,7 +96,7 @@ edit a particular object. Here's an example implementation:: // check if the voter is used correct, only allow one attribute // this isn't a requirement, it's just one easy way for you to // design your voter - if(1 !== count($attributes)) { + if (1 !== count($attributes)) { throw new \InvalidArgumentException( 'Only one attribute is allowed for VIEW or EDIT' ); From 7992fdfa97cdbe126e33ad119098ab923632a336 Mon Sep 17 00:00:00 2001 From: arnau_ventress_raring Date: Tue, 21 Oct 2014 12:19:27 +0200 Subject: [PATCH 262/835] Remove incoherence between Doctrine and Propel introduction paragraphs --- book/doctrine.rst | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 39a07663ad0..8eb1f28b9fd 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -5,11 +5,13 @@ Databases and Doctrine ====================== One of the most common and challenging tasks for any application -involves persisting and reading information to and from a database. Fortunately, -Symfony comes integrated with `Doctrine`_, a library whose sole goal is to -give you powerful tools to make this easy. In this chapter, you'll learn the -basic philosophy behind Doctrine and see how easy working with a database can -be. +involves persisting and reading information to and from a database. Although +the Symfony full-stack framework doesn't integrate any ORM by default, +the Symfony Standard Edition, which is the most widely used distribution, +comes integrated with `Doctrine`_, a library whose sole goal is to give +you powerful tools to make this easy. In this chapter, you'll learn the +basic philosophy behind Doctrine and see how easy working with a database +can be. .. note:: From 82ea9dfda46f7ee639ddccbd62b7289ea453da6b Mon Sep 17 00:00:00 2001 From: mhor Date: Tue, 21 Oct 2014 16:56:50 +0200 Subject: [PATCH 263/835] fix Twig-extensions links --- cookbook/templating/twig_extension.rst | 2 +- reference/dic_tags.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/templating/twig_extension.rst b/cookbook/templating/twig_extension.rst index f3f17dfbbc0..14913b7b63d 100644 --- a/cookbook/templating/twig_extension.rst +++ b/cookbook/templating/twig_extension.rst @@ -126,7 +126,7 @@ Learning further For a more in-depth look into Twig Extensions, please take a look at the `Twig extensions documentation`_. -.. _`Twig official extension repository`: https://github.com/fabpot/Twig-extensions +.. _`Twig official extension repository`: https://github.com/twigphp/Twig-extensions .. _`Twig extensions documentation`: http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension .. _`global variables`: http://twig.sensiolabs.org/doc/advanced.html#id1 .. _`functions`: http://twig.sensiolabs.org/doc/advanced.html#id2 diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index a746ef7ef86..833b4cc33d1 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -1393,6 +1393,6 @@ Then, tag it with the ``validator.initializer`` tag (it has no options). For an example, see the ``EntityInitializer`` class inside the Doctrine Bridge. .. _`Twig's documentation`: http://twig.sensiolabs.org/doc/advanced.html#creating-an-extension -.. _`Twig official extension repository`: https://github.com/fabpot/Twig-extensions +.. _`Twig official extension repository`: https://github.com/twigphp/Twig-extensions .. _`SwiftMailer's Plugin Documentation`: http://swiftmailer.org/docs/plugins.html .. _`Twig Loader`: http://twig.sensiolabs.org/doc/api.html#loaders From 49e5dad6eb824ea3a687905333625c7e2c3a81de Mon Sep 17 00:00:00 2001 From: Mike Sivolobov Date: Tue, 21 Oct 2014 21:52:46 +0700 Subject: [PATCH 264/835] Fixed link to documentation standards --- contributing/documentation/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/documentation/overview.rst b/contributing/documentation/overview.rst index 2bf7b9a7a29..0bf56f0820f 100644 --- a/contributing/documentation/overview.rst +++ b/contributing/documentation/overview.rst @@ -68,7 +68,7 @@ memorable name for the new branch: **Step 5.** Now make your changes in the documentation. Add, tweak, reword and even remove any content, but make sure that you comply with the -doc:`/contributing/documentation/standards`. +:doc:`/contributing/documentation/standards`. **Step 6.** **Push** the changes to your forked repository: From d1bfa91ea95482dd0d3c9c201ad7fa6ac860718b Mon Sep 17 00:00:00 2001 From: Matt Janssen Date: Tue, 21 Oct 2014 13:22:53 -0500 Subject: [PATCH 265/835] Added missing closing parenthesis to example. --- components/form/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/form/introduction.rst b/components/form/introduction.rst index 74b47461b65..fb39767a7fb 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -514,7 +514,7 @@ by ``handleRequest()`` to determine whether a form has been submitted): $formBuilder = $formFactory->createBuilder('form', null, array( 'action' => '/search', 'method' => 'GET', - ); + )); // ... From 6c108458a154a7a94d5c7167cba89abdbaf626ca Mon Sep 17 00:00:00 2001 From: astery Date: Wed, 22 Oct 2014 12:23:53 +0400 Subject: [PATCH 266/835] Missing apostrophe in source example. --- cookbook/doctrine/dbal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/doctrine/dbal.rst b/cookbook/doctrine/dbal.rst index 5cc468202c2..9c163c5271c 100644 --- a/cookbook/doctrine/dbal.rst +++ b/cookbook/doctrine/dbal.rst @@ -166,7 +166,7 @@ mapping type: // app/config/config.php $container->loadFromExtension('doctrine', array( 'dbal' => array( - mapping_types' => array( + 'mapping_types' => array( 'enum' => 'string', ), ), From 575c32055fe03ff69bf50c56c1bd6640f505592b Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Tue, 14 Oct 2014 17:42:35 +0200 Subject: [PATCH 267/835] Updated OptionsResolver documentation: removed static methods --- components/options_resolver.rst | 802 ++++++++++++++++---------------- 1 file changed, 409 insertions(+), 393 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 22b1943f9e9..ea1372f4ea6 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -5,7 +5,7 @@ The OptionsResolver Component ============================= - The OptionsResolver component helps you to easily process option arrays. + The OptionsResolver component is `array_replace()` on steroids. Installation ------------ @@ -15,14 +15,17 @@ You can install the component in 2 different ways: * :doc:`Install it via Composer ` (``symfony/options-resolver`` on `Packagist`_); * Use the official Git repository (https://github.com/symfony/OptionsResolver). -Usage ------ +Notes on Previous Versions +-------------------------- .. versionadded:: 2.6 This documentation was written for Symfony 2.6 and later. If you use an older version, please read the corresponding documentation using the version drop-down on the upper right. +Usage +----- + Imagine you have a ``Mailer`` class which has four options: ``host``, ``username``, ``password`` and ``port``:: @@ -62,8 +65,39 @@ check which options are set:: } This boilerplate is hard to read and repetitive. Also, the default values of the -options are buried in the business logic of your code. Let's use -:method:`Symfony\\Component\\OptionsResolver\\Options::resolve` to fix that:: +options are buried in the business logic of your code. We can use +:phpfunction:`array_replace` to fix that:: + + class Mailer + { + // ... + public function __construct(array $options = array()) + { + $this->options = array_replace(array( + 'host' => 'smtp.example.org', + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, + ), $options); + } + } + +Now all four options are guaranteed to be set. But what happens if the user of +the ``Mailer`` class does a mistake? + +.. code-block:: php + + $mailer = new Mailer(array( + 'usernme' => 'johndoe', + )); + +No error will be shown. In the best case, the bug will be appear during testing. +The developer will possibly spend a lot of time looking for the problem. In the +worst case, however, the bug won't even appear and will be deployed to the live +system. + +Let's use the :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` +class to fix this problem:: use Symfony\Component\OptionsResolver\Options; @@ -72,29 +106,31 @@ options are buried in the business logic of your code. Let's use // ... public function __construct(array $options = array()) { - $this->options = Options::resolve($options, array( + $resolver = new OptionsResolver(); + $resolver->setDefaults(array( 'host' => 'smtp.example.org', 'username' => 'user', 'password' => 'pa$$word', 'port' => 25, )); + + $this->options = $resolver->resolve($options); } } -Now all options are guaranteed to be set. Any option that wasn't passed through -``$options`` will be set to the specified default value. Additionally, an -:class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` +Like before, all options will be guaranteed to be set. Additionally, an +:class:`Symfony\\Component\\OptionsResolver\\Exception\\UndefinedOptionsException` is thrown if an unknown option is passed:: $mailer = new Mailer(array( 'usernme' => 'johndoe', )); - // InvalidOptionsException: The option "usernme" does not exist. Known - // options are: "host", "password", "username" + // UndefinedOptionsException: The option "usernme" does not exist. Known + // options are: "host", "password", "port", "username" -The rest of your code can now access the values of the options without -boilerplate code:: +The rest of your code can access the values of the options without boilerplate +code:: // ... class Mailer @@ -111,12 +147,7 @@ boilerplate code:: } } -Required Options -~~~~~~~~~~~~~~~~ - -If an option must be set by the caller, pass that option to -:method:`Symfony\\Component\\OptionsResolver\\Options::validateRequired`. -For example, let's make the ``host`` option required:: +It's a good practice to split the option configuration into a separate method:: // ... class Mailer @@ -124,17 +155,64 @@ For example, let's make the ``host`` option required:: // ... public function __construct(array $options = array()) { - Options::validateRequired($options, 'host'); + $resolver = new OptionsResolver(); + $this->configureOptions($resolver); + + $this->options = $resolver->resolve($options); + } - $this->options = Options::resolve($options, array( - 'host' => null, + protected function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults(array( + 'host' => 'smtp.example.org', 'username' => 'user', 'password' => 'pa$$word', - 'port' => 25, + 'port' => 25, + 'encryption' => null, )); } } +First, your code becomes easier to read, especially if the constructor does more +than processing options. Second, sub-classes may now override the +``configureOptions()`` method to adjust the configuration of the options:: + + // ... + class GoogleMailer extends Mailer + { + protected function configureOptions(OptionsResolver $resolver) + { + parent::configureOptions($resolver); + + $resolver->setDefaults(array( + 'host' => 'smtp.google.com', + 'encryption' => 'ssl', + )); + } + } + +Required Options +~~~~~~~~~~~~~~~~ + +If an option must be set by the caller, pass that option to +:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setRequired`. +For example, let's make the ``host`` option required:: + + // ... + class Mailer + { + // ... + protected function configureOptions(OptionsResolver $resolver) + { + // ... + $resolver->setRequired('host'); + } + } + +.. versionadded:: 2.6 + Before Symfony 2.6, `setRequired()` accepted only arrays. Since then, single + option names can be passed as well. + If you omit a required option, a :class:`Symfony\\Component\\OptionsResolver\\Exception\\MissingOptionsException` will be thrown:: @@ -143,42 +221,110 @@ will be thrown:: // MissingOptionsException: The required option "host" is missing. -The :method:`Symfony\\Component\\OptionsResolver\\Options::validateRequired` +The :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setRequired` method accepts a single name or an array of option names if you have more than -one required option. +one required option:: -.. note:: + // ... + class Mailer + { + // ... + protected function configureOptions(OptionsResolver $resolver) + { + // ... + $resolver->setRequired(array('host', 'username', 'password')); + } + } - As you can see, the ``host`` option must still be passed to - :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`, - otherwise the method will not accept that option. The default value, - however, can be omitted as the option must be set by the caller. +.. versionadded:: 2.6 + The methods :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isRequired` + and :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::getRequiredOptions` + were introduced in Symfony 2.6. + +Use :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isRequired` to find +out if an option is required. You can use +:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::getRequiredOptions` to +retrieve the names of all required options:: + + // ... + class GoogleMailer extends Mailer + { + protected function configureOptions(OptionsResolver $resolver) + { + parent::configureOptions($resolver); + + if ($resolver->isRequired('host')) { + // ... + } + + $requiredOptions = $resolver->getRequiredOptions(); + } + } + +.. versionadded:: 2.6 + The methods :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isMissing` + and :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::getMissingOptions` + were introduced in Symfony 2.6. + +If you want to check whether a required option is still missing from the default +options, you can use :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isMissing`. +The difference to :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isRequired` +is that this method will return false for required options that have already +been set:: + + // ... + class Mailer + { + // ... + protected function configureOptions(OptionsResolver $resolver) + { + // ... + $resolver->setRequired('host'); + } + } + + // ... + class GoogleMailer extends Mailer + { + protected function configureOptions(OptionsResolver $resolver) + { + parent::configureOptions($resolver); + + $resolver->isRequired('host'); + // => true + + $resolver->isMissing('host'); + // => true + + $resolver->setDefault('host', 'smtp.google.com'); + + $resolver->isRequired('host'); + // => true + + $resolver->isMissing('host'); + // => false + } + } + +The method :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::getMissingOptions` +lets you access the names of all missing options. Type Validation ~~~~~~~~~~~~~~~ You can run additional checks on the options to make sure they were passed correctly. To validate the types of the options, call -:method:`Symfony\\Component\\OptionsResolver\\Options::validateTypes`:: - +:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setAllowedTypes`:: + // ... class Mailer { // ... - public function __construct(array $options = array()) + protected function configureOptions(OptionsResolver $resolver) { // ... - Options::validateTypes($options, array( - 'host' => 'string', - 'port' => array('null', 'int'), - )); - - $this->options = Options::resolve($options, array( - 'host' => null, - 'username' => 'user', - 'password' => 'pa$$word', - 'port' => 25, - )); + $resolver->setAllowedTypes('host', 'string'); + $resolver->setAllowedTypes('port', array('null', 'int')); } } @@ -186,7 +332,8 @@ For each option, you can define either just one type or an array of acceptable types. You can pass any type for which an ``is_()`` method is defined. Additionally, you may pass fully qualified class or interface names. -If you pass an invalid option now, an :class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` +If you pass an invalid option now, an +:class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` is thrown:: $mailer = new Mailer(array( @@ -196,34 +343,40 @@ is thrown:: // InvalidOptionsException: The option "host" with value "25" is expected to // be of type "string" +In sub-classes, you can use :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::addAllowedTypes` +to add additional allowed types without erasing the ones already set. + +.. versionadded:: 2.6 + Before Symfony 2.6, `setAllowedTypes()` and `addAllowedTypes()` expected + the values to be given as an array mapping option names to allowed types: + + .. code-block:: php + + $resolver->setAllowedTypes(array('port' => array('null', 'int'))); + Value Validation ~~~~~~~~~~~~~~~~ Some options can only take one of a fixed list of predefined values. For example, suppose the ``Mailer`` class has a ``transport`` option which can be one of ``sendmail``, ``mail`` and ``smtp``. Use the method -:method:`Symfony\\Component\\OptionsResolver\\Options::validateValues` to verify +:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setAllowedValues` to verify that the passed option contains one of these values:: // ... class Mailer { // ... - public function __construct(array $options = array()) + protected function configureOptions(OptionsResolver $resolver) { // ... - Options::validateValues($options, array( - 'transport' => array('sendmail', 'mail', 'smtp'), - )); - - $this->options = Options::resolve($options, array( - // ... - 'transport' => 'sendmail', - )); + $resolver->setDefault('transport', 'sendmail'); + $resolver->setAllowedValues('transport', array('sendmail', 'mail', 'smtp')); } } -If you pass an invalid transport, an :class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` +If you pass an invalid transport, an +:class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` is thrown:: $mailer = new Mailer(array( @@ -233,467 +386,330 @@ is thrown:: // InvalidOptionsException: The option "transport" has the value "send-mail", // but is expected to be one of "sendmail", "mail", "smtp" -For options with more complicated validation schemes, pass a callback which +For options with more complicated validation schemes, pass a closure which returns ``true`` for acceptable values and ``false`` for invalid values:: - Options::validateValues($options, array( + $resolver->setAllowedValues(array( // ... - 'transport' => function ($value) { + $resolver->setAllowedValues('transport', function ($value) { // return true or false - }, + }); )); -Default Values that Depend on another Option -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In sub-classes, you can use :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::addAllowedValues` +to add additional allowed values without erasing the ones already set. -Suppose you want to set the default value of the ``port`` option based on the -encryption chosen by the user of the ``Mailer`` class. More precisely, we want -to set the port to ``465`` if SSL is used and to ``25`` otherwise. +.. versionadded:: 2.6 + Before Symfony 2.6, `setAllowedValues()` and `addAllowedValues()` expected + the values to be given as an array mapping option names to allowed values: -You can implement this feature by passing a closure as default value of the -``port`` option. The closure receives the options as argument. Based on these -options, you can return the desired default value:: + .. code-block:: php + + $resolver->setAllowedValues(array('transport' => array('sendmail', 'mail', 'smtp'))); + +Option Normalization +~~~~~~~~~~~~~~~~~~~~ + +Sometimes, option values need to be normalized before you can use them. For +instance, assume that the ``host`` should always start with ``http://``. To do +that, you can write normalizers. Normalizers are executed after validating an +option. You can configure a normalizer by calling +:method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizer`:: // ... class Mailer { // ... - public function __construct(array $options = array()) + protected function configureOptions(OptionsResolver $resolver) { // ... + $resolver->setNormalizer('host', function ($options, $value) { + if ('http://' !== substr($value, 0, 7)) { + $value = 'http://'.$value; + } - $this->options = Options::resolve($options, new Options(array( - // ... - 'encryption' => null, - 'port' => function (Options $options) { - if ('ssl' === $options['encryption']) { - return 465; - } - - return 25; - }, - ))); + return $value; + }); } } -Instead of a simple array, we now pass the default options as -:class:`Symfony\\Component\\OptionsResolver\\Options` instance to -:method:`Symfony\\Component\\OptionsResolver\\Options::resolve`. This class -makes sure that the closure stored in the default value of the ``port`` option -is called. In the closure, you can use the -:class:`Symfony\\Component\\OptionsResolver\\Options` instance just like a -normal option array. - -.. caution:: - - The first argument of the closure must be type hinted as ``Options``. - Otherwise, the closure is considered as the default value of the option. - If the closure is still not called, double check that you passed the default - options as :class:`Symfony\\Component\\OptionsResolver\\Options` instance. - -.. note:: - - The closure is only executed if the ``port`` option isn't set by the user. - -Coding Patterns -~~~~~~~~~~~~~~~ +.. versionadded:: 2.6 + The method :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizer` + was introduced in Symfony 2.6. Before, you had to use + :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizers`. -If you have a large list of options, the option processing code can take up a -lot of space of your method. To make your code easier to read and maintain, it -is a good practice to put the option definitions into static class properties:: +The normalizer receives the actual ``$value`` and returns the normalized form. +You see that the closure also takes an ``$options`` parameter. This is useful +if you need to use other options during normalization:: // ... class Mailer { - private static $defaultOptions = array( - 'host' => null, - 'username' => 'user', - 'password' => 'pa$$word', - 'port' => 25, - 'encryption' => null, - ); - - private static $requiredOptions = array( - 'host', - ); - - private static $optionTypes = array( - 'host' => 'string', - 'username' => 'string', - 'password' => 'string', - 'port' => 'int', - ); - - private static $optionValues = array( - 'encryption' => array(null, 'ssl', 'tls'), - ); - - protected $options; - - public function __construct(array $options = array()) + // ... + protected function configureOptions(OptionsResolver $resolver) { - Options::validateRequired($options, static::$requiredOptions); - Options::validateTypes($options, static::$optionTypes); - Options::validateValues($options, static::$optionValues); + // ... + $resolver->setNormalizer('host', function ($options, $value) { + if (!in_array(substr($value, 0, 7), array('http://', 'https://'))) { + if ('ssl' === $options['encryption']) { + $value = 'https://'.$value; + } else { + $value = 'http://'.$value; + } + } - $this->options = Options::resolve($options, static::$defaultOptions); + return $value; + }); } } -In this way, the class remains easy to read and maintain even with a lot of -options being processed and validated. +Default Values that Depend on another Option +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Suppose you want to set the default value of the ``port`` option based on the +encryption chosen by the user of the ``Mailer`` class. More precisely, we want +to set the port to ``465`` if SSL is used and to ``25`` otherwise. -.. caution:: +You can implement this feature by passing a closure as default value of the +``port`` option. The closure receives the options as argument. Based on these +options, you can return the desired default value:: - PHP does not support closures in property definitions. In such cases, you - must move your closure to a static method:: + use Symfony\Component\OptionsResolver\Options; - private static $defaultOptions = array( + // ... + class Mailer + { + // ... + protected function configureOptions(OptionsResolver $resolver) + { // ... - 'port' => array(__CLASS__, 'getDefaultPort'), - ); + $resolver->setDefault('encryption', null); - public static function getDefaultPort(Options $options) - { - if ('ssl' === $options['encryption']) { - return 465; - } + $resolver->setDefault('port', function (Options $options) { + if ('ssl' === $options['encryption']) { + return 465; + } - return 25; + return 25; + }); } + } -Decoupling the Option Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. caution:: -So far, the configuration of the options, their allowed types etc. was very -tightly coupled to the code that resolves the options. This is fine in most cases. -In some cases, however, the configuration of options must be distributed across -multiple classes. An example is a class hierarchy that supports the addition of -options by subclasses. In those cases, you can create an -:class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` object and pass that -object everywhere that you want to adjust the option configuration. Then, call -:method:`Symfony\\Component\\OptionsResolver\\Options::resolve` with the -configuration object to resolve the options. + The argument of the callable must be type hinted as ``Options``. Otherwise, + the callable is considered as the default value of the option. -The following code demonstrates how to write our previous ``Mailer`` class with -an :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` object:: +.. note:: - // ... - use Symfony\Component\OptionsResolver\OptionsConfig; + The closure is only executed if the ``port`` option isn't set by the user + or overwritten in a sub-class. + +A previously set default value can be accessed by adding a second argument to +the closure:: + // ... class Mailer { - protected $options; - - public function __construct(array $options = array()) - { - $config = new OptionsConfig(); - $this->configureOptions($config); - - $this->options = Options::resolve($options, $config); - } - - protected function configureOptions(OptionsConfig $config) + // ... + protected function configureOptions(OptionsResolver $resolver) { - $config->setDefaults(array( - 'host' => null, - 'username' => 'user', - 'password' => 'pa$$word', - 'port' => 25, + // ... + $resolver->setDefaults(array( 'encryption' => null, - )); - - $config->setRequired(array( - 'host', - )); - - $config->setAllowedTypes(array( - 'host' => 'string', - 'username' => 'string', - 'password' => 'string', - 'port' => 'int', - )); - - $config->setAllowedValues(array( - 'encryption' => array(null, 'ssl', 'tls'), + 'host' => 'example.org', )); } } -As you can see, the code is very similar as before. However, the performance -is marginally worse, since the creation of an additional object is required: -the :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` instance. - -Nevertheless, this design also has a benefit: We can extend the ``Mailer`` -class and adjust the options of the parent class in the subclass:: - - // ... class GoogleMailer extends Mailer { - protected function configureOptions(OptionsConfig $config) + protected function configureOptions(OptionsResolver $resolver) { - $config->setDefaults(array( - 'host' => 'smtp.google.com', - 'port' => 25, - 'encryption' => 'ssl', - )); + parent::configureOptions($resolver); - $config->setRequired(array( - 'username', - 'password', - )); + $options->setDefault('host', function (Options $options, $previousValue) { + if ('ssl' === $options['encryption']) { + return 'secure.example.org' + } + + // Take default value configured in the base class + return $previousValue; + }); } } -The ``host`` option is no longer required now, but defaults to "smtp.google.com". -The ``username`` and ``password`` options, however, are required in the -subclass. - -The :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` has various -useful methods to find out which options are set or required. Check out the -API documentation to find out more about these methods. +As seen in the example, this feature is mostly useful if you want to reuse the +default values set in parent classes in sub-classes. -.. note:: +Options without Default Values +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` class used - by the Form Component inherits from - :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig`. All the - documentation for ``OptionsConfig`` applies to ``OptionsResolver`` as well. - -Optional Options -~~~~~~~~~~~~~~~~ - -The :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` has one feature -that is not available when not using this class: You can specify optional -options. Optional options will be accepted and validated when set. When not set, -however, *no default value* will be added to the options array. Pass the names -of the optional options to -:method:`Symfony\\Component\\OptionsResolver\\OptionsConfig::setOptional`:: +In some cases, it is useful to define an option without setting a default value. +Mostly, you will need this when you want to know whether an option was passed +or not. If you set a default value for that option, this is not possible:: // ... class Mailer { // ... - protected function configureOptions(OptionsConfig $config) + protected function configureOptions(OptionsResolver $resolver) { // ... + $resolver->setDefault('port', 25); + } - $config->setOptional(array('port')); + // ... + public function sendMail($from, $to) + { + // Is this the default value or did the caller of the class really + // set the port to 25? + if (25 === $this->options['port']) { + // ... + } } } -This is useful if you need to know whether an option was explicitly passed. If -not, it will be missing from the options array:: +.. versionadded:: 2.6 + The method :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setDefined` + was introduced in Symfony 2.6. Before, you had to use + :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setOptional`. + +You can use :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefined` +to define an option without setting a default value. Then the option will only +be included in the resolved options if it was actually passed to +:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::resolve`:: // ... class Mailer { // ... - public function __construct(array $options = array()) + protected function configureOptions(OptionsResolver $resolver) { // ... + $resolver->setDefined('port'); + } + // ... + public function sendMail($from, $to) + { if (array_key_exists('port', $this->options)) { - echo "Set!"; + echo 'Set!'; } else { - echo "Not Set!"; + echo 'Not Set!'; } } } + $mailer = new Mailer(); + $mailer->sendMail($from, $to); + // => Not Set! + $mailer = new Mailer(array( 'port' => 25, )); - // Set! - - $mailer = new Mailer(); - // Not Set! - -.. tip:: + $mailer->sendMail($from, $to); + // => Set! - If you need this functionality when not using an - :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` object, check - the options before calling - :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`:: - - // ... - class Mailer - { - // ... - public function __construct(array $options = array()) - { - // ... - - if (array_key_exists('port', $options)) { - echo "Set!"; - } else { - echo "Not Set!"; - } - - $this->options = Options::resolve($options, array( - // ... - )); - } - } - -Overwriting Default Values -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -A previously set default value can be overwritten by invoking -:method:`Symfony\\Component\\OptionsResolver\\OptionsConfig::setDefaults` -again. When using a closure as the new value it is passed 2 arguments: - -* ``$options``: an :class:`Symfony\\Component\\OptionsResolver\\Options` - instance with all the other default options -* ``$previousValue``: the previously set default value - -.. code-block:: php +You can also pass an array of option names if you want to define multiple +options in one go:: // ... class Mailer { // ... - protected function configureOptions(OptionsConfig $config) + protected function configureOptions(OptionsResolver $resolver) { // ... - $config->setDefaults(array( - 'encryption' => 'ssl', - 'host' => 'localhost', - )); - - // ... - $config->setDefaults(array( - 'encryption' => 'tls', // simple overwrite - 'host' => function (Options $options, $previousValue) { - return 'localhost' == $previousValue - ? '127.0.0.1' - : $previousValue; - }, - )); + $resolver->setDefined(array('port', 'encryption')); } } -.. tip:: +.. versionadded:: 2.6 + The method :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::isDefined` + and :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::getDefinedOptions` + were introduced in Symfony 2.6. - If the previous default value is calculated by an expensive closure and - you don't need access to it, use the - :method:`Symfony\\Component\\OptionsResolver\\OptionsConfig::replaceDefaults` - method instead. It acts like ``setDefaults`` but erases the previous value - to improve performance. This means that the previous default value is not - available when overwriting with another closure:: +The methods :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isDefined` +and :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::getDefinedOptions` +let you find out which options are defined:: - // ... - class Mailer + // ... + class GoogleMailer extends Mailer + { + protected function configureOptions(OptionsResolver $resolver) { - // ... - protected function configureOptions(OptionsConfig $config) - { - // ... - $config->setDefaults(array( - 'encryption' => 'ssl', - 'heavy' => function (Options $options) { - // Some heavy calculations to create the $result - - return $result; - }, - )); - - $config->replaceDefaults(array( - 'encryption' => 'tls', // simple overwrite - 'heavy' => function (Options $options) { - // $previousValue not available - // ... - - return $someOtherResult; - }, - )); - } - } + parent::configureOptions($resolver); -.. note:: + if ($resolver->isDefined('host')) { + // One of the following was called: - Existing option keys that you do not mention when overwriting are preserved. + // $resolver->setDefault('host', ...); + // $resolver->setRequired('host'); + // $resolver->setDefined('host'); + } -Option Normalization -~~~~~~~~~~~~~~~~~~~~ + $definedOptions = $resolver->getDefinedOptions(); + } + } + +Performance Tweaks +~~~~~~~~~~~~~~~~~~ -Some values need to be normalized before you can use them. For instance, -assume that the ``host`` should always start with ``http://``. To do that, -you can write normalizers. Normalizers are executed after all options were -processed. You can configure these normalizers by calling -:method:`Symfony\\Components\\OptionsResolver\\OptionsConfig::setNormalizers`:: +With the current implementation, the ``configureOptions()`` method will be +called for every single instance of the ``Mailer`` class. Depending on the +amount of option configuration and the number of created instances, this may add +noticeable overhead to your application. If that overhead becomes a problem, you +can change your code to do the configuration only once per class:: // ... class Mailer { - // ... - protected function configureOptions(OptionsConfig $config) + private static $resolversByClass = array(); + + protected $options; + + public function __construct(array $options = array()) { - // ... + // Are we a Mailer, a GoogleMailer, ... ? + $class = get_class($this); - $config->setNormalizers(array( - 'host' => function (Options $options, $value) { - if ('http://' !== substr($value, 0, 7)) { - $value = 'http://'.$value; - } + // Did we call configureOptions() before for this class? + if (!isset(self::$resolversByClass[$class])) { + self::$resolversByClass[$class] = new OptionsResolver(); + $this->configureOptions(self::$resolversByClass[$class]); + } - return $value; - }, - )); + $this->options = self::$resolversByClass[$class]->resolve($options); } - } -The normalizer receives the actual ``$value`` and returns the normalized form. -You see that the closure also takes an ``$options`` parameter. This is useful -if you need to use other options for the normalization:: - - // ... - class Mailer - { - // ... - protected function configureOptions(OptionsConfig $config) + protected function configureOptions(OptionsResolver $resolver) { // ... - - $config->setNormalizers(array( - 'host' => function (Options $options, $value) { - if (!in_array(substr($value, 0, 7), array('http://', 'https://'))) { - if ($options['ssl']) { - $value = 'https://'.$value; - } else { - $value = 'http://'.$value; - } - } - - return $value; - }, - )); } } -.. tip:: +Now the :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` instance +will be created once per class and reused from that on. Be aware that this may +lead to memory leaks in long-running applications, if the default options contain +references to objects or object graphs. If that's the case for you, implement a +method ``clearDefaultOptions()`` and call it periodically:: - When not using an :class:`Symfony\\Component\\OptionsResolver\\OptionsConfig` - object, perform normalization after the call to - :method:`Symfony\\Component\\OptionsResolver\\Options::resolve`:: + // ... + class Mailer + { + private static $resolversByClass = array(); - // ... - class Mailer + public static function clearDefaultOptions() { - // ... - public function __construct(array $options = array()) - { - $this->options = Options::resolve($options, array( - // ... - )); - - if ('http://' !== substr($this->options['host'], 0, 7)) { - $this->options['host'] = 'http://'.$this->options['host']; - } - } + self::$resolversByClass = array(); } + // ... + } + That's it! You now have all the tools and knowledge needed to easily process options in your code. .. _Packagist: https://packagist.org/packages/symfony/options-resolver +.. _Form component: http://symfony.com/doc/current/components/form/introduction.html From 1a410e496c004de866de35367d785c6a80258b85 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Thu, 23 Oct 2014 11:26:28 +0200 Subject: [PATCH 268/835] Fixed sentence --- components/console/changing_default_command.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst index 1c191ba8571..757a0c9f88c 100644 --- a/components/console/changing_default_command.rst +++ b/components/console/changing_default_command.rst @@ -8,9 +8,9 @@ Changing the Default Command The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` method was introduced in Symfony 2.5. -will always run the ``ListCommand`` when no command name is passed. In order to change -the default command you just need to pass the command name you want to run by -default to the ``setDefaultCommand`` method:: +The Console component will always run the ``ListCommand`` when no command name is +passed. In order to change the default command you just need to pass the command +name to the ``setDefaultCommand`` method:: namespace Acme\Console\Command; From d3ea36c40a89e3d67b53c9f8100fed7336a1917d Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 23 Oct 2014 16:19:34 +0200 Subject: [PATCH 269/835] changed submit button label we create a "task" not a "post"... --- book/forms.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index 80d19185e94..657a0dc9a0c 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -103,7 +103,7 @@ from inside a controller:: $form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') - ->add('save', 'submit', array('label' => 'Create Post')) + ->add('save', 'submit', array('label' => 'Create Task')) ->getForm(); return $this->render('AcmeTaskBundle:Default:new.html.twig', array( @@ -219,7 +219,7 @@ controller:: $form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') - ->add('save', 'submit', array('label' => 'Create Post')) + ->add('save', 'submit', array('label' => 'Create Task')) ->getForm(); $form->handleRequest($request); @@ -297,7 +297,7 @@ To do this, add a second button with the caption "Save and add" to your form:: $form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') - ->add('save', 'submit', array('label' => 'Create Post')) + ->add('save', 'submit', array('label' => 'Create Task')) ->add('saveAndAdd', 'submit', array('label' => 'Save and Add')) ->getForm(); From d653ff4b621d82bb0f1ac7fd5cb2a9b495fbe074 Mon Sep 17 00:00:00 2001 From: oopsFrogs Date: Fri, 24 Oct 2014 08:11:37 +0800 Subject: [PATCH 270/835] Update form_customization.rst In the Form chapter of Book for Symphony 2.3, we use '{{ form(form) }}' to render the entire form ( in contrast to '{{ form_widget(form) }}' for older version of Symfony ). so maybe it should use '{{ form(form) }}' here for consistency. --- cookbook/form/form_customization.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 41361bf6775..c3750163157 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -67,7 +67,7 @@ just one line: .. code-block:: jinja - {{ form_widget(form) }} + {{ form(form) }} .. code-block:: php From ef86b529236450c7cccbecafcd31927e95350ca9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 25 Oct 2014 09:28:39 +0200 Subject: [PATCH 271/835] Fixed typo --- book/doctrine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index f8d9867bbc6..a731c25fec7 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -544,7 +544,7 @@ Take a look at the previous example in more detail: In fact, since Doctrine is aware of all your managed entities, when you call the ``flush()`` method, it calculates an overall changeset and executes - the queries in the correct order. It utilizes cached prepare statement to + the queries in the correct order. It utilizes cached prepared statement to slightly improve the performance. For example, if you persist a total of 100 ``Product`` objects and then subsequently call ``flush()``, Doctrine will execute 100 ``INSERT`` queries using a single prepared statement object. From 4b9a885883a38a19fd224dbffa95a53d0e08e83f Mon Sep 17 00:00:00 2001 From: Guillaume Rossignol Date: Sat, 25 Oct 2014 10:53:47 +0200 Subject: [PATCH 272/835] [reference][configuration][security]Added key_length for pbkdf2 encoder --- reference/configuration/security.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 0a3aaebd6e6..5e0353fedbd 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -62,6 +62,7 @@ Each part will be explained in the next section. hash_algorithm: sha512 encode_as_base64: true iterations: 1000 + key_length: 40 # Example options/values for what a custom encoder might look like Acme\DemoBundle\Entity\User3: From ba323dee2299e0c7051415f2ede77aedb3b07839 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 25 Oct 2014 12:26:18 -0400 Subject: [PATCH 273/835] Added a code example for emailing on 4xx and 5xx errors without 404's --- cookbook/logging/monolog_email.rst | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/cookbook/logging/monolog_email.rst b/cookbook/logging/monolog_email.rst index e51b3afea49..a609b4b5200 100644 --- a/cookbook/logging/monolog_email.rst +++ b/cookbook/logging/monolog_email.rst @@ -19,7 +19,12 @@ it is broken down. handlers: mail: type: fingers_crossed + # 500 errors are logged at the critical level action_level: critical + # to also log 400 level errors (but not 404's): + # action_level: error + # excluded_404: + # - ^/ handler: buffered buffered: type: buffer @@ -48,6 +53,12 @@ it is broken down. type="fingers_crossed" action-level="critical" handler="buffered" + /> array( 'type' => 'fingers_crossed', 'action_level' => 'critical', + // to also log 400 level errors (but not 404's): + // 'action_level' => 'error', + // 'excluded_404s' => array( + // '^/', + // ), 'handler' => 'buffered', ), 'buffered' => array( @@ -108,7 +124,8 @@ setting means that the output is then passed onto the ``buffered`` handler. .. tip:: If you want both 400 level and 500 level errors to trigger an email, - set the ``action_level`` to ``error`` instead of ``critical``. + set the ``action_level`` to ``error`` instead of ``critical``. See the + code above for an example. The ``buffered`` handler simply keeps all the messages for a request and then passes them onto the nested handler in one go. If you do not use this From d190831c8918a17126588aa75960c8f6f2647b91 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 25 Oct 2014 12:50:50 -0400 Subject: [PATCH 274/835] Tweaks after proofreading the 2.6 OptionsResolver stuff --- components/options_resolver.rst | 36 ++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index ea1372f4ea6..d11c4965752 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -5,7 +5,9 @@ The OptionsResolver Component ============================= - The OptionsResolver component is `array_replace()` on steroids. + The OptionsResolver component is `array_replace()` on steroids. It + allows you to create an options system with required options, defaults, + validation (type, value), normalization and more. Installation ------------ @@ -21,7 +23,7 @@ Notes on Previous Versions .. versionadded:: 2.6 This documentation was written for Symfony 2.6 and later. If you use an older version, please read the corresponding documentation using the version - drop-down on the upper right. + drop-down on the upper right. For a list of changes, see the `CHANGELOG`_ Usage ----- @@ -65,7 +67,7 @@ check which options are set:: } This boilerplate is hard to read and repetitive. Also, the default values of the -options are buried in the business logic of your code. We can use +options are buried in the business logic of your code. We can use the :phpfunction:`array_replace` to fix that:: class Mailer @@ -91,10 +93,9 @@ the ``Mailer`` class does a mistake? 'usernme' => 'johndoe', )); -No error will be shown. In the best case, the bug will be appear during testing. -The developer will possibly spend a lot of time looking for the problem. In the -worst case, however, the bug won't even appear and will be deployed to the live -system. +No error will be shown. In the best case, the bug will appear during testing, +but the developer will spend time looking for the problem. In the worst case, +the bug might not appear until it's deployed to the live system. Let's use the :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` class to fix this problem:: @@ -268,8 +269,8 @@ retrieve the names of all required options:: If you want to check whether a required option is still missing from the default options, you can use :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isMissing`. -The difference to :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isRequired` -is that this method will return false for required options that have already +The difference between this and :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isRequired` +is that this method will return false if a required option has already been set:: // ... @@ -360,8 +361,8 @@ Value Validation Some options can only take one of a fixed list of predefined values. For example, suppose the ``Mailer`` class has a ``transport`` option which can be one of ``sendmail``, ``mail`` and ``smtp``. Use the method -:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setAllowedValues` to verify -that the passed option contains one of these values:: +:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setAllowedValues` +to verify that the passed option contains one of these values:: // ... class Mailer @@ -470,9 +471,9 @@ Suppose you want to set the default value of the ``port`` option based on the encryption chosen by the user of the ``Mailer`` class. More precisely, we want to set the port to ``465`` if SSL is used and to ``25`` otherwise. -You can implement this feature by passing a closure as default value of the -``port`` option. The closure receives the options as argument. Based on these -options, you can return the desired default value:: +You can implement this feature by passing a closure as the default value of +the ``port`` option. The closure receives the options as argument. Based on +these options, you can return the desired default value:: use Symfony\Component\OptionsResolver\Options; @@ -546,8 +547,10 @@ Options without Default Values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In some cases, it is useful to define an option without setting a default value. -Mostly, you will need this when you want to know whether an option was passed -or not. If you set a default value for that option, this is not possible:: +This is useful if you need to know whether or not the user *actually* set +an option or not. For example, if you set the default value for an option, +it's not possible to know whether the user passed this value or if it simply +comes from the default:: // ... class Mailer @@ -713,3 +716,4 @@ options in your code. .. _Packagist: https://packagist.org/packages/symfony/options-resolver .. _Form component: http://symfony.com/doc/current/components/form/introduction.html +.. _CHANGELOG: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/OptionsResolver/CHANGELOG.md From 59fd436a5705702d71e3bfbf2f9b3aad0abca0bb Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 25 Oct 2014 19:54:56 +0200 Subject: [PATCH 275/835] Also did a quick proofread --- components/options_resolver.rst | 63 +++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index d11c4965752..dd6229e0a78 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -5,8 +5,8 @@ The OptionsResolver Component ============================= - The OptionsResolver component is `array_replace()` on steroids. It - allows you to create an options system with required options, defaults, + The OptionsResolver component is :phpfunction:`array_replace` on steroids. + It allows you to create an options system with required options, defaults, validation (type, value), normalization and more. Installation @@ -23,7 +23,7 @@ Notes on Previous Versions .. versionadded:: 2.6 This documentation was written for Symfony 2.6 and later. If you use an older version, please read the corresponding documentation using the version - drop-down on the upper right. For a list of changes, see the `CHANGELOG`_ + drop-down on the upper right. For a list of changes, see the `CHANGELOG`_. Usage ----- @@ -50,29 +50,35 @@ check which options are set:: public function sendMail($from, $to) { $mail = ...; + $mail->setHost(isset($this->options['host']) ? $this->options['host'] : 'smtp.example.org'); + $mail->setUsername(isset($this->options['username']) ? $this->options['username'] : 'user'); + $mail->setPassword(isset($this->options['password']) ? $this->options['password'] : 'pa$$word'); + $mail->setPort(isset($this->options['port']) ? $this->options['port'] : 25); + // ... } } This boilerplate is hard to read and repetitive. Also, the default values of the -options are buried in the business logic of your code. We can use the +options are buried in the business logic of your code. You could use the :phpfunction:`array_replace` to fix that:: class Mailer { // ... + public function __construct(array $options = array()) { $this->options = array_replace(array( @@ -85,7 +91,7 @@ options are buried in the business logic of your code. We can use the } Now all four options are guaranteed to be set. But what happens if the user of -the ``Mailer`` class does a mistake? +the ``Mailer`` class makes a mistake? .. code-block:: php @@ -97,14 +103,15 @@ No error will be shown. In the best case, the bug will appear during testing, but the developer will spend time looking for the problem. In the worst case, the bug might not appear until it's deployed to the live system. -Let's use the :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` -class to fix this problem:: +Fortunately, the :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` +class helps you to fix this problem:: use Symfony\Component\OptionsResolver\Options; class Mailer { // ... + public function __construct(array $options = array()) { $resolver = new OptionsResolver(); @@ -137,6 +144,7 @@ code:: class Mailer { // ... + public function sendMail($from, $to) { $mail = ...; @@ -154,6 +162,7 @@ It's a good practice to split the option configuration into a separate method:: class Mailer { // ... + public function __construct(array $options = array()) { $resolver = new OptionsResolver(); @@ -165,10 +174,10 @@ It's a good practice to split the option configuration into a separate method:: protected function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults(array( - 'host' => 'smtp.example.org', - 'username' => 'user', - 'password' => 'pa$$word', - 'port' => 25, + 'host' => 'smtp.example.org', + 'username' => 'user', + 'password' => 'pa$$word', + 'port' => 25, 'encryption' => null, )); } @@ -197,12 +206,13 @@ Required Options If an option must be set by the caller, pass that option to :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setRequired`. -For example, let's make the ``host`` option required:: +For example, to make the ``host`` option required, you can do:: // ... class Mailer { // ... + protected function configureOptions(OptionsResolver $resolver) { // ... @@ -211,8 +221,8 @@ For example, let's make the ``host`` option required:: } .. versionadded:: 2.6 - Before Symfony 2.6, `setRequired()` accepted only arrays. Since then, single - option names can be passed as well. + As of Symfony 2.6, ``setRequired()`` accepts both an array of options or a + single option. Prior to 2.6, you could only pass arrays. If you omit a required option, a :class:`Symfony\\Component\\OptionsResolver\\Exception\\MissingOptionsException` @@ -230,6 +240,7 @@ one required option:: class Mailer { // ... + protected function configureOptions(OptionsResolver $resolver) { // ... @@ -277,6 +288,7 @@ been set:: class Mailer { // ... + protected function configureOptions(OptionsResolver $resolver) { // ... @@ -321,6 +333,7 @@ correctly. To validate the types of the options, call class Mailer { // ... + protected function configureOptions(OptionsResolver $resolver) { // ... @@ -330,8 +343,8 @@ correctly. To validate the types of the options, call } For each option, you can define either just one type or an array of acceptable -types. You can pass any type for which an ``is_()`` method is defined. -Additionally, you may pass fully qualified class or interface names. +types. You can pass any type for which an ``is_()`` function is defined +in PHP. Additionally, you may pass fully qualified class or interface names. If you pass an invalid option now, an :class:`Symfony\\Component\\OptionsResolver\\Exception\\InvalidOptionsException` @@ -349,9 +362,7 @@ to add additional allowed types without erasing the ones already set. .. versionadded:: 2.6 Before Symfony 2.6, `setAllowedTypes()` and `addAllowedTypes()` expected - the values to be given as an array mapping option names to allowed types: - - .. code-block:: php + the values to be given as an array mapping option names to allowed types:: $resolver->setAllowedTypes(array('port' => array('null', 'int'))); @@ -368,6 +379,7 @@ to verify that the passed option contains one of these values:: class Mailer { // ... + protected function configureOptions(OptionsResolver $resolver) { // ... @@ -421,9 +433,11 @@ option. You can configure a normalizer by calling class Mailer { // ... + protected function configureOptions(OptionsResolver $resolver) { // ... + $resolver->setNormalizer('host', function ($options, $value) { if ('http://' !== substr($value, 0, 7)) { $value = 'http://'.$value; @@ -468,7 +482,7 @@ Default Values that Depend on another Option ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Suppose you want to set the default value of the ``port`` option based on the -encryption chosen by the user of the ``Mailer`` class. More precisely, we want +encryption chosen by the user of the ``Mailer`` class. More precisely, you want to set the port to ``465`` if SSL is used and to ``25`` otherwise. You can implement this feature by passing a closure as the default value of @@ -499,7 +513,7 @@ these options, you can return the desired default value:: .. caution:: The argument of the callable must be type hinted as ``Options``. Otherwise, - the callable is considered as the default value of the option. + the callable itself is considered as the default value of the option. .. note:: @@ -587,6 +601,7 @@ be included in the resolved options if it was actually passed to class Mailer { // ... + protected function configureOptions(OptionsResolver $resolver) { // ... @@ -640,6 +655,8 @@ let you find out which options are defined:: // ... class GoogleMailer extends Mailer { + // ... + protected function configureOptions(OptionsResolver $resolver) { parent::configureOptions($resolver); @@ -674,10 +691,10 @@ can change your code to do the configuration only once per class:: public function __construct(array $options = array()) { - // Are we a Mailer, a GoogleMailer, ... ? + // What type of Mailer is this, a Mailer, a GoogleMailer, ... ? $class = get_class($this); - // Did we call configureOptions() before for this class? + // Was configureOptions() executed before for this class? if (!isset(self::$resolversByClass[$class])) { self::$resolversByClass[$class] = new OptionsResolver(); $this->configureOptions(self::$resolversByClass[$class]); From 8931249df73463a57529c1ad0cacb56b1b53c682 Mon Sep 17 00:00:00 2001 From: flip111 Date: Sun, 26 Oct 2014 01:35:15 +0200 Subject: [PATCH 276/835] Cleaned up javascript code --- reference/forms/types/collection.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index 0c86877850d..c1f270c9e82 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -179,7 +179,9 @@ you need is the JavaScript: var emailCount = '{{ form.emails|length }}'; jQuery(document).ready(function() { - jQuery('#add-another-email').click(function() { + jQuery('#add-another-email').click(function(e) { + e.preventDefault(); + var emailList = jQuery('#email-fields-list'); // grab the prototype template @@ -192,9 +194,7 @@ you need is the JavaScript: // create a new list element and add it to the list var newLi = jQuery('
  • ').html(newWidget); - newLi.appendTo(jQuery('#email-fields-list')); - - return false; + newLi.appendTo(emailList); }); }) From 0db5e03b288d1154957768721347e7938101dc6a Mon Sep 17 00:00:00 2001 From: Eugene Leonovich Date: Mon, 27 Oct 2014 12:16:41 +0100 Subject: [PATCH 277/835] [OptionsResolver] Fix wrong namespace in example --- components/options_resolver.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index ea1372f4ea6..e0d354ba4fd 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -99,7 +99,7 @@ system. Let's use the :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` class to fix this problem:: - use Symfony\Component\OptionsResolver\Options; + use Symfony\Component\OptionsResolver\OptionsResolver; class Mailer { From a50bb96f7b6928de03110532fe9789d8c5658dd7 Mon Sep 17 00:00:00 2001 From: inso Date: Mon, 27 Oct 2014 20:08:43 +0200 Subject: [PATCH 278/835] Updated Valid constraint reference Revised PR #4351 for 2.3 branch --- reference/constraints/Valid.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/reference/constraints/Valid.rst b/reference/constraints/Valid.rst index 407c5339cbc..629f5a2d6cb 100644 --- a/reference/constraints/Valid.rst +++ b/reference/constraints/Valid.rst @@ -9,9 +9,9 @@ object and all sub-objects associated with it. | Applies to | :ref:`property or method ` | +----------------+---------------------------------------------------------------------+ | Options | - `traverse`_ | -| | - `message`_ | +| | - `deep`_ | +----------------+---------------------------------------------------------------------+ -| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Type` | +| Class | :class:`Symfony\\Component\\Validator\\Constraints\\Valid` | +----------------+---------------------------------------------------------------------+ .. include:: /reference/forms/types/options/_error_bubbling_hint.rst.inc @@ -267,9 +267,11 @@ If this constraint is applied to a property that holds an array of objects, then each object in that array will be validated only if this option is set to ``true``. -message -~~~~~~~ +deep +~~~~ -**type**: ``string`` **default**: ``This value should be true.`` +**type**: ``boolean`` **default**: ``false`` -This is the message that will be shown if the value is false. +If this constraint is applied to a property that holds an array of objects, +then each object in that array will be validated recursively if this option is set +to ``true``. From 5deccd2682f71562a2ff16fa59de149a20e3af1e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 23 Aug 2014 10:28:59 +0200 Subject: [PATCH 279/835] describe how to access form errors --- components/form/introduction.rst | 27 +++++++++++++++++++++++++++ reference/forms/types/form.rst | 2 ++ 2 files changed, 29 insertions(+) diff --git a/components/form/introduction.rst b/components/form/introduction.rst index 74b47461b65..7b0dcd371d1 100644 --- a/components/form/introduction.rst +++ b/components/form/introduction.rst @@ -660,6 +660,33 @@ and the errors will display next to the fields on error. For a list of all of the built-in validation constraints, see :doc:`/reference/constraints`. +Accessing Form Errors +~~~~~~~~~~~~~~~~~~~~~ + +You can use the :method:`Symfony\\Component\\Form\\FormInterface::getErrors` +method to access the list of errors. Each element is a :class:`Symfony\\Component\\Form\\FormError` +object:: + + $form = ...; + + // ... + + // an array of FormError objects, but only errors attached to this form level (e.g. "global errors) + $errors = $form->getErrors(); + + // an array of FormError objects, but only errors attached to the "firstName" field + $errors = $form['firstName']->getErrors(); + + // a string representation of all errors of the whole form tree + $errors = $form->getErrorsAsString(); + +.. note:: + + If you enable the :ref:`error_bubbling ` + option on a field, calling ``getErrors()`` on the parent form will include + errors from that field. However, there is no way to determine which field + an error was originally attached to. + .. _Packagist: https://packagist.org/packages/symfony/form .. _Twig: http://twig.sensiolabs.org .. _`Twig Configuration`: http://twig.sensiolabs.org/doc/intro.html diff --git a/reference/forms/types/form.rst b/reference/forms/types/form.rst index 7bf4d09df76..8ea4a6ce437 100644 --- a/reference/forms/types/form.rst +++ b/reference/forms/types/form.rst @@ -78,6 +78,8 @@ The actual default value of this option depends on other field options: .. include:: /reference/forms/types/options/empty_data.rst.inc :start-after: DEFAULT_PLACEHOLDER +.. _reference-form-option-error-bubbling: + .. include:: /reference/forms/types/options/error_bubbling.rst.inc .. include:: /reference/forms/types/options/error_mapping.rst.inc From 641dbc04e7e810836e5b39a707f4966fedcdaee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jure=20=C5=BDitnik?= Date: Tue, 28 Oct 2014 10:08:58 +0100 Subject: [PATCH 280/835] fix typo --- best_practices/configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst index 0c463239784..3368c59589e 100644 --- a/best_practices/configuration.rst +++ b/best_practices/configuration.rst @@ -119,7 +119,7 @@ You could, for example, define a ``NUM_ITEMS`` constant in the ``Post`` entity: The main advantage of defining constants is that you can use their values everywhere in your application. When using parameters, they are only available -from places wih access to the Symfony container. +from places with access to the Symfony container. Constants can be used for example in your Twig templates thanks to the ``constant()`` function: From f179ec7f6df63e8f76962d8165f14d4d66b42ef6 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 28 Oct 2014 07:17:26 -0700 Subject: [PATCH 281/835] A few more tweaks from comments --- components/options_resolver.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index dd6229e0a78..3cb4c42cd1b 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -22,8 +22,8 @@ Notes on Previous Versions .. versionadded:: 2.6 This documentation was written for Symfony 2.6 and later. If you use an older - version, please read the corresponding documentation using the version - drop-down on the upper right. For a list of changes, see the `CHANGELOG`_. + version, please `read the Symfony 2.5 documentation`_. For a list of changes, + see the `CHANGELOG`_. Usage ----- @@ -72,7 +72,7 @@ check which options are set:: } This boilerplate is hard to read and repetitive. Also, the default values of the -options are buried in the business logic of your code. You could use the +options are buried in the business logic of your code. Use the :phpfunction:`array_replace` to fix that:: class Mailer @@ -734,3 +734,4 @@ options in your code. .. _Packagist: https://packagist.org/packages/symfony/options-resolver .. _Form component: http://symfony.com/doc/current/components/form/introduction.html .. _CHANGELOG: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/OptionsResolver/CHANGELOG.md +.. _`read the Symfony 2.5 documentation`: http://symfony.com/doc/2.5/components/options_resolver.html From a1badc5716553ec3e6cb110aa999c67f74d37f20 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 28 Oct 2014 07:17:57 -0700 Subject: [PATCH 282/835] Renaming a method at the suggestion of my friend Bernhard --- components/options_resolver.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 3cb4c42cd1b..4af7e757e68 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -713,14 +713,14 @@ Now the :class:`Symfony\\Component\\OptionsResolver\\OptionsResolver` instance will be created once per class and reused from that on. Be aware that this may lead to memory leaks in long-running applications, if the default options contain references to objects or object graphs. If that's the case for you, implement a -method ``clearDefaultOptions()`` and call it periodically:: +method ``clearOptionsConfig()`` and call it periodically:: // ... class Mailer { private static $resolversByClass = array(); - public static function clearDefaultOptions() + public static function clearOptionsConfig() { self::$resolversByClass = array(); } From 28eefe22b4aa2be70e3d0863a4e2e54391fef2cb Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 28 Oct 2014 07:23:22 -0700 Subject: [PATCH 283/835] [#4299] Very minor language tweak --- components/serializer.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/serializer.rst b/components/serializer.rst index 4508482f88f..0d26457d0aa 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -301,7 +301,7 @@ Circular references are common when dealing with entity relations:: To avoid infinite loops, :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` throws a :class:`Symfony\\Component\\Serializer\\Exception\\CircularReferenceException` -when such case is encountered:: +when such a case is encountered:: $member = new Member(); $member->setName('Kévin'); @@ -315,8 +315,8 @@ when such case is encountered:: echo $serializer->serialize($org, 'json'); // Throws a CircularReferenceException The ``setCircularReferenceLimit()`` method of this normalizer sets the number -of times serializing the same object must occur before considering being -in a circular reference. Its default value is ``1``. +of times it will serialize the same object before considering it a circular +reference. Its default value is ``1``. Instead of throwing an exception, circular references can also be handled by custom callables. This is especially useful when serializing entities From b49731bc179d3fa16f48c0525b232444e9c30dd8 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 28 Oct 2014 07:24:54 -0700 Subject: [PATCH 284/835] Linking to the 2.6 headline thanks to a suggestion from @xabbuh --- components/options_resolver.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 4af7e757e68..7e85bf3c3cf 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -733,5 +733,5 @@ options in your code. .. _Packagist: https://packagist.org/packages/symfony/options-resolver .. _Form component: http://symfony.com/doc/current/components/form/introduction.html -.. _CHANGELOG: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/OptionsResolver/CHANGELOG.md +.. _CHANGELOG: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/OptionsResolver/CHANGELOG.md#260 .. _`read the Symfony 2.5 documentation`: http://symfony.com/doc/2.5/components/options_resolver.html From 5f8b13c461a10eff10fb5744e7e9e7d5153ba9fa Mon Sep 17 00:00:00 2001 From: luciantugui Date: Tue, 28 Oct 2014 21:32:11 +0100 Subject: [PATCH 285/835] Update custom_constraint.rst --- cookbook/validation/custom_constraint.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/cookbook/validation/custom_constraint.rst b/cookbook/validation/custom_constraint.rst index caf582a4eee..f95d311ed20 100644 --- a/cookbook/validation/custom_constraint.rst +++ b/cookbook/validation/custom_constraint.rst @@ -69,7 +69,6 @@ The validator class is also simple, and only has one required method ``validate( $this->context->buildViolation($constraint->message) ->setParameter('%string%', $value) ->addViolation(); - ); // If you're using the old 2.4 validation API /* From d07d5fecbfda5cf7904c6e7e57faa2872967adfd Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 28 Oct 2014 18:44:45 -0700 Subject: [PATCH 286/835] Revert "Added config example for Varnish 4.0" This is breaking the docs because the sphinx-php extension isn't updated yet --- cookbook/cache/varnish.rst | 72 +++++++++++--------------------------- 1 file changed, 20 insertions(+), 52 deletions(-) diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index 5bc9c841e86..cb6ba5b8174 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -54,62 +54,30 @@ Then, optimize Varnish so that it only parses the Response contents when there is at least one ESI tag by checking the ``Surrogate-Control`` header that Symfony adds automatically: -.. configuration-block:: +.. code-block:: text - .. code-block:: varnish4 + sub vcl_fetch { + /* + Check for ESI acknowledgement + and remove Surrogate-Control header + */ + if (beresp.http.Surrogate-Control ~ "ESI/1.0") { + unset beresp.http.Surrogate-Control; - /* (https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#req-not-available-in-vcl-backend-response) */ - sub vcl_backend_response { - // Check for ESI acknowledgement and remove Surrogate-Control header - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; - set beresp.do_esi = true; - } - /* By default Varnish ignores Pragma: nocache - (https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cache-control) - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache") { - // https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#hit-for-pass-objects-are-created-using-beresp-uncacheable - set beresp.uncacheable = true; - set beresp.ttl = 120s; - return (deliver); - } + // For Varnish >= 3.0 + set beresp.do_esi = true; + // For Varnish < 3.0 + // esi; } - - .. code-block:: varnish3 - - sub vcl_fetch { - // Check for ESI acknowledgement and remove Surrogate-Control header - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; - set beresp.do_esi = true; - } - /* By default Varnish ignores Cache-Control: nocache - (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache" || - beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private") { - return (hit_for_pass); - } - } - - .. code-block:: varnish2 - - sub vcl_fetch { - // Check for ESI acknowledgement and remove Surrogate-Control header - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; - esi; - } - /* By default Varnish ignores Cache-Control: nocache - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache" || - beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private") { - return (hit_for_pass); - } + /* By default Varnish ignores Cache-Control: nocache + (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), + so in order avoid caching it has to be done explicitly */ + if (beresp.http.Pragma ~ "no-cache" || + beresp.http.Cache-Control ~ "no-cache" || + beresp.http.Cache-Control ~ "private") { + return (hit_for_pass); } + } .. caution:: From ee2d7e473df3fea1929ca44bff1559cfb4b03276 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 29 Oct 2014 10:10:58 +0100 Subject: [PATCH 287/835] section for DebugBundle/Twig integration --- components/var_dumper/advanced.rst | 12 +++++++ components/var_dumper/introduction.rst | 48 +++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index bf15c1bfe79..c0b3dd9c953 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -77,6 +77,18 @@ For example, to get a dump as a string in a variable, you can do:: // $output is now populated with the dump representation of $variable +An other option for doing the same could be:: + + $output = fopen('php://memory', 'r+b'); + cloner = new VarCloner(); + $dumper = new CliDumper($output); + + $dumper->dump($cloner->cloneVar($variable)); + fseek($output, 0); + $output = stream_get_contents($output); + + // $output is now populated with the dump representation of $variable + Dumpers implement the :class:`Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface` interface that specifies the :method:`dump(Data $data) ` diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst index 1b4669cc83c..bd48be2809d 100644 --- a/components/var_dumper/introduction.rst +++ b/components/var_dumper/introduction.rst @@ -45,6 +45,52 @@ The advantages of this function are: so can you also use it directly. You can change the behavior of this function by calling :method:`VarDumper::setHandler($callable) `: -calls to ``dump()`` will then be forwarded to ``$callable``, given as first argument. +calls to ``dump()`` will then be forwarded to ``$callable``. + +Where does the output go? +------------------------- + +If you read the advanced documentation, you'll learn how to change the +format or redirect the output to wherever you want. + +By default, these are selected based on your current PHP SAPI: + +- on the command line (CLI SAPI), the output is written on `STDERR`. This + can be surprising to some because this bypasses PHP's output buffering + mechanism. On the other hand, this give the possibility to easily split + dumps from regular output by using pipe redirection. +- on other SAPIs, dumps are written as HTML on the regular output. + +DebugBundle and Twig integration +-------------------------------- + +The `DebugBundle` allows greater integration of the component into the +Symfony full stack framework. It is enabled by default in the dev +environement of the standard edition since version 2.6. + +Since generating (even debug) output in the controller or in the model +of your application may just break it by e.g. sending HTTP headers or +corrupting your view, the bundle configures the `dump()` function so that +variables are dumped in the web debug toolbar. + +But if the toolbar can not be displayed because you e.g. called `die`/`exit` +or a fatal error occurred, then dumps are written on the regular output. + +In a Twig template, two constructs are available for dumping a variable. +Choosing between both is generally only a matter of personal taste: + +- `{% dump foo.bar %}` is the way to go when the original template output + shall not be modified: variables are not dumped inline, but in the web + debug toolbar. +- on the contrary, `{{ dump(foo.bar) }}` dumps inline and thus may or not + be suited to your use case (e.g. you shouldn't use it in an HTML + attribute or a `script` tag). + +Reading a dump +-------------- + +For simple variables, reading the output should be straightforward:: + + dump(array(true, 1.1, "string")); .. _Packagist: https://packagist.org/packages/symfony/var-dumper From 6cdecde03f30dfe96a1d94cf8918dfbca831adc0 Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Mon, 20 Oct 2014 10:20:18 +0200 Subject: [PATCH 288/835] [Templating] Added a sentence that explains what a Template Helper is --- components/templating/introduction.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/templating/introduction.rst b/components/templating/introduction.rst index 0f4ec567a5b..946da963104 100644 --- a/components/templating/introduction.rst +++ b/components/templating/introduction.rst @@ -135,7 +135,8 @@ escaper using the Helpers ------- -The Templating component can be easily extended via helpers. The component has +The Templating component can be easily extended via helpers. Helpers are PHP objects that +provide features useful in a template context. The component has 2 built-in helpers: * :doc:`/components/templating/helpers/assetshelper` From ef3a58e299bca105ff036a30918d0d797f6b7df8 Mon Sep 17 00:00:00 2001 From: Geert De Deckere Date: Tue, 28 Oct 2014 16:16:47 +0100 Subject: [PATCH 289/835] Minor spelling fix --- book/forms.rst | 2 +- book/http_fundamentals.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index 657a0dc9a0c..c12eb6851f5 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -6,7 +6,7 @@ Forms Dealing with HTML forms is one of the most common - and challenging - tasks for a web developer. Symfony integrates a Form component that makes dealing with -forms easy. In this chapter, you'll build a complex form from the ground-up, +forms easy. In this chapter, you'll build a complex form from the ground up, learning the most important features of the form library along the way. .. note:: diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index 85b3f165d50..cd8d04d0de0 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -37,7 +37,7 @@ HTTP is the term used to describe this simple text-based language. And no matter how you develop on the web, the goal of your server is *always* to understand simple text requests, and return simple text responses. -Symfony is built from the ground-up around that reality. Whether you realize +Symfony is built from the ground up around that reality. Whether you realize it or not, HTTP is something you use everyday. With Symfony, you'll learn how to master it. From 4e6256b565b2e4de8f30f334ff22e60bd1e100ee Mon Sep 17 00:00:00 2001 From: Warnar Boekkooi Date: Wed, 29 Oct 2014 16:50:48 +0800 Subject: [PATCH 290/835] Removed unused use UsernameNotFoundException --- cookbook/security/api_key_authentication.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/cookbook/security/api_key_authentication.rst b/cookbook/security/api_key_authentication.rst index 0feefca9a4b..8fb6dfbdb38 100644 --- a/cookbook/security/api_key_authentication.rst +++ b/cookbook/security/api_key_authentication.rst @@ -31,7 +31,6 @@ value and then a User object is created:: use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\User\UserProviderInterface; - use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; class ApiKeyAuthenticator implements SimplePreAuthenticatorInterface From 23afdb3b5c2743883767c365757346320a757589 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 29 Oct 2014 09:17:16 -0700 Subject: [PATCH 291/835] Revert "Revert "Added config example for Varnish 4.0"" This re-adds PR #4280, which was temporarily reverted while we waited for the proper PHP extensions. --- cookbook/cache/varnish.rst | 72 +++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index cb6ba5b8174..5bc9c841e86 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -54,30 +54,62 @@ Then, optimize Varnish so that it only parses the Response contents when there is at least one ESI tag by checking the ``Surrogate-Control`` header that Symfony adds automatically: -.. code-block:: text +.. configuration-block:: - sub vcl_fetch { - /* - Check for ESI acknowledgement - and remove Surrogate-Control header - */ - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; + .. code-block:: varnish4 - // For Varnish >= 3.0 - set beresp.do_esi = true; - // For Varnish < 3.0 - // esi; + /* (https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#req-not-available-in-vcl-backend-response) */ + sub vcl_backend_response { + // Check for ESI acknowledgement and remove Surrogate-Control header + if (beresp.http.Surrogate-Control ~ "ESI/1.0") { + unset beresp.http.Surrogate-Control; + set beresp.do_esi = true; + } + /* By default Varnish ignores Pragma: nocache + (https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cache-control) + so in order avoid caching it has to be done explicitly */ + if (beresp.http.Pragma ~ "no-cache") { + // https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#hit-for-pass-objects-are-created-using-beresp-uncacheable + set beresp.uncacheable = true; + set beresp.ttl = 120s; + return (deliver); + } } - /* By default Varnish ignores Cache-Control: nocache - (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache" || - beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private") { - return (hit_for_pass); + + .. code-block:: varnish3 + + sub vcl_fetch { + // Check for ESI acknowledgement and remove Surrogate-Control header + if (beresp.http.Surrogate-Control ~ "ESI/1.0") { + unset beresp.http.Surrogate-Control; + set beresp.do_esi = true; + } + /* By default Varnish ignores Cache-Control: nocache + (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), + so in order avoid caching it has to be done explicitly */ + if (beresp.http.Pragma ~ "no-cache" || + beresp.http.Cache-Control ~ "no-cache" || + beresp.http.Cache-Control ~ "private") { + return (hit_for_pass); + } + } + + .. code-block:: varnish2 + + sub vcl_fetch { + // Check for ESI acknowledgement and remove Surrogate-Control header + if (beresp.http.Surrogate-Control ~ "ESI/1.0") { + unset beresp.http.Surrogate-Control; + esi; + } + /* By default Varnish ignores Cache-Control: nocache + so in order avoid caching it has to be done explicitly */ + if (beresp.http.Pragma ~ "no-cache" || + beresp.http.Cache-Control ~ "no-cache" || + beresp.http.Cache-Control ~ "private") { + return (hit_for_pass); + } } - } .. caution:: From b69973188b0965ac8909247bf809072baa013808 Mon Sep 17 00:00:00 2001 From: Peter Rehm Date: Mon, 20 Oct 2014 16:49:44 +0200 Subject: [PATCH 292/835] Updated information about handling validation of embedded forms to Valid constraint --- book/forms.rst | 20 +++++++++---------- .../types/options/cascade_validation.rst.inc | 9 +++++++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index c12eb6851f5..27135c4f817 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -1234,6 +1234,8 @@ objects. For example, a registration form may contain data belonging to a ``User`` object as well as many ``Address`` objects. Fortunately, this is easy and natural with the Form component. +.. _forms-embedding-single-object: + Embedding a Single Object ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1263,6 +1265,7 @@ Next, add a new ``category`` property to the ``Task`` class:: /** * @Assert\Type(type="Acme\TaskBundle\Entity\Category") + * @Assert\Valid() */ protected $category; @@ -1279,6 +1282,12 @@ Next, add a new ``category`` property to the ``Task`` class:: } } +.. tip:: + + The ``Valid`` Constraint has been added to the property ``category``. This + cascades the validation to the corresponding entity. If you omit this constraint + the child entity would not be validated. + Now that your application has been updated to reflect the new requirements, create a form class so that a ``Category`` object can be modified by the user:: @@ -1326,16 +1335,7 @@ class: } The fields from ``CategoryType`` can now be rendered alongside those from -the ``TaskType`` class. To activate validation on CategoryType, add -the ``cascade_validation`` option to ``TaskType``:: - - public function setDefaultOptions(OptionsResolverInterface $resolver) - { - $resolver->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Task', - 'cascade_validation' => true, - )); - } +the ``TaskType`` class. Render the ``Category`` fields in the same way as the original ``Task`` fields: diff --git a/reference/forms/types/options/cascade_validation.rst.inc b/reference/forms/types/options/cascade_validation.rst.inc index f110e9087b2..472a31d8251 100644 --- a/reference/forms/types/options/cascade_validation.rst.inc +++ b/reference/forms/types/options/cascade_validation.rst.inc @@ -8,8 +8,13 @@ For example, if you have a ``ProductType`` with an embedded ``CategoryType``, setting ``cascade_validation`` to ``true`` on ``ProductType`` will cause the data from ``CategoryType`` to also be validated. -Instead of using this option, you can also use the ``Valid`` constraint in -your model to force validation on a child object stored on a property. +.. tip:: + + Instead of using this option, it is recommended that you use the ``Valid`` + constraint in your model to force validation on a child object stored on + a property. This cascades only the validation but not the use of the + ``validation_group`` option on child forms. You can read more about this + in the section about :ref:`Embedding a Single Object `. .. include:: /reference/forms/types/options/_error_bubbling_hint.rst.inc From cb0ee899f51df62e72063e54f833ea33dcd5637b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 29 Oct 2014 20:30:04 +0100 Subject: [PATCH 293/835] =?UTF-8?q?=C2=A7=20about=20Data::getLimitedClone(?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/var_dumper/advanced.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index c0b3dd9c953..70bf85e5198 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -27,6 +27,23 @@ Before cloning, you can configure the limits with:: They will be applied when calling ``->cloneVar()`` afterwards. +Before dumping it, you can further limit the resulting +:class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object by calling its +:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::getLimitedClone` +method: +- the first `$maxDepth` argument allows limiting dumps in the depth dimension, +- the second `$maxItemsPerDepth` limits the number of items per depth level, +- and the last `$useRefHandles` defaults to `true` but allows removing internal + objects' handles for sparser output, +- but unlike the previous limits on cloners that remove data on purpose, these + limits can be changed back and forth before dumping since they do not affect + the intermediate representation internally. + +.. note:: + When no limit is applied, a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` + object is as accurate as the native :phpfunction:`serialize` function and thus + could have a wider purpose than strictly dumping for debugging. + Dumpers ~~~~~~~ From 5ca2df8c9d7e0448968f13e74472813e798fb99e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 29 Oct 2014 23:02:14 +0100 Subject: [PATCH 294/835] update Sphinx extension submodule reference --- _exts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_exts b/_exts index e58edd22d16..52f7bd2216c 160000 --- a/_exts +++ b/_exts @@ -1 +1 @@ -Subproject commit e58edd22d16cb247267025d557410dcbfa5fa959 +Subproject commit 52f7bd2216cc22ef52494f346c5643bb2a74513f From b3d96b8715beca866fdc71ed2b424c461b7bf2ca Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 29 Oct 2014 19:58:52 -0700 Subject: [PATCH 295/835] "Revert "Added config example for Varnish 4.0""" This reverts commit 23afdb3b5c2743883767c365757346320a757589. We thought we had the issue fixed, but apparently not quite yet - see #4280 --- cookbook/cache/varnish.rst | 72 +++++++++++--------------------------- 1 file changed, 20 insertions(+), 52 deletions(-) diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index 5bc9c841e86..cb6ba5b8174 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -54,62 +54,30 @@ Then, optimize Varnish so that it only parses the Response contents when there is at least one ESI tag by checking the ``Surrogate-Control`` header that Symfony adds automatically: -.. configuration-block:: +.. code-block:: text - .. code-block:: varnish4 + sub vcl_fetch { + /* + Check for ESI acknowledgement + and remove Surrogate-Control header + */ + if (beresp.http.Surrogate-Control ~ "ESI/1.0") { + unset beresp.http.Surrogate-Control; - /* (https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#req-not-available-in-vcl-backend-response) */ - sub vcl_backend_response { - // Check for ESI acknowledgement and remove Surrogate-Control header - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; - set beresp.do_esi = true; - } - /* By default Varnish ignores Pragma: nocache - (https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cache-control) - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache") { - // https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#hit-for-pass-objects-are-created-using-beresp-uncacheable - set beresp.uncacheable = true; - set beresp.ttl = 120s; - return (deliver); - } + // For Varnish >= 3.0 + set beresp.do_esi = true; + // For Varnish < 3.0 + // esi; } - - .. code-block:: varnish3 - - sub vcl_fetch { - // Check for ESI acknowledgement and remove Surrogate-Control header - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; - set beresp.do_esi = true; - } - /* By default Varnish ignores Cache-Control: nocache - (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache" || - beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private") { - return (hit_for_pass); - } - } - - .. code-block:: varnish2 - - sub vcl_fetch { - // Check for ESI acknowledgement and remove Surrogate-Control header - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; - esi; - } - /* By default Varnish ignores Cache-Control: nocache - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache" || - beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private") { - return (hit_for_pass); - } + /* By default Varnish ignores Cache-Control: nocache + (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), + so in order avoid caching it has to be done explicitly */ + if (beresp.http.Pragma ~ "no-cache" || + beresp.http.Cache-Control ~ "no-cache" || + beresp.http.Cache-Control ~ "private") { + return (hit_for_pass); } + } .. caution:: From 5ea29021c9fa1298d93c0c2dcc4ca71548caa994 Mon Sep 17 00:00:00 2001 From: micheal Date: Sat, 25 Oct 2014 11:59:13 -0700 Subject: [PATCH 296/835] Removed the redundant usage of layer. Since DBAL is database abstraction layer, and is defined in the document as such, to me it reads funny to have it as database abstraction layer layer. It sounds nitpicky, I know, but I find it an easier read this way. Also, the possessive DBAL stuck out ("DBAL's layer") as the DBAL doesn't own a layer, it is the layer. --- cookbook/doctrine/dbal.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/doctrine/dbal.rst b/cookbook/doctrine/dbal.rst index 9c163c5271c..2934ad0facb 100644 --- a/cookbook/doctrine/dbal.rst +++ b/cookbook/doctrine/dbal.rst @@ -1,12 +1,12 @@ .. index:: pair: Doctrine; DBAL -How to Use Doctrine's DBAL Layer +How to Use Doctrine's DBAL ================================ .. note:: - This article is about Doctrine DBAL's layer. Typically, you'll work with + This article is about the Doctrine DBAL. Typically, you'll work with the higher level Doctrine ORM layer, which simply uses the DBAL behind the scenes to actually communicate with the database. To read more about the Doctrine ORM, see ":doc:`/book/doctrine`". From 693698ec0549ac8b9888fe7560bfc2275f987743 Mon Sep 17 00:00:00 2001 From: micheal Date: Sat, 25 Oct 2014 13:36:44 -0700 Subject: [PATCH 297/835] Shorten the = to match the length of the text. --- cookbook/doctrine/dbal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/doctrine/dbal.rst b/cookbook/doctrine/dbal.rst index 2934ad0facb..6c9bc5c0420 100644 --- a/cookbook/doctrine/dbal.rst +++ b/cookbook/doctrine/dbal.rst @@ -2,7 +2,7 @@ pair: Doctrine; DBAL How to Use Doctrine's DBAL -================================ +========================== .. note:: From 84164fa907f0c652d082e486cece4d86b919d592 Mon Sep 17 00:00:00 2001 From: micheal Date: Sun, 26 Oct 2014 11:49:01 -0700 Subject: [PATCH 298/835] Removed the possession from Doctrine. --- cookbook/doctrine/dbal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/doctrine/dbal.rst b/cookbook/doctrine/dbal.rst index 6c9bc5c0420..f22e63b3a3c 100644 --- a/cookbook/doctrine/dbal.rst +++ b/cookbook/doctrine/dbal.rst @@ -1,7 +1,7 @@ .. index:: pair: Doctrine; DBAL -How to Use Doctrine's DBAL +How to Use Doctrine DBAL ========================== .. note:: From 27339c7de156e79339a47db9628da432911a0ce4 Mon Sep 17 00:00:00 2001 From: micheal Date: Wed, 29 Oct 2014 20:29:39 -0700 Subject: [PATCH 299/835] One more again. I could have sworn I had them lined up. --- cookbook/doctrine/dbal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/doctrine/dbal.rst b/cookbook/doctrine/dbal.rst index f22e63b3a3c..73d9e07e109 100644 --- a/cookbook/doctrine/dbal.rst +++ b/cookbook/doctrine/dbal.rst @@ -2,7 +2,7 @@ pair: Doctrine; DBAL How to Use Doctrine DBAL -========================== +======================== .. note:: From 6a3e17088332f9dc499aa765fe6877584893b4d3 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 30 Oct 2014 09:51:00 +0100 Subject: [PATCH 300/835] =?UTF-8?q?=C2=A7=20about=20DebugBundle=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/var_dumper/advanced.rst | 7 ++-- components/var_dumper/introduction.rst | 56 +++++++++++++++++--------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index 70bf85e5198..29aff0c31da 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -31,9 +31,10 @@ Before dumping it, you can further limit the resulting :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object by calling its :method:`Symfony\\Component\\VarDumper\\Cloner\\Data::getLimitedClone` method: -- the first `$maxDepth` argument allows limiting dumps in the depth dimension, -- the second `$maxItemsPerDepth` limits the number of items per depth level, -- and the last `$useRefHandles` defaults to `true` but allows removing internal + +- the first ``$maxDepth`` argument allows limiting dumps in the depth dimension, +- the second ``$maxItemsPerDepth`` limits the number of items per depth level, +- and the last ``$useRefHandles`` defaults to ``true`` but allows removing internal objects' handles for sparser output, - but unlike the previous limits on cloners that remove data on purpose, these limits can be changed back and forth before dumping since they do not affect diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst index bd48be2809d..2c2ae679899 100644 --- a/components/var_dumper/introduction.rst +++ b/components/var_dumper/introduction.rst @@ -23,11 +23,8 @@ You can install the component in 2 different ways: The dump() function ------------------- -The VarDumper component creates a global ``dump()`` function that is -configured out of the box: HTML or CLI output is automatically selected based -on the current PHP SAPI. - -The advantages of this function are: +The VarDumper component creates a global ``dump()`` function that you can +use instead of e.g. :phpfunction:`var_dump`. By using it, you'll gain: - per object and resource types specialized view to e.g. filter out Doctrine internals while dumping a single proxy entity, or get more @@ -47,44 +44,67 @@ You can change the behavior of this function by calling :method:`VarDumper::setHandler($callable) `: calls to ``dump()`` will then be forwarded to ``$callable``. -Where does the output go? -------------------------- +Output format and destination +----------------------------- -If you read the advanced documentation, you'll learn how to change the -format or redirect the output to wherever you want. +If you read the `advanced documentation `, you'll learn how to +change the format or redirect the output to wherever you want. By default, these are selected based on your current PHP SAPI: -- on the command line (CLI SAPI), the output is written on `STDERR`. This +- on the command line (CLI SAPI), the output is written on ``STDERR``. This can be surprising to some because this bypasses PHP's output buffering - mechanism. On the other hand, this give the possibility to easily split + mechanism. On the other hand, it give the possibility to easily split dumps from regular output by using pipe redirection. - on other SAPIs, dumps are written as HTML on the regular output. DebugBundle and Twig integration -------------------------------- -The `DebugBundle` allows greater integration of the component into the +The ``DebugBundle`` allows greater integration of the component into the Symfony full stack framework. It is enabled by default in the dev environement of the standard edition since version 2.6. Since generating (even debug) output in the controller or in the model of your application may just break it by e.g. sending HTTP headers or -corrupting your view, the bundle configures the `dump()` function so that +corrupting your view, the bundle configures the ``dump()`` function so that variables are dumped in the web debug toolbar. -But if the toolbar can not be displayed because you e.g. called `die`/`exit` +But if the toolbar can not be displayed because you e.g. called ``die``/``exit`` or a fatal error occurred, then dumps are written on the regular output. In a Twig template, two constructs are available for dumping a variable. -Choosing between both is generally only a matter of personal taste: +Choosing between both is mostly a matter of personal taste, still: -- `{% dump foo.bar %}` is the way to go when the original template output +- ``{% dump foo.bar %}`` is the way to go when the original template output shall not be modified: variables are not dumped inline, but in the web debug toolbar. -- on the contrary, `{{ dump(foo.bar) }}` dumps inline and thus may or not +- on the contrary, ``{{ dump(foo.bar) }}`` dumps inline and thus may or not be suited to your use case (e.g. you shouldn't use it in an HTML - attribute or a `script` tag). + attribute or a `` - + This is all that's needed to compile this CoffeeScript file and serve it as the compiled JavaScript. @@ -100,7 +100,7 @@ You can also combine multiple CoffeeScript files into a single output file: array('coffee') ) as $url): ?> - + Both the files will now be served up as a single file compiled into regular JavaScript. @@ -186,4 +186,4 @@ being run through the CoffeeScript filter): ) ) as $url): ?> - + diff --git a/cookbook/assetic/asset_management.rst b/cookbook/assetic/asset_management.rst index 90311adc4e4..6743796edba 100644 --- a/cookbook/assetic/asset_management.rst +++ b/cookbook/assetic/asset_management.rst @@ -69,7 +69,7 @@ To include JavaScript files, use the ``javascripts`` tag in any template: array('@AcmeFooBundle/Resources/public/js/*') ) as $url): ?> - + .. note:: @@ -126,7 +126,7 @@ above, except with the ``stylesheets`` tag: array('cssrewrite') ) as $url): ?> - + .. note:: @@ -178,7 +178,7 @@ To include an image you can use the ``image`` tag. array('@AcmeFooBundle/Resources/public/images/example.jpg') ) as $url): ?> Example - + You can also use Assetic for image optimization. More information in :doc:`/cookbook/assetic/jpeg_optimize`. @@ -231,7 +231,7 @@ but still serve them as a single file: ) ) as $url): ?> - + In the ``dev`` environment, each file is still served individually, so that you can debug problems more easily. However, in the ``prod`` environment @@ -268,7 +268,7 @@ combine third party assets, such as jQuery, with your own into a single file: ) ) as $url): ?> - + Using Named Assets ~~~~~~~~~~~~~~~~~~ @@ -341,7 +341,7 @@ with the ``@named_asset`` notation: ) ) as $url): ?> - + .. _cookbook-assetic-filters: @@ -417,7 +417,7 @@ into your template: array('uglifyjs2') ) as $url): ?> - + A more detailed guide about configuring and using Assetic filters as well as details of Assetic's debug mode can be found in :doc:`/cookbook/assetic/uglifyjs`. @@ -444,7 +444,7 @@ done from the template and is relative to the public document root: array('output' => 'js/compiled/main.js') ) as $url): ?> - + .. note:: @@ -567,4 +567,4 @@ some isolated directory (e.g. ``/js/compiled``), to keep things organized: array('output' => 'js/compiled/main.js') ) as $url): ?> - + diff --git a/cookbook/assetic/jpeg_optimize.rst b/cookbook/assetic/jpeg_optimize.rst index 162b859fcf9..01f67f6ebcb 100644 --- a/cookbook/assetic/jpeg_optimize.rst +++ b/cookbook/assetic/jpeg_optimize.rst @@ -69,7 +69,7 @@ It can now be used from a template: array('jpegoptim') ) as $url): ?> Example - + Removing all EXIF Data ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/cookbook/assetic/uglifyjs.rst b/cookbook/assetic/uglifyjs.rst index 64d76c0a0f8..958a1c616a5 100644 --- a/cookbook/assetic/uglifyjs.rst +++ b/cookbook/assetic/uglifyjs.rst @@ -133,7 +133,7 @@ can configure its location using the ``node`` key: .. code-block:: xml - - + .. note:: @@ -208,7 +208,7 @@ apply this filter when debug mode is off (e.g. ``app.php``): array('?uglifyjs2') ) as $url): ?> - + To try this out, switch to your ``prod`` environment (``app.php``). But before you do, don't forget to :ref:`clear your cache ` @@ -284,7 +284,7 @@ helper: array('cssrewrite') ) as $url): ?> - + Just like with the ``uglifyjs2`` filter, if you prefix the filter name with ``?`` (i.e. ``?uglifycss``), the minification will only happen when you're diff --git a/cookbook/assetic/yuicompressor.rst b/cookbook/assetic/yuicompressor.rst index ee200b82e46..fedd4dab1da 100644 --- a/cookbook/assetic/yuicompressor.rst +++ b/cookbook/assetic/yuicompressor.rst @@ -102,7 +102,7 @@ the view layer, this work is done in your templates: array('yui_js') ) as $url): ?> - + .. note:: @@ -130,7 +130,7 @@ can be repeated to minify your stylesheets. array('yui_css') ) as $url): ?> - + Disable Minification in Debug Mode ---------------------------------- @@ -156,7 +156,7 @@ apply this filter when debug mode is off. array('?yui_js') ) as $url): ?> - + .. tip:: diff --git a/cookbook/form/form_collections.rst b/cookbook/form/form_collections.rst index 6871be7cdab..df8ab95ecc2 100644 --- a/cookbook/form/form_collections.rst +++ b/cookbook/form/form_collections.rst @@ -226,7 +226,7 @@ zero tags when first created).
    • row($tag['name']) ?>
    • - +
    end($form) ?> diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 41361bf6775..7add50be9a4 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -795,7 +795,7 @@ and customize the ``form_errors`` fragment.
    • getMessage() ?>
    • - +
    @@ -860,11 +860,11 @@ fields (e.g. a whole form), and not just an individual field.
    • getMessage() ?>
    • - +
    - + diff --git a/cookbook/security/form_login.rst b/cookbook/security/form_login.rst index 79ce5f808d1..4df556c7c01 100644 --- a/cookbook/security/form_login.rst +++ b/cookbook/security/form_login.rst @@ -213,7 +213,7 @@ redirect to the URL defined by some ``account`` route, use the following:
    getMessage() ?>
    - + diff --git a/cookbook/security/impersonating_user.rst b/cookbook/security/impersonating_user.rst index c3bd0b708c6..8966357a198 100644 --- a/cookbook/security/impersonating_user.rst +++ b/cookbook/security/impersonating_user.rst @@ -84,7 +84,7 @@ to show a link to exit impersonation: > Exit impersonation - + Of course, this feature needs to be made available to a small group of users. By default, access is restricted to users having the ``ROLE_ALLOWED_TO_SWITCH`` diff --git a/cookbook/security/remember_me.rst b/cookbook/security/remember_me.rst index 7efb39f277d..668057201cf 100644 --- a/cookbook/security/remember_me.rst +++ b/cookbook/security/remember_me.rst @@ -90,7 +90,7 @@ might ultimately looks like this:
    getMessage() ?>
    - + diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index c1f270c9e82..82327fd7e4f 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -101,7 +101,7 @@ A much more flexible method would look like this: errors($emailField) ?> widget($emailField) ?> - + In both cases, no input fields would render unless your ``emails`` data array From 4d98ebfb2f85974747af12fe44ff237d71c35388 Mon Sep 17 00:00:00 2001 From: "Andrew M." Date: Sat, 8 Nov 2014 11:11:27 +0100 Subject: [PATCH 359/835] Fix typo: Objected => Object --- components/event_dispatcher/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/event_dispatcher/introduction.rst b/components/event_dispatcher/introduction.rst index e862af05347..f08500ea5b1 100644 --- a/components/event_dispatcher/introduction.rst +++ b/components/event_dispatcher/introduction.rst @@ -12,7 +12,7 @@ The EventDispatcher Component Introduction ------------ -Objected Oriented code has gone a long way to ensuring code extensibility. By +Object Oriented code has gone a long way to ensuring code extensibility. By creating classes that have well defined responsibilities, your code becomes more flexible and a developer can extend them with subclasses to modify their behaviors. But if they want to share the changes with other developers who have From dbba74aa17e93178f276ef4a8852feab17ac257f Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 8 Nov 2014 20:15:03 +0100 Subject: [PATCH 360/835] Created September CHANGELOG --- changelog.rst | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/changelog.rst b/changelog.rst index 7e562ed1e86..620466182a6 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,53 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +September, 2014 +--------------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `eac0e51 `_ #4195 Added a note about the total deprecation of YUI (javiereguiluz) +- `e44c791 `_ #4047 Documented info method (WouterJ) +- `d5d46ec `_ #4017 Clarify that route defaults don't need a placeholder (iamdto) +- `1d56da4 `_ #4239 Remove redundant references to trusting HttpCache (thewilkybarkid) +- `c306b68 `_ #4249 provide node path on configuration (desarrolla2) +- `9b4b36f `_ #4236 Javiereguiluz bundle install instructions (WouterJ) +- `a578de9 `_ #4223 Revamped the documentation about "Contributing Docs" (javiereguiluz) +- `de60dbe `_ #4182 Added note about exporting SYMFONY_ENV (jpb0104) +- `a8dc2bf `_ #4166 Translation custom loaders (raulfraile) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `5500e0b `_ #4267 Fix error in bundle installation standard example (WouterJ) +- `082755d `_ #4240 [Components][EventDispatcher] fix ContainerAwareEventDispatcher definition (xabbuh) +- `2319d6a `_ #4213 Handle "constraints" option in form unit testing (sarcher) +- `c567707 `_ #4222 [Components][DependencyInjection] do not reference services in parameters (xabbuh) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `df16779 `_ #4226 add note about parameters in imports (xabbuh) +- `c332063 `_ #4278 Missing word in DependencyInjection => Types of Injection (fabschurt) +- `3a4e226 `_ #4263 Fixed typo (zebba) +- `187c255 `_ #4259 Added feature freeze dates for Symfony versions (javiereguiluz) +- `efc1436 `_ #4247 [Reference] link translation DIC tags to components section (xabbuh) +- `17addb1 `_ #4238 Finished #3924 (WouterJ) +- `19a0c35 `_ #4252 Removed unnecessary comma (allejo) +- `9fd91d6 `_ #4219 Cache needs be cleared (burki94) +- `025f02e `_ #4220 Added a note about the side effects of enabling both PHP and Twig (javiereguiluz) +- `46fcb67 `_ #4218 Caution that roles should start with ROLE_ (jrjohnson) +- `78eea60 `_ #4077 Removed outdated translations from the official list (WouterJ) +- `2cf9e47 `_ #4171 Fixed version for composer install (zomberg) +- `5c62b36 `_ #4216 Update Collection.rst (azarzag) +- `8591b87 `_ #4215 Fixed code highlighting (WouterJ) +- `f276e34 `_ #4205 replace "Symfony2" with "Symfony" (xabbuh) +- `6db13ac `_ #4208 Added a note about the lacking features of Yaml Component (javiereguiluz) +- `f8c6201 `_ #4200 Moved 'contributing' images to their own directory (javiereguiluz) +- `b4650fa `_ #4199 fix name of the Yaml component (xabbuh) +- `9d89bb0 `_ #4190 add link to form testing chapter in test section (xabbuh) + August, 2014 ------------ From 67ae64f73f5f5f966c4ab0979ddcdd8165d9e4cc Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 8 Nov 2014 20:15:54 +0100 Subject: [PATCH 361/835] Created September CHANGELOG --- changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.rst b/changelog.rst index f4675f9fad6..53de1738021 100644 --- a/changelog.rst +++ b/changelog.rst @@ -19,12 +19,14 @@ September, 2014 New Documentation ~~~~~~~~~~~~~~~~~ +- `e8a1501 `_ #4201 [Components][Process] `````mustRun()````` documentation (xabbuh) - `eac0e51 `_ #4195 Added a note about the total deprecation of YUI (javiereguiluz) - `e44c791 `_ #4047 Documented info method (WouterJ) - `d5d46ec `_ #4017 Clarify that route defaults don't need a placeholder (iamdto) - `1d56da4 `_ #4239 Remove redundant references to trusting HttpCache (thewilkybarkid) - `c306b68 `_ #4249 provide node path on configuration (desarrolla2) - `9b4b36f `_ #4236 Javiereguiluz bundle install instructions (WouterJ) +- `ea068c2 `_ #4202 [Reference][Constraints] caution on `````null````` values in Expression constraint (xabbuh) - `a578de9 `_ #4223 Revamped the documentation about "Contributing Docs" (javiereguiluz) - `de60dbe `_ #4182 Added note about exporting SYMFONY_ENV (jpb0104) - `a8dc2bf `_ #4166 Translation custom loaders (raulfraile) @@ -32,10 +34,12 @@ New Documentation Fixed Documentation ~~~~~~~~~~~~~~~~~~~ +- `c289ac8 `_ #4279 Double-quotes instead of single quotes (UnexpectedValueException in Windows 8) (galdiolo) - `5500e0b `_ #4267 Fix error in bundle installation standard example (WouterJ) - `082755d `_ #4240 [Components][EventDispatcher] fix ContainerAwareEventDispatcher definition (xabbuh) - `2319d6a `_ #4213 Handle "constraints" option in form unit testing (sarcher) - `c567707 `_ #4222 [Components][DependencyInjection] do not reference services in parameters (xabbuh) +- `02d1091 `_ #4209 Fix method for adding placholders in progressBar (danez) Minor Documentation Changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -54,6 +58,7 @@ Minor Documentation Changes - `2cf9e47 `_ #4171 Fixed version for composer install (zomberg) - `5c62b36 `_ #4216 Update Collection.rst (azarzag) - `8591b87 `_ #4215 Fixed code highlighting (WouterJ) +- `8f01195 `_ #4212 Missing backtick, thanks to @Baptouuuu (WouterJ) - `f276e34 `_ #4205 replace "Symfony2" with "Symfony" (xabbuh) - `6db13ac `_ #4208 Added a note about the lacking features of Yaml Component (javiereguiluz) - `f8c6201 `_ #4200 Moved 'contributing' images to their own directory (javiereguiluz) From 51d703218b2624f4c34ddec843283e2437225471 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 8 Nov 2014 20:16:39 +0100 Subject: [PATCH 362/835] Created September CHANGELOG --- changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog.rst b/changelog.rst index 8b26c5b25c3..07f02b9b99b 100644 --- a/changelog.rst +++ b/changelog.rst @@ -22,9 +22,11 @@ New Documentation - `e8a1501 `_ #4201 [Components][Process] `````mustRun()````` documentation (xabbuh) - `eac0e51 `_ #4195 Added a note about the total deprecation of YUI (javiereguiluz) - `e44c791 `_ #4047 Documented info method (WouterJ) +- `2962e14 `_ #4003 [Twig][Form] Moved twig.form.resources to a higher level (stefanosala) - `d5d46ec `_ #4017 Clarify that route defaults don't need a placeholder (iamdto) - `1d56da4 `_ #4239 Remove redundant references to trusting HttpCache (thewilkybarkid) - `c306b68 `_ #4249 provide node path on configuration (desarrolla2) +- `9f0f14e `_ #4210 Move debug commands to debug namespace (matthieuauger) - `9b4b36f `_ #4236 Javiereguiluz bundle install instructions (WouterJ) - `ea068c2 `_ #4202 [Reference][Constraints] caution on `````null````` values in Expression constraint (xabbuh) - `a578de9 `_ #4223 Revamped the documentation about "Contributing Docs" (javiereguiluz) @@ -46,6 +48,7 @@ Minor Documentation Changes - `df16779 `_ #4226 add note about parameters in imports (xabbuh) - `c332063 `_ #4278 Missing word in DependencyInjection => Types of Injection (fabschurt) +- `287c7bf `_ #4275 added Nicolas to the list of mergers for the new var dumper component (fabpot) - `3a4e226 `_ #4263 Fixed typo (zebba) - `187c255 `_ #4259 Added feature freeze dates for Symfony versions (javiereguiluz) - `efc1436 `_ #4247 [Reference] link translation DIC tags to components section (xabbuh) From f934c315d865591a05b55b51816b427b188beb7c Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 8 Nov 2014 20:17:22 +0100 Subject: [PATCH 363/835] Created October CHANGELOG --- changelog.rst | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/changelog.rst b/changelog.rst index 620466182a6..cf5d99bab8a 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,73 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +October, 2014 +------------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `d7ef1c7 `_ #4348 Updated information about handling validation of embedded forms to Valid... (peterrehm) +- `691b13d `_ #4340 [Cookbook][Web Server] add sidebar for the built-in server in VMs (xabbuh) +- `d79c48d `_ #4280 [Cookbook][Cache] Added config example for Varnish 4.0 (thierrymarianne) +- `5849f7f `_ #4168 [Components][Form] describe how to access form errors (xabbuh) +- `c10e9c1 `_ #4371 Added a code example for emailing on 4xx and 5xx errors without 404's (weaverryan) +- `0c57939 `_ #4327 First import of the "Official Best Practices" book (javiereguiluz) +- `8dc90ef `_ #4224 [Components][HttpKernel] outline implications of the kernel.terminate event (xabbuh) +- `d3b5ba2 `_ #4085 [Component][Forms] add missing features introduced in 2.3 (xabbuh) +- `f433e64 `_ #4099 Composer installation verbosity tip (dannykopping) +- `925a162 `_ #4290 Updating library/bundle install docs to use "require" (weaverryan) +- `44f570b `_ #4294 Improve cookbook entry for error pages in 2.3~ (mpdude) +- `3b6c2b9 `_ #4269 [Cookbook][External Parameters] Enhance content (bicpi) +- `62bafad `_ #4246 [Reference] add description for the `````validation_groups````` option (xabbuh) +- `c2342a7 `_ #4241 [Form] Added information about float choice lists (peterrehm) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `68a2c7b `_ #4381 Updated Valid constraint reference (inso) +- `db01e57 `_ #4362 Missing apostrophe in source example. (astery) +- `d49d51f `_ #4350 Removed extra parenthesis (sivolobov) +- `e6d7d8f `_ #4315 Update choice.rst (odolbeau) +- `1b15d57 `_ #4300 [Components][PropertyAccess] Fix PropertyAccessorBuilder usage (Thierry Geindre) +- `061324f `_ #4297 [Cookbook][Doctrine] Fix typo in XML configuration for custom SQL functions (jdecool) +- `f81b7ad `_ #4292 Fixed broken external link to DemoController Test (danielsan) +- `9591a04 `_ #4284 change misleading language identifier (Kristof Van Cauwenbergh, kristofvc) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `a4f7d51 `_ #4396 Corrected latin abbreviation (GeertDD) +- `ebf2927 `_ #4387 Inline condition removed for easier reading (acidjames) +- `aa70028 `_ #4375 Removed the redundant usage of layer. (micheal) +- `f3dd676 `_ #4394 update Sphinx extension submodule reference (xabbuh) +- `9e03f2d `_ #4388 Minor spelling fix (GeertDD) +- `4dfd607 `_ #4356 Remove incoherence between Doctrine and Propel introduction paragraphs (arnaugm) +- `1d71332 `_ #4344 [Templating] Added a sentence that explains what a Template Helper is (iltar) +- `9a76309 `_ #4384 fix typo (kokoon) +- `3e8aa59 `_ #4376 Cleaned up javascript code (flip111) +- `06e7c5f `_ #4364 changed submit button label (OskarStark) +- `d1810ca `_ #4357 fix Twig-extensions links (mhor) +- `e2e2915 `_ #4359 Added missing closing parenthesis to example. (mattjanssen) +- `f1bb8bb `_ #4358 Fixed link to documentation standards (sivolobov) +- `65c891d `_ #4355 Missing space (ErikSaunier) +- `7359cb4 `_ #4196 Clarified the bundle base template bit. (Veltar) +- `6ceb8cb `_ #4345 Correct capitalization for the Content-Type header (GeertDD) +- `3e4c92a `_ #4104 Use ${APACHE_LOG_DIR} instead of /var/log/apache2 (xamgreen) +- `3da0776 `_ #4338 ESI Variable Details Continuation (Farkie, weaverryan) +- `7f461d2 `_ #4325 [Components][Form] Correct a typo (fabschurt) +- `d162329 `_ #4276 [Components][HttpFoundation] Make a small grammatical adjustment (fabschurt) +- `69bfac1 `_ #4322 [Components][DependencyInjection] Correct a typo: replace "then" by "the" (fabschurt) +- `8073239 `_ #4318 [Cookbook][Bundles] Correct a typo: remove unnecessary "the" word (fabschurt) +- `34e22d6 `_ #4317 Remove horizontal scrollbar and change event name to follow conventions (ifdattic) +- `090afab `_ #4287 support Varnish in configuration blocks (xabbuh) +- `1603463 `_ #4306 Improve readability (ifdattic) +- `31d7905 `_ #4302 View documentation had a reference to the wrong twig template (milan) +- `ef11ef4 `_ #4250 Clarifying Bundle Best Practices is for *reusable* bundles (weaverryan) +- `430eabf `_ #4298 Book HTTP Fundamentals routing example fixed with routing.xml file (peterkokot) +- `7ab6df9 `_ #4237 Finished #3886 (ahsio, WouterJ) +- `990b453 `_ #4245 [Contributing] tweaks to the contribution chapter (xabbuh) + September, 2014 --------------- From c8c99d6507c8f18f1c5a904e008034ddc77850e4 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 8 Nov 2014 20:17:56 +0100 Subject: [PATCH 364/835] Created October CHANGELOG --- changelog.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/changelog.rst b/changelog.rst index 1a89e4370e2..111af70c404 100644 --- a/changelog.rst +++ b/changelog.rst @@ -29,6 +29,7 @@ New Documentation - `d3b5ba2 `_ #4085 [Component][Forms] add missing features introduced in 2.3 (xabbuh) - `f433e64 `_ #4099 Composer installation verbosity tip (dannykopping) - `925a162 `_ #4290 Updating library/bundle install docs to use "require" (weaverryan) +- `86c67e8 `_ #4233 2.5 Validation API changes (nicolassing, lashae, Rootie, weaverryan) - `44f570b `_ #4294 Improve cookbook entry for error pages in 2.3~ (mpdude) - `3b6c2b9 `_ #4269 [Cookbook][External Parameters] Enhance content (bicpi) - `62bafad `_ #4246 [Reference] add description for the `````validation_groups````` option (xabbuh) @@ -37,6 +38,7 @@ New Documentation Fixed Documentation ~~~~~~~~~~~~~~~~~~~ +- `dde6919 `_ #4390 Update custom_constraint.rst (luciantugui) - `68a2c7b `_ #4381 Updated Valid constraint reference (inso) - `db01e57 `_ #4362 Missing apostrophe in source example. (astery) - `d49d51f `_ #4350 Removed extra parenthesis (sivolobov) @@ -53,10 +55,12 @@ Minor Documentation Changes - `ebf2927 `_ #4387 Inline condition removed for easier reading (acidjames) - `aa70028 `_ #4375 Removed the redundant usage of layer. (micheal) - `f3dd676 `_ #4394 update Sphinx extension submodule reference (xabbuh) +- `6406a27 `_ #4391 Removed unused use UsernameNotFoundException (boekkooi) - `9e03f2d `_ #4388 Minor spelling fix (GeertDD) - `4dfd607 `_ #4356 Remove incoherence between Doctrine and Propel introduction paragraphs (arnaugm) - `1d71332 `_ #4344 [Templating] Added a sentence that explains what a Template Helper is (iltar) - `9a76309 `_ #4384 fix typo (kokoon) +- `eb752cc `_ #4363 Fixed sentence (WouterJ) - `3e8aa59 `_ #4376 Cleaned up javascript code (flip111) - `06e7c5f `_ #4364 changed submit button label (OskarStark) - `d1810ca `_ #4357 fix Twig-extensions links (mhor) @@ -69,14 +73,18 @@ Minor Documentation Changes - `3da0776 `_ #4338 ESI Variable Details Continuation (Farkie, weaverryan) - `7f461d2 `_ #4325 [Components][Form] Correct a typo (fabschurt) - `d162329 `_ #4276 [Components][HttpFoundation] Make a small grammatical adjustment (fabschurt) +- `459052d `_ Merge remote-tracking branch 'origin/2.4' into 2.5 - `69bfac1 `_ #4322 [Components][DependencyInjection] Correct a typo: replace "then" by "the" (fabschurt) - `8073239 `_ #4318 [Cookbook][Bundles] Correct a typo: remove unnecessary "the" word (fabschurt) +- `228111b `_ #4316 Remove horizontal scrollbar (ifdattic) - `34e22d6 `_ #4317 Remove horizontal scrollbar and change event name to follow conventions (ifdattic) - `090afab `_ #4287 support Varnish in configuration blocks (xabbuh) - `1603463 `_ #4306 Improve readability (ifdattic) +- `e5fed9d `_ #4303 Fix spelling (nurikabe) - `31d7905 `_ #4302 View documentation had a reference to the wrong twig template (milan) - `ef11ef4 `_ #4250 Clarifying Bundle Best Practices is for *reusable* bundles (weaverryan) - `430eabf `_ #4298 Book HTTP Fundamentals routing example fixed with routing.xml file (peterkokot) +- `a535c9f `_ #4285 Update security.rst (placid2000) - `7ab6df9 `_ #4237 Finished #3886 (ahsio, WouterJ) - `990b453 `_ #4245 [Contributing] tweaks to the contribution chapter (xabbuh) From 2328b48ed97b2f86c71e923b81e63da0f47865e9 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 8 Nov 2014 20:18:32 +0100 Subject: [PATCH 365/835] Created October CHANGELOG --- changelog.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/changelog.rst b/changelog.rst index 041faaece25..ef7a0a168f2 100644 --- a/changelog.rst +++ b/changelog.rst @@ -21,18 +21,28 @@ New Documentation - `d7ef1c7 `_ #4348 Updated information about handling validation of embedded forms to Valid... (peterrehm) - `691b13d `_ #4340 [Cookbook][Web Server] add sidebar for the built-in server in VMs (xabbuh) +- `bd85865 `_ #4299 [Serializer] Handle circular references. symfony/symfony#12098. (dunglas) - `d79c48d `_ #4280 [Cookbook][Cache] Added config example for Varnish 4.0 (thierrymarianne) - `5849f7f `_ #4168 [Components][Form] describe how to access form errors (xabbuh) - `c10e9c1 `_ #4371 Added a code example for emailing on 4xx and 5xx errors without 404's (weaverryan) +- `1117741 `_ #4159 [WCM][OptionsResolver] Adjusted the OptionsResolver documentation to describe the 2.6 API (webmozart, peterrehm) - `0c57939 `_ #4327 First import of the "Official Best Practices" book (javiereguiluz) +- `2cd6646 `_ #4293 Document error page preview (Symfony ~2.6) (mpdude) +- `142c826 `_ #4005 [Cookbook][Web server] description for running PHP's built-in web server in the background (xabbuh) - `8dc90ef `_ #4224 [Components][HttpKernel] outline implications of the kernel.terminate event (xabbuh) - `d3b5ba2 `_ #4085 [Component][Forms] add missing features introduced in 2.3 (xabbuh) - `f433e64 `_ #4099 Composer installation verbosity tip (dannykopping) +- `f583a45 `_ #4204 [Reference][Constraints] validate `````null````` (Expression constraint in 2.6) (xabbuh) - `925a162 `_ #4290 Updating library/bundle install docs to use "require" (weaverryan) - `86c67e8 `_ #4233 2.5 Validation API changes (nicolassing, lashae, Rootie, weaverryan) +- `0f34bb8 `_ #3956 [Command] Added LockHelper (lyrixx) +- `278de83 `_ #3930 [Console] Add Process Helper documentation (romainneutron) - `44f570b `_ #4294 Improve cookbook entry for error pages in 2.3~ (mpdude) - `3b6c2b9 `_ #4269 [Cookbook][External Parameters] Enhance content (bicpi) +- `25a17fe `_ #4264 [#4003] A few more form_themes config changes (weaverryan) +- `5b65654 `_ #3912 [Security] Added remote_user firewall info and documentation for pre authenticated firewalls (Maxime Douailin, mdouailin) - `62bafad `_ #4246 [Reference] add description for the `````validation_groups````` option (xabbuh) +- `5d505bb `_ #4206 Added note about ProgressBar changes (kbond) - `c2342a7 `_ #4241 [Form] Added information about float choice lists (peterrehm) Fixed Documentation @@ -40,6 +50,7 @@ Fixed Documentation - `dde6919 `_ #4390 Update custom_constraint.rst (luciantugui) - `68a2c7b `_ #4381 Updated Valid constraint reference (inso) +- `dbb25b9 `_ #4379 [OptionsResolver] Fix wrong namespace in example (rybakit) - `db01e57 `_ #4362 Missing apostrophe in source example. (astery) - `d49d51f `_ #4350 Removed extra parenthesis (sivolobov) - `e6d7d8f `_ #4315 Update choice.rst (odolbeau) @@ -51,6 +62,7 @@ Fixed Documentation Minor Documentation Changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- `217bf5f `_ #4353 [Cookbook][Controller] fix route prefix in PHP code example (xabbuh) - `a4f7d51 `_ #4396 Corrected latin abbreviation (GeertDD) - `ebf2927 `_ #4387 Inline condition removed for easier reading (acidjames) - `aa70028 `_ #4375 Removed the redundant usage of layer. (micheal) @@ -59,6 +71,7 @@ Minor Documentation Changes - `9e03f2d `_ #4388 Minor spelling fix (GeertDD) - `4dfd607 `_ #4356 Remove incoherence between Doctrine and Propel introduction paragraphs (arnaugm) - `1d71332 `_ #4344 [Templating] Added a sentence that explains what a Template Helper is (iltar) +- `22b9b27 `_ #4372 Tweaks after proofreading the 2.6 OptionsResolver stuff (weaverryan, WouterJ) - `9a76309 `_ #4384 fix typo (kokoon) - `eb752cc `_ #4363 Fixed sentence (WouterJ) - `3e8aa59 `_ #4376 Cleaned up javascript code (flip111) @@ -71,6 +84,7 @@ Minor Documentation Changes - `6ceb8cb `_ #4345 Correct capitalization for the Content-Type header (GeertDD) - `3e4c92a `_ #4104 Use ${APACHE_LOG_DIR} instead of /var/log/apache2 (xamgreen) - `3da0776 `_ #4338 ESI Variable Details Continuation (Farkie, weaverryan) +- `9e0e12d `_ Merge branch '2.5' - `7f461d2 `_ #4325 [Components][Form] Correct a typo (fabschurt) - `d162329 `_ #4276 [Components][HttpFoundation] Make a small grammatical adjustment (fabschurt) - `459052d `_ Merge remote-tracking branch 'origin/2.4' into 2.5 From d90e09cf9f5c2686968e7b67317bfc3dfc218aca Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 8 Nov 2014 20:24:47 +0100 Subject: [PATCH 366/835] Fix build error --- changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.rst b/changelog.rst index cf5d99bab8a..e29f3cf1aed 100644 --- a/changelog.rst +++ b/changelog.rst @@ -116,7 +116,7 @@ Minor Documentation Changes - `19a0c35 `_ #4252 Removed unnecessary comma (allejo) - `9fd91d6 `_ #4219 Cache needs be cleared (burki94) - `025f02e `_ #4220 Added a note about the side effects of enabling both PHP and Twig (javiereguiluz) -- `46fcb67 `_ #4218 Caution that roles should start with ROLE_ (jrjohnson) +- `46fcb67 `_ #4218 Caution that roles should start with ``ROLE_`` (jrjohnson) - `78eea60 `_ #4077 Removed outdated translations from the official list (WouterJ) - `2cf9e47 `_ #4171 Fixed version for composer install (zomberg) - `5c62b36 `_ #4216 Update Collection.rst (azarzag) From 434fed77e9fd041f2ecc504b524fc2fe66d6d982 Mon Sep 17 00:00:00 2001 From: Mario Young Date: Sat, 8 Nov 2014 03:29:42 -0300 Subject: [PATCH 367/835] Correct link to scopes page --- cookbook/console/console_command.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index 1b3b8a18ef4..b0c28000ec4 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -78,7 +78,7 @@ service container. In other words, you have access to any configured service:: // ... } -However, due to the `container scopes `_ this +However, due to the `container scopes `_ this code doesn't work for some services. For instance, if you try to get the ``request`` service or any other service related to it, you'll get the following error: From 7a37cb8d43f0f5b13625d0806908fe56e9cfc358 Mon Sep 17 00:00:00 2001 From: Mario Young Date: Sat, 8 Nov 2014 13:51:52 -0300 Subject: [PATCH 368/835] changes from xabbuh change the link according to xabbuh comment --- cookbook/console/console_command.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index b0c28000ec4..ccd5065d769 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -78,7 +78,7 @@ service container. In other words, you have access to any configured service:: // ... } -However, due to the `container scopes `_ this +However, due to the :doc:`container scopes ` this code doesn't work for some services. For instance, if you try to get the ``request`` service or any other service related to it, you'll get the following error: From 0e2c491e5451271097cc9c78b4bfe205046b0c7f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 8 Aug 2014 11:05:04 +0200 Subject: [PATCH 369/835] removed references to documentation from external sources --- .travis.yml | 1 - bundles/index.rst | 13 ------------- bundles/map.rst.inc | 5 ----- contributing/documentation/format.rst | 1 - index.rst | 23 ----------------------- install.sh | 24 ------------------------ 6 files changed, 67 deletions(-) delete mode 100644 bundles/index.rst delete mode 100644 bundles/map.rst.inc delete mode 100644 install.sh diff --git a/.travis.yml b/.travis.yml index 73c2a7043f4..07124e8178c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ python: - "2.7" install: - - "bash install.sh" - "pip install -q -r requirements.txt --use-mirrors" script: sphinx-build -nW -b html -d _build/doctrees . _build/html diff --git a/bundles/index.rst b/bundles/index.rst deleted file mode 100644 index d8f1298f5d8..00000000000 --- a/bundles/index.rst +++ /dev/null @@ -1,13 +0,0 @@ -The Symfony Standard Edition Bundles -==================================== - -.. toctree:: - :hidden: - - SensioFrameworkExtraBundle/index - SensioGeneratorBundle/index - DoctrineFixturesBundle/index - DoctrineMigrationsBundle/index - DoctrineMongoDBBundle/index - -.. include:: /bundles/map.rst.inc diff --git a/bundles/map.rst.inc b/bundles/map.rst.inc deleted file mode 100644 index 92d742ce70c..00000000000 --- a/bundles/map.rst.inc +++ /dev/null @@ -1,5 +0,0 @@ -* :doc:`SensioFrameworkExtraBundle ` -* :doc:`SensioGeneratorBundle ` -* :doc:`DoctrineFixturesBundle ` -* :doc:`DoctrineMigrationsBundle ` -* :doc:`DoctrineMongoDBBundle ` diff --git a/contributing/documentation/format.rst b/contributing/documentation/format.rst index 6b89158101b..198b5f21218 100644 --- a/contributing/documentation/format.rst +++ b/contributing/documentation/format.rst @@ -209,7 +209,6 @@ submitting your documentation, follow these steps: * Install Sphinx_; * Install the Sphinx extensions using git submodules: ``$ git submodule update --init``; -* (Optionally) Install the bundle docs and CMF docs: ``$ bash install.sh``; * Run ``make html`` and view the generated HTML in the ``build/`` directory. .. _reStructuredText: http://docutils.sourceforge.net/rst.html diff --git a/index.rst b/index.rst index cefdfe8c506..2ef2df24f45 100644 --- a/index.rst +++ b/index.rst @@ -77,29 +77,6 @@ Get answers quickly with reference documents: .. include:: /reference/map.rst.inc -Bundles -------- - -The Symfony Standard Edition comes with some bundles. Learn more about them: - -.. toctree:: - :hidden: - - bundles/index - -.. include:: /bundles/map.rst.inc - -CMF ---- - -The Symfony CMF project makes it easier for developers to add CMS functionality -to applications built with the Symfony PHP framework. - -.. toctree:: - :hidden: - - cmf/index - Contributing ------------ diff --git a/install.sh b/install.sh deleted file mode 100644 index c50451959a0..00000000000 --- a/install.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -function sparse_checkout { - mkdir sparse_checkout - cd sparse_checkout - git init - git config core.sparsecheckout true - git remote add -f origin http://github.com/$1/$2 - echo Resources/doc > .git/info/sparse-checkout - git checkout master - rm -rf ../bundles/$2 - mv Resources/doc ../bundles/$2 - cd .. - rm -rf sparse_checkout -} - -sparse_checkout sensiolabs SensioFrameworkExtraBundle -sparse_checkout sensiolabs SensioGeneratorBundle -sparse_checkout doctrine DoctrineFixturesBundle -sparse_checkout doctrine DoctrineMigrationsBundle -sparse_checkout doctrine DoctrineMongoDBBundle -rm -rf cmf -git clone http://github.com/symfony-cmf/symfony-cmf-docs cmf - From c39ea1d44d87c2606c7e26043a99569b53b60903 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Mon, 11 Aug 2014 11:29:27 +0200 Subject: [PATCH 370/835] Removed more references to third party docs --- .gitignore | 6 ----- book/doctrine.rst | 25 ++++++++++-------- components/http_kernel/introduction.rst | 34 ++++++++++++------------- cookbook/controller/service.rst | 6 ++--- cookbook/templating/PHP.rst | 7 ++--- 5 files changed, 38 insertions(+), 40 deletions(-) diff --git a/.gitignore b/.gitignore index b0b2aafe151..805ea28a8f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,2 @@ /_build -/bundles/DoctrineFixturesBundle -/bundles/DoctrineMigrationsBundle -/bundles/DoctrineMongoDBBundle -/bundles/SensioFrameworkExtraBundle -/bundles/SensioGeneratorBundle -/cmf /_exts diff --git a/book/doctrine.rst b/book/doctrine.rst index 5ff42076169..03f79072582 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -22,7 +22,7 @@ can be. easy, and explained in the ":doc:`/cookbook/doctrine/dbal`" cookbook entry. You can also persist data to `MongoDB`_ using Doctrine ODM library. For - more information, read the ":doc:`/bundles/DoctrineMongoDBBundle/index`" + more information, read the "`DoctrineMongoDBBundle`_" documentation. A Simple Example: A Product @@ -471,10 +471,10 @@ in your application. To do this, run: new column to the existing ``product`` table. An even better way to take advantage of this functionality is via - :doc:`migrations `, which allow you to - generate these SQL statements and store them in migration classes that - can be run systematically on your production server in order to track - and migrate your database schema safely and reliably. + `migrations`_, which allow you to generate these SQL statements and store + them in migration classes that can be run systematically on your production + server in order to track and migrate your database schema safely and + reliably. Your database now has a fully-functional ``product`` table with columns that match the metadata you've specified. @@ -559,7 +559,7 @@ an ``UPDATE`` query if the record already exists in the database. Doctrine provides a library that allows you to programmatically load testing data into your project (i.e. "fixture data"). For information, see - :doc:`/bundles/DoctrineFixturesBundle/index`. + the "`DoctrineFixturesBundle`_" documentation. Fetching Objects from the Database ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -586,8 +586,7 @@ on its ``id`` value:: .. tip:: You can achieve the equivalent of this without writing any code by using - the ``@ParamConverter`` shortcut. See the - :doc:`FrameworkExtraBundle documentation ` + the ``@ParamConverter`` shortcut. See the `FrameworkExtraBundle documentation`_ for more details. When you query for a particular type of object, you always use what's known @@ -1083,7 +1082,7 @@ table, and ``product.category_id`` column, and new foreign key: This task should only be really used during development. For a more robust method of systematically updating your production database, read about - :doc:`Doctrine migrations `. + `migrations`_. Saving Related Entities ~~~~~~~~~~~~~~~~~~~~~~~ @@ -1400,8 +1399,8 @@ For more information about Doctrine, see the *Doctrine* section of the * :doc:`/cookbook/doctrine/common_extensions` * :doc:`/cookbook/doctrine/console` -* :doc:`/bundles/DoctrineFixturesBundle/index` -* :doc:`/bundles/DoctrineMongoDBBundle/index` +* :doc:`DoctrineFixturesBundle` +* :doc:`DoctrineMongoDBBundle` .. _`Doctrine`: http://www.doctrine-project.org/ .. _`MongoDB`: http://www.mongodb.org/ @@ -1414,3 +1413,7 @@ For more information about Doctrine, see the *Doctrine* section of the .. _`Lifecycle Events documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events .. _`Reserved SQL keywords documentation`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#quoting-reserved-words .. _`Persistent classes`: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#persistent-classes +.. _`DoctrineMongoDBBundle`: http://symfony.com/doc/current/bundles/DoctrineMongoDBBundle/index.html +.. _`migrations`: http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html +.. _`DoctrineFixturesBundle`: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html +.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index 2f4230543f0..13f93b45feb 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -288,16 +288,15 @@ on the event object that's passed to listeners on this event. the Symfony Framework, and many deal with collecting profiler data when the profiler is enabled. - One interesting listener comes from the :doc:`SensioFrameworkExtraBundle `, + One interesting listener comes from the `SensioFrameworkExtraBundle`_, which is packaged with the Symfony Standard Edition. This listener's - :doc:`@ParamConverter ` - functionality allows you to pass a full object (e.g. a ``Post`` object) - to your controller instead of a scalar value (e.g. an ``id`` parameter - that was on your route). The listener - ``ParamConverterListener`` - uses - reflection to look at each of the arguments of the controller and tries - to use different methods to convert those to objects, which are then - stored in the ``attributes`` property of the ``Request`` object. Read the - next section to see why this is important. + `@ParamConverter`_ functionality allows you to pass a full object (e.g. a + ``Post`` object) to your controller instead of a scalar value (e.g. an + ``id`` parameter that was on your route). The listener - + ``ParamConverterListener`` - uses reflection to look at each of the + arguments of the controller and tries to use different methods to convert + those to objects, which are then stored in the ``attributes`` property of + the ``Request`` object. Read the next section to see why this is important. 4) Getting the Controller Arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -395,14 +394,12 @@ return a ``Response``. .. sidebar:: ``kernel.view`` in the Symfony Framework There is no default listener inside the Symfony Framework for the ``kernel.view`` - event. However, one core bundle - - :doc:`SensioFrameworkExtraBundle ` - - *does* add a listener to this event. If your controller returns an array, - and you place the :doc:`@Template ` - annotation above the controller, then this listener renders a template, - passes the array you returned from your controller to that template, - and creates a ``Response`` containing the returned content from that - template. + event. However, one core bundle - `SensioFrameworkExtraBundle`_ - *does* + add a listener to this event. If your controller returns an array, + and you place the `@Template`_ annotation above the controller, then this + listener renders a template, passes the array you returned from your + controller to that template, and creates a ``Response`` containing the + returned content from that template. Additionally, a popular community bundle `FOSRestBundle`_ implements a listener on this event which aims to give you a robust view layer @@ -699,3 +696,6 @@ look like this:: .. _FOSRestBundle: https://github.com/friendsofsymfony/FOSRestBundle .. _`Create your own framework... on top of the Symfony2 Components`: http://fabien.potencier.org/article/50/create-your-own-framework-on-top-of-the-symfony2-components-part-1 .. _`PHP FPM`: http://php.net/manual/en/install.fpm.php +.. _`SensioFrameworkExtraBundle`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html +.. _`@ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html +.. _`@Template`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html diff --git a/cookbook/controller/service.rst b/cookbook/controller/service.rst index 2fa69b5c332..f0d23943579 100644 --- a/cookbook/controller/service.rst +++ b/cookbook/controller/service.rst @@ -132,9 +132,8 @@ the route ``_controller`` value: .. tip:: You can also use annotations to configure routing using a controller - defined as a service. See the - :doc:`FrameworkExtraBundle documentation ` - for details. + defined as a service. See the `FrameworkExtraBundle documentation`_ for + details. Alternatives to base Controller Methods --------------------------------------- @@ -267,3 +266,4 @@ inject *only* the exact service(s) that you need directly into the controller. .. _`Controller class source code`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php .. _`base Controller class`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html diff --git a/cookbook/templating/PHP.rst b/cookbook/templating/PHP.rst index a5d503c9c78..8331f279b1b 100644 --- a/cookbook/templating/PHP.rst +++ b/cookbook/templating/PHP.rst @@ -60,11 +60,10 @@ below renders the ``index.html.php`` template:: ); } -You can also use the :doc:`/bundles/SensioFrameworkExtraBundle/annotations/view` -shortcut to render the default ``AcmeHelloBundle:Hello:index.html.php`` template:: +You can also use the `@Template`_ shortcut to render the default +``AcmeHelloBundle:Hello:index.html.php`` template:: // src/Acme/HelloBundle/Controller/HelloController.php - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; // ... @@ -353,3 +352,5 @@ within an HTML context. The second argument lets you change the context. For instance, to output something in a JavaScript script, use the ``js`` context:: escape($var, 'js') ?> + +.. _`@Template`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view` From 2cb8d2728106de5235e693608f357de57ee1e1f1 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 11 Nov 2014 09:16:29 +0100 Subject: [PATCH 371/835] removed more references to documentation from external sources --- book/doctrine.rst | 4 ++-- book/http_cache.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 03f79072582..8859cc2a9f4 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -1399,8 +1399,8 @@ For more information about Doctrine, see the *Doctrine* section of the * :doc:`/cookbook/doctrine/common_extensions` * :doc:`/cookbook/doctrine/console` -* :doc:`DoctrineFixturesBundle` -* :doc:`DoctrineMongoDBBundle` +* `DoctrineFixturesBundle`_ +* `DoctrineMongoDBBundle`_ .. _`Doctrine`: http://www.doctrine-project.org/ .. _`MongoDB`: http://www.mongodb.org/ diff --git a/book/http_cache.rst b/book/http_cache.rst index 5d2d2b21059..1f0aeef9166 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -764,7 +764,7 @@ at some interval (the expiration) to verify that the content is still valid. You can also define HTTP caching headers for expiration and validation by using annotations. See the - :doc:`FrameworkExtraBundle documentation `. + `FrameworkExtraBundle documentation `_. .. index:: pair: Cache; Configuration From cea8f2a1c421c8bcc043cdc6eccd912b3bf3f0a5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 11 Nov 2014 09:43:43 +0100 Subject: [PATCH 372/835] fixed a reference --- book/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/security.rst b/book/security.rst index dc987079e0f..1eb00f7f2bc 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1204,7 +1204,7 @@ Thanks to the SensioFrameworkExtraBundle, you can also secure your controller us } For more information, see the -:doc:`FrameworkExtraBundle documentation `. +`FrameworkExtraBundle documentation `_. Securing other Services ~~~~~~~~~~~~~~~~~~~~~~~ From d27bcc56fc8f8e3f898dd99213fa2ee9aba0fbd9 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 11 Nov 2014 09:47:44 +0100 Subject: [PATCH 373/835] replace doc role for bundle docs with external ref --- book/security.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index dc987079e0f..aa31b30e72d 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1203,8 +1203,7 @@ Thanks to the SensioFrameworkExtraBundle, you can also secure your controller us // ... } -For more information, see the -:doc:`FrameworkExtraBundle documentation `. +For more information, see the `FrameworkExtraBundle documentation`_. Securing other Services ~~~~~~~~~~~~~~~~~~~~~~~ @@ -2304,6 +2303,7 @@ Learn more from the Cookbook * :doc:`/cookbook/security/remember_me` * :doc:`How to Restrict Firewalls to a Specific Request ` +.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/master/bundles/SensioFrameworkExtraBundle/annotations/security.html .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle .. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php .. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack From 96a12f0ac2a87b29a2bc6191c947aa3cdb597c78 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 11 Nov 2014 14:53:10 +0100 Subject: [PATCH 374/835] Reverted merge --- book/security.rst | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/book/security.rst b/book/security.rst index aa31b30e72d..a0fea2713b7 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1203,7 +1203,8 @@ Thanks to the SensioFrameworkExtraBundle, you can also secure your controller us // ... } -For more information, see the `FrameworkExtraBundle documentation`_. +For more information, see the +`FrameworkExtraBundle documentation `_. Securing other Services ~~~~~~~~~~~~~~~~~~~~~~~ @@ -1566,23 +1567,36 @@ is available by calling the PHP function :phpfunction:`hash_algos`. Determining the Hashed Password ............................... +.. versionadded:: 2.6 + The ``security.password_encoder`` service was introduced in Symfony 2.6. + If you're storing users in the database and you have some sort of registration form for users, you'll need to be able to determine the hashed password so that you can set it on your user before inserting it. No matter what algorithm you configure for your user object, the hashed password can always be determined in the following way from a controller:: - $factory = $this->get('security.encoder_factory'); $user = new Acme\UserBundle\Entity\User(); + $plainPassword = 'ryanpass'; + $encoded = $this->container->get('security.password_encoder') + ->encodePassword($user, $plainPassword); - $encoder = $factory->getEncoder($user); - $password = $encoder->encodePassword('ryanpass', $user->getSalt()); - $user->setPassword($password); + $user->setPassword($encoded); In order for this to work, just make sure that you have the encoder for your user class (e.g. ``Acme\UserBundle\Entity\User``) configured under the ``encoders`` key in ``app/config/security.yml``. +.. sidebar:: Get the User Encoder + + In some cases, you need a specific encoder for a given user (e.g. ``Acme\UserBundle\Entity\User``). + You can use the ``EncoderFactory`` to get this encoder:: + + $factory = $this->get('security.encoder_factory'); + $user = new Acme\UserBundle\Entity\User(); + + $encoder = $factory->getEncoder($user); + .. caution:: When you allow a user to submit a plaintext password (e.g. registration @@ -1590,6 +1604,20 @@ key in ``app/config/security.yml``. that the password is 4096 characters or less. Read more details in :ref:`How to implement a simple Registration Form `. +Validating a Plaintext Password +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sometimes you want to check if a plain password is valid for a given user:: + + // a user instance of some class which implements Symfony\Component\Security\Core\User\UserInterface + $user = ...; + + // the password that should be checked + $plainPassword = ...; + + $isValidPassword = $this->container->get('security.password_encoder') + ->isPasswordValid($user, $plainPassword); + Retrieving the User Object ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2303,7 +2331,6 @@ Learn more from the Cookbook * :doc:`/cookbook/security/remember_me` * :doc:`How to Restrict Firewalls to a Specific Request ` -.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/master/bundles/SensioFrameworkExtraBundle/annotations/security.html .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle .. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php .. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack From eb48e4dbc17a3386d5e5a4bce2f5f73813067774 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 11 Nov 2014 14:53:44 +0100 Subject: [PATCH 375/835] Redo URL fix --- book/security.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index a0fea2713b7..f9d75d6f913 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1203,8 +1203,7 @@ Thanks to the SensioFrameworkExtraBundle, you can also secure your controller us // ... } -For more information, see the -`FrameworkExtraBundle documentation `_. +For more information, see the `FrameworkExtraBundle documentation`_. Securing other Services ~~~~~~~~~~~~~~~~~~~~~~~ @@ -2331,6 +2330,7 @@ Learn more from the Cookbook * :doc:`/cookbook/security/remember_me` * :doc:`How to Restrict Firewalls to a Specific Request ` +.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/master/bundles/SensioFrameworkExtraBundle/annotations/security.html .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle .. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php .. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack From 9f7fda42b5e4021cbad14e7a4adcf52c152167d8 Mon Sep 17 00:00:00 2001 From: Nitaco Date: Tue, 11 Nov 2014 08:34:40 +0100 Subject: [PATCH 376/835] Updated first code-block:: bash The command for executing an console command was wrong. The php command was missing. It will help further readers. --- cookbook/console/console_command.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index ccd5065d769..4a2ea5cafb2 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -59,7 +59,7 @@ This command will now automatically be available to run: .. code-block:: bash - $ app/console demo:greet Fabien + $ php app/console demo:greet Fabien Getting Services from the Service Container ------------------------------------------- From 27abf4e4da26a06ef996a065bd20c834d64f5612 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 11 Nov 2014 16:12:22 +0100 Subject: [PATCH 377/835] Added the release dates for the upcoming Symfony 3 versions --- contributing/community/releases.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/contributing/community/releases.rst b/contributing/community/releases.rst index bf8b2eeed30..9ec7f1617f2 100644 --- a/contributing/community/releases.rst +++ b/contributing/community/releases.rst @@ -96,12 +96,16 @@ Version Feature Freeze Release End of Maintenance End of Life 2.4 09/2013 11/2013 09/2014 (10 months [1]_) 01/2015 2.5 02/2014 05/2014 01/2015 (8 months) 07/2015 2.6 09/2014 11/2014 07/2015 (8 months) 01/2016 -**2.7** 02/2015 05/2015 05/2018 (36 months) 05/2019 -2.8 09/2015 11/2015 07/2016 (8 months) 01/2017 +**2.7** 02/2015 05/2015 05/2018 (36 months [2]_) 05/2019 +3.0 09/2015 11/2015 07/2016 (8 months) 01/2017 +3.1 02/2016 05/2016 01/2017 (8 months) 07/2017 +3.2 09/2016 11/2016 07/2017 (8 months) 01/2018 +**3.3** 02/2017 05/2017 05/2020 (36 months) 05/2021 ... ... ... ... ... ======= ============== ======= ======================== =========== .. [1] Symfony 2.4 maintenance has been `extended to September 2014`_. +.. [2] Symfony 2.7 is the last version of the Symfony 2.x branch. .. tip:: From 6a9162bd30d6ce7358e116313ea3a654047b62d6 Mon Sep 17 00:00:00 2001 From: Alex Salguero Date: Tue, 11 Nov 2014 16:21:52 +0000 Subject: [PATCH 378/835] Removed unnecessary use statement in code example in the debugging cookbook --- cookbook/debugging.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/cookbook/debugging.rst b/cookbook/debugging.rst index 372fb707931..98fae25bad8 100644 --- a/cookbook/debugging.rst +++ b/cookbook/debugging.rst @@ -47,8 +47,6 @@ below:: $loader = require_once __DIR__.'/../app/autoload.php'; require_once __DIR__.'/../app/AppKernel.php'; - use Symfony\Component\HttpFoundation\Request; - $kernel = new AppKernel('dev', true); // $kernel->loadClassCache(); $request = Request::createFromGlobals(); From caf927fa69e7acffedd10d24f2147e0bbd915b07 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 11 Nov 2014 18:48:56 +0100 Subject: [PATCH 379/835] tweaks to #4427 Applied tweaks suggested by @javiereguiluz. --- book/routing.rst | 12 ++++++------ book/templating.rst | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/book/routing.rst b/book/routing.rst index cab6efb7953..5dccce3d777 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -410,7 +410,7 @@ entries? Update the route to have a new ``{page}`` placeholder: .. code-block:: php-annotations // src/AppBundle/Controller/BlogController.php - + // ... /** @@ -470,7 +470,7 @@ This is done by including it in the ``defaults`` collection: .. code-block:: php-annotations // src/AppBundle/Controller/BlogController.php - + // ... /** @@ -555,7 +555,7 @@ Take a quick look at the routes that have been created so far: .. code-block:: php-annotations // src/AppBundle/Controller/BlogController.php - + // ... class BlogController extends Controller { @@ -738,7 +738,7 @@ URL: .. code-block:: php-annotations // src/AppBundle/Controller/MainController.php - + // ... class MainController extends Controller { @@ -1283,8 +1283,8 @@ suppose you want to prefix all routes in the AppBundle with ``/site`` (e.g. return $collection; -The string ``/site`` will now be prepended to the path of each route loaded -from the new routing resource. +The path of each route being loaded from the new routing resource will now +be prefixed with the string ``/site``. Adding a Host Requirement to Imported Routes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/book/templating.rst b/book/templating.rst index 727b30aa8a6..9ab93b0107c 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -387,7 +387,7 @@ By default, templates can live in two different locations: third party bundle templates (see :ref:`overriding-bundle-templates`); * ``path/to/bundle/Resources/views/``: Each third party bundle houses its - templates in its ``Resources/views`` directory (and subdirectories). When you + templates in its ``Resources/views/`` directory (and subdirectories). When you plan to share your bundle, you should put the templates in the bundle instead of the ``app/`` directory. @@ -435,8 +435,8 @@ directory. This gives the power to override templates from any vendor bundle. .. tip:: - Hopefully the template naming syntax looks familiar - it's similair to the - naming convention used to refer to :ref:`controller-string-syntax`. + Hopefully the template naming syntax looks familiar - it's similar to + the naming convention used to refer to :ref:`controller-string-syntax`. Template Suffix ~~~~~~~~~~~~~~~ @@ -1353,7 +1353,7 @@ covered: {% endblock %} Notice that this template extends the section template (``Blog/layout.html.twig``) -which in-turn extends the base application layout (``base.html.twig``). This is +which in turn extends the base application layout (``base.html.twig``). This is the common three-level inheritance model. When building your application, you may choose to follow this method or simply @@ -1466,7 +1466,7 @@ Debugging When using PHP, you can use :phpfunction:`var_dump` if you need to quickly find the value of a variable passed. This is useful, for example, inside your -controller. The same can be achieved when using Twig thanks to the debug +controller. The same can be achieved when using Twig thanks to the Debug extension. Template parameters can then be dumped using the ``dump`` function: From 28137ab8070e25a213d7e0f29ad0b62385596fe6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 11 Nov 2014 19:00:50 +0100 Subject: [PATCH 380/835] moved inlined URL to the bottom of the file --- book/http_cache.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/http_cache.rst b/book/http_cache.rst index 1f0aeef9166..46e80ed4e73 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -763,8 +763,7 @@ at some interval (the expiration) to verify that the content is still valid. .. tip:: You can also define HTTP caching headers for expiration and validation by using - annotations. See the - `FrameworkExtraBundle documentation `_. + annotations. See the `FrameworkExtraBundle documentation`_. .. index:: pair: Cache; Configuration @@ -1127,4 +1126,5 @@ Learn more from the Cookbook .. _`HTTP Bis`: http://tools.ietf.org/wg/httpbis/ .. _`P4 - Conditional Requests`: http://tools.ietf.org/html/draft-ietf-httpbis-p4-conditional .. _`P6 - Caching: Browser and intermediary caches`: http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache +.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/cache.html .. _`ESI`: http://www.w3.org/TR/esi-lang From 733de39bc83bf3384bea6ff9396a7f0f0c1e37b4 Mon Sep 17 00:00:00 2001 From: Geert De Deckere Date: Tue, 11 Nov 2014 22:38:50 +0100 Subject: [PATCH 381/835] Added cache_busting to default asset config --- reference/configuration/assetic.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reference/configuration/assetic.rst b/reference/configuration/assetic.rst index f85346394a8..27c47aa6540 100644 --- a/reference/configuration/assetic.rst +++ b/reference/configuration/assetic.rst @@ -48,6 +48,9 @@ Full default Configuration # An array of named filters (e.g. some_filter, some_other_filter) some_filter: [] + workers: + cache_busting: + enabled: false twig: functions: # An array of named functions (e.g. some_function, some_other_function) From 420d411f102517184304ef48c25fba9f0c86da2b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 7 Nov 2014 16:54:49 +0100 Subject: [PATCH 382/835] tweaks to the Twig reference --- reference/twig_reference.rst | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 64a63f2064c..d9ac5c386a8 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -7,15 +7,19 @@ Symfony Twig Extensions ======================= Twig is the default template engine for Symfony. By itself, it already contains -a lot of built-in functions, filters, tags and tests (`http://twig.sensiolabs.org/documentation`_ -then scroll to the bottom). +a lot of built-in functions, filters, tags and tests (learn more about them +from the the `Twig Reference`_). -Symfony adds more custom extension on top of Twig to integrate some components -into the Twig templates. Below is information about all the custom functions, -filters, tags and tests that are added when using the Symfony Core Framework. +Symfony adds more custom extensions on top of Twig to integrate some components +into the Twig templates. You can find more information about the custom +:ref:`functions `, :ref:`filters `, +:ref:`tags ` and :ref:`tests ` +that are added when using the Symfony Core Framework. There may also be tags in bundles you use that aren't listed here. +.. _reference-twig-functions: + Functions --------- @@ -40,7 +44,7 @@ render Renders the fragment for the given controller (using the `controller`_ function) or URI. For more information, see :ref:`templating-embedding-controller`. -The render strategy can be specified in the ``strategy`` key of the options. +The render strategy can be specified in the ``strategy`` key of the options. .. tip:: @@ -65,7 +69,7 @@ Generates an ESI tag when possible or falls back to the behaviour of .. tip:: The URI can be generated by other functions, like `path`_ and `url`_. - + .. tip:: The ``render_esi()`` function is an example of the shortcut functions @@ -354,6 +358,8 @@ Returns the absolute URL (with scheme and host) for the given route. If ``schemeRelative`` is enabled, it'll create a scheme-relative URL. More information in :ref:`book-templating-pages`. +.. _reference-twig-filters: + Filters ------- @@ -554,6 +560,8 @@ file_link Generates a link to the provided file (and optionally line number) using a preconfigured scheme. +.. _reference-twig-tags: + Tags ---- @@ -620,6 +628,8 @@ trans_default_domain This will set the default domain in the current template. +.. _reference-twig-tests: + Tests ----- @@ -669,5 +679,5 @@ Those bundles can have other Twig extensions: ``{% image %}`` tags. You can read more about them in :doc:`the Assetic Documentation `. +.. _`Twig Reference`: http://twig.sensiolabs.org/documentation#reference .. _`the official Twig Extensions documentation`: http://twig.sensiolabs.org/doc/extensions/index.html -.. _`http://twig.sensiolabs.org/documentation`: http://twig.sensiolabs.org/documentation From c1eb0c57d15a6252e4168efa345eca294f0f86a1 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 11 Nov 2014 18:26:50 +0100 Subject: [PATCH 383/835] refer to the VarDumper component for dump() --- book/templating.rst | 31 +++++++++++++++++++++----- components/var_dumper/introduction.rst | 2 ++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index 1195b1244ab..a9e23ef0045 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -1487,12 +1487,33 @@ in a JavaScript string, use the ``js`` context: Debugging --------- -When using PHP, you can use :phpfunction:`var_dump` if you need to quickly find -the value of a variable passed. This is useful, for example, inside your -controller. The same can be achieved when using Twig thanks to the debug -extension. +When using PHP, you can use the +:ref:`dump() function from the VarDumper component ` +if you need to quickly find the value of a variable passed. This is useful, +for example, inside your controller:: -Template parameters can then be dumped using the ``dump`` function: + // src/AppBundle/Controller/ArticleController.php + namespace AppBundle\Controller; + + // ... + + class ArticleController extends Controller + { + public function recentListAction() + { + $articles = ...; + dump($articles); + + // ... + } + } + +.. note:: + + The output of the ``dump()`` function is then rendered in the web developer + toolbar. + +The same mechanism can be used in Twig templates thanks to ``dump`` function: .. code-block:: html+jinja diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst index 6f43b67ef3d..8f265755c22 100644 --- a/components/var_dumper/introduction.rst +++ b/components/var_dumper/introduction.rst @@ -20,6 +20,8 @@ You can install the component in 2 different ways: * :doc:`Install it via Composer ` (``symfony/var-dumper`` on `Packagist`_); * Use the official Git repository (https://github.com/symfony/var-dumper). +.. _components-var-dumper-dump: + The dump() Function ------------------- From f38bbef11312f65a364554a9f693dbc62e564a84 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 12 Nov 2014 20:52:33 +0100 Subject: [PATCH 384/835] Fixed make file --- make.bat | 416 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 263 insertions(+), 153 deletions(-) diff --git a/make.bat b/make.bat index a37807af545..cfda326358d 100644 --- a/make.bat +++ b/make.bat @@ -1,153 +1,263 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Symfony.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Symfony.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/Symfony" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Symfony" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Symfony.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Symfony.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end From baf4b9b37f6506327a78ca0bb3a15250ce2c352e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 25 Oct 2014 19:08:50 +0200 Subject: [PATCH 385/835] Revamped the Quick Start tutorial --- quick_tour/the_architecture.rst | 150 +++++------ quick_tour/the_big_picture.rst | 430 ++++++++++++++------------------ quick_tour/the_controller.rst | 306 +++++++++++++++++------ quick_tour/the_view.rst | 186 +++++++------- 4 files changed, 562 insertions(+), 510 deletions(-) diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst index 927f4512e60..9667fee3d70 100644 --- a/quick_tour/the_architecture.rst +++ b/quick_tour/the_architecture.rst @@ -13,17 +13,17 @@ Understanding the Directory Structure The directory structure of a Symfony :term:`application` is rather flexible, but the recommended structure is as follows: -* ``app/``: the application configuration; -* ``src/``: the project's PHP code; +* ``app/``: the application configuration and templates; +* ``src/``: the project's PHP code; * ``vendor/``: the third-party dependencies; -* ``web/``: the web root directory. +* ``web/``: the web root directory. The ``web/`` Directory ~~~~~~~~~~~~~~~~~~~~~~ The web root directory is the home of all public and static files like images, stylesheets, and JavaScript files. It is also where each :term:`front controller` -lives:: +lives, such as the production controller shown here:: // web/app.php require_once __DIR__.'/../app/bootstrap.php.cache'; @@ -33,7 +33,9 @@ lives:: $kernel = new AppKernel('prod', false); $kernel->loadClassCache(); - $kernel->handle(Request::createFromGlobals())->send(); + $request = Request::createFromGlobals(); + $response = $kernel->handle($request); + $response->send(); The controller first bootstraps the application using a kernel class (``AppKernel`` in this case). Then, it creates the ``Request`` object using the PHP's global @@ -51,8 +53,7 @@ configuration and as such, it is stored in the ``app/`` directory. This class must implement two methods: * ``registerBundles()`` must return an array of all bundles needed to run the - application; - + application, as explained in the next section; * ``registerContainerConfiguration()`` loads the application configuration (more on this later). @@ -73,12 +74,21 @@ A bundle is kind of like a plugin in other software. So why is it called a Symfony, from the core framework features to the code you write for your application. +All the code you write for your application is organized in bundles. In Symfony +speak, a bundle is a structured set of files (PHP files, stylesheets, JavaScripts, +images, ...) that implements a single feature (a blog, a forum, ...) and which +can be easily shared with other developers. + Bundles are first-class citizens in Symfony. This gives you the flexibility to use pre-built features packaged in third-party bundles or to distribute your own bundles. It makes it easy to pick and choose which features to enable in your application and optimize them the way you want. And at the end of the day, your application code is just as *important* as the core framework itself. +Symfony already includes an ``AppBundle`` that you may use to start developing +your application. Then, if you need to split the application into reusable +components, you can create your own bundles. + Registering a Bundle ~~~~~~~~~~~~~~~~~~~~ @@ -98,10 +108,10 @@ a single ``Bundle`` class that describes it:: new Symfony\Bundle\DoctrineBundle\DoctrineBundle(), new Symfony\Bundle\AsseticBundle\AsseticBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), + new AppBundle\AppBundle(); ); if (in_array($this->getEnvironment(), array('dev', 'test'))) { - $bundles[] = new Acme\DemoBundle\AcmeDemoBundle(); $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); @@ -110,16 +120,15 @@ a single ``Bundle`` class that describes it:: return $bundles; } -In addition to the AcmeDemoBundle that was already talked about, notice -that the kernel also enables other bundles such as the FrameworkBundle, -DoctrineBundle, SwiftmailerBundle and AsseticBundle bundle. They are all part -of the core framework. +In addition to the AppBundle that was already talked about, notice that the +kernel also enables other bundles such as the FrameworkBundle, DoctrineBundle, +SwiftmailerBundle and AsseticBundle bundle. They are all part of the core framework. Configuring a Bundle ~~~~~~~~~~~~~~~~~~~~ Each bundle can be customized via configuration files written in YAML, XML, or -PHP. Have a look at the default Symfony configuration: +PHP. Have a look at this sample of the default Symfony configuration: .. code-block:: yaml @@ -127,6 +136,7 @@ PHP. Have a look at the default Symfony configuration: imports: - { resource: parameters.yml } - { resource: security.yml } + - { resource: services.yml } framework: #esi: ~ @@ -138,7 +148,7 @@ PHP. Have a look at the default Symfony configuration: form: true csrf_protection: true validation: { enable_annotations: true } - templating: { engines: ['twig'] } #assets_version: SomeVersionScheme + templating: { engines: ['twig'] } default_locale: "%locale%" trusted_proxies: ~ session: ~ @@ -148,34 +158,6 @@ PHP. Have a look at the default Symfony configuration: debug: "%kernel.debug%" strict_variables: "%kernel.debug%" - # Assetic Configuration - assetic: - debug: "%kernel.debug%" - use_controller: false - bundles: [ ] - #java: /usr/bin/java - filters: - cssrewrite: ~ - #closure: - # jar: "%kernel.root_dir%/Resources/java/compiler.jar" - #yui_css: - # jar: "%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar" - - # Doctrine Configuration - doctrine: - dbal: - driver: "%database_driver%" - host: "%database_host%" - port: "%database_port%" - dbname: "%database_name%" - user: "%database_user%" - password: "%database_password%" - charset: UTF8 - - orm: - auto_generate_proxy_classes: "%kernel.debug%" - auto_mapping: true - # Swift Mailer Configuration swiftmailer: transport: "%mailer_transport%" @@ -184,9 +166,11 @@ PHP. Have a look at the default Symfony configuration: password: "%mailer_password%" spool: { type: memory } -Each first level entry like ``framework``, ``twig`` or ``doctrine`` defines the -configuration for a specific bundle. For example, ``framework`` configures the -FrameworkBundle while ``swiftmailer`` configures the SwiftmailerBundle. + # ... + +Each first level entry like ``framework``, ``twig`` or ``swiftmailer`` defines +the configuration for a specific bundle. For example, ``framework`` configures +the FrameworkBundle while ``swiftmailer`` configures the SwiftmailerBundle. Each :term:`environment` can override the default configuration by providing a specific configuration file. For example, the ``dev`` environment loads the @@ -207,18 +191,7 @@ and then modifies it to add some debugging tools: toolbar: true intercept_redirects: false - monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug - firephp: - type: firephp - level: info - - assetic: - use_controller: true + # ... Extending a Bundle ~~~~~~~~~~~~~~~~~~ @@ -226,8 +199,6 @@ Extending a Bundle In addition to being a nice way to organize and configure your code, a bundle can extend another bundle. Bundle inheritance allows you to override any existing bundle in order to customize its controllers, templates, or any of its files. -This is where the logical names (e.g. ``@AcmeDemoBundle/Controller/SecuredController.php``) -come in handy: they abstract where the resource is actually stored. Logical File Names .................. @@ -235,36 +206,27 @@ Logical File Names When you want to reference a file from a bundle, use this notation: ``@BUNDLE_NAME/path/to/file``; Symfony will resolve ``@BUNDLE_NAME`` to the real path to the bundle. For instance, the logical path -``@AcmeDemoBundle/Controller/DemoController.php`` would be converted to -``src/Acme/DemoBundle/Controller/DemoController.php``, because Symfony knows -the location of the AcmeDemoBundle. +``@AppBundle/Controller/DefaultController.php`` would be converted to +``src/AppBundle/Controller/DefaultController.php``, because Symfony knows +the location of the AppBundle. Logical Controller Names ........................ -For controllers, you need to reference method names using the format +For controllers, you need to reference action using the format ``BUNDLE_NAME:CONTROLLER_NAME:ACTION_NAME``. For instance, -``AcmeDemoBundle:Welcome:index`` maps to the ``indexAction`` method from the -``Acme\DemoBundle\Controller\WelcomeController`` class. - -Logical Template Names -...................... - -For templates, the logical name ``AcmeDemoBundle:Welcome:index.html.twig`` is -converted to the file path ``src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig``. -Templates become even more interesting when you realize they don't need to be -stored on the filesystem. You can easily store them in a database table for -instance. +``AppBundle:Default:index`` maps to the ``indexAction`` method from the +``AppBundle\Controller\DefaultController`` class. Extending Bundles ................. -If you follow these conventions, then you can use :doc:`bundle inheritance` -to "override" files, controllers or templates. For example, you can create -a bundle - AcmeNewBundle - and specify that it overrides AcmeDemoBundle. -When Symfony loads the ``AcmeDemoBundle:Welcome:index`` controller, it will -first look for the ``WelcomeController`` class in AcmeNewBundle and, if -it doesn't exist, then look inside AcmeDemoBundle. This means that one bundle +If you follow these conventions, then you can use :doc:`bundle inheritance ` +to override files, controllers or templates. For example, you can create +a bundle - NewBundle - and specify that it overrides AppBundle. +When Symfony loads the ``AppBundle:Default:index`` controller, it will +first look for the ``DefaultController`` class in NewBundle and, if +it doesn't exist, then look inside AppBundle. This means that one bundle can override almost any part of another bundle! Do you understand now why Symfony is so flexible? Share your bundles between @@ -276,22 +238,28 @@ Using Vendors ------------- Odds are that your application will depend on third-party libraries. Those -should be stored in the ``vendor/`` directory. This directory already contains -the Symfony libraries, the SwiftMailer library, the Doctrine ORM, the Twig -templating system, and some other third party libraries and bundles. +should be stored in the ``vendor/`` directory and managed by Composer. +This directory already contains the Symfony libraries, the SwiftMailer library, +the Doctrine ORM, the Twig templating system, and some other third party +libraries and bundles. Understanding the Cache and Logs -------------------------------- -Symfony is probably one of the fastest full-stack frameworks around. But how -can it be so fast if it parses and interprets tens of YAML and XML files for -each request? The speed is partly due to its cache system. The application +Symfony applications can contain tens of configuration files defined in several +formats (YAML, XML, PHP, etc.) Instead of parsing and combining all those files +for each request, Symfony uses its own cache system. In fact, the application configuration is only parsed for the very first request and then compiled down -to plain PHP code stored in the ``app/cache/`` directory. In the development -environment, Symfony is smart enough to flush the cache when you change a -file. But in the production environment, to speed things up, it is your -responsibility to clear the cache when you update your code or change its -configuration. +to plain PHP code stored in the ``app/cache/`` directory. + +In the development environment, Symfony is smart enough to update the cache when +you change a file. But in the production environment, to speed things up, it is +your responsibility to clear the cache when you update your code or change its +configuration. Execute this command to clear the cache in the ``prod`` environment: + +.. code-block:: bash + + $ php app/console cache:clear --env=prod When developing a web application, things can go wrong in many ways. The log files in the ``app/logs/`` directory tell you everything about the requests diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 212f406e62c..c1a1493e62f 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -1,81 +1,95 @@ The Big Picture =============== -Start using Symfony in 10 minutes! This chapter will walk you through some of -the most important concepts behind Symfony and explain how you can get started -quickly by showing you a simple project in action. +Start using Symfony in 10 minutes! This chapter will walk you through the most +important concepts behind Symfony and explain how you can get started quickly +by showing you a simple project in action. If you've used a web framework before, you should feel right at home with Symfony. If not, welcome to a whole new way of developing web applications. +The only technical requisite to follow this tutorial is to have **PHP 5.4 or higher +installed on your computer**. If you use a packaged PHP solution such as WAMP, +XAMP or MAMP, check out that they are using PHP 5.4 or a more recent version. +You can also execute the following command in your terminal or command console +to display the installed PHP version: + +.. code-block:: bash + + php --version + .. _installing-symfony2: Installing Symfony ------------------ -First, check that the PHP version installed on your computer meets the Symfony -requirements: 5.3.3 or higher. Then, open a console and execute the following -command to install the latest version of Symfony in the ``myproject/`` -directory: - -.. code-block:: bash +In the past, Symfony had to be installed manually for each new project. In order +to simplify this set up, a new **Symfony Installer** was introduced recently. +This means that the very first time you use Symfony on a computer, you have to +install the Symfony Installer. - $ composer create-project symfony/framework-standard-edition myproject/ '~2.3' +On **Linux** and **Mac OS X** systems, execute the following console commands: -.. tip:: +.. code-block:: bash - Add the ``-vvv`` flag to see everything that Composer is doing - this is - especially useful on a slow connection where it may seem that nothing is - happening. + $ curl -sS https://symfony.com/installer | php + $ sudo mv symfony.phar /usr/local/bin/symfony .. note:: - `Composer`_ is the package manager used by modern PHP applications and the - only recommended way to install Symfony. To install Composer on your - Linux or Mac system, execute the following commands: + If your system doesn't have cURL installed, execute instead the following + command: .. code-block:: bash - $ curl -sS https://getcomposer.org/installer | php - $ sudo mv composer.phar /usr/local/bin/composer - - To install Composer on a Windows system, download the `executable installer`_. + $ php -r "readfile('https://symfony.com/installer');" | php -Beware that the first time you install Symfony, it may take a few minutes to -download all its components. At the end of the installation process, the -installer will ask you to provide some configuration options for the Symfony -project. For this first project you can safely ignore this configuration by -pressing the ```` key repeatedly. +After installing the Symfony installer, you'll have to open a new console window +to be able to execute the new ``symfony`` command: -.. _running-symfony2: +.. code-block:: bash -Running Symfony ---------------- + $ symfony -Before running Symfony for the first time, execute the following command to -make sure that your system meets all the technical requirements: +On **Windows** systems, download the ``symfony.phar`` file from ........... and +save it into the directory where you create the Symfony projects. Then you can +execute the Symfony installer right away with this command: .. code-block:: bash - $ cd myproject/ - $ php app/check.php + c:\> php symfony.phar + +Creating Your First Symfony Project +----------------------------------- -Fix any error reported by the command and then use the PHP built-in web server -to run Symfony: +Once the Symfony Installer is set up, use the ``new`` command to create new +Symfony projects. Let's create a new project called ``myproject``: .. code-block:: bash - $ php app/console server:run + # Linux and Mac OS X + $ symfony new myproject + + # Windows + c:\> php symfony.phar new myproject + +This command downloads the latest Symfony stable version and creates an empty +project in the ``myproject/`` directory so you can start developing your +application right away. + +.. _running-symfony2: + +Running Symfony +--------------- -.. seealso:: +This tutorial leverages the internal web server provided by PHP to run Symfony +applications. Therefore, running a Symfony application is a matter of browsing +the project directory and executing this command: - Read more about the internal server :doc:`in the cookbook `. +.. code-block:: bash -If you get the error `There are no commands defined in the "server" namespace.`, -then you are probably using PHP 5.3. That's ok! But the built-in web server is -only available for PHP 5.4.0 or higher. If you have an older version of PHP or -if you prefer a traditional web server such as Apache or Nginx, read the -:doc:`/cookbook/configuration/web_server_configuration` article. + $ cd myproject/ + $ php app/console server:start Open your browser and access the ``http://localhost:8000`` URL to see the Welcome page of Symfony: @@ -84,235 +98,169 @@ Welcome page of Symfony: :align: center :alt: Symfony Welcome Page -Understanding the Fundamentals ------------------------------- - -One of the main goals of a framework is to keep your code organized and to allow -your application to evolve easily over time by avoiding the mixing of database -calls, HTML tags and business logic in the same script. To achieve this goal -with Symfony, you'll first need to learn a few fundamental concepts and terms. - -Symfony comes with some sample code that you can use to learn more about its -main concepts. Go to the following URL to be greeted by Symfony (replace -*Fabien* with your first name): - -.. code-block:: text - - http://localhost:8000/app_dev.php/demo/hello/Fabien - -.. image:: /images/quick_tour/hello_fabien.png - :align: center +Congratulations! Your first Symfony project is up and running! .. note:: - Instead of the greeting page, you may see a blank page or an error page. + Instead of the welcome page, you may see a blank page or an error page. This is caused by a directory permission misconfiguration. There are several possible solutions depending on your operating system. All of them are explained in the :ref:`Setting up Permissions ` section of the official book. -What's going on here? Have a look at each part of the URL: - -* ``app_dev.php``: This is a :term:`front controller`. It is the unique entry - point of the application and it responds to all user requests; - -* ``/demo/hello/Fabien``: This is the *virtual path* to the resource the user - wants to access. +When you are finished working on your Symfony application, you can stop the +server with the ``server:stop`` command: -Your responsibility as a developer is to write the code that maps the user's -*request* (``/demo/hello/Fabien``) to the *resource* associated with it -(the ``Hello Fabien!`` HTML page). - -Routing -~~~~~~~ - -Symfony routes the request to the code that handles it by matching the -requested URL (i.e. the virtual path) against some configured paths. The demo -paths are defined in the ``app/config/routing_dev.yml`` configuration file: - -.. code-block:: yaml - - # app/config/routing_dev.yml - # ... - - # AcmeDemoBundle routes (to be removed) - _acme_demo: - resource: "@AcmeDemoBundle/Resources/config/routing.yml" - -This imports a ``routing.yml`` file that lives inside the AcmeDemoBundle: - -.. code-block:: yaml - - # src/Acme/DemoBundle/Resources/config/routing.yml - _welcome: - path: / - defaults: { _controller: AcmeDemoBundle:Welcome:index } - - _demo: - resource: "@AcmeDemoBundle/Controller/DemoController.php" - type: annotation - prefix: /demo - - # ... +.. code-block:: bash -The first three lines (after the comment) define the code that is executed -when the user requests the "``/``" resource (i.e. the welcome page you saw -earlier). When requested, the ``AcmeDemoBundle:Welcome:index`` controller -will be executed. In the next section, you'll learn exactly what that means. + $ php app/console server:stop .. tip:: - In addition to YAML files, routes can be configured in XML or PHP files - and can even be embedded in PHP annotations. This flexibility is one of the - main features of Symfony, a framework that never imposes a particular - configuration format on you. + If you prefer a traditional web server such as Apache or Nginx, read the + :doc:`/cookbook/configuration/web_server_configuration` article. + +Understanding the Fundamentals +------------------------------ -Controllers -~~~~~~~~~~~ +One of the main goals of a framework is to keep your code organized and to allow +your application to evolve easily over time by avoiding the mixing of database +calls, HTML tags and other PHP code in the same script. To achieve this goal +with Symfony, you'll first need to learn a few fundamental concepts. -A controller is a PHP function or method that handles incoming *requests* and -returns *responses* (often HTML code). Instead of using the PHP global variables -and functions (like ``$_GET`` or ``header()``) to manage these HTTP messages, -Symfony uses objects: :ref:`Request ` -and :ref:`Response `. The simplest possible -controller might create the response by hand, based on the request:: +When developing a Symfony application, your responsibility as a developer is to +write the code that maps the user's *request* (e.g. ``http://localhost:8000/``) +to the *resource* associated with it (the ``Welcome to Symfony!`` HTML page). - use Symfony\Component\HttpFoundation\Response; +The code to execute is defined in **actions** and **controllers**. The mapping +between user's requests and that code is defined via the **routing** configuration. +And the contents displayed in the browser are usually rendered using **templates**. - $name = $request->get('name'); +When you browsed ``http://localhost:8000/`` earlier, Symfony executed the +controller defined in the ``src/AppBundle/Controller/DefaultController.php`` +file and rendered the ``app/Resources/views/default/index.html.twig`` template. +In the following sections you'll learn in detail the inner workings of Symfony +controllers, routes and templates. - return new Response('Hello '.$name); +Actions and Controllers +~~~~~~~~~~~~~~~~~~~~~~~ -Symfony chooses the controller based on the ``_controller`` value from the -routing configuration: ``AcmeDemoBundle:Welcome:index``. This string is the -controller *logical name*, and it references the ``indexAction`` method from -the ``Acme\DemoBundle\Controller\WelcomeController`` class:: +Open the ``src/AppBundle/Controller/DefaultController.php`` file and you'll see +the following code (for now, don't look at the ``@Route`` configuration because +that will be explained in the next section):: - // src/Acme/DemoBundle/Controller/WelcomeController.php - namespace Acme\DemoBundle\Controller; + namespace AppBundle\Controller; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; - class WelcomeController extends Controller + class DefaultController extends Controller { + /** + * @Route("/", name="homepage") + */ public function indexAction() { - return $this->render('AcmeDemoBundle:Welcome:index.html.twig'); + return $this->render('default/index.html.twig'); } } -.. tip:: - - You could have used the full class and method name - - ``Acme\DemoBundle\Controller\WelcomeController::indexAction`` - for the - ``_controller`` value. But using the logical name is shorter and allows - for more flexibility. +In Symfony applications, **controllers** are PHP classes whose names are suffixed +with the ``Controller`` word. In this example, the controller is called ``Default`` +and the PHP class is called ``DefaultController``. -The ``WelcomeController`` class extends the built-in ``Controller`` class, -which provides useful shortcut methods, like the -:ref:`render()` method that loads and renders -a template (``AcmeDemoBundle:Welcome:index.html.twig``). The returned value -is a ``Response`` object populated with the rendered content. So, if the need -arises, the ``Response`` can be tweaked before it is sent to the browser:: +The methods defined in a controller are called **actions**, they are usually +associated with one URL of the application and their names are suffixed with +``Action``. In this example, the ``Default`` controller has only one action +called ``index`` and defined in the ``indexAction`` method. - public function indexAction() - { - $response = $this->render('AcmeDemoBundle:Welcome:index.txt.twig'); - $response->headers->set('Content-Type', 'text/plain'); - - return $response; - } +Actions are usually very short - around 10-15 lines of code - because they just +call other parts of the application to get or generate the needed information and +then they render a template to show the results to the user. -No matter how you do it, the end goal of your controller is always to return -the ``Response`` object that should be delivered back to the user. This ``Response`` -object can be populated with HTML code, represent a client redirect, or even -return the contents of a JPG image with a ``Content-Type`` header of ``image/jpg``. +In this example, the ``index`` action is practically empty because it doesn't +need to call any other method. The action just renders a template with the +*Welcome to Symfony!* content. -The template name, ``AcmeDemoBundle:Welcome:index.html.twig``, is the template -*logical name* and it references the ``Resources/views/Welcome/index.html.twig`` -file inside the AcmeDemoBundle (located at ``src/Acme/DemoBundle``). -The `Bundles`_ section below will explain why this is useful. +Routing +~~~~~~~ -Now, take a look at the routing configuration again and find the ``_demo`` -key: +Symfony routes each request to the action that handles it by matching the +requested URL against the paths configured by the application. Open again the +``src/AppBundle/Controller/DefaultController.php`` file and take a look at the +three lines of code above the ``indexAction`` method: -.. code-block:: yaml +.. code-block:: php - # src/Acme/DemoBundle/Resources/config/routing.yml - # ... - _demo: - resource: "@AcmeDemoBundle/Controller/DemoController.php" - type: annotation - prefix: /demo + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; -The *logical name* of the file containing the ``_demo`` routes is -``@AcmeDemoBundle/Controller/DemoController.php`` and refers -to the ``src/Acme/DemoBundle/Controller/DemoController.php`` file. In this -file, routes are defined as annotations on action methods:: - - // src/Acme/DemoBundle/Controller/DemoController.php use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; - class DemoController extends Controller + class DefaultController extends Controller { /** - * @Route("/hello/{name}", name="_demo_hello") - * @Template() + * @Route("/", name="homepage") */ - public function helloAction($name) + public function indexAction() { - return array('name' => $name); + return $this->render('default/index.html.twig'); } - - // ... } -The ``@Route()`` annotation creates a new route matching the ``/hello/{name}`` -path to the ``helloAction()`` method. Any string enclosed in curly brackets, -like ``{name}``, is considered a variable that can be directly retrieved as a -method argument with the same name. +These three lines define the routing configuration via the ``@Route()`` annotation. +A **PHP annotation** is a convenient way to configure a method without having to +write regular PHP code. Beware that annotation blocks start with ``/**``, whereas +regular PHP comments start with ``/*``. + +The first value of ``@Route()`` defines the path that will trigger the execution +of the action. This path is configured via a relative URL, so you don't have to +add the host of your application (e.g. ```http://example.com``). In this case, +the value ``/`` refers to the application homepage. The second value of ``@Route()`` +(e.g. ``name="homepage"``) is optional and sets the name of this route. For now +this name is not needed, but later it'll be useful for linking pages. + +Considering all this, the ``@Route("/", name="homepage")`` annotation creates a +new route called ``homepage`` which makes Symfony execute the ``index`` action +of the ``Default`` controller when the users browses the ``/`` URL of the application. + +.. tip:: -If you take a closer look at the controller code, you can see that instead of -rendering a template and returning a ``Response`` object like before, it -just returns an array of parameters. The ``@Template()`` annotation tells -Symfony to render the template for you, passing to it each variable of the -returned array. The name of the template that's rendered follows the name -of the controller. So, in this example, the ``AcmeDemoBundle:Demo:hello.html.twig`` -template is rendered (located at ``src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig``). + In addition to PHP annotations, routes can be configured in YAML, XML or + PHP files. This flexibility is one of the main features of Symfony, a + framework that never imposes a particular configuration format on you. Templates ~~~~~~~~~ -The controller renders the ``src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig`` -template (or ``AcmeDemoBundle:Demo:hello.html.twig`` if you use the logical name): +The only content of the ``index`` action is this PHP instruction: -.. code-block:: jinja +.. code-block:: php - {# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #} - {% extends "AcmeDemoBundle::layout.html.twig" %} + return $this->render('default/index.html.twig'); - {% block title "Hello " ~ name %} +The ``$this->render()`` method is a convenient shortcut to render a template. +Symfony provides some useful shortcuts to any controller extending from the +``Controller`` class. - {% block content %} -

    Hello {{ name }}!

    - {% endblock %} +By default, application templates are stored in the ``app/Resources/views/`` +directory. Therefore, the ``default/index.html.twig`` template corresponds to the +``app/Resources/views/default/index.html.twig``. Open that file and you'll see +the following code: -By default, Symfony uses `Twig`_ as its template engine but you can also use -traditional PHP templates if you choose. The -:doc:`second part of this tutorial` will introduce how -templates work in Symfony. +.. code-block:: html+jinja -Bundles -~~~~~~~ + {# app/Resources/views/default/index.html.twig #} + {% extends 'base.html.twig' %} + + {% block body %} +

    Welcome to Symfony!

    + {% endblock %} -You might have wondered why the :term:`Bundle` word is used in many names you -have seen so far. All the code you write for your application is organized in -bundles. In Symfony speak, a bundle is a structured set of files (PHP files, -stylesheets, JavaScripts, images, ...) that implements a single feature (a -blog, a forum, ...) and which can be easily shared with other developers. As -of now, you have manipulated one bundle, AcmeDemoBundle. You will learn -more about bundles in the :doc:`last part of this tutorial`. +This template is created with `Twig`_, a new template engine created for modern +PHP applications. The :doc:`second part of this tutorial` +will introduce how templates work in Symfony. .. _quick-tour-big-picture-environments: @@ -334,9 +282,13 @@ the request, the query parameters, security details, and database queries: .. image:: /images/quick_tour/profiler.png :align: center -Of course, it would be unwise to have this tool enabled when you deploy your -application, so by default, the profiler is not enabled in the ``prod`` -environment. +This tool provides so much internal information about your application that you +may be worried about your visitors accessing sensible information. Symfony is +aware of this issue and for that reason, it won't display this bar when your +application is running in the production server. + +How does Symfony knows when is your application running locally or in a production +server? Keep reading to discover the concept of **execution environments**. .. _quick-tour-big-picture-environments-intro: @@ -348,6 +300,22 @@ your application. Symfony defines two environments by default: ``dev`` (suited for when developing the application locally) and ``prod`` (optimized for when executing the application on production). +When you visit the ``http://localhost:8000`` URL in your browser, you're executing +your Symfony application in the ``dev`` environment. To visit your application +in the ``prod`` environment, visit the ``http://localhost:8000/app.php`` URL instead. +If you prefer to always show the ``dev`` environment in the URL, you can visit +``http://localhost:8000/app_dev.php`` URL. + +The main difference between environments is that ``dev`` is optimized to provide +lots of information to the developer, which means worse application performance. +Meanwhile, ``prod`` is optimized to get the best performance, which means that +debug information is disabled, as well as the Web Debug Toolbar. + +The other difference between environments is the configuration options used to +execute the application. When you access the ``dev`` environment, Symfony loads +the ``app/config/config_dev.yml`` configuration file. When you access the ``prod`` +environment, Symfony loads ``app/config/config_prod.yml`` file. + Typically, the environments share a large amount of configuration options. For that reason, you put your common configuration in ``config.yml`` and override the specific configuration file for each environment where necessary: @@ -362,29 +330,9 @@ the specific configuration file for each environment where necessary: toolbar: true intercept_redirects: false -In this example, the ``dev`` environment loads the ``config_dev.yml`` configuration -file, which itself imports the common ``config.yml`` file and then modifies it -by enabling the web debug toolbar. - -When you visit the ``app_dev.php`` file in your browser, you're executing -your Symfony application in the ``dev`` environment. To visit your application -in the ``prod`` environment, visit the ``app.php`` file instead. - -The demo routes in our application are only available in the ``dev`` environment. -Therefore, if you try to access the ``http://localhost/app.php/demo/hello/Fabien`` -URL, you'll get a 404 error. - -.. tip:: - - If instead of using PHP's built-in webserver, you use Apache with - ``mod_rewrite`` enabled and take advantage of the ``.htaccess`` file - Symfony provides in ``web/``, you can even omit the ``app.php`` part of the - URL. The default ``.htaccess`` points all requests to the ``app.php`` front - controller: - - .. code-block:: text - - http://localhost/demo/hello/Fabien +In this example, the ``config_dev.yml`` configuration file imports the common +``config.yml`` file and then overrides any existing web debug toolbar configuration +with its own options. For more details on environments, see ":ref:`Environments & Front Controllers `" article. @@ -396,7 +344,7 @@ Congratulations! You've had your first taste of Symfony code. That wasn't so hard, was it? There's a lot more to explore, but you should already see how Symfony makes it really easy to implement web sites better and faster. If you are eager to learn more about Symfony, dive into the next section: -":doc:`The View`". +":doc:`The View `". .. _Composer: https://getcomposer.org/ .. _executable installer: http://getcomposer.org/download diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index decb50b98a7..376a8d25dc9 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -1,8 +1,100 @@ The Controller ============== -Still here after the first two parts? You are already becoming a Symfony -addict! Without further ado, discover what controllers can do for you. +Still here after the first two parts? You are already becoming a Symfony fan! +Without further ado, discover what controllers can do for you. + +Returning Raw Responses +----------------------- + +Symfony defines itself as a Request-Response framework. When the user makes a +request to your application, Symfony creates a ``Request`` object to encapsulate +all the information related to that request. Similarly, the result of executing +any action of any controller is the creation of a ``Response`` object which +Symfony uses to generate the HTML content returned to the user. + +So far, all the actions shown in this tutorial used the ``$this->render()`` +shortcut to return a rendered response as result. If case you need it, you can +also create a raw ``Response`` object to return any text content:: + + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + use Symfony\Component\HttpFoundation\Response; + + class DefaultController extends Controller + { + /** + * @Route("/", name="homepage") + */ + public function indexAction() + { + return new Response('Welcome to Symfony!'); + } + } + +Route Parameters +---------------- + +Most of the time, the URLs of applications include variable parts on them. If you +are creating for example a blog application, the URL to display the articles should +include their title or some other unique identifier to let the application know +the exact article to display. + +In Symfony applications, the variable parts of the routes are enclosed in curly +braces (e.g. ``/blog/read/{article_title}/``). Each variable part is assigned a +unique name that can be used later in the controller to retrieve each value. + +Let's create a new action with route variables to show this feature in action. +Open the ``src/AppBundle/Controller/DefaultController.php`` file and add a new +method called ``helloAction`` with the following content:: + + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + + class DefaultController extends Controller + { + // ... + + /** + * @Route("/hello/{name}", name="hello") + */ + public function helloAction($name) + { + return $this->render('default/hello.html.twig', array( + 'name' => $name + )); + } + } + +Open your browser and access the ``http://localhost:8000/hello/fabien`` URL to +see the result of executing this new action. Instead of the action result, you'll +see an error page. As you probably guessed, the cause of this error is that we're +trying to render a template (``default/hello.html.twig``) that doesn't exist yet. + +Create the new ``app/Resources/views/default/hello.html.twig`` template with the +following content: + +.. code-block:: html+jinja + + {# app/Resources/views/default/hello.html.twig #} + {% extends 'base.html.twig' %} + + {% block body %} +

    Hi {{ name }}! Welcome to Symfony!

    + {% endblock %} + +Browse again the ``http://localhost:8000/hello/fabien`` URL and you'll see this +new template rendered with the information passed by the controller. If you +change the last part of the URL (e.g. ``http://localhost:8000/hello/thomas``) +and reload your browser, the page will display a different message. And if you +remove the last part of the URL (e.g. ``http://localhost:8000/hello``), Symfony +will display an error because the route expects a name and you haven't provided it. Using Formats ------------- @@ -10,86 +102,114 @@ Using Formats Nowadays, a web application should be able to deliver more than just HTML pages. From XML for RSS feeds or Web Services, to JSON for Ajax requests, there are plenty of different formats to choose from. Supporting those formats -in Symfony is straightforward. Tweak the route by adding a default value of -``xml`` for the ``_format`` variable:: +in Symfony is straightforward thanks to a special variable called ``_format`` +which stores the format requested by the user. - // src/Acme/DemoBundle/Controller/DemoController.php +Tweak the ``hello`` route by adding a new ``_format`` variable with ``html`` as +its default value:: + + // src/AppBundle/Controller/DefaultController.php use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; // ... /** - * @Route("/hello/{name}", defaults={"_format"="xml"}, name="_demo_hello") - * @Template() + * @Route("/hello/{name}.{_format}", defaults={"_format"="html"}, name="hello") */ - public function helloAction($name) + public function helloAction($name, $_format) { - return array('name' => $name); + return $this->render('default/hello.'.$_format.'.twig', array( + 'name' => $name + )); } -By using the request format (as defined by the special ``_format`` variable), -Symfony automatically selects the right template, here ``hello.xml.twig``: +Obviously, when you support several request formats, you have to provide a +tempalte for each of the supported formats. In this case, you should create a +new ``hello.xml.twig`` template:: .. code-block:: xml+php - + {{ name }} +Now, when you browse to ``http://localhost:8000/hello/fabien``, you'll see the +regular HTML page because ``html`` is the default format. When visiting +``http://localhost:8000/hello/fabien.html`` you'll get again the HTML page, this +time because you explicitely asked for the ``html`` format. Lastly, if you visit +``http://localhost:8000/hello/fabien.xml`` you'll see the new XML template rendered +in your browser. + That's all there is to it. For standard formats, Symfony will also -automatically choose the best ``Content-Type`` header for the response. If -you want to support different formats for a single action, use the ``{_format}`` -placeholder in the route path instead:: +automatically choose the best ``Content-Type`` header for the response. To +restrict the the formats supported by a given action, use the ``requirements`` +option of the ``@Route()`` annotation:: - // src/Acme/DemoBundle/Controller/DemoController.php + // src/AppBundle/Controller/DefaultController.php use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; // ... /** - * @Route( - * "/hello/{name}.{_format}", - * defaults = { "_format" = "html" }, + * @Route("/hello/{name}.{_format}", + * defaults = {"_format"="html"}, * requirements = { "_format" = "html|xml|json" }, - * name = "_demo_hello" + * name = "hello" * ) - * @Template() */ - public function helloAction($name) + public function helloAction($name, $_format) { - return array('name' => $name); + return $this->render('default/hello.'.$_format.'.twig', array( + 'name' => $name + )); } -The controller will now match URLs like ``/demo/hello/Fabien.xml`` or -``/demo/hello/Fabien.json``. - -The ``requirements`` entry defines regular expressions that variables must -match. In this example, if you try to request the ``/demo/hello/Fabien.js`` -resource, you will get a 404 HTTP error, as it does not match the ``_format`` -requirement. +The ``hello`` action will now match URLs like ``/hello/fabien.xml`` or +``/hello/fabien.json``, but it will show a 404 error if you try to get URLs +like ``/hello/fabien.js``, because the value of the ``_format`` variable doesn't +meet its requirements. Redirecting and Forwarding -------------------------- -If you want to redirect the user to another page, use the ``redirect()`` +If you want to redirect the user to another page, use the ``redirectToRoute()`` method:: - return $this->redirect($this->generateUrl('_demo_hello', array('name' => 'Lucas'))); + // src/AppBundle/Controller/DefaultController.php + class DefaultController extends Controller + { + /** + * @Route("/", name="homepage") + */ + public function indexAction() + { + return $this->redirectToRoute('hello', array('name' => 'Fabien')); + } + } -The ``generateUrl()`` is the same method as the ``path()`` function used in the +The ``redirectToRoute()`` is similar to the ``path()`` function used in the templates. It takes the route name and an array of parameters as arguments and returns the associated friendly URL. -You can also internally forward the action to another using the ``forward()`` -method:: +You can also internally forward the action to another action of the same or +different controller using the ``forward()`` method:: - return $this->forward('AcmeDemoBundle:Hello:fancy', array( - 'name' => $name, - 'color' => 'green' - )); + // src/AppBundle/Controller/DefaultController.php + class DefaultController extends Controller + { + /** + * @Route("/", name="homepage") + */ + public function indexAction() + { + return $this->forward('AppBundle:Blog:index', array( + 'name' => $name + ); + } + } Displaying Error Pages ---------------------- @@ -98,34 +218,73 @@ Errors will inevitably happen during the execution of every web application. In the case of ``404`` errors, Symfony includes a handy shortcut that you can use in your controllers:: - throw $this->createNotFoundException(); + // src/AppBundle/Controller/DefaultController.php + class DefaultController extends Controller + { + /** + * @Route("/", name="homepage") + */ + public function indexAction() + { + throw $this->createNotFoundException(); + } + } For ``500`` errors, just throw a regular PHP exception inside the controller and Symfony will transform it into a proper ``500`` error page:: - throw new \Exception('Something went wrong!'); + // src/AppBundle/Controller/DefaultController.php + class DefaultController extends Controller + { + /** + * @Route("/", name="homepage") + */ + public function indexAction() + { + throw new \Exception('Something went horribly wrong!'); + } + } Getting Information from the Request ------------------------------------ -Symfony automatically injects the ``Request`` object when the controller has an -argument that's type hinted with ``Symfony\Component\HttpFoundation\Request``:: +Sometimes your controllers need to access the information related to the user +request, such as his/her preferred language, IP address or the URL query parameters. +To get access to this information, add a new argument of type ``Request`` to the +action. The name of this new argument doesn't matter, but it must be preceded +by the ``Request`` type in order to work (don't forget to add the new ``use`` +statement that imports this ``Request`` class):: + + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; - public function indexAction(Request $request) + class DefaultController extends Controller { - $request->isXmlHttpRequest(); // is it an Ajax request? - - $request->getPreferredLanguage(array('en', 'fr')); - - $request->query->get('page'); // get a $_GET parameter - - $request->request->get('page'); // get a $_POST parameter + /** + * @Route("/", name="homepage") + */ + public function indexAction(Request $request) + { + // is it an Ajax request? + $isAjax = $request->isXmlHttpRequest(); + + // what's the preferred language of the user? + $language = $request->getPreferredLanguage(array('en', 'fr')); + + // get the value of a $_GET parameter + $pageName = $request->query->get('page'); + + // get the value of a $_POST parameter + $pageName = $request->request->get('page'); + } } -In a template, you can also access the ``Request`` object via the -``app.request`` variable: +In a template, you can also access the ``Request`` object via the special +``app.request`` variable automatically provided by Symfony: .. code-block:: html+jinja @@ -164,40 +323,21 @@ You can also store "flash messages" that will auto-delete after the next request They are useful when you need to set a success message before redirecting the user to another page (which will then show the message):: - // store a message for the very next request (in a controller) - $session->getFlashBag()->add('notice', 'Congratulations, your action succeeded!'); - -.. code-block:: html+jinja - - {# display the flash message in the template #} -
    {{ app.session.flashbag.get('notice') }}
    - -Caching Resources ------------------ + public function indexAction(Request $request) + { + // ... -As soon as your website starts to generate more traffic, you will want to -avoid generating the same resource again and again. Symfony uses HTTP cache -headers to manage resources cache. For simple caching strategies, use the -convenient ``@Cache()`` annotation:: + // store a message for the very next request + $this->addFlash('notice', 'Congratulations, your action succeeded!'); + } - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; +And you can display the flash message in the template like this: - /** - * @Route("/hello/{name}", name="_demo_hello") - * @Template() - * @Cache(maxage="86400") - */ - public function helloAction($name) - { - return array('name' => $name); - } +.. code-block:: html+jinja -In this example, the resource will be cached for a day (``86400`` seconds). -Resource caching is managed by Symfony itself. But because caching is managed -using standard HTTP cache headers, you can use Varnish or Squid without having -to modify a single line of code in your application. +
    + {{ app.session.flashbag.get('notice') }} +
    Final Thoughts -------------- diff --git a/quick_tour/the_view.rst b/quick_tour/the_view.rst index 470cd51226e..f147df63825 100644 --- a/quick_tour/the_view.rst +++ b/quick_tour/the_view.rst @@ -3,27 +3,29 @@ The View After reading the first part of this tutorial, you have decided that Symfony was worth another 10 minutes. In this second part, you will learn more about -`Twig`_, the fast, flexible, and secure template engine for PHP. Twig makes your -templates more readable and concise; it also makes them more friendly for web -designers. +`Twig`_, the fast, flexible, and secure template engine for PHP applications. +Twig makes your templates more readable and concise; it also makes them more +friendly for web designers. Getting familiar with Twig -------------------------- The official `Twig documentation`_ is the best resource to learn everything -about this new template engine. This section just gives you a quick overview of +about this template engine. This section just gives you a quick overview of its main concepts. A Twig template is a text file that can generate any type of content (HTML, CSS, -JavaScript, XML, CSV, LaTeX, ...). Twig elements are separated from the rest of +JavaScript, XML, CSV, LaTeX, etc.) Twig elements are separated from the rest of the template contents using any of these delimiters: -* ``{{ ... }}``: prints the content of a variable or the result of an expression; +* ``{{ ... }}``: prints the content of a variable or the result of evaluating an + expression; * ``{% ... %}``: controls the logic of the template; it is used for example to execute ``for`` loops and ``if`` statements; -* ``{# ... #}``: allows including comments inside templates. +* ``{# ... #}``: allows including comments inside templates. Contrary to HTML + comments, they aren't included in the rendered template. Below is a minimal template that illustrates a few basics, using two variables ``page_title`` and ``navigation``, which would be passed into the template: @@ -46,34 +48,34 @@ Below is a minimal template that illustrates a few basics, using two variables -To render a template in Symfony, use the ``render`` method from within a controller -and pass the variables needed as an array using the optional second argument:: +To render a template in Symfony, use the ``render`` method from within a controller. +If the template needs variables to generate its contents, pass them as an array +using the second optional second argument:: - $this->render('AcmeDemoBundle:Demo:hello.html.twig', array( - 'name' => $name, + $this->render('default/index.html.twig', array( + 'variable_name' => 'variable_value', )); -Variables passed to a template can be strings, arrays, or even objects. Twig +Variables passed to a template can be strings, arrays or even objects. Twig abstracts the difference between them and lets you access "attributes" of a variable with the dot (``.``) notation. The following code listing shows how to -display the content of a variable depending on the type of the variable passed -by the controller: +display the content of a variable passed by the controller depending on its type: .. code-block:: jinja {# 1. Simple variables #} - {# array('name' => 'Fabien') #} + {# $this->render( ..., array('name' => 'Fabien') ) #} {{ name }} {# 2. Arrays #} - {# array('user' => array('name' => 'Fabien')) #} + {# $this->render( ..., array('user' => array('name' => 'Fabien')) ) #} {{ user.name }} {# alternative syntax for arrays #} {{ user['name'] }} {# 3. Objects #} - {# array('user' => new User('Fabien')) #} + {# $this->render( ..., array('user' => new User('Fabien')) ) #} {{ user.name }} {{ user.getName }} @@ -86,60 +88,63 @@ Decorating Templates More often than not, templates in a project share common elements, like the well-known header and footer. Twig solves this problem elegantly with a concept -called "template inheritance". This feature allows you to build a base "layout" -template that contains all the common elements of your site and defines "blocks" +called "template inheritance". This feature allows you to build a base template +that contains all the common elements of your site and defines "blocks" of contents that child templates can override. -The ``hello.html.twig`` template uses the ``extends`` tag to indicate that it -inherits from the common ``layout.html.twig`` template: +The ``index.html.twig`` template uses the ``extends`` tag to indicate that it +inherits from the ``base.html.twig`` template: .. code-block:: html+jinja - {# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #} - {% extends "AcmeDemoBundle::layout.html.twig" %} + {# app/Resources/views/default/index.html.twig #} + {% extends 'base.html.twig' %} - {% block title "Hello " ~ name %} - - {% block content %} -

    Hello {{ name }}!

    + {% block body %} +

    Welcome to Symfony!

    {% endblock %} -The ``AcmeDemoBundle::layout.html.twig`` notation sounds familiar, doesn't it? -It is the same notation used to reference a regular template. The ``::`` part -simply means that the controller element is empty, so the corresponding file -is directly stored under the ``Resources/views/`` directory of the bundle. - -Now, simplify the ``layout.html.twig`` template: +Open the ``app/Resources/views/base.html.twig`` file that corresponds to the +``base.html.twig`` template and you'll find the following Twig code: -.. code-block:: jinja +.. code-block:: html+jinja - {# src/Acme/DemoBundle/Resources/views/layout.html.twig #} -
    - {% block content %} - {% endblock %} -
    + {# app/Resources/views/base.html.twig #} + + + + + {% block title %}Welcome!{% endblock %} + {% block stylesheets %}{% endblock %} + + + + {% block body %}{% endblock %} + {% block javascripts %}{% endblock %} + + The ``{% block %}`` tags tell the template engine that a child template may -override those portions of the template. In this example, the ``hello.html.twig`` -template overrides the ``content`` block, meaning that the "Hello Fabien" text -is rendered inside the ``
    `` element. +override those portions of the template. In this example, the ``index.html.twig`` +template overrides the ``content`` block, but not the ``title`` block, which will +display the default content defined in the ``base.html.twig`` template. Using Tags, Filters, and Functions ---------------------------------- -One of the best feature of Twig is its extensibility via tags, filters, and +One of the best features of Twig is its extensibility via tags, filters, and functions. Take a look at the following sample template that uses filters extensively to modify the information before displaying it to the user: .. code-block:: jinja -

    {{ article.title|trim|capitalize }}

    +

    {{ article.title|capitalize }}

    -

    {{ article.content|striptags|slice(0, 1024) }}

    +

    {{ article.content|striptags|slice(0, 255) }} ...

    Tags: {{ article.tags|sort|join(", ") }}

    -

    Next article will be published on {{ 'next Monday'|date('M j, Y')}}

    +

    Activate your account before {{ 'next Monday'|date('M j, Y') }}

    Don't forget to check out the official `Twig documentation`_ to learn everything about filters, functions and tags. @@ -150,23 +155,28 @@ Including other Templates The best way to share a snippet of code between several templates is to create a new template fragment that can then be included from other templates. -First, create an ``embedded.html.twig`` template: +Imagine that we want to display ads on some pages of our application. First, +create an ``banner.html.twig`` template: .. code-block:: jinja - {# src/Acme/DemoBundle/Resources/views/Demo/embedded.html.twig #} - Hello {{ name }} + {# app/Resources/views/ads/banner.html.twig #} +
    + ... +
    -And change the ``hello.html.twig`` template to include it: +To display this ad on any page, include the ``banner.html.twig`` template using +the ``include()`` function: -.. code-block:: jinja +.. code-block:: html+jinja + + {# app/Resources/views/default/index.html.twig #} + {% extends 'base.html.twig' %} - {# src/Acme/DemoBundle/Resources/views/Demo/hello.html.twig #} - {% extends "AcmeDemoBundle::layout.html.twig" %} + {% block body %} +

    Welcome to Symfony!

    - {# override the body block from embedded.html.twig #} - {% block content %} - {{ include("AcmeDemoBundle:Demo:embedded.html.twig") }} + {{ include('ads/banner.html.twig') }} {% endblock %} Embedding other Controllers @@ -178,28 +188,28 @@ some variable not available in the main template. Suppose you've created a ``topArticlesAction`` controller method to display the most popular articles of your website. If you want to "render" the result of -that method (e.g. ``HTML``) inside the ``index`` template, use the ``render`` -function: +that method (usually some HTML content) inside the ``index`` template, use the +``render()`` function: .. code-block:: jinja - {# src/Acme/DemoBundle/Resources/views/Demo/index.html.twig #} - {{ render(controller("AcmeDemoBundle:Demo:topArticles", {'num': 10})) }} + {# app/Resources/views/index.html.twig #} + {{ render(controller('AppBundle:Default:topArticles')) }} -Here, the ``AcmeDemoBundle:Demo:topArticles`` string refers to the -``topArticlesAction`` action of the ``Demo`` controller, and the ``num`` -argument is made available to the controller:: +Here, the ``render()`` and ``controller()`` functions use the special +``AppBundle:Default:topArticles`` syntax to refer to the ``topArticlesAction`` +action of the ``Default`` controller (the ``AppBundle`` part will be explained later):: - // src/Acme/DemoBundle/Controller/DemoController.php + // src/AppBundle/Controller/DefaultController.php - class DemoController extends Controller + class DefaultController extends Controller { - public function topArticlesAction($num) + public function topArticlesAction() { - // look for the $num most popular articles in the database + // look for the most popular articles in the database $articles = ...; - return $this->render('AcmeDemoBundle:Demo:topArticles.html.twig', array( + return $this->render('default/top_articles.html.twig', array( 'articles' => $articles, )); } @@ -217,32 +227,16 @@ updated by just changing the configuration: .. code-block:: html+jinja - Greet Thomas! - -The ``path`` function takes the route name and an array of parameters as -arguments. The route name is the key under which routes are defined and the -parameters are the values of the variables defined in the route pattern:: + Return to homepage - // src/Acme/DemoBundle/Controller/DemoController.php - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; - - // ... - - /** - * @Route("/hello/{name}", name="_demo_hello") - * @Template() - */ - public function helloAction($name) - { - return array('name' => $name); - } +The ``path`` function takes the route name as the first argument and optionally +you can pass an array of route parameters as the second argument. .. tip:: The ``url`` function is very similar to the ``path`` function, but generates *absolute* URLs, which is very handy when rendering emails and RSS files: - ``{{ url('_demo_hello', {'name': 'Thomas'}) }}``. + ``Visit our website``. Including Assets: Images, JavaScripts and Stylesheets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -256,18 +250,20 @@ Symfony provides the ``asset`` function to deal with them easily: -The ``asset`` function's main purpose is to make your application more portable. -Thanks to this function, you can move the application root directory anywhere -under your web root directory without changing anything in your template's -code. +The ``asset()`` function looks for the web assets inside the ``web/`` directory. +If you store them in other directory, read ..... this article to learn how to +manage web assets. + +Using the ``asset`` function, your application is more portable. The reason is +that you can move the application root directory anywhere under your web root +directory without changing anything in your template's code. Final Thoughts -------------- Twig is simple yet powerful. Thanks to layouts, blocks, templates and action inclusions, it is very easy to organize your templates in a logical and -extensible way. However, if you're not comfortable with Twig, you can always -use PHP templates inside Symfony without any issues. +extensible way. You have only been working with Symfony for about 20 minutes, but you can already do pretty amazing stuff with it. That's the power of Symfony. Learning @@ -278,5 +274,5 @@ But I'm getting ahead of myself. First, you need to learn more about the control and that's exactly the topic of the :doc:`next part of this tutorial `. Ready for another 10 minutes with Symfony? -.. _Twig: http://twig.sensiolabs.org/ +.. _Twig: http://twig.sensiolabs.org/ .. _Twig documentation: http://twig.sensiolabs.org/documentation From 6105acb457d0dcd582ed2b2e9eb11b825062d438 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 25 Oct 2014 19:38:43 +0200 Subject: [PATCH 386/835] Fixed a RST formatting issue --- quick_tour/the_controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index 376a8d25dc9..21de0aa1770 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -126,7 +126,7 @@ its default value:: Obviously, when you support several request formats, you have to provide a tempalte for each of the supported formats. In this case, you should create a -new ``hello.xml.twig`` template:: +new ``hello.xml.twig`` template: .. code-block:: xml+php From 940924c459aa9630a8ca98c1b72500365b071b58 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 1 Nov 2014 19:48:01 +0100 Subject: [PATCH 387/835] Added a bunch of fixes suggested by @xabbuh --- quick_tour/the_architecture.rst | 6 +++--- quick_tour/the_big_picture.rst | 28 +++++++++++++++------------- quick_tour/the_controller.rst | 17 +++++++++-------- quick_tour/the_view.rst | 12 ++++++------ 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst index 9667fee3d70..b92d7d62f1f 100644 --- a/quick_tour/the_architecture.rst +++ b/quick_tour/the_architecture.rst @@ -122,7 +122,7 @@ a single ``Bundle`` class that describes it:: In addition to the AppBundle that was already talked about, notice that the kernel also enables other bundles such as the FrameworkBundle, DoctrineBundle, -SwiftmailerBundle and AsseticBundle bundle. They are all part of the core framework. +SwiftmailerBundle and AsseticBundle. They are all part of the core framework. Configuring a Bundle ~~~~~~~~~~~~~~~~~~~~ @@ -213,7 +213,7 @@ the location of the AppBundle. Logical Controller Names ........................ -For controllers, you need to reference action using the format +For controllers, you need to reference actions using the format ``BUNDLE_NAME:CONTROLLER_NAME:ACTION_NAME``. For instance, ``AppBundle:Default:index`` maps to the ``indexAction`` method from the ``AppBundle\Controller\DefaultController`` class. @@ -240,7 +240,7 @@ Using Vendors Odds are that your application will depend on third-party libraries. Those should be stored in the ``vendor/`` directory and managed by Composer. This directory already contains the Symfony libraries, the SwiftMailer library, -the Doctrine ORM, the Twig templating system, and some other third party +the Doctrine ORM, the Twig templating system and some other third party libraries and bundles. Understanding the Cache and Logs diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index c1a1493e62f..4390cc0a0d2 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -16,7 +16,7 @@ to display the installed PHP version: .. code-block:: bash - php --version + $ php --version .. _installing-symfony2: @@ -37,12 +37,13 @@ On **Linux** and **Mac OS X** systems, execute the following console commands: .. note:: - If your system doesn't have cURL installed, execute instead the following - command: + If your system doesn't have cURL installed, execute the following + commands instead: .. code-block:: bash $ php -r "readfile('https://symfony.com/installer');" | php + $ sudo mv symfony.phar /usr/local/bin/symfony After installing the Symfony installer, you'll have to open a new console window to be able to execute the new ``symfony`` command: @@ -214,16 +215,17 @@ A **PHP annotation** is a convenient way to configure a method without having to write regular PHP code. Beware that annotation blocks start with ``/**``, whereas regular PHP comments start with ``/*``. -The first value of ``@Route()`` defines the path that will trigger the execution -of the action. This path is configured via a relative URL, so you don't have to -add the host of your application (e.g. ```http://example.com``). In this case, -the value ``/`` refers to the application homepage. The second value of ``@Route()`` -(e.g. ``name="homepage"``) is optional and sets the name of this route. For now -this name is not needed, but later it'll be useful for linking pages. +The first value of ``@Route()`` defines the URL that will trigger the execution +of the action. As you don't have to add the host of your application to the URL +(e.g. ```http://example.com``), these URLs are always relative and they are usually +called *paths*. In this case, the ``/`` path refers to the application homepage. +The second value of ``@Route()`` (e.g. ``name="homepage"``) is optional and sets +the name of this route. For now this name is not needed, but later it'll be useful +for linking pages. Considering all this, the ``@Route("/", name="homepage")`` annotation creates a new route called ``homepage`` which makes Symfony execute the ``index`` action -of the ``Default`` controller when the users browses the ``/`` URL of the application. +of the ``Default`` controller when the user browses the ``/`` path of the application. .. tip:: @@ -259,7 +261,7 @@ the following code: {% endblock %} This template is created with `Twig`_, a new template engine created for modern -PHP applications. The :doc:`second part of this tutorial` +PHP applications. The :doc:`second part of this tutorial ` will introduce how templates work in Symfony. .. _quick-tour-big-picture-environments: @@ -287,8 +289,8 @@ may be worried about your visitors accessing sensible information. Symfony is aware of this issue and for that reason, it won't display this bar when your application is running in the production server. -How does Symfony knows when is your application running locally or in a production -server? Keep reading to discover the concept of **execution environments**. +How does Symfony know whether your application is running locally or on a +production server? Keep reading to discover the concept of **execution environments**. .. _quick-tour-big-picture-environments-intro: diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index 21de0aa1770..0f593f3a356 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -14,7 +14,7 @@ any action of any controller is the creation of a ``Response`` object which Symfony uses to generate the HTML content returned to the user. So far, all the actions shown in this tutorial used the ``$this->render()`` -shortcut to return a rendered response as result. If case you need it, you can +shortcut to return a rendered response as result. In case you need it, you can also create a raw ``Response`` object to return any text content:: // src/AppBundle/Controller/DefaultController.php @@ -125,7 +125,7 @@ its default value:: } Obviously, when you support several request formats, you have to provide a -tempalte for each of the supported formats. In this case, you should create a +template for each of the supported formats. In this case, you should create a new ``hello.xml.twig`` template: .. code-block:: xml+php @@ -144,7 +144,7 @@ in your browser. That's all there is to it. For standard formats, Symfony will also automatically choose the best ``Content-Type`` header for the response. To -restrict the the formats supported by a given action, use the ``requirements`` +restrict the formats supported by a given action, use the ``requirements`` option of the ``@Route()`` annotation:: // src/AppBundle/Controller/DefaultController.php @@ -190,9 +190,8 @@ method:: } } -The ``redirectToRoute()`` is similar to the ``path()`` function used in the -templates. It takes the route name and an array of parameters as arguments and -returns the associated friendly URL. +The ``redirectToRoute()`` method takes as arguments the route name and an optional +array of parameters and redirects the user to the URL generated with those arguments. You can also internally forward the action to another action of the same or different controller using the ``forward()`` method:: @@ -226,6 +225,7 @@ use in your controllers:: */ public function indexAction() { + // ... throw $this->createNotFoundException(); } } @@ -241,6 +241,7 @@ Symfony will transform it into a proper ``500`` error page:: */ public function indexAction() { + // ... throw new \Exception('Something went horribly wrong!'); } } @@ -249,7 +250,7 @@ Getting Information from the Request ------------------------------------ Sometimes your controllers need to access the information related to the user -request, such as his/her preferred language, IP address or the URL query parameters. +request, such as their preferred language, IP address or the URL query parameters. To get access to this information, add a new argument of type ``Request`` to the action. The name of this new argument doesn't matter, but it must be preceded by the ``Request`` type in order to work (don't forget to add the new ``use`` @@ -346,4 +347,4 @@ That's all there is to it, and I'm not even sure you'll have spent the full 10 minutes. You were briefly introduced to bundles in the first part, and all the features you've learned about so far are part of the core framework bundle. But thanks to bundles, everything in Symfony can be extended or replaced. -That's the topic of the :doc:`next part of this tutorial`. +That's the topic of the :doc:`next part of this tutorial `. diff --git a/quick_tour/the_view.rst b/quick_tour/the_view.rst index f147df63825..4b16967bd81 100644 --- a/quick_tour/the_view.rst +++ b/quick_tour/the_view.rst @@ -50,7 +50,7 @@ Below is a minimal template that illustrates a few basics, using two variables To render a template in Symfony, use the ``render`` method from within a controller. If the template needs variables to generate its contents, pass them as an array -using the second optional second argument:: +using the second optional argument:: $this->render('default/index.html.twig', array( 'variable_name' => 'variable_value', @@ -156,7 +156,7 @@ The best way to share a snippet of code between several templates is to create a new template fragment that can then be included from other templates. Imagine that we want to display ads on some pages of our application. First, -create an ``banner.html.twig`` template: +create a ``banner.html.twig`` template: .. code-block:: jinja @@ -229,8 +229,8 @@ updated by just changing the configuration: Return to homepage -The ``path`` function takes the route name as the first argument and optionally -you can pass an array of route parameters as the second argument. +The ``path`` function takes the route name as the first argument and you can +optionally pass an array of route parameters as the second argument. .. tip:: @@ -251,8 +251,8 @@ Symfony provides the ``asset`` function to deal with them easily: The ``asset()`` function looks for the web assets inside the ``web/`` directory. -If you store them in other directory, read ..... this article to learn how to -manage web assets. +If you store them in another directory, read :doc:`this article ` +to learn how to manage web assets. Using the ``asset`` function, your application is more portable. The reason is that you can move the application root directory anywhere under your web root From 811f6e8139d7127f61107b7fffacd672612f482e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 12 Nov 2014 10:21:08 +0100 Subject: [PATCH 388/835] Included a bunch of fixes suggested by the awesome Symfony doc reviewers --- quick_tour/the_architecture.rst | 16 +++++++------- quick_tour/the_big_picture.rst | 37 ++++++++++++++++++++------------- quick_tour/the_controller.rst | 4 ++++ quick_tour/the_view.rst | 8 +++---- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst index b92d7d62f1f..b3fab62cf1c 100644 --- a/quick_tour/the_architecture.rst +++ b/quick_tour/the_architecture.rst @@ -13,7 +13,7 @@ Understanding the Directory Structure The directory structure of a Symfony :term:`application` is rather flexible, but the recommended structure is as follows: -* ``app/``: the application configuration and templates; +* ``app/``: the application configuration, templates and translations; * ``src/``: the project's PHP code; * ``vendor/``: the third-party dependencies; * ``web/``: the web root directory. @@ -121,8 +121,8 @@ a single ``Bundle`` class that describes it:: } In addition to the AppBundle that was already talked about, notice that the -kernel also enables other bundles such as the FrameworkBundle, DoctrineBundle, -SwiftmailerBundle and AsseticBundle. They are all part of the core framework. +kernel also enables other bundles that are part of Symfony, such as FrameworkBundle, +DoctrineBundle, SwiftmailerBundle and AsseticBundle. Configuring a Bundle ~~~~~~~~~~~~~~~~~~~~ @@ -168,7 +168,7 @@ PHP. Have a look at this sample of the default Symfony configuration: # ... -Each first level entry like ``framework``, ``twig`` or ``swiftmailer`` defines +Each first level entry like ``framework``, ``twig`` and ``swiftmailer`` defines the configuration for a specific bundle. For example, ``framework`` configures the FrameworkBundle while ``swiftmailer`` configures the SwiftmailerBundle. @@ -238,10 +238,10 @@ Using Vendors ------------- Odds are that your application will depend on third-party libraries. Those -should be stored in the ``vendor/`` directory and managed by Composer. -This directory already contains the Symfony libraries, the SwiftMailer library, -the Doctrine ORM, the Twig templating system and some other third party -libraries and bundles. +should be stored in the ``vendor/`` directory. You should never touch anything +in this directory, because it is exclusively managed by Composer. This directory +already contains the Symfony libraries, the SwiftMailer library, the Doctrine ORM, +the Twig templating system and some other third party libraries and bundles. Understanding the Cache and Logs -------------------------------- diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 4390cc0a0d2..be24e7ecfaf 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -23,10 +23,9 @@ to display the installed PHP version: Installing Symfony ------------------ -In the past, Symfony had to be installed manually for each new project. In order -to simplify this set up, a new **Symfony Installer** was introduced recently. -This means that the very first time you use Symfony on a computer, you have to -install the Symfony Installer. +In the past, Symfony had to be installed manually for each new project. Now you +can use the **Symfony Installer**, which has to be installed the very first time +you use Symfony on a computer. On **Linux** and **Mac OS X** systems, execute the following console commands: @@ -52,9 +51,15 @@ to be able to execute the new ``symfony`` command: $ symfony -On **Windows** systems, download the ``symfony.phar`` file from ........... and -save it into the directory where you create the Symfony projects. Then you can -execute the Symfony installer right away with this command: +On **Windows** systems, execute the following console command: + + .. code-block:: bash + + c:\> php -r "readfile('https://symfony.com/installer');" | php + +This command downloads a file called ``symfony.phar`` which contains the Symfony +installer. Save or move that file to the directory where you create the Symfony +projects and then, execute the Symfony installer right away with this command: .. code-block:: bash @@ -90,7 +95,7 @@ the project directory and executing this command: .. code-block:: bash $ cd myproject/ - $ php app/console server:start + $ php app/console server:run Open your browser and access the ``http://localhost:8000`` URL to see the Welcome page of Symfony: @@ -166,9 +171,9 @@ that will be explained in the next section):: } } -In Symfony applications, **controllers** are PHP classes whose names are suffixed -with the ``Controller`` word. In this example, the controller is called ``Default`` -and the PHP class is called ``DefaultController``. +In Symfony applications, **controllers** are usually PHP classes whose names are +suffixed with the ``Controller`` word. In this example, the controller is called +``Default`` and the PHP class is called ``DefaultController``. The methods defined in a controller are called **actions**, they are usually associated with one URL of the application and their names are suffixed with @@ -230,8 +235,9 @@ of the ``Default`` controller when the user browses the ``/`` path of the applic .. tip:: In addition to PHP annotations, routes can be configured in YAML, XML or - PHP files. This flexibility is one of the main features of Symfony, a - framework that never imposes a particular configuration format on you. + PHP files, as explained in `the Routing chapter of the Symfony book`_ . + This flexibility is one of the main features of Symfony, a framework that + never imposes a particular configuration format on you. Templates ~~~~~~~~~ @@ -348,6 +354,7 @@ Symfony makes it really easy to implement web sites better and faster. If you are eager to learn more about Symfony, dive into the next section: ":doc:`The View `". -.. _Composer: https://getcomposer.org/ +.. _Composer: https://getcomposer.org/ .. _executable installer: http://getcomposer.org/download -.. _Twig: http://twig.sensiolabs.org/ +.. _Twig: http://twig.sensiolabs.org/ +.. _the Routing chapter of the Symfony book: http://symfony.com/doc/current/book/routing.html diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index 0f593f3a356..e669bb734e5 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -218,6 +218,8 @@ In the case of ``404`` errors, Symfony includes a handy shortcut that you can use in your controllers:: // src/AppBundle/Controller/DefaultController.php + // ... + class DefaultController extends Controller { /** @@ -234,6 +236,8 @@ For ``500`` errors, just throw a regular PHP exception inside the controller and Symfony will transform it into a proper ``500`` error page:: // src/AppBundle/Controller/DefaultController.php + // ... + class DefaultController extends Controller { /** diff --git a/quick_tour/the_view.rst b/quick_tour/the_view.rst index 4b16967bd81..a8741123837 100644 --- a/quick_tour/the_view.rst +++ b/quick_tour/the_view.rst @@ -64,18 +64,18 @@ display the content of a variable passed by the controller depending on its type .. code-block:: jinja {# 1. Simple variables #} - {# $this->render( ..., array('name' => 'Fabien') ) #} + {# $this->render('template.html.twig', array('name' => 'Fabien') ) #} {{ name }} {# 2. Arrays #} - {# $this->render( ..., array('user' => array('name' => 'Fabien')) ) #} + {# $this->render('template.html.twig', array('user' => array('name' => 'Fabien')) ) #} {{ user.name }} {# alternative syntax for arrays #} {{ user['name'] }} {# 3. Objects #} - {# $this->render( ..., array('user' => new User('Fabien')) ) #} + {# $this->render('template.html.twig', array('user' => new User('Fabien')) ) #} {{ user.name }} {{ user.getName }} @@ -251,7 +251,7 @@ Symfony provides the ``asset`` function to deal with them easily: The ``asset()`` function looks for the web assets inside the ``web/`` directory. -If you store them in another directory, read :doc:`this article ` +If you store them in another directory, read :doc:`this article ` to learn how to manage web assets. Using the ``asset`` function, your application is more portable. The reason is From 8c2d8373f35a635ce65ea69f5b8e7f74444b71cf Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 13 Nov 2014 11:43:46 -0500 Subject: [PATCH 389/835] [#4336] Backporting minor syntax fix --- reference/forms/types/entity.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index dcada6d96eb..16cf2dc0a16 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -183,7 +183,7 @@ directly. choices ~~~~~~~ -**type**: array || ``\Traversable`` **default**: ``null`` +**type**: array | ``\Traversable`` **default**: ``null`` Instead of allowing the `class`_ and `query_builder`_ options to fetch the entities to include for you, you can pass the ``choices`` option directly. From 178a2d6dccb79573a2f1721552f2ac2c5086eded Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 7 Nov 2014 23:03:02 +0100 Subject: [PATCH 390/835] consistent table headlines --- best_practices/templates.rst | 18 ++-- book/routing.rst | 59 +++++----- .../class_loader/class_map_generator.rst | 2 +- components/form/form_events.rst | 102 ++++++++---------- components/http_kernel/introduction.rst | 25 ++--- contributing/documentation/format.rst | 2 +- cookbook/security/entity_provider.rst | 6 +- cookbook/templating/twig_extension.rst | 10 +- reference/constraints/UserPassword.rst | 9 +- reference/forms/types/choice.rst | 2 +- .../types/options/_date_limitation.rst.inc | 4 +- .../options/_error_bubbling_body.rst.inc | 2 +- .../forms/types/options/block_name.rst.inc | 3 +- .../forms/types/options/button_attr.rst.inc | 3 - .../types/options/cascade_validation.rst.inc | 1 - .../types/options/checkbox_compound.rst.inc | 5 +- .../forms/types/options/date_format.rst.inc | 3 +- .../forms/types/options/date_input.rst.inc | 2 +- .../forms/types/options/disabled.rst.inc | 5 +- .../types/options/error_bubbling.rst.inc | 2 +- .../forms/types/options/expanded.rst.inc | 2 +- .../forms/types/options/grouping.rst.inc | 6 +- reference/forms/types/options/hours.rst.inc | 2 +- .../types/options/invalid_message.rst.inc | 2 +- .../invalid_message_parameters.rst.inc | 2 +- .../types/options/model_timezone.rst.inc | 2 +- .../forms/types/options/multiple.rst.inc | 2 +- .../types/options/preferred_choices.rst.inc | 4 +- reference/forms/types/options/seconds.rst.inc | 2 +- .../types/options/select_how_rendered.rst.inc | 19 ++-- reference/forms/types/options/trim.rst.inc | 2 +- .../forms/types/options/view_timezone.rst.inc | 2 +- .../forms/types/options/with_seconds.rst.inc | 2 +- reference/forms/types/options/years.rst.inc | 3 +- .../variables/check_or_radio_table.rst.inc | 2 +- 35 files changed, 149 insertions(+), 170 deletions(-) diff --git a/best_practices/templates.rst b/best_practices/templates.rst index 4783cb37aa7..0b6936ba3ef 100644 --- a/best_practices/templates.rst +++ b/best_practices/templates.rst @@ -37,15 +37,15 @@ But for the templates used in your application, it's much more convenient to store them in the ``app/Resources/views/`` directory. For starters, this drastically simplifies their logical names: -================================================== ================================== -Templates stored inside bundles Templates stored in ``app/`` -================================================== ================================== -``AcmeDemoBundle:Default:index.html.twig`` ``default/index.html.twig`` -``::layout.html.twig`` ``layout.html.twig`` -``AcmeDemoBundle::index.html.twig`` ``index.html.twig`` -``AcmeDemoBundle:Default:subdir/index.html.twig`` ``default/subdir/index.html.twig`` -``AcmeDemoBundle:Default/subdir:index.html.twig`` ``default/subdir/index.html.twig`` -================================================== ================================== +================================================= ================================== +Templates Stored inside Bundles Templates Stored in ``app/`` +================================================= ================================== +``AcmeDemoBundle:Default:index.html.twig`` ``default/index.html.twig`` +``::layout.html.twig`` ``layout.html.twig`` +``AcmeDemoBundle::index.html.twig`` ``index.html.twig`` +``AcmeDemoBundle:Default:subdir/index.html.twig`` ``default/subdir/index.html.twig`` +``AcmeDemoBundle:Default/subdir:index.html.twig`` ``default/subdir/index.html.twig`` +================================================= ================================== Another advantage is that centralizing your templates simplifies the work of your designers. They don't need to look for templates in lots of directories diff --git a/book/routing.rst b/book/routing.rst index cab6efb7953..394a7966f04 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -410,7 +410,7 @@ entries? Update the route to have a new ``{page}`` placeholder: .. code-block:: php-annotations // src/AppBundle/Controller/BlogController.php - + // ... /** @@ -470,7 +470,7 @@ This is done by including it in the ``defaults`` collection: .. code-block:: php-annotations // src/AppBundle/Controller/BlogController.php - + // ... /** @@ -522,13 +522,13 @@ longer required. The URL ``/blog`` will match this route and the value of the ``page`` parameter will be set to ``1``. The URL ``/blog/2`` will also match, giving the ``page`` parameter a value of ``2``. Perfect. -=========== ===== ========== -URL route parameters -=========== ===== ========== -``/blog`` blog {page} = 1 -``/blog/1`` blog {page} = 1 -``/blog/2`` blog {page} = 2 -=========== ===== ========== +=========== ======== ================== +URL Route Parameters +=========== ======== ================== +``/blog`` ``blog`` ``{page}`` = ``1`` +``/blog/1`` ``blog`` ``{page}`` = ``1`` +``/blog/2`` ``blog`` ``{page}`` = ``2`` +=========== ======== ================== .. caution:: @@ -555,7 +555,7 @@ Take a quick look at the routes that have been created so far: .. code-block:: php-annotations // src/AppBundle/Controller/BlogController.php - + // ... class BlogController extends Controller { @@ -631,13 +631,12 @@ will *never* be matched. Instead, a URL like ``/blog/my-blog-post`` will match the first route (``blog``) and return a nonsense value of ``my-blog-post`` to the ``{page}`` parameter. -+--------------------+-------+-----------------------+ -| URL | route | parameters | -+====================+=======+=======================+ -| /blog/2 | blog | {page} = 2 | -+--------------------+-------+-----------------------+ -| /blog/my-blog-post | blog | {page} = my-blog-post | -+--------------------+-------+-----------------------+ +====================== ======== =============================== +URL Route Parameters +====================== ======== =============================== +``/blog/2`` ``blog`` ``{page}`` = ``2`` +``/blog/my-blog-post`` ``blog`` ``{page}`` = ``"my-blog-post"`` +====================== ======== =============================== The answer to the problem is to add route *requirements*. The routes in this example would work perfectly if the ``/blog/{page}`` path *only* matched @@ -710,15 +709,13 @@ is *not* a number). As a result, a URL like ``/blog/my-blog-post`` will now properly match the ``blog_show`` route. -+----------------------+-----------+-------------------------+ -| URL | route | parameters | -+======================+===========+=========================+ -| /blog/2 | blog | {page} = 2 | -+----------------------+-----------+-------------------------+ -| /blog/my-blog-post | blog_show | {slug} = my-blog-post | -+----------------------+-----------+-------------------------+ -| /blog/2-my-blog-post | blog_show | {slug} = 2-my-blog-post | -+----------------------+-----------+-------------------------+ +======================== ============= =============================== +URL Route Parameters +======================== ============= =============================== +``/blog/2`` ``blog`` ``{page}`` = ``2`` +``/blog/my-blog-post`` ``blog_show`` ``{slug}`` = ``my-blog-post`` +``/blog/2-my-blog-post`` ``blog_show`` ``{slug}`` = ``2-my-blog-post`` +======================== ============= =============================== .. sidebar:: Earlier Routes always Win @@ -738,7 +735,7 @@ URL: .. code-block:: php-annotations // src/AppBundle/Controller/MainController.php - + // ... class MainController extends Controller { @@ -795,11 +792,11 @@ For incoming requests, the ``{_locale}`` portion of the URL is matched against the regular expression ``(en|fr)``. ======= ======================== -path parameters +Path Parameters ======= ======================== -``/`` {_locale} = en -``/en`` {_locale} = en -``/fr`` {_locale} = fr +``/`` ``{_locale}`` = ``"en"`` +``/en`` ``{_locale}`` = ``"en"`` +``/fr`` ``{_locale}`` = ``"fr"`` ``/es`` *won't match this route* ======= ======================== diff --git a/components/class_loader/class_map_generator.rst b/components/class_loader/class_map_generator.rst index 320a5d2acd4..6b7ae5f7a09 100644 --- a/components/class_loader/class_map_generator.rst +++ b/components/class_loader/class_map_generator.rst @@ -29,7 +29,7 @@ manually. For example, imagine a library with the following directory structure: These files contain the following classes: =========================== ================ -File Class name +File Class Name =========================== ================ ``library/bar/baz/Boo.php`` ``Acme\Bar\Baz`` --------------------------- ---------------- diff --git a/components/form/form_events.rst b/components/form/form_events.rst index 83768e78d4e..0f7aa4e3f32 100644 --- a/components/form/form_events.rst +++ b/components/form/form_events.rst @@ -61,15 +61,13 @@ The ``FormEvents::PRE_SET_DATA`` event is dispatched at the beginning of the :ref:`Form Events Information Table` -+-----------------+-----------+ -| Data type | Value | -+=================+===========+ -| Model data | ``null`` | -+-----------------+-----------+ -| Normalized data | ``null`` | -+-----------------+-----------+ -| View data | ``null`` | -+-----------------+-----------+ +=============== ======== +Data Type Value +=============== ======== +Model data ``null`` +Normalized data ``null`` +View data ``null`` +=============== ======== .. caution:: @@ -98,15 +96,13 @@ the form. :ref:`Form Events Information Table` -+-----------------+------------------------------------------------------+ -| Data type | Value | -+=================+======================================================+ -| Model data | Model data injected into ``setData()`` | -+-----------------+------------------------------------------------------+ -| Normalized data | Model data transformed using a model transformer | -+-----------------+------------------------------------------------------+ -| View data | Normalized data transformed using a view transformer | -+-----------------+------------------------------------------------------+ +=============== ==================================================== +Data Type Value +=============== ==================================================== +Model data Model data injected into ``setData()`` +Normalized data Model data transformed using a model transformer +View data Normalized data transformed using a view transformer +=============== ==================================================== .. sidebar:: ``FormEvents::POST_SET_DATA`` in the Form component @@ -140,15 +136,13 @@ It can be used to: :ref:`Form Events Information Table` -+-----------------+------------------------------------------+ -| Data type | Value | -+=================+==========================================+ -| Model data | Same as in ``FormEvents::POST_SET_DATA`` | -+-----------------+------------------------------------------+ -| Normalized data | Same as in ``FormEvents::POST_SET_DATA`` | -+-----------------+------------------------------------------+ -| View data | Same as in ``FormEvents::POST_SET_DATA`` | -+-----------------+------------------------------------------+ +=============== ======================================== +Data Type Value +=============== ======================================== +Model data Same as in ``FormEvents::POST_SET_DATA`` +Normalized data Same as in ``FormEvents::POST_SET_DATA`` +View data Same as in ``FormEvents::POST_SET_DATA`` +=============== ======================================== .. sidebar:: ``FormEvents::PRE_SUBMIT`` in the Form component @@ -170,15 +164,13 @@ It can be used to change data from the normalized representation of the data. :ref:`Form Events Information Table` -+-----------------+-------------------------------------------------------------------------------------+ -| Data type | Value | -+=================+=====================================================================================+ -| Model data | Same as in ``FormEvents::POST_SET_DATA`` | -+-----------------+-------------------------------------------------------------------------------------+ -| Normalized data | Data from the request reverse-transformed from the request using a view transformer | -+-----------------+-------------------------------------------------------------------------------------+ -| View data | Same as in ``FormEvents::POST_SET_DATA`` | -+-----------------+-------------------------------------------------------------------------------------+ +=============== =================================================================================== +Data Type Value +=============== =================================================================================== +Model data Same as in ``FormEvents::POST_SET_DATA`` +Normalized data Data from the request reverse-transformed from the request using a view transformer +View data Same as in ``FormEvents::POST_SET_DATA`` +=============== =================================================================================== .. caution:: @@ -202,15 +194,13 @@ It can be used to fetch data after denormalization. :ref:`Form Events Information Table` -+-----------------+---------------------------------------------------------------+ -| Data type | Value | -+=================+===============================================================+ -| Model data | Normalized data reverse-transformed using a model transformer | -+-----------------+---------------------------------------------------------------+ -| Normalized data | Same as in ``FormEvents::POST_SUBMIT`` | -+-----------------+---------------------------------------------------------------+ -| View data | Normalized data transformed using a view transformer | -+-----------------+---------------------------------------------------------------+ +=============== ============================================================= +Data Type Value +=============== ============================================================= +Model data Normalized data reverse-transformed using a model transformer +Normalized data Same as in ``FormEvents::POST_SUBMIT`` +View data Normalized data transformed using a view transformer +=============== ============================================================= .. caution:: @@ -242,19 +232,15 @@ processed. .. _component-form-event-table: -+------------------------+-------------------------------+------------------+ -| Name | ``FormEvents`` Constant | Event's data | -+========================+===============================+==================+ -| ``form.pre_set_data`` | ``FormEvents::PRE_SET_DATA`` | Model data | -+------------------------+-------------------------------+------------------+ -| ``form.post_set_data`` | ``FormEvents::POST_SET_DATA`` | Model data | -+------------------------+-------------------------------+------------------+ -| ``form.pre_bind`` | ``FormEvents::PRE_SUBMIT`` | Request data | -+------------------------+-------------------------------+------------------+ -| ``form.bind`` | ``FormEvents::SUBMIT`` | Normalized data | -+------------------------+-------------------------------+------------------+ -| ``form.post_bind`` | ``FormEvents::POST_SUBMIT`` | View data | -+------------------------+-------------------------------+------------------+ +====================== ============================= =============== +Name ``FormEvents`` Constant Event's Data +====================== ============================= =============== +``form.pre_set_data`` ``FormEvents::PRE_SET_DATA`` Model data +``form.post_set_data`` ``FormEvents::POST_SET_DATA`` Model data +``form.pre_bind`` ``FormEvents::PRE_SUBMIT`` Request data +``form.bind`` ``FormEvents::SUBMIT`` Normalized data +``form.post_bind`` ``FormEvents::POST_SUBMIT`` View data +====================== ============================= =============== .. versionadded:: 2.3 Before Symfony 2.3, ``FormEvents::PRE_SUBMIT``, ``FormEvents::SUBMIT`` diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index 13f93b45feb..cfc26067d2a 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -575,21 +575,16 @@ each event has their own event object: .. _component-http-kernel-event-table: -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| Name | ``KernelEvents`` Constant | Argument passed to the listener | -+===================+===============================+=====================================================================================+ -| kernel.request | ``KernelEvents::REQUEST`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.controller | ``KernelEvents::CONTROLLER`` | :class:`Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.view | ``KernelEvents::VIEW`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.response | ``KernelEvents::RESPONSE`` | :class:`Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.terminate | ``KernelEvents::TERMINATE`` | :class:`Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ -| kernel.exception | ``KernelEvents::EXCEPTION`` | :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent` | -+-------------------+-------------------------------+-------------------------------------------------------------------------------------+ +================= ============================ =================================================================================== +Name ``KernelEvents`` Constant Argument Passed to the Listener +================= ============================ =================================================================================== +kernel.request ``KernelEvents::REQUEST`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent` +kernel.controller ``KernelEvents::CONTROLLER`` :class:`Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent` +kernel.view ``KernelEvents::VIEW`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent` +kernel.response ``KernelEvents::RESPONSE`` :class:`Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent` +kernel.terminate ``KernelEvents::TERMINATE`` :class:`Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent` +kernel.exception ``KernelEvents::EXCEPTION`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent` +================= ============================ =================================================================================== .. _http-kernel-working-example: diff --git a/contributing/documentation/format.rst b/contributing/documentation/format.rst index 198b5f21218..eeedf1e00dd 100644 --- a/contributing/documentation/format.rst +++ b/contributing/documentation/format.rst @@ -92,7 +92,7 @@ The previous reST snippet renders as follow: The current list of supported formats are the following: =================== ====================================== -Markup format Use it to display +Markup Format Use It to Display =================== ====================================== ``html`` HTML ``xml`` XML diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index c2994ae7fd8..144d7809f48 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -447,11 +447,11 @@ The :class:`Symfony\\Component\\Security\\Core\\User\\AdvancedUserInterface` interface adds four extra methods to validate the account status: * :method:`Symfony\\Component\\Security\\Core\\User\\AdvancedUserInterface::isAccountNonExpired` - checks whether the user's account has expired, + checks whether the user's account has expired; * :method:`Symfony\\Component\\Security\\Core\\User\\AdvancedUserInterface::isAccountNonLocked` - checks whether the user is locked, + checks whether the user is locked; * :method:`Symfony\\Component\\Security\\Core\\User\\AdvancedUserInterface::isCredentialsNonExpired` - checks whether the user's credentials (password) has expired, + checks whether the user's credentials (password) has expired; * :method:`Symfony\\Component\\Security\\Core\\User\\AdvancedUserInterface::isEnabled` checks whether the user is enabled. diff --git a/cookbook/templating/twig_extension.rst b/cookbook/templating/twig_extension.rst index 14913b7b63d..1931e52d23e 100644 --- a/cookbook/templating/twig_extension.rst +++ b/cookbook/templating/twig_extension.rst @@ -98,10 +98,12 @@ Now you must let the Service Container know about your newly created Twig Extens .. note:: Keep in mind that Twig Extensions are not lazily loaded. This means that - there's a higher chance that you'll get a **CircularReferenceException** - or a **ScopeWideningInjectionException** if any services - (or your Twig Extension in this case) are dependent on the request service. - For more information take a look at :doc:`/cookbook/service_container/scopes`. + there's a higher chance that you'll get a + :class:`Symfony\\Component\\DependencyInjection\\Exception\\ServiceCircularReferenceException` + or a + :class:`Symfony\\Component\\DependencyInjection\\Exception\\ScopeWideningInjectionException` + if any services (or your Twig Extension in this case) are dependent on + the request service. For more information take a look at :doc:`/cookbook/service_container/scopes`. Using the custom Extension -------------------------- diff --git a/reference/constraints/UserPassword.rst b/reference/constraints/UserPassword.rst index ee879a0d7c2..84988594242 100644 --- a/reference/constraints/UserPassword.rst +++ b/reference/constraints/UserPassword.rst @@ -4,10 +4,11 @@ UserPassword .. note:: Since Symfony 2.2, the ``UserPassword*`` classes in the - ``Symfony\\Component\\Security\\Core\\Validator\\Constraint`` namespace are - deprecated and will be removed in Symfony 2.3. Please use the - ``UserPassword*`` classes in the - ``Symfony\\Component\\Security\\Core\\Validator\\Constraints`` namespace instead. + :namespace:`Symfony\\Component\\Security\\Core\\Validator\\Constraint ` + namespace are deprecated and will be removed in Symfony 2.3. Please use + the ``UserPassword*`` classes in the + :namespace:`Symfony\\Component\\Security\\Core\\Validator\\Constraints ` + namespace instead. This validates that an input value is equal to the current authenticated user's password. This is useful in a form where a user can change their password, diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index 95c91e8442b..a5757116817 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -104,7 +104,7 @@ is the item value and the array value is the item's label:: choice_list ~~~~~~~~~~~ -**type**: ``Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceListInterface`` +**type**: :class:`Symfony\\Component\\Form\\Extension\\Core\\ChoiceList\\ChoiceListInterface` This is one way of specifying the options to be used for this field. The ``choice_list`` option must be an instance of the ``ChoiceListInterface``. diff --git a/reference/forms/types/options/_date_limitation.rst.inc b/reference/forms/types/options/_date_limitation.rst.inc index 8a39d6f6b80..4178e4dbc51 100644 --- a/reference/forms/types/options/_date_limitation.rst.inc +++ b/reference/forms/types/options/_date_limitation.rst.inc @@ -1,5 +1,5 @@ .. caution:: - + If ``timestamp`` is used, ``DateType`` is limited to dates between Fri, 13 Dec 1901 20:45:54 GMT and Tue, 19 Jan 2038 03:14:07 GMT on 32bit - systems. This is due to a `limitation in PHP itself `_. \ No newline at end of file + systems. This is due to a `limitation in PHP itself `_. diff --git a/reference/forms/types/options/_error_bubbling_body.rst.inc b/reference/forms/types/options/_error_bubbling_body.rst.inc index 5fd93614456..19c0b2ddef7 100644 --- a/reference/forms/types/options/_error_bubbling_body.rst.inc +++ b/reference/forms/types/options/_error_bubbling_body.rst.inc @@ -1,3 +1,3 @@ If ``true``, any errors for this field will be passed to the parent field or form. For example, if set to ``true`` on a normal field, any errors for -that field will be attached to the main form, not to the specific field. \ No newline at end of file +that field will be attached to the main form, not to the specific field. diff --git a/reference/forms/types/options/block_name.rst.inc b/reference/forms/types/options/block_name.rst.inc index 4f11ba54f52..6cfdeaf9a10 100644 --- a/reference/forms/types/options/block_name.rst.inc +++ b/reference/forms/types/options/block_name.rst.inc @@ -1,7 +1,8 @@ block_name ~~~~~~~~~~~ -**type**: ``string`` **default**: the form's name (see :ref:`Knowing which block to customize `) +**type**: ``string`` **default**: the form's name (see :ref:`Knowing which +block to customize `) Allows you to override the block name used to render the form type. Useful for example if you have multiple instances of the same form and you diff --git a/reference/forms/types/options/button_attr.rst.inc b/reference/forms/types/options/button_attr.rst.inc index 39a2f6c35f4..fe1d7fde82b 100644 --- a/reference/forms/types/options/button_attr.rst.inc +++ b/reference/forms/types/options/button_attr.rst.inc @@ -10,6 +10,3 @@ 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/cascade_validation.rst.inc b/reference/forms/types/options/cascade_validation.rst.inc index 472a31d8251..c41b605f9f6 100644 --- a/reference/forms/types/options/cascade_validation.rst.inc +++ b/reference/forms/types/options/cascade_validation.rst.inc @@ -17,4 +17,3 @@ the data from ``CategoryType`` to also be validated. in the section about :ref:`Embedding a Single Object `. .. include:: /reference/forms/types/options/_error_bubbling_hint.rst.inc - diff --git a/reference/forms/types/options/checkbox_compound.rst.inc b/reference/forms/types/options/checkbox_compound.rst.inc index a97204c5cf8..bf328936647 100644 --- a/reference/forms/types/options/checkbox_compound.rst.inc +++ b/reference/forms/types/options/checkbox_compound.rst.inc @@ -3,6 +3,5 @@ compound **type**: ``boolean`` **default**: ``false`` -This option specifies if a form is compound. As it's not the -case for checkbox, by default the value is overridden with the -``false`` value. +This option specifies if a form is compound. As it's not the case for checkbox, +by default the value is overridden with the ``false`` value. diff --git a/reference/forms/types/options/date_format.rst.inc b/reference/forms/types/options/date_format.rst.inc index d8a6c370825..b6db52d68dd 100644 --- a/reference/forms/types/options/date_format.rst.inc +++ b/reference/forms/types/options/date_format.rst.inc @@ -1,7 +1,8 @@ format ~~~~~~ -**type**: ``integer`` or ``string`` **default**: `IntlDateFormatter::MEDIUM`_ (or ``yyyy-MM-dd`` if `widget`_ is ``single_text``) +**type**: ``integer`` or ``string`` **default**: `IntlDateFormatter::MEDIUM`_ +(or ``yyyy-MM-dd`` if `widget`_ is ``single_text``) Option passed to the ``IntlDateFormatter`` class, used to transform user input into the proper format. This is critical when the `widget`_ option is diff --git a/reference/forms/types/options/date_input.rst.inc b/reference/forms/types/options/date_input.rst.inc index 89aa73bc432..43f50abbd9d 100644 --- a/reference/forms/types/options/date_input.rst.inc +++ b/reference/forms/types/options/date_input.rst.inc @@ -14,4 +14,4 @@ your underlying object. Valid values are: The value that comes back from the form will also be normalized back into this format. -.. include:: /reference/forms/types/options/_date_limitation.rst.inc \ No newline at end of file +.. include:: /reference/forms/types/options/_date_limitation.rst.inc diff --git a/reference/forms/types/options/disabled.rst.inc b/reference/forms/types/options/disabled.rst.inc index 50d100a1f37..4a7190f4f10 100644 --- a/reference/forms/types/options/disabled.rst.inc +++ b/reference/forms/types/options/disabled.rst.inc @@ -6,6 +6,5 @@ disabled **type**: ``boolean`` **default**: ``false`` -If you don't want a user to modify the value of a field, you can set -the disabled option to true. Any submitted value will be ignored. - +If you don't want a user to modify the value of a field, you can set the disabled +option to true. Any submitted value will be ignored. diff --git a/reference/forms/types/options/error_bubbling.rst.inc b/reference/forms/types/options/error_bubbling.rst.inc index 21a53a887fa..dbe42833b17 100644 --- a/reference/forms/types/options/error_bubbling.rst.inc +++ b/reference/forms/types/options/error_bubbling.rst.inc @@ -3,4 +3,4 @@ error_bubbling **type**: ``Boolean`` **default**: ``false`` unless the form is ``compound`` -.. include:: /reference/forms/types/options/_error_bubbling_body.rst.inc \ No newline at end of file +.. include:: /reference/forms/types/options/_error_bubbling_body.rst.inc diff --git a/reference/forms/types/options/expanded.rst.inc b/reference/forms/types/options/expanded.rst.inc index 2543527300c..e41c28a5da4 100644 --- a/reference/forms/types/options/expanded.rst.inc +++ b/reference/forms/types/options/expanded.rst.inc @@ -4,4 +4,4 @@ expanded **type**: ``Boolean`` **default**: ``false`` If set to true, radio buttons or checkboxes will be rendered (depending -on the ``multiple`` value). If false, a select element will be rendered. \ No newline at end of file +on the ``multiple`` value). If false, a select element will be rendered. diff --git a/reference/forms/types/options/grouping.rst.inc b/reference/forms/types/options/grouping.rst.inc index e2e77178d5d..39aeddb6b2b 100644 --- a/reference/forms/types/options/grouping.rst.inc +++ b/reference/forms/types/options/grouping.rst.inc @@ -3,4 +3,8 @@ grouping **type**: ``integer`` **default**: ``false`` -This value is used internally as the ``NumberFormatter::GROUPING_USED`` value when using PHP's ``NumberFormatter`` class. Its documentation is non-existent, but it appears that if you set this to ``true``, numbers will be grouped with a comma or period (depending on your locale): ``12345.123`` would display as ``12,345.123``. \ No newline at end of file +This value is used internally as the ``NumberFormatter::GROUPING_USED`` value +when using PHP's ``NumberFormatter`` class. Its documentation is non-existent, +but it appears that if you set this to ``true``, numbers will be grouped with +a comma or period (depending on your locale): ``12345.123`` would display +as ``12,345.123``. diff --git a/reference/forms/types/options/hours.rst.inc b/reference/forms/types/options/hours.rst.inc index 51097ca25dc..2af89a163f6 100644 --- a/reference/forms/types/options/hours.rst.inc +++ b/reference/forms/types/options/hours.rst.inc @@ -4,4 +4,4 @@ hours **type**: ``array`` **default**: 0 to 23 List of hours available to the hours field type. This option is only relevant -when the ``widget`` option is set to ``choice``. \ No newline at end of file +when the ``widget`` option is set to ``choice``. diff --git a/reference/forms/types/options/invalid_message.rst.inc b/reference/forms/types/options/invalid_message.rst.inc index 3fbfbe276e8..c5c05432492 100644 --- a/reference/forms/types/options/invalid_message.rst.inc +++ b/reference/forms/types/options/invalid_message.rst.inc @@ -13,4 +13,4 @@ number field. Normal (business logic) validation (such as when setting a minimum length for a field) should be set using validation messages with your validation rules -(:ref:`reference`). \ No newline at end of file +(:ref:`reference`). diff --git a/reference/forms/types/options/invalid_message_parameters.rst.inc b/reference/forms/types/options/invalid_message_parameters.rst.inc index 71ae5bee601..286fbc45cb6 100644 --- a/reference/forms/types/options/invalid_message_parameters.rst.inc +++ b/reference/forms/types/options/invalid_message_parameters.rst.inc @@ -11,4 +11,4 @@ to that option and including the variables in this option:: // ... 'invalid_message' => 'You entered an invalid value - it should include %num% letters', 'invalid_message_parameters' => array('%num%' => 6), - )); \ No newline at end of file + )); diff --git a/reference/forms/types/options/model_timezone.rst.inc b/reference/forms/types/options/model_timezone.rst.inc index 4b0c6d36280..f06489ef65f 100644 --- a/reference/forms/types/options/model_timezone.rst.inc +++ b/reference/forms/types/options/model_timezone.rst.inc @@ -6,4 +6,4 @@ model_timezone Timezone that the input data is stored in. This must be one of the `PHP supported timezones`_. -.. _`PHP supported timezones`: http://php.net/manual/en/timezones.php \ No newline at end of file +.. _`PHP supported timezones`: http://php.net/manual/en/timezones.php diff --git a/reference/forms/types/options/multiple.rst.inc b/reference/forms/types/options/multiple.rst.inc index 7de60eda538..f5a61f28012 100644 --- a/reference/forms/types/options/multiple.rst.inc +++ b/reference/forms/types/options/multiple.rst.inc @@ -6,4 +6,4 @@ multiple If true, the user will be able to select multiple options (as opposed to choosing just one option). Depending on the value of the ``expanded`` option, this will render either a select tag or checkboxes if true and -a select tag or radio buttons if false. The returned value will be an array. \ No newline at end of file +a select tag or radio buttons if false. The returned value will be an array. diff --git a/reference/forms/types/options/preferred_choices.rst.inc b/reference/forms/types/options/preferred_choices.rst.inc index a195fd1a940..96f8268546c 100644 --- a/reference/forms/types/options/preferred_choices.rst.inc +++ b/reference/forms/types/options/preferred_choices.rst.inc @@ -20,9 +20,9 @@ This can be customized when rendering the field: .. configuration-block:: .. code-block:: jinja - + {{ form_widget(form.foo_choices, { 'separator': '=====' }) }} .. code-block:: php - + widget($form['foo_choices'], array('separator' => '=====')) ?> diff --git a/reference/forms/types/options/seconds.rst.inc b/reference/forms/types/options/seconds.rst.inc index 4e8d5c6c9cd..7acbf39025c 100644 --- a/reference/forms/types/options/seconds.rst.inc +++ b/reference/forms/types/options/seconds.rst.inc @@ -4,4 +4,4 @@ seconds **type**: ``array`` **default**: 0 to 59 List of seconds available to the seconds field type. This option is only -relevant when the ``widget`` option is set to ``choice``. \ No newline at end of file +relevant when the ``widget`` option is set to ``choice``. diff --git a/reference/forms/types/options/select_how_rendered.rst.inc b/reference/forms/types/options/select_how_rendered.rst.inc index cb785e18f54..e97205d768a 100644 --- a/reference/forms/types/options/select_how_rendered.rst.inc +++ b/reference/forms/types/options/select_how_rendered.rst.inc @@ -4,14 +4,11 @@ Select Tag, Checkboxes or Radio Buttons This field may be rendered as one of several different HTML fields, depending on the ``expanded`` and ``multiple`` options: -+------------------------------------------+----------+----------+ -| element type | expanded | multiple | -+==========================================+==========+==========+ -| select tag | false | false | -+------------------------------------------+----------+----------+ -| select tag (with ``multiple`` attribute) | false | true | -+------------------------------------------+----------+----------+ -| radio buttons | true | false | -+------------------------------------------+----------+----------+ -| checkboxes | true | true | -+------------------------------------------+----------+----------+ +======================================== ========= ========= +Element Type Expanded Multiple +======================================== ========= ========= +select tag ``false`` ``false`` +select tag (with ``multiple`` attribute) ``false`` ``true`` +radio buttons ``true`` ``false`` +checkboxes ``true`` ``true`` +======================================== ========= ========= diff --git a/reference/forms/types/options/trim.rst.inc b/reference/forms/types/options/trim.rst.inc index 1665e5d4333..68ba656be94 100644 --- a/reference/forms/types/options/trim.rst.inc +++ b/reference/forms/types/options/trim.rst.inc @@ -6,4 +6,4 @@ trim If true, the whitespace of the submitted string value will be stripped via the ``trim()`` function when the data is bound. This guarantees that if a value is submitted with extra whitespace, it will be removed before -the value is merged back onto the underlying object. \ No newline at end of file +the value is merged back onto the underlying object. diff --git a/reference/forms/types/options/view_timezone.rst.inc b/reference/forms/types/options/view_timezone.rst.inc index 5cffeeb5747..d2ab65adcbf 100644 --- a/reference/forms/types/options/view_timezone.rst.inc +++ b/reference/forms/types/options/view_timezone.rst.inc @@ -6,4 +6,4 @@ view_timezone Timezone for how the data should be shown to the user (and therefore also the data that the user submits). This must be one of the `PHP supported timezones`_. -.. _`PHP supported timezones`: http://php.net/manual/en/timezones.php \ No newline at end of file +.. _`PHP supported timezones`: http://php.net/manual/en/timezones.php diff --git a/reference/forms/types/options/with_seconds.rst.inc b/reference/forms/types/options/with_seconds.rst.inc index 684a748fb3e..cc9b0b12105 100644 --- a/reference/forms/types/options/with_seconds.rst.inc +++ b/reference/forms/types/options/with_seconds.rst.inc @@ -4,4 +4,4 @@ with_seconds **type**: ``Boolean`` **default**: ``false`` Whether or not to include seconds in the input. This will result in an additional -input to capture seconds. \ No newline at end of file +input to capture seconds. diff --git a/reference/forms/types/options/years.rst.inc b/reference/forms/types/options/years.rst.inc index 8a6e73a83a4..3c4655697f6 100644 --- a/reference/forms/types/options/years.rst.inc +++ b/reference/forms/types/options/years.rst.inc @@ -1,7 +1,8 @@ years ~~~~~ -**type**: ``array`` **default**: five years before to five years after the current year +**type**: ``array`` **default**: five years before to five years after the +current year List of years available to the year field type. This option is only relevant when the ``widget`` option is set to ``choice``. diff --git a/reference/forms/types/variables/check_or_radio_table.rst.inc b/reference/forms/types/variables/check_or_radio_table.rst.inc index b6ef7527640..b7f251c4810 100644 --- a/reference/forms/types/variables/check_or_radio_table.rst.inc +++ b/reference/forms/types/variables/check_or_radio_table.rst.inc @@ -1,5 +1,5 @@ ======== ============ ============================================ -Variable Type Usage +Variable Type Usage ======== ============ ============================================ checked ``Boolean`` Whether or not the current input is checked. ======== ============ ============================================ From cd182f2e045a6c471450afc9086c78eed187df20 Mon Sep 17 00:00:00 2001 From: Eric GELOEN Date: Sat, 18 Oct 2014 16:05:35 +0200 Subject: [PATCH 391/835] [Form] Add entity manager instance support for em option --- reference/forms/types/entity.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index dcada6d96eb..17728fa3be9 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -117,7 +117,7 @@ or the short alias name (as shown prior). em ~~ -**type**: ``string`` **default**: the default entity manager +**type**: ``string`` | ``Doctrine\Common\Persistence\ObjectManager`` **default**: the default entity manager If specified, the specified entity manager will be used to load the choices instead of the default entity manager. @@ -183,7 +183,7 @@ directly. choices ~~~~~~~ -**type**: array || ``\Traversable`` **default**: ``null`` +**type**: array | ``\Traversable`` **default**: ``null`` Instead of allowing the `class`_ and `query_builder`_ options to fetch the entities to include for you, you can pass the ``choices`` option directly. From 17c6cac8fc82ba35fe0d96e4b8f7c8a1d1430bf9 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 5 Nov 2014 11:55:06 -0500 Subject: [PATCH 392/835] [#4243] Tweaks to the new var-dumper component --- components/var_dumper/advanced.rst | 81 ++++++++++++++++++-------- components/var_dumper/introduction.rst | 39 +++++++++++-- 2 files changed, 91 insertions(+), 29 deletions(-) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index 9b70c8fdfd4..6b6cbf4ebce 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -5,14 +5,30 @@ Advanced Usage of the VarDumper Component ========================================= -``dump()`` function is just a thin wrapper and a more convenient way to call +The ``dump()`` function is just a thin wrapper and a more convenient way to call :method:`VarDumper::dump() `. You can change the behavior of this function by calling -:method:`VarDumper::setHandler($callable) `: -calls to ``dump()`` will then be forwarded to ``$callable``. +:method:`VarDumper::setHandler($callable) `. +Calls to ``dump()`` will then be forwarded to ``$callable``. + +By adding a handler, you can customize the `Cloners`_, `Dumpers`_ and `Casters`_ +explained below. A simple implementation of a handler function might look +like this:: + + use Symfony\Component\VarDumper\VarDumper; + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\CliDumper; + use Symfony\Component\VarDumper\Dumper\HtmlDumper; + + VarDumper::setHandler(function($var) { + $cloner = new VarCloner(); + $dumper = 'cli' === PHP_SAPI ? new CliDumper() : new HtmlDumper(); + + $dumper->dump($cloner->cloneVar($var)); + }); Cloners -~~~~~~~ +------- A cloner is used to create an intermediate representation of any PHP variable. Its output is a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` @@ -21,18 +37,24 @@ object that wraps this representation. You can create a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object this way:: + use Symfony\Component\VarDumper\Cloner\VarCloner; + $cloner = new VarCloner(); $data = $cloner->cloneVar($myVar); + // this is commonly then passed to the dumpe + // see the example at the top of this page + // $dumper->dump($data); -A cloner also applies limits when creating this representation, so that the +A cloner also applies limits when creating the representation, so that the corresponding Data object could represent only a subset of the cloned variable. -Before :method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::cloneVar`, +Before calling :method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::cloneVar`, you can configure these limits: * :method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMaxItems` - configures the maximum number of items that will be cloned *past the first - nesting level*. Items are counted using a breadth-first algorithm so that - lower level items have higher priority than deeply nested items; + configures the maximum number of items that will be cloned + *past the first nesting level*. Items are counted using a breadth-first + algorithm so that lower level items have higher priority than deeply nested + items; * :method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMaxString` configures the maximum number of characters that will be cloned before cutting overlong strings; @@ -45,7 +67,7 @@ method: * the first ``$maxDepth`` argument allows limiting dumps in the depth dimension, * the second ``$maxItemsPerDepth`` limits the number of items per depth level, -* and the last ``$useRefHandles`` defaults to ``true`` but allows removing +* and the last ``$useRefHandles`` defaults to ``true``, but allows removing internal objects' handles for sparser output, * but unlike the previous limits on cloners that remove data on purpose, these can be changed back and forth before dumping since they do not affect @@ -54,11 +76,11 @@ method: .. note:: When no limit is applied, a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` - object is as accurate as the native :phpfunction:`serialize` function - and thus could have a wider purpose than strictly dumping for debugging. + object is as accurate as the native :phpfunction:`serialize` function, + and thus could be for purposes beyond dumping for debugging. Dumpers -~~~~~~~ +------- A dumper is responsible for outputting a string representation of a PHP variable, using a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object as input. @@ -70,6 +92,9 @@ for optionally colored command line output. For example, if you want to dump some ``$variable``, just do:: + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\CliDumper; + $cloner = new VarCloner(); $dumper = new CliDumper(); @@ -77,7 +102,7 @@ For example, if you want to dump some ``$variable``, just do:: By using the first argument of the constructor, you can select the output stream where the dump will be written. By default, the ``CliDumper`` writes -on ``php://stdout`` and the ``HtmlDumper`` on ``php://output``, but any PHP +on ``php://stdout`` and the ``HtmlDumper`` on ``php://output``. But any PHP stream (resource or URL) is acceptable. Instead of a stream destination, you can also pass it a ``callable`` that @@ -90,6 +115,9 @@ method or the second argument of the For example, to get a dump as a string in a variable, you can do:: + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\CliDumper; + $cloner = new VarCloner(); $dumper = new CliDumper(); $output = ''; @@ -107,7 +135,10 @@ For example, to get a dump as a string in a variable, you can do:: // $output is now populated with the dump representation of $variable -An other option for doing the same could be:: +Another option for doing the same could be:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\CliDumper; cloner = new VarCloner(); $dumper = new CliDumper(); @@ -128,9 +159,9 @@ them from re-implementing the logic required to walk through a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object's internal structure. Casters -~~~~~~~ +------- -Objects and resources nested in a PHP variable are casted to arrays in the +Objects and resources nested in a PHP variable are "cast" to arrays in the intermediate :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` representation. You can tweak the array representation for each object/resource by hooking a Caster into this process. The component already includes many @@ -140,6 +171,8 @@ If you want to build your own Caster, you can register one before cloning a PHP variable. Casters are registered using either a Cloner's constructor or its ``addCasters()`` method:: + use Symfony\Component\VarDumper\Cloner\VarCloner; + $myCasters = array(...); $cloner = new VarCloner($myCasters); @@ -172,7 +205,7 @@ being cloned in an array. They are callables that accept four arguments: * an array modelled for objects after PHP's native ``(array)`` cast operator, * a :class:`Symfony\\Component\\VarDumper\\Cloner\\Stub` object representing the main properties of the object (class, type, etc.), -* true/false when the caster is called nested is a structure or not. +* true/false when the caster is called nested in a structure or not. Here is a simple caster not doing anything:: @@ -186,18 +219,18 @@ Here is a simple caster not doing anything:: For objects, the ``$array`` parameter comes pre-populated using PHP's native ``(array)`` casting operator or with the return value of ``$object->__debugInfo()`` if the magic method exists. Then, the return value of one Caster is given -as argument to the next Caster in the chain. +as the array argument to the next Caster in the chain. When casting with the ``(array)`` operator, PHP prefixes protected properties -with a ``\0*\0`` and private ones with the class owning the property: -e.g. ``\0Foobar\0`` prefixes all private properties of objects of type Foobar. -Casters follow this convention and add two more prefixes: ``\0~\0`` is used -for virtual properties and ``\0+\0`` for dynamic ones (runtime added +with a ``\0*\0`` and private ones with the class owning the property. For example, +``\0Foobar\0`` will be the prefix for all private properties of objects of +type Foobar. Casters follow this convention and add two more prefixes: ``\0~\0`` +is used for virtual properties and ``\0+\0`` for dynamic ones (runtime added properties not in the class declaration). .. note:: - Although you can, it is best advised not to alter the state of an object + Although you can, it is advised to not alter the state of an object while casting it in a Caster. .. tip:: diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst index 6f43b67ef3d..aadfa6e2704 100644 --- a/components/var_dumper/introduction.rst +++ b/components/var_dumper/introduction.rst @@ -37,13 +37,21 @@ use instead of e.g. :phpfunction:`var_dump`. By using it, you'll gain: reference structure of your data; * Ability to operate in the context of an output buffering handler. +For example:: + + require __DIR__.'/vendor/autoload.php'; + // create a variable, which could be anything! + $someVar = '...'; + + dump($someVar); + By default, the output format and destination are selected based on your current PHP SAPI: * On the command line (CLI SAPI), the output is written on ``STDOUT``. This can be surprising to some because this bypasses PHP's output buffering mechanism; -* On other SAPIs, dumps are written as HTML on the regular output. +* On other SAPIs, dumps are written as HTML in the regular output. .. note:: @@ -101,8 +109,8 @@ original value. You can configure the limits in terms of: -Reading a Dump --------------- +Dump Examples and Output +------------------------ For simple variables, reading the output should be straightforward. Here are some examples showing first a variable defined in PHP, @@ -115,6 +123,7 @@ then its dump representation:: 'a boolean' => true, 'an empty array' => array(), ); + dump($var); .. image:: /images/components/var_dumper/01-simple.png @@ -131,6 +140,7 @@ then its dump representation:: $var .= "Non-UTF-8 strings length are counted in octet size.\n"; $var .= "Because of this `\xE9` octet (\\xE9),\n"; $var .= "this string is not UTF-8 valid, thus the `b` prefix.\n"; + dump($var); .. image:: /images/components/var_dumper/02-multi-line-str.png @@ -144,6 +154,7 @@ then its dump representation:: } $var = new PropertyExample(); + dump($var); .. image:: /images/components/var_dumper/03-object.png @@ -161,6 +172,7 @@ then its dump representation:: $var = new DynamicPropertyExample(); $var->undeclaredProperty = 'Runtime added dynamic properties have `"` around their name.'; + dump($var); .. image:: /images/components/var_dumper/04-dynamic-property.png @@ -172,12 +184,20 @@ then its dump representation:: } $var = new ReferenceExample(); $var->aCircularReference = $var; + dump($var); .. image:: /images/components/var_dumper/05-soft-ref.png .. code-block:: php - $var = new \ErrorException("For some objects, properties have special values\nthat are best represented as constants, like\n`severity` below. Hovering displays the value (`2`).\n", 0, E_WARNING); + $var = new \ErrorException( + "For some objects, properties have special values + that are best represented as constants, like + `severity` below. Hovering displays the value (`2`).", + 0, + E_WARNING + ); + dump($var); .. image:: /images/components/var_dumper/06-constants.png @@ -190,6 +210,7 @@ then its dump representation:: $var[2] = array("Hard references (circular or sibling)"); $var[3] =& $var[2]; $var[3][] = "are dumped using `&number` prefixes."; + dump($var); .. image:: /images/components/var_dumper/07-hard-ref.png @@ -199,12 +220,20 @@ then its dump representation:: $var[] = "Some resources and special objects like the current"; $var[] = "one are sometimes best represented using virtual"; $var[] = "properties that describe their internal state."; + dump($var); .. image:: /images/components/var_dumper/08-virtual-property.png .. code-block:: php - $var = new AcmeController("When a dump goes over its maximum items limit,\nor when some special objects are encountered,\nchildren can be replaced by an ellipsis and\noptionnally followed by a number that says how\nmany have been removed; `9` in this case.\n"); + $var = new AcmeController( + "When a dump goes over its maximum items limit, + or when some special objects are encountered, + children can be replaced by an ellipsis and + optionnally followed by a number that says how + many have been removed; `9` in this case." + ); + dump($var); .. image:: /images/components/var_dumper/09-cut.png From a79196a4af9e4f99b7be053d31cf6ddf963ce469 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 8 Nov 2014 13:14:41 +0100 Subject: [PATCH 393/835] typos in the var-dumper component --- components/var_dumper/advanced.rst | 4 ++-- components/var_dumper/introduction.rst | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index 6b6cbf4ebce..b6aae5c792e 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -12,7 +12,7 @@ You can change the behavior of this function by calling Calls to ``dump()`` will then be forwarded to ``$callable``. By adding a handler, you can customize the `Cloners`_, `Dumpers`_ and `Casters`_ -explained below. A simple implementation of a handler function might look +as explained below. A simple implementation of a handler function might look like this:: use Symfony\Component\VarDumper\VarDumper; @@ -41,7 +41,7 @@ object this way:: $cloner = new VarCloner(); $data = $cloner->cloneVar($myVar); - // this is commonly then passed to the dumpe + // this is commonly then passed to the dumper // see the example at the top of this page // $dumper->dump($data); diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst index aadfa6e2704..990a4ad3b80 100644 --- a/components/var_dumper/introduction.rst +++ b/components/var_dumper/introduction.rst @@ -191,9 +191,9 @@ then its dump representation:: .. code-block:: php $var = new \ErrorException( - "For some objects, properties have special values - that are best represented as constants, like - `severity` below. Hovering displays the value (`2`).", + "For some objects, properties have special values\n" + ."that are best represented as constants, like\n" + ."`severity` below. Hovering displays the value (`2`).\n", 0, E_WARNING ); @@ -227,11 +227,11 @@ then its dump representation:: .. code-block:: php $var = new AcmeController( - "When a dump goes over its maximum items limit, - or when some special objects are encountered, - children can be replaced by an ellipsis and - optionnally followed by a number that says how - many have been removed; `9` in this case." + "When a dump goes over its maximum items limit,\n" + ."or when some special objects are encountered,\n" + ."children can be replaced by an ellipsis and\n" + ."optionnally followed by a number that says how\n" + ."many have been removed; `9` in this case.\n" ); dump($var); From 10b607b217466ad4a0acc93dad5a2a289135d41c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 13 Nov 2014 18:44:51 +0100 Subject: [PATCH 394/835] separate table columns with two spaces --- book/routing.rst | 66 +++++++------- book/templating.rst | 14 +-- .../class_loader/class_map_generator.rst | 19 ++-- components/form/form_events.rst | 88 +++++++++---------- components/http_kernel/introduction.rst | 20 ++--- reference/forms/types/collection.rst | 12 +-- reference/forms/types/file.rst | 10 +-- reference/forms/types/money.rst | 10 +-- .../types/options/select_how_rendered.rst.inc | 16 ++-- reference/forms/types/submit.rst | 10 +-- .../variables/check_or_radio_table.rst.inc | 10 +-- 11 files changed, 136 insertions(+), 139 deletions(-) diff --git a/book/routing.rst b/book/routing.rst index 394a7966f04..7e98a9d8974 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -522,13 +522,13 @@ longer required. The URL ``/blog`` will match this route and the value of the ``page`` parameter will be set to ``1``. The URL ``/blog/2`` will also match, giving the ``page`` parameter a value of ``2``. Perfect. -=========== ======== ================== -URL Route Parameters -=========== ======== ================== -``/blog`` ``blog`` ``{page}`` = ``1`` -``/blog/1`` ``blog`` ``{page}`` = ``1`` -``/blog/2`` ``blog`` ``{page}`` = ``2`` -=========== ======== ================== +=========== ======== ================== +URL Route Parameters +=========== ======== ================== +``/blog`` ``blog`` ``{page}`` = ``1`` +``/blog/1`` ``blog`` ``{page}`` = ``1`` +``/blog/2`` ``blog`` ``{page}`` = ``2`` +=========== ======== ================== .. caution:: @@ -631,12 +631,12 @@ will *never* be matched. Instead, a URL like ``/blog/my-blog-post`` will match the first route (``blog``) and return a nonsense value of ``my-blog-post`` to the ``{page}`` parameter. -====================== ======== =============================== -URL Route Parameters -====================== ======== =============================== -``/blog/2`` ``blog`` ``{page}`` = ``2`` -``/blog/my-blog-post`` ``blog`` ``{page}`` = ``"my-blog-post"`` -====================== ======== =============================== +====================== ======== =============================== +URL Route Parameters +====================== ======== =============================== +``/blog/2`` ``blog`` ``{page}`` = ``2`` +``/blog/my-blog-post`` ``blog`` ``{page}`` = ``"my-blog-post"`` +====================== ======== =============================== The answer to the problem is to add route *requirements*. The routes in this example would work perfectly if the ``/blog/{page}`` path *only* matched @@ -709,13 +709,13 @@ is *not* a number). As a result, a URL like ``/blog/my-blog-post`` will now properly match the ``blog_show`` route. -======================== ============= =============================== -URL Route Parameters -======================== ============= =============================== -``/blog/2`` ``blog`` ``{page}`` = ``2`` -``/blog/my-blog-post`` ``blog_show`` ``{slug}`` = ``my-blog-post`` -``/blog/2-my-blog-post`` ``blog_show`` ``{slug}`` = ``2-my-blog-post`` -======================== ============= =============================== +======================== ============= =============================== +URL Route Parameters +======================== ============= =============================== +``/blog/2`` ``blog`` ``{page}`` = ``2`` +``/blog/my-blog-post`` ``blog_show`` ``{slug}`` = ``my-blog-post`` +``/blog/2-my-blog-post`` ``blog_show`` ``{slug}`` = ``2-my-blog-post`` +======================== ============= =============================== .. sidebar:: Earlier Routes always Win @@ -791,14 +791,14 @@ URL: For incoming requests, the ``{_locale}`` portion of the URL is matched against the regular expression ``(en|fr)``. -======= ======================== -Path Parameters -======= ======================== -``/`` ``{_locale}`` = ``"en"`` -``/en`` ``{_locale}`` = ``"en"`` -``/fr`` ``{_locale}`` = ``"fr"`` -``/es`` *won't match this route* -======= ======================== +======= ======================== +Path Parameters +======= ======================== +``/`` ``{_locale}`` = ``"en"`` +``/en`` ``{_locale}`` = ``"en"`` +``/fr`` ``{_locale}`` = ``"fr"`` +``/es`` *won't match this route* +======= ======================== .. index:: single: Routing; Method requirement @@ -1062,11 +1062,11 @@ each separated by a colon: For example, a ``_controller`` value of ``AppBundle:Blog:show`` means: -========= ================== ============== -Bundle Controller Class Method Name -========= ================== ============== -AppBundle ``BlogController`` ``showAction`` -========= ================== ============== +========= ================== ============== +Bundle Controller Class Method Name +========= ================== ============== +AppBundle ``BlogController`` ``showAction`` +========= ================== ============== The controller might look like this:: diff --git a/book/templating.rst b/book/templating.rst index 727b30aa8a6..7af3b9ef144 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -444,13 +444,13 @@ Template Suffix Every template name also has two extensions that specify the *format* and *engine* for that template. -======================== ====== ====== -Filename Format Engine -======================== ====== ====== -``Blog/index.html.twig`` HTML Twig -``Blog/index.html.php`` HTML PHP -``Blog/index.css.twig`` CSS Twig -======================== ====== ====== +======================== ====== ====== +Filename Format Engine +======================== ====== ====== +``Blog/index.html.twig`` HTML Twig +``Blog/index.html.php`` HTML PHP +``Blog/index.css.twig`` CSS Twig +======================== ====== ====== By default, any Symfony template can be written in either Twig or PHP, and the last part of the extension (e.g. ``.twig`` or ``.php``) specifies which diff --git a/components/class_loader/class_map_generator.rst b/components/class_loader/class_map_generator.rst index 6b7ae5f7a09..d905c2b22e1 100644 --- a/components/class_loader/class_map_generator.rst +++ b/components/class_loader/class_map_generator.rst @@ -28,17 +28,14 @@ manually. For example, imagine a library with the following directory structure: These files contain the following classes: -=========================== ================ -File Class Name -=========================== ================ -``library/bar/baz/Boo.php`` ``Acme\Bar\Baz`` ---------------------------- ---------------- -``library/bar/Foo.php`` ``Acme\Bar`` ---------------------------- ---------------- -``library/foo/bar/Foo.php`` ``Acme\Foo\Bar`` ---------------------------- ---------------- -``library/foo/Bar.php`` ``Acme\Foo`` -=========================== ================ +=========================== ================ +File Class Name +=========================== ================ +``library/bar/baz/Boo.php`` ``Acme\Bar\Baz`` +``library/bar/Foo.php`` ``Acme\Bar`` +``library/foo/bar/Foo.php`` ``Acme\Foo\Bar`` +``library/foo/Bar.php`` ``Acme\Foo`` +=========================== ================ To make your life easier, the ClassLoader component comes with a :class:`Symfony\\Component\\ClassLoader\\ClassMapGenerator` class that makes diff --git a/components/form/form_events.rst b/components/form/form_events.rst index 0f7aa4e3f32..d89e4ca5e89 100644 --- a/components/form/form_events.rst +++ b/components/form/form_events.rst @@ -61,13 +61,13 @@ The ``FormEvents::PRE_SET_DATA`` event is dispatched at the beginning of the :ref:`Form Events Information Table` -=============== ======== -Data Type Value -=============== ======== -Model data ``null`` -Normalized data ``null`` -View data ``null`` -=============== ======== +=============== ======== +Data Type Value +=============== ======== +Model data ``null`` +Normalized data ``null`` +View data ``null`` +=============== ======== .. caution:: @@ -96,13 +96,13 @@ the form. :ref:`Form Events Information Table` -=============== ==================================================== -Data Type Value -=============== ==================================================== -Model data Model data injected into ``setData()`` -Normalized data Model data transformed using a model transformer -View data Normalized data transformed using a view transformer -=============== ==================================================== +=============== ==================================================== +Data Type Value +=============== ==================================================== +Model data Model data injected into ``setData()`` +Normalized data Model data transformed using a model transformer +View data Normalized data transformed using a view transformer +=============== ==================================================== .. sidebar:: ``FormEvents::POST_SET_DATA`` in the Form component @@ -136,13 +136,13 @@ It can be used to: :ref:`Form Events Information Table` -=============== ======================================== -Data Type Value -=============== ======================================== -Model data Same as in ``FormEvents::POST_SET_DATA`` -Normalized data Same as in ``FormEvents::POST_SET_DATA`` -View data Same as in ``FormEvents::POST_SET_DATA`` -=============== ======================================== +=============== ======================================== +Data Type Value +=============== ======================================== +Model data Same as in ``FormEvents::POST_SET_DATA`` +Normalized data Same as in ``FormEvents::POST_SET_DATA`` +View data Same as in ``FormEvents::POST_SET_DATA`` +=============== ======================================== .. sidebar:: ``FormEvents::PRE_SUBMIT`` in the Form component @@ -164,13 +164,13 @@ It can be used to change data from the normalized representation of the data. :ref:`Form Events Information Table` -=============== =================================================================================== -Data Type Value -=============== =================================================================================== -Model data Same as in ``FormEvents::POST_SET_DATA`` -Normalized data Data from the request reverse-transformed from the request using a view transformer -View data Same as in ``FormEvents::POST_SET_DATA`` -=============== =================================================================================== +=============== =================================================================================== +Data Type Value +=============== =================================================================================== +Model data Same as in ``FormEvents::POST_SET_DATA`` +Normalized data Data from the request reverse-transformed from the request using a view transformer +View data Same as in ``FormEvents::POST_SET_DATA`` +=============== =================================================================================== .. caution:: @@ -194,13 +194,13 @@ It can be used to fetch data after denormalization. :ref:`Form Events Information Table` -=============== ============================================================= -Data Type Value -=============== ============================================================= -Model data Normalized data reverse-transformed using a model transformer -Normalized data Same as in ``FormEvents::POST_SUBMIT`` -View data Normalized data transformed using a view transformer -=============== ============================================================= +=============== ============================================================= +Data Type Value +=============== ============================================================= +Model data Normalized data reverse-transformed using a model transformer +Normalized data Same as in ``FormEvents::POST_SUBMIT`` +View data Normalized data transformed using a view transformer +=============== ============================================================= .. caution:: @@ -232,15 +232,15 @@ processed. .. _component-form-event-table: -====================== ============================= =============== -Name ``FormEvents`` Constant Event's Data -====================== ============================= =============== -``form.pre_set_data`` ``FormEvents::PRE_SET_DATA`` Model data -``form.post_set_data`` ``FormEvents::POST_SET_DATA`` Model data -``form.pre_bind`` ``FormEvents::PRE_SUBMIT`` Request data -``form.bind`` ``FormEvents::SUBMIT`` Normalized data -``form.post_bind`` ``FormEvents::POST_SUBMIT`` View data -====================== ============================= =============== +====================== ============================= =============== +Name ``FormEvents`` Constant Event's Data +====================== ============================= =============== +``form.pre_set_data`` ``FormEvents::PRE_SET_DATA`` Model data +``form.post_set_data`` ``FormEvents::POST_SET_DATA`` Model data +``form.pre_bind`` ``FormEvents::PRE_SUBMIT`` Request data +``form.bind`` ``FormEvents::SUBMIT`` Normalized data +``form.post_bind`` ``FormEvents::POST_SUBMIT`` View data +====================== ============================= =============== .. versionadded:: 2.3 Before Symfony 2.3, ``FormEvents::PRE_SUBMIT``, ``FormEvents::SUBMIT`` diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index cfc26067d2a..3fee78f30b7 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -575,16 +575,16 @@ each event has their own event object: .. _component-http-kernel-event-table: -================= ============================ =================================================================================== -Name ``KernelEvents`` Constant Argument Passed to the Listener -================= ============================ =================================================================================== -kernel.request ``KernelEvents::REQUEST`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent` -kernel.controller ``KernelEvents::CONTROLLER`` :class:`Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent` -kernel.view ``KernelEvents::VIEW`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent` -kernel.response ``KernelEvents::RESPONSE`` :class:`Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent` -kernel.terminate ``KernelEvents::TERMINATE`` :class:`Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent` -kernel.exception ``KernelEvents::EXCEPTION`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent` -================= ============================ =================================================================================== +================= ============================ =================================================================================== +Name ``KernelEvents`` Constant Argument Passed to the Listener +================= ============================ =================================================================================== +kernel.request ``KernelEvents::REQUEST`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent` +kernel.controller ``KernelEvents::CONTROLLER`` :class:`Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent` +kernel.view ``KernelEvents::VIEW`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForControllerResultEvent` +kernel.response ``KernelEvents::RESPONSE`` :class:`Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent` +kernel.terminate ``KernelEvents::TERMINATE`` :class:`Symfony\\Component\\HttpKernel\\Event\\PostResponseEvent` +kernel.exception ``KernelEvents::EXCEPTION`` :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent` +================= ============================ =================================================================================== .. _http-kernel-working-example: diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index 82327fd7e4f..3bc2f6453d6 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -376,9 +376,9 @@ error_bubbling Field Variables --------------- -============ =========== ======================================== -Variable Type Usage -============ =========== ======================================== -allow_add ``Boolean`` The value of the `allow_add`_ option. -allow_delete ``Boolean`` The value of the `allow_delete`_ option. -============ =========== ======================================== +============ =========== ======================================== +Variable Type Usage +============ =========== ======================================== +allow_add ``Boolean`` The value of the `allow_add`_ option. +allow_delete ``Boolean`` The value of the `allow_delete`_ option. +============ =========== ======================================== diff --git a/reference/forms/types/file.rst b/reference/forms/types/file.rst index 3751bdff8b1..36f6c2d06d0 100644 --- a/reference/forms/types/file.rst +++ b/reference/forms/types/file.rst @@ -110,8 +110,8 @@ The default value is ``null``. Form Variables -------------- -======== ========== =============================================================================== -Variable Type Usage -======== ========== =============================================================================== -type ``string`` The type variable is set to ``file``, in order to render as a file input field. -======== ========== =============================================================================== +======== ========== =============================================================================== +Variable Type Usage +======== ========== =============================================================================== +type ``string`` The type variable is set to ``file``, in order to render as a file input field. +======== ========== =============================================================================== diff --git a/reference/forms/types/money.rst b/reference/forms/types/money.rst index dd466595c3b..6db6ead9c06 100644 --- a/reference/forms/types/money.rst +++ b/reference/forms/types/money.rst @@ -121,10 +121,10 @@ The default value is ``''`` (the empty string). Form Variables -------------- -============= ========== =============================================================== -Variable Type Usage -============= ========== =============================================================== -money_pattern ``string`` The format to use to display the money, including the currency. -============= ========== =============================================================== +============= ========== =============================================================== +Variable Type Usage +============= ========== =============================================================== +money_pattern ``string`` The format to use to display the money, including the currency. +============= ========== =============================================================== .. _`3 letter ISO 4217 code`: http://en.wikipedia.org/wiki/ISO_4217 diff --git a/reference/forms/types/options/select_how_rendered.rst.inc b/reference/forms/types/options/select_how_rendered.rst.inc index e97205d768a..b131971ffff 100644 --- a/reference/forms/types/options/select_how_rendered.rst.inc +++ b/reference/forms/types/options/select_how_rendered.rst.inc @@ -4,11 +4,11 @@ Select Tag, Checkboxes or Radio Buttons This field may be rendered as one of several different HTML fields, depending on the ``expanded`` and ``multiple`` options: -======================================== ========= ========= -Element Type Expanded Multiple -======================================== ========= ========= -select tag ``false`` ``false`` -select tag (with ``multiple`` attribute) ``false`` ``true`` -radio buttons ``true`` ``false`` -checkboxes ``true`` ``true`` -======================================== ========= ========= +======================================== ========= ========= +Element Type Expanded Multiple +======================================== ========= ========= +select tag ``false`` ``false`` +select tag (with ``multiple`` attribute) ``false`` ``true`` +radio buttons ``true`` ``false`` +checkboxes ``true`` ``true`` +======================================== ========= ========= diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst index 01847034d84..63385eb328c 100644 --- a/reference/forms/types/submit.rst +++ b/reference/forms/types/submit.rst @@ -76,8 +76,8 @@ from the "Registration" are validated. Form Variables -------------- -======== =========== ============================================================== -Variable Type Usage -======== =========== ============================================================== -clicked ``Boolean`` Whether the button is clicked or not. -======== =========== ============================================================== +======== =========== ============================================================== +Variable Type Usage +======== =========== ============================================================== +clicked ``Boolean`` Whether the button is clicked or not. +======== =========== ============================================================== diff --git a/reference/forms/types/variables/check_or_radio_table.rst.inc b/reference/forms/types/variables/check_or_radio_table.rst.inc index b7f251c4810..ae137a3f200 100644 --- a/reference/forms/types/variables/check_or_radio_table.rst.inc +++ b/reference/forms/types/variables/check_or_radio_table.rst.inc @@ -1,5 +1,5 @@ -======== ============ ============================================ -Variable Type Usage -======== ============ ============================================ -checked ``Boolean`` Whether or not the current input is checked. -======== ============ ============================================ +======== ============ ============================================ +Variable Type Usage +======== ============ ============================================ +checked ``Boolean`` Whether or not the current input is checked. +======== ============ ============================================ From a2c626fec00d6a2bb5348e1545bed4a9bfb07662 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Fri, 14 Nov 2014 01:28:08 +0100 Subject: [PATCH 395/835] Bot fixes --- reference/configuration/assetic.rst | 4 +- reference/configuration/doctrine.rst | 97 +++++++++++++++++++--------- reference/configuration/kernel.rst | 15 +++-- reference/configuration/monolog.rst | 9 ++- reference/configuration/security.rst | 8 +-- 5 files changed, 85 insertions(+), 48 deletions(-) diff --git a/reference/configuration/assetic.rst b/reference/configuration/assetic.rst index f85346394a8..8126d03fd49 100644 --- a/reference/configuration/assetic.rst +++ b/reference/configuration/assetic.rst @@ -4,8 +4,8 @@ AsseticBundle Configuration ("assetic") ======================================= -Full default Configuration -~~~~~~~~~~~~~~~~~~~~~~~~~~ +Full Default Configuration +-------------------------- .. configuration-block:: diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index 6f8fbf73562..df6657f312e 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -5,7 +5,7 @@ DoctrineBundle Configuration ("doctrine") ========================================= -Full default configuration +Full Default Configuration -------------------------- .. configuration-block:: @@ -180,8 +180,10 @@ Full default configuration + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/doctrine + http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd"> @@ -209,16 +211,44 @@ Full default configuration Acme\HelloBundle\MyCustomType - - - + + + + + - Acme\HelloBundle\DQL\StringFunction - Acme\HelloBundle\DQL\NumericFunction - Acme\HelloBundle\DQL\DatetimeFunction + Acme\HelloBundle\DQL\StringFunction + + + Acme\HelloBundle\DQL\NumericFunction + + + + Acme\HelloBundle\DQL\DatetimeFunction + + + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/doctrine + http://symfony.com/schema/dic/doctrine/doctrine-1.0.xsd" + > + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/monolog + http://symfony.com/schema/dic/monolog/monolog-1.0.xsd" + > `". * ``check_path`` (type: ``string``, default: ``/login_check``) @@ -266,8 +266,8 @@ The Login Form and Process a separate firewall just for ``check_path`` URL). * ``use_forward`` (type: ``Boolean``, default: ``false``) - If you'd like the user to be forwarded to the login form instead of being - redirected, set this option to ``true``. + If you'd like the user to be forwarded to the login form instead of + being redirected, set this option to ``true``. * ``username_parameter`` (type: ``string``, default: ``_username``) This is the field name that you should give to the username field of From cb0865d0bf77c572d4bb441ab27de4171e889fa6 Mon Sep 17 00:00:00 2001 From: guiguiboy Date: Thu, 13 Nov 2014 18:10:26 +0100 Subject: [PATCH 396/835] Update doctrine.rst --- book/doctrine.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 8859cc2a9f4..871695ff21d 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -747,8 +747,8 @@ normal ``Query`` object, which can be used to get the result of the query. (``:price`` in the example above) as it prevents SQL injection attacks. The ``getResult()`` method returns an array of results. To get only one -result, you can use ``getSingleResult()`` (which throws exception there is no -result) or ``getOneOrNullResult()``:: +result, you can use ``getSingleResult()`` (which throws an exception if there +is no result) or ``getOneOrNullResult()``:: $product = $query->getOneOrNullResult(); From 9befcafa3552387c832d422c58ac4bbd4d0ea522 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sun, 16 Nov 2014 13:35:18 +0100 Subject: [PATCH 397/835] Fixed wrong indentation --- reference/configuration/doctrine.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index df6657f312e..1d0b00cde72 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -235,9 +235,9 @@ Full Default Configuration - Acme\HelloBundle\DQL\StringFunction + + Acme\HelloBundle\DQL\StringFunction + Acme\HelloBundle\DQL\NumericFunction From 7f40f23918bcd9871af2e8cbec9332124c2b350b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 17 Nov 2014 15:03:55 +0100 Subject: [PATCH 398/835] remove versionadded directives for old versions --- book/security.rst | 3 --- components/filesystem/introduction.rst | 5 ----- cookbook/security/entity_provider.rst | 4 ---- reference/forms/types/options/with_minutes.rst.inc | 3 --- reference/twig_reference.rst | 11 ++--------- 5 files changed, 2 insertions(+), 24 deletions(-) diff --git a/book/security.rst b/book/security.rst index f9d75d6f913..13d2057cf61 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1546,9 +1546,6 @@ do the following: ), )); -.. versionadded:: 2.2 - The BCrypt encoder was introduced in Symfony 2.2. - You can now calculate the hashed password either programmatically (e.g. ``password_hash('ryanpass', PASSWORD_BCRYPT, array('cost' => 12));``) or via some online tool. diff --git a/components/filesystem/introduction.rst b/components/filesystem/introduction.rst index 8990198aa41..44603184c0c 100644 --- a/components/filesystem/introduction.rst +++ b/components/filesystem/introduction.rst @@ -6,11 +6,6 @@ The Filesystem Component The Filesystem component provides basic utilities for the filesystem. -.. versionadded:: 2.1 - The Filesystem component was introduced in Symfony 2.1. Previously, the - ``Filesystem`` class was located in the HttpKernel component. - - .. tip:: A lock handler feature was introduce in symfony 2.6. diff --git a/cookbook/security/entity_provider.rst b/cookbook/security/entity_provider.rst index c2994ae7fd8..5975f2f4823 100644 --- a/cookbook/security/entity_provider.rst +++ b/cookbook/security/entity_provider.rst @@ -877,7 +877,3 @@ then instead of these properties being checked, your ``isEqualTo`` method is simply called, and you can check whatever properties you want. Unless you understand this, you probably *won't* need to implement this interface or worry about it. - -.. versionadded:: 2.1 - In Symfony 2.1, the ``equals`` method was removed from ``UserInterface`` - and the ``EquatableInterface`` was introduced in its place. diff --git a/reference/forms/types/options/with_minutes.rst.inc b/reference/forms/types/options/with_minutes.rst.inc index 48e6cdddcaf..60ad9b94515 100644 --- a/reference/forms/types/options/with_minutes.rst.inc +++ b/reference/forms/types/options/with_minutes.rst.inc @@ -1,9 +1,6 @@ with_minutes ~~~~~~~~~~~~ -.. versionadded:: 2.2 - The ``with_minutes`` option was introduced in Symfony 2.2. - **type**: ``Boolean`` **default**: ``true`` Whether or not to include minutes in the input. This will result in an additional diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 8a7f5ad33e3..8ca549abec6 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -24,10 +24,6 @@ Functions render ~~~~~~ -.. versionadded:: 2.2 - The ``render()`` function was introduced in Symfony 2.2. Prior, the - ``{% render %}`` tag was used and had a different signature. - .. code-block:: jinja {{ render(uri, options) }} @@ -40,7 +36,7 @@ render Renders the fragment for the given controller (using the `controller`_ function) or URI. For more information, see :ref:`templating-embedding-controller`. -The render strategy can be specified in the ``strategy`` key of the options. +The render strategy can be specified in the ``strategy`` key of the options. .. tip:: @@ -65,7 +61,7 @@ Generates an ESI tag when possible or falls back to the behaviour of .. tip:: The URI can be generated by other functions, like `path`_ and `url`_. - + .. tip:: The ``render_esi()`` function is an example of the shortcut functions @@ -76,9 +72,6 @@ Generates an ESI tag when possible or falls back to the behaviour of controller ~~~~~~~~~~ -.. versionadded:: 2.2 - The ``controller()`` function was introduced in Symfony 2.2. - .. code-block:: jinja {{ controller(controller, attributes, query) }} From bd99a313f5133012f914bf549aa9b547be73f310 Mon Sep 17 00:00:00 2001 From: Evan Owens Date: Mon, 17 Nov 2014 16:32:24 -0500 Subject: [PATCH 399/835] Fix spelling --- book/routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/routing.rst b/book/routing.rst index 459ce686f03..dac9fa376e7 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -1132,7 +1132,7 @@ see :ref:`route-parameters-controller-arguments`. .. tip:: The special ``$_route`` variable is set to the name of the route that was - matced. + matched. You can even add extra information to your route definition and access it within your controller. For more information on this topic, From e2b3527c5d376c67219c011b4d0442112bd63cab Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Sun, 16 Nov 2014 18:36:56 +0000 Subject: [PATCH 400/835] Fixed typos --- best_practices/business-logic.rst | 4 ++-- best_practices/web-assets.rst | 2 +- quick_tour/the_controller.rst | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index b77cbfab1d5..87592834191 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -15,7 +15,7 @@ Inside here, you can create whatever directories you want to organize things: .. code-block:: text - symfoy2-project/ + symfony2-project/ ├─ app/ ├─ src/ │ └─ AppBundle/ @@ -33,7 +33,7 @@ and put things there: .. code-block:: text - symfoy2-project/ + symfony2-project/ ├─ app/ ├─ src/ │ ├─ Acme/ diff --git a/best_practices/web-assets.rst b/best_practices/web-assets.rst index e77e3db7721..862e60987a5 100644 --- a/best_practices/web-assets.rst +++ b/best_practices/web-assets.rst @@ -50,7 +50,7 @@ tools like GruntJS. comfortable with frontend tools like GruntJS. `Assetic`_ is an asset manager capable of compiling assets developed with -a lot of different frontend technologies like LESS, Sass and CoffeScript. +a lot of different frontend technologies like LESS, Sass and CoffeeScript. Combining all your assets with Assetic is a matter of wrapping all the assets with a single Twig tag: diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index e669bb734e5..e6031ec8494 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -138,7 +138,7 @@ new ``hello.xml.twig`` template: Now, when you browse to ``http://localhost:8000/hello/fabien``, you'll see the regular HTML page because ``html`` is the default format. When visiting ``http://localhost:8000/hello/fabien.html`` you'll get again the HTML page, this -time because you explicitely asked for the ``html`` format. Lastly, if you visit +time because you explicitly asked for the ``html`` format. Lastly, if you visit ``http://localhost:8000/hello/fabien.xml`` you'll see the new XML template rendered in your browser. From 9a52d0023b33396c6f4426e74907de8d5a69df64 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 18 Nov 2014 08:47:09 +0100 Subject: [PATCH 401/835] add syntax highlighting for Varnish code blocks --- cookbook/cache/varnish.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index 5bc9c841e86..8be11c7f098 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -38,7 +38,7 @@ First, configure Varnish so that it advertises its ESI support by adding a ``Surrogate-Capability`` header to requests forwarded to the backend application: -.. code-block:: text +.. code-block:: varnish4 sub vcl_recv { // Add a Surrogate-Capability header to announce ESI support. @@ -137,7 +137,7 @@ proxy before it has expired, it adds complexity to your caching setup. Varnish can be configured to accept a special HTTP ``PURGE`` method that will invalidate the cache for a given resource: -.. code-block:: text +.. code-block:: varnish4 /* Connect to the backend server @@ -186,7 +186,7 @@ that will invalidate the cache for a given resource: You must protect the ``PURGE`` HTTP method somehow to avoid random people purging your cached data. You can do this by setting up an access list: - .. code-block:: text + .. code-block:: varnish4 /* Connect to the backend server @@ -252,7 +252,7 @@ is 80 and not 8080. If this header weren't set properly, Symfony may append ``8080`` when generating absolute URLs: -.. code-block:: text +.. code-block:: varnish4 sub vcl_recv { if (req.http.X-Forwarded-Proto == "https" ) { From 2135b824946a4a0bdca38fd7ae419ac59f816f31 Mon Sep 17 00:00:00 2001 From: "Andrew M." Date: Tue, 18 Nov 2014 11:00:05 +0100 Subject: [PATCH 402/835] Change: ConsoleTerminateListener => ErrorLoggerListener --- cookbook/console/logging.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cookbook/console/logging.rst b/cookbook/console/logging.rst index 5de937a3ffb..61f54d3583c 100644 --- a/cookbook/console/logging.rst +++ b/cookbook/console/logging.rst @@ -190,7 +190,7 @@ First configure a listener for console terminate events in the service container # app/config/services.yml services: kernel.listener.command_dispatch: - class: Acme\DemoBundle\EventListener\ConsoleTerminateListener + class: Acme\DemoBundle\EventListener\ErrorLoggerListener arguments: logger: "@logger" tags: @@ -205,7 +205,7 @@ First configure a listener for console terminate events in the service container xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -218,28 +218,28 @@ First configure a listener for console terminate events in the service container use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - $definitionConsoleTerminateListener = new Definition( - 'Acme\DemoBundle\EventListener\ConsoleTerminateListener', + $definitionErrorLoggerListener = new Definition( + 'Acme\DemoBundle\EventListener\ErrorLoggerListener', array(new Reference('logger')) ); - $definitionConsoleTerminateListener->addTag( + $definitionErrorLoggerListener->addTag( 'kernel.event_listener', array('event' => 'console.terminate') ); $container->setDefinition( 'kernel.listener.command_dispatch', - $definitionConsoleTerminateListener + $definitionErrorLoggerListener ); Then implement the actual listener:: - // src/Acme/DemoBundle/EventListener/ConsoleTerminateListener.php + // src/Acme/DemoBundle/EventListener/ErrorLoggerListener.php namespace Acme\DemoBundle\EventListener; use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Psr\Log\LoggerInterface; - class ConsoleTerminateListener + class ErrorLoggerListener { private $logger; From 363e38fc2a3ae9c5b4f9f07a8cb0ce9cbd5ba5a3 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 18 Nov 2014 17:13:19 +0100 Subject: [PATCH 403/835] Added documentation about the DebugFormatter helper --- .../console/helpers/debug_formatter.rst | 109 ++++++++++++++++++ components/console/helpers/index.rst | 1 + components/console/helpers/map.rst.inc | 1 + 3 files changed, 111 insertions(+) create mode 100644 components/console/helpers/debug_formatter.rst diff --git a/components/console/helpers/debug_formatter.rst b/components/console/helpers/debug_formatter.rst new file mode 100644 index 00000000000..ba584b349fc --- /dev/null +++ b/components/console/helpers/debug_formatter.rst @@ -0,0 +1,109 @@ +.. index:: + single: Console Helpers; DebugFormatter Helper + +DebugFormatter Helper +===================== + +.. versionadded:: 2.6 + The DebugFormatter helper was introduced in Symfony 2.6. + +The :class:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper` provides +functions to output debug information when running an external program, for +instance a process or HTTP request. It is included in the default helper set, +which you can get by calling +:method:`Symfony\\Component\\Console\\Command\\Command::getHelperSet`:: + + $debugFormatter = $this->getHelper('debug_formatter'); + +The formatter only formats strings, which you can use to output to the console, +but also to log the information or anything else. + +All methods of this helper have an identifier as the first argument. This is an +unique value for each program. This way, the helper can debug information for +multiple programs at the same time. When using the +:doc:`Process component `, you probably want to use +:phpfunction:`spl_object_hash`. + +.. tip:: + + This information is often too verbose to show by default. You can use + :ref:`verbosity levels ` to only show it when in + debugging mode (``-vvv``). + +Starting a Program +------------------ + +As soon as you start a program, you can use +:method:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper::start` to +display information that the program is started:: + + // ... + $process = new Process(...); + $process->run(); + + $output->writeln($debugFormatter->start(spl_object_hash($process), 'Some process description')); + +This will output: + +.. code-block:: text + + RUN Some process description + +You can tweak the prefix using the third argument:: + + $output->writeln($debugFormatter->start(spl_object_hash($process), 'Some process description', 'STARTED'); + // will output: + // STARTED Some process description + +Output Progress Information +--------------------------- + +Some programs give output while they are running. This information can be shown +using +:method:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper::progress`:: + + // ... + $output->writeln($debugFormatter->progress(spl_object_hash($process), $buffer, Process::ERR === $type)); + +In case of success, this will output: + +.. code-block:: text + + OUT The output of the process + +And this in case of failure: + +.. code-block:: text + + ERR The output of the process + +The third argument is a boolean which tells the function if the output is error +output or not. When ``true``, the output is considered error output. + +The fourth and fifth argument allow you to override the prefix for respectively +the normal output and error output. + +Stopping a Program +------------------ + +When a program is stopped, you can use +:method:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper::progress` +to notify this to the users:: + + // ... + $output->writeln($debugFormatter->progress(spl_object_hash($process), 'Some command description', $process->isSuccesfull())); + +This will output: + +.. code-block:: text + + RES Some command description + +In case of failure, this will be in red and in case of success it will be green. + +Using multiple Programs +----------------------- + +As said before, you can also use the helper to display more programs at the +same time. Information about different programs will be shown in different +colors, to make it clear which output belongs to which command. diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index 909671fb08c..96034459478 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -7,6 +7,7 @@ The Console Helpers .. toctree:: :hidden: + debug_formatter dialoghelper formatterhelper processhelper diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index 15e6e9f8da9..ea7e472dca8 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,3 +1,4 @@ +* :doc:`/components/console/helpers/debug_formatter` (new in 2.6) * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` * :doc:`/components/console/helpers/processhelper` From 54c44eaa5fa08e2424159a8b7d3511d272a1eb56 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 18 Nov 2014 17:28:23 +0100 Subject: [PATCH 404/835] Renamed empty_value to placeholder --- cookbook/form/create_custom_field_type.rst | 8 ++++++-- cookbook/form/dynamic_form_modification.rst | 12 ++++++++---- reference/forms/types/birthday.rst | 4 ++-- reference/forms/types/choice.rst | 8 ++++---- reference/forms/types/country.rst | 4 ++-- reference/forms/types/currency.rst | 4 ++-- reference/forms/types/date.rst | 14 +++++++++----- reference/forms/types/datetime.rst | 4 ++-- reference/forms/types/entity.rst | 4 ++-- reference/forms/types/language.rst | 4 ++-- reference/forms/types/locale.rst | 4 ++-- .../types/options/checkbox_empty_data.rst.inc | 2 +- reference/forms/types/options/empty_data.rst.inc | 2 +- .../{empty_value.rst.inc => placeholder.rst.inc} | 12 ++++++++---- reference/forms/types/time.rst | 4 ++-- reference/forms/types/timezone.rst | 4 ++-- 16 files changed, 55 insertions(+), 39 deletions(-) rename reference/forms/types/options/{empty_value.rst.inc => placeholder.rst.inc} (73%) diff --git a/cookbook/form/create_custom_field_type.rst b/cookbook/form/create_custom_field_type.rst index aa5420d32a6..5e390008111 100644 --- a/cookbook/form/create_custom_field_type.rst +++ b/cookbook/form/create_custom_field_type.rst @@ -240,7 +240,7 @@ new instance of the type in one of your forms:: public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('gender_code', new GenderType(), array( - 'empty_value' => 'Choose a gender', + 'placeholder' => 'Choose a gender', )); } } @@ -249,6 +249,10 @@ But this only works because the ``GenderType()`` is very simple. What if the gender codes were stored in configuration or in a database? The next section explains how more complex field types solve this problem. +.. versionadded:: 2.6 + The ``placeholder`` option was introduced in Symfony 2.6 in favor of + ``empty_value``, which is available prior to 2.6. + .. _form-cookbook-form-field-service: Creating your Field Type as a Service @@ -378,7 +382,7 @@ configuration, using the field is now much easier:: public function buildForm(FormBuilderInterface $builder, array $options) { $builder->add('gender_code', 'gender', array( - 'empty_value' => 'Choose a gender', + 'placeholder' => 'Choose a gender', )); } } diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index 6d3b4cdb970..d939cd6321f 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -470,7 +470,7 @@ sport like this:: $builder ->add('sport', 'entity', array( 'class' => 'AcmeDemoBundle:Sport', - 'empty_value' => '', + 'placeholder' => '', )) ; @@ -487,7 +487,7 @@ sport like this:: $form->add('position', 'entity', array( 'class' => 'AcmeDemoBundle:Position', - 'empty_value' => '', + 'placeholder' => '', 'choices' => $positions, )); } @@ -497,6 +497,10 @@ sport like this:: // ... } +.. versionadded:: 2.6 + The ``placeholder`` option was introduced in Symfony 2.6 in favor of + ``empty_value``, which is available prior to 2.6. + When you're building this form to display to the user for the first time, then this example works perfectly. @@ -537,7 +541,7 @@ The type would now look like:: $builder ->add('sport', 'entity', array( 'class' => 'AcmeDemoBundle:Sport', - 'empty_value' => '', + 'placeholder' => '', )); ; @@ -546,7 +550,7 @@ The type would now look like:: $form->add('position', 'entity', array( 'class' => 'AcmeDemoBundle:Position', - 'empty_value' => '', + 'placeholder' => '', 'choices' => $positions, )); }; diff --git a/reference/forms/types/birthday.rst b/reference/forms/types/birthday.rst index d77551c3b29..2926a22b369 100644 --- a/reference/forms/types/birthday.rst +++ b/reference/forms/types/birthday.rst @@ -25,7 +25,7 @@ option defaults to 120 years ago to the current year. | Inherited options | from the :doc:`date ` type: | | | | | | - `days`_ | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `format`_ | | | - `input`_ | | | - `model_timezone`_ | @@ -66,7 +66,7 @@ These options inherit from the :doc:`date ` type: .. include:: /reference/forms/types/options/days.rst.inc -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/date_format.rst.inc diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index a5757116817..a35adba892e 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -15,7 +15,7 @@ option. +-------------+------------------------------------------------------------------------------+ | Options | - `choices`_ | | | - `choice_list`_ | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `expanded`_ | | | - `multiple`_ | | | - `preferred_choices`_ | @@ -122,7 +122,7 @@ With this option you can also allow float values to be selected as data. 'choice_list' => new ChoiceList(array(1, 0.5), array('Full', 'Half')) )); -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/expanded.rst.inc @@ -204,13 +204,13 @@ Field Variables +------------------------+--------------+-------------------------------------------------------------------+ | separator | ``string`` | The separator to use between choice groups. | +------------------------+--------------+-------------------------------------------------------------------+ -| empty_value | ``mixed`` | The empty value if not already in the list, otherwise | +| placeholder | ``mixed`` | The empty value if not already in the list, otherwise | | | | ``null``. | +------------------------+--------------+-------------------------------------------------------------------+ | is_selected | ``callable`` | A callable which takes a ``ChoiceView`` and the selected value(s) | | | | and returns whether the choice is in the selected value(s). | +------------------------+--------------+-------------------------------------------------------------------+ -| empty_value_in_choices | ``Boolean`` | Whether the empty value is in the choice list. | +| placeholder_in_choices | ``Boolean`` | Whether the empty value is in the choice list. | +------------------------+--------------+-------------------------------------------------------------------+ .. tip:: diff --git a/reference/forms/types/country.rst b/reference/forms/types/country.rst index c50ac519a48..fa19b1d31bd 100644 --- a/reference/forms/types/country.rst +++ b/reference/forms/types/country.rst @@ -27,7 +27,7 @@ you should just use the ``choice`` type directly. +-------------+-----------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `error_bubbling`_ | | | - `error_mapping`_ | | | - `expanded`_ | @@ -66,7 +66,7 @@ Inherited Options These options inherit from the :doc:`choice ` type: -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/error_bubbling.rst.inc diff --git a/reference/forms/types/currency.rst b/reference/forms/types/currency.rst index f009d726993..552b488d00a 100644 --- a/reference/forms/types/currency.rst +++ b/reference/forms/types/currency.rst @@ -21,7 +21,7 @@ should just use the ``choice`` type directly. +-------------+------------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `error_bubbling`_ | | | - `expanded`_ | | | - `multiple`_ | @@ -58,7 +58,7 @@ Inherited Options These options inherit from the :doc:`choice` type: -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/error_bubbling.rst.inc diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index 994ff6156b3..2b4db7ef336 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -20,7 +20,7 @@ day, and year) or three select boxes (see the `widget`_ option). | Rendered as | single text box or three select fields | +----------------------+-----------------------------------------------------------------------------+ | Options | - `days`_ | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `format`_ | | | - `input`_ | | | - `model_timezone`_ | @@ -82,23 +82,27 @@ Field Options .. include:: /reference/forms/types/options/days.rst.inc -empty_value +placeholder ~~~~~~~~~~~ +.. versionadded:: 2.6 + The ``placeholder`` option was introduced in Symfony 2.6 in favor of + ``empty_value``, which is available prior to 2.6. + **type**: ``string`` or ``array`` If your widget option is set to ``choice``, then this field will be represented -as a series of ``select`` boxes. The ``empty_value`` option can be used to +as a series of ``select`` boxes. The ``placeholder`` option can be used to add a "blank" entry to the top of each select box:: $builder->add('dueDate', 'date', array( - 'empty_value' => '', + 'placeholder' => '', )); Alternatively, you can specify a string to be displayed for the "blank" value:: $builder->add('dueDate', 'date', array( - 'empty_value' => array('year' => 'Year', 'month' => 'Month', 'day' => 'Day') + 'placeholder' => array('year' => 'Year', 'month' => 'Month', 'day' => 'Day') )); .. _reference-forms-type-date-format: diff --git a/reference/forms/types/datetime.rst b/reference/forms/types/datetime.rst index fc58493207f..e07b4dc9a55 100644 --- a/reference/forms/types/datetime.rst +++ b/reference/forms/types/datetime.rst @@ -18,7 +18,7 @@ data can be a ``DateTime`` object, a string, a timestamp or an array. | Options | - `date_format`_ | | | - `date_widget`_ | | | - `days`_ | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `format`_ | | | - `hours`_ | | | - `input`_ | @@ -67,7 +67,7 @@ Defines the ``widget`` option for the :doc:`date ` .. include:: /reference/forms/types/options/days.rst.inc -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc format ~~~~~~ diff --git a/reference/forms/types/entity.rst b/reference/forms/types/entity.rst index 17728fa3be9..73dfae62f92 100644 --- a/reference/forms/types/entity.rst +++ b/reference/forms/types/entity.rst @@ -24,7 +24,7 @@ objects from the database. +-------------+------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type: | | options | | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `expanded`_ | | | - `multiple`_ | | | - `preferred_choices`_ | @@ -194,7 +194,7 @@ Inherited Options These options inherit from the :doc:`choice ` type: -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/expanded.rst.inc diff --git a/reference/forms/types/language.rst b/reference/forms/types/language.rst index 120cb37b11c..b503f400ae9 100644 --- a/reference/forms/types/language.rst +++ b/reference/forms/types/language.rst @@ -28,7 +28,7 @@ you should just use the ``choice`` type directly. +-------------+------------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `error_bubbling`_ | | | - `error_mapping`_ | | | - `expanded`_ | @@ -67,7 +67,7 @@ Inherited Options These options inherit from the :doc:`choice ` type: -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/error_bubbling.rst.inc diff --git a/reference/forms/types/locale.rst b/reference/forms/types/locale.rst index e8610495901..f6b33c0af39 100644 --- a/reference/forms/types/locale.rst +++ b/reference/forms/types/locale.rst @@ -30,7 +30,7 @@ you should just use the ``choice`` type directly. +-------------+------------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `error_bubbling`_ | | | - `error_mapping`_ | | | - `expanded`_ | @@ -69,7 +69,7 @@ Inherited Options These options inherit from the :doc:`choice ` type: -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/error_bubbling.rst.inc diff --git a/reference/forms/types/options/checkbox_empty_data.rst.inc b/reference/forms/types/options/checkbox_empty_data.rst.inc index 9e3c2e79427..62bd00c58d0 100644 --- a/reference/forms/types/options/checkbox_empty_data.rst.inc +++ b/reference/forms/types/options/checkbox_empty_data.rst.inc @@ -3,6 +3,6 @@ empty_data **type**: ``string`` **default**: ``mixed`` -This option determines what value the field will return when the ``empty_value`` +This option determines what value the field will return when the ``placeholder`` choice is selected. In the checkbox and the radio type, the value of ``empty_data`` is overriden by the value returned by the data transformer (see :doc:`/cookbook/form/data_transformers`). diff --git a/reference/forms/types/options/empty_data.rst.inc b/reference/forms/types/options/empty_data.rst.inc index 34ca9cb878c..3d5e3a21e85 100644 --- a/reference/forms/types/options/empty_data.rst.inc +++ b/reference/forms/types/options/empty_data.rst.inc @@ -23,7 +23,7 @@ selected, you can do it like this: 'f' => 'Female' ), 'required' => false, - 'empty_value' => 'Choose your gender', + 'placeholder' => 'Choose your gender', 'empty_data' => null )); diff --git a/reference/forms/types/options/empty_value.rst.inc b/reference/forms/types/options/placeholder.rst.inc similarity index 73% rename from reference/forms/types/options/empty_value.rst.inc rename to reference/forms/types/options/placeholder.rst.inc index 19adfa5effe..143f8d60c12 100644 --- a/reference/forms/types/options/empty_value.rst.inc +++ b/reference/forms/types/options/placeholder.rst.inc @@ -1,6 +1,10 @@ -empty_value +placeholder ~~~~~~~~~~~ +.. versionadded:: 2.6 + The ``placeholder`` option was introduced in Symfony 2.6 in favor of + ``empty_value``, which is available prior to 2.6. + .. versionadded:: 2.3 Since Symfony 2.3, empty values are also supported if the ``expanded`` option is set to true. @@ -14,16 +18,16 @@ will appear at the top of a select widget. This option only applies if the * Add an empty value with "Choose an option" as the text:: $builder->add('states', 'choice', array( - 'empty_value' => 'Choose an option', + 'placeholder' => 'Choose an option', )); * Guarantee that no "empty" value option is displayed:: $builder->add('states', 'choice', array( - 'empty_value' => false, + 'placeholder' => false, )); -If you leave the ``empty_value`` option unset, then a blank (with no text) +If you leave the ``placeholder`` option unset, then a blank (with no text) option will automatically be added if and only if the ``required`` option is false:: diff --git a/reference/forms/types/time.rst b/reference/forms/types/time.rst index 71b3af1cdb5..2f04db3200b 100644 --- a/reference/forms/types/time.rst +++ b/reference/forms/types/time.rst @@ -15,7 +15,7 @@ as a ``DateTime`` object, a string, a timestamp or an array. +----------------------+-----------------------------------------------------------------------------+ | Rendered as | can be various tags (see below) | +----------------------+-----------------------------------------------------------------------------+ -| Options | - `empty_value`_ | +| Options | - `placeholder`_ | | | - `hours`_ | | | - `input`_ | | | - `minutes`_ | @@ -77,7 +77,7 @@ values. Field Options ------------- -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/hours.rst.inc diff --git a/reference/forms/types/timezone.rst b/reference/forms/types/timezone.rst index 175345b0330..e7e99266c55 100644 --- a/reference/forms/types/timezone.rst +++ b/reference/forms/types/timezone.rst @@ -23,7 +23,7 @@ you should just use the ``choice`` type directly. +-------------+------------------------------------------------------------------------+ | Inherited | from the :doc:`choice ` type | | options | | -| | - `empty_value`_ | +| | - `placeholder`_ | | | - `expanded`_ | | | - `multiple`_ | | | - `preferred_choices`_ | @@ -62,7 +62,7 @@ Inherited Options These options inherit from the :doc:`choice ` type: -.. include:: /reference/forms/types/options/empty_value.rst.inc +.. include:: /reference/forms/types/options/placeholder.rst.inc .. include:: /reference/forms/types/options/expanded.rst.inc From 22069f4523dfa9ed682e4e0367aeabd3fe268d32 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 18 Nov 2014 19:32:13 +0100 Subject: [PATCH 405/835] Documented html5 option --- reference/forms/types/date.rst | 3 +++ reference/forms/types/datetime.rst | 3 +++ reference/forms/types/options/html5.rst.inc | 13 +++++++++++++ reference/forms/types/time.rst | 3 +++ 4 files changed, 22 insertions(+) create mode 100644 reference/forms/types/options/html5.rst.inc diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index 994ff6156b3..0ac6b5496e5 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -22,6 +22,7 @@ day, and year) or three select boxes (see the `widget`_ option). | Options | - `days`_ | | | - `empty_value`_ | | | - `format`_ | +| | - `html5`_ | | | - `input`_ | | | - `model_timezone`_ | | | - `months`_ | @@ -105,6 +106,8 @@ Alternatively, you can specify a string to be displayed for the "blank" value:: .. include:: /reference/forms/types/options/date_format.rst.inc +.. include:: /reference/forms/types/options/html5.rst.inc + .. _form-reference-date-input: .. include:: /reference/forms/types/options/date_input.rst.inc diff --git a/reference/forms/types/datetime.rst b/reference/forms/types/datetime.rst index fc58493207f..a51f666e176 100644 --- a/reference/forms/types/datetime.rst +++ b/reference/forms/types/datetime.rst @@ -21,6 +21,7 @@ data can be a ``DateTime`` object, a string, a timestamp or an array. | | - `empty_value`_ | | | - `format`_ | | | - `hours`_ | +| | - `html5`_ | | | - `input`_ | | | - `minutes`_ | | | - `model_timezone`_ | @@ -82,6 +83,8 @@ field to be rendered as an ``input`` field with ``type="datetime"``. .. include:: /reference/forms/types/options/hours.rst.inc +.. include:: /reference/forms/types/options/html5.rst.inc + input ~~~~~ diff --git a/reference/forms/types/options/html5.rst.inc b/reference/forms/types/options/html5.rst.inc new file mode 100644 index 00000000000..fc2ad0f57be --- /dev/null +++ b/reference/forms/types/options/html5.rst.inc @@ -0,0 +1,13 @@ +html5 +~~~~~ + +.. versionadded:: 2.6 + The ``html5`` option was introduced in Symfony 2.6. + +**type**: ``boolean`` **default**: ``true`` + +If this is set to ``true`` (the default), it'll use the HTML5 type (date, time +or datetime) to render the field. When set to ``false``, it'll use the text type. + +This is usefull when you want to use a custom JavaScript datapicker, which +often require a text type instead of a HTML5 type. diff --git a/reference/forms/types/time.rst b/reference/forms/types/time.rst index 71b3af1cdb5..0221cf8d54e 100644 --- a/reference/forms/types/time.rst +++ b/reference/forms/types/time.rst @@ -17,6 +17,7 @@ as a ``DateTime`` object, a string, a timestamp or an array. +----------------------+-----------------------------------------------------------------------------+ | Options | - `empty_value`_ | | | - `hours`_ | +| | - `html5`_ | | | - `input`_ | | | - `minutes`_ | | | - `model_timezone`_ | @@ -81,6 +82,8 @@ Field Options .. include:: /reference/forms/types/options/hours.rst.inc +.. include:: /reference/forms/types/options/html5.rst.inc + input ~~~~~ From 9c9742b3247d99ce0323a10f97fb5abe30bcd75a Mon Sep 17 00:00:00 2001 From: Bartek Chmura Date: Tue, 18 Nov 2014 19:41:59 +0100 Subject: [PATCH 406/835] Terrible mistake! Comma instead of semicolon... ;-) --- best_practices/business-logic.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index 87592834191..23abb880c8e 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -299,7 +299,7 @@ Then, enable the bundle in ``AppKernel.php``, but only for the ``dev`` and if (in_array($this->getEnvironment(), array('dev', 'test'))) { // ... - $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(), + $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(); } return $bundles; From c55319330d7967dd0b1fb5f504b35433629b0702 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 19 Nov 2014 00:55:54 +0100 Subject: [PATCH 407/835] Documented expressionlanguage extensibility --- components/expression_language/extending.rst | 65 +++++++++++++++----- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst index 5f1f743d631..9a25be53736 100644 --- a/components/expression_language/extending.rst +++ b/components/expression_language/extending.rst @@ -51,24 +51,31 @@ an ``arguments`` variable as their first argument, which is equal to the second argument to ``evaluate()`` or ``compile()`` (e.g. the "values" when evaluating or the "names" if compiling). -Creating a new ExpressionLanguage Class ---------------------------------------- +Using Expression Providers +-------------------------- -When you use the ``ExpressionLanguage`` class in your library, it's recommend -to create a new ``ExpressionLanguage`` class and register the functions there. -Override ``registerFunctions`` to add your own functions:: +.. versionadded:: 2.6 + Expression providers were introduced in Symfony 2.6. - namespace Acme\AwesomeLib\ExpressionLanguage; +When you use the ``ExpressionLanguage`` class in your library, you often want +to add custom functions. To do so, you can create a new expression provider by +creating a class that implements +:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface`. - use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; +This interface requires one method: +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface::getFunctions`. +This method returns an array of expression functions (instances of +:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction`) to register. - class ExpressionLanguage extends BaseExpressionLanguage - { - protected function registerFunctions() - { - parent::registerFunctions(); // do not forget to also register core functions +.. code-block:: php - $this->register('lowercase', function ($str) { + use Symfony\Component\ExpressionLanguage\ExpressionFunction; + use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + + class StringExpressionLanguageProvider implements ExpressionFunctionProviderInterface + { + return array( + new ExpressionFunction('lowercase', function ($str) { return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); }, function ($arguments, $str) { if (!is_string($str)) { @@ -77,5 +84,35 @@ Override ``registerFunctions`` to add your own functions:: return strtolower($str); }); - } + ); } + +You can register providers using +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::registerProvider` +or by using the second argument of the constructor:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + // using the constructor + $language = new ExpressionLanguage(null, array(...)); + + $language->registerProvider(new StringExpressionLanguageProvider()); + +.. tip:: + + It is recommended to create your own ``ExpressionLanguage`` class in your + library. Now you can add the extension by overriding the constructor:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; + use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; + + class ExpressionLanguage extends BaseExpressionLanguage + { + public function __construct(ParserCacheInterface $parser = null, array $providers = array()) + { + // prepend the default provider to let users override it easily + array_unshift($providers, new MyExpressionLanguageProvider()); + + parent::__construct($parser, $providers); + } + } From ef4ca4255544d7b6df0aca566c918822fd3d17e1 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 19 Nov 2014 01:03:16 +0100 Subject: [PATCH 408/835] Documented new tags --- reference/dic_tags.rst | 142 ++++++++++++++++++++--------------------- 1 file changed, 69 insertions(+), 73 deletions(-) diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 0cd97bff55e..472cb2600e7 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -12,79 +12,47 @@ section of the Service Container chapter. Below is information about all of the tags available inside Symfony. There may also be tags in other bundles you use that aren't listed here. -+-----------------------------------+---------------------------------------------------------------------------+ -| Tag Name | Usage | -+===================================+===========================================================================+ -| `assetic.asset`_ | Register an asset to the current asset manager | -+-----------------------------------+---------------------------------------------------------------------------+ -| `assetic.factory_worker`_ | Add a factory worker | -+-----------------------------------+---------------------------------------------------------------------------+ -| `assetic.filter`_ | Register a filter | -+-----------------------------------+---------------------------------------------------------------------------+ -| `assetic.formula_loader`_ | Add a formula loader to the current asset manager | -+-----------------------------------+---------------------------------------------------------------------------+ -| `assetic.formula_resource`_ | Adds a resource to the current asset manager | -+-----------------------------------+---------------------------------------------------------------------------+ -| `assetic.templating.php`_ | Remove this service if PHP templating is disabled | -+-----------------------------------+---------------------------------------------------------------------------+ -| `assetic.templating.twig`_ | Remove this service if Twig templating is disabled | -+-----------------------------------+---------------------------------------------------------------------------+ -| `console.command`_ | Add a command | -+-----------------------------------+---------------------------------------------------------------------------+ -| `data_collector`_ | Create a class that collects custom data for the profiler | -+-----------------------------------+---------------------------------------------------------------------------+ -| `doctrine.event_listener`_ | Add a Doctrine event listener | -+-----------------------------------+---------------------------------------------------------------------------+ -| `doctrine.event_subscriber`_ | Add a Doctrine event subscriber | -+-----------------------------------+---------------------------------------------------------------------------+ -| `form.type`_ | Create a custom form field type | -+-----------------------------------+---------------------------------------------------------------------------+ -| `form.type_extension`_ | Create a custom "form extension" | -+-----------------------------------+---------------------------------------------------------------------------+ -| `form.type_guesser`_ | Add your own logic for "form type guessing" | -+-----------------------------------+---------------------------------------------------------------------------+ -| `kernel.cache_clearer`_ | Register your service to be called during the cache clearing process | -+-----------------------------------+---------------------------------------------------------------------------+ -| `kernel.cache_warmer`_ | Register your service to be called during the cache warming process | -+-----------------------------------+---------------------------------------------------------------------------+ -| `kernel.event_listener`_ | Listen to different events/hooks in Symfony | -+-----------------------------------+---------------------------------------------------------------------------+ -| `kernel.event_subscriber`_ | To subscribe to a set of different events/hooks in Symfony | -+-----------------------------------+---------------------------------------------------------------------------+ -| `kernel.fragment_renderer`_ | Add new HTTP content rendering strategies | -+-----------------------------------+---------------------------------------------------------------------------+ -| `monolog.logger`_ | Logging with a custom logging channel | -+-----------------------------------+---------------------------------------------------------------------------+ -| `monolog.processor`_ | Add a custom processor for logging | -+-----------------------------------+---------------------------------------------------------------------------+ -| `routing.loader`_ | Register a custom service that loads routes | -+-----------------------------------+---------------------------------------------------------------------------+ -| `security.voter`_ | Add a custom voter to Symfony's authorization logic | -+-----------------------------------+---------------------------------------------------------------------------+ -| `security.remember_me_aware`_ | To allow remember me authentication | -+-----------------------------------+---------------------------------------------------------------------------+ -| `serializer.encoder`_ | Register a new encoder in the ``serializer`` service | -+-----------------------------------+---------------------------------------------------------------------------+ -| `serializer.normalizer`_ | Register a new normalizer in the ``serializer`` service | -+-----------------------------------+---------------------------------------------------------------------------+ -| `swiftmailer.default.plugin`_ | Register a custom SwiftMailer Plugin | -+-----------------------------------+---------------------------------------------------------------------------+ -| `templating.helper`_ | Make your service available in PHP templates | -+-----------------------------------+---------------------------------------------------------------------------+ -| `translation.loader`_ | Register a custom service that loads translations | -+-----------------------------------+---------------------------------------------------------------------------+ -| `translation.extractor`_ | Register a custom service that extracts translation messages from a file | -+-----------------------------------+---------------------------------------------------------------------------+ -| `translation.dumper`_ | Register a custom service that dumps translation messages | -+-----------------------------------+---------------------------------------------------------------------------+ -| `twig.extension`_ | Register a custom Twig Extension | -+-----------------------------------+---------------------------------------------------------------------------+ -| `twig.loader`_ | Register a custom service that loads Twig templates | -+-----------------------------------+---------------------------------------------------------------------------+ -| `validator.constraint_validator`_ | Create your own custom validation constraint | -+-----------------------------------+---------------------------------------------------------------------------+ -| `validator.initializer`_ | Register a service that initializes objects before validation | -+-----------------------------------+---------------------------------------------------------------------------+ +======================================== ======================================================================== +Tag Name Usage +======================================== ======================================================================== +`assetic.asset`_ Register an asset to the current asset manager +`assetic.factory_worker`_ Add a factory worker +`assetic.filter`_ Register a filter +`assetic.formula_loader`_ Add a formula loader to the current asset manager +`assetic.formula_resource`_ Adds a resource to the current asset manager +`assetic.templating.php`_ Remove this service if PHP templating is disabled +`assetic.templating.twig`_ Remove this service if Twig templating is disabled +`console.command`_ Add a command +`data_collector`_ Create a class that collects custom data for the profiler +`doctrine.event_listener`_ Add a Doctrine event listener +`doctrine.event_subscriber`_ Add a Doctrine event subscriber +`form.type`_ Create a custom form field type +`form.type_extension`_ Create a custom "form extension" +`form.type_guesser`_ Add your own logic for "form type guessing" +`kernel.cache_clearer`_ Register your service to be called during the cache clearing process +`kernel.cache_warmer`_ Register your service to be called during the cache warming process +`kernel.event_listener`_ Listen to different events/hooks in Symfony +`kernel.event_subscriber`_ To subscribe to a set of different events/hooks in Symfony +`kernel.fragment_renderer`_ Add new HTTP content rendering strategies +`monolog.logger`_ Logging with a custom logging channel +`monolog.processor`_ Add a custom processor for logging +`routing.loader`_ Register a custom service that loads routes +`routing.expression_language_provider`_ Register a provider for expression language functions in routing +`security.expression_language_provider`_ Register a provider for expression language functions in security +`security.voter`_ Add a custom voter to Symfony's authorization logic +`security.remember_me_aware`_ To allow remember me authentication +`serializer.encoder`_ Register a new encoder in the ``serializer`` service +`serializer.normalizer`_ Register a new normalizer in the ``serializer`` service +`swiftmailer.default.plugin`_ Register a custom SwiftMailer Plugin +`templating.helper`_ Make your service available in PHP templates +`translation.loader`_ Register a custom service that loads translations +`translation.extractor`_ Register a custom service that extracts translation messages from a file +`translation.dumper`_ Register a custom service that dumps translation messages +`twig.extension`_ Register a custom Twig Extension +`twig.loader`_ Register a custom service that loads Twig templates +`validator.constraint_validator`_ Create your own custom validation constraint +`validator.initializer`_ Register a service that initializes objects before validation +======================================== ======================================================================== assetic.asset ------------- @@ -916,6 +884,34 @@ of your configuration, and tag it with ``routing.loader``: For more information, see :doc:`/cookbook/routing/custom_route_loader`. +routing.expression_language_provider +------------------------------------ + +.. versionadded:: 2.6 + The ``routing.expression_language_provider`` tag was introduced in Symfony + 2.6. + +**Purpose**: Register a provider for expression language functions in routing + +This tag is used to automatically register :ref:`expression function providers +` for the routing expression +component. Using these providers, you can add custom functions to the routing +expression language. + +security.expression_language_provider +------------------------------------- + +.. versionadded:: 2.6 + The ``security.expression_language_provider`` tag was introduced in Symfony + 2.6. + +**Purpose**: Register a provider for expression language functions in security + +This tag is used to automatically register :ref:`expression function providers +` for the security expression +component. Using these providers, you can add custom functions to the security +expression language. + security.remember_me_aware -------------------------- From 13f18b356593b9d21de404ca61affe2f9ea139ac Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 19 Nov 2014 16:27:06 +0100 Subject: [PATCH 409/835] link to a bundle's current (not master) docs --- best_practices/business-logic.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index 23abb880c8e..12eac4d0a26 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -334,7 +334,7 @@ in a matter of seconds. .. _`full definition`: http://en.wikipedia.org/wiki/Business_logic .. _`Doctrine project`: http://www.doctrine-project.org/ -.. _`fixture class`: http://symfony.com/doc/master/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures +.. _`fixture class`: http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#writing-simple-fixtures .. _`PSR-1`: http://www.php-fig.org/psr/psr-1/ .. _`PSR-2`: http://www.php-fig.org/psr/psr-2/ .. _`the Symfony Code Standards`: http://symfony.com/doc/current/contributing/code/standards.html From ef7495e5ae447babfc1bd1b066894d81e468d2e7 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 19 Nov 2014 16:33:27 +0100 Subject: [PATCH 410/835] link to a bundle's current (not master) docs --- book/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/security.rst b/book/security.rst index aa31b30e72d..d63d6c9a3dc 100644 --- a/book/security.rst +++ b/book/security.rst @@ -2303,7 +2303,7 @@ Learn more from the Cookbook * :doc:`/cookbook/security/remember_me` * :doc:`How to Restrict Firewalls to a Specific Request ` -.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/master/bundles/SensioFrameworkExtraBundle/annotations/security.html +.. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle .. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php .. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack From d7998b06a726be482bf450ac051a0eceb90d73d1 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Wed, 19 Nov 2014 19:34:01 +0100 Subject: [PATCH 411/835] Use new factory syntax --- components/dependency_injection/factories.rst | 156 ++++++------------ 1 file changed, 52 insertions(+), 104 deletions(-) diff --git a/components/dependency_injection/factories.rst b/components/dependency_injection/factories.rst index 9f74efa50d5..7fe20414661 100644 --- a/components/dependency_injection/factories.rst +++ b/components/dependency_injection/factories.rst @@ -4,6 +4,10 @@ Using a Factory to Create Services ================================== +.. versionadded:: 2.6 + The new ``setFactory`` method was introduced in Symfony 2.6. Refer to older + versions for the syntax for factories prior to 2.6. + Symfony's Service Container provides a powerful way of controlling the creation of objects, allowing you to specify arguments passed to the constructor as well as calling methods and setting parameters. Sometimes, however, this @@ -15,9 +19,9 @@ the class. Suppose you have a factory that configures and returns a new ``NewsletterManager`` object:: - class NewsletterFactory + class NewsletterManagerFactory { - public function get() + public static function createNewsletterManager() { $newsletterManager = new NewsletterManager(); @@ -28,22 +32,17 @@ object:: } To make the ``NewsletterManager`` object available as a service, you can -configure the service container to use the ``NewsletterFactory`` factory -class: +configure the service container to use the +``NewsletterFactory::createNewsletterManager()`` factory method: .. configuration-block:: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: newsletter_manager: - class: "%newsletter_manager.class%" - factory_class: "%newsletter_factory.class%" - factory_method: get + class: NewsletterManager + factory: [NewsletterManagerFactory, createNewsletterManager] .. code-block:: xml @@ -52,18 +51,10 @@ class: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - - + + + @@ -72,35 +63,26 @@ class: use Symfony\Component\DependencyInjection\Definition; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); - - $definition = new Definition('%newsletter_manager.class%'); - $definition->setFactoryClass('%newsletter_factory.class%'); - $definition->setFactoryMethod('get'); + $definition = new Definition('NewsletterManager'); + $definition->setFactory(array('NewsletterManagerFactory', 'createNewsletterManager')); $container->setDefinition('newsletter_manager', $definition); -When you specify the class to use for the factory (via ``factory_class``) -the method will be called statically. If the factory itself should be instantiated -and the resulting object's method called, configure the factory itself as a service. -In this case, the method (e.g. get) should be changed to be non-static: +Now, the method will be called statically. If the factory class itself should +be instantiated and the resulting object's method called, configure the factory +itself as a service. In this case, the method (e.g. get) should be changed to +be non-static. .. configuration-block:: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: - newsletter_factory: - class: "%newsletter_factory.class%" + newsletter_manager.factory: + class: NewsletterManagerFactory newsletter_manager: - class: "%newsletter_manager.class%" - factory_service: newsletter_factory - factory_method: get + class: NewsletterManager + factory: ["@newsletter_manager.factory", createNewsletterManager] .. code-block:: xml @@ -109,47 +91,29 @@ In this case, the method (e.g. get) should be changed to be non-static: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - - + - + + + .. code-block:: php + use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); + $container->register('newsletter_manager.factory', 'createNewsletterManager'); - $container->setDefinition('newsletter_factory', new Definition( - '%newsletter_factory.class%' + $newsletterManager = new Definition(); + $newsletterManager->setFactory(array( + new Reference('newsletter_manager.factory'), + 'createNewsletterManager' )); - $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%' - ))->setFactoryService( - 'newsletter_factory' - )->setFactoryMethod( - 'get' - ); - -.. note:: - - The factory service is specified by its id name and not a reference to - the service itself. So, you do not need to use the @ syntax for this in - YAML configurations. + $container->setDefinition('newsletter_manager', $newsletterManager); Passing Arguments to the Factory Method --------------------------------------- @@ -162,17 +126,13 @@ in the previous example takes the ``templating`` service as an argument: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: - newsletter_factory: - class: "%newsletter_factory.class%" + newsletter_manager.factory: + class: NewsletterManagerFactory + newsletter_manager: - class: "%newsletter_manager.class%" - factory_service: newsletter_factory - factory_method: get + class: NewsletterManager + factory: ["@newsletter_manager.factory", createNewsletterManager] arguments: - "@templating" @@ -183,42 +143,30 @@ in the previous example takes the ``templating`` service as an argument: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - - + - - - + + + .. code-block:: php + use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); + $container->register('newsletter_manager.factory', 'NewsletterManagerFactory'); - $container->setDefinition('newsletter_factory', new Definition( - '%newsletter_factory.class%' - )); - $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%', + $newsletterManager = new Definition( + 'NewsletterManager', array(new Reference('templating')) - ))->setFactoryService( - 'newsletter_factory' - )->setFactoryMethod( - 'get' ); + $newsletterManager->setFactory(array( + new Reference('newsletter_manager.factory'), + 'createNewsletterManager' + )); + $container->setDefinition('newsletter_manager', $newsletterManager); From f374bf4962cd38dff0d0efd85ed7c4f055987a5e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 19 Nov 2014 16:16:59 +0100 Subject: [PATCH 412/835] Updated the Symfony Installer installation instructions --- quick_tour/the_big_picture.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index be24e7ecfaf..bf10f772f1a 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -31,7 +31,7 @@ On **Linux** and **Mac OS X** systems, execute the following console commands: .. code-block:: bash - $ curl -sS https://symfony.com/installer | php + $ curl -LsS http://symfony.com/installer > symfony.phar $ sudo mv symfony.phar /usr/local/bin/symfony .. note:: @@ -41,7 +41,7 @@ On **Linux** and **Mac OS X** systems, execute the following console commands: .. code-block:: bash - $ php -r "readfile('https://symfony.com/installer');" | php + $ php -r "readfile('http://symfony.com/installer');" > symfony.phar $ sudo mv symfony.phar /usr/local/bin/symfony After installing the Symfony installer, you'll have to open a new console window @@ -53,9 +53,9 @@ to be able to execute the new ``symfony`` command: On **Windows** systems, execute the following console command: - .. code-block:: bash +.. code-block:: bash - c:\> php -r "readfile('https://symfony.com/installer');" | php + c:\> php -r "readfile('http://symfony.com/installer');" > symfony.phar This command downloads a file called ``symfony.phar`` which contains the Symfony installer. Save or move that file to the directory where you create the Symfony From c85a8ac2840ecd73dedad98cfc634c6274815bfc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 2 Nov 2014 19:18:29 +0100 Subject: [PATCH 413/835] Added a reference to the Bootstrap 3 form theme --- cookbook/form/form_customization.rst | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 781bc2744d1..3d50fd8a4e0 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -93,9 +93,18 @@ rendering a form. In other words, if you want to customize one portion of how a form is rendered, you'll import a *theme* which contains a customization of the appropriate form fragments. -Symfony comes with a default theme (`form_div_layout.html.twig`_ in Twig and -``FrameworkBundle:Form`` in PHP) that defines each and every fragment needed -to render every part of a form. +Symfony comes with four **built-in form themes** that define each and every +fragment needed to render every part of a form: + +* `form_div_layout.html.twig`_, wraps each form field inside a ``
    `` element. +* `form_table_layout.html.twig`_, wraps the entire form inside a ```` + element and each form field inside a ```` element. +* `bootstrap_3_layout.html.twig`_, wraps each form field inside a ``
    `` element + with the appropriate CSS classes to apply the default `Bootstrap 3 CSS framework`_ + styles. +* `bootstrap_3_horizontal_layout.html.twig`_, it's similar to the previous theme, + but the CSS classes applied are the ones used to display the forms horizontally + (i.e. the label and the widget in the same row). In the next section you will learn how to customize a theme by overriding some or all of its fragments. @@ -1059,3 +1068,7 @@ The array passed as the second argument contains form "variables". For more details about this concept in Twig, see :ref:`twig-reference-form-variables`. .. _`form_div_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig +.. _`form_table_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/form_table_layout.html.twig +.. _`bootstrap_3_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +.. _`bootstrap_3_horizontal_layout.html.twig`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_horizontal_layout.html.twig +.. _`Bootstrap 3 CSS framework`: http://getbootstrap.com/ From ab105293ed04d50612da15e5d81881c84623f104 Mon Sep 17 00:00:00 2001 From: MightyBranch Date: Tue, 11 Nov 2014 09:02:57 -0600 Subject: [PATCH 414/835] fix elseif statement --- book/from_flat_php_to_symfony2.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index 818dee0180b..f9c90d5d6e1 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -367,7 +367,7 @@ on the requested URI: require_once 'controllers.php'; // route the request internally - $uri = $_SERVER['REQUEST_URI']; + $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); if ('/index.php' == $uri) { list_action(); } elseif ('/index.php/show' == $uri && isset($_GET['id'])) { From b1ceb88c7f5eacec29346c4d23f24c09c4076e1f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 13 Nov 2014 20:17:01 -0500 Subject: [PATCH 415/835] Proposing that we make the service names *just* a little bit longer --- best_practices/business-logic.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index 23abb880c8e..b2d349ce2f5 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -81,7 +81,7 @@ Next, define a new service for that class. # app/config/services.yml services: # keep your service names short - slugger: + app.slugger: class: AppBundle\Utils\Slugger Traditionally, the naming convention for a service involved following the @@ -92,7 +92,8 @@ your code will be easier to read and use. .. best-practice:: The name of your application's services should be as short as possible, - ideally just one simple word. + but unique enough that you can search your project for the service if + you ever need to. Now you can use the custom slugger in any controller class, such as the ``AdminController``: @@ -104,7 +105,7 @@ Now you can use the custom slugger in any controller class, such as the // ... if ($form->isSubmitted() && $form->isValid()) { - $slug = $this->get('slugger')->slugify($post->getTitle()); + $slug = $this->get('app.slugger')->slugify($post->getTitle()); $post->setSlug($slug); // ... @@ -143,7 +144,7 @@ the class namespace as a parameter: slugger.class: AppBundle\Utils\Slugger services: - slugger: + app.slugger: class: "%slugger.class%" This practice is cumbersome and completely unnecessary for your own services: From f1708f552659de5fc272806c6bac6898dcd64559 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 13 Nov 2014 20:11:58 -0500 Subject: [PATCH 416/835] Modifying the best practice to use form_start() instead of + {{ form_start(form, {'attr': {'class': 'my-form-class'} }) }} {{ form_widget(form) }} - - -.. best-practice:: - - Don't use the ``form()`` or ``form_start()`` functions to render the - starting and ending form tags. - -Experienced Symfony developers will recognize that we're rendering the ``
    `` -tags manually instead of using the ``form_start()`` or ``form()`` functions. -While those are convenient, they take away from some clarity with little -benefit. - -.. tip:: - - The exception is a delete form because it's really just one button and - so benefits from some of these extra shortcuts. + {{ form_end(form) }} If you need more control over how your fields are rendered, then you should remove the ``form_widget(form)`` function and render your fields individually. From ae3b3cd3e5b63e762ce4102d78906bc554c3eef6 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 18 Nov 2014 19:38:04 +0100 Subject: [PATCH 417/835] Fixed typo --- best_practices/forms.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/forms.rst b/best_practices/forms.rst index 6f62eaf19d9..883f2c8a998 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -157,7 +157,7 @@ There are a lot of ways to render your form, ranging from rendering the entire thing in one line to rendering each part of each field independently. The best way depends on how much customization you need. -One of thhe simplest ways - which is especially useful during development - +One of the simplest ways - which is especially useful during development - is to render the form tags and use ``form_widget()`` to render all of the fields: From 3c71b6d21e4586dc10a729f09ea1e63fbee391aa Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Nov 2014 23:12:35 +0100 Subject: [PATCH 418/835] Use AppBundle instead of AcmeDemoBundle --- book/http_fundamentals.rst | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index cd8d04d0de0..7ff7b54dac3 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -427,7 +427,7 @@ by adding an entry for ``/contact`` to your routing configuration file: # app/config/routing.yml contact: path: /contact - defaults: { _controller: AcmeDemoBundle:Main:contact } + defaults: { _controller: AppBundle:Main:contact } .. code-block:: xml @@ -439,7 +439,7 @@ by adding an entry for ``/contact`` to your routing configuration file: http://symfony.com/schema/routing/routing-1.0.xsd"> - AcmeDemoBundle:Main:contact + AppBundle:Main:contact @@ -451,24 +451,18 @@ by adding an entry for ``/contact`` to your routing configuration file: $collection = new RouteCollection(); $collection->add('contact', new Route('/contact', array( - '_controller' => 'AcmeDemoBundle:Main:contact', + '_controller' => 'AppBundle:Main:contact', ))); return $collection; -.. note:: - - This example uses :doc:`YAML ` to define the routing - configuration. Routing configuration can also be written in other formats - such as XML or PHP. - When someone visits the ``/contact`` page, this route is matched, and the specified controller is executed. As you'll learn in the :doc:`routing chapter `, the ``AcmeDemoBundle:Main:contact`` string is a short syntax that points to a specific PHP method ``contactAction`` inside a class called ``MainController``:: - // src/Acme/DemoBundle/Controller/MainController.php - namespace Acme\DemoBundle\Controller; + // src/AppBundle/Controller/MainController.php + namespace AppBundle\Controller; use Symfony\Component\HttpFoundation\Response; From c8ce5074e05f5954458f6258871debf2157abc71 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Nov 2014 23:12:41 +0100 Subject: [PATCH 419/835] Other minor fixes --- book/http_fundamentals.rst | 40 +++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index 7ff7b54dac3..db2f9d86907 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -14,7 +14,7 @@ applications, while staying out of your way. Symfony is built on the best ideas from many technologies: the tools and concepts you're about to learn represent the efforts of thousands of people, over many years. In other words, you're not just learning "Symfony", you're learning the fundamentals of the -web, development best practices, and how to use many amazing new PHP libraries, +web, development best practices and how to use many amazing new PHP libraries, inside or independently of Symfony. So, get ready. True to the Symfony philosophy, this chapter begins by explaining the fundamental @@ -33,9 +33,9 @@ takes place: :align: center And while the actual language used is a bit more formal, it's still dead-simple. -HTTP is the term used to describe this simple text-based language. And no -matter how you develop on the web, the goal of your server is *always* to -understand simple text requests, and return simple text responses. +HTTP is the term used to describe this simple text-based language. No matter +how you develop on the web, the goal of your server is *always* to understand +simple text requests, and return simple text responses. Symfony is built from the ground up around that reality. Whether you realize it or not, HTTP is something you use everyday. With Symfony, you'll learn @@ -48,7 +48,7 @@ Step1: The Client Sends a Request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Every conversation on the web starts with a *request*. The request is a text -message created by a client (e.g. a browser, an iPhone app, etc) in a +message created by a client (e.g. a browser, a smartphone app, etc) in a special format known as HTTP. The client sends that request to a server, and then waits for the response. @@ -98,7 +98,7 @@ delete a specific blog entry, for example: There are actually nine HTTP methods defined by the HTTP specification, but many of them are not widely used or supported. In reality, many modern - browsers don't support the ``PUT`` and ``DELETE`` methods. + browsers don't even support the ``PUT`` and ``DELETE`` methods. In addition to the first line, an HTTP request invariably contains other lines of information called request headers. The headers can supply a wide @@ -161,7 +161,7 @@ communication on the web. And as important and powerful as this process is, it's inescapably simple. The most important fact is this: regardless of the language you use, the -type of application you build (web, mobile, JSON API), or the development +type of application you build (web, mobile, JSON API) or the development philosophy you follow, the end goal of an application is **always** to understand each request and create and return the appropriate response. @@ -277,6 +277,7 @@ an HTTP response message. This allows your application to use an object-oriented interface to construct the response that needs to be returned to the client:: use Symfony\Component\HttpFoundation\Response; + $response = new Response(); $response->setContent('

    Hello world!

    '); @@ -366,12 +367,13 @@ on that value. This can get ugly quickly:: // index.php use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; + $request = Request::createFromGlobals(); $path = $request->getPathInfo(); // the URI path being requested if (in_array($path, array('', '/'))) { $response = new Response('Welcome to the homepage.'); - } elseif ($path == '/contact') { + } elseif ('/contact' === $path) { $response = new Response('Contact us'); } else { $response = new Response('Page not found.', 404); @@ -485,8 +487,8 @@ email messages. .. _symfony2-build-your-app-not-your-tools: -Symfony: Build your App, not your Tools. ----------------------------------------- +Symfony: Build your App, not your Tools +--------------------------------------- You now know that the goal of any app is to interpret each incoming request and create an appropriate response. As an application grows, it becomes more @@ -522,24 +524,21 @@ regardless of how your project is developed. To name a few: about how that request should be handled (e.g. execute the ``contactAction()`` method); -* `Form`_ - A full-featured and flexible framework for creating forms and - handling form submissions; +* `Form ` - A full-featured and flexible + framework for creating forms and handling form submissions; * `Validator`_ - A system for creating rules about data and then validating whether or not user-submitted data follows those rules; -* :doc:`ClassLoader ` - An autoloading library that allows - PHP classes to be used without needing to manually ``require`` the files - containing those classes; - * :doc:`Templating ` - A toolkit for rendering templates, handling template inheritance (i.e. a template is decorated with a layout) and performing other common template tasks; -* `Security`_ - A powerful library for handling all types of security inside - an application; +* `Security ` - A powerful library for + handling all types of security inside an application; -* `Translation`_ - A framework for translating strings in your application. +* `Translation ` - A framework for + translating strings in your application. Each and every one of these components is decoupled and can be used in *any* PHP project, regardless of whether or not you use the Symfony framework. @@ -576,8 +575,5 @@ sensible defaults. For more advanced users, the sky is the limit. .. _`List of HTTP status codes`: http://en.wikipedia.org/wiki/List_of_HTTP_status_codes .. _`List of HTTP header fields`: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields .. _`List of common media types`: http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types -.. _`Form`: https://github.com/symfony/Form .. _`Validator`: https://github.com/symfony/Validator -.. _`Security`: https://github.com/symfony/Security -.. _`Translation`: https://github.com/symfony/Translation .. _`Swift Mailer`: http://swiftmailer.org/ From de4fcf4612dcfec9efc22fa187521c4ba5c2b036 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Nov 2014 23:28:42 +0100 Subject: [PATCH 420/835] Use AppBundle instead of AcmeStoreBundle --- book/doctrine.rst | 159 ++++++++++++++++++++++------------------------ 1 file changed, 75 insertions(+), 84 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 871695ff21d..5ec35ce9338 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -32,15 +32,6 @@ The easiest way to understand how Doctrine works is to see it in action. In this section, you'll configure your database, create a ``Product`` object, persist it to the database and fetch it back out. -.. sidebar:: Code along with the Example - - If you want to follow along with the example in this chapter, create - an ``AcmeStoreBundle`` via: - - .. code-block:: bash - - $ php app/console generate:bundle --namespace=Acme/StoreBundle - Configuring the Database ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -202,17 +193,15 @@ Creating an Entity Class Suppose you're building an application where products need to be displayed. Without even thinking about Doctrine or databases, you already know that you need a ``Product`` object to represent those products. Create this class -inside the ``Entity`` directory of your ``AcmeStoreBundle``:: +inside the ``Entity`` directory of your ``AppBundle``:: - // src/Acme/StoreBundle/Entity/Product.php - namespace Acme\StoreBundle\Entity; + // src/AppBundle/Entity/Product.php + namespace AppBundle\Entity; class Product { protected $name; - protected $price; - protected $description; } @@ -258,8 +247,8 @@ in a number of different formats including YAML, XML or directly inside the .. code-block:: php-annotations - // src/Acme/StoreBundle/Entity/Product.php - namespace Acme\StoreBundle\Entity; + // src/AppBundle/Entity/Product.php + namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; @@ -294,8 +283,8 @@ in a number of different formats including YAML, XML or directly inside the .. code-block:: yaml - # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml - Acme\StoreBundle\Entity\Product: + # src/AppBundle/Resources/config/doctrine/Product.orm.yml + AppBundle\Entity\Product: type: entity table: product id: @@ -314,14 +303,14 @@ in a number of different formats including YAML, XML or directly inside the .. code-block:: xml - + - + @@ -394,7 +383,7 @@ a regular PHP class, you need to create getter and setter methods (e.g. ``getNam .. code-block:: bash - $ php app/console doctrine:generate:entities Acme/StoreBundle/Entity/Product + $ php app/console doctrine:generate:entities AppBundle/Entity/Product This command makes sure that all of the getters and setters are generated for the ``Product`` class. This is a safe command - you can run it over and @@ -435,7 +424,9 @@ mapping information) of a bundle or an entire namespace: .. code-block:: bash - $ php app/console doctrine:generate:entities AcmeStoreBundle + # generates all entities in the AppBundle + $ php app/console doctrine:generate:entities AppBundle + # generates all entities of bundles in the Acme namespace $ php app/console doctrine:generate:entities Acme .. note:: @@ -485,17 +476,16 @@ Persisting Objects to the Database Now that you have a mapped ``Product`` entity and corresponding ``product`` table, you're ready to persist data to the database. From inside a controller, this is pretty easy. Add the following method to the ``DefaultController`` -of the bundle: +of the bundle:: -.. code-block:: php - :linenos: - // src/Acme/StoreBundle/Controller/DefaultController.php + // src/AppBundle/Controller/DefaultController.php // ... - use Acme\StoreBundle\Entity\Product; + use AppBundle\Entity\Product; use Symfony\Component\HttpFoundation\Response; + // ... public function createAction() { $product = new Product(); @@ -504,6 +494,7 @@ of the bundle: $product->setDescription('Lorem ipsum dolor'); $em = $this->getDoctrine()->getManager(); + $em->persist($product); $em->flush(); @@ -526,17 +517,17 @@ of the bundle: Take a look at the previous example in more detail: -* **lines 9-12** In this section, you instantiate and work with the ``$product`` +* **lines 10-13** In this section, you instantiate and work with the ``$product`` object like any other, normal PHP object. -* **line 14** This line fetches Doctrine's *entity manager* object, which is +* **line 15** This line fetches Doctrine's *entity manager* object, which is responsible for handling the process of persisting and fetching objects to and from the database. -* **line 15** The ``persist()`` method tells Doctrine to "manage" the ``$product`` +* **line 16** The ``persist()`` method tells Doctrine to "manage" the ``$product`` object. This does not actually cause a query to be made to the database (yet). -* **line 16** When the ``flush()`` method is called, Doctrine looks through +* **line 17** When the ``flush()`` method is called, Doctrine looks through all of the objects that it's managing to see if they need to be persisted to the database. In this example, the ``$product`` object has not been persisted yet, so the entity manager executes an ``INSERT`` query and a @@ -544,12 +535,12 @@ Take a look at the previous example in more detail: .. note:: - In fact, since Doctrine is aware of all your managed entities, when you call - the ``flush()`` method, it calculates an overall changeset and executes - the queries in the correct order. It utilizes cached prepared statement to - slightly improve the performance. For example, if you persist a total of 100 - ``Product`` objects and then subsequently call ``flush()``, Doctrine will - execute 100 ``INSERT`` queries using a single prepared statement object. + In fact, since Doctrine is aware of all your managed entities, when you call + the ``flush()`` method, it calculates an overall changeset and executes + the queries in the correct order. It utilizes cached prepared statement to + slightly improve the performance. For example, if you persist a total of 100 + ``Product`` objects and then subsequently call ``flush()``, Doctrine will + execute 100 ``INSERT`` queries using a single prepared statement object. When creating or updating objects, the workflow is always the same. In the next section, you'll see how Doctrine is smart enough to automatically issue @@ -571,7 +562,7 @@ on its ``id`` value:: public function showAction($id) { $product = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product') + ->getRepository('AppBundle:Product') ->find($id); if (!$product) { @@ -595,12 +586,12 @@ job is to help you fetch entities of a certain class. You can access the repository object for an entity class via:: $repository = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product'); + ->getRepository('AppBundle:Product'); .. note:: - The ``AcmeStoreBundle:Product`` string is a shortcut you can use anywhere - in Doctrine instead of the full class name of the entity (i.e. ``Acme\StoreBundle\Entity\Product``). + The ``AppBundle:Product`` string is a shortcut you can use anywhere + in Doctrine instead of the full class name of the entity (i.e. ``AppBundle\Entity\Product``). As long as your entity lives under the ``Entity`` namespace of your bundle, this will work. @@ -660,7 +651,7 @@ you have a route that maps a product id to an update action in a controller:: public function updateAction($id) { $em = $this->getDoctrine()->getManager(); - $product = $em->getRepository('AcmeStoreBundle:Product')->find($id); + $product = $em->getRepository('AppBundle:Product')->find($id); if (!$product) { throw $this->createNotFoundException( @@ -726,7 +717,7 @@ cost more than ``19.99``, ordered from cheapest to most expensive. You can use Doctrine's ``QueryBuilder`` for this:: $repository = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product'); + ->getRepository('AppBundle:Product'); $query = $repository->createQueryBuilder('p') ->where('p.price > :price') @@ -764,7 +755,7 @@ directly using DQL:: $em = $this->getDoctrine()->getManager(); $query = $em->createQuery( 'SELECT p - FROM AcmeStoreBundle:Product p + FROM AppBundle:Product p WHERE p.price > :price ORDER BY p.price ASC' )->setParameter('price', '19.99'); @@ -773,7 +764,7 @@ directly using DQL:: If you're comfortable with SQL, then DQL should feel very natural. The biggest difference is that you need to think in terms of "objects" instead of rows -in a database. For this reason, you select *from* the ``AcmeStoreBundle:Product`` +in a database. For this reason, you select *from* the ``AppBundle:Product`` *object* and then alias it as ``p`` (as you see, this is equal to what you already did in the previous section). @@ -796,13 +787,13 @@ To do this, add the name of the repository class to your mapping definition: .. code-block:: php-annotations - // src/Acme/StoreBundle/Entity/Product.php - namespace Acme\StoreBundle\Entity; + // src/AppBundle/Entity/Product.php + namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** - * @ORM\Entity(repositoryClass="Acme\StoreBundle\Entity\ProductRepository") + * @ORM\Entity(repositoryClass="AppBundle\Entity\ProductRepository") */ class Product { @@ -811,15 +802,15 @@ To do this, add the name of the repository class to your mapping definition: .. code-block:: yaml - # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml - Acme\StoreBundle\Entity\Product: + # src/AppBundle/Resources/config/doctrine/Product.orm.yml + AppBundle\Entity\Product: type: entity - repositoryClass: Acme\StoreBundle\Entity\ProductRepository + repositoryClass: AppBundle\Entity\ProductRepository # ... .. code-block:: xml - + + name="AppBundle\Entity\Product" + repository-class="AppBundle\Entity\ProductRepository"> @@ -839,7 +830,7 @@ used earlier to generate the missing getter and setter methods: .. code-block:: bash - $ php app/console doctrine:generate:entities Acme + $ php app/console doctrine:generate:entities AppBundle Next, add a new method - ``findAllOrderedByName()`` - to the newly generated repository class. This method will query for all of the ``Product`` entities, @@ -847,8 +838,8 @@ ordered alphabetically. .. code-block:: php - // src/Acme/StoreBundle/Entity/ProductRepository.php - namespace Acme\StoreBundle\Entity; + // src/AppBundle/Entity/ProductRepository.php + namespace AppBundle\Entity; use Doctrine\ORM\EntityRepository; @@ -858,7 +849,7 @@ ordered alphabetically. { return $this->getEntityManager() ->createQuery( - 'SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC' + 'SELECT p FROM AppBundle:Product p ORDER BY p.name ASC' ) ->getResult(); } @@ -872,7 +863,7 @@ ordered alphabetically. You can use this new method just like the default finder methods of the repository:: $em = $this->getDoctrine()->getManager(); - $products = $em->getRepository('AcmeStoreBundle:Product') + $products = $em->getRepository('AppBundle:Product') ->findAllOrderedByName(); .. note:: @@ -893,7 +884,7 @@ you can let Doctrine create the class for you. .. code-block:: bash - $ php app/console doctrine:generate:entity --entity="AcmeStoreBundle:Category" --fields="name:string(255)" + $ php app/console doctrine:generate:entity --entity="AppBundle:Category" --fields="name:string(255)" This task generates the ``Category`` entity for you, with an ``id`` field, a ``name`` field and the associated getter and setter functions. @@ -908,7 +899,7 @@ To relate the ``Category`` and ``Product`` entities, start by creating a .. code-block:: php-annotations - // src/Acme/StoreBundle/Entity/Category.php + // src/AppBundle/Entity/Category.php // ... use Doctrine\Common\Collections\ArrayCollection; @@ -930,8 +921,8 @@ To relate the ``Category`` and ``Product`` entities, start by creating a .. code-block:: yaml - # src/Acme/StoreBundle/Resources/config/doctrine/Category.orm.yml - Acme\StoreBundle\Entity\Category: + # src/AppBundle/Resources/config/doctrine/Category.orm.yml + AppBundle\Entity\Category: type: entity # ... oneToMany: @@ -942,14 +933,14 @@ To relate the ``Category`` and ``Product`` entities, start by creating a .. code-block:: xml - + - + + - + getDoctrine() - ->getRepository('AcmeStoreBundle:Product') + ->getRepository('AppBundle:Product') ->find($id); $categoryName = $product->getCategory()->getName(); @@ -1162,7 +1153,7 @@ You can also query in the other direction:: public function showProductAction($id) { $category = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Category') + ->getRepository('AppBundle:Category') ->find($id); $products = $category->getProducts(); @@ -1183,12 +1174,12 @@ to the given ``Category`` object via their ``category_id`` value. example:: $product = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product') + ->getRepository('AppBundle:Product') ->find($id); $category = $product->getCategory(); - // prints "Proxies\AcmeStoreBundleEntityCategoryProxy" + // prints "Proxies\AppBundleEntityCategoryProxy" echo get_class($category); This proxy object extends the true ``Category`` object, and looks and @@ -1220,12 +1211,12 @@ Of course, if you know up front that you'll need to access both objects, you can avoid the second query by issuing a join in the original query. Add the following method to the ``ProductRepository`` class:: - // src/Acme/StoreBundle/Entity/ProductRepository.php + // src/AppBundle/Entity/ProductRepository.php public function findOneByIdJoinedToCategory($id) { $query = $this->getEntityManager() ->createQuery( - 'SELECT p, c FROM AcmeStoreBundle:Product p + 'SELECT p, c FROM AppBundle:Product p JOIN p.category c WHERE p.id = :id' )->setParameter('id', $id); @@ -1243,7 +1234,7 @@ object and its related ``Category`` with just one query:: public function showAction($id) { $product = $this->getDoctrine() - ->getRepository('AcmeStoreBundle:Product') + ->getRepository('AppBundle:Product') ->findOneByIdJoinedToCategory($id); $category = $product->getCategory(); @@ -1304,7 +1295,7 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: php-annotations - // src/Acme/StoreBundle/Entity/Product.php + // src/AppBundle/Entity/Product.php /** * @ORM\PrePersist @@ -1316,8 +1307,8 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: yaml - # src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.yml - Acme\StoreBundle\Entity\Product: + # src/AppBundle/Resources/config/doctrine/Product.orm.yml + AppBundle\Entity\Product: type: entity # ... lifecycleCallbacks: @@ -1325,14 +1316,14 @@ the current date, only when the entity is first persisted (i.e. inserted): .. code-block:: xml - + - + From 4ef1ef313400fa44e65a1f96c0f1a759ac8d61f7 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Nov 2014 23:37:08 +0100 Subject: [PATCH 421/835] Apply best practices to forms --- book/forms.rst | 152 +++++++++++++++++++++---------------------------- 1 file changed, 66 insertions(+), 86 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index 27135c4f817..c7dc5489047 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -26,13 +26,12 @@ display "tasks". Because your users will need to edit and create tasks, you're going to need to build a form. But before you begin, first focus on the generic ``Task`` class that represents and stores the data for a single task:: - // src/Acme/TaskBundle/Entity/Task.php - namespace Acme\TaskBundle\Entity; + // src/AppBundle/Entity/Task.php + namespace AppBundle\Entity; class Task { protected $task; - protected $dueDate; public function getTask() @@ -56,16 +55,6 @@ going to need to build a form. But before you begin, first focus on the generic } } -.. note:: - - If you're coding along with this example, create the ``AcmeTaskBundle`` - first by running the following command (and accepting all of the default - options): - - .. code-block:: bash - - $ php app/console generate:bundle --namespace=Acme/TaskBundle - This class is a "plain-old-PHP-object" because, so far, it has nothing to do with Symfony or any other library. It's quite simply a normal PHP object that directly solves a problem inside *your* application (i.e. the need to @@ -84,11 +73,11 @@ render the actual HTML form. In Symfony, this is done by building a form object and then rendering it in a template. For now, this can all be done from inside a controller:: - // src/Acme/TaskBundle/Controller/DefaultController.php - namespace Acme\TaskBundle\Controller; + // src/AppBundle/Controller/DefaultController.php + namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use Acme\TaskBundle\Entity\Task; + use AppBundle\Entity\Task; use Symfony\Component\HttpFoundation\Request; class DefaultController extends Controller @@ -106,7 +95,7 @@ from inside a controller:: ->add('save', 'submit', array('label' => 'Create Task')) ->getForm(); - return $this->render('AcmeTaskBundle:Default:new.html.twig', array( + return $this->render('Default/new.html.twig', array( 'form' => $form->createView(), )); } @@ -154,13 +143,13 @@ helper functions: .. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} + {# app/Resources/views/Default/new.html.twig #} {{ form(form) }} .. code-block:: html+php - + form($form) ?> @@ -339,8 +328,8 @@ object. .. code-block:: yaml - # Acme/TaskBundle/Resources/config/validation.yml - Acme\TaskBundle\Entity\Task: + # AppBundle/Resources/config/validation.yml + AppBundle\Entity\Task: properties: task: - NotBlank: ~ @@ -350,7 +339,7 @@ object. .. code-block:: php-annotations - // Acme/TaskBundle/Entity/Task.php + // AppBundle/Entity/Task.php use Symfony\Component\Validator\Constraints as Assert; class Task @@ -369,14 +358,14 @@ object. .. code-block:: xml - + - + @@ -389,7 +378,7 @@ object. .. code-block:: php - // Acme/TaskBundle/Entity/Task.php + // AppBundle/Entity/Task.php use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Constraints\NotBlank; use Symfony\Component\Validator\Constraints\Type; @@ -435,14 +424,12 @@ corresponding errors printed out with the form. .. code-block:: html+jinja - {# src/Acme/DemoBundle/Resources/views/Default/new.html.twig #} - + {# app/Resources/views/Default/new.html.twig #} {{ form(form, {'attr': {'novalidate': 'novalidate'}}) }} .. code-block:: html+php - - + form($form, array( 'attr' => array('novalidate' => 'novalidate'), )) ?> @@ -525,7 +512,7 @@ to an array callback:: { $resolver->setDefaults(array( 'validation_groups' => array( - 'Acme\AcmeBundle\Entity\Client', + 'AppBundle\Entity\Client', 'determineValidationGroups', ), )); @@ -748,7 +735,7 @@ 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 #} + {# app/Resources/views/Default/new.html.twig #} {{ form_start(form) }} {{ form_errors(form) }} @@ -758,7 +745,7 @@ of code. Of course, you'll usually need much more flexibility when rendering: .. code-block:: html+php - + start($form) ?> errors($form) ?> @@ -970,14 +957,14 @@ to the ``form()`` or the ``form_start()`` helper: .. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} + {# app/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', @@ -1010,8 +997,8 @@ However, a better practice is to build the form in a separate, standalone PHP class, which can then be reused anywhere in your application. Create a new class that will house the logic for building the task form:: - // src/Acme/TaskBundle/Form/Type/TaskType.php - namespace Acme\TaskBundle\Form\Type; + // src/AppBundle/Form/Type/TaskType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -1036,10 +1023,10 @@ This new class contains all the directions needed to create the task form (note that the ``getName()`` method should return a unique identifier for this form "type"). It can be used to quickly build a form object in the controller:: - // src/Acme/TaskBundle/Controller/DefaultController.php + // src/AppBundle/Controller/DefaultController.php // add this new use statement at the top of the class - use Acme\TaskBundle\Form\Type\TaskType; + use AppBundle\Form\Type\TaskType; public function newAction() { @@ -1058,7 +1045,7 @@ the choice is ultimately up to you. .. sidebar:: Setting the ``data_class`` Every form needs to know the name of the class that holds the underlying - data (e.g. ``Acme\TaskBundle\Entity\Task``). Usually, this is just guessed + data (e.g. ``AppBundle\Entity\Task``). Usually, this is just guessed based off of the object passed to the second argument to ``createForm`` (i.e. ``$task``). Later, when you begin embedding forms, this will no longer be sufficient. So, while not always necessary, it's generally a @@ -1070,7 +1057,7 @@ the choice is ultimately up to you. public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Task', + 'data_class' => 'AppBundle\Entity\Task', )); } @@ -1121,16 +1108,16 @@ easy to use in your application. .. code-block:: yaml - # src/Acme/TaskBundle/Resources/config/services.yml + # src/AppBundle/Resources/config/services.yml services: acme_demo.form.type.task: - class: Acme\TaskBundle\Form\Type\TaskType + class: AppBundle\Form\Type\TaskType tags: - { name: form.type, alias: task } .. code-block:: xml - + + class="AppBundle\Form\Type\TaskType"> @@ -1148,11 +1135,11 @@ easy to use in your application. .. code-block:: php - // src/Acme/TaskBundle/Resources/config/services.php + // src/AppBundle/Resources/config/services.php $container ->register( 'acme_demo.form.type.task', - 'Acme\TaskBundle\Form\Type\TaskType' + 'AppBundle\Form\Type\TaskType' ) ->addTag('form.type', array( 'alias' => 'task', @@ -1161,7 +1148,7 @@ easy to use in your application. That's it! Now you can use your form type directly in a controller:: - // src/Acme/TaskBundle/Controller/DefaultController.php + // src/AppBundle/Controller/DefaultController.php // ... public function newAction() @@ -1174,7 +1161,7 @@ That's it! Now you can use your form type directly in a controller:: or even use from within the form type of another form:: - // src/Acme/TaskBundle/Form/Type/ListType.php + // src/AppBundle/Form/Type/ListType.php // ... class ListType extends AbstractType @@ -1242,8 +1229,8 @@ Embedding a Single Object Suppose that each ``Task`` belongs to a simple ``Category`` object. Start, of course, by creating the ``Category`` object:: - // src/Acme/TaskBundle/Entity/Category.php - namespace Acme\TaskBundle\Entity; + // src/AppBundle/Entity/Category.php + namespace AppBundle\Entity; use Symfony\Component\Validator\Constraints as Assert; @@ -1264,7 +1251,7 @@ Next, add a new ``category`` property to the ``Task`` class:: // ... /** - * @Assert\Type(type="Acme\TaskBundle\Entity\Category") + * @Assert\Type(type="AppBundle\Entity\Category") * @Assert\Valid() */ protected $category; @@ -1291,8 +1278,8 @@ Next, add a new ``category`` property to the ``Task`` class:: Now that your application has been updated to reflect the new requirements, create a form class so that a ``Category`` object can be modified by the user:: - // src/Acme/TaskBundle/Form/Type/CategoryType.php - namespace Acme\TaskBundle\Form\Type; + // src/AppBundle/Form/Type/CategoryType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -1308,7 +1295,7 @@ create a form class so that a ``Category`` object can be modified by the user:: public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Category', + 'data_class' => 'AppBundle\Entity\Category', )); } @@ -1412,7 +1399,7 @@ do this, create a new template file that will store the new markup: .. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Form/fields.html.twig #} + {# app/Resources/views/Form/fields.html.twig #} {% block form_row %} {% spaceless %}
    @@ -1425,7 +1412,7 @@ do this, create a new template file that will store the new markup: .. code-block:: html+php - +
    label($form, $label) ?> errors($form) ?> @@ -1441,19 +1428,19 @@ renders the form: .. code-block:: html+jinja - {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} - {% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' %} + {# app/Resources/views/Default/new.html.twig #} + {% form_theme form 'Form/fields.html.twig' %} - {% form_theme form 'AcmeTaskBundle:Form:fields.html.twig' 'AcmeTaskBundle:Form:fields2.html.twig' %} + {% form_theme form 'Form/fields.html.twig' 'Form/fields2.html.twig' %} - + {# ... render the form #} .. code-block:: html+php - - setTheme($form, array('AcmeTaskBundle:Form')) ?> + + setTheme($form, array('Form')) ?> - setTheme($form, array('AcmeTaskBundle:Form', 'AcmeTaskBundle:Form')) ?> + setTheme($form, array('Form', 'Form2')) ?> @@ -1474,14 +1461,6 @@ To customize any portion of a form, you just need to override the appropriate fragment. Knowing exactly which block or file to override is the subject of the next section. -.. code-block:: html+jinja - - {# src/Acme/TaskBundle/Resources/views/Default/new.html.twig #} - - {% form_theme form with 'AcmeTaskBundle:Form:fields.html.twig' %} - - {% form_theme form with ['AcmeTaskBundle:Form:fields.html.twig', 'AcmeTaskBundle:Form:fields2.html.twig'] %} - For a more extensive discussion, see :doc:`/cookbook/form/form_customization`. .. index:: @@ -1496,9 +1475,10 @@ In Symfony, every part of a form that is rendered - HTML form elements, errors, labels, etc. - is defined in a base theme, which is a collection of blocks in Twig and a collection of template files in PHP. -In Twig, every block needed is defined in a single template file (`form_div_layout.html.twig`_) -that lives inside the `Twig Bridge`_. Inside this file, you can see every block -needed to render a form and every default field type. +In Twig, every block needed is defined in a single template file (e.g. +`form_div_layout.html.twig`_) that lives inside the `Twig Bridge`_. Inside this +file, you can see every block needed to render a form and every default field +type. In PHP, the fragments are individual template files. By default they are located in the `Resources/views/Form` directory of the framework bundle (`view on GitHub`_). @@ -1529,7 +1509,7 @@ are 4 possible *parts* of a form that can be rendered: .. note:: - There are actually 2 other *parts* - ``rows`` and ``rest`` - + 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 @@ -1588,7 +1568,7 @@ file: twig: form: resources: - - 'AcmeTaskBundle:Form:fields.html.twig' + - 'Form/fields.html.twig' # ... .. code-block:: xml @@ -1603,7 +1583,7 @@ file: - AcmeTaskBundle:Form:fields.html.twig + Form/fields.html.twig @@ -1615,7 +1595,7 @@ file: $container->loadFromExtension('twig', array( 'form' => array( 'resources' => array( - 'AcmeTaskBundle:Form:fields.html.twig', + 'Form/fields.html.twig', ), ), // ... @@ -1631,7 +1611,7 @@ to define form output. .. code-block:: html+jinja - {% extends '::base.html.twig' %} + {% extends 'base.html.twig' %} {# import "_self" as the form theme #} {% form_theme form _self %} @@ -1661,7 +1641,7 @@ to define form output. PHP ... -To automatically include the customized templates from the ``Acme/TaskBundle/Resources/views/Form`` +To automatically include the customized templates from the ``app/Resources/views/Form`` directory created earlier in *all* templates, modify your application configuration file: @@ -1674,7 +1654,7 @@ file: templating: form: resources: - - 'AcmeTaskBundle:Form' + - 'Form' # ... .. code-block:: xml @@ -1690,7 +1670,7 @@ file: - AcmeTaskBundle:Form + Form @@ -1704,15 +1684,15 @@ file: 'templating' => array( 'form' => array( 'resources' => array( - 'AcmeTaskBundle:Form', + 'Form', ), ), ) // ... )); -Any fragments inside the ``Acme/TaskBundle/Resources/views/Form`` directory -are now used globally to define form output. +Any fragments inside the ``app/Resources/views/Form`` directory are now used +globally to define form output. .. index:: single: Forms; CSRF protection @@ -1752,7 +1732,7 @@ The CSRF token can be customized on a form-by-form basis. For example:: public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'data_class' => 'Acme\TaskBundle\Entity\Task', + 'data_class' => 'AppBundle\Entity\Task', 'csrf_protection' => true, 'csrf_field_name' => '_token', // a unique key to help generate the secret token From a29f9fbe38c7ca85fe906dbd2e07d82dfeeb3d25 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Nov 2014 23:46:25 +0100 Subject: [PATCH 422/835] Don't use form() helper --- book/forms.rst | 58 ++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index c7dc5489047..e05a1abc066 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -144,14 +144,16 @@ helper functions: .. code-block:: html+jinja {# app/Resources/views/Default/new.html.twig #} - - {{ form(form) }} + {{ form_start(form) }} + {{ form_widget(form) }} + {{ form_end(form) }} .. code-block:: html+php - - form($form) ?> + start($form) ?> + widget($form) ?> + end($form) ?> .. image:: /images/book/form-simple.png :align: center @@ -162,12 +164,24 @@ helper functions: 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(form)``, each field in the form is rendered, along -with a label and error message (if there is one). The ``form`` function also -surrounds everything in the necessary HTML ```` tag. 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! Just three lines are needed to render the complete form: + +* ``form_start(form)`` - Renders the start tag of the form, including the + correct enctype attributes when using file uploads; + +* ``form_widget(form)`` - Renders all fields, along with a label and error + message (if there is one) input element; + +* ``form_end()`` - Renders the end tag of the form and any fields that have not + yet been rendered, in case you rendered each field yourself. This is useful + for rendering hidden fields and taking advantage of the automatic + :ref:`CSRF Protection `. + +.. seealso:: + + 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"). @@ -753,25 +767,20 @@ of code. Of course, you'll usually need much more flexibility when rendering: row($form['dueDate']) ?> end($form) ?> -Take a look at each part: - -* ``form_start(form)`` - Renders the start tag of the form. +You already know the ``form_start()`` and ``form_end()`` functions, but what do +the other functions do? * ``form_errors(form)`` - Renders any errors global to the whole form (field-specific errors are displayed next to each field); * ``form_row(form.dueDate)`` - Renders the label, any errors, and the HTML form widget for the given field (e.g. ``dueDate``) inside, by default, a - ``div`` element; - -* ``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 `. + ``div`` element. The majority of the work is done by the ``form_row`` helper, which renders -the label, errors and HTML form widget of each field inside a ``div`` tag -by default. In the :ref:`form-theming` section, you'll learn how the ``form_row`` -output can be customized on many different levels. +the label and HTML form widget of each field inside a ``div`` tag by default. +In the :ref:`form-theming` section, you'll learn how the ``form_row`` output +can be customized on many different levels. .. tip:: @@ -958,18 +967,11 @@ to the ``form()`` or the ``form_start()`` helper: .. code-block:: html+jinja {# app/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', From 2a97453495cff1292e3f9a8e26302f570d83804f Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Nov 2014 23:46:38 +0100 Subject: [PATCH 423/835] Minor tweak --- book/forms.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index e05a1abc066..91d901f7647 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -12,8 +12,9 @@ learning the most important features of the form library along the way. .. note:: The Symfony Form component is a standalone library that can be used outside - of Symfony projects. For more information, see the `Symfony Form component`_ - on GitHub. + of Symfony projects. For more information, see the + :doc:`Form component documentation ` on + GitHub. .. index:: single: Forms; Create a simple form From fdc460d3c8d38cd395dded9910dae2e5bde539f7 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Thu, 20 Nov 2014 23:46:46 +0100 Subject: [PATCH 424/835] Minor standard fix for best practices guide --- best_practices/forms.rst | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/best_practices/forms.rst b/best_practices/forms.rst index 6d70561e914..91910a5582b 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -15,9 +15,7 @@ Building Forms The Form component allows you to build forms right inside your controller code. Honestly, unless you need to reuse the form somewhere else, that's totally fine. But for organize and reuse, we recommend that you define each -form in its own PHP class: - -.. code-block:: php +form in its own PHP class:: namespace AppBundle\Form; @@ -51,9 +49,7 @@ form in its own PHP class: } } -To use the class, use ``createForm`` and instantiate the new class: - -.. code-block:: php +To use the class, use ``createForm`` and instantiate the new class:: use AppBundle\Form\PostType; // ... @@ -108,9 +104,7 @@ directly in your form class, this would effectively limit the scope of that form This form *may* have been designed for creating posts, but if you wanted to reuse it for editing posts, the button label would be wrong. Instead, -some developers configure form buttons in the controller: - -.. code-block:: php +some developers configure form buttons in the controller:: namespace AppBundle\Controller\Admin; From fb1dd1f0722adbd2a6159efb11ec2d8edf41968d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 23 Nov 2014 13:13:01 +0100 Subject: [PATCH 425/835] typo fix --- contributing/documentation/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/documentation/overview.rst b/contributing/documentation/overview.rst index 0bf56f0820f..e610df1b994 100644 --- a/contributing/documentation/overview.rst +++ b/contributing/documentation/overview.rst @@ -170,7 +170,7 @@ Now you can **sync your fork** by executing the following command: $ git merge upstream/2.3 This command will update the ``2.3`` branch, which is the one you used to -create the new branch for your changes. If have used another base branch, +create the new branch for your changes. If you have used another base branch, e.g. ``master``, replace the ``2.3`` with the appropriate branch name. Great! Now you can proceed by following the same steps explained in the previous From 238beefd6f4fd215d4d515024520f33416e3f5bf Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 23 Nov 2014 21:32:57 +0100 Subject: [PATCH 426/835] Typo fix --- reference/forms/types/options/html5.rst.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/forms/types/options/html5.rst.inc b/reference/forms/types/options/html5.rst.inc index fc2ad0f57be..6a146e79eb1 100644 --- a/reference/forms/types/options/html5.rst.inc +++ b/reference/forms/types/options/html5.rst.inc @@ -9,5 +9,5 @@ html5 If this is set to ``true`` (the default), it'll use the HTML5 type (date, time or datetime) to render the field. When set to ``false``, it'll use the text type. -This is usefull when you want to use a custom JavaScript datapicker, which -often require a text type instead of a HTML5 type. +This is useful when you want to use a custom JavaScript datapicker, which +often requires a text type instead of a HTML5 type. From 086885b98eb9b36eea465384fe204485b1bca463 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 23 Nov 2014 21:56:56 +0100 Subject: [PATCH 427/835] Applied comments --- components/expression_language/extending.rst | 17 ++++++++++++----- reference/dic_tags.rst | 8 ++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst index 9a25be53736..9676452219e 100644 --- a/components/expression_language/extending.rst +++ b/components/expression_language/extending.rst @@ -51,6 +51,8 @@ an ``arguments`` variable as their first argument, which is equal to the second argument to ``evaluate()`` or ``compile()`` (e.g. the "values" when evaluating or the "names" if compiling). +.. _components-expression-language-provider: + Using Expression Providers -------------------------- @@ -63,9 +65,10 @@ creating a class that implements :class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface`. This interface requires one method: -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface::getFunctions`. -This method returns an array of expression functions (instances of -:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction`) to register. +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface::getFunctions`, +which returns an array of expression functions (instances of +:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction`) to +register. .. code-block:: php @@ -94,8 +97,12 @@ or by using the second argument of the constructor:: use Symfony\Component\ExpressionLanguage\ExpressionLanguage; // using the constructor - $language = new ExpressionLanguage(null, array(...)); + $language = new ExpressionLanguage(null, array( + new StringExpressionLanguageProvider(), + // ... + )); + // using registerProvider() $language->registerProvider(new StringExpressionLanguageProvider()); .. tip:: @@ -111,7 +118,7 @@ or by using the second argument of the constructor:: public function __construct(ParserCacheInterface $parser = null, array $providers = array()) { // prepend the default provider to let users override it easily - array_unshift($providers, new MyExpressionLanguageProvider()); + array_unshift($providers, new StringExpressionLanguageProvider()); parent::__construct($parser, $providers); } diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 472cb2600e7..87774a94f44 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -893,10 +893,10 @@ routing.expression_language_provider **Purpose**: Register a provider for expression language functions in routing -This tag is used to automatically register :ref:`expression function providers -` for the routing expression -component. Using these providers, you can add custom functions to the routing -expression language. +This tag is used to automatically register +:ref:`expression function providers ` +for the routing expression component. Using these providers, you can add custom +functions to the routing expression language. security.expression_language_provider ------------------------------------- From 9cbcc2bf06a0032d5c8459c39208c57fc8fa9b12 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 23 Nov 2014 22:09:02 +0100 Subject: [PATCH 428/835] Applied fixes --- components/dependency_injection/factories.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/dependency_injection/factories.rst b/components/dependency_injection/factories.rst index 7fe20414661..ab61e0782ad 100644 --- a/components/dependency_injection/factories.rst +++ b/components/dependency_injection/factories.rst @@ -5,8 +5,9 @@ Using a Factory to Create Services ================================== .. versionadded:: 2.6 - The new ``setFactory`` method was introduced in Symfony 2.6. Refer to older - versions for the syntax for factories prior to 2.6. + The new `Symfony\\Component\\DependencyInjection\\Definition::setFactory` + method was introduced in Symfony 2.6. Refer to older versions for the + syntax for factories prior to 2.6. Symfony's Service Container provides a powerful way of controlling the creation of objects, allowing you to specify arguments passed to the constructor @@ -92,10 +93,10 @@ be non-static. xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + - + @@ -106,7 +107,7 @@ be non-static. use Symfony\Component\DependencyInjection\Definition; // ... - $container->register('newsletter_manager.factory', 'createNewsletterManager'); + $container->register('newsletter_manager.factory', 'NewsletterManagerFactory'); $newsletterManager = new Definition(); $newsletterManager->setFactory(array( From d08925eb2152d02b9b17397174dd27c4a7de7a8c Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 23 Nov 2014 22:11:06 +0100 Subject: [PATCH 429/835] Yet another typo fix --- reference/forms/types/options/html5.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/options/html5.rst.inc b/reference/forms/types/options/html5.rst.inc index 6a146e79eb1..1022412f3b3 100644 --- a/reference/forms/types/options/html5.rst.inc +++ b/reference/forms/types/options/html5.rst.inc @@ -10,4 +10,4 @@ If this is set to ``true`` (the default), it'll use the HTML5 type (date, time or datetime) to render the field. When set to ``false``, it'll use the text type. This is useful when you want to use a custom JavaScript datapicker, which -often requires a text type instead of a HTML5 type. +often requires a text type instead of an HTML5 type. From bb085dae3c83d786dfb194b56d5a8a38ed2d7357 Mon Sep 17 00:00:00 2001 From: Gus Date: Thu, 20 Nov 2014 12:49:22 -0500 Subject: [PATCH 430/835] SetDescription required on Product entities Doctrine sets columns to NOT NULL by default. The code in the example throws an exception without the fix. --- book/doctrine.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/book/doctrine.rst b/book/doctrine.rst index 871695ff21d..e1f66134ed4 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -1105,6 +1105,7 @@ Now you can see this new code in action! Imagine you're inside a controller:: $product = new Product(); $product->setName('Foo'); $product->setPrice(19.99); + $product->setDescription('Lorem ipsum dolor'); // relate this product to the category $product->setCategory($category); From ef62911f2f3b5811ef1f74df3e95c7f7f759b608 Mon Sep 17 00:00:00 2001 From: micheal Date: Thu, 13 Nov 2014 00:14:49 -0800 Subject: [PATCH 431/835] Fix up the final sentence to be a bit cleaner. --- cookbook/security/target_path.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/security/target_path.rst b/cookbook/security/target_path.rst index 6f7a0536da1..c6c97de7b21 100644 --- a/cookbook/security/target_path.rst +++ b/cookbook/security/target_path.rst @@ -67,4 +67,4 @@ Next, create your own ``ExceptionListener``:: } } -Add as much or few logic here as required for your scenario! +Add as much or as little logic here as required for your scenario! From 6ef10db23d7e3fa747c201b8eaedb2a2da626545 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 24 Nov 2014 14:36:19 -0500 Subject: [PATCH 432/835] Moving forwarding section all the way to the bottom I could find very little real use-cases for this. --- book/controller.rst | 86 ++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/book/controller.rst b/book/controller.rst index 2193fc66cb9..1a9b8684ef1 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -515,49 +515,6 @@ The Symfony templating engine is explained in great detail in the // puts the content into a Response object for convenience $content = $templating->renderResponse('Hello/index.html.twig', array('name' => $name)); -.. index:: - single: Controller; Forwarding - -Forwarding -~~~~~~~~~~ - -You can also forward to another controller internally with the -:method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::forward` -method. Instead of redirecting the user's browser, it makes an internal sub-request, -and calls the controller. The ``forward()`` method returns the ``Response`` -object that's returned from *that* controller:: - - public function indexAction($name) - { - $response = $this->forward('AppBundle:Something:fancy', array( - 'name' => $name, - 'color' => 'green', - )); - - // ... further modify the response or return it directly - - return $response; - } - -Notice that the ``forward()`` method uses a special string representation -of the controller (see :ref:`controller-string-syntax`). In this case, the -target controller function will be ``SomethingController::fancyAction()`` -inside the ``AppBundle``. The array passed to the method becomes the arguments -on the resulting controller. This same idea is used when embedding controllers -into templates (see :ref:`templating-embedding-controller`). The target -controller method would look something like this:: - - public function fancyAction($name, $color) - { - // ... create and return a Response object - } - -Just like when creating a controller for a route, the order of the arguments of -``fancyAction`` doesn't matter. Symfony matches the index key names (e.g. -``name``) with the method argument names (e.g. ``$name``). If you change the -order of the arguments, Symfony will still pass the correct value to each -variable. - .. index:: single: Controller; Accessing services @@ -808,6 +765,49 @@ and template are needed). Use it! See :doc:`/cookbook/templating/render_without_controller`. +.. index:: + single: Controller; Forwarding + +Forwarding to Another Controller +-------------------------------- + +Though not very common, you can also forward to another controller internally +with the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::forward` +method. Instead of redirecting the user's browser, it makes an internal sub-request, +and calls the controller. The ``forward()`` method returns the ``Response`` +object that's returned from *that* controller:: + + public function indexAction($name) + { + $response = $this->forward('AppBundle:Something:fancy', array( + 'name' => $name, + 'color' => 'green', + )); + + // ... further modify the response or return it directly + + return $response; + } + +Notice that the ``forward()`` method uses a special string representation +of the controller (see :ref:`controller-string-syntax`). In this case, the +target controller function will be ``SomethingController::fancyAction()`` +inside the ``AppBundle``. The array passed to the method becomes the arguments +on the resulting controller. This same idea is used when embedding controllers +into templates (see :ref:`templating-embedding-controller`). The target +controller method would look something like this:: + + public function fancyAction($name, $color) + { + // ... create and return a Response object + } + +Just like when creating a controller for a route, the order of the arguments of +``fancyAction`` doesn't matter. Symfony matches the index key names (e.g. +``name``) with the method argument names (e.g. ``$name``). If you change the +order of the arguments, Symfony will still pass the correct value to each +variable. + Final Thoughts -------------- From ceb7b941dcfa3414d98041960d9943b4636fc87f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 24 Nov 2014 15:01:15 -0500 Subject: [PATCH 433/835] Big update based on feedback from xabbuh and WouterJ --- book/controller.rst | 70 +++++++++++++++------------------------------ 1 file changed, 23 insertions(+), 47 deletions(-) diff --git a/book/controller.rst b/book/controller.rst index 1a9b8684ef1..08e603fa3e1 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -202,18 +202,18 @@ to the controller: return $collection; Now, you can go to ``/hello/ryan`` (e.g. ``http://localhost:8000/app_dev.php/hello/ryan`` -if you're using the :doc:`built-in web server `_) +if you're using the :doc:`built-in web server `) and Symfony will execute the ``HelloController::indexAction()`` controller and pass in ``ryan`` for the ``$name`` variable. Creating a "page" means -simply creating a controller method and associated route. +simply creating a controller method and an associated route. Simple, right? -.. sidebar:: The AppBundle:Hello:index syntax for YML and XML +.. sidebar:: The AppBundle:Hello:index controller syntax If you use the YML or XML formats, you'll refer to the controller using a special shortcut syntax: ``AppBundle:Hello:index``. For more details - on this controllers format, see :ref:`controller-string-syntax`. + on the controller format, see :ref:`controller-string-syntax`. .. seealso:: @@ -236,15 +236,12 @@ that is passed to that method:: // ... use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - class HelloController + /** + * @Route("/hello/{name}", name="hello") + */ + public function indexAction($name) { - /** - * @Route("/hello/{name}", name="hello") - */ - public function indexAction($name) - { - // ... - } + // ... } The controller has a single argument, ``$name``, which corresponds to the @@ -260,6 +257,7 @@ Take the following more-interesting example: // src/AppBundle/Controller/HelloController.php // ... + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class HelloController @@ -360,7 +358,9 @@ the following guidelines in mind while you develop. Every route also has a special ``_route`` parameter, which is equal to the name of the route that was matched (e.g. ``hello``). Though not usually - useful, this is also available as a controller argument. + useful, this is also available as a controller argument. You can also + pass other variables from your route to your controller arguments. See + :doc:`/cookbook/routing/extra_information.`. .. _book-controller-request-argument: @@ -369,7 +369,7 @@ The ``Request`` as a Controller Argument What if you need to read query parameters, grab a request header or get access to an uploaded file? All of that information is stored in Symfony's ``Request`` -object. To get it in your contorller, just add it as an argument and +object. To get it in your controller, just add it as an argument and **type-hint it with the Request class**:: use Symfony\Component\HttpFoundation\Request; @@ -384,7 +384,7 @@ object. To get it in your contorller, just add it as an argument and .. seealso:: Want to know more about getting information from the request? See - :ref:`Access Request Information `. .. index:: single: Controller; Base controller class @@ -423,17 +423,6 @@ A great way to see the core functionality in action is to look in the This is optional, but can give you more control over the exact objects/dependencies that are injected into your controller. -.. index:: - single: Controller; Common tasks - -Common Controller Tasks ------------------------ - -Yes, a controller can do anything. But most controllers do the same basic -tasks over and over again. These tasks - like redirecting, rendering templates -and accessing core services - are easier with the shortcut methods you get -by extending the base ``Controller`` class. - .. index:: single: Controller; Redirecting @@ -502,19 +491,6 @@ The Symfony templating engine is explained in great detail in the ``AppBundle:Hello:index.html.twig`` would refer to the template located in ``src/AppBundle/Resources/views/Hello/index.html.twig``. See :ref:`template-referencing-in-bundle`. -.. tip:: - - The ``render`` method is a shortcut to direct use of the ``templating`` - service. The ``templating`` service can also be used directly:: - - $templating = $this->get('templating'); - - // returns the content - $content = $templating->render('Hello/index.html.twig', array('name' => $name)); - - // puts the content into a Response object for convenience - $content = $templating->renderResponse('Hello/index.html.twig', array('name' => $name)); - .. index:: single: Controller; Accessing services @@ -526,7 +502,7 @@ Accessing other Services Symfony comes packed with a lot of useful objects, called services. These are used for rendering templates, sending emails, querying the database and any other "work" you can think of. When you install a new bundle, it probably -brings in even *more* services. And of course, will even :ref:`add your own services `. +brings in even *more* services. When extending the base controller class, you can access any Symfony service via the ``get()`` method. Here are several common services you might need:: @@ -705,19 +681,19 @@ content that's sent back to the client:: $response->headers->set('Content-Type', 'application/json'); The ``headers`` property is a :class:`Symfony\\Component\\HttpFoundation\\HeaderBag` -object some nice methods for getting and setting the headers. The header -names are normalized so that using ``Content-Type`` is equivalent to ``content-type`` -or even ``content_type``. +object and has some nice methods for getting and setting the headers. The +header names are normalized so that using ``Content-Type`` is equivalent to +``content-type`` or even ``content_type``. There are also special classes to make certain kinds of responses easier: -- For JSON, there is :class:`Symfony\\Component\\HttpFoundation\\JsonResponse`. +* For JSON, there is :class:`Symfony\\Component\\HttpFoundation\\JsonResponse`. See :ref:`component-http-foundation-json-response`. -- For files, there is :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse`. +* For files, there is :class:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse`. See :ref:`component-http-foundation-serving-files`. -- For streamed responses, there is :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse`. +* For streamed responses, there is :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse`. See :ref:`streaming-response`. .. seealso:: @@ -763,7 +739,7 @@ Creating Static Pages You can create a static page without even creating a controller (only a route and template are needed). -Use it! See :doc:`/cookbook/templating/render_without_controller`. +See :doc:`/cookbook/templating/render_without_controller`. .. index:: single: Controller; Forwarding From c7876373c825a1ca898c8087e8ab0a55ac923344 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 24 Nov 2014 23:22:42 +0100 Subject: [PATCH 434/835] backport service factory improvements --- components/dependency_injection/factories.rst | 127 ++++++------------ 1 file changed, 44 insertions(+), 83 deletions(-) diff --git a/components/dependency_injection/factories.rst b/components/dependency_injection/factories.rst index 9f74efa50d5..6727bc838a5 100644 --- a/components/dependency_injection/factories.rst +++ b/components/dependency_injection/factories.rst @@ -15,9 +15,9 @@ the class. Suppose you have a factory that configures and returns a new ``NewsletterManager`` object:: - class NewsletterFactory + class NewsletterManagerFactory { - public function get() + public static function createNewsletterManager() { $newsletterManager = new NewsletterManager(); @@ -28,22 +28,18 @@ object:: } To make the ``NewsletterManager`` object available as a service, you can -configure the service container to use the ``NewsletterFactory`` factory +configure the service container to use the ``NewsletterManagerFactory`` factory class: .. configuration-block:: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: newsletter_manager: - class: "%newsletter_manager.class%" - factory_class: "%newsletter_factory.class%" - factory_method: get + class: NewsletterManager + factory_class: NewsletterManagerFactory + factory_method: createNewsletterManager .. code-block:: xml @@ -52,18 +48,12 @@ class: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - + class="NewsletterManager" + factory-class="NewsletterManagerFactory" + factory-method="createNewsletterManager" /> @@ -72,35 +62,29 @@ class: use Symfony\Component\DependencyInjection\Definition; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); - - $definition = new Definition('%newsletter_manager.class%'); - $definition->setFactoryClass('%newsletter_factory.class%'); - $definition->setFactoryMethod('get'); + $definition = new Definition('NewsletterManager'); + $definition->setFactoryClass('NewsletterManagerFactory'); + $definition->setFactoryMethod('createNewsletterManager'); $container->setDefinition('newsletter_manager', $definition); When you specify the class to use for the factory (via ``factory_class``) the method will be called statically. If the factory itself should be instantiated and the resulting object's method called, configure the factory itself as a service. -In this case, the method (e.g. get) should be changed to be non-static: +In this case, the method (e.g. ``createNewsletterManager``) should be changed +to be non-static: .. configuration-block:: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: - newsletter_factory: - class: "%newsletter_factory.class%" + newsletter_manager_factory: + class: NewsletterManagerFactory newsletter_manager: - class: "%newsletter_manager.class%" - factory_service: newsletter_factory - factory_method: get + class: NewsletterManager + factory_service: newsletter_manager_factory + factory_method: createNewsletterManager .. code-block:: xml @@ -109,20 +93,14 @@ In this case, the method (e.g. get) should be changed to be non-static: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - - + + class="NewsletterManager" + factory-service="newsletter_manager_factory" + factory-method="createNewsletterManager" /> @@ -130,19 +108,15 @@ In this case, the method (e.g. get) should be changed to be non-static: use Symfony\Component\DependencyInjection\Definition; - // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); - - $container->setDefinition('newsletter_factory', new Definition( - '%newsletter_factory.class%' + $container->setDefinition('newsletter_manager_factory', new Definition( + 'NewsletterManager' )); $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%' + 'NewsletterManagerFactory' ))->setFactoryService( - 'newsletter_factory' + 'newsletter_manager_factory' )->setFactoryMethod( - 'get' + 'createNewsletterManager' ); .. note:: @@ -155,24 +129,20 @@ Passing Arguments to the Factory Method --------------------------------------- If you need to pass arguments to the factory method, you can use the ``arguments`` -options inside the service container. For example, suppose the ``get`` method -in the previous example takes the ``templating`` service as an argument: +options inside the service container. For example, suppose the ``createNewsletterManager`` +method in the previous example takes the ``templating`` service as an argument: .. configuration-block:: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: - newsletter_factory: - class: "%newsletter_factory.class%" + newsletter_manager_factory: + class: NewsletterManagerFactory newsletter_manager: - class: "%newsletter_manager.class%" - factory_service: newsletter_factory - factory_method: get + class: NewsletterManager + factory_service: newsletter_manager_factory + factory_method: createNewsletterManager arguments: - "@templating" @@ -183,20 +153,14 @@ in the previous example takes the ``templating`` service as an argument: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - - + + class="NewsletterManager" + factory-service="newsletter_manager_factory" + factory-method="createNewsletterManager"> @@ -208,17 +172,14 @@ in the previous example takes the ``templating`` service as an argument: use Symfony\Component\DependencyInjection\Definition; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); - - $container->setDefinition('newsletter_factory', new Definition( - '%newsletter_factory.class%' + $container->setDefinition('newsletter_manager_factory', new Definition( + 'NewsletterManagerFactory' )); $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%', + 'NewsletterManager', array(new Reference('templating')) ))->setFactoryService( - 'newsletter_factory' + 'newsletter_manager_factory' )->setFactoryMethod( - 'get' + 'createNewsletterManager' ); From 903f52cfeaea737af3aad9388494aa5d43121f83 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 24 Nov 2014 19:31:03 -0500 Subject: [PATCH 435/835] Fixing build error --- book/controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/controller.rst b/book/controller.rst index 08e603fa3e1..1cfadb89c61 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -202,7 +202,7 @@ to the controller: return $collection; Now, you can go to ``/hello/ryan`` (e.g. ``http://localhost:8000/app_dev.php/hello/ryan`` -if you're using the :doc:`built-in web server `) +if you're using the :doc:`built-in web server `) and Symfony will execute the ``HelloController::indexAction()`` controller and pass in ``ryan`` for the ``$name`` variable. Creating a "page" means simply creating a controller method and an associated route. From 0d0b031daf44d7d693cb3e81d60c8195a9a3f56c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 24 Nov 2014 19:54:00 -0500 Subject: [PATCH 436/835] Removing bad period --- book/controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/controller.rst b/book/controller.rst index d8affd3306b..942d5a5baab 100644 --- a/book/controller.rst +++ b/book/controller.rst @@ -360,7 +360,7 @@ the following guidelines in mind while you develop. the name of the route that was matched (e.g. ``hello``). Though not usually useful, this is also available as a controller argument. You can also pass other variables from your route to your controller arguments. See - :doc:`/cookbook/routing/extra_information.`. + :doc:`/cookbook/routing/extra_information`. .. _book-controller-request-argument: From 5ee97915cf8b0a92545f005d7a47a13186275c94 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 25 Nov 2014 08:42:42 +0100 Subject: [PATCH 437/835] Some fixes --- book/forms.rst | 12 ++++++------ book/http_fundamentals.rst | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index 91d901f7647..dab0a3137f3 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -168,10 +168,10 @@ helper functions: That's it! Just three lines are needed to render the complete form: * ``form_start(form)`` - Renders the start tag of the form, including the - correct enctype attributes when using file uploads; + correct enctype attribute when using file uploads; -* ``form_widget(form)`` - Renders all fields, along with a label and error - message (if there is one) input element; +* ``form_widget(form)`` - Renders all of the fields, which includes the field + element itself, a label and any validation error messages for the field; * ``form_end()`` - Renders the end tag of the form and any fields that have not yet been rendered, in case you rendered each field yourself. This is useful @@ -779,9 +779,9 @@ the other functions do? ``div`` element. The majority of the work is done by the ``form_row`` helper, which renders -the label and HTML form widget of each field inside a ``div`` tag by default. -In the :ref:`form-theming` section, you'll learn how the ``form_row`` output -can be customized on many different levels. +the label, errors and HTML form widget of each field inside a ``div`` tag by +default. In the :ref:`form-theming` section, you'll learn how the ``form_row`` +output can be customized on many different levels. .. tip:: diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index db2f9d86907..eaadacff92f 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -524,7 +524,7 @@ regardless of how your project is developed. To name a few: about how that request should be handled (e.g. execute the ``contactAction()`` method); -* `Form ` - A full-featured and flexible +* :doc:`Form ` - A full-featured and flexible framework for creating forms and handling form submissions; * `Validator`_ - A system for creating rules about data and then validating @@ -534,10 +534,10 @@ regardless of how your project is developed. To name a few: templates, handling template inheritance (i.e. a template is decorated with a layout) and performing other common template tasks; -* `Security ` - A powerful library for +* :doc:`Security ` - A powerful library for handling all types of security inside an application; -* `Translation ` - A framework for +* :doc:`Translation ` - A framework for translating strings in your application. Each and every one of these components is decoupled and can be used in *any* From 828ba4d2ffc07773dc70be006e4a25ddbca78c3b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 24 Nov 2014 23:06:11 +0100 Subject: [PATCH 438/835] document configurable PropertyAccessor arguments --- components/property_access/introduction.rst | 2 ++ reference/configuration/framework.rst | 23 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 55bbda909c3..0b0265dabec 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -175,6 +175,8 @@ The ``getValue`` method can also use the magic ``__get`` method:: echo $accessor->getValue($person, 'Wouter'); // array(...) +.. _components-property-access-magic-call: + Magic ``__call()`` Method ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 9cfb5b6947b..991c55aaf34 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -48,6 +48,9 @@ Configuration * `profiler`_ * `collect`_ * :ref:`enabled ` +* `property_accessor`_ + * `magic_call`_ + * `throw_exception_on_invalid_index`_ secret ~~~~~~ @@ -495,6 +498,26 @@ and activate the data collectors by hand:: $profiler->enable(); +property_accessor +~~~~~~~~~~~~~~~~~ + +magic_call +.......... + +**type**: ``boolean`` **default**: ``false`` + +When enabled, the ``property_accessor`` service uses PHP's +:ref:`magic __call() method ` when +its ``getValue()`` method is called. + +throw_exception_on_invalid_index +................................ + +**type**: ``boolean`` **default**: ``false`` + +When enabled, the ``property_accessor`` service throws an exception when you +try to access an invalid index of an array. + Full default Configuration -------------------------- From dbba41a335efb39df54c120df6b5e232c7a148d2 Mon Sep 17 00:00:00 2001 From: Alex Luneburg Date: Fri, 28 Nov 2014 09:26:17 +1100 Subject: [PATCH 439/835] Add missing semicolons to PropertyAccess examples --- components/property_access/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 8d6492c8d8e..21d007545dd 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -363,7 +363,7 @@ configured to enable extra features. To do that you could use the $accessorBuilder->disableMagicCall(); // Check if magic __call handling is enabled - $accessorBuilder->isMagicCallEnabled() // true or false + $accessorBuilder->isMagicCallEnabled(); // true or false // At the end get the configured property accessor $accessor = $accessorBuilder->getPropertyAccessor(); @@ -376,7 +376,7 @@ configured to enable extra features. To do that you could use the Or you can pass parameters directly to the constructor (not the recommended way):: // ... - $accessor = new PropertyAccessor(true) // this enables handling of magic __call + $accessor = new PropertyAccessor(true); // this enables handling of magic __call .. _Packagist: https://packagist.org/packages/symfony/property-access From d6b4795ee4607771f6da68021bca5ccea3b12c22 Mon Sep 17 00:00:00 2001 From: Alex Luneburg Date: Fri, 28 Nov 2014 20:04:10 +1100 Subject: [PATCH 440/835] Add missing brackets to PropertyAccessor examples --- components/property_access/introduction.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/property_access/introduction.rst b/components/property_access/introduction.rst index 55bbda909c3..14a42a1aeeb 100644 --- a/components/property_access/introduction.rst +++ b/components/property_access/introduction.rst @@ -327,7 +327,7 @@ instead:: $person = new Person(); - if ($accessor->isReadable($person, 'firstName') { + if ($accessor->isReadable($person, 'firstName')) { // ... } @@ -338,7 +338,7 @@ method to find out whether a property path can be updated:: $person = new Person(); - if ($accessor->isWritable($person, 'firstName') { + if ($accessor->isWritable($person, 'firstName')) { // ... } From e431fdb55f58b415a7f6eb77699b043c02920037 Mon Sep 17 00:00:00 2001 From: skwi Date: Wed, 12 Nov 2014 23:24:15 +0100 Subject: [PATCH 441/835] New validation API usage in Class Constraint Validator --- cookbook/validation/custom_constraint.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cookbook/validation/custom_constraint.rst b/cookbook/validation/custom_constraint.rst index f95d311ed20..d6f9f4a3f9a 100644 --- a/cookbook/validation/custom_constraint.rst +++ b/cookbook/validation/custom_constraint.rst @@ -230,12 +230,20 @@ With this, the validator ``validate()`` method gets an object as its first argum public function validate($protocol, Constraint $constraint) { if ($protocol->getFoo() != $protocol->getBar()) { + // If you're using the new 2.5 validation API (you probably are!) + $this->context->buildViolation($constraint->message) + ->atPath('foo') + ->addViolation(); + + // If you're using the old 2.4 validation API + /* $this->context->addViolationAt( 'foo', $constraint->message, array(), null ); + */ } } } From 25e1069ef7873b430718ffbd5fba1f49817ccdc0 Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Sat, 29 Nov 2014 10:35:35 +0100 Subject: [PATCH 442/835] Added notes specifying the propagation behaviour for kernel.view and kernel.exception. --- components/http_kernel/introduction.rst | 10 ++++++++++ cookbook/service_container/event_listener.rst | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index 3fee78f30b7..698a770f1ed 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -391,6 +391,11 @@ At this stage, if no listener sets a response on the event, then an exception is thrown: either the controller *or* one of the view listeners must always return a ``Response``. +.. note:: + + When setting a response for the ``kernel.view`` event, the propagation + is stopped, so the lower priority listeners on that event don't get called. + .. sidebar:: ``kernel.view`` in the Symfony Framework There is no default listener inside the Symfony Framework for the ``kernel.view`` @@ -522,6 +527,11 @@ comes with an :class:`Symfony\\Component\\HttpKernel\\EventListener\\ExceptionLi which if you choose to use, will do this and more by default (see the sidebar below for more details). +.. note:: + + When setting a response for the ``kernel.exception`` event, the propagation + is stopped, so the lower priority listeners on that event don't get called. + .. sidebar:: ``kernel.exception`` in the Symfony Framework There are two main listeners to ``kernel.exception`` when using the diff --git a/cookbook/service_container/event_listener.rst b/cookbook/service_container/event_listener.rst index a78acded921..2fb529b64a6 100644 --- a/cookbook/service_container/event_listener.rst +++ b/cookbook/service_container/event_listener.rst @@ -57,6 +57,12 @@ event is just one of the core kernel events:: the ``kernel.exception`` event, it is :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent`. To see what type of object each event listener receives, see :class:`Symfony\\Component\\HttpKernel\\KernelEvents`. +.. note:: + + When setting a response for the ``kernel.view`` or ``kernel.exception`` + events, the propagation is stopped, so the lower priority listeners on + that event don't get called. + Now that the class is created, you just need to register it as a service and notify Symfony that it is a "listener" on the ``kernel.exception`` event by using a special "tag": From 5bc74702090961390d53e1a8530f283c8e371ea2 Mon Sep 17 00:00:00 2001 From: Andrew M Date: Sat, 29 Nov 2014 11:32:46 +0100 Subject: [PATCH 443/835] Add Ryan Weaver as 10th core team member --- contributing/code/core_team.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributing/code/core_team.rst b/contributing/code/core_team.rst index 03f866f2e3e..e1d7ca3a45a 100644 --- a/contributing/code/core_team.rst +++ b/contributing/code/core_team.rst @@ -63,7 +63,8 @@ Active Core Members * **Jakub Zalas** (:decider:`jakzal`); * **Jordi Boggiano** (:decider:`seldaek`); - * **Lukas Kahwe Smith** (:decider:`lsmith77`). + * **Lukas Kahwe Smith** (:decider:`lsmith77`); + * **Ryan Weaver** (:decider:`weaverryan`). Core Membership Application ~~~~~~~~~~~~~~~~~~~~~~~~~~~ From f7de50e51459c655578abee69ad45eee9be95872 Mon Sep 17 00:00:00 2001 From: jms85 Date: Sat, 29 Nov 2014 11:42:21 +0100 Subject: [PATCH 444/835] Update link to remove absolute URL --- best_practices/controllers.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst index 05cd7a1af9b..5d34928526b 100644 --- a/best_practices/controllers.rst +++ b/best_practices/controllers.rst @@ -206,7 +206,6 @@ Pre and Post Hooks ------------------ If you need to execute some code before or after the execution of your controllers, -you can use the EventDispatcher component to `set up before/after filters`_. +you can use the EventDispatcher component to :doc:`/cookbook/event_dispatcher/before_after_filters`. .. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html -.. _`set up before/after filters`: http://symfony.com/doc/current/cookbook/event_dispatcher/before_after_filters.html From d13943a8029aab08ad943242b87c3fc6e17f04ad Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Sat, 29 Nov 2014 14:02:00 +0100 Subject: [PATCH 445/835] Add missing info about kernel.request event. --- components/http_kernel/introduction.rst | 5 +++++ cookbook/service_container/event_listener.rst | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index 698a770f1ed..dfed94ab88d 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -167,6 +167,11 @@ return a ``Response`` directly, or to add information to the ``Request`` (e.g. setting the locale or setting some other information on the ``Request`` attributes). +.. note:: + + When setting a response for the ``kernel.view`` event, the propagation + is stopped, so the lower priority listeners on that event don't get called. + .. sidebar:: ``kernel.request`` in the Symfony Framework The most important listener to ``kernel.request`` in the Symfony Framework diff --git a/cookbook/service_container/event_listener.rst b/cookbook/service_container/event_listener.rst index 2fb529b64a6..ecba442b697 100644 --- a/cookbook/service_container/event_listener.rst +++ b/cookbook/service_container/event_listener.rst @@ -59,9 +59,9 @@ event is just one of the core kernel events:: .. note:: - When setting a response for the ``kernel.view`` or ``kernel.exception`` - events, the propagation is stopped, so the lower priority listeners on - that event don't get called. + When setting a response for the ``kernel.request``, ``kernel.view`` and + ``kernel.exception`` events, the propagation is stopped, so the lower + priority listeners on that event don't get called. Now that the class is created, you just need to register it as a service and notify Symfony that it is a "listener" on the ``kernel.exception`` event by From 37c29e9de23158c4f5731e14b122f64fe4b66972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garz=C3=83=C2=B3n?= Date: Sat, 29 Nov 2014 00:54:07 +0100 Subject: [PATCH 446/835] Book: Update link title to match cookbook article title --- book/page_creation.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/book/page_creation.rst b/book/page_creation.rst index f3acbfb120e..085f98e9cc6 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -897,9 +897,8 @@ The extension alias (configuration key) can also be used: .. note:: - See the cookbook article: - :doc:`How to expose a Semantic Configuration for a Bundle ` - for information on adding configuration for your own bundle. + See the cookbook article: :doc:`/cookbook/bundles/extension` for + information on adding configuration for your own bundle. .. index:: single: Environments; Introduction From 23db11a83095691035023d2fe28b307af5edbd0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garz=C3=83=C2=B3n?= Date: Sat, 29 Nov 2014 00:58:06 +0100 Subject: [PATCH 447/835] Best Practices: Update link title to match cookbook article title --- best_practices/configuration.rst | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/best_practices/configuration.rst b/best_practices/configuration.rst index 3368c59589e..136f8a3778c 100644 --- a/best_practices/configuration.rst +++ b/best_practices/configuration.rst @@ -74,9 +74,9 @@ add an extra layer of configuration that's not needed because you don't need or want these configuration values to change on each server. The configuration options defined in the ``config.yml`` file usually vary from -one `execution environment`_ to another. That's why Symfony already includes -``app/config/config_dev.yml`` and ``app/config/config_prod.yml`` files so -that you can override specific values for each environment. +one :doc:`/cookbook/configuration/environments` to another. That's why Symfony +already includes ``app/config/config_dev.yml`` and ``app/config/config_prod.yml`` +files so that you can override specific values for each environment. Constants vs Configuration Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -158,10 +158,10 @@ Semantic Configuration: Don't Do It Don't define a semantic dependency injection configuration for your bundles. -As explained in `How to Expose a semantic Configuration for a Bundle`_ article, -Symfony bundles have two choices on how to handle configuration: normal service -configuration through the ``services.yml`` file and semantic configuration -through a special ``*Extension`` class. +As explained in :doc:`/cookbook/bundles/extension` article, Symfony bundles +have two choices on how to handle configuration: normal service configuration +through the ``services.yml`` file and semantic configuration through a special +``*Extension`` class. Although semantic configuration is much more powerful and provides nice features such as configuration validation, the amount of work needed to define that @@ -174,10 +174,7 @@ Moving Sensitive Options Outside of Symfony Entirely When dealing with sensitive options, like database credentials, we also recommend that you store them outside the Symfony project and make them available through environment variables. Learn how to do it in the following article: -`How to Set external Parameters in the Service Container`_ +:doc:`/cookbook/configuration/external_parameters` .. _`feature toggles`: http://en.wikipedia.org/wiki/Feature_toggle -.. _`execution environment`: http://symfony.com/doc/current/cookbook/configuration/environments.html .. _`constant() function`: http://twig.sensiolabs.org/doc/functions/constant.html -.. _`How to Expose a semantic Configuration for a Bundle`: http://symfony.com/doc/current/cookbook/bundles/extension.html -.. _`How to Set external Parameters in the Service Container`: http://symfony.com/doc/current/cookbook/configuration/external_parameters.html From e72975037c1ce553150baff88e5a546c312bb203 Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Sat, 29 Nov 2014 14:31:49 +0100 Subject: [PATCH 448/835] Changed phrasing to explain the effects off propagation stopping. --- components/http_kernel/introduction.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index dfed94ab88d..6168d4ca196 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -170,7 +170,7 @@ attributes). .. note:: When setting a response for the ``kernel.view`` event, the propagation - is stopped, so the lower priority listeners on that event don't get called. + is stopped. This means listeners with lower priority won't be executed. .. sidebar:: ``kernel.request`` in the Symfony Framework @@ -399,7 +399,7 @@ return a ``Response``. .. note:: When setting a response for the ``kernel.view`` event, the propagation - is stopped, so the lower priority listeners on that event don't get called. + is stopped. This means listeners with lower priority won't be executed. .. sidebar:: ``kernel.view`` in the Symfony Framework @@ -535,7 +535,7 @@ below for more details). .. note:: When setting a response for the ``kernel.exception`` event, the propagation - is stopped, so the lower priority listeners on that event don't get called. + is stopped. This means listeners with lower priority won't be executed. .. sidebar:: ``kernel.exception`` in the Symfony Framework From 6aabece040cda7976a6b702bf4e7a8cd2818e007 Mon Sep 17 00:00:00 2001 From: Benjamin Clay Date: Sat, 29 Nov 2014 14:46:10 +0100 Subject: [PATCH 449/835] bug #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook --- cookbook/doctrine/mapping_model_classes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/doctrine/mapping_model_classes.rst b/cookbook/doctrine/mapping_model_classes.rst index 975d95cd0be..aca2ff18f98 100644 --- a/cookbook/doctrine/mapping_model_classes.rst +++ b/cookbook/doctrine/mapping_model_classes.rst @@ -18,7 +18,7 @@ register the mappings for your model classes. .. versionadded:: 2.3 The base mapping compiler pass was introduced in Symfony 2.3. The Doctrine bundles - support it from DoctrineBundle >= 1.2.1, MongoDBBundle >= 3.0.0, + support it from DoctrineBundle >= 1.3.0, MongoDBBundle >= 3.0.0, PHPCRBundle >= 1.0.0-alpha2 and the (unversioned) CouchDBBundle supports the compiler pass since the `CouchDB Mapping Compiler Pass pull request`_ was merged. From 3d75f6af69e5e58ff70159af3a4ebdb07b583ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9sar=20Su=C3=A1rez=20Ortega?= Date: Sat, 29 Nov 2014 14:21:27 +0100 Subject: [PATCH 450/835] Update conventions.rst The ``trigger_error``call seems to be written in one line: https://github.com/symfony/symfony/pull/12671/files --- contributing/code/conventions.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contributing/code/conventions.rst b/contributing/code/conventions.rst index 67962aebc00..6f102e8f6a2 100644 --- a/contributing/code/conventions.rst +++ b/contributing/code/conventions.rst @@ -103,7 +103,4 @@ A PHP ``E_USER_DEPRECATED`` error must also be triggered to help people with the migration starting one or two minor versions before the version where the feature will be removed (depending on the criticality of the removal):: - trigger_error( - 'XXX() is deprecated since version 2.X and will be removed in 2.Y. Use XXX instead.', - E_USER_DEPRECATED - ); + trigger_error('XXX() is deprecated since version 2.X and will be removed in 2.Y. Use XXX instead.', E_USER_DEPRECATED); From ae877f24883bd5aefad3c52b363a9ed60f5b2bea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mar=C3=ADa=20Sanchidri=C3=A1n?= Date: Sat, 29 Nov 2014 12:36:46 +0100 Subject: [PATCH 451/835] Remove absolute url from override_dir_structure link --- best_practices/creating-the-project.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index 244cb6741b4..16402420df4 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -220,7 +220,8 @@ Extending the Directory Structure --------------------------------- If your project or infrastructure requires some changes to the default directory -structure of Symfony, you can `override the location of the main directories`_: +structure of Symfony, you can +:doc:`override the location of the main directories `: ``cache/``, ``logs/`` and ``web/``. In addition, Symfony3 will use a slightly different directory structure when @@ -247,6 +248,5 @@ the Symfony2 directory structure. .. _`Composer`: https://getcomposer.org/ .. _`Get Started`: https://getcomposer.org/doc/00-intro.md .. _`Composer download page`: https://getcomposer.org/download/ -.. _`override the location of the main directories`: http://symfony.com/doc/current/cookbook/configuration/override_dir_structure.html .. _`public checksums repository`: https://github.com/sensiolabs/checksums .. _`these steps`: http://fabien.potencier.org/article/73/signing-project-releases From cf32b4c68480054a067f1f0ce1c176b6eb105df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Mar=C3=ADa=20Sanchidri=C3=A1n?= Date: Sat, 29 Nov 2014 14:24:56 +0100 Subject: [PATCH 452/835] Remove absolutes url --- best_practices/forms.rst | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/best_practices/forms.rst b/best_practices/forms.rst index 883f2c8a998..e8a7192fc82 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -69,9 +69,11 @@ To use the class, use ``createForm`` and instantiate the new class: Registering Forms as Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can also `register your form type as a service`_. But this is *not* recommended -unless you plan to reuse the new form type in many places or embed it in -other forms directly or via the `collection type`_. +You can also +:ref:`register your form type as a service `. +But this is *not* recommended unless you plan to reuse the new form type in many +places or embed it in other forms directly or via the +:doc:`collection type `. For most forms that are used only to edit or create something, registering the form as a service is over-kill, and makes it more difficult to figure @@ -169,7 +171,7 @@ fields: If you need more control over how your fields are rendered, then you should remove the ``form_widget(form)`` function and render your fields individually. -See `How to Customize Form Rendering`_ for more information on this and how +See :doc:`/cookbook/form/form_customization` for more information on this and how you can control *how* the form renders at a global level using form theming. Handling Form Submits @@ -210,8 +212,3 @@ Second, we recommend using ``$form->isSubmitted()`` in the ``if`` statement for clarity. This isn't technically needed, since ``isValid()`` first calls ``isSubmitted()``. But without this, the flow doesn't read well as it *looks* like the form is *always* processed (even on the GET request). - -.. _`register your form type as a service`: http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html#creating-your-field-type-as-a-service -.. _`collection type`: http://symfony.com/doc/current/reference/forms/types/collection.html -.. _`How to Customize Form Rendering`: http://symfony.com/doc/current/cookbook/form/form_customization.html -.. _`form event system`: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html From 079bd64dc7b0ac1107d48aed4eb6e38ea4d261cd Mon Sep 17 00:00:00 2001 From: Daniel Garzon Date: Sat, 29 Nov 2014 12:34:17 +0100 Subject: [PATCH 453/835] Change symfony-docs internal links avoiding absolute urls --- best_practices/web-assets.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/best_practices/web-assets.rst b/best_practices/web-assets.rst index 862e60987a5..a45d85542b5 100644 --- a/best_practices/web-assets.rst +++ b/best_practices/web-assets.rst @@ -49,8 +49,9 @@ tools like GruntJS. Use Assetic to compile, combine and minimize web assets, unless you're comfortable with frontend tools like GruntJS. -`Assetic`_ is an asset manager capable of compiling assets developed with -a lot of different frontend technologies like LESS, Sass and CoffeeScript. +:doc:`Assetic ` is an asset manager capable +of compiling assets developed with a lot of different frontend technologies +like LESS, Sass and CoffeeScript. Combining all your assets with Assetic is a matter of wrapping all the assets with a single Twig tag: @@ -86,12 +87,11 @@ separating the repositories if you want). Learn More about Assetic ------------------------ -Assetic can also minimize CSS and JavaScript assets `using UglifyCSS/UglifyJS`_ -to speed up your websites. You can even `compress images`_ with Assetic to -reduce their size before serving them to the user. Check out the -`official Assetic documentation`_ to learn more about all the available features. +Assetic can also minimize CSS and JavaScript assets +:doc:`using UglifyCSS/UglifyJS ` to speed up your +websites. You can even :doc:`compress images ` +with Assetic to reduce their size before serving them to the user. Check out +the `official Assetic documentation`_ to learn more about all the available +features. -.. _`Assetic`: http://symfony.com/doc/current/cookbook/assetic/asset_management.html -.. _`using UglifyCSS/UglifyJS`: http://symfony.com/doc/current/cookbook/assetic/uglifyjs.html -.. _`compress images`: http://symfony.com/doc/current/cookbook/assetic/jpeg_optimize.html .. _`official Assetic documentation`: https://github.com/kriswallsmith/assetic From c7f9cf5e3592834ead25026ccdf591612936a191 Mon Sep 17 00:00:00 2001 From: Daniel Garzon Date: Sat, 29 Nov 2014 12:41:39 +0100 Subject: [PATCH 454/835] Remove unused links --- best_practices/templates.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/best_practices/templates.rst b/best_practices/templates.rst index 0b6936ba3ef..01a4b80e80b 100644 --- a/best_practices/templates.rst +++ b/best_practices/templates.rst @@ -156,9 +156,5 @@ name is irrelevant because you never use it in your own code): tags: - { name: twig.extension } - .. _`Twig`: http://twig.sensiolabs.org/ .. _`Parsedown`: http://parsedown.org/ -.. _`Twig global variables`: http://symfony.com/doc/master/cookbook/templating/global_variables.html -.. _`override error pages`: http://symfony.com/doc/current/cookbook/controller/error_pages.html -.. _`render a template without using a controller`: http://symfony.com/doc/current/cookbook/templating/render_without_controller.html From ffa17b25356d7a25e0d3619bb47f1d5226fda355 Mon Sep 17 00:00:00 2001 From: Daniel Garzon Date: Sat, 29 Nov 2014 14:03:46 +0100 Subject: [PATCH 455/835] Change symfony-docs internal links avoiding absolute urls in security article --- best_practices/security.rst | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/best_practices/security.rst b/best_practices/security.rst index 026c672bcaa..4c18acc878b 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -6,7 +6,8 @@ Authentication and Firewalls (i.e. Getting the User's Credentials) You can configure Symfony to authenticate your users using any method you want and to load user information from any source. This is a complex topic, -but the `Security Cookbook Section`_ has a lot of information about this. +but the :doc:`Security Cookbook Section ` has a +lot of information about this. Regardless of your needs, authentication is configured in ``security.yml``, primarily under the ``firewalls`` key. @@ -72,8 +73,9 @@ Authorization (i.e. Denying Access) ----------------------------------- Symfony gives you several ways to enforce authorization, including the ``access_control`` -configuration in `security.yml`_, the :ref:`@Security annotation ` -and using :ref:`isGranted ` on the ``security.context`` +configuration in :doc:`security.yml ` the +:ref:`@Security annotation ` and using +:ref:`isGranted ` on the ``security.context`` service directly. .. best-practice:: @@ -240,8 +242,8 @@ Security Voters If your security logic is complex and can't be centralized into a method like ``isAuthor()``, you should leverage custom voters. These are an order -of magnitude easier than `ACL's`_ and will give you the flexibility you need -in almost all cases. +of magnitude easier than :doc:`ACL's ` and will give +you the flexibility you need in almost all cases. First, create a voter class. The following example shows a voter that implements the same ``getAuthorEmail`` logic you used above: @@ -337,27 +339,18 @@ The `FOSUserBundle`_, developed by the Symfony community, adds support for a database-backed user system in Symfony2. It also handles common tasks like user registration and forgotten password functionality. -Enable the `Remember Me feature`_ to allow your users to stay logged in for -a long period of time. +Enable the :doc:`Remember Me feature ` to +allow your users to stay logged in for a long period of time. When providing customer support, sometimes it's necessary to access the application as some *other* user so that you can reproduce the problem. Symfony provides -the ability to `impersonate users`_. +the ability to :doc:`impersonate users `. If your company uses a user login method not supported by Symfony, you can -develop `your own user provider`_ and `your own authentication provider`_. +develop :doc:`your own user provider ` and +:doc:`your own authentication provider `. -.. _`Security Cookbook Section`: http://symfony.com/doc/current/cookbook/security/index.html -.. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html .. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html .. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html -.. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html -.. _`security voter`: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html -.. _`Acces Control List`: http://symfony.com/doc/current/cookbook/security/acl.html -.. _`ACL's`: http://symfony.com/doc/current/cookbook/security/acl.html .. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle -.. _`Remember Me feature`: http://symfony.com/doc/current/cookbook/security/remember_me.html -.. _`impersonate users`: http://symfony.com/doc/current/cookbook/security/impersonating_user.html -.. _`your own user provider`: http://symfony.com/doc/current/cookbook/security/custom_provider.html -.. _`your own authentication provider`: http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html From 7282965f1fa98ab1bbe4619947ac66a5a22d3b20 Mon Sep 17 00:00:00 2001 From: Daniel Garzon Date: Sat, 29 Nov 2014 14:08:35 +0100 Subject: [PATCH 456/835] Change symfony-docs internal links avoiding absolute urls in introduction article - Removed unused links --- best_practices/introduction.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/best_practices/introduction.rst b/best_practices/introduction.rst index 6a83cd2ae6b..d1d0e760d32 100644 --- a/best_practices/introduction.rst +++ b/best_practices/introduction.rst @@ -59,7 +59,7 @@ Who this Book Is for (Hint: It's not a Tutorial) Any Symfony developer, whether you are an expert or a newcomer, can read this guide. But since this isn't a tutorial, you'll need some basic knowledge of Symfony to follow everything. If you are totally new to Symfony, welcome! -Start with `The Quick Tour`_ tutorial first. +Start with :doc:`The Quick Tour ` tutorial first. We've deliberately kept this guide short. We won't repeat explanations that you can find in the vast Symfony documentation, like discussions about dependency @@ -95,7 +95,3 @@ practices**. The reasons for not doing it are various: your tests or adding features that provide real value to the end users. .. _`Fabien Potencier`: https://connect.sensiolabs.com/profile/fabpot -.. _`The Quick Tour`: http://symfony.com/doc/current/quick_tour/the_big_picture.html -.. _`The Official Symfony Book`: http://symfony.com/doc/current/book/index.html -.. _`The Symfony Cookbook`: http://symfony.com/doc/current/cookbook/index.html -.. _`github.com/.../...`: http://github.com/.../... From d1e7334bf94ac82c0f73d549d1b8543f0692c008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garz=C3=B3n?= Date: Sat, 29 Nov 2014 15:35:22 +0100 Subject: [PATCH 457/835] Remove ExpressionLanguage reference for 2.3 version Remove "Using Expressions for Complex Security Restrictions" section using ExpressionLanguage in 2.3 version. ExpressionLanguage is a 2.4 feature. --- best_practices/security.rst | 85 ------------------------------------- 1 file changed, 85 deletions(-) diff --git a/best_practices/security.rst b/best_practices/security.rst index 026c672bcaa..5f5edea2304 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -121,89 +121,6 @@ Using ``@Security``, this looks like: // ... } -Using Expressions for Complex Security Restrictions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your security logic is a little bit more complex, you can use an `expression`_ -inside ``@Security``. In the following example, a user can only access the -controller if their email matches the value returned by the ``getAuthorEmail`` -method on the ``Post`` object: - -.. code-block:: php - - use AppBundle\Entity\Post; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; - - /** - * @Route("/{id}/edit", name="admin_post_edit") - * @Security("user.getEmail() == post.getAuthorEmail()") - */ - public function editAction(Post $post) - { - // ... - } - -Notice that this requires the use of the `ParamConverter`_, which automatically -queries for the ``Post`` object and puts it on the ``$post`` argument. This -is what makes it possible to use the ``post`` variable in the expression. - -This has one major drawback: an expression in an annotation cannot easily -be reused in other parts of the application. Imagine that you want to add -a link in a template that will only be seen by authors. Right now you'll -need to repeat the expression code using Twig syntax: - -.. code-block:: html+jinja - - {% if app.user and app.user.email == post.authorEmail %} - ... - {% endif %} - -The easiest solution - if your logic is simple enough - is to add a new method -to the ``Post`` entity that checks if a given user is its author: - -.. code-block:: php - - // src/AppBundle/Entity/Post.php - // ... - - class Post - { - // ... - - /** - * Is the given User the author of this Post? - * - * @return bool - */ - public function isAuthor(User $user = null) - { - return $user && $user->getEmail() == $this->getAuthorEmail(); - } - } - -Now you can reuse this method both in the template and in the security expression: - -.. code-block:: php - - use AppBundle\Entity\Post; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; - - /** - * @Route("/{id}/edit", name="admin_post_edit") - * @Security("post.isAuthor(user)") - */ - public function editAction(Post $post) - { - // ... - } - -.. code-block:: html+jinja - - {% if post.isAuthor(app.user) %} - ... - {% endif %} - .. _best-practices-directy-isGranted: Checking Permissions without @Security @@ -349,13 +266,11 @@ develop `your own user provider`_ and `your own authentication provider`_. .. _`Security Cookbook Section`: http://symfony.com/doc/current/cookbook/security/index.html .. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html -.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html .. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html .. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html .. _`security voter`: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html .. _`Acces Control List`: http://symfony.com/doc/current/cookbook/security/acl.html .. _`ACL's`: http://symfony.com/doc/current/cookbook/security/acl.html -.. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle .. _`Remember Me feature`: http://symfony.com/doc/current/cookbook/security/remember_me.html .. _`impersonate users`: http://symfony.com/doc/current/cookbook/security/impersonating_user.html From 332b6d4028d2a8d5baed12009ff886076d89168c Mon Sep 17 00:00:00 2001 From: Jerzy Zawadzki Date: Sat, 29 Nov 2014 17:00:27 +0100 Subject: [PATCH 458/835] Changed url to PHP-CS-FIXER repository --- best_practices/business-logic.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index b2d349ce2f5..7365caefd22 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -339,4 +339,4 @@ in a matter of seconds. .. _`PSR-1`: http://www.php-fig.org/psr/psr-1/ .. _`PSR-2`: http://www.php-fig.org/psr/psr-2/ .. _`the Symfony Code Standards`: http://symfony.com/doc/current/contributing/code/standards.html -.. _`PHP-CS-Fixer`: https://github.com/fabpot/PHP-CS-Fixer +.. _`PHP-CS-Fixer`: https://github.com/FriendsOfPHP/PHP-CS-Fixer From bebce0e0f9f7969e4b9cd7688737d142a132021c Mon Sep 17 00:00:00 2001 From: Roger Llopart Pla Date: Sun, 30 Nov 2014 11:17:47 +0100 Subject: [PATCH 459/835] Fix incorrect event name. --- components/http_kernel/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_kernel/introduction.rst b/components/http_kernel/introduction.rst index 6168d4ca196..a260aa5a9ad 100644 --- a/components/http_kernel/introduction.rst +++ b/components/http_kernel/introduction.rst @@ -169,7 +169,7 @@ attributes). .. note:: - When setting a response for the ``kernel.view`` event, the propagation + When setting a response for the ``kernel.request`` event, the propagation is stopped. This means listeners with lower priority won't be executed. .. sidebar:: ``kernel.request`` in the Symfony Framework From 27cb445c14e43c0f84d724e8c4c00a1ae3a384d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Garz=C3=B3n?= Date: Mon, 1 Dec 2014 17:42:01 +0100 Subject: [PATCH 460/835] Update Symfony reference to 2.6 version Update Symfony reference to from 2.5 to 2.6 version --- book/installation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index 4aa6f3b6277..8af678fff54 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -59,7 +59,7 @@ Distribution: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony '~2.5' + $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony '~2.6' .. tip:: @@ -112,10 +112,10 @@ one of the following commands (replacing ``###`` with your actual filename): .. code-block:: bash # for .tgz file - $ tar zxvf Symfony_Standard_Vendors_2.5.###.tgz + $ tar zxvf Symfony_Standard_Vendors_2.6.###.tgz # for a .zip file - $ unzip Symfony_Standard_Vendors_2.5.###.zip + $ unzip Symfony_Standard_Vendors_2.6.###.zip If you've downloaded "without vendors", you'll definitely need to read the next section. From 21b00b01682a8eed0f8ab4aafc0ce03cfe0a79aa Mon Sep 17 00:00:00 2001 From: Ruben Gonzalez Date: Mon, 1 Dec 2014 10:15:59 +0100 Subject: [PATCH 461/835] Typo when expression language is used to configure a service. Typo in XML and PHP code block when expression language is used to configure a service. ``` [Symfony\Component\ExpressionLanguage\SyntaxError] Unexpected character "@" around position 0. ``` --- book/service_container.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/service_container.rst b/book/service_container.rst index 1c939969b6f..a588f1c4156 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -720,7 +720,7 @@ via a ``container`` variable. Here's another example: - @=container.hasParameter('some_param') ? parameter('some_param') : 'default_value' + container.hasParameter('some_param') ? parameter('some_param') : 'default_value' @@ -733,7 +733,7 @@ via a ``container`` variable. Here's another example: $container->setDefinition('my_mailer', new Definition( 'Acme\HelloBundle\Mailer', array(new Expression( - "@=container.hasParameter('some_param') ? parameter('some_param') : 'default_value'" + "container.hasParameter('some_param') ? parameter('some_param') : 'default_value'" )) )); From d43daccd2c9387259ecc8a601b5c4934f5375cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20R=C3=ADos=20Francia?= Date: Tue, 2 Dec 2014 13:17:11 +0100 Subject: [PATCH 462/835] fixed little typo --- reference/twig_reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index d9ac5c386a8..8de5a7e4b69 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -403,7 +403,7 @@ transchoice .. code-block:: jinja - {{ message|transchoice(count, arguments, domai, locale) }} + {{ message|transchoice(count, arguments, domain, locale) }} ``message`` **type**: ``string`` From b69a5cd781156662f4c7a577e2a7a90bbd149de6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 3 Dec 2014 15:24:09 +0100 Subject: [PATCH 463/835] Updated installation instructions to use the new Symfony Installer --- best_practices/creating-the-project.rst | 118 +++++++----------------- 1 file changed, 31 insertions(+), 87 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index 16402420df4..ec0c80772a6 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -1,53 +1,42 @@ Creating the Project ==================== -Installing Symfony ------------------- +Symfony Installer +----------------- -There is only one recommended way to install Symfony: +In the past, Symfony projects were created with `Composer`_, the dependency manager +for PHP applications. However, the current recommendation is to use the **Symfony +Installer**, which has to be installed before creating your first project. -.. best-practice:: - - Always use `Composer`_ to install Symfony. - -Composer is the dependency manager used by modern PHP applications. Adding or -removing requirements for your project and updating the third-party libraries -used by your code is a breeze thanks to Composer. - -Dependency Management with Composer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Linux and Mac OS X systems +~~~~~~~~~~~~~~~~~~~~~~~~~~ -Before installing Symfony, you need to make sure that you have Composer installed -globally. Open your terminal (also called *command console*) and run the following -command: +Open your command console and execute the following: .. code-block:: bash - $ composer --version - Composer version 1e27ff5e22df81e3cd0cd36e5fdd4a3c5a031f4a 2014-08-11 15:46:48 + $ curl -LsS http://symfony.com/installer > symfony.phar + $ sudo mv symfony.phar /usr/local/bin/symfony + $ chmod a+x /usr/local/bin/symfony -You'll probably see a different version identifier. Never mind because Composer -is updated on a continuous basis and its specific version doesn't matter. +Now you can execute the Symfony Installer as a global system command called +``symfony``. -Installing Composer Globally -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Windows systems +~~~~~~~~~~~~~~~ -In case you don't have Composer installed globally, execute the following two -commands if you use Linux or Mac OS X (the second command will ask for your -user password): +Open your command console and execute the following: .. code-block:: bash - $ curl -sS https://getcomposer.org/installer | php - $ sudo mv composer.phar /usr/local/bin/composer + c:\> php -r "readfile('http://symfony.com/installer');" > symfony.phar -.. note:: +Then, move the downloaded ``symfony.phar`` file to your projects directory and +execute it as follows: - Depending on your Linux distribution, you may need to execute ``su`` command - instead of ``sudo``. +.. code-block:: bash -If you use a Windows system, download the executable installer from the -`Composer download page`_ and follow the steps to install it. + c:\> php symfony.phar Creating the Blog Application ----------------------------- @@ -58,64 +47,19 @@ to create files and execute the following commands: .. code-block:: bash + # Linux, Mac OS X $ cd projects/ - $ composer create-project symfony/framework-standard-edition blog/ - -This command will create a new directory called ``blog`` that will contain -a fresh new project based on the most recent stable Symfony version available. - -Checking the Symfony Installation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once the installation is finished, enter the ``blog/`` directory and check that -Symfony is correctly installed by executing the following command: - -.. code-block:: bash - - $ cd blog/ - $ php app/console --version - - Symfony version 2.6.* - app/dev/debug - -If you see the installed Symfony version, everything worked as expected. If not, -you can execute the following *script* to check what does prevent your system -from correctly executing Symfony applications: - -.. code-block:: bash - - $ php app/check.php - -Depending on your system, you can see up to two different lists when executing the -`check.php` script. The first one shows the mandatory requirements which your -system must meet to execute Symfony applications. The second list shows the -optional requirements suggested for an optimal execution of Symfony applications: - -.. code-block:: bash - - Symfony2 Requirements Checker - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - > PHP is using the following php.ini file: - /usr/local/zend/etc/php.ini - - > Checking Symfony requirements: - .....E.........................W..... - - [ERROR] - Your system is not ready to run Symfony2 projects - - Fix the following mandatory requirements - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - * date.timezone setting must be set - > Set the "date.timezone" setting in php.ini* (like Europe/Paris). - - Optional recommendations to improve your setup - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + $ symfony new blog - * short_open_tag should be disabled in php.ini - > Set short_open_tag to off in php.ini*. + # Windows + c:\> cd projects/ + c:\projects\> php symfony.phar new blog +This command creates a new directory called ``blog`` that contains a fresh new +project based on the most recent stable Symfony version available. In addition, +the installer checks if your system meets the technical requirements to execute +Symfony applications. If not, you'll see the list of changes needed to meet those +requirements. .. tip:: From 5f987fff7271316f30cb3f4994f6b818ecd0a8e2 Mon Sep 17 00:00:00 2001 From: David Zuelke Date: Thu, 4 Dec 2014 21:11:36 +0100 Subject: [PATCH 464/835] instructions for setting SYMFONY_ENV on Heroku for smooth deploys --- cookbook/deployment/heroku.rst | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/cookbook/deployment/heroku.rst b/cookbook/deployment/heroku.rst index 87baeb6c8d6..7d41ac09f6b 100644 --- a/cookbook/deployment/heroku.rst +++ b/cookbook/deployment/heroku.rst @@ -71,8 +71,9 @@ Deploying your Application on Heroku To deploy your application to Heroku, you must first create a ``Procfile``, which tells Heroku what command to use to launch the web server with the -correct settings. After you've done that, you can simply ``git push`` and -you're done! +correct document root. After that, you will ensure that your Symfony application +runs the ``prod`` environment, and then you'll be ready to ``git push`` to +Heroku for your first deploy! Creating a Procfile ~~~~~~~~~~~~~~~~~~~ @@ -110,6 +111,27 @@ create the ``Procfile`` file and to add it to the repository: [master 35075db] Procfile for Apache and PHP 1 file changed, 1 insertion(+) +Setting the ``prod`` environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +During a deploy, Heroku runs ``composer install --no-dev`` to install all of the +dependencies your application requires. However, typical `post-install-commands`_ +in ``composer.json``, e.g. to install assets or clear (or pre-warm) caches, run +using Symfony's ``dev`` environment by default. + +This is clearly not what you want - the app runs in "production" (even if you +use it just for an experiment, or as a staging environment), and so any build +steps should use the same ``prod`` environment as well. + +Thankfully, the solution to this problem is very simple: Symfony will pick up an +environment variable named ``SYMFONY_ENV`` and use that environment if nothing +else is explicitly set. As Heroku exposes all `config vars`_ as environment +variables, we can issue a single command to prepare our app for a deployment: + +.. code-block:: bash + + $ heroku config:set SYMFONY_ENV=prod + Pushing to Heroku ~~~~~~~~~~~~~~~~~ @@ -193,3 +215,5 @@ You should be seeing your Symfony application in your browser. .. _`ephemeral file system`: https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem .. _`Logplex`: https://devcenter.heroku.com/articles/logplex .. _`verified that the RSA key fingerprint is correct`: https://devcenter.heroku.com/articles/git-repository-ssh-fingerprints +.. _`post-install-commands`: https://getcomposer.org/doc/articles/scripts.md +.. _`config vars`: https://devcenter.heroku.com/articles/config-vars \ No newline at end of file From dd9de4efa1757793d38a80cc5f8d4d18cdbe974d Mon Sep 17 00:00:00 2001 From: xelaris Date: Sat, 29 Nov 2014 10:51:05 +0100 Subject: [PATCH 465/835] Add command to make symfony.phar executable. --- quick_tour/the_big_picture.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index bf10f772f1a..dab80a960bb 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -32,6 +32,7 @@ On **Linux** and **Mac OS X** systems, execute the following console commands: .. code-block:: bash $ curl -LsS http://symfony.com/installer > symfony.phar + $ chmod +x symfony.phar $ sudo mv symfony.phar /usr/local/bin/symfony .. note:: @@ -42,6 +43,7 @@ On **Linux** and **Mac OS X** systems, execute the following console commands: .. code-block:: bash $ php -r "readfile('http://symfony.com/installer');" > symfony.phar + $ chmod +x symfony.phar $ sudo mv symfony.phar /usr/local/bin/symfony After installing the Symfony installer, you'll have to open a new console window From 4257a2749cd59bce4315e7028a9e885101da77fd Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 4 Dec 2014 16:03:06 -0500 Subject: [PATCH 466/835] Updating the installer docs to exactly match that repository's README - see #4533 --- quick_tour/the_big_picture.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index dab80a960bb..361bfb1b84a 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -32,8 +32,8 @@ On **Linux** and **Mac OS X** systems, execute the following console commands: .. code-block:: bash $ curl -LsS http://symfony.com/installer > symfony.phar - $ chmod +x symfony.phar $ sudo mv symfony.phar /usr/local/bin/symfony + $ chmod a+x /usr/local/bin/symfony .. note:: @@ -43,8 +43,8 @@ On **Linux** and **Mac OS X** systems, execute the following console commands: .. code-block:: bash $ php -r "readfile('http://symfony.com/installer');" > symfony.phar - $ chmod +x symfony.phar $ sudo mv symfony.phar /usr/local/bin/symfony + $ chmod a+x /usr/local/bin/symfony After installing the Symfony installer, you'll have to open a new console window to be able to execute the new ``symfony`` command: From 8f25a15c6b4e845c825d50cb4d1c6fb054d5fe7a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 5 Dec 2014 17:38:45 +0100 Subject: [PATCH 467/835] fix feature freeze dates --- contributing/community/releases.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contributing/community/releases.rst b/contributing/community/releases.rst index 9ec7f1617f2..0f2dc92121f 100644 --- a/contributing/community/releases.rst +++ b/contributing/community/releases.rst @@ -94,13 +94,13 @@ Version Feature Freeze Release End of Maintenance End of Life 2.2 01/2013 03/2013 11/2013 (8 months) 05/2014 **2.3** 03/2013 05/2013 05/2016 (36 months) 05/2017 2.4 09/2013 11/2013 09/2014 (10 months [1]_) 01/2015 -2.5 02/2014 05/2014 01/2015 (8 months) 07/2015 +2.5 03/2014 05/2014 01/2015 (8 months) 07/2015 2.6 09/2014 11/2014 07/2015 (8 months) 01/2016 -**2.7** 02/2015 05/2015 05/2018 (36 months [2]_) 05/2019 +**2.7** 03/2015 05/2015 05/2018 (36 months [2]_) 05/2019 3.0 09/2015 11/2015 07/2016 (8 months) 01/2017 -3.1 02/2016 05/2016 01/2017 (8 months) 07/2017 +3.1 03/2016 05/2016 01/2017 (8 months) 07/2017 3.2 09/2016 11/2016 07/2017 (8 months) 01/2018 -**3.3** 02/2017 05/2017 05/2020 (36 months) 05/2021 +**3.3** 03/2017 05/2017 05/2020 (36 months) 05/2021 ... ... ... ... ... ======= ============== ======= ======================== =========== From 579fdd680d5e41218040565757cbbb380f4ec914 Mon Sep 17 00:00:00 2001 From: David Zuelke Date: Fri, 5 Dec 2014 18:14:51 +0100 Subject: [PATCH 468/835] capitalize section title as per feedback --- cookbook/deployment/heroku.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/deployment/heroku.rst b/cookbook/deployment/heroku.rst index 7d41ac09f6b..8d63df588a9 100644 --- a/cookbook/deployment/heroku.rst +++ b/cookbook/deployment/heroku.rst @@ -111,7 +111,7 @@ create the ``Procfile`` file and to add it to the repository: [master 35075db] Procfile for Apache and PHP 1 file changed, 1 insertion(+) -Setting the ``prod`` environment +Setting the ``prod`` Environment ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ During a deploy, Heroku runs ``composer install --no-dev`` to install all of the From 576bbfbd3b489a67cc2155a68dd4da3022172028 Mon Sep 17 00:00:00 2001 From: David Zuelke Date: Fri, 5 Dec 2014 18:15:06 +0100 Subject: [PATCH 469/835] remove first person as per feedback --- cookbook/deployment/heroku.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/deployment/heroku.rst b/cookbook/deployment/heroku.rst index 8d63df588a9..5525e7ffce5 100644 --- a/cookbook/deployment/heroku.rst +++ b/cookbook/deployment/heroku.rst @@ -126,7 +126,7 @@ steps should use the same ``prod`` environment as well. Thankfully, the solution to this problem is very simple: Symfony will pick up an environment variable named ``SYMFONY_ENV`` and use that environment if nothing else is explicitly set. As Heroku exposes all `config vars`_ as environment -variables, we can issue a single command to prepare our app for a deployment: +variables, you can issue a single command to prepare your app for a deployment: .. code-block:: bash From a68d3e5e965f1a818535c06178530bbbbcbaaf14 Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Wed, 12 Nov 2014 21:04:50 -0800 Subject: [PATCH 470/835] [RFC] Clarification on formatting for bangs (!) It seems to me that > Add a single space around operators would mean using the practice on the bang operator as well; as discussed in [this thread](https://github.com/php-fig/fig-standards/issues/102), there is somewhat of a conflict--so this may not be the way to go. If the PSR could be amended, then this change would make sense. --- contributing/code/standards.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index a2720e991df..448d818ad36 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -79,6 +79,14 @@ example containing most features described below: throw new \RuntimeException(sprintf('Unrecognized dummy option "%s"', $dummy)); } + + private function reverseBoolean($value = null) + { + if ( ! isset($value)) { + return; + } + return ! $value; + } } Structure @@ -86,7 +94,7 @@ Structure * Add a single space after each comma delimiter; -* Add a single space around operators (``==``, ``&&``, ...); +* Add a single space around operators (``==``, ``&&``, ``!``, ...); * Add a comma after each array item in a multi-line array, even after the last one; From 95a1cc51ae160c152d31875baff01dc3ce817294 Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Thu, 13 Nov 2014 09:13:51 -0800 Subject: [PATCH 471/835] Reverses statement about bang spacing and corresponding examples --- contributing/code/standards.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 448d818ad36..206eb076f3e 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -82,10 +82,10 @@ example containing most features described below: private function reverseBoolean($value = null) { - if ( ! isset($value)) { + if (!isset($value)) { return; } - return ! $value; + return !$value; } } @@ -94,7 +94,8 @@ Structure * Add a single space after each comma delimiter; -* Add a single space around operators (``==``, ``&&``, ``!``, ...); +* Add a single space around operators (``==``, ``&&``, ...), with the excption + of the not (!) operator; * Add a comma after each array item in a multi-line array, even after the last one; From 3212b6c857c3193a8c2d7821de398770eecf7fd6 Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Thu, 13 Nov 2014 09:15:50 -0800 Subject: [PATCH 472/835] Adds bullet addressing bang placement --- contributing/code/standards.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 206eb076f3e..9e2c58396ce 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -97,6 +97,8 @@ Structure * Add a single space around operators (``==``, ``&&``, ...), with the excption of the not (!) operator; ++ Place the not operator adjacent to the variable that it affects + * Add a comma after each array item in a multi-line array, even after the last one; From 30d19abc8b9857cab59332e52694b964f8561ef8 Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Thu, 13 Nov 2014 09:16:52 -0800 Subject: [PATCH 473/835] Fixes wrong bullet character --- contributing/code/standards.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 9e2c58396ce..110892d6dda 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -97,7 +97,7 @@ Structure * Add a single space around operators (``==``, ``&&``, ...), with the excption of the not (!) operator; -+ Place the not operator adjacent to the variable that it affects +* Place the not operator adjacent to the variable that it affects * Add a comma after each array item in a multi-line array, even after the last one; From 3799bc0a0abb5d8a76993588469ada70a057e824 Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Thu, 13 Nov 2014 11:46:46 -0800 Subject: [PATCH 474/835] Fixes spelling and formatting errors --- contributing/code/standards.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 110892d6dda..5964832034e 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -94,10 +94,10 @@ Structure * Add a single space after each comma delimiter; -* Add a single space around operators (``==``, ``&&``, ...), with the excption - of the not (!) operator; +* Add a single space around operators (``==``, ``&&``, ...), with the + exception of the not (``!``) operator; -* Place the not operator adjacent to the variable that it affects +* Place the not operator adjacent to the variable that it affects; * Add a comma after each array item in a multi-line array, even after the last one; From 835d5ebf2ff1fc54d8e7d22a890a599837b1cc4c Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Thu, 13 Nov 2014 13:51:16 -0800 Subject: [PATCH 475/835] Adds missing space before return statement --- contributing/code/standards.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 5964832034e..23998ea0965 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -85,6 +85,7 @@ example containing most features described below: if (!isset($value)) { return; } + return !$value; } } From 837e895cb0e6dc8aa41fc12d18b88f131f691649 Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Thu, 13 Nov 2014 13:51:48 -0800 Subject: [PATCH 476/835] Changes operator spacing language --- contributing/code/standards.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 23998ea0965..f09a2aff1d3 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -95,10 +95,10 @@ Structure * Add a single space after each comma delimiter; -* Add a single space around operators (``==``, ``&&``, ...), with the - exception of the not (``!``) operator; +* Add a single space around binary operators (``==``, ``&&``, ...), with + the exception of the concatenation (``,``) operator; -* Place the not operator adjacent to the variable that it affects; +* Place unary operators (``!``, ``--``, ...) adjacent to the affected variable; * Add a comma after each array item in a multi-line array, even after the last one; From 33e76a5b9b2136199b742084252c2e32494931e5 Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Fri, 14 Nov 2014 10:01:38 -0800 Subject: [PATCH 477/835] Incorporates @WouterJ 's suggestion for more realistic usage of bang --- contributing/code/standards.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index f09a2aff1d3..23fedc1f6f1 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -80,9 +80,9 @@ example containing most features described below: throw new \RuntimeException(sprintf('Unrecognized dummy option "%s"', $dummy)); } - private function reverseBoolean($value = null) + private function reverseBoolean($value = null, $theSwitch = false) { - if (!isset($value)) { + if (!$theSwitch) { return; } From ee888c65c6247b19ba211e2de7a9ca990c2906df Mon Sep 17 00:00:00 2001 From: "Bryan J. Agee" Date: Mon, 24 Nov 2014 10:59:04 -0800 Subject: [PATCH 478/835] Fixes concat operator typo --- contributing/code/standards.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 23fedc1f6f1..1fb4297ddce 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -96,7 +96,7 @@ Structure * Add a single space after each comma delimiter; * Add a single space around binary operators (``==``, ``&&``, ...), with - the exception of the concatenation (``,``) operator; + the exception of the concatenation (``.``) operator; * Place unary operators (``!``, ``--``, ...) adjacent to the affected variable; From 1fb20e57b0c91686fed925c69f66c327a89bd283 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 24 Nov 2014 23:55:23 +0100 Subject: [PATCH 479/835] remove service class parameters --- best_practices/business-logic.rst | 5 +- book/service_container.rst | 89 +++++-------------- components/dependency_injection/factories.rst | 78 ++++------------ .../dependency_injection/parameters.rst | 9 +- .../dependency_injection/parentservices.rst | 50 ++++------- components/dependency_injection/tags.rst | 15 +--- cookbook/console/logging.rst | 12 +-- cookbook/controller/service.rst | 44 ++------- cookbook/profiler/matchers.rst | 20 +---- cookbook/security/custom_provider.rst | 18 ++-- cookbook/security/securing_services.rst | 19 ++-- cookbook/service_container/scopes.rst | 18 +--- 12 files changed, 90 insertions(+), 287 deletions(-) diff --git a/best_practices/business-logic.rst b/best_practices/business-logic.rst index 7365caefd22..03ade3fffc3 100644 --- a/best_practices/business-logic.rst +++ b/best_practices/business-logic.rst @@ -140,12 +140,9 @@ the class namespace as a parameter: # app/config/services.yml # service definition with class namespace as parameter - parameters: - slugger.class: AppBundle\Utils\Slugger - services: app.slugger: - class: "%slugger.class%" + class: AppBundle\Utils\Slugger This practice is cumbersome and completely unnecessary for your own services: diff --git a/book/service_container.rst b/book/service_container.rst index fc08fedba7c..ea1996179b2 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -203,12 +203,11 @@ straightforward. Parameters make defining services more organized and flexible: # app/config/config.yml parameters: - my_mailer.class: Acme\HelloBundle\Mailer my_mailer.transport: sendmail services: my_mailer: - class: "%my_mailer.class%" + class: Acme\HelloBundle\Mailer arguments: ["%my_mailer.transport%"] .. code-block:: xml @@ -220,12 +219,11 @@ straightforward. Parameters make defining services more organized and flexible: xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Acme\HelloBundle\Mailer sendmail - + %my_mailer.transport% @@ -236,19 +234,18 @@ straightforward. Parameters make defining services more organized and flexible: // app/config/config.php use Symfony\Component\DependencyInjection\Definition; - $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer'); $container->setParameter('my_mailer.transport', 'sendmail'); $container->setDefinition('my_mailer', new Definition( - '%my_mailer.class%', + 'Acme\HelloBundle\Mailer', array('%my_mailer.transport%') )); The end result is exactly the same as before - the difference is only in -*how* you defined the service. By surrounding the ``my_mailer.class`` and -``my_mailer.transport`` strings in percent (``%``) signs, the container knows -to look for parameters with those names. When the container is built, it -looks up the value of each parameter and uses it in the service definition. +*how* you defined the service. By surrounding the ``my_mailer.transport`` +string in percent (``%``) signs, the container knows to look for a parameter +with that name. When the container is built, it looks up the value of each +parameter and uses it in the service definition. .. note:: @@ -348,12 +345,11 @@ directories don't exist, create them. # src/Acme/HelloBundle/Resources/config/services.yml parameters: - my_mailer.class: Acme\HelloBundle\Mailer my_mailer.transport: sendmail services: my_mailer: - class: "%my_mailer.class%" + class: Acme\HelloBundle\Mailer arguments: ["%my_mailer.transport%"] .. code-block:: xml @@ -365,12 +361,11 @@ directories don't exist, create them. xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - Acme\HelloBundle\Mailer sendmail - + %my_mailer.transport% @@ -381,11 +376,10 @@ directories don't exist, create them. // src/Acme/HelloBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; - $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer'); $container->setParameter('my_mailer.transport', 'sendmail'); $container->setDefinition('my_mailer', new Definition( - '%my_mailer.class%', + 'Acme\HelloBundle\Mailer', array('%my_mailer.transport%') )); @@ -608,15 +602,11 @@ the service container gives you a much more appealing option: .. code-block:: yaml # src/Acme/HelloBundle/Resources/config/services.yml - parameters: - # ... - newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager - services: my_mailer: # ... newsletter_manager: - class: "%newsletter_manager.class%" + class: Acme\HelloBundle\Newsletter\NewsletterManager arguments: ["@my_mailer"] .. code-block:: xml @@ -627,16 +617,11 @@ the service container gives you a much more appealing option: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Acme\HelloBundle\Newsletter\NewsletterManager - - - + @@ -648,15 +633,9 @@ the service container gives you a much more appealing option: use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - // ... - $container->setParameter( - 'newsletter_manager.class', - 'Acme\HelloBundle\Newsletter\NewsletterManager' - ); - $container->setDefinition('my_mailer', ...); $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%', + 'Acme\HelloBundle\Newsletter\NewsletterManager', array(new Reference('my_mailer')) )); @@ -704,15 +683,11 @@ Injecting the dependency by the setter method just needs a change of syntax: .. code-block:: yaml # src/Acme/HelloBundle/Resources/config/services.yml - parameters: - # ... - newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager - services: my_mailer: # ... newsletter_manager: - class: "%newsletter_manager.class%" + class: Acme\HelloBundle\Newsletter\NewsletterManager calls: - [setMailer, ["@my_mailer"]] @@ -724,16 +699,11 @@ Injecting the dependency by the setter method just needs a change of syntax: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - Acme\HelloBundle\Newsletter\NewsletterManager - - - + @@ -747,15 +717,9 @@ Injecting the dependency by the setter method just needs a change of syntax: use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - // ... - $container->setParameter( - 'newsletter_manager.class', - 'Acme\HelloBundle\Newsletter\NewsletterManager' - ); - $container->setDefinition('my_mailer', ...); $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%' + 'Acme\HelloBundle\Newsletter\NewsletterManager' ))->addMethodCall('setMailer', array( new Reference('my_mailer'), )); @@ -781,12 +745,9 @@ it exists and do nothing if it doesn't: .. code-block:: yaml # src/Acme/HelloBundle/Resources/config/services.yml - parameters: - # ... - services: newsletter_manager: - class: "%newsletter_manager.class%" + class: Acme\HelloBundle\Newsletter\NewsletterManager arguments: ["@?my_mailer"] .. code-block:: xml @@ -801,7 +762,7 @@ it exists and do nothing if it doesn't: - + @@ -814,15 +775,9 @@ it exists and do nothing if it doesn't: use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerInterface; - // ... - $container->setParameter( - 'newsletter_manager.class', - 'Acme\HelloBundle\Newsletter\NewsletterManager' - ); - $container->setDefinition('my_mailer', ...); $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%', + 'Acme\HelloBundle\Newsletter\NewsletterManager', array( new Reference( 'my_mailer', @@ -899,7 +854,7 @@ Configuring the service container is easy: # src/Acme/HelloBundle/Resources/config/services.yml services: newsletter_manager: - class: "%newsletter_manager.class%" + class: Acme\HelloBundle\Newsletter\NewsletterManager arguments: ["@mailer", "@templating"] .. code-block:: xml @@ -910,7 +865,7 @@ Configuring the service container is easy: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -920,7 +875,7 @@ Configuring the service container is easy: // src/Acme/HelloBundle/Resources/config/services.php $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%', + 'Acme\HelloBundle\Newsletter\NewsletterManager', array( new Reference('mailer'), new Reference('templating'), diff --git a/components/dependency_injection/factories.rst b/components/dependency_injection/factories.rst index 9f74efa50d5..126db187d6f 100644 --- a/components/dependency_injection/factories.rst +++ b/components/dependency_injection/factories.rst @@ -35,14 +35,10 @@ class: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: newsletter_manager: - class: "%newsletter_manager.class%" - factory_class: "%newsletter_factory.class%" + class: NewsletterManager + factory_class: NewsletterFactory factory_method: get .. code-block:: xml @@ -52,17 +48,11 @@ class: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - @@ -71,12 +61,8 @@ class: use Symfony\Component\DependencyInjection\Definition; - // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); - - $definition = new Definition('%newsletter_manager.class%'); - $definition->setFactoryClass('%newsletter_factory.class%'); + $definition = new Definition('NewsletterManager'); + $definition->setFactoryClass('NewsletterFactory'); $definition->setFactoryMethod('get'); $container->setDefinition('newsletter_manager', $definition); @@ -90,15 +76,11 @@ In this case, the method (e.g. get) should be changed to be non-static: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: newsletter_factory: - class: "%newsletter_factory.class%" + class: NewsletterFactory newsletter_manager: - class: "%newsletter_manager.class%" + class: NewsletterManager factory_service: newsletter_factory factory_method: get @@ -109,18 +91,12 @@ In this case, the method (e.g. get) should be changed to be non-static: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - - + @@ -130,15 +106,11 @@ In this case, the method (e.g. get) should be changed to be non-static: use Symfony\Component\DependencyInjection\Definition; - // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); - $container->setDefinition('newsletter_factory', new Definition( - '%newsletter_factory.class%' + 'NewsletterFactory' )); $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%' + 'NewsletterManager' ))->setFactoryService( 'newsletter_factory' )->setFactoryMethod( @@ -162,15 +134,11 @@ in the previous example takes the ``templating`` service as an argument: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - newsletter_factory.class: NewsletterFactory services: newsletter_factory: - class: "%newsletter_factory.class%" + class: NewsletterFactory newsletter_manager: - class: "%newsletter_manager.class%" + class: NewsletterManager factory_service: newsletter_factory factory_method: get arguments: @@ -183,18 +151,12 @@ in the previous example takes the ``templating`` service as an argument: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - NewsletterFactory - - - + @@ -207,15 +169,11 @@ in the previous example takes the ``templating`` service as an argument: use Symfony\Component\DependencyInjection\Definition; - // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('newsletter_factory.class', 'NewsletterFactory'); - $container->setDefinition('newsletter_factory', new Definition( - '%newsletter_factory.class%' + 'NewsletterFactory' )); $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%', + 'NewsletterManager', array(new Reference('templating')) ))->setFactoryService( 'newsletter_factory' diff --git a/components/dependency_injection/parameters.rst b/components/dependency_injection/parameters.rst index 9b47e5141b9..8e76c4dd615 100644 --- a/components/dependency_injection/parameters.rst +++ b/components/dependency_injection/parameters.rst @@ -149,11 +149,10 @@ making the class of a service a parameter: parameters: mailer.transport: sendmail - mailer.class: Mailer services: mailer: - class: "%mailer.class%" + class: Mailer arguments: ["%mailer.transport%"] .. code-block:: xml @@ -165,11 +164,10 @@ making the class of a service a parameter: sendmail - Mailer - + %mailer.transport% @@ -180,10 +178,9 @@ making the class of a service a parameter: use Symfony\Component\DependencyInjection\Reference; $container->setParameter('mailer.transport', 'sendmail'); - $container->setParameter('mailer.class', 'Mailer'); $container - ->register('mailer', '%mailer.class%') + ->register('mailer', 'Mailer') ->addArgument('%mailer.transport%'); .. note:: diff --git a/components/dependency_injection/parentservices.rst b/components/dependency_injection/parentservices.rst index 3158738fa15..b7729ab1f50 100644 --- a/components/dependency_injection/parentservices.rst +++ b/components/dependency_injection/parentservices.rst @@ -52,11 +52,6 @@ The service config for these classes would look something like this: .. code-block:: yaml - parameters: - # ... - newsletter_manager.class: NewsletterManager - greeting_card_manager.class: GreetingCardManager - services: my_mailer: # ... @@ -65,13 +60,13 @@ The service config for these classes would look something like this: # ... newsletter_manager: - class: "%newsletter_manager.class%" + class: NewsletterManager calls: - [setMailer, ["@my_mailer"]] - [setEmailFormatter, ["@my_email_formatter"]] greeting_card_manager: - class: "%greeting_card_manager.class%" + class: "GreetingCardManager" calls: - [setMailer, ["@my_mailer"]] - [setEmailFormatter, ["@my_email_formatter"]] @@ -83,12 +78,6 @@ The service config for these classes would look something like this: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - NewsletterManager - GreetingCardManager - - @@ -98,7 +87,7 @@ The service config for these classes would look something like this: - + @@ -107,7 +96,7 @@ The service config for these classes would look something like this: - + @@ -124,14 +113,11 @@ The service config for these classes would look something like this: use Symfony\Component\DependencyInjection\Reference; // ... - $container->setParameter('newsletter_manager.class', 'NewsletterManager'); - $container->setParameter('greeting_card_manager.class', 'GreetingCardManager'); - $container->register('my_mailer', ...); $container->register('my_email_formatter', ...); $container - ->register('newsletter_manager', '%newsletter_manager.class%') + ->register('newsletter_manager', 'NewsletterManager') ->addMethodCall('setMailer', array( new Reference('my_mailer'), )) @@ -141,7 +127,7 @@ The service config for these classes would look something like this: ; $container - ->register('greeting_card_manager', '%greeting_card_manager.class%') + ->register('greeting_card_manager', 'GreetingCardManager') ->addMethodCall('setMailer', array( new Reference('my_mailer'), )) @@ -208,11 +194,11 @@ a parent for a service. - [setEmailFormatter, ["@my_email_formatter"]] newsletter_manager: - class: "%newsletter_manager.class%" + class: "NewsletterManager" parent: mail_manager greeting_card_manager: - class: "%greeting_card_manager.class%" + class: "GreetingCardManager" parent: mail_manager .. code-block:: xml @@ -237,12 +223,12 @@ a parent for a service. @@ -267,11 +253,11 @@ a parent for a service. $container->setDefinition('mail_manager', $mailManager); $newsletterManager = new DefinitionDecorator('mail_manager'); - $newsletterManager->setClass('%newsletter_manager.class%'); + $newsletterManager->setClass('NewsletterManager'); $container->setDefinition('newsletter_manager', $newsletterManager); $greetingCardManager = new DefinitionDecorator('mail_manager'); - $greetingCardManager->setClass('%greeting_card_manager.class%'); + $greetingCardManager->setClass('GreetingCardManager'); $container->setDefinition('greeting_card_manager', $greetingCardManager); In this context, having a ``parent`` service implies that the arguments and @@ -336,13 +322,13 @@ to the ``NewsletterManager`` class, the config would look like this: - [setEmailFormatter, ["@my_email_formatter"]] newsletter_manager: - class: "%newsletter_manager.class%" + class: "NewsletterManager" parent: mail_manager calls: - [setMailer, ["@my_alternative_mailer"]] greeting_card_manager: - class: "%greeting_card_manager.class%" + class: "GreetingCardManager" parent: mail_manager .. code-block:: xml @@ -371,7 +357,7 @@ to the ``NewsletterManager`` class, the config would look like this: @@ -381,7 +367,7 @@ to the ``NewsletterManager`` class, the config would look like this: @@ -408,7 +394,7 @@ to the ``NewsletterManager`` class, the config would look like this: $container->setDefinition('mail_manager', $mailManager); $newsletterManager = new DefinitionDecorator('mail_manager'); - $newsletterManager->setClass('%newsletter_manager.class%'); + $newsletterManager->setClass('NewsletterManager'); ->addMethodCall('setMailer', array( new Reference('my_alternative_mailer'), )) @@ -416,7 +402,7 @@ to the ``NewsletterManager`` class, the config would look like this: $container->setDefinition('newsletter_manager', $newsletterManager); $greetingCardManager = new DefinitionDecorator('mail_manager'); - $greetingCardManager->setClass('%greeting_card_manager.class%'); + $greetingCardManager->setClass('GreetingCardManager'); $container->setDefinition('greeting_card_manager', $greetingCardManager); The ``GreetingCardManager`` will receive the same dependencies as before, diff --git a/components/dependency_injection/tags.rst b/components/dependency_injection/tags.rst index 1c4e08590e4..b8442c3b933 100644 --- a/components/dependency_injection/tags.rst +++ b/components/dependency_injection/tags.rst @@ -39,12 +39,9 @@ Then, define the chain as a service: .. code-block:: yaml - parameters: - acme_mailer.transport_chain.class: TransportChain - services: acme_mailer.transport_chain: - class: "%acme_mailer.transport_chain.class%" + class: TransportChain .. code-block:: xml @@ -53,12 +50,8 @@ Then, define the chain as a service: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - TransportChain - - - + @@ -66,9 +59,7 @@ Then, define the chain as a service: use Symfony\Component\DependencyInjection\Definition; - $container->setParameter('acme_mailer.transport_chain.class', 'TransportChain'); - - $container->setDefinition('acme_mailer.transport_chain', new Definition('%acme_mailer.transport_chain.class%')); + $container->setDefinition('acme_mailer.transport_chain', new Definition('TransportChain')); Define Services with a custom Tag --------------------------------- diff --git a/cookbook/console/logging.rst b/cookbook/console/logging.rst index 61f54d3583c..031121051f1 100644 --- a/cookbook/console/logging.rst +++ b/cookbook/console/logging.rst @@ -98,12 +98,8 @@ First configure a listener for console exception events in the service container xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - Acme\DemoBundle\EventListener\ConsoleExceptionListener - - - + @@ -116,12 +112,8 @@ First configure a listener for console exception events in the service container use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - $container->setParameter( - 'console_exception_listener.class', - 'Acme\DemoBundle\EventListener\ConsoleExceptionListener' - ); $definitionConsoleExceptionListener = new Definition( - '%console_exception_listener.class%', + 'Acme\DemoBundle\EventListener\ConsoleExceptionListener', array(new Reference('logger')) ); $definitionConsoleExceptionListener->addTag( diff --git a/cookbook/controller/service.rst b/cookbook/controller/service.rst index f0d23943579..bf2cadb2887 100644 --- a/cookbook/controller/service.rst +++ b/cookbook/controller/service.rst @@ -54,24 +54,15 @@ Then you can define it as a service as follows: .. code-block:: yaml # src/Acme/HelloBundle/Resources/config/services.yml - parameters: - # ... - acme.controller.hello.class: Acme\HelloBundle\Controller\HelloController - services: acme.hello.controller: - class: "%acme.controller.hello.class%" + class: Acme\HelloBundle\Controller\HelloController .. code-block:: xml - - - Acme\HelloBundle\Controller\HelloController - - - + .. code-block:: php @@ -79,14 +70,8 @@ Then you can define it as a service as follows: // src/Acme/HelloBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; - // ... - $container->setParameter( - 'acme.controller.hello.class', - 'Acme\HelloBundle\Controller\HelloController' - ); - $container->setDefinition('acme.hello.controller', new Definition( - '%acme.controller.hello.class%' + 'Acme\HelloBundle\Controller\HelloController' )); Referring to the Service @@ -208,27 +193,16 @@ argument: .. code-block:: yaml # src/Acme/HelloBundle/Resources/config/services.yml - parameters: - # ... - acme.controller.hello.class: Acme\HelloBundle\Controller\HelloController - services: acme.hello.controller: - class: "%acme.controller.hello.class%" + class: Acme\HelloBundle\Controller\HelloController arguments: ["@templating"] .. code-block:: xml - - - Acme\HelloBundle\Controller\HelloController - - - + @@ -239,14 +213,8 @@ argument: use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - // ... - $container->setParameter( - 'acme.controller.hello.class', - 'Acme\HelloBundle\Controller\HelloController' - ); - $container->setDefinition('acme.hello.controller', new Definition( - '%acme.controller.hello.class%', + 'Acme\HelloBundle\Controller\HelloController', array(new Reference('templating')) )); diff --git a/cookbook/profiler/matchers.rst b/cookbook/profiler/matchers.rst index b23af0d21f0..064e8fdd1b7 100644 --- a/cookbook/profiler/matchers.rst +++ b/cookbook/profiler/matchers.rst @@ -95,25 +95,16 @@ Then, you need to configure the service: .. code-block:: yaml - parameters: - acme_demo.profiler.matcher.super_admin.class: Acme\DemoBundle\Profiler\SuperAdminMatcher - services: acme_demo.profiler.matcher.super_admin: - class: "%acme_demo.profiler.matcher.super_admin.class%" + class: Acme\DemoBundle\Profiler\SuperAdminMatcher arguments: ["@security.context"] .. code-block:: xml - - Acme\DemoBundle\Profiler\SuperAdminMatcher - - + class="Acme\DemoBundle\Profiler\SuperAdminMatcher"> @@ -122,13 +113,8 @@ Then, you need to configure the service: use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - $container->setParameter( - 'acme_demo.profiler.matcher.super_admin.class', - 'Acme\DemoBundle\Profiler\SuperAdminMatcher' - ); - $container->setDefinition('acme_demo.profiler.matcher.super_admin', new Definition( - '%acme_demo.profiler.matcher.super_admin.class%', + 'Acme\DemoBundle\Profiler\SuperAdminMatcher', array(new Reference('security.context')) ); diff --git a/cookbook/security/custom_provider.rst b/cookbook/security/custom_provider.rst index 180a6feb119..5ba3ac7c986 100644 --- a/cookbook/security/custom_provider.rst +++ b/cookbook/security/custom_provider.rst @@ -176,22 +176,15 @@ Now you make the user provider available as a service: .. code-block:: yaml # src/Acme/WebserviceUserBundle/Resources/config/services.yml - parameters: - webservice_user_provider.class: Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider - services: webservice_user_provider: - class: "%webservice_user_provider.class%" + class: Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider .. code-block:: xml - - Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider - - - + .. code-block:: php @@ -199,9 +192,10 @@ Now you make the user provider available as a service: // src/Acme/WebserviceUserBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; - $container->setParameter('webservice_user_provider.class', 'Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider'); - - $container->setDefinition('webservice_user_provider', new Definition('%webservice_user_provider.class%'); + $container->setDefinition( + 'webservice_user_provider', + new Definition('Acme\WebserviceUserBundle\Security\User\WebserviceUserProvider') + ); .. tip:: diff --git a/cookbook/security/securing_services.rst b/cookbook/security/securing_services.rst index 641a43f04ec..3fc3ef16197 100644 --- a/cookbook/security/securing_services.rst +++ b/cookbook/security/securing_services.rst @@ -74,23 +74,16 @@ Then in your service configuration, you can inject the service: .. code-block:: yaml # src/Acme/HelloBundle/Resources/config/services.yml - parameters: - newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager - services: newsletter_manager: - class: "%newsletter_manager.class%" + class: Acme\HelloBundle\Newsletter\NewsletterManager arguments: ["@security.context"] .. code-block:: xml - - Acme\HelloBundle\Newsletter\NewsletterManager - - - + @@ -101,10 +94,8 @@ Then in your service configuration, you can inject the service: use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager'); - $container->setDefinition('newsletter_manager', new Definition( - '%newsletter_manager.class%', + 'Acme\HelloBundle\Newsletter\NewsletterManager', array(new Reference('security.context')) )); @@ -172,7 +163,7 @@ the :ref:`sidebar ` below): - + @@ -185,7 +176,7 @@ the :ref:`sidebar ` below): use Symfony\Component\DependencyInjection\Reference; $definition = new Definition( - '%newsletter_manager.class%', + 'Acme\HelloBundle\Newsletter\NewsletterManager', array(new Reference('security.context')) )); $definition->addTag('security.secure_service'); diff --git a/cookbook/service_container/scopes.rst b/cookbook/service_container/scopes.rst index 786de8a93e2..70ad093561e 100644 --- a/cookbook/service_container/scopes.rst +++ b/cookbook/service_container/scopes.rst @@ -301,26 +301,17 @@ The service config for this class would look something like this: .. code-block:: yaml # src/Acme/HelloBundle/Resources/config/services.yml - parameters: - # ... - my_mailer.class: Acme\HelloBundle\Mail\Mailer - services: my_mailer: - class: "%my_mailer.class%" + class: Acme\HelloBundle\Mail\Mailer arguments: ["@service_container"] # scope: container can be omitted as it is the default .. code-block:: xml - - - Acme\HelloBundle\Mail\Mailer - - - + @@ -331,11 +322,8 @@ The service config for this class would look something like this: use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - // ... - $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mail\Mailer'); - $container->setDefinition('my_mailer', new Definition( - '%my_mailer.class%', + 'Acme\HelloBundle\Mail\Mailer', array(new Reference('service_container')) )); From c0ef3e49a6e32c5d1aee0a22b32227a3b783f3fe Mon Sep 17 00:00:00 2001 From: marcel Date: Fri, 5 Dec 2014 10:06:23 +0100 Subject: [PATCH 480/835] Missing attribute 'original' I copy pasted the example messages.en.xliff provided. Running it on symfony 2.6 required me the attribute 'original'. I added ' original="file.ext" ' as attribute for the file tag and it worked. However I am not shure about the proper parameter for this attribute. --- best_practices/i18n.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/i18n.rst b/best_practices/i18n.rst index 1e7a86f3196..9da3fad3271 100644 --- a/best_practices/i18n.rst +++ b/best_practices/i18n.rst @@ -83,7 +83,7 @@ English in the application would be: - + title.post_list From 21d69eb07529093dfd37b8cefcf0787e4aff041b Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Sun, 7 Dec 2014 02:14:57 +0100 Subject: [PATCH 481/835] Replace form_enctype(form) with form_start(form). Since form_enctype(form) is deprecated since 2.3 form_start(form) should be used. --- best_practices/forms.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/best_practices/forms.rst b/best_practices/forms.rst index e8a7192fc82..60028850352 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -145,12 +145,12 @@ view layer: .. code-block:: html+jinja - + {{ form_start(form) }} {{ form_widget(form) }} - + {{ form_end(form) }} Rendering the Form ------------------ From e8e50faa629394269baa1ffc071fb406bd750b01 Mon Sep 17 00:00:00 2001 From: Abdellatif Ait boudad Date: Wed, 23 Jul 2014 20:34:48 +0000 Subject: [PATCH 482/835] added logging to translator. --- book/translation.rst | 9 ++++++ reference/configuration/framework.rst | 41 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/book/translation.rst b/book/translation.rst index 63a98e8309b..698bb7e203d 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -400,6 +400,15 @@ checks translation resources for several different locales: 3. If the translation still isn't found, Symfony uses the ``fallback`` configuration parameter, which defaults to ``en`` (see `Configuration`_). +.. versionadded:: 2.6 + The ability to log missing translations was introduced in Symfony 2.6. + +.. note:: + + When Symfony doesn't find a translation in the given locale, it will + add the missing translation to the log file. For details, + see :ref:`reference-framework-translator-logging`. + .. _book-translation-user-locale: Handling the User's Locale diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 9cfb5b6947b..700bc97a26d 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -48,6 +48,10 @@ Configuration * `profiler`_ * `collect`_ * :ref:`enabled ` +* `translator`_ + * :ref:`enabled ` + * `fallback`_ + * `logging`_ secret ~~~~~~ @@ -495,6 +499,42 @@ and activate the data collectors by hand:: $profiler->enable(); +translator +~~~~~~~~~~ + +.. _translator.enabled: + +enabled +....... + +**type**: ``boolean`` **default**: ``false`` + +Whether or not to enable the ``translator`` service in the service container. + +fallback +........ + +**default**: ``en`` + +This option is used when the translation key for the current locale wasn't found. + +For more details, see :doc:`/book/translation`. + +.. _reference-framework-translator-logging: + +logging +....... + +.. versionadded:: 2.6 + The ``logging`` option was introduced in Symfony 2.6. + +**default**: ``true`` when the debug mode is enabled, ``false`` otherwise. + +When ``true``, a log entry is made whenever the translator cannot find a translation +for a given key. The logs are made to the ``translation`` channel and at the ``debug`` +for level for keys where there is a translation in the fallback locale and the ``warning`` +level if there is no translation to use at all. + Full default Configuration -------------------------- @@ -612,6 +652,7 @@ Full default Configuration translator: enabled: false fallback: en + logging: "%kernel.debug%" # validation configuration validation: From 2af0b3f24047875f1f3809da60f62c859e5557cd Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 09:40:57 -0500 Subject: [PATCH 483/835] [#4050] Backporting some additions to 2.3 --- reference/configuration/framework.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 0238fef5020..bdbaf06aa96 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -45,6 +45,9 @@ Configuration * `profiler`_ * `collect`_ * :ref:`enabled ` +* `translator`_ + * :ref:`enabled ` + * `fallback`_ secret ~~~~~~ @@ -493,6 +496,27 @@ and activate the data collectors by hand:: $profiler->enable(); +translator +~~~~~~~~~~ + +.. _translator.enabled: + +enabled +....... + +**type**: ``boolean`` **default**: ``false`` + +Whether or not to enable the ``translator`` service in the service container. + +fallback +........ + +**default**: ``en`` + +This option is used when the translation key for the current locale wasn't found. + +For more details, see :doc:`/book/translation`. + Full default Configuration -------------------------- From 5ef52ee9b93e8b2c908ba0a03688c0ecbe2c7521 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 09:42:59 -0500 Subject: [PATCH 484/835] changing line breaks to be shorter, no real change --- reference/configuration/framework.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 700bc97a26d..08c976d933a 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -531,9 +531,9 @@ logging **default**: ``true`` when the debug mode is enabled, ``false`` otherwise. When ``true``, a log entry is made whenever the translator cannot find a translation -for a given key. The logs are made to the ``translation`` channel and at the ``debug`` -for level for keys where there is a translation in the fallback locale and the ``warning`` -level if there is no translation to use at all. +for a given key. The logs are made to the ``translation`` channel and at the +``debug`` for level for keys where there is a translation in the fallback +locale and the ``warning`` level if there is no translation to use at all. Full default Configuration -------------------------- From 47021bae1c406bed3dee35887b446f7f2c3ead8f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 10:25:42 -0500 Subject: [PATCH 485/835] Adding tiny note --- reference/configuration/doctrine.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index 1d0b00cde72..b91aee5422f 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -62,6 +62,8 @@ Full Default Configuration MultipleActiveResultSets: ~ driver: pdo_mysql platform_service: ~ + + # when true, queries are logged to a "doctrine" monolog channel logging: "%kernel.debug%" profiling: "%kernel.debug%" driver_class: ~ From deff25f49eb5670bf70ac32c41aa2cab881644cc Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Thu, 25 Sep 2014 14:09:32 +0200 Subject: [PATCH 486/835] Updated documentation regarding the SecurityContext split; symfony/symfony#11690 --- book/security.rst | 48 ++++++++++++------- book/templating.rst | 6 +++ components/security/authentication.rst | 16 +++++-- components/security/authorization.rst | 24 ++++++---- components/security/firewall.rst | 7 ++- cookbook/form/dynamic_form_modification.rst | 41 +++++++++------- cookbook/profiler/matchers.rst | 25 ++++++---- cookbook/security/acl.rst | 8 ++-- .../custom_authentication_provider.rst | 28 +++++------ cookbook/security/remember_me.rst | 6 ++- cookbook/security/securing_services.rst | 40 +++++++++------- cookbook/security/voters_data_permission.rst | 12 +++-- reference/dic_tags.rst | 2 +- reference/twig_reference.rst | 4 ++ 14 files changed, 168 insertions(+), 99 deletions(-) diff --git a/book/security.rst b/book/security.rst index c26108826d2..d80440dc50b 100644 --- a/book/security.rst +++ b/book/security.rst @@ -438,7 +438,7 @@ Next, create the controller that will display the login form:: use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\Security\Core\SecurityContextInterface; + use Symfony\Component\Security\Core\Security; class SecurityController extends Controller { @@ -447,19 +447,19 @@ Next, create the controller that will display the login form:: $session = $request->getSession(); // get the login error if there is one - if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { + if ($request->attributes->has(Security::AUTHENTICATION_ERROR)) { $error = $request->attributes->get( - SecurityContextInterface::AUTHENTICATION_ERROR + Security::AUTHENTICATION_ERROR ); - } elseif (null !== $session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - $error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR); - $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR); + } elseif (null !== $session && $session->has(Security::AUTHENTICATION_ERROR)) { + $error = $session->get(Security::AUTHENTICATION_ERROR); + $session->remove(Security::AUTHENTICATION_ERROR); } else { $error = ''; } // last username entered by the user - $lastUsername = (null === $session) ? '' : $session->get(SecurityContextInterface::LAST_USERNAME); + $lastUsername = (null === $session) ? '' : $session->get(Security::LAST_USERNAME); return $this->render( 'AcmeSecurityBundle:Security:login.html.twig', @@ -713,7 +713,7 @@ see :doc:`/cookbook/security/form_login`. ``/login_check`` doesn't match any firewall, you'll receive a ``Unable to find the controller for path "/login_check"`` exception. - **4. Multiple firewalls don't share security context** + **4. Multiple firewalls don't share the same security context** If you're using multiple firewalls and you authenticate against one firewall, you will *not* be authenticated against any other firewalls automatically. @@ -1174,7 +1174,7 @@ authorization from inside a controller:: public function helloAction($name) { - if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { + if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) { throw $this->createAccessDeniedException('Unable to access this page!'); } @@ -1186,6 +1186,10 @@ authorization from inside a controller:: .. versionadded:: 2.5 The ``createAccessDeniedException`` method was introduced in Symfony 2.5. +.. versionadded:: 2.6 + The ``security.authorization_checker`` service was introduced in Symfony 2.6. Prior + to Symfony 2.6, you had to use the ``isGranted()`` method of the ``security.context`` service. + The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller::createAccessDeniedException` method creates a special :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` object, which ultimately triggers a 403 HTTP response inside Symfony. @@ -1618,14 +1622,18 @@ Retrieving the User Object ~~~~~~~~~~~~~~~~~~~~~~~~~~ After authentication, the ``User`` object of the current user can be accessed -via the ``security.context`` service. From inside a controller, this will +via the ``security.token_storage`` service. From inside a controller, this will look like:: public function indexAction() { - $user = $this->get('security.context')->getToken()->getUser(); + $user = $this->get('security.token_storage')->getToken()->getUser(); } +.. versionadded:: 2.6 + The ``security.token_storage`` service was introduced in Symfony 2.6. Prior + to Symfony 2.6, you had to use the ``getToken()`` method of the ``security.context`` service. + In a controller this can be shortcut to: .. code-block:: php @@ -1895,13 +1903,17 @@ authorization from inside a controller:: public function helloAction($name) { - if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { + if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); } // ... } +.. versionadded:: 2.6 + The ``security.authorization_checker`` service was introduced in Symfony 2.6. Prior + to Symfony 2.6, you had to use the ``isGranted()`` method of the ``security.context`` service. + .. caution:: A firewall must be active or an exception will be thrown when the ``isGranted()`` @@ -1925,7 +1937,7 @@ accepts an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` object:: public function indexAction() { - if (!$this->get('security.context')->isGranted(new Expression( + if (!$this->get('security.authorization_checker')->isGranted(new Expression( '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())' ))) { throw new AccessDeniedException(); @@ -1934,6 +1946,10 @@ accepts an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` object:: // ... } +.. versionadded:: 2.6 + The ``security.authorization_checker`` service was introduced in Symfony 2.6. Prior + to Symfony 2.6, you had to use the ``isGranted()`` method of the ``security.context`` service. + In this example, if the current user has ``ROLE_ADMIN`` or if the current user object's ``isSuperAdmin()`` method returns ``true``, then access will be granted (note: your User object may not have an ``isSuperAdmin`` method, @@ -1979,10 +1995,10 @@ Additionally, you have access to a number of functions inside the expression: use Symfony\Component\ExpressionLanguage\Expression; // ... - $sc = $this->get('security.context'); - $access1 = $sc->isGranted('IS_AUTHENTICATED_REMEMBERED'); + $authorizationChecker = $this->get('security.authorization_checker'); + $access1 = $authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED'); - $access2 = $sc->isGranted(new Expression( + $access2 = $authorizationChecker->isGranted(new Expression( 'is_remember_me() or is_fully_authenticated()' )); diff --git a/book/templating.rst b/book/templating.rst index 66c00938542..e9c5120cdda 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -1168,6 +1168,12 @@ automatically:

    Application Environment: getEnvironment() ?>

    +.. versionadded:: 2.6 + The global ``app.security`` variable (or the ``$app->getSecurity()`` + method in PHP templates) is deprecated as of Symfony 2.6. Use `app.user` + (`$app->getUser()`) and `is_granted()` (`$view['security']->isGranted()`) + instead. + .. tip:: You can add your own global template variables. See the cookbook example diff --git a/components/security/authentication.rst b/components/security/authentication.rst index 01841b5bb4a..035065dc5b1 100644 --- a/components/security/authentication.rst +++ b/components/security/authentication.rst @@ -4,16 +4,22 @@ Authentication ============== +.. versionadded:: 2.6 + The ``TokenStorageInterface`` was introduced in Symfony 2.6. Prior, you + had to use the ``getToken()`` method of the + :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface`. + When a request points to a secured area, and one of the listeners from the firewall map is able to extract the user's credentials from the current :class:`Symfony\\Component\\HttpFoundation\\Request` object, it should create a token, containing these credentials. The next thing the listener should do is ask the authentication manager to validate the given token, and return an *authenticated* token if the supplied credentials were found to be valid. -The listener should then store the authenticated token in the security context:: +The listener should then store the authenticated token using +:class:`the token storage `:: use Symfony\Component\Security\Http\Firewall\ListenerInterface; - use Symfony\Component\Security\Core\SecurityContextInterface; + use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; @@ -21,9 +27,9 @@ The listener should then store the authenticated token in the security context:: class SomeAuthenticationListener implements ListenerInterface { /** - * @var SecurityContextInterface + * @var TokenStorageInterface */ - private $securityContext; + private $tokenStorage; /** * @var AuthenticationManagerInterface @@ -54,7 +60,7 @@ The listener should then store the authenticated token in the security context:: ->authenticationManager ->authenticate($unauthenticatedToken); - $this->securityContext->setToken($authenticatedToken); + $this->tokenStorage->setToken($authenticatedToken); } } diff --git a/components/security/authorization.rst b/components/security/authorization.rst index c5b357e5118..67edd61ad32 100644 --- a/components/security/authorization.rst +++ b/components/security/authorization.rst @@ -7,8 +7,8 @@ Authorization When any of the authentication providers (see :ref:`authentication_providers`) has verified the still-unauthenticated token, an authenticated token will be returned. The authentication listener should set this token directly -in the :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface` -using its :method:`Symfony\\Component\\Security\\Core\\SecurityContextInterface::setToken` +in the :class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorageInterface` +using its :method:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorageInterface::setToken` method. From then on, the user is authenticated, i.e. identified. Now, other parts @@ -29,6 +29,11 @@ An authorization decision will always be based on a few things: Any object for which access control needs to be checked, like an article or a comment object. +.. versionadded:: 2.6 + The ``TokenStorageInterface`` was introduced in Symfony 2.6. Prior, you + had to use the ``setToken()`` method of the + :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface`. + .. _components-security-access-decision-manager: Access Decision Manager @@ -227,23 +232,24 @@ are required for the current user to get access to the application:: $authenticationManager ); -Security Context -~~~~~~~~~~~~~~~~ +Authorization Checker +~~~~~~~~~~~~~~~~~~~~~ The access decision manager is also available to other parts of the application -via the :method:`Symfony\\Component\\Security\\Core\\SecurityContext::isGranted` -method of the :class:`Symfony\\Component\\Security\\Core\\SecurityContext`. +via the :method:`Symfony\\Component\\Security\\Core\\Authorization\\AuthorizationChecker::isGranted` +method of the :class:`Symfony\\Component\\Security\\Core\\Authorization\\AuthorizationChecker`. A call to this method will directly delegate the question to the access decision manager:: - use Symfony\Component\Security\SecurityContext; + use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; use Symfony\Component\Security\Core\Exception\AccessDeniedException; - $securityContext = new SecurityContext( + $authorizationChecker = new AuthorizationChecker( + $tokenStorage, $authenticationManager, $accessDecisionManager ); - if (!$securityContext->isGranted('ROLE_ADMIN')) { + if (!$authorizationChecker->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); } diff --git a/components/security/firewall.rst b/components/security/firewall.rst index 8d30debff6e..29c3730a1da 100644 --- a/components/security/firewall.rst +++ b/components/security/firewall.rst @@ -30,6 +30,11 @@ certain action or resource of the application:: throw new AccessDeniedException(); } +.. versionadded:: 2.6 + As of Symfony 2.6, the :class:`Symfony\\Component\\Security\\Core\\SecurityContext` class was split + in the :class:`Symfony\\Component\\Security\\Core\\Authentication\\Authorization\\AuthorizationChecker` and + :class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorage` classes. + .. note:: Read the dedicated sections to learn more about :doc:`/components/security/authentication` @@ -115,7 +120,7 @@ which will eventually result in an "HTTP/1.1 403: Access Denied" response. Entry Points ~~~~~~~~~~~~ -When the user is not authenticated at all (i.e. when the security context +When the user is not authenticated at all (i.e. when the token storage has no token yet), the firewall's entry point will be called to "start" the authentication process. An entry point should implement :class:`Symfony\\Component\\Security\\Http\\EntryPoint\\AuthenticationEntryPointInterface`, diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index d939cd6321f..0389ae88fb9 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -223,7 +223,7 @@ Using an event listener, your form might look like this:: use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormEvent; - use Symfony\Component\Security\Core\SecurityContext; + use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; class FriendMessageFormType extends AbstractType @@ -255,17 +255,17 @@ contains only this user's friends. Luckily it is pretty easy to inject a service inside of the form. This can be done in the constructor:: - private $securityContext; + private $tokenStorage; - public function __construct(SecurityContext $securityContext) + public function __construct(TokenStorageInterface $tokenStorage) { - $this->securityContext = $securityContext; + $this->tokenStorage = $tokenStorage; } .. note:: - You might wonder, now that you have access to the User (through the security - context), why not just use it directly in ``buildForm`` and omit the + You might wonder, now that you have access to the User (through the token + storage), why not just use it directly in ``buildForm`` and omit the event listener? This is because doing so in the ``buildForm`` method would result in the whole form type being modified and not just this one form instance. This may not usually be a problem, but technically @@ -275,22 +275,22 @@ done in the constructor:: Customizing the Form Type ~~~~~~~~~~~~~~~~~~~~~~~~~ -Now that you have all the basics in place you can take advantage of the ``SecurityContext`` +Now that you have all the basics in place you can take advantage of the ``TokenStorageInterface`` and fill in the listener logic:: // src/Acme/DemoBundle/FormType/FriendMessageFormType.php - use Symfony\Component\Security\Core\SecurityContext; + use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Doctrine\ORM\EntityRepository; // ... class FriendMessageFormType extends AbstractType { - private $securityContext; + private $tokenStorage; - public function __construct(SecurityContext $securityContext) + public function __construct(TokenStorageInterface $tokenStorage) { - $this->securityContext = $securityContext; + $this->tokenStorage = $tokenStorage; } public function buildForm(FormBuilderInterface $builder, array $options) @@ -301,7 +301,7 @@ and fill in the listener logic:: ; // grab the user, do a quick sanity check that one exists - $user = $this->securityContext->getToken()->getUser(); + $user = $this->tokenStorage->getToken()->getUser(); if (!$user) { throw new \LogicException( 'The FriendMessageFormType cannot be used without an authenticated user!' @@ -336,6 +336,11 @@ and fill in the listener logic:: // ... } +.. versionadded:: 2.6 + The :class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorageInterface` was + introduced in Symfony 2.6. Prior, you had to use the ``getToken()`` method of + :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface`. + .. note:: The ``multiple`` and ``expanded`` form options will default to false @@ -347,7 +352,7 @@ Using the Form Our form is now ready to use and there are two possible ways to use it inside of a controller: -a) create it manually and remember to pass the security context to it; +a) create it manually and remember to pass the token storage to it; or @@ -363,9 +368,9 @@ your new form type in many places or embedding it into other forms:: { public function newAction(Request $request) { - $securityContext = $this->container->get('security.context'); + $tokenStorage = $this->container->get('security.token_storage'); $form = $this->createForm( - new FriendMessageFormType($securityContext) + new FriendMessageFormType($tokenStorage) ); // ... @@ -386,7 +391,7 @@ it with :ref:`dic-tags-form-type`. services: acme.form.friend_message: class: Acme\DemoBundle\Form\Type\FriendMessageFormType - arguments: ["@security.context"] + arguments: ["@security.token_storage"] tags: - { name: form.type, alias: acme_friend_message } @@ -395,7 +400,7 @@ it with :ref:`dic-tags-form-type`. - + @@ -408,7 +413,7 @@ it with :ref:`dic-tags-form-type`. $container->setDefinition( 'acme.form.friend_message', $definition, - array('security.context') + array('security.token_storage') ); If you wish to create it from within a controller or any other service that has diff --git a/cookbook/profiler/matchers.rst b/cookbook/profiler/matchers.rst index b23af0d21f0..47d78e0d009 100644 --- a/cookbook/profiler/matchers.rst +++ b/cookbook/profiler/matchers.rst @@ -70,25 +70,30 @@ something like:: // src/Acme/DemoBundle/Profiler/SuperAdminMatcher.php namespace Acme\DemoBundle\Profiler; - use Symfony\Component\Security\Core\SecurityContext; + use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestMatcherInterface; class SuperAdminMatcher implements RequestMatcherInterface { - protected $securityContext; + protected $authorizationChecker; - public function __construct(SecurityContext $securityContext) + public function __construct(AuthorizationCheckerInterface $authorizationChecker) { - $this->securityContext = $securityContext; + $this->authorizationChecker = $authorizationChecker; } public function matches(Request $request) { - return $this->securityContext->isGranted('ROLE_SUPER_ADMIN'); + return $this->authorizationChecker->isGranted('ROLE_SUPER_ADMIN'); } } +.. versionadded:: 2.6 + The :class:`Symfony\\Component\\Security\\Core\\Authentication\\Authorization\\AuthorizationCheckerInterface` was + introduced in Symfony 2.6. Prior, you had to use the ``isGranted`` method of + :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface`. + Then, you need to configure the service: .. configuration-block:: @@ -101,7 +106,7 @@ Then, you need to configure the service: services: acme_demo.profiler.matcher.super_admin: class: "%acme_demo.profiler.matcher.super_admin.class%" - arguments: ["@security.context"] + arguments: ["@security.authorization_checker"] .. code-block:: xml @@ -114,7 +119,7 @@ Then, you need to configure the service: - + .. code-block:: php @@ -129,9 +134,13 @@ Then, you need to configure the service: $container->setDefinition('acme_demo.profiler.matcher.super_admin', new Definition( '%acme_demo.profiler.matcher.super_admin.class%', - array(new Reference('security.context')) + array(new Reference('security.authorization_checker')) ); +.. versionadded:: 2.6 + The ``security.authorization_checker`` service was introduced in Symfony 2.6. Prior + to Symfony 2.6, you had to use the ``isGranted()`` method of the ``security.context`` service. + Now the service is registered, the only thing left to do is configure the profiler to use this service as the matcher: diff --git a/cookbook/security/acl.rst b/cookbook/security/acl.rst index b26b271b4db..7f7005a3a9b 100644 --- a/cookbook/security/acl.rst +++ b/cookbook/security/acl.rst @@ -129,8 +129,8 @@ Creating an ACL and Adding an ACE $acl = $aclProvider->createAcl($objectIdentity); // retrieving the security identity of the currently logged-in user - $securityContext = $this->get('security.context'); - $user = $securityContext->getToken()->getUser(); + $tokenStorage = $this->get('security.token_storage'); + $user = $tokenStorage->getToken()->getUser(); $securityIdentity = UserSecurityIdentity::fromAccount($user); // grant owner access @@ -177,10 +177,10 @@ Checking Access public function editCommentAction(Comment $comment) { - $securityContext = $this->get('security.context'); + $authorizationChecker = $this->get('security.authorization_checker'); // check for edit access - if (false === $securityContext->isGranted('EDIT', $comment)) { + if (false === $authorizationChecker->isGranted('EDIT', $comment)) { throw new AccessDeniedException(); } diff --git a/cookbook/security/custom_authentication_provider.rst b/cookbook/security/custom_authentication_provider.rst index 4232c04ba32..2b3f0c920cb 100644 --- a/cookbook/security/custom_authentication_provider.rst +++ b/cookbook/security/custom_authentication_provider.rst @@ -96,13 +96,13 @@ provider. The Listener ------------ -Next, you need a listener to listen on the security context. The listener +Next, you need a listener to listen on the firewall. The listener is responsible for fielding requests to the firewall and calling the authentication provider. A listener must be an instance of :class:`Symfony\\Component\\Security\\Http\\Firewall\\ListenerInterface`. A security listener should handle the :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseEvent` event, and -set an authenticated token in the security context if successful. +set an authenticated token in the token storage if successful. .. code-block:: php @@ -111,20 +111,20 @@ set an authenticated token in the security context if successful. use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; - use Symfony\Component\Security\Http\Firewall\ListenerInterface; - use Symfony\Component\Security\Core\Exception\AuthenticationException; - use Symfony\Component\Security\Core\SecurityContextInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; + use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; + use Symfony\Component\Security\Core\Exception\AuthenticationException; + use Symfony\Component\Security\Http\Firewall\ListenerInterface; use Acme\DemoBundle\Security\Authentication\Token\WsseUserToken; class WsseListener implements ListenerInterface { - protected $securityContext; + protected $tokenStorage; protected $authenticationManager; - public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager) + public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager) { - $this->securityContext = $securityContext; + $this->tokenStorage = $tokenStorage; $this->authenticationManager = $authenticationManager; } @@ -146,7 +146,7 @@ set an authenticated token in the security context if successful. try { $authToken = $this->authenticationManager->authenticate($token); - $this->securityContext->setToken($authToken); + $this->tokenStorage->setToken($authToken); return; } catch (AuthenticationException $failed) { @@ -154,9 +154,9 @@ set an authenticated token in the security context if successful. // To deny the authentication clear the token. This will redirect to the login page. // Make sure to only clear your token, not those of other authentication listeners. - // $token = $this->securityContext->getToken(); + // $token = $this->tokenStorage->getToken(); // if ($token instanceof WsseUserToken && $this->providerKey === $token->getProviderKey()) { - // $this->securityContext->setToken(null); + // $this->tokenStorage->setToken(null); // } // return; } @@ -399,7 +399,7 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` wsse.security.authentication.listener: class: Acme\DemoBundle\Security\Firewall\WsseListener - arguments: ["@security.context", "@security.authentication.manager"] + arguments: ["@security.token_storage", "@security.authentication.manager"] .. code-block:: xml @@ -417,7 +417,7 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` - +
    @@ -441,7 +441,7 @@ to service ids that do not exist yet: ``wsse.security.authentication.provider`` $container->setDefinition('wsse.security.authentication.listener', new Definition( 'Acme\DemoBundle\Security\Firewall\WsseListener', array( - new Reference('security.context'), + new Reference('security.token_storage'), new Reference('security.authentication.manager'), ) ) diff --git a/cookbook/security/remember_me.rst b/cookbook/security/remember_me.rst index 668057201cf..590d8d9eaac 100644 --- a/cookbook/security/remember_me.rst +++ b/cookbook/security/remember_me.rst @@ -162,7 +162,7 @@ In the following example, the action is only allowed if the user has the public function editAction() { - if (false === $this->get('security.context')->isGranted( + if (false === $this->get('security.authorization_checker')->isGranted( 'IS_AUTHENTICATED_FULLY' )) { throw new AccessDeniedException(); @@ -171,6 +171,10 @@ In the following example, the action is only allowed if the user has the // ... } +.. versionadded:: 2.6 + The ``security.authorization_checker`` service was introduced in Symfony 2.6. Prior + to Symfony 2.6, you had to use the ``isGranted()`` method of the ``security.context`` service. + You can also choose to install and use the optional JMSSecurityExtraBundle_, which can secure your controller using annotations: diff --git a/cookbook/security/securing_services.rst b/cookbook/security/securing_services.rst index 641a43f04ec..0f62815950e 100644 --- a/cookbook/security/securing_services.rst +++ b/cookbook/security/securing_services.rst @@ -6,7 +6,7 @@ How to Secure any Service or Method in your Application ======================================================= In the security chapter, you can see how to :ref:`secure a controller ` -by requesting the ``security.context`` service from the Service Container +by requesting the ``security.authorization_checker`` service from the Service Container and checking the current user's role:: // ... @@ -14,14 +14,18 @@ and checking the current user's role:: public function helloAction($name) { - if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { + if (false === $this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); } // ... } -You can also secure *any* service in a similar way by injecting the ``security.context`` +.. versionadded:: 2.6 + The ``security.authorization_checker`` service was introduced in Symfony 2.6. Prior + to Symfony 2.6, you had to use the ``isGranted()`` method of the ``security.context`` service. + +You can also secure *any* service in a similar way by injecting the ``security.authorization_checker`` service into it. For a general introduction to injecting dependencies into services see the :doc:`/book/service_container` chapter of the book. For example, suppose you have a ``NewsletterManager`` class that sends out emails @@ -45,23 +49,23 @@ role. Before you add security, the class looks something like this: } Your goal is to check the user's role when the ``sendNewsletter()`` method is -called. The first step towards this is to inject the ``security.context`` +called. The first step towards this is to inject the ``security.authorization_checker`` service into the object. Since it won't make sense *not* to perform the security check, this is an ideal candidate for constructor injection, which guarantees -that the security context object will be available inside the ``NewsletterManager`` +that the authorization checker object will be available inside the ``NewsletterManager`` class:: namespace Acme\HelloBundle\Newsletter; - use Symfony\Component\Security\Core\SecurityContextInterface; + use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; class NewsletterManager { - protected $securityContext; + protected $authorizationChecker; - public function __construct(SecurityContextInterface $securityContext) + public function __construct(AuthorizationCheckerInterface $authorizationChecker) { - $this->securityContext = $securityContext; + $this->authorizationChecker = $authorizationChecker; } // ... @@ -80,7 +84,7 @@ Then in your service configuration, you can inject the service: services: newsletter_manager: class: "%newsletter_manager.class%" - arguments: ["@security.context"] + arguments: ["@security.authorization_checker"] .. code-block:: xml @@ -91,7 +95,7 @@ Then in your service configuration, you can inject the service: - + @@ -105,7 +109,7 @@ Then in your service configuration, you can inject the service: $container->setDefinition('newsletter_manager', new Definition( '%newsletter_manager.class%', - array(new Reference('security.context')) + array(new Reference('security.authorization_checker')) )); The injected service can then be used to perform the security check when the @@ -113,22 +117,22 @@ The injected service can then be used to perform the security check when the namespace Acme\HelloBundle\Newsletter; + use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; - use Symfony\Component\Security\Core\SecurityContextInterface; // ... class NewsletterManager { - protected $securityContext; + protected $authorizationChecker; - public function __construct(SecurityContextInterface $securityContext) + public function __construct(AuthorizationCheckerInterface $authorizationChecker) { - $this->securityContext = $securityContext; + $this->authorizationChecker = $authorizationChecker; } public function sendNewsletter() { - if (false === $this->securityContext->isGranted('ROLE_NEWSLETTER_ADMIN')) { + if (false === $this->authorizationChecker->isGranted('ROLE_NEWSLETTER_ADMIN')) { throw new AccessDeniedException(); } @@ -186,7 +190,7 @@ the :ref:`sidebar ` below): $definition = new Definition( '%newsletter_manager.class%', - array(new Reference('security.context')) + // ... )); $definition->addTag('security.secure_service'); $container->setDefinition('newsletter_manager', $definition); diff --git a/cookbook/security/voters_data_permission.rst b/cookbook/security/voters_data_permission.rst index 0238ee5e4ce..057d8e04919 100644 --- a/cookbook/security/voters_data_permission.rst +++ b/cookbook/security/voters_data_permission.rst @@ -25,8 +25,8 @@ How Symfony Uses Voters In order to use voters, you have to understand how Symfony works with them. All voters are called each time you use the ``isGranted()`` method on Symfony's -security context (i.e. the ``security.context`` service). Each one decides -if the current user should have access to some resource. +authorization checker (i.e. the ``security.authorization_checker`` service). Each +one decides if the current user should have access to some resource. Ultimately, Symfony uses one of three different approaches on what to do with the feedback from all voters: affirmative, consensus and unanimous. @@ -194,7 +194,7 @@ How to Use the Voter in a Controller ------------------------------------ The registered voter will then always be asked as soon as the method ``isGranted()`` -from the security context is called. +from the authorization checker is called. .. code-block:: php @@ -213,7 +213,7 @@ from the security context is called. $post = ...; // keep in mind, this will call all registered security voters - if (false === $this->get('security.context')->isGranted('view', $post)) { + if (false === $this->get('security.authorization_checker')->isGranted('view', $post)) { throw new AccessDeniedException('Unauthorised access!'); } @@ -221,4 +221,8 @@ from the security context is called. } } +.. versionadded:: 2.6 + The ``security.authorization_checker`` service was introduced in Symfony 2.6. Prior + to Symfony 2.6, you had to use the ``isGranted()`` method of the ``security.context`` service. + It's that easy! diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 87774a94f44..8a58ff4e8b1 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -933,7 +933,7 @@ security.voter **Purpose**: To add a custom voter to Symfony's authorization logic -When you call ``isGranted`` on Symfony's security context, a system of "voters" +When you call ``isGranted`` on Symfony's authorization checker, a system of "voters" is used behind the scenes to determine if the user should have access. The ``security.voter`` tag allows you to add your own custom voter to that system. diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 8152a28968f..662a7d931a6 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -683,6 +683,10 @@ The available attributes are: * ``app.debug`` * ``app.security`` +.. versionadded:: 2.6 + The ``app.security`` global is deprecated as of 2.6. The user is already available + as ``app.user`` and ``is_granted()`` is registered as function. + Symfony Standard Edition Extensions ----------------------------------- From 59f8bab4961f2f9a1dfb90e84b9c4f6a8273fcb7 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 7 Dec 2014 18:12:18 +0100 Subject: [PATCH 487/835] Added November changelog --- changelog.rst | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/changelog.rst b/changelog.rst index e29f3cf1aed..a9a06f11c59 100644 --- a/changelog.rst +++ b/changelog.rst @@ -13,6 +13,76 @@ documentation. Do you also want to participate in the Symfony Documentation? Take a look at the ":doc:`/contributing/documentation/overview`" article. +November, 2014 +-------------- + +New Documentation +~~~~~~~~~~~~~~~~~ + +- `135aae6 `_ #4433 Completely re-reading the controller chapter (weaverryan) +- `422e0f1 `_ #4465 Modifying the best practice to use form_start() instead of
    `_ #4463 [BestPractices] Proposing that we make the service names *just* a little bit longer (weaverryan) +- `1d88a1b `_ #4443 Added the release dates for the upcoming Symfony 3 versions (javiereguiluz) +- `f2ab245 `_ #4374 [WCM] Revamped the Quick Start tutorial (javiereguiluz) +- `2c190ed `_ #4427 Update most important book articles to follow the best practices (WouterJ) +- `12a09ab `_ #4377 Added interlinking and fixed install template for reusable bundles (WouterJ) +- `8259d71 `_ #4425 Updating component usage to use composer require (weaverryan) +- `0e80aba `_ #4369 [reference][configuration][security]Added key_length for pbkdf2 encoder (Guillaume-Rossignol) +- `5165419 `_ #4295 [Security] Hidden front controller for Nginx (phansys) + +Fixed Documentation +~~~~~~~~~~~~~~~~~~~ + +- `9d599a0 `_ minor #4544 #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook (ternel) +- `6aabece `_ #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook +- `4f66d48 `_ #4506 SetDescription required on Product entities (yearofthegus) +- `85bf906 `_ #4444 fix elseif statement (MightyBranch) +- `ad14e78 `_ #4494 Updated the Symfony Installer installation instructions (javiereguiluz) +- `33bf462 `_ #4407 [Components][Console] array options need array default values (xabbuh) +- `2ab2e1f `_ #4342 Reworded a misleading Doctrine explanation (javiereguiluz) + +Minor Documentation Changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- `05f5dba `_ #4536 Add Ryan Weaver as 10th core team member (ifdattic) +- `7b1ff2a `_ #4554 Changed url to PHP-CS-FIXER repository (jzawadzki) +- `9d599a0 `_ #4544 bug #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook (ternel) +- `7b3500c `_ #4542 Update conventions.rst (csuarez) +- `5aaba1e `_ #4529 Best Practices: Update link title to match cookbook article title (dangarzon) +- `ab8e7f5 `_ #4530 Book: Update link title to match cookbook article title (dangarzon) +- `bf61658 `_ #4523 Add missing semicolons to PropertyAccess examples (loonytoons) +- `5db8386 `_ #4462 [Reference] Fixed lots of things using the review bot (WouterJ) +- `dbfaac1 `_ #4459 Fix up the final sentence to be a bit cleaner. (micheal) +- `3761e50 `_ #4514 [Contributing][Documentation] typo fix (xabbuh) +- `21afb4c `_ #4445 Removed unnecessary use statement (Alex Salguero) +- `3969fd6 `_ #4432 [Reference][Twig] tweaks to the Twig reference (xabbuh) +- `188dd1f `_ #4400 Continues #4307 (SamanShafigh, WouterJ) +- `c008733 `_ #4399 Explain form() and form_widget() in form customization (oopsFrogs, WouterJ) +- `2139754 `_ #4253 Adder and remover sidenote (kshishkin) +- `b81eb4d `_ #4488 Terrible mistake! Comma instead of semicolon... (nuvolapl) +- `0ee3ae7 `_ #4481 [Cookbook][Cache] add syntax highlighting for Varnish code blocks (xabbuh) +- `0577559 `_ #4418 use the C lexer for Varnish config examples (xabbuh) +- `97d8f61 `_ #4403 Improved naming (WouterJ) +- `6298595 `_ #4453 Fixed make file (WouterJ) +- `0c7dd72 `_ #4475 Fixed typos (pborreli) +- `b847b2d `_ #4480 Fix spelling (nurikabe) +- `0d91cc5 `_ #4461 Update doctrine.rst (guiguiboy) +- `81fc1c6 `_ #4448 [Book][HTTP Cache] moved inlined URL to the bottom of the file (xabbuh) +- `6995b07 `_ #4435 consistent table headlines (xabbuh) +- `0380d34 `_ #4447 [Book] tweaks to #4427 (xabbuh) +- `eb0d8ac `_ #4441 Updated first code-block``::`` bash (Nitaco) +- `41bc061 `_ #4106 removed references to documentation from external sources (fabpot, WouterJ) +- `c9a8dff `_ #4352 [Best Practices] update best practices index (xabbuh) +- `8a93c95 `_ #4437 Correct link to scopes page (mayeco) +- `91eb652 `_ #4438 Fix typo: Objected => Object (ifdattic) +- `5d6d0c2 `_ #4436 remove semicolons in PHP templates (xabbuh) +- `97c4b2e `_ #4434 remove unused label (xabbuh) +- `4be6786 `_ #4326 [Components][Form] Grammar improvement (fabschurt) +- `a27238e `_ #4313 Improved and fixed twig reference (WouterJ) +- `1ce9dc5 `_ #4398 A few small improvements to the EventDispatcher Component docs (GeertDD) +- `42abc66 `_ #4421 [Best Practices] removed unused links in business-logic (77web) +- `61c0bc5 `_ #4419 [DependencyInjection] Add missing space in code (michaelperrin) + October, 2014 ------------- From 8c30ae9e97d5d59f51006e14a53046abe27f5174 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 7 Dec 2014 18:13:57 +0100 Subject: [PATCH 488/835] Added November changelog --- changelog.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/changelog.rst b/changelog.rst index 0203c9953a9..4a4349886ac 100644 --- a/changelog.rst +++ b/changelog.rst @@ -19,9 +19,11 @@ November, 2014 New Documentation ~~~~~~~~~~~~~~~~~ +- `33554fc `_ #4456 New validation API usage in Class Constraint Validator (skwi) - `135aae6 `_ #4433 Completely re-reading the controller chapter (weaverryan) - `422e0f1 `_ #4465 Modifying the best practice to use form_start() instead of `_ #4463 [BestPractices] Proposing that we make the service names *just* a little bit longer (weaverryan) +- `766e01f `_ #4169 [Components][Form] document $deep and $flatten of getErrors() (xabbuh) - `1d88a1b `_ #4443 Added the release dates for the upcoming Symfony 3 versions (javiereguiluz) - `f2ab245 `_ #4374 [WCM] Revamped the Quick Start tutorial (javiereguiluz) - `2c190ed `_ #4427 Update most important book articles to follow the best practices (WouterJ) @@ -35,15 +37,18 @@ Fixed Documentation - `9d599a0 `_ minor #4544 #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook (ternel) - `6aabece `_ #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook +- `e96ebd3 `_ #4522 Add missing brackets to PropertyAccessor examples (loonytoons) - `4f66d48 `_ #4506 SetDescription required on Product entities (yearofthegus) - `85bf906 `_ #4444 fix elseif statement (MightyBranch) - `ad14e78 `_ #4494 Updated the Symfony Installer installation instructions (javiereguiluz) +- `7cc4287 `_ #4442 replace doc role for bundle docs with external ref (xabbuh) - `33bf462 `_ #4407 [Components][Console] array options need array default values (xabbuh) - `2ab2e1f `_ #4342 Reworded a misleading Doctrine explanation (javiereguiluz) Minor Documentation Changes ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- `a109c4b `_ #4537 Update link to remove absolute URL (jms85, dangarzon) - `05f5dba `_ #4536 Add Ryan Weaver as 10th core team member (ifdattic) - `7b1ff2a `_ #4554 Changed url to PHP-CS-FIXER repository (jzawadzki) - `9d599a0 `_ #4544 bug #4273 - fix doctrine version in How to Provide Model Classes for several Doctrine Implementations cookbook (ternel) @@ -51,6 +56,7 @@ Minor Documentation Changes - `5aaba1e `_ #4529 Best Practices: Update link title to match cookbook article title (dangarzon) - `ab8e7f5 `_ #4530 Book: Update link title to match cookbook article title (dangarzon) - `bf61658 `_ #4523 Add missing semicolons to PropertyAccess examples (loonytoons) +- `8beadce `_ #4496 [Book][Security] link to a bundle's current (not master) docs (xabbuh) - `5db8386 `_ #4462 [Reference] Fixed lots of things using the review bot (WouterJ) - `dbfaac1 `_ #4459 Fix up the final sentence to be a bit cleaner. (micheal) - `3761e50 `_ #4514 [Contributing][Documentation] typo fix (xabbuh) From 70e2288b8a9e9bc9b2a0371f19432d8d2c07c849 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 7 Dec 2014 18:14:42 +0100 Subject: [PATCH 489/835] Added November changelog --- changelog.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/changelog.rst b/changelog.rst index 7e091268f9c..0dd2c9273b4 100644 --- a/changelog.rst +++ b/changelog.rst @@ -21,8 +21,14 @@ New Documentation - `33554fc `_ #4456 New validation API usage in Class Constraint Validator (skwi) - `135aae6 `_ #4433 Completely re-reading the controller chapter (weaverryan) +- `f748378 `_ #4498 Use new factory syntax (WouterJ) +- `59f0374 `_ #4490 Documented ExpressionLanguage extensibility (WouterJ) +- `ed241ab `_ #4487 Documented html5 option (WouterJ) +- `48a5af3 `_ #4486 Renamed empty_value to placeholder (WouterJ) - `422e0f1 `_ #4465 Modifying the best practice to use form_start() instead of `_ #4463 [BestPractices] Proposing that we make the service names *just* a little bit longer (weaverryan) +- `9a22865 `_ #4446 [Book][Templating] refer to the VarDumper component for dump() (xabbuh) +- `ed5c61f `_ #4411 Added a reference to the Bootstrap 3 form theme (javiereguiluz) - `766e01f `_ #4169 [Components][Form] document $deep and $flatten of getErrors() (xabbuh) - `1d88a1b `_ #4443 Added the release dates for the upcoming Symfony 3 versions (javiereguiluz) - `f2ab245 `_ #4374 [WCM] Revamped the Quick Start tutorial (javiereguiluz) @@ -30,7 +36,11 @@ New Documentation - `12a09ab `_ #4377 Added interlinking and fixed install template for reusable bundles (WouterJ) - `8259d71 `_ #4425 Updating component usage to use composer require (weaverryan) - `0e80aba `_ #4369 [reference][configuration][security]Added key_length for pbkdf2 encoder (Guillaume-Rossignol) +- `d1afa4d `_ #4243 [WIP] var-dumper component (nicolas-grekas) - `5165419 `_ #4295 [Security] Hidden front controller for Nginx (phansys) +- `23f790a `_ #4058 Skip console commands from event listeners (tPl0ch) +- `4b98d48 `_ #3386 [Translation] added method to expose collected message (Grygir) +- `242d4f6 `_ #4319 Documentation for debug:event-dispatcher command (matthieuauger) Fixed Documentation ~~~~~~~~~~~~~~~~~~~ @@ -57,6 +67,7 @@ Minor Documentation Changes - `ab8e7f5 `_ #4530 Book: Update link title to match cookbook article title (dangarzon) - `bf61658 `_ #4523 Add missing semicolons to PropertyAccess examples (loonytoons) - `8beadce `_ #4496 [Book][Security] link to a bundle's current (not master) docs (xabbuh) +- `43809b1 `_ #4479 remove versionadded directives for old versions (xabbuh) - `5db8386 `_ #4462 [Reference] Fixed lots of things using the review bot (WouterJ) - `dbfaac1 `_ #4459 Fix up the final sentence to be a bit cleaner. (micheal) - `3761e50 `_ #4514 [Contributing][Documentation] typo fix (xabbuh) From 32e6f064f72bcbf3c555167f95f00cbeba004328 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 7 Dec 2014 18:19:26 +0100 Subject: [PATCH 490/835] Added two missing pulls --- changelog.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.rst b/changelog.rst index 0dd2c9273b4..f7a6d58f4f9 100644 --- a/changelog.rst +++ b/changelog.rst @@ -31,6 +31,8 @@ New Documentation - `ed5c61f `_ #4411 Added a reference to the Bootstrap 3 form theme (javiereguiluz) - `766e01f `_ #4169 [Components][Form] document $deep and $flatten of getErrors() (xabbuh) - `1d88a1b `_ #4443 Added the release dates for the upcoming Symfony 3 versions (javiereguiluz) +- `3329bd2 `_ #4424 [#4243] Tweaks to the new var-dumper component (weaverryan, nicolas-grekas) +- `9caea6f `_ #4336 [Form] Add entity manager instance support for em option (egeloen) - `f2ab245 `_ #4374 [WCM] Revamped the Quick Start tutorial (javiereguiluz) - `2c190ed `_ #4427 Update most important book articles to follow the best practices (WouterJ) - `12a09ab `_ #4377 Added interlinking and fixed install template for reusable bundles (WouterJ) From 0065791f1f8a01c122cafdb658bb5a82ac8dc9e8 Mon Sep 17 00:00:00 2001 From: Panz Date: Fri, 28 Nov 2014 21:47:37 +0100 Subject: [PATCH 491/835] Update web_server_configuration.rst Fixes for issue #4527 https://github.com/symfony/symfony-docs/issues/4527 --- cookbook/configuration/web_server_configuration.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cookbook/configuration/web_server_configuration.rst b/cookbook/configuration/web_server_configuration.rst index f92c801a724..26db6a578d3 100644 --- a/cookbook/configuration/web_server_configuration.rst +++ b/cookbook/configuration/web_server_configuration.rst @@ -127,6 +127,9 @@ directive to pass requests for PHP files to PHP FPM: ServerName domain.tld ServerAlias www.domain.tld + # Force Apache to pass the Authorization header to PHP + SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 + ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/web/$1 DocumentRoot /var/www/project/web From f70dbf17048b3ce5fc3e8bf0e6395ec5635b5430 Mon Sep 17 00:00:00 2001 From: Panz Date: Fri, 28 Nov 2014 22:36:37 +0100 Subject: [PATCH 492/835] Update web_server_configuration.rst Commented the fix for PHP-FPM in Apache VirtualHost More detailed comments --- cookbook/configuration/web_server_configuration.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cookbook/configuration/web_server_configuration.rst b/cookbook/configuration/web_server_configuration.rst index 26db6a578d3..c7ac9f015ac 100644 --- a/cookbook/configuration/web_server_configuration.rst +++ b/cookbook/configuration/web_server_configuration.rst @@ -127,8 +127,10 @@ directive to pass requests for PHP files to PHP FPM: ServerName domain.tld ServerAlias www.domain.tld - # Force Apache to pass the Authorization header to PHP - SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 + # Uncomment the following line to force Apache to pass the Authorization + # header to PHP: required for "basic_auth" under PHP-FPM and FastCGI + # + # SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1 ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/web/$1 From 8ffe8a9d2d37fd8cb0b3452a3d3531356b6ded8c Mon Sep 17 00:00:00 2001 From: Pascal Borreli Date: Sat, 29 Nov 2014 10:29:48 +0100 Subject: [PATCH 493/835] Fixed typos --- best_practices/security.rst | 6 ++++-- components/console/helpers/dialoghelper.rst | 2 +- components/translation/usage.rst | 2 +- reference/twig_reference.rst | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/best_practices/security.rst b/best_practices/security.rst index 4c18acc878b..fbdba6c4321 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -75,7 +75,7 @@ Authorization (i.e. Denying Access) Symfony gives you several ways to enforce authorization, including the ``access_control`` configuration in :doc:`security.yml ` the :ref:`@Security annotation ` and using -:ref:`isGranted ` on the ``security.context`` +:ref:`isGranted ` on the ``security.context`` service directly. .. best-practice:: @@ -206,7 +206,7 @@ Now you can reuse this method both in the template and in the security expressio ... {% endif %} -.. _best-practices-directy-isGranted: +.. _best-practices-directly-isGranted: Checking Permissions without @Security -------------------------------------- @@ -352,5 +352,7 @@ develop :doc:`your own user provider ` and .. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html .. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html +.. _`security voter`: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html +.. _`ACL's`: http://symfony.com/doc/current/cookbook/security/acl.html .. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle diff --git a/components/console/helpers/dialoghelper.rst b/components/console/helpers/dialoghelper.rst index 09b3e215033..b6544139a26 100644 --- a/components/console/helpers/dialoghelper.rst +++ b/components/console/helpers/dialoghelper.rst @@ -264,7 +264,7 @@ from the command line, you need to overwrite the HelperSet used by the command:: $dialog = $command->getHelper('dialog'); $dialog->setInputStream($this->getInputStream("Test\n")); - // Equals to a user inputing "Test" and hitting ENTER + // Equals to a user inputting "Test" and hitting ENTER // If you need to enter a confirmation, "yes\n" will work $commandTester->execute(array('command' => $command->getName())); diff --git a/components/translation/usage.rst b/components/translation/usage.rst index 68a85e44681..1cf35f5f781 100644 --- a/components/translation/usage.rst +++ b/components/translation/usage.rst @@ -226,7 +226,7 @@ Pluralization ------------- Message pluralization is a tough topic as the rules can be quite complex. For -instance, here is the mathematic representation of the Russian pluralization +instance, here is the mathematical representation of the Russian pluralization rules:: (($number % 10 == 1) && ($number % 100 != 11)) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 8de5a7e4b69..e0ac46cc208 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -8,7 +8,7 @@ Symfony Twig Extensions Twig is the default template engine for Symfony. By itself, it already contains a lot of built-in functions, filters, tags and tests (learn more about them -from the the `Twig Reference`_). +from the `Twig Reference`_). Symfony adds more custom extensions on top of Twig to integrate some components into the Twig templates. You can find more information about the custom From 0212a98b4e1fd1f489a296b1bfd9008e66a83d1c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 17:20:54 -0500 Subject: [PATCH 494/835] Front-porting changes by @pborreli in #4531 --- components/var_dumper/advanced.rst | 2 +- components/var_dumper/introduction.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst index b6aae5c792e..fb261dfc157 100644 --- a/components/var_dumper/advanced.rst +++ b/components/var_dumper/advanced.rst @@ -235,4 +235,4 @@ properties not in the class declaration). .. tip:: - Before writting your own casters, you should check the existing ones. + Before writing your own casters, you should check the existing ones. diff --git a/components/var_dumper/introduction.rst b/components/var_dumper/introduction.rst index 06c7eb5816d..869c73fc6fe 100644 --- a/components/var_dumper/introduction.rst +++ b/components/var_dumper/introduction.rst @@ -67,7 +67,7 @@ DebugBundle and Twig Integration The ``DebugBundle`` allows greater integration of the component into the Symfony full stack framework. It is enabled by default in the *dev* and *test* -environement of the standard edition since version 2.6. +environment of the standard edition since version 2.6. Since generating (even debug) output in the controller or in the model of your application may just break it by e.g. sending HTTP headers or @@ -131,7 +131,7 @@ then its dump representation:: .. note:: - The gray arrow is a toggle button for hidding/showing children of + The gray arrow is a toggle button for hiding/showing children of nested structures. .. code-block:: php @@ -232,7 +232,7 @@ then its dump representation:: "When a dump goes over its maximum items limit,\n" ."or when some special objects are encountered,\n" ."children can be replaced by an ellipsis and\n" - ."optionnally followed by a number that says how\n" + ."optionally followed by a number that says how\n" ."many have been removed; `9` in this case.\n" ); dump($var); From c090a744c2ded4ff33b9f0f462adfdac8f5dae43 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 17:25:41 -0500 Subject: [PATCH 495/835] Fixing bad merge --- best_practices/security.rst | 89 ------------------------------------- 1 file changed, 89 deletions(-) diff --git a/best_practices/security.rst b/best_practices/security.rst index 818154f52e3..4c1eef754d6 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -123,95 +123,6 @@ Using ``@Security``, this looks like: // ... } -<<<<<<< HEAD -Using Expressions for Complex Security Restrictions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your security logic is a little bit more complex, you can use an `expression`_ -inside ``@Security``. In the following example, a user can only access the -controller if their email matches the value returned by the ``getAuthorEmail`` -method on the ``Post`` object: - -.. code-block:: php - - use AppBundle\Entity\Post; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; - - /** - * @Route("/{id}/edit", name="admin_post_edit") - * @Security("user.getEmail() == post.getAuthorEmail()") - */ - public function editAction(Post $post) - { - // ... - } - -Notice that this requires the use of the `ParamConverter`_, which automatically -queries for the ``Post`` object and puts it on the ``$post`` argument. This -is what makes it possible to use the ``post`` variable in the expression. - -This has one major drawback: an expression in an annotation cannot easily -be reused in other parts of the application. Imagine that you want to add -a link in a template that will only be seen by authors. Right now you'll -need to repeat the expression code using Twig syntax: - -.. code-block:: html+jinja - - {% if app.user and app.user.email == post.authorEmail %} - ... - {% endif %} - -The easiest solution - if your logic is simple enough - is to add a new method -to the ``Post`` entity that checks if a given user is its author: - -.. code-block:: php - - // src/AppBundle/Entity/Post.php - // ... - - class Post - { - // ... - - /** - * Is the given User the author of this Post? - * - * @return bool - */ - public function isAuthor(User $user = null) - { - return $user && $user->getEmail() == $this->getAuthorEmail(); - } - } - -Now you can reuse this method both in the template and in the security expression: - -.. code-block:: php - - use AppBundle\Entity\Post; - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; - - /** - * @Route("/{id}/edit", name="admin_post_edit") - * @Security("post.isAuthor(user)") - */ - public function editAction(Post $post) - { - // ... - } - -.. code-block:: html+jinja - - {% if post.isAuthor(app.user) %} - ... - {% endif %} - -.. _best-practices-directly-isGranted: -======= -.. _best-practices-directy-isGranted: ->>>>>>> pull/4548 - Checking Permissions without @Security -------------------------------------- From ee44fe415052f477c89b52c8daf07cecbe31a26f Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 17:26:21 -0500 Subject: [PATCH 496/835] Reverts a commit to 2.3 for 2.4 features. This re-adds them to the 2.5 branch Revert "Remove ExpressionLanguage reference for 2.3 version" This reverts commit d1e7334bf94ac82c0f73d549d1b8543f0692c008. Conflicts: best_practices/security.rst --- best_practices/security.rst | 87 +++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/best_practices/security.rst b/best_practices/security.rst index 4c1eef754d6..e9a3325d801 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -123,6 +123,91 @@ Using ``@Security``, this looks like: // ... } +Using Expressions for Complex Security Restrictions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your security logic is a little bit more complex, you can use an `expression`_ +inside ``@Security``. In the following example, a user can only access the +controller if their email matches the value returned by the ``getAuthorEmail`` +method on the ``Post`` object: + +.. code-block:: php + + use AppBundle\Entity\Post; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; + + /** + * @Route("/{id}/edit", name="admin_post_edit") + * @Security("user.getEmail() == post.getAuthorEmail()") + */ + public function editAction(Post $post) + { + // ... + } + +Notice that this requires the use of the `ParamConverter`_, which automatically +queries for the ``Post`` object and puts it on the ``$post`` argument. This +is what makes it possible to use the ``post`` variable in the expression. + +This has one major drawback: an expression in an annotation cannot easily +be reused in other parts of the application. Imagine that you want to add +a link in a template that will only be seen by authors. Right now you'll +need to repeat the expression code using Twig syntax: + +.. code-block:: html+jinja + + {% if app.user and app.user.email == post.authorEmail %} + ... + {% endif %} + +The easiest solution - if your logic is simple enough - is to add a new method +to the ``Post`` entity that checks if a given user is its author: + +.. code-block:: php + + // src/AppBundle/Entity/Post.php + // ... + + class Post + { + // ... + + /** + * Is the given User the author of this Post? + * + * @return bool + */ + public function isAuthor(User $user = null) + { + return $user && $user->getEmail() == $this->getAuthorEmail(); + } + } + +Now you can reuse this method both in the template and in the security expression: + +.. code-block:: php + + use AppBundle\Entity\Post; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; + + /** + * @Route("/{id}/edit", name="admin_post_edit") + * @Security("post.isAuthor(user)") + */ + public function editAction(Post $post) + { + // ... + } + +.. code-block:: html+jinja + + {% if post.isAuthor(app.user) %} + ... + {% endif %} + +.. _best-practices-directly-isGranted: + Checking Permissions without @Security -------------------------------------- @@ -267,7 +352,9 @@ develop :doc:`your own user provider ` and .. _`Security Cookbook Section`: http://symfony.com/doc/current/cookbook/security/index.html .. _`security.yml`: http://symfony.com/doc/current/reference/configuration/security.html +.. _`ParamConverter`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html .. _`@Security annotation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/security.html .. _`security voter`: http://symfony.com/doc/current/cookbook/security/voters_data_permission.html .. _`ACL's`: http://symfony.com/doc/current/cookbook/security/acl.html +.. _`expression`: http://symfony.com/doc/current/components/expression_language/introduction.html .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle From bf8cee66dc3a36e24a429b5ca19c6d54e00c5910 Mon Sep 17 00:00:00 2001 From: Jerzy Zawadzki Date: Sat, 29 Nov 2014 16:47:13 +0100 Subject: [PATCH 497/835] required PHPUnit version in the docs should be updated to 4.2 (or later) since the atLeast() method which is used in some of the tests --- contributing/code/tests.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index 3614cc772ca..709c88a8dc4 100644 --- a/contributing/code/tests.rst +++ b/contributing/code/tests.rst @@ -9,7 +9,7 @@ Symfony test suite to check that you have not broken anything. PHPUnit ------- -To run the Symfony test suite, `install PHPUnit`_ 3.7 (or later) first. +To run the Symfony test suite, `install PHPUnit`_ 4.2 (or later) first. Dependencies (optional) ----------------------- From 9d582e4afa504b8629c79c33cd22c0fa58fcae1e Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Sun, 30 Nov 2014 12:53:54 +0100 Subject: [PATCH 498/835] Use the new build env on Travis faster vms with more ram and cpu, much improved boot times --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 07124e8178c..73bca138115 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,8 @@ language: python python: - "2.7" +sudo: false + install: - "pip install -q -r requirements.txt --use-mirrors" From eabcc59cb6a02fa463e002963b2bf4ceadaad422 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 7 Dec 2014 23:56:18 +0100 Subject: [PATCH 499/835] readd mistakenly removed label --- best_practices/security.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/best_practices/security.rst b/best_practices/security.rst index 4c1eef754d6..35a6875c24f 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -123,6 +123,8 @@ Using ``@Security``, this looks like: // ... } +.. _best-practices-directly-isGranted: + Checking Permissions without @Security -------------------------------------- From 78e08f4fdb7286d8ae39d513db141e2c4138c482 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 18:37:18 -0500 Subject: [PATCH 500/835] Removing some installation instructions --- cookbook/workflow/new_project_git.rst | 30 +++------------------------ cookbook/workflow/new_project_svn.rst | 23 ++++++++++---------- 2 files changed, 14 insertions(+), 39 deletions(-) diff --git a/cookbook/workflow/new_project_git.rst b/cookbook/workflow/new_project_git.rst index 4f355591288..f74ae969f35 100644 --- a/cookbook/workflow/new_project_git.rst +++ b/cookbook/workflow/new_project_git.rst @@ -19,24 +19,10 @@ that's stored using the `Git`_ source control management system. Initial Project Setup --------------------- -To get started, you'll need to download Symfony and initialize your local -git repository: +To get started, you'll need to download Symfony and get things running. See +the :doc:`/book/installation` chapter for details. -#. Download the `Symfony Standard Edition`_ using Composer: - - .. code-block:: bash - - $ php composer.phar create-project symfony/framework-standard-edition path/ '~2.5' - - .. tip:: - - Add the ``-vvv`` flag to see everything that Composer is doing - this is - especially useful on a slow connection where it may seem that nothing is - happening. - - Composer will now download the Standard Distribution along with all of the - required vendor libraries. For more information about downloading Symfony using - Composer, see `Installing Symfony using Composer`_. +Once your project is running, just follow these simple steps: #. Initialize your Git repository: @@ -90,16 +76,6 @@ to learn more about how to configure and develop inside your application. .. include:: _vendor_deps.rst.inc -Vendors and Submodules -~~~~~~~~~~~~~~~~~~~~~~ - -Instead of using the ``composer.json`` system for managing your vendor -libraries, you may instead choose to use native `git submodules`_. There -is nothing wrong with this approach, though the ``composer.json`` system -is the official way to solve this problem and probably much easier to -deal with. Unlike Git submodules, Composer is smart enough to calculate -which libraries depend on which other libraries. - Storing your Project on a remote Server --------------------------------------- diff --git a/cookbook/workflow/new_project_svn.rst b/cookbook/workflow/new_project_svn.rst index c8140d1f09a..f7152980999 100644 --- a/cookbook/workflow/new_project_svn.rst +++ b/cookbook/workflow/new_project_svn.rst @@ -46,28 +46,27 @@ widespread standard structure: Initial Project Setup --------------------- -To get started, you'll need to download Symfony and get the basic Subversion setup: +To get started, you'll need to download Symfony and get the basic Subversion setup. +First, download and get your Symfony project running by following the +:doc:`Installation ` chapter. -1. Download the `Symfony Standard Edition`_ with or without vendors. +Once you have your new project directory and things are working, follow along +with these steps: -2. Unzip/untar the distribution. It will create a folder called Symfony with - your new project structure, config files, etc. Rename it to whatever you - like. - -3. Checkout the Subversion repository that will host this project. Suppose +1. Checkout the Subversion repository that will host this project. Suppose it is hosted on `Google code`_ and called ``myproject``: .. code-block:: bash $ svn checkout http://myproject.googlecode.com/svn/trunk myproject -4. Copy the Symfony project files in the Subversion folder: +2. Copy the Symfony project files in the Subversion folder: .. code-block:: bash $ mv Symfony/* myproject/ -5. Now, set the ignore rules. Not everything *should* be stored in your Subversion +3. Now, set the ignore rules. Not everything *should* be stored in your Subversion repository. Some files (like the cache) are generated and others (like the database configuration) are meant to be customized on each machine. This makes use of the ``svn:ignore`` property, so that specific files can @@ -88,21 +87,21 @@ To get started, you'll need to download Symfony and get the basic Subversion set $ svn ci -m "commit basic Symfony ignore list (vendor, app/bootstrap*, app/config/parameters.yml, app/cache/*, app/logs/*, web/bundles)" -6. The rest of the files can now be added and committed to the project: +4. The rest of the files can now be added and committed to the project: .. code-block:: bash $ svn add --force . $ svn ci -m "add basic Symfony Standard 2.X.Y" -7. Copy ``app/config/parameters.yml`` to ``app/config/parameters.yml.dist``. +5. Copy ``app/config/parameters.yml`` to ``app/config/parameters.yml.dist``. The ``parameters.yml`` file is ignored by svn (see above) so that machine-specific settings like database passwords aren't committed. By creating the ``parameters.yml.dist`` file, new developers can quickly clone the project, copy this file to ``parameters.yml``, customize it, and start developing. -8. Finally, download all of the third-party vendor libraries by +6. Finally, download all of the third-party vendor libraries by executing Composer. For details, see :ref:`installation-updating-vendors`. .. tip:: From 1fa0f59c45bcf68ef8cf2609e392b7120f770473 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 19:05:10 -0500 Subject: [PATCH 501/835] Fixing missing services key --- cookbook/configuration/pdo_session_storage.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index 2af00ac6af0..7955873d878 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -140,12 +140,13 @@ of your project's data, you can use the connection settings from the .. code-block:: yaml - pdo: - class: PDO - arguments: - - "mysql:host=%database_host%;port=%database_port%;dbname=%database_name%" - - "%database_user%" - - "%database_password%" + services: + pdo: + class: PDO + arguments: + - "mysql:host=%database_host%;port=%database_port%;dbname=%database_name%" + - "%database_user%" + - "%database_password%" .. code-block:: xml From 512632ecaf4afd4fec3b71ead1ccdc754d7076af Mon Sep 17 00:00:00 2001 From: Carlos Buenosvinos Date: Wed, 3 Dec 2014 23:23:37 +0100 Subject: [PATCH 502/835] When explaining how to install dependencies for running unit tests, there are references to "--dev" composer parameter that is the current default value. --- contributing/code/tests.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index 709c88a8dc4..c876a00d559 100644 --- a/contributing/code/tests.rst +++ b/contributing/code/tests.rst @@ -41,7 +41,7 @@ Step 2: Install vendors .. code-block:: bash - $ php composer.phar --dev install + $ php composer.phar install .. note:: @@ -56,14 +56,14 @@ Step 2: Install vendors .. code-block:: bash $ php installer - $ php composer.phar --dev install + $ php composer.phar install After installation, you can update the vendors to their latest version with the follow command: .. code-block:: bash - $ php composer.phar --dev update + $ php composer.phar update Running ------- From 3e51551799fe883c63fcc37669a62883d64677b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Paul=20Bentz?= Date: Sat, 29 Nov 2014 21:53:04 +0100 Subject: [PATCH 503/835] Update pdo_session_storage.rst Changes after the 2.6.0 release keeping the names of the values as they where. --- .../configuration/pdo_session_storage.rst | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index be5e606d2ef..e14ecf0ad74 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -26,10 +26,11 @@ configuration format of your choice): parameters: pdo.db_options: - db_table: session - db_id_col: session_id - db_data_col: session_value - db_time_col: session_time + db_table: session + db_id_col: session_id + db_data_col: session_data + db_time_col: session_time + db_lifetime_col: session_lifetime services: pdo: @@ -56,8 +57,9 @@ configuration format of your choice): session session_id - session_value + session_data session_time + session_lifetime @@ -93,10 +95,11 @@ configuration format of your choice): )); $container->setParameter('pdo.db_options', array( - 'db_table' => 'session', - 'db_id_col' => 'session_id', - 'db_data_col' => 'session_value', - 'db_time_col' => 'session_time', + 'db_table' => 'session', + 'db_id_col' => 'session_id', + 'db_data_col' => 'session_data', + 'db_time_col' => 'session_time', + 'db_lifetime_col' => 'session_lifetime', )); $pdoDefinition = new Definition('PDO', array( @@ -114,9 +117,10 @@ configuration format of your choice): $container->setDefinition('session.handler.pdo', $storageDefinition); * ``db_table``: The name of the session table in your database -* ``db_id_col``: The name of the id column in your session table (VARCHAR(255) or larger) -* ``db_data_col``: The name of the value column in your session table (TEXT or CLOB) +* ``db_id_col``: The name of the id column in your session table (VARCHAR(128)) +* ``db_data_col``: The name of the value column in your session table (BLOB) * ``db_time_col``: The name of the time column in your session table (INTEGER) +* ``db_lifetime_col``: The name of the lifetime column in your session table (INTEGER) Sharing your Database Connection Information -------------------------------------------- @@ -168,11 +172,11 @@ following (MySQL): .. code-block:: sql CREATE TABLE `session` ( - `session_id` varchar(255) NOT NULL, - `session_value` text NOT NULL, - `session_time` int(11) NOT NULL, - PRIMARY KEY (`session_id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + `session_id` VARBINARY(128) NOT NULL PRIMARY KEY, + `session_data` BLOB NOT NULL, + `session_time` INTEGER UNSIGNED NOT NULL, + `session_lifetime` MEDIUMINT NOT NULL + ) COLLATE utf8_bin, ENGINE = InnoDB; PostgreSQL ~~~~~~~~~~ @@ -182,10 +186,10 @@ For PostgreSQL, the statement should look like this: .. code-block:: sql CREATE TABLE session ( - session_id character varying(255) NOT NULL, - session_value text NOT NULL, - session_time integer NOT NULL, - CONSTRAINT session_pkey PRIMARY KEY (session_id) + session_id VARCHAR(128) NOT NULL PRIMARY KEY, + session_data BYTEA NOT NULL, + session_time INTEGER NOT NULL, + session_lifetime INTEGER NOT NULL ); Microsoft SQL Server @@ -197,8 +201,9 @@ For MSSQL, the statement might look like the following: CREATE TABLE [dbo].[session]( [session_id] [nvarchar](255) NOT NULL, - [session_value] [ntext] NOT NULL, + [session_data] [ntext] NOT NULL, [session_time] [int] NOT NULL, + [session_lifetime] [int] NOT NULL, PRIMARY KEY CLUSTERED( [session_id] ASC ) WITH ( From fd8b552efeefcc808d9b3cdbbd6f273b716aa735 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 19:43:47 -0500 Subject: [PATCH 504/835] Adding details about the changes to the PdoSessionHandler in 2.6 --- .../configuration/pdo_session_storage.rst | 117 +++++++++++++----- 1 file changed, 83 insertions(+), 34 deletions(-) diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index e14ecf0ad74..6603da8f8a6 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -4,6 +4,12 @@ How to Use PdoSessionHandler to Store Sessions in the Database ============================================================== +.. caution:: + + There was a backwards-compatability break in Symfony 2.6: the database + schema changed slightly. See :ref:`Symfony 2.6 Changes ` + for details. + The default Symfony session storage writes the session information to file(s). Most medium to large websites use a database to store the session values instead of files, because databases are easier to use and scale in a @@ -24,18 +30,11 @@ configuration format of your choice): # ... handler_id: session.handler.pdo - parameters: - pdo.db_options: - db_table: session - db_id_col: session_id - db_data_col: session_data - db_time_col: session_time - db_lifetime_col: session_lifetime - services: pdo: class: PDO arguments: + # see below for how to use your existing DB config dsn: "mysql:dbname=mydatabase" user: myuser password: mypassword @@ -44,7 +43,7 @@ configuration format of your choice): session.handler.pdo: class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler - arguments: ["@pdo", "%pdo.db_options%"] + arguments: ["@pdo"] .. code-block:: xml @@ -53,16 +52,6 @@ configuration format of your choice):
    - - - session - session_id - session_data - session_time - session_lifetime - - - mysql:dbname=mydatabase @@ -76,7 +65,6 @@ configuration format of your choice): - %pdo.db_options% @@ -94,14 +82,6 @@ configuration format of your choice): ), )); - $container->setParameter('pdo.db_options', array( - 'db_table' => 'session', - 'db_id_col' => 'session_id', - 'db_data_col' => 'session_data', - 'db_time_col' => 'session_time', - 'db_lifetime_col' => 'session_lifetime', - )); - $pdoDefinition = new Definition('PDO', array( 'mysql:dbname=mydatabase', 'myuser', @@ -112,15 +92,70 @@ configuration format of your choice): $storageDefinition = new Definition('Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler', array( new Reference('pdo'), - '%pdo.db_options%', )); $container->setDefinition('session.handler.pdo', $storageDefinition); -* ``db_table``: The name of the session table in your database -* ``db_id_col``: The name of the id column in your session table (VARCHAR(128)) -* ``db_data_col``: The name of the value column in your session table (BLOB) -* ``db_time_col``: The name of the time column in your session table (INTEGER) -* ``db_lifetime_col``: The name of the lifetime column in your session table (INTEGER) +Configuring the Table and Column Names +-------------------------------------- + +This will expect a ``sessions`` table with a number of different columns. +The table name, and all of the column names, can be configured by passing +a second array argument to ``PdoSessionHandler``: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/config.yml + services: + # ... + session.handler.pdo: + class: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler + arguments: + - "@pdo" + - { 'db_table': 'sessions'} + + .. code-block:: xml + + + + + + + + + sessions + + + + + .. code-block:: php + + // app/config/config.php + // ... + + $storageDefinition = new Definition('Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler', array( + new Reference('pdo'), + array('db_table' => 'session') + )); + $container->setDefinition('session.handler.pdo', $storageDefinition); + +.. versionadded:: 2.6 + The ``db_lifetime_col`` was introduced in Symfony 2.6 This column did + not exist previously. + +The following things can be configured: + +* ``db_table``: (default ``session``) The name of the session table in your + database; +* ``db_id_col``: (default ``sess_id``) The name of the id column in your + session table (VARCHAR(128)); +* ``db_data_col``: (default ``sess_data``) The name of the value column in + your session table (BLOB); +* ``db_time_col``: (default ``sess_time``) The name of the time column in + your session table (INTEGER); +* ``db_lifetime_col``: (default ``sess_lifetime``) The name of the lifetime + column in your session table (INTEGER). Sharing your Database Connection Information -------------------------------------------- @@ -163,6 +198,20 @@ of your project's data, you can use the connection settings from the Example SQL Statements ---------------------- +.. _pdo-session-handle-26-changes: + +.. sidebar:: Schema Changes needed when Upgrading to Symfony 2.6 + + If you use the `PdoSessionHandler` prior to Symfony 2.6 and upgrade, you'll + need to make a few changes to your session table: + + * A new session lifetime (``sess_lifetime`` by default) integer column + needs to be added; + * The data column (``sess_data`` by default) needs to be changed to a + BLOG type. + + Check the SQL statements below for more details. + MySQL ~~~~~ From 2f974bb1374edd3f97c69b760ef4b3ea48007c90 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 19:46:29 -0500 Subject: [PATCH 505/835] Updating statements, now that we're not overriding the names --- .../configuration/pdo_session_storage.rst | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index 6603da8f8a6..26bb6fc784a 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -221,10 +221,10 @@ following (MySQL): .. code-block:: sql CREATE TABLE `session` ( - `session_id` VARBINARY(128) NOT NULL PRIMARY KEY, - `session_data` BLOB NOT NULL, - `session_time` INTEGER UNSIGNED NOT NULL, - `session_lifetime` MEDIUMINT NOT NULL + `sess_id` VARBINARY(128) NOT NULL PRIMARY KEY, + `sess_data` BLOB NOT NULL, + `sess_time` INTEGER UNSIGNED NOT NULL, + `sess_lifetime` MEDIUMINT NOT NULL ) COLLATE utf8_bin, ENGINE = InnoDB; PostgreSQL @@ -235,10 +235,10 @@ For PostgreSQL, the statement should look like this: .. code-block:: sql CREATE TABLE session ( - session_id VARCHAR(128) NOT NULL PRIMARY KEY, - session_data BYTEA NOT NULL, - session_time INTEGER NOT NULL, - session_lifetime INTEGER NOT NULL + sess_id VARCHAR(128) NOT NULL PRIMARY KEY, + sess_data BYTEA NOT NULL, + sess_time INTEGER NOT NULL, + sess_lifetime INTEGER NOT NULL ); Microsoft SQL Server @@ -249,12 +249,12 @@ For MSSQL, the statement might look like the following: .. code-block:: sql CREATE TABLE [dbo].[session]( - [session_id] [nvarchar](255) NOT NULL, - [session_data] [ntext] NOT NULL, - [session_time] [int] NOT NULL, - [session_lifetime] [int] NOT NULL, + [sess_id] [nvarchar](255) NOT NULL, + [sess_data] [ntext] NOT NULL, + [sess_time] [int] NOT NULL, + [sess_lifetime] [int] NOT NULL, PRIMARY KEY CLUSTERED( - [session_id] ASC + [sess_id] ASC ) WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, From 5c0ceff246677d6321d02ac60a9d6d84c7a70e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20=C4=8Cern=C3=BD?= Date: Mon, 8 Dec 2014 11:44:17 +0000 Subject: [PATCH 506/835] Change refering block name from content to body | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | all | Fixed tickets | --- quick_tour/the_view.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_view.rst b/quick_tour/the_view.rst index a8741123837..2de45855636 100644 --- a/quick_tour/the_view.rst +++ b/quick_tour/the_view.rst @@ -126,7 +126,7 @@ Open the ``app/Resources/views/base.html.twig`` file that corresponds to the The ``{% block %}`` tags tell the template engine that a child template may override those portions of the template. In this example, the ``index.html.twig`` -template overrides the ``content`` block, but not the ``title`` block, which will +template overrides the ``body`` block, but not the ``title`` block, which will display the default content defined in the ``base.html.twig`` template. Using Tags, Filters, and Functions From 9d77a6d22d375f39b4130bde7ea43d37cfd06b15 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 9 Dec 2014 22:27:50 +0100 Subject: [PATCH 507/835] filesystem headlines match method names --- components/filesystem.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/components/filesystem.rst b/components/filesystem.rst index 16e7d27bab2..a21fedf1ad0 100644 --- a/components/filesystem.rst +++ b/components/filesystem.rst @@ -47,7 +47,7 @@ endpoint for filesystem operations:: string, an array or any object implementing :phpclass:`Traversable` as the target argument. -Mkdir +mkdir ~~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::mkdir` creates a directory. @@ -61,7 +61,7 @@ On posix filesystems, directories are created with a default mode value You can pass an array or any :phpclass:`Traversable` object as the first argument. -Exists +exists ~~~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::exists` checks for the @@ -78,7 +78,7 @@ presence of all files or directories and returns ``false`` if a file is missing: You can pass an array or any :phpclass:`Traversable` object as the first argument. -Copy +copy ~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::copy` is used to copy @@ -92,7 +92,7 @@ the third boolean argument:: // image.jpg will be overridden $fs->copy('image-ICC.jpg', 'image.jpg', true); -Touch +touch ~~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::touch` sets access and @@ -111,7 +111,7 @@ your own with the second argument. The third argument is the access time:: You can pass an array or any :phpclass:`Traversable` object as the first argument. -Chown +chown ~~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::chown` is used to change @@ -127,7 +127,7 @@ the owner of a file. The third argument is a boolean recursive option:: You can pass an array or any :phpclass:`Traversable` object as the first argument. -Chgrp +chgrp ~~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::chgrp` is used to change @@ -143,7 +143,7 @@ the group of a file. The third argument is a boolean recursive option:: You can pass an array or any :phpclass:`Traversable` object as the first argument. -Chmod +chmod ~~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::chmod` is used to change @@ -159,7 +159,7 @@ the mode of a file. The fourth argument is a boolean recursive option:: You can pass an array or any :phpclass:`Traversable` object as the first argument. -Remove +remove ~~~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::remove` is used to remove @@ -172,7 +172,7 @@ files, symlinks, directories easily:: You can pass an array or any :phpclass:`Traversable` object as the first argument. -Rename +rename ~~~~~~ :method:`Symfony\\Component\\Filesystem\\Filesystem::rename` is used to rename From fc43571b4083fb42ff43fbf50588b1b73b7707c6 Mon Sep 17 00:00:00 2001 From: xavren Date: Wed, 10 Dec 2014 16:50:37 -0500 Subject: [PATCH 508/835] [OptionsResolver] Fix Namespace link --- components/options_resolver.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 6e239e2f421..52ba3c647dc 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -360,7 +360,7 @@ pretend that the ``host`` should always start with ``http://``. To do that, you can write normalizers. These closures will be executed after all options are passed and should return the normalized value. You can configure these normalizers by calling -:method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizers`:: +:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setNormalizers`:: // ... protected function setDefaultOptions(OptionsResolverInterface $resolver) From 87582a49eb2bcc79eb4ec117a6238cac89348a0d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 23 Nov 2014 13:09:38 +0100 Subject: [PATCH 509/835] update contribution guide for 2.7/3.0 --- contributing/code/patches.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/contributing/code/patches.rst b/contributing/code/patches.rst index 62a2d051b30..033d19765a3 100644 --- a/contributing/code/patches.rst +++ b/contributing/code/patches.rst @@ -105,9 +105,13 @@ Choose the right Branch ~~~~~~~~~~~~~~~~~~~~~~~ Before working on a patch, you must determine on which branch you need to -work. The branch should be based on the ``master`` branch if you want to add a -new feature. But if you want to fix a bug, use the oldest but still maintained -version of Symfony where the bug happens (like ``2.3``). +work: + +* ``2.3``, if you are fixing a bug for an existing feature (you may have + to choose a higher branch if the feature you are fixing was introduced + in a later version); +* ``2.7``, if you are adding a new feature which is backward compatible; +* ``master``, if you are adding a new and backward incompatible feature. .. note:: From 4801a3c7d1396abcbbb8f36028ee70d9b461a776 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 5 Dec 2014 17:22:34 +0100 Subject: [PATCH 510/835] [ExpressionLanguage] add missing argument --- components/expression_language/caching.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/expression_language/caching.rst b/components/expression_language/caching.rst index 3ea35752e2f..d8724d49fbf 100644 --- a/components/expression_language/caching.rst +++ b/components/expression_language/caching.rst @@ -51,10 +51,10 @@ Using Parsed and Serialized Expressions Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and ``SerializedParsedExpression``:: - use Symfony\Component\ExpressionLanguage\ParsedExpression; // ... - $expression = new ParsedExpression($language->parse('1 + 4')); + // the parse() method returns a ParsedExpression + $expression = $language->parse('1 + 4', array()); echo $language->evaluate($expression); // prints 5 @@ -64,7 +64,7 @@ Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and // ... $expression = new SerializedParsedExpression( - serialize($language->parse('1 + 4')) + serialize($language->parse('1 + 4', array())) ); echo $language->evaluate($expression); // prints 5 From 1c2416341032220389e89fd0c5f77adcf11f162e Mon Sep 17 00:00:00 2001 From: xavren Date: Thu, 11 Dec 2014 17:21:44 -0500 Subject: [PATCH 511/835] [OptionsResolver] Fix namespace --- components/options_resolver.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 8cd6d3a2998..ef9a0f18bd2 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -427,7 +427,7 @@ Sometimes, option values need to be normalized before you can use them. For instance, assume that the ``host`` should always start with ``http://``. To do that, you can write normalizers. Normalizers are executed after validating an option. You can configure a normalizer by calling -:method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizer`:: +:method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setNormalizer`:: // ... class Mailer @@ -449,9 +449,9 @@ option. You can configure a normalizer by calling } .. versionadded:: 2.6 - The method :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizer` + The method :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setNormalizer` was introduced in Symfony 2.6. Before, you had to use - :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setNormalizers`. + :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setNormalizers`. The normalizer receives the actual ``$value`` and returns the normalized form. You see that the closure also takes an ``$options`` parameter. This is useful @@ -588,9 +588,9 @@ comes from the default:: } .. versionadded:: 2.6 - The method :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setDefined` + The method :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefined` was introduced in Symfony 2.6. Before, you had to use - :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::setOptional`. + :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setOptional`. You can use :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefined` to define an option without setting a default value. Then the option will only @@ -644,8 +644,8 @@ options in one go:: } .. versionadded:: 2.6 - The method :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::isDefined` - and :method:`Symfony\\Components\\OptionsResolver\\OptionsResolver::getDefinedOptions` + The method :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isDefined` + and :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::getDefinedOptions` were introduced in Symfony 2.6. The methods :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::isDefined` From 74b75deff6dee4dc0f283bb5ec14778674b0a07a Mon Sep 17 00:00:00 2001 From: Stepan Anchugov Date: Thu, 11 Dec 2014 12:45:26 +0500 Subject: [PATCH 512/835] Added '-ing' title ending to unify titles look Currently all of the titles in Console section use gerund, but not this one. --- components/console/console_arguments.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/console/console_arguments.rst b/components/console/console_arguments.rst index 0b998961c32..45f4b7d34c1 100644 --- a/components/console/console_arguments.rst +++ b/components/console/console_arguments.rst @@ -1,8 +1,10 @@ .. index:: single: Console; Console arguments -Understand how Console Arguments are Handled -============================================ +.. _understand-how-console-arguments-are-handled: + +Understanding how Console Arguments Are Handled +=============================================== It can be difficult to understand the way arguments are handled by the console application. The Symfony Console application, like many other CLI utility tools, follows the behavior From 7b16d3bda87cc95cb9c088ae3450230c23eed308 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 11 Dec 2014 18:11:08 -0500 Subject: [PATCH 513/835] [#4625] Removing link added for BC, because this is the top of the page anyways --- components/console/console_arguments.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/console/console_arguments.rst b/components/console/console_arguments.rst index 45f4b7d34c1..fa56c141353 100644 --- a/components/console/console_arguments.rst +++ b/components/console/console_arguments.rst @@ -1,8 +1,6 @@ .. index:: single: Console; Console arguments -.. _understand-how-console-arguments-are-handled: - Understanding how Console Arguments Are Handled =============================================== From 4b8e805675a228156bbf9a5094c4e56b7dd4301f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 12 Dec 2014 10:48:23 +0100 Subject: [PATCH 514/835] Restored the original section title --- best_practices/creating-the-project.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index ec0c80772a6..c9cfcea6f0a 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -1,8 +1,8 @@ Creating the Project ==================== -Symfony Installer ------------------ +Installing Symfony +------------------ In the past, Symfony projects were created with `Composer`_, the dependency manager for PHP applications. However, the current recommendation is to use the **Symfony From 6f562704c11ca524f4f07f4ad67102e28289b157 Mon Sep 17 00:00:00 2001 From: Marcin Sekalski Date: Fri, 12 Dec 2014 21:09:42 +0000 Subject: [PATCH 515/835] Add missing autoload include in basic application example --- components/console/introduction.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index add481437d3..767915ac0ab 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -88,6 +88,8 @@ an ``Application`` and adds commands to it:: Date: Fri, 12 Dec 2014 15:14:47 +0100 Subject: [PATCH 516/835] fixed StringExpressionLanguageProvider code example #4636 --- components/expression_language/extending.rst | 25 +++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst index 9676452219e..4aded7c5aeb 100644 --- a/components/expression_language/extending.rst +++ b/components/expression_language/extending.rst @@ -77,17 +77,20 @@ register. class StringExpressionLanguageProvider implements ExpressionFunctionProviderInterface { - return array( - new ExpressionFunction('lowercase', function ($str) { - return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); - }, function ($arguments, $str) { - if (!is_string($str)) { - return $str; - } - - return strtolower($str); - }); - ); + public function getFunctions() + { + return array( + new ExpressionFunction('lowercase', function ($str) { + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); + }, function ($arguments, $str) { + if (!is_string($str)) { + return $str; + } + + return strtolower($str); + }), + ); + } } You can register providers using From a51c623f45b33b69a953105dae4d6fba6df39703 Mon Sep 17 00:00:00 2001 From: Wouter J Date: Sat, 13 Dec 2014 15:28:10 +0100 Subject: [PATCH 517/835] Minor grammar fix --- components/http_foundation/session_configuration.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index 4a28190e795..c2608110621 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -239,7 +239,7 @@ Save Handler Proxy ~~~~~~~~~~~~~~~~~~ A Save Handler Proxy is basically a wrapper around a Save Handler that was -introduced to support seamlessly the migration from PHP 5.3 to PHP 5.4+. It +introduced to seamlessly support the migration from PHP 5.3 to PHP 5.4+. It further creates an extension point from where custom logic can be added that works independently of which handler is being wrapped inside. From de8b547732dc171676df11bdd8a705e471b0379f Mon Sep 17 00:00:00 2001 From: thewilkybarkid Date: Sat, 13 Dec 2014 16:32:21 +0000 Subject: [PATCH 518/835] Remove note that's no longer the case --- cookbook/configuration/external_parameters.rst | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/cookbook/configuration/external_parameters.rst b/cookbook/configuration/external_parameters.rst index 58a72fd898c..603164b8c93 100644 --- a/cookbook/configuration/external_parameters.rst +++ b/cookbook/configuration/external_parameters.rst @@ -14,12 +14,12 @@ Environment Variables --------------------- Symfony will grab any environment variable prefixed with ``SYMFONY__`` and -set it as a parameter in the service container. Some transformations are +set it as a parameter in the service container. Some transformations are applied to the resulting parameter name: * ``SYMFONY__`` prefix is removed; * Parameter name is lowercased; -* Double underscores are replaced with a period, as a period is not +* Double underscores are replaced with a period, as a period is not a valid character in an environment variable name. For example, if you're using Apache, environment variables can be set using @@ -98,14 +98,6 @@ You can now reference these parameters wherever you need them. ) )); -.. note:: - - Even in debug mode, setting or changing an environment variable - requires your cache to be cleared to make the parameter available. - In debug mode, this is required since only a change to a configuration - file that is loaded by Symfony triggers your configuration to be - re-evaluated. - Constants --------- From bce4c1689092125f09d340d02cf7bd0c743af5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?W=C5=82odzimierz=20Gajda?= Date: Sat, 13 Dec 2014 10:11:57 +0100 Subject: [PATCH 519/835] How to override vendor directory location --- .../configuration/override_dir_structure.rst | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/cookbook/configuration/override_dir_structure.rst b/cookbook/configuration/override_dir_structure.rst index c24c39e429b..2fcc258db25 100644 --- a/cookbook/configuration/override_dir_structure.rst +++ b/cookbook/configuration/override_dir_structure.rst @@ -154,3 +154,40 @@ the ``extra.symfony-web-dir`` option in the ``composer.json`` file: $ php app/console cache:clear --env=prod $ php app/console assetic:dump --env=prod --no-debug + +Override the ``vendor`` Directory +--------------------------------- + +To override the ``vendor`` directory you have to introduce changes in the +following files: + +* ``app/autoload.php`` +* ``composer.json`` + +The change in the ``composer.json`` takes the form: + +.. code-block:: json + + { + ... + "config": { + "bin-dir": "bin", + "vendor-dir": "/some/dir/vendor" + }, + ... + } + +In ``app/autoload.php`` you need to modify the path leading to ``vendor/autoload.php`` +file:: + + // app/autoload.php + /** + * @var ClassLoader $loader + */ + $loader = require '/some/dir/vendor/autoload.php'; + +.. tip:: + + This modification can be of interest if you work using virtual environment + and cannot use NFS. For example, when running Symfony app using Vagrant/VirtualBox + guest operating system. From d5be25ef3c89a0f55078f0cf03b5783ffaf2c3f5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 14 Dec 2014 16:23:25 +0100 Subject: [PATCH 520/835] Documented the characters that provoke a YAML escaping string --- components/yaml/yaml_format.rst | 51 +++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index a86bcd05030..9419526c0cc 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -23,6 +23,9 @@ The syntax for scalars is similar to the PHP syntax. Strings ~~~~~~~ +Strings in YAML can be wrapped both in single and double quotes. In some cases, +they can also be unquoted: + .. code-block:: yaml A string in YAML @@ -31,26 +34,50 @@ Strings 'A singled-quoted string in YAML' -.. tip:: +.. code-block:: yaml - In a single quoted string, a single quote ``'`` must be doubled: + "A double-quoted string in YAML" - .. code-block:: yaml +Quoted styles are useful when a string starts or ends with one or more +relevant spaces, because unquoted strings are trimmed on both ends when parsing +their contents. - 'A single quote '' in a single-quoted string' +When using single-quoted strings, any single quote ``'`` inside its contents +must be doubled to escape it: -.. code-block:: yaml + .. code-block:: yaml - "A double-quoted string in YAML\n" + 'A single quote '' inside a single-quoted string' -Quoted styles are useful when a string starts or ends with one or more -relevant spaces. +The double-quoted style provides a way to express arbitrary strings, by +using ``\`` escape sequences. It is very useful when you need to embed a +``\n`` or a Unicode character in a string. + +.. code-block:: yaml -.. tip:: + "A double-quoted string in YAML\n" - The double-quoted style provides a way to express arbitrary strings, by - using ``\`` escape sequences. It is very useful when you need to embed a - ``\n`` or a unicode character in a string. +If the string contains any of the following characters, it must be escaped with +single quotes: + +===== ===== ===== ===== ===== +``:`` ``{`` ``}`` ``[`` ``]`` +``,`` ``&`` ``*`` ``#`` ``?`` +``|`` ``-`` ``<`` ``>`` ``=`` +``!`` ``%`` ``@`` ``\``` +===== ===== ===== ===== ===== + +If the string contains any of the following control characters, it must be +escaped with double quotes. In addition, the escaping must use a double slash +``\\`` to avoid parsing issues: + +======== ======== ======== ======== ======== ======== ======== ======== +``\0`` ``\x01`` ``\x02`` ``\x03`` ``\x04`` ``\x05`` ``\x06`` ``\a`` +``\b`` ``\t`` ``\n`` ``\v`` ``\f`` ``\r`` ``\x0e`` ``\x0f`` +``\x10`` ``\x11`` ``\x12`` ``\x13`` ``\x14`` ``\x15`` ``\x16`` ``\x17`` +``\x18`` ``\x19`` ``\x1a`` ``\e`` ``\x1c`` ``\x1d`` ``\x1e`` ``\x1f`` +``\N`` ``\_`` ``\L`` ``\P`` +======== ======== ======== ======== ======== ======== ======== ======== When a string contains line breaks, you can use the literal style, indicated by the pipe (``|``), to indicate that the string will span several lines. In From 07f68430a57b05f3f0a11986fa7113e3f51e5362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?W=C5=82odzimierz=20Gajda?= Date: Sun, 14 Dec 2014 16:32:06 +0100 Subject: [PATCH 521/835] How to override vendor directory location - fix --- cookbook/configuration/override_dir_structure.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cookbook/configuration/override_dir_structure.rst b/cookbook/configuration/override_dir_structure.rst index 2fcc258db25..8a701d299c4 100644 --- a/cookbook/configuration/override_dir_structure.rst +++ b/cookbook/configuration/override_dir_structure.rst @@ -181,9 +181,7 @@ In ``app/autoload.php`` you need to modify the path leading to ``vendor/autoload file:: // app/autoload.php - /** - * @var ClassLoader $loader - */ + $loader = require '/some/dir/vendor/autoload.php'; .. tip:: From caaa2728fd2b8eabd26940fba6550e4968e3dcad Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 14 Dec 2014 17:02:12 +0100 Subject: [PATCH 522/835] Addressed all the comments made by Wouter --- components/yaml/yaml_format.rst | 84 ++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index 9419526c0cc..c45a45117a2 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -30,12 +30,8 @@ they can also be unquoted: A string in YAML -.. code-block:: yaml - 'A singled-quoted string in YAML' -.. code-block:: yaml - "A double-quoted string in YAML" Quoted styles are useful when a string starts or ends with one or more @@ -45,9 +41,32 @@ their contents. When using single-quoted strings, any single quote ``'`` inside its contents must be doubled to escape it: - .. code-block:: yaml +.. code-block:: yaml - 'A single quote '' inside a single-quoted string' + 'A single quote '' inside a single-quoted string' + +If the string contains any of the following characters, it must be escaped with +single quotes: + +* ``:`` +* ``{`` +* ``}`` +* ``[`` +* ``]`` +* ``,`` +* ``&`` +* ``*`` +* ``#`` +* ``?`` +* ``|`` +* ``-`` +* ``<`` +* ``>`` +* ``=`` +* ``!`` +* ``%`` +* ``@`` +* ``\``` The double-quoted style provides a way to express arbitrary strings, by using ``\`` escape sequences. It is very useful when you need to embed a @@ -57,27 +76,46 @@ using ``\`` escape sequences. It is very useful when you need to embed a "A double-quoted string in YAML\n" -If the string contains any of the following characters, it must be escaped with -single quotes: - -===== ===== ===== ===== ===== -``:`` ``{`` ``}`` ``[`` ``]`` -``,`` ``&`` ``*`` ``#`` ``?`` -``|`` ``-`` ``<`` ``>`` ``=`` -``!`` ``%`` ``@`` ``\``` -===== ===== ===== ===== ===== - If the string contains any of the following control characters, it must be escaped with double quotes. In addition, the escaping must use a double slash ``\\`` to avoid parsing issues: -======== ======== ======== ======== ======== ======== ======== ======== -``\0`` ``\x01`` ``\x02`` ``\x03`` ``\x04`` ``\x05`` ``\x06`` ``\a`` -``\b`` ``\t`` ``\n`` ``\v`` ``\f`` ``\r`` ``\x0e`` ``\x0f`` -``\x10`` ``\x11`` ``\x12`` ``\x13`` ``\x14`` ``\x15`` ``\x16`` ``\x17`` -``\x18`` ``\x19`` ``\x1a`` ``\e`` ``\x1c`` ``\x1d`` ``\x1e`` ``\x1f`` -``\N`` ``\_`` ``\L`` ``\P`` -======== ======== ======== ======== ======== ======== ======== ======== +* ``\0`` +* ``\x01`` +* ``\x02`` +* ``\x03`` +* ``\x04`` +* ``\x05`` +* ``\x06`` +* ``\a`` +* ``\b`` +* ``\t`` +* ``\n`` +* ``\v`` +* ``\f`` +* ``\r`` +* ``\x0e`` +* ``\x0f`` +* ``\x10`` +* ``\x11`` +* ``\x12`` +* ``\x13`` +* ``\x14`` +* ``\x15`` +* ``\x16`` +* ``\x17`` +* ``\x18`` +* ``\x19`` +* ``\x1a`` +* ``\e`` +* ``\x1c`` +* ``\x1d`` +* ``\x1e`` +* ``\x1f`` +* ``\N`` +* ``\_`` +* ``\L`` +* ``\P`` When a string contains line breaks, you can use the literal style, indicated by the pipe (``|``), to indicate that the string will span several lines. In From f58db719fc50f3b78a49b8cd046d5dd2d9dc87fc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 14 Dec 2014 19:09:26 +0100 Subject: [PATCH 523/835] Fixed section headers --- best_practices/creating-the-project.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index c9cfcea6f0a..ea0a2c25710 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -8,7 +8,7 @@ In the past, Symfony projects were created with `Composer`_, the dependency mana for PHP applications. However, the current recommendation is to use the **Symfony Installer**, which has to be installed before creating your first project. -Linux and Mac OS X systems +Linux and Mac OS X Systems ~~~~~~~~~~~~~~~~~~~~~~~~~~ Open your command console and execute the following: @@ -22,7 +22,7 @@ Open your command console and execute the following: Now you can execute the Symfony Installer as a global system command called ``symfony``. -Windows systems +Windows Systems ~~~~~~~~~~~~~~~ Open your command console and execute the following: From ee06eec36bf0e251aecc950295bd7925075466c6 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 14 Dec 2014 21:54:14 +0100 Subject: [PATCH 524/835] update ordered list syntax --- book/translation.rst | 6 +-- components/routing/introduction.rst | 14 +++--- components/serializer.rst | 6 +-- components/translation/introduction.rst | 6 +-- contributing/code/security.rst | 46 +++++++++---------- cookbook/deployment/heroku.rst | 4 +- .../custom_authentication_provider.rst | 6 +-- cookbook/workflow/new_project_svn.rst | 16 +++---- 8 files changed, 52 insertions(+), 52 deletions(-) diff --git a/book/translation.rst b/book/translation.rst index 255cd45fd55..281fadd8bc5 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -394,13 +394,13 @@ Imagine that the user's locale is ``fr_FR`` and that you're translating the key ``Symfony is great``. To find the French translation, Symfony actually checks translation resources for several different locales: -1. First, Symfony looks for the translation in a ``fr_FR`` translation resource +#. First, Symfony looks for the translation in a ``fr_FR`` translation resource (e.g. ``messages.fr_FR.xliff``); -2. If it wasn't found, Symfony looks for the translation in a ``fr`` translation +#. If it wasn't found, Symfony looks for the translation in a ``fr`` translation resource (e.g. ``messages.fr.xliff``); -3. If the translation still isn't found, Symfony uses the ``fallback`` configuration +#. If the translation still isn't found, Symfony uses the ``fallback`` configuration parameter, which defaults to ``en`` (see `Configuration`_). .. _book-translation-user-locale: diff --git a/components/routing/introduction.rst b/components/routing/introduction.rst index e751b0a36d2..85bcd1065de 100644 --- a/components/routing/introduction.rst +++ b/components/routing/introduction.rst @@ -72,25 +72,25 @@ Defining Routes A full route definition can contain up to seven parts: -1. The URL path route. This is matched against the URL passed to the `RequestContext`, +#. The URL path route. This is matched against the URL passed to the `RequestContext`, and can contain named wildcard placeholders (e.g. ``{placeholders}``) to match dynamic parts in the URL. -2. An array of default values. This contains an array of arbitrary values +#. An array of default values. This contains an array of arbitrary values that will be returned when the request matches the route. -3. An array of requirements. These define constraints for the values of the +#. An array of requirements. These define constraints for the values of the placeholders as regular expressions. -4. An array of options. These contain internal settings for the route and +#. An array of options. These contain internal settings for the route and are the least commonly needed. -5. A host. This is matched against the host of the request. See +#. A host. This is matched against the host of the request. See :doc:`/components/routing/hostname_pattern` for more details. -6. An array of schemes. These enforce a certain HTTP scheme (``http``, ``https``). +#. An array of schemes. These enforce a certain HTTP scheme (``http``, ``https``). -7. An array of methods. These enforce a certain HTTP request method (``HEAD``, +#. An array of methods. These enforce a certain HTTP request method (``HEAD``, ``GET``, ``POST``, ...). .. versionadded:: 2.2 diff --git a/components/serializer.rst b/components/serializer.rst index aeaabeb6067..472de02aa02 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -144,9 +144,9 @@ of the ``Person`` class would be encoded in XML format:: In this case, :method:`Symfony\\Component\\Serializer\\Serializer::deserialize` needs three parameters: -1. The information to be decoded -2. The name of the class this information will be decoded to -3. The encoder used to convert that information into an array +#. The information to be decoded +#. The name of the class this information will be decoded to +#. The encoder used to convert that information into an array Using Camelized Method Names for Underscored Attributes ------------------------------------------------------- diff --git a/components/translation/introduction.rst b/components/translation/introduction.rst index 5810fe5e770..c637dce0b5a 100644 --- a/components/translation/introduction.rst +++ b/components/translation/introduction.rst @@ -159,12 +159,12 @@ If the message is not located in the catalog of the specific locale, the translator will look into the catalog of one or more fallback locales. For example, assume you're trying to translate into the ``fr_FR`` locale: -1. First, the translator looks for the translation in the ``fr_FR`` locale; +#. First, the translator looks for the translation in the ``fr_FR`` locale; -2. If it wasn't found, the translator looks for the translation in the ``fr`` +#. If it wasn't found, the translator looks for the translation in the ``fr`` locale; -3. If the translation still isn't found, the translator uses the one or more +#. If the translation still isn't found, the translator uses the one or more fallback locales set explicitly on the translator. For (3), the fallback locales can be set by calling diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 49a6ff35e30..51acb3f1adf 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -19,10 +19,10 @@ Resolving Process For each report, we first try to confirm the vulnerability. When it is confirmed, the core-team works on a solution following these steps: -1. Send an acknowledgement to the reporter; -2. Work on a patch; -3. Get a CVE identifier from mitre.org; -4. Write a security announcement for the official Symfony `blog`_ about the +#. Send an acknowledgement to the reporter; +#. Work on a patch; +#. Get a CVE identifier from mitre.org; +#. Write a security announcement for the official Symfony `blog`_ about the vulnerability. This post should contain the following information: * a title that always include the "Security release" string; @@ -32,12 +32,12 @@ confirmed, the core-team works on a solution following these steps: * how to patch/upgrade/workaround affected applications; * the CVE identifier; * credits. -5. Send the patch and the announcement to the reporter for review; -6. Apply the patch to all maintained versions of Symfony; -7. Package new versions for all affected versions; -8. Publish the post on the official Symfony `blog`_ (it must also be added to +#. Send the patch and the announcement to the reporter for review; +#. Apply the patch to all maintained versions of Symfony; +#. Package new versions for all affected versions; +#. Publish the post on the official Symfony `blog`_ (it must also be added to the "`Security Advisories`_" category); -9. Update the security advisory list (see below). +#. Update the security advisory list (see below). .. note:: @@ -61,23 +61,23 @@ As Symfony is used by many large Open-Source projects, we standardized the way the Symfony security team collaborates on security issues with downstream projects. The process works as follows: -1. After the Symfony security team has acknowledged a security issue, it -immediately sends an email to the downstream project security teams to inform -them of the issue; +#. After the Symfony security team has acknowledged a security issue, it + immediately sends an email to the downstream project security teams to + inform them of the issue; -2. The Symfony security team creates a private Git repository to ease the -collaboration on the issue and access to this repository is given to the -Symfony security team, to the Symfony contributors that are impacted by the -issue, and to one representative of each downstream projects; +#. The Symfony security team creates a private Git repository to ease the + collaboration on the issue and access to this repository is given to the + Symfony security team, to the Symfony contributors that are impacted by + the issue, and to one representative of each downstream projects; -3. All people with access to the private repository work on a solution to -solve the issue via pull requests, code reviews, and comments; +#. All people with access to the private repository work on a solution to + solve the issue via pull requests, code reviews, and comments; -4. Once the fix is found, all involved projects collaborate to find the best -date for a joint release (there is no guarantee that all releases will be at -the same time but we will try hard to make them at about the same time). When -the issue is not known to be exploited in the wild, a period of two weeks -seems like a reasonable amount of time. +#. Once the fix is found, all involved projects collaborate to find the best + date for a joint release (there is no guarantee that all releases will + be at the same time but we will try hard to make them at about the same + time). When the issue is not known to be exploited in the wild, a period + of two weeks seems like a reasonable amount of time. The list of downstream projects participating in this process is kept as small as possible in order to better manage the flow of confidential information diff --git a/cookbook/deployment/heroku.rst b/cookbook/deployment/heroku.rst index 5525e7ffce5..44f33b46362 100644 --- a/cookbook/deployment/heroku.rst +++ b/cookbook/deployment/heroku.rst @@ -81,9 +81,9 @@ Creating a Procfile By default, Heroku will launch an Apache web server together with PHP to serve applications. However, two special circumstances apply to Symfony applications: -1. The document root is in the ``web/`` directory and not in the root directory +#. The document root is in the ``web/`` directory and not in the root directory of the application; -2. The Composer ``bin-dir``, where vendor binaries (and thus Heroku's own boot +#. The Composer ``bin-dir``, where vendor binaries (and thus Heroku's own boot scripts) are placed, is ``bin/`` , and not the default ``vendor/bin``. .. note:: diff --git a/cookbook/security/custom_authentication_provider.rst b/cookbook/security/custom_authentication_provider.rst index be18a236549..2a05498c913 100644 --- a/cookbook/security/custom_authentication_provider.rst +++ b/cookbook/security/custom_authentication_provider.rst @@ -20,9 +20,9 @@ The following chapter demonstrates how to create a custom authentication provider for WSSE authentication. The security protocol for WSSE provides several security benefits: -1. Username / Password encryption -2. Safe guarding against replay attacks -3. No web server configuration required +#. Username / Password encryption +#. Safe guarding against replay attacks +#. No web server configuration required WSSE is very useful for the securing of web services, may they be SOAP or REST. diff --git a/cookbook/workflow/new_project_svn.rst b/cookbook/workflow/new_project_svn.rst index c8140d1f09a..861c6871cec 100644 --- a/cookbook/workflow/new_project_svn.rst +++ b/cookbook/workflow/new_project_svn.rst @@ -48,26 +48,26 @@ Initial Project Setup To get started, you'll need to download Symfony and get the basic Subversion setup: -1. Download the `Symfony Standard Edition`_ with or without vendors. +#. Download the `Symfony Standard Edition`_ with or without vendors. -2. Unzip/untar the distribution. It will create a folder called Symfony with +#. Unzip/untar the distribution. It will create a folder called Symfony with your new project structure, config files, etc. Rename it to whatever you like. -3. Checkout the Subversion repository that will host this project. Suppose +#. Checkout the Subversion repository that will host this project. Suppose it is hosted on `Google code`_ and called ``myproject``: .. code-block:: bash $ svn checkout http://myproject.googlecode.com/svn/trunk myproject -4. Copy the Symfony project files in the Subversion folder: +#. Copy the Symfony project files in the Subversion folder: .. code-block:: bash $ mv Symfony/* myproject/ -5. Now, set the ignore rules. Not everything *should* be stored in your Subversion +#. Now, set the ignore rules. Not everything *should* be stored in your Subversion repository. Some files (like the cache) are generated and others (like the database configuration) are meant to be customized on each machine. This makes use of the ``svn:ignore`` property, so that specific files can @@ -88,21 +88,21 @@ To get started, you'll need to download Symfony and get the basic Subversion set $ svn ci -m "commit basic Symfony ignore list (vendor, app/bootstrap*, app/config/parameters.yml, app/cache/*, app/logs/*, web/bundles)" -6. The rest of the files can now be added and committed to the project: +#. The rest of the files can now be added and committed to the project: .. code-block:: bash $ svn add --force . $ svn ci -m "add basic Symfony Standard 2.X.Y" -7. Copy ``app/config/parameters.yml`` to ``app/config/parameters.yml.dist``. +#. Copy ``app/config/parameters.yml`` to ``app/config/parameters.yml.dist``. The ``parameters.yml`` file is ignored by svn (see above) so that machine-specific settings like database passwords aren't committed. By creating the ``parameters.yml.dist`` file, new developers can quickly clone the project, copy this file to ``parameters.yml``, customize it, and start developing. -8. Finally, download all of the third-party vendor libraries by +#. Finally, download all of the third-party vendor libraries by executing Composer. For details, see :ref:`installation-updating-vendors`. .. tip:: From 0875bd8acff7d4818e7871dde7185bd7373610b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?W=C5=82odzimierz=20Gajda?= Date: Mon, 15 Dec 2014 07:52:03 +0100 Subject: [PATCH 525/835] How to override vendor directory location - fix --- cookbook/configuration/override_dir_structure.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/configuration/override_dir_structure.rst b/cookbook/configuration/override_dir_structure.rst index 8a701d299c4..abfe379bd89 100644 --- a/cookbook/configuration/override_dir_structure.rst +++ b/cookbook/configuration/override_dir_structure.rst @@ -181,7 +181,7 @@ In ``app/autoload.php`` you need to modify the path leading to ``vendor/autoload file:: // app/autoload.php - + // ... $loader = require '/some/dir/vendor/autoload.php'; .. tip:: From 6a02f68f4e51067d5b16956040eb40dacb548dbc Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 23 Nov 2014 21:27:04 +0100 Subject: [PATCH 526/835] Applied comments from our great reviewers --- .../console/helpers/debug_formatter.rst | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/components/console/helpers/debug_formatter.rst b/components/console/helpers/debug_formatter.rst index ba584b349fc..6368686b78d 100644 --- a/components/console/helpers/debug_formatter.rst +++ b/components/console/helpers/debug_formatter.rst @@ -1,24 +1,24 @@ .. index:: single: Console Helpers; DebugFormatter Helper -DebugFormatter Helper -===================== +Debug Formatter Helper +====================== .. versionadded:: 2.6 - The DebugFormatter helper was introduced in Symfony 2.6. + The Debug Formatter helper was introduced in Symfony 2.6. The :class:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper` provides functions to output debug information when running an external program, for -instance a process or HTTP request. It is included in the default helper set, -which you can get by calling -:method:`Symfony\\Component\\Console\\Command\\Command::getHelperSet`:: +instance a process or HTTP request. It is included in the default helper set +and you can get it by calling +:method:`Symfony\\Component\\Console\\Command\\Command::getHelper`:: $debugFormatter = $this->getHelper('debug_formatter'); The formatter only formats strings, which you can use to output to the console, -but also to log the information or anything else. +but also to log the information or do anything else. -All methods of this helper have an identifier as the first argument. This is an +All methods of this helper have an identifier as the first argument. This is a unique value for each program. This way, the helper can debug information for multiple programs at the same time. When using the :doc:`Process component `, you probably want to use @@ -26,7 +26,7 @@ multiple programs at the same time. When using the .. tip:: - This information is often too verbose to show by default. You can use + This information is often too verbose to be shown by default. You can use :ref:`verbosity levels ` to only show it when in debugging mode (``-vvv``). @@ -62,8 +62,17 @@ Some programs give output while they are running. This information can be shown using :method:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper::progress`:: + use Symfony\Component\Process\Process; + + // ... + $process = new Process(...); + + $process->run(function ($type, $buffer) use ($output, $debugFormatter, $process) { + $output->writeln( + $debugFormatter->progress(spl_object_hash($process), $buffer, Process::ERR === $type) + ); + }); // ... - $output->writeln($debugFormatter->progress(spl_object_hash($process), $buffer, Process::ERR === $type)); In case of success, this will output: @@ -80,18 +89,24 @@ And this in case of failure: The third argument is a boolean which tells the function if the output is error output or not. When ``true``, the output is considered error output. -The fourth and fifth argument allow you to override the prefix for respectively -the normal output and error output. +The fourth and fifth argument allow you to override the prefix for the normal +output and error output respectively. Stopping a Program ------------------ When a program is stopped, you can use -:method:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper::progress` -to notify this to the users:: +:method:`Symfony\\Component\\Console\\Helper\\DebugFormatterHelper::run` to +notify this to the users:: // ... - $output->writeln($debugFormatter->progress(spl_object_hash($process), 'Some command description', $process->isSuccesfull())); + $output->writeln( + $debugFormatter->stop( + spl_object_hash($process), + 'Some command description', + $process->isSuccessfull() + ) + ); This will output: From 99f31fc212a66b329bfe3eb371e1ece744271c98 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Mon, 15 Dec 2014 11:53:01 +0100 Subject: [PATCH 527/835] Reordered list of helpers --- components/console/helpers/map.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index ea7e472dca8..f1794ef3d5e 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -1,4 +1,3 @@ -* :doc:`/components/console/helpers/debug_formatter` (new in 2.6) * :doc:`/components/console/helpers/dialoghelper` * :doc:`/components/console/helpers/formatterhelper` * :doc:`/components/console/helpers/processhelper` @@ -7,3 +6,4 @@ * :doc:`/components/console/helpers/questionhelper` * :doc:`/components/console/helpers/table` * :doc:`/components/console/helpers/tablehelper` +* :doc:`/components/console/helpers/debug_formatter` (new in 2.6) From c291a73f51b314bd507f5ed2b651adeeaf3736a3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 15 Dec 2014 17:20:25 +0100 Subject: [PATCH 528/835] Rewritten from scratch the chapter about installing Symfony --- book/installation.rst | 448 +++++++++++++++++++++-------------------- book/page_creation.rst | 9 +- 2 files changed, 233 insertions(+), 224 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index d83cf153ea7..aeb83a7c076 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -5,218 +5,199 @@ Installing and Configuring Symfony ================================== The goal of this chapter is to get you up and running with a working application -built on top of Symfony. Fortunately, Symfony offers "distributions", which -are functional Symfony "starter" projects that you can download and begin -developing in immediately. +built on top of Symfony. In order to simplify the process of creating new +applications, Symfony provides an installer that must be installed before +creating the first application. -.. tip:: +Installing the Symfony Installer +-------------------------------- - If you're looking for instructions on how best to create a new project - and store it via source control, see `Using Source Control`_. +Using the Symfony Installer is the only recommended way to create new Symfony +applications. This installer is a PHP application that has to be installed +only once and then it can create any number of Symfony applications. -.. _installing-a-symfony2-distribution: +.. note:: -Installing a Symfony Distribution ---------------------------------- + The installer requires PHP 5.4 or higher. If you still use the legacy + PHP 5.3 version, you cannot use the Symfony Installer. In that case, read the next + :ref:`Creating Symfony Applications without the Installer ` + section. -.. tip:: +Depending on your operating system, the installer must be installed in different +ways. - First, check that you have installed and configured a Web server (such - as Apache) with PHP. For more information on Symfony requirements, see the - :doc:`requirements reference `. +Linux and Mac OS X Systems +~~~~~~~~~~~~~~~~~~~~~~~~~~ -Symfony packages "distributions", which are fully-functional applications -that include the Symfony core libraries, a selection of useful bundles, a -sensible directory structure and some default configuration. When you download -a Symfony distribution, you're downloading a functional application skeleton -that can be used immediately to begin developing your application. +Open your command console and execute the following three commands: -Start by visiting the Symfony download page at `http://symfony.com/download`_. -On this page, you'll see the *Symfony Standard Edition*, which is the main -Symfony distribution. There are 2 ways to get your project started: +.. code-block:: bash -Option 1) Composer -~~~~~~~~~~~~~~~~~~ + $ curl -LsS http://symfony.com/installer > symfony.phar + $ sudo mv symfony.phar /usr/local/bin/symfony + $ chmod a+x /usr/local/bin/symfony -`Composer`_ is a dependency management library for PHP, which you can use -to download the Symfony Standard Edition. +This will create a global ``symfony`` command in your system that will be used +to create new Symfony applications. -Start by `downloading Composer`_ anywhere onto your local computer. If you -have curl installed, it's as easy as: +Windows Systems +~~~~~~~~~~~~~~~ -.. code-block:: bash +Open your command console and execute the following command: - $ curl -s https://getcomposer.org/installer | php - -.. note:: +.. code-block:: bash - If your computer is not ready to use Composer, you'll see some recommendations - when running this command. Follow those recommendations to get Composer - working properly. + c:\> php -r "readfile('http://symfony.com/installer');" > symfony.phar -Composer is an executable PHAR file, which you can use to download the Standard -Distribution: +Then, move the downloaded ``symfony.phar`` file to your projects directory and +execute it as follows: .. code-block:: bash - $ php composer.phar create-project symfony/framework-standard-edition /path/to/webroot/Symfony '2.3.*' + c:\> php symfony.phar -.. tip:: +Creating the Symfony Application +-------------------------------- - To download the vendor files faster, add the ``--prefer-dist`` option at - the end of any Composer command. +Once the Symfony Installer is ready, create your first Symfony application with +the ``new`` command: -.. tip:: +.. code-block:: bash - Add the ``-vvv`` flag to see everything that Composer is doing - this is - especially useful on a slow connection where it may seem that nothing is - happening. + # Linux, Mac OS X + $ symfony new my_project_name -This command may take several minutes to run as Composer downloads the Standard -Distribution along with all of the vendor libraries that it needs. When it finishes, -you should have a directory that looks something like this: + # Windows + c:\> cd projects/ + c:\projects\> php symfony.phar new my_project_name -.. code-block:: text +This command creates a new directory called ``my_project_name`` that contains a +fresh new project based on the most recent stable Symfony version available. In +addition, the installer checks if your system meets the technical requirements +to execute Symfony applications. If not, you'll see the list of changes needed +to meet those requirements. - path/to/webroot/ <- your web server directory (sometimes named htdocs or public) - Symfony/ <- the new directory - app/ - cache/ - config/ - logs/ - src/ - ... - vendor/ - ... - web/ - app.php - ... - -Option 2) Download an Archive -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can also download an archive of the Standard Edition. Here, you'll -need to make two choices: - -* Download either a ``.tgz`` or ``.zip`` archive - both are equivalent, download - whatever you're more comfortable using; - -* Download the distribution with or without vendors. If you're planning on - using more third-party libraries or bundles and managing them via Composer, - you should probably download "without vendors". - -Download one of the archives somewhere under your local web server's root -directory and unpack it. From a UNIX command line, this can be done with -one of the following commands (replacing ``###`` with your actual filename): +Basing your Project on a Specific Symfony Version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your project needs to be based on a specific Symfony version, pass the version +number as the second argument of the ``new`` command: .. code-block:: bash - # for .tgz file - $ tar zxvf Symfony_Standard_Vendors_2.3.###.tgz + # Linux, Mac OS X + $ symfony new my_project_name 2.3.23 - # for a .zip file - $ unzip Symfony_Standard_Vendors_2.3.###.zip + # Windows + c:\projects\> php symfony.phar new my_project_name 2.5.8 -If you've downloaded "without vendors", you'll definitely need to read the -next section. +Read the `Symfony Release process`_ to better understand why there are several +Symfony versions and which one to use for your projects. -.. note:: +.. _book-creating-applications-without-the-installer: - You can easily override the default directory structure. See - :doc:`/cookbook/configuration/override_dir_structure` for more - information. +Creating Symfony Applications without the Installer +--------------------------------------------------- -All public files and the front controller that handles incoming requests in -a Symfony application live in the ``Symfony/web/`` directory. So, assuming -you unpacked the archive into your web server's or virtual host's document root, -your application's URLs will start with ``http://localhost/Symfony/web/``. +Due to technical reasons, some of the past Symfony versions aren't compatible +with the Symfony Installer. In addition, if you still use PHP 5.3, you can't +execute the installer. In those cases, you must use the alternative installation +method based con `Composer`_. -.. note:: +Composer is the dependency manager used by modern PHP applications and it can +also be used to create new applications based on the Symfony framework. If you +don't have installed it globally, start by reading the next section. - The following examples assume you don't touch the document root settings - so all URLs start with ``http://localhost/Symfony/web/`` +Installing Composer Globally +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. _installation-updating-vendors: +On Linux and Mac OS X, execute the following two commands to install Composer +globally: + +.. code-block:: bash -Updating Vendors -~~~~~~~~~~~~~~~~ + $ curl -sS https://getcomposer.org/installer | php + $ sudo mv composer.phar /usr/local/bin/composer -At this point, you've downloaded a fully-functional Symfony project in which -you'll start to develop your own application. A Symfony project depends on -a number of external libraries. These are downloaded into the ``vendor/`` directory -of your project via a library called `Composer`_. +On Windows Systems, download the executable Composer installer that you can find +on the `Composer download page`_ and follow the steps. -Depending on how you downloaded Symfony, you may or may not need to update -your vendors right now. But, updating your vendors is always safe, and guarantees -that you have all the vendor libraries you need. +Creating a Symfony Application with Composer +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Step 1: Get `Composer`_ (The great new PHP packaging system) +Once Composer is installed on your computer, execute the ``create-project`` +command to create a new Symfony application based on its latest stable version: .. code-block:: bash - $ curl -s http://getcomposer.org/installer | php - -Make sure you download ``composer.phar`` in the same folder where -the ``composer.json`` file is located (this is your Symfony project -root by default). + $ composer create-project symfony/framework-standard-edition my_project_name -Step 2: Install vendors +If you need to base your application on a specific Symfony version, provide that +version as the second argument of the ``create-project`` command: .. code-block:: bash - $ php composer.phar install + $ composer create-project symfony/framework-standard-edition my_project_name '2.3.*' -This command downloads all of the necessary vendor libraries - including -Symfony itself - into the ``vendor/`` directory. +.. tip:: -.. note:: + If your Internet connection is slow, you may think that Composer is not + doing anything. If that's your case, add the ``-vvv`` flag to the previous + command to display a detailed output of everything that Composer is doing. - If you don't have ``curl`` installed, you can also just download the ``installer`` - file manually at http://getcomposer.org/installer. Place this file into your - project and then run: +Running the Symfony Application +------------------------------- - .. code-block:: bash +Symfony leverages the internal web server provided by PHP to run applications +while developing them. Therefore, running a Symfony application is a matter of +browsing the project directory and executing this command: - $ php installer - $ php composer.phar install +.. code-block:: bash -.. tip:: + $ cd my_project_name/ + $ php app/console server:run - When running ``php composer.phar install`` or ``php composer.phar update``, - Composer will execute post install/update commands to clear the cache - and install assets. By default, the assets will be copied into your ``web`` - directory. +Then, open your browser and access the ``http://localhost:8000`` URL to see the +Welcome page of Symfony: - Instead of copying your Symfony assets, you can create symlinks if - your operating system supports it. To create symlinks, add an entry - in the ``extra`` node of your composer.json file with the key - ``symfony-assets-install`` and the value ``symlink``: +.. image:: /images/quick_tour/welcome.png + :align: center + :alt: Symfony Welcome Page - .. code-block:: json +Instead of the Welcome Page, you may see a blank page or an error page. +This is caused by a directory permission misconfiguration. There are several +possible solutions depending on your operating system. All of them are +explained in the :ref:`Setting up Permissions ` +section. - "extra": { - "symfony-app-dir": "app", - "symfony-web-dir": "web", - "symfony-assets-install": "symlink" - } +.. note:: - When passing ``relative`` instead of ``symlink`` to symfony-assets-install, - the command will generate relative symlinks. + PHP's internal web server is available in PHP 5.4 or higher versions. If you + still use the legacy PHP 5.3 version, you'll have to configure a *virtual host* + in your web server. -Configuration and Setup -~~~~~~~~~~~~~~~~~~~~~~~ +The ``server:run`` command is only suitable while developing the application. In +order to run Symfony applications on production servers, you'll have to configure +your `Apache`_ or `Nginx`_ web server as explained in +:doc:`/cookbook/configuration/web_server_configuration`. -At this point, all of the needed third-party libraries now live in the ``vendor/`` -directory. You also have a default application setup in ``app/`` and some -sample code inside the ``src/`` directory. +When you are finished working on your Symfony application, you can stop the +server with the ``server:stop`` command: -Symfony comes with a visual server configuration tester to help make sure -your Web server and PHP are configured to use Symfony. Use the following URL -to check your configuration: +.. code-block:: bash + + $ php app/console server:stop + +Checking Symfony Application Configuration and Setup +---------------------------------------------------- + +Symfony applications come with a visual server configuration tester to show if +your environment is ready to use Symfony. Access the following URL to check your +configuration: .. code-block:: text - http://localhost/config.php + http://localhost:8000/config.php If there are any issues, correct them now before moving on. @@ -224,13 +205,21 @@ If there are any issues, correct them now before moving on. .. sidebar:: Setting up Permissions - One common issue is that the ``app/cache`` and ``app/logs`` directories - must be writable both by the web server and the command line user. On - a UNIX system, if your web server user is different from your command - line user, you can run the following commands just once in your project - to ensure that permissions will be setup properly. + One common issue when installing Symfony is that the ``app/cache`` and + ``app/logs`` directories must be writable both by the web server and the + command line user. On a UNIX system, if your web server user is different + from your command line user, you can try one of the following solutions. + + **1. Use the same user for the CLI and the web server** + + In development environments, it is a common practice to use the same UNIX + user for the CLI and the web server because it avoids any of these permissions + issues when setting up new projects. This can be done by editing your web server + configuration (e.g. commonly httpd.conf or apache2.conf for Apache) and setting + its user to be the same as your CLI user (e.g. for Apache, update the ``User`` + and ``Group`` values). - **1. Using ACL on a system that supports chmod +a** + **2. Using ACL on a system that supports chmod +a** Many systems allow you to use the ``chmod +a`` command. Try this first, and if you get an error - try the next method. This uses a command to @@ -246,7 +235,7 @@ If there are any issues, correct them now before moving on. $ sudo chmod +a "`whoami` allow delete,write,append,file_inherit,directory_inherit" app/cache app/logs - **2. Using ACL on a system that does not support chmod +a** + **3. Using ACL on a system that does not support chmod +a** Some systems don't support ``chmod +a``, but do support another utility called ``setfacl``. You may need to `enable ACL support`_ on your partition @@ -256,19 +245,18 @@ If there are any issues, correct them now before moving on. .. code-block:: bash - $ HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1` - $ sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs - $ sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs + $ HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep -v root | head -1 | cut -d\ -f1` + $ sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs + $ sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX app/cache app/logs If this doesn't work, try adding ``-n`` option. - **3. Without using ACL** + **4. Without using ACL** - If you don't have access to changing the ACL of the directories, you will - need to change the umask so that the cache and log directories will - be group-writable or world-writable (depending if the web server user - and the command line user are in the same group or not). To achieve - this, put the following line at the beginning of the ``app/console``, + If none of the previous methods work for you, change the umask so that the + cache and log directories will be group-writable or world-writable (depending + if the web server user and the command line user are in the same group or not). + To achieve this, put the following line at the beginning of the ``app/console``, ``web/app.php`` and ``web/app_dev.php`` files:: umask(0002); // This will let the permissions be 0775 @@ -280,37 +268,82 @@ If there are any issues, correct them now before moving on. Note that using the ACL is recommended when you have access to them on your server because changing the umask is not thread-safe. - **4. Use the same user for the CLI and the web server** +.. _installation-updating-vendors: + +Updating Symfony Applications +----------------------------- - In development environments, it is a common practice to use the same unix - user for the CLI and the web server because it avoids any of these permissions - issues when setting up new projects. This can be done by editing your web server - configuration (e.g. commonly httpd.conf or apache2.conf for Apache) and setting - its user to be the same as your CLI user (e.g. for Apache, update the User - and Group values). +At this point, you've create a fully-functional Symfony application in which +you'll start to develop your own project. A Symfony application depends on +a number of external libraries. These are downloaded into the ``vendor/`` directory +and they are managed exclusively by Composer. -When everything is fine, click on "Go to the Welcome page" to request your -first "real" Symfony webpage: +Updating those third-party libraries frequently is a good practice to prevent bugs +and security vulnerabilities. Execute the ``update`` Composer command to update +them all at once: -.. code-block:: text +.. code-block:: bash - http://localhost/app_dev.php/ + $ cd my_project_name/ + $ composer update -Symfony should welcome and congratulate you for your hard work so far! +Depending on the complexity of your project, this update process can take up to +several minutes to complete. -.. image:: /images/quick_tour/welcome.png +.. _installing-a-symfony2-distribution: -.. tip:: +Installing a Symfony Distribution +--------------------------------- + +Symfony project packages "distributions", which are fully-functional applications +that include the Symfony core libraries, a selection of useful bundles, a +sensible directory structure and some default configuration. In fact, when you +created a Symfony application in the previous sections, you actually downloaded the +default distribution provided by Symfony, which is called *Symfony Standard Edition*. + +The *Symfony Standard Edition* is by far the most popular distribution and it's +also the best choice for developers starting with Symfony. However, the Symfony +Community has published other popular distributions that you may use in your +applications: + +* The `Symfony CMF Standard Edition`_ is the best distribution to get started + with the `Symfony CMF`_ project, which is a project that makes it easier for + developers to add CMS functionality to applications built with the Symfony + framework. +* The `Symfony REST Edition`_ shows how to build an application that provides a + RESTful API using the FOSRestBundle and several other related bundles. + +Using Source Control +-------------------- + +If you're using a version control system like `Git`_, you can safely commit all your project's code. The reason is that Symfony applications already contain a +``.gitignore`` file specially prepared for Symfony. + +For specific instructions on how best to setup your project to be stored +in Git, see :doc:`/cookbook/workflow/new_project_git`. - To get nice and short urls you should point the document root of your - webserver or virtual host to the ``Symfony/web/`` directory. Though - this is not required for development it is recommended at the time your - application goes into production as all system and configuration files - become inaccessible to clients then. For information on configuring - your specific web server document root, read - :doc:`/cookbook/configuration/web_server_configuration` - or consult the official documentation of your webserver: - `Apache`_ | `Nginx`_ . +Checking out a Versioned Symfony Application +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When using Composer to manage application's dependencies, it's recommended to +ignore the entire ``vendor/`` directory before committing its code to the +repository. This means that when checking out a Symfony application from a Git +repository, there will be no ``vendor/`` directory and the application won't +work out-of-the-box. + +In order to make it work, check out the Symfony application and then execute the +``install`` Composer command to download and install all the dependencies required +by the application: + +.. code-block:: bash + + $ cd my_project_name/ + $ composer install + +How does Composer know which specific dependencies to install? Because when a +Symfony application is committed to a repository, the ``composer.json`` and +``composer.lock`` files are also committed. These files tell Composer which +dependencies (and which specific versions) to install for the application. Beginning Development --------------------- @@ -332,40 +365,13 @@ a wide variety of articles about solving specific problems with Symfony. If you want to remove the sample code from your distribution, take a look at this cookbook article: ":doc:`/cookbook/bundles/remove`" -Using Source Control --------------------- - -If you're using a version control system like ``Git`` or ``Subversion``, you -can setup your version control system and begin committing your project to -it as normal. The Symfony Standard Edition *is* the starting point for your -new project. - -For specific instructions on how best to setup your project to be stored -in Git, see :doc:`/cookbook/workflow/new_project_git`. - -Ignoring the ``vendor/`` Directory -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you've downloaded the archive *without vendors*, you can safely ignore -the entire ``vendor/`` directory and not commit it to source control. With -``Git``, this is done by creating and adding the following to a ``.gitignore`` -file: - -.. code-block:: text - - /vendor/ - -Now, the vendor directory won't be committed to source control. This is fine -(actually, it's great!) because when someone else clones or checks out the -project, they can simply run the ``php composer.phar install`` script to -install all the necessary project dependencies. - -.. _`enable ACL support`: https://help.ubuntu.com/community/FilePermissionsACLs -.. _`http://symfony.com/download`: http://symfony.com/download -.. _`Git`: http://git-scm.com/ -.. _`GitHub Bootcamp`: http://help.github.com/set-up-git-redirect +.. _`Symfony Release process`: http://symfony.com/doc/current/contributing/community/releases.html .. _`Composer`: http://getcomposer.org/ -.. _`downloading Composer`: http://getcomposer.org/download/ +.. _`Composer download page`: https://getcomposer.org/download/ .. _`Apache`: http://httpd.apache.org/docs/current/mod/core.html#documentroot .. _`Nginx`: http://wiki.nginx.org/Symfony -.. _`Symfony Installation Page`: http://symfony.com/download +.. _`Symfony CMF Standard Edition`: https://github.com/symfony-cmf/symfony-cmf-standard +.. _`Symfony CMF`: http://cmf.symfony.com/ +.. _`Symfony REST Edition`: https://github.com/gimler/symfony-rest-edition +.. _`FOSRestBundle`: https://github.com/FriendsOfSymfony/FOSRestBundle +.. _`Git`: http://git-scm.com/ diff --git a/book/page_creation.rst b/book/page_creation.rst index 085f98e9cc6..b3353b27596 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -489,13 +489,16 @@ Though entirely flexible, by default, each Symfony :term:`application` has the same basic and recommended directory structure: * ``app/``: This directory contains the application configuration; - * ``src/``: All the project PHP code is stored under this directory; - * ``vendor/``: Any vendor libraries are placed here by convention; - * ``web/``: This is the web root directory and contains any publicly accessible files; +.. note:: + + You can easily override the default directory structure. See + :doc:`/cookbook/configuration/override_dir_structure` for more + information. + .. _the-web-directory: The Web Directory From 2b86d807e5d2df89ed2dcdf3665be674d527e15f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 15 Dec 2014 19:01:49 +0100 Subject: [PATCH 529/835] Documented getProgress/setProgress methods --- components/console/helpers/progressbar.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index 627472eeff4..1138b5d03f8 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -42,7 +42,11 @@ number of units, and advance the progress as the command executes:: Instead of advancing the bar by a number of steps (with the :method:`Symfony\\Component\\Console\\Helper\\ProgressBar::advance` method), you can also set the current progress by calling the -:method:`Symfony\\Component\\Console\\Helper\\ProgressBar::setCurrent` method. +:method:`Symfony\\Component\\Console\\Helper\\ProgressBar::setProgress` method. + +.. versionadded:: 2.6 + The ``setProgress()`` method was introduced in Symfony 2.6. Previously it was + called ``setCurrent()``. .. caution:: @@ -300,10 +304,14 @@ that displays the number of remaining steps:: ProgressBar::setPlaceholderFormatterDefinition( 'remaining_steps', function (ProgressBar $bar, OutputInterface $output) { - return $bar->getMaxSteps() - $bar->getStep(); + return $bar->getMaxSteps() - $bar->getProgress(); } ); +.. versionadded:: 2.6 + The ``getProgress()`` method was introduced in Symfony 2.6. Previously it was + called ``getStep()``. + Custom Messages ~~~~~~~~~~~~~~~ From 6a3cbd9541dca200cb0c9883f955376e1ec2d5c4 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Dec 2014 07:49:20 +0100 Subject: [PATCH 530/835] Changed the "new in Symfony 2.6" message for consistency --- components/console/helpers/progressbar.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index 1138b5d03f8..1b8edf309cc 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -45,8 +45,7 @@ you can also set the current progress by calling the :method:`Symfony\\Component\\Console\\Helper\\ProgressBar::setProgress` method. .. versionadded:: 2.6 - The ``setProgress()`` method was introduced in Symfony 2.6. Previously it was - called ``setCurrent()``. + The ``setProgress()`` method was called ``setCurrent()`` prior to Symfony 2.6. .. caution:: @@ -309,8 +308,7 @@ that displays the number of remaining steps:: ); .. versionadded:: 2.6 - The ``getProgress()`` method was introduced in Symfony 2.6. Previously it was - called ``getStep()``. + The ``getProgress()`` method was called ``getStep()`` prior to Symfony 2.6. Custom Messages ~~~~~~~~~~~~~~~ From 8f202c6e475c4f8dc32e80663c617a73bcf38dcc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Dec 2014 08:40:57 +0100 Subject: [PATCH 531/835] Re-added a wrongly deleted link reference --- book/installation.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/book/installation.rst b/book/installation.rst index aeb83a7c076..f0b2fd79552 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -370,6 +370,7 @@ a wide variety of articles about solving specific problems with Symfony. .. _`Composer download page`: https://getcomposer.org/download/ .. _`Apache`: http://httpd.apache.org/docs/current/mod/core.html#documentroot .. _`Nginx`: http://wiki.nginx.org/Symfony +.. _`enable ACL support`: https://help.ubuntu.com/community/FilePermissionsACLs .. _`Symfony CMF Standard Edition`: https://github.com/symfony-cmf/symfony-cmf-standard .. _`Symfony CMF`: http://cmf.symfony.com/ .. _`Symfony REST Edition`: https://github.com/gimler/symfony-rest-edition From ce6e3eba726957c0c5ccec82b55936f523d5b3bb Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Dec 2014 12:19:30 +0100 Subject: [PATCH 532/835] Reworded some explanations to make them more clear --- components/yaml/yaml_format.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index c45a45117a2..7be2f0d931b 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -45,8 +45,9 @@ must be doubled to escape it: 'A single quote '' inside a single-quoted string' -If the string contains any of the following characters, it must be escaped with -single quotes: +Strings containing any of the following characters must be quoted. Although you +can use double quotes, for these characters is more convenient to use single +quotes, which avoids having to escape any backslash ``\``: * ``:`` * ``{`` @@ -69,16 +70,15 @@ single quotes: * ``\``` The double-quoted style provides a way to express arbitrary strings, by -using ``\`` escape sequences. It is very useful when you need to embed a -``\n`` or a Unicode character in a string. +using ``\`` to escape characters and sequences. For instance, it is very useful +when you need to embed a ``\n`` or a Unicode character in a string. .. code-block:: yaml "A double-quoted string in YAML\n" If the string contains any of the following control characters, it must be -escaped with double quotes. In addition, the escaping must use a double slash -``\\`` to avoid parsing issues: +escaped with double quotes: * ``\0`` * ``\x01`` From 07aa4718996a191062d6cdeb6f3f4fdb43987b42 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 16 Dec 2014 07:30:14 -0500 Subject: [PATCH 533/835] Matching up the index position with the map --- components/console/helpers/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/helpers/index.rst b/components/console/helpers/index.rst index 96034459478..bf51dc40c92 100644 --- a/components/console/helpers/index.rst +++ b/components/console/helpers/index.rst @@ -7,7 +7,6 @@ The Console Helpers .. toctree:: :hidden: - debug_formatter dialoghelper formatterhelper processhelper @@ -16,6 +15,7 @@ The Console Helpers questionhelper table tablehelper + debug_formatter The Console component comes with some useful helpers. These helpers contain function to ease some common tasks. From 63ea6597255541a4dc2858fe3e3f3de5ddb5efd4 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Dec 2014 15:37:17 +0100 Subject: [PATCH 534/835] Added a lot more cases for enclosing strings with quotes --- components/yaml/yaml_format.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index 7be2f0d931b..1d45167fc52 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -117,6 +117,19 @@ escaped with double quotes: * ``\L`` * ``\P`` +Finally, there are other cases when the strings must be quoted, no matter if +using single or double quotes: + +* when the string is ``true`` or ``false`` (otherwise, it would be treated as a + boolean value); +* when the string is ``null`` or ``~`` (otherwise, it would be considered as a + ``null`` value); +* when the string looks like a number, such as integers (e.g. ``2``, ``14``, etc.), + floats (e.g. ``2.6``, ``14.9``) and exponential numbers (e.g. ``12e7``, etc.) + (otherwise, it would be treated as a numeric value); +* when the string looks like a date (e.g. ``2014-12-31``) (otherwise it would be + automatically converted into a Unix timestamp). + When a string contains line breaks, you can use the literal style, indicated by the pipe (``|``), to indicate that the string will span several lines. In literals, newlines are preserved: From 0b7cb0ee91753c33548d750ef0303af32b156988 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Dec 2014 15:55:45 +0100 Subject: [PATCH 535/835] Fixed some code indentation --- cookbook/bundles/installation.rst | 4 +- reference/configuration/security.rst | 88 ++++++++++++++-------------- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/cookbook/bundles/installation.rst b/cookbook/bundles/installation.rst index c546ffad58c..d6124026554 100644 --- a/cookbook/bundles/installation.rst +++ b/cookbook/bundles/installation.rst @@ -34,9 +34,9 @@ the library on the `Packagist.org`_ site. Now that you know the package name, you can install it via Composer: - .. code-block:: bash +.. code-block:: bash - $ php composer.phar require friendsofsymfony/user-bundle + $ php composer.phar require friendsofsymfony/user-bundle This will choose the best version for your project, add it to ``composer.json`` and download the library into the ``vendor/`` directory. If you need a specific diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index b1fba8ed721..c680bba3d4f 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -419,20 +419,20 @@ multiple firewalls, the "context" could actually be shared: .. code-block:: xml - - - - - - - - - + + + + + + + + + .. code-block:: php - // app/config/security.php - $container->loadFromExtension('security', array( + // app/config/security.php + $container->loadFromExtension('security', array( 'firewalls' => array( 'somename' => array( // ... @@ -443,7 +443,7 @@ multiple firewalls, the "context" could actually be shared: 'context' => 'my_context' ), ), - )); + )); HTTP-Digest Authentication -------------------------- @@ -452,38 +452,38 @@ To use HTTP-Digest authentication you need to provide a realm and a key: .. configuration-block:: - .. code-block:: yaml - - # app/config/security.yml - security: - firewalls: - somename: - http_digest: - key: "a_random_string" - realm: "secure-api" - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - 'firewalls' => array( - 'somename' => array( - 'http_digest' => array( - 'key' => 'a_random_string', - 'realm' => 'secure-api', - ), - ), - ), - )); + .. code-block:: yaml + + # app/config/security.yml + security: + firewalls: + somename: + http_digest: + key: "a_random_string" + realm: "secure-api" + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'somename' => array( + 'http_digest' => array( + 'key' => 'a_random_string', + 'realm' => 'secure-api', + ), + ), + ), + )); .. _`PBKDF2`: http://en.wikipedia.org/wiki/PBKDF2 .. _`ircmaxell/password-compat`: https://packagist.org/packages/ircmaxell/password-compat From 52a05c3ce01707881bcf38ff4f3938c4e5d11c7a Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Tue, 16 Dec 2014 16:01:00 +0100 Subject: [PATCH 536/835] Fix indentation of YAML example --- cookbook/profiler/storage.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cookbook/profiler/storage.rst b/cookbook/profiler/storage.rst index ca3c5b40c7c..3e87bc9016c 100644 --- a/cookbook/profiler/storage.rst +++ b/cookbook/profiler/storage.rst @@ -15,11 +15,11 @@ uses MySQL as the storage for the profiler with a lifetime of one hour: # app/config/config.yml framework: - profiler: - dsn: "mysql:host=localhost;dbname=%database_name%" - username: "%database_user%" - password: "%database_password%" - lifetime: 3600 + profiler: + dsn: "mysql:host=localhost;dbname=%database_name%" + username: "%database_user%" + password: "%database_password%" + lifetime: 3600 .. code-block:: xml From 044e88bc1cf08a6c469259dc4808a14de368e25d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Dec 2014 16:43:32 +0100 Subject: [PATCH 537/835] Fixed a minor typo --- components/yaml/yaml_format.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index 1d45167fc52..d9a290f9537 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -46,7 +46,7 @@ must be doubled to escape it: 'A single quote '' inside a single-quoted string' Strings containing any of the following characters must be quoted. Although you -can use double quotes, for these characters is more convenient to use single +can use double quotes, for these characters it is more convenient to use single quotes, which avoids having to escape any backslash ``\``: * ``:`` From cb4784a3097276d2768cc1dd88bdd75e2c51ef0c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 14 Dec 2014 15:50:33 +0100 Subject: [PATCH 538/835] Linked the PDO/DBAL Session article from the Doctrine category --- cookbook/map.rst.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 32b751c60d5..99498b50a7a 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -68,6 +68,7 @@ * :doc:`/cookbook/doctrine/mapping_model_classes` * :doc:`/cookbook/doctrine/registration_form` * :doc:`/cookbook/doctrine/console` + * (configuration) :doc:`/cookbook/configuration/pdo_session_storage` * :doc:`/cookbook/email/index` @@ -164,6 +165,7 @@ * :doc:`/cookbook/session/locale_sticky_session` * :doc:`/cookbook/session/sessions_directory` * :doc:`/cookbook/session/php_bridge` + * (configuration) :doc:`/cookbook/configuration/pdo_session_storage` * **symfony1** From 224c28330ddb7a81a096317cf447ef80871d4c33 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 14 Dec 2014 22:03:30 +0100 Subject: [PATCH 539/835] replace Symfony2 with Symfony --- best_practices/creating-the-project.rst | 2 +- best_practices/security.rst | 2 +- components/yaml/introduction.rst | 2 +- contributing/documentation/license.rst | 2 +- cookbook/bundles/best_practices.rst | 17 ++++++++--------- cookbook/bundles/configuration.rst | 2 +- cookbook/bundles/override.rst | 2 +- cookbook/bundles/remove.rst | 2 +- .../event_dispatcher/before_after_filters.rst | 2 +- 9 files changed, 16 insertions(+), 17 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index 16402420df4..e8ddc97e219 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -243,7 +243,7 @@ it's released: └─ web/ The changes are pretty superficial, but for now, we recommend that you use -the Symfony2 directory structure. +the Symfony directory structure. .. _`Composer`: https://getcomposer.org/ .. _`Get Started`: https://getcomposer.org/doc/00-intro.md diff --git a/best_practices/security.rst b/best_practices/security.rst index 35a6875c24f..0519b917edf 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -253,7 +253,7 @@ Learn More ---------- The `FOSUserBundle`_, developed by the Symfony community, adds support for a -database-backed user system in Symfony2. It also handles common tasks like +database-backed user system in Symfony. It also handles common tasks like user registration and forgotten password functionality. Enable the :doc:`Remember Me feature ` to diff --git a/components/yaml/introduction.rst b/components/yaml/introduction.rst index 44fc33dad28..e7e92270493 100644 --- a/components/yaml/introduction.rst +++ b/components/yaml/introduction.rst @@ -82,7 +82,7 @@ yourself by referencing common configuration bits. Using the Symfony YAML Component -------------------------------- -The Symfony2 Yaml component is very simple and consists of two main classes: +The Symfony Yaml component is very simple and consists of two main classes: one parses YAML strings (:class:`Symfony\\Component\\Yaml\\Parser`), and the other dumps a PHP array to a YAML string (:class:`Symfony\\Component\\Yaml\\Dumper`). diff --git a/contributing/documentation/license.rst b/contributing/documentation/license.rst index c6e7e294846..d8a23b0efbe 100644 --- a/contributing/documentation/license.rst +++ b/contributing/documentation/license.rst @@ -3,7 +3,7 @@ Symfony Documentation License ============================= -The Symfony2 documentation is licensed under a Creative Commons +The Symfony documentation is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License (`CC BY-SA 3.0`_). **You are free:** diff --git a/cookbook/bundles/best_practices.rst b/cookbook/bundles/best_practices.rst index 5c025c7bf92..21947295b08 100644 --- a/cookbook/bundles/best_practices.rst +++ b/cookbook/bundles/best_practices.rst @@ -67,7 +67,7 @@ class name. .. note:: - Symfony2 core Bundles do not prefix the Bundle class with ``Symfony`` + Symfony core Bundles do not prefix the Bundle class with ``Symfony`` and always add a ``Bundle`` sub-namespace; for example: :class:`Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle`. @@ -160,8 +160,7 @@ instance, a ``HelloController`` controller is stored in ``Bundle/HelloBundle/Controller/HelloController.php`` and the fully qualified class name is ``Bundle\HelloBundle\Controller\HelloController``. -All classes and files must follow the Symfony2 coding -:doc:`standards `. +All classes and files must follow the Symfony coding :doc:`standards `. Some classes should be seen as facades and should be as short as possible, like Commands, Helpers, Listeners, and Controllers. @@ -175,7 +174,7 @@ Vendors ------- A bundle must not embed third-party PHP libraries. It should rely on the -standard Symfony2 autoloading instead. +standard Symfony autoloading instead. A bundle should not embed third-party libraries written in JavaScript, CSS, or any other language. @@ -248,13 +247,13 @@ following standardized instructions in your ``README.md`` file. { $bundles = array( // ... - + new \\(), ); - + // ... } - + // ... } ``` @@ -291,10 +290,10 @@ Configuration ------------- To provide more flexibility, a bundle can provide configurable settings by -using the Symfony2 built-in mechanisms. +using the Symfony built-in mechanisms. For simple configuration settings, rely on the default ``parameters`` entry of -the Symfony2 configuration. Symfony2 parameters are simple key/value pairs; a +the Symfony configuration. Symfony parameters are simple key/value pairs; a value being any valid PHP value. Each parameter name should start with the bundle alias, though this is just a best-practice suggestion. The rest of the parameter name will use a period (``.``) to separate different parts (e.g. diff --git a/cookbook/bundles/configuration.rst b/cookbook/bundles/configuration.rst index 9d374d2dc4d..445521f9c58 100644 --- a/cookbook/bundles/configuration.rst +++ b/cookbook/bundles/configuration.rst @@ -129,7 +129,7 @@ First things first, you have to create an extension class as explained in Whenever a user includes the ``acme_social`` key (which is the DI alias) in a configuration file, the configuration under it is added to an array of -configurations and passed to the ``load()`` method of your extension (Symfony2 +configurations and passed to the ``load()`` method of your extension (Symfony automatically converts XML and YAML to an array). For the configuration example in the previous section, the array passed to your diff --git a/cookbook/bundles/override.rst b/cookbook/bundles/override.rst index bdca242f217..736c72ffa64 100644 --- a/cookbook/bundles/override.rst +++ b/cookbook/bundles/override.rst @@ -18,7 +18,7 @@ For information on overriding templates, see Routing ------- -Routing is never automatically imported in Symfony2. If you want to include +Routing is never automatically imported in Symfony. If you want to include the routes from any bundle, then they must be manually imported from somewhere in your application (e.g. ``app/config/routing.yml``). diff --git a/cookbook/bundles/remove.rst b/cookbook/bundles/remove.rst index f289ecd144c..407ee421aa4 100644 --- a/cookbook/bundles/remove.rst +++ b/cookbook/bundles/remove.rst @@ -4,7 +4,7 @@ How to Remove the AcmeDemoBundle ================================ -The Symfony2 Standard Edition comes with a complete demo that lives inside a +The Symfony Standard Edition comes with a complete demo that lives inside a bundle called AcmeDemoBundle. It is a great boilerplate to refer to while starting a project, but you'll probably want to eventually remove it. diff --git a/cookbook/event_dispatcher/before_after_filters.rst b/cookbook/event_dispatcher/before_after_filters.rst index 7fe93410713..28ca972faec 100644 --- a/cookbook/event_dispatcher/before_after_filters.rst +++ b/cookbook/event_dispatcher/before_after_filters.rst @@ -9,7 +9,7 @@ executed just before or just after your controller actions acting as filters or hooks. In symfony1, this was achieved with the preExecute and postExecute methods. -Most major frameworks have similar methods but there is no such thing in Symfony2. +Most major frameworks have similar methods but there is no such thing in Symfony. The good news is that there is a much better way to interfere with the Request -> Response process using the :doc:`EventDispatcher component `. From 40e63b92ac1d22c14b669fa45105e014099b5fad Mon Sep 17 00:00:00 2001 From: "M.Wiesner" Date: Sat, 13 Dec 2014 18:18:09 +0100 Subject: [PATCH 540/835] Update the_controller.rst Corrected error in the sample code. --- quick_tour/the_controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_controller.rst b/quick_tour/the_controller.rst index e6031ec8494..114b2e587b3 100644 --- a/quick_tour/the_controller.rst +++ b/quick_tour/the_controller.rst @@ -295,7 +295,7 @@ In a template, you can also access the ``Request`` object via the special {{ app.request.query.get('page') }} - {{ app.request.parameter('page') }} + {{ app.request.request.get('page') }} Persisting Data in the Session ------------------------------ From 1a0534df735c75344f06934dc7d9fa4f1796a935 Mon Sep 17 00:00:00 2001 From: keefe kwan Date: Sat, 13 Dec 2014 10:37:55 -0700 Subject: [PATCH 541/835] Update controllers.rst --- best_practices/controllers.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst index 5d34928526b..369d7be4e4c 100644 --- a/best_practices/controllers.rst +++ b/best_practices/controllers.rst @@ -136,6 +136,8 @@ For example: .. code-block:: php + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + /** * @Route("/{id}", name="admin_post_show") */ From 7c4cd17ba9abe065bd79c88eec1382cc64bb6894 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 08:17:32 +0100 Subject: [PATCH 542/835] Documented the console environment variables --- cookbook/configuration/environments.rst | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index c3fd907859f..ee066b0a5ae 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -212,6 +212,37 @@ environment by using this code and changing the environment string. mode. You'll need to enable that in your front controller by calling :method:`Symfony\\Component\\Debug\\Debug::enable`. +Selecting the Environment for Console Commands +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, Symfony commands are executed in the ``dev`` environment and with the +debug mode enabled. Use ``--env`` and ``-no-debug`` options to modify this behavior: + +.. code-block:: bash + + # 'dev' environment and debug enabled + $ php app/console command_name + + # 'prod' environment and debug enabled + $ php app/console command_name --env=prod + + # 'prod' environment and debug disabled + $ php app/console command_name --env=prod --no-debug + +In addition to ``--env`` and ``--debug`` options, Symfony commands behavior can +also be controlled with environment variables. The Symfony console application +checks the existence and value of these environment variables before executing +any command: + +* ``SYMFONY_ENV``, sets the execution environment of the command to the value of + this variable. +* ``SYMFONY_DEBUG``, if ``true``, debug mode is enabled for the command. If + ``false``, debug mode is disabled. + +These environment variables are very useful for production servers, because they +allow you to ensure that commands are always run on ``prod`` environment without +having to add any command option. + .. index:: single: Environments; Creating a new environment From 6fc8f4cc5cb9ee6c15479d1c20c103e3b5eda550 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 09:33:52 +0100 Subject: [PATCH 543/835] Fixed some errors in the explanation --- cookbook/configuration/environments.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index ee066b0a5ae..9338578f522 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -223,11 +223,11 @@ debug mode enabled. Use ``--env`` and ``-no-debug`` options to modify this behav # 'dev' environment and debug enabled $ php app/console command_name - # 'prod' environment and debug enabled + # 'prod' environment (debug is always disabled for 'prod') $ php app/console command_name --env=prod - # 'prod' environment and debug disabled - $ php app/console command_name --env=prod --no-debug + # 'test' environment and debug disabled + $ php app/console command_name --env=test --no-debug In addition to ``--env`` and ``--debug`` options, Symfony commands behavior can also be controlled with environment variables. The Symfony console application @@ -235,9 +235,9 @@ checks the existence and value of these environment variables before executing any command: * ``SYMFONY_ENV``, sets the execution environment of the command to the value of - this variable. -* ``SYMFONY_DEBUG``, if ``true``, debug mode is enabled for the command. If - ``false``, debug mode is disabled. + this variable (``dev``, ``prod``, ``test``, etc.) +* ``SYMFONY_DEBUG``, if ``0``, debug mode is disabled. Otherwise, debug mode is + enabled. These environment variables are very useful for production servers, because they allow you to ensure that commands are always run on ``prod`` environment without From b665d8673b9c8e352eaf5b692a5fd8b8056e6145 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 12:05:17 +0100 Subject: [PATCH 544/835] Added a note about verifying Symfony digital signatures --- book/installation.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/book/installation.rst b/book/installation.rst index f0b2fd79552..88e4becfd01 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -77,6 +77,12 @@ addition, the installer checks if your system meets the technical requirements to execute Symfony applications. If not, you'll see the list of changes needed to meet those requirements. +.. tip:: + + For security reasons, all Symfony versions are digitally signed before + distributing them. If you want to verify the integrity of any Symfony + version, follow the steps `explained in this post`_. + Basing your Project on a Specific Symfony Version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -366,6 +372,7 @@ a wide variety of articles about solving specific problems with Symfony. at this cookbook article: ":doc:`/cookbook/bundles/remove`" .. _`Symfony Release process`: http://symfony.com/doc/current/contributing/community/releases.html +.. _`explained in this post`: http://fabien.potencier.org.nyud.net/article/73/signing-project-releases .. _`Composer`: http://getcomposer.org/ .. _`Composer download page`: https://getcomposer.org/download/ .. _`Apache`: http://httpd.apache.org/docs/current/mod/core.html#documentroot From 12eb76e57ce1b516a87390ecb496634618da4869 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 12:12:05 +0100 Subject: [PATCH 545/835] Reworded a confusing phrase --- book/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index 88e4becfd01..3ce2cfaf639 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -19,9 +19,9 @@ only once and then it can create any number of Symfony applications. .. note:: The installer requires PHP 5.4 or higher. If you still use the legacy - PHP 5.3 version, you cannot use the Symfony Installer. In that case, read the next + PHP 5.3 version, you cannot use the Symfony Installer. Read :ref:`Creating Symfony Applications without the Installer ` - section. + section to know how to proceed. Depending on your operating system, the installer must be installed in different ways. From 12c25573d9d4a328c921390981cad7f1c117cc5b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 12:14:20 +0100 Subject: [PATCH 546/835] Add an explicit command to better explain that we recommend to move symfony.phar to projects directory --- book/installation.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index 3ce2cfaf639..cb9cd176b55 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -54,7 +54,8 @@ execute it as follows: .. code-block:: bash - c:\> php symfony.phar + c:\> move symfony.phar c:\projects + c:\projects\> php symfony.phar Creating the Symfony Application -------------------------------- From 7e9cd04da2b85d9ab43f324e820072e45a39a5ca Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 12:15:56 +0100 Subject: [PATCH 547/835] Display the same version number on Linux and Windows to avoid confusions --- book/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index cb9cd176b55..2049d8c0139 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -96,7 +96,7 @@ number as the second argument of the ``new`` command: $ symfony new my_project_name 2.3.23 # Windows - c:\projects\> php symfony.phar new my_project_name 2.5.8 + c:\projects\> php symfony.phar new my_project_name 2.3.23 Read the `Symfony Release process`_ to better understand why there are several Symfony versions and which one to use for your projects. From 2e72138e38d586334c8ffd165e771e78439d262e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 12:18:13 +0100 Subject: [PATCH 548/835] Wrapped a line to follow doc standards --- book/installation.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index 2049d8c0139..058fd7a91d9 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -323,7 +323,8 @@ applications: Using Source Control -------------------- -If you're using a version control system like `Git`_, you can safely commit all your project's code. The reason is that Symfony applications already contain a +If you're using a version control system like `Git`_, you can safely commit all +your project's code. The reason is that Symfony applications already contain a ``.gitignore`` file specially prepared for Symfony. For specific instructions on how best to setup your project to be stored From 2665407b94b07457642c6985090876c73006b4c6 Mon Sep 17 00:00:00 2001 From: Pedro Nofuentes Date: Sat, 29 Nov 2014 16:07:47 +0100 Subject: [PATCH 549/835] Normalize the method listings on version 2.5 --- book/routing.rst | 10 +++++---- book/security.rst | 44 ++++++++++++++++++++++---------------- book/service_container.rst | 6 ++++-- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/book/routing.rst b/book/routing.rst index 582526f2d01..0f3c2900bdf 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -967,10 +967,12 @@ header matches ``firefox``. You can do any complex logic you need in the expression by leveraging two variables that are passed into the expression: -* ``context``: An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, - which holds the most fundamental information about the route being matched; -* ``request``: The Symfony :class:`Symfony\\Component\\HttpFoundation\\Request` - object (see :ref:`component-http-foundation-request`). +``context`` + An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, which + holds the most fundamental information about the route being matched. +``request`` + The Symfony :class:`Symfony\\Component\\HttpFoundation\\Request` object + (see :ref:`component-http-foundation-request`). .. caution:: diff --git a/book/security.rst b/book/security.rst index d63d6c9a3dc..4530f439f40 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1922,28 +1922,34 @@ syntax, see :doc:`/components/expression_language/syntax`. Inside the expression, you have access to a number of variables: -* ``user`` The user object (or the string ``anon`` if you're not authenticated); -* ``roles`` The array of roles the user has, including from the - :ref:`role hierarchy ` but not including - the ``IS_AUTHENTICATED_*`` attributes (see the functions below); -* ``object``: The object (if any) that's passed as the second argument to - ``isGranted`` ; -* ``token`` The token object; -* ``trust_resolver``: The :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationTrustResolverInterface`, - object: you'll probably use the ``is_*`` functions below instead. +``user`` + The user object (or the string ``anon`` if you're not authenticated). +``roles`` + The array of roles the user has, including from the + :ref:`role hierarchy ` but not including the + ``IS_AUTHENTICATED_*`` attributes (see the functions below). +``object`` + The object (if any) that's passed as the second argument to ``isGranted``. +``token`` + The token object. +``trust_resolver`` + The :class:`Symfony\\Component\\Security\\Core\\Authentication\\AuthenticationTrustResolverInterface`, + object: you'll probably use the ``is_*`` functions below instead. Additionally, you have access to a number of functions inside the expression: -* ``is_authenticated``: Returns ``true`` if the user is authenticated via "remember-me" - or authenticated "fully" - i.e. returns true if the user is "logged in"; -* ``is_anonymous``: Equal to using ``IS_AUTHENTICATED_ANONYMOUSLY`` with - the ``isGranted`` function; -* ``is_remember_me``: Similar, but not equal to ``IS_AUTHENTICATED_REMEMBERED``, - see below; -* ``is_fully_authenticated``: Similar, but not equal to ``IS_AUTHENTICATED_FULLY``, - see below; -* ``has_role``: Checks to see if the user has the given role - equivalent - to an expression like ``'ROLE_ADMIN' in roles``. +``is_authenticated`` + Returns ``true`` if the user is authenticated via "remember-me" or authenticated + "fully" - i.e. returns true if the user is "logged in". +``is_anonymous`` + Equal to using ``IS_AUTHENTICATED_ANONYMOUSLY`` with the ``isGranted`` function. +``is_remember_me`` + Similar, but not equal to ``IS_AUTHENTICATED_REMEMBERED``, see below. +``is_fully_authenticated`` + Similar, but not equal to ``IS_AUTHENTICATED_FULLY``, see below. +``has_role`` + Checks to see if the user has the given role - equivalent to an expression like + ``'ROLE_ADMIN' in roles``. .. sidebar:: ``is_remember_me`` is different than checking ``IS_AUTHENTICATED_REMEMBERED`` diff --git a/book/service_container.rst b/book/service_container.rst index 1c939969b6f..3636e986d67 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -694,8 +694,10 @@ To learn more about the expression language syntax, see :doc:`/components/expres In this context, you have access to 2 functions: -* ``service`` - returns a given service (see the example above); -* ``parameter`` - returns a specific parameter value (syntax is just like ``service``) +``service`` + Returns a given service (see the example above). +``parameter`` + Returns a specific parameter value (syntax is just like ``service``). You also have access to the :class:`Symfony\\Component\\DependencyInjection\\ContainerBuilder` via a ``container`` variable. Here's another example: From 8ead4a42049b0eb5cc7f7fa2f5a910f840337029 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 14:13:59 +0100 Subject: [PATCH 550/835] Minor grammar fix --- cookbook/configuration/environments.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index 9338578f522..ec4b2d9ed78 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -235,7 +235,7 @@ checks the existence and value of these environment variables before executing any command: * ``SYMFONY_ENV``, sets the execution environment of the command to the value of - this variable (``dev``, ``prod``, ``test``, etc.) + this variable (``dev``, ``prod``, ``test``, etc.); * ``SYMFONY_DEBUG``, if ``0``, debug mode is disabled. Otherwise, debug mode is enabled. From a1d88d881bb022381f39e598074a38fc94b05f73 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 14:19:29 +0100 Subject: [PATCH 551/835] More grammar fixes --- cookbook/configuration/environments.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index ec4b2d9ed78..5fbb5bb0669 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -216,7 +216,8 @@ Selecting the Environment for Console Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, Symfony commands are executed in the ``dev`` environment and with the -debug mode enabled. Use ``--env`` and ``-no-debug`` options to modify this behavior: +debug mode enabled. Use the ``--env`` and ``-no-debug`` options to modify this +behavior: .. code-block:: bash @@ -229,8 +230,8 @@ debug mode enabled. Use ``--env`` and ``-no-debug`` options to modify this behav # 'test' environment and debug disabled $ php app/console command_name --env=test --no-debug -In addition to ``--env`` and ``--debug`` options, Symfony commands behavior can -also be controlled with environment variables. The Symfony console application +In addition to the ``--env`` and ``--debug`` options, Symfony commands behavior +can also be controlled with environment variables. The Symfony console application checks the existence and value of these environment variables before executing any command: From 2fb082d751190d6f6ccbef9942f319549f2958ed Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 14:25:03 +0100 Subject: [PATCH 552/835] Rewordings and transformed a list into a definition list --- cookbook/configuration/environments.rst | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index 5fbb5bb0669..c2a05382f6f 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -230,15 +230,16 @@ behavior: # 'test' environment and debug disabled $ php app/console command_name --env=test --no-debug -In addition to the ``--env`` and ``--debug`` options, Symfony commands behavior -can also be controlled with environment variables. The Symfony console application -checks the existence and value of these environment variables before executing -any command: - -* ``SYMFONY_ENV``, sets the execution environment of the command to the value of - this variable (``dev``, ``prod``, ``test``, etc.); -* ``SYMFONY_DEBUG``, if ``0``, debug mode is disabled. Otherwise, debug mode is - enabled. +In addition to the ``--env`` and ``--debug`` options, the behavior of Symfony +commands can also be controlled with environment variables. The Symfony console +application checks the existence and value of these environment variables before +executing any command: + +``SYMFONY_ENV`` + Sets the execution environment of the command to the value of this variable + (``dev``, ``prod``, ``test``, etc.); +``SYMFONY_DEBUG`` + If ``0``, debug mode is disabled. Otherwise, debug mode is enabled. These environment variables are very useful for production servers, because they allow you to ensure that commands are always run on ``prod`` environment without From dd8157f2e048de6a99488da562e2bb752bbc8a50 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 14:26:09 +0100 Subject: [PATCH 553/835] More tweaks and grammar fixes --- cookbook/configuration/environments.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index c2a05382f6f..2033c2af71a 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -241,9 +241,9 @@ executing any command: ``SYMFONY_DEBUG`` If ``0``, debug mode is disabled. Otherwise, debug mode is enabled. -These environment variables are very useful for production servers, because they -allow you to ensure that commands are always run on ``prod`` environment without -having to add any command option. +These environment variables are very useful for production servers because they +allow you to ensure that commands are always run the on ``prod`` environment +without having to add any command option. .. index:: single: Environments; Creating a new environment From 714b2ca3915cfc98aa6b08a8963fe35f929e3517 Mon Sep 17 00:00:00 2001 From: Vlad Ghita Date: Tue, 16 Dec 2014 20:31:55 +0200 Subject: [PATCH 554/835] Removed double `firewall_restriction` entry --- cookbook/map.rst.inc | 1 + cookbook/security/index.rst | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 24a0cd78371..0a71fb704de 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -147,6 +147,7 @@ * :doc:`/cookbook/security/acl_advanced` * :doc:`/cookbook/security/force_https` * :doc:`/cookbook/security/firewall_restriction` + * :doc:`/cookbook/security/host_restriction` * :doc:`/cookbook/security/form_login` * :doc:`/cookbook/security/securing_services` * :doc:`/cookbook/security/custom_provider` diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index 91f4d654188..95c7c5d884a 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -14,7 +14,6 @@ Security force_https firewall_restriction host_restriction - firewall_restriction form_login securing_services custom_provider From 40d77729fa2a4a21e8959aa211266804fa3a57a3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 15:34:02 +0100 Subject: [PATCH 555/835] Reworded the reasons why you should use Composer-based installation --- book/installation.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index 058fd7a91d9..e1e38fe039d 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -106,10 +106,9 @@ Symfony versions and which one to use for your projects. Creating Symfony Applications without the Installer --------------------------------------------------- -Due to technical reasons, some of the past Symfony versions aren't compatible -with the Symfony Installer. In addition, if you still use PHP 5.3, you can't -execute the installer. In those cases, you must use the alternative installation -method based con `Composer`_. +If you still use PHP 5.3, or if you can't execute the installer for any reason, +you can create Symfony applications using the alternative installation method +based con `Composer`_. Composer is the dependency manager used by modern PHP applications and it can also be used to create new applications based on the Symfony framework. If you From 299cc2e4eb1c31e81c5fb02f9697544c9dedf9bc Mon Sep 17 00:00:00 2001 From: Babar Al-Amin Date: Wed, 17 Dec 2014 02:07:52 +0600 Subject: [PATCH 556/835] Spelling mistake tens to tons --- quick_tour/the_architecture.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst index b3fab62cf1c..8e353c6ccd1 100644 --- a/quick_tour/the_architecture.rst +++ b/quick_tour/the_architecture.rst @@ -246,7 +246,7 @@ the Twig templating system and some other third party libraries and bundles. Understanding the Cache and Logs -------------------------------- -Symfony applications can contain tens of configuration files defined in several +Symfony applications can contain several configuration files defined in several formats (YAML, XML, PHP, etc.) Instead of parsing and combining all those files for each request, Symfony uses its own cache system. In fact, the application configuration is only parsed for the very first request and then compiled down From 1d571ca47dcc53892afb7a4d39f42be588f02596 Mon Sep 17 00:00:00 2001 From: Pedro Nofuentes Date: Sat, 29 Nov 2014 12:22:06 +0100 Subject: [PATCH 557/835] Changed unordered list to definition list --- components/http_foundation/sessions.rst | 82 ++++++++++++------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/components/http_foundation/sessions.rst b/components/http_foundation/sessions.rst index 27296fc7529..37b89c4254e 100644 --- a/components/http_foundation/sessions.rst +++ b/components/http_foundation/sessions.rst @@ -67,71 +67,71 @@ as follows divided into a couple of groups. Session workflow -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::start`: - Starts the session - do not use ``session_start()``; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::start` + Starts the session - do not use ``session_start()``. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::migrate`: - Regenerates the session ID - do not use ``session_regenerate_id()``. - This method can optionally change the lifetime of the new cookie that will - be emitted by calling this method; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::migrate` + Regenerates the session ID - do not use ``session_regenerate_id()``. + This method can optionally change the lifetime of the new cookie that will + be emitted by calling this method. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::invalidate`: - Clears all session data and regenerates session ID. Do not use ``session_destroy()``; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::invalidate` + Clears all session data and regenerates session ID. Do not use ``session_destroy()``. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getId`: Gets the - session ID. Do not use ``session_id()``; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getId` + Gets the session ID. Do not use ``session_id()``. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::setId`: Sets the - session ID. Do not use ``session_id()``; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::setId` + Sets the session ID. Do not use ``session_id()``. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getName`: Gets the - session name. Do not use ``session_name()``; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getName` + Gets the session name. Do not use ``session_name()``. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::setName`: Sets the - session name. Do not use ``session_name()``. +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::setName` + Sets the session name. Do not use ``session_name()``. Session attributes -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::set`: - Sets an attribute by key; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::set` + Sets an attribute by key. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::get`: - Gets an attribute by key; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::get` + Gets an attribute by key. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::all`: - Gets all attributes as an array of key => value; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::all` + Gets all attributes as an array of key => value. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::has`: - Returns true if the attribute exists; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::has` + Returns true if the attribute exists. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::replace`: - Sets multiple attributes at once: takes a keyed array and sets each key => value pair; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::replace` + Sets multiple attributes at once: takes a keyed array and sets each key => value pair; -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::remove`: - Deletes an attribute by key; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::remove` + Deletes an attribute by key; -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::clear`: - Clear all attributes. +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::clear` + Clear all attributes. The attributes are stored internally in a "Bag", a PHP object that acts like an array. A few methods exist for "Bag" management: -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::registerBag`: - Registers a :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface`; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::registerBag` + Registers a :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface`. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getBag`: - Gets a :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface` by - bag name; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getBag` + Gets a :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface` by + bag name; -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getFlashBag`: - Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface`. - This is just a shortcut for convenience. +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getFlashBag` + Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface`. + This is just a shortcut for convenience. Session metadata -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getMetadataBag`: - Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag` - which contains information about the session. +:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getMetadataBag` + Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag` + which contains information about the session. Session Data Management ~~~~~~~~~~~~~~~~~~~~~~~ From 6b68e486fac738daba72b8a333c54046b0b453d5 Mon Sep 17 00:00:00 2001 From: Pedro Nofuentes Date: Sat, 29 Nov 2014 12:52:36 +0100 Subject: [PATCH 558/835] Changed paragraph to heading --- components/http_foundation/sessions.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/http_foundation/sessions.rst b/components/http_foundation/sessions.rst index 37b89c4254e..dcf225bb2a0 100644 --- a/components/http_foundation/sessions.rst +++ b/components/http_foundation/sessions.rst @@ -66,6 +66,7 @@ The :class:`Symfony\\Component\\HttpFoundation\\Session\\Session` has a simple A as follows divided into a couple of groups. Session workflow +................ :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::start` Starts the session - do not use ``session_start()``. @@ -91,6 +92,7 @@ Session workflow Sets the session name. Do not use ``session_name()``. Session attributes +.................. :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::set` Sets an attribute by key. @@ -128,6 +130,7 @@ an array. A few methods exist for "Bag" management: This is just a shortcut for convenience. Session metadata +................ :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getMetadataBag` Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag` From 3649bdb40dc82231b2259485a070035a67703c60 Mon Sep 17 00:00:00 2001 From: Pedro Nofuentes Date: Sat, 29 Nov 2014 13:35:16 +0100 Subject: [PATCH 559/835] Fixed heading capitalization to follow the standards --- components/http_foundation/sessions.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/http_foundation/sessions.rst b/components/http_foundation/sessions.rst index dcf225bb2a0..1e3d262bd1a 100644 --- a/components/http_foundation/sessions.rst +++ b/components/http_foundation/sessions.rst @@ -65,7 +65,7 @@ The :class:`Symfony\\Component\\HttpFoundation\\Session\\Session` class implemen The :class:`Symfony\\Component\\HttpFoundation\\Session\\Session` has a simple API as follows divided into a couple of groups. -Session workflow +Session Workflow ................ :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::start` @@ -91,7 +91,7 @@ Session workflow :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::setName` Sets the session name. Do not use ``session_name()``. -Session attributes +Session Attributes .................. :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::set` @@ -129,7 +129,7 @@ an array. A few methods exist for "Bag" management: Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface`. This is just a shortcut for convenience. -Session metadata +Session Metadata ................ :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getMetadataBag` From dda1905c82fbab1dc939882feed56662af34a5f6 Mon Sep 17 00:00:00 2001 From: Pedro Nofuentes Date: Sat, 29 Nov 2014 15:59:09 +0100 Subject: [PATCH 560/835] Changed to definition lists --- book/page_creation.rst | 50 +++++++++------- book/propel.rst | 24 +++++--- book/routing.rst | 11 ++-- book/templating.rst | 21 ++++--- book/testing.rst | 47 +++++++-------- components/http_foundation/introduction.rst | 64 ++++++++++----------- quick_tour/the_architecture.rst | 21 ++++--- quick_tour/the_view.rst | 14 +++-- 8 files changed, 140 insertions(+), 112 deletions(-) diff --git a/book/page_creation.rst b/book/page_creation.rst index 085f98e9cc6..5054b2d02a8 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -488,13 +488,17 @@ you'll know where to find and put different types of files and why. Though entirely flexible, by default, each Symfony :term:`application` has the same basic and recommended directory structure: -* ``app/``: This directory contains the application configuration; +``app/`` + This directory contains the application configuration. -* ``src/``: All the project PHP code is stored under this directory; +``src/`` + All the project PHP code is stored under this directory. -* ``vendor/``: Any vendor libraries are placed here by convention; +``vendor/`` + Any vendor libraries are placed here by convention. -* ``web/``: This is the web root directory and contains any publicly accessible files; +``web/`` + This is the web root directory and contains any publicly accessible files. .. _the-web-directory: @@ -554,11 +558,13 @@ needs to know about your application. You don't even need to worry about these methods when starting - Symfony fills them in for you with sensible defaults. -* ``registerBundles()``: Returns an array of all bundles needed to run the - application (see :ref:`page-creation-bundles`); +``registerBundles()`` + Returns an array of all bundles needed to run the application (see + :ref:`page-creation-bundles`). -* ``registerContainerConfiguration()``: Loads the main application configuration - resource file (see the `Application Configuration`_ section). +``registerContainerConfiguration()`` + Loads the main application configuration resource file (see the + `Application Configuration`_ section). In day-to-day development, you'll mostly use the ``app/`` directory to modify configuration and routing files in the ``app/config/`` directory (see @@ -743,23 +749,27 @@ bundle system follows a set of conventions that help to keep code consistent between all Symfony bundles. Take a look at ``AcmeDemoBundle``, as it contains some of the most common elements of a bundle: -* ``Controller/`` contains the controllers of the bundle (e.g. ``RandomController.php``); +``Controller/`` + Contains the controllers of the bundle (e.g. ``RandomController.php``). -* ``DependencyInjection/`` holds certain dependency injection extension classes, - which may import service configuration, register compiler passes or more - (this directory is not necessary); +``DependencyInjection/`` + Holds certain dependency injection extension classes, which may import service + configuration, register compiler passes or more (this directory is not + necessary). -* ``Resources/config/`` houses configuration, including routing configuration - (e.g. ``routing.yml``); +``Resources/config/`` + Houses configuration, including routing configuration (e.g. ``routing.yml``). -* ``Resources/views/`` holds templates organized by controller name (e.g. - ``Hello/index.html.twig``); +``Resources/views/`` + Holds templates organized by controller name (e.g. ``Hello/index.html.twig``). -* ``Resources/public/`` contains web assets (images, stylesheets, etc) and is - copied or symbolically linked into the project ``web/`` directory via - the ``assets:install`` console command; +``Resources/public/`` + Contains web assets (images, stylesheets, etc) and is copied or symbolically + linked into the project ``web/`` directory via the ``assets:install`` console + command. -* ``Tests/`` holds all tests for the bundle. +``Tests/`` + Holds all tests for the bundle. A bundle can be as small or large as the feature it implements. It contains only the files you need and nothing else. diff --git a/book/propel.rst b/book/propel.rst index 79377a03526..7d5f919593e 100644 --- a/book/propel.rst +++ b/book/propel.rst @@ -467,14 +467,22 @@ To add a hook, just add a new method to the object class:: Propel provides the following hooks: -* ``preInsert()`` code executed before insertion of a new object -* ``postInsert()`` code executed after insertion of a new object -* ``preUpdate()`` code executed before update of an existing object -* ``postUpdate()`` code executed after update of an existing object -* ``preSave()`` code executed before saving an object (new or existing) -* ``postSave()`` code executed after saving an object (new or existing) -* ``preDelete()`` code executed before deleting an object -* ``postDelete()`` code executed after deleting an object +``preInsert()`` + Code executed before insertion of a new object. +``postInsert()`` + Code executed after insertion of a new object. +``preUpdate()`` + Code executed before update of an existing object. +``postUpdate()`` + Code executed after update of an existing object. +``preSave()`` + Code executed before saving an object (new or existing). +``postSave()`` + Code executed after saving an object (new or existing). +``preDelete()`` + Code executed before deleting an object. +``postDelete()`` + Code executed after deleting an object. Behaviors --------- diff --git a/book/routing.rst b/book/routing.rst index dac9fa376e7..8b9525bb682 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -1036,12 +1036,15 @@ As you've seen, each routing parameter or default value is eventually available as an argument in the controller method. Additionally, there are three parameters that are special: each adds a unique piece of functionality inside your application: -* ``_controller``: As you've seen, this parameter is used to determine which - controller is executed when the route is matched; +``_controller`` + As you've seen, this parameter is used to determine which controller is + executed when the route is matched. -* ``_format``: Used to set the request format (:ref:`read more `); +``_format`` + Used to set the request format (:ref:`read more `). -* ``_locale``: Used to set the locale on the request (:ref:`read more `). +``_locale`` + Used to set the locale on the request (:ref:`read more `). .. index:: single: Routing; Controllers diff --git a/book/templating.rst b/book/templating.rst index 9fe541d1ff5..46dfde2e906 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -77,15 +77,18 @@ to web designers and, in several ways, more powerful than PHP templates: Twig defines three types of special syntax: -* ``{{ ... }}``: "Says something": prints a variable or the result of an - expression to the template; - -* ``{% ... %}``: "Does something": a **tag** that controls the logic of the - template; it is used to execute statements such as for-loops for example. - -* ``{# ... #}``: "Comment something": it's the equivalent of the PHP - ``/* comment */`` syntax. It's used to add single or multi-line comments. - The content of the comments isn't included in the rendered pages. +``{{ ... }}`` + "Says something": prints a variable or the result of an expression to the + template. + +``{% ... %}`` + "Does something": a **tag** that controls the logic of the template; it is + used to execute statements such as for-loops for example. + +``{# ... #}`` + "Comment something": it's the equivalent of the PHP ``/* comment */`` syntax. + It's used to add single or multi-line comments. The content of the comments + isn't included in the rendered pages. Twig also contains **filters**, which modify content before being rendered. The following makes the ``title`` variable all uppercase before rendering diff --git a/book/testing.rst b/book/testing.rst index 9876bce5e5d..d757d1c460c 100644 --- a/book/testing.rst +++ b/book/testing.rst @@ -525,31 +525,28 @@ selects the last one on the page, and then selects its immediate parent element: Many other methods are also available: -+------------------------+----------------------------------------------------+ -| Method | Description | -+========================+====================================================+ -| ``filter('h1.title')`` | Nodes that match the CSS selector | -+------------------------+----------------------------------------------------+ -| ``filterXpath('h1')`` | Nodes that match the XPath expression | -+------------------------+----------------------------------------------------+ -| ``eq(1)`` | Node for the specified index | -+------------------------+----------------------------------------------------+ -| ``first()`` | First node | -+------------------------+----------------------------------------------------+ -| ``last()`` | Last node | -+------------------------+----------------------------------------------------+ -| ``siblings()`` | Siblings | -+------------------------+----------------------------------------------------+ -| ``nextAll()`` | All following siblings | -+------------------------+----------------------------------------------------+ -| ``previousAll()`` | All preceding siblings | -+------------------------+----------------------------------------------------+ -| ``parents()`` | Returns the parent nodes | -+------------------------+----------------------------------------------------+ -| ``children()`` | Returns children nodes | -+------------------------+----------------------------------------------------+ -| ``reduce($lambda)`` | Nodes for which the callable does not return false | -+------------------------+----------------------------------------------------+ +``filter('h1.title')`` + Nodes that match the CSS selector. +``filterXpath('h1')`` + Nodes that match the XPath expression. +``eq(1)`` + Node for the specified index. +``first()`` + First node. +``last()`` + Last node. +``siblings()`` + Siblings. +``nextAll()`` + All following siblings. +``previousAll()`` + All preceding siblings. +``parents()`` + Returns the parent nodes. +``children()`` + Returns children nodes. +``reduce($lambda)`` + Nodes for which the callable does not return false. Since each of these methods returns a new ``Crawler`` instance, you can narrow down your node selection by chaining the method calls:: diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index 059debb6dc6..6475fab2c0a 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -90,47 +90,47 @@ instance (or a sub-class of), which is a data holder class: All :class:`Symfony\\Component\\HttpFoundation\\ParameterBag` instances have methods to retrieve and update its data: -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::all`: Returns - the parameters; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::all` + Returns the parameters. -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::keys`: Returns - the parameter keys; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::keys` + Returns the parameter keys. -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::replace`: - Replaces the current parameters by a new set; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::replace` + Replaces the current parameters by a new set. -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::add`: Adds - parameters; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::add` + Adds parameters. -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::get`: Returns a - parameter by name; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::get` + Returns a parameter by name. -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::set`: Sets a - parameter by name; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::set` + Sets a parameter by name. -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::has`: Returns - ``true`` if the parameter is defined; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::has` + Returns ``true`` if the parameter is defined. -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::remove`: Removes - a parameter. +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::remove` + Removes a parameter. The :class:`Symfony\\Component\\HttpFoundation\\ParameterBag` instance also has some methods to filter the input values: -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getAlpha`: Returns - the alphabetic characters of the parameter value; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getAlpha` + Returns the alphabetic characters of the parameter value; -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getAlnum`: Returns - the alphabetic characters and digits of the parameter value; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getAlnum` + Returns the alphabetic characters and digits of the parameter value; -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getDigits`: Returns - the digits of the parameter value; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getDigits` + Returns the digits of the parameter value; -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getInt`: Returns the - parameter value converted to integer; +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getInt` + Returns the parameter value converted to integer; -* :method:`Symfony\\Component\\HttpFoundation\\ParameterBag::filter`: Filters the - parameter by using the PHP :phpfunction:`filter_var` function. +:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::filter` + Filters the parameter by using the PHP :phpfunction:`filter_var` function. All getters takes up to three arguments: the first one is the parameter name and the second one is the default value to return if the parameter does not @@ -241,14 +241,14 @@ Accessing `Accept-*` Headers Data You can easily access basic data extracted from ``Accept-*`` headers by using the following methods: -* :method:`Symfony\\Component\\HttpFoundation\\Request::getAcceptableContentTypes`: - returns the list of accepted content types ordered by descending quality; +:method:`Symfony\\Component\\HttpFoundation\\Request::getAcceptableContentTypes` + Returns the list of accepted content types ordered by descending quality. -* :method:`Symfony\\Component\\HttpFoundation\\Request::getLanguages`: - returns the list of accepted languages ordered by descending quality; +:method:`Symfony\\Component\\HttpFoundation\\Request::getLanguages` + Returns the list of accepted languages ordered by descending quality. -* :method:`Symfony\\Component\\HttpFoundation\\Request::getCharsets`: - returns the list of accepted charsets ordered by descending quality. +:method:`Symfony\\Component\\HttpFoundation\\Request::getCharsets` + Returns the list of accepted charsets ordered by descending quality. .. versionadded:: 2.2 The :class:`Symfony\\Component\\HttpFoundation\\AcceptHeader` class was diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst index 8e353c6ccd1..f8e947dae75 100644 --- a/quick_tour/the_architecture.rst +++ b/quick_tour/the_architecture.rst @@ -13,10 +13,14 @@ Understanding the Directory Structure The directory structure of a Symfony :term:`application` is rather flexible, but the recommended structure is as follows: -* ``app/``: the application configuration, templates and translations; -* ``src/``: the project's PHP code; -* ``vendor/``: the third-party dependencies; -* ``web/``: the web root directory. +``app/`` + The application configuration, templates and translations. +``src/`` + The project's PHP code. +``vendor/`` + The third-party dependencies. +``web/`` + The web root directory. The ``web/`` Directory ~~~~~~~~~~~~~~~~~~~~~~ @@ -52,10 +56,11 @@ configuration and as such, it is stored in the ``app/`` directory. This class must implement two methods: -* ``registerBundles()`` must return an array of all bundles needed to run the - application, as explained in the next section; -* ``registerContainerConfiguration()`` loads the application configuration - (more on this later). +``registerBundles()`` + Must return an array of all bundles needed to run the application, as explained + in the next section. +``registerContainerConfiguration()`` + Loads the application configuration (more on this later). Autoloading is handled automatically via `Composer`_, which means that you can use any PHP class without doing anything at all! All dependencies diff --git a/quick_tour/the_view.rst b/quick_tour/the_view.rst index 2de45855636..975418f480d 100644 --- a/quick_tour/the_view.rst +++ b/quick_tour/the_view.rst @@ -18,14 +18,16 @@ A Twig template is a text file that can generate any type of content (HTML, CSS, JavaScript, XML, CSV, LaTeX, etc.) Twig elements are separated from the rest of the template contents using any of these delimiters: -* ``{{ ... }}``: prints the content of a variable or the result of evaluating an - expression; +``{{ ... }}`` + Prints the content of a variable or the result of evaluating an expression; -* ``{% ... %}``: controls the logic of the template; it is used for example to - execute ``for`` loops and ``if`` statements; +``{% ... %}`` + Controls the logic of the template; it is used for example to execute ``for`` + loops and ``if`` statements. -* ``{# ... #}``: allows including comments inside templates. Contrary to HTML - comments, they aren't included in the rendered template. +``{# ... #}`` + Allows including comments inside templates. Contrary to HTML comments, they + aren't included in the rendered template. Below is a minimal template that illustrates a few basics, using two variables ``page_title`` and ``navigation``, which would be passed into the template: From 3fd3963ed81e7458546d2747e23c6f0398c14ec8 Mon Sep 17 00:00:00 2001 From: Pedro Nofuentes Date: Tue, 2 Dec 2014 18:04:07 +0100 Subject: [PATCH 561/835] Changed to definition lists from Book section --- book/forms.rst | 27 +++++++------ book/http_cache.rst | 82 +++++++++++++++++++++----------------- book/http_fundamentals.rst | 38 +++++++++--------- book/internals.rst | 32 +++++++-------- book/templating.rst | 35 +++++++++------- book/validation.rst | 13 +++--- 6 files changed, 124 insertions(+), 103 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index dab0a3137f3..681ff57dc70 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -716,14 +716,16 @@ the correct values of a number of field options. And though you'll need to manually add your server-side validation, these field type options can then be guessed from that information. -* ``required``: The ``required`` option can be guessed based on the validation - rules (i.e. is the field ``NotBlank`` or ``NotNull``) or the Doctrine metadata - (i.e. is the field ``nullable``). This is very useful, as your client-side - validation will automatically match your validation rules. +``required`` + The ``required`` option can be guessed based on the validation rules (i.e. is + the field ``NotBlank`` or ``NotNull``) or the Doctrine metadata (i.e. is the + field ``nullable``). This is very useful, as your client-side validation will + automatically match your validation rules. -* ``max_length``: If the field is some sort of text field, then the ``max_length`` - option can be guessed from the validation constraints (if ``Length`` or - ``Range`` is used) or from the Doctrine metadata (via the field's length). +``max_length`` + If the field is some sort of text field, then the ``max_length`` option can be + guessed from the validation constraints (if ``Length`` or ``Range`` is used) or + from the Doctrine metadata (via the field's length). .. note:: @@ -771,12 +773,13 @@ of code. Of course, you'll usually need much more flexibility when rendering: You already know the ``form_start()`` and ``form_end()`` functions, but what do the other functions do? -* ``form_errors(form)`` - Renders any errors global to the whole form - (field-specific errors are displayed next to each field); +``form_errors(form)`` + Renders any errors global to the whole form (field-specific errors are displayed + next to each field). -* ``form_row(form.dueDate)`` - Renders the label, any errors, and the HTML - form widget for the given field (e.g. ``dueDate``) inside, by default, a - ``div`` element. +``form_row(form.dueDate)`` + Renders the label, any errors, and the HTML form widget for the given field + (e.g. ``dueDate``) inside, by default, a ``div`` element. The majority of the work is done by the ``form_row`` helper, which renders the label, errors and HTML form widget of each field inside a ``div`` tag by diff --git a/book/http_cache.rst b/book/http_cache.rst index 46e80ed4e73..1a41d2bb422 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -202,34 +202,39 @@ method:: Here is a list of the main options: -* ``default_ttl``: The number of seconds that a cache entry should be - considered fresh when no explicit freshness information is provided in a - response. Explicit ``Cache-Control`` or ``Expires`` headers override this - value (default: ``0``); - -* ``private_headers``: Set of request headers that trigger "private" - ``Cache-Control`` behavior on responses that don't explicitly state whether - the response is ``public`` or ``private`` via a ``Cache-Control`` directive. - (default: ``Authorization`` and ``Cookie``); - -* ``allow_reload``: Specifies whether the client can force a cache reload by - including a ``Cache-Control`` "no-cache" directive in the request. Set it to - ``true`` for compliance with RFC 2616 (default: ``false``); - -* ``allow_revalidate``: Specifies whether the client can force a cache - revalidate by including a ``Cache-Control`` "max-age=0" directive in the - request. Set it to ``true`` for compliance with RFC 2616 (default: false); - -* ``stale_while_revalidate``: Specifies the default number of seconds (the - granularity is the second as the Response TTL precision is a second) during - which the cache can immediately return a stale response while it revalidates - it in the background (default: ``2``); this setting is overridden by the - ``stale-while-revalidate`` HTTP ``Cache-Control`` extension (see RFC 5861); - -* ``stale_if_error``: Specifies the default number of seconds (the granularity - is the second) during which the cache can serve a stale response when an - error is encountered (default: ``60``). This setting is overridden by the - ``stale-if-error`` HTTP ``Cache-Control`` extension (see RFC 5861). +``default_ttl`` + The number of seconds that a cache entry should be considered fresh when no + explicit freshness information is provided in a response. Explicit + ``Cache-Control`` or ``Expires`` headers override this value (default: ``0``). + +``private_headers`` + Set of request headers that trigger "private" ``Cache-Control`` behavior on + responses that don't explicitly state whether the response is ``public`` or + ``private`` via a ``Cache-Control`` directive (default: ``Authorization`` + and ``Cookie``). + +``allow_reload`` + Specifies whether the client can force a cache reload by including a + ``Cache-Control`` "no-cache" directive in the request. Set it to ``true`` for + compliance with RFC 2616 (default: ``false``). + +``allow_revalidate`` + Specifies whether the client can force a cache revalidate by including a + ``Cache-Control`` "max-age=0" directive in the request. Set it to ``true`` for + compliance with RFC 2616 (default: false). + +``stale_while_revalidate`` + Specifies the default number of seconds (the granularity is the second as the + Response TTL precision is a second) during which the cache can immediately + return a stale response while it revalidates it in the background (default: + ``2``); this setting is overridden by the ``stale-while-revalidate`` HTTP + ``Cache-Control`` extension (see RFC 5861). + +``stale_if_error`` + Specifies the default number of seconds (the granularity is the second) during + which the cache can serve a stale response when an error is encountered + (default: ``60``). This setting is overridden by the ``stale-if-error`` HTTP + ``Cache-Control`` extension (see RFC 5861). If ``debug`` is ``true``, Symfony automatically adds a ``X-Symfony-Cache`` header to the response containing useful information about cache hits and @@ -339,11 +344,12 @@ and then returned to every subsequent user who asked for their account page! To handle this situation, every response may be set to be public or private: -* *public*: Indicates that the response may be cached by both private and - shared caches; +*public* + Indicates that the response may be cached by both private and shared caches. -* *private*: Indicates that all or part of the response message is intended - for a single user and must not be cached by a shared cache. +*private* + Indicates that all or part of the response message is intended for a single + user and must not be cached by a shared cache. Symfony conservatively defaults each response to be private. To take advantage of shared caches (like the Symfony reverse proxy), the response will need @@ -1033,12 +1039,14 @@ possible. The ``render_esi`` helper supports two other useful options: -* ``alt``: used as the ``alt`` attribute on the ESI tag, which allows you - to specify an alternative URL to be used if the ``src`` cannot be found; +``alt`` + Used as the ``alt`` attribute on the ESI tag, which allows you to specify an + alternative URL to be used if the ``src`` cannot be found. -* ``ignore_errors``: if set to true, an ``onerror`` attribute will be added - to the ESI with a value of ``continue`` indicating that, in the event of - a failure, the gateway cache will simply remove the ESI tag silently. +``ignore_errors`` + If set to true, an ``onerror`` attribute will be added to the ESI with a value + of ``continue`` indicating that, in the event of a failure, the gateway cache + will simply remove the ESI tag silently. .. index:: single: Cache; Invalidation diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index eaadacff92f..b49729183f7 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -515,30 +515,32 @@ libraries that can be used inside *any* PHP project. These libraries, called the *Symfony Components*, contain something useful for almost any situation, regardless of how your project is developed. To name a few: -* :doc:`HttpFoundation ` - Contains - the ``Request`` and ``Response`` classes, as well as other classes for handling - sessions and file uploads; +:doc:`HttpFoundation ` + Contains the ``Request`` and ``Response`` classes, as well as other classes for + handling sessions and file uploads. -* :doc:`Routing ` - Powerful and fast routing system that - allows you to map a specific URI (e.g. ``/contact``) to some information - about how that request should be handled (e.g. execute the ``contactAction()`` - method); +:doc:`Routing ` + Powerful and fast routing system that allows you to map a specific URI + (e.g. ``/contact``) to some information about how that request should be handled + (e.g. execute the ``contactAction()`` method). -* :doc:`Form ` - A full-featured and flexible - framework for creating forms and handling form submissions; +:doc:`Form ` + A full-featured and flexible framework for creating forms and handling form + submissions. -* `Validator`_ - A system for creating rules about data and then validating - whether or not user-submitted data follows those rules; +`Validator`_ + A system for creating rules about data and then validating whether or not + user-submitted data follows those rules. -* :doc:`Templating ` - A toolkit for rendering - templates, handling template inheritance (i.e. a template is decorated with - a layout) and performing other common template tasks; +:doc:`Templating ` + A toolkit for rendering templates, handling template inheritance (i.e. a + template is decorated with a layout) and performing other common template tasks. -* :doc:`Security ` - A powerful library for - handling all types of security inside an application; +:doc:`Security ` + A powerful library for handling all types of security inside an application. -* :doc:`Translation ` - A framework for - translating strings in your application. +:doc:`Translation ` + A framework for translating strings in your application. Each and every one of these components is decoupled and can be used in *any* PHP project, regardless of whether or not you use the Symfony framework. diff --git a/book/internals.rst b/book/internals.rst index e5492ced478..87bae69895d 100644 --- a/book/internals.rst +++ b/book/internals.rst @@ -212,15 +212,15 @@ Each event thrown by the Kernel is a subclass of :class:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent`. This means that each event has access to the same basic information: -* :method:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent::getRequestType` - - returns the *type* of the request (``HttpKernelInterface::MASTER_REQUEST`` - or ``HttpKernelInterface::SUB_REQUEST``); +:method:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent::getRequestType` + Returns the *type* of the request (``HttpKernelInterface::MASTER_REQUEST`` or + ``HttpKernelInterface::SUB_REQUEST``). -* :method:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent::getKernel` - - returns the Kernel handling the request; +:method:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent::getKernel` + Returns the Kernel handling the request. -* :method:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent::getRequest` - - returns the current ``Request`` being handled. +:method:`Symfony\\Component\\HttpKernel\\Event\\KernelEvent::getRequest` + Returns the current ``Request`` being handled. ``getRequestType()`` .................... @@ -347,18 +347,18 @@ The purpose of this event is to allow other systems to modify or replace the The FrameworkBundle registers several listeners: -* :class:`Symfony\\Component\\HttpKernel\\EventListener\\ProfilerListener`: - collects data for the current request; +:class:`Symfony\\Component\\HttpKernel\\EventListener\\ProfilerListener` + Collects data for the current request. -* :class:`Symfony\\Bundle\\WebProfilerBundle\\EventListener\\WebDebugToolbarListener`: - injects the Web Debug Toolbar; +:class:`Symfony\\Bundle\\WebProfilerBundle\\EventListener\\WebDebugToolbarListener` + Injects the Web Debug Toolbar. -* :class:`Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener`: fixes the - Response ``Content-Type`` based on the request format; +:class:`Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener` + Fixes the Response ``Content-Type`` based on the request format. -* :class:`Symfony\\Component\\HttpKernel\\EventListener\\EsiListener`: adds a - ``Surrogate-Control`` HTTP header when the Response needs to be parsed for - ESI tags. +:class:`Symfony\\Component\\HttpKernel\\EventListener\\EsiListener` + Adds a ``Surrogate-Control`` HTTP header when the Response needs to be parsed + for ESI tags. .. seealso:: diff --git a/book/templating.rst b/book/templating.rst index 46dfde2e906..dd2cb88300b 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -384,15 +384,16 @@ Template Naming and Locations By default, templates can live in two different locations: -* ``app/Resources/views/``: The applications ``views`` directory can contain - application-wide base templates (i.e. your application's layouts and - templates of the application bundle) as well as templates that override - third party bundle templates (see :ref:`overriding-bundle-templates`); +``app/Resources/views/`` + The applications ``views`` directory can contain application-wide base templates + (i.e. your application's layouts and templates of the application bundle) as + well as templates that override third party bundle templates + (see :ref:`overriding-bundle-templates`). -* ``path/to/bundle/Resources/views/``: Each third party bundle houses its - templates in its ``Resources/views/`` directory (and subdirectories). When you - plan to share your bundle, you should put the templates in the bundle instead - of the ``app/`` directory. +``path/to/bundle/Resources/views/`` + Each third party bundle houses its templates in its ``Resources/views/`` + directory (and subdirectories). When you plan to share your bundle, you should + put the templates in the bundle instead of the ``app/`` directory. Most of the templates you'll use live in the ``app/Resources/views/`` directory. The path you'll use will be relative to this directory. For example, @@ -1123,12 +1124,18 @@ is a :class:`Symfony\\Bundle\\FrameworkBundle\\Templating\\GlobalVariables` instance which will give you access to some application specific variables automatically: -* ``app.security`` - The security context. -* ``app.user`` - The current user object. -* ``app.request`` - The request object. -* ``app.session`` - The session object. -* ``app.environment`` - The current environment (dev, prod, etc). -* ``app.debug`` - True if in debug mode. False otherwise. +``app.security`` + The security context. +``app.user`` + The current user object. +``app.request`` + The request object. +``app.session`` + The session object. +``app.environment`` + The current environment (dev, prod, etc). +``app.debug`` + True if in debug mode. False otherwise. .. configuration-block:: diff --git a/book/validation.rst b/book/validation.rst index 0cd59ae5aea..43d231ec393 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -809,14 +809,15 @@ user registers and when a user updates their contact information later: With this configuration, there are three validation groups: -* ``Default`` - contains the constraints in the current class and all - referenced classes that belong to no other group; +``Default`` + Contains the constraints in the current class and all referenced classes + that belong to no other group. -* ``User`` - equivalent to all constraints of the ``User`` object in the - ``Default`` group; +``User`` + Equivalent to all constraints of the ``User`` object in the ``Default`` group. -* ``registration`` - contains the constraints on the ``email`` and ``password`` - fields only. +``registration`` + Contains the constraints on the ``email`` and ``password`` fields only. To tell the validator to use a specific group, pass one or more group names as the second argument to the ``validate()`` method:: From b45c3386d6dd84a7ffebd15f097fa274ff1d8f2f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 17:39:57 +0100 Subject: [PATCH 562/835] Imrpoved the wording of the note about using using legacy PHP 5.3 version --- book/installation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index e1e38fe039d..f065473ec3f 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -19,9 +19,9 @@ only once and then it can create any number of Symfony applications. .. note:: The installer requires PHP 5.4 or higher. If you still use the legacy - PHP 5.3 version, you cannot use the Symfony Installer. Read - :ref:`Creating Symfony Applications without the Installer ` - section to know how to proceed. + PHP 5.3 version, you cannot use the Symfony Installer. Read the + :ref:`book-creating-applications-without-the-installer` section to learn how + to proceed. Depending on your operating system, the installer must be installed in different ways. From 7de83a3bf619f86226ce8d3cd997552e1148eb10 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 17 Dec 2014 18:01:09 +0100 Subject: [PATCH 563/835] Replaced a "note" by a "seealso" --- book/page_creation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/page_creation.rst b/book/page_creation.rst index b3353b27596..67915f6e639 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -493,7 +493,7 @@ the same basic and recommended directory structure: * ``vendor/``: Any vendor libraries are placed here by convention; * ``web/``: This is the web root directory and contains any publicly accessible files; -.. note:: +.. seealso:: You can easily override the default directory structure. See :doc:`/cookbook/configuration/override_dir_structure` for more From bab8f1a48de3d604afa01caafbe5d6ccce01b2c5 Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Wed, 17 Dec 2014 23:25:44 +0100 Subject: [PATCH 564/835] Remove redundant "default" connection. --- reference/configuration/doctrine.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index b91aee5422f..2a04200b361 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -26,9 +26,6 @@ Full Default Configuration #schema_filter: ^sf2_ connections: - default: - dbname: database - # A collection of different named connections (e.g. default, conn2, etc) default: dbname: ~ From df25c3675cde5664e629b505288ed0d95f7e31e6 Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Thu, 18 Dec 2014 22:58:22 +0100 Subject: [PATCH 565/835] Harmonize PHPUnit version to 4.2 or above Use the same version as the one on the dedicated "tests" page and link to phpunit.de --- contributing/code/patches.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributing/code/patches.rst b/contributing/code/patches.rst index 033d19765a3..1f9bf7000e5 100644 --- a/contributing/code/patches.rst +++ b/contributing/code/patches.rst @@ -15,7 +15,7 @@ software: * Git; * PHP version 5.3.3 or above; -* PHPUnit 3.6.4 or above. +* `PHPUnit`_ 4.2 or above. Configure Git ~~~~~~~~~~~~~ @@ -415,3 +415,4 @@ messages of all the commits. When you are finished, execute the push command. .. _`fabbot`: http://fabbot.io .. _`PSR-1`: http://www.php-fig.org/psr/psr-1/ .. _`PSR-2`: http://www.php-fig.org/psr/psr-2/ +.. _PHPUnit: https://phpunit.de/manual/current/en/installation.html From 230d27556ccae0d150c85ba2b32f36ab26bf27f9 Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Thu, 18 Dec 2014 23:18:15 +0100 Subject: [PATCH 566/835] Change PHPUnit link to avoid redirect to homepage --- contributing/code/tests.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index c876a00d559..ebec01f3d3e 100644 --- a/contributing/code/tests.rst +++ b/contributing/code/tests.rst @@ -112,5 +112,5 @@ browser. The code coverage only works if you have Xdebug enabled and all dependencies installed. -.. _install PHPUnit: http://www.phpunit.de/manual/current/en/installation.html +.. _install PHPUnit: https://phpunit.de/manual/current/en/installation.html .. _`Composer`: http://getcomposer.org/ From df2a0f2668bb353e1e69b096545bd67f8b69480a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 19 Dec 2014 09:33:56 +0100 Subject: [PATCH 567/835] Fixes and tweaks --- cookbook/configuration/environments.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/configuration/environments.rst b/cookbook/configuration/environments.rst index 2033c2af71a..b658b16f360 100644 --- a/cookbook/configuration/environments.rst +++ b/cookbook/configuration/environments.rst @@ -216,7 +216,7 @@ Selecting the Environment for Console Commands ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, Symfony commands are executed in the ``dev`` environment and with the -debug mode enabled. Use the ``--env`` and ``-no-debug`` options to modify this +debug mode enabled. Use the ``--env`` and ``--no-debug`` options to modify this behavior: .. code-block:: bash @@ -242,8 +242,8 @@ executing any command: If ``0``, debug mode is disabled. Otherwise, debug mode is enabled. These environment variables are very useful for production servers because they -allow you to ensure that commands are always run the on ``prod`` environment -without having to add any command option. +allow you to ensure that commands always run in the ``prod`` environment without +having to add any command option. .. index:: single: Environments; Creating a new environment From 9ea36719403107a841076514e05730c0c42efd66 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Thu, 11 Dec 2014 15:25:25 +0100 Subject: [PATCH 568/835] Rewrite the varnish cookbook article --- cookbook/cache/varnish.rst | 267 +++++++++++++------------------------ 1 file changed, 96 insertions(+), 171 deletions(-) diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index 8be11c7f098..de4ba0f3f8a 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -7,27 +7,102 @@ How to Use Varnish to Speed up my Website Because Symfony's cache uses the standard HTTP cache headers, the :ref:`symfony-gateway-cache` can easily be replaced with any other reverse proxy. `Varnish`_ is a powerful, open-source, HTTP accelerator capable of serving -cached content quickly and including support for :ref:`Edge Side Includes `. +cached content fast and including support for :ref:`Edge Side Includes `. -Trusting Reverse Proxies ------------------------- +.. index:: + single: Varnish; configuration + +Make Symfony Trust the Reverse Proxy +------------------------------------ For ESI to work correctly and for the :ref:`X-FORWARDED ` headers to be used, you need to configure Varnish as a :doc:`trusted proxy `. -.. index:: - single: Varnish; configuration +.. _varnish-x-forwarded-headers: + +Routing and X-FORWARDED Headers +------------------------------- + +To ensure that the Symfony Router generates URLs correctly with Varnish, +a ``X-Forwarded-Port`` header must be present for Symfony to use the +correct port number. + +This port depends on your setup. Lets say that external connections come in +on the default HTTP port 80. For HTTPS connections, there is another proxy +(as Varnish does not do HTTPS itself) on the default HTTPS port 443 that +handles the SSL termination and forwards the requests as HTTP requests to +Varnish with a ``X-Forwarded-Proto`` header. In this case, you need to add +the following configuration snippet: + +.. code-block:: varnish4 + + sub vcl_recv { + if (req.http.X-Forwarded-Proto == "https" ) { + set req.http.X-Forwarded-Port = "443"; + } else { + set req.http.X-Forwarded-Port = "80"; + } + } + +.. note:: + + Remember to configure :ref:`framework.trusted_proxies ` + in the Symfony configuration so that Varnish is seen as a trusted proxy + and the ``X-Forwarded-*`` headers are used. + + Varnish automatically forwards the IP as ``X-Forwarded-For`` and leaves + the ``X-Forwarded-Proto`` header in the request. If you do not configure + Varnish as trusted proxy, Symfony will see all requests as coming through + insecure HTTP connections from the Varnish host instead of the real client. -Configuration -------------- +If the ``X-Forwarded-Port`` header is not set correctly, Symfony will append +the port where the PHP application is running when generating absolute URLs, +e.g. ``http://example.com:8080/my/path``. -As seen previously, Symfony is smart enough to detect whether it talks to a -reverse proxy that understands ESI or not. It works out of the box when you -use the Symfony reverse proxy, but you need a special configuration to make -it work with Varnish. Thankfully, Symfony relies on yet another standard -written by Akamai (`Edge Architecture`_), so the configuration tips in this -chapter can be useful even if you don't use Symfony. +Ensure Consistent Caching Behaviour +----------------------------------- + +Varnish uses the cache headers sent by your application to determine how +to cache content. However, versions prior to Varnish 4 did not respect +``Cache-Control: no-cache``. To ensure consistent behaviour, use the following +configuration if you are still using Varnish 3: + +.. configuration-block:: + + .. code-block:: varnish3 + + sub vcl_fetch { + /* By default, Varnish3 ignores Cache-Control: no-cache and private + https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control + */ + if (beresp.http.Cache-Control ~ "no-cache" || + beresp.http.Cache-Control ~ "private" + ) { + return (hit_for_pass); + } + } + + .. code-block:: varnish2 + + sub vcl_fetch { + // By default, Varnish2 ignores Pragma: nocache and Cache-Control: no-cache and private + if (beresp.http.Cache-Control ~ "no-cache" || + beresp.http.Cache-Control ~ "private" + ) { + return (hit_for_pass); + } + } + +Enable Edge Side Includes (ESI) +------------------------------- + +As explained in the :ref:`Edge Side Includes section`, +Symfony detects whether it talks to a reverse proxy that understands ESI or +not. When you use the Symfony reverse proxy, you don't need to do anything. +But to make Varnish instead of Symfony resolve the ESI tags, you need some +configuration in Varnish. Symfony uses the ``Surrogate-Capability`` header +from the `Edge Architecture`_ described by Akamai. .. note:: @@ -58,22 +133,12 @@ Symfony adds automatically: .. code-block:: varnish4 - /* (https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#req-not-available-in-vcl-backend-response) */ sub vcl_backend_response { // Check for ESI acknowledgement and remove Surrogate-Control header if (beresp.http.Surrogate-Control ~ "ESI/1.0") { unset beresp.http.Surrogate-Control; set beresp.do_esi = true; } - /* By default Varnish ignores Pragma: nocache - (https://www.varnish-cache.org/docs/4.0/users-guide/increasing-your-hitrate.html#cache-control) - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache") { - // https://www.varnish-cache.org/docs/4.0/whats-new/upgrading.html#hit-for-pass-objects-are-created-using-beresp-uncacheable - set beresp.uncacheable = true; - set beresp.ttl = 120s; - return (deliver); - } } .. code-block:: varnish3 @@ -84,14 +149,6 @@ Symfony adds automatically: unset beresp.http.Surrogate-Control; set beresp.do_esi = true; } - /* By default Varnish ignores Cache-Control: nocache - (https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control), - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache" || - beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private") { - return (hit_for_pass); - } } .. code-block:: varnish2 @@ -102,13 +159,6 @@ Symfony adds automatically: unset beresp.http.Surrogate-Control; esi; } - /* By default Varnish ignores Cache-Control: nocache - so in order avoid caching it has to be done explicitly */ - if (beresp.http.Pragma ~ "no-cache" || - beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private") { - return (hit_for_pass); - } } .. caution:: @@ -117,6 +167,12 @@ Symfony adds automatically: (read `GZIP and Varnish`_). If you're not using Varnish 3.0, put a web server in front of Varnish to perform the compression. +.. tip:: + + If you followed the advice about ensuring a consistent caching + behaviour, those vcl functions already exist. Just append the code + to the end of the function, they won't interfere with each other. + .. index:: single: Varnish; Invalidation @@ -134,139 +190,8 @@ proxy before it has expired, it adds complexity to your caching setup. invalidation by helping you to organize your caching and invalidation setup. -Varnish can be configured to accept a special HTTP ``PURGE`` method -that will invalidate the cache for a given resource: - -.. code-block:: varnish4 - - /* - Connect to the backend server - on the local machine on port 8080 - */ - backend default { - .host = "127.0.0.1"; - .port = "8080"; - } - - sub vcl_recv { - /* - Varnish default behavior doesn't support PURGE. - Match the PURGE request and immediately do a cache lookup, - otherwise Varnish will directly pipe the request to the backend - and bypass the cache - */ - if (req.request == "PURGE") { - return(lookup); - } - } - - sub vcl_hit { - // Match PURGE request - if (req.request == "PURGE") { - // Force object expiration for Varnish < 3.0 - set obj.ttl = 0s; - // Do an actual purge for Varnish >= 3.0 - // purge; - error 200 "Purged"; - } - } - - sub vcl_miss { - /* - Match the PURGE request and - indicate the request wasn't stored in cache. - */ - if (req.request == "PURGE") { - error 404 "Not purged"; - } - } - -.. caution:: - - You must protect the ``PURGE`` HTTP method somehow to avoid random people - purging your cached data. You can do this by setting up an access list: - - .. code-block:: varnish4 - - /* - Connect to the backend server - on the local machine on port 8080 - */ - backend default { - .host = "127.0.0.1"; - .port = "8080"; - } - - // ACL's can contain IP's, subnets and hostnames - acl purge { - "localhost"; - "192.168.55.0"/24; - } - - sub vcl_recv { - // Match PURGE request to avoid cache bypassing - if (req.request == "PURGE") { - // Match client IP to the ACL - if (!client.ip ~ purge) { - // Deny access - error 405 "Not allowed."; - } - // Perform a cache lookup - return(lookup); - } - } - - sub vcl_hit { - // Match PURGE request - if (req.request == "PURGE") { - // Force object expiration for Varnish < 3.0 - set obj.ttl = 0s; - // Do an actual purge for Varnish >= 3.0 - // purge; - error 200 "Purged"; - } - } - - sub vcl_miss { - // Match PURGE request - if (req.request == "PURGE") { - // Indicate that the object isn't stored in cache - error 404 "Not purged"; - } - } - -.. _varnish-x-forwarded-headers: - -Routing and X-FORWARDED Headers -------------------------------- - -To ensure that the Symfony Router generates URLs correctly with Varnish, -proper ```X-Forwarded``` headers must be added so that Symfony is aware of -the original port number of the request. Exactly how this is done depends -on your setup. As a simple example, Varnish and your web server are on the -same machine and that Varnish is listening on one port (e.g. 80) and Apache -on another (e.g. 8080). In this situation, Varnish should add the ``X-Forwarded-Port`` -header so that the Symfony application knows that the original port number -is 80 and not 8080. - -If this header weren't set properly, Symfony may append ``8080`` when generating -absolute URLs: - -.. code-block:: varnish4 - - sub vcl_recv { - if (req.http.X-Forwarded-Proto == "https" ) { - set req.http.X-Forwarded-Port = "443"; - } else { - set req.http.X-Forwarded-Port = "80"; - } - } - -.. note:: - - Remember to configure :ref:`framework.trusted_proxies ` - in the Symfony configuration so that Varnish is seen as a trusted proxy - and the ``X-Forwarded-`` headers are used. + The documentation of the `FOSHttpCacheBundle`_ explains how to configure + Varnish and other reverse proxies for cache invalidation. .. _`Varnish`: https://www.varnish-cache.org .. _`Edge Architecture`: http://www.w3.org/TR/edge-arch From dc9430aefaeb076186c60184bb1a1ea81b9bdac8 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Fri, 19 Dec 2014 17:53:34 +0100 Subject: [PATCH 569/835] drop varnish 2 example from varnish cookbook article --- conf.py | 2 -- cookbook/cache/varnish.rst | 27 --------------------------- 2 files changed, 29 deletions(-) diff --git a/conf.py b/conf.py index 46f9687837f..291205d1d73 100644 --- a/conf.py +++ b/conf.py @@ -101,12 +101,10 @@ lexers['php-annotations'] = PhpLexer(startinline=True) lexers['php-standalone'] = PhpLexer(startinline=True) lexers['php-symfony'] = PhpLexer(startinline=True) -lexers['varnish2'] = CLexer() lexers['varnish3'] = CLexer() lexers['varnish4'] = CLexer() config_block = { - 'varnish2': 'Varnish 2', 'varnish3': 'Varnish 3', 'varnish4': 'Varnish 4' } diff --git a/cookbook/cache/varnish.rst b/cookbook/cache/varnish.rst index de4ba0f3f8a..6c53138fdd9 100644 --- a/cookbook/cache/varnish.rst +++ b/cookbook/cache/varnish.rst @@ -83,17 +83,6 @@ configuration if you are still using Varnish 3: } } - .. code-block:: varnish2 - - sub vcl_fetch { - // By default, Varnish2 ignores Pragma: nocache and Cache-Control: no-cache and private - if (beresp.http.Cache-Control ~ "no-cache" || - beresp.http.Cache-Control ~ "private" - ) { - return (hit_for_pass); - } - } - Enable Edge Side Includes (ESI) ------------------------------- @@ -151,22 +140,6 @@ Symfony adds automatically: } } - .. code-block:: varnish2 - - sub vcl_fetch { - // Check for ESI acknowledgement and remove Surrogate-Control header - if (beresp.http.Surrogate-Control ~ "ESI/1.0") { - unset beresp.http.Surrogate-Control; - esi; - } - } - -.. caution:: - - Compression with ESI was not supported in Varnish until version 3.0 - (read `GZIP and Varnish`_). If you're not using Varnish 3.0, put a web - server in front of Varnish to perform the compression. - .. tip:: If you followed the advice about ensuring a consistent caching From 5842f5c036ae3c8e704ba13dce4de67f2789653d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 20 Dec 2014 16:00:27 -0500 Subject: [PATCH 570/835] Changing and -> or based on conversation in #4532 --- cookbook/service_container/event_listener.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/service_container/event_listener.rst b/cookbook/service_container/event_listener.rst index ecba442b697..e5ae89dd6bd 100644 --- a/cookbook/service_container/event_listener.rst +++ b/cookbook/service_container/event_listener.rst @@ -59,7 +59,7 @@ event is just one of the core kernel events:: .. note:: - When setting a response for the ``kernel.request``, ``kernel.view`` and + When setting a response for the ``kernel.request``, ``kernel.view`` or ``kernel.exception`` events, the propagation is stopped, so the lower priority listeners on that event don't get called. From 9d925ffb8fb65da19eb64884caa29797f27306bd Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 20 Dec 2014 16:10:47 -0500 Subject: [PATCH 571/835] [#4608] Using #. for numbered bullets --- cookbook/workflow/new_project_svn.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cookbook/workflow/new_project_svn.rst b/cookbook/workflow/new_project_svn.rst index f7152980999..384edc58f15 100644 --- a/cookbook/workflow/new_project_svn.rst +++ b/cookbook/workflow/new_project_svn.rst @@ -53,20 +53,20 @@ First, download and get your Symfony project running by following the Once you have your new project directory and things are working, follow along with these steps: -1. Checkout the Subversion repository that will host this project. Suppose +#. Checkout the Subversion repository that will host this project. Suppose it is hosted on `Google code`_ and called ``myproject``: .. code-block:: bash $ svn checkout http://myproject.googlecode.com/svn/trunk myproject -2. Copy the Symfony project files in the Subversion folder: +#. Copy the Symfony project files in the Subversion folder: .. code-block:: bash $ mv Symfony/* myproject/ -3. Now, set the ignore rules. Not everything *should* be stored in your Subversion +#. Now, set the ignore rules. Not everything *should* be stored in your Subversion repository. Some files (like the cache) are generated and others (like the database configuration) are meant to be customized on each machine. This makes use of the ``svn:ignore`` property, so that specific files can @@ -87,21 +87,21 @@ with these steps: $ svn ci -m "commit basic Symfony ignore list (vendor, app/bootstrap*, app/config/parameters.yml, app/cache/*, app/logs/*, web/bundles)" -4. The rest of the files can now be added and committed to the project: +#. The rest of the files can now be added and committed to the project: .. code-block:: bash $ svn add --force . $ svn ci -m "add basic Symfony Standard 2.X.Y" -5. Copy ``app/config/parameters.yml`` to ``app/config/parameters.yml.dist``. +#. Copy ``app/config/parameters.yml`` to ``app/config/parameters.yml.dist``. The ``parameters.yml`` file is ignored by svn (see above) so that machine-specific settings like database passwords aren't committed. By creating the ``parameters.yml.dist`` file, new developers can quickly clone the project, copy this file to ``parameters.yml``, customize it, and start developing. -6. Finally, download all of the third-party vendor libraries by +#. Finally, download all of the third-party vendor libraries by executing Composer. For details, see :ref:`installation-updating-vendors`. .. tip:: From 3395b50a714252a73471d086911f224c02570dff Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 20 Dec 2014 16:20:06 -0500 Subject: [PATCH 572/835] Re-wording parameters.yml section and removing note about vendor #4608 The vendors was removed because we really need to keep this focused on svn and not repeat ourselves and create duplicate docs --- cookbook/workflow/new_project_svn.rst | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/cookbook/workflow/new_project_svn.rst b/cookbook/workflow/new_project_svn.rst index 384edc58f15..934faa4ce2a 100644 --- a/cookbook/workflow/new_project_svn.rst +++ b/cookbook/workflow/new_project_svn.rst @@ -94,20 +94,12 @@ with these steps: $ svn add --force . $ svn ci -m "add basic Symfony Standard 2.X.Y" -#. Copy ``app/config/parameters.yml`` to ``app/config/parameters.yml.dist``. - The ``parameters.yml`` file is ignored by svn (see above) so that - machine-specific settings like database passwords aren't committed. By - creating the ``parameters.yml.dist`` file, new developers can quickly clone - the project, copy this file to ``parameters.yml``, customize it, and start - developing. - -#. Finally, download all of the third-party vendor libraries by - executing Composer. For details, see :ref:`installation-updating-vendors`. - -.. tip:: - - If you rely on any "dev" versions, then Git may be used to install - those libraries, since there is no archive available for download. +That's it! Since the ``app/config/parameters.yml`` file is ignored, you can +store machine-specific settings like database passwords here without committing +them. The ``parameters.yml.dist`` file *is* committed, but is not read by +Symfony. And by adding any new keys you need to both files, new developers +can quickly clone the project, copy this file to ``parameters.yml``, customize +it, and start developing. At this point, you have a fully-functional Symfony project stored in your Subversion repository. The development can start with commits in the Subversion From fae8c40b7f2fe0c3fb22ca1739b3db16f4f42d2f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 12 Dec 2014 19:59:46 +0100 Subject: [PATCH 573/835] link to the API documentation --- book/stable_api.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/book/stable_api.rst b/book/stable_api.rst index 756b41c59b7..73ced34d6ea 100644 --- a/book/stable_api.rst +++ b/book/stable_api.rst @@ -20,6 +20,10 @@ in the stable API is in order to fix a security issue. The stable API is based on a whitelist, tagged with `@api`. Therefore, everything not tagged explicitly is not part of the stable API. +.. seealso:: + + You can browse the Symfony API documentation on `api.symfony.com`_. + .. tip:: Read more about the stable API in :doc:`/contributing/code/bc`. @@ -47,3 +51,5 @@ As of Symfony 2.0, the following components have a public tagged API: * Translation * Validator * Yaml + +.. _`api.symfony.com`: http://api.symfony.com From 117cb789ce6c5d5023ad5128c56d46beff6a67f9 Mon Sep 17 00:00:00 2001 From: keefe kwan Date: Sat, 13 Dec 2014 11:13:27 -0700 Subject: [PATCH 574/835] Update forms.rst Edited organize to organization. --- best_practices/forms.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/forms.rst b/best_practices/forms.rst index e78b6ddfbf7..5bd2cfb8757 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -14,7 +14,7 @@ Building Forms The Form component allows you to build forms right inside your controller code. Honestly, unless you need to reuse the form somewhere else, that's -totally fine. But for organize and reuse, we recommend that you define each +totally fine. But for organization and reuse, we recommend that you define each form in its own PHP class:: namespace AppBundle\Form; From 9039e5befe4ca5e5585a42abcf41eed446ade1b9 Mon Sep 17 00:00:00 2001 From: Benjamin Clay Date: Sat, 29 Nov 2014 15:47:37 +0100 Subject: [PATCH 575/835] Ref #3903 - Normalize methods listings --- book/forms.rst | 23 ++-- components/http_foundation/sessions.rst | 120 +++++++++--------- components/security/authorization.rst | 12 +- cookbook/form/create_custom_field_type.rst | 31 +++-- .../custom_authentication_provider.rst | 28 ++-- 5 files changed, 112 insertions(+), 102 deletions(-) diff --git a/book/forms.rst b/book/forms.rst index 681ff57dc70..7463506118d 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -167,16 +167,19 @@ helper functions: That's it! Just three lines are needed to render the complete form: -* ``form_start(form)`` - Renders the start tag of the form, including the - correct enctype attribute when using file uploads; - -* ``form_widget(form)`` - Renders all of the fields, which includes the field - element itself, a label and any validation error messages for the field; - -* ``form_end()`` - Renders the end tag of the form and any fields that have not - yet been rendered, in case you rendered each field yourself. This is useful - for rendering hidden fields and taking advantage of the automatic - :ref:`CSRF Protection `. +``form_start(form)`` + Renders the start tag of the form, including the correct enctype attribute + when using file uploads. + +``form_widget(form)`` + Renders all of the fields, which includes the field element itself, a label + and any validation error messages for the field. + +``form_end()`` + Renders the end tag of the form and any fields that have not + yet been rendered, in case you rendered each field yourself. This is useful + for rendering hidden fields and taking advantage of the automatic + :ref:`CSRF Protection `. .. seealso:: diff --git a/components/http_foundation/sessions.rst b/components/http_foundation/sessions.rst index 1e3d262bd1a..5c9d7accd94 100644 --- a/components/http_foundation/sessions.rst +++ b/components/http_foundation/sessions.rst @@ -107,10 +107,10 @@ Session Attributes Returns true if the attribute exists. :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::replace` - Sets multiple attributes at once: takes a keyed array and sets each key => value pair; + Sets multiple attributes at once: takes a keyed array and sets each key => value pair. :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::remove` - Deletes an attribute by key; + Deletes an attribute by key. :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::clear` Clear all attributes. @@ -123,7 +123,7 @@ an array. A few methods exist for "Bag" management: :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getBag` Gets a :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface` by - bag name; + bag name. :method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getFlashBag` Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface`. @@ -157,16 +157,16 @@ bag types if necessary. :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface` has the following API which is intended mainly for internal purposes: -* :method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::getStorageKey`: - Returns the key which the bag will ultimately store its array under in ``$_SESSION``. - Generally this value can be left at its default and is for internal use. +:method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::getStorageKey` + Returns the key which the bag will ultimately store its array under in ``$_SESSION``. + Generally this value can be left at its default and is for internal use. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::initialize`: - This is called internally by Symfony session storage classes to link bag data - to the session. +:method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::initialize` + This is called internally by Symfony session storage classes to link bag data + to the session. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::getName`: - Returns the name of the session bag. +:method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::getName` + Returns the name of the session bag. Attributes ~~~~~~~~~~ @@ -175,11 +175,11 @@ The purpose of the bags implementing the :class:`Symfony\\Component\\HttpFoundat is to handle session attribute storage. This might include things like user ID, and remember me login settings or other user based state information. -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag` - This is the standard default implementation. +:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag` + This is the standard default implementation. -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\NamespacedAttributeBag` - This implementation allows for attributes to be stored in a structured namespace. +:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\NamespacedAttributeBag` + This implementation allows for attributes to be stored in a structured namespace. Any plain key-value storage system is limited in the extent to which complex data can be stored since each key must be unique. You can achieve @@ -210,29 +210,29 @@ This way you can easily access a key within the stored array directly and easily :class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface` has a simple API -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::set`: - Sets an attribute by key; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::set` + Sets an attribute by key. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::get`: - Gets an attribute by key; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::get` + Gets an attribute by key. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::all`: - Gets all attributes as an array of key => value; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::all` + Gets all attributes as an array of key => value. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::has`: - Returns true if the attribute exists; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::has` + Returns true if the attribute exists. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::keys`: - Returns an array of stored attribute keys; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::keys` + Returns an array of stored attribute keys. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::replace`: - Sets multiple attributes at once: takes a keyed array and sets each key => value pair. +:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::replace` + Sets multiple attributes at once: takes a keyed array and sets each key => value pair. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::remove`: - Deletes an attribute by key; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::remove` + Deletes an attribute by key. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::clear`: - Clear the bag; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::clear` + Clear the bag. Flash Messages ~~~~~~~~~~~~~~ @@ -246,49 +246,49 @@ updated page or an error page. Flash messages set in the previous page request would be displayed immediately on the subsequent page load for that session. This is however just one application for flash messages. -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\AutoExpireFlashBag` - In this implementation, messages set in one page-load will - be available for display only on the next page load. These messages will auto - expire regardless of if they are retrieved or not. +:class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\AutoExpireFlashBag` + In this implementation, messages set in one page-load will + be available for display only on the next page load. These messages will auto + expire regardless of if they are retrieved or not. -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBag` - In this implementation, messages will remain in the session until - they are explicitly retrieved or cleared. This makes it possible to use ESI - caching. +:class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBag` + In this implementation, messages will remain in the session until + they are explicitly retrieved or cleared. This makes it possible to use ESI + caching. :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface` has a simple API -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::add`: - Adds a flash message to the stack of specified type; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::add` + Adds a flash message to the stack of specified type. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::set`: - Sets flashes by type; This method conveniently takes both single messages as - a ``string`` or multiple messages in an ``array``. +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::set` + Sets flashes by type; This method conveniently takes both single messages as + a ``string`` or multiple messages in an ``array``. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::get`: - Gets flashes by type and clears those flashes from the bag; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::get` + Gets flashes by type and clears those flashes from the bag. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::setAll`: - Sets all flashes, accepts a keyed array of arrays ``type => array(messages)``; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::setAll` + Sets all flashes, accepts a keyed array of arrays ``type => array(messages)``. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::all`: - Gets all flashes (as a keyed array of arrays) and clears the flashes from the bag; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::all` + Gets all flashes (as a keyed array of arrays) and clears the flashes from the bag. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peek`: - Gets flashes by type (read only); +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peek` + Gets flashes by type (read only). -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peekAll`: - Gets all flashes (read only) as keyed array of arrays; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peekAll` + Gets all flashes (read only) as keyed array of arrays. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::has`: - Returns true if the type exists, false if not; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::has` + Returns true if the type exists, false if not. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::keys`: - Returns an array of the stored flash types; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::keys` + Returns an array of the stored flash types. -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::clear`: - Clears the bag; +:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::clear` + Clears the bag. For simple applications it is usually sufficient to have one flash message per type, for example a confirmation notice after a form is submitted. However, diff --git a/components/security/authorization.rst b/components/security/authorization.rst index c5b357e5118..9c3000a429e 100644 --- a/components/security/authorization.rst +++ b/components/security/authorization.rst @@ -40,13 +40,13 @@ itself depends on multiple voters, and makes a final verdict based on all the votes (either positive, negative or neutral) it has received. It recognizes several strategies: -* ``affirmative`` (default) +``affirmative`` (default) grant access as soon as any voter returns an affirmative response; -* ``consensus`` +``consensus`` grant access if there are more voters granting access than there are denying; -* ``unanimous`` +``unanimous`` only grant access if none of the voters has denied access; .. code-block:: php @@ -85,14 +85,14 @@ of :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterf which means they have to implement a few methods which allows the decision manager to use them: -* ``supportsAttribute($attribute)`` +``supportsAttribute($attribute)`` will be used to check if the voter knows how to handle the given attribute; -* ``supportsClass($class)`` +``supportsClass($class)`` will be used to check if the voter is able to grant or deny access for an object of the given class; -* ``vote(TokenInterface $token, $object, array $attributes)`` +``vote(TokenInterface $token, $object, array $attributes)`` this method will do the actual voting and return a value equal to one of the class constants of :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface`, i.e. ``VoterInterface::ACCESS_GRANTED``, ``VoterInterface::ACCESS_DENIED`` diff --git a/cookbook/form/create_custom_field_type.rst b/cookbook/form/create_custom_field_type.rst index 3cb5f33c265..891a274f7a8 100644 --- a/cookbook/form/create_custom_field_type.rst +++ b/cookbook/form/create_custom_field_type.rst @@ -60,20 +60,23 @@ all of the logic and rendering of that field type. To see some of the logic, check out the `ChoiceType`_ class. There are three methods that are particularly important: -* ``buildForm()`` - Each field type has a ``buildForm`` method, which is where - you configure and build any field(s). Notice that this is the same method - you use to setup *your* forms, and it works the same here. - -* ``buildView()`` - This method is used to set any extra variables you'll - need when rendering your field in a template. For example, in `ChoiceType`_, - a ``multiple`` variable is set and used in the template to set (or not - set) the ``multiple`` attribute on the ``select`` field. See `Creating a Template for the Field`_ - for more details. - -* ``setDefaultOptions()`` - This defines options for your form type that - can be used in ``buildForm()`` and ``buildView()``. There are a lot of - options common to all fields (see :doc:`/reference/forms/types/form`), - but you can create any others that you need here. +``buildForm()`` + Each field type has a ``buildForm`` method, which is where + you configure and build any field(s). Notice that this is the same method + you use to setup *your* forms, and it works the same here. + +``buildView()`` + This method is used to set any extra variables you'll + need when rendering your field in a template. For example, in `ChoiceType`_, + a ``multiple`` variable is set and used in the template to set (or not + set) the ``multiple`` attribute on the ``select`` field. See `Creating a Template for the Field`_ + for more details. + +``setDefaultOptions()`` + This defines options for your form type that + can be used in ``buildForm()`` and ``buildView()``. There are a lot of + options common to all fields (see :doc:`/reference/forms/types/form`), + but you can create any others that you need here. .. tip:: diff --git a/cookbook/security/custom_authentication_provider.rst b/cookbook/security/custom_authentication_provider.rst index be18a236549..e2f7706ca5b 100644 --- a/cookbook/security/custom_authentication_provider.rst +++ b/cookbook/security/custom_authentication_provider.rst @@ -333,18 +333,22 @@ create a class which implements The :class:`Symfony\\Bundle\\SecurityBundle\\DependencyInjection\\Security\\Factory\\SecurityFactoryInterface` requires the following methods: -* ``create`` method, which adds the listener and authentication provider - to the DI container for the appropriate security context; - -* ``getPosition`` method, which must be of type ``pre_auth``, ``form``, ``http``, - and ``remember_me`` and defines the position at which the provider is called; - -* ``getKey`` method which defines the configuration key used to reference - the provider in the firewall configuration; - -* ``addConfiguration`` method, which is used to define the configuration - options underneath the configuration key in your security configuration. - Setting configuration options are explained later in this chapter. +``create`` + Method which adds the listener and authentication provider + to the DI container for the appropriate security context. + +``getPosition`` + Method which must be of type ``pre_auth``, ``form``, ``http``, + and ``remember_me`` and defines the position at which the provider is called. + +``getKey`` + Method which defines the configuration key used to reference + the provider in the firewall configuration. + +``addConfiguration`` + Method which is used to define the configuration + options underneath the configuration key in your security configuration. + Setting configuration options are explained later in this chapter. .. note:: From d4d2236ab63037d03f83705f03b8af1b41a2466d Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Thu, 11 Dec 2014 15:17:40 +0100 Subject: [PATCH 576/835] clean up cache invalidation information on the cache chapter --- book/http_cache.rst | 64 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/book/http_cache.rst b/book/http_cache.rst index 46e80ed4e73..048d1b96e75 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -328,6 +328,13 @@ its creation more manageable:: // set a custom Cache-Control directive $response->headers->addCacheControlDirective('must-revalidate', true); +.. tip:: + + If you need to set cache headers for many different controller actions, + you might want to look into the FOSHttpCacheBundle_. It provides a way + to define cache headers based on the URL pattern and other request + properties. + Public vs private Responses ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1051,21 +1058,35 @@ Cache Invalidation "There are only two hard things in Computer Science: cache invalidation and naming things." -- Phil Karlton -You should never need to invalidate cached data because invalidation is already -taken into account natively in the HTTP cache models. If you use validation, -you never need to invalidate anything by definition; and if you use expiration -and need to invalidate a resource, it means that you set the expires date -too far away in the future. +Once an URL is cached by a caching reverse proxy, the proxy will not ask the +application for that content anymore. This allows the cache to do fast +responses and reduces the load on your application. However, you risk +delivering outdated content. A way out of this dilemma is to use long +cache lifetimes, but to actively notify the caching proxy when content +changes. Reverse proxies usually provide a channel to receive such +notifications, usually through special HTTP requests. -.. note:: +.. tip:: + + While cache invalidation sounds powerful, avoid it when possible. If you + fail to invalidate something, outdated caches will stay for a potentially + long time. Instead, use short cache lifetimes or use the validation model, + and adjust your controllers to perform efficient validation checks as + explained in :ref:`optimizing-cache-validation`. - Since invalidation is a topic specific to each type of reverse proxy, - if you don't worry about invalidation, you can switch between reverse - proxies without changing anything in your application code. + Furthermore, since invalidation is a topic specific to each type of reverse + proxy, using this concept will tie you to a specific reverse proxy or need + additional efforts to support different proxies. -Actually, all reverse proxies provide ways to purge cached data, but you -should avoid them as much as possible. The most standard way is to purge the -cache for a given URL by requesting it with the special ``PURGE`` HTTP method. +Sometimes, however, you need that extra performance you can get when +explicitly invalidating. For invalidation, your application needs to detect +when content changes and tell the cache to remove the URLs which contain +that data from its cache. + +If one content corresponds to one URL, the ``PURGE`` model works well. +You send a request to the cache proxy with the HTTP method ``PURGE`` instead +of ``GET`` and make the cache proxy detect this and remove the data from the +cache instead of going to Symfony to get a response. Here is how you can configure the Symfony reverse proxy to support the ``PURGE`` HTTP method:: @@ -1085,11 +1106,15 @@ Here is how you can configure the Symfony reverse proxy to support the return parent::invalidate($request, $catch); } + if ('127.0.0.1' !== $request->getClientIp()) { + return new Response('Invalid HTTP method', Response::HTTP_BAD_REQUEST); + } + $response = new Response(); if ($this->getStore()->purge($request->getUri())) { $response->setStatusCode(200, 'Purged'); } else { - $response->setStatusCode(404, 'Not purged'); + $response->setStatusCode(200, 'Not found'); } return $response; @@ -1101,6 +1126,18 @@ Here is how you can configure the Symfony reverse proxy to support the You must protect the ``PURGE`` HTTP method somehow to avoid random people purging your cached data. +In many applications, content is used in various URLs. More flexible concepts +exist for those cases: + +* **Banning** invalidates responses matching regular expressions on the + URL or other criteria. +* **Cache tagging** lets you add a tag for each content used in a response + so that you can invalidate all URLs containing a certain content. + +If you need such features, you should use the `FOSHttpCacheBundle`_. This +bundle documents the configuration for the caching proxy and provides +services to send invalidation requests based on URLs and Symfony routes. + Summary ------- @@ -1128,3 +1165,4 @@ Learn more from the Cookbook .. _`P6 - Caching: Browser and intermediary caches`: http://tools.ietf.org/html/draft-ietf-httpbis-p6-cache .. _`FrameworkExtraBundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/cache.html .. _`ESI`: http://www.w3.org/TR/esi-lang +.. _`FOSHttpCacheBundle`: http://foshttpcachebundle.readthedocs.org/ From f6fac501a4b9ed5cabe80ac51d139de4fb241e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20S=C4=99kalski?= Date: Mon, 22 Dec 2014 15:09:15 +0000 Subject: [PATCH 577/835] Fix for symfony code standards --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 767915ac0ab..a2b3c52b7fd 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -88,7 +88,7 @@ an ``Application`` and adds commands to it:: Date: Mon, 22 Dec 2014 09:26:03 +0100 Subject: [PATCH 578/835] Some general grammar and style fixes in the book --- book/doctrine.rst | 18 +++++++++--------- book/forms.rst | 12 ++++++------ book/from_flat_php_to_symfony2.rst | 6 +++--- book/http_cache.rst | 8 ++++---- book/http_fundamentals.rst | 4 ++-- book/installation.rst | 6 +++--- book/page_creation.rst | 10 +++++----- book/security.rst | 12 ++++++------ book/service_container.rst | 6 +++--- book/templating.rst | 6 +++--- book/testing.rst | 6 +++--- book/translation.rst | 4 ++-- book/validation.rst | 12 ++++++------ 13 files changed, 55 insertions(+), 55 deletions(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index 82e22ba1386..c0059b337d1 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -119,7 +119,7 @@ for you: .. sidebar:: Setting up the Database to be UTF8 One mistake even seasoned developers make when starting a Symfony project - is forgetting to setup default charset and collation on their database, + is forgetting to set up default charset and collation on their database, ending up with latin type collations, which are default for most databases. They might even remember to do it the very first time, but forget that it's all gone after running a relatively common command during development: @@ -339,7 +339,7 @@ see the :ref:`book-doctrine-field-types` section. You can also check out Doctrine's `Basic Mapping Documentation`_ for all details about mapping information. If you use annotations, you'll - need to prepend all annotations with ``ORM\`` (e.g. ``ORM\Column(..)``), + need to prepend all annotations with ``ORM\`` (e.g. ``ORM\Column(...)``), which is not shown in Doctrine's documentation. You'll also need to include the ``use Doctrine\ORM\Mapping as ORM;`` statement, which *imports* the ``ORM`` annotations prefix. @@ -357,7 +357,7 @@ see the :ref:`book-doctrine-field-types` section. .. note:: - When using another library or program (ie. Doxygen) that uses annotations, + When using another library or program (i.e. Doxygen) that uses annotations, you should place the ``@IgnoreAnnotation`` annotation on the class to indicate which annotations Symfony should ignore. @@ -385,7 +385,7 @@ a regular PHP class, you need to create getter and setter methods (e.g. ``getNam $ php app/console doctrine:generate:entities AppBundle/Entity/Product -This command makes sure that all of the getters and setters are generated +This command makes sure that all the getters and setters are generated for the ``Product`` class. This is a safe command - you can run it over and over again: it only generates getters and setters that don't exist (i.e. it doesn't replace your existing methods). @@ -432,7 +432,7 @@ mapping information) of a bundle or an entire namespace: .. note:: Doctrine doesn't care whether your properties are ``protected`` or ``private``, - or whether or not you have a getter or setter function for a property. + or whether you have a getter or setter function for a property. The getters and setters are generated here only because you'll need them to interact with your PHP object. @@ -770,7 +770,7 @@ already did in the previous section). The DQL syntax is incredibly powerful, allowing you to easily join between entities (the topic of :ref:`relations ` will be -covered later), group, etc. For more information, see the official Doctrine +covered later), group, etc. For more information, see the official `Doctrine Query Language`_ documentation. Custom Repository Classes @@ -833,7 +833,7 @@ used earlier to generate the missing getter and setter methods: $ php app/console doctrine:generate:entities AppBundle Next, add a new method - ``findAllOrderedByName()`` - to the newly generated -repository class. This method will query for all of the ``Product`` entities, +repository class. This method will query for all the ``Product`` entities, ordered alphabetically. .. code-block:: php @@ -1352,7 +1352,7 @@ Doctrine's `Lifecycle Events documentation`_. transforming data in the entity (e.g. setting a created/updated field, generating a slug value). - If you need to do some heavier lifting - like perform logging or send + If you need to do some heavier lifting - like performing logging or sending an email - you should register an external class as an event listener or subscriber and give it access to whatever resources you need. For more information, see :doc:`/cookbook/doctrine/event_listeners_subscribers`. @@ -1362,7 +1362,7 @@ Doctrine's `Lifecycle Events documentation`_. Doctrine Field Types Reference ------------------------------ -Doctrine comes with a large number of field types available. Each of these +Doctrine comes with numerous field types available. Each of these maps a PHP data type to a specific column type in whatever database you're using. For each field type, the ``Column`` can be configured further, setting the ``length``, ``nullable`` behavior, ``name`` and other options. To see a diff --git a/book/forms.rst b/book/forms.rst index 681ff57dc70..33ac67e0806 100644 --- a/book/forms.rst +++ b/book/forms.rst @@ -170,7 +170,7 @@ That's it! Just three lines are needed to render the complete form: * ``form_start(form)`` - Renders the start tag of the form, including the correct enctype attribute when using file uploads; -* ``form_widget(form)`` - Renders all of the fields, which includes the field +* ``form_widget(form)`` - Renders all the fields, which includes the field element itself, a label and any validation error messages for the field; * ``form_end()`` - Renders the end tag of the form and any fields that have not @@ -262,7 +262,7 @@ possible paths: .. note:: You can use the method :method:`Symfony\\Component\\Form\\FormInterface::isSubmitted` - to check whether a form was submitted, regardless of whether or not the + to check whether a form was submitted, regardless of whether the submitted data is actually valid. #. When the user submits the form with valid data, the submitted data is again @@ -330,9 +330,9 @@ Form Validation In the previous section, you learned how a form can be submitted with valid or invalid data. In Symfony, validation is applied to the underlying object (e.g. ``Task``). In other words, the question isn't whether the "form" is -valid, but whether or not the ``$task`` object is valid after the form has +valid, but whether the ``$task`` object is valid after the form has applied the submitted data to it. Calling ``$form->isValid()`` is a shortcut -that asks the ``$task`` object whether or not it has valid data. +that asks the ``$task`` object whether it has valid data. Validation is done by adding a set of rules (called constraints) to a class. To see this in action, add validation constraints so that the ``task`` field cannot @@ -639,7 +639,7 @@ the documentation for each type. option on your field to ``false`` or :ref:`disable HTML5 validation `. - Also note that setting the ``required`` option to ``true`` will **not** + Also, note that setting the ``required`` option to ``true`` will **not** result in server-side validation to be applied. In other words, if a user submits a blank value for the field (either with an old browser or web service, for example), it will be accepted as a valid value unless @@ -1486,7 +1486,7 @@ In Twig, every block needed is defined in a single template file (e.g. file, you can see every block needed to render a form and every default field type. -In PHP, the fragments are individual template files. By default they are located in +In PHP, the fragments are individual template files. By default, they are located in the `Resources/views/Form` directory of the framework bundle (`view on GitHub`_). Each fragment name follows the same basic pattern and is broken up into two pieces, diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index f9c90d5d6e1..dc6d463bd65 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -126,7 +126,7 @@ is primarily an HTML file that uses a template-like PHP syntax: -By convention, the file that contains all of the application logic - ``index.php`` - +By convention, the file that contains all the application logic - ``index.php`` - is known as a "controller". The term :term:`controller` is a word you'll hear a lot, regardless of the language or framework you use. It refers simply to the area of *your* code that processes user input and prepares the response. @@ -177,7 +177,7 @@ of the application are isolated in a new file called ``model.php``: .. tip:: - The filename ``model.php`` is used because the logic and data access of + The file name ``model.php`` is used because the logic and data access of an application is traditionally known as the "model" layer. In a well-organized application, the majority of the code representing your "business logic" should live in the model (as opposed to living in a controller). And unlike @@ -244,7 +244,7 @@ the layout: -You've now introduced a methodology that allows for the reuse of the +You've now introduced a methodology that allows reusing the layout. Unfortunately, to accomplish this, you're forced to use a few ugly PHP functions (``ob_start()``, ``ob_get_clean()``) in the template. Symfony uses a Templating component that allows this to be accomplished cleanly diff --git a/book/http_cache.rst b/book/http_cache.rst index 1a41d2bb422..b09ce90dde0 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -236,7 +236,7 @@ Here is a list of the main options: (default: ``60``). This setting is overridden by the ``stale-if-error`` HTTP ``Cache-Control`` extension (see RFC 5861). -If ``debug`` is ``true``, Symfony automatically adds a ``X-Symfony-Cache`` +If ``debug`` is ``true``, Symfony automatically adds an ``X-Symfony-Cache`` header to the response containing useful information about cache hits and misses. @@ -427,7 +427,7 @@ on a cache to store and return "fresh" responses. model of the specification dominates your work. Unfortunately, the actual specification document - `RFC 2616`_ - can be difficult to read. - There is an on-going effort (`HTTP Bis`_) to rewrite the RFC 2616. It does + There is an ongoing effort (`HTTP Bis`_) to rewrite the RFC 2616. It does not describe a new version of HTTP, but mostly clarifies the original HTTP specification. The organization is also improved as the specification is split into seven parts; everything related to HTTP caching can be @@ -482,7 +482,7 @@ The resulting HTTP header will look like this: timezone as required by the specification. Note that in HTTP versions before 1.1 the origin server wasn't required to -send the ``Date`` header. Consequently the cache (e.g. the browser) might +send the ``Date`` header. Consequently, the cache (e.g. the browser) might need to rely on the local clock to evaluate the ``Expires`` header making the lifetime calculation vulnerable to clock skew. Another limitation of the ``Expires`` header is that the specification states that "HTTP/1.1 @@ -529,7 +529,7 @@ stale. The validation model addresses this issue. Under this model, the cache continues to store responses. The difference is that, for each request, the cache asks -the application whether or not the cached response is still valid. If the +the application whether the cached response is still valid. If the cache *is* still valid, your application should return a 304 status code and no content. This tells the cache that it's ok to return the cached response. diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index b49729183f7..74da155cc7f 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -38,7 +38,7 @@ how you develop on the web, the goal of your server is *always* to understand simple text requests, and return simple text responses. Symfony is built from the ground up around that reality. Whether you realize -it or not, HTTP is something you use everyday. With Symfony, you'll learn +it or not, HTTP is something you use every day. With Symfony, you'll learn how to master it. .. index:: @@ -542,7 +542,7 @@ regardless of how your project is developed. To name a few: :doc:`Translation ` A framework for translating strings in your application. -Each and every one of these components is decoupled and can be used in *any* +Each one of these components is decoupled and can be used in *any* PHP project, regardless of whether or not you use the Symfony framework. Every part is made to be used if needed and replaced when necessary. diff --git a/book/installation.rst b/book/installation.rst index f065473ec3f..66f32524942 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -279,7 +279,7 @@ If there are any issues, correct them now before moving on. Updating Symfony Applications ----------------------------- -At this point, you've create a fully-functional Symfony application in which +At this point, you've created a fully-functional Symfony application in which you'll start to develop your own project. A Symfony application depends on a number of external libraries. These are downloaded into the ``vendor/`` directory and they are managed exclusively by Composer. @@ -326,10 +326,10 @@ If you're using a version control system like `Git`_, you can safely commit all your project's code. The reason is that Symfony applications already contain a ``.gitignore`` file specially prepared for Symfony. -For specific instructions on how best to setup your project to be stored +For specific instructions on how best to set up your project to be stored in Git, see :doc:`/cookbook/workflow/new_project_git`. -Checking out a Versioned Symfony Application +Checking out a versioned Symfony Application ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When using Composer to manage application's dependencies, it's recommended to diff --git a/book/page_creation.rst b/book/page_creation.rst index cf8ca140e58..32fd6cf5154 100644 --- a/book/page_creation.rst +++ b/book/page_creation.rst @@ -93,7 +93,7 @@ Before you begin: Create the Bundle ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before you begin, you'll need to create a *bundle*. In Symfony, a :term:`bundle` -is like a plugin, except that all of the code in your application will live +is like a plugin, except that all the code in your application will live inside a bundle. A bundle is nothing more than a directory that houses everything related @@ -108,7 +108,7 @@ create the route. To create a bundle called ``AcmeDemoBundle`` (a play bundle that you'll build in this chapter), run the following command and follow the on-screen -instructions (use all of the default options): +instructions (use all the default options): .. code-block:: bash @@ -327,7 +327,7 @@ An optional, but common, third step in the process is to create a template. Optional Step 3: Create the Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Templates allow you to move all of the presentation (e.g. HTML code) into +Templates allow you to move all the presentation code (e.g. HTML) into a separate file and reuse different portions of the page layout. Instead of writing the HTML inside the controller, render a template instead: @@ -607,7 +607,7 @@ You'll learn more about each of these directories in later chapters. The Source (``src``) Directory ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Put simply, the ``src/`` directory contains all of the actual code (PHP code, +Put simply, the ``src/`` directory contains all the actual code (PHP code, templates, configuration files, stylesheets, etc) that drives *your* application. When developing, the vast majority of your work will be done inside one or more bundles that you create in this directory. @@ -788,7 +788,7 @@ bundle. Application Configuration ------------------------- -An application consists of a collection of bundles representing all of the +An application consists of a collection of bundles representing all the features and capabilities of your application. Each bundle can be customized via configuration files written in YAML, XML or PHP. By default, the main configuration file lives in the ``app/config/`` directory and is called diff --git a/book/security.rst b/book/security.rst index 1a5d90bc823..41efa44eeda 100644 --- a/book/security.rst +++ b/book/security.rst @@ -171,7 +171,7 @@ Firewalls (Authentication) When a user makes a request to a URL that's protected by a firewall, the security system is activated. The job of the firewall is to determine whether -or not the user needs to be authenticated, and if they do, to send a response +the user needs to be authenticated, and if they do, to send a response back to the user initiating the authentication process. A firewall is activated when the URL of an incoming request matches the configured @@ -708,7 +708,7 @@ see :doc:`/cookbook/security/form_login`. Next, make sure that your ``check_path`` URL (e.g. ``/login_check``) is behind the firewall you're using for your form login (in this example, the single firewall matches *all* URLs, including ``/login_check``). If - ``/login_check`` doesn't match any firewall, you'll receive a ``Unable + ``/login_check`` doesn't match any firewall, you'll receive an ``Unable to find the controller for path "/login_check"`` exception. **4. Multiple firewalls don't share security context** @@ -825,7 +825,7 @@ things: ................... Symfony creates an instance of :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher` -for each ``access_control`` entry, which determines whether or not a given +for each ``access_control`` entry, which determines whether a given access control should be used on this request. The following ``access_control`` options are used for matching: @@ -1458,7 +1458,7 @@ key in ``app/config/security.yml``. When you allow a user to submit a plaintext password (e.g. registration form, change password form), you *must* have validation that guarantees - that the password is 4096 characters or less. Read more details in + that the password is 4096 characters or fewer. Read more details in :ref:`How to implement a simple Registration Form `. Retrieving the User Object @@ -1659,7 +1659,7 @@ Roles The idea of a "role" is key to the authorization process. Each user is assigned a set of roles and then each resource requires one or more roles. If the user -has any one of the required roles, access is granted. Otherwise access is denied. +has any one of the required roles, access is granted. Otherwise, access is denied. Roles are pretty simple, and are basically strings that you can invent and use as needed (though roles are objects internally). For example, if you @@ -2062,7 +2062,7 @@ Security can be a deep and complex issue to solve correctly in your application. Fortunately, Symfony's Security component follows a well-proven security model based around *authentication* and *authorization*. Authentication, which always happens first, is handled by a firewall whose job is to determine -the identity of the user through several different methods (e.g. HTTP authentication, +the identity of the user through several methods (e.g. HTTP authentication, login form, etc). In the cookbook, you'll find examples of other methods for handling authentication, including how to implement a "remember me" cookie functionality. diff --git a/book/service_container.rst b/book/service_container.rst index ea1996179b2..a5253d7b3bc 100644 --- a/book/service_container.rst +++ b/book/service_container.rst @@ -174,7 +174,7 @@ is never created. This saves memory and increases the speed of your application. This also means that there's very little or no performance hit for defining lots of services. Services that are never used are never constructed. -As an added bonus, the ``Mailer`` service is only created once and the same +As a bonus, the ``Mailer`` service is only created once and the same instance is returned each time you ask for the service. This is almost always the behavior you'll need (it's more flexible and powerful), but you'll learn later how you can configure a service that has multiple instances in the @@ -525,7 +525,7 @@ In this case, the extension allows you to customize the ``error_handler``, the FrameworkBundle uses the options specified here to define and configure the services specific to it. The bundle takes care of creating all the necessary ``parameters`` and ``services`` for the service container, while still allowing -much of the configuration to be easily customized. As an added bonus, most +much of the configuration to be easily customized. As a bonus, most service container extensions are also smart enough to perform validation - notifying you of options that are missing or the wrong data type. @@ -964,7 +964,7 @@ console. To show all services and the class for each service, run: $ php app/console container:debug -By default only public services are shown, but you can also view private services: +By default, only public services are shown, but you can also view private services: .. code-block:: bash diff --git a/book/templating.rst b/book/templating.rst index dd2cb88300b..4b003e8bad6 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -408,7 +408,7 @@ Referencing Templates in a Bundle ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Symfony uses a **bundle**:**directory**:**filename** string syntax for -templates that live inside a bundle. This allows for several different types of +templates that live inside a bundle. This allows for several types of templates, each which lives in a specific location: * ``AcmeBlogBundle:Blog:index.html.twig``: This syntax is used to specify a @@ -504,7 +504,7 @@ Including other Templates ~~~~~~~~~~~~~~~~~~~~~~~~~ You'll often want to include the same template or code fragment on several -different pages. For example, in an application with "news articles", the +pages. For example, in an application with "news articles", the template code displaying an article might be used on the article detail page, on a page displaying the most popular articles, or in a list of the latest articles. @@ -1521,7 +1521,7 @@ Templates are a generic way to render content in *any* format. And while in most cases you'll use templates to render HTML content, a template can just as easily generate JavaScript, CSS, XML or any other format you can dream of. -For example, the same "resource" is often rendered in several different formats. +For example, the same "resource" is often rendered in several formats. To render an article index page in XML, simply include the format in the template name: diff --git a/book/testing.rst b/book/testing.rst index d757d1c460c..d7e98d551fa 100644 --- a/book/testing.rst +++ b/book/testing.rst @@ -47,7 +47,7 @@ Unit Tests A unit test is usually a test against a specific PHP class. If you want to test the overall behavior of your application, see the section about `Functional Tests`_. -Writing Symfony unit tests is no different than writing standard PHPUnit +Writing Symfony unit tests is no different from writing standard PHPUnit unit tests. Suppose, for example, that you have an *incredibly* simple class called ``Calculator`` in the ``Utility/`` directory of your bundle:: @@ -622,7 +622,7 @@ Just like links, you select forms with the ``selectButton()`` method:: button. The ``selectButton()`` method can select ``button`` tags and submit ``input`` -tags. It uses several different parts of the buttons to find them: +tags. It uses several parts of the buttons to find them: * The ``value`` attribute value; @@ -775,7 +775,7 @@ PHPUnit Configuration Each application has its own PHPUnit configuration, stored in the ``app/phpunit.xml.dist`` file. You can edit this file to change the defaults or -create an ``app/phpunit.xml`` file to setup a configuration for your local +create an ``app/phpunit.xml`` file to set up a configuration for your local machine only. .. tip:: diff --git a/book/translation.rst b/book/translation.rst index 255cd45fd55..ba2ef11e5d1 100644 --- a/book/translation.rst +++ b/book/translation.rst @@ -392,7 +392,7 @@ Fallback Translation Locales Imagine that the user's locale is ``fr_FR`` and that you're translating the key ``Symfony is great``. To find the French translation, Symfony actually -checks translation resources for several different locales: +checks translation resources for several locales: 1. First, Symfony looks for the translation in a ``fr_FR`` translation resource (e.g. ``messages.fr_FR.xliff``); @@ -437,7 +437,7 @@ The Locale and the URL ~~~~~~~~~~~~~~~~~~~~~~ Since you can store the locale of the user in the session, it may be tempting -to use the same URL to display a resource in many different languages based +to use the same URL to display a resource in different languages based on the user's locale. For example, ``http://www.example.com/contact`` could show content in English for one user and French for another user. Unfortunately, this violates a fundamental rule of the Web: that a particular URL returns diff --git a/book/validation.rst b/book/validation.rst index 43d231ec393..b547335f8cd 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -31,7 +31,7 @@ your application:: } So far, this is just an ordinary class that serves some purpose inside your -application. The goal of validation is to tell you whether or not the data +application. The goal of validation is to tell you whether the data of an object is valid. For this to work, you'll configure a list of rules (called :ref:`constraints `) that the object must follow in order to be valid. These rules can be specified via a number of @@ -112,7 +112,7 @@ Using the ``validator`` Service Next, to actually validate an ``Author`` object, use the ``validate`` method on the ``validator`` service (class :class:`Symfony\\Component\\Validator\\Validator`). The job of the ``validator`` is easy: to read the constraints (i.e. rules) -of a class and verify whether or not the data on the object satisfies those +of a class and verify whether the data on the object satisfies those constraints. If validation fails, a non-empty list of errors (class :class:`Symfony\\Component\\Validator\\ConstraintViolationList`) is returned. Take this simple example from inside a controller:: @@ -303,13 +303,13 @@ to its class and then pass it to the ``validator`` service. Behind the scenes, a constraint is simply a PHP object that makes an assertive statement. In real life, a constraint could be: "The cake must not be burned". In Symfony, constraints are similar: they are assertions that a condition -is true. Given a value, a constraint will tell you whether or not that value +is true. Given a value, a constraint will tell you whether that value adheres to the rules of the constraint. Supported Constraints ~~~~~~~~~~~~~~~~~~~~~ -Symfony packages a large number of the most commonly-needed constraints: +Symfony packages many of the most commonly-needed constraints: .. include:: /reference/constraints/map.rst.inc @@ -686,8 +686,8 @@ Validation Groups ----------------- So far, you've been able to add constraints to a class and ask whether or -not that class passes all of the defined constraints. In some cases, however, -you'll need to validate an object against only *some* of the constraints +not that class passes all the defined constraints. In some cases, however, +you'll need to validate an object against only *some* constraints on that class. To do this, you can organize each constraint into one or more "validation groups", and then apply validation against just one group of constraints. From 5c754e0b9193861108461d6913006d91e8b49c87 Mon Sep 17 00:00:00 2001 From: frne Date: Tue, 23 Dec 2014 15:39:42 +0100 Subject: [PATCH 579/835] Fixes incorrect latin abbrev --- book/doctrine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/doctrine.rst b/book/doctrine.rst index c0059b337d1..0f3f62086d0 100644 --- a/book/doctrine.rst +++ b/book/doctrine.rst @@ -357,7 +357,7 @@ see the :ref:`book-doctrine-field-types` section. .. note:: - When using another library or program (i.e. Doxygen) that uses annotations, + When using another library or program (e.g. Doxygen) that uses annotations, you should place the ``@IgnoreAnnotation`` annotation on the class to indicate which annotations Symfony should ignore. From 86d7a6d588e4afa81af82bed386eb3cb5ce66563 Mon Sep 17 00:00:00 2001 From: frne Date: Tue, 23 Dec 2014 17:28:48 +0100 Subject: [PATCH 580/835] Some more fixes after proofreading --- book/from_flat_php_to_symfony2.rst | 6 +++--- book/http_cache.rst | 6 +++--- book/validation.rst | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index dc6d463bd65..daae6e19c37 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -177,7 +177,7 @@ of the application are isolated in a new file called ``model.php``: .. tip:: - The file name ``model.php`` is used because the logic and data access of + The filename ``model.php`` is used because the logic and data access of an application is traditionally known as the "model" layer. In a well-organized application, the majority of the code representing your "business logic" should live in the model (as opposed to living in a controller). And unlike @@ -244,8 +244,8 @@ the layout: -You've now introduced a methodology that allows reusing the -layout. Unfortunately, to accomplish this, you're forced to use a few ugly +You now have a setup that will allow you to reuse the layout. +Unfortunately, to accomplish this, you're forced to use a few ugly PHP functions (``ob_start()``, ``ob_get_clean()``) in the template. Symfony uses a Templating component that allows this to be accomplished cleanly and easily. You'll see it in action shortly. diff --git a/book/http_cache.rst b/book/http_cache.rst index b09ce90dde0..aeee80fa13b 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -528,9 +528,9 @@ won't be asked to return the updated response until the cache finally becomes stale. The validation model addresses this issue. Under this model, the cache continues -to store responses. The difference is that, for each request, the cache asks -the application whether the cached response is still valid. If the -cache *is* still valid, your application should return a 304 status code +to store responses. The difference is that, for each request, the cache asks the +application if the cached response is still valid or if it needs to be regenerated. +If the cache *is* still valid, your application should return a 304 status code and no content. This tells the cache that it's ok to return the cached response. Under this model, you only save CPU if you're able to determine that the diff --git a/book/validation.rst b/book/validation.rst index b547335f8cd..c6037c81f2f 100644 --- a/book/validation.rst +++ b/book/validation.rst @@ -31,7 +31,7 @@ your application:: } So far, this is just an ordinary class that serves some purpose inside your -application. The goal of validation is to tell you whether the data +application. The goal of validation is to tell you if the data of an object is valid. For this to work, you'll configure a list of rules (called :ref:`constraints `) that the object must follow in order to be valid. These rules can be specified via a number of @@ -112,7 +112,7 @@ Using the ``validator`` Service Next, to actually validate an ``Author`` object, use the ``validate`` method on the ``validator`` service (class :class:`Symfony\\Component\\Validator\\Validator`). The job of the ``validator`` is easy: to read the constraints (i.e. rules) -of a class and verify whether the data on the object satisfies those +of a class and verify if the data on the object satisfies those constraints. If validation fails, a non-empty list of errors (class :class:`Symfony\\Component\\Validator\\ConstraintViolationList`) is returned. Take this simple example from inside a controller:: @@ -303,7 +303,7 @@ to its class and then pass it to the ``validator`` service. Behind the scenes, a constraint is simply a PHP object that makes an assertive statement. In real life, a constraint could be: "The cake must not be burned". In Symfony, constraints are similar: they are assertions that a condition -is true. Given a value, a constraint will tell you whether that value +is true. Given a value, a constraint will tell you if that value adheres to the rules of the constraint. Supported Constraints From 44b03ac961304ac69ee221523c18180b5d407f8a Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 23 Dec 2014 14:30:19 -0500 Subject: [PATCH 581/835] adding note to assetic cache busting --- reference/configuration/assetic.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/reference/configuration/assetic.rst b/reference/configuration/assetic.rst index f7e52faf03b..ae9415ea4fe 100644 --- a/reference/configuration/assetic.rst +++ b/reference/configuration/assetic.rst @@ -49,6 +49,9 @@ Full Default Configuration # An array of named filters (e.g. some_filter, some_other_filter) some_filter: [] workers: + # see https://github.com/symfony/AsseticBundle/pull/119 + # Cache can also be busted via the framework.templating.assets_version + # setting - see the "framework" configuration section cache_busting: enabled: false twig: From 79ad3b5c154e3a0f536f12994169f4dd7019d134 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 20 Dec 2014 23:53:43 +0100 Subject: [PATCH 582/835] add validation config reference section --- reference/configuration/framework.rst | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index bdbaf06aa96..dbe3f463ac7 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -48,6 +48,10 @@ Configuration * `translator`_ * :ref:`enabled ` * `fallback`_ +* `validation`_ + * `cache`_ + * `enable_annotations`_ + * `translation_domain`_ secret ~~~~~~ @@ -517,6 +521,35 @@ This option is used when the translation key for the current locale wasn't found For more details, see :doc:`/book/translation`. +validation +~~~~~~~~~~ + +cache +..... + +**type**: ``string`` + +This value is used to determine the service that is used to persist class +metadata in a cache. The actual service name is built by prefix the configured +value with ``validator.mapping.cache.`` (e.g. if the value is ``apc``, the +``validator.mapping.cache.apc`` service will be injected). The service has +to implement the :class:`Symfony\\Component\\Validator\\Mapping\\Cache\\CacheInterface`. + +enable_annotations +.................. + +**type**: ``Boolean`` **default**: ``false`` + +If this option is enabled, validation constraints can be defined using annotations. + +translation_domain +.................. + +**type**: ``string`` **default**: ``validators`` + +The translation domain that is used when translating validation constraint +error messages. + Full default Configuration -------------------------- @@ -646,3 +679,4 @@ Full default Configuration .. _`protocol-relative`: http://tools.ietf.org/html/rfc3986#section-4.2 .. _`PhpStormOpener`: https://github.com/pinepain/PhpStormOpener +.. _`egulias/email-validator`: https://github.com/egulias/EmailValidator From 2bddbb17ce1d7e676d6d3be846ad63dabde8e0f9 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Thu, 25 Dec 2014 17:57:51 +0100 Subject: [PATCH 583/835] move invalidation up into expiration and validation section --- book/http_cache.rst | 193 +++++++++++++++++++++++--------------------- 1 file changed, 100 insertions(+), 93 deletions(-) diff --git a/book/http_cache.rst b/book/http_cache.rst index 048d1b96e75..97d9de9959f 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -335,7 +335,7 @@ its creation more manageable:: to define cache headers based on the URL pattern and other request properties. -Public vs private Responses +Public vs Private Responses ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Both gateway and proxy caches are considered "shared" caches as the cached @@ -801,7 +801,105 @@ Additionally, most cache-related HTTP headers can be set via the single )); .. index:: - single: Cache; ESI +single: Cache; Invalidation + +.. _http-cache-invalidation: + +Cache Invalidation +~~~~~~~~~~~~~~~~~~ + + "There are only two hard things in Computer Science: cache invalidation + and naming things." -- Phil Karlton + +Once an URL is cached by a gateway cache, the cache will not ask the +application for that content anymore. This allows the cache to provide fast +responses and reduces the load on your application. However, you risk +delivering outdated content. A way out of this dilemma is to use long +cache lifetimes, but to actively notify the gateway cache when content +changes. Reverse proxies usually provide a channel to receive such +notifications, typically through special HTTP requests. + +.. tip:: + + While cache invalidation is powerful, avoid it when possible. If you fail + to invalidate something, outdated caches will be served for a potentially + long time. Instead, use short cache lifetimes or use the validation model, + and adjust your controllers to perform efficient validation checks as + explained in :ref:`optimizing-cache-validation`. + + Furthermore, since invalidation is a topic specific to each type of reverse + proxy, using this concept will tie you to a specific reverse proxy or need + additional efforts to support different proxies. + +Sometimes, however, you need that extra performance you can get when +explicitly invalidating. For invalidation, your application needs to detect +when content changes and tell the cache to remove the URLs which contain +that data from its cache. + +If one content corresponds to one URL, the ``PURGE`` model works well. +You send a request to the cache proxy with the HTTP method ``PURGE`` instead +of ``GET`` and make the cache proxy detect this and remove the data from the +cache instead of going to Symfony to get a response. + +Here is how you can configure the Symfony reverse proxy to support the +``PURGE`` HTTP method:: + + // app/AppCache.php + + // ... + use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + + class AppCache extends HttpCache + { + protected function invalidate(Request $request, $catch = false) + { + if ('PURGE' !== $request->getMethod()) { + return parent::invalidate($request, $catch); + } + + if ('127.0.0.1' !== $request->getClientIp()) { + return new Response('Invalid HTTP method', Response::HTTP_BAD_REQUEST); + } + + $response = new Response(); + if ($this->getStore()->purge($request->getUri())) { + $response->setStatusCode(200, 'Purged'); + } else { + $response->setStatusCode(200, 'Not found'); + } + + return $response; + } + } + +.. caution:: + + You must protect the ``PURGE`` HTTP method somehow to avoid random people + purging your cached data. + +**Purge** instructs the cache to drop a resource in *all its variants* +(according to the ``Vary`` header, see above). An alternative to purging is +**refreshing** a content. Refreshing means that the caching proxy is +instructed to discard its local cache and fetch the content again. This way, +the new content is already available in the cache. The drawback of refreshing +is that variants are not invalidated. + +In many applications, the same content bit is used on various pages with +different URLs. More flexible concepts exist for those cases: + +* **Banning** invalidates responses matching regular expressions on the + URL or other criteria. +* **Cache tagging** lets you add a tag for each content used in a response + so that you can invalidate all URLs containing a certain content. + +If you need such features, you should use the `FOSHttpCacheBundle`_. This +bundle documents the configuration for the caching proxy and provides +services to send invalidation requests based on URLs and Symfony routes. + +.. index:: +single: Cache; ESI single: ESI .. _edge-side-includes: @@ -1047,97 +1145,6 @@ The ``render_esi`` helper supports two other useful options: to the ESI with a value of ``continue`` indicating that, in the event of a failure, the gateway cache will simply remove the ESI tag silently. -.. index:: - single: Cache; Invalidation - -.. _http-cache-invalidation: - -Cache Invalidation ------------------- - - "There are only two hard things in Computer Science: cache invalidation - and naming things." -- Phil Karlton - -Once an URL is cached by a caching reverse proxy, the proxy will not ask the -application for that content anymore. This allows the cache to do fast -responses and reduces the load on your application. However, you risk -delivering outdated content. A way out of this dilemma is to use long -cache lifetimes, but to actively notify the caching proxy when content -changes. Reverse proxies usually provide a channel to receive such -notifications, usually through special HTTP requests. - -.. tip:: - - While cache invalidation sounds powerful, avoid it when possible. If you - fail to invalidate something, outdated caches will stay for a potentially - long time. Instead, use short cache lifetimes or use the validation model, - and adjust your controllers to perform efficient validation checks as - explained in :ref:`optimizing-cache-validation`. - - Furthermore, since invalidation is a topic specific to each type of reverse - proxy, using this concept will tie you to a specific reverse proxy or need - additional efforts to support different proxies. - -Sometimes, however, you need that extra performance you can get when -explicitly invalidating. For invalidation, your application needs to detect -when content changes and tell the cache to remove the URLs which contain -that data from its cache. - -If one content corresponds to one URL, the ``PURGE`` model works well. -You send a request to the cache proxy with the HTTP method ``PURGE`` instead -of ``GET`` and make the cache proxy detect this and remove the data from the -cache instead of going to Symfony to get a response. - -Here is how you can configure the Symfony reverse proxy to support the -``PURGE`` HTTP method:: - - // app/AppCache.php - - // ... - use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpFoundation\Response; - - class AppCache extends HttpCache - { - protected function invalidate(Request $request, $catch = false) - { - if ('PURGE' !== $request->getMethod()) { - return parent::invalidate($request, $catch); - } - - if ('127.0.0.1' !== $request->getClientIp()) { - return new Response('Invalid HTTP method', Response::HTTP_BAD_REQUEST); - } - - $response = new Response(); - if ($this->getStore()->purge($request->getUri())) { - $response->setStatusCode(200, 'Purged'); - } else { - $response->setStatusCode(200, 'Not found'); - } - - return $response; - } - } - -.. caution:: - - You must protect the ``PURGE`` HTTP method somehow to avoid random people - purging your cached data. - -In many applications, content is used in various URLs. More flexible concepts -exist for those cases: - -* **Banning** invalidates responses matching regular expressions on the - URL or other criteria. -* **Cache tagging** lets you add a tag for each content used in a response - so that you can invalidate all URLs containing a certain content. - -If you need such features, you should use the `FOSHttpCacheBundle`_. This -bundle documents the configuration for the caching proxy and provides -services to send invalidation requests based on URLs and Symfony routes. - Summary ------- From 979034a10a606516495bae3cbdba26d7c6d7ca5c Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Thu, 25 Dec 2014 18:07:22 +0100 Subject: [PATCH 584/835] move fos httpcache bundle tip up to beginning of invalidation section --- book/http_cache.rst | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/book/http_cache.rst b/book/http_cache.rst index 97d9de9959f..19da513be99 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -401,8 +401,8 @@ header when none is set by the developer by following these rules: .. _http-expiration-validation: -HTTP Expiration and Validation ------------------------------- +HTTP Expiration, Validation and Invalidation +-------------------------------------------- The HTTP specification defines two caching models: @@ -419,7 +419,9 @@ The HTTP specification defines two caching models: header) to check if the page has changed since being cached. The goal of both models is to never generate the same response twice by relying -on a cache to store and return "fresh" responses. +on a cache to store and return "fresh" responses. To achieve long caching times +but still provide updated content immediately, *cache invalidation* is +sometimes used. .. sidebar:: Reading the HTTP Specification @@ -819,7 +821,7 @@ cache lifetimes, but to actively notify the gateway cache when content changes. Reverse proxies usually provide a channel to receive such notifications, typically through special HTTP requests. -.. tip:: +.. warning:: While cache invalidation is powerful, avoid it when possible. If you fail to invalidate something, outdated caches will be served for a potentially @@ -836,6 +838,13 @@ explicitly invalidating. For invalidation, your application needs to detect when content changes and tell the cache to remove the URLs which contain that data from its cache. +.. tip:: + + If you want to use cache invalidation, have a look at the + `FOSHttpCacheBundle`_. This bundle provides services to help with various + cache invalidation concepts, and also documents the configuration for the + a couple of common caching proxies. + If one content corresponds to one URL, the ``PURGE`` model works well. You send a request to the cache proxy with the HTTP method ``PURGE`` instead of ``GET`` and make the cache proxy detect this and remove the data from the @@ -894,10 +903,6 @@ different URLs. More flexible concepts exist for those cases: * **Cache tagging** lets you add a tag for each content used in a response so that you can invalidate all URLs containing a certain content. -If you need such features, you should use the `FOSHttpCacheBundle`_. This -bundle documents the configuration for the caching proxy and provides -services to send invalidation requests based on URLs and Symfony routes. - .. index:: single: Cache; ESI single: ESI From b4383b56162776c08d443b8fffb3e5013bc32a48 Mon Sep 17 00:00:00 2001 From: GuGuss Date: Thu, 27 Nov 2014 17:52:14 +0100 Subject: [PATCH 585/835] Deploy Symfony application on Platform.sh. --- cookbook/deployment/index.rst | 1 + cookbook/deployment/platformsh.rst | 195 +++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) create mode 100644 cookbook/deployment/platformsh.rst diff --git a/cookbook/deployment/index.rst b/cookbook/deployment/index.rst index ef5699e8cea..7c6d2c674be 100644 --- a/cookbook/deployment/index.rst +++ b/cookbook/deployment/index.rst @@ -5,5 +5,6 @@ Deployment :maxdepth: 2 tools + platformsh azure-website heroku diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst new file mode 100644 index 00000000000..2137cff4249 --- /dev/null +++ b/cookbook/deployment/platformsh.rst @@ -0,0 +1,195 @@ +.. index:: + single: Deployment; Deploying to Platform.sh + +Deploying to Platform.sh +======================== + +This step by step cookbook describes how to deploy a Symfony web application to +`Platform.sh`_ . You can read more about using Symfony with Platform.sh on the +official `Platform.sh documentation`_. + +Deploy an existing site +----------------------- + +In this guide, we assume your codebase is already versioned with Git. + +Get a project on Platform.sh +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You need to subscribe to a `Platform.sh project`_. Choose the development plan +and go through the checkout process. + +Once your project is ready, give it a name and choose: **Import an existing +site**. + +Prepare your Application +~~~~~~~~~~~~~~~~~~~~~~~~ + +To deploy your Symfony application on Platform.sh, you simply need to add a +``.platform.app.yaml`` at the root of your Git repository which will tell +Platform.sh how to deploy your application (read more about `Platform.sh +configuration files`_). + +.. code-block:: yaml + + # This file describes an application. You can have multiple applications + # in the same project. + + # The name of this app. Must be unique within a project. + name: php + + # The toolstack used to build the application. + toolstack: "php:symfony" + + # The relationships of the application with services or other applications. + # The left-hand side is the name of the relationship as it will be exposed + # to the application in the PLATFORM_RELATIONSHIPS variable. The right-hand + # side is in the form `:`. + relationships: + database: "mysql:mysql" + + # The configuration of app when it is exposed to the web. + web: + # The public directory of the app, relative to its root. + document_root: "/web" + # The front-controller script to send non-static requests to. + passthru: "/app.php" + + # The size of the persistent disk of the application (in MB). + disk: 2048 + + # The mounts that will be performed when the package is deployed. + mounts: + "/app/cache": "shared:files/cache" + "/app/logs": "shared:files/logs" + + # The hooks that will be performed when the package is deployed. + hooks: + build: | + rm web/app_dev.php + app/console --env=prod assetic:dump --no-debug + deploy: | + app/console --env=prod cache:clear + + # The configuration of scheduled execution. + # see http://symfony.com/doc/current/components/console/introduction.html + #crons: + # symfony: + # spec: "*/20 * * * *" + # cmd: "php cron.php example:test" + +For best practices, you should also add a ``.platform`` folder at the root of +your Git repository which contains the following files: + +.. code-block:: yaml + + # .platform/routes.yaml + "http://{default}/": + type: upstream + upstream: "php:php" + +.. code-block:: yaml + + # .platform/services.yaml + mysql: + type: mysql + disk: 2048 + +Configure database access +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Platform.sh overrides your database specific configuration via importing the +following file: + +.. code-block:: yaml + + # app/config/parameters_platform.php + setParameter('database_driver', 'pdo_' . $endpoint['scheme']); + $container->setParameter('database_host', $endpoint['host']); + $container->setParameter('database_port', $endpoint['port']); + $container->setParameter('database_name', $endpoint['path']); + $container->setParameter('database_user', $endpoint['username']); + $container->setParameter('database_password', $endpoint['password']); + $container->setParameter('database_path', ''); + } + + # Hack. + ini_set('session.save_path', '/tmp/sessions'); + +Make sure this file is listed in your *imports*: + +.. code-block:: yaml + + # app/config/config.yml + imports: + - { resource: parameters_platform.php } + +Deploy your Application +~~~~~~~~~~~~~~~~~~~~~~~ + +Now you need to add a remote to Platform.sh in your Git repository (copy the +command that you see on the Platform.sh web UI): + +.. code-block:: bash + + $ git remote add platform kjh43kbobssae@git.eu.platform.sh:kjh43kbobssae.git + +Commit the Platform.sh specific files created in the previous section: + +.. code-block:: bash + + $ git add .platform.app.yaml .platform/* + $ git add app/config/config.yml app/config/parameters_platform.php + $ git commit -m "Adding Platform.sh configuration files." + +Push your code base to the newly added remote: + +.. code-block:: bash + + $ git push -u platform master + + Counting objects: 27, done. + Delta compression using up to 4 threads. + Compressing objects: 100% (11/11), done. + Writing objects: 100% (16/16), 2.47 KiB | 0 bytes/s, done. + Total 16 (delta 7), reused 12 (delta 5) + + Processing activity environment.push + Found 213 new commits. + + Building application 'php' with toolstack 'php:symfony' (tree: 2248cf8) + Found a `composer.json`, installing dependencies. + ... + +That's it! Your application is being deployed on Platform.sh and you'll soon be +able to access it in your browser. + +Deploy a new site +----------------- + +You can start a new `Platform.sh project`_. Choose the development plan and go +through the checkout process. + +Once your project is ready, give it a name and choose: **Create a new site**. +Choose the *Symfony* stack and a starting point such as *Standard*. + +That's it! Your Symfony application will be bootstaped and deployed. You'll soon +be able to see it in your browser. + +.. _`Platform.sh`: https://platform.sh +.. _`Platform.sh documentation`: https://docs.platform.sh/toolstacks/symfony/symfony-getting-started/ +.. _`Platform.sh project`: https://marketplace.commerceguys.com/platform/buy-now +.. _`Platform.sh configuration files`: https://docs.platform.sh/reference/configuration-files/ \ No newline at end of file From c2a567551da282e4016575f9f5a24a1a8b5b34ae Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Fri, 28 Nov 2014 12:27:31 +0100 Subject: [PATCH 586/835] Fix typo and add Github link. --- cookbook/deployment/platformsh.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index 2137cff4249..be267f980e9 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -4,8 +4,8 @@ Deploying to Platform.sh ======================== -This step by step cookbook describes how to deploy a Symfony web application to -`Platform.sh`_ . You can read more about using Symfony with Platform.sh on the +This step-by-step cookbook describes how to deploy a Symfony web application to +`Platform.sh`_. You can read more about using Symfony with Platform.sh on the official `Platform.sh documentation`_. Deploy an existing site @@ -95,6 +95,8 @@ your Git repository which contains the following files: type: mysql disk: 2048 +An example of these configurations can be found on `Github`_. + Configure database access ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -186,10 +188,11 @@ through the checkout process. Once your project is ready, give it a name and choose: **Create a new site**. Choose the *Symfony* stack and a starting point such as *Standard*. -That's it! Your Symfony application will be bootstaped and deployed. You'll soon +That's it! Your Symfony application will be bootstrapped and deployed. You'll soon be able to see it in your browser. .. _`Platform.sh`: https://platform.sh -.. _`Platform.sh documentation`: https://docs.platform.sh/toolstacks/symfony/symfony-getting-started/ +.. _`Platform.sh documentation`: https://docs.platform.sh/toolstacks/symfony/symfony-getting-started .. _`Platform.sh project`: https://marketplace.commerceguys.com/platform/buy-now -.. _`Platform.sh configuration files`: https://docs.platform.sh/reference/configuration-files/ \ No newline at end of file +.. _`Platform.sh configuration files`: https://docs.platform.sh/reference/configuration-files +.. _`Github`: https://github.com/platformsh/platformsh-examples From b70f413efa7cdd14504ac39664634bc3e1b1078e Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Fri, 28 Nov 2014 14:00:41 +0100 Subject: [PATCH 587/835] Github => GitHub --- cookbook/deployment/platformsh.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index be267f980e9..bed3272644c 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -95,7 +95,7 @@ your Git repository which contains the following files: type: mysql disk: 2048 -An example of these configurations can be found on `Github`_. +An example of these configurations can be found on `GitHub`_. Configure database access ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -195,4 +195,4 @@ be able to see it in your browser. .. _`Platform.sh documentation`: https://docs.platform.sh/toolstacks/symfony/symfony-getting-started .. _`Platform.sh project`: https://marketplace.commerceguys.com/platform/buy-now .. _`Platform.sh configuration files`: https://docs.platform.sh/reference/configuration-files -.. _`Github`: https://github.com/platformsh/platformsh-examples +.. _`GitHub`: https://github.com/platformsh/platformsh-examples From 62f76bf48348da5b93d932b6fd33030530843ae6 Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Sat, 29 Nov 2014 13:22:51 +0100 Subject: [PATCH 588/835] Better match Symfony documentation standard. Based on feedbacks from @xabbuh and @timglabisch. --- cookbook/deployment/platformsh.rst | 36 +++++++++++++----------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index bed3272644c..ceafae5cd26 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -8,21 +8,19 @@ This step-by-step cookbook describes how to deploy a Symfony web application to `Platform.sh`_. You can read more about using Symfony with Platform.sh on the official `Platform.sh documentation`_. -Deploy an existing site +Deploy an Existing Site ----------------------- -In this guide, we assume your codebase is already versioned with Git. +In this guide, it is assumed your codebase is already versioned with Git. -Get a project on Platform.sh +Get a Project on Platform.sh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You need to subscribe to a `Platform.sh project`_. Choose the development plan -and go through the checkout process. +and go through the checkout process. Once your project is ready, give it a name +and choose: **Import an existing site**. -Once your project is ready, give it a name and choose: **Import an existing -site**. - -Prepare your Application +Prepare Your Application ~~~~~~~~~~~~~~~~~~~~~~~~ To deploy your Symfony application on Platform.sh, you simply need to add a @@ -32,6 +30,7 @@ configuration files`_). .. code-block:: yaml + # .platform.app.yaml # This file describes an application. You can have multiple applications # in the same project. @@ -71,13 +70,6 @@ configuration files`_). deploy: | app/console --env=prod cache:clear - # The configuration of scheduled execution. - # see http://symfony.com/doc/current/components/console/introduction.html - #crons: - # symfony: - # spec: "*/20 * * * *" - # cmd: "php cron.php example:test" - For best practices, you should also add a ``.platform`` folder at the root of your Git repository which contains the following files: @@ -97,13 +89,13 @@ your Git repository which contains the following files: An example of these configurations can be found on `GitHub`_. -Configure database access +Configure Database Access ~~~~~~~~~~~~~~~~~~~~~~~~~ Platform.sh overrides your database specific configuration via importing the following file: -.. code-block:: yaml +.. code-block:: php # app/config/parameters_platform.php setParameter('database_path', ''); } - # Hack. + # Store session into /tmp. ini_set('session.save_path', '/tmp/sessions'); Make sure this file is listed in your *imports*: @@ -147,7 +139,11 @@ command that you see on the Platform.sh web UI): .. code-block:: bash - $ git remote add platform kjh43kbobssae@git.eu.platform.sh:kjh43kbobssae.git + $ git remote add platform [PROJECT-ID]@git.[CLUSTER].platform.sh:[PROJECT].git + + +* PROJECT-ID: Unique identifier of your project. Something like: *kjh43kbobssae*. +* CLUSTER: Server location where your project is deployed. It can be *eu* or *us*. Commit the Platform.sh specific files created in the previous section: @@ -179,7 +175,7 @@ Push your code base to the newly added remote: That's it! Your application is being deployed on Platform.sh and you'll soon be able to access it in your browser. -Deploy a new site +Deploy a new Site ----------------- You can start a new `Platform.sh project`_. Choose the development plan and go From a8b6df84344f34cbe50cfae51f241e2764115f77 Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Sat, 29 Nov 2014 14:01:43 +0100 Subject: [PATCH 589/835] Reference available services --- cookbook/deployment/platformsh.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index ceafae5cd26..b9439dc89fa 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -87,7 +87,8 @@ your Git repository which contains the following files: type: mysql disk: 2048 -An example of these configurations can be found on `GitHub`_. +An example of these configurations can be found on `GitHub`_. The list of +available services can be found `here`_. Configure Database Access ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -192,3 +193,4 @@ be able to see it in your browser. .. _`Platform.sh project`: https://marketplace.commerceguys.com/platform/buy-now .. _`Platform.sh configuration files`: https://docs.platform.sh/reference/configuration-files .. _`GitHub`: https://github.com/platformsh/platformsh-examples +.. _`here`: https://docs.platform.sh/reference/configuration-files/#configure-services From 8fb400e72beea302cce81ca42e6b646f33e7a4fc Mon Sep 17 00:00:00 2001 From: GuGuss Date: Sat, 29 Nov 2014 14:27:06 +0100 Subject: [PATCH 590/835] Add some references. --- cookbook/deployment/platformsh.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index b9439dc89fa..a9d87758370 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -88,7 +88,7 @@ your Git repository which contains the following files: disk: 2048 An example of these configurations can be found on `GitHub`_. The list of -available services can be found `here`_. +available services can be found on the `Platform.sh documentation `_. Configure Database Access ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -176,6 +176,12 @@ Push your code base to the newly added remote: That's it! Your application is being deployed on Platform.sh and you'll soon be able to access it in your browser. +Every code change that you do from now on will be pushed to Git in order to +redeploy your environment on Platform.sh. + +More information about migrating your database and files can be found on the +`Platform.sh documentation `_ + Deploy a new Site ----------------- @@ -185,12 +191,13 @@ through the checkout process. Once your project is ready, give it a name and choose: **Create a new site**. Choose the *Symfony* stack and a starting point such as *Standard*. -That's it! Your Symfony application will be bootstrapped and deployed. You'll soon -be able to see it in your browser. +That's it! Your Symfony application will be bootstrapped and deployed. You'll +soon be able to see it in your browser. .. _`Platform.sh`: https://platform.sh .. _`Platform.sh documentation`: https://docs.platform.sh/toolstacks/symfony/symfony-getting-started .. _`Platform.sh project`: https://marketplace.commerceguys.com/platform/buy-now .. _`Platform.sh configuration files`: https://docs.platform.sh/reference/configuration-files .. _`GitHub`: https://github.com/platformsh/platformsh-examples -.. _`here`: https://docs.platform.sh/reference/configuration-files/#configure-services +.. _`configure-services`: https://docs.platform.sh/reference/configuration-files/#configure-services +.. _`migrate-existing-site`: https://docs.platform.sh/toolstacks/symfony/migrate-existing-site/ From 75a1c56c1abf26b9376a817cd6d5dedb20275ab9 Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Wed, 3 Dec 2014 12:08:22 +0100 Subject: [PATCH 591/835] Fix duplicated references. This should make the Travis build pass. --- cookbook/deployment/platformsh.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index a9d87758370..505b3669126 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -88,7 +88,7 @@ your Git repository which contains the following files: disk: 2048 An example of these configurations can be found on `GitHub`_. The list of -available services can be found on the `Platform.sh documentation `_. +`available services `_ can be found on the Platform.sh documentation. Configure Database Access ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -179,8 +179,8 @@ able to access it in your browser. Every code change that you do from now on will be pushed to Git in order to redeploy your environment on Platform.sh. -More information about migrating your database and files can be found on the -`Platform.sh documentation `_ +More information about `migrating your database and files `_ can be found on the +Platform.sh documentation. Deploy a new Site ----------------- From 29988aae9373f7a81c239d6f14c8f93c6b24bc45 Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Mon, 8 Dec 2014 15:29:33 +0100 Subject: [PATCH 592/835] Use alphabetical order. --- cookbook/deployment/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/deployment/index.rst b/cookbook/deployment/index.rst index 7c6d2c674be..2b1a962fb54 100644 --- a/cookbook/deployment/index.rst +++ b/cookbook/deployment/index.rst @@ -5,6 +5,6 @@ Deployment :maxdepth: 2 tools - platformsh azure-website heroku + platformsh From 18ca974cad54372f23993a38b6cbdf93fc9f67f2 Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Mon, 8 Dec 2014 15:43:13 +0100 Subject: [PATCH 593/835] Fix coding standards #2 Thanks @WouterJ and @stof --- cookbook/deployment/platformsh.rst | 61 ++++++++++++------------------ 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index 505b3669126..836a4ed38d3 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -25,17 +25,18 @@ Prepare Your Application To deploy your Symfony application on Platform.sh, you simply need to add a ``.platform.app.yaml`` at the root of your Git repository which will tell -Platform.sh how to deploy your application (read more about `Platform.sh -configuration files`_). +Platform.sh how to deploy your application (read more about +`Platform.sh configuration files`_). .. code-block:: yaml # .platform.app.yaml + # This file describes an application. You can have multiple applications # in the same project. # The name of this app. Must be unique within a project. - name: php + name: myphpproject # The toolstack used to build the application. toolstack: "php:symfony" @@ -98,27 +99,27 @@ following file: .. code-block:: php - # app/config/parameters_platform.php + // app/config/parameters_platform.php setParameter('database_driver', 'pdo_' . $endpoint['scheme']); - $container->setParameter('database_host', $endpoint['host']); - $container->setParameter('database_port', $endpoint['port']); - $container->setParameter('database_name', $endpoint['path']); - $container->setParameter('database_user', $endpoint['username']); - $container->setParameter('database_password', $endpoint['password']); - $container->setParameter('database_path', ''); + if (empty($endpoint['query']['is_master'])) { + continue; + } + + $container->setParameter('database_driver', 'pdo_' . $endpoint['scheme']); + $container->setParameter('database_host', $endpoint['host']); + $container->setParameter('database_port', $endpoint['port']); + $container->setParameter('database_name', $endpoint['path']); + $container->setParameter('database_user', $endpoint['username']); + $container->setParameter('database_password', $endpoint['password']); + $container->setParameter('database_path', ''); } # Store session into /tmp. @@ -140,11 +141,12 @@ command that you see on the Platform.sh web UI): .. code-block:: bash - $ git remote add platform [PROJECT-ID]@git.[CLUSTER].platform.sh:[PROJECT].git - + $ git remote add platform [PROJECT-ID]@git.[CLUSTER].platform.sh:[PROJECT-ID].git -* PROJECT-ID: Unique identifier of your project. Something like: *kjh43kbobssae*. -* CLUSTER: Server location where your project is deployed. It can be *eu* or *us*. +``PROJECT-ID`` + Unique identifier of your project. Something like ``kjh43kbobssae`` +``CLUSTER`` + Server location where your project is deplyed. It can be ``eu`` or ``us`` Commit the Platform.sh specific files created in the previous section: @@ -158,20 +160,7 @@ Push your code base to the newly added remote: .. code-block:: bash - $ git push -u platform master - - Counting objects: 27, done. - Delta compression using up to 4 threads. - Compressing objects: 100% (11/11), done. - Writing objects: 100% (16/16), 2.47 KiB | 0 bytes/s, done. - Total 16 (delta 7), reused 12 (delta 5) - - Processing activity environment.push - Found 213 new commits. - - Building application 'php' with toolstack 'php:symfony' (tree: 2248cf8) - Found a `composer.json`, installing dependencies. - ... + $ git push platform master That's it! Your application is being deployed on Platform.sh and you'll soon be able to access it in your browser. From 257e9a813dc3283a2495d67a589601f974f5136c Mon Sep 17 00:00:00 2001 From: Augustin Delaporte Date: Tue, 9 Dec 2014 10:58:37 +0100 Subject: [PATCH 594/835] Coding standard for PHP block. --- cookbook/deployment/platformsh.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cookbook/deployment/platformsh.rst b/cookbook/deployment/platformsh.rst index 836a4ed38d3..a8c086f3ecd 100644 --- a/cookbook/deployment/platformsh.rst +++ b/cookbook/deployment/platformsh.rst @@ -95,9 +95,7 @@ Configure Database Access ~~~~~~~~~~~~~~~~~~~~~~~~~ Platform.sh overrides your database specific configuration via importing the -following file: - -.. code-block:: php +following file:: // app/config/parameters_platform.php Date: Wed, 24 Dec 2014 10:36:08 +0100 Subject: [PATCH 595/835] Update map.rst.inc --- cookbook/map.rst.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 99498b50a7a..24f6a51e0ab 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -54,6 +54,7 @@ * :doc:`/cookbook/deployment/tools` * :doc:`/cookbook/deployment/azure-website` * :doc:`/cookbook/deployment/heroku` + * :doc:`/cookbook/deployment/platformsh` * :doc:`/cookbook/doctrine/index` From 0accf631a5b97b95823721d262ba9eeded230418 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sat, 27 Dec 2014 09:35:26 +0100 Subject: [PATCH 596/835] cleanup cache book chapter --- book/http_cache.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/book/http_cache.rst b/book/http_cache.rst index 19da513be99..e6903692dc8 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -400,6 +400,7 @@ header when none is set by the developer by following these rules: ``private`` directive automatically (except when ``s-maxage`` is set). .. _http-expiration-validation: +.. _http-expiration-and-validation: HTTP Expiration, Validation and Invalidation -------------------------------------------- @@ -775,7 +776,7 @@ at some interval (the expiration) to verify that the content is still valid. annotations. See the `FrameworkExtraBundle documentation`_. .. index:: - pair: Cache; Configuration + pair: Cache; Configuration More Response Methods ~~~~~~~~~~~~~~~~~~~~~ @@ -803,7 +804,7 @@ Additionally, most cache-related HTTP headers can be set via the single )); .. index:: -single: Cache; Invalidation + single: Cache; Invalidation .. _http-cache-invalidation: @@ -821,7 +822,7 @@ cache lifetimes, but to actively notify the gateway cache when content changes. Reverse proxies usually provide a channel to receive such notifications, typically through special HTTP requests. -.. warning:: +.. caution:: While cache invalidation is powerful, avoid it when possible. If you fail to invalidate something, outdated caches will be served for a potentially @@ -846,7 +847,8 @@ that data from its cache. a couple of common caching proxies. If one content corresponds to one URL, the ``PURGE`` model works well. -You send a request to the cache proxy with the HTTP method ``PURGE`` instead +You send a request to the cache proxy with the HTTP method ``PURGE`` (using +the word "PURGE" is a convention, technically this can be any string) instead of ``GET`` and make the cache proxy detect this and remove the data from the cache instead of going to Symfony to get a response. @@ -899,13 +901,13 @@ In many applications, the same content bit is used on various pages with different URLs. More flexible concepts exist for those cases: * **Banning** invalidates responses matching regular expressions on the - URL or other criteria. + URL or other criteria; * **Cache tagging** lets you add a tag for each content used in a response so that you can invalidate all URLs containing a certain content. .. index:: -single: Cache; ESI - single: ESI + single: Cache; ESI + single: ESI .. _edge-side-includes: From a9508887c8255703935e9e1cf2552d44e4f76733 Mon Sep 17 00:00:00 2001 From: timglabisch Date: Sat, 27 Dec 2014 22:00:40 +0100 Subject: [PATCH 597/835] replace "or" with "," --- components/console/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/console/introduction.rst b/components/console/introduction.rst index a2b3c52b7fd..3461eefebc0 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -297,7 +297,7 @@ declare a one-letter shortcut that you can call with a single dash like .. tip:: It is also possible to make an option *optionally* accept a value (so that - ``--yell`` or ``--yell=loud`` or ``--yell loud`` work). Options can also be configured to + ``--yell``, ``--yell=loud`` or ``--yell loud`` work). Options can also be configured to accept an array of values. For example, add a new option to the command that can be used to specify From 4cd204633a71ed0145b2bfb224222a594f8b1842 Mon Sep 17 00:00:00 2001 From: Rodrigo Rigotti Mammano Date: Sat, 27 Dec 2014 11:53:42 -0200 Subject: [PATCH 598/835] Update form_customization.rst There are two code samples which are inconsistent which each other. I updated one of them. --- cookbook/form/form_customization.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cookbook/form/form_customization.rst b/cookbook/form/form_customization.rst index 0bd6708f148..aa7622618b2 100644 --- a/cookbook/form/form_customization.rst +++ b/cookbook/form/form_customization.rst @@ -502,7 +502,8 @@ resource to use such a layout: # app/config/config.yml twig: form: - resources: ['form_table_layout.html.twig'] + resources: + - 'form_table_layout.html.twig' # ... .. code-block:: xml From d89ad215ce9c6830a31c61cbd6f486fe82691c6a Mon Sep 17 00:00:00 2001 From: WouterJ Date: Tue, 16 Dec 2014 00:45:47 +0100 Subject: [PATCH 599/835] Tried to clarify private services --- components/dependency_injection/advanced.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/components/dependency_injection/advanced.rst b/components/dependency_injection/advanced.rst index 2d07d0eab13..74e7fe35463 100644 --- a/components/dependency_injection/advanced.rst +++ b/components/dependency_injection/advanced.rst @@ -20,11 +20,14 @@ argument for another service. .. _inlined-private-services: -.. note:: +Since a container is not able to detect if a service is retrieved from inside +the container or the outside, a private service may still be retrieved using +the ``get()`` method. - If you use a private service as an argument to only one other service, - this will result in an inlined instantiation (e.g. ``new PrivateFooBar()``) - inside this other service, making it publicly unavailable at runtime. +What makes private services special, is that they are converted from services +to inlined instantiation (e.g. ``new PrivateThing()``) when they are only +injected once, to increase the container performance. This means that you can +never be sure if a private service exists in the container. Simply said: A service will be private when you do not want to access it directly from your code. @@ -60,7 +63,8 @@ Here is an example: $definition->setPublic(false); $container->setDefinition('foo', $definition); -Now that the service is private, you *cannot* call:: +Now that the service is private, you *should not* call (should not means, this +*might* fail, see the explaination above):: $container->get('foo'); From a94bd718bc0c19b92f6b955b0e92b8daf1b33ce4 Mon Sep 17 00:00:00 2001 From: Peter Rehm Date: Wed, 3 Dec 2014 22:38:27 +0100 Subject: [PATCH 600/835] Completed the needed context to successfully test commands --- components/console/helpers/dialoghelper.rst | 10 ++++++++++ components/console/introduction.rst | 2 ++ 2 files changed, 12 insertions(+) diff --git a/components/console/helpers/dialoghelper.rst b/components/console/helpers/dialoghelper.rst index b6544139a26..96c5c880d09 100644 --- a/components/console/helpers/dialoghelper.rst +++ b/components/console/helpers/dialoghelper.rst @@ -253,13 +253,18 @@ Testing a Command which Expects Input If you want to write a unit test for a command which expects some kind of input from the command line, you need to overwrite the HelperSet used by the command:: + use Symfony\Component\Console\Application; use Symfony\Component\Console\Helper\DialogHelper; use Symfony\Component\Console\Helper\HelperSet; + use Symfony\Component\Console\Tester\CommandTester; // ... public function testExecute() { // ... + $application = new Application(); + $application->add(new MyCommand()); + $command = $application->find('my:command:name'); $commandTester = new CommandTester($command); $dialog = $command->getHelper('dialog'); @@ -285,3 +290,8 @@ By setting the input stream of the ``DialogHelper``, you imitate what the console would do internally with all user input through the cli. This way you can test any user interaction (even complex ones) by passing an appropriate input stream. + +.. seealso:: + + You find more information about testing commands in the console component + docs about :ref:`testing console commands `. diff --git a/components/console/introduction.rst b/components/console/introduction.rst index 3461eefebc0..2b80d439c5a 100644 --- a/components/console/introduction.rst +++ b/components/console/introduction.rst @@ -377,6 +377,8 @@ tools capable of helping you with different tasks: * :doc:`/components/console/helpers/progresshelper`: shows a progress bar * :doc:`/components/console/helpers/tablehelper`: displays tabular data as a table +.. _component-console-testing-commands: + Testing Commands ---------------- From ee6291c2eb86e484975d879b368fbaff50d30c1e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 23 Dec 2014 23:08:24 +0100 Subject: [PATCH 601/835] [Reference] update the configuration reference Move some `versionadded` directive to right position and add missing type information. --- reference/configuration/doctrine.rst | 64 ++++++++++------- reference/configuration/framework.rst | 26 +++---- reference/configuration/security.rst | 94 ++++++++++++++++--------- reference/configuration/swiftmailer.rst | 4 +- 4 files changed, 114 insertions(+), 74 deletions(-) diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index 2a04200b361..5a7afc42dca 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -314,31 +314,45 @@ Explicit definition of all the mapped entities is the only necessary configuration for the ORM and there are several configuration options that you can control. The following configuration options exist for a mapping: -* ``type`` One of ``annotation``, ``xml``, ``yml``, ``php`` or ``staticphp``. - This specifies which type of metadata type your mapping uses. - -* ``dir`` Path to the mapping or entity files (depending on the driver). - If this path is relative it is assumed to be relative to the bundle root. - This only works if the name of your mapping is a bundle name. If you want - to use this option to specify absolute paths you should prefix the path - with the kernel parameters that exist in the DIC (for example ``%kernel.root_dir%``). - -* ``prefix`` A common namespace prefix that all entities of this mapping - share. This prefix should never conflict with prefixes of other defined - mappings otherwise some of your entities cannot be found by Doctrine. - This option defaults to the bundle namespace + ``Entity``, for example - for an application bundle called ``AcmeHelloBundle`` prefix would be - ``Acme\HelloBundle\Entity``. - -* ``alias`` Doctrine offers a way to alias entity namespaces to simpler, - shorter names to be used in DQL queries or for Repository access. When - using a bundle the alias defaults to the bundle name. - -* ``is_bundle`` This option is a derived value from ``dir`` and by default - is set to true if dir is relative proved by a ``file_exists()`` check - that returns false. It is false if the existence check returns true. In - this case an absolute path was specified and the metadata files are most - likely in a directory outside of a bundle. +type +.... + +One of ``annotation``, ``xml``, ``yml``, ``php`` or ``staticphp``. This specifies +which type of metadata type your mapping uses. + +dir +... + +Path to the mapping or entity files (depending on the driver). If this path +is relative it is assumed to be relative to the bundle root. This only works +if the name of your mapping is a bundle name. If you want to use this option +to specify absolute paths you should prefix the path with the kernel parameters +that exist in the DIC (for example ``%kernel.root_dir%``). + +prefix +...... + +A common namespace prefix that all entities of this mapping share. This prefix +should never conflict with prefixes of other defined mappings otherwise some +of your entities cannot be found by Doctrine. This option defaults to the +bundle namespace + ``Entity``, for example for an application bundle called +``AcmeHelloBundle`` prefix would be ``Acme\HelloBundle\Entity``. + +alias +..... + +Doctrine offers a way to alias entity namespaces to simpler, shorter names +to be used in DQL queries or for Repository access. When using a bundle the +alias defaults to the bundle name. + +is_bundle +......... + +This option is a derived value from ``dir`` and by default is set to ``true`` +if dir is relative proved by a ``file_exists()`` check that returns ``false``. +It is ``false`` if the existence check returns ``true``. In this case an +absolute path was specified and the metadata files are most likely in a directory +outside of a bundle. .. index:: single: Configuration; Doctrine DBAL diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index bdbaf06aa96..dacdb535b76 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -464,19 +464,24 @@ would be ``/images/logo.png?version=5``. profiler ~~~~~~~~ -.. versionadded:: 2.2 - The ``enabled`` option was introduced in Symfony 2.2. Previously, the profiler - could only be disabled by omitting the ``framework.profiler`` configuration - entirely. - .. _profiler.enabled: enabled ....... -**default**: ``true`` in the ``dev`` and ``test`` environments +.. versionadded:: 2.2 + The ``enabled`` option was introduced in Symfony 2.2. Prior to Symfony + 2.2, the profiler could only be disabled by omitting the ``framework.profiler`` + configuration entirely. + +**type**: ``boolean`` **default**: ``false`` -The profiler can be disabled by setting this key to ``false``. +The profiler can be enabled by setting this key to ``true``. When you are +using the Symfony Standard Edition, the profiler is enabled in the ``dev`` +and ``test`` environments. + +collect +....... .. versionadded:: 2.3 The ``collect`` option was introduced in Symfony 2.3. Previously, when @@ -484,10 +489,7 @@ The profiler can be disabled by setting this key to ``false``. but the collectors were disabled. Now, the profiler and the collectors can be controlled independently. -collect -....... - -**default**: ``true`` +**type**: ``boolean`` **default**: ``true`` This option configures the way the profiler behaves when it is enabled. If set to ``true``, the profiler collects data for all requests. If you want to only @@ -511,7 +513,7 @@ Whether or not to enable the ``translator`` service in the service container. fallback ........ -**default**: ``en`` +**type**: ``string`` **default**: ``en`` This option is used when the translation key for the current locale wasn't found. diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index c680bba3d4f..c6418805901 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -248,41 +248,65 @@ For even more details, see :doc:`/cookbook/security/form_login`. The Login Form and Process ~~~~~~~~~~~~~~~~~~~~~~~~~~ -* ``login_path`` (type: ``string``, default: ``/login``) - This is the route or path that the user will be redirected to (unless - ``use_forward`` is set to ``true``) when they try to access a - protected resource but isn't fully authenticated. - - This path **must** be accessible by a normal, un-authenticated user, - else you may create a redirect loop. For details, see - ":ref:`Avoid Common Pitfalls `". - -* ``check_path`` (type: ``string``, default: ``/login_check``) - This is the route or path that your login form must submit to. The - firewall will intercept any requests (``POST`` requests only, by default) - to this URL and process the submitted login credentials. - - Be sure that this URL is covered by your main firewall (i.e. don't create - a separate firewall just for ``check_path`` URL). - -* ``use_forward`` (type: ``Boolean``, default: ``false``) - If you'd like the user to be forwarded to the login form instead of - being redirected, set this option to ``true``. - -* ``username_parameter`` (type: ``string``, default: ``_username``) - This is the field name that you should give to the username field of - your login form. When you submit the form to ``check_path``, the security - system will look for a POST parameter with this name. - -* ``password_parameter`` (type: ``string``, default: ``_password``) - This is the field name that you should give to the password field of - your login form. When you submit the form to ``check_path``, the security - system will look for a POST parameter with this name. - -* ``post_only`` (type: ``Boolean``, default: ``true``) - By default, you must submit your login form to the ``check_path`` URL - as a POST request. By setting this option to ``false``, you can send a - GET request to the ``check_path`` URL. +login_path +.......... + +**type**: ``string`` **default**: ``/login`` + +This is the route or path that the user will be redirected to (unless ``use_forward`` +is set to ``true``) when they try to access a protected resource but isn't +fully authenticated. + +This path **must** be accessible by a normal, un-authenticated user, else +you may create a redirect loop. For details, see +":ref:`Avoid Common Pitfalls `". + +check_path +.......... + +**type**: ``string`` **default**: ``/login_check`` + +This is the route or path that your login form must submit to. The firewall +will intercept any requests (``POST`` requests only, by default) to this +URL and process the submitted login credentials. + +Be sure that this URL is covered by your main firewall (i.e. don't create +a separate firewall just for ``check_path`` URL). + +use_forward +........... + +**type**: ``Boolean`` **default**: ``false`` + +If you'd like the user to be forwarded to the login form instead of being +redirected, set this option to ``true``. + +username_parameter +.................. + +**type**: ``string`` **default**: ``_username`` + +This is the field name that you should give to the username field of your +login form. When you submit the form to ``check_path``, the security system +will look for a POST parameter with this name. + +password_parameter +.................. + +**type**: ``string`` **default**: ``_password`` + +This is the field name that you should give to the password field of your +login form. When you submit the form to ``check_path``, the security system +will look for a POST parameter with this name. + +post_only +......... + +**type**: ``Boolean`` **default**: ``true`` + +By default, you must submit your login form to the ``check_path`` URL as +a POST request. By setting this option to ``false``, you can send a GET request +to the ``check_path`` URL. Redirecting after Login ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/reference/configuration/swiftmailer.rst b/reference/configuration/swiftmailer.rst index 9295493eb68..3aaf52b858a 100644 --- a/reference/configuration/swiftmailer.rst +++ b/reference/configuration/swiftmailer.rst @@ -130,7 +130,7 @@ antiflood threshold ......... -**type**: ``string`` **default**: ``99`` +**type**: ``integer`` **default**: ``99`` Used with ``Swift_Plugins_AntiFloodPlugin``. This is the number of emails to send before restarting the transport. @@ -138,7 +138,7 @@ to send before restarting the transport. sleep ..... -**type**: ``string`` **default**: ``0`` +**type**: ``integer`` **default**: ``0`` Used with ``Swift_Plugins_AntiFloodPlugin``. This is the number of seconds to sleep for during a transport restart. From 9ab1c56caddbbf7c7de3493937671cfdddac6d4e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 23 Aug 2014 18:23:12 +0200 Subject: [PATCH 602/835] use a global Composer installation --- book/from_flat_php_to_symfony2.rst | 2 +- book/installation.rst | 11 +---- book/performance.rst | 5 +-- .../dependency_injection/lazy_services.rst | 2 +- components/intl.rst | 12 ++--- components/using_components.rst | 10 ++--- contributing/code/tests.rst | 26 ++--------- cookbook/bundles/installation.rst | 2 +- cookbook/composer.rst | 44 +++++++++++++++++++ cookbook/deployment/tools.rst | 4 +- cookbook/index.rst | 1 + cookbook/map.rst.inc | 4 ++ cookbook/symfony1.rst | 2 +- cookbook/workflow/_vendor_deps.rst.inc | 20 ++++----- 14 files changed, 83 insertions(+), 62 deletions(-) create mode 100644 cookbook/composer.rst diff --git a/book/from_flat_php_to_symfony2.rst b/book/from_flat_php_to_symfony2.rst index f9c90d5d6e1..19f4f9bfdc1 100644 --- a/book/from_flat_php_to_symfony2.rst +++ b/book/from_flat_php_to_symfony2.rst @@ -447,7 +447,7 @@ into a vendor/ directory: .. code-block:: bash - $ php composer.phar install + $ composer install Beside downloading your dependencies, Composer generates a ``vendor/autoload.php`` file, which takes care of autoloading for all the files in the Symfony Framework as well as diff --git a/book/installation.rst b/book/installation.rst index f065473ec3f..28060a8aff6 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -117,16 +117,7 @@ don't have installed it globally, start by reading the next section. Installing Composer Globally ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -On Linux and Mac OS X, execute the following two commands to install Composer -globally: - -.. code-block:: bash - - $ curl -sS https://getcomposer.org/installer | php - $ sudo mv composer.phar /usr/local/bin/composer - -On Windows Systems, download the executable Composer installer that you can find -on the `Composer download page`_ and follow the steps. +Start with :doc:`installing Composer globally `. Creating a Symfony Application with Composer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/book/performance.rst b/book/performance.rst index b2e21a42610..e3f296484ad 100644 --- a/book/performance.rst +++ b/book/performance.rst @@ -60,7 +60,7 @@ command line, and might become part of your deploy process: .. code-block:: bash - $ php composer.phar dump-autoload --optimize + $ composer dump-autoload --optimize Internally, this builds the big class map array in ``vendor/composer/autoload_classmap.php``. @@ -128,8 +128,7 @@ Note that there are two disadvantages when using a bootstrap file: * when debugging, one will need to place break points inside the bootstrap file. If you're using the Symfony Standard Edition, the bootstrap file is automatically -rebuilt after updating the vendor libraries via the ``php composer.phar install`` -command. +rebuilt after updating the vendor libraries via the ``composer install`` command. Bootstrap Files and Byte Code Caches ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/components/dependency_injection/lazy_services.rst b/components/dependency_injection/lazy_services.rst index 2d3eafc1785..2af8471b054 100644 --- a/components/dependency_injection/lazy_services.rst +++ b/components/dependency_injection/lazy_services.rst @@ -30,7 +30,7 @@ the `ProxyManager bridge`_: .. code-block:: bash - $ php composer.phar require symfony/proxy-manager-bridge:~2.3 + $ composer require symfony/proxy-manager-bridge:~2.3 .. note:: diff --git a/components/intl.rst b/components/intl.rst index 698be9d619f..bbd311f3562 100644 --- a/components/intl.rst +++ b/components/intl.rst @@ -85,13 +85,13 @@ code:: the server. For example, consider that your development machines ship ICU 4.8 and the server - ICU 4.2. When you run ``php composer.phar update`` on the development machine, version + ICU 4.2. When you run ``composer update`` on the development machine, version 1.2.* of the Icu component will be installed. But after deploying the - application, ``php composer.phar install`` will fail with the following error: + application, ``composer install`` will fail with the following error: .. code-block:: bash - $ php composer.phar install + $ composer install Loading composer repositories with package information Installing dependencies from lock file Your requirements could not be resolved to an installable set of packages. @@ -104,8 +104,8 @@ code:: The error tells you that the requested version of the Icu component, version 1.2, is not compatible with PHP's ICU version 4.2. - One solution to this problem is to run ``php composer.phar update`` instead of - ``php composer.phar install``. It is highly recommended **not** to do this. The + One solution to this problem is to run ``composer update`` instead of + ``composer install``. It is highly recommended **not** to do this. The ``update`` command will install the latest versions of each Composer dependency to your production server and potentially break the application. @@ -130,7 +130,7 @@ code:: * "1.0.*" if the server does not have the intl extension installed; * "1.1.*" if the server is compiled with ICU 4.2 or lower. - Finally, run ``php composer.phar update symfony/icu`` on your development machine, test + Finally, run ``composer update symfony/icu`` on your development machine, test extensively and deploy again. The installation of the dependencies will now succeed. diff --git a/components/using_components.rst b/components/using_components.rst index 37db031de39..4103b956afc 100644 --- a/components/using_components.rst +++ b/components/using_components.rst @@ -31,16 +31,16 @@ whatever component you want. .. tip:: - If you get a command not found for ``composer``, you'll need to - `Install composer`_. Depending on how you install, you may end up with - a ``composer.phar`` file in your directory. In that case, no worries! - Just run ``php composer.phar require symfony/finder``. + `Install composer`_ if you don't have it already present on your system. + Depending on how you install, you may end up with a ``composer.phar`` + file in your directory. In that case, no worries! Just run + ``php composer.phar require symfony/finder``. If you know you need a specific version of the library, add that to the command: .. code-block:: bash - $ composer require symfony/finder:~2.3 + $ composer require symfony/finder **3.** Write your code! diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index ebec01f3d3e..ead51268289 100644 --- a/contributing/code/tests.rst +++ b/contributing/code/tests.rst @@ -28,42 +28,24 @@ The test suite needs the following third-party libraries: To install them all, use `Composer`_: -Step 1: Get `Composer`_ +Step 1: :doc:`Install Composer globally ` -.. code-block:: bash - - $ curl -s http://getcomposer.org/installer | php - -Make sure you download ``composer.phar`` in the same folder where -the ``composer.json`` file is located. - -Step 2: Install vendors +Step 2: Install vendors. .. code-block:: bash - $ php composer.phar install + $ composer install .. note:: Note that the script takes some time to finish. -.. note:: - - If you don't have ``curl`` installed, you can also just download the ``installer`` - file manually at http://getcomposer.org/installer. Place this file into your - project and then run: - - .. code-block:: bash - - $ php installer - $ php composer.phar install - After installation, you can update the vendors to their latest version with the follow command: .. code-block:: bash - $ php composer.phar update + $ composer --dev update Running ------- diff --git a/cookbook/bundles/installation.rst b/cookbook/bundles/installation.rst index d6124026554..bd29a2c6226 100644 --- a/cookbook/bundles/installation.rst +++ b/cookbook/bundles/installation.rst @@ -36,7 +36,7 @@ Now that you know the package name, you can install it via Composer: .. code-block:: bash - $ php composer.phar require friendsofsymfony/user-bundle + $ composer require friendsofsymfony/user-bundle This will choose the best version for your project, add it to ``composer.json`` and download the library into the ``vendor/`` directory. If you need a specific diff --git a/cookbook/composer.rst b/cookbook/composer.rst new file mode 100644 index 00000000000..6444c8167e2 --- /dev/null +++ b/cookbook/composer.rst @@ -0,0 +1,44 @@ +.. index:: + double: Composer; Installation + +Installing Composer +=================== + +`Composer`_ is the package manager used by modern PHP applications and the +recommended way to install Symfony2. + +Install Composer on Linux and Mac OS X +-------------------------------------- + +To install Composer on Linux or Mac OS X, execute the following two commands: + +.. code-block:: bash + + $ curl -sS https://getcomposer.org/installer | php + $ sudo mv composer.phar /usr/local/bin/composer + +-.. note:: + + If you don't have ``curl`` installed, you can also just download the + ``installer`` file manually at http://getcomposer.org/installer and + then run: + + .. code-block:: bash + + $ php installer + $ sudo mv composer.phar /usr/local/bin/composer + +Install Composer on Windows +--------------------------- + +Download the installer from `getcomposer.org/download`_, execute it and follow +the instructions. + +Learn more +---------- + +You can read more about Composer in `its documentation`_. + +.. _`Composer`: https://getcomposer.org/ +.. _`getcomposer.org/download`: https://getcomposer.org/download +.. _`its documentation`: https://getcomposer.org/doc/00-intro.md diff --git a/cookbook/deployment/tools.rst b/cookbook/deployment/tools.rst index 76992a17c59..4004c06aeb8 100644 --- a/cookbook/deployment/tools.rst +++ b/cookbook/deployment/tools.rst @@ -101,7 +101,7 @@ as you normally do: .. code-block:: bash - $ php composer.phar install --no-dev --optimize-autoloader + $ composer install --no-dev --optimize-autoloader .. tip:: @@ -142,7 +142,7 @@ setup: * Running any database migrations * Clearing your APC cache -* Running ``assets:install`` (taken care of already in ``composer.phar install``) +* Running ``assets:install`` (already taken care of in ``composer install``) * Add/edit CRON jobs * Pushing assets to a CDN * ... diff --git a/cookbook/index.rst b/cookbook/index.rst index 2b35a25a453..7fc317ab45a 100644 --- a/cookbook/index.rst +++ b/cookbook/index.rst @@ -7,6 +7,7 @@ The Cookbook assetic/index bundles/index cache/index + composer configuration/index console/index controller/index diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index 24f6a51e0ab..c37d0013e87 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -21,6 +21,10 @@ * :doc:`/cookbook/cache/varnish` +* **Composer** + + * :doc:`/cookbook/composer` + * :doc:`/cookbook/configuration/index` * :doc:`/cookbook/configuration/environments` diff --git a/cookbook/symfony1.rst b/cookbook/symfony1.rst index a393534e8ec..10ba32149ff 100644 --- a/cookbook/symfony1.rst +++ b/cookbook/symfony1.rst @@ -165,7 +165,7 @@ defined in the ``composer.json`` file. If you look at the ``HelloController`` from the Symfony2 Standard Edition you can see that it lives in the ``Acme\DemoBundle\Controller`` namespace. Yet, the AcmeDemoBundle is not defined in your ``composer.json`` file. Nonetheless are -the files autoloaded. This is because you can tell composer to autoload files +the files autoloaded. This is because you can tell Composer to autoload files from specific directories without defining a dependency: .. code-block:: json diff --git a/cookbook/workflow/_vendor_deps.rst.inc b/cookbook/workflow/_vendor_deps.rst.inc index 93d009550b4..322336627ca 100644 --- a/cookbook/workflow/_vendor_deps.rst.inc +++ b/cookbook/workflow/_vendor_deps.rst.inc @@ -9,18 +9,18 @@ way or another the goal is to download these files into your ``vendor/`` directory and, ideally, to give you some sane way to manage the exact version you need for each. -By default, these libraries are downloaded by running a ``php composer.phar install`` -"downloader" binary. This ``composer.phar`` file is from a library called -`Composer`_ and you can read more about installing it in the :ref:`Installation ` +By default, these libraries are downloaded by running a ``composer install`` +"downloader" binary. This ``composer`` file is from a library called `Composer`_ +and you can read more about installing it in the :ref:`Installation ` chapter. -The ``composer.phar`` file reads from the ``composer.json`` file at the root +The ``composer`` command reads from the ``composer.json`` file at the root of your project. This is an JSON-formatted file, which holds a list of each of the external packages you need, the version to be downloaded and more. -The ``composer.phar`` file also reads from a ``composer.lock`` file, which -allows you to pin each library to an **exact** version. In fact, if a ``composer.lock`` +``composer`` also reads from a ``composer.lock`` file, which allows you to +pin each library to an **exact** version. In fact, if a ``composer.lock`` file exists, the versions inside will override those in ``composer.json``. -To upgrade your libraries to new versions, run ``php composer.phar update``. +To upgrade your libraries to new versions, run ``composer update``. .. tip:: @@ -29,7 +29,7 @@ To upgrade your libraries to new versions, run ``php composer.phar update``. .. code-block:: bash - $ php composer.phar require doctrine/doctrine-fixtures-bundle + $ composer require doctrine/doctrine-fixtures-bundle To learn more about Composer, see `GetComposer.org`_: @@ -37,12 +37,12 @@ It's important to realize that these vendor libraries are *not* actually part of *your* repository. Instead, they're simply un-tracked files that are downloaded into the ``vendor/``. But since all the information needed to download these files is saved in ``composer.json`` and ``composer.lock`` (which *are* stored -in the repository), any other developer can use the project, run ``php composer.phar install``, +in the repository), any other developer can use the project, run ``composer install``, and download the exact same set of vendor libraries. This means that you're controlling exactly what each vendor library looks like, without needing to actually commit them to *your* repository. -So, whenever a developer uses your project, they should run the ``php composer.phar install`` +So, whenever a developer uses your project, they should run the ``composer install`` script to ensure that all of the needed vendor libraries are downloaded. .. sidebar:: Upgrading Symfony From 7427051c8127ef513b8a2043fed2f13f86f5a1a9 Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Sun, 28 Dec 2014 21:19:24 +0200 Subject: [PATCH 603/835] Set twig service as private During the best practices workshop it was suggested that it's best to set services to private if they won't be accessed from your code/container --- best_practices/templates.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/best_practices/templates.rst b/best_practices/templates.rst index 01a4b80e80b..516e85b1e42 100644 --- a/best_practices/templates.rst +++ b/best_practices/templates.rst @@ -153,6 +153,7 @@ name is irrelevant because you never use it in your own code): app.twig.app_extension: class: AppBundle\Twig\AppExtension arguments: ["@markdown"] + public: false tags: - { name: twig.extension } From 8ded86a29288b4547fe6dfd296d8d3a5067b1c6a Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Mon, 29 Dec 2014 00:51:14 +0100 Subject: [PATCH 604/835] Use new security.authorization_checker service Replace deprecated `security.context` with the `security.authorization_checker` service. --- best_practices/security.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/best_practices/security.rst b/best_practices/security.rst index 1336f20603a..c1cb2e3f23e 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -75,14 +75,14 @@ Authorization (i.e. Denying Access) Symfony gives you several ways to enforce authorization, including the ``access_control`` configuration in :doc:`security.yml ` the :ref:`@Security annotation ` and using -:ref:`isGranted ` on the ``security.context`` +:ref:`isGranted ` on the ``security.authorization_checker`` service directly. .. best-practice:: * For protecting broad URL patterns, use ``access_control``; * Whenever possible, use the ``@Security`` annotation; - * Check security directly on the ``security.context`` service whenever + * Check security directly on the ``security.authorization_checker`` service whenever you have a more complex situation. There are also different ways to centralize your authorization logic, like @@ -315,7 +315,7 @@ Now, you can use the voter with the ``@Security`` annotation: // ... } -You can also use this directly with the ``security.context`` service, or +You can also use this directly with the ``security.authorization_checker`` service, or via the even easier shortcut in a controller: .. code-block:: php @@ -327,7 +327,7 @@ via the even easier shortcut in a controller: { $post = // query for the post ... - if (!$this->get('security.context')->isGranted('edit', $post)) { + if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) { throw $this->createAccessDeniedException(); } } From 3d62349398695224643387c5a6f316e760489b5e Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Mon, 29 Dec 2014 10:06:00 +0200 Subject: [PATCH 605/835] Fix representation It might be just me, but with `unless` it sounds like **if you want to use your forms in multiple places you should define it in your controllers**. | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.3 | Fixed tickets | --- best_practices/forms.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/forms.rst b/best_practices/forms.rst index 5bd2cfb8757..3778e1d891f 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -13,7 +13,7 @@ Building Forms Define your forms as PHP classes. The Form component allows you to build forms right inside your controller -code. Honestly, unless you need to reuse the form somewhere else, that's +code. Honestly, if you don't need to reuse the form somewhere else, that's totally fine. But for organization and reuse, we recommend that you define each form in its own PHP class:: From a8ec695ab682f7cac1545c13bccd8adb76460c06 Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Mon, 29 Dec 2014 15:55:01 +0200 Subject: [PATCH 606/835] Fix typo: than in Twig => than Twig templates | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.3 | Fixed tickets | #4696 --- best_practices/templates.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/best_practices/templates.rst b/best_practices/templates.rst index 01a4b80e80b..5daf929713a 100644 --- a/best_practices/templates.rst +++ b/best_practices/templates.rst @@ -9,7 +9,7 @@ languages - like `Twig`_ - were created to make templating even better. Use Twig templating format for your templates. -Generally speaking, PHP templates are much more verbose than in Twig because +Generally speaking, PHP templates are much more verbose than Twig templates because they lack native support for lots of modern features needed by templates, like inheritance, automatic escaping and named arguments for filters and functions. From 051b5ca29d747668e24175596e6f60feaeed52b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 29 Dec 2014 15:23:55 +0100 Subject: [PATCH 607/835] [Cookbook] Fix XML example of RTE --- cookbook/doctrine/resolve_target_entity.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/doctrine/resolve_target_entity.rst b/cookbook/doctrine/resolve_target_entity.rst index 9fbe7b7176c..8b5e1049e68 100644 --- a/cookbook/doctrine/resolve_target_entity.rst +++ b/cookbook/doctrine/resolve_target_entity.rst @@ -136,7 +136,7 @@ about the replacement: - Acme\AppBundle\Entity\Customer + Acme\AppBundle\Entity\Customer From 3a40d5a54fe92b4dd42a3f9f25edb2f7a76f7bda Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 29 Dec 2014 16:12:15 +0100 Subject: [PATCH 608/835] MAde some tweaks suggested by Wouter --- components/yaml/yaml_format.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index d9a290f9537..9e01336734e 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -118,16 +118,16 @@ escaped with double quotes: * ``\P`` Finally, there are other cases when the strings must be quoted, no matter if -using single or double quotes: +you're using single or double quotes: -* when the string is ``true`` or ``false`` (otherwise, it would be treated as a +* When the string is ``true`` or ``false`` (otherwise, it would be treated as a boolean value); -* when the string is ``null`` or ``~`` (otherwise, it would be considered as a +* When the string is ``null`` or ``~`` (otherwise, it would be considered as a ``null`` value); -* when the string looks like a number, such as integers (e.g. ``2``, ``14``, etc.), +* When the string looks like a number, such as integers (e.g. ``2``, ``14``, etc.), floats (e.g. ``2.6``, ``14.9``) and exponential numbers (e.g. ``12e7``, etc.) (otherwise, it would be treated as a numeric value); -* when the string looks like a date (e.g. ``2014-12-31``) (otherwise it would be +* When the string looks like a date (e.g. ``2014-12-31``) (otherwise it would be automatically converted into a Unix timestamp). When a string contains line breaks, you can use the literal style, indicated From 2824867ce86cf2ab0e2510d8d687d3535174fce0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 29 Dec 2014 16:47:07 +0100 Subject: [PATCH 609/835] Rewroded some contents and fixed some lists of elements --- components/yaml/yaml_format.rst | 68 +++++---------------------------- 1 file changed, 10 insertions(+), 58 deletions(-) diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst index 9e01336734e..c8a6f4a0813 100644 --- a/components/yaml/yaml_format.rst +++ b/components/yaml/yaml_format.rst @@ -34,9 +34,9 @@ they can also be unquoted: "A double-quoted string in YAML" -Quoted styles are useful when a string starts or ends with one or more -relevant spaces, because unquoted strings are trimmed on both ends when parsing -their contents. +Quoted styles are useful when a string starts or end with one or more relevant +spaces, because unquoted strings are trimmed on both end when parsing their +contents. Quotes are required when the string contains special or reserved characters. When using single-quoted strings, any single quote ``'`` inside its contents must be doubled to escape it: @@ -49,25 +49,8 @@ Strings containing any of the following characters must be quoted. Although you can use double quotes, for these characters it is more convenient to use single quotes, which avoids having to escape any backslash ``\``: -* ``:`` -* ``{`` -* ``}`` -* ``[`` -* ``]`` -* ``,`` -* ``&`` -* ``*`` -* ``#`` -* ``?`` -* ``|`` -* ``-`` -* ``<`` -* ``>`` -* ``=`` -* ``!`` -* ``%`` -* ``@`` -* ``\``` +* ``:``, ``{``, ``}``, ``[``, ``]``, ``,``, ``&``, ``*``, ``#``, ``?``, ``|``, + ``-``, ``<``, ``>``, ``=``, ``!``, ``%``, ``@``, ``\``` The double-quoted style provides a way to express arbitrary strings, by using ``\`` to escape characters and sequences. For instance, it is very useful @@ -80,42 +63,11 @@ when you need to embed a ``\n`` or a Unicode character in a string. If the string contains any of the following control characters, it must be escaped with double quotes: -* ``\0`` -* ``\x01`` -* ``\x02`` -* ``\x03`` -* ``\x04`` -* ``\x05`` -* ``\x06`` -* ``\a`` -* ``\b`` -* ``\t`` -* ``\n`` -* ``\v`` -* ``\f`` -* ``\r`` -* ``\x0e`` -* ``\x0f`` -* ``\x10`` -* ``\x11`` -* ``\x12`` -* ``\x13`` -* ``\x14`` -* ``\x15`` -* ``\x16`` -* ``\x17`` -* ``\x18`` -* ``\x19`` -* ``\x1a`` -* ``\e`` -* ``\x1c`` -* ``\x1d`` -* ``\x1e`` -* ``\x1f`` -* ``\N`` -* ``\_`` -* ``\L`` -* ``\P`` +* ``\0``, ``\x01``, ``\x02``, ``\x03``, ``\x04``, ``\x05``, ``\x06``, ``\a``, + ``\b``, ``\t``, ``\n``, ``\v``, ``\f``, ``\r``, ``\x0e``, ``\x0f``, ``\x10``, + ``\x11``, ``\x12``, ``\x13``, ``\x14``, ``\x15``, ``\x16``, ``\x17``, ``\x18``, + ``\x19``, ``\x1a``, ``\e``, ``\x1c``, ``\x1d``, ``\x1e``, ``\x1f``, ``\N``, + ``\_``, ``\L``, ``\P`` Finally, there are other cases when the strings must be quoted, no matter if you're using single or double quotes: From 7f3fb719fbc1f2082efffba4d89ab8faed903d75 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 14 Dec 2014 16:56:49 +0100 Subject: [PATCH 610/835] Documented the security:check command --- book/installation.rst | 12 ++++++++++++ book/security.rst | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/book/installation.rst b/book/installation.rst index f065473ec3f..62032a3a80a 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -296,6 +296,18 @@ them all at once: Depending on the complexity of your project, this update process can take up to several minutes to complete. +.. tip:: + + Symfony provides a command to check whether your project's dependencies + contain any know security vulnerability: + + .. code-block:: bash + + $ php app/console security:check + + A good security practice is to execute this command regularly to be able to + update or replace compromised dependencies as soon as possible. + .. _installing-a-symfony2-distribution: Installing a Symfony Distribution diff --git a/book/security.rst b/book/security.rst index 1a5d90bc823..84c631e6d0a 100644 --- a/book/security.rst +++ b/book/security.rst @@ -2055,6 +2055,26 @@ to work correctly. Just pass a file name to enable it:: You can also access a secure random instance directly from the Symfony dependency injection container; its name is ``security.secure_random``. +Checking Dependencies Security +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 2.6 + The ``security:check`` command was introduced in Symfony 2.6. + +When using lots of dependencies in your Symfony projects, odds are that some of +them contain security vulnerabilities. That's why Symfony includes a command +called ``security:check`` that checks whether any of your installed dependencies +contain a known security vulnerability: + +.. code-block:: bash + + $ php app/console security:check + +A good security practice is to execute this command regularly to be able to +update or replace compromised dependencies as soon as possible. Internally, +this command uses the public `security advisories database`_ published by the +FriendsOfPHP organization. + Final Words ----------- @@ -2088,3 +2108,4 @@ Learn more from the Cookbook .. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle .. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php .. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack +.. _`security advisories database`: https://github.com/FriendsOfPHP/security-advisories From 36d3f2bdb12c618e7951129416042c27ce506c57 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 14 Dec 2014 20:01:31 +0100 Subject: [PATCH 611/835] This command is available sin Symfony 2.5 --- book/security.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index 84c631e6d0a..eae81117008 100644 --- a/book/security.rst +++ b/book/security.rst @@ -2058,8 +2058,8 @@ to work correctly. Just pass a file name to enable it:: Checking Dependencies Security ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 2.6 - The ``security:check`` command was introduced in Symfony 2.6. +.. versionadded:: 2.5 + The ``security:check`` command was introduced in Symfony 2.5. When using lots of dependencies in your Symfony projects, odds are that some of them contain security vulnerabilities. That's why Symfony includes a command From 0e7d0cd0558c669ffee7d377edd23597ea48dd86 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 14 Dec 2014 20:05:44 +0100 Subject: [PATCH 612/835] Added a note about the security advisories database --- contributing/code/security.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 51acb3f1adf..30e59ef3f23 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -38,6 +38,8 @@ confirmed, the core-team works on a solution following these steps: #. Publish the post on the official Symfony `blog`_ (it must also be added to the "`Security Advisories`_" category); #. Update the security advisory list (see below). +#. Update the public `security advisories database`_ maintained by the + FriendsOfPHP organization and which is used by the ``security:check`` command. .. note:: From e552369a792bcfef8ef8d828d77e7fc412ac93fe Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sun, 14 Dec 2014 20:29:51 +0100 Subject: [PATCH 613/835] Added a missing link reference --- contributing/code/security.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 30e59ef3f23..8649586a3bf 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -121,6 +121,7 @@ releases, starting from Symfony 1.0.0: * March 21, 2008: `symfony 1.0.12 is (finally) out ! `_ * June 25, 2007: `symfony 1.0.5 released (security fix) `_ -.. _Git repository: https://github.com/symfony/symfony -.. _blog: http://symfony.com/blog/ +.. _Git repository: https://github.com/symfony/symfony +.. _blog: http://symfony.com/blog/ .. _Security Advisories: http://symfony.com/blog/category/security-advisories +.. _`security advisories database`: https://github.com/FriendsOfPHP/security-advisories From 3c9a962377347931fa0697577e26c6a0b2313611 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Dec 2014 14:30:05 +0100 Subject: [PATCH 614/835] Added a note about the security:check command --- contributing/code/security.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 8649586a3bf..6e183b3d1e5 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -95,6 +95,11 @@ of the downstream projects included in this process: Security Advisories ------------------- +.. tip:: + + You can check your Symfony application for known security vulnerabilities + using the ``security:check`` command. + This section indexes security vulnerabilities that were fixed in Symfony releases, starting from Symfony 1.0.0: From fdfb1a066205244beab848852cf0dcdab25b687e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 16 Dec 2014 14:34:32 +0100 Subject: [PATCH 615/835] Added a note about the SensioDistributionBundle necessary for security:check --- book/security.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/book/security.rst b/book/security.rst index eae81117008..a2fd662a603 100644 --- a/book/security.rst +++ b/book/security.rst @@ -2059,7 +2059,9 @@ Checking Dependencies Security ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2.5 - The ``security:check`` command was introduced in Symfony 2.5. + The ``security:check`` command was introduced in Symfony 2.5. This command is + included in ``SensioDistributionBundle``, which has to be registered in your + application in order to use this command. When using lots of dependencies in your Symfony projects, odds are that some of them contain security vulnerabilities. That's why Symfony includes a command From 18fa2c6560f91cad9a336e54fd739e7a0660f778 Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Sun, 28 Dec 2014 21:03:07 +0200 Subject: [PATCH 616/835] Misc changes --- best_practices/controllers.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/best_practices/controllers.rst b/best_practices/controllers.rst index 369d7be4e4c..07bdf5ebb8e 100644 --- a/best_practices/controllers.rst +++ b/best_practices/controllers.rst @@ -110,8 +110,9 @@ for the homepage of our app: */ public function indexAction() { - $em = $this->getDoctrine()->getManager(); - $posts = $em->getRepository('App:Post')->findLatest(); + $posts = $this->getDoctrine() + ->getRepository('AppBundle:Post') + ->findLatest(); return $this->render('default/index.html.twig', array( 'posts' => $posts @@ -136,6 +137,7 @@ For example: .. code-block:: php + use AppBundle\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; /** @@ -146,7 +148,7 @@ For example: $deleteForm = $this->createDeleteForm($post); return $this->render('admin/post/show.html.twig', array( - 'post' => $post, + 'post' => $post, 'delete_form' => $deleteForm->createView(), )); } @@ -188,8 +190,10 @@ flexible: .. code-block:: php + use AppBundle\Entity\Post; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; + use Symfony\Component\HttpFoundation\Request; /** * @Route("/comment/{postSlug}/new", name = "comment_new") From 1e34823772375d5a9df5f721c398a9b202e83f4c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 29 Dec 2014 13:45:05 -0500 Subject: [PATCH 617/835] Fixes thanks to comments and a new note about the LegacyPdoSessionHandler --- .../configuration/pdo_session_storage.rst | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/cookbook/configuration/pdo_session_storage.rst b/cookbook/configuration/pdo_session_storage.rst index 26bb6fc784a..a388d759133 100644 --- a/cookbook/configuration/pdo_session_storage.rst +++ b/cookbook/configuration/pdo_session_storage.rst @@ -6,7 +6,7 @@ How to Use PdoSessionHandler to Store Sessions in the Database .. caution:: - There was a backwards-compatability break in Symfony 2.6: the database + There was a backwards-compatibility break in Symfony 2.6: the database schema changed slightly. See :ref:`Symfony 2.6 Changes ` for details. @@ -118,10 +118,9 @@ a second array argument to ``PdoSessionHandler``: .. code-block:: xml - - - + sessions @@ -132,17 +131,22 @@ a second array argument to ``PdoSessionHandler``: .. code-block:: php // app/config/config.php + + use Symfony\Component\DependencyInjection\Definition; // ... - $storageDefinition = new Definition('Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler', array( - new Reference('pdo'), - array('db_table' => 'session') - )); + $storageDefinition = new Definition( + 'Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler', + array( + new Reference('pdo'), + array('db_table' => 'session') + ) + ); $container->setDefinition('session.handler.pdo', $storageDefinition); .. versionadded:: 2.6 - The ``db_lifetime_col`` was introduced in Symfony 2.6 This column did - not exist previously. + The ``db_lifetime_col`` was introduced in Symfony 2.6. Prior to 2.6, + this column did not exist. The following things can be configured: @@ -202,7 +206,7 @@ Example SQL Statements .. sidebar:: Schema Changes needed when Upgrading to Symfony 2.6 - If you use the `PdoSessionHandler` prior to Symfony 2.6 and upgrade, you'll + If you use the ``PdoSessionHandler`` prior to Symfony 2.6 and upgrade, you'll need to make a few changes to your session table: * A new session lifetime (``sess_lifetime`` by default) integer column @@ -212,6 +216,10 @@ Example SQL Statements Check the SQL statements below for more details. + To keep the old (2.5 and earlier) functionality, change your class name + to use ``LegacyPdoSessionHandler`` instead of ``PdoSessionHandler`` (the + legacy class was added in Symfony 2.6.2). + MySQL ~~~~~ From 10b06daff42318e64b3ae98c056d92e8f38259a8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 29 Dec 2014 19:46:41 +0100 Subject: [PATCH 618/835] [Reference] fix wording --- reference/configuration/framework.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index dbe3f463ac7..1eff10507f3 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -530,7 +530,7 @@ cache **type**: ``string`` This value is used to determine the service that is used to persist class -metadata in a cache. The actual service name is built by prefix the configured +metadata in a cache. The actual service name is built by prefixing the configured value with ``validator.mapping.cache.`` (e.g. if the value is ``apc``, the ``validator.mapping.cache.apc`` service will be injected). The service has to implement the :class:`Symfony\\Component\\Validator\\Mapping\\Cache\\CacheInterface`. From 58f4a00434a46d28901a570172b66891adfa50d9 Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Mon, 29 Dec 2014 23:33:45 +0100 Subject: [PATCH 619/835] Use denyAccessUnlessGranted shortcut --- best_practices/security.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/best_practices/security.rst b/best_practices/security.rst index c1cb2e3f23e..3d4dcb04db3 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -327,9 +327,13 @@ via the even easier shortcut in a controller: { $post = // query for the post ... - if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) { - throw $this->createAccessDeniedException(); - } + $this->denyAccessUnlessGranted('edit', $post); + + // or without the shortcut: + // + // if (!$this->get('security.authorization_checker')->isGranted('edit', $post)) { + // throw $this->createAccessDeniedException(); + // } } Learn More From 897dc705b5bbd2866b9aa7c04d438d17d51869be Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 30 Dec 2014 08:55:23 +0100 Subject: [PATCH 620/835] Added a lot of changes suggested by reviewers --- book/security.rst | 20 ++++++++++++++------ contributing/code/security.rst | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/book/security.rst b/book/security.rst index a2fd662a603..4907a3428a8 100644 --- a/book/security.rst +++ b/book/security.rst @@ -2055,18 +2055,20 @@ to work correctly. Just pass a file name to enable it:: You can also access a secure random instance directly from the Symfony dependency injection container; its name is ``security.secure_random``. -Checking Dependencies Security -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _book-security-checking-vulnerabilities: + +Checking for Known Security Vulnerabilities in Dependencies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded:: 2.5 The ``security:check`` command was introduced in Symfony 2.5. This command is included in ``SensioDistributionBundle``, which has to be registered in your application in order to use this command. -When using lots of dependencies in your Symfony projects, odds are that some of -them contain security vulnerabilities. That's why Symfony includes a command -called ``security:check`` that checks whether any of your installed dependencies -contain a known security vulnerability: +When using lots of dependencies in your Symfony projects, some of them may +contain security vulnerabilities. That's why Symfony includes a command called +``security:check`` that checks your ``composer.lock`` file to find any known +security vulnerability in your installed dependencies: .. code-block:: bash @@ -2077,6 +2079,12 @@ update or replace compromised dependencies as soon as possible. Internally, this command uses the public `security advisories database`_ published by the FriendsOfPHP organization. +.. tip:: + + The ``security:check`` command terminates with a non-zero exit code if + any of your dependencies is affected by a known security vulnerability. + Therefore, you can easily integrate it in your build process. + Final Words ----------- diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 6e183b3d1e5..c3d8e709942 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -98,7 +98,7 @@ Security Advisories .. tip:: You can check your Symfony application for known security vulnerabilities - using the ``security:check`` command. + using the ``security:check`` command. See :doc:`` This section indexes security vulnerabilities that were fixed in Symfony releases, starting from Symfony 1.0.0: From 6a5561732c730a6f05fb377ed3f470d65c0a7c54 Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Tue, 30 Dec 2014 14:49:23 +0200 Subject: [PATCH 621/835] Update forms.rst --- best_practices/forms.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/best_practices/forms.rst b/best_practices/forms.rst index 3778e1d891f..897e4266468 100644 --- a/best_practices/forms.rst +++ b/best_practices/forms.rst @@ -13,8 +13,8 @@ Building Forms Define your forms as PHP classes. The Form component allows you to build forms right inside your controller -code. Honestly, if you don't need to reuse the form somewhere else, that's -totally fine. But for organization and reuse, we recommend that you define each +code. This is perfectly fine if you don't need to reuse the form somewhere else. +But for organization and reuse, we recommend that you define each form in its own PHP class:: namespace AppBundle\Form; From 9b9f45271f46be90e5587dd8bd5094db30b75bcf Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Tue, 30 Dec 2014 17:28:01 +0200 Subject: [PATCH 622/835] Improve readability Remove horizontal scroll on smaller width | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.3 | Fixed tickets | --- book/http_fundamentals.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/http_fundamentals.rst b/book/http_fundamentals.rst index 74da155cc7f..b955c03ea7e 100644 --- a/book/http_fundamentals.rst +++ b/book/http_fundamentals.rst @@ -242,8 +242,8 @@ have all the request information at your fingertips:: $request->headers->get('host'); $request->headers->get('content_type'); - $request->getMethod(); // GET, POST, PUT, DELETE, HEAD - $request->getLanguages(); // an array of languages the client accepts + $request->getMethod(); // GET, POST, PUT, DELETE, HEAD + $request->getLanguages(); // an array of languages the client accepts As a bonus, the ``Request`` class does a lot of work in the background that you'll never need to worry about. For example, the ``isSecure()`` method From 7b9d583cdefc7c03f67961b31f7d42ce903b52fe Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Mon, 29 Dec 2014 02:00:59 +0100 Subject: [PATCH 623/835] Clarify tip for creating a new AppBundle The previous description was not quite right, since an AppBundle can also be there in versions before 2.6. --- best_practices/creating-the-project.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/best_practices/creating-the-project.rst b/best_practices/creating-the-project.rst index d9f378ce98a..42dca76c3da 100644 --- a/best_practices/creating-the-project.rst +++ b/best_practices/creating-the-project.rst @@ -152,8 +152,7 @@ that follows these best practices: .. tip:: - If you are using Symfony 2.6 or a newer version, the ``AppBundle`` bundle - is already generated for you. If you are using an older Symfony version, + If your Symfony installation doesn't come with a pre-generated ``AppBundle``, you can generate it by hand executing this command: .. code-block:: bash From a496a56e03b077fa7134ffaadcf2f8ce90c1e6f3 Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Tue, 30 Dec 2014 19:23:10 +0200 Subject: [PATCH 624/835] Fix typo: con => on | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.3 | Fixed tickets | --- book/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index 4b3fa4b2f69..a7965c4568e 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -108,7 +108,7 @@ Creating Symfony Applications without the Installer If you still use PHP 5.3, or if you can't execute the installer for any reason, you can create Symfony applications using the alternative installation method -based con `Composer`_. +based on `Composer`_. Composer is the dependency manager used by modern PHP applications and it can also be used to create new applications based on the Symfony framework. If you From 7ae34891054f83fad540ca526d36ec65c09ccc3e Mon Sep 17 00:00:00 2001 From: Alexander Schwenn Date: Tue, 30 Dec 2014 07:44:30 +0100 Subject: [PATCH 625/835] [Reference] Add default_locale config description --- reference/configuration/framework.rst | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index dbe3f463ac7..8336ef0497c 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -19,6 +19,7 @@ Configuration * `http_method_override`_ * `ide`_ * `test`_ +* `default_locale`_ * `trusted_proxies`_ * `form`_ * enabled @@ -76,8 +77,8 @@ http_method_override 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 +method gets called automatically. It becomes the service container parameter +named ``kernel.http_method_override``. For more information, see :doc:`/cookbook/routing/method_parameters`. ide @@ -151,6 +152,17 @@ This setting should be present in your ``test`` environment (usually via .. _reference-framework-trusted-proxies: +default_locale +~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``en`` + +The default locale is used if no ``_locale`` routing parameter has been set. It +becomes the service container parameter named ``kernel.default_locale`` and it +is also available with the +:method:`Request::getDefaultLocale ` +method. + trusted_proxies ~~~~~~~~~~~~~~~ From d9a9310fa40a659368dc3484661b6e4020e81909 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sun, 7 Dec 2014 16:40:17 -0500 Subject: [PATCH 626/835] Completely re-reading the security book - more tutorial-styled - tried to move things into other entries - tried to keep as many anchor references as possible --- book/security.rst | 2268 ++++------------- components/map.rst.inc | 1 + components/security/index.rst | 1 + components/security/secure-tools.rst | 57 + cookbook/map.rst.inc | 3 + cookbook/security/access_control.rst | 299 +++ cookbook/security/form_login.rst | 6 +- cookbook/security/form_login_setup.rst | 472 ++++ cookbook/security/index.rst | 3 + cookbook/security/multiple_user_providers.rst | 148 ++ images/book/security_admin_role_access.png | Bin 77809 -> 0 bytes .../book/security_anonymous_user_access.png | Bin 80752 -> 0 bytes ...ty_anonymous_user_denied_authorization.png | Bin 99753 -> 0 bytes images/book/security_anonymous_wdt.png | Bin 0 -> 26288 bytes .../security_authentication_authorization.png | Bin 41578 -> 0 bytes .../book/security_full_step_authorization.png | Bin 142027 -> 0 bytes images/book/security_http_basic_popup.png | Bin 0 -> 40064 bytes .../security_ryan_no_role_admin_access.png | Bin 88966 -> 0 bytes images/book/symfony_loggedin_wdt.png | Bin 0 -> 21855 bytes reference/twig_reference.rst | 2 + 20 files changed, 1554 insertions(+), 1706 deletions(-) create mode 100644 components/security/secure-tools.rst create mode 100644 cookbook/security/access_control.rst create mode 100644 cookbook/security/form_login_setup.rst create mode 100644 cookbook/security/multiple_user_providers.rst delete mode 100644 images/book/security_admin_role_access.png delete mode 100644 images/book/security_anonymous_user_access.png delete mode 100644 images/book/security_anonymous_user_denied_authorization.png create mode 100644 images/book/security_anonymous_wdt.png delete mode 100644 images/book/security_authentication_authorization.png delete mode 100644 images/book/security_full_step_authorization.png create mode 100644 images/book/security_http_basic_popup.png delete mode 100644 images/book/security_ryan_no_role_admin_access.png create mode 100644 images/book/symfony_loggedin_wdt.png diff --git a/book/security.rst b/book/security.rst index 41efa44eeda..21f91ded579 100644 --- a/book/security.rst +++ b/book/security.rst @@ -4,1066 +4,32 @@ Security ======== -Security is a two-step process whose goal is to prevent a user from accessing -a resource that they should not have access to. +Symfony's security system is incredibly powerful, but it can also be confusing +to setup. In this chapter, you'll learn how to setup your application's security +step-by-step, from configuring your firewall and how you load users to denying +access and fetching the User object. Depending on what you need, sometimes +the initial setup can be tough. But once it's done, Symfony's security system +is both flexible and (hopefully) fun to work with. -In the first step of the process, the security system identifies who the user -is by requiring the user to submit some sort of identification. This is called -**authentication**, and it means that the system is trying to find out who -you are. +Since there's a lot to talk about, this chapter is organized into a few big +sections: -Once the system knows who you are, the next step is to determine if you should -have access to a given resource. This part of the process is called **authorization**, -and it means that the system is checking to see if you have privileges to -perform a certain action. +1) Initial ``security.yml`` setup (*authentication*); -.. image:: /images/book/security_authentication_authorization.png - :align: center - -Since the best way to learn is to see an example, just imagine that you want -to secure your application with HTTP Basic authentication. - -.. note:: - - :doc:`Symfony's security component ` is - available as a standalone PHP library for use inside any PHP project. - -Basic Example: HTTP Authentication ----------------------------------- - -The Security component can be configured via your application configuration. -In fact, most standard security setups are just a matter of using the right -configuration. The following configuration tells Symfony to secure any URL -matching ``/admin/*`` and to ask the user for credentials using basic HTTP -authentication (i.e. the old-school username/password box): - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - firewalls: - secured_area: - pattern: ^/ - anonymous: ~ - http_basic: - realm: "Secured Demo Area" - - access_control: - - { path: ^/admin/, roles: ROLE_ADMIN } - # Include the following line to also secure the /admin path itself - # - { path: ^/admin$, roles: ROLE_ADMIN } - - providers: - in_memory: - memory: - users: - ryan: { password: ryanpass, roles: 'ROLE_USER' } - admin: { password: kitten, roles: 'ROLE_ADMIN' } - - encoders: - Symfony\Component\Security\Core\User\User: plaintext - - .. code-block:: xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - 'firewalls' => array( - 'secured_area' => array( - 'pattern' => '^/', - 'anonymous' => array(), - 'http_basic' => array( - 'realm' => 'Secured Demo Area', - ), - ), - ), - 'access_control' => array( - array('path' => '^/admin/', 'role' => 'ROLE_ADMIN'), - // Include the following line to also secure the /admin path itself - // array('path' => '^/admin$', 'role' => 'ROLE_ADMIN'), - ), - 'providers' => array( - 'in_memory' => array( - 'memory' => array( - 'users' => array( - 'ryan' => array( - 'password' => 'ryanpass', - 'roles' => 'ROLE_USER', - ), - 'admin' => array( - 'password' => 'kitten', - 'roles' => 'ROLE_ADMIN', - ), - ), - ), - ), - ), - 'encoders' => array( - 'Symfony\Component\Security\Core\User\User' => 'plaintext', - ), - )); - -.. tip:: - - A standard Symfony distribution separates the security configuration - into a separate file (e.g. ``app/config/security.yml``). If you don't - have a separate security file, you can put the configuration directly - into your main config file (e.g. ``app/config/config.yml``). - -The end result of this configuration is a fully-functional security system -that looks like the following: - -* There are two users in the system (``ryan`` and ``admin``); -* Users authenticate themselves via the basic HTTP authentication prompt; -* Any URL matching ``/admin/*`` is secured, and only the ``admin`` user - can access it; -* All URLs *not* matching ``/admin/*`` are accessible by all users (and the - user is never prompted to log in). - -Read this short summary about how security works and how each part of the -configuration comes into play. - -How Security Works: Authentication and Authorization ----------------------------------------------------- - -Symfony's security system works by determining who a user is (i.e. authentication) -and then checking to see if that user should have access to a specific resource -or URL. - -.. _book-security-firewalls: - -Firewalls (Authentication) -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When a user makes a request to a URL that's protected by a firewall, the -security system is activated. The job of the firewall is to determine whether -the user needs to be authenticated, and if they do, to send a response -back to the user initiating the authentication process. - -A firewall is activated when the URL of an incoming request matches the configured -firewall's regular expression ``pattern`` config value. In this example, the -``pattern`` (``^/``) will match *every* incoming request. The fact that the -firewall is activated does *not* mean, however, that the HTTP authentication -username and password box is displayed for every URL. For example, any user -can access ``/foo`` without being prompted to authenticate. - -.. image:: /images/book/security_anonymous_user_access.png - :align: center - -This works first because the firewall allows *anonymous users* via the ``anonymous`` -configuration parameter. In other words, the firewall doesn't require the -user to fully authenticate immediately. And because no special ``role`` is -needed to access ``/foo`` (under the ``access_control`` section), the request -can be fulfilled without ever asking the user to authenticate. - -If you remove the ``anonymous`` key, the firewall will *always* make a user -fully authenticate immediately. - -Access Controls (Authorization) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If a user requests ``/admin/foo``, however, the process behaves differently. -This is because of the ``access_control`` configuration section that says -that any URL matching the regular expression pattern ``^/admin`` (i.e. ``/admin`` -or anything matching ``/admin/*``) requires the ``ROLE_ADMIN`` role. Roles -are the basis for most authorization: a user can access ``/admin/foo`` only -if it has the ``ROLE_ADMIN`` role. - -.. image:: /images/book/security_anonymous_user_denied_authorization.png - :align: center - -Like before, when the user originally makes the request, the firewall doesn't -ask for any identification. However, as soon as the access control layer -denies the user access (because the anonymous user doesn't have the ``ROLE_ADMIN`` -role), the firewall jumps into action and initiates the authentication process. -The authentication process depends on the authentication mechanism you're -using. For example, if you're using the form login authentication method, -the user will be redirected to the login page. If you're using HTTP authentication, -the user will be sent an HTTP 401 response so that the user sees the username -and password box. - -The user now has the opportunity to submit its credentials back to the application. -If the credentials are valid, the original request can be re-tried. - -.. image:: /images/book/security_ryan_no_role_admin_access.png - :align: center - -In this example, the user ``ryan`` successfully authenticates with the firewall. -But since ``ryan`` doesn't have the ``ROLE_ADMIN`` role, they're still denied -access to ``/admin/foo``. Ultimately, this means that the user will see some -sort of message indicating that access has been denied. - -.. tip:: - - When Symfony denies the user access, the user sees an error screen and - receives a 403 HTTP status code (``Forbidden``). You can customize the - access denied error screen by following the directions in the - :ref:`Error Pages ` cookbook entry - to customize the 403 error page. - -Finally, if the ``admin`` user requests ``/admin/foo``, a similar process -takes place, except now, after being authenticated, the access control layer -will let the request pass through: - -.. image:: /images/book/security_admin_role_access.png - :align: center - -The request flow when a user requests a protected resource is straightforward, -but incredibly flexible. As you'll see later, authentication can be handled -in any number of ways, including via a form login, X.509 certificate, or by -authenticating the user via Twitter. Regardless of the authentication method, -the request flow is always the same: - -#. A user accesses a protected resource; -#. The application redirects the user to the login form; -#. The user submits its credentials (e.g. username/password); -#. The firewall authenticates the user; -#. The authenticated user re-tries the original request. - -.. note:: - - The *exact* process actually depends a little bit on which authentication - mechanism you're using. For example, when using form login, the user - submits its credentials to one URL that processes the form (e.g. ``/login_check``) - and then is redirected back to the originally requested URL (e.g. ``/admin/foo``). - But with HTTP authentication, the user submits its credentials directly - to the original URL (e.g. ``/admin/foo``) and then the page is returned - to the user in that same request (i.e. no redirect). - - These types of idiosyncrasies shouldn't cause you any problems, but they're - good to keep in mind. - -.. tip:: - - You'll also learn later how *anything* can be secured in Symfony, including - specific controllers, objects, or even PHP methods. - -.. _book-security-form-login: - -Using a Traditional Login Form ------------------------------- - -.. tip:: - - In this section, you'll learn how to create a basic login form that continues - to use the hard-coded users that are defined in the ``security.yml`` file. - - To load users from the database, please read :doc:`/cookbook/security/entity_provider`. - By reading that article and this section, you can create a full login form - system that loads users from the database. - -So far, you've seen how to blanket your application beneath a firewall and -then protect access to certain areas with roles. By using HTTP Authentication, -you can effortlessly tap into the native username/password box offered by -all browsers. However, Symfony supports many authentication mechanisms out -of the box. For details on all of them, see the -:doc:`Security Configuration Reference `. - -In this section, you'll enhance this process by allowing the user to authenticate -via a traditional HTML login form. - -First, enable form login under your firewall: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - firewalls: - secured_area: - pattern: ^/ - anonymous: ~ - form_login: - login_path: login - check_path: login_check - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - 'firewalls' => array( - 'secured_area' => array( - 'pattern' => '^/', - 'anonymous' => array(), - 'form_login' => array( - 'login_path' => 'login', - 'check_path' => 'login_check', - ), - ), - ), - )); - -.. tip:: - - If you don't need to customize your ``login_path`` or ``check_path`` - values (the values used here are the default values), you can shorten - your configuration: - - .. configuration-block:: - - .. code-block:: yaml - - form_login: ~ - - .. code-block:: xml - - - - .. code-block:: php - - 'form_login' => array(), - -Now, when the security system initiates the authentication process, it will -redirect the user to the login form (``/login`` by default). Implementing this -login form visually is your job. First, create the two routes you used in the -security configuration: the ``login`` route will display the login form (i.e. -``/login``) and the ``login_check`` route will handle the login form -submission (i.e. ``/login_check``): - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/routing.yml - login: - path: /login - defaults: { _controller: AcmeSecurityBundle:Security:login } - login_check: - path: /login_check - - .. code-block:: xml - - - - - - - AcmeSecurityBundle:Security:login - - - - - - .. code-block:: php - - // app/config/routing.php - use Symfony\Component\Routing\RouteCollection; - use Symfony\Component\Routing\Route; - - $collection = new RouteCollection(); - $collection->add('login', new Route('/login', array( - '_controller' => 'AcmeDemoBundle:Security:login', - ))); - $collection->add('login_check', new Route('/login_check', array())); - - return $collection; - -.. note:: - - You will *not* need to implement a controller for the ``/login_check`` - URL as the firewall will automatically catch and process any form submitted - to this URL. - -.. versionadded:: 2.1 - As of Symfony 2.1, you *must* have routes configured for your ``login_path`` - and ``check_path``. These keys can be route names (as shown in this example) - or URLs that have routes configured for them. - -Notice that the name of the ``login`` route matches the ``login_path`` config -value, as that's where the security system will redirect users that need -to login. - -Next, create the controller that will display the login form:: - - // src/Acme/SecurityBundle/Controller/SecurityController.php; - namespace Acme\SecurityBundle\Controller; - - use Symfony\Bundle\FrameworkBundle\Controller\Controller; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\Security\Core\SecurityContextInterface; - - class SecurityController extends Controller - { - public function loginAction(Request $request) - { - $session = $request->getSession(); - - // get the login error if there is one - if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - $error = $request->attributes->get( - SecurityContextInterface::AUTHENTICATION_ERROR - ); - } elseif (null !== $session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { - $error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR); - $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR); - } else { - $error = ''; - } - - // last username entered by the user - $lastUsername = (null === $session) ? '' : $session->get(SecurityContextInterface::LAST_USERNAME); - - return $this->render( - 'AcmeSecurityBundle:Security:login.html.twig', - array( - // last username entered by the user - 'last_username' => $lastUsername, - 'error' => $error, - ) - ); - } - } - -Don't let this controller confuse you. As you'll see in a moment, when the -user submits the form, the security system automatically handles the form -submission for you. If the user had submitted an invalid username or password, -this controller reads the form submission error from the security system so -that it can be displayed back to the user. - -In other words, your job is to display the login form and any login errors -that may have occurred, but the security system itself takes care of checking -the submitted username and password and authenticating the user. - -Finally, create the corresponding template: - -.. configuration-block:: - - .. code-block:: html+jinja - - {# src/Acme/SecurityBundle/Resources/views/Security/login.html.twig #} - {% if error %} -
    {{ error.message }}
    - {% endif %} - - - - - - - - - {# - If you want to control the URL the user - is redirected to on success (more details below) - - #} - - - - - .. code-block:: html+php - - - -
    getMessage() ?>
    - - -
    - - - - - - - - - - - -.. caution:: - - This login form is currently not protected against CSRF attacks. Read - :doc:`/cookbook/security/csrf_in_login_form` on how to protect your login form. - -.. tip:: - - The ``error`` variable passed into the template is an instance of - :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException`. - It may contain more information - or even sensitive information - about - the authentication failure, so use it wisely! - -The form has very few requirements. First, by submitting the form to ``/login_check`` -(via the ``login_check`` route), the security system will intercept the form -submission and process the form for you automatically. Second, the security -system expects the submitted fields to be called ``_username`` and ``_password`` -(these field names can be :ref:`configured `). - -And that's it! When you submit the form, the security system will automatically -check the user's credentials and either authenticate the user or send the -user back to the login form where the error can be displayed. - -To review the whole process: - -#. The user tries to access a resource that is protected; -#. The firewall initiates the authentication process by redirecting the - user to the login form (``/login``); -#. The ``/login`` page renders login form via the route and controller created - in this example; -#. The user submits the login form to ``/login_check``; -#. The security system intercepts the request, checks the user's submitted - credentials, authenticates the user if they are correct, and sends the - user back to the login form if they are not. - -By default, if the submitted credentials are correct, the user will be redirected -to the original page that was requested (e.g. ``/admin/foo``). If the user -originally went straight to the login page, he'll be redirected to the homepage. -This can be highly customized, allowing you to, for example, redirect the -user to a specific URL. - -For more details on this and how to customize the form login process in general, -see :doc:`/cookbook/security/form_login`. - -.. _book-security-common-pitfalls: - -.. sidebar:: Avoid common Pitfalls - - When setting up your login form, watch out for a few common pitfalls. - - **1. Create the correct routes** - - First, be sure that you've defined the ``login`` and ``login_check`` - routes correctly and that they correspond to the ``login_path`` and - ``check_path`` config values. A misconfiguration here can mean that you're - redirected to a 404 page instead of the login page, or that submitting - the login form does nothing (you just see the login form over and over - again). - - **2. Be sure the login page isn't secure** - - Also, be sure that the login page does *not* require any roles to be - viewed. For example, the following configuration - which requires the - ``ROLE_ADMIN`` role for all URLs (including the ``/login`` URL), will - cause a redirect loop: - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - - # ... - access_control: - - { path: ^/, roles: ROLE_ADMIN } - - .. code-block:: xml - - - - - - - - - .. code-block:: php - - // app/config/security.php - - // ... - 'access_control' => array( - array('path' => '^/', 'role' => 'ROLE_ADMIN'), - ), - - Removing the access control on the ``/login`` URL fixes the problem: - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - - # ... - access_control: - - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - - { path: ^/, roles: ROLE_ADMIN } - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // app/config/security.php - - // ... - 'access_control' => array( - array('path' => '^/login', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), - array('path' => '^/', 'role' => 'ROLE_ADMIN'), - ), - - Also, if your firewall does *not* allow for anonymous users, you'll need - to create a special firewall that allows anonymous users for the login - page: - - .. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - - # ... - firewalls: - login_firewall: - pattern: ^/login$ - anonymous: ~ - secured_area: - pattern: ^/ - form_login: ~ - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - - // ... - 'firewalls' => array( - 'login_firewall' => array( - 'pattern' => '^/login$', - 'anonymous' => array(), - ), - 'secured_area' => array( - 'pattern' => '^/', - 'form_login' => array(), - ), - ), - - **3. Be sure /login_check is behind a firewall** - - Next, make sure that your ``check_path`` URL (e.g. ``/login_check``) - is behind the firewall you're using for your form login (in this example, - the single firewall matches *all* URLs, including ``/login_check``). If - ``/login_check`` doesn't match any firewall, you'll receive an ``Unable - to find the controller for path "/login_check"`` exception. - - **4. Multiple firewalls don't share security context** - - If you're using multiple firewalls and you authenticate against one firewall, - you will *not* be authenticated against any other firewalls automatically. - Different firewalls are like different security systems. To do this you have - to explicitly specify the same :ref:`reference-security-firewall-context` - for different firewalls. But usually for most applications, having one - main firewall is enough. - - **5. Routing error pages are not covered by firewalls** - - As Routing is done *before* security, Routing error pages are not covered - by any firewall. This means you can't check for security or even access - the user object on these pages. See :doc:`/cookbook/controller/error_pages` - for more details. - -Authorization -------------- - -The first step in security is always authentication. Once the user has been -authenticated, authorization begins. Authorization provides a standard and -powerful way to decide if a user can access any resource (a URL, a model -object, a method call, ...). This works by assigning specific roles to each -user, and then requiring different roles for different resources. - -The process of authorization has two different sides: - -#. The user has a specific set of roles; -#. A resource requires a specific role in order to be accessed. - -In this section, you'll focus on how to secure different resources (e.g. URLs, -method calls, etc) with different roles. Later, you'll learn more about how -roles are created and assigned to users. - -Securing specific URL Patterns -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The most basic way to secure part of your application is to secure an entire -URL pattern. You've seen this already in the first example of this chapter, -where anything matching the regular expression pattern ``^/admin`` requires -the ``ROLE_ADMIN`` role. - -You can define as many URL patterns as you need - each is a regular expression. - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - # ... - access_control: - - { path: ^/admin/users, roles: ROLE_SUPER_ADMIN } - - { path: ^/admin, roles: ROLE_ADMIN } - - .. code-block:: xml - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - // ... - 'access_control' => array( - array('path' => '^/admin/users', 'role' => 'ROLE_SUPER_ADMIN'), - array('path' => '^/admin', 'role' => 'ROLE_ADMIN'), - ), - )); - -.. tip:: - - Prepending the path with ``^`` ensures that only URLs *beginning* with - the pattern are matched. For example, a path of simply ``/admin`` (without - the ``^``) would correctly match ``/admin/foo`` but would also match URLs - like ``/foo/admin``. - -.. _security-book-access-control-explanation: - -Understanding how ``access_control`` Works -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -For each incoming request, Symfony checks each ``access_control`` entry -to find *one* that matches the current request. As soon as it finds a matching -``access_control`` entry, it stops - only the **first** matching ``access_control`` -is used to enforce access. - -Each ``access_control`` has several options that configure two different -things: - -#. :ref:`should the incoming request match this access control entry ` -#. :ref:`once it matches, should some sort of access restriction be enforced `: - -.. _security-book-access-control-matching-options: - -1. Matching Options -................... - -Symfony creates an instance of :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher` -for each ``access_control`` entry, which determines whether a given -access control should be used on this request. The following ``access_control`` -options are used for matching: - -* ``path`` -* ``ip`` or ``ips`` -* ``host`` -* ``methods`` - -Take the following ``access_control`` entries as an example: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - # ... - access_control: - - { path: ^/admin, roles: ROLE_USER_IP, ip: 127.0.0.1 } - - { path: ^/admin, roles: ROLE_USER_HOST, host: symfony\.com$ } - - { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] } - - { path: ^/admin, roles: ROLE_USER } - - .. code-block:: xml - - - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - // ... - 'access_control' => array( - array( - 'path' => '^/admin', - 'role' => 'ROLE_USER_IP', - 'ip' => '127.0.0.1', - ), - array( - 'path' => '^/admin', - 'role' => 'ROLE_USER_HOST', - 'host' => 'symfony\.com$', - ), - array( - 'path' => '^/admin', - 'role' => 'ROLE_USER_METHOD', - 'method' => 'POST, PUT', - ), - array( - 'path' => '^/admin', - 'role' => 'ROLE_USER', - ), - ), - )); - -For each incoming request, Symfony will decide which ``access_control`` -to use based on the URI, the client's IP address, the incoming host name, -and the request method. Remember, the first rule that matches is used, and -if ``ip``, ``host`` or ``method`` are not specified for an entry, that ``access_control`` -will match any ``ip``, ``host`` or ``method``: - -+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ -| URI | IP | HOST | METHOD | ``access_control`` | Why? | -+=================+=============+=============+============+================================+=============================================================+ -| ``/admin/user`` | 127.0.0.1 | example.com | GET | rule #1 (``ROLE_USER_IP``) | The URI matches ``path`` and the IP matches ``ip``. | -+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ -| ``/admin/user`` | 127.0.0.1 | symfony.com | GET | rule #1 (``ROLE_USER_IP``) | The ``path`` and ``ip`` still match. This would also match | -| | | | | | the ``ROLE_USER_HOST`` entry, but *only* the **first** | -| | | | | | ``access_control`` match is used. | -+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ -| ``/admin/user`` | 168.0.0.1 | symfony.com | GET | rule #2 (``ROLE_USER_HOST``) | The ``ip`` doesn't match the first rule, so the second | -| | | | | | rule (which matches) is used. | -+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ -| ``/admin/user`` | 168.0.0.1 | symfony.com | POST | rule #2 (``ROLE_USER_HOST``) | The second rule still matches. This would also match the | -| | | | | | third rule (``ROLE_USER_METHOD``), but only the **first** | -| | | | | | matched ``access_control`` is used. | -+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ -| ``/admin/user`` | 168.0.0.1 | example.com | POST | rule #3 (``ROLE_USER_METHOD``) | The ``ip`` and ``host`` don't match the first two entries, | -| | | | | | but the third - ``ROLE_USER_METHOD`` - matches and is used. | -+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ -| ``/admin/user`` | 168.0.0.1 | example.com | GET | rule #4 (``ROLE_USER``) | The ``ip``, ``host`` and ``method`` prevent the first | -| | | | | | three entries from matching. But since the URI matches the | -| | | | | | ``path`` pattern of the ``ROLE_USER`` entry, it is used. | -+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ -| ``/foo`` | 127.0.0.1 | symfony.com | POST | matches no entries | This doesn't match any ``access_control`` rules, since its | -| | | | | | URI doesn't match any of the ``path`` values. | -+-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ - -.. _security-book-access-control-enforcement-options: - -2. Access Enforcement -..................... - -Once Symfony has decided which ``access_control`` entry matches (if any), -it then *enforces* access restrictions based on the ``roles`` and ``requires_channel`` -options: - -* ``role`` If the user does not have the given role(s), then access is denied - (internally, an :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` - is thrown); - -* ``requires_channel`` If the incoming request's channel (e.g. ``http``) - does not match this value (e.g. ``https``), the user will be redirected - (e.g. redirected from ``http`` to ``https``, or vice versa). - -.. tip:: - - If access is denied, the system will try to authenticate the user if not - already (e.g. redirect the user to the login page). If the user is already - logged in, the 403 "access denied" error page will be shown. See - :doc:`/cookbook/controller/error_pages` for more information. - -.. _book-security-securing-ip: - -Securing by IP -~~~~~~~~~~~~~~ - -Certain situations may arise when you may need to restrict access to a given -path based on IP. This is particularly relevant in the case of -:ref:`Edge Side Includes ` (ESI), for example. When ESI is -enabled, it's recommended to secure access to ESI URLs. Indeed, some ESI may -contain some private content like the current logged in user's information. To -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``. - -.. caution:: - - As you'll read in the explanation below the example, the ``ip`` option - does not restrict to a specific IP address. Instead, using the ``ip`` - key means that the ``access_control`` entry will only match this IP address, - and users accessing it from a different IP address will continue down - the ``access_control`` list. - -Here is an example of how you might secure all ESI routes that start with a -given prefix, ``/esi``, from outside access: - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/security.yml - security: - # ... - access_control: - - { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } - - { path: ^/esi, roles: ROLE_NO_ACCESS } - - .. code-block:: xml - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - // ... - 'access_control' => array( - array( - 'path' => '^/esi', - 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', - 'ips' => '127.0.0.1, ::1' - ), - array( - 'path' => '^/esi', - 'role' => 'ROLE_NO_ACCESS' - ), - ), - )); - -Here is how it works when the path is ``/esi/something`` coming from the -``10.0.0.1`` IP: +2) Denying access to your app (*authorization*); -* The first access control rule is ignored as the ``path`` matches but the - ``ip`` does not match either of the IPs listed; +3) Fetching the current User object -* 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`` - role as it's not defined, access is denied (the ``ROLE_NO_ACCESS`` role can - be anything that does not match an existing role, it just serves as a trick - to always deny access). +These are followed by a number of small (but still captivating) sections, +like :ref:`logging out ` and :ref:`encoding user passwords `. -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 - ``IS_AUTHENTICATED_ANONYMOUSLY`` role. - -* The second access rule is not examined as the first rule matched. - -.. _book-security-securing-channel: +.. _book-security-firewalls: -Forcing a Channel (http, https) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1) Initial security.yml Setup (Authentication) +---------------------------------------------- -You can also require a user to access a URL via SSL; just use the -``requires_channel`` argument in any ``access_control`` entries. If this -``access_control`` is matched and the request is using the ``http`` channel, -the user will be redirected to ``https``: +The security system is configured in ``app/config/security.yml``. The default +configuration looks like this: .. configuration-block:: @@ -1071,66 +37,50 @@ the user will be redirected to ``https``: # app/config/security.yml security: - # ... - access_control: - - { path: ^/cart/checkout, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } - - .. code-block:: xml + providers: + in_memory: + memory: ~ - - - + firewalls: + dev: + pattern: ^/(_(profiler|wdt)|css|images|js)/ + security: false - - - - + default: + anonymous: ~ - .. code-block:: php +The ``firewalls`` key is the *heart* of your security configuration. The +``dev`` firewall isn't important, it just makes sure that Symfony's development +tools - which live under URLs like ``/_profiler`` and ``/_wdt`` aren't blocked +by your security. - // app/config/security.php - $container->loadFromExtension('security', array( - 'access_control' => array( - array( - 'path' => '^/cart/checkout', - 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', - 'requires_channel' => 'https', - ), - ), - )); +All other URLs will be handled by the ``default`` firewall (no ``pattern`` +key means it matches *all* URLs). You can think of the firewall like your +security system, and so it usually makes sense to have just one main firewall. +But this does *not* mean that every URL requires authentication - the ``anonymous`` +key takes care of this. In fact, if you go to the homepage right now, you'll +have access and you'll see that you're "authenticated" as ``anon.``. Don't +be fooled by the "Yes" next to Authenticated, you're just an anonymous user: -Users ------ +.. image:: /images/book/security_anonymous_wdt.png + :align: center -In the previous sections, you learned how you can protect different resources -by requiring a set of *roles* for a resource. This section explores -the other side of authorization: users. +You'll learn later how to deny access to certain URLs or controllers. -Where do Users Come from? (*User Providers*) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. tip:: -During authentication, the user submits a set of credentials (usually a username -and password). The job of the authentication system is to match those credentials -against some pool of users. So where does this list of users come from? + Security is *highly* configurable and there's a + :doc:`Security Configuration Reference ` + that shows all of the options with some extra explanation. -In Symfony, users can come from anywhere - a configuration file, a database -table, a web service, or anything else you can dream up. Anything that provides -one or more users to the authentication system is known as a "user provider". -Symfony comes standard with the two most common user providers: one that -loads users from a configuration file and one that loads users from a database -table. +A) Configuring how your Users will Authenticate +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Specifying Users in a Configuration File -........................................ +The main job of a firewall is to configure *how* your users will authenticate. +Will they use a login form? Http Basic? An API token? All of the above? -The easiest way to specify your users is directly in a configuration file. -In fact, you've seen this already in the example in this chapter. +Let's start with Http Basic (the old-school pop-up) and work up from there. +To activate this, add the ``http_basic`` key under your firewall: .. configuration-block:: @@ -1139,133 +89,36 @@ In fact, you've seen this already in the example in this chapter. # app/config/security.yml security: # ... - providers: - default_provider: - memory: - users: - ryan: { password: ryanpass, roles: 'ROLE_USER' } - admin: { password: kitten, roles: 'ROLE_ADMIN' } - - .. code-block:: xml - - - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - // ... - 'providers' => array( - 'default_provider' => array( - 'memory' => array( - 'users' => array( - 'ryan' => array( - 'password' => 'ryanpass', - 'roles' => 'ROLE_USER', - ), - 'admin' => array( - 'password' => 'kitten', - 'roles' => 'ROLE_ADMIN', - ), - ), - ), - ), - ), - )); - -This user provider is called the "in-memory" user provider, since the users -aren't stored anywhere in a database. The actual user object is provided -by Symfony (:class:`Symfony\\Component\\Security\\Core\\User\\User`). - -.. tip:: - - Any user provider can load users directly from configuration by specifying - the ``users`` configuration parameter and listing the users beneath it. -.. caution:: - - If your username is completely numeric (e.g. ``77``) or contains a dash - (e.g. ``user-name``), you should use an alternative syntax when specifying - users in YAML: - - .. code-block:: yaml - - users: - - { name: 77, password: pass, roles: 'ROLE_USER' } - - { name: user-name, password: pass, roles: 'ROLE_USER' } - -For smaller sites, this method is quick and easy to setup. For more complex -systems, you'll want to load your users from the database. - -.. _book-security-user-entity: - -Loading Users from the Database -............................... - -If you'd like to load your users via the Doctrine ORM, you can easily do -this by creating a ``User`` class and configuring the ``entity`` provider. - -.. tip:: - - A high-quality open source bundle is available that allows your users - to be stored in a database. Read more about the `FOSUserBundle`_ - on GitHub. - -With this approach, you'll first create your own ``User`` class, which will -be stored in the database. + firewalls: + # ... + default: + anonymous: ~ + http_basic: ~ -.. code-block:: php +Simple! To try this, you need to require the user to be logged in to see +a page. To make things interesting, create a new page at ``/admin``. For +example, if you use annotations, create something like this:: - // src/Acme/UserBundle/Entity/User.php - namespace Acme\UserBundle\Entity; + // src/AppBundle/Controller/DefaultController.php + // ... - use Symfony\Component\Security\Core\User\UserInterface; - use Doctrine\ORM\Mapping as ORM; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Symfony\Component\HttpFoundation\Response; - /** - * @ORM\Entity - */ - class User implements UserInterface + class DefaultController extends Controller { /** - * @ORM\Column(type="string", length=255) + * @Route("/admin") */ - protected $username; - - // ... + public function adminAction() + { + return new Response('Admin page!'); + } } -As far as the security system is concerned, the only requirement for your -custom user class is that it implements the :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface` -interface. This means that your concept of a "user" can be anything, as long -as it implements this interface. - -.. note:: - - The user object will be serialized and saved in the session during requests, - therefore it is recommended that you `implement the \Serializable interface`_ - in your user object. This is especially important if your ``User`` class - has a parent class with private properties. - -Next, configure an ``entity`` user provider, and point it to your ``User`` -class: +Next, add an ``access_control`` entry to ``security.yml`` that requires the +user to be logged in to access this URL: .. configuration-block:: @@ -1273,66 +126,48 @@ class: # app/config/security.yml security: - providers: - main: - entity: - class: Acme\UserBundle\Entity\User - property: username + # ... + firewalls: + # ... + + access_control: + # require ROLE_ADMIN for /admin/* + - { path: ^/admin, roles: ROLE_ADMIN } - .. code-block:: xml +.. note:: - - - + You'll learn more about this ``ROLE_ADMIN`` thing and denying access + later in the :ref:`security-authorization` section. - - - - - - +Great! Now, if you go to ``/admin``, you'll see the HTTP Basic popup: - .. code-block:: php +.. image:: /images/book/security_http_basic_popup.png + :align: center - // app/config/security.php - $container->loadFromExtension('security', array( - 'providers' => array( - 'main' => array( - 'entity' => array( - 'class' => 'Acme\UserBundle\Entity\User', - 'property' => 'username', - ), - ), - ), - )); +But who can you login as? Where do users come from? + +.. _book-security-form-login: -With the introduction of this new provider, the authentication system will -attempt to load a ``User`` object from the database by using the ``username`` -field of that class. +.. tip:: -.. note:: - This example is just meant to show you the basic idea behind the ``entity`` - provider. For a full working example, see :doc:`/cookbook/security/entity_provider`. + Want to use a traditional login form? Great! See :doc:`/cookbook/security/form_login_setup`. + What other methods are supported? See the :doc:`Configuration Reference ` + or :doc:`build your own `. -For more information on creating your own custom provider (e.g. if you needed -to load users via a web service), see :doc:`/cookbook/security/custom_provider`. +.. _security-user-providers: -.. _book-security-encoding-user-password: +B) Configuring how Users are Loaded +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Encoding the User's Password -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +When you type in your username, Symfony needs to load that user's information +from somewhere. This is called a "user provider", and you're in charge of +configuring it. Symfony has a built-in way to +:doc:`load users from the database `, +or you can :doc:`create your own user provider `. -So far, for simplicity, all the examples have stored the users' passwords -in plain text (whether those users are stored in a configuration file or in -a database somewhere). Of course, in a real application, you'll want to encode -your users' passwords for security reasons. This is easily accomplished by -mapping your User class to one of several built-in "encoders". For example, -to store your users in memory, but obscure their passwords via ``bcrypt``, -do the following: +The easiest (but most limited) way, is to configure Symfony to load hardcoded +users directly from the ``security.yml`` file itself. This is called an "in memory" +provider, but it's better to think of it as an "in configuration" provider: .. configuration-block:: @@ -1340,177 +175,94 @@ do the following: # app/config/security.yml security: - # ... providers: in_memory: memory: users: ryan: - password: $2a$12$w/aHvnC/XNeDVrrl65b3dept8QcKqpADxUlbraVXXsC03Jam5hvoO + password: ryanpass, roles: 'ROLE_USER' admin: - password: $2a$12$HmOsqRDJK0HuMDQ5Fb2.AOLMQHyNHGD0seyjU3lEVusjT72QQEIpW + password: kitten roles: 'ROLE_ADMIN' - encoders: - Symfony\Component\Security\Core\User\User: - algorithm: bcrypt - cost: 12 - - .. code-block:: xml - - - - - - - - - - - - - - - - - - - .. code-block:: php - - // app/config/security.php - $container->loadFromExtension('security', array( - // ... - 'providers' => array( - 'in_memory' => array( - 'memory' => array( - 'users' => array( - 'ryan' => array( - 'password' => '$2a$12$w/aHvnC/XNeDVrrl65b3dept8QcKqpADxUlbraVXXsC03Jam5hvoO', - 'roles' => 'ROLE_USER', - ), - 'admin' => array( - 'password' => '$2a$12$HmOsqRDJK0HuMDQ5Fb2.AOLMQHyNHGD0seyjU3lEVusjT72QQEIpW', - 'roles' => 'ROLE_ADMIN', - ), - ), - ), - ), - ), - 'encoders' => array( - 'Symfony\Component\Security\Core\User\User' => array( - 'algorithm' => 'bcrypt', - 'iterations' => 12, - ), - ), - )); - -.. versionadded:: 2.2 - The BCrypt encoder was introduced in Symfony 2.2. +Like with ``firewalls``, you can have multiple ``providers``, but you'll +probably only need one. If you *do* have multiple, you can configure which +*one* provider to use for your firewall under its ``provider`` key (e.g. +``provider: in_memory``). -You can now calculate the hashed password either programmatically -(e.g. ``password_hash('ryanpass', PASSWORD_BCRYPT, array('cost' => 12));``) -or via some online tool. +Try to login using username ``admin`` and password ``kitten``. You should +see an error! -.. include:: /cookbook/security/_ircmaxwell_password-compat.rst.inc - -Supported algorithms for this method depend on your PHP version. A full list -is available by calling the PHP function :phpfunction:`hash_algos`. + No encoder has been configured for account "Symfony\Component\Security\Core\User\User" -.. versionadded:: 2.2 - As of Symfony 2.2 you can also use the :ref:`PBKDF2 ` - password encoder. - -Determining the Hashed Password -............................... +To fix this, add an ``encoders`` key: -If you're storing users in the database and you have some sort of registration -form for users, you'll need to be able to determine the hashed password so -that you can set it on your user before inserting it. No matter what algorithm -you configure for your user object, the hashed password can always be determined -in the following way from a controller:: +.. configuration-block:: - $factory = $this->get('security.encoder_factory'); - $user = new Acme\UserBundle\Entity\User(); + .. code-block:: yaml - $encoder = $factory->getEncoder($user); - $password = $encoder->encodePassword('ryanpass', $user->getSalt()); - $user->setPassword($password); + # app/config/security.yml + security: + # ... -In order for this to work, just make sure that you have the encoder for your -user class (e.g. ``Acme\UserBundle\Entity\User``) configured under the ``encoders`` -key in ``app/config/security.yml``. + encoders: + Symfony\Component\Security\Core\User\User: plaintext -.. caution:: +User providers load user information and put it into a ``User`` object. If +you :doc:`load users from the database ` +or :doc:`some other source `, you'll +use your own custom User class. But when you use the "in memory" provider, +it gives you a ``Symfony\Component\Security\Core\User\User`` object. - When you allow a user to submit a plaintext password (e.g. registration - form, change password form), you *must* have validation that guarantees - that the password is 4096 characters or fewer. Read more details in - :ref:`How to implement a simple Registration Form `. +Whatever your User class is, you need to tell Symfony what algorithm was +used to encode the passwords. In this case, the passwords are just plaintext, +but in a second, you'll change this to use ``bcrypt``. -Retrieving the User Object -~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you refresh now, you'll be logged in! The web debug toolbar even tells +you who you are and what roles you have: -After authentication, the ``User`` object of the current user can be accessed -via the ``security.context`` service. From inside a controller, this will -look like:: +.. image:: /images/book/symfony_loggedin_wdt.png + :align: center - public function indexAction() - { - $user = $this->get('security.context')->getToken()->getUser(); - } +Because this URL requires ``ROLE_ADMIN``, if you had logged in as ``ryan``, +this would deny you access. More on that later (:ref:`security-authorization-access-control`). -In a controller this can be shortcut to: +.. _book-security-user-entity: -.. code-block:: php +Loading Users from the Database +............................... - public function indexAction() - { - $user = $this->getUser(); - } +If you'd like to load your users via the Doctrine ORM, that's easy! See +:doc:`/cookbook/security/entity_provider` for all the details. -.. note:: +.. _book-security-encoding-user-password: - Anonymous users are technically authenticated, meaning that the ``isAuthenticated()`` - method of an anonymous user object will return true. To check if your - user is actually authenticated, check for the ``IS_AUTHENTICATED_FULLY`` - role. +C) Encoding the Users Password +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In a Twig Template this object can be accessed via the ``app.user`` key, -which calls the :method:`GlobalVariables::getUser() ` -method: +Whether your users are stored in ``security.yml``, in a database or somewhere +else, you'll want to encode their passwords. The best algorithm to use is +``bcrypt``: .. configuration-block:: - .. code-block:: html+jinja - -

    Username: {{ app.user.username }}

    + .. code-block:: yaml - .. code-block:: html+php + # app/config/security.yml + security: + # ... -

    Username: getUser()->getUsername() ?>

    + encoders: + Symfony\Component\Security\Core\User\User: + algorithm: bcrypt + cost: 12 -Using multiple User Providers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. include:: /cookbook/security/_ircmaxwell_password-compat.rst.inc -Each authentication mechanism (e.g. HTTP Authentication, form login, etc) -uses exactly one user provider, and will use the first declared user provider -by default. But what if you want to specify a few users via configuration -and the rest of your users in the database? This is possible by creating -a new provider that chains the two together: +Of course, your user's passwords now need to be encoded with this exact algorithm. +For hardcoded users, you can use an `online tool`_, which will give you something +like this: .. configuration-block:: @@ -1518,166 +270,141 @@ a new provider that chains the two together: # app/config/security.yml security: + # ... + providers: - chain_provider: - chain: - providers: [in_memory, user_db] in_memory: memory: users: - foo: { password: test } - user_db: - entity: { class: Acme\UserBundle\Entity\User, property: username } + ryan: + password: $2a$12$LCY0MefVIEc3TYPHV9SNnuzOfyr2p/AXIGoQJEDs4am4JwhNz/jli + roles: 'ROLE_USER' + admin: + password: $2a$12$cyTWeE9kpq1PjqKFiWUZFuCRPwVyAZwm4XzMZ1qPUFl7/flCM3V0G + roles: 'ROLE_ADMIN' - .. code-block:: xml +Everything will now work exactly like before. But if you have dynamic users +(e.g. from a database), how can you programmatically encode the password +before inserting them into the database? Don't worry, see +:ref:`security-encoding-password` for details. - - - +.. tip:: - - - - in_memory - user_db - - - - - - - - - - - - + Supported algorithms for this method depend on your PHP version. A full list + is available by calling the PHP function :phpfunction:`hash_algos`. - .. code-block:: php +D) Configuration Done! +~~~~~~~~~~~~~~~~~~~~~~ - // app/config/security.php - $container->loadFromExtension('security', array( - 'providers' => array( - 'chain_provider' => array( - 'chain' => array( - 'providers' => array('in_memory', 'user_db'), - ), - ), - 'in_memory' => array( - 'memory' => array( - 'users' => array( - 'foo' => array('password' => 'test'), - ), - ), - ), - 'user_db' => array( - 'entity' => array( - 'class' => 'Acme\UserBundle\Entity\User', - 'property' => 'username', - ), - ), - ), - )); +Congratulations! You now have a working authentication system that uses Http +Basic and loads users right from the ``security.yml`` file. -Now, all authentication mechanisms will use the ``chain_provider``, since -it's the first specified. The ``chain_provider`` will, in turn, try to load -the user from both the ``in_memory`` and ``user_db`` providers. +Your next steps depend on your setup: -You can also configure the firewall or individual authentication mechanisms -to use a specific provider. Again, unless a provider is specified explicitly, -the first provider is always used: +* Configure a different way for your users to login, like a :ref:`login form ` + or :doc:`something completely custom `; -.. configuration-block:: +* Load users from a different source, like the :doc:`database ` + or :doc:`some other source `; - .. code-block:: yaml +* Learn how to deny access, load the User object and deal with roles in the + :ref:`Authorization ` section. - # app/config/security.yml - security: - firewalls: - secured_area: - # ... - pattern: ^/ - provider: user_db - http_basic: - realm: "Secured Demo Area" - provider: in_memory - form_login: ~ +.. _`security-authorization`: + +2) Denying Access, Roles and other Authorization +------------------------------------------------ + +Users can now login to your app using ``http_basic`` or some other method. +Great! Now, you need to learn how to deny access and work with the User object. +This is called **authorization**, and its job is to decide if a user can +access some resource (a URL, a model object, a method call, ...). + +.. note:: + + The authorization system is flexible, and can even support complex ACL's + where you determine, for example, if user A can "EDIT" some object B + (e.g. a Product). For details, see :doc:`/cookbook/security/voters_data_permission`. + +The process of authorization has two different sides: + +#. The user receives a specific set of roles when logging in (e.g. ``ROLE_ADMIN``). +#. You add code so that a resource (e.g. URL, controller) requires a specific + role in order to be accessed. + +.. _book-security-roles: + +Roles +~~~~~ + +When a user logs in, they receive a set of roles (e.g. ``ROLE_ADMIN``). In +the example above, these are hardcoded into ``security.yml``. If you're +loading users from the database, these are probably stored on a column +in your table. - .. code-block:: xml +.. caution:: - - - + All roles **must** begin with the ``ROLE_`` prefix. Otherwise, they won't + be handled by Symfony. If you define your own roles with a dedicated + ``Role`` class (more advanced), don't use the ``ROLE_`` prefix. - - - - - - - - +Roles are simple, and are basically strings that you invent and use as needed. +For example, if you need to start limiting access to the blog admin section +of your website, you could protect that section using a ``ROLE_BLOG_ADMIN`` +role. This role doesn't need to be defined anywhere - you can just start using +it. - .. code-block:: php +.. tip:: - // app/config/security.php - $container->loadFromExtension('security', array( - 'firewalls' => array( - 'secured_area' => array( - // ... - 'pattern' => '^/', - 'provider' => 'user_db', - 'http_basic' => array( - // ... - 'provider' => 'in_memory', - ), - 'form_login' => array(), - ), - ), - )); + Make sure every user has at least *one* role, or your user will look + like they're not authenticated. A common convention is to give *every* + user ``ROLE_USER``. -In this example, if a user tries to log in via HTTP authentication, the authentication -system will use the ``in_memory`` user provider. But if the user tries to -log in via the form login, the ``user_db`` provider will be used (since it's -the default for the firewall as a whole). +You can also specify a :ref:`role hierarchy ` where +some roles automatically mean that you also have other roles. -For more information about user provider and firewall configuration, see -the :doc:`/reference/configuration/security`. +Add Code to Deny Access +~~~~~~~~~~~~~~~~~~~~~~~ -.. _book-security-roles: +There are **two** ways to deny access to something: -Roles ------ +1) :ref:`access_control in security.yml ` + allows you to protect URL patterns (e.g. ``/admin/*``). This is easy, + but less flexible; -The idea of a "role" is key to the authorization process. Each user is assigned -a set of roles and then each resource requires one or more roles. If the user -has any one of the required roles, access is granted. Otherwise, access is denied. +2) :ref:`in your code via the security.context service `. -Roles are pretty simple, and are basically strings that you can invent and -use as needed (though roles are objects internally). For example, if you -need to start limiting access to the blog admin section of your website, -you could protect that section using a ``ROLE_BLOG_ADMIN`` role. This role -doesn't need to be defined anywhere - you can just start using it. +.. _security-authorization-access-control: -.. note:: +Securing URL patterns (access_control) +...................................... - All roles **must** begin with the ``ROLE_`` prefix to be managed by - Symfony. If you define your own roles with a dedicated ``Role`` class - (more advanced), don't use the ``ROLE_`` prefix. +The most basic way to secure part of your application is to secure an entire +URL pattern. You saw this earlier, where anything matching the regular expression +``^/admin`` requires the ``ROLE_ADMIN`` role: -Hierarchical Roles -~~~~~~~~~~~~~~~~~~ +.. configuration-block:: -Instead of associating many roles to users, you can define role inheritance -rules by creating a role hierarchy: + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + firewalls: + # ... + + access_control: + # require ROLE_ADMIN for /admin/* + - { path: ^/admin, roles: ROLE_ADMIN } + +This is great for securing entire sections, but you'll also probably want +to :ref:`secure your individual controllers ` +as well. + +You can define as many URL patterns as you need - each is a regular expression. +**BUT**, only **one** will be matched. Symfony will look at each starting +at the top, and stop as soon as it finds one ``access_control`` entry that +matches the URL. .. configuration-block:: @@ -1685,9 +412,10 @@ rules by creating a role hierarchy: # app/config/security.yml security: - role_hierarchy: - ROLE_ADMIN: ROLE_USER - ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] + # ... + access_control: + - { path: ^/admin/users, roles: ROLE_SUPER_ADMIN } + - { path: ^/admin, roles: ROLE_ADMIN } .. code-block:: xml @@ -1700,8 +428,11 @@ rules by creating a role hierarchy: http://symfony.com/schema/dic/services/services-1.0.xsd"> - ROLE_USER - ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH + + + + + @@ -1709,84 +440,74 @@ rules by creating a role hierarchy: // app/config/security.php $container->loadFromExtension('security', array( - 'role_hierarchy' => array( - 'ROLE_ADMIN' => 'ROLE_USER', - 'ROLE_SUPER_ADMIN' => array( - 'ROLE_ADMIN', - 'ROLE_ALLOWED_TO_SWITCH', - ), + // ... + 'access_control' => array( + array('path' => '^/admin/users', 'role' => 'ROLE_SUPER_ADMIN'), + array('path' => '^/admin', 'role' => 'ROLE_ADMIN'), ), )); -In the above configuration, users with ``ROLE_ADMIN`` role will also have the -``ROLE_USER`` role. The ``ROLE_SUPER_ADMIN`` role has ``ROLE_ADMIN``, ``ROLE_ALLOWED_TO_SWITCH`` -and ``ROLE_USER`` (inherited from ``ROLE_ADMIN``). +Prepending the path with ``^`` means that only URLs *beginning* with the +pattern are matched. For example, a path of simply ``/admin`` (without +the ``^``) would match ``/admin/foo`` but would also match URLs like ``/foo/admin``. + +.. _security-book-access-control-explanation: -Access Control --------------- +.. sidebar:: Understanding how ``access_control`` Works -Now that you have a User and Roles, you can go further than URL-pattern based -authorization. + The ``access_control`` section is very powerful, but it can also be dangerous + (because it involves security) if you don't understand *how* it works. + In addition to the URL, the ``access_control`` can match on IP address, + host name and HTTP methods. It can also be used to redirect a user to + the ``https`` version of a URL pattern. -.. _book-security-securing-controller: + To learn about all of this, see :doc:`/cookbook/security/access_control`. -Access Control in Controllers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _`book-security-securing-controller`: -Protecting your application based on URL patterns is easy, but may not be -fine-grained enough in certain cases. When necessary, you can easily force -authorization from inside a controller:: +Securing Controllers and other Code +................................... + +You can easily deny access from inside a controller:: // ... use Symfony\Component\Security\Core\Exception\AccessDeniedException; public function helloAction($name) { - if (false === $this->get('security.context')->isGranted('ROLE_ADMIN')) { + if (!$this->get('security.context')->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); } // ... } -.. caution:: - - A firewall must be active or an exception will be thrown when the ``isGranted()`` - method is called. It's almost always a good idea to have a main firewall that - covers all URLs (as is shown in this chapter). +That's it! If the user isn't logged in yet, they will be asked to login (e.g. +redirected to the login page). If they *are* logged in, they'll be shown +the 403 access denied page (which you can :ref:`customize `). .. _book-security-securing-controller-annotations: -You can also choose to install and use the optional `JMSSecurityExtraBundle`_, -which can secure your controller using annotations:: +Thanks to the SensioFrameworkExtraBundle, you can also secure your controller +using annotations:: // ... - use JMS\SecurityExtraBundle\Annotation\Secure; + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; /** - * @Secure(roles="ROLE_ADMIN") + * @Security("has_role('ROLE_ADMIN')") */ public function helloAction($name) { // ... } -Access Control in Other Services -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In fact, anything in Symfony can be protected using a strategy similar to -the one seen in the previous section. For example, suppose you have a service -(i.e. a PHP class) whose job is to send emails from one user to another. -You can restrict use of this class - no matter where it's being used from - -to users that have a specific role. - -For more information on how you can use the Security component to secure -different services and methods in your application, see :doc:`/cookbook/security/securing_services`. +For more information, see the `FrameworkExtraBundle documentation`_. .. _book-security-template: Access Control in Templates -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +........................... If you want to check if the current user has a role inside a template, use the built-in helper function: @@ -1805,28 +526,168 @@ the built-in helper function: Delete -.. note:: +If you use this function and are *not* behind a firewall, an exception +will be thrown. Again, it's almost always a good +idea to have a main firewall that covers all URLs (as has been shown +in this chapter). + +.. caution:: + + Be careful with this in your layout or on your error pages! Because of + some internal Symfony details, to avoid broken error pages in the ``prod`` + environment, wrap calls in these templates with a check for ``app.user``: + + .. code-block:: html+jinja + + {% if app.user and is_granted('ROLE_ADMIN') %} + +Securing other Services +....................... + +In fact, anything in Symfony can be protected by doing something similar +to this. For example, suppose you have a service (i.e. a PHP class) whose +job is to send emails. You can restrict use of this class - no matter where +it's being used from - to only certain users. + +For more information see :doc:`/cookbook/security/securing_services`. + +Checking to see if a User is Logged In (IS_AUTHENTICATED_FULLY) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +So far, you've checked access based on roles - those strings that start with +``ROLE_`` and are assigned to users. But if you *only* want to check if a +user is logged in (you don't care about roles), then you can see ``IS_AUTHENTICATED_FULLY``:: + + // ... + use Symfony\Component\Security\Core\Exception\AccessDeniedException; + + public function helloAction($name) + { + if (!$this->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY')) { + throw new AccessDeniedException(); + } + + // ... + } + +.. tip:: - If you use this function and are *not* at a URL behind a firewall - active, an exception will be thrown. Again, it's almost always a good - idea to have a main firewall that covers all URLs (as has been shown - in this chapter). + You can of course also use this in ``access_control``. + +``IS_AUTHENTICATED_FULLY`` isn't a role, but it kind of acts like one, and every +user that has successfull logged in will have this. In fact, there are thre +special attributes like this: + +* ``IS_AUTHENTICATED_FULLY``: All "logged-in" users have this; +* ``IS_AUTHENTICATED_REMEMBERED``: Similar to ``IS_AUTHENTICATED_FULLY`` + but important if you're using :doc:`remember me functionality `; +* ``IS_AUTHENTICATED_ANONYMOUSLY``: *All* users (even anonymous ones) have + this - this is useful when *whitelisting* URLs to guarantee access - some + details are in :doc:`/cookbook/security/access_control`. Access Control Lists (ACLs): Securing individual Database Objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Imagine you are designing a blog system where your users can comment on your -posts. Now, you want a user to be able to edit their own comments, but not -those of other users. Also, as the admin user, you yourself want to be able -to edit *all* comments. +Imagine you are designing a blog where users can comment on your posts. You +also want a user to be able to edit their own comments, but not those of +other users. Also, as the admin user, you yourself want to be able to edit +*all* comments. + +To accomplish this you have 2 options: + +* :doc:`Voters ` allow you to + use business logic (e.g. the user can edit this post because they were + the creator) to determine access. You'll probably want this option - it's + flexible enough to solve the above situation. + +* :doc:`ACLs ` allow you to create a database structure + where you can assign *any* arbitrary user *any* access (e.g. EDIT, VIEW) + to *any* object in your system. Use this if you need an admin user to be + able to grant customized access across your system via some admin interface. + +In both cases, you'll still deny access using methods similar to what was +shown above. + +Retrieving the User Object +-------------------------- + +After authentication, the ``User`` object of the current user can be accessed +via the ``security.context`` service. From inside a controller, this will +look like:: + + public function indexAction() + { + if (!$this->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY')) { + throw new AccessDeniedException(); + } + + $user = $this->getUser(); + + // the above is a shortcut for this + $user = $this->get('security.context')->getToken()->getUser(); + } + +.. tip:: + + The user will be an object and the class of that object will depend on + your :ref:`user provider `. + +Now you can call whatever methods are on *your* User object. For example, +if your User object has a ``getFirstName()`` method, you could use that:: + + use Symfony\Component\HttpFoundation\Response; + + public function indexAction() + { + // ... + + return new Response('Well hi there '.$user->getFirstName()); + } + +Always Check if the User is Logged In +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's important to check if the user is authenticated first. If they're not, +``$user`` will either be ``null`` or the string ``anon.``. Wait, what? Yes, +this is a quirk. If you're not logged in, the user is technically the string +``anon.``, though the ``getUser()`` controller shortcut converts this to +``null`` for convenience. + +The point is this: always check to see if the user is logged in before using +the User object, and use the ``isGranted`` method (or +:ref:`access_control `) to do this:: + + // yay! Use this to see if the user is logged in + if (!$this->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY')) { + throw new AccessDeniedException(); + } + + // boo :(. Never check for the User object to see if they're logged in + if ($this->getUser()) { + + } + +Retreiving the User in a Template +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In a Twig Template this object can be accessed via the `app.user `_ +key: + +.. configuration-block:: + + .. code-block:: html+jinja + + {% if is_granted('IS_AUTHENTICATED_FULLY') %} +

    Username: {{ app.user.username }}

    + {% endif %} + + .. code-block:: html+php -The Security component comes with an optional access control list (ACL) system -that you can use when you need to control access to individual instances -of an object in your system. *Without* ACL, you can secure your system so that -only certain users can edit blog comments in general. But *with* ACL, you -can restrict or allow access on a comment-by-comment basis. + isGranted('IS_AUTHENTICATED_FULLY')): ?> +

    Username: getUser()->getUsername() ?>

    + -For more information, see the cookbook article: :doc:`/cookbook/security/acl`. +.. _book-security-logging-out: Logging Out ----------- @@ -1881,30 +742,7 @@ the firewall can handle this automatically for you when you activate the // ... )); -Once this is configured under your firewall, sending a user to ``/logout`` -(or whatever you configure the ``path`` to be), will un-authenticate the -current user. The user will then be sent to the homepage (the value defined -by the ``target`` parameter). Both the ``path`` and ``target`` config parameters -default to what's specified here. In other words, unless you need to customize -them, you can omit them entirely and shorten your configuration: - -.. configuration-block:: - - .. code-block:: yaml - - logout: ~ - - .. code-block:: xml - - - - .. code-block:: php - - 'logout' => array(), - -Note that you will *not* need to implement a controller for the ``/logout`` -URL as the firewall takes care of everything. You *do*, however, need to create -a route so that you can use it to generate the URL: +Next, you'll need to create a route for this URL (but not a controller): .. configuration-block:: @@ -1937,15 +775,100 @@ a route so that you can use it to generate the URL: return $collection; +And that's it! By sending a user to ``/logout`` (or whatever you configure +the ``path`` to be), Symfony will un-authenticate the current user. and +redirect them the homepage (the value defined by ``target``). + +Once the user has been logged out, they will be redirected to whatever path +is defined by the ``target`` parameter above (e.g. the ``homepage``). + +.. tip:: + + If you need to do something more interesting after logging out, you can + specify a logout success handler by adding a ``success_handler`` key + and pointing it to a service id of a class that implements + :class:`Symfony\\Component\\Security\\Http\\Logout\\LogoutSuccessHandlerInterface`. + See :doc:`Security Configuration Reference `. + +.. _`security-encoding-password`: + +Dynamically Encoding a Password +------------------------------- + +If, for example, you're storing users in the database, you'll need to encode +the users' passwords before inserting them. No matter what algorithm you +configure for your user object, the hashed password can always be determined +in the following way from a controller:: + + $factory = $this->get('security.encoder_factory'); + // whatever *your* User object is + $user = new AppBundle\Entity\User(); + + $encoder = $factory->getEncoder($user); + $password = $encoder->encodePassword('ryanpass', $user->getSalt()); + $user->setPassword($password); + +In order for this to work, just make sure that you have the encoder for your +user class (e.g. ``AppBundle\Entity\User``) configured under the ``encoders`` +key in ``app/config/security.yml``. + .. caution:: - As of Symfony 2.1, you *must* have a route that corresponds to your logout - path. Without this route, logging out will not work. + When you allow a user to submit a plaintext password (e.g. registration + form, change password form), you *must* have validation that guarantees + that the password is 4096 characters or fewer. Read more details in + :ref:`How to implement a simple Registration Form `. + +.. _security-role-hierarchy: -Once the user has been logged out, they will be redirected to whatever path -is defined by the ``target`` parameter above (e.g. the ``homepage``). For -more information on configuring the logout, see the -:doc:`Security Configuration Reference `. +Hierarchical Roles +------------------ + +Instead of associating many roles to users, you can define role inheritance +rules by creating a role hierarchy: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + role_hierarchy: + ROLE_ADMIN: ROLE_USER + ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] + + .. code-block:: xml + + + + + + + ROLE_USER + ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'role_hierarchy' => array( + 'ROLE_ADMIN' => 'ROLE_USER', + 'ROLE_SUPER_ADMIN' => array( + 'ROLE_ADMIN', + 'ROLE_ALLOWED_TO_SWITCH', + ), + ), + )); + +In the above configuration, users with ``ROLE_ADMIN`` role will also have the +``ROLE_USER`` role. The ``ROLE_SUPER_ADMIN`` role has ``ROLE_ADMIN``, ``ROLE_ALLOWED_TO_SWITCH`` +and ``ROLE_USER`` (inherited from ``ROLE_ADMIN``). Stateless Authentication ------------------------ @@ -1999,92 +922,31 @@ cookie will be ever created by Symfony): If you use a form login, Symfony will create a cookie even if you set ``stateless`` to ``true``. -Utilities ---------- - -.. versionadded:: 2.2 - The ``StringUtils`` and ``SecureRandom`` classes were introduced in Symfony - 2.2 - -The Symfony Security component comes with a collection of nice utilities related -to security. These utilities are used by Symfony, but you should also use -them if you want to solve the problem they address. - -Comparing Strings -~~~~~~~~~~~~~~~~~ - -The time it takes to compare two strings depends on their differences. This -can be used by an attacker when the two strings represent a password for -instance; it is known as a `Timing attack`_. - -Internally, when comparing two passwords, Symfony uses a constant-time -algorithm; you can use the same strategy in your own code thanks to the -:class:`Symfony\\Component\\Security\\Core\\Util\\StringUtils` class:: - - use Symfony\Component\Security\Core\Util\StringUtils; - - // is password1 equals to password2? - $bool = StringUtils::equals($password1, $password2); - -Generating a secure random Number -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Whenever you need to generate a secure random number, you are highly -encouraged to use the Symfony -:class:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom` class:: - - use Symfony\Component\Security\Core\Util\SecureRandom; - - $generator = new SecureRandom(); - $random = $generator->nextBytes(10); - -The -:method:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom::nextBytes` -methods returns a random string composed of the number of characters passed as -an argument (10 in the above example). - -The SecureRandom class works better when OpenSSL is installed but when it's -not available, it falls back to an internal algorithm, which needs a seed file -to work correctly. Just pass a file name to enable it:: - - $generator = new SecureRandom('/some/path/to/store/the/seed.txt'); - $random = $generator->nextBytes(10); - -.. note:: - - You can also access a secure random instance directly from the Symfony - dependency injection container; its name is ``security.secure_random``. - Final Words ----------- -Security can be a deep and complex issue to solve correctly in your application. -Fortunately, Symfony's Security component follows a well-proven security -model based around *authentication* and *authorization*. Authentication, -which always happens first, is handled by a firewall whose job is to determine -the identity of the user through several methods (e.g. HTTP authentication, -login form, etc). In the cookbook, you'll find examples of other methods -for handling authentication, including how to implement a "remember me" cookie -functionality. - -Once a user is authenticated, the authorization layer can determine whether -or not the user should have access to a specific resource. Most commonly, -*roles* are applied to URLs, classes or methods and if the current user -doesn't have that role, access is denied. The authorization layer, however, -is much deeper, and follows a system of "voting" so that multiple parties -can determine if the current user should have access to a given resource. -Find out more about this and other topics in the cookbook. +Woh! Nice work! You now know more than the basics of security. The hardest +parts are when you have custom requirements: like a custom authentication +strategy (e.g. API tokens), complex authorization logic and many other things +(because security is complex!). + +Fortunately, there are a lot of :doc:`Security Cookbook Articles ` +aimed at describing many of these situations. Also, see the +:doc:`Security Reference Section `. Many +of the options don't have specific details, but seeing the full possible +configuration tree may be useful. + +Good luck! Learn more from the Cookbook ---------------------------- * :doc:`Forcing HTTP/HTTPS ` * :doc:`Impersonating a User ` -* :doc:`Blacklist users by IP address with a custom voter ` +* :doc:`/cookbook/security/voters_data_permission` * :doc:`Access Control Lists (ACLs) ` * :doc:`/cookbook/security/remember_me` +* :doc:`/cookbook/security/multiple_user_providers` -.. _`JMSSecurityExtraBundle`: http://jmsyst.com/bundles/JMSSecurityExtraBundle/1.2 -.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle -.. _`implement the \Serializable interface`: http://php.net/manual/en/class.serializable.php -.. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack +.. _`online tool`: https://www.dailycred.com/blog/12/bcrypt-calculator +.. _`frameworkextrabundle documentation`: http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html diff --git a/components/map.rst.inc b/components/map.rst.inc index 529a132b418..4499ab1f349 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -114,6 +114,7 @@ * :doc:`/components/security/firewall` * :doc:`/components/security/authentication` * :doc:`/components/security/authorization` + * :doc:`/components/security/secure-tools` * **Serializer** diff --git a/components/security/index.rst b/components/security/index.rst index 94e3e6c77d6..cd3794f8f50 100644 --- a/components/security/index.rst +++ b/components/security/index.rst @@ -8,3 +8,4 @@ Security firewall authentication authorization + secure-tools \ No newline at end of file diff --git a/components/security/secure-tools.rst b/components/security/secure-tools.rst new file mode 100644 index 00000000000..8e7df74a6da --- /dev/null +++ b/components/security/secure-tools.rst @@ -0,0 +1,57 @@ +Securely Comparing Strings and Generating Random Numbers +======================================================== + +.. versionadded:: 2.2 + The ``StringUtils`` and ``SecureRandom`` classes were introduced in Symfony + 2.2 + +The Symfony Security component comes with a collection of nice utilities related +to security. These utilities are used by Symfony, but you should also use +them if you want to solve the problem they address. + +Comparing Strings +~~~~~~~~~~~~~~~~~ + +The time it takes to compare two strings depends on their differences. This +can be used by an attacker when the two strings represent a password for +instance; it is known as a `Timing attack`_. + +Internally, when comparing two passwords, Symfony uses a constant-time +algorithm; you can use the same strategy in your own code thanks to the +:class:`Symfony\\Component\\Security\\Core\\Util\\StringUtils` class:: + + use Symfony\Component\Security\Core\Util\StringUtils; + + // is password1 equals to password2? + $bool = StringUtils::equals($password1, $password2); + +Generating a secure random Number +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whenever you need to generate a secure random number, you are highly +encouraged to use the Symfony +:class:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom` class:: + + use Symfony\Component\Security\Core\Util\SecureRandom; + + $generator = new SecureRandom(); + $random = $generator->nextBytes(10); + +The +:method:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom::nextBytes` +methods returns a random string composed of the number of characters passed as +an argument (10 in the above example). + +The SecureRandom class works better when OpenSSL is installed but when it's +not available, it falls back to an internal algorithm, which needs a seed file +to work correctly. Just pass a file name to enable it:: + + $generator = new SecureRandom('/some/path/to/store/the/seed.txt'); + $random = $generator->nextBytes(10); + +.. note:: + + If you're using the Symfony Framework, you can access a secure random + instance directly from the container: its name is ``security.secure_random``. + +.. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack \ No newline at end of file diff --git a/cookbook/map.rst.inc b/cookbook/map.rst.inc index c37d0013e87..ee726b0f96f 100644 --- a/cookbook/map.rst.inc +++ b/cookbook/map.rst.inc @@ -138,6 +138,7 @@ * :doc:`/cookbook/security/index` + * :doc:`/cookbook/security/form_login_setup` * :doc:`/cookbook/security/entity_provider` * :doc:`/cookbook/security/remember_me` * :doc:`/cookbook/security/impersonating_user` @@ -153,6 +154,8 @@ * :doc:`/cookbook/security/pre_authenticated` * :doc:`/cookbook/security/target_path` * :doc:`/cookbook/security/csrf_in_login_form` + * :doc:`/cookbook/security/access_control` + * :doc:`/cookbook/security/multiple_user_providers` * **Serializer** diff --git a/cookbook/security/access_control.rst b/cookbook/security/access_control.rst new file mode 100644 index 00000000000..0ec0772247e --- /dev/null +++ b/cookbook/security/access_control.rst @@ -0,0 +1,299 @@ +How does the Security access_control Work? +========================================== + +For each incoming request, Symfony checks each ``access_control`` entry +to find *one* that matches the current request. As soon as it finds a matching +``access_control`` entry, it stops - only the **first** matching ``access_control`` +is used to enforce access. + +Each ``access_control`` has several options that configure two different +things: + +#. :ref:`should the incoming request match this access control entry ` +#. :ref:`once it matches, should some sort of access restriction be enforced `: + +.. _security-book-access-control-matching-options: + +1. Matching Options +------------------- + +Symfony creates an instance of :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher` +for each ``access_control`` entry, which determines whether or not a given +access control should be used on this request. The following ``access_control`` +options are used for matching: + +* ``path`` +* ``ip`` or ``ips`` +* ``host`` +* ``methods`` + +Take the following ``access_control`` entries as an example: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + access_control: + - { path: ^/admin, roles: ROLE_USER_IP, ip: 127.0.0.1 } + - { path: ^/admin, roles: ROLE_USER_HOST, host: symfony\.com$ } + - { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] } + - { path: ^/admin, roles: ROLE_USER } + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'access_control' => array( + array( + 'path' => '^/admin', + 'role' => 'ROLE_USER_IP', + 'ip' => '127.0.0.1', + ), + array( + 'path' => '^/admin', + 'role' => 'ROLE_USER_HOST', + 'host' => 'symfony\.com$', + ), + array( + 'path' => '^/admin', + 'role' => 'ROLE_USER_METHOD', + 'method' => 'POST, PUT', + ), + array( + 'path' => '^/admin', + 'role' => 'ROLE_USER', + ), + ), + )); + +For each incoming request, Symfony will decide which ``access_control`` +to use based on the URI, the client's IP address, the incoming host name, +and the request method. Remember, the first rule that matches is used, and +if ``ip``, ``host`` or ``method`` are not specified for an entry, that ``access_control`` +will match any ``ip``, ``host`` or ``method``: + ++-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ +| URI | IP | HOST | METHOD | ``access_control`` | Why? | ++=================+=============+=============+============+================================+=============================================================+ +| ``/admin/user`` | 127.0.0.1 | example.com | GET | rule #1 (``ROLE_USER_IP``) | The URI matches ``path`` and the IP matches ``ip``. | ++-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ +| ``/admin/user`` | 127.0.0.1 | symfony.com | GET | rule #1 (``ROLE_USER_IP``) | The ``path`` and ``ip`` still match. This would also match | +| | | | | | the ``ROLE_USER_HOST`` entry, but *only* the **first** | +| | | | | | ``access_control`` match is used. | ++-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ +| ``/admin/user`` | 168.0.0.1 | symfony.com | GET | rule #2 (``ROLE_USER_HOST``) | The ``ip`` doesn't match the first rule, so the second | +| | | | | | rule (which matches) is used. | ++-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ +| ``/admin/user`` | 168.0.0.1 | symfony.com | POST | rule #2 (``ROLE_USER_HOST``) | The second rule still matches. This would also match the | +| | | | | | third rule (``ROLE_USER_METHOD``), but only the **first** | +| | | | | | matched ``access_control`` is used. | ++-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ +| ``/admin/user`` | 168.0.0.1 | example.com | POST | rule #3 (``ROLE_USER_METHOD``) | The ``ip`` and ``host`` don't match the first two entries, | +| | | | | | but the third - ``ROLE_USER_METHOD`` - matches and is used. | ++-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ +| ``/admin/user`` | 168.0.0.1 | example.com | GET | rule #4 (``ROLE_USER``) | The ``ip``, ``host`` and ``method`` prevent the first | +| | | | | | three entries from matching. But since the URI matches the | +| | | | | | ``path`` pattern of the ``ROLE_USER`` entry, it is used. | ++-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ +| ``/foo`` | 127.0.0.1 | symfony.com | POST | matches no entries | This doesn't match any ``access_control`` rules, since its | +| | | | | | URI doesn't match any of the ``path`` values. | ++-----------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ + +.. _security-book-access-control-enforcement-options: + +2. Access Enforcement +--------------------- + +Once Symfony has decided which ``access_control`` entry matches (if any), +it then *enforces* access restrictions based on the ``roles`` and ``requires_channel`` +options: + +* ``role`` If the user does not have the given role(s), then access is denied + (internally, an :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException` + is thrown); + +* ``requires_channel`` If the incoming request's channel (e.g. ``http``) + does not match this value (e.g. ``https``), the user will be redirected + (e.g. redirected from ``http`` to ``https``, or vice versa). + +.. tip:: + + If access is denied, the system will try to authenticate the user if not + already (e.g. redirect the user to the login page). If the user is already + logged in, the 403 "access denied" error page will be shown. See + :doc:`/cookbook/controller/error_pages` for more information. + +.. _book-security-securing-ip: + +Securing by IP +-------------- + +Certain situations may arise when you may need to restrict access to a given +path based on IP. This is particularly relevant in the case of +:ref:`Edge Side Includes ` (ESI), for example. When ESI is +enabled, it's recommended to secure access to ESI URLs. Indeed, some ESI may +contain some private content like the current logged in user's information. To +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``. + +.. caution:: + + As you'll read in the explanation below the example, the ``ip`` option + does not restrict to a specific IP address. Instead, using the ``ip`` + key means that the ``access_control`` entry will only match this IP address, + and users accessing it from a different IP address will continue down + the ``access_control`` list. + +Here is an example of how you might secure all ESI routes that start with a +given prefix, ``/esi``, from outside access: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + access_control: + - { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } + - { path: ^/esi, roles: ROLE_NO_ACCESS } + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'access_control' => array( + array( + 'path' => '^/esi', + 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', + 'ips' => '127.0.0.1, ::1' + ), + array( + 'path' => '^/esi', + 'role' => 'ROLE_NO_ACCESS' + ), + ), + )); + +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 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`` + role as it's not defined, access is denied (the ``ROLE_NO_ACCESS`` role can + 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`` 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 + ``IS_AUTHENTICATED_ANONYMOUSLY`` role. + +* The second access rule is not examined as the first rule matched. + +.. _book-security-securing-channel: + +Forcing a Channel (http, https) +------------------------------- + +You can also require a user to access a URL via SSL; just use the +``requires_channel`` argument in any ``access_control`` entries. If this +``access_control`` is matched and the request is using the ``http`` channel, +the user will be redirected to ``https``: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + access_control: + - { path: ^/cart/checkout, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https } + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'access_control' => array( + array( + 'path' => '^/cart/checkout', + 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY', + 'requires_channel' => 'https', + ), + ), + )); diff --git a/cookbook/security/form_login.rst b/cookbook/security/form_login.rst index 4df556c7c01..337d02a2fdf 100644 --- a/cookbook/security/form_login.rst +++ b/cookbook/security/form_login.rst @@ -4,9 +4,9 @@ How to Customize your Form Login ================================ -Using a :ref:`form login ` for authentication is -a common, and flexible, method for handling authentication in Symfony. Pretty -much every aspect of the form login can be customized. The full, default +Using a :doc:`form login ` for authentication +is a common, and flexible, method for handling authentication in Symfony. +Pretty much every aspect of the form login can be customized. The full, default configuration is shown in the next section. Form Login Configuration Reference diff --git a/cookbook/security/form_login_setup.rst b/cookbook/security/form_login_setup.rst new file mode 100644 index 00000000000..5f414bb9cc8 --- /dev/null +++ b/cookbook/security/form_login_setup.rst @@ -0,0 +1,472 @@ +How to Build a Traditional Login Form +===================================== + +.. tip:: + + If you need a login form and are storing users in some sort of a database, + then see you should consider using `FOSUserBundle`_, which helps you + build your ``User`` object and gives you many routes and controllers + for common tasks like login, registration and forgot password. + +In this entry, you'll build a traditional login form. Of course, when the +user logs in, you can load your users from anywhere - like the database. +See :ref:`security-user-providers` for details. + +This chapter assumes that you've followed the beginning of the +:doc:`security chapter ` and have ``http_basic`` authentication +working properly. + +First, enable form login under your firewall: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + # ... + + firewalls: + default: + anonymous: ~ + http_basic: ~ + form_login: + login_path: /login + check_path: /login_check + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'main' => array( + 'anonymous' => array(), + 'form_login' => array( + 'login_path' => '/login', + 'check_path' => '/login_check', + ), + ), + ), + )); + +.. tip:: + + The ``login_path`` and ``check_path`` can also be route names. + +Now, when the security system initiates the authentication process, it will +redirect the user to the login form ``/login``. Implementing this login form +visually is your job. First, create a new ``SecurityController`` inside a +bundle with an empty ``loginAction``:: + + // src/AppBundle/Controller/SecurityController.php + namespace AppBundle\Controller; + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + use Symfony\Bundle\FrameworkBundle\Controller\Controller; + + class SecurityController extends Controller + { + public function loginAction(Request $request) + { + // todo... + } + } + +Next, create two routes: one for each of the paths your configured earlier +under your ``form_login`` configuration (``/login`` and ``/login_check``): + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/AppBundle/Controller/SecurityController.php + // ... + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; + + class SecurityController extends Controller + { + /** + * @Route("/login", name="login_route") + */ + public function loginAction(Request $request) + { + // todo ... + } + + /** + * @Route("/login_check", name="login_check") + */ + public function loginCheckAction() + { + } + } + + .. code-block:: yaml + + # app/config/routing.yml + login_route: + path: /login + defaults: { _controller: AppBundle:Security:login } + login_check: + path: /login_check + + .. code-block:: xml + + + + + + + AppBundle:Security:login + + + + + + .. code-block:: php + + // app/config/routing.php + use Symfony\Component\Routing\RouteCollection; + use Symfony\Component\Routing\Route; + + $collection = new RouteCollection(); + $collection->add('login_route', new Route('/login', array( + '_controller' => 'AppBundle:Security:login', + ))); + $collection->add('login_check', new Route('/login_check', array())); + + return $collection; + +Great! Next, add the logic to ``loginAction`` that will display the login +form:: + + // src/AppBundle/Controller/SecurityController.php + // ... + + // ADD THIS use STATEMENT above your class + use Symfony\Component\Security\Core\SecurityContextInterface; + + public function loginAction(Request $request) + { + $session = $request->getSession(); + + // get the login error if there is one + if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { + $error = $request->attributes->get( + SecurityContextInterface::AUTHENTICATION_ERROR + ); + } elseif (null !== $session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { + $error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR); + $session->remove(SecurityContextInterface::AUTHENTICATION_ERROR); + } else { + $error = ''; + } + + // last username entered by the user + $lastUsername = (null === $session) ? '' : $session->get(SecurityContextInterface::LAST_USERNAME); + + return $this->render( + 'security/login.html.twig', + array( + // last username entered by the user + 'last_username' => $lastUsername, + 'error' => $error, + ) + ); + } + +Don't let this controller confuse you. As you'll see in a moment, when the +user submits the form, the security system automatically handles the form +submission for you. If the user had submitted an invalid username or password, +this controller reads the form submission error from the security system so +that it can be displayed back to the user. + +In other words, your job is to *display* the login form and any login errors +that may have occurred, but the security system itself takes care of checking +the submitted username and password and authenticating the user. + +Finally, create the template: + +.. configuration-block:: + + .. code-block:: html+jinja + + {# app/Resources/views/security/login.html.twig #} + {# ... you will probably extends your base template, like base.html.twig #} + + {% if error %} +
    {{ error.message }}
    + {% endif %} + +
    + + + + + + + {# + If you want to control the URL the user + is redirected to on success (more details below) + + #} + + + + + .. code-block:: html+php + + + +
    getMessage() ?>
    + + +
    + + + + + + + + + + + + +.. tip:: + + The ``error`` variable passed into the template is an instance of + :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException`. + It may contain more information - or even sensitive information - about + the authentication failure, so use it wisely! + +The form can look like anything, but has a few requirements: + +* The form must POST to ``/login_check``, since that's what you configured + under the ``form_login`` key in ``security.yml``. + +* The username must have the name ``_username`` and the password must have + the name ``_password``. + +.. tip:: + + Actually, all of this can be configured under the ``form_login`` key. See + :ref:`reference-security-firewall-form-login` for more details. + +.. caution:: + + This login form is currently not protected against CSRF attacks. Read + :doc:`/cookbook/security/csrf_in_login_form` on how to protect your login + form. + +And that's it! When you submit the form, the security system will automatically +check the user's credentials and either authenticate the user or send the +user back to the login form where the error can be displayed. + +To review the whole process: + +#. The user tries to access a resource that is protected; +#. The firewall initiates the authentication process by redirecting the + user to the login form (``/login``); +#. The ``/login`` page renders login form via the route and controller created + in this example; +#. The user submits the login form to ``/login_check``; +#. The security system intercepts the request, checks the user's submitted + credentials, authenticates the user if they are correct, and sends the + user back to the login form if they are not. + +Redirecting after Success +------------------------- + +If the submitted credentials are correct, the user will be redirected to +the original page that was requested (e.g. ``/admin/foo``). If the user originally +went straight to the login page, they'll be redirected to the homepage. This +can all be customized, allowing you to, for example, redirect the user to +a specific URL. + +For more details on this and how to customize the form login process in general, +see :doc:`/cookbook/security/form_login`. + +.. _book-security-common-pitfalls: + +Avoid common Pitfalls +===================== + +When setting up your login form, watch out for a few common pitfalls. + +**1. Create the correct routes** + +First, be sure that you've defined the ``/login`` and ``/login_check`` +routes correctly and that they correspond to the ``login_path`` and +``check_path`` config values. A misconfiguration here can mean that you're +redirected to a 404 page instead of the login page, or that submitting +the login form does nothing (you just see the login form over and over +again). + +**2. Be sure the login page isn't secure (redirect loop!)** + +Also, be sure that the login page is accessible by anonymous users. For example, +the following configuration - which requires the ``ROLE_ADMIN`` role for +all URLs (including the ``/login`` URL), will cause a redirect loop: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + + # ... + access_control: + - { path: ^/, roles: ROLE_ADMIN } + + .. code-block:: xml + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // ... + 'access_control' => array( + array('path' => '^/', 'role' => 'ROLE_ADMIN'), + ), + +Adding an access control that matches ``/login/*`` and requires *no* authentication +fixes the problem: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + + # ... + access_control: + - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/, roles: ROLE_ADMIN } + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // ... + 'access_control' => array( + array('path' => '^/login', 'role' => 'IS_AUTHENTICATED_ANONYMOUSLY'), + array('path' => '^/', 'role' => 'ROLE_ADMIN'), + ), + +Also, if your firewall does *not* allow for anonymous users (no ``anonymous`` +key), you'll need to create a special firewall that allows anonymous users +for the login page: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + + # ... + firewalls: + login_firewall: + pattern: ^/login$ + anonymous: ~ + secured_area: + pattern: ^/ + form_login: ~ + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + + // ... + 'firewalls' => array( + 'login_firewall' => array( + 'pattern' => '^/login$', + 'anonymous' => array(), + ), + 'secured_area' => array( + 'pattern' => '^/', + 'form_login' => array(), + ), + ), + +**3. Be sure /login_check is behind a firewall** + +Next, make sure that your ``check_path`` URL (e.g. ``/login_check``) is behind +the firewall you're using for your form login (in this example, the single +firewall matches *all* URLs, including ``/login_check``). If ``/login_check`` +doesn't match any firewall, you'll receive a ``Unable to find the controller +for path "/login_check"`` exception. + +**4. Multiple firewalls don't share security context** + +If you're using multiple firewalls and you authenticate against one firewall, +you will *not* be authenticated against any other firewalls automatically. +Different firewalls are like different security systems. To do this you have +to explicitly specify the same :ref:`reference-security-firewall-context` +for different firewalls. But usually for most applications, having one +main firewall is enough. + +**5. Routing error pages are not covered by firewalls** + +As routing is done *before* security, 404 error pages are not covered by +any firewall. This means you can't check for security or even access the +user object on these pages. See :doc:`/cookbook/controller/error_pages` +for more details. + +.. _`FOSUserBundle`: https://github.com/FriendsOfSymfony/FOSUserBundle \ No newline at end of file diff --git a/cookbook/security/index.rst b/cookbook/security/index.rst index a0175648843..5bf643c10e8 100644 --- a/cookbook/security/index.rst +++ b/cookbook/security/index.rst @@ -4,6 +4,7 @@ Security .. toctree:: :maxdepth: 2 + form_login_setup entity_provider remember_me impersonating_user @@ -19,3 +20,5 @@ Security pre_authenticated target_path csrf_in_login_form + access_control + multiple_user_providers diff --git a/cookbook/security/multiple_user_providers.rst b/cookbook/security/multiple_user_providers.rst new file mode 100644 index 00000000000..4766ed92e44 --- /dev/null +++ b/cookbook/security/multiple_user_providers.rst @@ -0,0 +1,148 @@ +How to Use multiple User Providers +================================== + +Each authentication mechanism (e.g. HTTP Authentication, form login, etc) +uses exactly one user provider, and will use the first declared user provider +by default. But what if you want to specify a few users via configuration +and the rest of your users in the database? This is possible by creating +a new provider that chains the two together: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + providers: + chain_provider: + chain: + providers: [in_memory, user_db] + in_memory: + memory: + users: + foo: { password: test } + user_db: + entity: { class: Acme\UserBundle\Entity\User, property: username } + + .. code-block:: xml + + + + + + + + + in_memory + user_db + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'providers' => array( + 'chain_provider' => array( + 'chain' => array( + 'providers' => array('in_memory', 'user_db'), + ), + ), + 'in_memory' => array( + 'memory' => array( + 'users' => array( + 'foo' => array('password' => 'test'), + ), + ), + ), + 'user_db' => array( + 'entity' => array( + 'class' => 'Acme\UserBundle\Entity\User', + 'property' => 'username', + ), + ), + ), + )); + +Now, all authentication mechanisms will use the ``chain_provider``, since +it's the first specified. The ``chain_provider`` will, in turn, try to load +the user from both the ``in_memory`` and ``user_db`` providers. + +You can also configure the firewall or individual authentication mechanisms +to use a specific provider. Again, unless a provider is specified explicitly, +the first provider is always used: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/security.yml + security: + firewalls: + secured_area: + # ... + pattern: ^/ + provider: user_db + http_basic: + realm: "Secured Demo Area" + provider: in_memory + form_login: ~ + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'firewalls' => array( + 'secured_area' => array( + // ... + 'pattern' => '^/', + 'provider' => 'user_db', + 'http_basic' => array( + // ... + 'provider' => 'in_memory', + ), + 'form_login' => array(), + ), + ), + )); + +In this example, if a user tries to log in via HTTP authentication, the authentication +system will use the ``in_memory`` user provider. But if the user tries to +log in via the form login, the ``user_db`` provider will be used (since it's +the default for the firewall as a whole). + +For more information about user provider and firewall configuration, see +the :doc:`/reference/configuration/security`. diff --git a/images/book/security_admin_role_access.png b/images/book/security_admin_role_access.png deleted file mode 100644 index 8cbc86d26d41607ce0863101c6db6ab686174a5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77809 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 diff --git a/images/book/security_anonymous_user_access.png b/images/book/security_anonymous_user_access.png deleted file mode 100644 index 89694aa1ade17fb30b1cafd28bcd8397b75f73d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80752 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%0Agiv`NIExif5jA2D^6c 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 F{|_$WaP|NI diff --git a/images/book/security_anonymous_user_denied_authorization.png b/images/book/security_anonymous_user_denied_authorization.png deleted file mode 100644 index e793f10e95338bf56b0ac05a8477f60e294bf1f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99753 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

    V*!F%hcD+c8?ozB>P6p&M9Db37dL_=;it`G$#y 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 BOHTj* literal 0 HcmV?d00001 diff --git a/images/book/security_authentication_authorization.png b/images/book/security_authentication_authorization.png deleted file mode 100644 index 6b085cf817e12d37cf6b4f2bf3755992b515919c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41578 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 z 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
    !cPITho(^F><*F>2IJhHoe1F1 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

    Username: getUser()->getUsername() ?>

    @@ -1148,7 +1148,7 @@ Next, you'll need to create a route for this URL (but not a controller): return $collection; And that's it! By sending a user to ``/logout`` (or whatever you configure -the ``path`` to be), Symfony will un-authenticate the current user. and +the ``path`` to be), Symfony will un-authenticate the current user. and redirect them the homepage (the value defined by ``target``). Once the user has been logged out, they will be redirected to whatever path @@ -1180,6 +1180,9 @@ in the following way from a controller:: $user->setPassword($encoded); +.. versionadded:: 2.6 + The ``security.password_encoder`` service was introduced in Symfony 2.6. + In order for this to work, just make sure that you have the encoder for your user class (e.g. ``AppBundle\Entity\User``) configured under the ``encoders`` key in ``app/config/security.yml``. From f1345b2ea6faa4b526d9534794ff76a96b27325c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 2 Jan 2015 16:37:33 +0100 Subject: [PATCH 649/835] [Contributing][Code] update year in license --- contributing/code/license.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/license.rst b/contributing/code/license.rst index aff6b6666d4..9f06e5b67c9 100644 --- a/contributing/code/license.rst +++ b/contributing/code/license.rst @@ -16,7 +16,7 @@ According to `Wikipedia`_: The License ----------- -Copyright (c) 2004-2013 Fabien Potencier +Copyright (c) 2004-2015 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c6580f896d666e8dbce974033502f08b1d080e66 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 2 Jan 2015 17:27:53 +0100 Subject: [PATCH 650/835] Use AppBundle whenever it's possible --- cookbook/assetic/apply_to_option.rst | 24 +++---- cookbook/assetic/asset_management.rst | 66 +++++++++---------- cookbook/assetic/jpeg_optimize.rst | 6 +- cookbook/assetic/uglifyjs.rst | 14 ++-- cookbook/assetic/yuicompressor.rst | 14 ++-- cookbook/bundles/extension.rst | 5 +- cookbook/configuration/apache_router.rst | 12 ++-- cookbook/console/console_command.rst | 10 +-- cookbook/console/logging.rst | 24 +++---- cookbook/console/sending_emails.rst | 2 +- cookbook/controller/error_pages.rst | 10 +-- cookbook/controller/service.rst | 58 ++++++++-------- cookbook/doctrine/custom_dql_functions.rst | 24 +++---- cookbook/doctrine/dbal.rst | 12 ++-- cookbook/doctrine/file_uploads.rst | 20 +++--- .../doctrine/multiple_entity_managers.rst | 8 +-- cookbook/email/testing.rst | 2 +- .../event_dispatcher/before_after_filters.rst | 44 ++++++------- cookbook/form/create_custom_field_type.rst | 44 ++++++------- cookbook/form/dynamic_form_modification.rst | 66 +++++++++---------- cookbook/form/form_customization.rst | 44 ++++++------- cookbook/form/inherit_data_option.rst | 28 ++++---- cookbook/form/use_empty_data.rst | 4 +- cookbook/profiler/matchers.rst | 25 +++---- cookbook/request/mime_type.rst | 24 +++---- cookbook/routing/extra_information.rst | 6 +- cookbook/routing/method_parameters.rst | 18 ++--- cookbook/routing/redirect_in_config.rst | 13 ++-- cookbook/routing/redirect_trailing_slash.rst | 10 +-- cookbook/routing/scheme.rst | 6 +- .../routing/service_container_parameters.rst | 32 ++++----- cookbook/routing/slash_in_parameter.rst | 6 +- cookbook/security/acl.rst | 6 +- .../custom_authentication_provider.rst | 46 ++++++------- cookbook/security/securing_services.rst | 32 ++++----- cookbook/security/target_path.rst | 16 ++--- cookbook/security/voters.rst | 10 +-- cookbook/security/voters_data_permission.rst | 24 +++---- cookbook/service_container/event_listener.rst | 20 +++--- cookbook/service_container/scopes.rst | 44 ++++++------- cookbook/session/locale_sticky_session.rst | 16 ++--- cookbook/templating/PHP.rst | 50 +++++++------- cookbook/templating/namespaced_paths.rst | 8 +-- .../templating/render_without_controller.rst | 14 ++-- cookbook/templating/twig_extension.rst | 22 +++---- cookbook/testing/database.rst | 8 +-- .../testing/simulating_authentication.rst | 10 +-- cookbook/validation/custom_constraint.rst | 43 ++++++------ cookbook/web_services/php_soap_extension.rst | 14 ++-- 49 files changed, 534 insertions(+), 530 deletions(-) diff --git a/cookbook/assetic/apply_to_option.rst b/cookbook/assetic/apply_to_option.rst index 8d5cf98fac8..4c080691f03 100644 --- a/cookbook/assetic/apply_to_option.rst +++ b/cookbook/assetic/apply_to_option.rst @@ -59,14 +59,14 @@ templates: .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/example.coffee' filter='coffee' %} + {% javascripts '@AppBundle/Resources/public/js/example.coffee' filter='coffee' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/example.coffee'), + array('@AppBundle/Resources/public/js/example.coffee'), array('coffee') ) as $url): ?> @@ -84,8 +84,8 @@ You can also combine multiple CoffeeScript files into a single output file: .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/example.coffee' - '@AcmeFooBundle/Resources/public/js/another.coffee' + {% javascripts '@AppBundle/Resources/public/js/example.coffee' + '@AppBundle/Resources/public/js/another.coffee' filter='coffee' %} {% endjavascripts %} @@ -94,8 +94,8 @@ You can also combine multiple CoffeeScript files into a single output file: javascripts( array( - '@AcmeFooBundle/Resources/public/js/example.coffee', - '@AcmeFooBundle/Resources/public/js/another.coffee', + '@AppBundle/Resources/public/js/example.coffee', + '@AppBundle/Resources/public/js/another.coffee', ), array('coffee') ) as $url): ?> @@ -170,9 +170,9 @@ being run through the CoffeeScript filter): .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/example.coffee' - '@AcmeFooBundle/Resources/public/js/another.coffee' - '@AcmeFooBundle/Resources/public/js/regular.js' %} + {% javascripts '@AppBundle/Resources/public/js/example.coffee' + '@AppBundle/Resources/public/js/another.coffee' + '@AppBundle/Resources/public/js/regular.js' %} {% endjavascripts %} @@ -180,9 +180,9 @@ being run through the CoffeeScript filter): javascripts( array( - '@AcmeFooBundle/Resources/public/js/example.coffee', - '@AcmeFooBundle/Resources/public/js/another.coffee', - '@AcmeFooBundle/Resources/public/js/regular.js', + '@AppBundle/Resources/public/js/example.coffee', + '@AppBundle/Resources/public/js/another.coffee', + '@AppBundle/Resources/public/js/regular.js', ) ) as $url): ?> diff --git a/cookbook/assetic/asset_management.rst b/cookbook/assetic/asset_management.rst index 6743796edba..785e239b1a0 100644 --- a/cookbook/assetic/asset_management.rst +++ b/cookbook/assetic/asset_management.rst @@ -59,14 +59,14 @@ To include JavaScript files, use the ``javascripts`` tag in any template: .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/*' %} + {% javascripts '@AppBundle/Resources/public/js/*' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/*') + array('@AppBundle/Resources/public/js/*') ) as $url): ?> @@ -81,7 +81,7 @@ To include JavaScript files, use the ``javascripts`` tag in any template: {# ... #} {% block javascripts %} - {% javascripts '@AcmeFooBundle/Resources/public/js/*' %} + {% javascripts '@AppBundle/Resources/public/js/*' %} {% endjavascripts %} {% endblock %} @@ -92,7 +92,7 @@ To include JavaScript files, use the ``javascripts`` tag in any template: You can also include CSS Stylesheets: see :ref:`cookbook-assetic-including-css`. In this example, all of the files in the ``Resources/public/js/`` directory -of the ``AcmeFooBundle`` will be loaded and served from a different location. +of the ``AppBundle`` will be loaded and served from a different location. The actual rendered tag might simply look like: .. code-block:: html @@ -115,14 +115,14 @@ above, except with the ``stylesheets`` tag: .. code-block:: html+jinja - {% stylesheets 'bundles/acme_foo/css/*' filter='cssrewrite' %} + {% stylesheets 'bundles/app/css/*' filter='cssrewrite' %} {% endstylesheets %} .. code-block:: html+php stylesheets( - array('bundles/acme_foo/css/*'), + array('bundles/app/css/*'), array('cssrewrite') ) as $url): ?> @@ -138,7 +138,7 @@ above, except with the ``stylesheets`` tag: {# ... #} {% block stylesheets %} - {% stylesheets 'bundles/acme_foo/css/*' filter='cssrewrite' %} + {% stylesheets 'bundles/app/css/*' filter='cssrewrite' %} {% endstylesheets %} {% endblock %} @@ -151,11 +151,11 @@ the :ref:`cssrewrite ` filter. .. note:: Notice that in the original example that included JavaScript files, you - referred to the files using a path like ``@AcmeFooBundle/Resources/public/file.js``, + referred to the files using a path like ``@AppBundle/Resources/public/file.js``, but that in this example, you referred to the CSS files using their actual, - publicly-accessible path: ``bundles/acme_foo/css``. You can use either, except + publicly-accessible path: ``bundles/app/css``. You can use either, except that there is a known issue that causes the ``cssrewrite`` filter to fail - when using the ``@AcmeFooBundle`` syntax for CSS Stylesheets. + when using the ``@AppBundle`` syntax for CSS Stylesheets. .. _cookbook-assetic-including-image: @@ -168,14 +168,14 @@ To include an image you can use the ``image`` tag. .. code-block:: html+jinja - {% image '@AcmeFooBundle/Resources/public/images/example.jpg' %} + {% image '@AppBundle/Resources/public/images/example.jpg' %} Example {% endimage %} .. code-block:: html+php image( - array('@AcmeFooBundle/Resources/public/images/example.jpg') + array('@AppBundle/Resources/public/images/example.jpg') ) as $url): ?> Example @@ -198,7 +198,7 @@ You can see an example in the previous section. .. caution:: When using the ``cssrewrite`` filter, don't refer to your CSS files using - the ``@AcmeFooBundle`` syntax. See the note in the above section for details. + the ``@AppBundle`` syntax. See the note in the above section for details. Combining Assets ~~~~~~~~~~~~~~~~ @@ -215,7 +215,7 @@ but still serve them as a single file: .. code-block:: html+jinja {% javascripts - '@AcmeFooBundle/Resources/public/js/*' + '@AppBundle/Resources/public/js/*' '@AcmeBarBundle/Resources/public/js/form.js' '@AcmeBarBundle/Resources/public/js/calendar.js' %} @@ -225,7 +225,7 @@ but still serve them as a single file: javascripts( array( - '@AcmeFooBundle/Resources/public/js/*', + '@AppBundle/Resources/public/js/*', '@AcmeBarBundle/Resources/public/js/form.js', '@AcmeBarBundle/Resources/public/js/calendar.js', ) @@ -254,8 +254,8 @@ combine third party assets, such as jQuery, with your own into a single file: .. code-block:: html+jinja {% javascripts - '@AcmeFooBundle/Resources/public/js/thirdparty/jquery.js' - '@AcmeFooBundle/Resources/public/js/*' %} + '@AppBundle/Resources/public/js/thirdparty/jquery.js' + '@AppBundle/Resources/public/js/*' %} {% endjavascripts %} @@ -263,8 +263,8 @@ combine third party assets, such as jQuery, with your own into a single file: javascripts( array( - '@AcmeFooBundle/Resources/public/js/thirdparty/jquery.js', - '@AcmeFooBundle/Resources/public/js/*', + '@AppBundle/Resources/public/js/thirdparty/jquery.js', + '@AppBundle/Resources/public/js/*', ) ) as $url): ?> @@ -287,8 +287,8 @@ configuration under the ``assetic`` section. Read more in the assets: jquery_and_ui: inputs: - - '@AcmeFooBundle/Resources/public/js/thirdparty/jquery.js' - - '@AcmeFooBundle/Resources/public/js/thirdparty/jquery.ui.js' + - '@AppBundle/Resources/public/js/thirdparty/jquery.js' + - '@AppBundle/Resources/public/js/thirdparty/jquery.ui.js' .. code-block:: xml @@ -299,8 +299,8 @@ configuration under the ``assetic`` section. Read more in the - @AcmeFooBundle/Resources/public/js/thirdparty/jquery.js - @AcmeFooBundle/Resources/public/js/thirdparty/jquery.ui.js + @AppBundle/Resources/public/js/thirdparty/jquery.js + @AppBundle/Resources/public/js/thirdparty/jquery.ui.js @@ -312,8 +312,8 @@ configuration under the ``assetic`` section. Read more in the 'assets' => array( 'jquery_and_ui' => array( 'inputs' => array( - '@AcmeFooBundle/Resources/public/js/thirdparty/jquery.js', - '@AcmeFooBundle/Resources/public/js/thirdparty/jquery.ui.js', + '@AppBundle/Resources/public/js/thirdparty/jquery.js', + '@AppBundle/Resources/public/js/thirdparty/jquery.ui.js', ), ), ), @@ -328,7 +328,7 @@ with the ``@named_asset`` notation: {% javascripts '@jquery_and_ui' - '@AcmeFooBundle/Resources/public/js/*' %} + '@AppBundle/Resources/public/js/*' %} {% endjavascripts %} @@ -337,7 +337,7 @@ with the ``@named_asset`` notation: javascripts( array( '@jquery_and_ui', - '@AcmeFooBundle/Resources/public/js/*', + '@AppBundle/Resources/public/js/*', ) ) as $url): ?> @@ -406,14 +406,14 @@ into your template: .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/*' filter='uglifyjs2' %} + {% javascripts '@AppBundle/Resources/public/js/*' filter='uglifyjs2' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/*'), + array('@AppBundle/Resources/public/js/*'), array('uglifyjs2') ) as $url): ?> @@ -432,14 +432,14 @@ done from the template and is relative to the public document root: .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/*' output='js/compiled/main.js' %} + {% javascripts '@AppBundle/Resources/public/js/*' output='js/compiled/main.js' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/*'), + array('@AppBundle/Resources/public/js/*'), array(), array('output' => 'js/compiled/main.js') ) as $url): ?> @@ -555,14 +555,14 @@ some isolated directory (e.g. ``/js/compiled``), to keep things organized: .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/*' output='js/compiled/main.js' %} + {% javascripts '@AppBundle/Resources/public/js/*' output='js/compiled/main.js' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/*'), + array('@AppBundle/Resources/public/js/*'), array(), array('output' => 'js/compiled/main.js') ) as $url): ?> diff --git a/cookbook/assetic/jpeg_optimize.rst b/cookbook/assetic/jpeg_optimize.rst index 01f67f6ebcb..7955771ca02 100644 --- a/cookbook/assetic/jpeg_optimize.rst +++ b/cookbook/assetic/jpeg_optimize.rst @@ -57,7 +57,7 @@ It can now be used from a template: .. code-block:: html+jinja - {% image '@AcmeFooBundle/Resources/public/images/example.jpg' + {% image '@AppBundle/Resources/public/images/example.jpg' filter='jpegoptim' output='/images/example.jpg' %} Example {% endimage %} @@ -65,7 +65,7 @@ It can now be used from a template: .. code-block:: html+php image( - array('@AcmeFooBundle/Resources/public/images/example.jpg'), + array('@AppBundle/Resources/public/images/example.jpg'), array('jpegoptim') ) as $url): ?> Example @@ -204,7 +204,7 @@ The Twig template can now be changed to the following: .. code-block:: html+jinja - Example + Example You can specify the output directory in the config in the following way: diff --git a/cookbook/assetic/uglifyjs.rst b/cookbook/assetic/uglifyjs.rst index 958a1c616a5..a028c50e7b7 100644 --- a/cookbook/assetic/uglifyjs.rst +++ b/cookbook/assetic/uglifyjs.rst @@ -161,14 +161,14 @@ your assets are a part of the view layer, this work is done in your templates: .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/*' filter='uglifyjs2' %} + {% javascripts '@AppBundle/Resources/public/js/*' filter='uglifyjs2' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/*'), + array('@AppBundle/Resources/public/js/*'), array('uglifyj2s') ) as $url): ?> @@ -176,7 +176,7 @@ your assets are a part of the view layer, this work is done in your templates: .. note:: - The above example assumes that you have a bundle called ``AcmeFooBundle`` + The above example assumes that you have a bundle called ``AppBundle`` and your JavaScript files are in the ``Resources/public/js`` directory under your bundle. This isn't important however - you can include your JavaScript files no matter where they are. @@ -197,14 +197,14 @@ apply this filter when debug mode is off (e.g. ``app.php``): .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/*' filter='?uglifyjs2' %} + {% javascripts '@AppBundle/Resources/public/js/*' filter='?uglifyjs2' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/*'), + array('@AppBundle/Resources/public/js/*'), array('?uglifyjs2') ) as $url): ?> @@ -272,14 +272,14 @@ helper: .. code-block:: html+jinja - {% stylesheets 'bundles/AcmeFoo/css/*' filter='uglifycss' filter='cssrewrite' %} + {% stylesheets 'bundles/App/css/*' filter='uglifycss' filter='cssrewrite' %} {% endstylesheets %} .. code-block:: html+php stylesheets( - array('bundles/AcmeFoo/css/*'), + array('bundles/App/css/*'), array('uglifycss'), array('cssrewrite') ) as $url): ?> diff --git a/cookbook/assetic/yuicompressor.rst b/cookbook/assetic/yuicompressor.rst index fedd4dab1da..7e3671fa2fb 100644 --- a/cookbook/assetic/yuicompressor.rst +++ b/cookbook/assetic/yuicompressor.rst @@ -91,14 +91,14 @@ the view layer, this work is done in your templates: .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/*' filter='yui_js' %} + {% javascripts '@AppBundle/Resources/public/js/*' filter='yui_js' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/*'), + array('@AppBundle/Resources/public/js/*'), array('yui_js') ) as $url): ?> @@ -106,7 +106,7 @@ the view layer, this work is done in your templates: .. note:: - The above example assumes that you have a bundle called ``AcmeFooBundle`` + The above example assumes that you have a bundle called ``AppBundle`` and your JavaScript files are in the ``Resources/public/js`` directory under your bundle. This isn't important however - you can include your JavaScript files no matter where they are. @@ -119,14 +119,14 @@ can be repeated to minify your stylesheets. .. code-block:: html+jinja - {% stylesheets '@AcmeFooBundle/Resources/public/css/*' filter='yui_css' %} + {% stylesheets '@AppBundle/Resources/public/css/*' filter='yui_css' %} {% endstylesheets %} .. code-block:: html+php stylesheets( - array('@AcmeFooBundle/Resources/public/css/*'), + array('@AppBundle/Resources/public/css/*'), array('yui_css') ) as $url): ?> @@ -145,14 +145,14 @@ apply this filter when debug mode is off. .. code-block:: html+jinja - {% javascripts '@AcmeFooBundle/Resources/public/js/*' filter='?yui_js' %} + {% javascripts '@AppBundle/Resources/public/js/*' filter='?yui_js' %} {% endjavascripts %} .. code-block:: html+php javascripts( - array('@AcmeFooBundle/Resources/public/js/*'), + array('@AppBundle/Resources/public/js/*'), array('?yui_js') ) as $url): ?> diff --git a/cookbook/bundles/extension.rst b/cookbook/bundles/extension.rst index d335b8a3f26..bdaf74570a6 100644 --- a/cookbook/bundles/extension.rst +++ b/cookbook/bundles/extension.rst @@ -23,8 +23,9 @@ following conventions: * It has to live in the ``DependencyInjection`` namespace of the bundle; * The name is equal to the bundle name with the ``Bundle`` suffix replaced by - ``Extension`` (e.g. the Extension class of ``AcmeHelloBundle`` would be - called ``AcmeHelloExtension``). + ``Extension`` (e.g. the Extension class of ``AppBundle`` would be called + ``AppExtension`` and the one for ``AcmeHelloBundle`` would be called + ``AcmeHelloExtension``). The Extension class should implement the :class:`Symfony\\Component\\DependencyInjection\\Extension\\ExtensionInterface`, diff --git a/cookbook/configuration/apache_router.rst b/cookbook/configuration/apache_router.rst index 90f652992f4..7fdcb7cc57c 100644 --- a/cookbook/configuration/apache_router.rst +++ b/cookbook/configuration/apache_router.rst @@ -52,7 +52,7 @@ Symfony to use the ``ApacheUrlMatcher`` instead of the default one: Generating mod_rewrite Rules ---------------------------- -To test that it's working, create a very basic route for the AcmeDemoBundle: +To test that it's working, create a very basic route for the AppBundle: .. configuration-block:: @@ -61,20 +61,20 @@ To test that it's working, create a very basic route for the AcmeDemoBundle: # app/config/routing.yml hello: path: /hello/{name} - defaults: { _controller: AcmeDemoBundle:Demo:hello } + defaults: { _controller: AppBundle:Demo:hello } .. code-block:: xml - AcmeDemoBundle:Demo:hello + AppBundle:Demo:hello .. code-block:: php // app/config/routing.php $collection->add('hello', new Route('/hello/{name}', array( - '_controller' => 'AcmeDemoBundle:Demo:hello', + '_controller' => 'AppBundle:Demo:hello', ))); Now generate the mod_rewrite rules: @@ -93,7 +93,7 @@ Which should roughly output the following: # hello RewriteCond %{REQUEST_URI} ^/hello/([^/]+?)$ - RewriteRule .* app.php [QSA,L,E=_ROUTING__route:hello,E=_ROUTING_name:%1,E=_ROUTING__controller:AcmeDemoBundle\:Demo\:hello] + RewriteRule .* app.php [QSA,L,E=_ROUTING__route:hello,E=_ROUTING_name:%1,E=_ROUTING__controller:AppBundle\:Demo\:hello] You can now rewrite ``web/.htaccess`` to use the new rules, so with this example it should look like this: @@ -109,7 +109,7 @@ it should look like this: # hello RewriteCond %{REQUEST_URI} ^/hello/([^/]+?)$ - RewriteRule .* app.php [QSA,L,E=_ROUTING__route:hello,E=_ROUTING_name:%1,E=_ROUTING__controller:AcmeDemoBundle\:Demo\:hello] + RewriteRule .* app.php [QSA,L,E=_ROUTING__route:hello,E=_ROUTING_name:%1,E=_ROUTING__controller:AppBundle\:Demo\:hello] .. note:: diff --git a/cookbook/console/console_command.rst b/cookbook/console/console_command.rst index 4a2ea5cafb2..2959a2c5044 100644 --- a/cookbook/console/console_command.rst +++ b/cookbook/console/console_command.rst @@ -14,11 +14,11 @@ Automatically Registering Commands To make the console commands available automatically with Symfony, create a ``Command`` directory inside your bundle and create a PHP file suffixed with ``Command.php`` for each command that you want to provide. For example, if you -want to extend the AcmeDemoBundle to greet you from the command line, create +want to extend the AppBundle to greet you from the command line, create ``GreetCommand.php`` and add the following to it:: - // src/Acme/DemoBundle/Command/GreetCommand.php - namespace Acme\DemoBundle\Command; + // src/AppBundle/Command/GreetCommand.php + namespace AppBundle\Command; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Input\InputArgument; @@ -147,7 +147,7 @@ instead of use Symfony\Component\Console\Tester\CommandTester; use Symfony\Bundle\FrameworkBundle\Console\Application; - use Acme\DemoBundle\Command\GreetCommand; + use AppBundle\Command\GreetCommand; class ListCommandTest extends \PHPUnit_Framework_TestCase { @@ -186,7 +186,7 @@ you can extend your test from use Symfony\Component\Console\Tester\CommandTester; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; - use Acme\DemoBundle\Command\GreetCommand; + use AppBundle\Command\GreetCommand; class ListCommandTest extends WebTestCase { diff --git a/cookbook/console/logging.rst b/cookbook/console/logging.rst index 031121051f1..df5d6b97f97 100644 --- a/cookbook/console/logging.rst +++ b/cookbook/console/logging.rst @@ -26,8 +26,8 @@ extends :class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand This means that you can simply access the standard logger service through the container and use it to do the logging:: - // src/Acme/DemoBundle/Command/GreetCommand.php - namespace Acme\DemoBundle\Command; + // src/AppBundle/Command/GreetCommand.php + namespace AppBundle\Command; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Console\Input\InputArgument; @@ -84,7 +84,7 @@ First configure a listener for console exception events in the service container # app/config/services.yml services: kernel.listener.command_dispatch: - class: Acme\DemoBundle\EventListener\ConsoleExceptionListener + class: AppBundle\EventListener\ConsoleExceptionListener arguments: logger: "@logger" tags: @@ -99,7 +99,7 @@ First configure a listener for console exception events in the service container xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -113,7 +113,7 @@ First configure a listener for console exception events in the service container use Symfony\Component\DependencyInjection\Reference; $definitionConsoleExceptionListener = new Definition( - 'Acme\DemoBundle\EventListener\ConsoleExceptionListener', + 'AppBundle\EventListener\ConsoleExceptionListener', array(new Reference('logger')) ); $definitionConsoleExceptionListener->addTag( @@ -127,8 +127,8 @@ First configure a listener for console exception events in the service container Then implement the actual listener:: - // src/Acme/DemoBundle/EventListener/ConsoleExceptionListener.php - namespace Acme\DemoBundle\EventListener; + // src/AppBundle/EventListener/ConsoleExceptionListener.php + namespace AppBundle\EventListener; use Symfony\Component\Console\Event\ConsoleExceptionEvent; use Psr\Log\LoggerInterface; @@ -182,7 +182,7 @@ First configure a listener for console terminate events in the service container # app/config/services.yml services: kernel.listener.command_dispatch: - class: Acme\DemoBundle\EventListener\ErrorLoggerListener + class: AppBundle\EventListener\ErrorLoggerListener arguments: logger: "@logger" tags: @@ -197,7 +197,7 @@ First configure a listener for console terminate events in the service container xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - + @@ -211,7 +211,7 @@ First configure a listener for console terminate events in the service container use Symfony\Component\DependencyInjection\Reference; $definitionErrorLoggerListener = new Definition( - 'Acme\DemoBundle\EventListener\ErrorLoggerListener', + 'AppBundle\EventListener\ErrorLoggerListener', array(new Reference('logger')) ); $definitionErrorLoggerListener->addTag( @@ -225,8 +225,8 @@ First configure a listener for console terminate events in the service container Then implement the actual listener:: - // src/Acme/DemoBundle/EventListener/ErrorLoggerListener.php - namespace Acme\DemoBundle\EventListener; + // src/AppBundle/EventListener/ErrorLoggerListener.php + namespace AppBundle\EventListener; use Symfony\Component\Console\Event\ConsoleTerminateEvent; use Psr\Log\LoggerInterface; diff --git a/cookbook/console/sending_emails.rst b/cookbook/console/sending_emails.rst index 2ecd95e4104..c6e870189e5 100644 --- a/cookbook/console/sending_emails.rst +++ b/cookbook/console/sending_emails.rst @@ -69,7 +69,7 @@ Configuring the Request Context per Command To change it only in one command you can simply fetch the Request Context from the ``router`` service and override its settings:: - // src/Acme/DemoBundle/Command/DemoCommand.php + // src/AppBundle/Command/DemoCommand.php // ... class DemoCommand extends ContainerAwareCommand diff --git a/cookbook/controller/error_pages.rst b/cookbook/controller/error_pages.rst index fa8efef21fc..3217c602017 100644 --- a/cookbook/controller/error_pages.rst +++ b/cookbook/controller/error_pages.rst @@ -178,7 +178,7 @@ to point to it. # app/config/config.yml twig: - exception_controller: AcmeFooBundle:Exception:showException + exception_controller: AppBundle:Exception:showException .. code-block:: xml @@ -191,7 +191,7 @@ to point to it. http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd"> - AcmeFooBundle:Exception:showException + AppBundle:Exception:showException @@ -199,7 +199,7 @@ to point to it. // app/config/config.php $container->loadFromExtension('twig', array( - 'exception_controller' => 'AcmeFooBundle:Exception:showException', + 'exception_controller' => 'AppBundle:Exception:showException', // ... )); @@ -265,7 +265,7 @@ another page or rendering specialized error pages. If your listener calls ``setResponse()`` on the :class:`Symfony\\Component\\HttpKernel\\Event\\GetResponseForExceptionEvent`, - event propagation will be stopped and the response will be sent to + event propagation will be stopped and the response will be sent to the client. This approach allows you to create centralized and layered error @@ -277,7 +277,7 @@ several) listeners deal with them. To see an example, have a look at the `ExceptionListener`_ in the Security Component. - + It handles various security-related exceptions that are thrown in your application (like :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`) and takes measures like redirecting the user to the login page, diff --git a/cookbook/controller/service.rst b/cookbook/controller/service.rst index bf2cadb2887..1ce43de4911 100644 --- a/cookbook/controller/service.rst +++ b/cookbook/controller/service.rst @@ -34,8 +34,8 @@ Defining the Controller as a Service A controller can be defined as a service in the same way as any other class. For example, if you have the following simple controller:: - // src/Acme/HelloBundle/Controller/HelloController.php - namespace Acme\HelloBundle\Controller; + // src/AppBundle/Controller/HelloController.php + namespace AppBundle\Controller; use Symfony\Component\HttpFoundation\Response; @@ -53,25 +53,25 @@ Then you can define it as a service as follows: .. code-block:: yaml - # src/Acme/HelloBundle/Resources/config/services.yml + # app/config/services.yml services: - acme.hello.controller: - class: Acme\HelloBundle\Controller\HelloController + app.hello_controller: + class: AppBundle\Controller\HelloController .. code-block:: xml - + - + .. code-block:: php - // src/Acme/HelloBundle/Resources/config/services.php + // app/config/services.php use Symfony\Component\DependencyInjection\Definition; - $container->setDefinition('acme.hello.controller', new Definition( - 'Acme\HelloBundle\Controller\HelloController' + $container->setDefinition('app.hello_controller', new Definition( + 'AppBundle\Controller\HelloController' )); Referring to the Service @@ -79,9 +79,9 @@ Referring to the Service To refer to a controller that's defined as a service, use the single colon (:) notation. For example, to forward to the ``indexAction()`` method of the service -defined above with the id ``acme.hello.controller``:: +defined above with the id ``app.hello_controller``:: - $this->forward('acme.hello.controller:indexAction', array('name' => $name)); + $this->forward('app.hello_controller:indexAction', array('name' => $name)); .. note:: @@ -98,20 +98,20 @@ the route ``_controller`` value: # app/config/routing.yml hello: path: /hello - defaults: { _controller: acme.hello.controller:indexAction } + defaults: { _controller: app.hello_controller:indexAction } .. code-block:: xml - acme.hello.controller:indexAction + app.hello_controller:indexAction .. code-block:: php // app/config/routing.php $collection->add('hello', new Route('/hello', array( - '_controller' => 'acme.hello.controller:indexAction', + '_controller' => 'app.hello_controller:indexAction', ))); .. tip:: @@ -133,8 +133,8 @@ For example, if you want to render a template instead of creating the ``Response object directly, then your code would look like this if you were extending Symfony's base controller:: - // src/Acme/HelloBundle/Controller/HelloController.php - namespace Acme\HelloBundle\Controller; + // src/AppBundle/Controller/HelloController.php + namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; @@ -143,7 +143,7 @@ Symfony's base controller:: public function indexAction($name) { return $this->render( - 'AcmeHelloBundle:Hello:index.html.twig', + 'AppBundle:Hello:index.html.twig', array('name' => $name) ); } @@ -161,8 +161,8 @@ If you look at the source code for the ``render`` function in Symfony's In a controller that's defined as a service, you can instead inject the ``templating`` service and use it directly:: - // src/Acme/HelloBundle/Controller/HelloController.php - namespace Acme\HelloBundle\Controller; + // src/AppBundle/Controller/HelloController.php + namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface; use Symfony\Component\HttpFoundation\Response; @@ -179,7 +179,7 @@ service and use it directly:: public function indexAction($name) { return $this->templating->renderResponse( - 'AcmeHelloBundle:Hello:index.html.twig', + 'AppBundle:Hello:index.html.twig', array('name' => $name) ); } @@ -192,29 +192,29 @@ argument: .. code-block:: yaml - # src/Acme/HelloBundle/Resources/config/services.yml + # app/config/services.yml services: - acme.hello.controller: - class: Acme\HelloBundle\Controller\HelloController + app.hello_controller: + class: AppBundle\Controller\HelloController arguments: ["@templating"] .. code-block:: xml - + - + .. code-block:: php - // src/Acme/HelloBundle/Resources/config/services.php + // app/config/services.php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - $container->setDefinition('acme.hello.controller', new Definition( - 'Acme\HelloBundle\Controller\HelloController', + $container->setDefinition('app.hello_controller', new Definition( + 'AppBundle\Controller\HelloController', array(new Reference('templating')) )); diff --git a/cookbook/doctrine/custom_dql_functions.rst b/cookbook/doctrine/custom_dql_functions.rst index dcdfc6bc492..747a353e7a5 100644 --- a/cookbook/doctrine/custom_dql_functions.rst +++ b/cookbook/doctrine/custom_dql_functions.rst @@ -19,12 +19,12 @@ In Symfony, you can register your custom DQL functions as follows: # ... dql: string_functions: - test_string: Acme\HelloBundle\DQL\StringFunction - second_string: Acme\HelloBundle\DQL\SecondStringFunction + test_string: AppBundle\DQL\StringFunction + second_string: AppBundle\DQL\SecondStringFunction numeric_functions: - test_numeric: Acme\HelloBundle\DQL\NumericFunction + test_numeric: AppBundle\DQL\NumericFunction datetime_functions: - test_datetime: Acme\HelloBundle\DQL\DatetimeFunction + test_datetime: AppBundle\DQL\DatetimeFunction .. code-block:: xml @@ -39,10 +39,10 @@ In Symfony, you can register your custom DQL functions as follows: - Acme\HelloBundle\DQL\StringFunction - Acme\HelloBundle\DQL\SecondStringFunction - Acme\HelloBundle\DQL\NumericFunction - Acme\HelloBundle\DQL\DatetimeFunction + AppBundle\DQL\StringFunction + AppBundle\DQL\SecondStringFunction + AppBundle\DQL\NumericFunction + AppBundle\DQL\DatetimeFunction @@ -56,14 +56,14 @@ In Symfony, you can register your custom DQL functions as follows: // ... 'dql' => array( 'string_functions' => array( - 'test_string' => 'Acme\HelloBundle\DQL\StringFunction', - 'second_string' => 'Acme\HelloBundle\DQL\SecondStringFunction', + 'test_string' => 'AppBundle\DQL\StringFunction', + 'second_string' => 'AppBundle\DQL\SecondStringFunction', ), 'numeric_functions' => array( - 'test_numeric' => 'Acme\HelloBundle\DQL\NumericFunction', + 'test_numeric' => 'AppBundle\DQL\NumericFunction', ), 'datetime_functions' => array( - 'test_datetime' => 'Acme\HelloBundle\DQL\DatetimeFunction', + 'test_datetime' => 'AppBundle\DQL\DatetimeFunction', ), ), ), diff --git a/cookbook/doctrine/dbal.rst b/cookbook/doctrine/dbal.rst index 73d9e07e109..39cb0eabeaf 100644 --- a/cookbook/doctrine/dbal.rst +++ b/cookbook/doctrine/dbal.rst @@ -93,8 +93,8 @@ mapping types, read Doctrine's `Custom Mapping Types`_ section of their document doctrine: dbal: types: - custom_first: Acme\HelloBundle\Type\CustomFirst - custom_second: Acme\HelloBundle\Type\CustomSecond + custom_first: AppBundle\Type\CustomFirst + custom_second: AppBundle\Type\CustomSecond .. code-block:: xml @@ -107,8 +107,8 @@ mapping types, read Doctrine's `Custom Mapping Types`_ section of their document - - + + @@ -119,8 +119,8 @@ mapping types, read Doctrine's `Custom Mapping Types`_ section of their document $container->loadFromExtension('doctrine', array( 'dbal' => array( 'types' => array( - 'custom_first' => 'Acme\HelloBundle\Type\CustomFirst', - 'custom_second' => 'Acme\HelloBundle\Type\CustomSecond', + 'custom_first' => 'AppBundle\Type\CustomFirst', + 'custom_second' => 'AppBundle\Type\CustomSecond', ), ), )); diff --git a/cookbook/doctrine/file_uploads.rst b/cookbook/doctrine/file_uploads.rst index fa400525187..b68806f59d4 100644 --- a/cookbook/doctrine/file_uploads.rst +++ b/cookbook/doctrine/file_uploads.rst @@ -23,8 +23,8 @@ Basic Setup First, create a simple Doctrine entity class to work with:: - // src/Acme/DemoBundle/Entity/Document.php - namespace Acme\DemoBundle\Entity; + // src/AppBundle/Entity/Document.php + namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; @@ -154,8 +154,8 @@ rules:: .. code-block:: yaml - # src/Acme/DemoBundle/Resources/config/validation.yml - Acme\DemoBundle\Entity\Document: + # src/AppBundle/Resources/config/validation.yml + AppBundle\Entity\Document: properties: file: - File: @@ -163,8 +163,8 @@ rules:: .. code-block:: php-annotations - // src/Acme/DemoBundle/Entity/Document.php - namespace Acme\DemoBundle\Entity; + // src/AppBundle/Entity/Document.php + namespace AppBundle\Entity; // ... use Symfony\Component\Validator\Constraints as Assert; @@ -181,8 +181,8 @@ rules:: .. code-block:: xml - - + + @@ -192,7 +192,7 @@ rules:: .. code-block:: php - // src/Acme/DemoBundle/Entity/Document.php + // src/AppBundle/Entity/Document.php namespace Acme\DemoBundle\Entity; // ... @@ -220,7 +220,7 @@ rules:: The following controller shows you how to handle the entire process:: // ... - use Acme\DemoBundle\Entity\Document; + use AppBundle\Entity\Document; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; use Symfony\Component\HttpFoundation\Request; // ... diff --git a/cookbook/doctrine/multiple_entity_managers.rst b/cookbook/doctrine/multiple_entity_managers.rst index 689d9d1bba2..ce2a3f685c6 100644 --- a/cookbook/doctrine/multiple_entity_managers.rst +++ b/cookbook/doctrine/multiple_entity_managers.rst @@ -49,7 +49,7 @@ The following configuration code shows how you can configure two entity managers default: connection: default mappings: - AcmeDemoBundle: ~ + AppBundle: ~ AcmeStoreBundle: ~ customer: connection: customer @@ -90,7 +90,7 @@ The following configuration code shows how you can configure two entity managers - + @@ -134,7 +134,7 @@ The following configuration code shows how you can configure two entity managers 'default' => array( 'connection' => 'default', 'mappings' => array( - 'AcmeDemoBundle' => null, + 'AppBundle' => null, 'AcmeStoreBundle' => null, ), ), @@ -150,7 +150,7 @@ The following configuration code shows how you can configure two entity managers In this case, you've defined two entity managers and called them ``default`` and ``customer``. The ``default`` entity manager manages entities in the -``AcmeDemoBundle`` and ``AcmeStoreBundle``, while the ``customer`` entity +``AppBundle`` and ``AcmeStoreBundle``, while the ``customer`` entity manager manages entities in the ``AcmeCustomerBundle``. You've also defined two connections, one for each entity manager. diff --git a/cookbook/email/testing.rst b/cookbook/email/testing.rst index db6e717ec36..d5fce3b4186 100644 --- a/cookbook/email/testing.rst +++ b/cookbook/email/testing.rst @@ -33,7 +33,7 @@ Start with an easy controller action that sends an e-mail:: In your functional test, use the ``swiftmailer`` collector on the profiler to get information about the messages send on the previous request:: - // src/Acme/DemoBundle/Tests/Controller/MailControllerTest.php + // src/AppBundle/Tests/Controller/MailControllerTest.php use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class MailControllerTest extends WebTestCase diff --git a/cookbook/event_dispatcher/before_after_filters.rst b/cookbook/event_dispatcher/before_after_filters.rst index 28ca972faec..262365037b5 100644 --- a/cookbook/event_dispatcher/before_after_filters.rst +++ b/cookbook/event_dispatcher/before_after_filters.rst @@ -74,7 +74,7 @@ controller that matches the request needs token validation. A clean and easy way is to create an empty interface and make the controllers implement it:: - namespace Acme\DemoBundle\Controller; + namespace AppBundle\Controller; interface TokenAuthenticatedController { @@ -83,9 +83,9 @@ implement it:: A controller that implements this interface simply looks like this:: - namespace Acme\DemoBundle\Controller; + namespace AppBundle\Controller; - use Acme\DemoBundle\Controller\TokenAuthenticatedController; + use AppBundle\Controller\TokenAuthenticatedController; use Symfony\Bundle\FrameworkBundle\Controller\Controller; class FooController extends Controller implements TokenAuthenticatedController @@ -104,10 +104,10 @@ Next, you'll need to create an event listener, which will hold the logic that you want executed before your controllers. If you're not familiar with event listeners, you can learn more about them at :doc:`/cookbook/service_container/event_listener`:: - // src/Acme/DemoBundle/EventListener/TokenListener.php - namespace Acme\DemoBundle\EventListener; + // src/AppBundle/EventListener/TokenListener.php + namespace AppBundle\EventListener; - use Acme\DemoBundle\Controller\TokenAuthenticatedController; + use AppBundle\Controller\TokenAuthenticatedController; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; @@ -153,33 +153,33 @@ your listener to be called just before any controller is executed. .. code-block:: yaml - # app/config/config.yml (or inside your services.yml) + # app/config/services.yml services: - demo.tokens.action_listener: - class: Acme\DemoBundle\EventListener\TokenListener + app.tokens.action_listener: + class: AappBundle\EventListener\TokenListener arguments: ["%tokens%"] tags: - { name: kernel.event_listener, event: kernel.controller, method: onKernelController } .. code-block:: xml - - + + %tokens% .. code-block:: php - // app/config/config.php (or inside your services.php) + // app/config/services.php use Symfony\Component\DependencyInjection\Definition; - $listener = new Definition('Acme\DemoBundle\EventListener\TokenListener', array('%tokens%')); + $listener = new Definition('AppBundle\EventListener\TokenListener', array('%tokens%')); $listener->addTag('kernel.event_listener', array( 'event' => 'kernel.controller', 'method' => 'onKernelController' )); - $container->setDefinition('demo.tokens.action_listener', $listener); + $container->setDefinition('app.tokens.action_listener', $listener); With this configuration, your ``TokenListener`` ``onKernelController`` method will be executed on each request. If the controller that is about to be executed @@ -248,10 +248,10 @@ event: .. code-block:: yaml - # app/config/config.yml (or inside your services.yml) + # app/config/services.yml services: - demo.tokens.action_listener: - class: Acme\DemoBundle\EventListener\TokenListener + app.tokens.action_listener: + class: AppBundle\EventListener\TokenListener arguments: ["%tokens%"] tags: - { name: kernel.event_listener, event: kernel.controller, method: onKernelController } @@ -259,8 +259,8 @@ event: .. code-block:: xml - - + + %tokens% @@ -268,10 +268,10 @@ event: .. code-block:: php - // app/config/config.php (or inside your services.php) + // app/config/services.php use Symfony\Component\DependencyInjection\Definition; - $listener = new Definition('Acme\DemoBundle\EventListener\TokenListener', array('%tokens%')); + $listener = new Definition('AppBundle\EventListener\TokenListener', array('%tokens%')); $listener->addTag('kernel.event_listener', array( 'event' => 'kernel.controller', 'method' => 'onKernelController' @@ -280,7 +280,7 @@ event: 'event' => 'kernel.response', 'method' => 'onKernelResponse' )); - $container->setDefinition('demo.tokens.action_listener', $listener); + $container->setDefinition('app.tokens.action_listener', $listener); That's it! The ``TokenListener`` is now notified before every controller is executed (``onKernelController``) and after every controller returns a response diff --git a/cookbook/form/create_custom_field_type.rst b/cookbook/form/create_custom_field_type.rst index 891a274f7a8..17b28778bac 100644 --- a/cookbook/form/create_custom_field_type.rst +++ b/cookbook/form/create_custom_field_type.rst @@ -20,8 +20,8 @@ will be called ``GenderType`` and the file will be stored in the default locatio for form fields, which is ``\Form\Type``. Make sure the field extends :class:`Symfony\\Component\\Form\\AbstractType`:: - // src/Acme/DemoBundle/Form/Type/GenderType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/GenderType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -111,7 +111,7 @@ link for details), create a ``gender_widget`` block to handle this: .. code-block:: html+jinja - {# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #} + {# src/AppBundle/Resources/views/Form/fields.html.twig #} {% block gender_widget %} {% spaceless %} {% if expanded %} @@ -132,7 +132,7 @@ link for details), create a ``gender_widget`` block to handle this: .. code-block:: html+php - +
      block($form, 'widget_container_attributes') ?>> @@ -164,14 +164,14 @@ link for details), create a ``gender_widget`` block to handle this: twig: form: resources: - - 'AcmeDemoBundle:Form:fields.html.twig' + - 'AppBundle:Form:fields.html.twig' .. code-block:: xml - AcmeDemoBundle:Form:fields.html.twig + AppBundle:Form:fields.html.twig @@ -181,7 +181,7 @@ link for details), create a ``gender_widget`` block to handle this: $container->loadFromExtension('twig', array( 'form' => array( 'resources' => array( - 'AcmeDemoBundle:Form:fields.html.twig', + 'AppBundle:Form:fields.html.twig', ), ), )); @@ -197,7 +197,7 @@ link for details), create a ``gender_widget`` block to handle this: templating: form: resources: - - 'AcmeDemoBundle:Form' + - 'AppBundle:Form' .. code-block:: xml @@ -212,7 +212,7 @@ link for details), create a ``gender_widget`` block to handle this: - AcmeDemoBundle:Form + AppBundle:Form @@ -225,7 +225,7 @@ link for details), create a ``gender_widget`` block to handle this: 'templating' => array( 'form' => array( 'resources' => array( - 'AcmeDemoBundle:Form', + 'AppBundle:Form', ), ), ), @@ -237,8 +237,8 @@ Using the Field Type You can now use your custom field type immediately, simply by creating a new instance of the type in one of your forms:: - // src/Acme/DemoBundle/Form/Type/AuthorType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/AuthorType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -301,10 +301,10 @@ the ``genders`` parameter value as the first argument to its to-be-created .. code-block:: yaml - # src/Acme/DemoBundle/Resources/config/services.yml + # src/AppBundle/Resources/config/services.yml services: acme_demo.form.type.gender: - class: Acme\DemoBundle\Form\Type\GenderType + class: AppBundle\Form\Type\GenderType arguments: - "%genders%" tags: @@ -312,20 +312,20 @@ the ``genders`` parameter value as the first argument to its to-be-created .. code-block:: xml - - + + %genders% .. code-block:: php - // src/Acme/DemoBundle/Resources/config/services.php + // src/AppBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; $container ->setDefinition('acme_demo.form.type.gender', new Definition( - 'Acme\DemoBundle\Form\Type\GenderType', + 'AppBundle\Form\Type\GenderType', array('%genders%') )) ->addTag('form.type', array( @@ -343,8 +343,8 @@ returned by the ``getName`` method defined earlier. You'll see the importance of this in a moment when you use the custom field type. But first, add a ``__construct`` method to ``GenderType``, which receives the gender configuration:: - // src/Acme/DemoBundle/Form/Type/GenderType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/GenderType.php + namespace AppBundle\Form\Type; use Symfony\Component\OptionsResolver\OptionsResolverInterface; @@ -374,8 +374,8 @@ Great! The ``GenderType`` is now fueled by the configuration parameters and registered as a service. Additionally, because you used the ``form.type`` alias in its configuration, using the field is now much easier:: - // src/Acme/DemoBundle/Form/Type/AuthorType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/AuthorType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\FormBuilderInterface; diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index 36b4b9e9800..462d6a1f7d6 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -36,8 +36,8 @@ Customizing your Form Based on the Underlying Data Before jumping right into dynamic form generation, hold on and recall what a bare form class looks like:: - // src/Acme/DemoBundle/Form/Type/ProductType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/ProductType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -54,7 +54,7 @@ a bare form class looks like:: public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( - 'data_class' => 'Acme\DemoBundle\Entity\Product' + 'data_class' => 'AppBundle\Entity\Product' )); } @@ -90,8 +90,8 @@ Adding an Event Listener to a Form Class So, instead of directly adding that ``name`` widget, the responsibility of creating that particular field is delegated to an event listener:: - // src/Acme/DemoBundle/Form/Type/ProductType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/ProductType.php + namespace AppBundle\Form\Type; // ... use Symfony\Component\Form\FormEvent; @@ -156,11 +156,11 @@ For better reusability or if there is some heavy logic in your event listener, you can also move the logic for creating the ``name`` field to an :ref:`event subscriber `:: - // src/Acme/DemoBundle/Form/Type/ProductType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/ProductType.php + namespace AppBundle\Form\Type; // ... - use Acme\DemoBundle\Form\EventListener\AddNameFieldSubscriber; + use AppBundle\Form\EventListener\AddNameFieldSubscriber; class ProductType extends AbstractType { @@ -177,8 +177,8 @@ you can also move the logic for creating the ``name`` field to an Now the logic for creating the ``name`` field resides in it own subscriber class:: - // src/Acme/DemoBundle/Form/EventListener/AddNameFieldSubscriber.php - namespace Acme\DemoBundle\Form\EventListener; + // src/AppBundle/Form/EventListener/AddNameFieldSubscriber.php + namespace AppBundle\Form\EventListener; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; @@ -221,8 +221,8 @@ Creating the Form Type Using an event listener, your form might look like this:: - // src/Acme/DemoBundle/Form/Type/FriendMessageFormType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/FriendMessageFormType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -283,7 +283,7 @@ Customizing the Form Type Now that you have all the basics in place you can take advantage of the ``SecurityContext`` and fill in the listener logic:: - // src/Acme/DemoBundle/FormType/FriendMessageFormType.php + // src/AppBundle/FormType/FriendMessageFormType.php use Symfony\Component\Security\Core\SecurityContext; use Doctrine\ORM\EntityRepository; @@ -319,7 +319,7 @@ and fill in the listener logic:: $form = $event->getForm(); $formOptions = array( - 'class' => 'Acme\DemoBundle\Entity\User', + 'class' => 'AppBundle\Entity\User', 'property' => 'fullName', 'query_builder' => function (EntityRepository $er) use ($user) { // build a custom query @@ -390,7 +390,7 @@ it with :ref:`dic-tags-form-type`. # app/config/config.yml services: acme.form.friend_message: - class: Acme\DemoBundle\Form\Type\FriendMessageFormType + class: AppBundle\Form\Type\FriendMessageFormType arguments: ["@security.context"] tags: - { name: form.type, alias: acme_friend_message } @@ -399,7 +399,7 @@ it with :ref:`dic-tags-form-type`. - + @@ -408,7 +408,7 @@ it with :ref:`dic-tags-form-type`. .. code-block:: php // app/config/config.php - $definition = new Definition('Acme\DemoBundle\Form\Type\FriendMessageFormType'); + $definition = new Definition('AppBundle\Form\Type\FriendMessageFormType'); $definition->addTag('form.type', array('alias' => 'acme_friend_message')); $container->setDefinition( 'acme.form.friend_message', @@ -459,8 +459,8 @@ will need the correct options in order for validation to pass. The meetup is passed as an entity field to the form. So we can access each sport like this:: - // src/Acme/DemoBundle/Form/Type/SportMeetupType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/SportMeetupType.php + namespace AppBundle\Form\Type; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; @@ -474,7 +474,7 @@ sport like this:: { $builder ->add('sport', 'entity', array( - 'class' => 'AcmeDemoBundle:Sport', + 'class' => 'AppBundle:Sport', 'empty_value' => '', )) ; @@ -491,7 +491,7 @@ sport like this:: $positions = null === $sport ? array() : $sport->getAvailablePositions(); $form->add('position', 'entity', array( - 'class' => 'AcmeDemoBundle:Position', + 'class' => 'AppBundle:Position', 'empty_value' => '', 'choices' => $positions, )); @@ -532,12 +532,12 @@ new field automatically and map it to the submitted client data. The type would now look like:: - // src/Acme/DemoBundle/Form/Type/SportMeetupType.php - namespace Acme\DemoBundle\Form\Type; + // src/AppBundle/Form/Type/SportMeetupType.php + namespace AppBundle\Form\Type; // ... use Symfony\Component\Form\FormInterface; - use Acme\DemoBundle\Entity\Sport; + use AppBundle\Entity\Sport; class SportMeetupType extends AbstractType { @@ -545,7 +545,7 @@ The type would now look like:: { $builder ->add('sport', 'entity', array( - 'class' => 'AcmeDemoBundle:Sport', + 'class' => 'AppBundle:Sport', 'empty_value' => '', )); ; @@ -554,7 +554,7 @@ The type would now look like:: $positions = null === $sport ? array() : $sport->getAvailablePositions(); $form->add('position', 'entity', array( - 'class' => 'AcmeDemoBundle:Position', + 'class' => 'AppBundle:Position', 'empty_value' => '', 'choices' => $positions, )); @@ -596,13 +596,13 @@ One piece that is still missing is the client-side updating of your form after the sport is selected. This should be handled by making an AJAX call back to your application. Assume that you have a sport meetup creation controller:: - // src/Acme/DemoBundle/Controller/MeetupController.php - namespace Acme\DemoBundle\Controller; + // src/AppBundle/Controller/MeetupController.php + namespace AppBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; - use Acme\DemoBundle\Entity\SportMeetup; - use Acme\DemoBundle\Form\Type\SportMeetupType; + use AppBundle\Entity\SportMeetup; + use AppBundle\Form\Type\SportMeetupType; // ... class MeetupController extends Controller @@ -617,7 +617,7 @@ your application. Assume that you have a sport meetup creation controller:: } return $this->render( - 'AcmeDemoBundle:Meetup:create.html.twig', + 'AppBundle:Meetup:create.html.twig', array('form' => $form->createView()) ); } @@ -632,7 +632,7 @@ field according to the current selection in the ``sport`` field: .. code-block:: html+jinja - {# src/Acme/DemoBundle/Resources/views/Meetup/create.html.twig #} + {# src/AppBundle/Resources/views/Meetup/create.html.twig #} {{ form_start(form) }} {{ form_row(form.sport) }} {#

    90hOW|DPi0y@eTuZ~^xY6QK zo`DYGa$`jMARh`iRmLb{*{Qq6C=f0bw#njHu7tN_szrIf;YI#>I1R2E6V zPYIvi1RLVvwD;S7-Y!9>|HmryK?AV}7%R;?&wEdCwv&$2_>c2jFN($R@mP9S@wnUs zyv{w%=Yh$?Lk)YI=u^Zam{G?UXMhV3I3u$`kxhXk5~751ee`jS1q2`&!v_jmbZf)Z zmo|GF{PoZ7v-H-$h^NtrGA19u3v5K$3KEVwJPBG0dR@{Vy4e=EV;&edR7Ksao{NPM zvd6_2hQOz*Cx9V>ApqS&OdX&IeuXd~P;)FRXjN2(rFg!q*%K1!u zV(1zs10Y}kd6E*iE`1I?ECBfCIsbOldrk2`E^AIKruTfB{dseND{|l`6z~g10M(4% zrGBU5(L`(S;~~F-!YScDf@lhaF`$O?a4dlVq8JuKfAn>OJigDi5KZ2?l^J+}nSW`N z-T7G(d!)_#>-7W?@d+%#)+}6j3k(=IKr6hDJlES15W~{X!tQ`Co)(Dm83`|-K1JK! z0a9)X`nh*p@Im=p!Y2l~%3E|ba>kH^ zLLfjqW~+VSc|!dmLI*jKn*T8q=HYWV61ivQ*>8n(fzaMzPwd`>m=*0aggtOuelqH(lxNFLlLU57Z@#L80{%afyG(fVzVnTT>&y2xAIC6= z-qgo*X4msdmXV$N^-!uqLF$t#6@%mn3syZjlcWa;baRYMwDF6UvgV*4m1zf-XJAO!K$ordOWY#9nkG}(IRpE zn1_b}0=^9TWm+}c#+#!VPeB|?B z5t}isfVU!RtAp>y*(}h;r?=R7oQnBVO-LG9vaqG+@~OP+`9-|-*R7XnCyhwwkS&>3 zjUvS?*(IR}J@ri$i+)u;L4bg(rR)gXkvhXGU$HjvDgEoX<6}!BGGuDAW<<1xv;rPzLi~otDpuNReP27*Zz47u|B1@RVqobx0a^_ ze6|UCZ0%NC&tYIvIgX`0(`6FoAU)1byTd^)9k-Oc%xv8nTy^J;g5vepQ*{&Fgzm?_ zUzOKUvm4+1b<`r0>dV>5rxDgR23Aq;C)wlPiWGWTxD;>zwg|udUJMb%om&w9{V2Cv zO5^F!VVn4fv)yO)Ncy(&Z)8-&Og%HVtbKcrp}Q=sq;fj95Lep7?$^7<$wDHw%W`{T z$PUNj(`AYf&FWBYCqIdx10E}y$%vTvt}q3Qc9p05k_s6GL@g(3%UDx!bYep#s*H%w ziYL2;nviPEHq-IDvNBjyee)dmMPD&p)ABJHE!)&`d131c0#;V;S2)wc?h#T|JgcP~nGZY(6E@VL_f*c$oaiNDMb8O3xZkbRTb z0I}smniathfFf`J*YOU!aufUOT20v*vt`PVDOp)n>ekv>T6Wm)0ULH#R93ohZNUaA zrDF!E5tNsWYo|+G2eQ2;3KjJ5;F2p!WfmdN2pRb}D8jHlFh-Q0ot=67PR`@WKAc0# zj?c*$^B|~CIax||8lRT{D;_TAQE_BUwV;VfY2ZhBrrD=pYR5=Fk7`8TH8Z9GA}umq z!Lnh?;b3Z3RxF{woQk0(Q%X%u%}!5C)0DB8ApFJXq9-7rOQpa~r}m_s!A91e*JiHA zq{hgkGLRqTDUre9N9w&WnIAU2shl&0I#tQ81nlHOWPzQYqgEs(*O$Xq&p$z-Lc&DY zoku~RE-hg_KkV6Y3aTx6=6V_%sx*EYTy(hP(`82XLKKA5dM+!0fX z^U9oAit{=xVm#}LiTupmi!KLa$l_!`3M|XqP_ol}AZA$n;Be+|u}JAU=Z-10ELm*I zFJSK#sS3A~T2M7(XKihm6}Zn@4;;13YEL~J(ET7G$!r<1`66#6#@{GOXH`E-MSaI{ z?Twu~aUHeO+G;oRW8|i+T!w}sJQmcT5?9C%L`9(JoC(OkbkTv?1E53VQ0!_}-=G7I zk+#^zVk7inU0h(R7x9TYjYq$|sy6PTO`!}OUO#+q>3lU~ z`7|_NWa`2ARL9gkW=5xZkne=auD|v^^PP#5j@Nqt}M%19%`kuTe}yPJj+2xEs3qtK0>=fcchA@Ls_NuEBqk7u}xl^EA* zX)=heTCg^*s9s@+bl1VoB9*&-%C74In{nZDT4zI z3XTFC2>o;E3PW>(#kUC+TvFQfv!)b;7Hvrebu5;Yx7!_CWR9n8z;+R|%mR}+xcEaZ zl6NyXcJ7i%)eW+`C28q2da4AA558uW#`Sr#jKcQIqiEg>#|vko`H#Tfb3e33n}ge$ z99mmOC@E)=fxzN6MVfmGR6`x`=6xr=ga}w{HUzfnvq1=@Z_3btnIyEm#aEU+r9iFk zhGu}oSVt@LHg31Q)DOFqpkWt46Q5aDs;Q`*$j++08_GL|UKzTZP&j zeW%mWlDl$gNl5_-pN$px@#(zuH(bZXgqvvXTF1BiSI$egLZussaqBF!#>dv2J;h!D zU|*Qg41_^;HP03u9iIGME5iH6@Bj_GO5-Mp=mic42A z=y6z{4D@%v2Zh$KMi@b&iI`;xPZBxRCUssp;|J`ksKal(2%EP+Whem$YlrY17CkD* zbL`X(dftEyZbof=_60fP`11jRW#jN5P$WQs zTfu?)Lt4Sz6e{?4QwZ|l1b3w?wZms=6~s`e5HKhpAn0%?+ynvcSQ{jSw4ZI*RePp< zEQKHe!x#dBLs8IWD5ML(arsvznKz4wpuKt$4;ApAuK;HXaEms_m~EQ;f9w4KL1rAl zU_Nl=2o(Qu4ncu~U;r2Xzxxq=J*=t!>jm)tN~+a4`PO_AGdbTsd1X<>?S^uqve{?H z)K?>XcafLu4t2Wc7&kskCb1*9sIT=#s5<8aZ0qYoRaUJw_Tstd?!NcRP^0cTk<2 zt%Q|DLn?WML^6noVu+3CG}@1EI14ZST&%jy_u7F@oau8VqgBG;vHE_TDg;DU0{|=w z0Kg)iD-@ntTZ=|88#%4vi4c&&sfrycV|K`1PW@To4rK{3v>MNI2Y>H4|D=SkeOmub z%Ko8WSz7U+QhYtn`|B4`>-Y@vA2Yqf--rC(}(4Umf zw%dW&e6;H2;ipA!zKU6nYcT4=&IKhZqKQ2~?B^s=eo8G6+4>>&sv7S@q zY1T<;YaSMHp}h%<-jZ?Ya8yhp)YLkCE6aRpjRZ4y(usSF;BVei-ofq)~-Ft}oS6=FkNR7RB{Y7{Rj98(0Nlnf(o4Z%Z{yuD2<=gdhxb)H3! z%T84pTpoSy-7u}PE?!y2Jd9No=SIJ z#eT=fZnGz(pTpfwP!_ORZ`JWwD#@ZQq*pwNQLu^kWm-BG5V0xG7n<-m=)0|nFzHLP zwoewaUCsvNmx)skDjm|UA;TUzu5|=-TgrLUY~ilUM}@z97X3;}IQ#tYIu#8=#J-E` z;Eic`){#v2GVguxEW)j};*dlI^~4mzF7r0NxO1`MICrN)nS4&ZxyCqK`=z=62*LU{ zRd=v#75BT+$W3zpC!h=I%n-minwy_DXx5xIZ5%hYw6Xbf@hBrBgHET-;r3VC#id!c z^a9%c;VglMsdV@mjcp5#3O=v#hCuL1MD0Tt-`*R7bpLDM-8K z=zMaqy@Eyq;K_u7Clq^T_Ox&SS&jli04eP0kHhJZ(H6|mky29mfl5F+3c6(H2DRb! zT%2_E;TN=j%5m~YP{iLCYTMa16ri)_N-Z@UDV(?b=u;OQ;qso6{SrtNpw>@K1z^!h&#IUiaXHvxjgcU7+1fKHAQL#?DM`k~ zFQ_(6c7SSmfhKE?F~%fPgn}yfo({k}FHq-txzQbUZw1w#UQeL7ot?_gA6X=e+bMT5 z68Li0d17(>w#0$f_O<_{JjICmAa+N)(j|KYM<2Nl9lV60H*g$zMRg=F|Hl>h!%4YF=cp1iqPG?)~UJ8i;pre z!dR^KvHm!{O=_Z^Bn%L1Ytk`QxWCG_&!ChGbaXvZ%e4HpIzp8D{e&{oF?$pu`p}h5 z$YF7K_p0KA{nfMZjekOj0=j(_d3Cyl6N~KiuRcefpoD7Bj$RFWOU30 zm+co8V`eSsUgwO%;mATf^aEWxXWogty+eIZGK5+U_vN(VF~quIq_Gikj7E=VgKp*T z7?ZbaxP_W@qZFVk(Da?u4Ab;yr?W&zrkX62>SzfrlLB5V)keq-pNf}n7g?fl~9p&p0b#{IM!ltPKOwLa<&h`8zNGct+bRSYz+7QcX+^f@_q}~Ye>$1iJ z)3zvkZ5KW%vH9od)a1k@nxN3iU|W4R8Od&tg=5+zYVzj~ZkHrSPrjHQlsdD+*^_J} z*AJHiM{hJUwYpfZ84!>ly`jldd#?#zK^S(E0SvEhuA_}%m<&FE*EwB{^%kjkQBG^G z?$grE=gi{$RADtxz+`>=ws0xFb&Sq5nb!UYlkVvn1gV#O`@G?lHf8QoOzW&+D!sS9 zLt>*bh@_hyb-_DvG-mT%nX0~gV0}Vpfp=*g>Y#f7)5547T<#nvCWBG={$hg$aL|L0 z#g*^Jb&e9|8-=)sCB2T9J)Q5HSI?HFC?hmI*XKVk3K~nQZs+U@*_Y9muxxU@8|V|W zA5nxy>BhuUgL=@0C!l+Gtw+@fSmP6%25z~1*63}HOA4lfs~(}@Z}8YCgP!dM&A3wP zoI4`R1@XPFu6WEJ_tKB^#sSXW$0D?A^F^hzGBFbcl+vJ*C58_|y zdN++H<_7iz;)5<|^JX2k_bm%c;c+-N$98F9>iW{EL%R7>G*^Ai?d1U}=4>!HINe*} zw1-rmFWinb)F+P1{T{Cp=gRuoeG-|;Wg2a68$D9pO1Iq_d!WS&M5cquux7VkVYbCE zIUoSUe4-2MA`@gPI#PER!Z%xH!IlmKi;9Xj&m3CF*{%{ZGS7UH_Z+l6O<*GkywUCG zV=F?;w4{>!hh?%WezCVvbh4qvr2gRqpHQgLti9vsI}>%+jY(sOBn(c#55%nE@JOPX=@N+XFlgkBA-yXR~A#yE5qTrVrP&HK1EMFXL6?xO_0@Eb!JG?uF! zqtzBzzcO={w2VsEo~|5`UqLToaOhg?zFrzaPEJB95+Xrg!14k@c2(4CxzTbSNlRaE zRZw<2%IkLI4Fjw{CC_Ql2Itw8_%-6B;YHL%e0?mveU&V zPe&9T4($jQf0l;S#e4Kd8(btYA)W;ZphUSf9HOHa^dJ)(5wHEF^_qx(Ijzw{Ye>VN zg1`UW)@UBb+n$NxenK(=+TdYc;}{y;o^jKDE3awsJLA$F;K4{`tvY{E~Sm21@iSM(~8;p0q|*JckdjYqEV6NRZB>rSd~nN-~941t|VgAiVn^r25`vZ!A91y2k|LWyA{Mt@0+lxz`o zd9e)LTPH-*b4AL1Vl>#iV_9D7VbNiyPXuG*u+NY3fOIXza=Njn~nR=la*pX{Q)Z z>D@^y8~5b&lq_rZ!0*yuqsXB->W8l{5ne+}qPD>r(aPV)BS8C0U>ztCNl_C<3g4g~~`%m|>EsCeuKaS5UAEmUr3P z`J!wTIK#Tu`3Wt9ssOkd^6}WjpoZ0nHpU_3kAMJPojMG7p(buweWih=E!)o!gkS!Q z@z-qzv)Ol zFI`Lh#qMSXYa*F3Gc*VSLc^vOUW?pJ|86#>Wx$T~A;fKLdVGREJ32XCcRz0fI+QeP zUR__;?q}xa<|>vC(c@DWwDl8t_qelDs2QU}Xww5%PVt z>d~nQ6h%+gkMt{+IJy@98B-?gId`}vlA6*z)|8kWehUxwf%ZlwoVd-eqZ-qJHgf_M zb)K)u)Zx@Onek9w12mg9FVptOh?jRmOTHV+P2s!Hj2!J@C!33J9R>Fxq}C>R1R_#W zgZ~{SeKsf60E0z`$-31?r`J;@{aQq4GX57ZQEM`;H5G?G#fh-6D(nh*7pmRHvH+j_ zOei?(qYzi%0HAPweQ6g`Cvct#N)Df4dOat;+Ke{yQMhNN{nJnch!0;ElZP@1b`l}u zo@_us!<~D}Mqwp0z@N%`;vrKZmZFdvR5^d2&N(Em?Kr|eocK{#7!aBLPwS|@t5E;l zfsL!!s5hwpUhyXf1Z;T`5k~=;gB0+prsJ&Kj+16r4aB;W^iXF>WJ7W6OxOnExwrQH z6))s>4O&9q8$yCM`Gfh!{Mc0D?$= zzdG=q>AgWJd;P6ERKb-}QF8$}>xAIzml(gj!M72OBt{wpDBPf+;xCo^?iZq5h$Sx( zq1?pKJ20s~b_MJs#%S2n$0ctQ`!+j6G*;{`7w2SHU-UM_$!l$y%aKT#{CQTOpy;kK zac!PK5M%Vmil@Vm1>e_gU$V>;~hUG-%<|jw~h~Jk{{zonyHdj-gEq zgqIi=Awx2q+qB8K8HyE-=uz}U0Vk269Dg1y?H!}&xqd5Hk5TMB7Cc_zR6fwYyt>)B z--;-0<-jfgyF$SsmY>y+5YnENs^?tWqp{;iMucxI(j?8tBgawqt(i174Ir>Im9tW* zh_62U#LprEE148ja5A5De2)xoiY<&Y0n#u2q z?&?7kco-TjZZUKdC$`U4+7GuD0t@iX36k+;UK8$_^{nHdpZ6##^M<%~yX0qI;_K8CeA@-DLYF}7=k zqiUqfcmY^W9SJIm%u*@phzSc?5jpW*+bkle=gnGk3^A#vI7KhcswnbR%uoZC9wqR-e1iZ zQMjFzAiGufa+3#(S28Tf_~f_6!BPB?Gzyy2Ln>{T zMB%aL?Ry6tlwaFSptiv1Q23PFKJ|~pz&4Iiuj4c^$K6?(&*|~+zC|Wrq(`IQU#8Bc zh-8>E)O7UIUP+|CSQB2=58!^@U$E01v-7}+2j$MONAX#CaPTAz86j~H(sA3j~n>x zOy<+&g3Aw?-U6v8sC>g-6)8;}x7%LIg<={mFllE6{E>>(QiAz#j@_Y}0s*Kzv0 zlUTtyp-H1O+tGkRR#=tS+d{lSD7Qor#ovvTM3z>m(90cCcUc=MM-@|h zwGo?p&rRVm94x6^ZNY(Cr+{dn@Gu}Ay!;45Y25*9pQ?d1?FD!(%=}Z@5k2K&heU9? zw2wHXXsZFvIwG}B34AN4EAicIsBFMHiBI!yR!{D*=M0V4UR!9}dZ!I9h9?~kPqOr0 zKVqNvo#+Y<(e#9`^<5M1NmKW^wbfh?*E~ipbiC^qda8&}4dJ~Kv@T+ym{NcP#|{l8 z%+jO1uefN+C`jwR@3d#ANG_;~?;YjSqL>{zFR`mxD0{2&Hr7+($%rRLWt^!*sSPca zwp7EcsRoQNT5A`Ob+3z|j&2ePii>v1%qR^{Oo{A~-=K%zLSkF*T|cRL=a7i^TWeS7 zSza`!HgPZ zOor~{%y`kYp^tF-QaAUXKbH1)zuRj@sPPmZn1%HscP#ON6T5KR}DEb;%) zRlE>@@DtxzTp;+rjMJPH=nxm`f2Y$%px@#QXqTrJowDp&**w)`9>Oi zO(Xkd~LzIw45Mi&~>0}8iY6Kt@3*r$JC^!=^nMfZhb{pA8&E7d8mWur@ z|6z7O*&=^WxgdLhKQhn*;s7gD6a)WSel`x;FU2kyS?i%#{ygLWCpDTQGx}Yb{`bJv zlKTPvb!5A1{tj%xRDqBmToA>-2KE5{m(g_^?k_|6a{6Rch)ohumXn(0B@HeNryeHu z^4)C|;f!o8ciW0d6IwNeEp3@_0)IN@F*WdTuDi;iuhagn4@dW?`O~V zi77GB1TsV$F~iLcVt>eAH))(lN?=bn3!BufxZ3Y<(Plf{w)!c82nM~HUSAEO(*N#P zZHkWG@!VYf?<6 zeK=Q)&Y7i*#!HS|*RYx3(kWOEX=XV*S!h#=#B8SRY-u+7l7?)jUBZ7;@{<1|a+M2d zY@>4}Xo;%aPXrA6UkUdqyU!rfCiHaMU-Ny6Q!OW<_m3;ey(jZ!3e{{gH8l0~;wMxs z{d5N@Yd#H^--e^;d*gMIU$Y6+Sf_1hnC`Fkm0P$yH}XYC+F)|}-19@HcQiG$^pY`K zovv#6_M|s_6&<>S-in_pAO!(mLM)Z#kWEtl1?m?+o) zTBScmOfa$z|Ek5~PleHk!M-G3%*>kGN~eRzn!p!Oa@S--!{u|U&f3Y$Bs!`3)qUrt zWLnS0E1P0Ev~#WT(_Sy%cOi)0c(hut{igW$v5ssBOvdA;Rb$$)F&6SGFRfGpQ)1TC zT{quQNF;!yW~O$n&KQy|NW60Sak6$?*4OA*{8OZQ1Mx_s?&G%a`}p^YRu*7S(tv`0 zaT8q*WIob_JEjf)a%(>R(&;D~n5HHNt2A09VLz^8jP7^E=JWg@xteg)HzDhq>&0O8 z%b-7;l5XDbjU>`ohR*!{Fsb83eHAZ6Iq&NuPqCpfduk*x`%5Bez*q?FbhjdQzoZ-* z`gACDV&VC~e6w?pry@jx1o40k4DyYOsJ&{(t_NW{BoY>@*F>% zV#_tV3ufep`StmLfc~T=r|&OPnPaJxoLZTSE`+;69S;sDs8CpprU&$K>*w>^{z5+= zS4l$_E$(NF=uZ2D!ed({FLur+kfqgB+AVkEgNb23z7rbJ%YXI*09+YMzYu!bc`Gsw zlP*tB>oluhns2JRySJ>_TAQ1*xm+yQ8ccYR4w(~*+iw%+r+~#mLTbLI@0=GyZJ zxM?NYWxzqWZh20DThU@uv*+h49&FD{3mD59GI_a%nx(x=+Lbm3$82*1FATg{4%HH?)_exa1 zUxuZ^s#}?O7oMBaZ^p>rz<_y0`B9Ou(Du_ciy@)TjYUUQ){=%T2gJCF@HiAO|7Y^r;6Q_Q+;Q_FXn=tP`L9a*_%55A z%Pi?z!9cYKeLcE?dXze7{%Nv5v41PbH3SU(2{rSN07Pw$A9j)Q7WM=TQ+k|4@*!Vi zqCt1YGD0rZ5KWjk?6T$_Nr-KxDlKjlLabC>^zK%+j%U0yvi1h>0Z8MZz&Q}nF#oY{ z^9T@PgV4LV|gFnRW@+K5&2fdQL!81k#3NVl!#P0A&?{2{P(`|7J2R#5c zxDzn&Q?3h1IsBhx0S@>AAOM4J18S20g9N+$07$UgCMPKMzju1d2S6)N_$`7B{|8^- z{=t{ncakQQ|JkGee?Jmkj)C(mVa^$k_;P^%&2dw@hd=fMYAcinSbgkrl<(`7zouk& zAi&!#*ft0RC`R;t)hNz@vbjIMW=@g7-U+d9y-Y+zguQi2Hx=|Gn0)LhM+gKQT3V=j6*d$BXI1v62VX)Y29L}hk|G-62H}*|g1#{d^Jf=7iq+TTL(r+kDR2bA@ zyJXM}@OSY#t6^Hh;_7}y)HR!^F*F^T$--s|<<5E3eALLuM~gh_HmIe%8fAeap}Igp z%4%;)LPkC2@yqW`w`2QbW+BI|&p%QV)b$@wcX|17BA-7DYy6y1|5{rtU*~lcgU|UoLxMD8-3VL6akny zn?ZAf==&*64%Z!&Pb>$evl^Y-=cPpI&D^Ol`1n~%f6e8NzE--fG*Cz5{hj{%4xIm5 zGb>61>$+e*t_BijXl1EZK<80oWK3K?la?KGK-IsOWp?*FOJ0WGP#I6?%ScgxyfO(97jl@U9a|Dw9w-V zFo{eI-+8{L=+3RMX(arBG^Pi#GU>)kC3mY1GJ~&WP3z-S+k5-@ z7|%GV`8I;r>-Y2Fv8Lhvj{O(!G*wNO+hiFK$V`t(e+g3M`S0gHT1hgaRd*9IadPdx zOX2;n+`1kER37*FS~GYIZI?4(ze&;lYAD=+$9cV&9YEQ1ZlV6Q*x*e8ks!c^X#4gG z)=Yz4F`!y;S6YMj?eKgo(fPUK_2MZ$qx)`P>gzjy#Vi;{c`y3WVL9`*wu7g92VmbIE-9KD@jhqqXg6qVT$Zj*7Z`&faRR**%u=TD@4k z-doieS<9y+ymVKN-NTB_H`^0Ms;pbWTr#KiIZV*izFHQ|1; z-Ugwsdu6PChl-jSoACwZXuZwjE^T8*y9LCr=__RT=&k4G(t!{9)Q?vKk5WXI#(WH!C??Xeain7d?!*Db$bj^2#32Kgt zrH#h7nwBsvwceS|_-kqT8AJZ8ZxUiktwghsAw;?{ArKM)kc_`(nZn#D4sZ`RbIm7R zcJ0&L^3G?=M1$q!GxT$fM&oOIul*_*u~1Gkc8*^Ay}Z!3(=2^`V5G;f$^orax5+dP zXCJfv;flUpJynlxG~hqmZrM#WFUa{C9cp*a}y06^4F8rvLSu_7a@TXNKgE?fcvzUJ@LY;{w4WXSivz z(DE?zwKE+@MvjVlL-*5UYIL8B4k&)XoH3dl(VgSl*=Th)9G^lxGOG;Beg{No&h!qa z@tHbbYKD^{=abkE6Fcn(pZiu9=Q(^1r6;L!IQOjI?Iz(VGP_$E4DU#Ty0#0b?KTuO zx;vIq1n-TAc}n9pg5PH_jeb7i-}$pTkg>Fcbx2sRK2Nkx>Om%1J>=*+e0vd+w-IzC z`49e6^u(Z26*&5gy@kjhbU%`pmsPz-Ff4m@GBPuLe&PSJFnNEvIv9y@autYa&eeGJ zx#LOcZCTFMg8<<}T36rLZM=hqwsB}YiJJe-0Dt?8h6cXF5q$)cyAEC{mkak*C2f4F zQmGn)$sC%igg72zgH#8(w6apG)A_V+Rcpf@_nTS`4wnlh7yRJSv(xSRZ$0#1*4zC2 zGQuX@mGuKHxz5Hzek2Y;V2===p&cjfHy#fKEVM%EJ(ls8!BRQka%l{{et~DcZ;5yw z_ym4*1pDu2h};C%ev>{*rY!zZy(Ci(0y_E&$p7ltBRVBe{13nZyo|idyinB(3eg7% zT-*EAfoy;g1S(Fz3R)IYzBi-69MQ+RqMal$xu+>k01bJAX@QpG{Qop_)?ZP5QNvdp z3CS6hkZ#m3-92;-B_J@gl$4}McQ+z1bjlD?f;7^Ck^>{)(A^;^{a*E5>-iI&=dSz9 zUF)2)&%S4$z0dxfdoE`>NGT8~6np10HvtaSzj$Jx6bMIzv1O~W{;^C5E)$f4pt}qVoDXWuRUls0&vUzuUQ5`cE8UP7DKN8F-F0S^lXJqKUmCo}8jbeT0>Qbk^JeRkrrK*0w)pGJhjHnZqti!8ZP4tv>~9A9d1EAt2;DqtR%aE_ zD!AbBN~Wtf+EhGCQ)Ldw;vPJOmvjPYZP6f}69z{FPgu^1(`n04FT5ESkzB+oInrdXi)}AR9scuO1eW25q@wg*4R`ykaLTAzQ zJ{8_6uR;c7C+j_M+6lTRo@kUse&|Y-@5EZSIOs}mu@#Rr*1%#h&#o8j;@j@Il79*t z(xi}uFxFQV5_%SBgF8qV%&5kkF6`CbsoR#-FjPcuq~R)1Q|qzh1hn3i2zS;xo%Riy zYH%Di*RJ{Hi;dvdG6p#?=bgZ)WbU@`5uh>73^Z-a_Ku?p?)Bab7!tZRXGf)WkC341 z=ZZa(YL0RV@(H2A`G8_K&838!LiT)g$8-s{w{Yxt3F+;WosNQ^M;&cK&l92Eq=`O{ z(HPtcWa(PuQ-NHeFzKKBCYBrKs33jpXWk0uH-zaiF>;;3S8UtHTSbv=6fnw_iNXL@~@tKN40>%3&`PNGz4KOm5~U#c{c zflY}BhA!*ZyOhKu&5sf-=%AF~g~#E>Jt}-XT@V}`j2AQa2xl|)AW0S?7d#+mcRCuE z@f-BGeojj>F*o3HB78w^%xS`!=xMNB`7McoUAoL91-=-C)YAaIPnnKiOgGkQyJwU- z+CazgPoAjoSvTX9t!rGbmUrJ+O>|LJ?d<-dlW2D8^U3w)LFGC}Q5NqGQ*#BM!d$gg zC2#B5h`b`j>3L&c(S!NIvBTOGU$q-Q_$K17qf;z$>K6lJe*TcBN}YbN?~v|AKCw_} zJK!-~_ygKV(x__Ao5gL56|tB{l|j`(-)Wx;SfkXe7IKa4?+tly z@?}5yEp1ziOxC`?Kbt7k?diJL=|L&SD9gwG^yo(|fn zReHj15d7Ls_QLSq)>}bMdnOE>tmQ+Zs9&7}F|0uHhRM&X?QJ}ho2KVmCafh zOwKqPsA`jDAh14s_;CNt_@ZCVkyH8A(5I$`c;BOR7BNXZF{ix6sT@6WiRu!si5UaE z^_>!~7Gz?Ss2|iVQKO{~UkTnGA6cuQ51~J-3L_4#&`GhI^2) zD;!Scsv~4>=4szBJIE14uFQ9H zot&^`Be#mmVfUhrljOJW|eR$M!u(3!vZ&xrfYOwXkt2YZ@JJr-8nwJAx`^(Q7Fvw5b}k5j0FOO_+2;In!{ zx^k((Jy_It(=iv@?3KbdY1Sikz~Ul{g}#N}5;g~o#(n*z+_GFv(*;d_ZbJ+up)4V2 zzr~&F5i^>`c)yE(qA-BmAIcR#UgZ6#wHXoJ);U3NCyqZZX?QRqym|Ckih0!3#=vVf zG1i;O`Cwsg=wgn&uY69y(DiLEB;8iO%1G=@-NB>R-kc(wZ+s5ZXe1ua#FCj0#N3(w z^?S#hPk}(GGAgjX5JZHzZ+Egv?*D}R?UV>I2M1NYwfl=k?3kxHCgSu|=;FVDWYaus z1PE^-}pY>Ris2O53=m*UCHO52)_vO zw!qiL(e>zbG)ON~8mBFJqeOY$77m|y7NbW;EzuOf9$oQyNLMOX+wc2XuJKK_e^5KD z**m_kkWJU1BrzXRG4xv^YL<@1PSPz9CxT>R?@8>r^mvp zq0WT4S+9=0(l)2z<^A3Bm5!Ts2h6dg|FOd5z=0;o$>)3L<+@7Bhbx_wIr-8x?a?9X z`j>q>itBwqg~%7@`i(AfU_*nOL8Ylh^0r@gjnfup@H)vaZZ*lzu(@Wf%YwlyJTT&kx%F3(@Gz2^;Psf9BCrDx7E2{ z(fD?4lqKPj{`m_HG29|FzMJ&@Be&GR z(H^jMXcnkNtkdq^*ImA?{#eZM6k(%l(;NK5&FNglUVD7-+DQ;rfzUhI=}gz)$kY^Y zsc*L6SG6zi^G~QA9VgT%>U~jX*y5$G>mPPLd%(H>YvdDmUxvrwxy5^vBw$(L5o;Rk zVi-M$)W%!jmG4Z66Qb2zHXwZ z*D6^1XvsE~_SIwB1Y}9c<)TJBy!z1lY_Rs=e4pFrq`%^5-K&kGDJWc_+{W#i$JfKx z#Dq5frLZI(O*g=y-#Z)+|c&85h_5c39@_`1-hplmK$i z^9DCFGc#s{tH;S5k;JrPljDV*J4Y+cHHh;R&rb(V$qV^r<;-r)_4Mfj!5yx+xVYbQ zAohe+`;P1EhEe-cTI#F5=^r>>xIcP!d^^c)b(wFn(t!7J`5qw0w7YX|fxkH7=g2$~ zgnV7LI!0Wdififm9O1M9x#O(oa-0@8dAFmF?oXzudg|TQ_|$ndZ{eo^V<9%%Zn=StQU(M85*_2(Xl|mfSGR%4S-}=hPVEwAgkQ7G6Zm9?-|jB{Szo ziV7VMbZj)$CHU(JpI=!qX1}v1w9B{GAS7i9dPvey$tFLN{mO|XS$c{!TVYQX-lsxb_rgvgWFfnB(U`5cHj~wU%>SxYJ_7y zueROWn~vB-6yai-hyLZN`gJ|@P!SqM>Ga|C29m6K@jq@Hi1YosZRVY?+{okCnWT&2 z8!T9FUu!Q3TNAArSxfb+kVt>r^^{zq|4doyXVV{RZ4E(Hg%p}5N0@Jw(9$k4sYQ)$ zoE=|uQFi0Fcl#NtmbYu&j6L=zplENQJR#v>#IfCi^+c|dqN)`0r5FRmyU~iX4x0G0 zz8(lFJa>V+J67xn0P$=Trzh^kiZFkxLEUBQVx>efiPVXZ5iMC*Q<=wvR5IYbYnlkh zz4cU+NFsW)OF6En!Mtx!RxJMgy$_~n&*6s-1V_LDR@AM41lO`zzi#GRBN1}{pWuII zPo~HWzgNolIq?`o)agbZza+SNzlItI*{S#fKspQq^@$ncdRl%=ZlK zDp-EKFt^TmAJ-wwSK#=vg!IwXqHfUxDz{E(Ha)OPP0t*rwpc!A#-;MZmDVh{H2xvW z;r?UOjF&l41?$2kl|2`&w9yF0%1l3Du#0`C(1pQI!O3mnE$)HWhxgd(Utk zO&N8ijm!IDa?zq2?yY70dhHqB>g?jx$$#ctGLF z`B2i&f(44-a^m4sD9O?-6p+S=l4hS??zeM!)Fte7?lZSupH4fI5-L;>{z9S55cd^P z1Yg06(iMc4fx`Xl)U}yO>Wh*JgJ!QzSSaa$gp4^*gsh%Z^rP#zZ3ICh9vR*eCgUuu zQbGV03u?~jI9Tj9t&Qqrx z$>izP6pE99^P6AQ+?EpkE{^X?T#Z@2Z1_(+dOik+I*ls}zHvy}bF7rNjk}QG-54;k zB}&aEOnPH9+!1t7!iT`+3x*$d+!j zbw_kLL)8ar^aqJeLK5TpXELaBbKx7pWBf!EE}i*5E2ydx`6csV*&yDKJA#ZHq28IU z9=0=^S*z)lsFg#RQ`gR@plxCnxVH&Xk*mcYt$bn0hH21z5w@q85F)s^BzhEXDt|Erjc&Df?JbIo}vIUJs z?@TuUXBNLSH8qu%DvKJz;btB9OlB6sv2}*RqyTVpzIPYgG&5sFL7RZ{35QBR1@Otu zZ}&|!HJvfGNFb!BiY|s=**qzTDG_TV)4=I-^Ta0UumVYplP4P)x@Mp3{B%25=p>QR zl1_qxRlzc7bQeJ*b|dJ|=sO5ZxoO&qJp4?$DoZFL$CIM*JcUEm$%tk!7d|B&NGV{X zL!$a_K;6zLkKJ`YQr7jy63HGeiVGfDNO?av1|ZcNPq9AjZp>oKM8?UlXgcr@(cpP) zW`FObE)65XkmaPa>%y$e1Jx7y_gKVOJ_ml|U;28?AXtJ&VuQE>+`b~wI^ zX~`okpRUA@y`w|%|AoP)z}}PsWRs+va{W{U4~^nAg-cOSR8#b9UM&@q+$X9|fjr0O z+k!8={4*8$!$aES>uGjGs`CR%7(!pENRx&O7m@|w)E1G6;_}JLEHaXMb*HdlldRJmh&?Z~Rmr1H z+3kmEf6b8Ctt}1PT}}-u98`Ufmi`O*9W>=^oMTKIt|{{xb_QL6+S- z_DK@Qi14XqMJH{{E4KncnvyU_#_xGey?0Y7Mhsy4X2}$_z2AqstcFd(_5SWu4 z5}>;JD9q+#$}Te7_v)Ld&)TYaWTNuCvP+Tm@{<=HniEf=5vpeIJMP@UVpo=v)?u(; z;{}qv8EI%a%~((zygY9&$-k6l5J;2g2MO>#IGq$c0zcyyL6#JMI~Y{Q0_A{BYxnj1 zBlgscP!4xo=I(z~zK8<|378yFR`tIvEsy{KLzbR0{NJ{~{|!|;ZtunBG{=3)_kVr| OxRmA9PBkHnl0HDFU1`l9XMwYaWY7&wP)5)f=GhEf|Qg=i3&=whyjRTN?@c)zXb~e zNhtg&oG{#Zc3g69wtZe^v}YQQ-Cr;bqB&o%8-_c8^mKlK0X|4dfgmAa5t>+LkLz7* z5Bp_6NcBKm1)x?TumARG93PAI{kU62fu7275__Ls_^^KDpe+i=Ft`8_I;dTV!a5-M z-tht|AVTot7uBs@iQ8BiO&+WB<- z!XZQ4{JxVQbo~nO`N3!<+pMjYRjcJEdS8k8nIP`v2l3YJp~G$^g?Z$6#>9bm)geZ9 zl-?!B@w@?i{Dfm!g4Vf$t*iCb3(=sTI!|8OR9<71+dvt#(i!68R(pl+xj?uKdoo-@Ml{X`F!JQ*#xQO#JNs;S zlDMCK(~1%Dcs)jWZxEV#_D8{QD|+;wcbey-zSS_hhuej498Sm|1) z`1!nmkcNE@!CIq)w*KDqYk4C7EFBX>2>JB5lPHrn(KLOO$Wj0&!v4W8ic%0ND@X*! z2nQYl8I-rD_?3wM2eb(KBGHfU71HDRePKlC2N*6p?oDuxs(HKi`L?IyYYt>XcbV_! z^E(C~C?}4nYB=oD22n%g6w_+zZSxa4>>{ACN}~J?O_wh*jt(Nr-{! zf02u;6rh<4+~^yUi^2mw(}#!&nxF?U8Gu#`-`ooiCa4yQn;(=)Boj)29ArrNOB`=L zAo_Q5Ea)M$o1mW@7%CtuXg-&w6p<>(Jhx-ks}$utBsi~SmZlNbE6AHT7Lo}J&mWJ- zH&&Q}s1cD2(qa%Q5Q7*DDM0+UA>loeQD{b}u`u~Qr#>uWfSRBsF>pNUnD3!zI7w10 zmpF4F$vg^Hc%)b{@k6Z9sJkIfJqD+LreHO(Pc8$61}erMT>O$?3YUd){Btl=uOeg7JN>& z49LvX^3_V%jN{4V3Fr*|gnHw?w}RGyhJq%6CKdq^K@wpeq7I))bp7M~hxU)jIOaId zxahdxxH1(_5>OI6njckmdFVOcl0~_+xrVu!IrBNtx#PLUx%c@mY?>&bD15j$vasUJ zW2h-y+W6!Ym5}AEXBsPTD}*Z^ykMEw>CovEx~OaPEy;E#cWie~cg^>NN0G;vQxnWs zSP)oI*l*Z0SUA`NSbnTEtR_reOnQc8=5GDZ>A~^BvBSQ^amNg^$TM!H9_I_h}VKnJB^$l@Xtix~RBl zg3%SxVNj`2JZNRes(;~A>X7qM_YvAD5zFU`>^G3+6c!vBjp>YeQMyt3ltGtCm|>e4 zoC%+?oz*ZAGhH)@Fcs7v*1y+7)mPQ)9tt1v99EBY#g!znC&|X$#P2A|DM2cis;a3R zDmp6DsCZThml#$jRxg%Bsw}I>$a$B33W*5*(kc=(l2+DQl5J9PIt$%JiXlN0J6?4C z=LALnez(?n_4%)JX6#LDuqdV|g!ImI;VGQ-2#tP?ZVmP(J_|{+5Oa1jpB4Ez&Z339 zh(*>}=_Ri+u>z9<)zTN49I+kA9vPo4fo;O&+yO)@Q+?O9+a{!rxDNTKx%RsJJIVuV z6ebkvgx?7WVj|JO_*Tp!sn{9D88UbW{OGQgL$Ik>X+{~+88GP~srNIM9 zG>6rnHRd$h)Rr`?)GahkG-THy*MwIc*A&*d)|OYjTNauo>NDz68u^WWHvNYBM)H=B zE=(_62kahNx9>I&9%Sxi#$*o2!DKUe=58kUFK<3agXV*F=TF0(n;fc~wtl4|jYDQw zi&_s^UtDutt8S`>rVrxo*N3EwC6DBnq?;t4#HXaIHm}sT1iNIvl*dHN=FMQ}3hi8U z?|e;umVd&3UVYjCc0sg2uL1L5@8He=JFqNRY(;>{S)NwVXN)Jm_T$jzX)-^?q zl0OG|t}U1%pk7C!~TVEm9a#K6M6%ElSW~Rhw=xHk64D=NSAMnd%hKJvqs3DrsT$wM9Jip~qG?gB(mvARQqR-1 zYfq|p=DmI>?=PvYu+%K+n)GBlf0=~Ij%6qW%pWuP)py!Uxf#Cw4zd!lDN-<+f=k9R zV}C9OsW2|5t>~~owJv7*_jX{>>#5psjGb7QV(W#LYEmwQh1GLSF!7{f%6 zz$4@CIg7v0y@s?|w|=(7xpFkm`ZoAIX4)e1GJ&6v-Cgo7oAYS?)cjKR#CxfF*7GBE zras{ibTUPMsLKL3ionvPQTn9Iz(e&}=k@CfD=1B@p}m2gPt7Ye(;-Lm z4fEN~h0n~-lgK8{q0U&s_-wEK`+m0MAm>=sU)5P9;V1vQ;!xgDa$|`<(fjec@uRI3r72=-Y6oT8VuO7xhYON} z1ClHd**gn7);8?+5p352EmMf2w`~9sh!nNQAzT;TwjHF8jiEWnHIExow~z@sMnl*| z@4haqm7)_623kgv2HM6P1Sq0c27BU6|Gh5SZt^%0X-dXt?1gT#%+d_;Y$k)7Lel~x zi*19m6Au)U8z$Fj+cjE=8PJ+1Txr;~*a(|c*{oITS=OJs-(4RX9%bGir_qG;M39cF zp|PILb>dX0*?zdGK^U7~Tojq)yokclOO%gde2;%v?0#$mMUxf3+K_+jRQw&V1|H~{Q^=P0%SwXLl$(wqtv3uV;G~q zFmpV=Zl{`BpilxOt|+_B9v}BROm1wZwbG+rcR5O)V*}0pUzMYjkCxukR ze4l@A_OL<36cgaE?2xigHTQR8SigL(uI_C#t##DyxY+`Y=%P9bwLt<%dGATe!SI6X zMyL+dZcRU-$H2}Q)al(YhET@o%JI*E(~;IW+u`25L^a5Ojg`@Hmi3xpF4;NSIr!}h z++1Cs-a6kFz^tL*VXVSyL-COukYp1}6EEX5V*5s+$Iiw(C^OdJJ7 zch*XlPU3x0LusX}vG&vU)b-W%)^o+AyCNu6S&YmvnNHVdErrjvTf-jIezy&n;j(kH z`7=)pEGAnlekOkH^w!NBC;kp9a&~q~`onOCZV5kqA{!*5Ihnitr8tYL6${DxKPVF* z<9Jk$R7Ezms&PtQHQ#E?S{php7shv0i|?l!d@i`x*sK)n;oAll`M#>PQOQHgV;@3R zya@O_ogbygc4i)b#hguyRo4>|uMe{wWD`6T-{;?%bB=g>p7YIf_l!PvduJbSPml&F z4pIkOzb*PRT3dopUdYLdoplHQUql&nkL`%DtunxLIrUZw7J9?Fvw*dm^BvZ&kH z+#TIX-q#>hf*XSiMBRp;$tuYf#3ZG8W5RGXnJw7(jq)rcj5&>+w$?{PmyLIx)3mdB z=LCn5IJ$Yfrzmd&gPD^a04ZcEsQq^A5-y1!V0YCcZnCQMziiBJ&~i$;e|g(|KLgwQ zE&5pqg7!HPmF4mM4c_xdN)3G*y)F$;Fe%v5XEES3c-bf0Q~t$H^!QlEOdOUvxYmG> za6b)Bj#G47e3?zqVN1=*ImM~HM?5q%6!|>)DM^$P)$m&X+^}=Va%_D3rp-8sK_P%P ziRO%aFbVq%SRHC9HiqHLHpAWY?m2eYjm^ zeQN2s6KI}nZfDcw?ep#TIUpI+yaCG1)*jz6m4+#o)b1YtQtO zwLUr1b^_y)Vxe+V{p78^q5t}~-Q$SiyQi&afETy`s5fZ>Xtf1IuK-+ti&u)e4}B6G zC^Z38uL>6N;s|JN5j^*N%I6v3RCQVc&Lp!_0C}1vY8Q3^Fz62}2cgkNs1~TF0*DWB z7$BqoEf0anqla`4WaTdp;FxT%osO zq`NDAl(_FX;1sNx>ETbFq?+UsRTKXc2vpEps@ELb#L#f-5r;ho$*64yai<(+J$9jR zH($;4ULS97d2a|U@K@7L=&a(tczhZu5<9EiH@LhwN~P2}StjDz!wVVINXVyv>7cA& zvKW4(I(@oZ%Nx<$$QA`IndQR-K_|y9H2wxB1EGGwd zC;K=D-M6S$zw7vZ)>GsW;2`E4W?wRL;)9ymr>O?bD{PgceXQxpspH0Eu6n2YzUB!O zcq9k)_+r%;Xp+^mA2)z{k|r}?Es#4G;~38S_alRl9)tr(+AN+a*n6~IzzVjmFxx(o zKGwFAW9;8i`$Npm`_jCAxu#N17VdT6>An$$M@()hBx4BbP`Li)Jq8X7EpTfXn@IYh zAHq7~HlaTf*iu}-i=|Zl$cW-gcMI={Gm4kVuE`YrSrV=Kvju-r0owR%YmF@!lSgkD zYkX=#$^=||F^N4%pr)WUq&~7H(-2W7+snw`-!`p0b~QNA%RHIUXEH83_Vb5OcUDAP z%~Y{flT>w4Q%XBi{x=^nL0yv5%FPX2UWD%#oe>?Hj`AK!y?OfiF}CoO zT9ww7XqNnzl@}c-$^d3T#&Cm}x|G$P!xRn-yd-^v2OEVZcZViCrdZ2C7P)JdqSi7b zytQoDJ-)I|<+gfvp*JOPX>XyPrML#Bw5xoffRaI@^Q3s@N!eE5C3Ak5Y{F)|;GZ$@ zsYQgdF)ZS=fE1GYHT&$tx1$9#9;*A!KW+iGV@LDk*L7Md+S75B|&SvGJY zFO3<=noKv;jne#5?ljAM>u%>^dM_0vP+rJiH`%)Q46berkGsm3rNbQ0Ppcc}V}oWb zFT)Mv4hj$P5#bZV)c8I{)muOJM;VtYpU=JUKihA*cVhjU+_aM`62Mk^fQL|wUbtCM zbV6itXybtCeny{|T+!3MOc|1W`a;NgfO^CytV5_>sSXS8I_Q=^(TJm?u) zsTye}YKFKkmNfP0IEBsc8gdKY3quIIfH#3* z0snzxLVJ0_^6w=)?)vTZz_4^*!0y}FN0d#LkM?fGZt`q4q)MVpq^g!ozudgYd7)#p zRKR8EW-&-bezbzGz2Bdj^XBx@_KIP&DIzN-JcBKzE?`h>AwYrsK~n*CaY_trYK11M znzbhPdgBJv+6h;F%m{~<6TH6x#&xrp)RW;;`-Q=N$6*dO1==GU=`SftPuBYVLI+sQ z%f+$_D14JaH!ogg;PwhgNJJg3FJEkqIYxO*C(^q*Qdjo zY1z9Y!1d|lQ^UbB+?(l9H$il+_rl@z`uTFv17rc`PUTK$S@=(5hq4t{cK`x{LHW-G z43wUU2?Rt4Bq1!Qg`y{ftwsmC>ZpALAUGM0W;4afeHV=P{U>D9bf?)0RAsh?0SI^QtH-84LS7w z3*Qj4|NnCbzQFi&22B#9w!Qxa5W)fkEa1P;8U_aC(Hb^Sh~4x3FM#oj;r`c%)<{qY z*Vf1-GNS)lG4St2DI5me|H2jv0DEhTRU!TRf2RfEn#%S+W^(K@!MnE8{QAEG3MB0b zNZdp_Iyh7?8C6tcHLav=Y0J5(>n4>=XFgnfPBNh$s>-b`-sm4KxR_tC4?DyCX9&0) zX{un*q-@Y>JP&K@>LimV)s40WXBU^2K>zm&?K2oHIHppk-x;D8ks^xx?N?5}Tefa_ zFKT%t9>!P}ty-3<+;$tbX0Dq<f1VJkS0u`@y`M zNjD2_={>IP^(sr&$wj*d>UbdRqu5EujmF$KkFJy!6MQoiOy#dpx#9gYX42ktqW z^Bk^i!Z;s-d<4AB@Bi#1B|G?2SsiY7XyA@peX;42EN&P5xBH`HPTO6ee<_n+VIgS+ zNeLnh0>X$dFz9TJE`Ou>()ju38s$b17Pq4Zd3EIi59(v(8`Wvnd(xmvUjEK1c=^`U z%k<2QwU#~Tkn=F3kwT<&%%1b9y{4z z9JL=n(YSp6g^Cg!PGneVoN1++0|egLPVysD2;R3Ln$#Xhpi?N zNPKE!6ZJFgV+|YXh3#3t$2l1}_3LH&HWqN4EZ#_F`4Qm;u!-}P!HCm?G_TUE(uYSz zM$BPodYW}Rsyydj4rAUqJCa<6`DXsP+Vn$zD=eU#7jWr60MB-MEFNF*2fDgUu|$8T z+Y6J&m7%)r7`xGQt{+(+3>S=WB!*z*daHxcW}C2W+L2KdJI}TwNYJh{Pr@>F7eU zshR9}y<{otmdIrFE6VZ)2?jvq*&PfA89V)af2lQ^BI$U)83hIf?ahf1H1(2PNM1H7WcXh75y= z!So}_NDvE}F?AQISbmDU;)0pQMWAIO!bTVw?KsZ7aLGIO5p$?#utt#7$RXuv^AWeI z@YY3JhKy3Qje$AuLwJaM8hJTe?7#`9oy{J!v%1-P3%HhwDI@lJML2RnBAcNh)=~9Q z^~9oa`@CMZOn@UyvfUtBw%sp8^gC|R#MTqYWt)}W7fNKs0wCaMh(wdvxsBtU7cX1W z=p=&BACzL{|C-EnJmq~V@_QL~{#ie}Xn+RrRWK}l&dy|u@m;)NCwr56V8iF!f7KX~ z_Kd62Xr8+kw79@$C)NBqwbv$&!J8sks=C_*@q_# zK|_%g34_S!@jvVJZ0O)VZ-s!KJQH(rL(!%;3a@Ny&sONtKVGgaq<1Uul}UfkRauFB zsbfA>#QH1e3&Y)(3Mc)o*XNMa!Ic9R%>W4y1qOg)f)8)G>_m;=drUH2(n$V66|m{N z3pP5Q$%zq-!afQ>aBjVfP2<~YR1RgN_dNdO6+7^Fxo%RrC6~b!>klJgVnqxIOkkQy z@YLwH&DK4BZLp_FNJyYIYaQqaE`gLzJ1Mz){;l1^1zCKu6)kPlecN{q>+O39e`>>y))}or{(~@ebX3O=j z6JJGopoqMgg& zE72TvwsxNyL*J2`reo~v zrzPcO@k}c7j(430WDJauf2Wh)Lh;io`Ui^lmJ5*BLHum1AA&|;M}e{{`uUqlo;RT! zKO`g)x0)0jeQyZ_m|p1BRV{b~B&1y`Y+XaWU!Wl&pS%=^t(KH$J;pQc{qQyDN$rm< zgg=NyfqDa(se`(S&@>=&RW2nPS))H zq>*WqXbSJelEaQn>V0<=X%#u9;r$eA1OgL89XL)9DOu>;U_8nN6m`Hry_Q~*{tCpSp zVH}%5kDD%&sTdT~&)0((wXXwW%k55O0a>#4<_f=StC?J}4hPe1FjH)Zv%(1)m>^M7 z%icbN-t2&0SxvvD4fDTS$qnK&jzS~wRseZJi!OVOCei31p;Cg^)MH70tWxHiA~m~3{s z@qRuoeO|5mUc~vcqNm$<=O`=4>4ZF7f5+w$MSy}F)S=nLPo2#e^c44(wlr6 zE8B*e00%Zbc&uL2MAXc1s?W{?OJ7WLU3iE@ga!5Tn-ajpn!YtYrG7R(j{@$=|^s=nh8=+dZoN>Iro>mm3YfzI3 z=&+;1!}~94dZMi~TlOkuPDp<3vAmn;`c0d7_*t;PqD=w&y}zP$+%}9*(Y$B`zQdg# z4Br{pZJl0JI>|W$XmvKjsh{YvWarHBQ^0lfj`?%395pSJ@Db;eWJ zG&-HFo8v1zy0VYfP3ZLeN>9#LH@sk;J=m!YwdibDqkT!Lu*0DiHiQ4@KxGy;Dn2SY zDzf>4bZ=g8BrPS-->kao(mdkZ*?0XcJ2(go|81Y2uOk%``tDca;!Za3EPMA{pW`G` zl5{vq`L-g}Es|sXCE8VAG`@)1yGz;Al@5a89T)ERmm3>1flR^C8(?3|UHL7|F3O#o zWKd`>$MB#6V{MXN@Sshq4(ly0^^4g`HJ(2j+;}&*=P@RtVrbMnpmv7a1s6uEErg&= zo)KT_MKI!4qdw>y0;7E#2%9|Cou>HLwbP?=K>2Kb!O#dFqfHz@!WWUf0ebSRcxGm1 z%Z-@8BYOiO5q&u*ub$%*t~PtduR%>Ctxz!rjwQiEuhVs@tgHNB^u^zBPlg2*dG!ZC z7^UB#zh0}4c^1oWB0dN{NJOJ?&4AUl6$=D}HxM_Xy6}>;t*Pk9Mg|pzifk0v{?T&^ z6Zlg|jS>w=zSeDj69-g)<6)*u|2C~8L)%E#>!+zknEk}7W|O%e8h^@Z!ygQeM*Q1b zS|+rIJ1m;CIRC_en*!OJ>eiQQ%{uR|cTWMaZDfYh;IQ17;6Zl^ct+eF`P)1VgzPRx z&(~Xh;kdvU(O|tvln>hr0)pXQ_YB5j2h3s{PVGn=Jk5v@Ek@ztQ;Tr4Z7?c6fGgkF z5oR>y8NvSfm%-ik!NMGEJI=orC9jJ@m1Y8L5SeA}jksnaE3qNjs{3G;-h%t(sIjy? zfilkAtmFp7gGcBYFA8|8oCm^mF@%0i9KX93Z5uPvR(PSHkm?!28+C;8DGF#`Q;sU;C8aw(cDhyCc$u62*S21h z5B`y>f||Yn#hyL@W3LB{H|4z|obAFZPF>ry?7Qiu{jPw?<6y&?&3^YFIm6GA$mw<- zfbad3M6SaKKrjtdSo*98uTJ|w0e4H`kem()2cQA~8Uqw=^U6J(&3T*>9VUnJoL?sE z1C1{mF)t@5rZF&r&-e);a9qZ>SIkAfy?YH>avF-`-dl9S<_dy$v-vmmvxM{k1m(bU z?u0mv)|u-106b_OrGq}ub~K>})h>vzQ8>4ScCG@fnvw zY)aR-rbHE{_+zf{2qiobK0FUd5RajCGN8xl_{}8X_?8IJ`fJJMOjx_}NftAQTei7~ z@TGbNfFiu)=w43$<7@D2It}Fb14g*m%6Dz(bCEL;S(Qw zUoSgNqSfWVbub9aVIZ*Fl9x`R>=)7(k!*f|8$HT+o`5+Qaa}N&YmPc2eCsl-C@f4r zu@E#LF=zxb?liaj(LyQT`U3${jlH+X7Tc%1I@i}{F^2bZM)c4nw!xh$=6^Ru=Qdku zsef5%@C+H+0C0+fKu`uWBQ%q<7OH;dRYPI#fdWjgqYRm`&iRS*aR--u*QmqGvEEcA zEKT|gS9Ld)=rqlmr{B63IN(AUIJC=`O8iwuuwXfjS1ay1v*6YAw@09GjKJjpfu8Jm z%Bdu&e(AhJF8BL>P1=v}LX?%Q+s_2!0|9fDwkg+F|8s5tlofeWx10ILGEC^%B21;XB5hDzbrMbB+g^bq4~sE^H6}AI!A!PQR$pIS z1qi~r274o#ErX0#vF+ckGxpB)?3PJ%=@Ld0Pt4&Sv?y>7g<7|B-q1?LN7ZM<&X$sF zm)fm~R_#&XwrT{`ljuHXEE~Od7=ry^B!h=0f@v7pCcHKVk0P0IpcsXQe@ur%u8lJn zFCjXbV%Fc^UkifA)!aqMYtXm`Je_|Wpc9j(Iux(G9jlP-twTct6^8KD;Cvu zw#r+>q!APZ#P&26mreof2=^78eI{9(BS$*6U@}InW#B$|wM#6br6OpKIm_3^WM3kqnN&7ZGf`n50rPEYz2e4SZi zz$ol;-yn5EwrbKf6YRDdih>S5`>L%oP{5-o6YK3}Ai)a-vE z=GpFcU7$^whH1{&ZSQkEo9tLddmaim)1uJdwhoB(eK1&?1P!U<(FeqbqY0{m-3#Vi z^4-3UkQr_+0z36+1cm;UbQt0|xGC^N{|$dx zIAyM6*U5Ms}P;<6Ouv8=2o;Bf9vN+fTQ+We1_gVqnsOPkP)9UZCBY-wgy zb~KiMBoXPeQq6n1QO2WKhHW?FN{17`MIamkk4J+jJ{5AUQD?!YWIDV2PnXc3l4VsI9Xky8jZXrjX*DWa&3xK7;-R1zXxHduzxA9{XBd@;87J}4b7tXZ{Wl)*F2%MzW;<+B1Dlx2W!0kmZLn7aR|0oqB&#-RXrh zb3x~JzcHJl@gv~&MtSb}6{1TkJy4Cmfs~R+bmSns;A#zstgVf1;EPSM#j}e(RqG1p@Q~9>$U`HHiZVAL)01*z^4NufLZ6n4eX~26U=%#cjJW|pid;(wOj0zmie9c~@yNjo`pi!3)K==#Iek={&N12joeDt%|5QOU5V~(*7>9`?+K!s!JLc4#H zYoH65=p7_VtMxEo{nONmlzQV{PTe6(4!ml zDX~6|+n#G#uievvYV1sxU~egEp>Sje{K{v5(e$AIb+cSL z^s<^S*7lC9))~}UuGKTq-k#+6L~{@C`2KXKSvTUzWTN5vz4=TGzO%yI9;N8&*iHk) z%5dxIryIK;=NZ5W27=)RzP~;g%@<3VaF!J0?5cb}RBW_zygC%4>AzDSE@djUF=HBW zWdj{;{OM4*4PKA^P9N;F^@I%?ZK80(f#!?Vkj`G6i3;ZO(w^iRdhUiia>fH7*MLw3^Gld2hS;X2nm8 zFnYH&!AdVvW^e7(@JC^DXxw#>pXx;qLp|D(m|=q6W}`)mL*E1ru@LM2Vbyv&ElUs@ z)dURl-LcA)x`Pkx)cg5Fm|nm}tUm%s?6@d|t5P#I;~OI`1t>g606aJWHfzork=6Pw zC|RUrOR@Gp9igU1(uiKIwznN%e73_p+bmL+U*1gMF>$@`!;|B;qT@Ebe%8yC zu1?QVNdh9W9`eMw8m>M@VT0>(;XHbHP79D;9VDUu5;Tn`2;6mEFd&WsC%$?+4B1iOC*@PPkFh~Kvo4GSh>zuD5ZE@-JMoA%Be5wl zp^^UWPod};;3;ooP3?zo_WEWGj5<`PKf*qZAUKzI%GbqTtvL_yvWdq5Ho6*yQg1-o z)L6E|s( z!h}!c1fW`$R#|{A>U3XvG+JZ__wYbkejq0^csw1ArSOCslfMLO4SA1P72F=sV9v@5 zM?GJ`YO!;m)by}%{uus=R)U##MYN?jB?Q`gl#*FjA9RK5Gn(4i|94{1+I2Bz`FvgI zhFUM-S2;J2c$NJE!&Bhf;cxCkTks%avcVILuDs!3@diBOC8cn-m2NGn2DV-nE9iv! z+F;XZL`OkEo>BbfAqR(Y`@Qv+rV#=wPrG=zj=EJ-7L^#ZVMn1tLbXn(QvA zaVGj=X75b>eA2JJnnAcbQS^=U9OU}A5#sE4cR=z`COSN_EPBeRnR+2nP~GMR2Yf&D zykf5$MGx?<#q2L9_zXU{bcZJIRWUdmPZA?w9k5JAVlmG){$>7r`ZjCjK9i4}Y&(lL z-@ByT?mLJsW$!oA4gq+MEQt>K)3r(^hYotR6PS$==X-(7UH(ne4X=*6hFK5ikR3|K zV3wyJ2ef&_-!}quWM(F1Afj)k{Z(Cg7pEl#)AJ-sU+gqtzuPFo`2a1hcY@CSEYLs- zhUG>-nro&!EqM8)H)VhsHAU8YX!4aKtFfp%F<$xQOs$J@qryYMyHeivd=m}WY){j8 z@IOs>W||7L8h85CKXUz|LjQ@>OG%*9BzS1Xk|}h+Vw-VV2PtBC`G0lpUsz}~Q-U#e z-blWA&WK>K9XW&H2n?)xs&sq0oxZ`8A2z>wajZ{fK2Retd_qQREjwQfsURGWWMHG_ zRz>Ux1(F3fSuQhiEBH`i_}D0Y%te~;!LY&b7wsv4)|xCZ>Sc@%${kkJ=M+yHWv{|% z%W!UI0LFiRUz3<3&I85KrWXj3sFST10!Lr+hZ}^%{C%n3;z+Yaw%X#}uWp30r&Zs` zuXmk$Ns}FQKDz*!t5<|kU&(tguP<^MbN+O-!L}E@6#O&O9GZ#k8zdhVtMP8{np(tMs=mHCS=R@(-0jwff?!aChGmkJXgKIp|$?R3^oyz zt{`1mPq4yuKE!bC_Xi{2>wCtpT`!vM?(Vk^HAsO+LdIoneSKoGO?Kdd=p6_hG8M!z zXr#*lYe*h2a##h!`~lg;JEuMR4n}N3kSUn=N+s{@1JdW=YmSWAutO{m4!Th3MuY^z zDq+7HI>^(<-Jt8RFQm9`0^Pu?y8Xz=s}N^1?pvc6f0t$sk=@7FjaOV?o?Vv~%OK9| zu*5bHCc~|88#%W5WP5pwrTiPCl8NI9mAwL4lYHl^^-}eQ;wGo;UgbUuBOn6N1No-) zq_L_!fjUk-)tYl+=JgN+0(6FWXZhJaVMsF5Z;oe2@AUh&$Xt$n>f=(`;FuQ2xT=mE zAcST+VTVUC!b_c9(7O)u8JhtUfs?V`XX$ujqJcHat3!`BWBqORx3u#8=;1s8lC>Nx z9Gr;lE{~HKC3PAQg!pI`&h#m1dm;A6x=)z*k)gb)*?A`LE_#HX$+Z%&V^q;Kj<;}g zHfR`!JfE+&tl5up0av%;ze`T=vwo?FEO)$NM{~abc;Le+Ifv?nlcO0jdAgROMe021 zIYatf5W9aSw|Gb9-D_#jP_xW=G2xm_(WI}@vVar1|5Z>0-t2vWg#7?pIPBJgx%er` z#$hG$!q)hI6?9G(Jj4V5lt0htc2a1wjQeZ-f`DIrBlptGGVQ8D?VVy|%hhjv8V7>4 zc>qDmTRFtHDU5mpii_s>E1oj{Pp*vA;K0=ZHKz#;+;L6M@S;1l^-M*&UnL+1)NU+E z-!R!Xj8qrslb$yPFz9LLH#y*Xn&qZUy#@Iey@fU$ax%Dj0HMAinGtK7wMOif$CG>k zj9B_!IE0J)Yx08T12nhk4Y0pPyS>nQ&8=i`2|o4KH&>KQ{TNrGAKZR!ZaZ1%kNt3P zR&J(+zG<8U9Q_QN5j&v-5}*7zZreX)^ltn9u|OFZ1R%wb_E?mATZU$XnN7Wp(kAhl zW4*S8zvo>#X?lJJ-m?deqxf|QX&far25IFW3Yo?}hSA96)Xn%80}12*>jg-9Kt68x@H5E)YDUxw{Yia;|3RaN?sbER zWepoeoce5#^d;8;zy9La%PjPG(C$U(4;ZrfvwfJcR)_4Pzjv0xUULo@`#o`JD0v{* z`*+2)G-H8=wo7BeEz|!P77HG=AJ|8IW}%m4GTnM^i*`M@QI_IRF~tohDr7><3YiJl z7Vn>Cw09b84UUKVMoL~nC=8ZWAt?UTGLgTT>@`*^O-cD2=>>$=U6mHNfv2 zJmxF6P}Cv13rPOU~iue z&T$zMCU0B&O~K)mBRf7LV z4=AQH9N_?3y};14CJCvI!$sH#=nFTn3=Zk0IACyr6AsjC(g)-Xyt?<8xm>}V3n1~ zAu*|48AmyVe`z&%VJu%I1SBHSpi2;-SCLFi)HmJg9`W)fZhUxw@k?a5W$2512?Yv! zXSF3BBe*@ao(lt$bjiX)+q}6sT?|^XJLH9TncS`dxazTvU&Fyz+zGMgR+c`jDi1ii zXGAvl8+W>?#RxpSKRrHR`lW~W+Ky87pbD%bi#cjfa#@{5*n|r}W5XfvXvS@T4WVF6 z{c-+NN7sQb9S+EX=O%z==;7kyNXrarXBu~b3cF3EUckLfX_*<7tPAFmb$+E)BlZSC z6@L4B<=DnNU&m(f!=~;**<}{jap3LwD!F`4&^5{S@iS_mhh48GEWWPu!H2aq+3pv~dzHUgB zJtn5>!DVAIBn#eJtLhYwp!^b}J1(zgQe1<=G!(P!zzZI%)POpF8SK$XEZ-?|BNL zh*0_-3ZrvKfn3WjnNdKkr4Y+Hfo+PUh;;=kb_`RMHOfEf`e+x389{R874g!nrSKRowCo)*r!cI#->|#TB!A@hD57)Uf3KlivjgNGJ zEx(PURcQeR7*(Z>!_HO1A_};3X343Uq`W=H)Hac-%_OJ zyH2laZYM-PgM~O66HtsnyxDLEuZPYs64?p^ay9bS=NXoL91+d3yG^}hOJ846-486I z^mJW=An%(-Veqn}0&U!hoyl4HO4Y59&VSiSuSLH)>Na{ZV8azW_-ibylU~=KjKB%z z(!*-Pu3_gt06;{qtu-QFFYQvYHgq-qDE!gyUY6R3eFnb-P{qUEmWp3z zG{OPK<3OLMgYe*U%m6DDF zNSN>%q?TeJ~d6a;Ib#-nb(Uf@Ke-vt$*{4jcIl-=V4SxgDG&I4z0&MsT;la$kcUClI~?WcEgFIEaCq#Wpqe>$V#-vmXb>sf zcS>5XT0JX~cG}D6%Zt-I^VtbUfAdq;y^eY@t2n;>>|ecc(%y-q@AGK{6$v5y{RkW& zER~6v5#7)qP9d`2L7;J*;u+3Bu#tah`Z*O%yx4YLlCDQMxb1nahkA6G z@=S*&_oI`k1&TMD((lWT#S!h(2WSAoW@}3fVj_Ua*6-!i-rX3JDxR)l@`KFiN-1O1 zWT8XSRXUX6;9^k#KOch$GD%HewJ4X_fAYNlOBW8KpC6;oen4V$sejd#cui9k=~P2l%h0g8#Q z9T-6vRSZV`PLmn}yC7E3q?oHO$OD1T@R7(I`qM*-gwA}K&&p~$blZ#(Sgc1_)_eQ# zgtFUrFf)PS>N>#%dm$4~kEaN&1PBuIAbvOz#`+2g$381$JuotovB~)2LHj1zit92W z@5aXf#1PJrjY$eo4-E4!V0V&^E%|#X3_G@T{SQV+6I=2 z6gSZ&gkw{a`I^9G&qhgCD8a0Lsw@X84A;b-CdeCxauqm9#aOLe*@Sh`90s^j)Cq~i zn=>J>qaQbLNHZ2vUXSfyK!|KkyE-2<3o{-DPB!su_qPQGY%Ow+n6>DqIsc-HnUil# zjnb!X*cV+n3?gn<$Qh@?@G31^8_1qjuV-9g8;3O;==+-B;{*{`flyT?Dp=^4gaH8o zPPmy2#uxa6?2i3Mw`&Dr5pnv!uw}pkIOh@aDEb9Q5R90 zwGHpp(-=pNbkFw#2`us4tnq)kY@^W^l9}(f`Pm~FuGp&*@X2ui0yQyxb}+#Wa=pkl z?kH4+n9R|tF7A_C7sYQGd9Dt?uE(8~7jKTC8BH0qNz?m9UhZ@Yi|ni7QW#a-=G?W5 z6BUd=3~2DwEVzDeU3)IN-$K32d~oLHi{HJKBZ;oYRg>DR zOI{6;A|2eU1Y4?qwIz{vtt^9i&|bL-AXt3JF}F6-YvX%L0>|ItJRpBGmjn}0lwEMl z@JZ|b*`M~>)QuwVn1c3ia*ijj=mHyeDG@p$xr%eMu@XN_cN)6&IC))FWAO@vu9{r0 z@7F`dnDCEndFUb68#3m$6>U!=oC%QCFp!jQ`ezMp?o@l819HTz{fVDm zARWAqsPF%+Oxvg~2Q=ufRH;uxbU<}Nb->%naPit$oXa~|0o}FN7>s_g+ z9CjN8SF(~H*Q$m$9Fn)sPH~P+{)YDVNOpvW4+!4V1}DPt!aUsnt52w`_Wq8^ z(r1EM4kd;zRi2XM3&Et@HuLq-{6V|1^Y7GVtLDauYh1x60+RU%3>s$;)*c_82Ld(0 z3kDH44vWCX=&usj}p~dez3(;abfqY_W$R!-3b8|5qXH;!!zJHLy)^F1Bl#rW^wN-Xbv#+ zGI)G{G&Uc-GcS5v1ZX-WTr#TOLr6MgLHXS`9%66s7$oVMo9ER0Nw^LQPNB;6s`-%K zz9c*iKumR9J*n;1V^=*uot=zDPmrnS=pjro&eNF+b}cO=sFS%sXVhg2#EMiFz=2Gx zv70mc3DM<3W3zDy287$f;H{&uvj~_=hiRQ(P zUJxA*Uq|`rUl2@3Io&wCLpKH)8-X~UN__YqTpJ^#@F7{~N(qeWUY81dQSuh)1{i@R z@leFziwb=#M!t0vZB1YE`b}%Xl(wiL#S0}eEC_ZD!fcOO*GrY)W8C)u$0^OZ zz2IARh)ZrIp64&q$971yJ->r_vK4X86@Q%s;~O?^w5U%?jrvICn69WI z*EozsWOo{reCyS3fm;;hf3UC2ugqS)o!9y*^(+Ye78!bU91ko>T~T|*iTGkHco-Euz;>6|xh^qcw4(D#ba zWjuf?Qfj}RP|v@0OTCAzzl=TZgf-W<0Nd+RW)i$cy_~xR4S8gc6*j-N|Hf8+ zdwU$5aKmPm-#}E*+BT}{RVVjBjbsUI9Y5`Gogl(?GS4vuv>T+A{oU)VyrD~y^DH)-cxyYRynf*-dop~f6tj^?))Yj(XLNp3S%B(&wkE@s#stvAVz7<0-SxQJkC z{K9JM3ssLF3ub(>qvW@I`Ew))v0=gUnIHlPG@Kb*k1hum&P#eG0 zK>0#|IHF!R(Ki!5xzj<;cF_Nd^=5x+T^hk7dWO0oo^ue+OagzF#1MqzYoFAM<<0p7 zhE3!#jvx;yfme)wA}b&( zrR5n2g}|9#|)Dy}yQpBv4!k)oS37&tw4AU>w{go*3+>h^u%XFY>zQ3FVo)f}hlW7hI6GjZJZ` zdQNV;5)emGjA%zUS3u%@$UWz#h{M*CGrm&pCQrgaGQNhYO1PsiCC_7o$M;N+asf`1 z;sB?hSVTfY3W&|9fG@Q+6BE)m?23nYOChh$d$ zgUTJX{yR~p)RDYY6DE1kyhT07T9FYoH?Oj6fbBWYo(l@GJPnyBsTkf zD!tTCIk`n8fRy5&PR7V(AP4phgfO|IjhWQt8VA@_@dI5&-02cC!5SOkSijI$ux&kw zb+Gn_kge}e`*Ou*R`o|%tnVN{o$^Ux6EwYmbR;|vVJI^GVvExe6>cQxLJtkMrwbuC z5j!yc1n{;?XEB(&UjQG3x7o(~x}_1eJapIon$rp03^YtK7BeE5s0*!|Zt?BneZXBg z`$v;znT-Q>;BV7nq(+AVb(9>UcIXbxGUot>PrKkBf8$b&12YXsu&%kXArkivQS(I zeNkP#SjCPu`>Z9pg;FHq`LU`Wql--kR7mGX7TYrgO5c0ChDCn#SH}Y)AUHj#PLz7J z8b_Z?`U;nqKPrKj_-4W3ra75`-$*D{OR>{o%-3*Sc6tS=YvkrxP8+H4fW#AW2QE(U zhhFbP)@c)CQAtkjCE8<05L2`)(@6=3xcPT}xDs`pfB1vugbR&w7TZkMJWzwwIQi~n zu<{W8$MorR4$4E0=7z=-pww@WCG$9$!IOJlTuq4G5;6=_YdQA>Q~Ch+0$T93b>+S> zoaUHjot6|g7M2Nm2o?~IfEJ~|)W05h#xPJWdI&u<{sBbD>Cp1)fAd)`lyJIg?@RTL zrJDO7h<#Qd?Mu3gEbIGLCzGKFmNgbmzW;(J$8@7zUqGWPaF2%_MkT$aRL!obVRX{` z)4HY?i_;%~$da2!9m3O+8zT`P42GiJ(0TDUl=u8RxY_#3ioJ-axY&kNxr}q}cH%bI zY~)zMew_n70O>9*u6N#Nzv=tc#7@3ZZDK>@zM5iCP*BI;>V|iN%7tGGQLJtR6ELLR zrMC%o<=q|xlPOljtaE#Re27xsEG~{bZ6&}ZJJ;{}DWP`AqSVWxk$Wew3R$-!LrX3}#7J3_ocxs& zoiW{oHp8FU&>;h$ACwU6gXrm8UTvEg>NNN7e$zIRkDV;*2kad-e4;nE0S;o6DJ+*_6FM9hE5tt@xg$4t zmArK2+0?VLQ@X=R4Cew+i211C*MAe0!O1*sbC=OCOSY4{%tU^fyqob$T=8h98KIbT z@}-&X#B>)P9dtby>JIYIKsP!%j=uigYJ9xhS;a?l43rtZxg4%cBqW^fch>wBR~Jd# zE=PgR+UhI48}8_PG}OMXw;0n=vLZ)>s!fA%K1L+AGuO0m)?&qKV(#pSKo{hWmK%Ba zv!mBLV`x?=rWe))%}K#l5=ItJ>inkxW+Jb=Q#VnDyJI}aZ`HLAz6_4&H^sMCwG(*V zvJ@#SZ0otz$n}aJE8*2+HwMMkcwuR0+=A)sJ)|*B2)ULtg2#|!ODKP1?>B`%6b#%e znN#v9BKP7mcYnV^Q;^U{QW5q@kgt$⪻C;swK0ZqKR)x;F7qVLD(}W_?u9%dg-*2 zx%;w-v;rbt3Kf8b5Fz5?&`@Hq-=V{7Dl1+J%VJ^`te2GU#JYX!L8iAacA$Z~2z9>y zH-*JwyV!Dk$U57GWOp{*vG0V3Et9BO#kg7oM3x?+uPFkk{W+S`JTi$n;`_`Q?X~!B z=)3i;jb(bM#Bov9oj_7%)RZn>Wa8C>dA((P{FtbsCRBEjJUhbfwZ>4&z3CHRE@AkY ztY>G4F!mM+Ixt1XU8Y4;6oF4dvaCp)UzsC0G@C*K)j3pNA9_VBb}`;eoC>SjVLezL zoNN#RTak%cYeMqCy6}4kV~+t|>_?-)+0rJ_MkBidg(_{ZyJUA`s-M$^-goUo3zyr5 ziAS`cr;tdl!qJe`k%8CK=o{GVW2&5%Z(Q}N^ z;w*EORJe|tqx_J*qrDR)Opo7-IfPQ6kdz}`?L&e>Xkt_gg8f$tIE6hh?_`|rCrlO^;;+S*-U_V^L(`(Pm$H(_IwjUM- z1w7gJe|t23@nmrFesqcx_N=x-o;yd@Q<2H1u%W1k&zR|*V5*l>Bx7nzt(0DqK3~Hx zmQD}KMUV}0(_n9lkH+~Bf1rLDJ>Yl>+!vVl#tqnU0?D4+HmZ6orPAd$>$`5KgKbix zawUKgcBd&T!T;6!JE9<~9tuSd&mt+TJrp_lkHz0iFjo+t510VTgupq%!gH-cB^qs6 zK1G;FcA=j;(Z`=}U)wh)c6_!aDwK1as6_r3HuovW1%)9*{hLsP@S?P&YNQ~na5WYl zrk3J1+2IW{i-`A+eSyoORJovrKOhOWMyCOpDRP2! zGm(Jd6%HwTjmQG;GN0Ls{~TuRhvgoGW1t3PV|OWG-Ya?YygR!kX_wz0rPzJ0Cz1}| z_e(ewlzEbP5X7cjr~)Xmae@#DL4b#YhY>h4YJno+gu+|)S?40jC6@dCeX_nA6kFAC zmz{;>(rW2{wds?GT^_UAj$t^?)kG7CTUEva1TFXEF5C)P`1FL%3Go-n%oD!^r58$o z!|uR)@F9%{LvG=~V%TX`eXo|6;>J&$QX`;d0S%i6V98PwZD`VRFbxt zECUu84ItkO;yemIbL8vSGE$_VgAhqbW$Y&urRuOMf4H%Kd-j0_PPP-X*ScvaZr4OE zg8Z4$?Luhdk>k)1A+g{JW{ zmw~N_9e3J*j*EOsFg!kGP2|GYfm=0>kWjqYoB5J!}V=Y{dcmdJ->~V2V_K5aL*RPa`za{03f4? z47gB<4NVM1Qi{qsaOZ^~ViNscz}QeCfqm1 z47zJyE~7+$a#`6l`=dDl^KAIn+wR+IFROoI{bV-{k}4x2XOC+@I%Tt9Djil{7FgWspWoyypNkN{J68afqC%I0( zCDcCAGWlw_-RRRS@JJ{a3sdAWE-fk%HX&uV=ILN{6B_ba{&0RAvvq=F8H>qpdm)i| zcSVVa&#BD(4Pj2xyZT?wfP{BlP>6?f)E*m44Fy$R$o_Jc{?;ILMQ)7B0j3-YqPXV$ zSjICC@womOOA5>Iam9L27As>7IZk^KiJnX)CdH&16mR;!d7?Y9!6p9CXI8RK41|gp$mI6R=$dE{Ulub-| zD5ALmiKV111E)~=4#csQ%t$3xv27^QwF7FtwBkCLYMn>8PE#qDo@-H$m8te4jmH$@ z{5Kk{i$|nv?)ZV$9T^?nM)g<~RG{*Jm}qg31Xr3J$>XECHXg|RGq1%19b3@#Fu)9_ z-jQZ`3x&-o@s_W2JmFmU7nM)AsBj12G!*M>vz6q`xtZYS+=K#&F!3LG%XerTLWLPQLNibq03K}0VxV#-1h`!UCbpfV;Mf+INbf=yhWk#8oE zHG#FxppY;yV&L2*A`b#cvMEee@x@Dlfml-kAj_Ko9$+A20cIgl$BYNJf%-_bh`H-C}*kX~3k z$3}6xT15ecwddC8t+X%l*uvW(5I3RmBKF(q&*Wwhw7s&OhYNrQ)%C^$LEtZ7L8QT@ zH!hoDSHF@1ru~*?n^bxgfrEbu;VHRrAc?cfL?F!jr~Ps1@DqP)1@QoT+t&}r1^M~KjMqGh!ucQIP#uxL^0}~kpWWLwNHGWihv&4?EvV4daXCfMd5~465 z2b0&;@Lv8RoEjYB2Z+yzTSapz^bwYw@K@_^KjY;)TWe=JLX%W|ETB0%A0BZ|!^Nd>k*rN` zWZE{$Qk#5+Kh<)P^|CGKB++9?jmFn4@4x##%Mog?^SVHL%sz-`NNy0jgXjpZJ#%Xo z^mhqVZZ_q*-lq`5Y5di2yXV5+LC8kqVJqwJ=|En73?v>;Z2OG!TYcq0Pg4Pq1vBd6 z7wgl+QDy#?-qG_SieOc-8q!AOL_488Q1md#T4M@LWOoSNx)pM(lJyRvN90?LZ%ufC z0>=X(X-!K~WdxZU*pC{SvFHTi6;#Z)i;HmqjQY;8qZXOeZFfivO*Um;K!6Gd0>%zifW` z@6j#4cLwJZrp^T0MQKGPn+aq21Ity1C%x#I4sByeK&-(~W`plBW)Y{jM5Efe54GRANq zu^;BjcM>DC9ybA0D)Ro~aG8tc$LqOpQQF#kA^=TtUs5DWgXX1NEeaS6&&iP7bqW7C zdjp=OC>f6)T-UT*{9TykwBp?PRXXfwg`3!`2Sp4o`~$tfbzM}u$-DK!r-+Xpr}l%E z^oYcZ;2Uo+axuc+uqzYK(A<@O@78n${%Gh|2`yMn;#v~|)CY8w^)tOtjMpsaYDxf^PmKAN(%hkSxUv0`yNlmFRV zczH!p_h}G*c+bhGP&*eT7TYh6$2s@%()snoTbKM3=ukWgj6|MG2xkEu@}eB=WpkfvpbQ?a-t zO@m3=bt1BpkU&Tg{4~%LoE+(hx^2}evO-W*=5ahV#^k+5ZX=;y#9S(+^E~d1`{=f# zT%y6#S`dH!GK6-6${lza3cQ>gUif(=-e$M#g9VI%6D0!wUalKf^uPRw6v_#bx@#T$>)&Bv@jVmM!%>Xe17U)Gn6ydbKy2OD#r7O=9de z`aBCBSw@W`c0~e(Piw++s?tyX1%{ybM$RT=M?ho|weFPl?dmsqkf`jG}5tvMCbQ?71FZNyxy zdT7c^PCCqo_k>Vu=cOpv+p)+|>DosmrwlN5KXe3LtazJH`&pHiINx|51xXUpAxoS? zbHBu6-rg;1(xFR|8zEeN9hlM666gvG#!+Js7%!X*YGhu3BA28lC@wHz#p*PHW^!+x znCNgZq0j15ofrZ_Z6=9Z9=W^(i9P`j%Kgr8o8j$)X-KfTMj?Khg`&D!;a7X!?Jmmh&7gqpSR9_} zMYqn6VdUNGp$8%aVSvf>km;*U)`ge%D&(w=WQk_|=jykFNBanPeoU_hNXzlr1RVj} zO-zu90q@^OVaVGnz7;^K`E*shP%s4zn<0CiQFOEUqV+zzwyI5#E#gazC74PH?JIOU zz}o8>*}i(ao7ZSMejwta*2Q4O$UF`h483!lS5OcPUHV?0>_rX!NgNE|-f74kD<-p! zQ|6TPUrbFsx>xJ(J}-{A)-GlG&h$RE1*-6|Wr6Lyl#`j#8zxDzj(X8d< zoX(RMp$G*z<$_@aoIEZzt+!-i=-08V7TQ3~zkIXBJbm=2MTV7?(_p#l9fa3?U&7Ra z_sV@1;!B#r;@vG~ESA=X^nagfB0=h>U+~Orav)S|Pm9A|<>mUX2I5K=H@hF3bfpCf z<4#X(9du79QU*pD?89siE{lSoQ0U_CEL0T-=#)EM^PiLopL1H=<+2^p;Y(69JBhK; zGQV^oez{^K=jXtDWrG8&8E_g_s*%1#1KkkLOwuARe{7-KLx4lWwR|I9W`^?u`U-R? zi<+QR-z+Bj9dg+F641pyI;(tkp8W$_FIMbury4NR7(%7pznS+{()HI_oYMJfXpPkX!wf{do$gWZYUfU^xr!4=&2YeyT`=6SVdzlK`E)yfve{ZP+Oh67-+yxGRVrosLQZk?;b{(g!2ITp8dys z9HFFSeS*9$51ABd1;#XM3?mN;J($cb}$G?l} z3tdYrx3i}?@1D}rAS1Ks$}$usKXgO?n=C5%Om3tl(|}CkJ8@5S%GJb#tIK!G7q>{% z@@L-oQR||^n;RFSw(gJU#8LyOG|?#7oqM4;uzw|73Lg8zI48 z@3iBI7#n66_mF8X0{*SC;zw@ECpwfB*^8bR1gs=aL3sfqi%Ew!6v{UC)TOEUHS4%# zTe-eAap_I*q zw+r|rEODNdTiXa{@}L(~ zbELt0HLzisMLAgC6X98TYGOy&P;rRpEVf)CEnQjQ$v$2)qTJ1cuJJ-yWT)OS*XyYS zsEx%A6-7EB=Eq+JLBCCeb)gW?;Cq;jj$(X+qN3ms)XvmmY(Na^Sy|AbK-Gi07@a_% z@2SI=g6#?YdppC2-0K4hV1?&1ZrYs8!pqC64`@o9W%BCSU1_d_cq*4iU#BirXCB^BU{Hv{Kowr6pZ1^6pzAW5Pkr$FS z6p~UbDBDMN{Z&GO9bPOWEnHMI2&{g66DDz`ath}b^SG`CuB!%)mFaG!ye@foYz?pPSrg*7?@%i}%l~{s-%zF! z={5rx!{Wg6Mp<K@A(hd4z^xPs%i~pX!`A&n&4~c$RV_&HIQL1WK6>qXP$?g;r?mo&+EZ~7y9iSD@XsY-Py;R|j=j z!w^^Xg1%6Ml*HHdZjnc!jjILlqhV@z?Dxo+sc59j@OlI!)#1R}HI_(ELazF-7+CQV z!(5dgkyJCs8gkz=9TxGge)nIS;gLOIJUxStuOj>N^Zh}>{;yh=63Rx-z%p~AE<%7h z71?j`0{7BvT9-)o5o_%9>y$wM7~A<>Mo$1dL@(4U@g3Xv;q}>Ky#T+*zs&wQ88MW* z!tBIiPU0!sRs;wn50UPi2XWP;g0XV$*zY{?WPEdDj|f?1L(xFA$F0OFXlZwvog4g zvDmjBhB#6TM|_udn_gA0Ea(fhxKG3irUg;V3CX}C;Qb%-YDi8Bj*Dxw+8u~~q2v## zpGv3k9-C3_>nCbD_pBP*+Wb~2k=7tsvjBhDU2x=c`aTf4*ub{YN(4nL*|HMN&cSgP2;9tEPtx*0|9~g=H(U@nwB%LB z;~n7kVN+f7E(KEo7~@c5(qdA=^QzIFUElo-^nF(>PrQU=cwk`(Ff-&rmNN)M1qm0! zpr}To&rkRLL^e_EL_2+>@cgfb+q4*ES z8Ak#;3MfJ-61OD_U$SQvz~FmQ!04mZLiu(B+G(FQ$tEmzrM|o-%;|cTTCFeB?_`t z>Nzwpv45~57k*HJ3QFVK$~`|{nTy%CCc|A8M-?A32@O6`60Ldvxxz_AB8x4RDcr4n zqMEI98AB065!vy;3%1Oz-%0R%-%>E1|7VzKpEw+|@Xo*u`vXx>&FRl7ax5TpGvObM zpcb`#2ch|2lqhq{5_JRG_7^*z`!!1>Q#%kNJOGYW_exM!8?ZUYz+M2)Nh^BKBMVR> zD;$!C4!OH-D1AA3Vr)(?5Dk4VRA{!};$F7HO=xL?Akf=h(C44fI48Kn z!K$W~ZzX!I0SVmIZ3LS$8Er(zzuQPGWOw|^uP;B{1o}$~isTMOqk~~n7NyO zJZ~BIWr>8Ke$=K>*T4Ff)*6NSEppl3>vsAeHJ$U&DX>cgq365E(`T<^XFIeo(Rhugxupxam77-I_bP8?4Se;n!QN; zHl`UMp}z#3sEfSB&%$MkP<7%8l7hUQBdm|-xuktj^L?EOYwFOUJYRdAZ|x za~AeZ1MhMm)IXXiOi!>W#(O&8*Mns67pMTxC?wSs-lFhZHg;ywA#VhhLoBfNO)rca zakODq0u3KOyS|sSLb*f~^us{w&PD9^%Ow}{6%BaCpqm~_WIc4C(T`0wAZ#^lhREhW zNsoo1WAhg+3$A<17rgYxo5S&&=hAZ2w`mW;-EM-;1Iz#PHy4@_c6qc2RHfr1-j_5ps*=n+Ad<@^_ep}Q0j2Q<@P`AC5vNsYkMN-P^bP@_qq1Z;r0z9NR(=B*j}fmSViKe~L5Xw4o75-zH*pzvkVKj@$TthB#uIu&{m zW-4>@L}0TtLWnEm=?0bC_H_6j%X>3XGF8EWHoF*t8^9sP%|l>+hv@lWKvYO-NZN;r zIcC+i5%6#BJaB2s{n>T(y!g~A0W)_<(Qa7riD})y6d}QkMD(tVX`#~8cjGrZI|(By zud=O}sMsMQH>MLUYmxn$i*oaR4|4^rM@r(HT2g^4uH~+Ph(ILIJdR7D+CZpE<1ee{ zlEO+wN!B9sKVuc<=6qOgs7^PyfXqx>z%EXZ22m9TfxI>%Y=9wQ1?@ue__3>h-B7Rs z12FLG73Zq!ZtfkeN3mIXqO!Y5eFhRmFi7?~-Rfu;DzKgp%W^Jntg^Wrb%PRJp@f{3 z(!}YLuJ%Gi8ykWS0wx45^-N!gQ{*n$747lEf%mrGqZ~&uFpoq+LXQmjhnnP!oZl$7i{3Dqu9E}^S1tU9Y{w_bt)b?!nrqLuCY#1(#7 zUjG1;&^CP65>kvM>VT_NXHfUX#?hM4ZAe_aQS0TA?c z5HZE*l8eyMK`wA%Ibh!?!zjaYlip5z>n(RnIkG4@Cgs_a>-Or)eX6Rf4SM-^`we@m zZtqlu|17uhv$r)ZV4Es~Vf{Tv*IABIpW31u(bV)V9T! z7(6c0m2%>s2l+A|=K|uoj->@RsBo5cg`yNZLungt< zt12>OWIFmeXKv@*ZglX{(S7Ue?1VYANEnx{_q(C%kQX_f%&^j3Zkgy!P4S;yaT+iI zsNDT)b*BMM_i`o2mFX?6(Pm4F!*f~Ud8$0v02j86BfY-jU}S-Mw)h^~3`lhhO8^{R zw!?M@8i&P91qI+jB{RAZXK+Rs5{j5%WAd2}LbrtokshjnbwN24DS-k^Y9Ip6S21OY z-9sEUa+-IxPe+xBB;n@fX1&dxq0R9v0&Lvd?9@Oksb8Q+5O;eq*ussES1+=r$Hwe5 zt)#RCet#!~_2M@t~;z{y1uE{=BBQC!qU>7GN_jiDQRwmDJFCK z75sALU?Y2STf~;wq#ZdTE=%yJ0B<~$8sRW5!ufgu_G`HDd`D}(VpEBaTLK4_GQFyl^HMzOodKK2L7^0RMsKY7{>F4;2}HIH z@u(C5XdUFsm%%C!ypnJTq5voFG2uu#LKq@M=JgD@&t}pLxj*Q}%*Q;;?Lo+o-gelA zK`?iTt$5WMwFBcM{z2z;>HDvJ>K*3Fd|mS35PsjRTB-vHt4fQ6$&MiubH_*gZvT%3 z&`m`8+KD^OuPnuq^>;em2^?T*!H+J_4AUsnzgjuwDSOd*GsK}cuqCuI@$O{1qoVDK zo^08)eUW;RouCWOs7CPX`Cy}NKsEEy@Gut@lu5R(dJ$D16hxbtdi9=7NZ>QD#w z=p+FZobacG^WCoqu5MSKl|fW9cQmxSD8-d4p_C6Ws_RS9@`}E@-Gf5#Yl_tfh+6X) zBD|M?i-Bmj?v7smBbRM|?9=MF6|+`@L}v^iR^ElQg=eZn`Y`L%khS6?4TWfBO`w0= zG+Brj29nVVkGEkp#GY$?E>sEk@%Ayqw9nymQQ%=C@ zN)FuimwLXxHqTsmVqNY1!i+$Q9@%p24-W}}km$?pU&dQ9R}yV3Q)BX7gNxNo&usgZ z1BPt9ZQn@tk?BR4tjqFG+aMlAWFvm#E#aUUb2Ik$5k#3o!gviyii#-PwtietWI%WN zJw^Ba*NfQ9t}pUmOh?>QnONZLDU0cH6xn*0dvy~Y5hO}kur(TBFu>oP z`nY1=QFyerAcT9x#F$9d6tH%bPvE`fzHFNmOLCtXq_kBzGqs{n%e$B%Cj@;6%&j5+ zShDj|2r>_tCzanijUfwDMJAS0&-d>19LVjaHti!L+N4wMJoRgCq>_0OjpX4Zv@=Ta zC~+3$1sqi68ofQMDlyQkH=$b0hFIZv5t`8S^{nFefuUs4qje*_uq+|Vcc+H>cWR1c zu)FX*_mR<`?0>)pXB< z{=T8TY$Me?DA@@_F@cs<;OGt7+<2nr;W zrFlzKPMtX~pO*L{w^jR}lS)Adz_62`048P_4mROr;ETfckD4k=7%s$@+O3S+0LWCC zOxg#95%d@J;3iNFgCYbihvdImnueGoS{Q;wTJd}N9Picl#sv0%!U+3*1KJ1YU1#-) z^#-?-unb>NeTlmKBsVD5*UKWg=%~v?k*~*R1qOQjyQKw}7I{y|mbo?jgT8mh)&07~ z8rQ<`^D+|Gnp8fS6aB5I&Yd<|K`>HPJpp%N1rqxEq?V%Dv$u;(wY19*?o~A}lj-FE zr{Abb?f3&G@=X2Ckh8Wl+kB|8Tv~QB#Ft-OGr>)jPlwZ)3Q@7Ks>_eJ=JhZ{{~9Z~ zv_?tA%Vcn^fW2U-Sw3h5!!HNCE;nyBq<^&VmXFPa;ebX=uq?6qy|4a$O%_tIA;?91 z11RhF_zjo7*_+y}KeMikNhwuXE~ed*!q|2QBdskBua5f#Eelw@4R^4YFU71Hc?=0Q zFKeakexNW}b8s<~!Y?U=!l9|VRY3@-Lwy8N^{irIFwH8^ic5yU1_&4DSVRk@g_$0P zu)VZR8ZjFB9K+vM^|0MN8n5Hol50cInVX3m9RCd`4hBEF4!~{MYdYaXwjwk__G*#Y zXnDJtI8*|RfBBdDaUqy$t*?g;7$b!Tp}=4u3ghZUvFIR`>H4#L{T4iVhTvbv86bnm zh2|zPzS-To_xbz3G?@MrlO8xGe;Z-wPA=#NL70GxJ(7+_#_aK12H2X!A1oZV>Pu!tekeBjzDZX6W|}W5dcos2D6k z%I8~a2CKiGcXco_ou#D?9mh3y_U+4>NW*J?0euTQEk@zMF)!#ZIfV6}(E4_6ncjQ0 ziagig!(h{jTxkmZX_X}neW>o$y=Wh8?ewlAUhe3f?q*e4O;7Z3h5b4pgt0>Lo8!Lf#MOe|cl?v_ zJ<$U>9+VN5c%9;13%w2)YqUPFugYEZBo~^2_SvqZXYa|t$rB1Azm{E*=Bd66%9Ul^hh!wk+emR~9E?>8;~ z%vZ)#c|9(0t-~oWkx=i#%4c`IJ_=;N3&+z@{J5FJ(A>XXQqC9j(jetPi4p>0M=VF6pzZW`aarG>7R6F@69lPlOR4`3|bEi6WbKkE-9VG#^_SV-i)nAq6H{pQvV)1P>`=ep@MS*=Oe zlD|C0!hY1~zMN3AC~8Bv3k1D-jYZO&Ul=}BJHd!Y+H0IW!bRU_45(_EwjEF48X(;z2xlsh^o9)l! zz9uv-^FzF>xS}%ksbOQ6d0|L|Z>9tZ%gbtJmX-tu&u?$TYinypagzh4dn~?eA4=5C zcC73qWq_yOU(T0=yd=curPk}L`bv*c7%ac{1#&Xc9JWKbfwlps!J(fm zxIH$ZJO{6A>2p6{_I*oBEr!}d5%@if6i^WUoXajv=_LGc`*t%vAU3>EVo9Q=scq@X zYMPyMSD@K>A>sv5t|*jHeqB4z&^PlplfBv6?AmN?wl>?gt=)R(ck#b_@8)vObDs0% z2SS6*Tra;0BxvS*W1*K}02@v>iG;h|uKaJm%EC9SY7<{oWFj`>WZYjk`!K5$?1P}M zgni?L|76&&K#ReE?yb@(uS!PEq*ILuNl$$Z4+3|W15Wqfj~+?9ga)MszayY;4fp@0 zZtnzHBd;fo`Y@h`Ct4J`j-1~>;s5YJZBd+ZOM<-|)rorMrrbHA#k@Uzs|Ay6QDH&w z+my#kFyQNzpfmH=HU_%kZur*dZ6yA>bFS^Ob%xEc*z80~X_b7bXATVUTe4%>**UMQ?e0gM7LNVc zl)L>SNEwl@K@`i&EhZ`0ap#Whxj9H#UKYTemVlhsm@_h7RvT>H+}sghLzzwF%3|>c z&%E*lOlKhM1u)y=EbHuuLldh)_jniv6jrGzq5FpL_lGNmY)V@er+A(RvF2o}$d=79 z>GlvZ&FhxQl>r(CjV^YMX#qCXuPQfNcEncFh^{pM-P!pn~aH2cfRhTFwl7nr}@ z-N9S`qU7AwWeT(;tW%^H$;6ho`07AV7@Xn7U_>Kr3ZQcx8a)|$w|4uykSGXc78h$C zUtX5AY0X^Jps|?0ln++^&_I?JIIfTF-pcAvnSeq&kfvcaF9_qk zrb+za(T{cbwP9C%Z75l+{@uh`ka`l~WT%kl>)$H;^-`2%0FS}q5&dd$dvNgGzhJ%yg$c(f@1rjSpn=O0n7XA@)UI$6v4iyq_f zBrCS*wO_Ra{RDzgxG&b+A!;w$Bk~=(Zcp9bB<9jJm1Z_`HX+xq&f=ZIq4m@E zN{pyt?(~Rk3h)?*Ojo?#Kib}i0INWlw^J}@98A1aE;cxi5urbald>!b08CHn9{Ch}J%=Hxo~;7mUterv?RRf) zhkX865Honj=T@Qsc z*<5Yb=sOGh9gF~##omgiekWVB9-S&@3Mx@g5};41^|yb4-||-Sa>|Hn_NN^uxlbs} zmg#tZz-Jh(?iX10{c|CT8Gq34w@Qs|We&SlHqVuO*HJ`R-Hm+hNY@D^TZYJDTt{-$ zi~G+vm12f*5Re!K`9NtHY)|VCGD;$!x>^mY2h@HGbxV2Oh&cB47;ci&F&!p#3wCg_ z;~IV@kpe8v(stT<>`@-P+=sJKQI}3uJF*3ZGWd3rQR{n<$hm4X7xlBk#+91Zhlvb1j_@f8>fPLG3zSV+>e-Rjf@SWW7}BTAgeW8?C0J)i@P!Etz&i z#1g#5ktgFLoFn7&INfSAMA1@MSUq#c#C#zbSeXw$ZF)73E|!lr^0#-1y4DL~+7xFB)C?8;m~l;X#Mq zg)!Tz#=+Mh16!+%06(`=j9;%B#{<+?cmJKJCW&DRZyub|S#!$tXivmv2{vhyA(qg` zy}DiAEMT`rgGa{@q^Q!w?{grcO{>S%8PX%zh3A_sP^*+6U;Sv6xkEGbVck*3--6b? zKfr1|!1Je~4N`579F1d=?+;opX$#P1#`*Qme1rWNbkE$fS(yaiRg9w4aiJ0+l6j0x6)6wstVju5{5-i*|)H9m!_DW`rH43KF)8>(P z2{@h2KuY|xq&L{`z;F&@!ecjq$Spf%Rojh57GNFMdbh;|I;}Opk^eyI2b?Ux zd{b^5{YlYH85+(&_#;sbdRJE7YMnP}6TajU8$FnnP`YpeB(GXQsEN!sSQ8Qiu;l|C zj&}-gg`g;X0hxUl7p!;qtrKww1plVp(!V7!CqX~z!MjIzF)~_FRRTBOPCC_TY~8J2 z@jId_;DBO3%abh*GhVPM&WSpn`j1J?Tz9cUsl!3@3-Us2ggl8)Ewi(rt0x+X)Pudg z%`&G7nRFe$-Cpbq8FFM~$k3{2?T8ZIaSrdH#EbLiyi)s>dsn9 z8PgrvHD`0zb`d})OmdI$Kw%@=`ufJEBECeU7cwbO83X>bimh{ zeJq@9H6=BPN2(&hIt;)|ekY(bZ9k)oMAO65{HBkK%f=@@4T|I}o|Yc_dQDCWLI&&< zQe8u3y`l6OX76b?lf$7a3Ma`~L-6W7385Ue(Ds{pa!EjKyWRLmVRNi8zdQkZ8z`+1 z=h#I(Cv@{@{im&(+kpD<^IVRNa;5H(OenZ=%qgznPyyw?X@{w-fV67dKkY^M$0^A_ zl>Ryq?_4>9@$Bz=C0=T`7=To>{UOCjH_SraQ|7}ita#11m))^nG{DG+!ec%+<#8#0 z`NId_!qcqyr_54K>K8eo@qyoeZO_1T>Q09J`^?t8dPgm3h3-eGLv#t6dA4s6Q+1 zhB_Xi<*Fz2`Fr(XNPzdKQ)#r-^n%fB+JjYZ(U*-wl$ww6o%bo@%|wqN7z3+os^S%> zTD%+@-E$7D@$RMariY#@*~VsTH!@$1Oswz|M^~1=b~TM@gNrO98jD8@KI|1-eHlnM z(r1LFgLAmg-N#VG%}hsOpwmhVq@TDri`Si*8TZm|$QEdvMhLzLtE-pWUA-gW7$0ICyn zizeK8(9uU}CLh9|s69D+$OQ!ja_c7eRQ56ly7#UX!m!{#IZc?XT&J# zVX?=mzCwS)qD_Dqgq+Kvi(8$$Br_(Z_h&P1O@8xs3Xa9}9z%W9@|UV7>)b;p`6l#pMFi>^MnGylmw3FLad9-V z$lP+$sW28jFK9%X#4lnz2}^-!{%(RNOC`l{veLJAV|29gt$tMt^?yMJ=^3` z*rJ^P!$BPc>MTemOtAHZmgjNxxqh*Y2F2 zJ<89ap0(PAPvw2rf1R(`!lajh=WneyGo1i5Ah0SBdDi1+!(~G4VyO#--=4vRPI}Pa zCROu_61qmLA=p=*+dT)M&Ubc26Nk6qHI!VNMDlZHgU$%gE&q@UzVVZR$qlS#(9%)w zCFTU!LW}R<_?X_EoL*0YeMn?BO=%wVb4(u+h@l?{ct;X?2_5|G{v-xFn8J?GdKNW2 zi{#6r^0&MuB;mbUfTKtl<$D}q*ROvXJVrR5e-2ZJEnRud7eUo_mF}C$+hl{2Up!}8 zPKE=;1H)s48E7(VF41acrOeF6XQ#N)TGe4xj5rZ6*%N%e)w86^;Ji2Jh{fP)uip;g z60-0VW==8G^YC86(z|@+Ks(uN)>+N&c%Li_$NA*=Cox_jEcgHa} z|MMQb9Y7V{DM>dJ{ac#a1`_(BsqsSt$D%}XnaAnUWvAY{`uo%QiFkjrE3P`sk3yHV ze}QG56SGO_uw~cFyF<_4@k_N}d?Sp@15v0dUh|>h*ZHAlXMd+M{tj-LXD)jjy86=L zENU5Hj)S_ehSF_0)5p;;J+fG<+d;r=l@*#XB(E#|`+Qin^Xb-Syj^%>xH!kTt6l+9 z+Y*tFW5<|Ovz_f=PqRH3&-W(3_cRL!smsg z4#UO~iuytT^p^%r#2ZtY+bw6zA#eg`$FXD7!!!x>Q90@usPKB$P*M_`qtor|HcI<} zf=(jH0&QI`9ce7N@s9Q*A_J3?)k{OhWP$B@+5^r#Dcuu?$IUcYo{^6Qwr&E-g? z)AP$3;&YZl>rL#eC-Y4 z&(hONyvx4kEOX9p=Iy;q=n>QYD#MQh*%zX0%U{|zG$c9e1`lDp)3?hI5oTdKW6i*j z!Q3Bxa$>c?>ciEYuVzed`8hbw=);CrQ# zn1%C1s*z|cTJm5K)Vq2c&MQ?h+B|F;?lkjfWA?mU3p$q_wVvwcL0e2t@wzUf13WsD zfOR+@hwbCvVOsfZ`TzSMRYU_WCb}p{jrCNZ1Qv)?_QnFKKRFHeMdV#Y;IPnl1a|5Y zunU&w9`KH-4gVItqFsj1P`eTKET?>)3DE_H^o2h-auG0h{S<@KZ)#e=;l~^q=_5bn zAohCnGOs&kljF@m1qYN98#!&ot|Epq{dE$RB05{fGDRJKtPw^%rHRgzdS%dPi-S+V z2V^6)RyW5oR+rAtN1h#pV-B!EWDFR8@E>~v;lyHw)ZGekrfx@aA1!B_>GMXgCFz;E4Jg9#%&Zl`Qev zW2j}ZvI1+9i2Z~lIG?W033g-=4pXK78%mh3{{ZdIL zNTLh&uZcp-p~dH&WPT}Ol;9rBl_#8)vBgDc+c!pK;D^y*1p1E#myGrpD1lI_$DgM9 z-q|y@$6qC4ZwpzThamdG3%K3idV0;-ruf0oIGp@e%3o^H!sBS zqAipYP|qc$@a=mi%lC#m``Z3()sJt0bN9&ofds?mR1RQhKPDS>q!F=pDt8Il)5J~r*BxB6EGrx=%_ za))gn9>a-K;%ITe?ASobp#2lMBhE#WD27{ z4p!rc_?MuFAA?XJC6`JhpKfVJR=KD9O$|t14CRLK`ONuD>iE5dFO+)+{uZIb*g19V z@tKgOxd`F2*R(P%>ewvpLqNyJ$gehY5b4FAO|1F#ZaDXPm;6mFqX-Fk86LtGF#*>6 zTjrUW0~+;x+cy8g$f`X1l_6~Ve^`LiVM_w9kfUeU9*7fPx<)-exAK8WxzRr|bB11l z<^{C*(s%6a_>D!kV~xypH;W;JGYr76ljF%9@jHvPP@zV47H+c3{oP5`d=6Xy zPO&izWPC-8JkK31*3Rz1rHO-_oB;7QjBnlaj}qKD7qnsidfO=`-kC@x`0gY_JFVIN z$KgjSdF>L&cpL`=EL+MM()D;CwyITdX9$YRU!Pqh1HqHKl# z{wHHWeSI6lfuz-47avJ?YsG|2(EjxH&Z?ABt~uX|wqSs}vB{lN*!T9gjyLi*{eVCY zO{K1W)>CV~&M>`Q{OC9rE?-J^kZF2mCc-ufij(W?6SFFwNw{!HlVN_5DL<@Jbei~E zaCL9^3dSlaKS$Dk!+0x1V4EktB4ZNQe#D24ps4iYmLFCN%FiVngo3hJJg3osO6;I+ zs@H?naFXoWYytY=#;VhVa*8ETM2omc!s1jF{Gjc`?`3N0gAoV5fm8lrbx8s6PK z<{(jPgqzi%(cD3<*POA^uDrjxYQZ3bKv@*4bG(?9e(Grl{rHk;q3S@C<`!`d;=;=v z?&>d~7@G2bo}H*-fY(CPXF4D}|BvX5RHwUpi5ZwpH~Gs^6xsVcj5Yuh!Wv6-v>L%?fKqVoZ5m zu|6jo={Gk{14mZF6PA+e#_zN^#`(~NOGpy=sL@c=50UX=%TCH5ICqg+>2T8-lZo#S zb#*4YL-`@s(NPF9Tl#l%zTj~WO2)0*&6Dccq}IMMy*KBMG5wUysSU7AikIbc+il~? z*ocKWA|JI}c&;Ly`wCzsdD(paG*W1)4e2lJ87I14kZXXh7!DDKGw&hsMk=H2tQuiT zSb>hyXV|?s%IlHgA2ibe#aP1*2lQoqpuZ$ulZ4XgOJ^0 z5R1DF(&zPBawygMdAX_A`%<^J!kgW-6IKBHo*Aj1i2pLV7*q^hf3dC(emM=yOiV## zZ~{LoJjrOc zU6NcA(cXG+3>U0G=euv*4J7&QQEA9bYM87{v=33atgT&N7LKx7Z^-ZcaY0P->2}eZ z^e_&j=4t1b9`L%o2@CM148XCRSC5)CP@|-1B(t`K{}hJulg$ZG^nb|}fQVm!5%^nM z0@s1EKiU66F4oPV+kJhs+rKLJlAs@+$94#%x272r*zr103OwDuQPsUCiWe5(s!zT> zvzZ)MxW}9`X@Lh!m5R;w-(Iz!anW3U^R>mCmkJyKg-*ZV7(+^J%M%No2p*cc@$wV+ zbO+3=bz09p6emYjs9v&@&Siwr>`Re-t}ge6JIUVb2165B(UtU+Gs;e+GLo`*iOPSQ z{VNay(BA*VIsT?ol`Zhzj~Jk)l$)ALzgNjglnW_pGt4uJjbpar^UdZ!grOp38yEZ_T!4zxODQh{fy#-W(Nw_IKoTLnu+J zylv|w7o>ndc&8}@QP^omLLvwQJI?>+oD=E*+uG#OEF=xEHeEWaSej9~lbuG#?2M#^{$a!0hIBH=M*|al|$;`zHi7A5Q0Xpv;EwJCj15 zkUk_)_75;V%qRs6B@CEZE(Vx+={naiQ&dY$82zKhV8zYSODjC$lPRmaS>mV;HGXO?M?E+ zy{V}K@b}+8>>cB($YhVP?q+@PBP?ok-s@B1^DPFV7!MvUU6z~{*-1q)*N=ZvKaa`jC{#%dMYW!fYTF!g^N|X!WkgB>cgZTDg zB@8b42ZLB!Ax&fEau1LdeB-CA+aVMjObJjQMdJr-IG z!x;h3KOz@|Idzo4>ZdqgII+9RXc9sPv;H4&@gg*o>;geRC_TS1%Y!B z63_U=1oq7BjcI_W7_7!@Pj_(<832;lAdp%692qZeZ{#DkYwQ%P{mYM?(0}brV1qWF zX+D3~&Bgmo?*7#m_K{*hmGA47iQM{bh<5^KcWW^gimtG@Ix3%){ai60TuIeo9f>_4 zwPMLdhkFLT$WSQ@>AQLY{9XbT?@>XzC*VNKV?#dqZqA?~2D;J&IMeDF0EU$>Kn@RI z&Nrk?MsGfRH#d0u9+~RA130`OTdwP1eL@AKx*umd?M$+spbvkyhaKf<7^L%RRwqN^ zS^*2oo~A}cA>1_EE;HGE_|wZ-3Zu-8Em2O)elbU7lJwPqTdLNz#PD74lSQ26F(O2{ zc*lt#5TVz!B8Tt3dLahA+wZu~{b({D=}+EtuacxN+WsZ4{9``%`gTK2nE=l1HAy`L z1(GYtzvy(0F4&*9I=La6OJYp2E4yW;|FB7w_!gQkoII$>AhVGK1t`ILb+%_x3cIHF^c72Tc*cdmpJWkNkVE)szrVf?vi(?ZKKM202Q>_T~~fE_&wY1jtJtnJncJbQT{%Oa znZI`Zm9w7@WONjR4>v>FLR5H}0I8p&-1Vhp2HKXXOyfT88X6ipF~bMj_koYmX;zQk zCGg2*AZHo^lPX3^LLzjoro<0n2fQMY(QSquvxfrly!1p9C;A1^iQ>gqfSgIz5)lr@ z$9l*GH$T$nqu_%7`O8-=hMw+nI@5ox8pV(0WaR1&EKocY@o(F6 zxnL{_2Eb~4V$+hQ`h4su)M9>Babz^<$W~UXnMur+#!jQaC#V^emjU&qpQa3i=*tcfo;zs z^~%%unbL3KuD$dSt}cS^@xx9Q7w;aLxT{8jpme-&RwuA=r>OVlN-$$E7Yf?r0h%wcmd5@w-B4Ija0r+ zAYYw8+3@%g!+^1oy%xayjNU{u34(jXx+{vqJ=DP~XoH364x}q6p(TlOy;R8o=Dz2eo~X`=d!DeF*%nPNro zOib*Cg8c(^?_>eIgsD!EKysA~P1147ceNHt*CuRp5z zW;OBYnvVT5JjHbER}Gn8&X+PPfw@KYKU8F^q6t=HFnFU_(ReA{_W{>r^Rv)+Tzw`s zu!AydNf4i{=FmyL9G`245Lsl5Z(toK(jBfX+?=NytwPHYLGg7Q!?s#@9wS4J$PLZ+ zr^{$OC|pDPHv+@%cZLf175;D8<*i%6+dXX>ufZw2t{+JUo{}$enQXsW8EbwjSEs~J zOvL?z458Y&Z>Dy-uI3BzzE`sqC>8&PG`z*7q3x60n)T$x*V%BJP7t{}XNf8mPZD=` z;8{=K0xY`n!K%S_UTL(6FwX2rq_7e8$P7;~ZtX39fmcOg4l>OPN%=5RHvr-CcYml| zwsCzNi!8F)0~rHIw1SSe-w95wX+gInwKQzO=CFp0yKdD*lrA<(#BoecZqI>@9r3f0 z0A38hoGrvZHm%Cn z{6YX=I23t_HPd=?Y&boE1kHUA?U&Ot`6I|sO-hH(zA~njZqD5<1}BG6f$0)xNECSL zVABZ1cxJu89AUcw<@JExID6gOmqq_>tlw&O`e}W#LfN z?5HcVi$SQ?D5XzECPoQk<8C^oiLEzPVf_y+-X{{CK-`a~CNgF%i&09K@$W9hidF(Z zzY$WJjRkdiNV;er($GAwVe(z4iSM~3?Xu=k(}5w)M0VeP??g)e`pE;fDub`%(54jP zMpM$fyoc81%%(yw?|P^_?1g|LY06AvkR4kRH=8rFuE9J-JAXFEcC61Dr7 zHSU!K^bN)Y6(~8))URUuf`~pB)~YtcpJxTrL=Alm^>*EY72;?JpPpofl?Es@4MeSw zpWeSnJC&IfkmEO?E_C#iJ}5mL;O;~PRw5?gKhr4z^{C2E|6$BE%@fPQEr|L^E9yw{ z``?wOHXW<_LnV1Ie7iv0^cgmSP#p03JvV+Hg@Hxk7evlK_0{Ca?{_(DI3(WNIv!9+ zPw7-6|96t79I12@N}E0DU%v>D%c0l1lgqx-!3>q5zv*72617oyt2W$yB z3cqG4i73Z}@dO+5df2RFDP2mU6ye@&muiMqJE7F4+ndKED(@2{C=@=jhT~c0f+4LgY{>r$;PsSioTsF*x%lUWL7iz8`@oki;0% zLa#fqv+V`ePu>d&+QNlOszMuUaUql5}iGFct=X@2;3Nd zmYkFXuS2-YY#2`{$zwU! zrfaEhy72eNSLRP#a_P9fUn)gdtAmdtJ34EO?@Mk~uk}8fsqGM7>eUkJY|)cYJH?jC zejk56lQQSzDN5MP5p*zwBbf=v;6tMVcU$(+wh#)$lBv-OG?RV48pO!r z7sKD6F@`q-910c*{9k5ipih2HO-QeGBNY!F7S@Sy+n-}$1v~pdPmV`TjG0@2hxrMo zrA+!A#vCtDF#m$HVp*J&OKU7V&`gBBRY>r98D+f>I- z-GbuJvCYczhEfj{d9dy=*zO23I#53)rF?f$BG68qSh#|()jIVSgHt_ zu+8u`X+Ktg@%`@>loef7&b>$|gPkJ7B^8V5@_A5F=%iFV_I1Q?`mMrgId5#2_1!1ADW6fC)xrhPCjFXx^tDK3 zU(Wrted7YAY13(5@9(=|z(}=e4cUSKP))cpJB^G*ri{<|i(xS2?xvfw;1ANl^eMy= zYlNZHTv2bTZN|RWgK%16TY8XOh@>GT6HrS`C?VAe#jl_5v@Z%n{kiNPJcy>z@i;L< z!YZrf5C2WjmEV#ci2tRZ-;S>EwBk_D_J}~czF>q%4r`54>UQFkrCaRKffwJGDF_u3 zC=J+LKm?>)+$XW-7l*1urm9QHMg7jplGYzuqjctRJwgI25|=?p!Nuw2ZEUq~>dtds zW3FeWa3rLuwUV%C!SeA`6moUgBmZ@KTs_`%R0Z$?SH7dLxy6qwvz(X`Im3x{SRqo7(Br5p}#y0#)$rBgodUAIXZ0IX~#w((lpP#`>Tb;WF5de3>gN6Aes z-pY)|94q;yv>k{!GNq=4Cgwd||DB6VRvH5EUf8`SH#s)ex7Qe?2_G8E)9MqFz`lJG)V_r}~yprh0gIQ&5yQ2Z{T=C~5@llG?1NDQXp#YUvx z7+5wLh*I;z7lY}7-@9O4tee$ke#0#|>$|bp5S}3WZTjo50D3n#XxyL+THxHtZ`?jv zQW%i)Y^>kEyEsdD zNGGwo^sXK1F>6gS%@a59 zh+`tHQ1@}u1ACnuemOyl9dVU6l+fLSC#@C$s{cdcx0r@9YJ4#B8Git{JJop3c zFpIo>yjzu@%VAm6h)xxJp4!o7dz0lC$+3VWPdLd^M>o3EThAo(%)G39+>oNbQ5lTS zbMKCdZUKt*p4COo%&zm}C)DV_Y}b6VlLU_a;`k*`?VS`hSbN=jc+a6BcDtTvY?YUY zDRZA#*171s6XHbobznpFLw>kQd<8-y)qjj^(Egv&7iZL{(o=FJc2rtj60uT*Kk96O zD(yCO9nCp}lkks7?t=y5;aEGQ4N-^bqinW^l-B>;=;Yd#_zI4Txf4V>eE6Qc zF^UFw@>KPdvxY&Xkyy=m&~>#T$+aQB6Vr`KyL`wJ{F(S8?FLn2wafpvkP zy=_C3_Cws}SdE46G(htv^T0Lb+lxo~aJOWsNC?EIVQd=?1;f!mbnZD$KNJs36v*|Ft>E#@Dx*z9T z2UK#{mz_-#_vO){|JM*oyOPS1I~T(yhdKJPl+IvllYRS{Nxjs5j;SM-S;V7|&7+b* zW_r`7L!V{{j;l}Avs>Vq$Y`V=PR8c<-sM%;%L*HrDgM6|o4IoIV2C~>kzQn2SQyA| zR?Y41BF_rHF3VjfIxT8A==(vSCcHUQZ^+-+rW7u6*c0WobCqT2g3jDq&4Vh|n>B3y z@3|wx@{)MBQZRgG_!R8p%;|+>wZM^d;(?6A@#BCZGj>Obb5b>BhPstop3$8U;qLJX zKh}S7!u!3`d0<{Y)2=5s#Jnt1M?!Uw2GEgj>@ZMe#GTQC)Am)GzQ=7Q+Ux~{3@?=M& zW-KfmP&-I@N3tWN(4tKwe>Qi|QX}y+^Nms5ptV~dd(BY=h8JBs!DMcZ3=d9q1G6fLjqti(V+h!U<7b=>k3lN=eJWy^17W)^tq4Bn`a?PpFDfk*XpV<*QOyZfAo`8m*NE1pOjL^Af4 zmJzA^>3UFnZFesRtI{8DFyjI5fVD0C#jz~i5adqBYN0_ z@k*WVo5&ZdvN{Q`y$iVnAs1QDR}n9<4g%xCX~8eC?Z57>1BE<2!<2nICAxQ%d&O7b z2MSH#UlLAnmV$3U6q$xxT#O|-Jhgi5!HikUrc(!UF;NXPh>Q)fJUcQZ7!0*AZgs%@ z-df-k6Z??aOt@#JUhNeWspnEnA6)E~dp%wmS$B8%`R1$sh zH2!fEOS0cu6ovhc@@_WMES=go?@mKU#|DA=n&2QatTj$~t`((~Ss8Xq^mkiinpGD~ zh)pWMUUcCxQI2kksSl^r;@^oL=|yp{S(IdqD+VTJ{a)Ae33vxwmDjEkr2suY&<);6 ztz<(t4Z8^Ar$~LE%M=|p&C?8Aj`jMK`J#7@ z;Nc;^Ua&(LXQ8&^1zx;*5#BME+2j}TK3ax&>Sx!AJ3Biw7O5uEFOApyH#sbs@J>e=h?D&!xV#S0##z+3?x3d_!P(;UijJ5MVh60u7Gz z)Y^KbZdbnZaoL8a`3tQXB=j0c9aq)mRvmGI?IQvwW7vK4xk0X+cgPM?!yuyr7vHjW zhBo-{t$}YIr$MsG_|JCY>^Qwn8{3L(iPI3v4w^j}W*HE35R9QmL}B-eedE>S`Y-JU#l1PIsdYpm zo3#T2m<&peHCw%_^rHtw*FBEvn5OsZw4*N3hE!639a!wEW zbX*Alq0k!^k_pkw#uvJKD2YUXjv^R7HlbeWDLy> zuS%xxz^VoZ8J(8c0g414tyGwhjxckqw@papok-f-#pHe^<{i2}3nfpE-VS`8KA-iG z>DiOqr+gC?S?TafSV{4bz8BHGHL&Z7o5(I*-ORklji0Dj35fNud> zXH%jn!8R6p<9mB-lttV+vLVC9YU^MaauK`E5tP2R#c5pcSA9k_1q&@`Uq;z-sv7XK z#ad$- z2p9J^56^3%8zLkk>+#``qf}`jS>65I{#?&AZf~ zSIC$Cv^eN|^!>tPg}wj$%OYg(Pn1Y8Tq|`V|2sD%mPcL*}4qC%O~ZQJ#~wY5gp-kne1F&^^iMdSXp zqgN*QE?$_RN15P1jkG+ryWP18sPmKdF(cw|a%@cgx2u4Y`DQG167NOjI;aeQ>+XSmqaw!T*RZ&zX+e65 zC}yoCoS#gOl)WM6$>~-#+V@}5rfh{4br4#1MY!-IJx^w~W{7!l9UPZDz&AGKkfLaX z7tz^Jp=$Mm>%>(;`6QM-PqO7@s${OdjSXfjdfL!ty0Z?;9NG$sy{fhOe8SCpfrSpY zTW($QBgS&;9WRv@X84XdE3Puk23W@ic$*Kd9%_0CN+EdU?~%$S?BWd&x)kz2=8H!W zia1#&LH&~xo55~L;PJr$D^xX*i!g{9FW#wQVvvVAl#)TTvlf=d$hq4X2bmOzLd@)? zx6ygZ?sl&C!+i;M2Z)3O*!u|eVHkAKie^IlMKy!enKXDo=x+!BtMz2ugj6i;|E zOWVcM{oS0yK`o!tIT1O5sHa97^)HB*BP0P57s?$fw}V+N)WbaCK}FxNBm={{^Z%O% zXahkpo(C+`ia%i$SePqb>kZNw$NmHyE~7P`xNxDA=8FG~p18R!JHH(&%cCJ6!FMz} zOPQcERdJoTc~$Zp#ayVTYx6pDu%wQ;aVp9Du!%5Z#O7Jvdf$3Lw<1kz2nEM zu1l9MCeZef@^K?Te4mxpID`%CfgYk*BhC5j$@oTJavWk@SkNe**X^EUwm|X^+$+wU zcpMuNAWz~!ATRffjf&t2TCcJF85nX6DGfU*Ke9J9l!-Fy@pu1F^V>pdsU{hmZbAhP zSj%+%D3H-kV2w+q5TFWT3pQjmNj170z6_d(laZ@s%SV;9?20zmMu^BwEk3UqT+fy9?=` zZ;br@JZ!v&-aut?j2#-%`Y5i<4M@Q;7+&%lVdb9%%{UKC0jNx3r1edU)Z9?K^T>y9 z-DQ_w-BBgSI{jZBLL8k*v{foM)idd$@KOKhcN5w!pq_lho^tP#hGROt{q3B^&D}aDi!va$__nzZ5B1yOl=+xE= ztO^La@(wU7(d%_~8w|bOuWPlT|3UIBa!2xmtf>>C1EXQK*#~#S@7xNj>MsiF8Duop z*g&L+^p&J9hzu|l=M5oNhM%iwudvKSyYSNVtWA`jlKA6NI>c`2_~kt#7vha9>B1DE3jgxjwpaiXM5KC<=&vhdr|fqBuCV zWV*}-hT}i}Swn7bBWK;&`D7cqJ6d-n9|?)6gNx@RZ%&JB@Lr;k;5q4zH~})dIDi<| zNJe?Sd@^IECNz6)MRiC^?l`E{hpCSK=B)o&ZS}R`TZ0pe)YAiuf)%S=ZSih*h}JR^SWRe!ct;6pZJ+^qq3qsL~dCvOhlorM{o+gl?po zG)j@O9pB%zR_S<%L-)7a!8GmJpTLWtxkCE3g(>t@?cbJ(kej!M_I~JBODNjb83r&Vu9Q%dc~xh#VP z1ga;4=m6AfE0ucqwhQZ8y75c^q%}5zu3&|-6V3uuDy96Ibz5$eYxdeAa985YMo%Ao zQY(^h8CSuk*ok9<4rsab?EQCNn3}3?GRQ!HdRtxcSchZQ@+;7umJJ%oa$3}5yl?(JNKJ5WT#4@HCE8sy=B^2}SX zMpCl`44`ys?!|bKLPlh4h4+el@u1Gg<#T?#idj_I1beTBUlCwH1Y`fd%;kMXjm>*(U-|= z8Z}$#NNwmyIiSfCw2sAGOE>7!TJM#Vgnl|5Qg)u3*=qnRwyQ`DB@`VmRc1m0wB~zv zL@U7?e7rMfg@?+1n$F+y0)C<91#xqG-Bp7@6njf@DEu6NHEaZ@a>ozQl!t%TTH|-r zl9%1byCS*Op|$)eLB#dx!@XI%qVK}IOGR^2HU^uZi-`{}{(5>Idt*8=^tZ0w7Iz;9 z`5g6(6^yH3D}6#ow`r7Ufe|A+48%#3yBb9TpferTW-1BPVPg}jv9DhK&zX|#9 z6AHygK5v1PaonM*!y-&MPj_gksv|=`G;O*o`TejD1K3f5>!X-}cSn*b?BZj;`1k#i zRJmnT*5ZaB+IIt@g~(PySU$G(O6AlJi;a_*ZD-09CqM^X=^#_IxvhlaFAZx+wNygF#zjuOgffW>}pk1xT&|7@1On0OMEWT6gX(##F+?WBd zcSA^_?L9x2z~Wr7`R*w^SwHeb9Vu6Gl`Adf?;xOm0eaY(zKU}mQ$Hh+(#)-S(ALkk zt`y24Di=i(P$)HBj*Vw^GaDH4e6ObebQS8|ngT}BJ3Ar6Wu$<5p~2cMj?(RonkY42 z(pbZ-)Z*kP3tJH~gh~s4BMre_p6k+=|MNW=k^OTr-AJ@_8+Ni6b#maC_}=$RuO(B9 zQ&kZ|$AR7f_$so=5e05{7rz9b&Fg@t(>VI1@6%x2$=o(-wS1otvH`je*FqPT{Y-Ek z@l;>biPCwk%JNNvs;^K>3q)JQNd2-`K7LS76hcZ3>!?SwnhcweXIuHL>w2S$N?ue- z1A;sc1XzESNHMq$nsGucUNaHWyh9RvYh*0H`k6(}S0AaF!R0n2Y&a)TW8A;8NCC%e z@jj847!B8lJ4u5FWOm7C$q{qP=XH#SHUDUT9Zl_rcvdj*C^Lk!5zzkCyZcQ<{1+RH zSyt|Y;eG(2ldu2^s1yF_FzGZnURvZnXK_zKmT*d|#+i1$xFl|Pf~}6s{jlo~Z~H%C z9}L^5%bn0F{dT&{MG!j7PjE$J3&$=Zl(-dR|Cv>r?e68^Tl^9WLz)HhUXmRAe(=TvxXS2l4zeZV-s2;mU zDHa2RgbeWU#dP4{7>L-=Or+}LGZ^V$*dx<(gOZARa9I8bVb!1ZGJivTBrlBqYXiyd zO1K|Vt{Mg;LYBHMcp?2=FqAu59wBu?Npm7jdlG|s7Q`Q6piwqpdaV|Ve<+8fe(bwH z1RoS5sRx@@On!NM5rNb8zTc9c&f5a_Vg+T0Y~R?o(c(YQqD$Jlrx zGt3JXPr+RH?~K|2R3duM3DbTRiwn$UxyweM2F`7MqlNUZ4vvn>0b#VZVU4|EO9Xgj z79y8FdwRduc;1>52&Db`)6HPBWBcm)ub}$imFsfw8ct$ws~zlXCuU$~$40c`^|f|n zn2N94=X58ZhfPb;U1W$CFz|pQp->BAL70DM9PrIl*c;C91MyZ?dILd7di@DU@v^S8;uh_WMcN){3|$C- z`#pplUHMyHJz{wtw&vvrOf$4%&w}*6gKOetrEhJUuiRF=*j?9w=pSXs{}gB#2OzKx zm*5EH0J)1!EmPWz?@9K;-Aa;AD22?Bh)r@wwTam?W1pKFo$=-Dw<Qc%~xm@qt_nhDGuBgQe#omqJ5;{JMYSAi^(#`H=pc?{tp#nR?}*d;l@rvyXs%>FJiE8mDcw^X7>Go(5sFfm`GJ+n z%Jy(SH3jDEz`!jdoLJdJg`Rg5P~b4}4u zXZ{X7_p{_PIZdoUu%ar8YF>Yx8h)JZQDQv^a)mGS3+kotU%t1G*DK{k-e|$SRbU=t zo6}#p%8JpQ#DT*FtQ~oJ!czM&t$vrE)=Z!VX{f4S!qNhnNraQsJ7V*+C0VXx#&l+k za{E2ixlq_cmxCeZSgKzVtL-I=EI@y|6!7OJOdFRg2LkCEbbp*f0B2R(tgteEqQ`_W z6q{}T4lS1Jf(77}iTyX8bq#b?b#h{hg#3Z;A?scA-x$Ua#F>-u5Si;Af+5Jj_rtc> zD+%DrMd0-vX@W49&0s_Z&)tG_Q6}3ASa`l9raCW=M6X4RULW55I?`=&6%LNe#y-r? z_qBI=dYWaDOb3;ViPH@DlX0xk90*H|>jM8&BSFngF3T7>1Q7#;2Y2cqJVXpS6SMpw zEIe_a&LaeE0L`aG(meH`hxI~y9<5~ei3e6z_F!`Xn|1X1dcu+o zHhJF6kTWR`BDT%G-zXm|O(FI2f12_Z6z2X>N)q9Yy8Vce2>svR`rQxVF7_6RR6KG( zEZf_KjcW23Y5MQ`bwfBmnPHy%JA*^P!H(vW0TO1R<-wYp%9%BoD|-C^D;bR^fRfGU z`UeUS-AFhjJcma)6A+f6_m3H=i%Kp%zzutp@t=4dV(w zqPT-%39{Mc#zU`IzCBi=mi2chFZa1G@Xrt8c^b__1|5|R0+r3<oKG-w2v4HUKpX{?-u3swJ{;My+G#^rk35%uL#Eu&d#bn;Mt)i(FQ_Df=Xn; zhE$*gdA!0vN>37ljL^Uk1(i zGr5=fcS~sJlT#Rm@aO7e$|x99MbMe?^&FWp0oe8pYSO!~c_|HttzUJ=aB?iHWXj*z#NW-Pg(p3oA=z z@tr>>5`-NFd@TYp#PWpV@Uai=soIX~&kBs7PDuwJGN~`V`iYjFFOklVVpiB6$Mhgl zmm37=dbQ1Za$+lellxMniepts_7#V(;BHKuLBE@L+%jYDeP41WgTG@NGPj+43Ym5O zh3s*_Y9r0^rA8>C{P|%Tg2>+#3FGHM_Y;O)fAUV{#5+Y33+B5x386ePqn>b_`DY<;HtEG;nK&obt1XQpx422_>#Y(po%a$|6*eG z1p<=%aX1iKOGc%3=L{OdXDS^qqe)yy&P7`~a%enw$a7Bb*dGiEs>Qhv@!s3%BuBr{ z4VABTd}11hFd2#-LN!83fBZbxhfDv?xh`SY^xhI%?;2-jM_VQet`)t^Z@Mzx`b)z?e)6jGVXH{NeZX#)F;Z$P0FYj z%Y4`KpIr1XpPLWw-1S>(AsRraNWUf z8OsOYj!R?+tjJlzx)-49?;88;nRv=hx9~MhqjH@&qn}qH(1*(!WjXll8U%Z(+5KEn zf443)swjFt#D@*_0sG=GG{$l^#v@{5oDJ&DMV27ur13Xcy2myda@2IJBjo ze!C9|;Cw}mZkDn?wK=|c#N%az--5JAH9YV*YQUPw9TY2=gRQ55ipE`EIPWO&=!YWi#)`%3X0db~R$ zGKg?minL^+O$}q8$tLV@iPTpAnZC;BN;BF$vC1R{$=x{fw=ml&wEgEuu+{DZ3{yCPI6$H;jNtWArR<%^niOirmQi?t{Mg<9@MV6P$NMYXqT za>!Ud_-1YUvqndIQ%XJ*{A3P4B8!tY%^q8~^c@>7gn~MCuLtm*Sv`VYX|-^UI2url zM&E$pXFlVbUAAGcDk|;umrv@}BG&XYK$}uGoTWBcSoMm~xzATLD9kaDNSx=WR)RpX zvc>kpZtjQ*{Ns%*i|tkMQT8h1YoL>iVkO|k zQH6sRqc!j4)8r-ud)HuukZFJY2EZ?1%|Q>V!&zx%YHRuv2_k5$<)w5j+bh+{Q=;@) zZs$9soZx(J7J?$`Nv54=G53Z@=DxSTI&4%n?U%Tt)5pWy&W_FC(sTZpZFHD&X zJx%B1=aT-krV2|nZH6Zlb#fTdTjUb^?YG{@-QT`O)lPqT1;TOvReVBGsqbdNs2y8x ze45cj4jc61B33cjy&rNVzixEPrPRi^hQ%My?9VlsfaIz7i&w9mbi2q*>TLE4VTGf? zG=z;#GeZ457ctjKc&B|TNzVg~6BSV=_D2wz1mqKx?c5MwzHMfl6rV@+Ypuj2%O`YE zQ;;nse~U~C-R_FX`i?)bPn<4nOD?u_>A5d^Xpe!lyCgsrOWlK1d%>5HrS>IQC`$x*Xp&#<;312gSbmNJ(;i8pz&-kPyuOB@#vbisB-x~P5Y^_uz!msDGAYtQq$**^NLBwIJ0X3`} z>*X%BCKNF&Hk&AEWb+P1qf>zPN8@j3m&8ns8BA4OMDc~m`E9qkrRI9o0|Vw%t-h`G z+VRX|H}&_$?zdn1j!7NJ52LUv;P7;|kWl#9zScyKY)u`B15+#uZo4=qvH-sv0U z%e8VC$qi}7M_9!09@n;s;|n>@xx%5OEycPlUQ^O}uuji~2d0;C|MVM@eYrQCv#QjEuH zaT@T{B6oKhOjw8&;0?iHKb!O0=zd!6X`(TNAYbQ_*d9W5?4x@1a_j04ZLN*D!k|he z9NWyd&n0rR-#2dZzL+THD&4>8|FHS$chB)Y+TI=R5^Rjq{iJwzp$Bt>{Yq5m#}N0b z)7+$QD!Pm{nH{Bk_A!)G^w#_q(`cABd`ppe zU?jwrxf(<}|2bgIl|)JVNfPcH4wea4h3cRBn{YhN{K>ziPhex(%%b1Ewx-oGh}F$p ztncVvAnw$DPWA6!XXBa3I4`k@?bLA3zI9>P`(%sjiAqNr8kOE4q2ClR>@`Kqo}BP$ z+>bP;^IK5T>J=5K7jqpgB+SEbVF0pn8KZG26S+`!sjgd9K+;Dw%vj|c%vDoFKd77g zhg_|%mXI0kA?jAfp=Aa$aw7{^{ajZ%2CloV)lxmznR(!1x+}Icj8ORd{^yn$7KC-; zj0ww3nUU+AKr``JGA$+=N&?DHfRx~q0!OoGk?0je9WNiC$!ADnzUayrBC=T{tG(fJ zwH}V&{c_;pV!hV&Ov9#@P;6S@QBTf4lCHTQbIi)3R1dY|c8b4OENiah@CcDycX@m_ zn08MX$NO`vv}}*@IP2{m++F+f^$}rR)~f>+Vqw27mkQ z2++EIiQ38%9&L; z7i-@ehCknKQNfgIf`(yxCnt@%k}o<{XGprFgpLo`SCP_TT1_+;anD7qe-PsjGZ7M3X{>oK;7y5VUpSx z$Q=AVCnY;ID~37hdxhuiAy56V)zkInr}0(5=P?#@M33FNQR*#LpFF}x+BtqY0da0dfh+fBX#J%`m3Lt94?(LpGJ6KU!B^id~wh9 zff&}yfdX2%nQK9}uW&V`Q$&S9WTtl30auynE_*- zR%^v9r?K*gBGKO{X)Y}~;9%%%pAcrZEv934N|v8j5;r!($fSlyFdN)M?0S7zS2Des zFKL!LjS|2lVzMQ-$cQJ+4VLU7kN*k%!5Xu*$J;aMt7JFswc$RUKEM@f8}kY=z(w1C z*qNQjR;Luy#$#r4Ptw-n`s~;@gw-UTbt0ls@}6t$3Y?(7{fo*m35t(2G~8J>;Kq^1 zTkP{6`rZ>;#8HUrHQ8H1S6C^qSl6+|$S_mN|Kk1RGIg``eDbi1QBrDVj`#XQtJTc6YI)r_Mtj^_9uE={_*YZ{Bq{yk$nQ#} z2J9}!OFSU^?>|$d*u;2fJQWJ4_eADM#~=IMF*9zG+e&W-$X$70^3?)qtX6Qs9hBkQ_G3WP0AH`WZBaL2R301kOj}^q+u%g zppo})Jk1J~OVbu|qacMi#F5u=orcf1S=&DqILfn#`3((XIrmk$YZpR-rvDu#O^b>O z;U%Cnr8Q*HORm2!)L&n4n>nlsY#Z-nygHi0ZDDR;q(E`Pa|4o58GkQIj1*PPWenwFY!(qdUiw!a)E1rDPu<_~W(cJ+Hj2_&*pO>4rMt_tW{gD; zD*Rad$q9OxC~vAB=h8K`5jqW#2$)NYFunY3uL_drP9|MOaqk~+_!q9*0*NK{1F~J4yq|hBTsPNS3 zG>E#S)JbI6ye@&SIgF)5=Rof@7%MqSVIm5C%ef!p8TL+Fte zcZ1B?An8geaOv>R(LAO?87kyGOz8GR{8McYcq?u*67IB_7jv#XE!8caqwD_8-z__z zt$Nyf4m5TkNeV-rN+YY2~Sm@O__GGz~d%P@g7N*O0>#cmN(Ce0(>;aV@TNc5lhfpmH)O6y4I%>X%}2iVOmCGxvuN<<74BE+=h zCLh7kJ#oPMa2C8;lNn2)(VlHQ8xvRAU%9kY7}*fnnD0p%CTKA>Pdv_8kPNv5=0?v- zxLBZ_>R4^t70W-QlqX71e{LrE(siSALJ~Cy8;iCQs#NQkEZoV-@-2-0PKF;;D8#1o zEL60{_SYIE-(itK{g2KpE@1C2I`=P~ifa~P18x=$O{hu)w4>i2B(HUw3T|y)Hye!l zsd!4S97XVH3A7({8+nQBecqU+j%$R*8;Ry^{6K+k#j{1yNn$Y}Ywn8>k9q^YR4DZC zB`HGY7-fCj1pH-I#P@$w6P<{uGt1j#GbhKiG9WsszlwaouJ=BiH0Ow&$Cwnd1GWEB zJe+TRM?=99!vcKgXpXNX^WH~CgJdW@Ur}Q!D^6+(I-stI8LJuN80W-S;cOdo*EnJ) zKWUE<%$2l_+(LZ+m%8BG{%B5tn&HTNd#;0yn&xxudDn4DvRLcZXeb&dluw?@Y#7^Y zF*!W1%2i-~0BNb_oVyxi3EXy(Oo58js$~RDf+e1b-7;6rDkiHY3dlQ-(?P2nE<^V^P#tJCC#AWiJmWL8yYy-^R^- zgM+fCWW*;<5c?X`M>c+x{~C%sjFA+Euj#0ht9hG~zX-!)NiF=#H6iNZX>Rfda2R@X z&h_D>dfKTpHkNxuNiD#CTM4LOL;#%c80@oy_!zg_*Vwz9t43s2`>3?I>V*8<&Q}7F z@ws-YWAH&|SwgjDr{?D?xE8wDD^J0o5Myz~0*~508f;Y}GZVV?(`;AeTI0ciIJTkc z4i;2~Pn%}x4t|%@U))qx5`{XBJmrl}zddilD0_UXzd*sE)Qom$9rN1_52hIgc22HK+QR9o ztCdtkN(rrLWqtD}cKG zP5Wh3Ub@gBL57=s&oP%2@YzvvWQs2{{wO={&N$cGU1cea!gD_K^$q;bk>H`ms~c#1 zhz3VJv||^j+Egyc`V(EQMH57$8LWL@48kR;yX^WIUuXD?;>ss_PA)QfDu}TWq$D@n zTrvH_iii5MN&fKVwaHA}WhL{a>9*Y9_JO9Ok7Vd#{@NQQj;W)7O!CbFkH*zVk6IBR z=%Eog+-o|I`w&^Tu&|=qPpirR*}5y~T{ps`9jw(2TOQEiU&c!)#>>W{$CMpd5m6`N zZVEceMGGdFkS!@lR63JQv%CbKPXHJvX5s`sC<0L16-e(c@>hD$BEdM9CDy z6k~IA^$ZmNc7vf|4jk@%Q$Us3TZh8+?JBTxY>h0bYoAMfMBPyKjpS zNP6(+mD32umx&lH$9srMU^k-xmuq(|Mt6leaeBxq3#HAO+v5z{kU$m}q z$%{`pLrXHlfX^xe(j9q{d0?sc4kfn9>D{zV<~?&kT~H-L214FVZwHJ0e}69c%Q(e2 z{kWw{Oj@5=im9i%w#J8EhVFPhADm2U{VAaspS}R*IMH_HR7UI?%}e`0P*G5-sVE+5 znn$=#4a0os@E}|NlO7|)x+%BsP%qE-&}E+T+qSc+{$%NW&~mDNOVzYrGA*tcaV?bp zrmwS(pwG3dABVjs5>r=$GfQbdB>@G|oQGvB+--H+&K?!F;{khFtds=9K6>~FW*mIL z2=-(e{a$ZmF{C0lzI*KKCi^Lh9=iwp1DBl{|4M3v@K{&@@P*DMx$ee-s1pR=0zQ!x z7Ml**y9!K(RsI_P=o76D2l-B#QU($)ir${=7V{Gs!dYrAic0zy-K6$K=14H)M_V?Y zaNzb`IEet7y0kZwEz*Duwge3G9k|7s3 zt~e+WVd)_d1*F3$mS=>{?ued9R(c-};fs7|5Sp)9i5qT`0N>WyZ}=eTFh@uK4pF!I zw0>x3OXm8$pzZJ%Ir7WDly9mWO}i!ihPNV@;sTd+t4(utmEWI%9g$I>vLv^!@bBqZ zG2vkq7dyUf3ZCZE&NFuNR1=x}_-WA}xQq0MlCS0<%jFHMk}Ak=@o^A74}iNE1^6<3V{hqxXV(%k zq6PDKikkl7OpO6>r-6>z)Y@RzL53DBIw0!E&KPxrHOmnlluY1d+gZBbm?fa9ERoB^=labk|>ZMG+<56T-OdAtuez!NE~py z)GBzqvNgw8*rWt%`6rUiRXy8sQQvnWC=;qFC>5~stV-YeUXxs^-|l9#-ee2mX_O_G z>)2TTjEhv^@f~|#NsoenjEHG99xE6eV7s`e`*~^~83qa*IUZuym9;$)t`#$XoLcI@ zpFcKzkkvf}bSav;*O(D?fZ#o#0g${f5RKY4?2CSN&Xm3JreXAn!v4(y2Lc?4F6>M? zL-L~@3r4DR@`JQ0J6q&Jek_R$@n0zJEs#fHmcJ^$c)^zBa}+z~Q$)mL1*!ZC8t7jP zV3AMwHbg#`-*L<9{0B5Z-e(^RqZtaL^j+e6NJ=P-{mM)x%PZg#xc;?lUaYxDmyPlJ zn~UV0{oVhpM?3fnznV^?iBni}en-`V+C|Q#(b6I-y_liM zf$D0Uvg`*YMOhC=snOkUTf#4Z6=UB*hHsP)jiDDTWe)cw@8eP@(UpeBnvx_gXg0|R zoZ1uZa2tC8rTiOo1p69VAx3R3Ngh(`A{Zody+S~ac6HmPZ|SvOS0G7f(v&`caz^DCMd=v1^9=x8vo5I-{m%aqyCV>UlCAn*QB*$DkeA( ztqrfeqGc(GLY!I?WDD10E?_ZGc4JC`;%|Q`*<;(HO~om#5K~4?)Y^^ouYuZSovT%q zjKE7UWU-arRo<3!+DaUUW>F&vDcq&3E7+QU<1Ki&AB-^!h=NoiEobB_YR1BD4h{uM z+XX=3d-!U*CV^_COPa-|DRk-%h403%6z(;IcOjY>GK=^vrk%412)Jwh&G=;3PZLmn%F^$Iof z%dPNVMj_8a!&^51rDmNN+M0wWJ{-x>k{8{3NYuD|7Cf7UuR-BXtG$?UG+jzqKrAsC zEFLTyEEX&kEOV|W({^7Qxb;U{xb^L>4>UN%Q``gsR(X2>q?m9FDP^TpSa_(~@J7WR z*c^O-+t5FZJd9zR1-)fp2l6OwC)oJ)oyAE zkvNf_yv#FJ@Hkc64Umu7r@};psKCU8q=mwyt)XD)xq&C>L5UyOdyS*hB$ub{g<15( zzIKwr$fT6xzbQ5sT$ILZM^gj+-ezl`x|>V$jz-}?C-vTraw}XrQGQSgjm73vfG>I; z5_Xc4Lb;M8MIfbidBAZA`Uy}UV>e!?_>^Fi!{*jXjLL(lxu{PHmiu2|5H(T8b)t5e zmJ+Qt5A9$Mt++5#=aaDy;DY^cCO4GjI;A`>*hMW+VSZB3p-MzJhM1CZoNzQ09!{Fc zAJOOie);;C{BifM0!Zs))*zYr)gHcOdLRJ3M<57r^2E)zMcquE$E98*e9@AV=^7>B zD*ut(rfHX5;p;ZN`Faz{-#h1JW7+@n+v^v;_fr(s1+3=l^#zE z75&9$x}ZJS;%6%Yk(5E}CSGos-s#Z+e7M@?t;13IVP)LDfp1swe(rMWfIIr<=XbQn zTR@)n`nM!{)-wSYfr|#U1wgD%x3Zw7Hl@5?6SKXhnnXvj1doUVC zz5jUBsszNY z6D&*tkx3xgvtA*6DPhajlbQ4c_q^+5i@=r5QR?RHrpTQ8uP={Rxn&|o8yv}GWTTQh z)&ESP#keS>xxge`**+1*;RdVunfB%KYVcwy<2W~3ADKZ z&~t_SsUHmdx4R#mYIzU@_%ZbP+=S;#&lQb= z4Zp#xjFmvJasjNNK7q-SvA7Mzq9t9Vj}WrlHjh!YL@rOQXum%Y~Fezyr>H48vT zM;(hkO!5R#y^`j=h)UOy%GQa@^?E%nV508aDpK!+HSa0y!;8{iVs1T{LU7nDAPG%1 zo5_Mp=>O^oWnmKi0nzLxKI24Gw;8?J7(yO=VZXrHW4s8s_my&UxIzoyN0}M0D+-&L zNXt%n!uh2_)S^Yo?Vn6;b{KqC1F@WD_Bdu7AR3q5e8(S?%6M{N*`bH;cW6vz-Xr5@ zFZd#Y@L$5pt-+wEbm3zX;q`-OI1(fsHq!~P)A*h21njL$U)AyULyf8Nn=|!5f!6Fo z>Fb$H<}yfK@D8jjN=;NAHW~I`u`w4e+!hWuuQWW0qF59JZqm8Cdn{dIK5TGKreRfM zEYN5WQ7JIT3S|Xll4;P}?%B^@TbApV$Odivf|Jt#V?7A`CTrZ} zcaH6I`|JPNymGPsHjfaWUr5Jo>in&Y7VRWnh~J(owxJ_6n}g`|PFgVC|EG1SRXaUOK8Pn_O6^QE+CBa|GNKx@V#gW#yg}E9QrBld$lW0D%2& znX}8kjn+!~P=C&Gf^|4gHS~!HZT!RZd4VgK|LW~f8e{j`=h+z?jbJR^0D4Xi zu#ll#M{ZtP8mbgiB+JJ!P{7Q=S#UDxP;_bXVYR3o1xT=rXGm~~mr`!iLEV(Z{aOg1 zVcMhZ?`RE{Uy0Cy_unFKmgON!kvte4k0#G^KOQRb5*1pWbY5b_dyWeJW8mmvc7M9q zBsk35Vg~dB@Nird1%bNnP?W0evhyZxH^49`crj><&?xs#R#Ji4`1n6$+`L@omFnsX zb2&)xjE#cIhg5gx<4wha{)lRnPAljVa^Esm z-VVT{h6sVXant~tm=L%&_%BL1(7;g&lvsGK?Kdf;5z47AH18jQP0TJ`cc8Oz5XBKE&eXF($$y25~M+e8_b*QF4h?(setcCZ6n`fd^WLT|7sxT|Qm z_W5=r92uAp?p@Y`JD0Civ39F&oaNS795 zrv8?g`peOuqZTvHErh(Ab7a?io5`~ra!G{fl(oiQ$#R(=RYo{?p$P}AZzm5_ZSXAz z+E|o~$73xaB2IqD#y^ma zlUVdJ#zwLc5~I=GpCkt97-7${-9rVrHzv=gzBG2-yAQ2jc|LIJ83Ro+A;fLI;{M;W zmq0h0^-TN!TmUI_r zR7(o#UTH&k_8a&nG22gC-@V_<-4soUSHs3fk?zb%H4zx@;Q-ND-SvX7?RQJr_38$j z`qqX}`5SAHcKBjwM-XnNPC49k#VbFOOO^7`1lJ8W38}KvWDvRH6T0jDYkKDwk26dl zh%L_*Gta_e$L6)Z-RMGYvOsa-ziwI_oMS!Up_=;(Gm-1@ES4*;liMy|%tF>LS~3Ku z%~FU33_Y~;<5JbyB83R*C#K?V5;TjWy2D}L#n{C7I7+R+a+F^)GWP2;S=o|Cr3 z!xsOu7;|{jbdO`tjo3NcB3A)gA^mZg3>CPXxyFepXSXp~b0KeylA~1J1VnU{oHKWe zpWHn8UZ)G?>32g>Pv#G(Jo)ORcNcB=%x`dIT7q7plo^4Fvt$xHv52dsc5q#vxE_zr zJ9P;?OBce-Qve{!!85!OATz^1+%QJBKA9P;iIp!cNs z23Svt{&DMzaWn9J`|tJeAWA|vK>jLFGMykv;tF3R#v{cc#zoWpddUv}J#{{vl)|*M z96|Uz&k1srXSTUGsg}Vu0;AGG7#_Vn4;k~Gc}Xuuc0PKfOJG1l{}U$g5Q>1{;51Jn zhy)ZF9eI@Eg&!Ui1i8KsxH)+4#2G1(A%->)Htc`LbC*DB zZhTeaMF5Yt$#l>V19B+V0T0H-H<@K@Y&uFqyEDp^dr{u zl^qyhCAzlWMnXZtS@Fi-;d5u;a#{S}S9+t3D84=q;(fyvKo^y`y2Mg^jTl(9VJ#(l5=Jx>_zJcvI zwJEZR8U7XS2VbR5SszFR{sTd(K_j&GV@J2)@MTF(U&HJYbEjy(8)OqiggxNATr_X9 z9KQsX;nkxcQVkbBxn2&PY|@e|O=yS_x>ml2db?RZ6?e)D`d$cd715GJNC%huNyL_L2_pj`aP4{Vr)Zgf34iI;<(w}|F3>wBL*J^He%;o<6tz0 zuc5LeE6NdaHTya>KoJ(jp!N>Ctq$H5=otL`#WH0m`Ajb45jg&~|8S&%Yy4WzcRe3& z!6{}$qWs5X6^^WcwaE{+-4|kK?63x6B<8(*_mfdc8JZVsqQI<>0BR!MGvDV8JV**$ zj?-k1so2P)?-wR>RFJ`-GyB2Jnu$9DW8^4}ZD9;E>B&s4z?y z=ez4liXQOx`VXXn4TR^c<~{WEm2l>PBqLPmZ8r@fT*Nl?2?Srvf5Ydt1KaJ8Uk_j^ z=7EjXMg_suL`9H*xQo=KA$mP|ykjJd#VL_|R+U#==vC&pbwwXSx zo0lmKY}-%Tn&D6mo1fwf6SuxLS9;{g-*cKL|1m*aT4z5L@jTyo>fY`@OZUXX1s;s2 z722%UK|IfN;CFfJbUC|6g*KEPUj0|Z)nPzOWP&~YMk@Qtk2`(&-i5Q`}?QV>ht)IL|9rQyVO(QtO+mF;rlTwm|XVJ^P zP&S_!9#ej_jmiWJM<|cVTxZcPdW#4vjcX4|Cj|O)fjsM-58OM)Dm`ktT%Eo#(E=Nb zO$#sHiZ}+-f}y$oAH0rr)g0Pn#B_+bvXk2v;8bf01DWGt3Uewr$w8hMEAczlVa{=k zwsZ&V%8h*Je0R0IjrAb2-U8cy+x3i?YP1$NdHpm{cz(t=xr$_`WlYF|w|1J{8ThB^ zz^5EXSI#=V(#Vksc=i)QLa?O629vh6qGEM@OMWGO>uq=?GI!b^$%XavD2>R^@6r0u zel37dgw8DvJu6CXoyA1;m*g+4r?$yV7Te*vQrpKBF08F^_#>qbw%)n=Yt(II0MF5k zfNZI5N1-TaN?I!O`PBGvVif~{yZ^>J!AfCBt7Yf6G*$a_)ij6pv>M&MJdt%pviX7w zfA7ddkT+&eF%5d`1U!>{IemtT*|!fN zf0MZTfT|LZw^dfKLz>N#Ya3HrBeH{MftYl7yR56NbTvN%L+bBfb@Z4GP4MDFdZ1;Q zS=w!-ENN!yFl!DF=e4xfu)7i7KDcIjE?dVm2#cSGHueoUhCw|?CXB>Mj7(7$h72qZ z%6W-qKmR`KI-#?c=D>G&Df(R7@nVM+k<;FwCk!}wP@M1Rj}Om!nhW8D_4Lgz@fL#< zBUwCacHQs{dAu~dr7?vxdgEBWB_n)L%?CcdCNwE1J0Z$# zFz{YN$I_+CBqMtR?r$`^O0A%n0hJ=VI~v_CaNtmd9Ge51Y9s=SSZ=*;7=!8Ol;WZ8 z3l`jDR`U{?Z-4%_;%rE1RQ)RYc7bW@6lQ&kurdrng+)nf3@G|#K zScb^A^%i-!=Ktst53^JQykRpWj+Zj8Zz}BbwQ7xf+nM!R9QNAHEwIWTiAFmt#Pv>h z7N1Mi&#)r^VD=I-ZiI&~K`Y{a2}1vheOVxezl3=%-;*#4>iV+%YHyNzTP*)a2}*dL zkq_qf%a`gdLwH$Cc#)q4+ZK4il~^@b!uXKdroK%HVbk&zBFG~a6#*w)eA^?s&M$?4nYs} zwzahe{0?W=BE2$o6qyYKKQVEOC&nd$gK$RV=Xzs>>SN~A*TxjhkNQi~I?-#WCcyaf zgGA2-aAnt$?F0%H5U_674(6qk<05yGcjA9d(K)SZ;M$o3h!#gStD=*e%n8fP%>1IZ-6qd9ej$H{*j~!*McbyZTjoh{# z<`S0ap}Q&^>jBsdJx7hGA%C_;Hq00|m!p+jSZ`)Iiu6xRb)vtPfOsl05)kooz(b?* z>JhbH>zo7GBWoJ^5|)9K;7Vg-pi?e8 z%r`8|Etqv@KH#u%ufpsyU=imJ+Q0e>*$J{UvJ2+hM1j-X>ciqwbci)SM}BfY9l>Mi zkjbH(f-T_JYVEh_xHfE6%}e3p5`C7Po2Z%EoEXfaJ|tW(b|M0PSo&zWh026Td<5O} zQLg4fW``WtYyQ9cxi4PFU6*bj#k|A*$NRjXB;3d;Fl#N5L&^7=8By6s1E#cZP3SSyULN_x~N(5 zH2MbHlw|kY8b`I;6LwM*tdEeV@)r06O!%g3xV+4k`Dj*nVci;H$!*7WF8pyDC^7`a zO8aMRkiM`JorBBCaWXTx7}d9bS)X#CoPEo%k4Ybe1GR53{xDB`L9wuRDrteOuq3DD zv^*X#>qy;z&DE8chFz^X5r3FpSFpiuSX}61``JLO=jSs{ zA0atHw!cY}Y?6yjfGuv1s&gzK?W@yznzUNk;@j~uQ6>uF#96NDTV~~aDPc4zjQ^59 z8X&mU*#a3vlR!`~IcKt=D``aN?(RCFR$(89^v_iEgD8^_JIHc~kY#h8A-e7sNRsiB zcmJjD&E5IRWZh{z6uWJYSh+S+p*?D~zkGnLRPN{R)9^;dm%Ka2D3apCIQOu^C|SC*VzL^&@lYgrjm72PJHy2SGYPxWJP|I9-=+pwP(nxmXlGea zb%%FZHr=eZc$~Kjq8)z(6 z)W>sTA{)uJN3_+GP;pUECVFvGQPj`=_J9GU{4~FrItkMP4Grp#7{M(Uml}SBp6Q?M z`grGGwmy*3WYD@Kve9Y6EQlO1ntOJAG8_$Ryy%AltdqGyl0E8Gdc>!4lt3KkFXq-X z#-Fsz^*CdQpA#Jmn>}VI+I!z)rJ0Xej9B{*fdIQ((LsDqM`^@5t$8F+sES8crWE3> zu`lsO2RRzL&^44i&rWdvWHfPZXXy%XHZui<-&v5oB2;5}MTJmIL|8Oh>WkF2IA1=! z5wT2#AwhK1@id#fC4fpC4u8^@yF{)pYz!h>y$gKVa*Xv6;S4>K5))iD7}CRF{t@Y2vKUEbe1Y8(Q5d%XfJiJEc%^ z9e*JDWls=V+KAGrR5iMaFmz^EAS`3d4Vy;`zhA+wHdv=D&(sE~{27UuN&*7f(vUFG z7YVh1SXOjWC>&x1r-_!V(F(yLmeOV9=ZI5(NUGQwE^~phGG2l<7h>JmaqD0`=K+-x zN+rr;D}mY9fPz)S!5$AA-FJM1h0DO6J?6o-6fSvZ4oz24eoCTqwp5QFbG2 zpfzSeYi2gc4(d?S^PSO*cD^ z?(f7YgyuY4@hx{6#530a!Z)qj_rzO3=?np6iO=- zqJ`H2e#ohtgHv$}WuQDEssrTxNe#T3%ybR)>kp<1z*Ewe@=;AuZJGti6;RSwAk0Bn z%$)33alkAn6g~X~6B3F(cI^?p1l!nXhKka{ofxm7NGA9R5|2nij7~Y6f;m4*(ZVi2Bmoh|eLJGcM9hP1bGM z#WjIx{%nNLH)1wwE!^xCW&8C;&}QMe6rX9VjMVn2v_xD{ny>v6gIPzJRe0|Yc~Ye8 zX%+RMn*05AFVAt0rpDkku|I1+=&1JYalg-(eNzsOt!?6}*c)y(7>ZDepeZ;11@go~ zBYXi3(REew-pO3}jXIpn5;pseYc?;p)jy@xn#BK(R&qJH zf0KIrP5q<)j{eW2>tt{(f2o}1JpB@t3l4D6y!TuFm;N;a;o05IUvl#4x+tovjMVAY zzV85qDy4O%k{oRlQSenTWe3vH`^#&$_J#jY%MJ2cy(I)^dm{U)-&EkRTD??bJ~n7z zVYZhn;NfmE)%VCP4NdNc$(E^?sUmc{6~p8f!}!UKw|bt&Z4@B1`asG$Ay(TqueH2( zmesFojy-v_tWUpV7B=9&wmr>uB&vt-&JCVUBb4d-Q9V}SmY{3bH55vC@iDu0;lFud z%Cm}JID$0(p6@<$Am3n{)ri4vBa{|`Y{;(Z!ed67{0iW9tj&L)SENl(W(LbndZ~hp z%@t0upiuoSki-&%=)rAe>KJmV`I${_b zDa*V2{K0akgVCPL`Z&OiROyXQ{kqcj7QJNBo=(hs`VfgP-Vm2SeCscngz&~uT=VRMe%1|!k@AO0^Gpq=e&`{# z8UU&w=hHJns*Et7z$Q|zCGl>KIgM%xb*^1jKRU&!=7sK=+zk`N2DA_obE@2siMM6G ztE+-Z+{sS^m_iI+hCq7^3L$}V67grrRJZD5;b;@$0*nX(w`KPK$}n~LUygk z&+__eoL2L~klY013A3&3F>a7%(aC%7?No0;$3w1CCQ82zv9moTYNMQN^~Bp7&H}7? zLA%U(Ef{AQ>1Ru@Ns#kPW@BPHDxu#W4p@(iGg0h@#t&oY$@(3vHvdB2DFrg(cH22G zehs3?1V5q^@cJ&E`D1+ZGy{vA%oYv(k^M$hZuOrAX&t(TzPJP1<0o7G2P!K7#}FlD z=J5{wek}V!c7DFh{@jWDx6bvzRR73vVTe&57hWj>GQll3&lD?Bfyp3MPtJw*c`t{r zDi=i{AhNR^0)+Nq(!HVuaJW=?)9YS*;<~HZ=HIp>d~QA-na*PDVQSz+7cbJa;-BYY z>^sG|SPr(9K-;Xjz3NcndF?e!YLq8pm&=O z(Ut=>f>&Gf9j0zTjgwPyji7Ne?R<`{J7R5O>joZYm|P+^o&8ZGKb}#bi}}tSVJk%j z000lKyYHynBISRDq^D*hQ_-{Oks$no9S98zO*2N+S!CVwC=>TpYlKC+QRDItbl(eq z#xy}i+LxaEu!qz}YFHrk`f7WSC0Y5NKSX+}Dbt5e@Itw5K^|;vwae7tHc5}u)}%RP z6}*$=ExNYaV}9eqw`yv{?Kk7ktv4=XxK z+-zK~&ec`Q>S(pXTJ#9jNQ7Xeyq2hYK(+A-ly5uecFzgDF%7~_P;NB|%$YMA*9ySX;u zKL6Hb{wub7lW)5hP3T?r9S!~v*00In8Ro@Z-;U(BacYvgP;9eZ0nWQf zr>}CKbCEM5*$Q;6n(S&67ps!_d-o?Q3Ky2m4SU_YO$kmv>VtI4EC`~1-$~_3)BD$O z4dwsCyh$G1pZ(&q+SGkjbZT%Xu=hJ+V0|%DyRogy6IA}wIk|Ay*WkY?lp^I92}D|j z`s|ZH<3hU;W$f)(0gBJbFfqw<)04fGIBb?NAka~7Xti^(tUu?N*_Z zzAYi0g?23lxUJNhE>|03x7LrveQeec(l)rNOg6v7*`Hlv&7q9flgCpx!uxud%2=jo z*~w)S82cmBT9>LxKPZSM1c+!VOV4( z@e3v*n2_oNFac{I%5d`U22T%-mRG_p=2L%Y^~pj7jx5U}04*6C3qt|m^!{?M5dZ&v zn9g^1s3{q&28=%aItn5zJ$OVw&07s&wTav&B1^?qF=R;l-61{{iYat zp!d5r9x>lNQoTMENqyyF5WP1HmcXm;I7wjies8W8omO13L2Kp;m=E zgs0ZqtYBf&jt`AC2xp$Ove!EGDMwA=oLikpOPU0tMxDP5bg!bqJ07qO!3B%<_feBl zkwd8F{|#vMMe8oxu6dm?$a->=5c9suHaPx5UQ?3mQcY&m=5k2+uFez>@oR-4Ju<8h z2cp3P5quPr2VEK*&bA>+FRsa_RxX|_^*nf#TP91P%YNVPrj~}>esfkJ;XV);`&C_a z!qvI7ZgBItYN_VbTC#PJiJ&bn@tY6q3@ZdB|i|ohsyMY^kvCN%~i(KI0Q;h zZZY}2F-^0PSFVj$!1v<#?&KSKYT7$aQyr1E5@hZztAF}y+a}+`5`~jstQkKTG)tW~ zGZGTF@L$Cb>A!q0oPfW5Aw@hEcw3(-!{sMT?>&);xP&~QhH}t9b&7^sf_5wlH*~kc z$iLcWK;J?i@m)r_W@B-NXDgXiXwoer(@Z9%sXo)7ip9xrG@Y$5&@-AU*o@m@L-F*9 zA6mP!TD<)%>qbHuXEic^jj{535nL`B|EuB7>Y9i#ZkLr~mu>p6FOm)?=a*pqu?PRv zo10EsS?>8L(nUqhFR_511#HEDo_^iNF*F+qxq_P9yNe8VwdW?tZ;6%l+n{?{rn z5Q!+!=2dAw&z$guX$`bDm}8Jwl`GCf(lg6r z%QKhQyzdQtm1Rbll2OGRs)*QhC;iXiVE9m@$J+V@B0smv)GMJntFnohS&%^A=p*O> z9W3#V*M!^rI{BODZQQvh$*O^vzk!6|bN#HBn4l|Z-hZ%!G+g^2CCK8rnGGHqi0a01 z;J6`5#O?!R;Z89CT;m^?(N{$ z7>rj_{*F$|vBEVBi5j=Yh_xt*Z_!Ed!W{Ot1gNKHO?KRJA;5$b(nc1y%g=7M?Ur=E zbMf1%-e4t6V)-*%STY9+fq0fC!8=CL;{ZFq^HY*lO)fHG6pdAm?|HJ{Z-6aT3S^YA zAJ5DlMlbCeL_g9bd@h=t4{LseVMJJM3Amh-Yxj1z$=C&dODU;o$6nkIl1!*0Nto z`RZ2*8Au9W*eI4(reSH5o#L?5TqTeUR-^a|!T+2)U1t}MtGuy_t(KT;V^-7FIMYTs zMQ=J%VgfR(gY#p7L2(mPl~w6*iM6>v5jQ^j&JFARUe435nq16pf?}rs&jL`=V$1G1 zCSzu4P{Ms>uF;P9;$yjrI!Ipk@AI~HsRrme?qzg1#PXNmh51-(dT40d;-scMTme_L z6)a2NgTu;Q5hlTyivL`&i@klH@S;=jOG2{?2MF8pw&vh+BqGG&zTpTVUlcB(zc2HM ze0C>Hi{cHz#|N?q6!Ai}Ghm3_{u32YPde*U&8|Gu&uo0L@nZt9fh5uOba4P8nvwgm z5K{e6HN5XygKAk{EuTUFla_7k>Ecq?Wv>>(L3)%;L_v%_6zUcs&{(U>tuikKLz|Dr z!3cSpC^%WQN_FGakDbCO)4yc58@CF-r+Kh2;L8qi*tmY#xb6O52vrcT_s%W97CO(Bk^~HA3lAGg^;M8H(@7g2me1-bO&5i*KNXn&w@&%7IQuLH}zaRPU^P9&heTJYFjO5)z*E zj<*YX=Izr0%IRHSxFmShtq1An0G!+~)Le6Xzbu>Wm?zE_XQ5>Y&&um~G?(qz0;-J^ zE&d0wdPuk!C!ta9u}=svzDIavgNwU=GRa>r{k753fniG;tj>u#(U2^0Ac@DeXO_U4 zyT?2lCr_IhiGWG~N2Qz}bh)(P5$^*@OF54FqL2haB7{r%bbPuO4#)wQXIiNK*wx*DJ?LDIbU-93R( zi|pWz>rZkZ|5$R}3+RO}e8cWm=0OvWk`bk@Z?`a+ibVp3>(9eq)j!qs%2)dK*I&OI zUem`07ys6^V+>5qL$h@Cpy&MvUSp$r-?Y+7mY103%MNGK^$zHgI_0+f`Q9MLnA}EK zap-A*#3YJ;#qABbWtAY^YG)WM%@{Ck2OeHc$eNQ!HpKd)ZN%Mjw6;$xsd8UtM%U|2cHF5;t> zyoeAAgszcHWZ-Vh$8^B5o^Z2)P+cx7*ANo&EU_50v<@|SJDq{<$NG4(fp`rj{JU2W z(oW9C(CQPt+_2`aeRu=U3&528 zfJ|aOXTNWs4o(cPOyANUl$q>Tmt<1prZ!avtg>L|;BO;qrfTBW4C+Qn&tI#xhgL0F znG98zm-A7lAenV&zU6EcDdn)BHH_yYS#&2#s7CT;F#sUr5>( zga%1*wErFGYj31vPR9Efhlt}t{IQ$0TTcZXXiN+&^c0I>#@-!?>C#`{6Pfi%+yVFg zPd8y200AS?(bau<`@dn`p0zIC$4F(iU;U19A&C*_F50h(A zh8b^3L9;mFe9S9RMxey}P|5*PxXVA8RQ*8$C&Qxxa$Gl?=h8)etOlzEk*?2(M%SdM zijQ|N9!Iwf{uw&yflHf+1osR9TijfIo^J?$trkkdI?y|@L1+`_+pEM($5(v}EsYjW zh+_vxPhDD0;Lnp|agr4XoJ2ZpLH;fO6mZ|o#)NdA%FZV<`60Ti5J(nND(jZu{N`Zn zdSG0^!Dx&J!0kj@x_p_xX+jhzw76F2?EbZT>if^_{K=v`2r&TNvh5IAc0dM69rBNh zz?3Zf6ldsYq};pgAA-0~vT(18GG>3z=SE%NmU)2zSEs3=cKB`@wJ1ejS*Qs)t%AS( zD*cZ$o4FbvXnuGNYThe_rPEbr_4tDW$F(+E;x3RtTGgI{F3D|gsuhck9RWWQQcp-l z>e2?`10X^Ps5Muap0mJY?(IG=xLQOQy*;47X3&&)BF3~1nx$ZKH=a6c!9$R>kJQVK z72m%_-UbSkx)HUzwA&4JjW)Ai!h)eXVrs!($JE0_+3+3mH5w$B%eoAb66X)HeuUEzQQ$zYBu2 zP&0#S{ocHMgdV@Y*15vkHE##31kvep#o&)p_V7C1MIeK(&hlT2Ra*#F>(5i0w?0L{ zf5_)jt;8p4FWsG%A38U$#sXmS{AJ^WEB`z2I*&i!V5vi(Mj&?)Xx_(SGi(@dWseQ@GvU?15hQ zC#%(?HPAabi@$?K@^w4 zQD63rW&h;bmoel7UBg9VIr*!=+`^1b&*i4ahVPYeJ#!+PJ8-Zh=sA&&O`Vbf{$ zxjs_7&-9f7E(l%AQc#LYih+)^UW7g-te&KGu=m@WPxG-rkKCy2Q7*9}4 z;){HORLS{v^(W~t8sIZ8F>ig&lZ`iA?|%#y3Wk5$3nm5_0-Yo0E{YOhSh9;Th;ih@ z@3g#rk-%1?m;NuH32i7#G1z$Snkkir&e$*|odh6TXfd1bwRzxQ?WkAZ%R$=us$P^j zwBYB97f|j`m(c5R=2b29LaC(Tk;7!zbWp>3kE_1QQ7mkUi;XA5X6V}aTnNB0L6)M~ z!oxjFMEF*KN*q9^OYy@AQ=X%DN?J`!W8E9y4g%?DblFiaR_O;(z(3}RrU&t~O!(E? zL)Tj%5vmPmRj3rT!2ZJ%Xx}Zwc$}Ai0s^1Z$;Ty21P}35~rT_j= zCbvbLI!=bD!(AEzsEDcX)5J=*GV5qX1l9k;@Vx$84S`~?w-YK1WcNvC1DaRSQ^eu} z!VRS6c7BHb!b97-%2!4WH3e3_}gfaWN-E%QvP zP@{Jy*K2})a{V>@UJDTF%+iOFNxl>Xr|yQ+>mwf+l_GqPa+3DMgag58f{=jdDH>D< zIIKGY?fTb1hiSRvBWII~m#K-88o#2!>T%Bm-v3HXygVNsptNhq(VBK;ZB3kHXHEU-0cREEBG;++=Wh5nFCrE0!t-{ zh3|hNDz*R=M;O!<8GpghDdW*uam9;^*fnN)?*?ep=?GCKFRt^u!}{C7aM>Wdv$Y*o zy=rvP3#nC(g(X=(Y7)U?s8oLWnaB{fOFgz%oEQ7^_5N3IK)x_lpt6{#G6Bj@EVSn^ z8O$%-B(1uPh*6)WAl$zR9Q1%mp(rF=|am9dt2V`+1b9)OE>oxud zn%tR`cmnFII_NxA+MiC{Xvrob{US&6%F}ZGpm>O|k>SB;(v$IlvTCIk;n(kKA`mhU zjgrKMV?k<^@tEuHowM!FtM4Bud7+6@Wd5y|;&4>=(TNm9m9mi<({Vf0;(#wj`Lp@? zKot4c9!d;oI1PPONgb!E=YA>;LtM3?>{URxnuyva#O?n#l9g zWBxEigq4Sfa**gay#d26dsdT#{#v(n@Z=r{j_c|`k^1UlAl{IMXoqfKa`;$LEwIqI zPP%OP(Ak5eJ7BodDt}SRNc)4JTj`|<{^1(0CHVgxD7@bA69Yd3py zD+4TtS%^WNIIRR(I8zI$t#${Zk0COg$tT+zp{jP$Sa7Yne}$Wnq?@&mC}=(%ajiSC z>nZT`Zp8}4alqJZ7`#E$UWg9}0Ni@)NVi=&0W{uPIeI>n0`V@pv z7SoWcU;*a5X5e4GkuIILMi3dw{eh;7Y~Br|YR5cBLS8NhoAvyAjBAhk1$CC4g|M34 zJ&)7*1bL^kkDI-crqSKa7gEs{r-R=TOeOJltv0HzOcaUp0#zKkdd)&`3KMIUH?4J0pN~O^X>%{svO{{$9wDzHo$ZfrIEP zl7_SZ!)LV<$G&&%>z+wzo}*~ztBZMerGlSHWGH*a0`Z0|MTX!98e=4n7|Fq_TlXVJ zxMd#c0-2bwLl1}If(6!zYAY+7Qm*K>w}{`VfI6%}f$J@U^>C2ul5Asnh^L^KI`5&qYeamX!wn|mW)!j^4h@@^FQpTVa9y#JLX^MspUK0f`H z;W1yPj*f^+?eG7Tl#BkgzuuGc>aLC%v5)Djd*!O6M&*c9p2LM{#G{YjpEG$!dFKmv zLGN(f(a!s)z0s?q>mx`!C=y_rogd^1ypQh>63wcYBjg&`tEtMR`bo^npYW;k_rSuT z;mu1zkocX($6ZP?EvaNM^8z$Pxr0+xFh_FR0d9G@q{mqF2n)S(rzd7^Rs?|e;`)fw zd!~P~9fCWAs6DCzk>8V0KqZxinlmj@5OsHw=nq)(%#MDgcAZFSO-?ic+&vCb#xEch zlZ5(1DenE4FotMrT+>CL?$vqJ-rF?+K{;x!j`3b_# z=A%M$dMICl{?pd^9#!`bT&AZLfymqc)&1+>?gHxdRtil3%X z#}Lva)o*hlj&wqKKUq}t1qJ#CLCCd0141>KPW+wGQT5nZ~c8?{!`Ko%9C4Fy8 zVIw?rLLM$R0xxn*L($?Y*473F-i^uyw!;h8P1^4jk-KJmRvOY`K6`0d+{c$AKjAcR zElVO~<61c05mYslzT#&4BSUn-vUq&@p7J%7e9bc-X>9-d61duZEaJMmFs5-+m>{JY zKm}-jufcmyv1OmRd8c&@<~(As3cqO>sz8Q7jH#vf|H8|@uijxfPxhuPSG{Au1rmzI$*-^WDFwnw{NiXy!uEEiAQn_2AciQWv(|&D zi$gNmA;=N7os|oX`sOY82e%GpJi1&OC9*Vx-LaI4k+tu9^(6^Bixl?gP$8DK_mdW^ zjA+9L`!NN7XKwkV6Osz4c}!ai&PJ7^9v6P7`Ts}%ll>-_7V9PKzWZyEN1u8l#ejB} z6-6FYCB5!4Y5PPW?KYdP;16j8<|oVH!yDo2aTTnLBMcM}8rW+ctLlTu-T}bj-D3~^ zlrUt^bPxj|l2grC_x|3jVY9{YnH#->JoR?R&lO_FV{G%06za1EhE6fVaUe)INS0Rl zzQ+t;=x|2-ER62J_#tz*uclA;WJ_KqUg%#6Q833Vn_0 zdQT0+Y(hg;?XbCdEtqfr42)A<>Q+Ty&_jVdDl72xcBH^^61x*|q6b1RJRgz#7I@d= z^cAMuo&)40xd+>wjMX<$lXra@eSpVh()V6%FyP*Ygg~TWf7XlRR9lmQ3y>9$)62w2&3P12*eJAa82CLR>=R{ywI+t;f#kTZ zJ5C2|whfxNa11da9}sQCeUl`va_Br_G+`hS+6)i;&*8X?POTo#jtIcvn50CVsymE$ zni74IM)ekv%S{q^N0IZ8q3o)lObXQywHU?mA?cXdZR<_aEnvT4^!f4Y;MH0fVqAQt z5Y%jxBge&avB4q5`T7anI?7zSsTxp2 zLURwzV*2`#JCc=5zn=LnL#?zrJfsorcP~VzRZfS1PMl#28OulFObfd@CVEpYc8h^w zzINHfwa^)(b|c(pwuN#{cHyKYi07RT51q`JOsJL2`fl_%fSbLSfKI#Jr*LRA#t_ie zD>0{9880lzgm12|E6#dVI~KHmw=jSWLSOit)wbk8!kEWFuUj6R^6H`s0_UzlZZs=Q z15ZO@ln*G6=ZK=~v36s`*2`eivQo zri6@hw*+CFF`6N;%S2(E}_5-6eJ>Ir~Lp zhpC*A#Mzh*gMu#%j7HUG2@nCNOm=B5WcPq|XAgBIGLm9OGa<$Bmj3gnL z?)UA>z07#d+LI|4*gycKD(V@k+4PEWxy~EiZBWwf#&$QzTQW;5Cub%)6rA>Vq$e3K zuEZ^wS69cA=xco3jPVDy4-`tXg)r#J+>BV34R)oU-FfpTjQ|57FPpw5FxI{kjG!R& zLc!&S4Ds?HOP`N9%z!HC1g^Bt3uV`f?i)$jAURWBdpWC1Kp{8}jdN9`$$-dN8SGb3 zvR*H6J4(I=6$h?ar;@QK4dUYG0<4C(Pi4?IK-RLG7IZ7M^A|F&hf(MtU%bvTUw zOM*$ai&rU0D=C!mr5}bU%V?e1waWpkSPEqiDc^kH>mPv*|R8Y__1l0N?O4Xi2t`jKs^*N=lrM z>hW^Qn^`T@;}18cg}n@J`>h58!j=a$&rpPDY%K*zIcX_7P*^&+FR3($ij=U)R_v>4 zoS3p88sx7fbI=I3lbdInk5|bxZ=mBdp+|r9iwc+RsK9>rhuB6>u8lvQfJ-*9P-qQYk(GX%mLQXz?g z9<#kKk6Y%j|6LqkSbE7%xEGx)R`C70cMs{3|FyB!;Oq@)K%>5jgBN>f&b?p!^HIUIS*@pc*d5+)Rr+xQc#l`%@AY5-eKIC^uYk&spF2##D zEwqg!clxw^>5CtDG$6{V=TE{T#+RPY|KxKQ!L1eJZGM-)#MQ+TSd~j zwdSuMZH6XZbN!}_9gt5>(&FmT0v2!E6%*ae&<-Tu3#=fL{cQ38t%Bo;#RXG^8%*X9 zf4PoAx5900kfP9qo2>alp_mN*lV?hS36@~1LwzMNy_16s!Vg&_;mKfY@1Mtlyvyk< z$$v#BpDn2dlGV_8G`UjmT88=R2wXeb#XY(9arCmTaQONUbk$H?y7ZwMn4l89%kfk3 zGlraU%3+kzDYs~Mldd3Gc#v=H@Wh94*15D-pIUUR!?;T<@T|t3wmqv>IT1Nf1PrWzhm7QlXOe+f;UuN{Vg2cd1$XY z5Ec!rSP{0Gixs!tdvJ)G4GPJQl)-4-F_b;vcER1{Y!`lUL=h*%ia+1^d}n0S`+a%0 zMZfRoD^?Aa;w0t2k=OR2A6jw@!%d?PYm(IqE%)C^-=;nvzLU&N<8;v;kKMryaCQ7X z;7Y4mBfX5I;b-2dl{qSwhx+?x$YQisg>goV9Ys^2sW6!``YIRB7fjDRA6(N7!lWXf z_w(`Mo{kQxPdnFH#DBw@E+n0941IR`Gj!Vpe2-!CI0KD+vp%?f=|0B}?CglYCwG4M zBtvKXfW`eT2uheihe`Q03Zktv_I}#^cDSPuOSZi(Azaib{QXTHuK{N&p2r68`QO|Z zk1Ru>?dR!>Z2UrYWKF~zxITaoy2lZ=`a|YC%K59l0bP|pi~UwY{GC0zh4|)VJ&AYZ zN+FGyETxt7^Mk==Y3t+7$9Jqw^AGf=VeP0=wa?8L2sy^sqsZ(Xx~gq})_`NBKB_{l zF5qnTamX(TZmZmP(wRrPG*|DhNh>f~>;KOJlx4-~b-W0>&5z;Q9PlNDi}nbwOM_Tv zDUq5$KJ^mtX9Gp^KI219g6Yv-^9(uxi7W{#qsTo+ojfWfN-by;y~%r#Qj*dNYIfk^ ziK!G9=uA%5-+XaFbOT-g(p3{3f^oto!;RDR?qn1(B8cPe#ZKZ%QaSy&5Mnz>O~;a5 zSF_{&c~>Y>>W55t4wR!aM{x|S?X{dnsh_BLNj>s=dP;e3zZiMxvvxdp-mNMvo@SNjj&J3uvoIry(osH^ z&^g{X>C-Ki-V0C7a9$nqlm2x%o|RHikc5Oaz_c_1G77LML!#Rg#pZ;97`c#kSj`h9 z*S6ZYhFt!|A-g8+I%UT@v>A)I7;G`6?(~r!Y{8Pu=I`u?V>#LO1e$tO zqMeO4Y~2H7i*1{OO4P5o1Rro@OIhY)bl|L_YxP?3b@ZM;7jPls^KxQTvDq*Jy6YR2 zAJDL>AE*qduE){4U=rn8zwfeC95zR=6)+6pUs4Y0^!N}v!KfM>GY=g2R1IiS8Q^E^ zye5Gb>rnpUTlIi+LSlVrYB&VMsWV>nWh^iEE_G5Ta){*nG-fZx+eV$(5?DT-&4`)2M9-nHZ( z%@Y$B9cN&&Cgjvz&wB1Ie^D!f*!L*dw*KAiS9|%zS1R%LA`pN`cc|g@S9h@5uIL2( zNTP8uC*ItM>n4Bl25Dc#&OPRakpxJ#Ji(8Cph;E)-}>W+C(7di6)oN(i1SPVQ7@qx zz1a^NhYck{0)McAMvn+Kr#m1fqQrMiPP)Dg+CClo95aZa7- z5oHoU8$CZ(^Y;G21C!Gz#lru{gv1lxo-lyk;g)0&2!gghMh(I z)ie5?@Tr}x)FB7gc9LvR2V;0{>P~x<1zj{bbGM^oQ?!OWL6k*~ zB610*(r$JRmumJZ`b&bE_;q1S?fgQ=QjKWLLaK8Ps5s77`s06EyGG5o?V)3|ORCLB zPwACp2~Sw%&Cd#{NR9M*%zqI6*eKNnXs=?ijdgJ~P|5H(wC)!WHkEWr#fUx7D228D zhG*z&^J$E)|1o})uUeV4^W92o)9uynh$-2f-kzgxl8PGZ7RItWC3Z}qLCXOw?beR+ zHE1p-U+;cpoNxeGOv+Q`^ zm|zCenZK_?W62ivW`o9V1EU@Jgpx;TJ9(b62z&0*#ea-p zMjL-BfiBB7cXJ9f1iW_}x_|bya_Y9hq`6++o!CtWdCa@yFK%P~mLbA%8vZ2^T6I3XoO@&Gxb^<{QE>Ip`5ThWBwmoh%|U^XH4$ID!xE z^764DiD*O$s|}d!_nx2}`JZp+x&tNZo{_;nWmS~pCw1sBRfz5#2@m2ZrLk{+a(R==*ZnM&lLnyiJ*xgky{%qStg zz&0Fo!dEt@2=$d7^G~SVWtO*&RoLZTqE+Oh>VwDdtZLM1cc$^b<1zCK7^mmM$Ph$f z;RK4AzZ!!XEp*LsKDZZ?l0S1qi}WV5(158Z>B|(NhfwB=RZ8~N)8s@Hc>?CE#2NVh zJXr30pK-8^zcxVoi;J6c_me!gnjx16J7?@|gxDYCGs?C1=$R6$gc+0GN{JK4$CJD$Gy9!VrfQmeoAO~qZT!UY~hf1-kJm^e7AUP)6(cu3h!*AN* zfU<{y?jM&+`vNJMu`avmje} zlM{Xpw`G`hnp5Fs65pp4DEvw={KuzZ!_WI5iE2&xPqhjCc}`cbTfQE;F5>Qi^SDB7 za|?8;KU&T<+vf)VEj>w*-A`fm9Mz=fmsW9SY(?Vh7`x6n2H70-j*tq-At?@N+F13T zXAWe-6J@7jWb7Pv=}dp2p>`p*LTZWd!&uRImW{ywH(TbctNtZV<5);*l0J5M8AEb5QFdNMk>A z?7T~#7r1t7&}GF|lB3C=?Rk__DIwK8VH-HsQeR^f_Z(_obd>w2L}@FDay4_RSV`%2 z^Oj0aCz4`o2N`v#>A>DMKSCEB?78VSdL_O6&7Y?F<|x{V z7n1dnume0+$hBtS_L@~e+Sw~>YVEAJQNc@30(GDg^Y+|C}= zywSlM_=NSgmrCR(GiQ#@**5ALjUMQmb%cvIt<9#x9Nr^K67Tb5#z^lx@L!yQ+vzhn zij?V<#}Ci{Um8fNGzhC@NLR-v3G3z14%dQgEbqc<0HC*gn?CkneXL#Wbb zO-^%C+EoVyZW|`^E=g1b3MI37TkO8%c&7SQHn->Nwsg*r>S2VTmqWHBKXY_@oyqE! zt_aRgFYIP}(Q)wa#_ zm)MC>pg@aDiTF8>Ipf6$@s3e91MLD=>To9Nkz)~bmlpQ0Wb$X7*r(8bBVEDtFI|Vp zs;n`N@Q*yhg5kC6wB#wx)^kmPw>~rn;U*hf)xm50oCVn}w0nO55t7#|^f213b<<=r zsP-+FZ>~;c1>k;4L{yKzMfFF7k@t*`PO@&X4~sj8b3Yi=tEG`W=8sf5#`=Y%TRYve z4%A||kpsR{J%iCrn4pQ63mLjkzI|u$y{OO6hVsyLgvrrUt?N!MPH&I2E>mUj(4TNl z`Q#_@@ut0Q`o+^-@-%~SGeYxFr?UA5%)?Yr{vMeAov>v{%b)u&aiQ?EQLc00%)cgInQ~{Iq$Qa z=bZDNoNO(3K2Vgq{G|xUAe}T9-qQcBx&2M!0B_i5&v$m+NhzDF55AOY<-D019=EoS zQ!l?V8@aH_s&uP18GGI|ui1R?)?`Vf5*~Ga<@$oBKl1x=RrJTuTEemgPPqOl#N43>GS@=x+;=^IbGPR5p(F=YFPZziLs^9&E8`fY8?&XI{ZJPX#z?Ql zm1a{1ao37VcAqoaoEY3OPSWM;p{jTKlppwOEwG_UVZo&v1EZQsv}9f1)l(Im@khDM9BIxYPJy9T$ zkk}kB`7V645Uz21S$U+)b<}RYck>)RCnuG16Kxvg8M0M1-2qW31uHLRG8V1+y3#`Y z+5>^fOJiai= znK)DraeOFfJ0X-|{T62H0CDPbEbCL!5KX&keQJ|w-<6R(r+t`Hv@ev=i<#F33C8f= zKCEkN#u{n{@r&sNeNX0bbFp*nCZ_V9dGoZkSFr?xDbEMH9A1RRgdPfSyx)$;1@@X| z1J=6?cRtL|*~``x3b-N$M@GC17am$6w=_Yif-y;-)_pOoL+b-e8+A)h6Mu0X8hlYz0lW?HEY(3sU=}SWeJ}3-aqGAYpEz(*sv(?7qGgrRCh_O;( zVzXXU=s307PDBfDJ$Vuke<&^|v|;-cMqo!6;6t9e)I6piu9z}Jm2VJ{Scp?w1Rm3* z%_U)Y{GnTz^Gw}~Cmwt%q-5?!pDcJL6*ZAx`)*n2x`&dkMRC4-2Lyc+^tNpjr;L#^ zH)8%HkBJ|A(Vm|-=MAj^%yxJSw4Y?t@d+AF7jVkuidm9g?+9=HcxQOv=!@sI&y~+m z+J`sfb_9}e$vlb~sl5J)9|Qh4x#}U`Cjzvj5JtA*{ds3&>`=dXQKEkG*v5cp#$Dx~ z=MdD`vMWtSKk8Q`NB$CXs=P0ywx2TnOTVC?PZxBYLWnAUrrxLLAXoi8jcBmXZL;!d z6!c}d;4uZ4!qbAB@LmUb%#aT@0++4dA|kJPn)v#Q12xP%#A!Qq)dh9ToUQi|EM$Ke zr-Gs4+mA9L-QhC$Ul~f>lV%a$$29QeDr~@}wMe(!c z0}wjjgu47Hep07IYJ75_h{^nWY$jlilR_H<7i_vo5MCOcB5`^a!do%V50v@Pp<-&L z79Y?F>E>mLhVs2WO5NY4m1fX!d#+;@m@{8;A_av$blbRKb9*+w!p{s1R0N!z47Tbr zv?N_)%q75S$^0Dh1=avbs8Ox}VXC`(vW>e~mz8`0qpqkZ2i-}?N!mZDIyZWwVOHObJRo+(Sk=cyVU zsEO=q9hd2m%=(?4-b?gF*LUT&e+hG)K}h=U?Myj|I~z%4 zgh@qguMl~l26CpZ_d~NdWzPDONd)N(8#Y$BR7*}?*j-KiZY^%5TCpbAU+;B6!6@W% z!pP~etM;$bXN=V6+%HdHWhJ4I>~Dok$7YqF^A_(uO$}6yVlG$|TPfAQLM6THL=6|v z!IEAe@fNd*$NW4UUDLww`(FO5HYwT$4xs8E*9=A?UgHM0oDN$+tD<4QvT`H$lYL@uC?L8!%d#L#v4t6R8)_JXEKk9wYn}sTH&*; zt7Uw(S)&7SGFt;=VvIZp{yn^E6`uNV_2nQ#sT|5r9y5nA&s`bT!r?WG?4Z%7qnO6Z znvT{dfH=2geG77djDA1dGq}?E0^-b+cCo36UsPqA`=^i8;NHeCtXLBE$oVP8Zpq&9AgR3!4beQf&_=Qp+Ccx7Rk6~~OViV!7H<-h998_UD~9N-~8GVcIz<~ zcW-czgXeBitsb2Ju~ar7j*;!!eaM$w4|l{15c)_>AFiN5HoU1lU|QXaDW#rqQZNHF^bdf(%4 zcU-=1@k@h17!dN&#KPH$IWM=M5k_yQUdwQXz44_!cu-TyO+T&}TfXG|r^{~fvOn#M zO{bJ%G1b=cj)5=tP>551+=z*ZB`)p-0R|P&^`*`Ox{$J{IHfKdqd*`;HA2|P0hEqK zyN)1=F&7V?`qRx7?psMEu$A?<-HBbZqH`d%-$|Z#wzM)7lv|C;aOiHV&mdfLs^b3L zXa9v|yPFoptW8{uL3Indxx?O!>vJu&&SZAUV266fGoe9jH=IU;@3^cqE~ts!jyEMP zpvs4==D_%IM*FJXxX~~Cf-dMc&#VmP0ft}-2d&Mn*i3~>(ukg(1(>_%>bY-G9IpJH zKkiX&So8G&?Ugw}j;c0eB-?qY`n^E1D;s_=tXm7}(3T=9k@<8WH?Q_=3PYSLdfoHy zXyv@NreYKaX)N_6&u&ZT87I8qnvTu*mpS*T!RAxBz7^H{>@5D__DR(*6J62h>XQJ& zld)ZUEw4*YIChqkh`GT6g!a9T$tQxMvneOmT|BoUf*3@kJGh&m2+hIb5#f5WV;7BlKlol_pLT1Eh=^+~2UV=Bn=S z$pyqE+EKqgz?mjI>Cj>>qKDMBH3}%-pp)v=_p@f5PNnrXAgj{^Tvg#vFL4?fFnCwc8C2c3H<7o0kF)pU^NSl7%T^y5-l;qfXg#JDx1O>R zhk^tdbHzNpO9%_od}km2d3YE$eTk!kE9=SKrm`0%=ev}d5sgMj#CF{a$*fD~NoRA= z2BWr{zMfn$7ll|bq^}Fnp3ByOWeli%-xD7J!p#;=jlcMkATgC5zg6NIdoc)!j!}-i zjPtw`Rd?!@lQW~xuV`kT_mtf;19oQehnr}U@}CMc@Fy%e>g+~$s*frv|C($*W^mf* zTyfryC`Y)Uk2^Xm>A|H>p1m)6bXma1%E4_+DM9oT{lE%9M*juNm@&ud>#^~iRvV2# zG0ozs#R4zfLZ6m~@3^5use>XCaW|fHW%%owL&!O8DJFWE{8e=vaG)G78xF{ihL3z(snw^Mmio&`U^((SZg!;u)xO`o;kNzr}@z+&A zNWz&Ip{s9`M7N42K+>E;kKQC~99G@Qm-AbHJmX`5oR*TlVU^D7&=We>%_m_LHTGtA zZ8|B)&A{tM=O5T>ceqaI=~HZ)tXrnfs>D|c^>>j|zDZ=fRQ_|RN9a}}5mh4*5%>vh zQ8_BFbcG5=8;pz=1sJ63KFZ3XU2bgn+2S8Cd~;C)?~xLHJ?{Q{n=Inbq^1XOtIT|_ zj)a)-1yBIg7wCbpOHq1+X)pAkJAap<5b|(z10$H!5G`J1lFeaA7tw&B8^ zk+Mi0m)bxY)~Eg<3L^_O`^(;Wqs_c|nkNGXvH{eGeNV`%{n6RcrHab=wzH1veNC^Q zLpYpn(lmb%*)0$7ER?(rl9f#HzN*0;)BMvs9a~ZshcYnUBPL!RGEU&DDeuqs5r$LS zZmejz`8|sqrC<3n|h0XaF-T#jXEE}XN>HQ3B#L+j40fKXcn}i4%^WZa7nL&sZDDR z645csPaH@@hJmOL&<8`i@;3AC;6@RQ%}}RlLy1&o+B&25;WP-@+Ur|10~Xm(&?m|l zkY%NFQoO+HVxF!JKiae@SlZo%{cOQovfdYJhmZw;<8P^r z{s)|40U~{=)yA0U*;)>QjDW!e+2VV-PLG)&3v<854^GE|+F&_PL}Vpey2lz}kkd?@ z)l-Tre9=3@>Ma->4l?y++THFDFO4meX0nBQ_oF75EfRD%hcflQS{#S!aAhw>Kf*A5 z8CfvV<3E__it|@B9>sx3RR>Ejk9OGzr^j-rW{5h7D+Yu@a*+M3;>r9xJFl6<@J0S6 zMx$1ci{+mh%mUSH#DDct%kTRC0B4EJOO!fe49D*?JsHjqoY=|4wr$(CZQHi(OgOQv8%>-{Y}?7p{C3~&p7Z{LcR$?I)!kLq zRo%C%x~guZf}A)!3^oi95D>higoqLl5U|~MI~@w-`@4gRyc-DUhmxhRu!5wpFrk8z zy_uzrDG-nbbgiZ*hw9ey)=`{JooC*WXI}V}^TkbSw$Ps8}{9=e+OB)0Y4Qs@6#%(d_I?DQdY82O0cW5D1S0Zl{Y-l+`) zkQ^isml#SU3J6d#XG-)CWQYK_OkjN+EmZ<8cs7}U%?~80Af_I%ieeJpBL5ryp1!o};lurvV<@HF!}zZmVkTmQ5$ijYP%`76!)sW4oCX_F z1MaKy3?xI#MelgHL}tNlnQ*+XvEIUaHJgNGC-PuJS^XLVeh_0ugyS0+M!-M{A#D!q z$y*vxB7qjl1g27DYi*w+mnph{-izXaB4S*N7vPJ6B;jD2&+Ux@4Oq;d;kXKh{U*{l z5r+DORLMfRyo6yqim^8)?vN3ggnmdkmqL32P0R^sR$|HarwY3{mN{evK3o|_l6T0b z1z04;L~G6Q7_fx4J<*U8tQCcIO9}|&1Bj_W zsEFWJ{uS^6pmz5+G5u%ZIF^DVcs>=r>n z{E6i+sV~cFs33OhJ$CJoV8mp;DT2_>tkN`274dt3F<#qT33!%pwD5q5&qh1y;KD5X9^zRDdeW5`4vOU_CT2ook4^LJ6PQZZ9m`h0&qqlT8UfV1!g zy35B-5eR*OoN^PTP#PmDR6$1NfPsu$5dr^68BtMLVFe_BvtY>uM8jbpw+OYo;BT5} z33PneZ*Tq9+ZX^1gxv!o@U^3#N$P{6%mkF%1nijdEb9y;>}>!H%rHnFj0pwoNC@hy z1ga%K@Bn1P+VpC$uxtDh=}&?6+Go}?>+fjDxE#3};8C_;nKErNMB^qGiA0u3dcl*Cd3 zBPlvZl#W<+%ypk_K8i=^H4$?R#{}XftfpZ0rx+Tf6cSmelGsUsjKc4q>M4j+VCcb8 zLZ!uVg@6Jng>=P-3N;n>Q%)-o)@UsuP!`ZESlTExk+Xv7**FV(7ly2$zu^FpqhjgV zmSggj<=|uJd)!wFh;YNv2m>@jKP}@82H=bzQ+zUPhcsm<%JH57SVLX?Q^tOVks81? zJZgy6!4E@W2JXhp^)D+<=d?DE?{4G`TaFyKm~z2TZ;+|k_$mgBv{ zwFmZYx;#9)fd3-P4=CSYJ%hZP`oQ`D@kf3MjtWr7l#7{59?4>grc0&EtIN8_+XdJ~ z*`?-%>LuyL|4rme;7k1E@Wt{){ZM|Dd{qUZ7R)jba3BjoR)R8zY6(FY_+c0r7$_Jc zm~Myznz;ish!tiHhlur;;oZc$z` zBkmjY8%R{{WRzrhWx@r&GNCfBGT^eAsr#v&us^F$*?W1@z0 zV@i|uX{M>hp$S0M#OWCJc-FMm^lOA`7W@qPuvtIGSQ@l^8=^`P|_^mO*X@fdw!@Nx24e|EdeJv!UY z-aFb}K0@4{+3)TDQUW3)q}ZmZ7cWpL5I2xikeHQX(5e@x*L9GKkRH?cDf3hQIBzzF zp|ie_L6bwiP4u}Ksyrf6B4i9J5t8_>2&8DK$gv1h)LT@2xMX-^NNKn(DmEH*$aN@q z_-<%@=!h(Y?3^r#%!*V`YF_$OB1|eT?L0{*Eh+6bfs1sJkd=0t#FiwNRF{O8TsVa> zDKe2V(UUwnc`*?_otI*rB0Om!1vZsB#hWTVX*r2vGC%oBy^rdcN`x}yCmb~sRo2fD z>ZPAOin~gU%2KLU3a>RZh3B<7#T%vX(xS>_LZvdMDyg!r)mBwj$*1hBoGm~tLM`T} zBCgU8AE&XW`mFn`K`a347>h>hHEaBNn+2Wa`K2O@MXPBGX^S^YOiRQwhhvTtlQW(_ z!>3uNm&>+GAC={$;)|VyPonyY{5ri_{$A1dX$7>V+JCVkuoB?tuw{Nk{dmRFW5{7Z zVI{^&{n5>e!H&Q}$DYTO$O_HeZgab;GMr-MHR-Y{Gh_2>)z`VXJ=L?!^A~t_X!fvX zw0fFR8c`Z++MQaKns?2mwsEa(t#eJdEtoBcO_j}ItCjs`=R+WEac%U4=6b7}lN(wm zxp&Hk+FSCYF+vi8A_9C|-`vPe$zEG>vvSNru5FvG+&WCFuY;PaoZF?7q5F+%rQ6G; z)v4Ep>mS(>T0re|?y7INcj{YXc)NJrXh?bEk=~3QJs*9edNKRo_07%=Z_Cc%N9A=2 zcZQ=74e=(AXe_|T$6 zheYq#x}uHZjv}HP^o7*QA^|NyE;282F{U$>S)P$JWxS}7uMtiquEWg+Dg~-L!=-GO zp^xEv{N`n+J$XI32-!X91R1_$j#Q&eM>Av-g@L?wqlJ^yZFV=gH~ppXZsypqq%d-5HM#J-Z(Xy^*H(!FbBRR<&Vfe| z0G#|^v=}r1D!q1r9tNkmQzPF<6-htJI+mV$;Y@GMWwKmp)dQac&4V*^MRe3uKq_KY zb9Ge}cvbvzfz_^+8QoRehWqfllyR-m#c;KbdQ)AXwc|3M-cu)^%iyEfx@>NCi))gr zXzPl-=@Q99t8$TdHW7m@e?`GCJW~sT|npP74l{2*1CnHm1YNl zuYVnD$%AgT{klb``CAj@I{v-eiW|>)^SR9x(}|U*#dZBl;@{%+cvpOO7i-t$CyU1o zucDWIwO?xXAIcZyvA!L?vLD*7SMWdJn-Q`Q-T0b3mp(RjJiijwXUDGNb|Mq|5>GIZ z>3e!&{M)&_4B)uaH~~y~UsBtuJ-i7mpzRB3wKKg7ss6e?oC(5Tfbz>{0aTW z@=@}tvedHe@)~kZ-r7F)_HFLZPjyBpmgGhAP5G9689(QluQ>T%Qg2=Nv>!pPgocYo zjMkGD=Buv~x75>_{jWbO-Yf5#tcQ(Ftz4h5{Vt|D&b=1s*Y%BY5^-?mNMS6UX#>K@ zs5pKE*rNj>bpsh(fd=&5&*WJvgVH4;SAa4iTryAXP#GX03;85ap$T{vT^5winKN!= z5Z8eGhQ$oU96_AVEqWsP$!PFHkI~PRn5nwS#$4946Tml2*>^DPn`Is&pTw`Vorl!q z5HZHfsOK0Z(zGAdoF>z36S^S{-c?kT6zo(l6=W5)CitKd+nuFSi*#Yj<<#+GQ{Aie z;jVQEs}0Kv=LV-Z=13+?mV3%BQ(Xg1^H9rFt9pl)mYdtR^CK?_;v*bX40H>^7vU3qLQV~;2>~&V1J{$Y z%6{1hj$M>uDLM-&9Z$dQ&1s-}D6k~c z%ngm_2?Aqc__Bilq{&>HO@^YgO=d zPulfvE0PeKIDA28)KyX5~&fyEeO7`-Xe0JL}W_q6S(L zN*n_FMHcxh?$CUazLgjA4NT%Du=`T`(&zx{JQXR`I0Z+wMFnH^Y^7dk=6(gm{`9HHryTvIrt$)q&Wq;T~ zf+$r~Vp)csup_vcPvM$z73H0zwy<~ZTO2jSQ)oCfJNe3)?d$Tg=6oH%{WRdFR<%&r zcZ~Dm>wUfqtwaGRRX3V&eDc<0R1K&~FKXx@bS0^2yS3b{O~LQVws0M3@4t4#bHDf>(J63`9mGUbZk-ojVonexQZ&RsFO6Djv=N_mbi(C;ts zU%?)8p_$=uldx2A7X9OH7W@`m=O0gg=$~A+MPP@6uil&E-2@$Fo@N95zX6i|9nYe! zpBJs!(db**{yut(dNb?i{%{+#6+re5C^1Aq5!^}vUE-DnOp44FkS?fV;L6d9L%(-; z9HXCkxO_V-U!_C9Ao#_&ix84Ynb~0z(PzTyi#h9aIOxpm{O~FDWcgtI+<_YnLK1rG z%Oh%!F&MEN&Dx4i)4B-J$!bYHln>KvQgTW*>fimqkwf32NXbMhWmdI3<;c`k)gNND z(mH;>YRB$ng=IBqmAA;Y@SmU^YaIj5&`-5b=xr$3>Y1MnHO%&HnhkaAg&0+tbs4Ie zb10Yy5`q5;MH3<$-CFPk#g}lQ>|g_)?5ZTorH6u9NFy=9p9`r z4WEU$15KfH;2HBRyW>ATJS-43B=tp zTqlvscd+VP|gjSLBKqW(8#mlY(M<=1*OkbFC zn9i!_rP!lbsfDiL+fjA|Q?b}4)#LX0x-ILonWoM7kd7T@zrcR732o2k{j}3P{5DX# zWXpJAj^FIkbuqw~;=BLM(a_mw-j#lJ0_dswTogPqzYy*r;h|R>+1G37I>|2qu=wz` z*FWD8)qZ_(dKXVabI<}wNdsL6m~#Ujfl?@e2rD6u;xT}7B7oXXLD1}h1=iv~hpHa0 z-uuD!w*gEbxZ#~6M+V>k;zJ1zCzSR`9`Q6qpPwWugi~eK707Mw$)cSFPes&oGYmk* zA*+GoLo9}TMlTJID`+;TZph_9(L=f;z5~_UdpEQ$U|jSa*j{KK2(R2=fTGX-;aUYC>(8Y#z=r&SyA9p0RN-RA5p9^&mr zA06%3qpVOypm!1PQ^ugzp+aLEpk823qO>OUNx4YjCpx7ZCgG=2rah!5C%h5rS0a^F z6g+F5gx%Vqlc;(v617O5cCzZQv^0CP_*wP_SD#XQwZC@maG2Wsv=p(wAAZi1Ot!?E z8q~&sPiw8U)86`Bx#hL_d#!HevqZRqWRcP!JGrSLI;%P*JApeMGMyqFGrx7rEfepQ zPmKG*xAiSFG3h-X;vT#KvNJpf+822Q?-C^uZx#Q6SBq;T*NNc~;z6}*>ar~aK>Er1 zXTRT<>upl0P1$*V6#)Y`iAN{jGLO|M1;)H`XXCVM_njMDCbS%Irgu$0PI*SNRl!uV z>RgyJdZlyQ-sy_xEx8Z0D>!TgF3|H`s%crOkSEL}W^9 z`xU*f=Q zV#k*HE(Zf0R2;Y*>l{^`z3-4b*F2xDEpKVBx6le;tYGM1H<9I#8!$|f@zAT0%B35`d&C(e~_|>h%=xDtZmAT>Wvw0FbY;)V|$cCt$V7oz6r6( z?@{B?;5BK-zP_`U+{R3B$qpf*4=Muk5kvAn`e%WsJ#g?INg$+p>;=auzssHK(dYJ{Amynca#-)Y)xhSt~Z)5*^7 z9_KFTvFHxv#rxc8KE5R1kvg&3&l+$h){Dl*!Ty#HdOWZ&P+WlAC9Z*NOp47J??R-% z(;i35Gg>JYTa-CVe(Z6Ne(xOvG=i5L>P=?AHxgaw zdVhH`gA;+{^}`gWHS?VznR&}H(SFrjw%Ktd%ld6VZ^JO%c#=)coFLt48Qk4Do;gpy zJNNhU3)B_QWjI;svp>QgXeroBSakBy5Lc0F@exsYk?P@`q8i2E-8J_+vLW)vOGO#J zhNscbZBTk;-GwlO%c%_BuCFnZG|R_f`9_8oucTQ!jnCu1$tv)uX4wI{EuTPWzb|Vohb}+W#(&L!xt=b+k1WM9X?=}CR1b23=s^`z>d5r0t!N~G0OiB>c>zX7 ztj0%1yeKzpVt^g^ck{oTEhd`3BBLfODi&xwBOl*q&isLd5SDQJF@h$)(+#Cmj^HLe`aufxBqq16BGVt zii^o*RGob(J#^h`{&-!o{PJ?&f!J!tKmN&bt-|4T>2)Y;g{(!s^j-j47eT|*;# zR~KGl;(v_(=ks6dH1)9jUru(;|1H+Hfb{=H=o#r4=>LcIH!9D+UM>Yo4^tZr5ldTB zJLhj6e9UZYJpY;h{~7sTj{gT!^M5fJSsDK?=KmS_Z%iKge**j;f&NQd|LOf!7at4{ z{r{+*4@UP)-~=jI7;&b!Mw@&UBZ@cLhFESxRt`yXf+Krw**0Kp9FL&r?Z zJuv$Z1O|L4rNRGiBq#vLnM@EUvGN~~L2c+8K-7l>1p+da3I;4v7W54m)P+0%hXDx_ zLP0U8qkti0!h8b(?I=r-KS1CD2?_gjB#{J6sJ?-JL<#=?L9{!#U{6WS@$p5FoleLH!Gt!5~1a7#bcvm9}#ESMMiPwgV@_j=0<92B*)nLyOjC zxqSX);Y3XwTwolL!{@ta=ceCwU*Ni3r|eR;&Qe`TS(({4JQxOl_M?pkQ+V4+cze4) zm_j};CN}n=?%A|kEy#0vwotfR&=B`q;Q|=I8}x0~CdqWx>Ol|4Y`+pOFk;4$5=4QX zqJOClPp+ko1r2Aw0fz}de(zWbXh8$bHxPjKy(c9R^&G+hM6J)$@JH7;ADh+1 zD0@Jrz!<7tQF7G5xScCb;6DR z&$P)q-}8`YI0NW3)^zYETjT@n;Lh0`<2#h{VGC=T%Zd4pRPDZ@m^abRgcw*OYacb^ zK(qh)JswPpaboDAvY)ep9C-?4h3-1KOpWQoO9v!LJ}(JXKW(tp7Z#GxEb%H%b?Cyy z87j0h4zUm|PU@j4uQ-u5*Plov9+8y(NTY?UtfdJ}k5W`p86>oy@Pn8%Frg*I!BiIg z=-lbmXVP-TfewgUj?Jd|(`gMqfE4wF^m|h*VOPi_1Dbx*(DN?*nQBT-L!*c9$ZCf$_Hq+8@lgt)WnPDSh`!s>8>2?P<-9<* zL@e)Y5j}QN`+>rWdsLqVt2e>Kdh6uaaUcyZ8OZYp-qKHY+4*!f9t@$FOA&7~MJWx* zdlDCag@t_ZiMGg>4$_Ot|_H*Z|PDq1|) zKRl#a7Ila%n`$41mkVzZK;$oy8XKR6+%jWgPgGP$4<(Shtf>4t>xg$Pg*=5s$i36J zm)(#ZmLw?x;H|b9t|zmO%D2H zqR?uYC>u=&bc~H1b(M9XM(}=Mb+wumCRtaEF$+U!p1YzOh#$i&|7g|;W}1;H5z6^3 z`|dELW~gWFisYJLV`W*h4klDOsc)R|6Y4Iq7Sjr%J1hXtu)qPijvGDjrYGLp(cvGAOROe43=)+uq$k128^`tG#;Q@TGs=7*ybf`n5a5U2 zM?5`Ko%X>miYBdyG9{HNoSkk|-v*}{+`cVnqd8Ad6q7Ne6pjOjZyWfR%kMU0e9s)( z`Nj3f$jF|dGMxtigbHQA*8KHWyR;e}?vU9E^-)zANL>oD?Z?vik zdsoQ_Ev_s|7W{N9QZkDJN;|f4B!QwWYS9i4@9V5Sxhv}!s;AZ68TVJ(Kytqr`cwBnMYUkAtqanZe#Qu~xG(^M^UB zW*-p=T5MgjLVHKwMvYSSo{AZ4d!aUmsgxgOdTy#MKzh%dOCh-$F3_P4kYi`XVrniJ z1NH(O^+XscCH))~DUIp;+I4G#7Bq~8B2@oU9B0E~b|#xrIG`l83@^!X>EPw-EOhSb zuh|~3=XWTM*qigWY47D@P==&ft<<=P1QYNDVlm^G1RR}1x~D4xMt4RJu;1)(eZ4=y z3tT_xyQRcLDRQxe$dqjoTP~QRtkjB$z=&i)9aRO<*Zf|7;{tXIsP zs!Xfj%aSyNJ8Y(0yvho(ts;4*-u3~7f)3M4fx_gZ7p0xdC6q|qbvKTFPP@p7+d@^f zqtT-Alo&iLKIEA9*SWsQ&A~80-7*Fl?At6rgG5}zP+qB0a3N_ige!!0Ug?J(cAr}1 z$%bL?%&SiZDczHm?bkKP?~Q)yTCl(!56pWz%zZm<)sEvHLL=(eBms2mM9^vnLFJGH z1X*n4-PJwJ0Y$6J@j=TO_*og$Hvz$-bRZzmtvt+5#~05Hz1{74jdt^rrN+Ksci1uF z83ZS!A%$t}Nr1uc=U60=ou!-lo5<0;i~&7BKkH(o#th1Z`JYo^20t)m2&I$t?I1c? z{>Ev_BAT84=Y?dmLNSLEIam_+rRma7yLArL_FOwVtjw`(*1~?f5boBAdG_)OfPO>q zb(hw-0iAz6_;&KMGymyH#vjy?WVei%9F75Sgq1P7=+RvucX=_XqwvsVmH{Ch#~531 z37Z3{8WQ7wbKV6GmkG%WTbSMMQ)gO(dyeZQJZ#3C%rRXkgP{j*kn`e;o3nXJx}8DF zJkU>&PvsA#2#5iWa2a7_$t`3KsA51y{Fdb=V32!WAx}htn;?rG(MFCXC4&3C=MFk^*v675)=YopX7Z(z@M9o)I>Z|I=);|$J$ z63gpjygD7n`$}6jod%=wEcm~565N-`R_ku5+f1$h8PO2fd0nc0Lj`@d!s-brBUS%& zz>$>PoeNV>%HF9|F3i-G6sjC;&DDk!s?$-Les9$u45goYO(?S2g)o)haXf%I%%9;K z=K(y#cDH!^oU7{n8u@uS`lP>w{yu{TzF<){3Hg1uJ_CiBbK}s|f!a&_(Gip@5va#3 zJ$dp~T5{;1q23fysy*@Q=Bth&CEo6eG%~h14_R5U6ji0u|D&MD0!#A^ z!6m1r??04aPn(kSN=C_&`IM_rkjrA7V1Z*)b@f{TGFW3JI`28EV=4c=ATM!V; zjgFflbeDo$K4^Tg7_rN*O`w#>TU9wi#Su*rLqY*5VM|*jO)@ll8G8 zbgwERMIVOmww!iGBp87L|6)Q-M+Oc1xXAQ;qonYD{Nm55*ErVur_W!!r)=m%SmlZb z$HW3ZWZ$8NPwF*lF7GV1sU=F%#Tm}Kl;O-Wd;Y& z{hc)@DdLso-)ZfZx0}-Swl%CZ-JboD>U2FVZk-`;IrxOPzFITb^w`A&|48vkpfxQ9tKQOXG7BaGfva1co7ASDVn$O_KTXLo+6_v964%wdA#Z9yu7v9TvnDE!)L8)ICac2wJ8!_4 z>i0F%J{tZcdBQs-Pe&;1^Te$r=1yZnNdnxXUt?ESE+O;l5||HVmHUdyN7tsacw#&S z0$leY&4$TU1=e_VGibZKl+r1EkY-JU8*!gr&J%RTpq5|<+i2$f!lu4?OIEF$k0CxD z&LGrnsYNRZ5!@=OgAcAMDkCE!rQU&zgeDlT6zezuvPwRZQI>=teNj^MrFz2GhLLag z)P_Nx+&LLa_;MbtTr*$N2i}pl>kZYE*aS&#C(+{ES1PvrC&69mLMerOzMhVnYS=-BCOIT;i*#wE0(JK3dF_P9mkI5b$dr}Z9vVJa z&cI;$mdUD$!tzEDdHJ=&K3bkp)7*~`We+WB!~hij*~b;P+yjmW)?$Q9@82e)=G~Cw zLwaxb#gYWn^83AAmgoFd?v6Scjru@RnPbDE0vi#St^0O*Sxs{?VqaAh7^M~`1w^}% zi--4S)s=um6!Td}4$c)THdafLXJ9Zu!8TeVEW9H|@o-A+cqQE8%{%RxqU^2}OUL^_ zLRXB9a@iXiu5R7+h5BF2_NMI>Y<{jniu_3_<}`EjTL2A_cx5D`MXZ>$=h}u7x-LQg zF$BfuFm23U{fJRefuV5eWsuyCBM#=DUj8o{CHLFa*oWdn(P94E^}&?RuRm&ZEL3hm zlx`q~IktYiy9E+?iJPFnZU}&T1h$Z0sMgP|{X^pG&$BZ}Pds3w;iP6iO?sEZ<@jV% zpt@J4U1z+tRHa;X{wWLAzO-wm-%|Fjq$rHcp%=G`|NJiwzK1ee94eo(&J}CRw;}Q% zUT$Y}Ly)V#d2ls~jpzPb?Vq(pV=j zk~%h_t0vJf!So#nx)AL-o6MGZH?{yH-mB|7(Fcf2b2Y)6Zf%8#wfV#y3jM5PcYRcdO z@#6!|bqQuGMA*#bM<*@q>%D#bWD4U{qWN>k1GbyB?_j8iQCuidwfQ5-OCUn)_wIh0 zX>Sw_qaMgYNfF6NK8Y4o7A_&AbZaI=pOL!4muY$( zMcKl#s`QesNrwK`uU*LD3j9*wZ>(~eRHu=fcVj)4bH05)e9&GzS%{tgbt$@?-1m2L ze$^t2Utd+TiNWCrl~AFv)&J~UG^{G$n-2K)?A;8X=bFB`I&@ks_}9RUJ)WnQ_m9pE{SM!#iR;zBlVFS zw>aquN?kPLU#ut053)q1)F|}xdz>l5+H z!V&%Ra(mL=-p`GEJ1`EGNGk1EwLZ||5ya5~`cWYYrdUFSlr=<#pgl0LGc7nvcMU?j zZ`oF{mHYQtPI0k(lc%ikjaiKjC~KgN>(f@ z$v-9V5UFwD)r=u{Q7F<(?j=$nymcKw0d@{@$Z_T)D#DMxBy+V*U7bsdPifTAqmzv?6K@DW4sdnW*BV!kFaIPCH zoys8P@QMu86h%P|IdNh*S}J}<9|5T98bSP|(&u<^%~737F`#Xc3+E~pt!#^k*aJz* zK?G)Ks<33$M4G&^ZGSPfmz0^RtQtD0Ss{N7%a{u<)xhP*gcwi*0zL?{YKG}{G{v$= zD@jB)mgL6lMjTj3b?~sTD&u123iNj~&{zq`y62_QDaX+z!ZXtpV-Whsi>laRIAAi` z?k3(-yH~aSgGoq9ss)q>vPqyMCXk=g!H5|3*iP|b+wyM#VSi5UWYf%Q?Z#1bal?%( z%u43u0?s*>>I#Xa?OC*IiJ~#doZwB9lNToOBwui(oG?dO)68cI#BDikIJibLGA)&p znXR&DM}lDDy*Dhy#kwmuuty1;7But>jyekr3a32Tb9n8;{+d|IXyhd4XBR7LM_FY% zDuwY){;BvEteDZP8^o;{w|)_yT`d(+{u{$`8JU!oby`a{nY!sC!$%SBrK*kK;2r@{8O2t7^ZMhDTj)x;rQxKeU`;deck>%-Cm?A?4a z_1NZu(O{+Mv}0*%>RGyV)|mr^8&Smq^hPfGagxLNyWQ_vXd89=zyR+ zM&Cj~PBh%;9N`YMG`meLX-EF{ZIXVKfLi6M+*u6EUeWw_o#koL0jhRR4wsVgI5TRw zUh8;F7%5~1=hW)K4)TtU^015V@4EWtj*do#N~=#3l81s_F0I%F=1CSZZE8up9rQ}c z`kB>%E|}dBMcn3N6{`IoMXUv_n!&O7c(c;PcIk1~m|9 z!O)DynUc!JTcHtrVGbNx3>C2!j^%`+{Uog_@*g<0uJ8!(`~)pEa-)C=|3)D8PLZ<47VJUaf2tVmR|~Ad%K2dGT;RC8vSb@*k96U>;%cldgY$t<%U- z3ELH=O>trVAikFc6E=oA1FNnd za8ufqR?=o+E9Aprx6eUq-pmOXSJYTCENNyOO2XF>6RlCKxJDgy!+AqWo0)6jO0D8q zDJvHo#Q|Fw_Xvk-{!|)e3Jy3KOB7+(M}#LRY^1v zOLi?hD(WUI^?N2e5g;TqS}O~e>S-F3(FHpPn#SllGc;OB)E-L$dK4*Hgy3jXgpQUn zX4RywqKYY-=G{>{lr=gX<8UaWpPF5JqF)(Z&2FJc-qp}ym(Qk~L@TF&kxAk7Z*@#v zMWYt=p}DCOG^ajIP-N9A?DpFjSG-)SMx|0zcV#t%oPh+$7o@{ZWwuV)q^I|wk}HRK z*0#HOlz`PpOcUwBhP7)v+-ldJRV;h5uPbW$dJ- z33weS&622_WAUU*BPuad|0O|FLsMH-)u>W4Js~4KM%fQDdCmb)Q7~vF#i-LPn99~R zm}j%b6x&{%1*z87)-Th{LBQ#X>4fq7yd5R;tOgksl1s4(bC)5K=LQMKt-1$3qAO5M z%qDrE_RdajK^fbT3eNcK{d(hP?~LtDC@F+*w@ae|{+UoAR2vzfQ_R@eRy2{59_zNN z!r>lvxJXA?NjD9wo0n;0=MXkJgz9Ik3hI9-n_OGeXX5YGyOFfa5qOE84)P z79Vb7oxpoFqM4nigH$BTRHMQn~DMKmhR zMDoki$bEFg>JbB7Gem)_R+WtksZedOT7e&gHqH!Zl+4PQSXk>^^^lm3 zcxc^*CO4y2&ekZ(P?WECAZST#sDxBg=7%n6sPj%!x=7aT1gcpwQGizV5G$s}vnVDm z$>x~l__i=E8Zs61c#aPitdJ-SKBi8+=4ISP3{xlSKnfXD{yy_&tHVg>3l&eUCO zTFrjfJhrf4o1Pi=Fqsf0y#)$X(mwJ|3bw8#UwW3=P$JiRe}LqG0YngtY}bY- zyHxB%R&HH6cx7|PhNxC)3y7TjOm%?$+`+!vO#)bWiWMd%cGmrPGP9?!;bD>oD>*Tn zvX!Be!w~a+|O$nmD6sMw{mL%a?JKdUGE85tNLjVbOX;|Z-LtEcQ zo0Z7AwGhsSNF`rj6}o+@%Ce*!Ix!#016CBei)2+qOHrFlf>&svK)L~Wywa#c;~*EZ zP@JKpfh0t0XbEHb@5g{#?(eMJ-v%c<4m${2N}i^7E6bTocKoPkU6Xcr{d>;#J3GRy zAVcSfXF}Z1zU%YN?OSX2I#hcyOo0B#J z+oZ0j6NxBjF#dRvE(Cr{WYbi!t+f-WB=sFqiwhipek^{!E_ZCKRH!E&GE&7XaRXwi zvTo(>$>YSFfUG6myT=^QJ;31C!$OnkOlM%1I!5SJAUl4ji>S!opAMxX9;)J0gsADE zVq$<~bPY+Ti%Zkrn1fBEGZ{Q+MwtPf^6OPPR-o0@D-eil>!5_Ito?NwLBS7ak0@I5=DkJ!LY>sWvzKZ1ApsAZqtJ;0|m|B!w_~sGcNEe z3F&EDV~JJLaw=6t2Qw*78L#`tT3$%NX?dZleM!m!yUq_P zOArZ3SUA%N*pcc#kC(g}ucUNhkly!OSiOd1 zjlWz}$T{F-6_`jFW8||ekyI!afk-gdhftC+|8m`Qx?mO5i5{qx)P+y{bu0zHje^8N zt22Nu@JeNNU{pb-p}xi3MiJe9f4NiF^CZ)WT*sK*#Q|PR_R_;sr00SUXD7CqCLXcqtjxr>sdAmZd-9Dl%VyB)7G5 zyMO}=V0a)*$~(;3j_8i2>GuB$wAF6C+}gU9on{a6Ef$N#=5!jeZIy51y>wgENicc8 z3puu@%?D=zg%yfS!ZHEo|xpv=P|h! zMm)Rhd85;W|9sHTQL%a-rmS91n>W*T*PaADQEztp+nwSV6bJMg8Ss17+zXG}ZAx9z z9Poa12LAQl_HL{Xg7TTM!Z^eKwPFAIAvSP@plE|?^j5{KF(1lyuY$B~!4NnK7?9`x z__MLWV>w?!2i87{85=D3tLOE!x~>C8+}t2ecoW9z!Wk0MZ+|8%=Hu-V5&`~@50U?V zZ1NX2;~6y1vS>2e>4sV4ar&>W+lh32KS?tC?uVuFe7`IJ-1^k|G&l%kVYa#m1EQZu z0oEKD)U2GSPUO?^+z!Wr9>37p^|wLp4Kx3=Vd}>7Qmv>lyK1CEfIMvZzP^~v|E&YZ zt_|4!7TOOCOK?v!yf*Ob{fJ+y7Go3>Z0<04nj4Yzifg6$?%7g%a>g9w3Wlir4ZcH* zna1b+p~`VkWpBL(&!%hwG%;kV%kvJ*ScY6Vpgkx;rc)}UG656rl6>g1I1N<%tNSZr z*sl?vyb@9@78je{Xv~%*dZwV$ThYddjK z0t-ZT^0upx3T&q`7~^?v$L#$U9znphux^vAf0dEvG@T=#TAj`3`>X{YkTBi#WbTb@ z-VQV#Na#O3YKzC?PczKk;j~YgfAM_OA>jKQ6*t2gfiiIz=uyg!mnHV(m+06gwNwExyjpP$RRCB zs1$O~n$-QZx0~);u5Da{;fSLbn`Ulr~VK4hzNLGXsDSak0ff;SSs{>xAOe&*MoVyCKe$M_YtEc zcu(2GjeC#l_QHV8}k71*9hiIB40} zFec}A^X&~lJV)`|b)2VJS9V_S7vzWIAd;T@e>m+(9!r<|`<*P$fIIRG49e5ZLQf;g zjNPenrQWB)@VNtS!cl%a>%8AY7=V6P;Z#;uf`|(#G7!!NL>kLR-Hj7LXc{7fdp%^1kAs(-6Xnkge8aU8|j$}3g=v3iI= zUPi!uPWKA`gF@_iX)vC|4N+Qgw+APCjmiVt+N9gL`i zrE}H&^q19oiRXS+7#bSdL+0Zy{|hZXlStC>ehC!{@~p-?UJc3=dg$1YGTBZFi)zTz z=%?W++1Lb=C7(r@-)x`;abh4rKaKpZ_|Tz>ii!beTv$q;W<)6gF64UZ7 z0qx%hZzyni4I}#%Y|EzaL)7p1zY)_w?R3^&GS6Vdh%$ZVRp*0?Yv$N z(s923_;vsFy4-us5x5X|5EMWUa#quc2=RIgmSVsNt~s<;4_E^{Br?VyKe+2rEIAgk zzy)CWHIM!7xo78q>~izpVI`N#`T5=q44ZsS?)4$$5OO2^kgmUW;U>8{8kfYbM*6op z)%o@LS)K10@eixom+Su%3KRA0V!@Q0?Ou}SHmAhu3#NbZpT;AN|M}(tazlB_IRE?6 z0qXxRiQ@))^2yUFaE{SvOiN1>=D--nBZ};J99gYaETdpU$6zpEi3Dm9APE8N7K0AF z6hZR46XaGGhtcDTS1vfLpSCJ#!m9KIyXW8JTJM;Xv^$+JU(heuJ#UmNhb&X94f=($ zz!%e0Ow19}xLfeW39+ir_#oz>hrRK}8{%Bv^xhKv^1+3T7|i_8SU7t1ly~Zg48QqM z_yu|?LiIOq-rLvHHg7#0b5xuTiy~X^ej5>#wi8y=_4v;l~VL?Lu6M zI2`d>dbA(5!)x4d!>9-6xt`m&yGD*8(`IDUYRBQ>WNy)19#XQ81 z8pNvlgod4USSl(PuB+bY;KFW>eF0;Wq0z-!RoL1RG8x?#l&FY5n`;*={QVP8{t3Qnmvt8~ z#UmR2<%`cN#qL~^kw#IRFn{(zAN}yr&G)~ViOwaKJh*ck$8Em#uDx7c9mm~YRGD*s z^dO0buwldcJDp@pjr1X(D>BH%i+Jc4%EFR|*m7+5A_+A`2Lc~Uj~EG~S7C527NZL# z5_<%le)z{uN&8Cu_Pd{VsV*NeDrvH6kQ(#$$XhsW-$$S3Bjuxm4?i;EmdV$0=jE6i z4~y;Mpx;;NR8BVaqmMrF(Be0OJME8u{G+TMATauYEt5vi?7!;%SJy;8^;Gnh?o%|A9;mGS?r*1ZG?OQd?|kI_Dsb32>eAuLjx8>;^JCg^uPic zo-TLZd1qy1CDuo7zWHWMbMV^5;lrLldupRGd$Y$xNlrw{v!C28W=RHyNQ#=1v3S}D zDs#TcW?J$p9(~u{sb4&Iyn8d5_assCRz4obojM;DPTb;ATg~|)QP+o&H@pv7-g;@> zjxf#W@v&UZy7I7j8IQ-QPW^OMI7vN!z+zgl&f{r{EZ$~N18}7{ORi`O=?aC|kqlK2 z#UconGvG?fJ}QInnESgq(1m3YlJkly+Vmr`b1G;M@qc`Grsmm6YgVP)=*j18>YK+|V*;rNI^UYsjSv&N~zMS(o-3+WhiDyaQ-dD;qC3>j- zKr9i3v?daQ@5Q<-%^J5AcX?*kmSEa$b&RlB(8 zd#6N@yG5$O&GBQoI0=Kw`|rOW94s-4xAtLVbaXTpN^ZI379`=<25>Z3INQ8=vpBmf zea>Iw%I4tCwUBKxA$Y%scB<)6&CYFat;t)nc_$aHne!CpSqa3CoU(0xYt3d0;3fV7 z7+=+=&sBYnZriY7=b@j5Xkt}o50q`#eBcy!ecZGt?66#YRUdxv)f-ZM!up)9-@Kvh zz)yWf@Tr0Ul<+n~EFg9D2C?srHm;!U4=zHf);WF#{e z?1N&&(Z7HH{QUg%^z?#)0xbFhS@5Am2s9Tw$Upw^kHw1@W0j6BSO#K=pv03%duG8O zl^BVb(_Q?t-yfmWNzoF9EK{UN(Yv278z3ddL`;i!uSCiSNmop875MPB;=G>p*=;kQ z{aa<`lrgu?TlV3>57RU6-HO@BuZl2@CIZ_c5o4xg7TwSN_g=Cq=h64@-iwPv3UbpJ z%)sbgp_-y&wK+3~9o+in_~C;yK4|pJ+uDZ(BmEP2E@B{tHCKq?`t7&h3i6+S{`u6Y zQ!&EFst`8suqeB0*RH3YdJ1!1K_-Yq-#)GLELpYR?>X9gh*p=nIQ9PV@EKQysjiz7 zi}A?Lb+4B1_@><2x^s%un!FVoKmBya0l260HE+CH@@e^&dI}xn24y>)b{`%|KUxcJ=SyY3} zb<~;{7W`$9vwp+cJGS%5KjYdjiYzr_-Q-$4R7j9z(j?(JxWCu9<(N>4O9Hzi-QfWPym4I4D+ zwOt=QE@$aXLi;J427uOuF z*G5If-{N6JKNKKbO6)2C0z+p&0%;KRsR zf0f7wD>|Z$-{auLQjR-y=!1>r*Ux!;Ga^F}FoR1q4l`!YJQ}t8k`1eE|G!rkN z`p@anc6?*FT#1nFmRrVRaYVASOW#SOGFWrPBEqg+U$wbVa_`K0XnBHiF`-F${Y=X1 zVh&Gh)}3kH3DVs6;+du;WE)W!Z-t5lLQ~4lq%FhWMbr%U+e$TO zir@M2$(kXXVxx*ylEhQrRzJB1e`T4T8Y2h0T!|;!74rqc*s){r<8j$6-b}oq=SvTS zLnEfFJacCGxgXDQefztAtPpYci)YTf$PXb~8&j-V^KjV?S3ERjozSeIDLZI?m{Nw0 z!RHtHf;cWq#T2tLx@b|XcZu9diVYDD* zBv-UT>+F=ye;_gA-h1#`nn*ME`WpqW=Dtihzk6`b>eYY8s6;Fvp|KvG{?wDW(`4sV z5We_)YN4-k;q~yM8eVvSm_niW!yo=2sS!Bfg9$(K>Z`9p9@88wj(~%qFV;u!8YsOL z@!}JHQ3k%RvLu@QM~@sV-rnj*T(YuEq~RBRiDQ@eD%U?BQ#Sm1z`rpx6@xE~e*J6f z+SEX50bf6p4;WGUk{>i^P@6Ko@&>~1|C?*^{$U`J-1pzWWe9$RX8FFJFayfo9w7iS zXAaq@^%G>L}E-ta_T)>u2n_*sUVV2qAvj?p@P`hrZtka z4X;j;TI_IG5`TpmgMqZ z-_A~jNiY&t!CLfHEO?23d)WbCFC@#K*Iw%@JKi6`zAyG`u+HjdedMvn9`mK4Gx&Hg zVBZ%rR;;u7SsxJ>jh{Z^se?rf{6-wT1xdJBNGrpY$o32>fiJvb3HGy!&&0Z9X>1td zLuxnNFbYa#?>gC1I=i0<`l%}k+y)mqZg1$TMu0-B6H%c=F~6J8Sj53w68HrtA9^xg z2En@_Y*f;zy$df@VN`;pS*#lh16Y(OeXj%!1IHn`LJm!ZY@==%h19XPjHNQ*3y&;t z5cSU6O*q}zHVN9DhZsQUsW{Mj@%3Yu&P71D(*?|FkL@jv`=`<4At1gYN`tI+By}JD zrDA9ILF`ho1W!2hN&0xi)zu@}pH4#n9Ya7HGy;mkl`ODPid}m=0;u3+Qb$@-cI^xV ze0dD%+pg5F_q;2`wCB}})kuN>?dY9x3o)G(0Wtvb`c*eL{|~MOF>ZW=y^jC@002ov JPDHLkV1iwZf8qcD literal 0 HcmV?d00001 diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index e0ac46cc208..55288e2291b 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -651,6 +651,8 @@ this test is the most effective way. Global Variables ---------------- +.. _reference-twig-global-app: + app ~~~ From 95d6a7de8e8f7655bb86d75a8a2d11d082f45b71 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 17 Dec 2014 18:20:03 -0500 Subject: [PATCH 627/835] [#4606] Updating thanks to comments from everyone! --- components/security/secure-tools.rst | 29 +++++++------- cookbook/security/access_control.rst | 52 +++++++++++--------------- cookbook/security/form_login_setup.rst | 12 +++--- reference/configuration/security.rst | 2 +- 4 files changed, 46 insertions(+), 49 deletions(-) diff --git a/components/security/secure-tools.rst b/components/security/secure-tools.rst index 8e7df74a6da..2ee5a98b920 100644 --- a/components/security/secure-tools.rst +++ b/components/security/secure-tools.rst @@ -1,13 +1,9 @@ Securely Comparing Strings and Generating Random Numbers ======================================================== -.. versionadded:: 2.2 - The ``StringUtils`` and ``SecureRandom`` classes were introduced in Symfony - 2.2 - -The Symfony Security component comes with a collection of nice utilities related -to security. These utilities are used by Symfony, but you should also use -them if you want to solve the problem they address. +The Symfony Security component comes with a collection of nice utilities +related to security. These utilities are used by Symfony, but you should +also use them if you want to solve the problem they address. Comparing Strings ~~~~~~~~~~~~~~~~~ @@ -22,10 +18,15 @@ algorithm; you can use the same strategy in your own code thanks to the use Symfony\Component\Security\Core\Util\StringUtils; - // is password1 equals to password2? - $bool = StringUtils::equals($password1, $password2); + // is some known string (e.g. password) equal to some user input? + $bool = StringUtils::equals($knownString, $userInput); + +.. caution:: + + To avoid timing attacks, the known string must be the first argument + and the user-entered string the second. -Generating a secure random Number +Generating a Secure random Number ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Whenever you need to generate a secure random number, you are highly @@ -39,13 +40,15 @@ encouraged to use the Symfony The :method:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom::nextBytes` -methods returns a random string composed of the number of characters passed as +method returns a random string composed of the number of characters passed as an argument (10 in the above example). -The SecureRandom class works better when OpenSSL is installed but when it's +The SecureRandom class works better when OpenSSL is installed. But when it's not available, it falls back to an internal algorithm, which needs a seed file to work correctly. Just pass a file name to enable it:: + use Symfony\Component\Security\Core\Util\SecureRandom; + $generator = new SecureRandom('/some/path/to/store/the/seed.txt'); $random = $generator->nextBytes(10); @@ -54,4 +57,4 @@ to work correctly. Just pass a file name to enable it:: If you're using the Symfony Framework, you can access a secure random instance directly from the container: its name is ``security.secure_random``. -.. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack \ No newline at end of file +.. _`Timing attack`: http://en.wikipedia.org/wiki/Timing_attack diff --git a/cookbook/security/access_control.rst b/cookbook/security/access_control.rst index 0ec0772247e..9e0cb9ea532 100644 --- a/cookbook/security/access_control.rst +++ b/cookbook/security/access_control.rst @@ -1,4 +1,4 @@ -How does the Security access_control Work? +How Does the Security access_control Work? ========================================== For each incoming request, Symfony checks each ``access_control`` entry @@ -150,33 +150,24 @@ options: .. _book-security-securing-ip: -Securing by IP --------------- +Matching access_control By IP +----------------------------- -Certain situations may arise when you may need to restrict access to a given -path based on IP. This is particularly relevant in the case of -:ref:`Edge Side Includes ` (ESI), for example. When ESI is -enabled, it's recommended to secure access to ESI URLs. Indeed, some ESI may -contain some private content like the current logged in user's information. To -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``. +Certain situations may arise when you need to have an ``access_control`` +entry that *only* matches requests coming from some IP address or range. +For example, this *could* be used to deny access to a URL pattern to all +requests *except* those from a trusted, internal server. .. caution:: - As you'll read in the explanation below the example, the ``ip`` option - does not restrict to a specific IP address. Instead, using the ``ip`` + As you'll read in the explanation below the example, the ``ips`` option + does not restrict to a specific IP address. Instead, using the ``ips`` key means that the ``access_control`` entry will only match this IP address, and users accessing it from a different IP address will continue down the ``access_control`` list. -Here is an example of how you might secure all ESI routes that start with a -given prefix, ``/esi``, from outside access: +Here is an example of how you configure some example ``/internal*`` URL +pattern so that it is only accessible by requests from the local server itself: .. configuration-block:: @@ -186,8 +177,9 @@ given prefix, ``/esi``, from outside access: security: # ... access_control: - - { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } - - { path: ^/esi, roles: ROLE_NO_ACCESS } + # + - { path: ^/internal, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1] } + - { path: ^/internal, roles: ROLE_NO_ACCESS } .. code-block:: xml @@ -227,19 +219,19 @@ given prefix, ``/esi``, from outside access: ), )); -Here is how it works when the path is ``/esi/something`` coming from the -``10.0.0.1`` IP: +Here is how it works when the path is ``/internal/something`` coming from +the external IP address ``10.0.0.1``: * The first access control rule is ignored as the ``path`` matches but the - ``ip`` does not match either of the IPs listed; + IP address 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`` - role as it's not defined, access is denied (the ``ROLE_NO_ACCESS`` role can - be anything that does not match an existing role, it just serves as a trick - to always deny access). + ``path``) and so it matches. If you make sure that no users ever have + ``ROLE_NO_ACCESS``, then access is denied (``ROLE_NO_ACCESS`` can 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`` or ``::1`` (the IPv6 loopback +But 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 diff --git a/cookbook/security/form_login_setup.rst b/cookbook/security/form_login_setup.rst index 5f414bb9cc8..8266e22726c 100644 --- a/cookbook/security/form_login_setup.rst +++ b/cookbook/security/form_login_setup.rst @@ -4,9 +4,9 @@ How to Build a Traditional Login Form .. tip:: If you need a login form and are storing users in some sort of a database, - then see you should consider using `FOSUserBundle`_, which helps you - build your ``User`` object and gives you many routes and controllers - for common tasks like login, registration and forgot password. + then you should consider using `FOSUserBundle`_, which helps you build + your ``User`` object and gives you many routes and controllers for common + tasks like login, registration and forgot password. In this entry, you'll build a traditional login form. Of course, when the user logs in, you can load your users from anywhere - like the database. @@ -69,7 +69,9 @@ First, enable form login under your firewall: .. tip:: - The ``login_path`` and ``check_path`` can also be route names. + The ``login_path`` and ``check_path`` can also be route names (but cannot + have mandatory wildcards - e.g. ``/login/{foo}`` where ``foo`` has no + default value). Now, when the security system initiates the authentication process, it will redirect the user to the login form ``/login``. Implementing this login form @@ -99,7 +101,6 @@ under your ``form_login`` configuration (``/login`` and ``/login_check``): // src/AppBundle/Controller/SecurityController.php // ... - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; class SecurityController extends Controller @@ -410,6 +411,7 @@ for the login page: # ... firewalls: + # order matters! This must be before the ^/ firewall login_firewall: pattern: ^/login$ anonymous: ~ diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index c680bba3d4f..4b638216138 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -228,7 +228,7 @@ Each part will be explained in the next section. # use the urldecoded format path: ~ # Example: ^/path to resource/ host: ~ - ip: ~ + ips: [] methods: [] roles: [] role_hierarchy: From 614da1552ebef929dbad0fe404c66a06e2f692dc Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 17 Dec 2014 18:21:13 -0500 Subject: [PATCH 628/835] Changing to _ for consistency --- components/map.rst.inc | 2 +- components/security/index.rst | 2 +- components/security/{secure-tools.rst => secure_tools.rst} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename components/security/{secure-tools.rst => secure_tools.rst} (100%) diff --git a/components/map.rst.inc b/components/map.rst.inc index 4499ab1f349..a2039699b4b 100644 --- a/components/map.rst.inc +++ b/components/map.rst.inc @@ -114,7 +114,7 @@ * :doc:`/components/security/firewall` * :doc:`/components/security/authentication` * :doc:`/components/security/authorization` - * :doc:`/components/security/secure-tools` + * :doc:`/components/security/secure_tools` * **Serializer** diff --git a/components/security/index.rst b/components/security/index.rst index cd3794f8f50..e9fa2c24b14 100644 --- a/components/security/index.rst +++ b/components/security/index.rst @@ -8,4 +8,4 @@ Security firewall authentication authorization - secure-tools \ No newline at end of file + secure_tools \ No newline at end of file diff --git a/components/security/secure-tools.rst b/components/security/secure_tools.rst similarity index 100% rename from components/security/secure-tools.rst rename to components/security/secure_tools.rst From aedfcd2708f0c98886c25ac57f83422eabed17a8 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 30 Dec 2014 20:51:31 -0500 Subject: [PATCH 629/835] [#4606] Tweaks thanks entirely to stof --- book/security.rst | 70 +++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/book/security.rst b/book/security.rst index 21f91ded579..bed403e24ef 100644 --- a/book/security.rst +++ b/book/security.rst @@ -290,8 +290,11 @@ before inserting them into the database? Don't worry, see .. tip:: - Supported algorithms for this method depend on your PHP version. A full list - is available by calling the PHP function :phpfunction:`hash_algos`. + Supported algorithms for this method depend on your PHP version, but + include the algorithms returned by the PHP function :phpfunction:`hash_algos` + as well as a few others (e.g. bcrypt). See the ``encoders`` key in the + :doc:`Security Reference Section ` + for examples. D) Configuration Done! ~~~~~~~~~~~~~~~~~~~~~~ @@ -320,17 +323,20 @@ Great! Now, you need to learn how to deny access and work with the User object. This is called **authorization**, and its job is to decide if a user can access some resource (a URL, a model object, a method call, ...). -.. note:: - - The authorization system is flexible, and can even support complex ACL's - where you determine, for example, if user A can "EDIT" some object B - (e.g. a Product). For details, see :doc:`/cookbook/security/voters_data_permission`. - The process of authorization has two different sides: #. The user receives a specific set of roles when logging in (e.g. ``ROLE_ADMIN``). #. You add code so that a resource (e.g. URL, controller) requires a specific - role in order to be accessed. + "attribute" (most commonly a role like ``ROLE_ADMIN``) in order to be + accessed. + +.. tip:: + + In addition to roles (e.g. ``ROLE_ADMIN``), you can protect a resource + using other attributes/strings (e.g. ``EDIT``) and use voters or Symfony's + ACL system to give these meaning. This might come in handy if you need + to check if user A can "EDIT" some object B (e.g. a Product with id 5). + See :ref:`security-secure-objects`. .. _book-security-roles: @@ -344,9 +350,11 @@ in your table. .. caution:: - All roles **must** begin with the ``ROLE_`` prefix. Otherwise, they won't - be handled by Symfony. If you define your own roles with a dedicated - ``Role`` class (more advanced), don't use the ``ROLE_`` prefix. + All roles you assign to a user **must** begin with the ``ROLE_`` prefix. + Otherwise, they won't be handled by Symfony's security system in the + normal way (i.e. unless you're doing something advanced, assigning a + role like ``FOO`` to a user and then checking for ``FOO`` as described + :ref:`below ` will not work). Roles are simple, and are basically strings that you invent and use as needed. For example, if you need to start limiting access to the blog admin section @@ -363,6 +371,8 @@ it. You can also specify a :ref:`role hierarchy ` where some roles automatically mean that you also have other roles. +.. _security-role-authorization: + Add Code to Deny Access ~~~~~~~~~~~~~~~~~~~~~~~ @@ -486,24 +496,6 @@ That's it! If the user isn't logged in yet, they will be asked to login (e.g. redirected to the login page). If they *are* logged in, they'll be shown the 403 access denied page (which you can :ref:`customize `). -.. _book-security-securing-controller-annotations: - -Thanks to the SensioFrameworkExtraBundle, you can also secure your controller -using annotations:: - - // ... - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security; - - /** - * @Security("has_role('ROLE_ADMIN')") - */ - public function helloAction($name) - { - // ... - } - -For more information, see the `FrameworkExtraBundle documentation`_. - .. _book-security-template: Access Control in Templates @@ -575,16 +567,24 @@ user is logged in (you don't care about roles), then you can see ``IS_AUTHENTICA You can of course also use this in ``access_control``. ``IS_AUTHENTICATED_FULLY`` isn't a role, but it kind of acts like one, and every -user that has successfull logged in will have this. In fact, there are thre +user that has successfully logged in will have this. In fact, there are three special attributes like this: -* ``IS_AUTHENTICATED_FULLY``: All "logged-in" users have this; -* ``IS_AUTHENTICATED_REMEMBERED``: Similar to ``IS_AUTHENTICATED_FULLY`` - but important if you're using :doc:`remember me functionality `; +* ``IS_AUTHENTICATED_REMEMBERED``: *All* logged in users have this, even + if they are logged in because of a "remember me cookie". Even if you don't + use the :doc:`remember me functionality `, + you can use this to check if the user is logged in. + +* ``IS_AUTHENTICATED_FULLY``: This is similar to ``IS_AUTHENTICATED_REMEMBERED``, + but stronger. Users who are logged in only because of a "remember me cookie" + will have ``IS_AUTHENTICATED_REMEMBERED`` but will not have ``IS_AUTHENTICATED_FULLY``. + * ``IS_AUTHENTICATED_ANONYMOUSLY``: *All* users (even anonymous ones) have this - this is useful when *whitelisting* URLs to guarantee access - some details are in :doc:`/cookbook/security/access_control`. +.. _security-secure-objects: + Access Control Lists (ACLs): Securing individual Database Objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -667,7 +667,7 @@ the User object, and use the ``isGranted`` method (or } -Retreiving the User in a Template +Retrieving the User in a Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In a Twig Template this object can be accessed via the `app.user `_ From fe9fdac392f8c9c600c40c04e0b053e3ea7eefcd Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 30 Dec 2014 21:09:50 -0500 Subject: [PATCH 630/835] [#4606] Getting my XML (and PHP) on in the new security chapter --- book/security.rst | 310 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 308 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index bed403e24ef..cf336bf7d62 100644 --- a/book/security.rst +++ b/book/security.rst @@ -49,6 +49,48 @@ configuration looks like this: default: anonymous: ~ + .. code-block:: xml + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'providers' => array( + 'in_memory' => array( + 'memory' => array(), + ), + ), + 'firewalls' => array( + 'dev' => array( + 'pattern' => '^/(_(profiler|wdt)|css|images|js)/', + 'security' => false, + ), + 'default' => array( + 'anonymous' => null, + ), + ), + )); + The ``firewalls`` key is the *heart* of your security configuration. The ``dev`` firewall isn't important, it just makes sure that Symfony's development tools - which live under URLs like ``/_profiler`` and ``/_wdt`` aren't blocked @@ -96,6 +138,39 @@ To activate this, add the ``http_basic`` key under your firewall: anonymous: ~ http_basic: ~ + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + // ... + 'default' => array( + 'anonymous' => null, + 'http_basic' => null, + ), + ), + )); + Simple! To try this, you need to require the user to be logged in to see a page. To make things interesting, create a new page at ``/admin``. For example, if you use annotations, create something like this:: @@ -131,9 +206,49 @@ user to be logged in to access this URL: # ... access_control: - # require ROLE_ADMIN for /admin/* + # require ROLE_ADMIN for /admin* - { path: ^/admin, roles: ROLE_ADMIN } + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + // ... + 'default' => array( + // ... + ), + ), + 'access_control' => array( + // require ROLE_ADMIN for /admin* + array('path' => '^/admin', 'role' => 'ROLE_ADMIN'), + ), + )); + .. note:: You'll learn more about this ``ROLE_ADMIN`` thing and denying access @@ -185,6 +300,50 @@ provider, but it's better to think of it as an "in configuration" provider: admin: password: kitten roles: 'ROLE_ADMIN' + # ... + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'providers' => array( + 'in_memory' => array( + 'memory' => array( + 'users' => array( + 'ryan' => array( + 'password' => 'ryanpass', + 'roles' => 'ROLE_USER', + ), + 'admin' => array( + 'password' => 'kitten', + 'roles' => 'ROLE_ADMIN', + ), + ), + ), + ), + ), + // ... + )); Like with ``firewalls``, you can have multiple ``providers``, but you'll probably only need one. If you *do* have multiple, you can configure which @@ -208,6 +367,37 @@ To fix this, add an ``encoders`` key: encoders: Symfony\Component\Security\Core\User\User: plaintext + # ... + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + + 'encoders' => array( + 'Symfony\Component\Security\Core\User\User' => 'plaintext', + ), + // ... + )); User providers load user information and put it into a ``User`` object. If you :doc:`load users from the database ` @@ -258,6 +448,39 @@ else, you'll want to encode their passwords. The best algorithm to use is algorithm: bcrypt cost: 12 + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + + 'encoders' => array( + 'Symfony\Component\Security\Core\User\User' => array( + 'algorithm' => 'plaintext', + 'cost' => 12, + ) + ), + // ... + )); + .. include:: /cookbook/security/_ircmaxwell_password-compat.rst.inc Of course, your user's passwords now need to be encoded with this exact algorithm. @@ -283,6 +506,49 @@ like this: password: $2a$12$cyTWeE9kpq1PjqKFiWUZFuCRPwVyAZwm4XzMZ1qPUFl7/flCM3V0G roles: 'ROLE_ADMIN' + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + 'providers' => array( + 'in_memory' => array( + 'memory' => array( + 'users' => array( + 'ryan' => array( + 'password' => '$2a$12$LCY0MefVIEc3TYPHV9SNnuzOfyr2p/AXIGoQJEDs4am4JwhNz/jli', + 'roles' => 'ROLE_USER', + ), + 'admin' => array( + 'password' => '$2a$12$cyTWeE9kpq1PjqKFiWUZFuCRPwVyAZwm4XzMZ1qPUFl7/flCM3V0G', + 'roles' => 'ROLE_ADMIN', + ), + ), + ), + ), + ), + // ... + )); + Everything will now work exactly like before. But if you have dynamic users (e.g. from a database), how can you programmatically encode the password before inserting them into the database? Don't worry, see @@ -404,9 +670,49 @@ URL pattern. You saw this earlier, where anything matching the regular expressio # ... access_control: - # require ROLE_ADMIN for /admin/* + # require ROLE_ADMIN for /admin* - { path: ^/admin, roles: ROLE_ADMIN } + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // app/config/security.php + $container->loadFromExtension('security', array( + // ... + 'firewalls' => array( + // ... + 'default' => array( + // ... + ), + ), + 'access_control' => array( + // require ROLE_ADMIN for /admin* + array('path' => '^/admin', 'role' => 'ROLE_ADMIN'), + ), + )); + This is great for securing entire sections, but you'll also probably want to :ref:`secure your individual controllers ` as well. From 91db61ab6237a3479f6b4e50ef85b5a8fd358a6f Mon Sep 17 00:00:00 2001 From: Iltar van der Berg Date: Tue, 30 Dec 2014 14:48:29 +0100 Subject: [PATCH 631/835] [Security] Removed deprecated example about SecurityContext --- components/security/firewall.rst | 26 +++++++++++++++----------- cookbook/profiler/matchers.rst | 2 +- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/components/security/firewall.rst b/components/security/firewall.rst index 29c3730a1da..64603efb319 100644 --- a/components/security/firewall.rst +++ b/components/security/firewall.rst @@ -1,38 +1,42 @@ .. index:: single: Security, Firewall -The Firewall and Security Context -================================= +The Firewall and Authorization +============================== -Central to the Security component is the security context, which is an instance -of :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface`. When all -steps in the process of authenticating the user have been taken successfully, -you can ask the security context if the authenticated user has access to a +Central to the Security component is authorization. This is handled by an instance +of :class:`Symfony\\Component\\Security\\Core\\Authorization\\AuthorizationCheckerInterface`. +When all steps in the process of authenticating the user have been taken successfully, +you can ask the authorization checker if the authenticated user has access to a certain action or resource of the application:: - use Symfony\Component\Security\Core\SecurityContext; + use Symfony\Component\Security\Core\Authorization\AuthorizationChecker; use Symfony\Component\Security\Core\Exception\AccessDeniedException; + // instance of Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface + $tokenStorage = ...; + // instance of Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface $authenticationManager = ...; // instance of Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface $accessDecisionManager = ...; - $securityContext = new SecurityContext( + $authorizationChecker = new AuthorizationChecker( + $tokenStorage, $authenticationManager, $accessDecisionManager ); // ... authenticate the user - if (!$securityContext->isGranted('ROLE_ADMIN')) { + if (!$authorizationChecker->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); } .. versionadded:: 2.6 - As of Symfony 2.6, the :class:`Symfony\\Component\\Security\\Core\\SecurityContext` class was split - in the :class:`Symfony\\Component\\Security\\Core\\Authentication\\Authorization\\AuthorizationChecker` and + As of Symfony 2.6, the :class:`Symfony\\Component\\Security\\Core\\SecurityContext` class was split + in the :class:`Symfony\\Component\\Security\\Core\\Authorization\\AuthorizationChecker` and :class:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\Storage\\TokenStorage` classes. .. note:: diff --git a/cookbook/profiler/matchers.rst b/cookbook/profiler/matchers.rst index 485d7f9201c..b01ba820952 100644 --- a/cookbook/profiler/matchers.rst +++ b/cookbook/profiler/matchers.rst @@ -90,7 +90,7 @@ something like:: } .. versionadded:: 2.6 - The :class:`Symfony\\Component\\Security\\Core\\Authentication\\Authorization\\AuthorizationCheckerInterface` was + The :class:`Symfony\\Component\\Security\\Core\\Authorization\\AuthorizationCheckerInterface` was introduced in Symfony 2.6. Prior, you had to use the ``isGranted`` method of :class:`Symfony\\Component\\Security\\Core\\SecurityContextInterface`. From e174d4763a683f548196770ee0aa4cdd42df67fe Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 30 Dec 2014 22:21:31 -0500 Subject: [PATCH 632/835] Updating one more reference of security.context that I missed in the merge --- cookbook/expression/expressions.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cookbook/expression/expressions.rst b/cookbook/expression/expressions.rst index d4b18df95c8..249e4b875a9 100644 --- a/cookbook/expression/expressions.rst +++ b/cookbook/expression/expressions.rst @@ -39,7 +39,7 @@ accepts an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` object:: public function indexAction() { - if (!$this->get('security.context')->isGranted(new Expression( + if (!$this->get('security.authorization_checker')->isGranted(new Expression( '"ROLE_ADMIN" in roles or (user and user.isSuperAdmin())' ))) { throw $this->createAccessDeniedException(); @@ -99,10 +99,10 @@ Additionally, you have access to a number of functions inside the expression: use Symfony\Component\ExpressionLanguage\Expression; // ... - $sc = $this->get('security.context'); - $access1 = $sc->isGranted('IS_AUTHENTICATED_REMEMBERED'); + $ac = $this->get('security.authorization_checker'); + $access1 = $ac->isGranted('IS_AUTHENTICATED_REMEMBERED'); - $access2 = $sc->isGranted(new Expression( + $access2 = $ac->isGranted(new Expression( 'is_remember_me() or is_fully_authenticated()' )); From 725f92706d2ce72478e1ea51dbe2b2ac674dae9d Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 30 Dec 2014 22:27:40 -0500 Subject: [PATCH 633/835] removing duplicate key --- book/security.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index 744e098c1ba..5545c7f782c 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1054,8 +1054,6 @@ key: .. _book-security-logging-out: -.. _book-security-logging-out: - Logging Out ----------- From 2edb030dd1005ba53f00df27ed9082195fdcd27c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 30 Dec 2014 22:30:34 -0500 Subject: [PATCH 634/835] Fixing build error --- book/security.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/book/security.rst b/book/security.rst index 5545c7f782c..89c5f4826b9 100644 --- a/book/security.rst +++ b/book/security.rst @@ -921,8 +921,6 @@ special attributes like this: this - this is useful when *whitelisting* URLs to guarantee access - some details are in :doc:`/cookbook/security/access_control`. -.. _security-secure-objects: - .. _book-security-template-expression: .. versionadded:: 2.4 @@ -950,6 +948,8 @@ You can also use expressions inside your templates: For more details on expressions and security, see :ref:`book-security-expressions`. +.. _security-secure-objects: + Access Control Lists (ACLs): Securing individual Database Objects ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 5deb315cca2619418595d965ee9d094c2f9d1542 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Tue, 30 Dec 2014 22:35:59 -0500 Subject: [PATCH 635/835] Fixing build error --- cookbook/expression/expressions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/expression/expressions.rst b/cookbook/expression/expressions.rst index d4b18df95c8..7c8484fbfc7 100644 --- a/cookbook/expression/expressions.rst +++ b/cookbook/expression/expressions.rst @@ -64,7 +64,7 @@ Inside the expression, you have access to a number of variables: The user object (or the string ``anon`` if you're not authenticated). ``roles`` The array of roles the user has, including from the - :ref:`role hierarchy ` but not including the + :ref:`role hierarchy ` but not including the ``IS_AUTHENTICATED_*`` attributes (see the functions below). ``object`` The object (if any) that's passed as the second argument to ``isGranted``. From c3662c08b7abda04a286c41d364f31e32cb5f92d Mon Sep 17 00:00:00 2001 From: KULDIP PIPALIYA Date: Wed, 31 Dec 2014 13:45:48 +0530 Subject: [PATCH 636/835] Link fixed --- book/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/installation.rst b/book/installation.rst index 4b3fa4b2f69..565c5f85658 100644 --- a/book/installation.rst +++ b/book/installation.rst @@ -364,7 +364,7 @@ a wide variety of articles about solving specific problems with Symfony. at this cookbook article: ":doc:`/cookbook/bundles/remove`" .. _`Symfony Release process`: http://symfony.com/doc/current/contributing/community/releases.html -.. _`explained in this post`: http://fabien.potencier.org.nyud.net/article/73/signing-project-releases +.. _`explained in this post`: http://fabien.potencier.org/article/73/signing-project-releases .. _`Composer`: http://getcomposer.org/ .. _`Composer download page`: https://getcomposer.org/download/ .. _`Apache`: http://httpd.apache.org/docs/current/mod/core.html#documentroot From 2186ec6dcdd26a099442dc302e983dd4a3b00a24 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 31 Dec 2014 13:13:43 +0100 Subject: [PATCH 637/835] fix note directive --- cookbook/composer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/composer.rst b/cookbook/composer.rst index 6444c8167e2..d57624c4a29 100644 --- a/cookbook/composer.rst +++ b/cookbook/composer.rst @@ -17,7 +17,7 @@ To install Composer on Linux or Mac OS X, execute the following two commands: $ curl -sS https://getcomposer.org/installer | php $ sudo mv composer.phar /usr/local/bin/composer --.. note:: +.. note:: If you don't have ``curl`` installed, you can also just download the ``installer`` file manually at http://getcomposer.org/installer and From c66ee9f7155141c05294297772e1b41c8048be0c Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Thu, 1 Jan 2015 17:27:49 +0200 Subject: [PATCH 638/835] Remove horizontal scrollbar | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.3 | Fixed tickets | --- book/internals.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/book/internals.rst b/book/internals.rst index 87bae69895d..0aebef69362 100644 --- a/book/internals.rst +++ b/book/internals.rst @@ -532,7 +532,8 @@ method to access tokens based on some criteria:: $tokens = $container->get('profiler')->find('127.0.0.1', '', 10, '', ''); // get the latest 10 tokens for requests that happened between 2 and 4 days ago - $tokens = $container->get('profiler')->find('', '', 10, '4 days ago', '2 days ago'); + $tokens = $container->get('profiler') + ->find('', '', 10, '4 days ago', '2 days ago'); If you want to manipulate profiling data on a different machine than the one where the information were generated, use the @@ -576,9 +577,12 @@ the configuration for the development environment: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:webprofiler="http://symfony.com/schema/dic/webprofiler" xmlns:framework="http://symfony.com/schema/dic/symfony" - xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd - http://symfony.com/schema/dic/webprofiler http://symfony.com/schema/dic/webprofiler/webprofiler-1.0.xsd - http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + xsi:schemaLocation="http://symfony.com/schema/dic/services + http://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/webprofiler + http://symfony.com/schema/dic/webprofiler/webprofiler-1.0.xsd + http://symfony.com/schema/dic/symfony + http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> @@ -638,7 +642,9 @@ If you enable the web profiler, you also need to mount the profiler routes: use Symfony\Component\Routing\RouteCollection; - $profiler = $loader->import('@WebProfilerBundle/Resources/config/routing/profiler.xml'); + $profiler = $loader->import( + '@WebProfilerBundle/Resources/config/routing/profiler.xml' + ); $profiler->addPrefix('/_profiler'); $collection = new RouteCollection(); From 4c8d75f255d52273a6ef1a4f0271d8fc0a87f566 Mon Sep 17 00:00:00 2001 From: muxator Date: Thu, 1 Jan 2015 17:39:38 +0100 Subject: [PATCH 639/835] Renamed example: "Acme\BlogBundle" -> "AppBundle" In the context of this sentence, the correct fully-qualified class name for the controller seems to be AppBundle\Controller\BlogController::showAction and not Acme\BlogBundle\Controller\BlogController::showAction --- book/routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book/routing.rst b/book/routing.rst index 631d0857b2b..57b489d2eb9 100644 --- a/book/routing.rst +++ b/book/routing.rst @@ -1169,7 +1169,7 @@ Notice that Symfony adds the string ``Controller`` to the class name (``Blog`` => ``BlogController``) and ``Action`` to the method name (``show`` => ``showAction``). You could also refer to this controller using its fully-qualified class name -and method: ``Acme\BlogBundle\Controller\BlogController::showAction``. +and method: ``AppBundle\Controller\BlogController::showAction``. But if you follow some simple conventions, the logical name is more concise and allows more flexibility. From ba881a390709007381cc0fb6c3e0d787fd65f544 Mon Sep 17 00:00:00 2001 From: Florian Klein Date: Fri, 12 Dec 2014 18:50:54 +0100 Subject: [PATCH 640/835] Update by_reference.rst.inc --- reference/forms/types/options/by_reference.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/options/by_reference.rst.inc b/reference/forms/types/options/by_reference.rst.inc index 8f7fffab0fe..3e7987addb9 100644 --- a/reference/forms/types/options/by_reference.rst.inc +++ b/reference/forms/types/options/by_reference.rst.inc @@ -42,4 +42,4 @@ call the setter on the parent object. Similarly, if you're using the :doc:`collection` form type where your underlying collection data is an object (like with Doctrine's ``ArrayCollection``), then ``by_reference`` must be set to ``false`` if you -need the setter (e.g. ``setAuthors()``) to be called. +need the adder and remover (e.g. ``addAuthor()`` and ``removeAuthor()``) to be called. From d9c22098c1629dd5ae5a121bc2e985df1800eb70 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 1 Jan 2015 17:47:34 -0500 Subject: [PATCH 641/835] [#4651] Fixing build error --- contributing/code/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/security.rst b/contributing/code/security.rst index c3d8e709942..52b91ac8b59 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -98,7 +98,7 @@ Security Advisories .. tip:: You can check your Symfony application for known security vulnerabilities - using the ``security:check`` command. See :doc:`` + using the ``security:check`` command. See :ref:``. This section indexes security vulnerabilities that were fixed in Symfony releases, starting from Symfony 1.0.0: From 81cae4032f260b3edf7fb1a2e3fa26fb2389c729 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 1 Jan 2015 17:50:19 -0500 Subject: [PATCH 642/835] Fixing bad merge - this section should not have been left in --- book/security.rst | 56 ----------------------------------------------- 1 file changed, 56 deletions(-) diff --git a/book/security.rst b/book/security.rst index 3603764981a..daecfa876c4 100644 --- a/book/security.rst +++ b/book/security.rst @@ -1228,62 +1228,6 @@ cookie will be ever created by Symfony): If you use a form login, Symfony will create a cookie even if you set ``stateless`` to ``true``. -Utilities ---------- - -.. versionadded:: 2.2 - The ``StringUtils`` and ``SecureRandom`` classes were introduced in Symfony - 2.2 - -The Symfony Security component comes with a collection of nice utilities related -to security. These utilities are used by Symfony, but you should also use -them if you want to solve the problem they address. - -Comparing Strings -~~~~~~~~~~~~~~~~~ - -The time it takes to compare two strings depends on their differences. This -can be used by an attacker when the two strings represent a password for -instance; it is known as a `Timing attack`_. - -Internally, when comparing two passwords, Symfony uses a constant-time -algorithm; you can use the same strategy in your own code thanks to the -:class:`Symfony\\Component\\Security\\Core\\Util\\StringUtils` class:: - - use Symfony\Component\Security\Core\Util\StringUtils; - - // is password1 equals to password2? - $bool = StringUtils::equals($password1, $password2); - -Generating a secure random Number -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Whenever you need to generate a secure random number, you are highly -encouraged to use the Symfony -:class:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom` class:: - - use Symfony\Component\Security\Core\Util\SecureRandom; - - $generator = new SecureRandom(); - $random = $generator->nextBytes(10); - -The -:method:`Symfony\\Component\\Security\\Core\\Util\\SecureRandom::nextBytes` -methods returns a random string composed of the number of characters passed as -an argument (10 in the above example). - -The SecureRandom class works better when OpenSSL is installed but when it's -not available, it falls back to an internal algorithm, which needs a seed file -to work correctly. Just pass a file name to enable it:: - - $generator = new SecureRandom('/some/path/to/store/the/seed.txt'); - $random = $generator->nextBytes(10); - -.. note:: - - You can also access a secure random instance directly from the Symfony - dependency injection container; its name is ``security.secure_random``. - .. _book-security-checking-vulnerabilities: Checking for Known Security Vulnerabilities in Dependencies From 676305285929f136c00021bf1e1d1d58482dc59e Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Thu, 1 Jan 2015 17:52:22 -0500 Subject: [PATCH 643/835] fixing bad link --- contributing/code/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 52b91ac8b59..0cf3bb3cfb3 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -98,7 +98,7 @@ Security Advisories .. tip:: You can check your Symfony application for known security vulnerabilities - using the ``security:check`` command. See :ref:``. + using the ``security:check`` command. See :ref:`book-security-checking-vulnerabilities`. This section indexes security vulnerabilities that were fixed in Symfony releases, starting from Symfony 1.0.0: From a6c47db74b772fb49c72fdda2c7e461182296c15 Mon Sep 17 00:00:00 2001 From: nietonfir Date: Fri, 2 Jan 2015 02:08:02 +0100 Subject: [PATCH 644/835] Fixed typo in factories.rst --- components/dependency_injection/factories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/factories.rst b/components/dependency_injection/factories.rst index 6af4691ad9d..e4cc04152f9 100644 --- a/components/dependency_injection/factories.rst +++ b/components/dependency_injection/factories.rst @@ -96,7 +96,7 @@ be non-static. - + From 09626311ddaf51b3b7ad090466a8368ac5f73ce7 Mon Sep 17 00:00:00 2001 From: "Andrew (Andrius) Marcinkevicius" Date: Fri, 2 Jan 2015 12:31:22 +0200 Subject: [PATCH 645/835] Fix typo: as => is | Q | A | ------------- | --- | Doc fix? | yes | New docs? | no | Applies to | 2.3 | Fixed tickets | --- contributing/code/conventions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/conventions.rst b/contributing/code/conventions.rst index 6f102e8f6a2..e48eebd414e 100644 --- a/contributing/code/conventions.rst +++ b/contributing/code/conventions.rst @@ -75,7 +75,7 @@ must be used instead (where ``XXX`` is the name of the related thing): While "setXXX" and "replaceXXX" are very similar, there is one notable difference: "setXXX" may replace, or add new elements to the relation. "replaceXXX", on the other hand, cannot add new elements. If an unrecognized - key as passed to "replaceXXX" it must throw an exception. + key is passed to "replaceXXX" it must throw an exception. .. _contributing-code-conventions-deprecations: From 7fc4d525b530d84d66ef9a64b92bb94e75098786 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 20 Dec 2014 23:53:43 +0100 Subject: [PATCH 646/835] document the `2.5` validation options --- reference/configuration/framework.rst | 44 ++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 14e198d0025..cda9a5bbb29 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -55,6 +55,8 @@ Configuration * `cache`_ * `enable_annotations`_ * `translation_domain`_ + * `strict_email`_ + * `api`_ secret ~~~~~~ @@ -531,11 +533,8 @@ cache **type**: ``string`` -This value is used to determine the service that is used to persist class -metadata in a cache. The actual service name is built by prefixing the configured -value with ``validator.mapping.cache.`` (e.g. if the value is ``apc``, the -``validator.mapping.cache.apc`` service will be injected). The service has -to implement the :class:`Symfony\\Component\\Validator\\Mapping\\Cache\\CacheInterface`. +The service that is used to persist class metadata in a cache. The service +has to implement the :class:`Symfony\\Component\\Validator\\Mapping\\Cache\\CacheInterface`. enable_annotations .................. @@ -552,6 +551,41 @@ translation_domain The translation domain that is used when translating validation constraint error messages. +strict_email +............ + +.. versionadded:: 2.5 + The ``strict_email`` option was introduced in Symfony 2.5. + +**type**: ``Boolean`` **default**: ``false`` + +If this option is enabled, the `egulias/email-validator`_ library will be +used by the :doc:`/reference/constraints/Email` constraint validator. Otherwise, +the validator uses a simple regular expression to validate email addresses. + +api +... + +.. versionadded:: 2.5 + The ``api`` option was introduced in Symfony 2.5. + +**type**: ``string`` + +Starting with Symfony 2.5, the Validator component introduced a new validation +API. The ``api`` option is used to switch between the different implementations: + +``2.4`` + Use the vaidation API that is compatible with older Symfony versions. + +``2.5`` + Use the validation API introduced in Symfony 2.5. + +``2.5-bc`` or ``auto`` + If you omit a value or set the ``api`` option to ``2.5-bc`` or ``auto``, + Symfony will use an API implementation that is compatible with both the + legacy implementation and the ``2.5`` implementation. You have to use + PHP 5.3.9 or higher to be able to use this implementation. + Full default Configuration -------------------------- From 46647c126286b79de0d92c53a7f5564370757cdf Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 2 Jan 2015 15:34:42 +0100 Subject: [PATCH 647/835] add missing comma and remove a serial comma --- best_practices/security.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/best_practices/security.rst b/best_practices/security.rst index 0519b917edf..19ce6574c14 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -73,7 +73,7 @@ Authorization (i.e. Denying Access) ----------------------------------- Symfony gives you several ways to enforce authorization, including the ``access_control`` -configuration in :doc:`security.yml ` the +configuration in :doc:`security.yml `, the :ref:`@Security annotation ` and using :ref:`isGranted ` on the ``security.context`` service directly. @@ -232,8 +232,8 @@ Now, you can use the voter with the ``@Security`` annotation: // ... } -You can also use this directly with the ``security.context`` service, or -via the even easier shortcut in a controller: +You can also use this directly with the ``security.context`` service or via +the even easier shortcut in a controller: .. code-block:: php From c0c6dab33eb659ccebe26d9c5428b429c181fb2b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 2 Jan 2015 16:33:18 +0100 Subject: [PATCH 648/835] add missing versionadded directive The `versionadded` directive was present when #3995 was merged, but it apparently got lost in one the refactorings of the chapter afterwards. --- book/security.rst | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/book/security.rst b/book/security.rst index 5d4d4ff6649..6942b426965 100644 --- a/book/security.rst +++ b/book/security.rst @@ -209,7 +209,7 @@ user to be logged in to access this URL: # ... firewalls: # ... - + access_control: # require ROLE_ADMIN for /admin* - { path: ^/admin, roles: ROLE_ADMIN } @@ -676,7 +676,7 @@ URL pattern. You saw this earlier, where anything matching the regular expressio # ... firewalls: # ... - + access_control: # require ROLE_ADMIN for /admin* - { path: ^/admin, roles: ROLE_ADMIN } @@ -870,9 +870,9 @@ in this chapter). Be careful with this in your layout or on your error pages! Because of some internal Symfony details, to avoid broken error pages in the ``prod`` environment, wrap calls in these templates with a check for ``app.user``: - + .. code-block:: html+jinja - + {% if app.user and is_granted('ROLE_ADMIN') %} Securing other Services @@ -1036,7 +1036,7 @@ the User object, and use the ``isGranted`` method (or // boo :(. Never check for the User object to see if they're logged in if ($this->getUser()) { - + } Retrieving the User in a Template @@ -1055,7 +1055,7 @@ key: .. code-block:: html+php - isGranted('IS_AUTHENTICATED_FULLY')): ?> + isGranted('IS_AUTHENTICATED_FULLY')): ?>