2011-10-05 06:22:53 +02:00
< ? php
if ( ! defined ( 'ENT_SUBSTITUTE' )) {
define ( 'ENT_SUBSTITUTE' , 8 );
}
/*
* This file is part of Twig .
*
2018-05-10 12:24:53 +02:00
* ( c ) Fabien Potencier
2011-10-05 06:22:53 +02:00
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
2018-05-10 12:24:53 +02:00
/**
* @ final
*/
2011-10-05 06:22:53 +02:00
class Twig_Extension_Core extends Twig_Extension
{
2013-08-01 21:20:12 +02:00
protected $dateFormats = array ( 'F j, Y H:i' , '%d days' );
protected $numberFormat = array ( 0 , '.' , ',' );
protected $timezone = null ;
2018-05-10 12:24:53 +02:00
protected $escapers = array ();
/**
* Defines a new escaper to be used via the escape filter .
*
* @ param string $strategy The strategy name that should be used as a strategy in the escape call
* @ param callable $callable A valid PHP callable
*/
public function setEscaper ( $strategy , $callable )
{
$this -> escapers [ $strategy ] = $callable ;
}
/**
* Gets all defined escapers .
*
* @ return array An array of escapers
*/
public function getEscapers ()
{
return $this -> escapers ;
}
2013-08-01 21:20:12 +02:00
/**
* Sets the default format to be used by the date filter .
*
* @ param string $format The default date format string
* @ param string $dateIntervalFormat The default date interval format string
*/
public function setDateFormat ( $format = null , $dateIntervalFormat = null )
{
if ( null !== $format ) {
$this -> dateFormats [ 0 ] = $format ;
}
if ( null !== $dateIntervalFormat ) {
$this -> dateFormats [ 1 ] = $dateIntervalFormat ;
}
}
/**
* Gets the default format to be used by the date filter .
*
* @ return array The default date format string and the default date interval format string
*/
public function getDateFormat ()
{
return $this -> dateFormats ;
}
/**
* Sets the default timezone to be used by the date filter .
*
* @ param DateTimeZone | string $timezone The default timezone string or a DateTimeZone object
*/
public function setTimezone ( $timezone )
{
$this -> timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone ( $timezone );
}
/**
* Gets the default timezone to be used by the date filter .
*
* @ return DateTimeZone The default timezone currently in use
*/
public function getTimezone ()
{
if ( null === $this -> timezone ) {
$this -> timezone = new DateTimeZone ( date_default_timezone_get ());
}
return $this -> timezone ;
}
/**
* Sets the default format to be used by the number_format filter .
*
2018-05-10 12:24:53 +02:00
* @ param int $decimal the number of decimal places to use
* @ param string $decimalPoint the character ( s ) to use for the decimal point
* @ param string $thousandSep the character ( s ) to use for the thousands separator
2013-08-01 21:20:12 +02:00
*/
public function setNumberFormat ( $decimal , $decimalPoint , $thousandSep )
{
$this -> numberFormat = array ( $decimal , $decimalPoint , $thousandSep );
}
/**
* Get the default format used by the number_format filter .
*
* @ return array The arguments for number_format ()
*/
public function getNumberFormat ()
{
return $this -> numberFormat ;
}
2011-10-05 06:22:53 +02:00
public function getTokenParsers ()
{
return array (
new Twig_TokenParser_For (),
new Twig_TokenParser_If (),
new Twig_TokenParser_Extends (),
new Twig_TokenParser_Include (),
new Twig_TokenParser_Block (),
new Twig_TokenParser_Use (),
new Twig_TokenParser_Filter (),
new Twig_TokenParser_Macro (),
new Twig_TokenParser_Import (),
new Twig_TokenParser_From (),
new Twig_TokenParser_Set (),
new Twig_TokenParser_Spaceless (),
2013-08-01 21:20:12 +02:00
new Twig_TokenParser_Flush (),
new Twig_TokenParser_Do (),
new Twig_TokenParser_Embed (),
2018-05-10 12:24:53 +02:00
new Twig_TokenParser_With (),
2011-10-05 06:22:53 +02:00
);
}
public function getFilters ()
{
$filters = array (
// formatting filters
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'date' , 'twig_date_format_filter' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'date_modify' , 'twig_date_modify_filter' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'format' , 'sprintf' ),
2018-05-10 12:24:53 +02:00
new Twig_SimpleFilter ( 'replace' , 'twig_replace_filter' ),
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'number_format' , 'twig_number_format_filter' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'abs' , 'abs' ),
2018-05-10 12:24:53 +02:00
new Twig_SimpleFilter ( 'round' , 'twig_round' ),
2011-10-05 06:22:53 +02:00
// encoding
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'url_encode' , 'twig_urlencode_filter' ),
new Twig_SimpleFilter ( 'json_encode' , 'twig_jsonencode_filter' ),
new Twig_SimpleFilter ( 'convert_encoding' , 'twig_convert_encoding' ),
2011-10-05 06:22:53 +02:00
// string filters
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'title' , 'twig_title_string_filter' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'capitalize' , 'twig_capitalize_string_filter' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'upper' , 'strtoupper' ),
new Twig_SimpleFilter ( 'lower' , 'strtolower' ),
new Twig_SimpleFilter ( 'striptags' , 'strip_tags' ),
2018-05-10 12:24:53 +02:00
new Twig_SimpleFilter ( 'trim' , 'twig_trim_filter' ),
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'nl2br' , 'nl2br' , array ( 'pre_escape' => 'html' , 'is_safe' => array ( 'html' ))),
2011-10-05 06:22:53 +02:00
// array helpers
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'join' , 'twig_join_filter' ),
2018-05-10 12:24:53 +02:00
new Twig_SimpleFilter ( 'split' , 'twig_split_filter' , array ( 'needs_environment' => true )),
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'sort' , 'twig_sort_filter' ),
new Twig_SimpleFilter ( 'merge' , 'twig_array_merge' ),
new Twig_SimpleFilter ( 'batch' , 'twig_array_batch' ),
// string/array filters
new Twig_SimpleFilter ( 'reverse' , 'twig_reverse_filter' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'length' , 'twig_length_filter' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'slice' , 'twig_slice' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'first' , 'twig_first' , array ( 'needs_environment' => true )),
new Twig_SimpleFilter ( 'last' , 'twig_last' , array ( 'needs_environment' => true )),
2011-10-05 06:22:53 +02:00
// iteration and runtime
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'default' , '_twig_default_filter' , array ( 'node_class' => 'Twig_Node_Expression_Filter_Default' )),
new Twig_SimpleFilter ( 'keys' , 'twig_get_array_keys_filter' ),
2011-10-05 06:22:53 +02:00
// escaping
2013-08-01 21:20:12 +02:00
new Twig_SimpleFilter ( 'escape' , 'twig_escape_filter' , array ( 'needs_environment' => true , 'is_safe_callback' => 'twig_escape_filter_is_safe' )),
new Twig_SimpleFilter ( 'e' , 'twig_escape_filter' , array ( 'needs_environment' => true , 'is_safe_callback' => 'twig_escape_filter_is_safe' )),
2011-10-05 06:22:53 +02:00
);
if ( function_exists ( 'mb_get_info' )) {
2013-08-01 21:20:12 +02:00
$filters [] = new Twig_SimpleFilter ( 'upper' , 'twig_upper_filter' , array ( 'needs_environment' => true ));
$filters [] = new Twig_SimpleFilter ( 'lower' , 'twig_lower_filter' , array ( 'needs_environment' => true ));
2011-10-05 06:22:53 +02:00
}
return $filters ;
}
public function getFunctions ()
{
return array (
2018-05-10 12:24:53 +02:00
new Twig_SimpleFunction ( 'max' , 'max' ),
new Twig_SimpleFunction ( 'min' , 'min' ),
2013-08-01 21:20:12 +02:00
new Twig_SimpleFunction ( 'range' , 'range' ),
new Twig_SimpleFunction ( 'constant' , 'twig_constant' ),
new Twig_SimpleFunction ( 'cycle' , 'twig_cycle' ),
new Twig_SimpleFunction ( 'random' , 'twig_random' , array ( 'needs_environment' => true )),
new Twig_SimpleFunction ( 'date' , 'twig_date_converter' , array ( 'needs_environment' => true )),
new Twig_SimpleFunction ( 'include' , 'twig_include' , array ( 'needs_environment' => true , 'needs_context' => true , 'is_safe' => array ( 'all' ))),
2018-05-10 12:24:53 +02:00
new Twig_SimpleFunction ( 'source' , 'twig_source' , array ( 'needs_environment' => true , 'is_safe' => array ( 'all' ))),
2011-10-05 06:22:53 +02:00
);
}
public function getTests ()
{
return array (
2013-08-01 21:20:12 +02:00
new Twig_SimpleTest ( 'even' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Even' )),
new Twig_SimpleTest ( 'odd' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Odd' )),
new Twig_SimpleTest ( 'defined' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Defined' )),
2018-05-10 12:24:53 +02:00
new Twig_SimpleTest ( 'sameas' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Sameas' , 'deprecated' => '1.21' , 'alternative' => 'same as' )),
new Twig_SimpleTest ( 'same as' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Sameas' )),
2013-08-01 21:20:12 +02:00
new Twig_SimpleTest ( 'none' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Null' )),
new Twig_SimpleTest ( 'null' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Null' )),
2018-05-10 12:24:53 +02:00
new Twig_SimpleTest ( 'divisibleby' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Divisibleby' , 'deprecated' => '1.21' , 'alternative' => 'divisible by' )),
new Twig_SimpleTest ( 'divisible by' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Divisibleby' )),
2013-08-01 21:20:12 +02:00
new Twig_SimpleTest ( 'constant' , null , array ( 'node_class' => 'Twig_Node_Expression_Test_Constant' )),
new Twig_SimpleTest ( 'empty' , 'twig_test_empty' ),
new Twig_SimpleTest ( 'iterable' , 'twig_test_iterable' ),
2011-10-05 06:22:53 +02:00
);
}
public function getOperators ()
{
return array (
array (
'not' => array ( 'precedence' => 50 , 'class' => 'Twig_Node_Expression_Unary_Not' ),
2018-05-10 12:24:53 +02:00
'-' => array ( 'precedence' => 500 , 'class' => 'Twig_Node_Expression_Unary_Neg' ),
'+' => array ( 'precedence' => 500 , 'class' => 'Twig_Node_Expression_Unary_Pos' ),
2011-10-05 06:22:53 +02:00
),
array (
2018-05-10 12:24:53 +02:00
'or' => array ( 'precedence' => 10 , 'class' => 'Twig_Node_Expression_Binary_Or' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'and' => array ( 'precedence' => 15 , 'class' => 'Twig_Node_Expression_Binary_And' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'b-or' => array ( 'precedence' => 16 , 'class' => 'Twig_Node_Expression_Binary_BitwiseOr' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'b-xor' => array ( 'precedence' => 17 , 'class' => 'Twig_Node_Expression_Binary_BitwiseXor' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'b-and' => array ( 'precedence' => 18 , 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'==' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_Equal' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'!=' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_NotEqual' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'<' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_Less' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'>' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_Greater' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'>=' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_GreaterEqual' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'<=' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_LessEqual' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
2011-10-05 06:22:53 +02:00
'not in' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_NotIn' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
2018-05-10 12:24:53 +02:00
'in' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_In' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'matches' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_Matches' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'starts with' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_StartsWith' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'ends with' => array ( 'precedence' => 20 , 'class' => 'Twig_Node_Expression_Binary_EndsWith' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'..' => array ( 'precedence' => 25 , 'class' => 'Twig_Node_Expression_Binary_Range' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'+' => array ( 'precedence' => 30 , 'class' => 'Twig_Node_Expression_Binary_Add' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'-' => array ( 'precedence' => 30 , 'class' => 'Twig_Node_Expression_Binary_Sub' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'~' => array ( 'precedence' => 40 , 'class' => 'Twig_Node_Expression_Binary_Concat' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'*' => array ( 'precedence' => 60 , 'class' => 'Twig_Node_Expression_Binary_Mul' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'/' => array ( 'precedence' => 60 , 'class' => 'Twig_Node_Expression_Binary_Div' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'//' => array ( 'precedence' => 60 , 'class' => 'Twig_Node_Expression_Binary_FloorDiv' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'%' => array ( 'precedence' => 60 , 'class' => 'Twig_Node_Expression_Binary_Mod' , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'is' => array ( 'precedence' => 100 , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'is not' => array ( 'precedence' => 100 , 'associativity' => Twig_ExpressionParser :: OPERATOR_LEFT ),
'**' => array ( 'precedence' => 200 , 'class' => 'Twig_Node_Expression_Binary_Power' , 'associativity' => Twig_ExpressionParser :: OPERATOR_RIGHT ),
'??' => array ( 'precedence' => 300 , 'class' => 'Twig_Node_Expression_NullCoalesce' , 'associativity' => Twig_ExpressionParser :: OPERATOR_RIGHT ),
2011-10-05 06:22:53 +02:00
),
);
}
public function getName ()
{
return 'core' ;
}
}
/**
* Cycles over a value .
*
2018-05-10 12:24:53 +02:00
* @ param ArrayAccess | array $values
* @ param int $position The cycle position
2011-10-05 06:22:53 +02:00
*
* @ return string The next value in the cycle
*/
2013-08-01 21:20:12 +02:00
function twig_cycle ( $values , $position )
2011-10-05 06:22:53 +02:00
{
if ( ! is_array ( $values ) && ! $values instanceof ArrayAccess ) {
return $values ;
}
2013-08-01 21:20:12 +02:00
return $values [ $position % count ( $values )];
}
/**
* Returns a random value depending on the supplied parameter type :
* - a random item from a Traversable or array
* - a random character from a string
2018-05-10 12:24:53 +02:00
* - a random integer between 0 and the integer parameter .
2013-08-01 21:20:12 +02:00
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
* @ param Traversable | array | int | float | string $values The values to pick a random item from
2013-08-01 21:20:12 +02:00
*
2018-05-10 12:24:53 +02:00
* @ throws Twig_Error_Runtime when $values is an empty array ( does not apply to an empty string which is returned as is )
2013-08-01 21:20:12 +02:00
*
* @ return mixed A random value from the given sequence
*/
function twig_random ( Twig_Environment $env , $values = null )
{
if ( null === $values ) {
return mt_rand ();
}
if ( is_int ( $values ) || is_float ( $values )) {
return $values < 0 ? mt_rand ( $values , 0 ) : mt_rand ( 0 , $values );
}
2018-05-10 12:24:53 +02:00
if ( $values instanceof Traversable ) {
2013-08-01 21:20:12 +02:00
$values = iterator_to_array ( $values );
} elseif ( is_string ( $values )) {
if ( '' === $values ) {
return '' ;
}
if ( null !== $charset = $env -> getCharset ()) {
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$values = twig_convert_encoding ( $values , 'UTF-8' , $charset );
}
// unicode version of str_split()
// split at all positions, but not after the start and not before the end
$values = preg_split ( '/(?<!^)(?!$)/u' , $values );
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
foreach ( $values as $i => $value ) {
$values [ $i ] = twig_convert_encoding ( $value , $charset , 'UTF-8' );
}
}
} else {
return $values [ mt_rand ( 0 , strlen ( $values ) - 1 )];
}
}
if ( ! is_array ( $values )) {
return $values ;
}
if ( 0 === count ( $values )) {
throw new Twig_Error_Runtime ( 'The random function cannot pick from an empty array.' );
}
return $values [ array_rand ( $values , 1 )];
2011-10-05 06:22:53 +02:00
}
/**
* Converts a date to the given format .
*
* < pre >
* {{ post . published_at | date ( " m/d/Y " ) }}
* </ pre >
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
* @ param DateTime | DateTimeInterface | DateInterval | string $date A date
* @ param string | null $format The target format , null to use the default
* @ param DateTimeZone | string | null | false $timezone The target timezone , null to use the default , false to leave unchanged
2013-08-01 21:20:12 +02:00
*
* @ return string The formatted date
*/
function twig_date_format_filter ( Twig_Environment $env , $date , $format = null , $timezone = null )
{
if ( null === $format ) {
2018-05-10 12:24:53 +02:00
$formats = $env -> getExtension ( 'Twig_Extension_Core' ) -> getDateFormat ();
2013-08-01 21:20:12 +02:00
$format = $date instanceof DateInterval ? $formats [ 1 ] : $formats [ 0 ];
}
if ( $date instanceof DateInterval ) {
return $date -> format ( $format );
}
return twig_date_converter ( $env , $date , $timezone ) -> format ( $format );
}
/**
2018-05-10 12:24:53 +02:00
* Returns a new date object modified .
2013-08-01 21:20:12 +02:00
*
* < pre >
* {{ post . published_at | date_modify ( " -1day " ) | date ( " m/d/Y " ) }}
* </ pre >
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
* @ param DateTime | string $date A date
* @ param string $modifier A modifier string
2013-08-01 21:20:12 +02:00
*
* @ return DateTime A new date object
*/
function twig_date_modify_filter ( Twig_Environment $env , $date , $modifier )
{
$date = twig_date_converter ( $env , $date , false );
2018-05-10 12:24:53 +02:00
$resultDate = $date -> modify ( $modifier );
2013-08-01 21:20:12 +02:00
2018-05-10 12:24:53 +02:00
// This is a hack to ensure PHP 5.2 support and support for DateTimeImmutable
// DateTime::modify does not return the modified DateTime object < 5.3.0
// and DateTimeImmutable does not modify $date.
return null === $resultDate ? $date : $resultDate ;
2013-08-01 21:20:12 +02:00
}
/**
* Converts an input to a DateTime instance .
*
* < pre >
* { % if date ( user . created_at ) < date ( '+2days' ) % }
* { # do something #}
* { % endif % }
* </ pre >
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
* @ param DateTime | DateTimeInterface | string | null $date A date
* @ param DateTimeZone | string | null | false $timezone The target timezone , null to use the default , false to leave unchanged
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ return DateTime A DateTime instance
2011-10-05 06:22:53 +02:00
*/
2013-08-01 21:20:12 +02:00
function twig_date_converter ( Twig_Environment $env , $date = null , $timezone = null )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
// determine the timezone
2018-05-10 12:24:53 +02:00
if ( false !== $timezone ) {
if ( null === $timezone ) {
$timezone = $env -> getExtension ( 'Twig_Extension_Core' ) -> getTimezone ();
} elseif ( ! $timezone instanceof DateTimeZone ) {
$timezone = new DateTimeZone ( $timezone );
}
2011-10-05 06:22:53 +02:00
}
2018-05-10 12:24:53 +02:00
// immutable dates
if ( $date instanceof DateTimeImmutable ) {
return false !== $timezone ? $date -> setTimezone ( $timezone ) : $date ;
}
if ( $date instanceof DateTime || $date instanceof DateTimeInterface ) {
2013-08-01 21:20:12 +02:00
$date = clone $date ;
if ( false !== $timezone ) {
2018-05-10 12:24:53 +02:00
$date -> setTimezone ( $timezone );
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
return $date ;
}
2018-05-10 12:24:53 +02:00
if ( null === $date || 'now' === $date ) {
return new DateTime ( $date , false !== $timezone ? $timezone : $env -> getExtension ( 'Twig_Extension_Core' ) -> getTimezone ());
}
2013-08-01 21:20:12 +02:00
$asString = ( string ) $date ;
if ( ctype_digit ( $asString ) || ( ! empty ( $asString ) && '-' === $asString [ 0 ] && ctype_digit ( substr ( $asString , 1 )))) {
2018-05-10 12:24:53 +02:00
$date = new DateTime ( '@' . $date );
} else {
$date = new DateTime ( $date , $env -> getExtension ( 'Twig_Extension_Core' ) -> getTimezone ());
2013-08-01 21:20:12 +02:00
}
if ( false !== $timezone ) {
2018-05-10 12:24:53 +02:00
$date -> setTimezone ( $timezone );
2013-08-01 21:20:12 +02:00
}
return $date ;
}
2018-05-10 12:24:53 +02:00
/**
* Replaces strings within a string .
*
* @ param string $str String to replace in
* @ param array | Traversable $from Replace values
* @ param string | null $to Replace to , deprecated ( @ see https :// secure . php . net / manual / en / function . strtr . php )
*
* @ return string
*/
function twig_replace_filter ( $str , $from , $to = null )
{
if ( $from instanceof Traversable ) {
$from = iterator_to_array ( $from );
} elseif ( is_string ( $from ) && is_string ( $to )) {
@ trigger_error ( 'Using "replace" with character by character replacement is deprecated since version 1.22 and will be removed in Twig 2.0' , E_USER_DEPRECATED );
return strtr ( $str , $from , $to );
} elseif ( ! is_array ( $from )) {
throw new Twig_Error_Runtime ( sprintf ( 'The "replace" filter expects an array or "Traversable" as replace values, got "%s".' , is_object ( $from ) ? get_class ( $from ) : gettype ( $from )));
}
return strtr ( $str , $from );
}
/**
* Rounds a number .
*
* @ param int | float $value The value to round
* @ param int | float $precision The rounding precision
* @ param string $method The method to use for rounding
*
* @ return int | float The rounded number
*/
function twig_round ( $value , $precision = 0 , $method = 'common' )
{
if ( 'common' == $method ) {
return round ( $value , $precision );
}
if ( 'ceil' != $method && 'floor' != $method ) {
throw new Twig_Error_Runtime ( 'The round filter only supports the "common", "ceil", and "floor" methods.' );
}
return $method ( $value * pow ( 10 , $precision )) / pow ( 10 , $precision );
}
2013-08-01 21:20:12 +02:00
/**
* Number format filter .
*
* All of the formatting options can be left null , in that case the defaults will
* be used . Supplying any of the parameters will override the defaults set in the
* environment object .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
* @ param mixed $number A float / int / string of the number to format
* @ param int $decimal the number of decimal points to display
* @ param string $decimalPoint the character ( s ) to use for the decimal point
* @ param string $thousandSep the character ( s ) to use for the thousands separator
2013-08-01 21:20:12 +02:00
*
* @ return string The formatted number
*/
function twig_number_format_filter ( Twig_Environment $env , $number , $decimal = null , $decimalPoint = null , $thousandSep = null )
{
2018-05-10 12:24:53 +02:00
$defaults = $env -> getExtension ( 'Twig_Extension_Core' ) -> getNumberFormat ();
2013-08-01 21:20:12 +02:00
if ( null === $decimal ) {
$decimal = $defaults [ 0 ];
}
if ( null === $decimalPoint ) {
$decimalPoint = $defaults [ 1 ];
}
if ( null === $thousandSep ) {
$thousandSep = $defaults [ 2 ];
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
return number_format (( float ) $number , $decimal , $decimalPoint , $thousandSep );
2011-10-05 06:22:53 +02:00
}
/**
2018-05-10 12:24:53 +02:00
* URL encodes ( RFC 3986 ) a string as a path segment or an array as a query string .
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ param string | array $url A URL or an array of query parameters
2011-10-05 06:22:53 +02:00
*
* @ return string The URL encoded value
*/
2018-05-10 12:24:53 +02:00
function twig_urlencode_filter ( $url )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
if ( is_array ( $url )) {
2018-05-10 12:24:53 +02:00
if ( defined ( 'PHP_QUERY_RFC3986' )) {
return http_build_query ( $url , '' , '&' , PHP_QUERY_RFC3986 );
}
2013-08-01 21:20:12 +02:00
2018-05-10 12:24:53 +02:00
return http_build_query ( $url , '' , '&' );
2011-10-05 06:22:53 +02:00
}
2018-05-10 12:24:53 +02:00
return rawurlencode ( $url );
2011-10-05 06:22:53 +02:00
}
2018-05-10 12:24:53 +02:00
if ( PHP_VERSION_ID < 50300 ) {
2011-10-05 06:22:53 +02:00
/**
2013-08-01 21:20:12 +02:00
* JSON encodes a variable .
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ param mixed $value the value to encode
* @ param int $options Not used on PHP 5.2 . x
2011-10-05 06:22:53 +02:00
*
* @ return mixed The JSON encoded value
*/
function twig_jsonencode_filter ( $value , $options = 0 )
{
if ( $value instanceof Twig_Markup ) {
$value = ( string ) $value ;
} elseif ( is_array ( $value )) {
array_walk_recursive ( $value , '_twig_markup2string' );
}
return json_encode ( $value );
}
} else {
/**
2013-08-01 21:20:12 +02:00
* JSON encodes a variable .
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ param mixed $value the value to encode
* @ param int $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
2011-10-05 06:22:53 +02:00
*
* @ return mixed The JSON encoded value
*/
function twig_jsonencode_filter ( $value , $options = 0 )
{
if ( $value instanceof Twig_Markup ) {
$value = ( string ) $value ;
} elseif ( is_array ( $value )) {
array_walk_recursive ( $value , '_twig_markup2string' );
}
return json_encode ( $value , $options );
}
}
function _twig_markup2string ( & $value )
{
if ( $value instanceof Twig_Markup ) {
$value = ( string ) $value ;
}
}
/**
* Merges an array with another one .
*
* < pre >
* { % set items = { 'apple' : 'fruit' , 'orange' : 'fruit' } % }
*
* { % set items = items | merge ({ 'peugeot' : 'car' }) % }
*
* { # items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
* </ pre >
*
2018-05-10 12:24:53 +02:00
* @ param array | Traversable $arr1 An array
* @ param array | Traversable $arr2 An array
2011-10-05 06:22:53 +02:00
*
* @ return array The merged array
*/
function twig_array_merge ( $arr1 , $arr2 )
{
2018-05-10 12:24:53 +02:00
if ( $arr1 instanceof Traversable ) {
$arr1 = iterator_to_array ( $arr1 );
} elseif ( ! is_array ( $arr1 )) {
throw new Twig_Error_Runtime ( sprintf ( 'The merge filter only works with arrays or "Traversable", got "%s" as first argument.' , gettype ( $arr1 )));
}
if ( $arr2 instanceof Traversable ) {
$arr2 = iterator_to_array ( $arr2 );
} elseif ( ! is_array ( $arr2 )) {
throw new Twig_Error_Runtime ( sprintf ( 'The merge filter only works with arrays or "Traversable", got "%s" as second argument.' , gettype ( $arr2 )));
2011-10-05 06:22:53 +02:00
}
return array_merge ( $arr1 , $arr2 );
}
2013-08-01 21:20:12 +02:00
/**
* Slices a variable .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2013-08-01 21:20:12 +02:00
* @ param mixed $item A variable
2018-05-10 12:24:53 +02:00
* @ param int $start Start of the slice
* @ param int $length Size of the slice
* @ param bool $preserveKeys Whether to preserve key or not ( when the input is an array )
2013-08-01 21:20:12 +02:00
*
* @ return mixed The sliced variable
*/
function twig_slice ( Twig_Environment $env , $item , $start , $length = null , $preserveKeys = false )
{
2018-05-10 12:24:53 +02:00
if ( $item instanceof Traversable ) {
while ( $item instanceof IteratorAggregate ) {
$item = $item -> getIterator ();
}
if ( $start >= 0 && $length >= 0 && $item instanceof Iterator ) {
try {
return iterator_to_array ( new LimitIterator ( $item , $start , null === $length ? - 1 : $length ), $preserveKeys );
} catch ( OutOfBoundsException $exception ) {
return array ();
}
}
$item = iterator_to_array ( $item , $preserveKeys );
2013-08-01 21:20:12 +02:00
}
if ( is_array ( $item )) {
return array_slice ( $item , $start , $length , $preserveKeys );
}
$item = ( string ) $item ;
if ( function_exists ( 'mb_get_info' ) && null !== $charset = $env -> getCharset ()) {
2018-05-10 12:24:53 +02:00
return ( string ) mb_substr ( $item , $start , null === $length ? mb_strlen ( $item , $charset ) - $start : $length , $charset );
2013-08-01 21:20:12 +02:00
}
2018-05-10 12:24:53 +02:00
return ( string ) ( null === $length ? substr ( $item , $start ) : substr ( $item , $start , $length ));
2013-08-01 21:20:12 +02:00
}
/**
* Returns the first element of the item .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2013-08-01 21:20:12 +02:00
* @ param mixed $item A variable
*
* @ return mixed The first element of the item
*/
function twig_first ( Twig_Environment $env , $item )
{
$elements = twig_slice ( $env , $item , 0 , 1 , false );
2018-05-10 12:24:53 +02:00
return is_string ( $elements ) ? $elements : current ( $elements );
2013-08-01 21:20:12 +02:00
}
/**
* Returns the last element of the item .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2013-08-01 21:20:12 +02:00
* @ param mixed $item A variable
*
* @ return mixed The last element of the item
*/
function twig_last ( Twig_Environment $env , $item )
{
$elements = twig_slice ( $env , $item , - 1 , 1 , false );
2018-05-10 12:24:53 +02:00
return is_string ( $elements ) ? $elements : current ( $elements );
2013-08-01 21:20:12 +02:00
}
2011-10-05 06:22:53 +02:00
/**
* Joins the values to a string .
*
* The separator between elements is an empty string per default , you can define it with the optional parameter .
*
* < pre >
* {{ [ 1 , 2 , 3 ] | join ( '|' ) }}
* { # returns 1|2|3 #}
*
* {{ [ 1 , 2 , 3 ] | join }}
* { # returns 123 #}
* </ pre >
*
* @ param array $value An array
* @ param string $glue The separator
*
* @ return string The concatenated string
*/
function twig_join_filter ( $value , $glue = '' )
{
2018-05-10 12:24:53 +02:00
if ( $value instanceof Traversable ) {
2013-08-01 21:20:12 +02:00
$value = iterator_to_array ( $value , false );
}
2011-10-05 06:22:53 +02:00
return implode ( $glue , ( array ) $value );
}
/**
2013-08-01 21:20:12 +02:00
* Splits the string into an array .
2011-10-05 06:22:53 +02:00
*
* < pre >
2013-08-01 21:20:12 +02:00
* {{ " one,two,three " | split ( ',' ) }}
* { # returns [one, two, three] #}
*
* {{ " one,two,three,four,five " | split ( ',' , 3 ) }}
* { # returns [one, two, "three,four,five"] #}
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* {{ " 123 " | split ( '' ) }}
* { # returns [1, 2, 3] #}
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* {{ " aabbcc " | split ( '' , 2 ) }}
* { # returns [aa, bb, cc] #}
2011-10-05 06:22:53 +02:00
* </ pre >
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
* @ param string $value A string
* @ param string $delimiter The delimiter
* @ param int $limit The limit
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ return array The split string as an array
2011-10-05 06:22:53 +02:00
*/
2018-05-10 12:24:53 +02:00
function twig_split_filter ( Twig_Environment $env , $value , $delimiter , $limit = null )
2013-08-01 21:20:12 +02:00
{
2018-05-10 12:24:53 +02:00
if ( ! empty ( $delimiter )) {
return null === $limit ? explode ( $delimiter , $value ) : explode ( $delimiter , $value , $limit );
}
if ( ! function_exists ( 'mb_get_info' ) || null === $charset = $env -> getCharset ()) {
2013-08-01 21:20:12 +02:00
return str_split ( $value , null === $limit ? 1 : $limit );
}
2018-05-10 12:24:53 +02:00
if ( $limit <= 1 ) {
return preg_split ( '/(?<!^)(?!$)/u' , $value );
}
$length = mb_strlen ( $value , $charset );
if ( $length < $limit ) {
return array ( $value );
}
$r = array ();
for ( $i = 0 ; $i < $length ; $i += $limit ) {
$r [] = mb_substr ( $value , $i , $limit , $charset );
}
return $r ;
2013-08-01 21:20:12 +02:00
}
// The '_default' filter is used internally to avoid using the ternary operator
// which costs a lot for big contexts (before PHP 5.4). So, on average,
// a function call is cheaper.
2018-05-10 12:24:53 +02:00
/**
* @ internal
*/
2013-08-01 21:20:12 +02:00
function _twig_default_filter ( $value , $default = '' )
2011-10-05 06:22:53 +02:00
{
if ( twig_test_empty ( $value )) {
return $default ;
}
2013-08-01 21:20:12 +02:00
return $value ;
2011-10-05 06:22:53 +02:00
}
/**
* Returns the keys for the given array .
*
* It is useful when you want to iterate over the keys of an array :
*
* < pre >
* { % for key in array | keys % }
* { # ... #}
* { % endfor % }
* </ pre >
*
* @ param array $array An array
*
* @ return array The keys
*/
function twig_get_array_keys_filter ( $array )
{
2018-05-10 12:24:53 +02:00
if ( $array instanceof Traversable ) {
while ( $array instanceof IteratorAggregate ) {
$array = $array -> getIterator ();
}
if ( $array instanceof Iterator ) {
$keys = array ();
$array -> rewind ();
while ( $array -> valid ()) {
$keys [] = $array -> key ();
$array -> next ();
}
return $keys ;
}
$keys = array ();
foreach ( $array as $key => $item ) {
$keys [] = $key ;
}
return $keys ;
2011-10-05 06:22:53 +02:00
}
if ( ! is_array ( $array )) {
return array ();
}
return array_keys ( $array );
}
/**
2013-08-01 21:20:12 +02:00
* Reverses a variable .
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2013-08-01 21:20:12 +02:00
* @ param array | Traversable | string $item An array , a Traversable instance , or a string
2018-05-10 12:24:53 +02:00
* @ param bool $preserveKeys Whether to preserve key or not
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ return mixed The reversed input
2011-10-05 06:22:53 +02:00
*/
2013-08-01 21:20:12 +02:00
function twig_reverse_filter ( Twig_Environment $env , $item , $preserveKeys = false )
2011-10-05 06:22:53 +02:00
{
2018-05-10 12:24:53 +02:00
if ( $item instanceof Traversable ) {
2013-08-01 21:20:12 +02:00
return array_reverse ( iterator_to_array ( $item ), $preserveKeys );
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
if ( is_array ( $item )) {
return array_reverse ( $item , $preserveKeys );
}
if ( null !== $charset = $env -> getCharset ()) {
$string = ( string ) $item ;
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$item = twig_convert_encoding ( $string , 'UTF-8' , $charset );
}
preg_match_all ( '/./us' , $item , $matches );
$string = implode ( '' , array_reverse ( $matches [ 0 ]));
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$string = twig_convert_encoding ( $string , $charset , 'UTF-8' );
}
return $string ;
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
return strrev (( string ) $item );
2011-10-05 06:22:53 +02:00
}
/**
* Sorts an array .
*
2018-05-10 12:24:53 +02:00
* @ param array | Traversable $array
*
* @ return array
2011-10-05 06:22:53 +02:00
*/
function twig_sort_filter ( $array )
{
2018-05-10 12:24:53 +02:00
if ( $array instanceof Traversable ) {
$array = iterator_to_array ( $array );
} elseif ( ! is_array ( $array )) {
throw new Twig_Error_Runtime ( sprintf ( 'The sort filter only works with arrays or "Traversable", got "%s".' , gettype ( $array )));
}
2011-10-05 06:22:53 +02:00
asort ( $array );
return $array ;
}
2018-05-10 12:24:53 +02:00
/**
* @ internal
*/
2011-10-05 06:22:53 +02:00
function twig_in_filter ( $value , $compare )
{
if ( is_array ( $compare )) {
2018-05-10 12:24:53 +02:00
return in_array ( $value , $compare , is_object ( $value ) || is_resource ( $value ));
} elseif ( is_string ( $compare ) && ( is_string ( $value ) || is_int ( $value ) || is_float ( $value ))) {
return '' === $value || false !== strpos ( $compare , ( string ) $value );
} elseif ( $compare instanceof Traversable ) {
if ( is_object ( $value ) || is_resource ( $value )) {
foreach ( $compare as $item ) {
if ( $item === $value ) {
return true ;
}
}
} else {
foreach ( $compare as $item ) {
if ( $item == $value ) {
return true ;
}
}
2013-08-01 21:20:12 +02:00
}
2018-05-10 12:24:53 +02:00
return false ;
2011-10-05 06:22:53 +02:00
}
return false ;
}
2018-05-10 12:24:53 +02:00
/**
* Returns a trimmed string .
*
* @ return string
*
* @ throws Twig_Error_Runtime When an invalid trimming side is used ( not a string or not 'left' , 'right' , or 'both' )
*/
function twig_trim_filter ( $string , $characterMask = null , $side = 'both' )
{
if ( null === $characterMask ) {
$characterMask = " \t \n \r \0 \x0B " ;
}
switch ( $side ) {
case 'both' :
return trim ( $string , $characterMask );
case 'left' :
return ltrim ( $string , $characterMask );
case 'right' :
return rtrim ( $string , $characterMask );
default :
throw new Twig_Error_Runtime ( 'Trimming side must be "left", "right" or "both".' );
}
}
2011-10-05 06:22:53 +02:00
/**
* Escapes a string .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
* @ param mixed $string The value to be escaped
2013-08-01 21:20:12 +02:00
* @ param string $strategy The escaping strategy
* @ param string $charset The charset
2018-05-10 12:24:53 +02:00
* @ param bool $autoescape Whether the function is called by the auto - escaping feature ( true ) or by the developer ( false )
*
* @ return string
2011-10-05 06:22:53 +02:00
*/
2013-08-01 21:20:12 +02:00
function twig_escape_filter ( Twig_Environment $env , $string , $strategy = 'html' , $charset = null , $autoescape = false )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
if ( $autoescape && $string instanceof Twig_Markup ) {
2011-10-05 06:22:53 +02:00
return $string ;
}
2013-08-01 21:20:12 +02:00
if ( ! is_string ( $string )) {
if ( is_object ( $string ) && method_exists ( $string , '__toString' )) {
$string = ( string ) $string ;
2018-05-10 12:24:53 +02:00
} elseif ( in_array ( $strategy , array ( 'html' , 'js' , 'css' , 'html_attr' , 'url' ))) {
2013-08-01 21:20:12 +02:00
return $string ;
}
2011-10-05 06:22:53 +02:00
}
if ( null === $charset ) {
$charset = $env -> getCharset ();
}
2013-08-01 21:20:12 +02:00
switch ( $strategy ) {
case 'html' :
2018-05-10 12:24:53 +02:00
// see https://secure.php.net/htmlspecialchars
2013-08-01 21:20:12 +02:00
// Using a static variable to avoid initializing the array
// each time the function is called. Moving the declaration on the
// top of the function slow downs other escaping strategies.
static $htmlspecialcharsCharsets = array (
'ISO-8859-1' => true , 'ISO8859-1' => true ,
'ISO-8859-15' => true , 'ISO8859-15' => true ,
'utf-8' => true , 'UTF-8' => true ,
'CP866' => true , 'IBM866' => true , '866' => true ,
'CP1251' => true , 'WINDOWS-1251' => true , 'WIN-1251' => true ,
'1251' => true ,
'CP1252' => true , 'WINDOWS-1252' => true , '1252' => true ,
'KOI8-R' => true , 'KOI8-RU' => true , 'KOI8R' => true ,
'BIG5' => true , '950' => true ,
'GB2312' => true , '936' => true ,
'BIG5-HKSCS' => true ,
'SHIFT_JIS' => true , 'SJIS' => true , '932' => true ,
'EUC-JP' => true , 'EUCJP' => true ,
'ISO8859-5' => true , 'ISO-8859-5' => true , 'MACROMAN' => true ,
);
if ( isset ( $htmlspecialcharsCharsets [ $charset ])) {
return htmlspecialchars ( $string , ENT_QUOTES | ENT_SUBSTITUTE , $charset );
}
if ( isset ( $htmlspecialcharsCharsets [ strtoupper ( $charset )])) {
// cache the lowercase variant for future iterations
$htmlspecialcharsCharsets [ $charset ] = true ;
return htmlspecialchars ( $string , ENT_QUOTES | ENT_SUBSTITUTE , $charset );
}
$string = twig_convert_encoding ( $string , 'UTF-8' , $charset );
$string = htmlspecialchars ( $string , ENT_QUOTES | ENT_SUBSTITUTE , 'UTF-8' );
return twig_convert_encoding ( $string , $charset , 'UTF-8' );
2011-10-05 06:22:53 +02:00
case 'js' :
// escape all non-alphanumeric characters
2018-05-10 12:24:53 +02:00
// into their \x or \uHHHH representations
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$string = twig_convert_encoding ( $string , 'UTF-8' , $charset );
2011-10-05 06:22:53 +02:00
}
2018-05-10 12:24:53 +02:00
if ( 0 == strlen ( $string ) ? false : 1 !== preg_match ( '/^./su' , $string )) {
2011-10-05 06:22:53 +02:00
throw new Twig_Error_Runtime ( 'The string to escape is not a valid UTF-8 string.' );
}
2013-08-01 21:20:12 +02:00
$string = preg_replace_callback ( '#[^a-zA-Z0-9,\._]#Su' , '_twig_escape_js_callback' , $string );
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$string = twig_convert_encoding ( $string , $charset , 'UTF-8' );
2011-10-05 06:22:53 +02:00
}
return $string ;
2013-08-01 21:20:12 +02:00
case 'css' :
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$string = twig_convert_encoding ( $string , 'UTF-8' , $charset );
}
2018-05-10 12:24:53 +02:00
if ( 0 == strlen ( $string ) ? false : 1 !== preg_match ( '/^./su' , $string )) {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( 'The string to escape is not a valid UTF-8 string.' );
}
$string = preg_replace_callback ( '#[^a-zA-Z0-9]#Su' , '_twig_escape_css_callback' , $string );
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$string = twig_convert_encoding ( $string , $charset , 'UTF-8' );
}
return $string ;
case 'html_attr' :
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$string = twig_convert_encoding ( $string , 'UTF-8' , $charset );
}
2018-05-10 12:24:53 +02:00
if ( 0 == strlen ( $string ) ? false : 1 !== preg_match ( '/^./su' , $string )) {
2013-08-01 21:20:12 +02:00
throw new Twig_Error_Runtime ( 'The string to escape is not a valid UTF-8 string.' );
}
$string = preg_replace_callback ( '#[^a-zA-Z0-9,\.\-_]#Su' , '_twig_escape_html_attr_callback' , $string );
2018-05-10 12:24:53 +02:00
if ( 'UTF-8' !== $charset ) {
2013-08-01 21:20:12 +02:00
$string = twig_convert_encoding ( $string , $charset , 'UTF-8' );
}
return $string ;
case 'url' :
2018-05-10 12:24:53 +02:00
if ( PHP_VERSION_ID < 50300 ) {
2013-08-01 21:20:12 +02:00
return str_replace ( '%7E' , '~' , rawurlencode ( $string ));
}
return rawurlencode ( $string );
2011-10-05 06:22:53 +02:00
default :
2018-05-10 12:24:53 +02:00
static $escapers ;
if ( null === $escapers ) {
$escapers = $env -> getExtension ( 'Twig_Extension_Core' ) -> getEscapers ();
}
if ( isset ( $escapers [ $strategy ])) {
return call_user_func ( $escapers [ $strategy ], $env , $string , $charset );
}
$validStrategies = implode ( ', ' , array_merge ( array ( 'html' , 'js' , 'url' , 'css' , 'html_attr' ), array_keys ( $escapers )));
throw new Twig_Error_Runtime ( sprintf ( 'Invalid escaping strategy "%s" (valid ones: %s).' , $strategy , $validStrategies ));
2011-10-05 06:22:53 +02:00
}
}
2018-05-10 12:24:53 +02:00
/**
* @ internal
*/
2011-10-05 06:22:53 +02:00
function twig_escape_filter_is_safe ( Twig_Node $filterArgs )
{
foreach ( $filterArgs as $arg ) {
if ( $arg instanceof Twig_Node_Expression_Constant ) {
return array ( $arg -> getAttribute ( 'value' ));
}
2013-08-01 21:20:12 +02:00
return array ();
2011-10-05 06:22:53 +02:00
}
return array ( 'html' );
}
2013-08-01 21:20:12 +02:00
if ( function_exists ( 'mb_convert_encoding' )) {
function twig_convert_encoding ( $string , $to , $from )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
return mb_convert_encoding ( $string , $to , $from );
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
} elseif ( function_exists ( 'iconv' )) {
function twig_convert_encoding ( $string , $to , $from )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
return iconv ( $from , $to , $string );
2011-10-05 06:22:53 +02:00
}
} else {
2013-08-01 21:20:12 +02:00
function twig_convert_encoding ( $string , $to , $from )
2011-10-05 06:22:53 +02:00
{
throw new Twig_Error_Runtime ( 'No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).' );
}
}
function _twig_escape_js_callback ( $matches )
{
$char = $matches [ 0 ];
2018-05-10 12:24:53 +02:00
/*
* A few characters have short escape sequences in JSON and JavaScript .
* Escape sequences supported only by JavaScript , not JSON , are ommitted .
* \ " is also supported but omitted, because the resulting string is not HTML safe.
*/
static $shortMap = array (
'\\' => '\\\\' ,
'/' => '\\/' ,
" \x08 " => '\b' ,
" \x0C " => '\f' ,
" \x0A " => '\n' ,
" \x0D " => '\r' ,
" \x09 " => '\t' ,
);
if ( isset ( $shortMap [ $char ])) {
return $shortMap [ $char ];
2013-08-01 21:20:12 +02:00
}
// \uHHHH
$char = twig_convert_encoding ( $char , 'UTF-16BE' , 'UTF-8' );
2018-05-10 12:24:53 +02:00
$char = strtoupper ( bin2hex ( $char ));
2013-08-01 21:20:12 +02:00
2018-05-10 12:24:53 +02:00
if ( 4 >= strlen ( $char )) {
return sprintf ( '\u%04s' , $char );
}
return sprintf ( '\u%04s\u%04s' , substr ( $char , 0 , - 4 ), substr ( $char , - 4 ));
2013-08-01 21:20:12 +02:00
}
function _twig_escape_css_callback ( $matches )
{
$char = $matches [ 0 ];
// \xHH
if ( ! isset ( $char [ 1 ])) {
$hex = ltrim ( strtoupper ( bin2hex ( $char )), '0' );
if ( 0 === strlen ( $hex )) {
$hex = '0' ;
}
return '\\' . $hex . ' ' ;
2011-10-05 06:22:53 +02:00
}
// \uHHHH
2013-08-01 21:20:12 +02:00
$char = twig_convert_encoding ( $char , 'UTF-16BE' , 'UTF-8' );
2011-10-05 06:22:53 +02:00
2013-08-01 21:20:12 +02:00
return '\\' . ltrim ( strtoupper ( bin2hex ( $char )), '0' ) . ' ' ;
}
/**
* This function is adapted from code coming from Zend Framework .
*
2018-05-10 12:24:53 +02:00
* @ copyright Copyright ( c ) 2005 - 2012 Zend Technologies USA Inc . ( https :// www . zend . com )
* @ license https :// framework . zend . com / license / new - bsd New BSD License
2013-08-01 21:20:12 +02:00
*/
function _twig_escape_html_attr_callback ( $matches )
{
/*
* While HTML supports far more named entities , the lowest common denominator
* has become HTML5 ' s XML Serialisation which is restricted to the those named
* entities that XML supports . Using HTML entities would result in this error :
* XML Parsing Error : undefined entity
*/
static $entityMap = array (
34 => 'quot' , /* quotation mark */
38 => 'amp' , /* ampersand */
60 => 'lt' , /* less-than sign */
62 => 'gt' , /* greater-than sign */
);
$chr = $matches [ 0 ];
$ord = ord ( $chr );
2018-05-10 12:24:53 +02:00
/*
2013-08-01 21:20:12 +02:00
* The following replaces characters undefined in HTML with the
* hex entity for the Unicode replacement character .
*/
2018-05-10 12:24:53 +02:00
if (( $ord <= 0x1f && " \t " != $chr && " \n " != $chr && " \r " != $chr ) || ( $ord >= 0x7f && $ord <= 0x9f )) {
2013-08-01 21:20:12 +02:00
return '�' ;
}
2018-05-10 12:24:53 +02:00
/*
2013-08-01 21:20:12 +02:00
* Check if the current character to escape has a name entity we should
* replace it with while grabbing the hex value of the character .
*/
2018-05-10 12:24:53 +02:00
if ( 1 == strlen ( $chr )) {
2013-08-01 21:20:12 +02:00
$hex = strtoupper ( substr ( '00' . bin2hex ( $chr ), - 2 ));
} else {
$chr = twig_convert_encoding ( $chr , 'UTF-16BE' , 'UTF-8' );
$hex = strtoupper ( substr ( '0000' . bin2hex ( $chr ), - 4 ));
}
$int = hexdec ( $hex );
if ( array_key_exists ( $int , $entityMap )) {
return sprintf ( '&%s;' , $entityMap [ $int ]);
}
2018-05-10 12:24:53 +02:00
/*
2013-08-01 21:20:12 +02:00
* Per OWASP recommendations , we ' ll use hex entities for any other
* characters where a named entity does not exist .
*/
return sprintf ( '&#x%s;' , $hex );
2011-10-05 06:22:53 +02:00
}
// add multibyte extensions if possible
if ( function_exists ( 'mb_get_info' )) {
/**
2013-08-01 21:20:12 +02:00
* Returns the length of a variable .
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2013-08-01 21:20:12 +02:00
* @ param mixed $thing A variable
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ return int The length of the value
2011-10-05 06:22:53 +02:00
*/
function twig_length_filter ( Twig_Environment $env , $thing )
{
2018-05-10 12:24:53 +02:00
if ( null === $thing ) {
return 0 ;
}
if ( is_scalar ( $thing )) {
return mb_strlen ( $thing , $env -> getCharset ());
}
if ( $thing instanceof \SimpleXMLElement ) {
return count ( $thing );
}
if ( is_object ( $thing ) && method_exists ( $thing , '__toString' ) && ! $thing instanceof \Countable ) {
return mb_strlen (( string ) $thing , $env -> getCharset ());
}
if ( $thing instanceof \Countable || is_array ( $thing )) {
return count ( $thing );
}
if ( $thing instanceof \IteratorAggregate ) {
return iterator_count ( $thing );
}
return 1 ;
2011-10-05 06:22:53 +02:00
}
/**
* Converts a string to uppercase .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2011-10-05 06:22:53 +02:00
* @ param string $string A string
*
* @ return string The uppercased string
*/
function twig_upper_filter ( Twig_Environment $env , $string )
{
2018-05-10 12:24:53 +02:00
if ( null !== $charset = $env -> getCharset ()) {
2011-10-05 06:22:53 +02:00
return mb_strtoupper ( $string , $charset );
}
return strtoupper ( $string );
}
/**
* Converts a string to lowercase .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2011-10-05 06:22:53 +02:00
* @ param string $string A string
*
* @ return string The lowercased string
*/
function twig_lower_filter ( Twig_Environment $env , $string )
{
2018-05-10 12:24:53 +02:00
if ( null !== $charset = $env -> getCharset ()) {
2011-10-05 06:22:53 +02:00
return mb_strtolower ( $string , $charset );
}
return strtolower ( $string );
}
/**
* Returns a titlecased string .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2011-10-05 06:22:53 +02:00
* @ param string $string A string
*
* @ return string The titlecased string
*/
function twig_title_string_filter ( Twig_Environment $env , $string )
{
2018-05-10 12:24:53 +02:00
if ( null !== $charset = $env -> getCharset ()) {
2011-10-05 06:22:53 +02:00
return mb_convert_case ( $string , MB_CASE_TITLE , $charset );
}
return ucwords ( strtolower ( $string ));
}
/**
* Returns a capitalized string .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2011-10-05 06:22:53 +02:00
* @ param string $string A string
*
* @ return string The capitalized string
*/
function twig_capitalize_string_filter ( Twig_Environment $env , $string )
{
2018-05-10 12:24:53 +02:00
if ( null !== $charset = $env -> getCharset ()) {
return mb_strtoupper ( mb_substr ( $string , 0 , 1 , $charset ), $charset ) . mb_strtolower ( mb_substr ( $string , 1 , mb_strlen ( $string , $charset ), $charset ), $charset );
2011-10-05 06:22:53 +02:00
}
return ucfirst ( strtolower ( $string ));
}
}
// and byte fallback
2013-08-01 21:20:12 +02:00
else {
2011-10-05 06:22:53 +02:00
/**
2013-08-01 21:20:12 +02:00
* Returns the length of a variable .
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2013-08-01 21:20:12 +02:00
* @ param mixed $thing A variable
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ return int The length of the value
2011-10-05 06:22:53 +02:00
*/
function twig_length_filter ( Twig_Environment $env , $thing )
{
2018-05-10 12:24:53 +02:00
if ( null === $thing ) {
return 0 ;
}
if ( is_scalar ( $thing )) {
return strlen ( $thing );
}
if ( $thing instanceof \SimpleXMLElement ) {
return count ( $thing );
}
if ( is_object ( $thing ) && method_exists ( $thing , '__toString' ) && ! $thing instanceof \Countable ) {
return strlen (( string ) $thing );
}
if ( $thing instanceof \Countable || is_array ( $thing )) {
return count ( $thing );
}
if ( $thing instanceof \IteratorAggregate ) {
return iterator_count ( $thing );
}
return 1 ;
2011-10-05 06:22:53 +02:00
}
/**
* Returns a titlecased string .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2011-10-05 06:22:53 +02:00
* @ param string $string A string
*
* @ return string The titlecased string
*/
function twig_title_string_filter ( Twig_Environment $env , $string )
{
return ucwords ( strtolower ( $string ));
}
/**
* Returns a capitalized string .
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
2011-10-05 06:22:53 +02:00
* @ param string $string A string
*
* @ return string The capitalized string
*/
function twig_capitalize_string_filter ( Twig_Environment $env , $string )
{
return ucfirst ( strtolower ( $string ));
}
}
2018-05-10 12:24:53 +02:00
/**
* @ internal
*/
2011-10-05 06:22:53 +02:00
function twig_ensure_traversable ( $seq )
{
2013-08-01 21:20:12 +02:00
if ( $seq instanceof Traversable || is_array ( $seq )) {
2011-10-05 06:22:53 +02:00
return $seq ;
}
2013-08-01 21:20:12 +02:00
return array ();
2011-10-05 06:22:53 +02:00
}
/**
2013-08-01 21:20:12 +02:00
* Checks if a variable is empty .
2011-10-05 06:22:53 +02:00
*
* < pre >
2013-08-01 21:20:12 +02:00
* { # evaluates to true if the foo variable is null, false, or the empty string #}
* { % if foo is empty % }
* { # ... #}
2011-10-05 06:22:53 +02:00
* { % endif % }
* </ pre >
*
2013-08-01 21:20:12 +02:00
* @ param mixed $value A variable
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ return bool true if the value is empty , false otherwise
2011-10-05 06:22:53 +02:00
*/
2013-08-01 21:20:12 +02:00
function twig_test_empty ( $value )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
if ( $value instanceof Countable ) {
return 0 == count ( $value );
}
2011-10-05 06:22:53 +02:00
2018-05-10 12:24:53 +02:00
if ( is_object ( $value ) && method_exists ( $value , '__toString' )) {
return '' === ( string ) $value ;
}
2013-08-01 21:20:12 +02:00
return '' === $value || false === $value || null === $value || array () === $value ;
2011-10-05 06:22:53 +02:00
}
/**
2013-08-01 21:20:12 +02:00
* Checks if a variable is traversable .
2011-10-05 06:22:53 +02:00
*
* < pre >
2013-08-01 21:20:12 +02:00
* { # evaluates to true if the foo variable is an array or a traversable object #}
2018-05-10 12:24:53 +02:00
* { % if foo is iterable % }
2013-08-01 21:20:12 +02:00
* { # ... #}
* { % endif % }
2011-10-05 06:22:53 +02:00
* </ pre >
*
2013-08-01 21:20:12 +02:00
* @ param mixed $value A variable
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ return bool true if the value is traversable
2011-10-05 06:22:53 +02:00
*/
2013-08-01 21:20:12 +02:00
function twig_test_iterable ( $value )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
return $value instanceof Traversable || is_array ( $value );
2011-10-05 06:22:53 +02:00
}
/**
2013-08-01 21:20:12 +02:00
* Renders a template .
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ param Twig_Environment $env
* @ param array $context
* @ param string | array $template The template to render or an array of templates to try consecutively
* @ param array $variables The variables to pass to the template
* @ param bool $withContext
* @ param bool $ignoreMissing Whether to ignore missing templates or not
* @ param bool $sandboxed Whether to sandbox the template or not
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ return string The rendered template
2011-10-05 06:22:53 +02:00
*/
2013-08-01 21:20:12 +02:00
function twig_include ( Twig_Environment $env , $context , $template , $variables = array (), $withContext = true , $ignoreMissing = false , $sandboxed = false )
2011-10-05 06:22:53 +02:00
{
2018-05-10 12:24:53 +02:00
$alreadySandboxed = false ;
$sandbox = null ;
2013-08-01 21:20:12 +02:00
if ( $withContext ) {
$variables = array_merge ( $context , $variables );
}
2011-10-05 06:22:53 +02:00
2018-05-10 12:24:53 +02:00
if ( $isSandboxed = $sandboxed && $env -> hasExtension ( 'Twig_Extension_Sandbox' )) {
$sandbox = $env -> getExtension ( 'Twig_Extension_Sandbox' );
2013-08-01 21:20:12 +02:00
if ( ! $alreadySandboxed = $sandbox -> isSandboxed ()) {
$sandbox -> enableSandbox ();
}
}
2011-10-05 06:22:53 +02:00
2018-05-10 12:24:53 +02:00
$result = null ;
2013-08-01 21:20:12 +02:00
try {
2018-05-10 12:24:53 +02:00
$result = $env -> resolveTemplate ( $template ) -> render ( $variables );
2013-08-01 21:20:12 +02:00
} catch ( Twig_Error_Loader $e ) {
if ( ! $ignoreMissing ) {
2018-05-10 12:24:53 +02:00
if ( $isSandboxed && ! $alreadySandboxed ) {
$sandbox -> disableSandbox ();
}
2013-08-01 21:20:12 +02:00
throw $e ;
}
2018-05-10 12:24:53 +02:00
} catch ( Throwable $e ) {
if ( $isSandboxed && ! $alreadySandboxed ) {
$sandbox -> disableSandbox ();
}
throw $e ;
} catch ( Exception $e ) {
if ( $isSandboxed && ! $alreadySandboxed ) {
$sandbox -> disableSandbox ();
}
throw $e ;
2013-08-01 21:20:12 +02:00
}
if ( $isSandboxed && ! $alreadySandboxed ) {
$sandbox -> disableSandbox ();
}
2018-05-10 12:24:53 +02:00
return $result ;
}
/**
* Returns a template content without rendering it .
*
* @ param Twig_Environment $env
* @ param string $name The template name
* @ param bool $ignoreMissing Whether to ignore missing templates or not
*
* @ return string The template source
*/
function twig_source ( Twig_Environment $env , $name , $ignoreMissing = false )
{
$loader = $env -> getLoader ();
try {
if ( ! $loader instanceof Twig_SourceContextLoaderInterface ) {
return $loader -> getSource ( $name );
} else {
return $loader -> getSourceContext ( $name ) -> getCode ();
}
} catch ( Twig_Error_Loader $e ) {
if ( ! $ignoreMissing ) {
throw $e ;
}
}
2011-10-05 06:22:53 +02:00
}
/**
2013-08-01 21:20:12 +02:00
* Provides the ability to get constants from instances as well as class / global constants .
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ param string $constant The name of the constant
* @ param null | object $object The object to get the constant from
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ return string
2011-10-05 06:22:53 +02:00
*/
2013-08-01 21:20:12 +02:00
function twig_constant ( $constant , $object = null )
2011-10-05 06:22:53 +02:00
{
2013-08-01 21:20:12 +02:00
if ( null !== $object ) {
$constant = get_class ( $object ) . '::' . $constant ;
}
return constant ( $constant );
2011-10-05 06:22:53 +02:00
}
2018-05-10 12:24:53 +02:00
/**
* Checks if a constant exists .
*
* @ param string $constant The name of the constant
* @ param null | object $object The object to get the constant from
*
* @ return bool
*/
function twig_constant_is_defined ( $constant , $object = null )
{
if ( null !== $object ) {
$constant = get_class ( $object ) . '::' . $constant ;
}
return defined ( $constant );
}
2011-10-05 06:22:53 +02:00
/**
2013-08-01 21:20:12 +02:00
* Batches item .
2011-10-05 06:22:53 +02:00
*
2018-05-10 12:24:53 +02:00
* @ param array $items An array of items
* @ param int $size The size of the batch
* @ param mixed $fill A value used to fill missing items
2011-10-05 06:22:53 +02:00
*
2013-08-01 21:20:12 +02:00
* @ return array
2011-10-05 06:22:53 +02:00
*/
2013-08-01 21:20:12 +02:00
function twig_array_batch ( $items , $size , $fill = null )
2011-10-05 06:22:53 +02:00
{
2018-05-10 12:24:53 +02:00
if ( $items instanceof Traversable ) {
2013-08-01 21:20:12 +02:00
$items = iterator_to_array ( $items , false );
2011-10-05 06:22:53 +02:00
}
2013-08-01 21:20:12 +02:00
$size = ceil ( $size );
$result = array_chunk ( $items , $size , true );
2018-05-10 12:24:53 +02:00
if ( null !== $fill && ! empty ( $result )) {
2013-08-01 21:20:12 +02:00
$last = count ( $result ) - 1 ;
2013-09-19 08:08:25 +02:00
if ( $fillCount = $size - count ( $result [ $last ])) {
$result [ $last ] = array_merge (
$result [ $last ],
array_fill ( 0 , $fillCount , $fill )
);
}
2013-08-01 21:20:12 +02:00
}
return $result ;
2011-10-05 06:22:53 +02:00
}
2018-05-10 12:24:53 +02:00
class_alias ( 'Twig_Extension_Core' , 'Twig\Extension\CoreExtension' , false );