You are here

Template.php in Zircon Profile 8

File

vendor/twig/twig/lib/Twig/Template.php
View source
<?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.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
abstract class Twig_Template implements Twig_TemplateInterface {
  protected static $cache = array();
  protected $parent;
  protected $parents = array();
  protected $env;
  protected $blocks = array();
  protected $traits = array();

  /**
   * Constructor.
   *
   * @param Twig_Environment $env A Twig_Environment instance
   */
  public function __construct(Twig_Environment $env) {
    $this->env = $env;
  }

  /**
   * Returns the template name.
   *
   * @return string The template name
   */
  public abstract function getTemplateName();

  /**
   * @deprecated since 1.20 (to be removed in 2.0)
   */
  public function getEnvironment() {
    @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 1.20 and will be removed in 2.0.', E_USER_DEPRECATED);
    return $this->env;
  }

  /**
   * Returns the parent template.
   *
   * This method is for internal use only and should never be called
   * directly.
   *
   * @param array $context
   *
   * @return Twig_TemplateInterface|false The parent template or false if there is no parent
   *
   * @internal
   */
  public function getParent(array $context) {
    if (null !== $this->parent) {
      return $this->parent;
    }
    try {
      $parent = $this
        ->doGetParent($context);
      if (false === $parent) {
        return false;
      }
      if ($parent instanceof self) {
        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;
    }
    return $this->parents[$parent];
  }
  protected function doGetParent(array $context) {
    return false;
  }
  public function isTraitable() {
    return true;
  }

  /**
   * Displays a parent block.
   *
   * This method is for internal use only and should never be called
   * directly.
   *
   * @param string $name    The block name to display from the parent
   * @param array  $context The context
   * @param array  $blocks  The current set of blocks
   *
   * @internal
   */
  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());
    }
  }

  /**
   * Displays a block.
   *
   * This method is for internal use only and should never be called
   * directly.
   *
   * @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
   *
   * @internal
   */
  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) {

      // avoid RCEs when sandbox is enabled
      if (!$template instanceof self) {
        throw new LogicException('A block must be a method on a Twig_Template instance.');
      }
      try {
        $template
          ->{$block}($context, $blocks);
      } catch (Twig_Error $e) {
        if (!$e
          ->getTemplateFile()) {
          $e
            ->setTemplateFile($template
            ->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, $template
          ->getTemplateName(), $e);
      }
    }
    elseif (false !== ($parent = $this
      ->getParent($context))) {
      $parent
        ->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
    }
  }

  /**
   * Renders a parent block.
   *
   * This method is for internal use only and should never be called
   * directly.
   *
   * @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
   *
   * @internal
   */
  public function renderParentBlock($name, array $context, array $blocks = array()) {
    ob_start();
    $this
      ->displayParentBlock($name, $context, $blocks);
    return ob_get_clean();
  }

  /**
   * Renders a block.
   *
   * This method is for internal use only and should never be called
   * directly.
   *
   * @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
   *
   * @return string The rendered block
   *
   * @internal
   */
  public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true) {
    ob_start();
    $this
      ->displayBlock($name, $context, $blocks, $useBlocks);
    return ob_get_clean();
  }

  /**
   * Returns whether a block exists or not.
   *
   * 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.
   *
   * @param string $name The block name
   *
   * @return bool true if the block exists, false otherwise
   *
   * @internal
   */
  public function hasBlock($name) {
    return isset($this->blocks[(string) $name]);
  }

  /**
   * Returns all block names.
   *
   * This method is for internal use only and should never be called
   * directly.
   *
   * @return array An array of block names
   *
   * @see hasBlock
   *
   * @internal
   */
  public function getBlockNames() {
    return array_keys($this->blocks);
  }
  protected function loadTemplate($template, $templateName = null, $line = null, $index = null) {
    try {
      if (is_array($template)) {
        return $this->env
          ->resolveTemplate($template);
      }
      if ($template instanceof self) {
        return $template;
      }
      return $this->env
        ->loadTemplate($template, $index);
    } catch (Twig_Error $e) {
      if (!$e
        ->getTemplateFile()) {
        $e
          ->setTemplateFile($templateName ? $templateName : $this
          ->getTemplateName());
      }
      if ($e
        ->getTemplateLine()) {
        throw $e;
      }
      if (!$line) {
        $e
          ->guess();
      }
      else {
        $e
          ->setTemplateLine($line);
      }
      throw $e;
    }
  }

  /**
   * Returns all blocks.
   *
   * This method is for internal use only and should never be called
   * directly.
   *
   * @return array An array of blocks
   *
   * @see hasBlock
   *
   * @internal
   */
  public function getBlocks() {
    return $this->blocks;
  }

  /**
   * Returns the template source code.
   *
   * @return string|null The template source code or null if it is not available
   */
  public function getSource() {
    $reflector = new ReflectionClass($this);
    $file = $reflector
      ->getFileName();
    if (!file_exists($file)) {
      return;
    }
    $source = file($file, FILE_IGNORE_NEW_LINES);
    array_splice($source, 0, $reflector
      ->getEndLine());
    $i = 0;
    while (isset($source[$i]) && '/* */' === substr_replace($source[$i], '', 3, -2)) {
      $source[$i] = str_replace('*//* ', '*/', substr($source[$i], 3, -2));
      ++$i;
    }
    array_splice($source, $i);
    return implode("\n", $source);
  }

  /**
   * {@inheritdoc}
   */
  public function display(array $context, array $blocks = array()) {
    $this
      ->displayWithErrorHandling($this->env
      ->mergeGlobals($context), array_merge($this->blocks, $blocks));
  }

  /**
   * {@inheritdoc}
   */
  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());
      }

      // 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, $this
        ->getTemplateName(), $e);
    }
  }

  /**
   * 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
   */
  protected abstract function doDisplay(array $context, array $blocks = array());

  /**
   * Returns a variable from the context.
   *
   * 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 bool   $ignoreStrictCheck Whether to ignore the strict variable check or not
   *
   * @return mixed The content of the context variable
   *
   * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
   *
   * @internal
   */
  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];
  }

  /**
   * Returns the attribute value for a given array/object.
   *
   * @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
   *
   * @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
   */
  protected function getAttribute($object, $item, array $arguments = array(), $type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false) {

    // array
    if (self::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 (self::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 (self::ARRAY_CALL === $type) {
          if (null === $object) {
            $message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
          }
          else {
            $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
          }
        }
        elseif (null === $object) {
          $message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
        }
        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;
      }
      if (null === $object) {
        $message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
      }
      else {
        $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
      }
      throw new Twig_Error_Runtime($message, -1, $this
        ->getTemplateName());
    }

    // object property
    if (self::METHOD_CALL !== $type && !$object instanceof self) {

      // Twig_Template does not have public properties, and we don't want to allow access to internal ones
      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);

    // object method
    if (!isset(self::$cache[$class]['methods'])) {

      // get_class_methods returns all methods accessible in the scope, but we only want public ones to be accessible in templates
      if ($object instanceof self) {
        $ref = new ReflectionClass($class);
        $methods = array();
        foreach ($ref
          ->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) {
          $methodName = strtolower($refMethod->name);

          // Accessing the environment from templates is forbidden to prevent untrusted changes to the environment
          if ('getenvironment' !== $methodName) {
            $methods[$methodName] = true;
          }
        }
        self::$cache[$class]['methods'] = $methods;
      }
      else {
        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);
    }

    // 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;
    }

    // useful when calling a template method from a template
    // this is not supported but unfortunately heavily used in the Symfony profiler
    if ($object instanceof Twig_TemplateInterface) {
      return $ret === '' ? '' : new Twig_Markup($ret, $this->env
        ->getCharset());
    }
    return $ret;
  }

}

Classes

Namesort descending Description
Twig_Template Default base class for compiled templates.