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

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.     public 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 = [], string $rootPath null)
  31.     {
  32.         $this->rootPath = (null === $rootPath getcwd() : $rootPath).\DIRECTORY_SEPARATOR;
  33.         if (null !== $rootPath && 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 getSourceContext($name)
  120.     {
  121.         if (null === ($path $this->findTemplate($name)) || false === $path) {
  122.             return new Source(''$name'');
  123.         }
  124.         return new Source(file_get_contents($path), $name$path);
  125.     }
  126.     public function getCacheKey($name)
  127.     {
  128.         if (null === ($path $this->findTemplate($name)) || false === $path) {
  129.             return '';
  130.         }
  131.         $len = \strlen($this->rootPath);
  132.         if (=== strncmp($this->rootPath$path$len)) {
  133.             return substr($path$len);
  134.         }
  135.         return $path;
  136.     }
  137.     public function exists($name)
  138.     {
  139.         $name $this->normalizeName($name);
  140.         if (isset($this->cache[$name])) {
  141.             return true;
  142.         }
  143.         return null !== ($path $this->findTemplate($namefalse)) && false !== $path;
  144.     }
  145.     public function isFresh($name$time)
  146.     {
  147.         // false support to be removed in 3.0
  148.         if (null === ($path $this->findTemplate($name)) || false === $path) {
  149.             return false;
  150.         }
  151.         return filemtime($path) < $time;
  152.     }
  153.     /**
  154.      * Checks if the template can be found.
  155.      *
  156.      * In Twig 3.0, findTemplate must return a string or null (returning false won't work anymore).
  157.      *
  158.      * @param string $name  The template name
  159.      * @param bool   $throw Whether to throw an exception when an error occurs
  160.      *
  161.      * @return string|false|null The template name or false/null
  162.      */
  163.     protected function findTemplate($name$throw true)
  164.     {
  165.         $name $this->normalizeName($name);
  166.         if (isset($this->cache[$name])) {
  167.             return $this->cache[$name];
  168.         }
  169.         if (isset($this->errorCache[$name])) {
  170.             if (!$throw) {
  171.                 return false;
  172.             }
  173.             throw new LoaderError($this->errorCache[$name]);
  174.         }
  175.         try {
  176.             list($namespace$shortname) = $this->parseName($name);
  177.             $this->validateName($shortname);
  178.         } catch (LoaderError $e) {
  179.             if (!$throw) {
  180.                 return false;
  181.             }
  182.             throw $e;
  183.         }
  184.         if (!isset($this->paths[$namespace])) {
  185.             $this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".'$namespace);
  186.             if (!$throw) {
  187.                 return false;
  188.             }
  189.             throw new LoaderError($this->errorCache[$name]);
  190.         }
  191.         foreach ($this->paths[$namespace] as $path) {
  192.             if (!$this->isAbsolutePath($path)) {
  193.                 $path $this->rootPath.$path;
  194.             }
  195.             if (is_file($path.'/'.$shortname)) {
  196.                 if (false !== $realpath realpath($path.'/'.$shortname)) {
  197.                     return $this->cache[$name] = $realpath;
  198.                 }
  199.                 return $this->cache[$name] = $path.'/'.$shortname;
  200.             }
  201.         }
  202.         $this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).'$nameimplode(', '$this->paths[$namespace]));
  203.         if (!$throw) {
  204.             return false;
  205.         }
  206.         throw new LoaderError($this->errorCache[$name]);
  207.     }
  208.     private function normalizeName($name)
  209.     {
  210.         return preg_replace('#/{2,}#''/'str_replace('\\''/', (string) $name));
  211.     }
  212.     private function parseName($name$default self::MAIN_NAMESPACE)
  213.     {
  214.         if (isset($name[0]) && '@' == $name[0]) {
  215.             if (false === $pos strpos($name'/')) {
  216.                 throw new LoaderError(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").'$name));
  217.             }
  218.             $namespace substr($name1$pos 1);
  219.             $shortname substr($name$pos 1);
  220.             return [$namespace$shortname];
  221.         }
  222.         return [$default$name];
  223.     }
  224.     private function validateName($name)
  225.     {
  226.         if (false !== strpos($name"\0")) {
  227.             throw new LoaderError('A template name cannot contain NUL bytes.');
  228.         }
  229.         $name ltrim($name'/');
  230.         $parts explode('/'$name);
  231.         $level 0;
  232.         foreach ($parts as $part) {
  233.             if ('..' === $part) {
  234.                 --$level;
  235.             } elseif ('.' !== $part) {
  236.                 ++$level;
  237.             }
  238.             if ($level 0) {
  239.                 throw new LoaderError(sprintf('Looks like you try to load a template outside configured directories (%s).'$name));
  240.             }
  241.         }
  242.     }
  243.     private function isAbsolutePath($file)
  244.     {
  245.         return strspn($file'/\\'01)
  246.             || (\strlen($file) > && ctype_alpha($file[0])
  247.                 && ':' === $file[1]
  248.                 && strspn($file'/\\'21)
  249.             )
  250.             || null !== parse_url($file, \PHP_URL_SCHEME)
  251.         ;
  252.     }
  253. }
  254. class_alias('Twig\Loader\FilesystemLoader''Twig_Loader_Filesystem');