} /** * Registers a Function. * * @param string|TwigFunction $name The function name or a \Twig_SimpleFunction instance * @param \Twig_FunctionInterface|TwigFunction $function */ public function addFunction($name, $function = null) { if (!$name instanceof TwigFunction && !($function instanceof TwigFunction || $function instanceof \Twig_FunctionInterface)) { throw new \LogicException('A function must be an instance of \Twig_FunctionInterface or \Twig_SimpleFunction.'); } if ($name instanceof TwigFunction) { $function = $name; $name = $function->getName(); } else { @trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated since version 1.21. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".', __METHOD__, $name), \E_USER_DEPRECATED); } if ($this->extensionInitialized) { throw new \LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name)); } $this->staging->addFunction($name, $function); } /** * Get a function by name. * * Subclasses may override this method and load functions differently; * so no list of functions is available. * * @param string $name function name * * @return \Twig_Function|false * * @internal */ public function getFunction($name) { if (!$this->extensionInitialized) { $this->initExtensions(); } if (isset($this->functions[$name])) { return $this->functions[$name]; } foreach ($this->functions as $pattern => $function) { $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); if ($count) { if (preg_match('#^'.$pattern.'$#', $name, $matches)) { array_shift($matches); $function->setArguments($matches); return $function; } } } foreach ($this->functionCallbacks as $callback) { if (false !== $function = \call_user_func($callback, $name)) { return $function; } } return false; } public function registerUndefinedFunctionCallback($callable) { $this->functionCallbacks[] = $callable; } /** * Gets registered functions. * * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback. * * @return \Twig_FunctionInterface[] * * @see registerUndefinedFunctionCallback * * @internal */ public function getFunctions() { if (!$this->extensionInitialized) { $this->initExtensions(); } return $this->functions; } /** * Registers a Global. * * New globals can be added before compiling or rendering a template; * but after, you can only update existing globals. * * @param string $name The global name * @param mixed $value The global value */ public function addGlobal($name, $value) { if ($this->extensionInitialized || $this->runtimeInitialized) { if (null === $this->globals) { $this->globals = $this->initGlobals(); } if (!\array_key_exists($name, $this->globals)) { // The deprecation notice must be turned into the following exception in Twig 2.0 @trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated since version 1.21.', $name), \E_USER_DEPRECATED); //throw new \LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name)); } } if ($this->extensionInitialized || $this->runtimeInitialized) { // update the value $this->globals[$name] = $value; } else { $this->staging->addGlobal($name, $value); } } /** * Gets the registered Globals. * * @return array An array of globals * * @internal */ public function getGlobals() { if (!$this->runtimeInitialized && !$this->extensionInitialized) { return $this->initGlobals(); } if (null === $this->globals) { $this->globals = $this->initGlobals(); } return $this->globals; } /** * Merges a context with the defined globals. * * @param array $context An array representing the context * * @return array The context merged with the globals */ public function mergeGlobals(array $context) { // we don't use array_merge as the context being generally // bigger than globals, this code is faster. foreach ($this->getGlobals() as $key => $value) { if (!\array_key_exists($key, $context)) { $context[$key] = $value; } } return $context; } /** * Gets the registered unary Operators. * * @return array An array of unary operators * * @internal */ public function getUnaryOperators() { if (!$this->extensionInitialized) { $this->initExtensions(); } return $this->unaryOperators; } /** * Gets the registered binary Operators. * * @return array An array of binary operators * * @internal */ public function getBinaryOperators() { if (!$this->extensionInitialized) { $this->initExtensions(); } return $this->binaryOperators; } /** * @deprecated since 1.23 (to be removed in 2.0) */ public function computeAlternatives($name, $items) { @trigger_error(sprintf('The %s method is deprecated since version 1.23 and will be removed in Twig 2.0.', __METHOD__), \E_USER_DEPRECATED); return SyntaxError::computeAlternatives($name, $items); } /** * @internal */ protected function initGlobals() { $globals = []; foreach ($this->extensions as $name => $extension) { if (!$extension instanceof GlobalsInterface) { $m = new \ReflectionMethod($extension, 'getGlobals'); $parentClass = $m->getDeclaringClass()->getName(); if ('Twig_Extension' !== $parentClass && 'Twig\Extension\AbstractExtension' !== $parentClass) { @trigger_error(sprintf('Defining the getGlobals() method in the "%s" extension without explicitly implementing Twig\Extension\GlobalsInterface is deprecated since version 1.23.', $name), \E_USER_DEPRECATED); } } $extGlob = $extension->getGlobals(); if (!\is_array($extGlob)) { throw new \UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', \get_class($extension))); } $globals[] = $extGlob; } $globals[] = $this->staging->getGlobals(); return \call_user_func_array('array_merge', $globals); } /** * @internal */ protected function initExtensions() { if ($this->extensionInitialized) { return; } $this->parsers = new \Twig_TokenParserBroker([], [], false); $this->filters = []; $this->functions = []; $this->tests = []; $this->visitors = []; $this->unaryOperators = []; $this->binaryOperators = []; foreach ($this->extensions as $extension) { $this->initExtension($extension); } $this->initExtension($this->staging); // Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception $this->extensionInitialized = true; } /** * @internal */ protected function initExtension(ExtensionInterface $extension) { // filters foreach ($extension->getFilters() as $name => $filter) { if ($filter instanceof TwigFilter) { $name = $filter->getName(); } else { @trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated since version 1.21. Use \Twig_SimpleFilter instead.', \get_class($filter), $name), \E_USER_DEPRECATED); } $this->filters[$name] = $filter; } // functions foreach ($extension->getFunctions() as $name => $function) { if ($function instanceof TwigFunction) { $name = $function->getName(); } else { @trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated since version 1.21. Use \Twig_SimpleFunction instead.', \get_class($function), $name), \E_USER_DEPRECATED); } $this->functions[$name] = $function; } // tests foreach ($extension->getTests() as $name => $test) { if ($test instanceof TwigTest) { $name = $test->getName(); } else { @trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated since version 1.21. Use \Twig_SimpleTest instead.', \get_class($test), $name), \E_USER_DEPRECATED); } $this->tests[$name] = $test; } // token parsers foreach ($extension->getTokenParsers() as $parser) { if ($parser instanceof TokenParserInterface) { $this->parsers->addTokenParser($parser); } elseif ($parser instanceof \Twig_TokenParserBrokerInterface) { @trigger_error('Registering a \Twig_TokenParserBrokerInterface instance is deprecated since version 1.21.', \E_USER_DEPRECATED); $this->parsers->addTokenParserBroker($parser); } else { throw new \LogicException('getTokenParsers() must return an array of \Twig_TokenParserInterface or \Twig_TokenParserBrokerInterface instances.'); } } // node visitors foreach ($extension->getNodeVisitors() as $visitor) { $this->visitors[] = $visitor; } // operators if ($operators = $extension->getOperators()) { if (!\is_array($operators)) { throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', \get_class($extension), \is_object($operators) ? \get_class($operators) : \gettype($operators).(\is_resource($operators) ? '' : '#'.$operators))); } if (2 !== \count($operators)) { throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', \get_class($extension), \count($operators))); } $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]); } } /** * @deprecated since 1.22 (to be removed in 2.0) */ protected function writeCacheFile($file, $content) { $this->cache->write($file, $content); } private function updateOptionsHash() { $hashParts = array_merge( array_keys($this->extensions), [ (int) \function_exists('twig_template_get_attributes'), \PHP_MAJOR_VERSION, \PHP_MINOR_VERSION, self::VERSION, (int) $this->debug, $this->baseTemplateClass, (int) $this->strictVariables, ] ); $this->optionsHash = implode(':', $hashParts); } } class_alias('Twig\Environment', 'Twig_Environment');