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 12namespace Twig; 13 14use Twig\Cache\CacheInterface; 15use Twig\Cache\FilesystemCache; 16use Twig\Cache\NullCache; 17use Twig\Error\Error; 18use Twig\Error\LoaderError; 19use Twig\Error\RuntimeError; 20use Twig\Error\SyntaxError; 21use Twig\Extension\CoreExtension; 22use Twig\Extension\EscaperExtension; 23use Twig\Extension\ExtensionInterface; 24use Twig\Extension\OptimizerExtension; 25use Twig\Loader\ArrayLoader; 26use Twig\Loader\ChainLoader; 27use Twig\Loader\LoaderInterface; 28use Twig\Node\ModuleNode; 29use Twig\Node\Node; 30use Twig\NodeVisitor\NodeVisitorInterface; 31use Twig\RuntimeLoader\RuntimeLoaderInterface; 32use Twig\TokenParser\TokenParserInterface; 33 34/** 35 * Stores the Twig configuration and renders templates. 36 * 37 * @author Fabien Potencier <fabien@symfony.com> 38 */ 39class Environment 40{ 41 public const VERSION = '2.14.6'; 42 public const VERSION_ID = 21406; 43 public const MAJOR_VERSION = 2; 44 public const MINOR_VERSION = 14; 45 public const RELEASE_VERSION = 6; 46 public const EXTRA_VERSION = ''; 47 48 private $charset; 49 private $loader; 50 private $debug; 51 private $autoReload; 52 private $cache; 53 private $lexer; 54 private $parser; 55 private $compiler; 56 private $baseTemplateClass; 57 private $globals = []; 58 private $resolvedGlobals; 59 private $loadedTemplates; 60 private $strictVariables; 61 private $templateClassPrefix = '__TwigTemplate_'; 62 private $originalCache; 63 private $extensionSet; 64 private $runtimeLoaders = []; 65 private $runtimes = []; 66 private $optionsHash; 67 68 /** 69 * Constructor. 70 * 71 * Available options: 72 * 73 * * debug: When set to true, it automatically set "auto_reload" to true as 74 * well (default to false). 75 * 76 * * charset: The charset used by the templates (default to UTF-8). 77 * 78 * * base_template_class: The base template class to use for generated 79 * templates (default to \Twig\Template). 80 * 81 * * cache: An absolute path where to store the compiled templates, 82 * a \Twig\Cache\CacheInterface implementation, 83 * or false to disable compilation cache (default). 84 * 85 * * auto_reload: Whether to reload the template if the original source changed. 86 * If you don't provide the auto_reload option, it will be 87 * determined automatically based on the debug value. 88 * 89 * * strict_variables: Whether to ignore invalid variables in templates 90 * (default to false). 91 * 92 * * autoescape: Whether to enable auto-escaping (default to html): 93 * * false: disable auto-escaping 94 * * html, js: set the autoescaping to one of the supported strategies 95 * * name: set the autoescaping strategy based on the template name extension 96 * * PHP callback: a PHP callback that returns an escaping strategy based on the template "name" 97 * 98 * * optimizations: A flag that indicates which optimizations to apply 99 * (default to -1 which means that all optimizations are enabled; 100 * set it to 0 to disable). 101 */ 102 public function __construct(LoaderInterface $loader, $options = []) 103 { 104 $this->setLoader($loader); 105 106 $options = array_merge([ 107 'debug' => false, 108 'charset' => 'UTF-8', 109 'base_template_class' => Template::class, 110 'strict_variables' => false, 111 'autoescape' => 'html', 112 'cache' => false, 113 'auto_reload' => null, 114 'optimizations' => -1, 115 ], $options); 116 117 $this->debug = (bool) $options['debug']; 118 $this->setCharset($options['charset']); 119 $this->baseTemplateClass = '\\'.ltrim($options['base_template_class'], '\\'); 120 if ('\\'.Template::class !== $this->baseTemplateClass && '\Twig_Template' !== $this->baseTemplateClass) { 121 @trigger_error('The "base_template_class" option on '.__CLASS__.' is deprecated since Twig 2.7.0.', \E_USER_DEPRECATED); 122 } 123 $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload']; 124 $this->strictVariables = (bool) $options['strict_variables']; 125 $this->setCache($options['cache']); 126 $this->extensionSet = new ExtensionSet(); 127 128 $this->addExtension(new CoreExtension()); 129 $this->addExtension(new EscaperExtension($options['autoescape'])); 130 $this->addExtension(new OptimizerExtension($options['optimizations'])); 131 } 132 133 /** 134 * Gets the base template class for compiled templates. 135 * 136 * @return string The base template class name 137 */ 138 public function getBaseTemplateClass() 139 { 140 if (1 > \func_num_args() || \func_get_arg(0)) { 141 @trigger_error('The '.__METHOD__.' is deprecated since Twig 2.7.0.', \E_USER_DEPRECATED); 142 } 143 144 return $this->baseTemplateClass; 145 } 146 147 /** 148 * Sets the base template class for compiled templates. 149 * 150 * @param string $class The base template class name 151 */ 152 public function setBaseTemplateClass($class) 153 { 154 @trigger_error('The '.__METHOD__.' is deprecated since Twig 2.7.0.', \E_USER_DEPRECATED); 155 156 $this->baseTemplateClass = $class; 157 $this->updateOptionsHash(); 158 } 159 160 /** 161 * Enables debugging mode. 162 */ 163 public function enableDebug() 164 { 165 $this->debug = true; 166 $this->updateOptionsHash(); 167 } 168 169 /** 170 * Disables debugging mode. 171 */ 172 public function disableDebug() 173 { 174 $this->debug = false; 175 $this->updateOptionsHash(); 176 } 177 178 /** 179 * Checks if debug mode is enabled. 180 * 181 * @return bool true if debug mode is enabled, false otherwise 182 */ 183 public function isDebug() 184 { 185 return $this->debug; 186 } 187 188 /** 189 * Enables the auto_reload option. 190 */ 191 public function enableAutoReload() 192 { 193 $this->autoReload = true; 194 } 195 196 /** 197 * Disables the auto_reload option. 198 */ 199 public function disableAutoReload() 200 { 201 $this->autoReload = false; 202 } 203 204 /** 205 * Checks if the auto_reload option is enabled. 206 * 207 * @return bool true if auto_reload is enabled, false otherwise 208 */ 209 public function isAutoReload() 210 { 211 return $this->autoReload; 212 } 213 214 /** 215 * Enables the strict_variables option. 216 */ 217 public function enableStrictVariables() 218 { 219 $this->strictVariables = true; 220 $this->updateOptionsHash(); 221 } 222 223 /** 224 * Disables the strict_variables option. 225 */ 226 public function disableStrictVariables() 227 { 228 $this->strictVariables = false; 229 $this->updateOptionsHash(); 230 } 231 232 /** 233 * Checks if the strict_variables option is enabled. 234 * 235 * @return bool true if strict_variables is enabled, false otherwise 236 */ 237 public function isStrictVariables() 238 { 239 return $this->strictVariables; 240 } 241 242 /** 243 * Gets the current cache implementation. 244 * 245 * @param bool $original Whether to return the original cache option or the real cache instance 246 * 247 * @return CacheInterface|string|false A Twig\Cache\CacheInterface implementation, 248 * an absolute path to the compiled templates, 249 * or false to disable cache 250 */ 251 public function getCache($original = true) 252 { 253 return $original ? $this->originalCache : $this->cache; 254 } 255 256 /** 257 * Sets the current cache implementation. 258 * 259 * @param CacheInterface|string|false $cache A Twig\Cache\CacheInterface implementation, 260 * an absolute path to the compiled templates, 261 * or false to disable cache 262 */ 263 public function setCache($cache) 264 { 265 if (\is_string($cache)) { 266 $this->originalCache = $cache; 267 $this->cache = new FilesystemCache($cache); 268 } elseif (false === $cache) { 269 $this->originalCache = $cache; 270 $this->cache = new NullCache(); 271 } elseif ($cache instanceof CacheInterface) { 272 $this->originalCache = $this->cache = $cache; 273 } else { 274 throw new \LogicException('Cache can only be a string, false, or a \Twig\Cache\CacheInterface implementation.'); 275 } 276 } 277 278 /** 279 * Gets the template class associated with the given string. 280 * 281 * The generated template class is based on the following parameters: 282 * 283 * * The cache key for the given template; 284 * * The currently enabled extensions; 285 * * Whether the Twig C extension is available or not; 286 * * PHP version; 287 * * Twig version; 288 * * Options with what environment was created. 289 * 290 * @param string $name The name for which to calculate the template class name 291 * @param int|null $index The index if it is an embedded template 292 * 293 * @return string The template class name 294 * 295 * @internal 296 */ 297 public function getTemplateClass($name, $index = null) 298 { 299 $key = $this->getLoader()->getCacheKey($name).$this->optionsHash; 300 301 return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '___'.$index); 302 } 303 304 /** 305 * Renders a template. 306 * 307 * @param string|TemplateWrapper $name The template name 308 * @param array $context An array of parameters to pass to the template 309 * 310 * @return string The rendered template 311 * 312 * @throws LoaderError When the template cannot be found 313 * @throws SyntaxError When an error occurred during compilation 314 * @throws RuntimeError When an error occurred during rendering 315 */ 316 public function render($name, array $context = []) 317 { 318 return $this->load($name)->render($context); 319 } 320 321 /** 322 * Displays a template. 323 * 324 * @param string|TemplateWrapper $name The template name 325 * @param array $context An array of parameters to pass to the template 326 * 327 * @throws LoaderError When the template cannot be found 328 * @throws SyntaxError When an error occurred during compilation 329 * @throws RuntimeError When an error occurred during rendering 330 */ 331 public function display($name, array $context = []) 332 { 333 $this->load($name)->display($context); 334 } 335 336 /** 337 * Loads a template. 338 * 339 * @param string|TemplateWrapper $name The template name 340 * 341 * @throws LoaderError When the template cannot be found 342 * @throws RuntimeError When a previously generated cache is corrupted 343 * @throws SyntaxError When an error occurred during compilation 344 * 345 * @return TemplateWrapper 346 */ 347 public function load($name) 348 { 349 if ($name instanceof TemplateWrapper) { 350 return $name; 351 } 352 353 if ($name instanceof Template) { 354 @trigger_error('Passing a \Twig\Template instance to '.__METHOD__.' is deprecated since Twig 2.7.0, use \Twig\TemplateWrapper instead.', \E_USER_DEPRECATED); 355 356 return new TemplateWrapper($this, $name); 357 } 358 359 return new TemplateWrapper($this, $this->loadTemplate($name)); 360 } 361 362 /** 363 * Loads a template internal representation. 364 * 365 * This method is for internal use only and should never be called 366 * directly. 367 * 368 * @param string $name The template name 369 * @param int $index The index if it is an embedded template 370 * 371 * @return Template A template instance representing the given template name 372 * 373 * @throws LoaderError When the template cannot be found 374 * @throws RuntimeError When a previously generated cache is corrupted 375 * @throws SyntaxError When an error occurred during compilation 376 * 377 * @internal 378 */ 379 public function loadTemplate($name, $index = null) 380 { 381 return $this->loadClass($this->getTemplateClass($name), $name, $index); 382 } 383 384 /** 385 * @internal 386 */ 387 public function loadClass($cls, $name, $index = null) 388 { 389 $mainCls = $cls; 390 if (null !== $index) { 391 $cls .= '___'.$index; 392 } 393 394 if (isset($this->loadedTemplates[$cls])) { 395 return $this->loadedTemplates[$cls]; 396 } 397 398 if (!class_exists($cls, false)) { 399 $key = $this->cache->generateKey($name, $mainCls); 400 401 if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) { 402 $this->cache->load($key); 403 } 404 405 $source = null; 406 if (!class_exists($cls, false)) { 407 $source = $this->getLoader()->getSourceContext($name); 408 $content = $this->compileSource($source); 409 $this->cache->write($key, $content); 410 $this->cache->load($key); 411 412 if (!class_exists($mainCls, false)) { 413 /* Last line of defense if either $this->bcWriteCacheFile was used, 414 * $this->cache is implemented as a no-op or we have a race condition 415 * where the cache was cleared between the above calls to write to and load from 416 * the cache. 417 */ 418 eval('?>'.$content); 419 } 420 421 if (!class_exists($cls, false)) { 422 throw new RuntimeError(sprintf('Failed to load Twig template "%s", index "%s": cache might be corrupted.', $name, $index), -1, $source); 423 } 424 } 425 } 426 427 // to be removed in 3.0 428 $this->extensionSet->initRuntime($this); 429 430 return $this->loadedTemplates[$cls] = new $cls($this); 431 } 432 433 /** 434 * Creates a template from source. 435 * 436 * This method should not be used as a generic way to load templates. 437 * 438 * @param string $template The template source 439 * @param string $name An optional name of the template to be used in error messages 440 * 441 * @return TemplateWrapper A template instance representing the given template name 442 * 443 * @throws LoaderError When the template cannot be found 444 * @throws SyntaxError When an error occurred during compilation 445 */ 446 public function createTemplate($template, string $name = null) 447 { 448 $hash = hash('sha256', $template, false); 449 if (null !== $name) { 450 $name = sprintf('%s (string template %s)', $name, $hash); 451 } else { 452 $name = sprintf('__string_template__%s', $hash); 453 } 454 455 $loader = new ChainLoader([ 456 new ArrayLoader([$name => $template]), 457 $current = $this->getLoader(), 458 ]); 459 460 $this->setLoader($loader); 461 try { 462 return new TemplateWrapper($this, $this->loadTemplate($name)); 463 } finally { 464 $this->setLoader($current); 465 } 466 } 467 468 /** 469 * Returns true if the template is still fresh. 470 * 471 * Besides checking the loader for freshness information, 472 * this method also checks if the enabled extensions have 473 * not changed. 474 * 475 * @param string $name The template name 476 * @param int $time The last modification time of the cached template 477 * 478 * @return bool true if the template is fresh, false otherwise 479 */ 480 public function isTemplateFresh($name, $time) 481 { 482 return $this->extensionSet->getLastModified() <= $time && $this->getLoader()->isFresh($name, $time); 483 } 484 485 /** 486 * Tries to load a template consecutively from an array. 487 * 488 * Similar to load() but it also accepts instances of \Twig\Template and 489 * \Twig\TemplateWrapper, and an array of templates where each is tried to be loaded. 490 * 491 * @param string|TemplateWrapper|array $names A template or an array of templates to try consecutively 492 * 493 * @return TemplateWrapper|Template 494 * 495 * @throws LoaderError When none of the templates can be found 496 * @throws SyntaxError When an error occurred during compilation 497 */ 498 public function resolveTemplate($names) 499 { 500 if (!\is_array($names)) { 501 $names = [$names]; 502 } 503 504 foreach ($names as $name) { 505 if ($name instanceof Template) { 506 return $name; 507 } 508 if ($name instanceof TemplateWrapper) { 509 return $name; 510 } 511 512 try { 513 return $this->loadTemplate($name); 514 } catch (LoaderError $e) { 515 if (1 === \count($names)) { 516 throw $e; 517 } 518 } 519 } 520 521 throw new LoaderError(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names))); 522 } 523 524 public function setLexer(Lexer $lexer) 525 { 526 $this->lexer = $lexer; 527 } 528 529 /** 530 * Tokenizes a source code. 531 * 532 * @return TokenStream 533 * 534 * @throws SyntaxError When the code is syntactically wrong 535 */ 536 public function tokenize(Source $source) 537 { 538 if (null === $this->lexer) { 539 $this->lexer = new Lexer($this); 540 } 541 542 return $this->lexer->tokenize($source); 543 } 544 545 public function setParser(Parser $parser) 546 { 547 $this->parser = $parser; 548 } 549 550 /** 551 * Converts a token stream to a node tree. 552 * 553 * @return ModuleNode 554 * 555 * @throws SyntaxError When the token stream is syntactically or semantically wrong 556 */ 557 public function parse(TokenStream $stream) 558 { 559 if (null === $this->parser) { 560 $this->parser = new Parser($this); 561 } 562 563 return $this->parser->parse($stream); 564 } 565 566 public function setCompiler(Compiler $compiler) 567 { 568 $this->compiler = $compiler; 569 } 570 571 /** 572 * Compiles a node and returns the PHP code. 573 * 574 * @return string The compiled PHP source code 575 */ 576 public function compile(Node $node) 577 { 578 if (null === $this->compiler) { 579 $this->compiler = new Compiler($this); 580 } 581 582 return $this->compiler->compile($node)->getSource(); 583 } 584 585 /** 586 * Compiles a template source code. 587 * 588 * @return string The compiled PHP source code 589 * 590 * @throws SyntaxError When there was an error during tokenizing, parsing or compiling 591 */ 592 public function compileSource(Source $source) 593 { 594 try { 595 return $this->compile($this->parse($this->tokenize($source))); 596 } catch (Error $e) { 597 $e->setSourceContext($source); 598 throw $e; 599 } catch (\Exception $e) { 600 throw new SyntaxError(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e); 601 } 602 } 603 604 public function setLoader(LoaderInterface $loader) 605 { 606 $this->loader = $loader; 607 } 608 609 /** 610 * Gets the Loader instance. 611 * 612 * @return LoaderInterface 613 */ 614 public function getLoader() 615 { 616 return $this->loader; 617 } 618 619 /** 620 * Sets the default template charset. 621 * 622 * @param string $charset The default charset 623 */ 624 public function setCharset($charset) 625 { 626 if ('UTF8' === $charset = null === $charset ? null : strtoupper($charset)) { 627 // iconv on Windows requires "UTF-8" instead of "UTF8" 628 $charset = 'UTF-8'; 629 } 630 631 $this->charset = $charset; 632 } 633 634 /** 635 * Gets the default template charset. 636 * 637 * @return string The default charset 638 */ 639 public function getCharset() 640 { 641 return $this->charset; 642 } 643 644 /** 645 * Returns true if the given extension is registered. 646 * 647 * @param string $class The extension class name 648 * 649 * @return bool Whether the extension is registered or not 650 */ 651 public function hasExtension($class) 652 { 653 return $this->extensionSet->hasExtension($class); 654 } 655 656 /** 657 * Adds a runtime loader. 658 */ 659 public function addRuntimeLoader(RuntimeLoaderInterface $loader) 660 { 661 $this->runtimeLoaders[] = $loader; 662 } 663 664 /** 665 * Gets an extension by class name. 666 * 667 * @param string $class The extension class name 668 * 669 * @return ExtensionInterface 670 */ 671 public function getExtension($class) 672 { 673 return $this->extensionSet->getExtension($class); 674 } 675 676 /** 677 * Returns the runtime implementation of a Twig element (filter/function/test). 678 * 679 * @param string $class A runtime class name 680 * 681 * @return object The runtime implementation 682 * 683 * @throws RuntimeError When the template cannot be found 684 */ 685 public function getRuntime($class) 686 { 687 if (isset($this->runtimes[$class])) { 688 return $this->runtimes[$class]; 689 } 690 691 foreach ($this->runtimeLoaders as $loader) { 692 if (null !== $runtime = $loader->load($class)) { 693 return $this->runtimes[$class] = $runtime; 694 } 695 } 696 697 throw new RuntimeError(sprintf('Unable to load the "%s" runtime.', $class)); 698 } 699 700 public function addExtension(ExtensionInterface $extension) 701 { 702 $this->extensionSet->addExtension($extension); 703 $this->updateOptionsHash(); 704 } 705 706 /** 707 * Registers an array of extensions. 708 * 709 * @param array $extensions An array of extensions 710 */ 711 public function setExtensions(array $extensions) 712 { 713 $this->extensionSet->setExtensions($extensions); 714 $this->updateOptionsHash(); 715 } 716 717 /** 718 * Returns all registered extensions. 719 * 720 * @return ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on) 721 */ 722 public function getExtensions() 723 { 724 return $this->extensionSet->getExtensions(); 725 } 726 727 public function addTokenParser(TokenParserInterface $parser) 728 { 729 $this->extensionSet->addTokenParser($parser); 730 } 731 732 /** 733 * Gets the registered Token Parsers. 734 * 735 * @return TokenParserInterface[] 736 * 737 * @internal 738 */ 739 public function getTokenParsers() 740 { 741 return $this->extensionSet->getTokenParsers(); 742 } 743 744 /** 745 * Gets registered tags. 746 * 747 * @return TokenParserInterface[] 748 * 749 * @internal 750 */ 751 public function getTags() 752 { 753 $tags = []; 754 foreach ($this->getTokenParsers() as $parser) { 755 $tags[$parser->getTag()] = $parser; 756 } 757 758 return $tags; 759 } 760 761 public function addNodeVisitor(NodeVisitorInterface $visitor) 762 { 763 $this->extensionSet->addNodeVisitor($visitor); 764 } 765 766 /** 767 * Gets the registered Node Visitors. 768 * 769 * @return NodeVisitorInterface[] 770 * 771 * @internal 772 */ 773 public function getNodeVisitors() 774 { 775 return $this->extensionSet->getNodeVisitors(); 776 } 777 778 public function addFilter(TwigFilter $filter) 779 { 780 $this->extensionSet->addFilter($filter); 781 } 782 783 /** 784 * Get a filter by name. 785 * 786 * Subclasses may override this method and load filters differently; 787 * so no list of filters is available. 788 * 789 * @param string $name The filter name 790 * 791 * @return TwigFilter|false 792 * 793 * @internal 794 */ 795 public function getFilter($name) 796 { 797 return $this->extensionSet->getFilter($name); 798 } 799 800 public function registerUndefinedFilterCallback(callable $callable) 801 { 802 $this->extensionSet->registerUndefinedFilterCallback($callable); 803 } 804 805 /** 806 * Gets the registered Filters. 807 * 808 * Be warned that this method cannot return filters defined with registerUndefinedFilterCallback. 809 * 810 * @return TwigFilter[] 811 * 812 * @see registerUndefinedFilterCallback 813 * 814 * @internal 815 */ 816 public function getFilters() 817 { 818 return $this->extensionSet->getFilters(); 819 } 820 821 public function addTest(TwigTest $test) 822 { 823 $this->extensionSet->addTest($test); 824 } 825 826 /** 827 * Gets the registered Tests. 828 * 829 * @return TwigTest[] 830 * 831 * @internal 832 */ 833 public function getTests() 834 { 835 return $this->extensionSet->getTests(); 836 } 837 838 /** 839 * Gets a test by name. 840 * 841 * @param string $name The test name 842 * 843 * @return TwigTest|false 844 * 845 * @internal 846 */ 847 public function getTest($name) 848 { 849 return $this->extensionSet->getTest($name); 850 } 851 852 public function addFunction(TwigFunction $function) 853 { 854 $this->extensionSet->addFunction($function); 855 } 856 857 /** 858 * Get a function by name. 859 * 860 * Subclasses may override this method and load functions differently; 861 * so no list of functions is available. 862 * 863 * @param string $name function name 864 * 865 * @return TwigFunction|false 866 * 867 * @internal 868 */ 869 public function getFunction($name) 870 { 871 return $this->extensionSet->getFunction($name); 872 } 873 874 public function registerUndefinedFunctionCallback(callable $callable) 875 { 876 $this->extensionSet->registerUndefinedFunctionCallback($callable); 877 } 878 879 /** 880 * Gets registered functions. 881 * 882 * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback. 883 * 884 * @return TwigFunction[] 885 * 886 * @see registerUndefinedFunctionCallback 887 * 888 * @internal 889 */ 890 public function getFunctions() 891 { 892 return $this->extensionSet->getFunctions(); 893 } 894 895 /** 896 * Registers a Global. 897 * 898 * New globals can be added before compiling or rendering a template; 899 * but after, you can only update existing globals. 900 * 901 * @param string $name The global name 902 * @param mixed $value The global value 903 */ 904 public function addGlobal($name, $value) 905 { 906 if ($this->extensionSet->isInitialized() && !\array_key_exists($name, $this->getGlobals())) { 907 throw new \LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name)); 908 } 909 910 if (null !== $this->resolvedGlobals) { 911 $this->resolvedGlobals[$name] = $value; 912 } else { 913 $this->globals[$name] = $value; 914 } 915 } 916 917 /** 918 * Gets the registered Globals. 919 * 920 * @return array An array of globals 921 * 922 * @internal 923 */ 924 public function getGlobals() 925 { 926 if ($this->extensionSet->isInitialized()) { 927 if (null === $this->resolvedGlobals) { 928 $this->resolvedGlobals = array_merge($this->extensionSet->getGlobals(), $this->globals); 929 } 930 931 return $this->resolvedGlobals; 932 } 933 934 return array_merge($this->extensionSet->getGlobals(), $this->globals); 935 } 936 937 /** 938 * Merges a context with the defined globals. 939 * 940 * @param array $context An array representing the context 941 * 942 * @return array The context merged with the globals 943 */ 944 public function mergeGlobals(array $context) 945 { 946 // we don't use array_merge as the context being generally 947 // bigger than globals, this code is faster. 948 foreach ($this->getGlobals() as $key => $value) { 949 if (!\array_key_exists($key, $context)) { 950 $context[$key] = $value; 951 } 952 } 953 954 return $context; 955 } 956 957 /** 958 * Gets the registered unary Operators. 959 * 960 * @return array An array of unary operators 961 * 962 * @internal 963 */ 964 public function getUnaryOperators() 965 { 966 return $this->extensionSet->getUnaryOperators(); 967 } 968 969 /** 970 * Gets the registered binary Operators. 971 * 972 * @return array An array of binary operators 973 * 974 * @internal 975 */ 976 public function getBinaryOperators() 977 { 978 return $this->extensionSet->getBinaryOperators(); 979 } 980 981 private function updateOptionsHash() 982 { 983 $this->optionsHash = implode(':', [ 984 $this->extensionSet->getSignature(), 985 \PHP_MAJOR_VERSION, 986 \PHP_MINOR_VERSION, 987 self::VERSION, 988 (int) $this->debug, 989 $this->baseTemplateClass, 990 (int) $this->strictVariables, 991 ]); 992 } 993} 994 995class_alias('Twig\Environment', 'Twig_Environment'); 996