2011-10-05 06:22:53 +02:00
< ? php
/*
* This file is part of Twig .
*
* ( c ) 2009 Fabien Potencier
* ( c ) 2009 Armin Ronacher
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
/**
* Default base class for compiled templates .
*
2013-08-01 21:20:12 +02:00
* @ author Fabien Potencier < fabien @ symfony . com >
2011-10-05 06:22:53 +02:00
*/
abstract class Twig_Template implements Twig_TemplateInterface
{
2013-08-01 21:20:12 +02:00
protected static $cache = array ();
2011-10-05 06:22:53 +02:00
2013-08-01 21:20:12 +02:00
protected $parent ;
2011-10-05 06:22:53 +02:00
protected $parents ;
protected $env ;
protected $blocks ;
2013-08-01 21:20:12 +02:00
protected $traits ;
2013-09-19 08:08:25 +02:00
protected $macros ;
2011-10-05 06:22:53 +02:00
/**
* Constructor .
*
* @ param Twig_Environment $env A Twig_Environment instance
*/
public function __construct ( Twig_Environment $env )
{
$this -> env = $env ;
$this -> blocks = array ();
2013-08-01 21:20:12 +02:00
$this -> traits = array ();
2013-09-19 08:08:25 +02:00
$this -> macros = array ();
2011-10-05 06:22:53 +02:00
}
/**
* Returns the template name .
*
* @ return string The template name
*/
abstract public function getTemplateName ();
/**
2013-08-01 21:20:12 +02:00
* { @ inheritdoc }
2011-10-05 06:22:53 +02:00
*/
public function getEnvironment ()
{
return $this -> env ;
}
/**
* Returns the parent template .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
2011-10-05 06:22:53 +02:00
* @ return Twig_TemplateInterface | false The parent template or false if there is no parent
*/
public function getParent ( array $context )
{
2013-08-01 21:20:12 +02:00
if ( null !== $this -> parent ) {
return $this -> parent ;
}
2011-10-05 06:22:53 +02:00
$parent = $this -> doGetParent ( $context );
if ( false === $parent ) {
return false ;
} elseif ( $parent instanceof Twig_Template ) {
$name = $parent -> getTemplateName ();
$this -> parents [ $name ] = $parent ;
$parent = $name ;
} elseif ( ! isset ( $this -> parents [ $parent ])) {
$this -> parents [ $parent ] = $this -> env -> loadTemplate ( $parent );
}
return $this -> parents [ $parent ];
}
2013-08-01 21:20:12 +02:00
protected function doGetParent ( array $context )
{
return false ;
}
public function isTraitable ()
{
return true ;
}
2011-10-05 06:22:53 +02:00
/**
* Displays a parent block .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
2011-10-05 06:22:53 +02:00
* @ param string $name The block name to display from the parent
* @ param array $context The context
* @ param array $blocks The current set of blocks
*/
public function displayParentBlock ( $name , array $context , array $blocks = array ())
{
2013-08-01 21:20:12 +02:00
$name = ( string ) $name ;
if ( isset ( $this -> traits [ $name ])) {
$this -> traits [ $name ][ 0 ] -> displayBlock ( $name , $context , $blocks );
} elseif ( false !== $parent = $this -> getParent ( $context )) {
2011-10-05 06:22:53 +02:00
$parent -> displayBlock ( $name , $context , $blocks );
} else {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( sprintf ( 'The template has no parent and no traits defining the "%s" block' , $name ), - 1 , $this -> getTemplateName ());
2011-10-05 06:22:53 +02:00
}
}
/**
* Displays a block .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
2011-10-05 06:22:53 +02:00
* @ param string $name The block name to display
* @ param array $context The context
* @ param array $blocks The current set of blocks
*/
public function displayBlock ( $name , array $context , array $blocks = array ())
{
2013-08-01 21:20:12 +02:00
$name = ( string ) $name ;
2011-10-05 06:22:53 +02:00
if ( isset ( $blocks [ $name ])) {
$b = $blocks ;
unset ( $b [ $name ]);
call_user_func ( $blocks [ $name ], $context , $b );
} elseif ( isset ( $this -> blocks [ $name ])) {
call_user_func ( $this -> blocks [ $name ], $context , $blocks );
} elseif ( false !== $parent = $this -> getParent ( $context )) {
$parent -> displayBlock ( $name , $context , array_merge ( $this -> blocks , $blocks ));
}
}
/**
* Renders a parent block .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
2011-10-05 06:22:53 +02:00
* @ param string $name The block name to render from the parent
* @ param array $context The context
* @ param array $blocks The current set of blocks
*
* @ return string The rendered block
*/
public function renderParentBlock ( $name , array $context , array $blocks = array ())
{
ob_start ();
$this -> displayParentBlock ( $name , $context , $blocks );
return ob_get_clean ();
}
/**
* Renders a block .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
2011-10-05 06:22:53 +02:00
* @ param string $name The block name to render
* @ param array $context The context
* @ param array $blocks The current set of blocks
*
* @ return string The rendered block
*/
public function renderBlock ( $name , array $context , array $blocks = array ())
{
ob_start ();
$this -> displayBlock ( $name , $context , $blocks );
return ob_get_clean ();
}
/**
* Returns whether a block exists or not .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
* This method does only return blocks defined in the current template
* or defined in " used " traits .
*
* It does not return blocks from parent templates as the parent
* template name can be dynamic , which is only known based on the
* current context .
*
2011-10-05 06:22:53 +02:00
* @ param string $name The block name
*
* @ return Boolean true if the block exists , false otherwise
*/
public function hasBlock ( $name )
{
2013-08-01 21:20:12 +02:00
return isset ( $this -> blocks [( string ) $name ]);
2011-10-05 06:22:53 +02:00
}
/**
* Returns all block names .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
2011-10-05 06:22:53 +02:00
* @ return array An array of block names
2013-08-01 21:20:12 +02:00
*
* @ see hasBlock
2011-10-05 06:22:53 +02:00
*/
public function getBlockNames ()
{
return array_keys ( $this -> blocks );
}
/**
* Returns all blocks .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
2011-10-05 06:22:53 +02:00
* @ return array An array of blocks
2013-08-01 21:20:12 +02:00
*
* @ see hasBlock
2011-10-05 06:22:53 +02:00
*/
public function getBlocks ()
{
return $this -> blocks ;
}
/**
2013-08-01 21:20:12 +02:00
* { @ inheritdoc }
2011-10-05 06:22:53 +02:00
*/
public function display ( array $context , array $blocks = array ())
{
2013-08-01 21:20:12 +02:00
$this -> displayWithErrorHandling ( $this -> env -> mergeGlobals ( $context ), $blocks );
2011-10-05 06:22:53 +02:00
}
/**
2013-08-01 21:20:12 +02:00
* { @ inheritdoc }
2011-10-05 06:22:53 +02:00
*/
public function render ( array $context )
{
$level = ob_get_level ();
ob_start ();
try {
$this -> display ( $context );
} catch ( Exception $e ) {
while ( ob_get_level () > $level ) {
ob_end_clean ();
}
throw $e ;
}
return ob_get_clean ();
}
2013-08-01 21:20:12 +02:00
protected function displayWithErrorHandling ( array $context , array $blocks = array ())
{
try {
$this -> doDisplay ( $context , $blocks );
} catch ( Twig_Error $e ) {
if ( ! $e -> getTemplateFile ()) {
$e -> setTemplateFile ( $this -> getTemplateName ());
}
// this is mostly useful for Twig_Error_Loader exceptions
// see Twig_Error_Loader
if ( false === $e -> getTemplateLine ()) {
$e -> setTemplateLine ( - 1 );
$e -> guess ();
}
throw $e ;
} catch ( Exception $e ) {
throw new Twig_Error_Runtime ( sprintf ( 'An exception has been thrown during the rendering of a template ("%s").' , $e -> getMessage ()), - 1 , null , $e );
}
}
2011-10-05 06:22:53 +02:00
/**
* Auto - generated method to display the template with the given context .
*
* @ param array $context An array of parameters to pass to the template
* @ param array $blocks An array of blocks to pass to the template
*/
abstract protected function doDisplay ( array $context , array $blocks = array ());
/**
* Returns a variable from the context .
*
2013-08-01 21:20:12 +02:00
* This method is for internal use only and should never be called
* directly .
*
* This method should not be overridden in a sub - class as this is an
* implementation detail that has been introduced to optimize variable
* access for versions of PHP before 5.4 . This is not a way to override
* the way to get a variable value .
*
* @ param array $context The context
* @ param string $item The variable to return from the context
* @ param Boolean $ignoreStrictCheck Whether to ignore the strict variable check or not
2011-10-05 06:22:53 +02:00
*
* @ return The content of the context variable
*
* @ throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
*/
2013-08-01 21:20:12 +02:00
final protected function getContext ( $context , $item , $ignoreStrictCheck = false )
2011-10-05 06:22:53 +02:00
{
if ( ! array_key_exists ( $item , $context )) {
2013-08-01 21:20:12 +02:00
if ( $ignoreStrictCheck || ! $this -> env -> isStrictVariables ()) {
2011-10-05 06:22:53 +02:00
return null ;
}
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( sprintf ( 'Variable "%s" does not exist' , $item ), - 1 , $this -> getTemplateName ());
2011-10-05 06:22:53 +02:00
}
return $context [ $item ];
}
/**
* Returns the attribute value for a given array / object .
*
2013-08-01 21:20:12 +02:00
* @ param mixed $object The object or array from where to get the item
* @ param mixed $item The item to get from the array or object
* @ param array $arguments An array of arguments to pass if the item is an object method
2013-09-19 08:08:25 +02:00
* @ param string $type The type of attribute ( @ see Twig_Template constants )
2013-08-01 21:20:12 +02:00
* @ param Boolean $isDefinedTest Whether this is only a defined check
* @ param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not
*
* @ return mixed The attribute value , or a Boolean when $isDefinedTest is true , or null when the attribute is not set and $ignoreStrictCheck is true
*
* @ throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
2011-10-05 06:22:53 +02:00
*/
2013-09-19 08:08:25 +02:00
protected function getAttribute ( $object , $item , array $arguments = array (), $type = Twig_Template :: ANY_CALL , $isDefinedTest = false , $ignoreStrictCheck = false )
2011-10-05 06:22:53 +02:00
{
// array
2013-09-19 08:08:25 +02:00
if ( Twig_Template :: METHOD_CALL !== $type ) {
2013-08-01 21:20:12 +02:00
$arrayItem = is_bool ( $item ) || is_float ( $item ) ? ( int ) $item : $item ;
if (( is_array ( $object ) && array_key_exists ( $arrayItem , $object ))
|| ( $object instanceof ArrayAccess && isset ( $object [ $arrayItem ]))
2011-10-05 06:22:53 +02:00
) {
if ( $isDefinedTest ) {
return true ;
}
2013-08-01 21:20:12 +02:00
return $object [ $arrayItem ];
2011-10-05 06:22:53 +02:00
}
2013-09-19 08:08:25 +02:00
if ( Twig_Template :: ARRAY_CALL === $type || ! is_object ( $object )) {
2011-10-05 06:22:53 +02:00
if ( $isDefinedTest ) {
return false ;
}
2013-08-01 21:20:12 +02:00
if ( $ignoreStrictCheck || ! $this -> env -> isStrictVariables ()) {
2011-10-05 06:22:53 +02:00
return null ;
}
if ( is_object ( $object )) {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( sprintf ( 'Key "%s" in object (with ArrayAccess) of type "%s" does not exist' , $arrayItem , get_class ( $object )), - 1 , $this -> getTemplateName ());
} elseif ( is_array ( $object )) {
throw new Twig_Error_Runtime ( sprintf ( 'Key "%s" for array with keys "%s" does not exist' , $arrayItem , implode ( ', ' , array_keys ( $object ))), - 1 , $this -> getTemplateName ());
2013-09-19 08:08:25 +02:00
} elseif ( Twig_Template :: ARRAY_CALL === $type ) {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( sprintf ( 'Impossible to access a key ("%s") on a %s variable ("%s")' , $item , gettype ( $object ), $object ), - 1 , $this -> getTemplateName ());
2011-10-05 06:22:53 +02:00
} else {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( sprintf ( 'Impossible to access an attribute ("%s") on a %s variable ("%s")' , $item , gettype ( $object ), $object ), - 1 , $this -> getTemplateName ());
2011-10-05 06:22:53 +02:00
}
}
}
if ( ! is_object ( $object )) {
if ( $isDefinedTest ) {
return false ;
}
2013-08-01 21:20:12 +02:00
if ( $ignoreStrictCheck || ! $this -> env -> isStrictVariables ()) {
2011-10-05 06:22:53 +02:00
return null ;
}
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( sprintf ( 'Impossible to invoke a method ("%s") on a %s variable ("%s")' , $item , gettype ( $object ), $object ), - 1 , $this -> getTemplateName ());
2011-10-05 06:22:53 +02:00
}
$class = get_class ( $object );
// object property
2013-09-19 08:08:25 +02:00
if ( Twig_Template :: METHOD_CALL !== $type ) {
2013-08-01 21:20:12 +02:00
if ( isset ( $object -> $item ) || array_key_exists (( string ) $item , $object )) {
2011-10-05 06:22:53 +02:00
if ( $isDefinedTest ) {
return true ;
}
if ( $this -> env -> hasExtension ( 'sandbox' )) {
$this -> env -> getExtension ( 'sandbox' ) -> checkPropertyAllowed ( $object , $item );
}
return $object -> $item ;
}
}
// object method
2013-08-01 21:20:12 +02:00
if ( ! isset ( self :: $cache [ $class ][ 'methods' ])) {
self :: $cache [ $class ][ 'methods' ] = array_change_key_case ( array_flip ( get_class_methods ( $object )));
}
2011-10-05 06:22:53 +02:00
$lcItem = strtolower ( $item );
if ( isset ( self :: $cache [ $class ][ 'methods' ][ $lcItem ])) {
2013-08-01 21:20:12 +02:00
$method = ( string ) $item ;
2011-10-05 06:22:53 +02:00
} elseif ( isset ( self :: $cache [ $class ][ 'methods' ][ 'get' . $lcItem ])) {
$method = 'get' . $item ;
} elseif ( isset ( self :: $cache [ $class ][ 'methods' ][ 'is' . $lcItem ])) {
$method = 'is' . $item ;
} elseif ( isset ( self :: $cache [ $class ][ 'methods' ][ '__call' ])) {
2013-08-01 21:20:12 +02:00
$method = ( string ) $item ;
2011-10-05 06:22:53 +02:00
} else {
if ( $isDefinedTest ) {
return false ;
}
2013-08-01 21:20:12 +02:00
if ( $ignoreStrictCheck || ! $this -> env -> isStrictVariables ()) {
2011-10-05 06:22:53 +02:00
return null ;
}
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( sprintf ( 'Method "%s" for object "%s" does not exist' , $item , get_class ( $object )), - 1 , $this -> getTemplateName ());
2011-10-05 06:22:53 +02:00
}
if ( $isDefinedTest ) {
return true ;
}
if ( $this -> env -> hasExtension ( 'sandbox' )) {
$this -> env -> getExtension ( 'sandbox' ) -> checkMethodAllowed ( $object , $method );
}
$ret = call_user_func_array ( array ( $object , $method ), $arguments );
2013-08-01 21:20:12 +02:00
// useful when calling a template method from a template
// this is not supported but unfortunately heavily used in the Symfony profiler
2011-10-05 06:22:53 +02:00
if ( $object instanceof Twig_TemplateInterface ) {
2013-08-01 21:20:12 +02:00
return $ret === '' ? '' : new Twig_Markup ( $ret , $this -> env -> getCharset ());
2011-10-05 06:22:53 +02:00
}
return $ret ;
}
2013-08-01 21:20:12 +02:00
2013-09-19 08:08:25 +02:00
/**
* Calls macro in a template .
*
* @ param Twig_Template $template The template
* @ param string $macro The name of macro
* @ param array $arguments The arguments of macro
* @ param array $namedNames An array of names of arguments as keys
* @ param integer $namedCount The count of named arguments
* @ param integer $positionalCount The count of positional arguments
*
* @ return string The content of a macro
*
* @ throws Twig_Error_Runtime if the macro is not defined
* @ throws Twig_Error_Runtime if the argument is defined twice
* @ throws Twig_Error_Runtime if the argument is unknown
*/
protected function callMacro ( Twig_Template $template , $macro , array $arguments , array $namedNames = array (), $namedCount = 0 , $positionalCount = - 1 )
{
if ( ! isset ( $template -> macros [ $macro ][ 'reflection' ])) {
if ( ! isset ( $template -> macros [ $macro ])) {
throw new Twig_Error_Runtime ( sprintf ( 'Macro "%s" is not defined in the template "%s".' , $macro , $template -> getTemplateName ()));
}
$template -> macros [ $macro ][ 'reflection' ] = new ReflectionMethod ( $template , $template -> macros [ $macro ][ 'method' ]);
}
if ( $namedCount < 1 ) {
return $template -> macros [ $macro ][ 'reflection' ] -> invokeArgs ( $template , $arguments );
}
$i = 0 ;
$args = array ();
foreach ( $template -> macros [ $macro ][ 'arguments' ] as $name => $value ) {
if ( isset ( $namedNames [ $name ])) {
if ( $i < $positionalCount ) {
throw new Twig_Error_Runtime ( sprintf ( 'Argument "%s" is defined twice for macro "%s" defined in the template "%s".' , $name , $macro , $template -> getTemplateName ()));
}
$args [] = $arguments [ $name ];
if ( -- $namedCount < 1 ) {
break ;
}
} elseif ( $i < $positionalCount ) {
$args [] = $arguments [ $i ];
} else {
$args [] = $value ;
}
$i ++ ;
}
if ( $namedCount > 0 ) {
$parameters = array_keys ( array_diff_key ( $namedNames , $template -> macros [ $macro ][ 'arguments' ]));
throw new Twig_Error_Runtime ( sprintf ( 'Unknown argument%s "%s" for macro "%s" defined in the template "%s".' , count ( $parameters ) > 1 ? 's' : '' , implode ( '", "' , $parameters ), $macro , $template -> getTemplateName ()));
}
return $template -> macros [ $macro ][ 'reflection' ] -> invokeArgs ( $template , $args );
}
2013-08-01 21:20:12 +02:00
/**
* This method is only useful when testing Twig . Do not use it .
*/
public static function clearCache ()
{
self :: $cache = array ();
}
2011-10-05 06:22:53 +02:00
}