Upgrade Twig library

This commit is contained in:
Michael Foster 2013-08-01 15:20:12 -04:00
parent 22f3a95e0e
commit 0fe5528574
133 changed files with 5080 additions and 1386 deletions

View file

@ -17,10 +17,9 @@
* source code of the template). If you don't want to see your cache grows out of
* control, you need to take care of clearing the old cache file by yourself.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Array implements Twig_LoaderInterface
class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
protected $templates;
@ -47,18 +46,15 @@ class Twig_Loader_Array implements Twig_LoaderInterface
*/
public function setTemplate($name, $template)
{
$this->templates[$name] = $template;
$this->templates[(string) $name] = $template;
}
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
* {@inheritdoc}
*/
public function getSource($name)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
@ -67,14 +63,19 @@ class Twig_Loader_Array implements Twig_LoaderInterface
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
* {@inheritdoc}
*/
public function exists($name)
{
return isset($this->templates[(string) $name]);
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
@ -83,13 +84,15 @@ class Twig_Loader_Array implements Twig_LoaderInterface
}
/**
* 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
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
return true;
}
}

View file

@ -12,11 +12,11 @@
/**
* Loads templates from other loaders.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Chain implements Twig_LoaderInterface
class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
private $hasSourceCache = array();
protected $loaders;
/**
@ -40,61 +40,100 @@ class Twig_Loader_Chain implements Twig_LoaderInterface
public function addLoader(Twig_LoaderInterface $loader)
{
$this->loaders[] = $loader;
$this->hasSourceCache = array();
}
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
* {@inheritdoc}
*/
public function getSource($name)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->getSource($name);
} catch (Twig_Error_Loader $e) {
$exceptions[] = $e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(', ', $exceptions)));
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
* {@inheritdoc}
*/
public function exists($name)
{
$name = (string) $name;
if (isset($this->hasSourceCache[$name])) {
return $this->hasSourceCache[$name];
}
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface) {
if ($loader->exists($name)) {
return $this->hasSourceCache[$name] = true;
}
continue;
}
try {
$loader->getSource($name);
return $this->hasSourceCache[$name] = true;
} catch (Twig_Error_Loader $e) {
}
}
return $this->hasSourceCache[$name] = false;
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->getCacheKey($name);
} catch (Twig_Error_Loader $e) {
$exceptions[] = get_class($loader).': '.$e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
}
/**
* 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
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->isFresh($name, $time);
} catch (Twig_Error_Loader $e) {
$exceptions[] = get_class($loader).': '.$e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
}
}

View file

@ -12,10 +12,9 @@
/**
* Loads template from the filesystem.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Filesystem implements Twig_LoaderInterface
class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
protected $paths;
protected $cache;
@ -25,44 +24,64 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
*
* @param string|array $paths A path or an array of paths where to look for templates
*/
public function __construct($paths)
public function __construct($paths = array())
{
$this->setPaths($paths);
if ($paths) {
$this->setPaths($paths);
}
}
/**
* Returns the paths to the templates.
*
* @param string $namespace A path namespace
*
* @return array The array of paths where to look for templates
*/
public function getPaths()
public function getPaths($namespace = '__main__')
{
return $this->paths;
return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
}
/**
* Returns the path namespaces.
*
* The "__main__" namespace is always defined.
*
* @return array The array of defined namespaces
*/
public function getNamespaces()
{
return array_keys($this->paths);
}
/**
* Sets the paths where templates are stored.
*
* @param string|array $paths A path or an array of paths where to look for templates
* @param string|array $paths A path or an array of paths where to look for templates
* @param string $namespace A path namespace
*/
public function setPaths($paths)
public function setPaths($paths, $namespace = '__main__')
{
if (!is_array($paths)) {
$paths = array($paths);
}
$this->paths = array();
$this->paths[$namespace] = array();
foreach ($paths as $path) {
$this->addPath($path);
$this->addPath($path, $namespace);
}
}
/**
* Adds a path where templates are stored.
*
* @param string $path A path where to look for templates
* @param string $path A path where to look for templates
* @param string $namespace A path name
*
* @throws Twig_Error_Loader
*/
public function addPath($path)
public function addPath($path, $namespace = '__main__')
{
// invalidate the cache
$this->cache = array();
@ -71,15 +90,37 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
}
$this->paths[] = $path;
$this->paths[$namespace][] = rtrim($path, '/\\');
}
/**
* Gets the source code of a template, given its name.
* Prepends a path where templates are stored.
*
* @param string $name The name of the template to load
* @param string $path A path where to look for templates
* @param string $namespace A path name
*
* @return string The template source code
* @throws Twig_Error_Loader
*/
public function prependPath($path, $namespace = '__main__')
{
// invalidate the cache
$this->cache = array();
if (!is_dir($path)) {
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
}
$path = rtrim($path, '/\\');
if (!isset($this->paths[$namespace])) {
$this->paths[$namespace][] = $path;
} else {
array_unshift($this->paths[$namespace], $path);
}
}
/**
* {@inheritdoc}
*/
public function getSource($name)
{
@ -87,11 +128,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
* {@inheritdoc}
*/
public function getCacheKey($name)
{
@ -99,18 +136,36 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
}
/**
* 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
* {@inheritdoc}
*/
public function exists($name)
{
$name = (string) $name;
if (isset($this->cache[$name])) {
return true;
}
try {
$this->findTemplate($name);
return true;
} catch (Twig_Error_Loader $exception) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
return filemtime($this->findTemplate($name)) < $time;
return filemtime($this->findTemplate($name)) <= $time;
}
protected function findTemplate($name)
{
$name = (string) $name;
// normalize name
$name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
@ -120,13 +175,28 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
$this->validateName($name);
foreach ($this->paths as $path) {
$namespace = '__main__';
if (isset($name[0]) && '@' == $name[0]) {
if (false === $pos = strpos($name, '/')) {
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
}
$namespace = substr($name, 1, $pos - 1);
$name = substr($name, $pos + 1);
}
if (!isset($this->paths[$namespace])) {
throw new Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace));
}
foreach ($this->paths[$namespace] as $path) {
if (is_file($path.'/'.$name)) {
return $this->cache[$name] = $path.'/'.$name;
}
}
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths)));
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace])));
}
protected function validateName($name)
@ -135,6 +205,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
throw new Twig_Error_Loader('A template name cannot contain NUL bytes.');
}
$name = ltrim($name, '/');
$parts = explode('/', $name);
$level = 0;
foreach ($parts as $part) {

View file

@ -12,22 +12,21 @@
/**
* Loads a template from a string.
*
* This loader should only be used for unit testing as it has many limitations
* (for instance, the include or extends tag does not make any sense for a string
* loader).
*
* When using this loader with a cache mechanism, you should know that a new cache
* key is generated each time a template content "changes" (the cache key being the
* source code of the template). If you don't want to see your cache grows out of
* control, you need to take care of clearing the old cache file by yourself.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_String implements Twig_LoaderInterface
class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
* {@inheritdoc}
*/
public function getSource($name)
{
@ -35,11 +34,15 @@ class Twig_Loader_String implements Twig_LoaderInterface
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
* {@inheritdoc}
*/
public function exists($name)
{
return true;
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
@ -47,10 +50,7 @@ class Twig_Loader_String implements Twig_LoaderInterface
}
/**
* 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
* {@inheritdoc}
*/
public function isFresh($name, $time)
{