diff --git a/book/_security-2012-6431.rst.inc b/book/_security-2012-6431.rst.inc deleted file mode 100644 index 5d9137bba63..00000000000 --- a/book/_security-2012-6431.rst.inc +++ /dev/null @@ -1,9 +0,0 @@ -.. note:: - - Since Symfony 2.0.20/2.1.5, the Twig ``render`` tag now takes an absolute url - instead of a controller logical path. This fixes an important security - issue (`CVE-2012-6431`_) reported on the official blog. If your application - uses an older version of Symfony or still uses the previous ``render`` tag - syntax, you should upgrade as soon as possible. - -.. _`CVE-2012-6431`: http://symfony.com/blog/security-release-symfony-2-0-20-and-2-1-5-released \ No newline at end of file diff --git a/book/http_cache.rst b/book/http_cache.rst index 266a238005a..76eef9765e6 100644 --- a/book/http_cache.rst +++ b/book/http_cache.rst @@ -881,58 +881,37 @@ matter), Symfony2 uses the standard ``render`` helper to configure ESI tags: .. code-block:: jinja - {% render url('latest_news', { 'max': 5 }), {'standalone': true} %} + {# you can use a controller reference #} + {{ render_esi(controller('...:news', { 'max': 5 })) }} + + {# ... or a URL #} + {{ render_esi(url('latest_news', { 'max': 5 })) }} .. code-block:: php + render( + new ControllerReference('...:news', array('max' => 5)), + array('strategy' => 'esi')) + ?> + render( $view['router']->generate('latest_news', array('max' => 5), true), - array('standalone' => true) + array('strategy' => 'esi') ) ?> -.. include:: /book/_security-2012-6431.rst.inc - -The ``render`` tag takes the absolute url to the embedded action. This means -that you need to define a new route to the controller that you're embedding: - -.. code-block:: yaml - - # app/config/routing.yml - latest_news: - pattern: /esi/latest-news/{max} - defaults: { _controller: AcmeNewsBundle:News:news } - requirements: { max: \d+ } - -.. caution:: - - Unless you want this URL to be accessible to the outside world, you - should use Symfony's firewall to secure it (by allowing access to your - reverse proxy's IP range). See the :ref:`Securing by IP` - section of the :doc:`Security Chapter ` for more information - on how to do this. - -.. tip:: +By using the ``esi`` rendering strategy (via the ``render_esi`` Twig +function), you tell Symfony2 that the action should be rendered as an ESI tag. +You might be 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. - The best practice is to mount all your ESI urls on a single prefix (e.g. - ``/esi``) of your choice. This has two main advantages. First, it eases - the management of ESI urls as you can easily identify the routes used for ESI. - Second, it eases security management since securing all urls starting - with the same prefix is easier than securing each individual url. See - the above note for more details on securing ESI URLs. - -By setting ``standalone`` to ``true`` in the ``render`` Twig tag, you tell -Symfony2 that the action should be rendered as an ESI tag. You might be -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. - -When standalone is ``false`` (the default), Symfony2 merges the included page -content within the main one before sending the response to the client. But -when standalone is ``true``, *and* if Symfony2 detects that it's talking -to a gateway cache that supports ESI, it generates an ESI include tag. But -if there is no gateway cache or if it does not support ESI, Symfony2 will -just merge the included page content within the main one as it would have -done were standalone set to ``false``. +When using the default ``render`` function (or setting the strategy to +``default``), Symfony2 merges the included page content within the main one +before sending the response to the client. But when using ``esi`` strategy, +*and* if Symfony2 detects that it's talking to a gateway cache that supports +ESI, it generates an ESI include tag. But if there is no gateway cache or if +it does not support ESI, Symfony2 will just merge the included page content +within the main one as it would have done if you had used ``render``. .. note:: @@ -947,14 +926,52 @@ of the master page. public function newsAction($max) { - // ... + // ... - $response->setSharedMaxAge(60); + $response->setSharedMaxAge(60); } With ESI, the full page cache will be valid for 600 seconds, but the news component cache will only last for 60 seconds. +When using a controller reference, the ESI tag should reference the embedded +action as an accessible URL so the gateway cache can fetch it independently of +the rest of the page. Of course, an action can't be accessed via a URL unless +it has a route that points to it. Symfony2 takes care of this via a generic +route and controller. For the ESI include tag to work properly, you must +define the ``_proxy`` route: + +.. configuration-block:: + + .. code-block:: yaml + + # app/config/routing.yml + _proxy: + resource: "@FrameworkBundle/Resources/config/routing/proxy.xml" + prefix: /proxy + + .. code-block:: xml + + + + + + + + + + .. code-block:: php + + // app/config/routing.php + use Symfony\Component\Routing\RouteCollection; + use Symfony\Component\Routing\Route; + + $collection->addCollection($loader->import('@FrameworkBundle/Resources/config/routing/proxy.xml', '/proxy')); + + return $collection; + One great advantage of this caching strategy is that you can make your application as dynamic as needed and at the same time, hit the application as little as possible. @@ -967,7 +984,7 @@ little as possible. obey the ``max-age`` directive and cache the entire page. And you don't want that. -The ``render`` helper supports two other useful options: +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; diff --git a/book/templating.rst b/book/templating.rst index decaf19417b..54d282ebc2f 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -532,9 +532,7 @@ Including this template from any other template is simple:

Recent Articles

{% for article in articles %} - {% include 'AcmeArticleBundle:Article:articleDetails.html.twig' - with {'article': article} - %} + {{ include('AcmeArticleBundle:Article:articleDetails.html.twig', {'article': article}) }} {% endfor %} {% endblock %} @@ -551,7 +549,7 @@ Including this template from any other template is simple: stop() ?> -The template is included using the ``{% include %}`` tag. Notice that the +The template is included using the ``{{ include() }}`` function. Notice that the template name follows the same typical convention. The ``articleDetails.html.twig`` template uses an ``article`` variable. This is passed in by the ``list.html.twig`` template using the ``with`` command. @@ -623,43 +621,8 @@ The ``recentList`` template is perfectly straightforward: (e.g. ``/article/*slug*``). This is a bad practice. In the next section, you'll learn how to do this correctly. -Even though this controller will only be used internally, you'll need to -create a route that points to the controller: - -.. configuration-block:: - - .. code-block:: yaml - - latest_articles: - pattern: /articles/latest/{max} - defaults: { _controller: AcmeArticleBundle:Article:recentArticles } - - .. code-block:: xml - - - - - - - AcmeArticleBundle:Article:recentArticles - - - - .. code-block:: php - - use Symfony\Component\Routing\RouteCollection; - use Symfony\Component\Routing\Route; - - $collection = new RouteCollection(); - $collection->add('latest_articles', new Route('/articles/latest/{max}', array( - '_controller' => 'AcmeArticleBundle:Article:recentArticles', - ))); - - return $collection; - -To include the controller, you'll need to refer to it using an absolute url: +To include the controller, you'll need to refer to it using the standard +string syntax for controllers (i.e. **bundle**:**controller**:**action**): .. configuration-block:: @@ -669,7 +632,7 @@ To include the controller, you'll need to refer to it using an absolute url: {# ... #} .. code-block:: html+php @@ -679,12 +642,10 @@ To include the controller, you'll need to refer to it using an absolute url: -.. include:: /book/_security-2012-6431.rst.inc - Whenever you find that you need a variable or a piece of information that you don't have access to in a template, consider rendering a controller. Controllers are fast to execute and promote good code organization and reuse. @@ -703,13 +664,20 @@ Symfony2 uses the standard ``render`` helper to configure ``hinclude`` tags: .. code-block:: jinja - {% render url('...'), {'standalone': 'js'} %} + {{ render_hinclude(controller('...')) }} + + {{ render_hinclude(url('...')) }} .. code-block:: php + render( + new ControllerReference('...'), + array('strategy' => 'hinclude') + ) ?> + render( $view['router']->generate('...'), - array('standalone' => 'js') + array('strategy' => 'hinclude') ) ?> .. note:: @@ -756,18 +724,14 @@ any global default templates that is defined): .. code-block:: jinja - {% render '...:news' with - {}, - {'standalone': 'js', 'default': 'AcmeDemoBundle:Default:content.html.twig'} - %} + {{ render_hinclude(controller('...'), {'default': 'AcmeDemoBundle:Default:content.html.twig'}) }} .. code-block:: php render( - '...:news', - array(), + new ControllerReference('...'), array( - 'standalone' => 'js', + 'strategy' => 'hinclude', 'default' => 'AcmeDemoBundle:Default:content.html.twig', ) ) ?> @@ -778,18 +742,14 @@ Or you can also specify a string to display as the default content: .. code-block:: jinja - {% render '...:news' with - {}, - {'standalone': 'js', 'default': 'Loading...'} - %} + {{ render_hinclude(controller('...'), {'default': 'Loading...'}) }} .. code-block:: php render( - '...:news', - array(), + new ControllerReference('...'), array( - 'standalone' => 'js', + 'strategy' => 'hinclude', 'default' => 'Loading...', ) ) ?> diff --git a/cookbook/service_container/scopes.rst b/cookbook/service_container/scopes.rst index 2eea3583f04..aee6710636f 100644 --- a/cookbook/service_container/scopes.rst +++ b/cookbook/service_container/scopes.rst @@ -7,7 +7,7 @@ How to work with Scopes This entry is all about scopes, a somewhat advanced topic related to the :doc:`/book/service_container`. If you've ever gotten an error mentioning "scopes" when creating services, or need to create a service that depends -on the `request` service, then this entry is for you. +on the ``request`` service, then this entry is for you. Understanding Scopes -------------------- @@ -16,49 +16,49 @@ The scope of a service controls how long an instance of a service is used by the container. The Dependency Injection component provides two generic scopes: -- `container` (the default one): The same instance is used each time you +- ``container`` (the default one): The same instance is used each time you request it from this container. -- `prototype`: A new instance is created each time you request the service. +- ``prototype``: A new instance is created each time you request the service. -The FrameworkBundle also defines a third scope: `request`. This scope is +The FrameworkBundle also defines a third scope: ``request``. This scope is tied to the request, meaning a new instance is created for each subrequest and is unavailable outside the request (for instance in the CLI). Scopes add a constraint on the dependencies of a service: a service cannot depend on services from a narrower scope. For example, if you create a generic -`my_foo` service, but try to inject the `request` component, you'll receive +``my_foo`` service, but try to inject the ``request`` component, you'll receive a :class:`Symfony\\Component\\DependencyInjection\\Exception\\ScopeWideningInjectionException` when compiling the container. Read the sidebar below for more details. .. sidebar:: Scopes and Dependencies - Imagine you've configured a `my_mailer` service. You haven't configured - the scope of the service, so it defaults to `container`. In other words, - every time you ask the container for the `my_mailer` service, you get + Imagine you've configured a ``my_mailer`` service. You haven't configured + the scope of the service, so it defaults to ``container``. In other words, + every time you ask the container for the ``my_mailer`` service, you get the same object back. This is usually how you want your services to work. - Imagine, however, that you need the `request` service in your `my_mailer` + Imagine, however, that you need the ``request`` service in your ``my_mailer`` service, maybe because you're reading the URL of the current request. So, you add it as a constructor argument. Let's look at why this presents a problem: - * When requesting `my_mailer`, an instance of `my_mailer` (let's call - it *MailerA*) is created and the `request` service (let's call it + * When requesting ``my_mailer``, an instance of ``my_mailer`` (let's call + it *MailerA*) is created and the ``request`` service (let's call it *RequestA*) is passed to it. Life is good! * You've now made a subrequest in Symfony, which is a fancy way of saying - that you've called, for example, the `{% render ... %}` Twig function, - which executes another controller. Internally, the old `request` service + that you've called, for example, the ``{{ render(...) }}`` Twig function, + which executes another controller. Internally, the old ``request`` service (*RequestA*) is actually replaced by a new request instance (*RequestB*). This happens in the background, and it's totally normal. - * In your embedded controller, you once again ask for the `my_mailer` - service. Since your service is in the `container` scope, the same + * In your embedded controller, you once again ask for the ``my_mailer`` + service. Since your service is in the ``container`` scope, the same instance (*MailerA*) is just re-used. But here's the problem: the *MailerA* instance still contains the old *RequestA* object, which is now **not** the correct request object to have (*RequestB* is now - the current `request` service). This is subtle, but the mis-match could + the current ``request`` service). This is subtle, but the mis-match could cause major problems, which is why it's not allowed. So, that's the reason *why* scopes exist, and how they can cause @@ -101,9 +101,9 @@ The scope of a service is set in the definition of the service: new Definition('Acme\HelloBundle\Mail\GreetingCardManager') )->setScope('request'); -If you don't specify the scope, it defaults to `container`, which is what +If you don't specify the scope, it defaults to ``container``, which is what you want most of the time. Unless your service depends on another service -that's scoped to a narrower scope (most commonly, the `request` service), +that's scoped to a narrower scope (most commonly, the ``request`` service), you probably don't need to set the scope. Using a Service from a narrower Scope @@ -111,10 +111,10 @@ Using a Service from a narrower Scope If your service depends on a scoped service, the best solution is to put it in the same scope (or a narrower one). Usually, this means putting your -new service in the `request` scope. +new service in the ``request`` scope. But this is not always possible (for instance, a twig extension must be in -the `container` scope as the Twig environment needs it as a dependency). +the ``container`` scope as the Twig environment needs it as a dependency). In these cases, you should pass the entire container into your service and retrieve your dependency from the container each time you need it to be sure you have the right instance:: diff --git a/cookbook/templating/render_without_controller.rst b/cookbook/templating/render_without_controller.rst index 3caa0a7e5f7..2f2b63e6d52 100644 --- a/cookbook/templating/render_without_controller.rst +++ b/cookbook/templating/render_without_controller.rst @@ -64,7 +64,7 @@ this is probably only useful if you'd like to cache this page partial (see .. code-block:: html+jinja - {% render url('acme_privacy') %} + {{ render(url('acme_privacy')) }} .. code-block:: html+php diff --git a/quick_tour/the_view.rst b/quick_tour/the_view.rst index 36cabefe769..ad3ed4557ff 100644 --- a/quick_tour/the_view.rst +++ b/quick_tour/the_view.rst @@ -170,7 +170,7 @@ And change the ``index.html.twig`` template to include it: {# override the body block from embedded.html.twig #} {% block content %} - {% include "AcmeDemoBundle:Demo:embedded.html.twig" %} + {{ include("AcmeDemoBundle:Demo:embedded.html.twig") }} {% endblock %} Embedding other Controllers @@ -180,57 +180,19 @@ And what if you want to embed the result of another controller in a template? That's very useful when working with Ajax, or when the embedded template needs some variable not available in the main template. -Suppose you've created a ``fancyAction`` controller method, and you want to "render" -it inside the ``index`` template. First, create a route to your new controller -in one of your application's routing configuration files. - -.. configuration-block:: - - .. code-block:: yaml - - # app/config/routing.yml - fancy: - pattern: /included/fancy/{name}/{color} - defaults: { _controller: AcmeDemoBundle:Demo:fancy } - - .. code-block:: xml - - - - - - - AcmeDemoBundle:Demo:fancy - - - - .. code-block:: php - - // app/config/routing.php - use Symfony\Component\Routing\RouteCollection; - use Symfony\Component\Routing\Route; - - $collection = new RouteCollection(); - $collection->add('fancy', new Route('/included/fancy/{name}/{color}', array( - '_controller' => 'AcmeDemoBundle:Demo:fancy', - ))); - - return $collection; - -To include the result (e.g. ``HTML``) of the controller, use the ``render`` tag: +Suppose you've created a ``fancyAction`` controller method, and you want to +"render" it inside the ``index`` template, which means including the result +(e.g. ``HTML``) of the controller, use the ``render`` function: .. code-block:: jinja {# src/Acme/DemoBundle/Resources/views/Demo/index.html.twig #} - {% render url('fancy', { 'name': name, 'color': 'green'}) %} - -.. include:: /book/_security-2012-6431.rst.inc + {{ render(controller("AcmeDemoBundle:Demo:fancy", {'name': name, 'color': 'green'})) }} -The ``render`` tag will execute the ``AcmeDemoBundle:Demo:fancy`` controller -and include its result. For example, your new ``fancyAction`` might look -like this:: +Here, the ``AcmeDemoBundle:Demo:fancy`` string refers to the ``fancy`` action +of the ``Demo`` controller. The arguments (``name`` and ``color``) act like +simulated request variables (as if the ``fancyAction`` were handling a whole +new request) and are made available to the controller:: // src/Acme/DemoBundle/Controller/DemoController.php @@ -243,7 +205,7 @@ like this:: return $this->render('AcmeDemoBundle:Demo:fancy.html.twig', array( 'name' => $name, - 'object' => $object + 'object' => $object, )); } diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 404b9b1dc2f..661d7574205 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -23,6 +23,18 @@ Functions +----------------------------------------------------+--------------------------------------------------------------------------------------------+ | Function Syntax | Usage | +====================================================+============================================================================================+ +| ``render(controller('B:C:a', {params}))`` | This will render the Response Content for the given controller or | +| ``render(path('route', {params}))`` | URL. For more information, see :ref:`templating-embedding-controller`. | +| ``render(url('route', {params}))`` | | ++----------------------------------------------------+--------------------------------------------------------------------------------------------+ +| ``render_esi(controller('B:C:a', {params}))`` | This will generates 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('B:C:a', {params}))`` | 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}))`` | | ++----------------------------------------------------+--------------------------------------------------------------------------------------------+ | ``asset(path, packageName = null)`` | Get the public path of the asset, more information in | | | ":ref:`book-templating-assets`". | +----------------------------------------------------+--------------------------------------------------------------------------------------------+ @@ -113,10 +125,6 @@ Tags +---------------------------------------------------+-------------------------------------------------------------------+ | Tag Syntax | Usage | +===================================================+===================================================================+ -| ``{% render url('route', {parameters}) %}`` | This will render the Response Content for the given controller | -| | that the URL points to. For more information, | -| | see :ref:`templating-embedding-controller`. | -+---------------------------------------------------+-------------------------------------------------------------------+ | ``{% form_theme form 'file' %}`` | This will look inside the given file for overridden form blocks, | | | more information in :doc:`/cookbook/form/form_customization`. | +---------------------------------------------------+-------------------------------------------------------------------+