2011-10-05 06:22:53 +02:00
< ? php
/*
* This file is part of Twig .
*
2018-05-10 12:24:53 +02:00
* ( c ) Fabien Potencier
2011-10-05 06:22:53 +02:00
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
/**
* Loads template from the filesystem .
*
2013-08-01 21:20:12 +02:00
* @ author Fabien Potencier < fabien @ symfony . com >
2011-10-05 06:22:53 +02:00
*/
2018-05-10 12:24:53 +02:00
class Twig_Loader_Filesystem implements Twig_LoaderInterface , Twig_ExistsLoaderInterface , Twig_SourceContextLoaderInterface
2011-10-05 06:22:53 +02:00
{
2013-09-19 08:08:25 +02:00
/** Identifier of the main namespace. */
const MAIN_NAMESPACE = '__main__' ;
protected $paths = array ();
protected $cache = array ();
2018-05-10 12:24:53 +02:00
protected $errorCache = array ();
private $rootPath ;
2011-10-05 06:22:53 +02:00
/**
2018-05-10 12:24:53 +02:00
* @ param string | array $paths A path or an array of paths where to look for templates
* @ param string | null $rootPath The root path common to all relative paths ( null for getcwd ())
2011-10-05 06:22:53 +02:00
*/
2018-05-10 12:24:53 +02:00
public function __construct ( $paths = array (), $rootPath = null )
2011-10-05 06:22:53 +02:00
{
2018-05-10 12:24:53 +02:00
$this -> rootPath = ( null === $rootPath ? getcwd () : $rootPath ) . DIRECTORY_SEPARATOR ;
if ( false !== $realPath = realpath ( $rootPath )) {
$this -> rootPath = $realPath . DIRECTORY_SEPARATOR ;
}
2013-08-01 21:20:12 +02:00
if ( $paths ) {
$this -> setPaths ( $paths );
}
2011-10-05 06:22:53 +02:00
}
/**
* Returns the paths to the templates .
*
2013-08-01 21:20:12 +02:00
* @ param string $namespace A path namespace
*
2011-10-05 06:22:53 +02:00
* @ return array The array of paths where to look for templates
*/
2013-09-19 08:08:25 +02:00
public function getPaths ( $namespace = self :: MAIN_NAMESPACE )
2013-08-01 21:20:12 +02:00
{
return isset ( $this -> paths [ $namespace ]) ? $this -> paths [ $namespace ] : array ();
}
/**
* Returns the path namespaces .
*
2013-09-19 08:08:25 +02:00
* The main namespace is always defined .
2013-08-01 21:20:12 +02:00
*
* @ return array The array of defined namespaces
*/
public function getNamespaces ()
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
return array_keys ( $this -> paths );
2011-10-05 06:22:53 +02:00
}
/**
* Sets the paths where templates are stored .
*
2013-08-01 21:20:12 +02:00
* @ param string | array $paths A path or an array of paths where to look for templates
* @ param string $namespace A path namespace
2011-10-05 06:22:53 +02:00
*/
2013-09-19 08:08:25 +02:00
public function setPaths ( $paths , $namespace = self :: MAIN_NAMESPACE )
2011-10-05 06:22:53 +02:00
{
if ( ! is_array ( $paths )) {
$paths = array ( $paths );
}
2013-08-01 21:20:12 +02:00
$this -> paths [ $namespace ] = array ();
2011-10-05 06:22:53 +02:00
foreach ( $paths as $path ) {
2013-08-01 21:20:12 +02:00
$this -> addPath ( $path , $namespace );
2011-10-05 06:22:53 +02:00
}
}
/**
* Adds a path where templates are stored .
*
2013-08-01 21:20:12 +02:00
* @ param string $path A path where to look for templates
2018-05-10 12:24:53 +02:00
* @ param string $namespace A path namespace
2013-08-01 21:20:12 +02:00
*
* @ throws Twig_Error_Loader
2011-10-05 06:22:53 +02:00
*/
2013-09-19 08:08:25 +02:00
public function addPath ( $path , $namespace = self :: MAIN_NAMESPACE )
2011-10-05 06:22:53 +02:00
{
// invalidate the cache
2018-05-10 12:24:53 +02:00
$this -> cache = $this -> errorCache = array ();
2011-10-05 06:22:53 +02:00
2018-05-10 12:24:53 +02:00
$checkPath = $this -> isAbsolutePath ( $path ) ? $path : $this -> rootPath . $path ;
if ( ! is_dir ( $checkPath )) {
throw new Twig_Error_Loader ( sprintf ( 'The "%s" directory does not exist ("%s").' , $path , $checkPath ));
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
$this -> paths [ $namespace ][] = rtrim ( $path , '/\\' );
2011-10-05 06:22:53 +02:00
}
/**
2013-08-01 21:20:12 +02:00
* Prepends a path where templates are stored .
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ param string $path A path where to look for templates
2018-05-10 12:24:53 +02:00
* @ param string $namespace A path namespace
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ throws Twig_Error_Loader
*/
2013-09-19 08:08:25 +02:00
public function prependPath ( $path , $namespace = self :: MAIN_NAMESPACE )
2013-08-01 21:20:12 +02:00
{
// invalidate the cache
2018-05-10 12:24:53 +02:00
$this -> cache = $this -> errorCache = array ();
2013-08-01 21:20:12 +02:00
2018-05-10 12:24:53 +02:00
$checkPath = $this -> isAbsolutePath ( $path ) ? $path : $this -> rootPath . $path ;
if ( ! is_dir ( $checkPath )) {
throw new Twig_Error_Loader ( sprintf ( 'The "%s" directory does not exist ("%s").' , $path , $checkPath ));
2013-08-01 21:20:12 +02:00
}
$path = rtrim ( $path , '/\\' );
if ( ! isset ( $this -> paths [ $namespace ])) {
$this -> paths [ $namespace ][] = $path ;
} else {
array_unshift ( $this -> paths [ $namespace ], $path );
}
}
2011-10-05 06:22:53 +02:00
public function getSource ( $name )
{
2018-05-10 12:24:53 +02:00
@ trigger_error ( sprintf ( 'Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.' , get_class ( $this )), E_USER_DEPRECATED );
2011-10-05 06:22:53 +02:00
return file_get_contents ( $this -> findTemplate ( $name ));
}
2018-05-10 12:24:53 +02:00
public function getSourceContext ( $name )
{
$path = $this -> findTemplate ( $name );
return new Twig_Source ( file_get_contents ( $path ), $name , $path );
}
2011-10-05 06:22:53 +02:00
public function getCacheKey ( $name )
{
2018-05-10 12:24:53 +02:00
$path = $this -> findTemplate ( $name );
$len = strlen ( $this -> rootPath );
if ( 0 === strncmp ( $this -> rootPath , $path , $len )) {
return substr ( $path , $len );
}
return $path ;
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
public function exists ( $name )
{
2018-05-10 12:24:53 +02:00
$name = $this -> normalizeName ( $name );
2013-08-01 21:20:12 +02:00
if ( isset ( $this -> cache [ $name ])) {
return true ;
}
try {
2018-05-10 12:24:53 +02:00
return false !== $this -> findTemplate ( $name , false );
2013-08-01 21:20:12 +02:00
} catch ( Twig_Error_Loader $exception ) {
2018-05-10 12:24:53 +02:00
@ 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 );
2013-08-01 21:20:12 +02:00
return false ;
}
}
2011-10-05 06:22:53 +02:00
public function isFresh ( $name , $time )
{
2018-05-10 12:24:53 +02:00
return filemtime ( $this -> findTemplate ( $name )) < $time ;
2011-10-05 06:22:53 +02:00
}
protected function findTemplate ( $name )
{
2018-05-10 12:24:53 +02:00
$throw = func_num_args () > 1 ? func_get_arg ( 1 ) : true ;
$name = $this -> normalizeName ( $name );
2011-10-05 06:22:53 +02:00
if ( isset ( $this -> cache [ $name ])) {
return $this -> cache [ $name ];
}
2018-05-10 12:24:53 +02:00
if ( isset ( $this -> errorCache [ $name ])) {
if ( ! $throw ) {
return false ;
2013-08-01 21:20:12 +02:00
}
2018-05-10 12:24:53 +02:00
throw new Twig_Error_Loader ( $this -> errorCache [ $name ]);
2013-08-01 21:20:12 +02:00
}
2018-05-10 12:24:53 +02:00
$this -> validateName ( $name );
list ( $namespace , $shortname ) = $this -> parseName ( $name );
2013-08-01 21:20:12 +02:00
if ( ! isset ( $this -> paths [ $namespace ])) {
2018-05-10 12:24:53 +02:00
$this -> errorCache [ $name ] = sprintf ( 'There are no registered paths for namespace "%s".' , $namespace );
if ( ! $throw ) {
return false ;
}
throw new Twig_Error_Loader ( $this -> errorCache [ $name ]);
2013-08-01 21:20:12 +02:00
}
foreach ( $this -> paths [ $namespace ] as $path ) {
2018-05-10 12:24:53 +02:00
if ( ! $this -> isAbsolutePath ( $path )) {
$path = $this -> rootPath . '/' . $path ;
}
2013-09-19 08:08:25 +02:00
if ( is_file ( $path . '/' . $shortname )) {
2018-05-10 12:24:53 +02:00
if ( false !== $realpath = realpath ( $path . '/' . $shortname )) {
return $this -> cache [ $name ] = $realpath ;
}
2013-09-19 08:08:25 +02:00
return $this -> cache [ $name ] = $path . '/' . $shortname ;
2011-10-05 06:22:53 +02:00
}
}
2018-05-10 12:24:53 +02:00
$this -> errorCache [ $name ] = sprintf ( 'Unable to find template "%s" (looked into: %s).' , $name , implode ( ', ' , $this -> paths [ $namespace ]));
if ( ! $throw ) {
return false ;
}
throw new Twig_Error_Loader ( $this -> errorCache [ $name ]);
}
protected function parseName ( $name , $default = self :: MAIN_NAMESPACE )
{
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 );
$shortname = substr ( $name , $pos + 1 );
return array ( $namespace , $shortname );
}
return array ( $default , $name );
}
protected function normalizeName ( $name )
{
return preg_replace ( '#/{2,}#' , '/' , str_replace ( '\\' , '/' , ( string ) $name ));
2011-10-05 06:22:53 +02:00
}
protected function validateName ( $name )
{
if ( false !== strpos ( $name , " \0 " )) {
throw new Twig_Error_Loader ( 'A template name cannot contain NUL bytes.' );
}
2013-08-01 21:20:12 +02:00
$name = ltrim ( $name , '/' );
2011-10-05 06:22:53 +02:00
$parts = explode ( '/' , $name );
$level = 0 ;
foreach ( $parts as $part ) {
if ( '..' === $part ) {
-- $level ;
} elseif ( '.' !== $part ) {
++ $level ;
}
if ( $level < 0 ) {
throw new Twig_Error_Loader ( sprintf ( 'Looks like you try to load a template outside configured directories (%s).' , $name ));
}
}
}
2018-05-10 12:24:53 +02:00
private function isAbsolutePath ( $file )
{
return strspn ( $file , '/\\' , 0 , 1 )
|| ( strlen ( $file ) > 3 && ctype_alpha ( $file [ 0 ])
&& ':' === substr ( $file , 1 , 1 )
&& strspn ( $file , '/\\' , 2 , 1 )
)
|| null !== parse_url ( $file , PHP_URL_SCHEME )
;
}
2011-10-05 06:22:53 +02:00
}
2018-05-10 12:24:53 +02:00
class_alias ( 'Twig_Loader_Filesystem' , 'Twig\Loader\FilesystemLoader' , false );