[ Index ] |
PHP Cross Reference of phpBB-3.2.11-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of Twig. 5 * 6 * (c) Fabien Potencier 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12 namespace Twig\Extension { 13 use Twig\ExpressionParser; 14 use Twig\TokenParser\ApplyTokenParser; 15 use Twig\TokenParser\BlockTokenParser; 16 use Twig\TokenParser\DeprecatedTokenParser; 17 use Twig\TokenParser\DoTokenParser; 18 use Twig\TokenParser\EmbedTokenParser; 19 use Twig\TokenParser\ExtendsTokenParser; 20 use Twig\TokenParser\FilterTokenParser; 21 use Twig\TokenParser\FlushTokenParser; 22 use Twig\TokenParser\ForTokenParser; 23 use Twig\TokenParser\FromTokenParser; 24 use Twig\TokenParser\IfTokenParser; 25 use Twig\TokenParser\ImportTokenParser; 26 use Twig\TokenParser\IncludeTokenParser; 27 use Twig\TokenParser\MacroTokenParser; 28 use Twig\TokenParser\SetTokenParser; 29 use Twig\TokenParser\SpacelessTokenParser; 30 use Twig\TokenParser\UseTokenParser; 31 use Twig\TokenParser\WithTokenParser; 32 use Twig\TwigFilter; 33 use Twig\TwigFunction; 34 use Twig\TwigTest; 35 36 /** 37 * @final 38 */ 39 class CoreExtension extends AbstractExtension 40 { 41 protected $dateFormats = ['F j, Y H:i', '%d days']; 42 protected $numberFormat = [0, '.', ',']; 43 protected $timezone = null; 44 protected $escapers = []; 45 46 /** 47 * Defines a new escaper to be used via the escape filter. 48 * 49 * @param string $strategy The strategy name that should be used as a strategy in the escape call 50 * @param callable $callable A valid PHP callable 51 */ 52 public function setEscaper($strategy, $callable) 53 { 54 $this->escapers[$strategy] = $callable; 55 } 56 57 /** 58 * Gets all defined escapers. 59 * 60 * @return array An array of escapers 61 */ 62 public function getEscapers() 63 { 64 return $this->escapers; 65 } 66 67 /** 68 * Sets the default format to be used by the date filter. 69 * 70 * @param string $format The default date format string 71 * @param string $dateIntervalFormat The default date interval format string 72 */ 73 public function setDateFormat($format = null, $dateIntervalFormat = null) 74 { 75 if (null !== $format) { 76 $this->dateFormats[0] = $format; 77 } 78 79 if (null !== $dateIntervalFormat) { 80 $this->dateFormats[1] = $dateIntervalFormat; 81 } 82 } 83 84 /** 85 * Gets the default format to be used by the date filter. 86 * 87 * @return array The default date format string and the default date interval format string 88 */ 89 public function getDateFormat() 90 { 91 return $this->dateFormats; 92 } 93 94 /** 95 * Sets the default timezone to be used by the date filter. 96 * 97 * @param \DateTimeZone|string $timezone The default timezone string or a \DateTimeZone object 98 */ 99 public function setTimezone($timezone) 100 { 101 $this->timezone = $timezone instanceof \DateTimeZone ? $timezone : new \DateTimeZone($timezone); 102 } 103 104 /** 105 * Gets the default timezone to be used by the date filter. 106 * 107 * @return \DateTimeZone The default timezone currently in use 108 */ 109 public function getTimezone() 110 { 111 if (null === $this->timezone) { 112 $this->timezone = new \DateTimeZone(date_default_timezone_get()); 113 } 114 115 return $this->timezone; 116 } 117 118 /** 119 * Sets the default format to be used by the number_format filter. 120 * 121 * @param int $decimal the number of decimal places to use 122 * @param string $decimalPoint the character(s) to use for the decimal point 123 * @param string $thousandSep the character(s) to use for the thousands separator 124 */ 125 public function setNumberFormat($decimal, $decimalPoint, $thousandSep) 126 { 127 $this->numberFormat = [$decimal, $decimalPoint, $thousandSep]; 128 } 129 130 /** 131 * Get the default format used by the number_format filter. 132 * 133 * @return array The arguments for number_format() 134 */ 135 public function getNumberFormat() 136 { 137 return $this->numberFormat; 138 } 139 140 public function getTokenParsers() 141 { 142 return [ 143 new ApplyTokenParser(), 144 new ForTokenParser(), 145 new IfTokenParser(), 146 new ExtendsTokenParser(), 147 new IncludeTokenParser(), 148 new BlockTokenParser(), 149 new UseTokenParser(), 150 new FilterTokenParser(), 151 new MacroTokenParser(), 152 new ImportTokenParser(), 153 new FromTokenParser(), 154 new SetTokenParser(), 155 new SpacelessTokenParser(), 156 new FlushTokenParser(), 157 new DoTokenParser(), 158 new EmbedTokenParser(), 159 new WithTokenParser(), 160 new DeprecatedTokenParser(), 161 ]; 162 } 163 164 public function getFilters() 165 { 166 $filters = [ 167 // formatting filters 168 new TwigFilter('date', 'twig_date_format_filter', ['needs_environment' => true]), 169 new TwigFilter('date_modify', 'twig_date_modify_filter', ['needs_environment' => true]), 170 new TwigFilter('format', 'sprintf'), 171 new TwigFilter('replace', 'twig_replace_filter'), 172 new TwigFilter('number_format', 'twig_number_format_filter', ['needs_environment' => true]), 173 new TwigFilter('abs', 'abs'), 174 new TwigFilter('round', 'twig_round'), 175 176 // encoding 177 new TwigFilter('url_encode', 'twig_urlencode_filter'), 178 new TwigFilter('json_encode', 'twig_jsonencode_filter'), 179 new TwigFilter('convert_encoding', 'twig_convert_encoding'), 180 181 // string filters 182 new TwigFilter('title', 'twig_title_string_filter', ['needs_environment' => true]), 183 new TwigFilter('capitalize', 'twig_capitalize_string_filter', ['needs_environment' => true]), 184 new TwigFilter('upper', 'strtoupper'), 185 new TwigFilter('lower', 'strtolower'), 186 new TwigFilter('striptags', 'strip_tags'), 187 new TwigFilter('trim', 'twig_trim_filter'), 188 new TwigFilter('nl2br', 'nl2br', ['pre_escape' => 'html', 'is_safe' => ['html']]), 189 new TwigFilter('spaceless', 'twig_spaceless', ['is_safe' => ['html']]), 190 191 // array helpers 192 new TwigFilter('join', 'twig_join_filter'), 193 new TwigFilter('split', 'twig_split_filter', ['needs_environment' => true]), 194 new TwigFilter('sort', 'twig_sort_filter'), 195 new TwigFilter('merge', 'twig_array_merge'), 196 new TwigFilter('batch', 'twig_array_batch'), 197 new TwigFilter('filter', 'twig_array_filter'), 198 new TwigFilter('map', 'twig_array_map'), 199 new TwigFilter('reduce', 'twig_array_reduce'), 200 201 // string/array filters 202 new TwigFilter('reverse', 'twig_reverse_filter', ['needs_environment' => true]), 203 new TwigFilter('length', 'twig_length_filter', ['needs_environment' => true]), 204 new TwigFilter('slice', 'twig_slice', ['needs_environment' => true]), 205 new TwigFilter('first', 'twig_first', ['needs_environment' => true]), 206 new TwigFilter('last', 'twig_last', ['needs_environment' => true]), 207 208 // iteration and runtime 209 new TwigFilter('default', '_twig_default_filter', ['node_class' => '\Twig\Node\Expression\Filter\DefaultFilter']), 210 new TwigFilter('keys', 'twig_get_array_keys_filter'), 211 212 // escaping 213 new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']), 214 new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']), 215 ]; 216 217 if (\function_exists('mb_get_info')) { 218 $filters[] = new TwigFilter('upper', 'twig_upper_filter', ['needs_environment' => true]); 219 $filters[] = new TwigFilter('lower', 'twig_lower_filter', ['needs_environment' => true]); 220 } 221 222 return $filters; 223 } 224 225 public function getFunctions() 226 { 227 return [ 228 new TwigFunction('max', 'max'), 229 new TwigFunction('min', 'min'), 230 new TwigFunction('range', 'range'), 231 new TwigFunction('constant', 'twig_constant'), 232 new TwigFunction('cycle', 'twig_cycle'), 233 new TwigFunction('random', 'twig_random', ['needs_environment' => true]), 234 new TwigFunction('date', 'twig_date_converter', ['needs_environment' => true]), 235 new TwigFunction('include', 'twig_include', ['needs_environment' => true, 'needs_context' => true, 'is_safe' => ['all']]), 236 new TwigFunction('source', 'twig_source', ['needs_environment' => true, 'is_safe' => ['all']]), 237 ]; 238 } 239 240 public function getTests() 241 { 242 return [ 243 new TwigTest('even', null, ['node_class' => '\Twig\Node\Expression\Test\EvenTest']), 244 new TwigTest('odd', null, ['node_class' => '\Twig\Node\Expression\Test\OddTest']), 245 new TwigTest('defined', null, ['node_class' => '\Twig\Node\Expression\Test\DefinedTest']), 246 new TwigTest('sameas', null, ['node_class' => '\Twig\Node\Expression\Test\SameasTest', 'deprecated' => '1.21', 'alternative' => 'same as']), 247 new TwigTest('same as', null, ['node_class' => '\Twig\Node\Expression\Test\SameasTest']), 248 new TwigTest('none', null, ['node_class' => '\Twig\Node\Expression\Test\NullTest']), 249 new TwigTest('null', null, ['node_class' => '\Twig\Node\Expression\Test\NullTest']), 250 new TwigTest('divisibleby', null, ['node_class' => '\Twig\Node\Expression\Test\DivisiblebyTest', 'deprecated' => '1.21', 'alternative' => 'divisible by']), 251 new TwigTest('divisible by', null, ['node_class' => '\Twig\Node\Expression\Test\DivisiblebyTest']), 252 new TwigTest('constant', null, ['node_class' => '\Twig\Node\Expression\Test\ConstantTest']), 253 new TwigTest('empty', 'twig_test_empty'), 254 new TwigTest('iterable', 'twig_test_iterable'), 255 ]; 256 } 257 258 public function getOperators() 259 { 260 return [ 261 [ 262 'not' => ['precedence' => 50, 'class' => '\Twig\Node\Expression\Unary\NotUnary'], 263 '-' => ['precedence' => 500, 'class' => '\Twig\Node\Expression\Unary\NegUnary'], 264 '+' => ['precedence' => 500, 'class' => '\Twig\Node\Expression\Unary\PosUnary'], 265 ], 266 [ 267 'or' => ['precedence' => 10, 'class' => '\Twig\Node\Expression\Binary\OrBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 268 'and' => ['precedence' => 15, 'class' => '\Twig\Node\Expression\Binary\AndBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 269 'b-or' => ['precedence' => 16, 'class' => '\Twig\Node\Expression\Binary\BitwiseOrBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 270 'b-xor' => ['precedence' => 17, 'class' => '\Twig\Node\Expression\Binary\BitwiseXorBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 271 'b-and' => ['precedence' => 18, 'class' => '\Twig\Node\Expression\Binary\BitwiseAndBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 272 '==' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\EqualBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 273 '!=' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\NotEqualBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 274 '<' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\LessBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 275 '>' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\GreaterBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 276 '>=' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\GreaterEqualBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 277 '<=' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\LessEqualBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 278 'not in' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\NotInBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 279 'in' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\InBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 280 'matches' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\MatchesBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 281 'starts with' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\StartsWithBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 282 'ends with' => ['precedence' => 20, 'class' => '\Twig\Node\Expression\Binary\EndsWithBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 283 '..' => ['precedence' => 25, 'class' => '\Twig\Node\Expression\Binary\RangeBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 284 '+' => ['precedence' => 30, 'class' => '\Twig\Node\Expression\Binary\AddBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 285 '-' => ['precedence' => 30, 'class' => '\Twig\Node\Expression\Binary\SubBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 286 '~' => ['precedence' => 40, 'class' => '\Twig\Node\Expression\Binary\ConcatBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 287 '*' => ['precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\MulBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 288 '/' => ['precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\DivBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 289 '//' => ['precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\FloorDivBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 290 '%' => ['precedence' => 60, 'class' => '\Twig\Node\Expression\Binary\ModBinary', 'associativity' => ExpressionParser::OPERATOR_LEFT], 291 'is' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT], 292 'is not' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT], 293 '**' => ['precedence' => 200, 'class' => '\Twig\Node\Expression\Binary\PowerBinary', 'associativity' => ExpressionParser::OPERATOR_RIGHT], 294 '??' => ['precedence' => 300, 'class' => '\Twig\Node\Expression\NullCoalesceExpression', 'associativity' => ExpressionParser::OPERATOR_RIGHT], 295 ], 296 ]; 297 } 298 299 public function getName() 300 { 301 return 'core'; 302 } 303 } 304 305 class_alias('Twig\Extension\CoreExtension', 'Twig_Extension_Core'); 306 } 307 308 namespace { 309 use Twig\Environment; 310 use Twig\Error\LoaderError; 311 use Twig\Error\RuntimeError; 312 use Twig\Loader\SourceContextLoaderInterface; 313 use Twig\Markup; 314 use Twig\Node\Expression\ConstantExpression; 315 use Twig\Node\Node; 316 317 /** 318 * Cycles over a value. 319 * 320 * @param \ArrayAccess|array $values 321 * @param int $position The cycle position 322 * 323 * @return string The next value in the cycle 324 */ 325 function twig_cycle($values, $position) 326 { 327 if (!\is_array($values) && !$values instanceof \ArrayAccess) { 328 return $values; 329 } 330 331 return $values[$position % \count($values)]; 332 } 333 334 /** 335 * Returns a random value depending on the supplied parameter type: 336 * - a random item from a \Traversable or array 337 * - a random character from a string 338 * - a random integer between 0 and the integer parameter. 339 * 340 * @param \Traversable|array|int|float|string $values The values to pick a random item from 341 * @param int|null $max Maximum value used when $values is an int 342 * 343 * @throws RuntimeError when $values is an empty array (does not apply to an empty string which is returned as is) 344 * 345 * @return mixed A random value from the given sequence 346 */ 347 function twig_random(Environment $env, $values = null, $max = null) 348 { 349 if (null === $values) { 350 return null === $max ? mt_rand() : mt_rand(0, $max); 351 } 352 353 if (\is_int($values) || \is_float($values)) { 354 if (null === $max) { 355 if ($values < 0) { 356 $max = 0; 357 $min = $values; 358 } else { 359 $max = $values; 360 $min = 0; 361 } 362 } else { 363 $min = $values; 364 $max = $max; 365 } 366 367 return mt_rand($min, $max); 368 } 369 370 if (\is_string($values)) { 371 if ('' === $values) { 372 return ''; 373 } 374 if (null !== $charset = $env->getCharset()) { 375 if ('UTF-8' !== $charset) { 376 $values = twig_convert_encoding($values, 'UTF-8', $charset); 377 } 378 379 // unicode version of str_split() 380 // split at all positions, but not after the start and not before the end 381 $values = preg_split('/(?<!^)(?!$)/u', $values); 382 383 if ('UTF-8' !== $charset) { 384 foreach ($values as $i => $value) { 385 $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8'); 386 } 387 } 388 } else { 389 return $values[mt_rand(0, \strlen($values) - 1)]; 390 } 391 } 392 393 if (!twig_test_iterable($values)) { 394 return $values; 395 } 396 397 $values = twig_to_array($values); 398 399 if (0 === \count($values)) { 400 throw new RuntimeError('The random function cannot pick from an empty array.'); 401 } 402 403 return $values[array_rand($values, 1)]; 404 } 405 406 /** 407 * Converts a date to the given format. 408 * 409 * {{ post.published_at|date("m/d/Y") }} 410 * 411 * @param \DateTime|\DateTimeInterface|\DateInterval|string $date A date 412 * @param string|null $format The target format, null to use the default 413 * @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged 414 * 415 * @return string The formatted date 416 */ 417 function twig_date_format_filter(Environment $env, $date, $format = null, $timezone = null) 418 { 419 if (null === $format) { 420 $formats = $env->getExtension('\Twig\Extension\CoreExtension')->getDateFormat(); 421 $format = $date instanceof \DateInterval ? $formats[1] : $formats[0]; 422 } 423 424 if ($date instanceof \DateInterval) { 425 return $date->format($format); 426 } 427 428 return twig_date_converter($env, $date, $timezone)->format($format); 429 } 430 431 /** 432 * Returns a new date object modified. 433 * 434 * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }} 435 * 436 * @param \DateTime|string $date A date 437 * @param string $modifier A modifier string 438 * 439 * @return \DateTime 440 */ 441 function twig_date_modify_filter(Environment $env, $date, $modifier) 442 { 443 $date = twig_date_converter($env, $date, false); 444 $resultDate = $date->modify($modifier); 445 446 // This is a hack to ensure PHP 5.2 support and support for \DateTimeImmutable 447 // \DateTime::modify does not return the modified \DateTime object < 5.3.0 448 // and \DateTimeImmutable does not modify $date. 449 return null === $resultDate ? $date : $resultDate; 450 } 451 452 /** 453 * Converts an input to a \DateTime instance. 454 * 455 * {% if date(user.created_at) < date('+2days') %} 456 * {# do something #} 457 * {% endif %} 458 * 459 * @param \DateTime|\DateTimeInterface|string|null $date A date 460 * @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged 461 * 462 * @return \DateTime 463 */ 464 function twig_date_converter(Environment $env, $date = null, $timezone = null) 465 { 466 // determine the timezone 467 if (false !== $timezone) { 468 if (null === $timezone) { 469 $timezone = $env->getExtension('\Twig\Extension\CoreExtension')->getTimezone(); 470 } elseif (!$timezone instanceof \DateTimeZone) { 471 $timezone = new \DateTimeZone($timezone); 472 } 473 } 474 475 // immutable dates 476 if ($date instanceof \DateTimeImmutable) { 477 return false !== $timezone ? $date->setTimezone($timezone) : $date; 478 } 479 480 if ($date instanceof \DateTime || $date instanceof \DateTimeInterface) { 481 $date = clone $date; 482 if (false !== $timezone) { 483 $date->setTimezone($timezone); 484 } 485 486 return $date; 487 } 488 489 if (null === $date || 'now' === $date) { 490 return new \DateTime($date, false !== $timezone ? $timezone : $env->getExtension('\Twig\Extension\CoreExtension')->getTimezone()); 491 } 492 493 $asString = (string) $date; 494 if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { 495 $date = new \DateTime('@'.$date); 496 } else { 497 $date = new \DateTime($date, $env->getExtension('\Twig\Extension\CoreExtension')->getTimezone()); 498 } 499 500 if (false !== $timezone) { 501 $date->setTimezone($timezone); 502 } 503 504 return $date; 505 } 506 507 /** 508 * Replaces strings within a string. 509 * 510 * @param string $str String to replace in 511 * @param array|\Traversable $from Replace values 512 * @param string|null $to Replace to, deprecated (@see https://secure.php.net/manual/en/function.strtr.php) 513 * 514 * @return string 515 */ 516 function twig_replace_filter($str, $from, $to = null) 517 { 518 if (\is_string($from) && \is_string($to)) { 519 @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); 520 521 return strtr($str, $from, $to); 522 } 523 524 if (!twig_test_iterable($from)) { 525 throw new RuntimeError(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".', \is_object($from) ? \get_class($from) : \gettype($from))); 526 } 527 528 return strtr($str, twig_to_array($from)); 529 } 530 531 /** 532 * Rounds a number. 533 * 534 * @param int|float $value The value to round 535 * @param int|float $precision The rounding precision 536 * @param string $method The method to use for rounding 537 * 538 * @return int|float The rounded number 539 */ 540 function twig_round($value, $precision = 0, $method = 'common') 541 { 542 if ('common' == $method) { 543 return round($value, $precision); 544 } 545 546 if ('ceil' != $method && 'floor' != $method) { 547 throw new RuntimeError('The round filter only supports the "common", "ceil", and "floor" methods.'); 548 } 549 550 return $method($value * pow(10, $precision)) / pow(10, $precision); 551 } 552 553 /** 554 * Number format filter. 555 * 556 * All of the formatting options can be left null, in that case the defaults will 557 * be used. Supplying any of the parameters will override the defaults set in the 558 * environment object. 559 * 560 * @param mixed $number A float/int/string of the number to format 561 * @param int $decimal the number of decimal points to display 562 * @param string $decimalPoint the character(s) to use for the decimal point 563 * @param string $thousandSep the character(s) to use for the thousands separator 564 * 565 * @return string The formatted number 566 */ 567 function twig_number_format_filter(Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null) 568 { 569 $defaults = $env->getExtension('\Twig\Extension\CoreExtension')->getNumberFormat(); 570 if (null === $decimal) { 571 $decimal = $defaults[0]; 572 } 573 574 if (null === $decimalPoint) { 575 $decimalPoint = $defaults[1]; 576 } 577 578 if (null === $thousandSep) { 579 $thousandSep = $defaults[2]; 580 } 581 582 return number_format((float) $number, $decimal, $decimalPoint, $thousandSep); 583 } 584 585 /** 586 * URL encodes (RFC 3986) a string as a path segment or an array as a query string. 587 * 588 * @param string|array $url A URL or an array of query parameters 589 * 590 * @return string The URL encoded value 591 */ 592 function twig_urlencode_filter($url) 593 { 594 if (\is_array($url)) { 595 if (\defined('PHP_QUERY_RFC3986')) { 596 return http_build_query($url, '', '&', PHP_QUERY_RFC3986); 597 } 598 599 return http_build_query($url, '', '&'); 600 } 601 602 return rawurlencode($url); 603 } 604 605 /** 606 * JSON encodes a variable. 607 * 608 * @param mixed $value the value to encode 609 * @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 610 * 611 * @return mixed The JSON encoded value 612 */ 613 function twig_jsonencode_filter($value, $options = 0) 614 { 615 if ($value instanceof Markup) { 616 $value = (string) $value; 617 } elseif (\is_array($value)) { 618 array_walk_recursive($value, '_twig_markup2string'); 619 } 620 621 return json_encode($value, $options); 622 } 623 624 function _twig_markup2string(&$value) 625 { 626 if ($value instanceof Markup) { 627 $value = (string) $value; 628 } 629 } 630 631 /** 632 * Merges an array with another one. 633 * 634 * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %} 635 * 636 * {% set items = items|merge({ 'peugeot': 'car' }) %} 637 * 638 * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #} 639 * 640 * @param array|\Traversable $arr1 An array 641 * @param array|\Traversable $arr2 An array 642 * 643 * @return array The merged array 644 */ 645 function twig_array_merge($arr1, $arr2) 646 { 647 if (!twig_test_iterable($arr1)) { 648 throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($arr1))); 649 } 650 651 if (!twig_test_iterable($arr2)) { 652 throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', \gettype($arr2))); 653 } 654 655 return array_merge(twig_to_array($arr1), twig_to_array($arr2)); 656 } 657 658 /** 659 * Slices a variable. 660 * 661 * @param mixed $item A variable 662 * @param int $start Start of the slice 663 * @param int $length Size of the slice 664 * @param bool $preserveKeys Whether to preserve key or not (when the input is an array) 665 * 666 * @return mixed The sliced variable 667 */ 668 function twig_slice(Environment $env, $item, $start, $length = null, $preserveKeys = false) 669 { 670 if ($item instanceof \Traversable) { 671 while ($item instanceof \IteratorAggregate) { 672 $item = $item->getIterator(); 673 } 674 675 if ($start >= 0 && $length >= 0 && $item instanceof \Iterator) { 676 try { 677 return iterator_to_array(new \LimitIterator($item, $start, null === $length ? -1 : $length), $preserveKeys); 678 } catch (\OutOfBoundsException $e) { 679 return []; 680 } 681 } 682 683 $item = iterator_to_array($item, $preserveKeys); 684 } 685 686 if (\is_array($item)) { 687 return \array_slice($item, $start, $length, $preserveKeys); 688 } 689 690 $item = (string) $item; 691 692 if (\function_exists('mb_get_info') && null !== $charset = $env->getCharset()) { 693 return (string) mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset); 694 } 695 696 return (string) (null === $length ? substr($item, $start) : substr($item, $start, $length)); 697 } 698 699 /** 700 * Returns the first element of the item. 701 * 702 * @param mixed $item A variable 703 * 704 * @return mixed The first element of the item 705 */ 706 function twig_first(Environment $env, $item) 707 { 708 $elements = twig_slice($env, $item, 0, 1, false); 709 710 return \is_string($elements) ? $elements : current($elements); 711 } 712 713 /** 714 * Returns the last element of the item. 715 * 716 * @param mixed $item A variable 717 * 718 * @return mixed The last element of the item 719 */ 720 function twig_last(Environment $env, $item) 721 { 722 $elements = twig_slice($env, $item, -1, 1, false); 723 724 return \is_string($elements) ? $elements : current($elements); 725 } 726 727 /** 728 * Joins the values to a string. 729 * 730 * The separators between elements are empty strings per default, you can define them with the optional parameters. 731 * 732 * {{ [1, 2, 3]|join(', ', ' and ') }} 733 * {# returns 1, 2 and 3 #} 734 * 735 * {{ [1, 2, 3]|join('|') }} 736 * {# returns 1|2|3 #} 737 * 738 * {{ [1, 2, 3]|join }} 739 * {# returns 123 #} 740 * 741 * @param array $value An array 742 * @param string $glue The separator 743 * @param string|null $and The separator for the last pair 744 * 745 * @return string The concatenated string 746 */ 747 function twig_join_filter($value, $glue = '', $and = null) 748 { 749 if (!twig_test_iterable($value)) { 750 $value = (array) $value; 751 } 752 753 $value = twig_to_array($value, false); 754 755 if (0 === \count($value)) { 756 return ''; 757 } 758 759 if (null === $and || $and === $glue) { 760 return implode($glue, $value); 761 } 762 763 if (1 === \count($value)) { 764 return $value[0]; 765 } 766 767 return implode($glue, \array_slice($value, 0, -1)).$and.$value[\count($value) - 1]; 768 } 769 770 /** 771 * Splits the string into an array. 772 * 773 * {{ "one,two,three"|split(',') }} 774 * {# returns [one, two, three] #} 775 * 776 * {{ "one,two,three,four,five"|split(',', 3) }} 777 * {# returns [one, two, "three,four,five"] #} 778 * 779 * {{ "123"|split('') }} 780 * {# returns [1, 2, 3] #} 781 * 782 * {{ "aabbcc"|split('', 2) }} 783 * {# returns [aa, bb, cc] #} 784 * 785 * @param string $value A string 786 * @param string $delimiter The delimiter 787 * @param int $limit The limit 788 * 789 * @return array The split string as an array 790 */ 791 function twig_split_filter(Environment $env, $value, $delimiter, $limit = null) 792 { 793 if (!empty($delimiter)) { 794 return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit); 795 } 796 797 if (!\function_exists('mb_get_info') || null === $charset = $env->getCharset()) { 798 return str_split($value, null === $limit ? 1 : $limit); 799 } 800 801 if ($limit <= 1) { 802 return preg_split('/(?<!^)(?!$)/u', $value); 803 } 804 805 $length = mb_strlen($value, $charset); 806 if ($length < $limit) { 807 return [$value]; 808 } 809 810 $r = []; 811 for ($i = 0; $i < $length; $i += $limit) { 812 $r[] = mb_substr($value, $i, $limit, $charset); 813 } 814 815 return $r; 816 } 817 818 // The '_default' filter is used internally to avoid using the ternary operator 819 // which costs a lot for big contexts (before PHP 5.4). So, on average, 820 // a function call is cheaper. 821 /** 822 * @internal 823 */ 824 function _twig_default_filter($value, $default = '') 825 { 826 if (twig_test_empty($value)) { 827 return $default; 828 } 829 830 return $value; 831 } 832 833 /** 834 * Returns the keys for the given array. 835 * 836 * It is useful when you want to iterate over the keys of an array: 837 * 838 * {% for key in array|keys %} 839 * {# ... #} 840 * {% endfor %} 841 * 842 * @param array $array An array 843 * 844 * @return array The keys 845 */ 846 function twig_get_array_keys_filter($array) 847 { 848 if ($array instanceof \Traversable) { 849 while ($array instanceof \IteratorAggregate) { 850 $array = $array->getIterator(); 851 } 852 853 if ($array instanceof \Iterator) { 854 $keys = []; 855 $array->rewind(); 856 while ($array->valid()) { 857 $keys[] = $array->key(); 858 $array->next(); 859 } 860 861 return $keys; 862 } 863 864 $keys = []; 865 foreach ($array as $key => $item) { 866 $keys[] = $key; 867 } 868 869 return $keys; 870 } 871 872 if (!\is_array($array)) { 873 return []; 874 } 875 876 return array_keys($array); 877 } 878 879 /** 880 * Reverses a variable. 881 * 882 * @param array|\Traversable|string $item An array, a \Traversable instance, or a string 883 * @param bool $preserveKeys Whether to preserve key or not 884 * 885 * @return mixed The reversed input 886 */ 887 function twig_reverse_filter(Environment $env, $item, $preserveKeys = false) 888 { 889 if ($item instanceof \Traversable) { 890 return array_reverse(iterator_to_array($item), $preserveKeys); 891 } 892 893 if (\is_array($item)) { 894 return array_reverse($item, $preserveKeys); 895 } 896 897 if (null !== $charset = $env->getCharset()) { 898 $string = (string) $item; 899 900 if ('UTF-8' !== $charset) { 901 $item = twig_convert_encoding($string, 'UTF-8', $charset); 902 } 903 904 preg_match_all('/./us', $item, $matches); 905 906 $string = implode('', array_reverse($matches[0])); 907 908 if ('UTF-8' !== $charset) { 909 $string = twig_convert_encoding($string, $charset, 'UTF-8'); 910 } 911 912 return $string; 913 } 914 915 return strrev((string) $item); 916 } 917 918 /** 919 * Sorts an array. 920 * 921 * @param array|\Traversable $array 922 * 923 * @return array 924 */ 925 function twig_sort_filter($array) 926 { 927 if ($array instanceof \Traversable) { 928 $array = iterator_to_array($array); 929 } elseif (!\is_array($array)) { 930 throw new RuntimeError(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', \gettype($array))); 931 } 932 933 asort($array); 934 935 return $array; 936 } 937 938 /** 939 * @internal 940 */ 941 function twig_in_filter($value, $compare) 942 { 943 if ($value instanceof Markup) { 944 $value = (string) $value; 945 } 946 if ($compare instanceof Markup) { 947 $compare = (string) $compare; 948 } 949 950 if (\is_array($compare)) { 951 return \in_array($value, $compare, \is_object($value) || \is_resource($value)); 952 } elseif (\is_string($compare) && (\is_string($value) || \is_int($value) || \is_float($value))) { 953 return '' === $value || false !== strpos($compare, (string) $value); 954 } elseif ($compare instanceof \Traversable) { 955 if (\is_object($value) || \is_resource($value)) { 956 foreach ($compare as $item) { 957 if ($item === $value) { 958 return true; 959 } 960 } 961 } else { 962 foreach ($compare as $item) { 963 if ($item == $value) { 964 return true; 965 } 966 } 967 } 968 969 return false; 970 } 971 972 return false; 973 } 974 975 /** 976 * Returns a trimmed string. 977 * 978 * @return string 979 * 980 * @throws RuntimeError When an invalid trimming side is used (not a string or not 'left', 'right', or 'both') 981 */ 982 function twig_trim_filter($string, $characterMask = null, $side = 'both') 983 { 984 if (null === $characterMask) { 985 $characterMask = " \t\n\r\0\x0B"; 986 } 987 988 switch ($side) { 989 case 'both': 990 return trim($string, $characterMask); 991 case 'left': 992 return ltrim($string, $characterMask); 993 case 'right': 994 return rtrim($string, $characterMask); 995 default: 996 throw new RuntimeError('Trimming side must be "left", "right" or "both".'); 997 } 998 } 999 1000 /** 1001 * Removes whitespaces between HTML tags. 1002 * 1003 * @return string 1004 */ 1005 function twig_spaceless($content) 1006 { 1007 return trim(preg_replace('/>\s+</', '><', $content)); 1008 } 1009 1010 /** 1011 * Escapes a string. 1012 * 1013 * @param mixed $string The value to be escaped 1014 * @param string $strategy The escaping strategy 1015 * @param string $charset The charset 1016 * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) 1017 * 1018 * @return string 1019 */ 1020 function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) 1021 { 1022 if ($autoescape && $string instanceof Markup) { 1023 return $string; 1024 } 1025 1026 if (!\is_string($string)) { 1027 if (\is_object($string) && method_exists($string, '__toString')) { 1028 $string = (string) $string; 1029 } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) { 1030 return $string; 1031 } 1032 } 1033 1034 if ('' === $string) { 1035 return ''; 1036 } 1037 1038 if (null === $charset) { 1039 $charset = $env->getCharset(); 1040 } 1041 1042 switch ($strategy) { 1043 case 'html': 1044 // see https://secure.php.net/htmlspecialchars 1045 1046 // Using a static variable to avoid initializing the array 1047 // each time the function is called. Moving the declaration on the 1048 // top of the function slow downs other escaping strategies. 1049 static $htmlspecialcharsCharsets = [ 1050 'ISO-8859-1' => true, 'ISO8859-1' => true, 1051 'ISO-8859-15' => true, 'ISO8859-15' => true, 1052 'utf-8' => true, 'UTF-8' => true, 1053 'CP866' => true, 'IBM866' => true, '866' => true, 1054 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, 1055 '1251' => true, 1056 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, 1057 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, 1058 'BIG5' => true, '950' => true, 1059 'GB2312' => true, '936' => true, 1060 'BIG5-HKSCS' => true, 1061 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, 1062 'EUC-JP' => true, 'EUCJP' => true, 1063 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, 1064 ]; 1065 1066 if (isset($htmlspecialcharsCharsets[$charset])) { 1067 return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); 1068 } 1069 1070 if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) { 1071 // cache the lowercase variant for future iterations 1072 $htmlspecialcharsCharsets[$charset] = true; 1073 1074 return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); 1075 } 1076 1077 $string = twig_convert_encoding($string, 'UTF-8', $charset); 1078 $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); 1079 1080 return twig_convert_encoding($string, $charset, 'UTF-8'); 1081 1082 case 'js': 1083 // escape all non-alphanumeric characters 1084 // into their \x or \uHHHH representations 1085 if ('UTF-8' !== $charset) { 1086 $string = twig_convert_encoding($string, 'UTF-8', $charset); 1087 } 1088 1089 if (!preg_match('//u', $string)) { 1090 throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); 1091 } 1092 1093 $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string); 1094 1095 if ('UTF-8' !== $charset) { 1096 $string = twig_convert_encoding($string, $charset, 'UTF-8'); 1097 } 1098 1099 return $string; 1100 1101 case 'css': 1102 if ('UTF-8' !== $charset) { 1103 $string = twig_convert_encoding($string, 'UTF-8', $charset); 1104 } 1105 1106 if (!preg_match('//u', $string)) { 1107 throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); 1108 } 1109 1110 $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string); 1111 1112 if ('UTF-8' !== $charset) { 1113 $string = twig_convert_encoding($string, $charset, 'UTF-8'); 1114 } 1115 1116 return $string; 1117 1118 case 'html_attr': 1119 if ('UTF-8' !== $charset) { 1120 $string = twig_convert_encoding($string, 'UTF-8', $charset); 1121 } 1122 1123 if (!preg_match('//u', $string)) { 1124 throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); 1125 } 1126 1127 $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string); 1128 1129 if ('UTF-8' !== $charset) { 1130 $string = twig_convert_encoding($string, $charset, 'UTF-8'); 1131 } 1132 1133 return $string; 1134 1135 case 'url': 1136 return rawurlencode($string); 1137 1138 default: 1139 static $escapers; 1140 1141 if (null === $escapers) { 1142 $escapers = $env->getExtension('\Twig\Extension\CoreExtension')->getEscapers(); 1143 } 1144 1145 if (isset($escapers[$strategy])) { 1146 return \call_user_func($escapers[$strategy], $env, $string, $charset); 1147 } 1148 1149 $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers))); 1150 1151 throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies)); 1152 } 1153 } 1154 1155 /** 1156 * @internal 1157 */ 1158 function twig_escape_filter_is_safe(Node $filterArgs) 1159 { 1160 foreach ($filterArgs as $arg) { 1161 if ($arg instanceof ConstantExpression) { 1162 return [$arg->getAttribute('value')]; 1163 } 1164 1165 return []; 1166 } 1167 1168 return ['html']; 1169 } 1170 1171 if (\function_exists('mb_convert_encoding')) { 1172 function twig_convert_encoding($string, $to, $from) 1173 { 1174 return mb_convert_encoding($string, $to, $from); 1175 } 1176 } elseif (\function_exists('iconv')) { 1177 function twig_convert_encoding($string, $to, $from) 1178 { 1179 return iconv($from, $to, $string); 1180 } 1181 } else { 1182 function twig_convert_encoding($string, $to, $from) 1183 { 1184 throw new RuntimeError('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).'); 1185 } 1186 } 1187 1188 if (\function_exists('mb_ord')) { 1189 function twig_ord($string) 1190 { 1191 return mb_ord($string, 'UTF-8'); 1192 } 1193 } else { 1194 function twig_ord($string) 1195 { 1196 $code = ($string = unpack('C*', substr($string, 0, 4))) ? $string[1] : 0; 1197 if (0xF0 <= $code) { 1198 return (($code - 0xF0) << 18) + (($string[2] - 0x80) << 12) + (($string[3] - 0x80) << 6) + $string[4] - 0x80; 1199 } 1200 if (0xE0 <= $code) { 1201 return (($code - 0xE0) << 12) + (($string[2] - 0x80) << 6) + $string[3] - 0x80; 1202 } 1203 if (0xC0 <= $code) { 1204 return (($code - 0xC0) << 6) + $string[2] - 0x80; 1205 } 1206 1207 return $code; 1208 } 1209 } 1210 1211 function _twig_escape_js_callback($matches) 1212 { 1213 $char = $matches[0]; 1214 1215 /* 1216 * A few characters have short escape sequences in JSON and JavaScript. 1217 * Escape sequences supported only by JavaScript, not JSON, are ommitted. 1218 * \" is also supported but omitted, because the resulting string is not HTML safe. 1219 */ 1220 static $shortMap = [ 1221 '\\' => '\\\\', 1222 '/' => '\\/', 1223 "\x08" => '\b', 1224 "\x0C" => '\f', 1225 "\x0A" => '\n', 1226 "\x0D" => '\r', 1227 "\x09" => '\t', 1228 ]; 1229 1230 if (isset($shortMap[$char])) { 1231 return $shortMap[$char]; 1232 } 1233 1234 // \uHHHH 1235 $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8'); 1236 $char = strtoupper(bin2hex($char)); 1237 1238 if (4 >= \strlen($char)) { 1239 return sprintf('\u%04s', $char); 1240 } 1241 1242 return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4)); 1243 } 1244 1245 function _twig_escape_css_callback($matches) 1246 { 1247 $char = $matches[0]; 1248 1249 return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : twig_ord($char)); 1250 } 1251 1252 /** 1253 * This function is adapted from code coming from Zend Framework. 1254 * 1255 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com) 1256 * @license https://framework.zend.com/license/new-bsd New BSD License 1257 */ 1258 function _twig_escape_html_attr_callback($matches) 1259 { 1260 $chr = $matches[0]; 1261 $ord = \ord($chr); 1262 1263 /* 1264 * The following replaces characters undefined in HTML with the 1265 * hex entity for the Unicode replacement character. 1266 */ 1267 if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) { 1268 return '�'; 1269 } 1270 1271 /* 1272 * Check if the current character to escape has a name entity we should 1273 * replace it with while grabbing the hex value of the character. 1274 */ 1275 if (1 == \strlen($chr)) { 1276 /* 1277 * While HTML supports far more named entities, the lowest common denominator 1278 * has become HTML5's XML Serialisation which is restricted to the those named 1279 * entities that XML supports. Using HTML entities would result in this error: 1280 * XML Parsing Error: undefined entity 1281 */ 1282 static $entityMap = [ 1283 34 => '"', /* quotation mark */ 1284 38 => '&', /* ampersand */ 1285 60 => '<', /* less-than sign */ 1286 62 => '>', /* greater-than sign */ 1287 ]; 1288 1289 if (isset($entityMap[$ord])) { 1290 return $entityMap[$ord]; 1291 } 1292 1293 return sprintf('&#x%02X;', $ord); 1294 } 1295 1296 /* 1297 * Per OWASP recommendations, we'll use hex entities for any other 1298 * characters where a named entity does not exist. 1299 */ 1300 return sprintf('&#x%04X;', twig_ord($chr)); 1301 } 1302 1303 // add multibyte extensions if possible 1304 if (\function_exists('mb_get_info')) { 1305 /** 1306 * Returns the length of a variable. 1307 * 1308 * @param mixed $thing A variable 1309 * 1310 * @return int The length of the value 1311 */ 1312 function twig_length_filter(Environment $env, $thing) 1313 { 1314 if (null === $thing) { 1315 return 0; 1316 } 1317 1318 if (is_scalar($thing)) { 1319 return mb_strlen($thing, $env->getCharset()); 1320 } 1321 1322 if ($thing instanceof \Countable || \is_array($thing) || $thing instanceof \SimpleXMLElement) { 1323 return \count($thing); 1324 } 1325 1326 if ($thing instanceof \Traversable) { 1327 return iterator_count($thing); 1328 } 1329 1330 if (\is_object($thing) && method_exists($thing, '__toString')) { 1331 return mb_strlen((string) $thing, $env->getCharset()); 1332 } 1333 1334 return 1; 1335 } 1336 1337 /** 1338 * Converts a string to uppercase. 1339 * 1340 * @param string $string A string 1341 * 1342 * @return string The uppercased string 1343 */ 1344 function twig_upper_filter(Environment $env, $string) 1345 { 1346 if (null !== $charset = $env->getCharset()) { 1347 return mb_strtoupper($string, $charset); 1348 } 1349 1350 return strtoupper($string); 1351 } 1352 1353 /** 1354 * Converts a string to lowercase. 1355 * 1356 * @param string $string A string 1357 * 1358 * @return string The lowercased string 1359 */ 1360 function twig_lower_filter(Environment $env, $string) 1361 { 1362 if (null !== $charset = $env->getCharset()) { 1363 return mb_strtolower($string, $charset); 1364 } 1365 1366 return strtolower($string); 1367 } 1368 1369 /** 1370 * Returns a titlecased string. 1371 * 1372 * @param string $string A string 1373 * 1374 * @return string The titlecased string 1375 */ 1376 function twig_title_string_filter(Environment $env, $string) 1377 { 1378 if (null !== $charset = $env->getCharset()) { 1379 return mb_convert_case($string, MB_CASE_TITLE, $charset); 1380 } 1381 1382 return ucwords(strtolower($string)); 1383 } 1384 1385 /** 1386 * Returns a capitalized string. 1387 * 1388 * @param string $string A string 1389 * 1390 * @return string The capitalized string 1391 */ 1392 function twig_capitalize_string_filter(Environment $env, $string) 1393 { 1394 if (null !== $charset = $env->getCharset()) { 1395 return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset); 1396 } 1397 1398 return ucfirst(strtolower($string)); 1399 } 1400 } 1401 // and byte fallback 1402 else { 1403 /** 1404 * Returns the length of a variable. 1405 * 1406 * @param mixed $thing A variable 1407 * 1408 * @return int The length of the value 1409 */ 1410 function twig_length_filter(Environment $env, $thing) 1411 { 1412 if (null === $thing) { 1413 return 0; 1414 } 1415 1416 if (is_scalar($thing)) { 1417 return \strlen($thing); 1418 } 1419 1420 if ($thing instanceof \SimpleXMLElement) { 1421 return \count($thing); 1422 } 1423 1424 if (\is_object($thing) && method_exists($thing, '__toString') && !$thing instanceof \Countable) { 1425 return \strlen((string) $thing); 1426 } 1427 1428 if ($thing instanceof \Countable || \is_array($thing)) { 1429 return \count($thing); 1430 } 1431 1432 if ($thing instanceof \IteratorAggregate) { 1433 return iterator_count($thing); 1434 } 1435 1436 return 1; 1437 } 1438 1439 /** 1440 * Returns a titlecased string. 1441 * 1442 * @param string $string A string 1443 * 1444 * @return string The titlecased string 1445 */ 1446 function twig_title_string_filter(Environment $env, $string) 1447 { 1448 return ucwords(strtolower($string)); 1449 } 1450 1451 /** 1452 * Returns a capitalized string. 1453 * 1454 * @param string $string A string 1455 * 1456 * @return string The capitalized string 1457 */ 1458 function twig_capitalize_string_filter(Environment $env, $string) 1459 { 1460 return ucfirst(strtolower($string)); 1461 } 1462 } 1463 1464 /** 1465 * @internal 1466 */ 1467 function twig_ensure_traversable($seq) 1468 { 1469 if ($seq instanceof \Traversable || \is_array($seq)) { 1470 return $seq; 1471 } 1472 1473 return []; 1474 } 1475 1476 /** 1477 * @internal 1478 */ 1479 function twig_to_array($seq, $preserveKeys = true) 1480 { 1481 if ($seq instanceof \Traversable) { 1482 return iterator_to_array($seq, $preserveKeys); 1483 } 1484 1485 if (!\is_array($seq)) { 1486 return $seq; 1487 } 1488 1489 return $preserveKeys ? $seq : array_values($seq); 1490 } 1491 1492 /** 1493 * Checks if a variable is empty. 1494 * 1495 * {# evaluates to true if the foo variable is null, false, or the empty string #} 1496 * {% if foo is empty %} 1497 * {# ... #} 1498 * {% endif %} 1499 * 1500 * @param mixed $value A variable 1501 * 1502 * @return bool true if the value is empty, false otherwise 1503 */ 1504 function twig_test_empty($value) 1505 { 1506 if ($value instanceof \Countable) { 1507 return 0 == \count($value); 1508 } 1509 1510 if (\is_object($value) && method_exists($value, '__toString')) { 1511 return '' === (string) $value; 1512 } 1513 1514 return '' === $value || false === $value || null === $value || [] === $value; 1515 } 1516 1517 /** 1518 * Checks if a variable is traversable. 1519 * 1520 * {# evaluates to true if the foo variable is an array or a traversable object #} 1521 * {% if foo is iterable %} 1522 * {# ... #} 1523 * {% endif %} 1524 * 1525 * @param mixed $value A variable 1526 * 1527 * @return bool true if the value is traversable 1528 */ 1529 function twig_test_iterable($value) 1530 { 1531 return $value instanceof \Traversable || \is_array($value); 1532 } 1533 1534 /** 1535 * Renders a template. 1536 * 1537 * @param array $context 1538 * @param string|array $template The template to render or an array of templates to try consecutively 1539 * @param array $variables The variables to pass to the template 1540 * @param bool $withContext 1541 * @param bool $ignoreMissing Whether to ignore missing templates or not 1542 * @param bool $sandboxed Whether to sandbox the template or not 1543 * 1544 * @return string The rendered template 1545 */ 1546 function twig_include(Environment $env, $context, $template, $variables = [], $withContext = true, $ignoreMissing = false, $sandboxed = false) 1547 { 1548 $alreadySandboxed = false; 1549 $sandbox = null; 1550 if ($withContext) { 1551 $variables = array_merge($context, $variables); 1552 } 1553 1554 if ($isSandboxed = $sandboxed && $env->hasExtension('\Twig\Extension\SandboxExtension')) { 1555 $sandbox = $env->getExtension('\Twig\Extension\SandboxExtension'); 1556 if (!$alreadySandboxed = $sandbox->isSandboxed()) { 1557 $sandbox->enableSandbox(); 1558 } 1559 } 1560 1561 $loaded = null; 1562 try { 1563 $loaded = $env->resolveTemplate($template); 1564 } catch (LoaderError $e) { 1565 if (!$ignoreMissing) { 1566 if ($isSandboxed && !$alreadySandboxed) { 1567 $sandbox->disableSandbox(); 1568 } 1569 1570 throw $e; 1571 } 1572 } catch (\Throwable $e) { 1573 if ($isSandboxed && !$alreadySandboxed) { 1574 $sandbox->disableSandbox(); 1575 } 1576 1577 throw $e; 1578 } catch (\Exception $e) { 1579 if ($isSandboxed && !$alreadySandboxed) { 1580 $sandbox->disableSandbox(); 1581 } 1582 1583 throw $e; 1584 } 1585 1586 try { 1587 $ret = $loaded ? $loaded->render($variables) : ''; 1588 } catch (\Exception $e) { 1589 if ($isSandboxed && !$alreadySandboxed) { 1590 $sandbox->disableSandbox(); 1591 } 1592 1593 throw $e; 1594 } 1595 1596 if ($isSandboxed && !$alreadySandboxed) { 1597 $sandbox->disableSandbox(); 1598 } 1599 1600 return $ret; 1601 } 1602 1603 /** 1604 * Returns a template content without rendering it. 1605 * 1606 * @param string $name The template name 1607 * @param bool $ignoreMissing Whether to ignore missing templates or not 1608 * 1609 * @return string The template source 1610 */ 1611 function twig_source(Environment $env, $name, $ignoreMissing = false) 1612 { 1613 $loader = $env->getLoader(); 1614 try { 1615 if (!$loader instanceof SourceContextLoaderInterface) { 1616 return $loader->getSource($name); 1617 } else { 1618 return $loader->getSourceContext($name)->getCode(); 1619 } 1620 } catch (LoaderError $e) { 1621 if (!$ignoreMissing) { 1622 throw $e; 1623 } 1624 } 1625 } 1626 1627 /** 1628 * Provides the ability to get constants from instances as well as class/global constants. 1629 * 1630 * @param string $constant The name of the constant 1631 * @param object|null $object The object to get the constant from 1632 * 1633 * @return string 1634 */ 1635 function twig_constant($constant, $object = null) 1636 { 1637 if (null !== $object) { 1638 $constant = \get_class($object).'::'.$constant; 1639 } 1640 1641 return \constant($constant); 1642 } 1643 1644 /** 1645 * Checks if a constant exists. 1646 * 1647 * @param string $constant The name of the constant 1648 * @param object|null $object The object to get the constant from 1649 * 1650 * @return bool 1651 */ 1652 function twig_constant_is_defined($constant, $object = null) 1653 { 1654 if (null !== $object) { 1655 $constant = \get_class($object).'::'.$constant; 1656 } 1657 1658 return \defined($constant); 1659 } 1660 1661 /** 1662 * Batches item. 1663 * 1664 * @param array $items An array of items 1665 * @param int $size The size of the batch 1666 * @param mixed $fill A value used to fill missing items 1667 * 1668 * @return array 1669 */ 1670 function twig_array_batch($items, $size, $fill = null, $preserveKeys = true) 1671 { 1672 if (!twig_test_iterable($items)) { 1673 throw new RuntimeError(sprintf('The "batch" filter expects an array or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items))); 1674 } 1675 1676 $size = ceil($size); 1677 1678 $result = array_chunk(twig_to_array($items, $preserveKeys), $size, $preserveKeys); 1679 1680 if (null !== $fill && $result) { 1681 $last = \count($result) - 1; 1682 if ($fillCount = $size - \count($result[$last])) { 1683 for ($i = 0; $i < $fillCount; ++$i) { 1684 $result[$last][] = $fill; 1685 } 1686 } 1687 } 1688 1689 return $result; 1690 } 1691 1692 function twig_array_filter($array, $arrow) 1693 { 1694 if (\is_array($array)) { 1695 if (\PHP_VERSION_ID >= 50600) { 1696 return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH); 1697 } 1698 1699 return array_filter($array, $arrow); 1700 } 1701 1702 // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator 1703 return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow); 1704 } 1705 1706 function twig_array_map($array, $arrow) 1707 { 1708 $r = []; 1709 foreach ($array as $k => $v) { 1710 $r[$k] = $arrow($v, $k); 1711 } 1712 1713 return $r; 1714 } 1715 1716 function twig_array_reduce($array, $arrow, $initial = null) 1717 { 1718 if (!\is_array($array)) { 1719 $array = iterator_to_array($array); 1720 } 1721 1722 return array_reduce($array, $arrow, $initial); 1723 } 1724 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |