added an include function
authorFabien Potencier <fabien.potencier@gmail.com>
Thu, 15 Nov 2012 12:20:44 +0000 (13:20 +0100)
committerFabien Potencier <fabien.potencier@gmail.com>
Mon, 17 Dec 2012 07:43:49 +0000 (08:43 +0100)
15 files changed:
CHANGELOG
doc/functions/include.rst [new file with mode: 0644]
doc/functions/index.rst
doc/tags/include.rst
lib/Twig/Extension/Core.php
test/Twig/Tests/Fixtures/functions/include/basic.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/expression.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/ignore_missing.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/missing.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/missing_nested.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/sandbox.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/template_instance.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/templates_as_array.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/with_context.test [new file with mode: 0644]
test/Twig/Tests/Fixtures/functions/include/with_variables.test [new file with mode: 0644]

index fffef7e..b1bcb6a 100644 (file)
--- 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 (file)
index 0000000..eaddfe6
--- /dev/null
@@ -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
index 4d9a003..c5ac896 100644 (file)
@@ -14,3 +14,4 @@ Functions
     dump
     date
     template_from_string
+    include
index 1440c50..01ad532 100644 (file)
@@ -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.
index 3727f5f..6c26d04 100644 (file)
@@ -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 (file)
index 0000000..a434182
--- /dev/null
@@ -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 (file)
index 0000000..aba30ce
--- /dev/null
@@ -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 (file)
index 0000000..43a2ccc
--- /dev/null
@@ -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 (file)
index 0000000..4d2f6cf
--- /dev/null
@@ -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 (file)
index 0000000..78fddc7
--- /dev/null
@@ -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 (file)
index 0000000..788a2ab
--- /dev/null
@@ -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 (file)
index 0000000..18d405a
--- /dev/null
@@ -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 (file)
index 0000000..1a81006
--- /dev/null
@@ -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 (file)
index 0000000..35611fb
--- /dev/null
@@ -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 (file)
index 0000000..b2ace94
--- /dev/null
@@ -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