Twig-1.3.0/lib/Twig/Extension/Core.php
changeset 4 9a001a04b634
equal deleted inserted replaced
3:6d109e3804ac 4:9a001a04b634
       
     1 <?php
       
     2 
       
     3 if (!defined('ENT_SUBSTITUTE')) {
       
     4     define('ENT_SUBSTITUTE', 8);
       
     5 }
       
     6 
       
     7 /*
       
     8  * This file is part of Twig.
       
     9  *
       
    10  * (c) 2009 Fabien Potencier
       
    11  *
       
    12  * For the full copyright and license information, please view the LICENSE
       
    13  * file that was distributed with this source code.
       
    14  */
       
    15 class Twig_Extension_Core extends Twig_Extension
       
    16 {
       
    17     /**
       
    18      * Returns the token parser instance to add to the existing list.
       
    19      *
       
    20      * @return array An array of Twig_TokenParser instances
       
    21      */
       
    22     public function getTokenParsers()
       
    23     {
       
    24         return array(
       
    25             new Twig_TokenParser_For(),
       
    26             new Twig_TokenParser_If(),
       
    27             new Twig_TokenParser_Extends(),
       
    28             new Twig_TokenParser_Include(),
       
    29             new Twig_TokenParser_Block(),
       
    30             new Twig_TokenParser_Use(),
       
    31             new Twig_TokenParser_Filter(),
       
    32             new Twig_TokenParser_Macro(),
       
    33             new Twig_TokenParser_Import(),
       
    34             new Twig_TokenParser_From(),
       
    35             new Twig_TokenParser_Set(),
       
    36             new Twig_TokenParser_Spaceless(),
       
    37         );
       
    38     }
       
    39 
       
    40     /**
       
    41      * Returns a list of filters to add to the existing list.
       
    42      *
       
    43      * @return array An array of filters
       
    44      */
       
    45     public function getFilters()
       
    46     {
       
    47         $filters = array(
       
    48             // formatting filters
       
    49             'date'    => new Twig_Filter_Function('twig_date_format_filter'),
       
    50             'format'  => new Twig_Filter_Function('sprintf'),
       
    51             'replace' => new Twig_Filter_Function('twig_strtr'),
       
    52 
       
    53             // encoding
       
    54             'url_encode'  => new Twig_Filter_Function('twig_urlencode_filter'),
       
    55             'json_encode' => new Twig_Filter_Function('twig_jsonencode_filter'),
       
    56 
       
    57             // string filters
       
    58             'title'      => new Twig_Filter_Function('twig_title_string_filter', array('needs_environment' => true)),
       
    59             'capitalize' => new Twig_Filter_Function('twig_capitalize_string_filter', array('needs_environment' => true)),
       
    60             'upper'      => new Twig_Filter_Function('strtoupper'),
       
    61             'lower'      => new Twig_Filter_Function('strtolower'),
       
    62             'striptags'  => new Twig_Filter_Function('strip_tags'),
       
    63 
       
    64             // array helpers
       
    65             'join'    => new Twig_Filter_Function('twig_join_filter'),
       
    66             'reverse' => new Twig_Filter_Function('twig_reverse_filter'),
       
    67             'length'  => new Twig_Filter_Function('twig_length_filter', array('needs_environment' => true)),
       
    68             'sort'    => new Twig_Filter_Function('twig_sort_filter'),
       
    69             'merge'   => new Twig_Filter_Function('twig_array_merge'),
       
    70 
       
    71             // iteration and runtime
       
    72             'default' => new Twig_Filter_Function('twig_default_filter'),
       
    73             'keys'    => new Twig_Filter_Function('twig_get_array_keys_filter'),
       
    74 
       
    75             // escaping
       
    76             'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
       
    77             'e'      => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
       
    78         );
       
    79 
       
    80         if (function_exists('mb_get_info')) {
       
    81             $filters['upper'] = new Twig_Filter_Function('twig_upper_filter', array('needs_environment' => true));
       
    82             $filters['lower'] = new Twig_Filter_Function('twig_lower_filter', array('needs_environment' => true));
       
    83         }
       
    84 
       
    85         return $filters;
       
    86     }
       
    87 
       
    88     /**
       
    89      * Returns a list of global functions to add to the existing list.
       
    90      *
       
    91      * @return array An array of global functions
       
    92      */
       
    93     public function getFunctions()
       
    94     {
       
    95         return array(
       
    96             'range'    => new Twig_Function_Function('range'),
       
    97             'constant' => new Twig_Function_Function('constant'),
       
    98             'cycle'    => new Twig_Function_Function('twig_cycle'),
       
    99         );
       
   100     }
       
   101 
       
   102     /**
       
   103      * Returns a list of filters to add to the existing list.
       
   104      *
       
   105      * @return array An array of filters
       
   106      */
       
   107     public function getTests()
       
   108     {
       
   109         return array(
       
   110             'even'        => new Twig_Test_Function('twig_test_even'),
       
   111             'odd'         => new Twig_Test_Function('twig_test_odd'),
       
   112             'defined'     => new Twig_Test_Function('twig_test_defined'),
       
   113             'sameas'      => new Twig_Test_Function('twig_test_sameas'),
       
   114             'none'        => new Twig_Test_Function('twig_test_none'),
       
   115             'null'        => new Twig_Test_Function('twig_test_none'),
       
   116             'divisibleby' => new Twig_Test_Function('twig_test_divisibleby'),
       
   117             'constant'    => new Twig_Test_Function('twig_test_constant'),
       
   118             'empty'       => new Twig_Test_Function('twig_test_empty'),
       
   119         );
       
   120     }
       
   121 
       
   122     /**
       
   123      * Returns a list of operators to add to the existing list.
       
   124      *
       
   125      * @return array An array of operators
       
   126      */
       
   127     public function getOperators()
       
   128     {
       
   129         return array(
       
   130             array(
       
   131                 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
       
   132                 '-'   => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Neg'),
       
   133                 '+'   => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Pos'),
       
   134             ),
       
   135             array(
       
   136                 'b-and'  => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   137                 'b-xor'  => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   138                 'b-or'   => array('precedence' => 5, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   139                 'or'     => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   140                 'and'    => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   141                 '=='     => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   142                 '!='     => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   143                 '<'      => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   144                 '>'      => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   145                 '>='     => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   146                 '<='     => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   147                 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   148                 'in'     => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   149                 '+'      => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   150                 '-'      => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   151                 '~'      => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   152                 '*'      => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   153                 '/'      => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   154                 '//'     => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   155                 '%'      => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   156                 'is'     => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   157                 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   158                 '..'     => array('precedence' => 110, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
       
   159                 '**'     => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
       
   160             ),
       
   161         );
       
   162     }
       
   163 
       
   164     public function parseNotTestExpression(Twig_Parser $parser, $node)
       
   165     {
       
   166         return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
       
   167     }
       
   168 
       
   169     public function parseTestExpression(Twig_Parser $parser, $node)
       
   170     {
       
   171         $stream = $parser->getStream();
       
   172         $name = $stream->expect(Twig_Token::NAME_TYPE);
       
   173         $arguments = null;
       
   174         if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
       
   175             $arguments = $parser->getExpressionParser()->parseArguments();
       
   176         }
       
   177 
       
   178         return new Twig_Node_Expression_Test($node, $name->getValue(), $arguments, $parser->getCurrentToken()->getLine());
       
   179     }
       
   180 
       
   181     /**
       
   182      * Returns the name of the extension.
       
   183      *
       
   184      * @return string The extension name
       
   185      */
       
   186     public function getName()
       
   187     {
       
   188         return 'core';
       
   189     }
       
   190 }
       
   191 
       
   192 /**
       
   193  * Cycles over a value.
       
   194  *
       
   195  * @param ArrayAccess|array $values An array or an ArrayAccess instance
       
   196  * @param integer           $i      The cycle value
       
   197  *
       
   198  * @return string The next value in the cycle
       
   199  */
       
   200 function twig_cycle($values, $i)
       
   201 {
       
   202     if (!is_array($values) && !$values instanceof ArrayAccess) {
       
   203         return $values;
       
   204     }
       
   205 
       
   206     return $values[$i % count($values)];
       
   207 }
       
   208 
       
   209 /**
       
   210  * Converts a date to the given format.
       
   211  *
       
   212  * <pre>
       
   213  *   {{ post.published_at|date("m/d/Y") }}
       
   214  * </pre>
       
   215  *
       
   216  * @param DateTime|string     $date     A date
       
   217  * @param string              $format   A format
       
   218  * @param DateTimeZone|string $timezone A timezone
       
   219  *
       
   220  * @return string The formatter date
       
   221  */
       
   222 function twig_date_format_filter($date, $format = 'F j, Y H:i', $timezone = null)
       
   223 {
       
   224     if (!$date instanceof DateTime && !$date instanceof DateInterval) {
       
   225         if (ctype_digit((string) $date)) {
       
   226             $date = new DateTime('@'.$date);
       
   227             $date->setTimezone(new DateTimeZone(date_default_timezone_get()));
       
   228         } else {
       
   229             $date = new DateTime($date);
       
   230         }
       
   231     }
       
   232 
       
   233     if (null !== $timezone) {
       
   234         if (!$timezone instanceof DateTimeZone) {
       
   235             $timezone = new DateTimeZone($timezone);
       
   236         }
       
   237 
       
   238         $date->setTimezone($timezone);
       
   239     }
       
   240 
       
   241     return $date->format($format);
       
   242 }
       
   243 
       
   244 /**
       
   245  * URL encodes a string.
       
   246  *
       
   247  * @param string $url A URL
       
   248  * @param bool   $raw true to use rawurlencode() instead of urlencode
       
   249  *
       
   250  * @return string The URL encoded value
       
   251  */
       
   252 function twig_urlencode_filter($url, $raw = false)
       
   253 {
       
   254     if ($raw) {
       
   255         return rawurlencode($url);
       
   256     }
       
   257 
       
   258     return urlencode($url);
       
   259 }
       
   260 
       
   261 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
       
   262     /**
       
   263      * JSON encodes a PHP variable.
       
   264      *
       
   265      * @param mixed   $value   The value to encode.
       
   266      * @param integer $options Not used on PHP 5.2.x
       
   267      *
       
   268      * @return mixed The JSON encoded value
       
   269      */
       
   270     function twig_jsonencode_filter($value, $options = 0)
       
   271     {
       
   272         if ($value instanceof Twig_Markup) {
       
   273             $value = (string) $value;
       
   274         } elseif (is_array($value)) {
       
   275             array_walk_recursive($value, '_twig_markup2string');
       
   276         }
       
   277 
       
   278         return json_encode($value);
       
   279     }
       
   280 } else {
       
   281     /**
       
   282      * JSON encodes a PHP variable.
       
   283      *
       
   284      * @param mixed   $value   The value to encode.
       
   285      * @param integer $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT
       
   286      *
       
   287      * @return mixed The JSON encoded value
       
   288      */
       
   289     function twig_jsonencode_filter($value, $options = 0)
       
   290     {
       
   291         if ($value instanceof Twig_Markup) {
       
   292             $value = (string) $value;
       
   293         } elseif (is_array($value)) {
       
   294             array_walk_recursive($value, '_twig_markup2string');
       
   295         }
       
   296 
       
   297         return json_encode($value, $options);
       
   298     }
       
   299 }
       
   300 
       
   301 function _twig_markup2string(&$value)
       
   302 {
       
   303     if ($value instanceof Twig_Markup) {
       
   304         $value = (string) $value;
       
   305     }
       
   306 }
       
   307 
       
   308 /**
       
   309  * Merges an array with another one.
       
   310  *
       
   311  * <pre>
       
   312  *  {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
       
   313  *
       
   314  *  {% set items = items|merge({ 'peugeot': 'car' }) %}
       
   315  *
       
   316  *  {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
       
   317  * </pre>
       
   318  *
       
   319  * @param array $arr1 An array
       
   320  * @param array $arr2 An array
       
   321  *
       
   322  * @return array The merged array
       
   323  */
       
   324 function twig_array_merge($arr1, $arr2)
       
   325 {
       
   326     if (!is_array($arr1) || !is_array($arr2)) {
       
   327         throw new Twig_Error_Runtime('The merge filter only work with arrays or hashes.');
       
   328     }
       
   329 
       
   330     return array_merge($arr1, $arr2);
       
   331 }
       
   332 
       
   333 /**
       
   334  * Joins the values to a string.
       
   335  *
       
   336  * The separator between elements is an empty string per default, you can define it with the optional parameter.
       
   337  *
       
   338  * <pre>
       
   339  *  {{ [1, 2, 3]|join('|') }}
       
   340  *  {# returns 1|2|3 #}
       
   341  *
       
   342  *  {{ [1, 2, 3]|join }}
       
   343  *  {# returns 123 #}
       
   344  * </pre>
       
   345  *
       
   346  * @param array  $value An array
       
   347  * @param string $glue  The separator
       
   348  *
       
   349  * @return string The concatenated string
       
   350  */
       
   351 function twig_join_filter($value, $glue = '')
       
   352 {
       
   353     return implode($glue, (array) $value);
       
   354 }
       
   355 
       
   356 /**
       
   357  * Returns the value or the default value when it is undefined or empty.
       
   358  *
       
   359  * <pre>
       
   360  *
       
   361  *  {{ var.foo|default('foo item on var is not defined') }}
       
   362  *
       
   363  * </pre>
       
   364  *
       
   365  * @param mixed $value   A value
       
   366  * @param mixed $default The default value
       
   367  *
       
   368  * @param mixed The value or the default value;
       
   369  */
       
   370 function twig_default_filter($value, $default = '')
       
   371 {
       
   372     if (twig_test_empty($value)) {
       
   373         return $default;
       
   374     } else {
       
   375         return $value;
       
   376     }
       
   377 }
       
   378 
       
   379 /**
       
   380  * Returns the keys for the given array.
       
   381  *
       
   382  * It is useful when you want to iterate over the keys of an array:
       
   383  *
       
   384  * <pre>
       
   385  *  {% for key in array|keys %}
       
   386  *      {# ... #}
       
   387  *  {% endfor %}
       
   388  * </pre>
       
   389  *
       
   390  * @param array $array An array
       
   391  *
       
   392  * @return array The keys
       
   393  */
       
   394 function twig_get_array_keys_filter($array)
       
   395 {
       
   396     if (is_object($array) && $array instanceof Traversable) {
       
   397         return array_keys(iterator_to_array($array));
       
   398     }
       
   399 
       
   400     if (!is_array($array)) {
       
   401         return array();
       
   402     }
       
   403 
       
   404     return array_keys($array);
       
   405 }
       
   406 
       
   407 /**
       
   408  * Reverses an array.
       
   409  *
       
   410  * @param array|Traversable $array An array or a Traversable instance
       
   411  *
       
   412  * return array The array reversed
       
   413  */
       
   414 function twig_reverse_filter($array)
       
   415 {
       
   416     if (is_object($array) && $array instanceof Traversable) {
       
   417         return array_reverse(iterator_to_array($array));
       
   418     }
       
   419 
       
   420     if (!is_array($array)) {
       
   421         return array();
       
   422     }
       
   423 
       
   424     return array_reverse($array);
       
   425 }
       
   426 
       
   427 /**
       
   428  * Sorts an array.
       
   429  *
       
   430  * @param array $array An array
       
   431  */
       
   432 function twig_sort_filter($array)
       
   433 {
       
   434     asort($array);
       
   435 
       
   436     return $array;
       
   437 }
       
   438 
       
   439 /* used internally */
       
   440 function twig_in_filter($value, $compare)
       
   441 {
       
   442     if (is_array($compare)) {
       
   443         return in_array($value, $compare);
       
   444     } elseif (is_string($compare)) {
       
   445         return false !== strpos($compare, (string) $value);
       
   446     } elseif (is_object($compare) && $compare instanceof Traversable) {
       
   447         return in_array($value, iterator_to_array($compare, false));
       
   448     }
       
   449 
       
   450     return false;
       
   451 }
       
   452 
       
   453 /**
       
   454  * Replaces placeholders in a string.
       
   455  *
       
   456  * <pre>
       
   457  *  {{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }}
       
   458  * </pre>
       
   459  *
       
   460  * @param string $pattern      A string
       
   461  * @param string $replacements The values for the placeholders
       
   462  *
       
   463  * @return string The string where the placeholders have been replaced
       
   464  */
       
   465 function twig_strtr($pattern, $replacements)
       
   466 {
       
   467     return str_replace(array_keys($replacements), array_values($replacements), $pattern);
       
   468 }
       
   469 
       
   470 /**
       
   471  * Escapes a string.
       
   472  *
       
   473  * @param Twig_Environment $env        A Twig_Environment instance
       
   474  * @param string           $string     The value to be escaped
       
   475  * @param string           $type       The escaping strategy
       
   476  * @param string           $charset    The charset
       
   477  * @param Boolean          $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
       
   478  */
       
   479 function twig_escape_filter(Twig_Environment $env, $string, $type = 'html', $charset = null, $autoescape = false)
       
   480 {
       
   481     if ($autoescape && is_object($string) && $string instanceof Twig_Markup) {
       
   482         return $string;
       
   483     }
       
   484 
       
   485     if (!is_string($string) && !(is_object($string) && method_exists($string, '__toString'))) {
       
   486         return $string;
       
   487     }
       
   488 
       
   489     if (null === $charset) {
       
   490         $charset = $env->getCharset();
       
   491     }
       
   492 
       
   493     switch ($type) {
       
   494         case 'js':
       
   495             // escape all non-alphanumeric characters
       
   496             // into their \xHH or \uHHHH representations
       
   497             if ('UTF-8' != $charset) {
       
   498                 $string = _twig_convert_encoding($string, 'UTF-8', $charset);
       
   499             }
       
   500 
       
   501             if (null === $string = preg_replace_callback('#[^\p{L}\p{N} ]#u', '_twig_escape_js_callback', $string)) {
       
   502                 throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
       
   503             }
       
   504 
       
   505             if ('UTF-8' != $charset) {
       
   506                 $string = _twig_convert_encoding($string, $charset, 'UTF-8');
       
   507             }
       
   508 
       
   509             return $string;
       
   510 
       
   511         case 'html':
       
   512             return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
       
   513 
       
   514         default:
       
   515             throw new Twig_Error_Runtime(sprintf('Invalid escape type "%s".', $type));
       
   516     }
       
   517 }
       
   518 
       
   519 /* used internally */
       
   520 function twig_escape_filter_is_safe(Twig_Node $filterArgs)
       
   521 {
       
   522     foreach ($filterArgs as $arg) {
       
   523         if ($arg instanceof Twig_Node_Expression_Constant) {
       
   524             return array($arg->getAttribute('value'));
       
   525         } else {
       
   526             return array();
       
   527         }
       
   528 
       
   529         break;
       
   530     }
       
   531 
       
   532     return array('html');
       
   533 }
       
   534 
       
   535 if (function_exists('iconv')) {
       
   536     function _twig_convert_encoding($string, $to, $from)
       
   537     {
       
   538         return iconv($from, $to, $string);
       
   539     }
       
   540 } elseif (function_exists('mb_convert_encoding')) {
       
   541     function _twig_convert_encoding($string, $to, $from)
       
   542     {
       
   543         return mb_convert_encoding($string, $to, $from);
       
   544     }
       
   545 } else {
       
   546     function _twig_convert_encoding($string, $to, $from)
       
   547     {
       
   548         throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
       
   549     }
       
   550 }
       
   551 
       
   552 function _twig_escape_js_callback($matches)
       
   553 {
       
   554     $char = $matches[0];
       
   555 
       
   556     // \xHH
       
   557     if (!isset($char[1])) {
       
   558         return '\\x'.substr('00'.bin2hex($char), -2);
       
   559     }
       
   560 
       
   561     // \uHHHH
       
   562     $char = _twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
       
   563 
       
   564     return '\\u'.substr('0000'.bin2hex($char), -4);
       
   565 }
       
   566 
       
   567 // add multibyte extensions if possible
       
   568 if (function_exists('mb_get_info')) {
       
   569     /**
       
   570      * Returns the length of a PHP variable.
       
   571      *
       
   572      * @param Twig_Environment $env   A Twig_Environment instance
       
   573      * @param mixed            $thing A PHP variable
       
   574      *
       
   575      * @return integer The length of the value
       
   576      */
       
   577     function twig_length_filter(Twig_Environment $env, $thing)
       
   578     {
       
   579         return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
       
   580     }
       
   581 
       
   582     /**
       
   583      * Converts a string to uppercase.
       
   584      *
       
   585      * @param Twig_Environment $env    A Twig_Environment instance
       
   586      * @param string           $string A string
       
   587      *
       
   588      * @return string The uppercased string
       
   589      */
       
   590     function twig_upper_filter(Twig_Environment $env, $string)
       
   591     {
       
   592         if (null !== ($charset = $env->getCharset())) {
       
   593             return mb_strtoupper($string, $charset);
       
   594         }
       
   595 
       
   596         return strtoupper($string);
       
   597     }
       
   598 
       
   599     /**
       
   600      * Converts a string to lowercase.
       
   601      *
       
   602      * @param Twig_Environment $env    A Twig_Environment instance
       
   603      * @param string           $string A string
       
   604      *
       
   605      * @return string The lowercased string
       
   606      */
       
   607     function twig_lower_filter(Twig_Environment $env, $string)
       
   608     {
       
   609         if (null !== ($charset = $env->getCharset())) {
       
   610             return mb_strtolower($string, $charset);
       
   611         }
       
   612 
       
   613         return strtolower($string);
       
   614     }
       
   615 
       
   616     /**
       
   617      * Returns a titlecased string.
       
   618      *
       
   619      * @param Twig_Environment $env    A Twig_Environment instance
       
   620      * @param string           $string A string
       
   621      *
       
   622      * @return string The titlecased string
       
   623      */
       
   624     function twig_title_string_filter(Twig_Environment $env, $string)
       
   625     {
       
   626         if (null !== ($charset = $env->getCharset())) {
       
   627             return mb_convert_case($string, MB_CASE_TITLE, $charset);
       
   628         }
       
   629 
       
   630         return ucwords(strtolower($string));
       
   631     }
       
   632 
       
   633     /**
       
   634      * Returns a capitalized string.
       
   635      *
       
   636      * @param Twig_Environment $env    A Twig_Environment instance
       
   637      * @param string           $string A string
       
   638      *
       
   639      * @return string The capitalized string
       
   640      */
       
   641     function twig_capitalize_string_filter(Twig_Environment $env, $string)
       
   642     {
       
   643         if (null !== ($charset = $env->getCharset())) {
       
   644             return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).
       
   645                          mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset);
       
   646         }
       
   647 
       
   648         return ucfirst(strtolower($string));
       
   649     }
       
   650 }
       
   651 // and byte fallback
       
   652 else
       
   653 {
       
   654     /**
       
   655      * Returns the length of a PHP variable.
       
   656      *
       
   657      * @param Twig_Environment $env   A Twig_Environment instance
       
   658      * @param mixed            $thing A PHP variable
       
   659      *
       
   660      * @return integer The length of the value
       
   661      */
       
   662     function twig_length_filter(Twig_Environment $env, $thing)
       
   663     {
       
   664         return is_scalar($thing) ? strlen($thing) : count($thing);
       
   665     }
       
   666 
       
   667     /**
       
   668      * Returns a titlecased string.
       
   669      *
       
   670      * @param Twig_Environment $env    A Twig_Environment instance
       
   671      * @param string           $string A string
       
   672      *
       
   673      * @return string The titlecased string
       
   674      */
       
   675     function twig_title_string_filter(Twig_Environment $env, $string)
       
   676     {
       
   677         return ucwords(strtolower($string));
       
   678     }
       
   679 
       
   680     /**
       
   681      * Returns a capitalized string.
       
   682      *
       
   683      * @param Twig_Environment $env    A Twig_Environment instance
       
   684      * @param string           $string A string
       
   685      *
       
   686      * @return string The capitalized string
       
   687      */
       
   688     function twig_capitalize_string_filter(Twig_Environment $env, $string)
       
   689     {
       
   690         return ucfirst(strtolower($string));
       
   691     }
       
   692 }
       
   693 
       
   694 /* used internally */
       
   695 function twig_ensure_traversable($seq)
       
   696 {
       
   697     if (is_array($seq) || (is_object($seq) && $seq instanceof Traversable)) {
       
   698         return $seq;
       
   699     } else {
       
   700         return array();
       
   701     }
       
   702 }
       
   703 
       
   704 /**
       
   705  * Checks that a variable points to the same memory address than another one.
       
   706  *
       
   707  * <pre>
       
   708  * {% if foo.attribute is sameas(false) %}
       
   709  *    the foo attribute really is the ``false`` PHP value
       
   710  * {% endif %}
       
   711  * </pre>
       
   712  *
       
   713  * @param mixed $value A PHP variable
       
   714  * @param mixed $test  The PHP variable to test against
       
   715  *
       
   716  * @return Boolean true if the values are the same, false otherwise
       
   717  */
       
   718 function twig_test_sameas($value, $test)
       
   719 {
       
   720     return $value === $test;
       
   721 }
       
   722 
       
   723 /**
       
   724  * Checks that a variable is null.
       
   725  *
       
   726  * <pre>
       
   727  *  {{ var is none }}
       
   728  * </pre>
       
   729  *
       
   730  * @param mixed $value a PHP variable.
       
   731  *
       
   732  * @return Boolean true if the value is null, false otherwise
       
   733  */
       
   734 function twig_test_none($value)
       
   735 {
       
   736     return null === $value;
       
   737 }
       
   738 
       
   739 /**
       
   740  * Checks if a variable is divisible by a number.
       
   741  *
       
   742  * <pre>
       
   743  *  {% if loop.index is divisibleby(3) %}
       
   744  * </pre>
       
   745  *
       
   746  * @param integer $value A PHP value
       
   747  * @param integer $num   A number
       
   748  *
       
   749  * @return Boolean true if the value is divisible by the number, false otherwise
       
   750  */
       
   751 function twig_test_divisibleby($value, $num)
       
   752 {
       
   753     return 0 == $value % $num;
       
   754 }
       
   755 
       
   756 /**
       
   757  * Checks if a number is even.
       
   758  *
       
   759  * <pre>
       
   760  *  {{ var is even }}
       
   761  * </pre>
       
   762  *
       
   763  * @param integer $value An integer
       
   764  *
       
   765  * @return Boolean true if the value is even, false otherwise
       
   766  */
       
   767 function twig_test_even($value)
       
   768 {
       
   769     return $value % 2 == 0;
       
   770 }
       
   771 
       
   772 /**
       
   773  * Checks if a number is odd.
       
   774  *
       
   775  * <pre>
       
   776  *  {{ var is odd }}
       
   777  * </pre>
       
   778  *
       
   779  * @param integer $value An integer
       
   780  *
       
   781  * @return Boolean true if the value is odd, false otherwise
       
   782  */
       
   783 function twig_test_odd($value)
       
   784 {
       
   785     return $value % 2 == 1;
       
   786 }
       
   787 
       
   788 /**
       
   789  * Checks if a variable is the exact same value as a constant.
       
   790  *
       
   791  * <pre>
       
   792  *  {% if post.status is constant('Post::PUBLISHED') %}
       
   793  *    the status attribute is exactly the same as Post::PUBLISHED
       
   794  *  {% endif %}
       
   795  * </pre>
       
   796  *
       
   797  * @param mixed $value    A PHP value
       
   798  * @param mixed $constant The constant to test against
       
   799  *
       
   800  * @return Boolean true if the value is the same as the constant, false otherwise
       
   801  */
       
   802 function twig_test_constant($value, $constant)
       
   803 {
       
   804     return constant($constant) === $value;
       
   805 }
       
   806 
       
   807 /**
       
   808  * Checks if a variable is defined in the current context.
       
   809  *
       
   810  * <pre>
       
   811  * {# defined works with variable names #}
       
   812  * {% if foo is defined %}
       
   813  *     {# ... #}
       
   814  * {% endif %}
       
   815  * </pre>
       
   816  *
       
   817  * @param mixed $name    A PHP variable
       
   818  * @param array $context The current context
       
   819  *
       
   820  * @return Boolean true if the value is defined, false otherwise
       
   821  */
       
   822 function twig_test_defined($name, $context)
       
   823 {
       
   824     return array_key_exists($name, $context);
       
   825 }
       
   826 
       
   827 /**
       
   828  * Checks if a variable is empty.
       
   829  *
       
   830  * <pre>
       
   831  * {# evaluates to true if the foo variable is null, false, or the empty string #}
       
   832  * {% if foo is empty %}
       
   833  *     {# ... #}
       
   834  * {% endif %}
       
   835  * </pre>
       
   836  *
       
   837  * @param mixed $value A PHP variable
       
   838  *
       
   839  * @return Boolean true if the value is empty, false otherwise
       
   840  */
       
   841 function twig_test_empty($value)
       
   842 {
       
   843     if ($value instanceof Countable) {
       
   844         return 0 == count($value);
       
   845     }
       
   846     return false === $value || (empty($value) && '0' != $value);
       
   847 }