* 0.9.4-DEV
+If you have custom loaders, you MUST upgrade them for this release: The
+Twig_Loader base class has been removed, and the Twig_LoaderInterface has also
+been changed (see the source code for more information or the documentation).
+
+ * refactored loaders
* 0.9.3 (2009-11-11)
Loaders
-------
+>**CAUTION**
+>This section describes the loaders as implemented in Twig version 0.9.4 and
+>above.
+
Loaders are responsible for loading templates from a resource such as the file
system.
you pass it the source code directly.
[php]
- $loader = new Twig_Loader_String($cacheDir);
+ $loader = new Twig_Loader_String();
* `Twig_Loader_Array`: Loads a template from a PHP array. It's passed an
array of strings bound to template names. This loader is useful for unit
interface Twig_LoaderInterface
{
/**
- * Loads a template by name.
+ * Gets the source code of a template, given its name.
*
- * @param string $name The template name
+ * @param string $name string The name of the template to load
*
- * @return string The class name of the compiled template
+ * @return string The template source code
*/
- public function load($name);
+ public function getSource($name);
/**
- * Sets the Environment related to this loader.
+ * Gets the cache key to use for the cache for a given template name.
+ *
+ * @param string $name string The name of the template to load
*
- * @param Twig_Environment $env A Twig_Environment instance
+ * @return string The cache key
*/
- public function setEnvironment(Twig_Environment $env);
+ public function getCacheKey($name);
+
+ /**
+ * Returns true if the template is still fresh.
+ *
+ * @param string $name The template name
+ * @param timestamp $time The last modification time of the cached template
+ */
+ public function isFresh($name, $time);
}
-But if you want to create your own loader, you'd better inherit from the
-`Twig_Loader` class, which already provides a lot of useful features. In this
-case, you just need to implement the `getSource()` method. As an example, here
-is how the built-in `Twig_Loader_String` reads:
+As an example, here is how the built-in `Twig_Loader_String` reads:
[php]
- class Twig_Loader_String extends Twig_Loader
+ class Twig_Loader_String implements Twig_LoaderInterface
{
- /**
- * Gets the source code of a template, given its name.
- *
- * @param string $name string The name of the template to load
- *
- * @return array An array consisting of the source code as the first element,
- * and the last modification time as the second one
- * or false if it's not relevant
- */
public function getSource($name)
{
- return array($name, false);
+ return $name;
}
- }
-The `getSource()` method must return an array of two values:
+ public function getCacheKey($name)
+ {
+ return $name;
+ }
- * The first one is the template source code;
+ public function isFresh($name, $time)
+ {
+ return false;
+ }
+ }
- * The second one is the last modification time of the template (used by the
- auto-reload feature), or `false` if the loader does not support
- auto-reloading.
+The `isFresh()` method must return `true` if the current cached template is
+still fresh, given the last modification time, or `false` otherwise.
Using Extensions
----------------
public function getCacheFilename($name)
{
- return $this->getCache() ? $this->getCache().'/twig_'.md5($name).'.php' : false;
+ return $this->getCache() ? $this->getCache().'/'.$this->getTemplateClass($name).'.php' : false;
}
public function getTrimBlocks()
*/
public function getTemplateClass($name)
{
- return '__TwigTemplate_'.md5($name);
+ return '__TwigTemplate_'.md5($this->loader->getCacheKey($name));
}
+ /**
+ * Loads a template by name.
+ *
+ * @param string $name The template name
+ *
+ * @return Twig_TemplateInterface A template instance representing the given template name
+ */
public function loadTemplate($name)
{
- if (!$this->runtimeInitialized)
- {
- $this->initRuntime();
+ $cls = $this->getTemplateClass($name);
- $this->runtimeInitialized = true;
+ if (isset($this->loadedTemplates[$cls]))
+ {
+ return $this->loadedTemplates[$cls];
}
- if (isset($this->loadedTemplates[$name]))
+ if (!class_exists($cls, false))
{
- return $this->loadedTemplates[$name];
+ if (false === $cache = $this->getCacheFilename($name))
+ {
+ eval('?>'.$this->compileSource($this->loader->getSource($name), $name));
+ }
+ else
+ {
+ if (!file_exists($cache) || ($this->isAutoReload() && !$this->loader->isFresh($name, filemtime($cache))))
+ {
+ $content = $this->compileSource($this->loader->getSource($name), $name);
+
+ if (false === file_put_contents($cache, $content, LOCK_EX))
+ {
+ eval('?>'.$content);
+ }
+ else
+ {
+ require_once $cache;
+ }
+ }
+ else
+ {
+ require_once $cache;
+ }
+ }
}
- $cls = $this->getLoader()->load($name, $this);
+ if (!$this->runtimeInitialized)
+ {
+ $this->initRuntime();
- return $this->loadedTemplates[$name] = new $cls($this);
+ $this->runtimeInitialized = true;
+ }
+
+ return $this->loadedTemplates[$cls] = new $cls($this);
}
public function clearTemplateCache()
$lexer->setEnvironment($this);
}
- public function tokenize($source, $name = null)
+ public function tokenize($source, $name)
{
- return $this->getLexer()->tokenize($source, null === $name ? $source : $name);
+ return $this->getLexer()->tokenize($source, $name);
}
public function getParser()
return $this->getCompiler()->compile($node)->getSource();
}
+ public function compileSource($source, $name)
+ {
+ return $this->compile($this->parse($this->tokenize($source, $name)));
+ }
+
public function setLoader(Twig_LoaderInterface $loader)
{
$this->loader = $loader;
- $loader->setEnvironment($this);
}
public function getLoader()
+++ /dev/null
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2009 Fabien Potencier
- * (c) 2009 Armin Ronacher
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-/**
- * Base loader class for all builtin loaders.
- *
- * @package twig
- * @author Fabien Potencier <fabien.potencier@symfony-project.com>
- * @version SVN: $Id$
- */
-abstract class Twig_Loader implements Twig_LoaderInterface
-{
- protected $env;
-
- /**
- * Loads a template by name.
- *
- * @param string $name The template name
- *
- * @return string The class name of the compiled template
- */
- public function load($name)
- {
- $cls = $this->env->getTemplateClass($name);
-
- if (class_exists($cls, false))
- {
- return $cls;
- }
-
- if (false === $cache = $this->env->getCacheFilename($name))
- {
- list($source, ) = $this->getSource($name);
- $this->evalString($source, $name);
-
- return $cls;
- }
-
- if (!file_exists($cache))
- {
- list($source, $mtime) = $this->getSource($name);
- if (false === $mtime)
- {
- $this->evalString($source, $name);
-
- return $cls;
- }
-
- $this->save($this->compile($source, $name), $cache);
- }
- elseif ($this->env->isAutoReload())
- {
- list($source, $mtime) = $this->getSource($name);
- if (filemtime($cache) < $mtime)
- {
- $this->save($this->compile($source, $name), $cache);
- }
- }
-
- require_once $cache;
-
- return $cls;
- }
-
- /**
- * Saves a PHP string in the cache.
- *
- * If the cache file cannot be written, then the PHP string is evaluated.
- *
- * @param string $content The PHP string
- * @param string $cache The absolute path of the cache
- */
- protected function save($content, $cache)
- {
- if (false === file_put_contents($cache, $content, LOCK_EX))
- {
- eval('?>'.$content);
- }
- }
-
- /**
- * Sets the Environment related to this loader.
- *
- * @param Twig_Environment $env A Twig_Environment instance
- */
- public function setEnvironment(Twig_Environment $env)
- {
- $this->env = $env;
- }
-
- protected function compile($source, $name)
- {
- return $this->env->compile($this->env->parse($this->env->tokenize($source, $name)));
- }
-
- protected function evalString($source, $name)
- {
- eval('?>'.$this->compile($source, $name));
- }
-
- /**
- * Gets the source code of a template, given its name.
- *
- * @param string $name string The name of the template to load
- *
- * @return array An array consisting of the source code as the first element,
- * and the last modification time as the second one
- * or false if it's not relevant
- */
- abstract protected function getSource($name);
-}
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @version SVN: $Id$
*/
-class Twig_Loader_Array extends Twig_Loader
+class Twig_Loader_Array implements Twig_LoaderInterface
{
protected $templates;
}
}
+ /**
+ * Gets the source code of a template, given its name.
+ *
+ * @param string $name string The name of the template to load
+ *
+ * @return string The template source code
+ */
public function getSource($name)
{
if (!isset($this->templates[$name]))
throw new LogicException(sprintf('Template "%s" is not defined.', $name));
}
- return array($this->templates[$name], false);
+ return $this->templates[$name];
+ }
+
+ /**
+ * Gets the cache key to use for the cache for a given template name.
+ *
+ * @param string $name string The name of the template to load
+ *
+ * @return string The cache key
+ */
+ public function getCacheKey($name)
+ {
+ if (!isset($this->templates[$name]))
+ {
+ throw new LogicException(sprintf('Template "%s" is not defined.', $name));
+ }
+
+ return $this->templates[$name];
+ }
+
+ /**
+ * Returns true if the template is still fresh.
+ *
+ * @param string $name The template name
+ * @param timestamp $time The last modification time of the cached template
+ */
+ public function isFresh($name, $time)
+ {
+ return false;
}
}
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @version SVN: $Id$
*/
-class Twig_Loader_Filesystem extends Twig_Loader
+class Twig_Loader_Filesystem implements Twig_LoaderInterface
{
protected $paths;
+ protected $cache;
/**
* Constructor.
*/
public function setPaths($paths)
{
+ // invalidate the cache
+ $this->cache = array();
+
if (!is_array($paths))
{
$paths = array($paths);
*
* @param string $name string The name of the template to load
*
- * @return array An array consisting of the source code as the first element,
- * and the last modification time as the second one
- * or false if it's not relevant
+ * @return string The template source code
*/
public function getSource($name)
{
- foreach ($this->paths as $path)
+ return file_get_contents($this->findTemplate($name));
+ }
+
+ /**
+ * Gets the cache key to use for the cache for a given template name.
+ *
+ * @param string $name string The name of the template to load
+ *
+ * @return string The cache key
+ */
+ public function getCacheKey($name)
+ {
+ return $this->findTemplate($name);
+ }
+
+ /**
+ * Returns true if the template is still fresh.
+ *
+ * @param string $name The template name
+ * @param timestamp $time The last modification time of the cached template
+ */
+ public function isFresh($name, $time)
+ {
+ return $time < filemtime($this->findTemplate($name));
+ }
+
+ protected function findTemplate($name)
+ {
+ if (isset($this->cache[$name]))
{
- $file = realpath($path.DIRECTORY_SEPARATOR.$name);
+ return $this->cache[$name];
+ }
- if (false === $file)
+ foreach ($this->paths as $path)
+ {
+ if (false === $file = realpath($path.DIRECTORY_SEPARATOR.$name))
{
continue;
}
throw new RuntimeException('Looks like you try to load a template outside configured directories.');
}
- return array(file_get_contents($file), filemtime($file));
+ return $this->cache[$name] = $file;
}
throw new RuntimeException(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths)));
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @version SVN: $Id$
*/
-class Twig_Loader_String extends Twig_Loader
+class Twig_Loader_String implements Twig_LoaderInterface
{
/**
* Gets the source code of a template, given its name.
*
* @param string $name string The name of the template to load
*
- * @return array An array consisting of the source code as the first element,
- * and the last modification time as the second one
- * or false if it's not relevant
+ * @return string The template source code
*/
- public function getSource($source)
+ public function getSource($name)
{
- return array($source, false);
+ return $name;
+ }
+
+ /**
+ * Gets the cache key to use for the cache for a given template name.
+ *
+ * @param string $name string The name of the template to load
+ *
+ * @return string The cache key
+ */
+ public function getCacheKey($name)
+ {
+ return $name;
+ }
+
+ /**
+ * Returns true if the template is still fresh.
+ *
+ * @param string $name The template name
+ * @param timestamp $time The last modification time of the cached template
+ */
+ public function isFresh($name, $time)
+ {
+ return false;
}
}
interface Twig_LoaderInterface
{
/**
- * Loads a template by name.
+ * Gets the source code of a template, given its name.
*
- * @param string $name The template name
+ * @param string $name string The name of the template to load
*
- * @return string The class name of the compiled template
+ * @return string The template source code
*/
- public function load($name);
+ public function getSource($name);
/**
- * Sets the Environment related to this loader.
+ * Gets the cache key to use for the cache for a given template name.
*
- * @param Twig_Environment $env A Twig_Environment instance
+ * @param string $name string The name of the template to load
+ *
+ * @return string The cache key
+ */
+ public function getCacheKey($name);
+
+ /**
+ * Returns true if the template is still fresh.
+ *
+ * @param string $name The template name
+ * @param timestamp $time The last modification time of the cached template
*/
- public function setEnvironment(Twig_Environment $env);
+ public function isFresh($name, $time);
}
{
$compiler
->addDebugInfo($this)
- ->write('$this->env->getLoader()->load(')
+ ->write('$this->env->loadTemplate(')
->string($this->macro)
->raw(");\n\n")
->write("if (!class_exists(")
if (!is_null($this->extends))
{
$compiler
- ->write('$this->env->loadTemplate(')
+ ->write('$this->loadTemplate(')
->repr($this->extends)
->raw(");\n\n")
;
"include" tag
--TEMPLATE--
FOO
-{% include "%prefix%foo.twig" %}
+{% include "foo.twig" %}
BAR
--TEMPLATE(foo.twig)--
"include" tag allows expressions for the template to include
--TEMPLATE--
FOO
-{% include "%prefix%" ~ foo %}
+{% include foo %}
BAR
--TEMPLATE(foo.twig)--
--TEST--
"extends" tag
--TEMPLATE--
-{% extends "%prefix%foo.twig" %}
+{% extends "foo.twig" %}
{% block content %}
FOO
--TEST--
"extends" tag
--TEMPLATE--
-{% extends "%prefix%foo.twig" %}
+{% extends "foo.twig" %}
{% block content %}{% parent %}FOO{% parent %}{% endblock %}
--TEMPLATE(foo.twig)--
--TEST--
"macro" tag
--TEMPLATE--
-{% import '%prefix%index.twig' as forms %}
+{% import 'index.twig' as forms %}
{{ forms.input('username') }}
{{ forms.input('password', null, 'password', 1) }}
--TEST--
"macro" tag
--TEMPLATE--
-{% import '%prefix%forms.twig' as forms %}
+{% import 'forms.twig' as forms %}
{{ forms.input('username') }}
{{ forms.input('password', null, 'password', 1) }}
+++ /dev/null
-<?php
-
-/*
- * This file is part of Twig.
- *
- * (c) 2009 Fabien Potencier
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-/**
- * Loads a template from variables.
- *
- * @package twig
- * @author Fabien Potencier <fabien.potencier@symfony-project.com>
- * @version SVN: $Id$
- */
-class Twig_Loader_Var extends Twig_Loader
-{
- protected $templates;
- protected $prefix;
-
- public function __construct(array $templates, $prefix)
- {
- $this->prefix = $prefix;
- $this->templates = array();
- foreach ($templates as $name => $template)
- {
- $this->templates[$this->prefix.'_'.$name] = $template;
- }
- }
-
- public function getSource($name)
- {
- if (!isset($this->templates[$this->prefix.'_'.$name]))
- {
- throw new LogicException(sprintf('Template "%s" is not defined.', $name));
- }
-
- return array(str_replace('%prefix%', $this->prefix.'_', $this->templates[$this->prefix.'_'.$name]), false);
- }
-}
require_once dirname(__FILE__).'/../../../../lib/Twig/Autoloader.php';
Twig_Autoloader::register();
-require_once dirname(__FILE__).'/../../../lib/Twig_Loader_Var.php';
-
class Object
{
public $bar = 'bar';
function get_environment($sandboxed, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array())
{
- static $prefix = 0;
-
- ++$prefix;
-
- $loader = new Twig_Loader_Var($templates, $prefix);
+ $loader = new Twig_Loader_Array($templates);
$twig = new Twig_Environment($loader, array('trim_blocks' => true, 'debug' => true));
$policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties);
$twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
require_once dirname(__FILE__).'/../../lib/Twig/Autoloader.php';
Twig_Autoloader::register();
-require_once dirname(__FILE__).'/../lib/Twig_Loader_Var.php';
-
class Foo
{
public function bar($param1 = null, $param2 = null)
throw new InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file)));
}
- $prefix = rand(1, 999999999);
$message = $match[1];
$templates = array();
preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $match[2], $matches, PREG_SET_ORDER);
foreach ($matches as $match)
{
- $templates[$prefix.'_'.($match[1] ? $match[1] : 'index.twig')] = $match[2];
+ $templates[($match[1] ? $match[1] : 'index.twig')] = $match[2];
}
- $loader = new Twig_Loader_Var($templates, $prefix);
- $twig = new Twig_Environment($loader, array('trim_blocks' => true));
+ $loader = new Twig_Loader_Array($templates);
+ $twig = new Twig_Environment($loader, array('trim_blocks' => true, 'cache' => false));
$twig->addExtension(new Twig_Extension_Escaper());
- $template = $twig->loadTemplate($prefix.'_index.twig');
+ $template = $twig->loadTemplate('index.twig');
preg_match_all('/--DATA--(.*?)--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $matches, PREG_SET_ORDER);
foreach ($matches as $match)