From d2b97b49f240efb453e75558f00e17ccb1d915c5 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 4 Aug 2013 08:27:13 +0200 Subject: [PATCH] optimized the filesystem loader * added a cache for templates that do not exist * sped up Filesystem::exists() by avoiding the creation of exceptions --- CHANGELOG | 1 + lib/Twig/Loader/Filesystem.php | 55 +++++++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 44a483f..d4b5f5f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ * 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 * added support for calling macros defined in a template without importing them first * added support for named arguments for macros diff --git a/lib/Twig/Loader/Filesystem.php b/lib/Twig/Loader/Filesystem.php index 5ce13dd..aea1c6c 100644 --- a/lib/Twig/Loader/Filesystem.php +++ b/lib/Twig/Loader/Filesystem.php @@ -21,6 +21,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI protected $paths; protected $cache; + protected $errorCache; /** * Constructor. @@ -87,7 +88,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI public function addPath($path, $namespace = self::MAIN_NAMESPACE) { // invalidate the cache - $this->cache = array(); + $this->cache = $this->errorCache = array(); if (!is_dir($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) { // invalidate the cache - $this->cache = array(); + $this->cache = $this->errorCache = array(); if (!is_dir($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) { - $name = (string) $name; + $name = $this->normalizeName($name); + if (isset($this->cache[$name])) { return true; } - try { - $this->findTemplate($name); - - return true; - } catch (Twig_Error_Loader $exception) { + if (isset($this->errorCache[$name])) { 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) { - $name = (string) $name; + $name = $this->normalizeName($name); + + if (isset($this->cache[$name])) { + return $this->cache[$name]; + } + + if (isset($this->errorCache[$name])) { + throw new Twig_Error_Loader($this->errorCache[$name]); + } - // normalize name - $name = preg_replace('#/{2,}#', '/', strtr($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); $namespace = self::MAIN_NAMESPACE; $shortname = $name; 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)); + $this->errorCache[$name] = sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name); + + return; } $namespace = substr($name, 1, $pos - 1); @@ -190,16 +206,25 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI } 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) { 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) -- 1.7.2.5