optimized the filesystem loader

* added a cache for templates that do not exist
* sped up Filesystem::exists() by avoiding the creation of exceptions
This commit is contained in:
Fabien Potencier
2013-08-04 08:27:13 +02:00
parent 1b0a54189c
commit d2b97b49f2
2 changed files with 42 additions and 16 deletions
+1
View File
@@ -1,5 +1,6 @@
* 1.14.0 (2013-XX-XX) * 1.14.0 (2013-XX-XX)
* improved the performance of the filesystem loader
* fixed the filesystem loader cache when a template name exists in several namespaces * fixed the filesystem loader cache when a template name exists in several namespaces
* added support for calling macros defined in a template without importing them first * added support for calling macros defined in a template without importing them first
* added support for named arguments for macros * added support for named arguments for macros
+41 -16
View File
@@ -21,6 +21,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
protected $paths; protected $paths;
protected $cache; protected $cache;
protected $errorCache;
/** /**
* Constructor. * Constructor.
@@ -87,7 +88,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
public function addPath($path, $namespace = self::MAIN_NAMESPACE) public function addPath($path, $namespace = self::MAIN_NAMESPACE)
{ {
// invalidate the cache // invalidate the cache
$this->cache = array(); $this->cache = $this->errorCache = array();
if (!is_dir($path)) { if (!is_dir($path)) {
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
@@ -107,7 +108,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
public function prependPath($path, $namespace = self::MAIN_NAMESPACE) public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
{ {
// invalidate the cache // invalidate the cache
$this->cache = array(); $this->cache = $this->errorCache = array();
if (!is_dir($path)) { if (!is_dir($path)) {
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path)); throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
@@ -143,18 +144,19 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
*/ */
public function exists($name) public function exists($name)
{ {
$name = (string) $name; $name = $this->normalizeName($name);
if (isset($this->cache[$name])) { if (isset($this->cache[$name])) {
return true; return true;
} }
try { if (isset($this->errorCache[$name])) {
$this->findTemplate($name);
return true;
} catch (Twig_Error_Loader $exception) {
return false; return false;
} }
$this->doFindTemplate($name);
return isset($this->cache[$name]);
} }
/** /**
@@ -167,22 +169,36 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
protected function findTemplate($name) protected function findTemplate($name)
{ {
$name = (string) $name; $name = $this->normalizeName($name);
// normalize name
$name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
if (isset($this->cache[$name])) { if (isset($this->cache[$name])) {
return $this->cache[$name]; return $this->cache[$name];
} }
if (isset($this->errorCache[$name])) {
throw new Twig_Error_Loader($this->errorCache[$name]);
}
$this->doFindTemplate($name);
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
throw new Twig_Error_Loader($this->errorCache[$name]);
}
protected function doFindTemplate($name, $exists = false)
{
$this->validateName($name); $this->validateName($name);
$namespace = self::MAIN_NAMESPACE; $namespace = self::MAIN_NAMESPACE;
$shortname = $name; $shortname = $name;
if (isset($name[0]) && '@' == $name[0]) { if (isset($name[0]) && '@' == $name[0]) {
if (false === $pos = strpos($name, '/')) { if (false === $pos = strpos($name, '/')) {
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); $this->errorCache[$name] = sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name);
return;
} }
$namespace = substr($name, 1, $pos - 1); $namespace = substr($name, 1, $pos - 1);
@@ -190,16 +206,25 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
} }
if (!isset($this->paths[$namespace])) { if (!isset($this->paths[$namespace])) {
throw new Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace)); $this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".', $namespace);
return;
} }
foreach ($this->paths[$namespace] as $path) { foreach ($this->paths[$namespace] as $path) {
if (is_file($path.'/'.$shortname)) { if (is_file($path.'/'.$shortname)) {
return $this->cache[$name] = $path.'/'.$shortname; $this->cache[$name] = $path.'/'.$shortname;
return;
} }
} }
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]))); $this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]));
}
protected function normalizeName($name)
{
return preg_replace('#/{2,}#', '/', strtr((string) $name, '\\', '/'));
} }
protected function validateName($name) protected function validateName($name)