From f7febc0c2c2959a28411b61a7fa8c6a39f6847ff Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Sun, 12 Sep 2010 19:36:40 +0200 Subject: [PATCH] added token parser broker (closes #104) Twig_Extension::getTokenParsers() can now return an array of TokenParsers, or a Twig_TokenParserBrokerInterface. This allows extensions to implement custom logics for resolving token parsers. --- CHANGELOG | 1 + lib/Twig/Environment.php | 15 ++++- lib/Twig/Extension.php | 2 +- lib/Twig/Extension/Escaper.php | 4 +- lib/Twig/Extension/I18n.php | 4 +- lib/Twig/Extension/Sandbox.php | 4 +- lib/Twig/ExtensionInterface.php | 2 +- lib/Twig/Parser.php | 12 +-- lib/Twig/TokenParserBroker.php | 109 +++++++++++++++++++++++++++++++ lib/Twig/TokenParserBrokerInterface.php | 45 +++++++++++++ 10 files changed, 179 insertions(+), 19 deletions(-) create mode 100644 lib/Twig/TokenParserBroker.php create mode 100644 lib/Twig/TokenParserBrokerInterface.php diff --git a/CHANGELOG b/CHANGELOG index d202eb8..3c3588b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,7 @@ Backward incompatibilities: * the odd and even filters are now tests: {{ foo|odd }} must now be written {{ foo is odd }} + * added a way to implement custom logic for resolving token parsers given a tag name * fixed js escaper to be stricter (now uses a whilelist-based js escaper) * added a "constant" filter * added a "constant" test diff --git a/lib/Twig/Environment.php b/lib/Twig/Environment.php index 28e8fe0..d25ff7f 100644 --- a/lib/Twig/Environment.php +++ b/lib/Twig/Environment.php @@ -342,15 +342,24 @@ class Twig_Environment $this->getTokenParsers(); } - $this->parsers[] = $parser; + $this->parsers->addTokenParser($parser); } public function getTokenParsers() { if (null === $this->parsers) { - $this->parsers = array(); + $this->parsers = new Twig_TokenParserBroker; foreach ($this->getExtensions() as $extension) { - $this->parsers = array_merge($this->parsers, $extension->getTokenParsers()); + $parsers = $extension->getTokenParsers(); + foreach($parsers as $parser) { + if ($parser instanceof Twig_TokenParserInterface) { + $this->parsers->addTokenParser($parser); + } else if ($parser instanceof Twig_TokenParserBrokerInterface) { + $this->parsers->addTokenParserBroker($parser); + } else { + throw new InvalidArgumentException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances'); + } + } } } diff --git a/lib/Twig/Extension.php b/lib/Twig/Extension.php index a0eeb5b..0a99675 100644 --- a/lib/Twig/Extension.php +++ b/lib/Twig/Extension.php @@ -22,7 +22,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface /** * Returns the token parser instances to add to the existing list. * - * @return array An array of Twig_TokenParser instances + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances */ public function getTokenParsers() { diff --git a/lib/Twig/Extension/Escaper.php b/lib/Twig/Extension/Escaper.php index 639f8c6..cf43ad6 100644 --- a/lib/Twig/Extension/Escaper.php +++ b/lib/Twig/Extension/Escaper.php @@ -18,9 +18,9 @@ class Twig_Extension_Escaper extends Twig_Extension } /** - * Returns the token parser instance to add to the existing list. + * Returns the token parser instances to add to the existing list. * - * @return array An array of Twig_TokenParser instances + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances */ public function getTokenParsers() { diff --git a/lib/Twig/Extension/I18n.php b/lib/Twig/Extension/I18n.php index d1d7a10..8b29677 100644 --- a/lib/Twig/Extension/I18n.php +++ b/lib/Twig/Extension/I18n.php @@ -11,9 +11,9 @@ class Twig_Extension_I18n extends Twig_Extension { /** - * Returns the token parser instance to add to the existing list. + * Returns the token parser instances to add to the existing list. * - * @return array An array of Twig_TokenParser instances + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances */ public function getTokenParsers() { diff --git a/lib/Twig/Extension/Sandbox.php b/lib/Twig/Extension/Sandbox.php index a298749..bc049fe 100644 --- a/lib/Twig/Extension/Sandbox.php +++ b/lib/Twig/Extension/Sandbox.php @@ -21,9 +21,9 @@ class Twig_Extension_Sandbox extends Twig_Extension } /** - * Returns the token parser instance to add to the existing list. + * Returns the token parser instances to add to the existing list. * - * @return array An array of Twig_TokenParser instances + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances */ public function getTokenParsers() { diff --git a/lib/Twig/ExtensionInterface.php b/lib/Twig/ExtensionInterface.php index 756e953..de4f684 100644 --- a/lib/Twig/ExtensionInterface.php +++ b/lib/Twig/ExtensionInterface.php @@ -28,7 +28,7 @@ interface Twig_ExtensionInterface /** * Returns the token parser instances to add to the existing list. * - * @return array An array of Twig_TokenParser instances + * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances */ public function getTokenParsers(); diff --git a/lib/Twig/Parser.php b/lib/Twig/Parser.php index 5d1f1be..2eb8883 100644 --- a/lib/Twig/Parser.php +++ b/lib/Twig/Parser.php @@ -43,12 +43,8 @@ class Twig_Parser implements Twig_ParserInterface public function parse(Twig_TokenStream $stream) { // tag handlers - $this->handlers = array(); - foreach ($this->env->getTokenParsers() as $handler) { - $handler->setParser($this); - - $this->handlers[$handler->getTag()] = $handler; - } + $this->handlers = $this->env->getTokenParsers(); + $this->handlers->setParser($this); // node visitors $this->visitors = $this->env->getNodeVisitors(); @@ -118,13 +114,13 @@ class Twig_Parser implements Twig_ParserInterface return new Twig_Node($rv, array(), $lineno); } - if (!isset($this->handlers[$token->getValue()])) { + $subparser = $this->handlers->getTokenParser($token->getValue()); + if (null === $subparser) { throw new Twig_SyntaxError(sprintf('Unknown tag name "%s"', $token->getValue()), $token->getLine()); } $this->stream->next(); - $subparser = $this->handlers[$token->getValue()]; $node = $subparser->parse($token); if (!is_null($node)) { $rv[] = $node; diff --git a/lib/Twig/TokenParserBroker.php b/lib/Twig/TokenParserBroker.php new file mode 100644 index 0000000..8e46b90 --- /dev/null +++ b/lib/Twig/TokenParserBroker.php @@ -0,0 +1,109 @@ + + * @version SVN: $Id$ + */ +class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface +{ + protected $parser; + protected $parsers = array(); + protected $brokers = array(); + + /** + * Constructor + * + * @param array|Iterable $parsers An Iterable of Twig_TokenParserInterface instances + * @param array|Iterable $brokers An Iterable of Twig_TokenParserBrokerInterface instances + */ + public function __construct($parsers = array(), $brokers = array()) + { + foreach($parsers as $parser) { + if (!$parser instanceof Twig_TokenParserInterface) { + throw new InvalidArgumentException( + '$parsers must a an array of Twig_TokenParserInterface'); + } + $this->parsers[$parser->getTag()] = $parser; + } + foreach($brokers as $broker) { + if (!$broker instanceof Twig_TokenParserBrokerInterface) { + throw new InvalidArgumentException( + '$brokers must a an array of Twig_TokenParserBrokerInterface'); + } + $this->brokers[] = $broker; + } + } + + /** + * Adds a TokenParser + * + * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance + */ + public function addTokenParser(Twig_TokenParserInterface $parser) + { + $this->parsers[$parser->getTag()] = $parser; + } + + /** + * Adds a TokenParserBroker + * + * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance + */ + public function addTokenParserBroker(Twig_TokenParserBroker $broker) + { + $this->brokers[] = $broker; + } + + /** + * Get a suitable TokenParser for $tag + * + * First looks in parsers, then in brokers. + * + * @param string $tag A tag name + * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found + */ + public function getTokenParser($tag) + { + if (isset($this->parsers[$tag])) { + return $this->parsers[$tag]; + } + $broker = end($this->brokers); + while (false !== $broker) { + $parser = $broker->getTokenParser($tag); + if (null !== $parser) { + return $parser; + } + $broker = prev($this->brokers); + } + return null; + } + + public function getParser() + { + return $this->parser; + } + + public function setParser(Twig_ParserInterface $parser) + { + $this->parser = $parser; + foreach($this->parsers as $tokenParser) { + $tokenParser->setParser($parser); + } + foreach($this->brokers as $broker) { + $broker->setParser($parser); + } + } +} diff --git a/lib/Twig/TokenParserBrokerInterface.php b/lib/Twig/TokenParserBrokerInterface.php new file mode 100644 index 0000000..36a83cf --- /dev/null +++ b/lib/Twig/TokenParserBrokerInterface.php @@ -0,0 +1,45 @@ + + * @version SVN: $Id$ + */ +interface Twig_TokenParserBrokerInterface +{ + /** + * Get a TokenParser suitable for $tag + * + * @param string $tag A tag name + * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found + */ + function getTokenParser($tag); + + /** + * Calls Twig_TokenParserInterface::setParser on all parsers the implementation knowns of + * + * @param Twig_ParserInterface $parser A Twig_ParserInterface interface + */ + function setParser(Twig_ParserInterface $parser); + + /** + * Get the Twig_ParserInterface + * + * @return null|Twig_ParserInterface A Twig_ParserInterface instance of null + */ + function getParser(); +} -- 1.7.2.5