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 ;
2015-03-11 00:16:45 +01:00
protected $parents = array ();
2011-10-05 06:22:53 +02:00
protected $env ;
protected $blocks ;
2013-08-01 21:20:12 +02:00
protected $traits ;
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 ();
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 ;
}
2015-03-11 00:16:45 +01:00
try {
$parent = $this -> doGetParent ( $context );
if ( false === $parent ) {
return false ;
}
if ( $parent instanceof Twig_Template ) {
return $this -> parents [ $parent -> getTemplateName ()] = $parent ;
}
if ( ! isset ( $this -> parents [ $parent ])) {
$this -> parents [ $parent ] = $this -> loadTemplate ( $parent );
}
} catch ( Twig_Error_Loader $e ) {
$e -> setTemplateFile ( null );
$e -> guess ();
throw $e ;
2011-10-05 06:22:53 +02:00
}
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 ])) {
2015-03-11 00:16:45 +01:00
$this -> traits [ $name ][ 0 ] -> displayBlock ( $name , $context , $blocks , false );
2013-08-01 21:20:12 +02:00
} elseif ( false !== $parent = $this -> getParent ( $context )) {
2015-03-11 00:16:45 +01:00
$parent -> displayBlock ( $name , $context , $blocks , false );
2011-10-05 06:22:53 +02:00
} 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 .
*
2015-03-11 00:16:45 +01:00
* @ param string $name The block name to display
* @ param array $context The context
* @ param array $blocks The current set of blocks
* @ param bool $useBlocks Whether to use the current set of blocks
2011-10-05 06:22:53 +02:00
*/
2015-03-11 00:16:45 +01:00
public function displayBlock ( $name , array $context , array $blocks = array (), $useBlocks = true )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
$name = ( string ) $name ;
2015-03-11 00:16:45 +01:00
if ( $useBlocks && isset ( $blocks [ $name ])) {
$template = $blocks [ $name ][ 0 ];
$block = $blocks [ $name ][ 1 ];
2011-10-05 06:22:53 +02:00
} elseif ( isset ( $this -> blocks [ $name ])) {
2015-03-11 00:16:45 +01:00
$template = $this -> blocks [ $name ][ 0 ];
$block = $this -> blocks [ $name ][ 1 ];
} else {
$template = null ;
$block = null ;
}
if ( null !== $template ) {
try {
$template -> $block ( $context , $blocks );
} catch ( Twig_Error $e ) {
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 , $template -> getTemplateName (), $e );
}
2011-10-05 06:22:53 +02:00
} elseif ( false !== $parent = $this -> getParent ( $context )) {
2015-03-11 00:16:45 +01:00
$parent -> displayBlock ( $name , $context , array_merge ( $this -> blocks , $blocks ), false );
2011-10-05 06:22:53 +02:00
}
}
/**
* 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 .
*
2015-03-11 00:16:45 +01:00
* @ param string $name The block name to render
* @ param array $context The context
* @ param array $blocks The current set of blocks
* @ param bool $useBlocks Whether to use the current set of blocks
2011-10-05 06:22:53 +02:00
*
* @ return string The rendered block
*/
2015-03-11 00:16:45 +01:00
public function renderBlock ( $name , array $context , array $blocks = array (), $useBlocks = true )
2011-10-05 06:22:53 +02:00
{
ob_start ();
2015-03-11 00:16:45 +01:00
$this -> displayBlock ( $name , $context , $blocks , $useBlocks );
2011-10-05 06:22:53 +02:00
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
*
2015-03-11 00:16:45 +01:00
* @ return bool true if the block exists , false otherwise
2011-10-05 06:22:53 +02:00
*/
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 );
}
2015-03-11 00:16:45 +01:00
protected function loadTemplate ( $template , $templateName = null , $line = null , $index = null )
{
try {
if ( is_array ( $template )) {
return $this -> env -> resolveTemplate ( $template );
}
if ( $template instanceof Twig_Template ) {
return $template ;
}
return $this -> env -> loadTemplate ( $template , $index );
} catch ( Twig_Error $e ) {
$e -> setTemplateFile ( $templateName ? $templateName : $this -> getTemplateName ());
if ( ! $line ) {
$e -> guess ();
} else {
$e -> setTemplateLine ( $line );
}
throw $e ;
}
}
2011-10-05 06:22:53 +02:00
/**
* 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 ())
{
2015-03-11 00:16:45 +01:00
$this -> displayWithErrorHandling ( $this -> env -> mergeGlobals ( $context ), array_merge ( $this -> blocks , $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 ) {
2015-03-11 00:16:45 +01:00
throw new Twig_Error_Runtime ( sprintf ( 'An exception has been thrown during the rendering of a template ("%s").' , $e -> getMessage ()), - 1 , $this -> getTemplateName (), $e );
2013-08-01 21:20:12 +02:00
}
}
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 .
*
2015-03-11 00:16:45 +01:00
* @ param array $context The context
* @ param string $item The variable to return from the context
* @ param bool $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 ()) {
2015-03-11 00:16:45 +01:00
return ;
2011-10-05 06:22:53 +02:00
}
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 .
*
2015-03-11 00:16:45 +01: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
* @ param string $type The type of attribute ( @ see Twig_Template constants )
* @ param bool $isDefinedTest Whether this is only a defined check
* @ param bool $ignoreStrictCheck Whether to ignore the strict attribute check or not
2013-08-01 21:20:12 +02:00
*
* @ 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 ()) {
2015-03-11 00:16:45 +01:00
return ;
2011-10-05 06:22:53 +02:00
}
2015-03-11 00:16:45 +01:00
if ( $object instanceof ArrayAccess ) {
$message = sprintf ( 'Key "%s" in object with ArrayAccess of class "%s" does not exist' , $arrayItem , get_class ( $object ));
} elseif ( is_object ( $object )) {
$message = sprintf ( 'Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface' , $item , get_class ( $object ));
2013-08-01 21:20:12 +02:00
} elseif ( is_array ( $object )) {
2015-03-11 00:16:45 +01:00
if ( empty ( $object )) {
$message = sprintf ( 'Key "%s" does not exist as the array is empty' , $arrayItem );
} else {
$message = sprintf ( 'Key "%s" for array with keys "%s" does not exist' , $arrayItem , implode ( ', ' , array_keys ( $object )));
}
2013-09-19 08:08:25 +02:00
} elseif ( Twig_Template :: ARRAY_CALL === $type ) {
2015-03-11 00:16:45 +01:00
$message = sprintf ( 'Impossible to access a key ("%s") on a %s variable ("%s")' , $item , gettype ( $object ), $object );
2011-10-05 06:22:53 +02:00
} else {
2015-03-11 00:16:45 +01:00
$message = sprintf ( 'Impossible to access an attribute ("%s") on a %s variable ("%s")' , $item , gettype ( $object ), $object );
2011-10-05 06:22:53 +02:00
}
2015-03-11 00:16:45 +01:00
throw new Twig_Error_Runtime ( $message , - 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 ()) {
2015-03-11 00:16:45 +01:00
return ;
2011-10-05 06:22:53 +02:00
}
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
}
// 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 ;
}
}
2015-03-11 00:16:45 +01:00
$class = get_class ( $object );
2011-10-05 06:22:53 +02:00
// 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 )));
}
2015-03-11 00:16:45 +01:00
$call = false ;
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 ;
2015-03-11 00:16:45 +01:00
$call = true ;
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 ()) {
2015-03-11 00:16:45 +01:00
return ;
2011-10-05 06:22:53 +02:00
}
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 );
}
2015-03-11 00:16:45 +01:00
// Some objects throw exceptions when they have __call, and the method we try
// to call is not supported. If ignoreStrictCheck is true, we should return null.
try {
$ret = call_user_func_array ( array ( $object , $method ), $arguments );
} catch ( BadMethodCallException $e ) {
if ( $call && ( $ignoreStrictCheck || ! $this -> env -> isStrictVariables ())) {
return ;
}
throw $e ;
}
2011-10-05 06:22:53 +02:00
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 ;
}
}