From: Fabien Potencier Date: Thu, 15 Nov 2012 12:20:44 +0000 (+0100) Subject: added an include function X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=f4f88a51e275d380663b04bd371b215ef70bb7f0;p=konrad%2Ftwig.git added an include function --- diff --git a/CHANGELOG b/CHANGELOG index fffef7e..b1bcb6a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ * 1.12.0 (2012-XX-XX) + * added an include function (does the same as the include tag but in a more flexible way) * added the ability to set default values for macro arguments * added support for named arguments for filters, tests, and functions * moved filters/functions/tests syntax errors to the parser diff --git a/doc/functions/include.rst b/doc/functions/include.rst new file mode 100644 index 0000000..eaddfe6 --- /dev/null +++ b/doc/functions/include.rst @@ -0,0 +1,80 @@ +``include`` +=========== + +.. versionadded:: 1.12 + The include function was added in Twig 1.12. + +The ``include`` function returns the rendered content of a template: + +.. code-block:: jinja + + {{ include('template.html') }} + {{ include(some_var) }} + +Included templates have access to the variables of the active context. + +If you are using the filesystem loader, the templates are looked for in the +paths defined by it. + +The context is passed by default to the template but you can also pass +additional variables: + +.. code-block:: jinja + + {# template.html will have access to the variables from the current context and the additional ones provided #} + {{ include('template.html', {foo: 'bar'}) }} + +You can disable access to the context by setting ``with_context`` to +``false``: + +.. code-block:: jinja + + {# only the foo variable will be accessible #} + {{ include('template.html', {foo: 'bar'}, with_context = false) }} + +.. code-block:: jinja + + {# no variables will be accessible #} + {{ include('template.html', with_context = false) }} + +And if the expression evaluates to a ``Twig_Template`` object, Twig will use it +directly:: + + // {{ include(template) }} + + $template = $twig->loadTemplate('some_template.twig'); + + $twig->loadTemplate('template.twig')->display(array('template' => $template)); + +When you set the ``ignore_missing`` flag, Twig will return an empty string if +the template does not exist: + +.. code-block:: jinja + + {{ include('sidebar.html', ignore_missing = true) }} + +You can also provide a list of templates that are checked for existence before +inclusion. The first template that exists will be rendered: + +.. code-block:: jinja + + {{ include(['page_detailed.html', 'page.html']) }} + +If ``ignore_missing`` is set, it will fall back to rendering nothing if none +of the templates exist, otherwise it will throw an exception. + +When including a template created by an end user, you should consider +sandboxing it: + +.. code-block:: jinja + + {{ include('page.html', sandboxed = true) }} + +Arguments +--------- + + * ``template``: The template to render + * ``variables``: The variables to pass to the template + * ``with_context``: Whether to pass the current context variables or not + * ``ignore_missing``: Whether to ignore missing templates or not + * ``sandboxed``: Whether to sandbox the template or not diff --git a/doc/functions/index.rst b/doc/functions/index.rst index 4d9a003..c5ac896 100644 --- a/doc/functions/index.rst +++ b/doc/functions/index.rst @@ -14,3 +14,4 @@ Functions dump date template_from_string + include diff --git a/doc/tags/include.rst b/doc/tags/include.rst index 1440c50..01ad532 100644 --- a/doc/tags/include.rst +++ b/doc/tags/include.rst @@ -19,23 +19,23 @@ You can add additional variables by passing them after the ``with`` keyword: .. code-block:: jinja - {# the foo template will have access to the variables from the current context and the foo one #} - {% include 'foo' with {'foo': 'bar'} %} + {# template.html will have access to the variables from the current context and the additional ones provided #} + {% include 'template.html' with {'foo': 'bar'} %} {% set vars = {'foo': 'bar'} %} - {% include 'foo' with vars %} + {% include 'template.html' with vars %} You can disable access to the context by appending the ``only`` keyword: .. code-block:: jinja {# only the foo variable will be accessible #} - {% include 'foo' with {'foo': 'bar'} only %} + {% include 'template.html' with {'foo': 'bar'} only %} .. code-block:: jinja - {# no variable will be accessible #} - {% include 'foo' only %} + {# no variables will be accessible #} + {% include 'template.html' only %} .. tip:: @@ -68,9 +68,9 @@ placed just after the template name. Here some valid examples: .. code-block:: jinja - {% include "sidebar.html" ignore missing %} - {% include "sidebar.html" ignore missing with {'foo': 'bar} %} - {% include "sidebar.html" ignore missing only %} + {% include 'sidebar.html' ignore missing %} + {% include 'sidebar.html' ignore missing with {'foo': 'bar} %} + {% include 'sidebar.html' ignore missing only %} .. versionadded:: 1.2 The possibility to pass an array of templates has been added in Twig 1.2. diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index 3727f5f..6c26d04 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -190,6 +190,7 @@ class Twig_Extension_Core extends Twig_Extension 'cycle' => new Twig_Function_Function('twig_cycle'), 'random' => new Twig_Function_Function('twig_random', array('needs_environment' => true)), 'date' => new Twig_Function_Function('twig_date_converter', array('needs_environment' => true)), + 'include' => new Twig_Function_Function('twig_include', array('needs_environment' => true, 'needs_context' => true)), ); } @@ -1216,3 +1217,40 @@ function twig_test_iterable($value) { return $value instanceof Traversable || is_array($value); } + +/** + * Renders a template. + * + * @param string template The template to render + * @param array variables The variables to pass to the template + * @param Boolean with_context Whether to pass the current context variables or not + * @param Boolean ignore_missing Whether to ignore missing templates or not + * @param Boolean sandboxed Whether to sandbox the template or not + * + * @return string The rendered template + */ +function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false) +{ + if ($withContext) { + $variables = array_merge($context, $variables); + } + + if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) { + $sandbox = $env->getExtension('sandbox'); + if (!$alreadySandboxed = $sandbox->isSandboxed()) { + $sandbox->enableSandbox(); + } + } + + try { + return $env->resolveTemplate($template)->display($variables); + } catch (Twig_Error_Loader $e) { + if (!$ignoreMissing) { + throw $e; + } + } + + if ($isSandboxed && !$alreadySandboxed) { + $sandbox->disableSandbox(); + } +} diff --git a/test/Twig/Tests/Fixtures/functions/include/basic.test b/test/Twig/Tests/Fixtures/functions/include/basic.test new file mode 100644 index 0000000..a434182 --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/basic.test @@ -0,0 +1,17 @@ +--TEST-- +"include" function +--TEMPLATE-- +FOO +{{ include("foo.twig") }} + +BAR +--TEMPLATE(foo.twig)-- +FOOBAR +--DATA-- +return array() +--EXPECT-- +FOO + +FOOBAR + +BAR diff --git a/test/Twig/Tests/Fixtures/functions/include/expression.test b/test/Twig/Tests/Fixtures/functions/include/expression.test new file mode 100644 index 0000000..aba30ce --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/expression.test @@ -0,0 +1,17 @@ +--TEST-- +"include" function allows expressions for the template to include +--TEMPLATE-- +FOO +{{ include(foo) }} + +BAR +--TEMPLATE(foo.twig)-- +FOOBAR +--DATA-- +return array('foo' => 'foo.twig') +--EXPECT-- +FOO + +FOOBAR + +BAR diff --git a/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test b/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test new file mode 100644 index 0000000..43a2ccc --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test @@ -0,0 +1,10 @@ +--TEST-- +"include" function +--TEMPLATE-- +{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }} +{{ include("foo.twig", ignore_missing = true) }} +{{ include("foo.twig", ignore_missing = true, variables = {}) }} +{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }} +--DATA-- +return array() +--EXPECT-- diff --git a/test/Twig/Tests/Fixtures/functions/include/missing.test b/test/Twig/Tests/Fixtures/functions/include/missing.test new file mode 100644 index 0000000..4d2f6cf --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/missing.test @@ -0,0 +1,8 @@ +--TEST-- +"include" function +--TEMPLATE-- +{{ include("foo.twig") }} +--DATA-- +return array(); +--EXCEPTION-- +Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2. diff --git a/test/Twig/Tests/Fixtures/functions/include/missing_nested.test b/test/Twig/Tests/Fixtures/functions/include/missing_nested.test new file mode 100644 index 0000000..78fddc7 --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/missing_nested.test @@ -0,0 +1,16 @@ +--TEST-- +"include" function +--TEMPLATE-- +{% extends "base.twig" %} + +{% block content %} + {{ parent() }} +{% endblock %} +--TEMPLATE(base.twig)-- +{% block content %} + {{ include("foo.twig") }} +{% endblock %} +--DATA-- +return array(); +--EXCEPTION-- +Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3. diff --git a/test/Twig/Tests/Fixtures/functions/include/sandbox.test b/test/Twig/Tests/Fixtures/functions/include/sandbox.test new file mode 100644 index 0000000..788a2ab --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/sandbox.test @@ -0,0 +1,10 @@ +--TEST-- +"include" tag sandboxed +--TEMPLATE-- +{{ include("foo.twig", sandboxed = true) }} +--TEMPLATE(foo.twig)-- +{{ foo|e }} +--DATA-- +return array() +--EXCEPTION-- +Twig_Sandbox_SecurityError: Filter "e" is not allowed in "index.twig" at line 2. diff --git a/test/Twig/Tests/Fixtures/functions/include/template_instance.test b/test/Twig/Tests/Fixtures/functions/include/template_instance.test new file mode 100644 index 0000000..18d405a --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/template_instance.test @@ -0,0 +1,10 @@ +--TEST-- +"include" function accepts Twig_Template instance +--TEMPLATE-- +{{ include(foo) }} FOO +--TEMPLATE(foo.twig)-- +BAR +--DATA-- +return array('foo' => $twig->loadTemplate('foo.twig')) +--EXPECT-- +BAR FOO diff --git a/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test b/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test new file mode 100644 index 0000000..1a81006 --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test @@ -0,0 +1,12 @@ +--TEST-- +"include" function +--TEMPLATE-- +{{ include(["foo.twig", "bar.twig"]) }} +{{- include(["bar.twig", "foo.twig"]) }} +--TEMPLATE(foo.twig)-- +foo +--DATA-- +return array() +--EXPECT-- +foo +foo diff --git a/test/Twig/Tests/Fixtures/functions/include/with_context.test b/test/Twig/Tests/Fixtures/functions/include/with_context.test new file mode 100644 index 0000000..35611fb --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/with_context.test @@ -0,0 +1,16 @@ +--TEST-- +"include" function accept variables and with_context +--TEMPLATE-- +{{ include("foo.twig") }} +{{- include("foo.twig", with_context = false) }} +{{- include("foo.twig", {'foo1': 'bar'}) }} +{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }} +--TEMPLATE(foo.twig)-- +{% for k, v in _context %}{{ k }},{% endfor %} +--DATA-- +return array('foo' => 'bar') +--EXPECT-- +foo,global,_parent, +global,_parent, +foo,global,foo1,_parent, +foo1,global,_parent, diff --git a/test/Twig/Tests/Fixtures/functions/include/with_variables.test b/test/Twig/Tests/Fixtures/functions/include/with_variables.test new file mode 100644 index 0000000..b2ace94 --- /dev/null +++ b/test/Twig/Tests/Fixtures/functions/include/with_variables.test @@ -0,0 +1,12 @@ +--TEST-- +"include" function accept variables +--TEMPLATE-- +{{ include("foo.twig", {'foo': 'bar'}) }} +{{- include("foo.twig", vars) }} +--TEMPLATE(foo.twig)-- +{{ foo }} +--DATA-- +return array('vars' => array('foo' => 'bar')) +--EXPECT-- +bar +bar