vendor/twig/twig/src/Loader/FilesystemLoader.php line 181

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Twig.
  4.  *
  5.  * (c) Fabien Potencier
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Twig\Loader;
  11. use Twig\Error\LoaderError;
  12. use Twig\Source;
  13. /**
  14.  * Loads template from the filesystem.
  15.  *
  16.  * @author Fabien Potencier <fabien@symfony.com>
  17.  */
  18. class FilesystemLoader implements LoaderInterfaceExistsLoaderInterfaceSourceContextLoaderInterface
  19. {
  20.     /** Identifier of the main namespace. */
  21.     const MAIN_NAMESPACE '__main__';
  22.     protected $paths = [];
  23.     protected $cache = [];
  24.     protected $errorCache = [];
  25.     private $rootPath;
  26.     /**
  27.      * @param string|array $paths    A path or an array of paths where to look for templates
  28.      * @param string|null  $rootPath The root path common to all relative paths (null for getcwd())
  29.      */
  30.     public function __construct($paths = [], $rootPath null)
  31.     {
  32.         $this->rootPath = (null === $rootPath getcwd() : $rootPath).\DIRECTORY_SEPARATOR;
  33.         if (false !== $realPath realpath($rootPath)) {
  34.             $this->rootPath $realPath.\DIRECTORY_SEPARATOR;
  35.         }
  36.         if ($paths) {
  37.             $this->setPaths($paths);
  38.         }
  39.     }
  40.     /**
  41.      * Returns the paths to the templates.
  42.      *
  43.      * @param string $namespace A path namespace
  44.      *
  45.      * @return array The array of paths where to look for templates
  46.      */
  47.     public function getPaths($namespace self::MAIN_NAMESPACE)
  48.     {
  49.         return isset($this->paths[$namespace]) ? $this->paths[$namespace] : [];
  50.     }
  51.     /**
  52.      * Returns the path namespaces.
  53.      *
  54.      * The main namespace is always defined.
  55.      *
  56.      * @return array The array of defined namespaces
  57.      */
  58.     public function getNamespaces()
  59.     {
  60.         return array_keys($this->paths);
  61.     }
  62.     /**
  63.      * Sets the paths where templates are stored.
  64.      *
  65.      * @param string|array $paths     A path or an array of paths where to look for templates
  66.      * @param string       $namespace A path namespace
  67.      */
  68.     public function setPaths($paths$namespace self::MAIN_NAMESPACE)
  69.     {
  70.         if (!\is_array($paths)) {
  71.             $paths = [$paths];
  72.         }
  73.         $this->paths[$namespace] = [];
  74.         foreach ($paths as $path) {
  75.             $this->addPath($path$namespace);
  76.         }
  77.     }
  78.     /**
  79.      * Adds a path where templates are stored.
  80.      *
  81.      * @param string $path      A path where to look for templates
  82.      * @param string $namespace A path namespace
  83.      *
  84.      * @throws LoaderError
  85.      */
  86.     public function addPath($path$namespace self::MAIN_NAMESPACE)
  87.     {
  88.         // invalidate the cache
  89.         $this->cache $this->errorCache = [];
  90.         $checkPath $this->isAbsolutePath($path) ? $path $this->rootPath.$path;
  91.         if (!is_dir($checkPath)) {
  92.             throw new LoaderError(sprintf('The "%s" directory does not exist ("%s").'$path$checkPath));
  93.         }
  94.         $this->paths[$namespace][] = rtrim($path'/\\');
  95.     }
  96.     /**
  97.      * Prepends a path where templates are stored.
  98.      *
  99.      * @param string $path      A path where to look for templates
  100.      * @param string $namespace A path namespace
  101.      *
  102.      * @throws LoaderError
  103.      */
  104.     public function prependPath($path$namespace self::MAIN_NAMESPACE)
  105.     {
  106.         // invalidate the cache
  107.         $this->cache $this->errorCache = [];
  108.         $checkPath $this->isAbsolutePath($path) ? $path $this->rootPath.$path;
  109.         if (!is_dir($checkPath)) {
  110.             throw new LoaderError(sprintf('The "%s" directory does not exist ("%s").'$path$checkPath));
  111.         }
  112.         $path rtrim($path'/\\');
  113.         if (!isset($this->paths[$namespace])) {
  114.             $this->paths[$namespace][] = $path;
  115.         } else {
  116.             array_unshift($this->paths[$namespace], $path);
  117.         }
  118.     }
  119.     public function getSource($name)
  120.     {
  121.         @trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
  122.         if (null === ($path $this->findTemplate($name)) || false === $path) {
  123.             return '';
  124.         }
  125.         return file_get_contents($path);
  126.     }
  127.     public function getSourceContext($name)
  128.     {
  129.         if (null === ($path $this->findTemplate($name)) || false === $path) {
  130.             return new Source(''$name'');
  131.         }
  132.         return new Source(file_get_contents($path), $name$path);
  133.     }
  134.     public function getCacheKey($name)
  135.     {
  136.         if (null === ($path $this->findTemplate($name)) || false === $path) {
  137.             return '';
  138.         }
  139.         $len = \strlen($this->rootPath);
  140.         if (=== strncmp($this->rootPath$path$len)) {
  141.             return substr($path$len);
  142.         }
  143.         return $path;
  144.     }
  145.     public function exists($name)
  146.     {
  147.         $name $this->normalizeName($name);
  148.         if (isset($this->cache[$name])) {
  149.             return true;
  150.         }
  151.         try {
  152.             return null !== ($path $this->findTemplate($namefalse)) && false !== $path;
  153.         } catch (LoaderError $e) {
  154.             @trigger_error(sprintf('In %s::findTemplate(), you must accept a second argument that when set to "false" returns "false" instead of throwing an exception. Not supporting this argument is deprecated since version 1.27.', \get_class($this)), E_USER_DEPRECATED);
  155.             return false;
  156.         }
  157.     }
  158.     public function isFresh($name$time)
  159.     {
  160.         // false support to be removed in 3.0
  161.         if (null === ($path $this->findTemplate($name)) || false === $path) {
  162.             return false;
  163.         }
  164.         return filemtime($path) < $time;
  165.     }
  166.     /**
  167.      * Checks if the template can be found.
  168.      *
  169.      * @param string $name The template name
  170.      *
  171.      * @return string|false|null The template name or false/null
  172.      */
  173.     protected function findTemplate($name)
  174.     {
  175.         $throw = \func_num_args() > func_get_arg(1) : true;
  176.         $name $this->normalizeName($name);
  177.         if (isset($this->cache[$name])) {
  178.             return $this->cache[$name];
  179.         }
  180.         if (isset($this->errorCache[$name])) {
  181.             if (!$throw) {
  182.                 return false;
  183.             }
  184.             throw new LoaderError($this->errorCache[$name]);
  185.         }
  186.         try {
  187.             $this->validateName($name);
  188.             list($namespace$shortname) = $this->parseName($name);
  189.         } catch (LoaderError $e) {
  190.             if (!$throw) {
  191.                 return false;
  192.             }
  193.             throw $e;
  194.         }
  195.         if (!isset($this->paths[$namespace])) {
  196.             $this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".'$namespace);
  197.             if (!$throw) {
  198.                 return false;
  199.             }
  200.             throw new LoaderError($this->errorCache[$name]);
  201.         }
  202.         foreach ($this->paths[$namespace] as $path) {
  203.             if (!$this->isAbsolutePath($path)) {
  204.                 $path $this->rootPath.$path;
  205.             }
  206.             if (is_file($path.'/'.$shortname)) {
  207.                 if (false !== $realpath realpath($path.'/'.$shortname)) {
  208.                     return $this->cache[$name] = $realpath;
  209.                 }
  210.                 return $this->cache[$name] = $path.'/'.$shortname;
  211.             }
  212.         }
  213.         $this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).'$nameimplode(', '$this->paths[$namespace]));
  214.         if (!$throw) {
  215.             return false;
  216.         }
  217.         throw new LoaderError($this->errorCache[$name]);
  218.     }
  219.     protected function parseName($name$default self::MAIN_NAMESPACE)
  220.     {
  221.         if (isset($name[0]) && '@' == $name[0]) {
  222.             if (false === $pos strpos($name'/')) {
  223.                 throw new LoaderError(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").'$name));
  224.             }
  225.             $namespace substr($name1$pos 1);
  226.             $shortname substr($name$pos 1);
  227.             return [$namespace$shortname];
  228.         }
  229.         return [$default$name];
  230.     }
  231.     protected function normalizeName($name)
  232.     {
  233.         return preg_replace('#/{2,}#''/'str_replace('\\''/', (string) $name));
  234.     }
  235.     protected function validateName($name)
  236.     {
  237.         if (false !== strpos($name"\0")) {
  238.             throw new LoaderError('A template name cannot contain NUL bytes.');
  239.         }
  240.         $name ltrim($name'/');
  241.         $parts explode('/'$name);
  242.         $level 0;
  243.         foreach ($parts as $part) {
  244.             if ('..' === $part) {
  245.                 --$level;
  246.             } elseif ('.' !== $part) {
  247.                 ++$level;
  248.             }
  249.             if ($level 0) {
  250.                 throw new LoaderError(sprintf('Looks like you try to load a template outside configured directories (%s).'$name));
  251.             }
  252.         }
  253.     }
  254.     private function isAbsolutePath($file)
  255.     {
  256.         return strspn($file'/\\'01)
  257.             || (\strlen($file) > && ctype_alpha($file[0])
  258.                 && ':' === substr($file11)
  259.                 && strspn($file'/\\'21)
  260.             )
  261.             || null !== parse_url($filePHP_URL_SCHEME)
  262.         ;
  263.     }
  264. }
  265. class_alias('Twig\Loader\FilesystemLoader''Twig_Loader_Filesystem');