View source  
  <?php
abstract class Twig_Template implements Twig_TemplateInterface {
  protected static $cache = array();
  protected $parent;
  protected $parents;
  protected $env;
  protected $blocks;
  protected $traits;
  
  public function __construct(Twig_Environment $env) {
    $this->env = $env;
    $this->blocks = array();
    $this->traits = array();
  }
  
  public abstract function getTemplateName();
  
  public function getEnvironment() {
    return $this->env;
  }
  
  public function getParent(array $context) {
    if (null !== $this->parent) {
      return $this->parent;
    }
    $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];
  }
  protected function doGetParent(array $context) {
    return false;
  }
  public function isTraitable() {
    return true;
  }
  
  public function displayParentBlock($name, array $context, array $blocks = array()) {
    $name = (string) $name;
    if (isset($this->traits[$name])) {
      $this->traits[$name][0]
        ->displayBlock($name, $context, $blocks, false);
    }
    elseif (false !== ($parent = $this
      ->getParent($context))) {
      $parent
        ->displayBlock($name, $context, $blocks, false);
    }
    else {
      throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this
        ->getTemplateName());
    }
  }
  
  public function displayBlock($name, array $context, array $blocks = array(), $useBlocks = true) {
    $name = (string) $name;
    if ($useBlocks && isset($blocks[$name])) {
      $template = $blocks[$name][0];
      $block = $blocks[$name][1];
    }
    elseif (isset($this->blocks[$name])) {
      $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);
      }
    }
    elseif (false !== ($parent = $this
      ->getParent($context))) {
      $parent
        ->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
    }
  }
  
  public function renderParentBlock($name, array $context, array $blocks = array()) {
    ob_start();
    $this
      ->displayParentBlock($name, $context, $blocks);
    return ob_get_clean();
  }
  
  public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true) {
    ob_start();
    $this
      ->displayBlock($name, $context, $blocks, $useBlocks);
    return ob_get_clean();
  }
  
  public function hasBlock($name) {
    return isset($this->blocks[(string) $name]);
  }
  
  public function getBlockNames() {
    return array_keys($this->blocks);
  }
  
  public function getBlocks() {
    return $this->blocks;
  }
  
  public function display(array $context, array $blocks = array()) {
    $this
      ->displayWithErrorHandling($this->env
      ->mergeGlobals($context), array_merge($this->blocks, $blocks));
  }
  
  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();
  }
  protected function displayWithErrorHandling(array $context, array $blocks = array()) {
    try {
      $this
        ->doDisplay($context, $blocks);
    } catch (Twig_Error $e) {
      if (!$e
        ->getTemplateFile()) {
        $e
          ->setTemplateFile($this
          ->getTemplateName());
      }
      
      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, $this
        ->getTemplateName(), $e);
    }
  }
  
  protected abstract function doDisplay(array $context, array $blocks = array());
  
  protected final function getContext($context, $item, $ignoreStrictCheck = false) {
    if (!array_key_exists($item, $context)) {
      if ($ignoreStrictCheck || !$this->env
        ->isStrictVariables()) {
        return;
      }
      throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this
        ->getTemplateName());
    }
    return $context[$item];
  }
  
  protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) {
    
    if (Twig_Template::METHOD_CALL !== $type) {
      $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])) {
        if ($isDefinedTest) {
          return true;
        }
        return $object[$arrayItem];
      }
      if (Twig_Template::ARRAY_CALL === $type || !is_object($object)) {
        if ($isDefinedTest) {
          return false;
        }
        if ($ignoreStrictCheck || !$this->env
          ->isStrictVariables()) {
          return;
        }
        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));
        }
        elseif (is_array($object)) {
          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)));
          }
        }
        elseif (Twig_Template::ARRAY_CALL === $type) {
          $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
        }
        else {
          $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
        }
        throw new Twig_Error_Runtime($message, -1, $this
          ->getTemplateName());
      }
    }
    if (!is_object($object)) {
      if ($isDefinedTest) {
        return false;
      }
      if ($ignoreStrictCheck || !$this->env
        ->isStrictVariables()) {
        return;
      }
      throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this
        ->getTemplateName());
    }
    
    if (Twig_Template::METHOD_CALL !== $type) {
      if (isset($object->{$item}) || array_key_exists((string) $item, $object)) {
        if ($isDefinedTest) {
          return true;
        }
        if ($this->env
          ->hasExtension('sandbox')) {
          $this->env
            ->getExtension('sandbox')
            ->checkPropertyAllowed($object, $item);
        }
        return $object->{$item};
      }
    }
    $class = get_class($object);
    
    if (!isset(self::$cache[$class]['methods'])) {
      self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
    }
    $call = false;
    $lcItem = strtolower($item);
    if (isset(self::$cache[$class]['methods'][$lcItem])) {
      $method = (string) $item;
    }
    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'])) {
      $method = (string) $item;
      $call = true;
    }
    else {
      if ($isDefinedTest) {
        return false;
      }
      if ($ignoreStrictCheck || !$this->env
        ->isStrictVariables()) {
        return;
      }
      throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this
        ->getTemplateName());
    }
    if ($isDefinedTest) {
      return true;
    }
    if ($this->env
      ->hasExtension('sandbox')) {
      $this->env
        ->getExtension('sandbox')
        ->checkMethodAllowed($object, $method);
    }
    
    try {
      $ret = call_user_func_array(array(
        $object,
        $method,
      ), $arguments);
    } catch (BadMethodCallException $e) {
      if ($call && ($ignoreStrictCheck || !$this->env
        ->isStrictVariables())) {
        return;
      }
      throw $e;
    }
    
    if ($object instanceof Twig_TemplateInterface) {
      return $ret === '' ? '' : new Twig_Markup($ret, $this->env
        ->getCharset());
    }
    return $ret;
  }
  
  public static function clearCache() {
    self::$cache = array();
  }
}