2024-06-20 14:10:42 +00:00
< ? php
/*
* This file is part of Twig .
*
* ( c ) Fabien Potencier
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Twig ;
use Twig\Cache\CacheInterface ;
use Twig\Cache\FilesystemCache ;
use Twig\Cache\NullCache ;
2025-01-13 09:56:01 +00:00
use Twig\Cache\RemovableCacheInterface ;
2024-06-20 14:10:42 +00:00
use Twig\Error\Error ;
use Twig\Error\LoaderError ;
use Twig\Error\RuntimeError ;
use Twig\Error\SyntaxError ;
use Twig\Extension\CoreExtension ;
use Twig\Extension\EscaperExtension ;
use Twig\Extension\ExtensionInterface ;
use Twig\Extension\OptimizerExtension ;
use Twig\Extension\YieldNotReadyExtension ;
use Twig\Loader\ArrayLoader ;
use Twig\Loader\ChainLoader ;
use Twig\Loader\LoaderInterface ;
use Twig\Node\Expression\Binary\AbstractBinary ;
use Twig\Node\Expression\Unary\AbstractUnary ;
use Twig\Node\ModuleNode ;
use Twig\Node\Node ;
use Twig\NodeVisitor\NodeVisitorInterface ;
use Twig\Runtime\EscaperRuntime ;
use Twig\RuntimeLoader\FactoryRuntimeLoader ;
use Twig\RuntimeLoader\RuntimeLoaderInterface ;
use Twig\TokenParser\TokenParserInterface ;
/**
* Stores the Twig configuration and renders templates .
*
* @ author Fabien Potencier < fabien @ symfony . com >
*/
class Environment
{
2025-01-13 09:56:01 +00:00
public const VERSION = '3.18.0' ;
public const VERSION_ID = 31800 ;
public const MAJOR_VERSION = 3 ;
public const MINOR_VERSION = 18 ;
2024-09-05 17:51:48 +00:00
public const RELEASE_VERSION = 0 ;
2024-06-20 14:10:42 +00:00
public const EXTRA_VERSION = '' ;
private $charset ;
private $loader ;
private $debug ;
private $autoReload ;
private $cache ;
private $lexer ;
private $parser ;
private $compiler ;
/** @var array<string, mixed> */
private $globals = [];
private $resolvedGlobals ;
private $loadedTemplates ;
private $strictVariables ;
private $originalCache ;
private $extensionSet ;
private $runtimeLoaders = [];
private $runtimes = [];
private $optionsHash ;
/** @var bool */
private $useYield ;
private $defaultRuntimeLoader ;
2025-01-13 09:56:01 +00:00
private array $hotCache = [];
2024-06-20 14:10:42 +00:00
/**
* Constructor .
*
* Available options :
*
* * debug : When set to true , it automatically set " auto_reload " to true as
* well ( default to false ) .
*
* * charset : The charset used by the templates ( default to UTF - 8 ) .
*
* * cache : An absolute path where to store the compiled templates ,
* a \Twig\Cache\CacheInterface implementation ,
* or false to disable compilation cache ( default ) .
*
* * auto_reload : Whether to reload the template if the original source changed .
* If you don ' t provide the auto_reload option , it will be
* determined automatically based on the debug value .
*
* * strict_variables : Whether to ignore invalid variables in templates
* ( default to false ) .
*
* * autoescape : Whether to enable auto - escaping ( default to html ) :
* * false : disable auto - escaping
* * html , js : set the autoescaping to one of the supported strategies
* * name : set the autoescaping strategy based on the template name extension
* * PHP callback : a PHP callback that returns an escaping strategy based on the template " name "
*
* * optimizations : A flag that indicates which optimizations to apply
* ( default to - 1 which means that all optimizations are enabled ;
* set it to 0 to disable ) .
*
2025-01-13 09:56:01 +00:00
* * use_yield : true : forces templates to exclusively use " yield " instead of " echo " ( all extensions must be yield ready )
* false ( default ) : allows templates to use a mix of " yield " and " echo " calls to allow for a progressive migration
* Switch to " true " when possible as this will be the only supported mode in Twig 4.0
2024-06-20 14:10:42 +00:00
*/
2025-01-13 09:56:01 +00:00
public function __construct ( LoaderInterface $loader , array $options = [])
2024-06-20 14:10:42 +00:00
{
$this -> setLoader ( $loader );
$options = array_merge ([
'debug' => false ,
'charset' => 'UTF-8' ,
'strict_variables' => false ,
'autoescape' => 'html' ,
'cache' => false ,
'auto_reload' => null ,
'optimizations' => - 1 ,
'use_yield' => false ,
], $options );
$this -> useYield = ( bool ) $options [ 'use_yield' ];
$this -> debug = ( bool ) $options [ 'debug' ];
$this -> setCharset ( $options [ 'charset' ] ? ? 'UTF-8' );
$this -> autoReload = null === $options [ 'auto_reload' ] ? $this -> debug : ( bool ) $options [ 'auto_reload' ];
$this -> strictVariables = ( bool ) $options [ 'strict_variables' ];
$this -> setCache ( $options [ 'cache' ]);
$this -> extensionSet = new ExtensionSet ();
$this -> defaultRuntimeLoader = new FactoryRuntimeLoader ([
EscaperRuntime :: class => function () { return new EscaperRuntime ( $this -> charset ); },
]);
$this -> addExtension ( new CoreExtension ());
$escaperExt = new EscaperExtension ( $options [ 'autoescape' ]);
$escaperExt -> setEnvironment ( $this , false );
$this -> addExtension ( $escaperExt );
if ( \PHP_VERSION_ID >= 80000 ) {
$this -> addExtension ( new YieldNotReadyExtension ( $this -> useYield ));
}
$this -> addExtension ( new OptimizerExtension ( $options [ 'optimizations' ]));
}
/**
* @ internal
*/
public function useYield () : bool
{
return $this -> useYield ;
}
/**
* Enables debugging mode .
*/
public function enableDebug ()
{
$this -> debug = true ;
$this -> updateOptionsHash ();
}
/**
* Disables debugging mode .
*/
public function disableDebug ()
{
$this -> debug = false ;
$this -> updateOptionsHash ();
}
/**
* Checks if debug mode is enabled .
*
* @ return bool true if debug mode is enabled , false otherwise
*/
public function isDebug ()
{
return $this -> debug ;
}
/**
* Enables the auto_reload option .
*/
public function enableAutoReload ()
{
$this -> autoReload = true ;
}
/**
* Disables the auto_reload option .
*/
public function disableAutoReload ()
{
$this -> autoReload = false ;
}
/**
* Checks if the auto_reload option is enabled .
*
* @ return bool true if auto_reload is enabled , false otherwise
*/
public function isAutoReload ()
{
return $this -> autoReload ;
}
/**
* Enables the strict_variables option .
*/
public function enableStrictVariables ()
{
$this -> strictVariables = true ;
$this -> updateOptionsHash ();
}
/**
* Disables the strict_variables option .
*/
public function disableStrictVariables ()
{
$this -> strictVariables = false ;
$this -> updateOptionsHash ();
}
/**
* Checks if the strict_variables option is enabled .
*
* @ return bool true if strict_variables is enabled , false otherwise
*/
public function isStrictVariables ()
{
return $this -> strictVariables ;
}
2025-01-13 09:56:01 +00:00
public function removeCache ( string $name ) : void
{
$cls = $this -> getTemplateClass ( $name );
$this -> hotCache [ $name ] = $cls . '_' . bin2hex ( random_bytes ( 16 ));
if ( $this -> cache instanceof RemovableCacheInterface ) {
$this -> cache -> remove ( $name , $cls );
} else {
throw new \LogicException ( \sprintf ( 'The "%s" cache class does not support removing template cache as it does not implement the "RemovableCacheInterface" interface.' , \get_class ( $this -> cache )));
}
}
2024-06-20 14:10:42 +00:00
/**
* Gets the current cache implementation .
*
* @ param bool $original Whether to return the original cache option or the real cache instance
*
* @ return CacheInterface | string | false A Twig\Cache\CacheInterface implementation ,
* an absolute path to the compiled templates ,
* or false to disable cache
*/
public function getCache ( $original = true )
{
return $original ? $this -> originalCache : $this -> cache ;
}
/**
* Sets the current cache implementation .
*
* @ param CacheInterface | string | false $cache A Twig\Cache\CacheInterface implementation ,
* an absolute path to the compiled templates ,
* or false to disable cache
*/
public function setCache ( $cache )
{
if ( \is_string ( $cache )) {
$this -> originalCache = $cache ;
$this -> cache = new FilesystemCache ( $cache , $this -> autoReload ? FilesystemCache :: FORCE_BYTECODE_INVALIDATION : 0 );
} elseif ( false === $cache ) {
$this -> originalCache = $cache ;
$this -> cache = new NullCache ();
} elseif ( $cache instanceof CacheInterface ) {
$this -> originalCache = $this -> cache = $cache ;
} else {
throw new \LogicException ( 'Cache can only be a string, false, or a \Twig\Cache\CacheInterface implementation.' );
}
}
/**
* Gets the template class associated with the given string .
*
* The generated template class is based on the following parameters :
*
* * The cache key for the given template ;
* * The currently enabled extensions ;
* * PHP version ;
* * Twig version ;
* * Options with what environment was created .
*
* @ param string $name The name for which to calculate the template class name
* @ param int | null $index The index if it is an embedded template
*
* @ internal
*/
public function getTemplateClass ( string $name , ? int $index = null ) : string
{
2025-01-13 09:56:01 +00:00
$key = ( $this -> hotCache [ $name ] ? ? $this -> getLoader () -> getCacheKey ( $name )) . $this -> optionsHash ;
2024-06-20 14:10:42 +00:00
2024-09-05 17:51:48 +00:00
return '__TwigTemplate_' . hash ( \PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128' , $key ) . ( null === $index ? '' : '___' . $index );
2024-06-20 14:10:42 +00:00
}
/**
* Renders a template .
*
* @ param string | TemplateWrapper $name The template name
*
* @ throws LoaderError When the template cannot be found
* @ throws SyntaxError When an error occurred during compilation
* @ throws RuntimeError When an error occurred during rendering
*/
public function render ( $name , array $context = []) : string
{
return $this -> load ( $name ) -> render ( $context );
}
/**
* Displays a template .
*
* @ param string | TemplateWrapper $name The template name
*
* @ throws LoaderError When the template cannot be found
* @ throws SyntaxError When an error occurred during compilation
* @ throws RuntimeError When an error occurred during rendering
*/
public function display ( $name , array $context = []) : void
{
$this -> load ( $name ) -> display ( $context );
}
/**
* Loads a template .
*
* @ param string | TemplateWrapper $name The template name
*
* @ throws LoaderError When the template cannot be found
* @ throws RuntimeError When a previously generated cache is corrupted
* @ throws SyntaxError When an error occurred during compilation
*/
public function load ( $name ) : TemplateWrapper
{
if ( $name instanceof TemplateWrapper ) {
return $name ;
}
if ( $name instanceof Template ) {
trigger_deprecation ( 'twig/twig' , '3.9' , 'Passing a "%s" instance to "%s" is deprecated.' , self :: class , __METHOD__ );
return $name ;
}
return new TemplateWrapper ( $this , $this -> loadTemplate ( $this -> getTemplateClass ( $name ), $name ));
}
/**
* Loads a template internal representation .
*
* This method is for internal use only and should never be called
* directly .
*
* @ param string $name The template name
* @ param int | null $index The index if it is an embedded template
*
* @ throws LoaderError When the template cannot be found
* @ throws RuntimeError When a previously generated cache is corrupted
* @ throws SyntaxError When an error occurred during compilation
*
* @ internal
*/
public function loadTemplate ( string $cls , string $name , ? int $index = null ) : Template
{
$mainCls = $cls ;
if ( null !== $index ) {
$cls .= '___' . $index ;
}
if ( isset ( $this -> loadedTemplates [ $cls ])) {
return $this -> loadedTemplates [ $cls ];
}
if ( ! class_exists ( $cls , false )) {
$key = $this -> cache -> generateKey ( $name , $mainCls );
if ( ! $this -> isAutoReload () || $this -> isTemplateFresh ( $name , $this -> cache -> getTimestamp ( $key ))) {
$this -> cache -> load ( $key );
}
if ( ! class_exists ( $cls , false )) {
$source = $this -> getLoader () -> getSourceContext ( $name );
$content = $this -> compileSource ( $source );
2025-01-13 09:56:01 +00:00
if ( ! isset ( $this -> hotCache [ $name ])) {
$this -> cache -> write ( $key , $content );
$this -> cache -> load ( $key );
}
2024-06-20 14:10:42 +00:00
if ( ! class_exists ( $mainCls , false )) {
/* Last line of defense if either $this -> bcWriteCacheFile was used ,
* $this -> cache is implemented as a no - op or we have a race condition
* where the cache was cleared between the above calls to write to and load from
* the cache .
*/
eval ( '?>' . $content );
}
if ( ! class_exists ( $cls , false )) {
2024-09-05 17:51:48 +00:00
throw new RuntimeError ( \sprintf ( 'Failed to load Twig template "%s", index "%s": cache might be corrupted.' , $name , $index ), - 1 , $source );
2024-06-20 14:10:42 +00:00
}
}
}
$this -> extensionSet -> initRuntime ();
return $this -> loadedTemplates [ $cls ] = new $cls ( $this );
}
/**
* Creates a template from source .
*
* This method should not be used as a generic way to load templates .
*
* @ param string $template The template source
* @ param string | null $name An optional name of the template to be used in error messages
*
* @ throws LoaderError When the template cannot be found
* @ throws SyntaxError When an error occurred during compilation
*/
public function createTemplate ( string $template , ? string $name = null ) : TemplateWrapper
{
$hash = hash ( \PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128' , $template , false );
if ( null !== $name ) {
2024-09-05 17:51:48 +00:00
$name = \sprintf ( '%s (string template %s)' , $name , $hash );
2024-06-20 14:10:42 +00:00
} else {
2024-09-05 17:51:48 +00:00
$name = \sprintf ( '__string_template__%s' , $hash );
2024-06-20 14:10:42 +00:00
}
$loader = new ChainLoader ([
new ArrayLoader ([ $name => $template ]),
$current = $this -> getLoader (),
]);
$this -> setLoader ( $loader );
try {
return new TemplateWrapper ( $this , $this -> loadTemplate ( $this -> getTemplateClass ( $name ), $name ));
} finally {
$this -> setLoader ( $current );
}
}
/**
* Returns true if the template is still fresh .
*
* Besides checking the loader for freshness information ,
* this method also checks if the enabled extensions have
* not changed .
*
* @ param int $time The last modification time of the cached template
*/
public function isTemplateFresh ( string $name , int $time ) : bool
{
return $this -> extensionSet -> getLastModified () <= $time && $this -> getLoader () -> isFresh ( $name , $time );
}
/**
* Tries to load a template consecutively from an array .
*
* Similar to load () but it also accepts instances of \Twig\TemplateWrapper
* and an array of templates where each is tried to be loaded .
*
* @ param string | TemplateWrapper | array < string | TemplateWrapper > $names A template or an array of templates to try consecutively
*
* @ throws LoaderError When none of the templates can be found
* @ throws SyntaxError When an error occurred during compilation
*/
public function resolveTemplate ( $names ) : TemplateWrapper
{
if ( ! \is_array ( $names )) {
return $this -> load ( $names );
}
$count = \count ( $names );
foreach ( $names as $name ) {
if ( $name instanceof Template ) {
trigger_deprecation ( 'twig/twig' , '3.9' , 'Passing a "%s" instance to "%s" is deprecated.' , Template :: class , __METHOD__ );
return new TemplateWrapper ( $this , $name );
}
if ( $name instanceof TemplateWrapper ) {
return $name ;
}
if ( 1 !== $count && ! $this -> getLoader () -> exists ( $name )) {
continue ;
}
return $this -> load ( $name );
}
2024-09-05 17:51:48 +00:00
throw new LoaderError ( \sprintf ( 'Unable to find one of the following templates: "%s".' , implode ( '", "' , $names )));
2024-06-20 14:10:42 +00:00
}
public function setLexer ( Lexer $lexer )
{
$this -> lexer = $lexer ;
}
/**
* @ throws SyntaxError When the code is syntactically wrong
*/
public function tokenize ( Source $source ) : TokenStream
{
if ( null === $this -> lexer ) {
$this -> lexer = new Lexer ( $this );
}
return $this -> lexer -> tokenize ( $source );
}
public function setParser ( Parser $parser )
{
$this -> parser = $parser ;
}
/**
* Converts a token stream to a node tree .
*
* @ throws SyntaxError When the token stream is syntactically or semantically wrong
*/
public function parse ( TokenStream $stream ) : ModuleNode
{
if ( null === $this -> parser ) {
$this -> parser = new Parser ( $this );
}
return $this -> parser -> parse ( $stream );
}
public function setCompiler ( Compiler $compiler )
{
$this -> compiler = $compiler ;
}
/**
* Compiles a node and returns the PHP code .
*/
public function compile ( Node $node ) : string
{
if ( null === $this -> compiler ) {
$this -> compiler = new Compiler ( $this );
}
return $this -> compiler -> compile ( $node ) -> getSource ();
}
/**
* Compiles a template source code .
*
* @ throws SyntaxError When there was an error during tokenizing , parsing or compiling
*/
public function compileSource ( Source $source ) : string
{
try {
return $this -> compile ( $this -> parse ( $this -> tokenize ( $source )));
} catch ( Error $e ) {
$e -> setSourceContext ( $source );
throw $e ;
} catch ( \Exception $e ) {
2024-09-05 17:51:48 +00:00
throw new SyntaxError ( \sprintf ( 'An exception has been thrown during the compilation of a template ("%s").' , $e -> getMessage ()), - 1 , $source , $e );
2024-06-20 14:10:42 +00:00
}
}
public function setLoader ( LoaderInterface $loader )
{
$this -> loader = $loader ;
}
public function getLoader () : LoaderInterface
{
return $this -> loader ;
}
public function setCharset ( string $charset )
{
if ( 'UTF8' === $charset = strtoupper ( $charset ? : '' )) {
// iconv on Windows requires "UTF-8" instead of "UTF8"
$charset = 'UTF-8' ;
}
$this -> charset = $charset ;
}
public function getCharset () : string
{
return $this -> charset ;
}
public function hasExtension ( string $class ) : bool
{
return $this -> extensionSet -> hasExtension ( $class );
}
public function addRuntimeLoader ( RuntimeLoaderInterface $loader )
{
$this -> runtimeLoaders [] = $loader ;
}
/**
* @ template TExtension of ExtensionInterface
*
* @ param class - string < TExtension > $class
*
* @ return TExtension
*/
public function getExtension ( string $class ) : ExtensionInterface
{
return $this -> extensionSet -> getExtension ( $class );
}
/**
* Returns the runtime implementation of a Twig element ( filter / function / tag / test ) .
*
* @ template TRuntime of object
*
* @ param class - string < TRuntime > $class A runtime class name
*
* @ return TRuntime The runtime implementation
*
* @ throws RuntimeError When the template cannot be found
*/
public function getRuntime ( string $class )
{
if ( isset ( $this -> runtimes [ $class ])) {
return $this -> runtimes [ $class ];
}
foreach ( $this -> runtimeLoaders as $loader ) {
if ( null !== $runtime = $loader -> load ( $class )) {
return $this -> runtimes [ $class ] = $runtime ;
}
}
if ( null !== $runtime = $this -> defaultRuntimeLoader -> load ( $class )) {
return $this -> runtimes [ $class ] = $runtime ;
}
2024-09-05 17:51:48 +00:00
throw new RuntimeError ( \sprintf ( 'Unable to load the "%s" runtime.' , $class ));
2024-06-20 14:10:42 +00:00
}
public function addExtension ( ExtensionInterface $extension )
{
$this -> extensionSet -> addExtension ( $extension );
$this -> updateOptionsHash ();
}
/**
* @ param ExtensionInterface [] $extensions An array of extensions
*/
public function setExtensions ( array $extensions )
{
$this -> extensionSet -> setExtensions ( $extensions );
$this -> updateOptionsHash ();
}
/**
* @ return ExtensionInterface [] An array of extensions ( keys are for internal usage only and should not be relied on )
*/
public function getExtensions () : array
{
return $this -> extensionSet -> getExtensions ();
}
public function addTokenParser ( TokenParserInterface $parser )
{
$this -> extensionSet -> addTokenParser ( $parser );
}
/**
* @ return TokenParserInterface []
*
* @ internal
*/
public function getTokenParsers () : array
{
return $this -> extensionSet -> getTokenParsers ();
}
/**
* @ internal
*/
public function getTokenParser ( string $name ) : ? TokenParserInterface
{
return $this -> extensionSet -> getTokenParser ( $name );
}
public function registerUndefinedTokenParserCallback ( callable $callable ) : void
{
$this -> extensionSet -> registerUndefinedTokenParserCallback ( $callable );
}
public function addNodeVisitor ( NodeVisitorInterface $visitor )
{
$this -> extensionSet -> addNodeVisitor ( $visitor );
}
/**
* @ return NodeVisitorInterface []
*
* @ internal
*/
public function getNodeVisitors () : array
{
return $this -> extensionSet -> getNodeVisitors ();
}
public function addFilter ( TwigFilter $filter )
{
$this -> extensionSet -> addFilter ( $filter );
}
/**
* @ internal
*/
public function getFilter ( string $name ) : ? TwigFilter
{
return $this -> extensionSet -> getFilter ( $name );
}
public function registerUndefinedFilterCallback ( callable $callable ) : void
{
$this -> extensionSet -> registerUndefinedFilterCallback ( $callable );
}
/**
* Gets the registered Filters .
*
* Be warned that this method cannot return filters defined with registerUndefinedFilterCallback .
*
* @ return TwigFilter []
*
* @ see registerUndefinedFilterCallback
*
* @ internal
*/
public function getFilters () : array
{
return $this -> extensionSet -> getFilters ();
}
public function addTest ( TwigTest $test )
{
$this -> extensionSet -> addTest ( $test );
}
/**
* @ return TwigTest []
*
* @ internal
*/
public function getTests () : array
{
return $this -> extensionSet -> getTests ();
}
/**
* @ internal
*/
public function getTest ( string $name ) : ? TwigTest
{
return $this -> extensionSet -> getTest ( $name );
}
public function addFunction ( TwigFunction $function )
{
$this -> extensionSet -> addFunction ( $function );
}
/**
* @ internal
*/
public function getFunction ( string $name ) : ? TwigFunction
{
return $this -> extensionSet -> getFunction ( $name );
}
public function registerUndefinedFunctionCallback ( callable $callable ) : void
{
$this -> extensionSet -> registerUndefinedFunctionCallback ( $callable );
}
/**
* Gets registered functions .
*
* Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback .
*
* @ return TwigFunction []
*
* @ see registerUndefinedFunctionCallback
*
* @ internal
*/
public function getFunctions () : array
{
return $this -> extensionSet -> getFunctions ();
}
/**
* Registers a Global .
*
* New globals can be added before compiling or rendering a template ;
* but after , you can only update existing globals .
*
* @ param mixed $value The global value
*/
public function addGlobal ( string $name , $value )
{
if ( $this -> extensionSet -> isInitialized () && ! \array_key_exists ( $name , $this -> getGlobals ())) {
2024-09-05 17:51:48 +00:00
throw new \LogicException ( \sprintf ( 'Unable to add global "%s" as the runtime or the extensions have already been initialized.' , $name ));
2024-06-20 14:10:42 +00:00
}
if ( null !== $this -> resolvedGlobals ) {
$this -> resolvedGlobals [ $name ] = $value ;
} else {
$this -> globals [ $name ] = $value ;
}
}
/**
* @ return array < string , mixed >
*/
public function getGlobals () : array
{
if ( $this -> extensionSet -> isInitialized ()) {
if ( null === $this -> resolvedGlobals ) {
$this -> resolvedGlobals = array_merge ( $this -> extensionSet -> getGlobals (), $this -> globals );
}
return $this -> resolvedGlobals ;
}
return array_merge ( $this -> extensionSet -> getGlobals (), $this -> globals );
}
2025-01-13 09:56:01 +00:00
public function resetGlobals () : void
{
$this -> resolvedGlobals = null ;
$this -> extensionSet -> resetGlobals ();
}
/**
* @ deprecated since Twig 3.14
*/
2024-06-20 14:10:42 +00:00
public function mergeGlobals ( array $context ) : array
{
2025-01-13 09:56:01 +00:00
trigger_deprecation ( 'twig/twig' , '3.14' , 'The "%s" method is deprecated.' , __METHOD__ );
2024-06-20 14:10:42 +00:00
2025-01-13 09:56:01 +00:00
return $context + $this -> getGlobals ();
2024-06-20 14:10:42 +00:00
}
/**
* @ internal
*
2025-01-13 09:56:01 +00:00
* @ return array < string , array { precedence : int , precedence_change ? : OperatorPrecedenceChange , class : class - string < AbstractUnary > } >
2024-06-20 14:10:42 +00:00
*/
public function getUnaryOperators () : array
{
return $this -> extensionSet -> getUnaryOperators ();
}
/**
* @ internal
*
2025-01-13 09:56:01 +00:00
* @ return array < string , array { precedence : int , precedence_change ? : OperatorPrecedenceChange , class : class - string < AbstractBinary > , associativity : ExpressionParser :: OPERATOR_ * } >
2024-06-20 14:10:42 +00:00
*/
public function getBinaryOperators () : array
{
return $this -> extensionSet -> getBinaryOperators ();
}
private function updateOptionsHash () : void
{
$this -> optionsHash = implode ( ':' , [
$this -> extensionSet -> getSignature (),
\PHP_MAJOR_VERSION ,
\PHP_MINOR_VERSION ,
self :: VERSION ,
( int ) $this -> debug ,
( int ) $this -> strictVariables ,
$this -> useYield ? '1' : '0' ,
]);
}
}