added token parser broker (closes #104)
authorArnaud Le Blanc <arnaud.lb@gmail.com>
Sun, 12 Sep 2010 17:36:40 +0000 (19:36 +0200)
committerFabien Potencier <fabien.potencier@gmail.com>
Mon, 20 Sep 2010 09:17:03 +0000 (11:17 +0200)
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
lib/Twig/Environment.php
lib/Twig/Extension.php
lib/Twig/Extension/Escaper.php
lib/Twig/Extension/I18n.php
lib/Twig/Extension/Sandbox.php
lib/Twig/ExtensionInterface.php
lib/Twig/Parser.php
lib/Twig/TokenParserBroker.php [new file with mode: 0644]
lib/Twig/TokenParserBrokerInterface.php [new file with mode: 0644]

index d202eb8..3c3588b 100644 (file)
--- 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
index 28e8fe0..d25ff7f 100644 (file)
@@ -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');
+                    }
+                }
             }
         }
 
index a0eeb5b..0a99675 100644 (file)
@@ -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()
     {
index 639f8c6..cf43ad6 100644 (file)
@@ -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()
     {
index d1d7a10..8b29677 100644 (file)
@@ -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()
     {
index a298749..bc049fe 100644 (file)
@@ -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()
     {
index 756e953..de4f684 100644 (file)
@@ -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();
 
index 5d1f1be..2eb8883 100644 (file)
@@ -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 (file)
index 0000000..8e46b90
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ * (c) 2010 Arnaud Le Blanc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Default implementation of a token parser broker.
+ *
+ * @package    twig
+ * @author     Arnaud Le Blanc <arnaud.lb@gmail.com>
+ * @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 (file)
index 0000000..36a83cf
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+
+/*
+ * This file is part of Twig.
+ *
+ * (c) 2010 Fabien Potencier
+ * (c) 2010 Arnaud Le Blanc
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Interface implemented by token parser brokers.
+ *
+ * Token parser brokers allows to implement custom logic in the process of resolving a token parser for a given tag name.
+ *
+ * @package    twig
+ * @author     Arnaud Le Blanc <arnaud.lb@gmail.com>
+ * @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();
+}