[limb-svn] r5788 - in 3.x/trunk/limb: . macro macro/src macro/tests macro/tests/cases

svn at limb-project.com svn at limb-project.com
Thu May 3 01:03:39 MSD 2007


Author: pachanga
Date: 2007-05-03 01:03:38 +0400 (Thu, 03 May 2007)
New Revision: 5788
URL: http://fisheye.limb-project.com/changelog/limb/?cs=5788

Added:
   3.x/trunk/limb/macro/
   3.x/trunk/limb/macro/common.inc.php
   3.x/trunk/limb/macro/src/
   3.x/trunk/limb/macro/src/lmbMacroParser.class.php
   3.x/trunk/limb/macro/src/lmbMacroParserListener.interface.php
   3.x/trunk/limb/macro/src/lmbMacroSourceLocation.class.php
   3.x/trunk/limb/macro/src/lmbMacroTemplate.class.php
   3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php
   3.x/trunk/limb/macro/tests/
   3.x/trunk/limb/macro/tests/cases/
   3.x/trunk/limb/macro/tests/cases/.init.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroParserMalformedTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroParserTest.class.php
   3.x/trunk/limb/macro/tests/cases/lmbMacroTemplateTest.class.php
Log:
-- initial(almost useless) commit of MACRO package

Added: 3.x/trunk/limb/macro/common.inc.php
===================================================================
--- 3.x/trunk/limb/macro/common.inc.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/common.inc.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,13 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id: common.inc.php 5247 2007-03-15 07:53:10Z serega $
+ * @package    macro
+ */
+require_once('limb/core/common.inc.php');
+?>
\ No newline at end of file

Added: 3.x/trunk/limb/macro/src/lmbMacroParser.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroParser.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroParser.class.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,255 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id$
+ * @package    macro
+ */
+
+class lmbMacroParser
+{
+  protected $publicId;
+  protected $observer;
+  protected $rawtext;
+  protected $position;
+  protected $length;
+
+  function __construct($observer)
+  {
+    $this->observer = $observer;
+  }
+
+  function getLineNumber()
+  {
+    return 1 + substr_count(substr($this->rawtext, 0, $this->position), "\n");
+  }
+
+  function getCurrentLocation()
+  {
+    return new lmbMacroSourceLocation($this->getPublicId(), $this->getLineNumber());
+  }
+
+  function getPublicId()
+  {
+    return $this->publicId;
+  }
+
+  /**
+  * Moves the position forward past any whitespace characters
+  */
+  function ignoreWhitespace()
+  {
+    while($this->position < $this->length &&
+        strpos(" \n\r\t", $this->rawtext{$this->position}) !== false)
+      $this->position++;
+  }
+
+  /**
+  * Begins the parsing operation, setting up any decorators, depending on
+  * parse options invoking _parse() to execute parsing
+  */
+  function parse($data, $publicId = null)
+  {
+    $this->rawtext = $data;
+    $this->length = strlen($data);
+    $this->position = 0;
+    $this->publicId = $publicId;
+
+    do
+    {
+      $start = $this->position;
+      $this->position = strpos($this->rawtext, '<%', $start);
+      if($this->position === false)
+      {
+        if($start < $this->length)
+          $this->observer->characters(substr($this->rawtext, $start));
+        return;
+      }
+
+      if($this->position > $start)
+      {
+        $this->observer->characters(substr($this->rawtext, $start, $this->position - $start));
+      }
+
+      $this->position += 2;   // ignore '<%' string
+      if($this->position >= $this->length)
+      {
+        $this->observer->unexpectedEOF('<%');
+        return;
+      }
+
+      $element_pos = $this->position;
+      $this->position += 1;
+
+      switch($this->rawtext{$element_pos})
+      {
+        case '/':
+          $start = $this->position;
+          while($this->position < $this->length &&
+                $this->rawtext{$this->position} != '%' &&
+                $this->rawtext{$this->position+1} != '>')
+            $this->position++;
+
+          if($this->position >= $this->length)
+          {
+            $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+            return;
+          }
+
+          $tag = substr($this->rawtext, $start, $this->position - $start);
+
+          $this->observer->endElement($tag);
+          $this->position += 2;   // ignore '%>' string
+          break;
+
+      default:
+          while($this->position < $this->length && strpos("%/ \n\r\t", $this->rawtext{$this->position}) === false)
+            $this->position++;
+
+          if($this->position >= $this->length)
+          {
+            $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+            return;
+          }
+
+          $tag = substr($this->rawtext, $element_pos, $this->position - $element_pos);
+          $attributes = array();
+
+          $this->ignoreWhitespace();
+
+          //tag attributes
+          while($this->position < $this->length &&
+                $this->rawtext{$this->position} != '%' &&
+                $this->rawtext{$this->position} != '/')
+          {
+            $start = $this->position;
+            while($this->position < $this->length && strpos("%= \n\r\t", $this->rawtext{$this->position}) === false)
+              $this->position++;
+
+            if($this->position >= $this->length)
+            {
+              $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+              return;
+            }
+
+            $attribute_name = substr($this->rawtext, $start, $this->position - $start);
+            $attribute_value = null;
+
+            $this->ignoreWhitespace();
+            if($this->position >= $this->length)
+            {
+              $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+              return;
+            }
+
+            if($this->rawtext{$this->position} == '=')
+            {
+              $attribute_value = "";
+
+              $this->position++;
+              $this->ignoreWhitespace();
+              if($this->position >= $this->length)
+              {
+                $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                return;
+              }
+
+              $quote = $this->rawtext{$this->position};
+              if($quote == '"' || $quote == "'")
+              {
+                $start = $this->position + 1;
+                $this->position = strpos($this->rawtext, $quote, $start);
+                if($this->position === false)
+                {
+                  $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                  return;
+                }
+
+                $attribute_value = substr($this->rawtext, $start, $this->position - $start);
+
+                $this->position++;
+                if($this->position >= $this->length)
+                {
+                  $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                  return;
+                }
+
+                if(strpos("% \n\r\t", $this->rawtext{$this->position}) === false)
+                  $this->observer->invalidAttributeSyntax();
+
+              }
+              else
+              {
+                $start = $this->position;
+                while($this->position < $this->length && strpos("% \n\r\t", $this->rawtext{$this->position}) === false)
+                  $this->position++;
+
+                if($this->position >= $this->length)
+                {
+                  $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+                  return;
+                }
+                $attribute_value = substr($this->rawtext, $start, $this->position - $start);
+              }
+            }
+
+            $attributes[$attribute_name] = $attribute_value;
+
+            $this->ignoreWhitespace();
+          }
+
+          if($this->position >= $this->length)
+          {
+            $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+            return;
+          }
+
+          //self closing tag check
+          if($this->rawtext{$this->position} == '/' && $this->rawtext{$this->position + 1} == '%')
+          {
+            $this->position += 2;
+            if($this->position >= $this->length)
+            {
+              $this->observer->unexpectedEOF(substr($this->rawtext, $element_pos - 1));
+              return;
+            }
+
+            if($this->rawtext{$this->position} != '>')
+            {
+              $start = $this->position;
+              while($this->position < $this->length && $this->rawtext{$this->position} != '>')
+                $this->position++;
+
+              if($this->position >= $this->length)
+              {
+                $this->observer->invalidEntitySyntax(substr($this->rawtext, $element_pos - 2));
+                break;
+              }
+
+              $this->observer->invalidEntitySyntax(substr($this->rawtext, $element_pos - 2,
+                                                          $this->position - $element_pos + 2));
+              $this->position += 1;
+              break;
+            }
+            $this->observer->emptyElement($tag, $attributes);
+          }
+          else
+          {
+            $this->observer->startElement($tag, $attributes);
+            //skipping %
+            $this->position += 1;
+          }
+
+          $this->position += 1;
+
+          break;
+        }
+    }
+    while ($this->position < $this->length);
+  }
+}
+?>

Added: 3.x/trunk/limb/macro/src/lmbMacroParserListener.interface.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroParserListener.interface.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroParserListener.interface.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id: WactParserListener.interface.php 5553 2007-04-06 09:05:17Z serega $
+ * @package    view
+ */
+
+interface lmbMacroParserListener
+{
+  function startElement($tag_name, $attrs);
+  function endElement($tag_name);
+  function emptyElement($tag_name, $attrs);
+  function characters($data);
+  function unexpectedEOF($data);
+  function invalidEntitySyntax($data);
+  function invalidAttributeSyntax();
+  function setDocumentLocator($locator);
+}
+?>
\ No newline at end of file

Added: 3.x/trunk/limb/macro/src/lmbMacroSourceLocation.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroSourceLocation.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroSourceLocation.class.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id: WactSourceLocation.class.php 5021 2007-02-12 13:04:07Z pachanga $
+ * @package    macro
+ */
+
+class lmbMacroSourceLocation
+{
+  public $file;
+  public $line;
+
+  function __construct($file = null, $line = null)
+  {
+    if($file)
+      $this->file = $file;
+    else
+      $this->file = 'unknown file';
+
+    if($line)
+      $this->line = $line;
+    else
+      $this->line = 'unknown line';
+  }
+
+  function getFile()
+  {
+    return $this->file;
+  }
+
+  function getLine()
+  {
+    return $this->line;
+  }
+}
+?>
\ No newline at end of file

Added: 3.x/trunk/limb/macro/src/lmbMacroTemplate.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTemplate.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroTemplate.class.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id: lmbPHPView.class.php 5012 2007-02-08 15:38:06Z pachanga $
+ * @package    macro
+ */
+
+class lmbMacroTemplate
+{
+  protected $file;
+  protected $cache_dir;
+  protected $vars = array();
+  protected $includes = array();
+
+  function __construct($file, $cache_dir)
+  {
+    $this->file = $file;
+    $this->cache_dir = $cache_dir;
+  }
+
+  function set($name, $value)
+  {
+    $this->vars[$name] = $value;
+  }
+
+  function render()
+  {
+    ob_start();
+    $file = $this->_compile($class);
+    include($file);
+    $body = new $class($this->vars);
+    $body->paint();
+    $out = ob_get_contents();
+    ob_end_clean();
+    return $out;
+  }
+
+  protected function _compile(&$class_name)
+  {
+    $contents = file_get_contents($this->file);
+    $prefix = 'p' . md5($this->file);
+    $class_name = "{$prefix}Body";
+    
+    $this->_processVars($contents);
+    $body = $this->_generateBody($class_name, $contents);
+
+    $compiled_file = $this->cache_dir . '/' . $prefix . '.php';
+    file_put_contents($compiled_file, $body, LOCK_EX);
+    return $compiled_file;
+  }
+
+  protected function _generateBody($class_name, $contents)
+  {
+    $include_methods = '';
+    foreach($this->includes as $name => $body)
+      $include_methods .= "$body\n";
+
+    $code = <<<EOD
+<?php
+class {$class_name}
+{
+  protected \$vars = array();
+
+  function __construct(\$vars)
+  {
+    \$this->vars = \$vars;
+  }
+
+  function __get(\$name)
+  {
+    if(isset(\$this->vars[\$name]))
+      return \$this->vars[\$name];
+  }
+
+  $include_methods
+
+  function paint(){ ?>$contents<?php }
+}
+?>
+EOD;
+  return $code;
+  }
+
+  protected function _processVars(&$contents)
+  {
+    $contents = str_replace('<?=', '<?php echo ', $contents);
+    $contents = preg_replace('~<\?(?!php|=)~', '<?php ', $contents);    
+    $contents = str_replace('@$', '$this->', $contents);
+    $contents = preg_replace_callback('~\{(\$[^\W]+)\}~', array($this, '_varSugarCallback'), $contents);
+    $contents = preg_replace_callback('~\{([^\W]+\([^\}]+)\}~', array($this, '_functionSugarCallback'), $contents);
+  }
+
+  protected function _varSugarCallback($matches)
+  {
+    return '<?php echo ' . $matches[1] . ' ?>';
+  }
+
+  protected function _functionSugarCallback($matches)
+  {
+    return '<?php echo ' . $matches[1] . ' ?>';
+  }
+
+  protected function _includeCallback($matches)
+  {
+    if(!preg_match('~file=(?:"|\')([^"\']+)(?:"|\')~', $matches[0], $m))
+      throw new lmbException('Invalid <%INCLUDE..>: ' . $matches[0]);
+
+    $file = lmbFs :: normalizePath($m[1]);
+
+    $args = '';
+    if(preg_match('~args=(?:"|\')\(([^\)]+)\)(?:"|\')~', $matches[0], $m))
+      $args = $m[1];
+
+    $contents = file_get_contents($file);
+    $this->_processIncludes($contents);
+    $method_name = 'paintInclude' . sizeof($this->includes);
+    $method_body = "function $method_name(){ \$args = func_get_args();extract(\$args);?>$contents<?php }";
+
+    $this->includes[$method_name] = $method_body;
+
+    return "<?php \$this->$method_name(array($args)); ?>";
+  }
+}
+?>

Added: 3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php
===================================================================
--- 3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/src/lmbMacroTreeBuilder.class.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,188 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id$
+ * @package    macro
+ */
+
+/**
+* Acts on the lmbMacroTreeRootNode in response to events within the lmbMacroSourceFileParser
+*
+* When adding an open tag to the tree, call pushExpectedTag().  When closing
+* a tag, call popExpectedTag(), which ensures the tree is balanced.
+*
+* These methods do not actually add nodes to the tree, as tags and nodes
+* do not necessarily match up.
+*
+* To add a node to the tree, you have the following choices.  To add a node
+* which can have children, use pushNode().  To add a terminal node use addNode(),
+* or addWactTextNode().
+*
+*/
+class lmbMacroTreeBuilder
+{
+    protected $compiler;
+    protected $component;
+
+    /**
+    * Stack of tags pushed onto the tree builder, may also contain components
+    * @see pushExpectedTag
+    * @see popExpectedTag
+    * @see pushCursor
+    * @var array of array($tagname, $info) or array($Component)
+    * @access private
+    */
+    var $expectedTags = array();
+
+    function __construct($compiler)
+    {
+      $this->compiler = $compiler;
+    }
+
+    /**
+    * Returns the current component
+    */
+    function getCursor()
+    {
+      return $this->component;
+    }
+
+    /**
+    * Sets the cursor (the current working component) of the tree builder
+    */
+    function setCursor($component)
+    {
+      $this->component = $component;
+    }
+
+    /**
+    * Begins a component's build phase in relation to the component tree.
+    * Adds a component to the tree, then makes that component the 'cursor'.
+    */
+    function pushNode($newComponent)
+    {
+      $this->component->addChild($newComponent);
+      $this->setCursor($newComponent);
+      return $this->component->preParse($this->compiler);
+    }
+
+    /**
+    * Adds a component to the tree, without descending into it.
+    * This begins and finishes the component's composition
+    */
+    function addNode($childComponent)
+    {
+      $childComponent->preParse($this->compiler);
+      $this->component->addChild($childComponent);
+    }
+
+    function addTextNode($text)
+    {
+      $this->addNode(new lmbMacroTextNode(null, $text));
+    }
+
+    /**
+    * Ends a component's build phase in relation to the tree.
+    * Checks child server ids and moves the 'cursor' up the tree to the parent
+    * component.
+    */
+    function popNode($hasClosingTag)
+    {
+      $this->component->hasClosingTag = $hasClosingTag;
+      $this->component->checkChildrenServerIds();
+      $this->setCursor($this->component->parent);
+    }
+
+    /**
+    * Expects the passed tag.  Optionally $info may be passed which is info
+    * about that tag.  The parser state that calls TreeBuilder may use this info
+    * to differentiate, say, plain vs. component tags.
+    */
+    function pushExpectedTag($tag, $info = null, $location = null)
+    {
+      array_push($this->expectedTags, array($tag, $info, $location));
+    }
+
+    /**
+    * Sets the cursor to a new position, and pushes the old cursor onto the
+    * expected tags stack.
+    * @see popExpectedTag
+    */
+    function pushCursor($newPosition, $location)
+    {
+      // use of array() is to preserve reference from array_pop()
+      array_push($this->expectedTags, array($this->component, PARSER_TAG_IS_COMPONENT, $location));
+      $this->setCursor($newPosition);
+    }
+
+    /**
+    * Tests the passed tag against what is expected.  Returns any info that
+    * was kept about the expected tag.
+    * If the item in the tag stack is a component, then the cursor is
+    * restored to that, and popExpectedTag is called again.
+    */
+    function popExpectedTag($tag, $location)
+    {
+      if(!$expectedTagItem = array_pop($this->expectedTags))
+      {
+        throw new lmbMacroException('Lonely closing tag', array('tag' => $tag,
+                                                            'file' => $location->getFile(),
+                                                            'line' => $location->getLine()));
+      }
+
+      // if we have a component on the stack, restore the cursor to that, and
+      // pop the stack again
+      if(is_object($expectedTagItem[0]))
+      {
+        $this->component =& $expectedTagItem[0];
+        return $this->popExpectedTag($tag, $location);
+      }
+
+      $expectedTag = $expectedTagItem[0];
+      $info = $expectedTagItem[1];
+
+      if(strcasecmp($expectedTag, $tag) !== 0)
+      {
+        throw new lmbMacroException('Unexpected closing tag',
+                                 array('file' => $location->getFile(),
+                                      'tag' => $tag,
+                                      'line' => $location->getLine(),
+                                      'ExpectTag' => $expectedTag,
+                                      'ExpectTagFile' => $expectedTagItem[2]->getFile(),
+                                      'ExpectedTagLine' => $expectedTagItem[2]->getLine()));
+      }
+      return $info;
+    }
+
+  /**
+  * Return the size of the expected tags stack
+  */
+  function getExpectedTagCount()
+  {
+    return count($this->expectedTags);
+  }
+
+  /**
+  * Returns the current expected tag
+  */
+  function getExpectedTag()
+  {
+    // Returns the tagname of the first non-component item on the stack
+    $item = end($this->expectedTags);
+    while($item && !is_string($item[0]))
+      $item = prev($this->expectedTags);
+    return $item ? $item[0] : false;
+  }
+
+  function getExpectedTagLocation()
+  {
+    $item = end($this->expectedTags);
+    return $item[2];
+  }
+}
+?>

Added: 3.x/trunk/limb/macro/tests/cases/.init.php
===================================================================
--- 3.x/trunk/limb/macro/tests/cases/.init.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/tests/cases/.init.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,3 @@
+<?php
+require_once(dirname(__FILE__) . '/../../common.inc.php');
+?>

Added: 3.x/trunk/limb/macro/tests/cases/lmbMacroParserMalformedTest.class.php
===================================================================
--- 3.x/trunk/limb/macro/tests/cases/lmbMacroParserMalformedTest.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/tests/cases/lmbMacroParserMalformedTest.class.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id$
+ * @package    view
+ */
+
+lmb_require('limb/macro/src/lmbMacroParserListener.interface.php');
+lmb_require('limb/macro/src/lmbMacroParser.class.php');
+
+Mock::generate('lmbMacroParserListener', 'MockMacroParserListener');
+
+class lmbMacroParserMalformedTest extends UnitTestCase
+{
+  protected $parser;
+  protected $listener;
+
+  function setUp()
+  {
+    $this->listener = new MockMacroParserListener();
+    $this->parser = new lmbMacroParser($this->listener);
+  }
+
+  function testOpenElementMalformedClose()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('invalidEntitySyntax', array('<%tag attribute=\'value\'/%morestuff'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<%tag attribute=\'value\'/%morestuff');
+  }
+
+  /*function testOpenElementMalformedClose2()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('invalidEntitySyntax', array('<tag attribute=\'value\'/morestuff>'));
+    $this->listener->expectNever('startElement');
+    $this->parser->parse('stuff<tag attribute=\'value\'/morestuff>');
+  }
+
+  function testElementNestedSingleQuote()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => '', "'" => NULL)));
+    $this->listener->expectOnce('invalidAttributeSyntax');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<tag attribute=\'\'\'>');
+  }
+
+  function testElementNestedDoubleQuote()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => '', '"' => NULL)));
+    $this->listener->expectOnce('invalidAttributeSyntax');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<tag attribute=""">');
+  }
+
+  function testElementMalformedAttribute()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => 'test', 'extra' => NULL)));
+    $this->listener->expectOnce('invalidAttributeSyntax');
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<tag attribute="test"extra>');
+  }*/
+}
+
+?>
\ No newline at end of file

Added: 3.x/trunk/limb/macro/tests/cases/lmbMacroParserTest.class.php
===================================================================
--- 3.x/trunk/limb/macro/tests/cases/lmbMacroParserTest.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/tests/cases/lmbMacroParserTest.class.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id$
+ * @package    view
+ */
+
+lmb_require('limb/macro/src/lmbMacroParserListener.interface.php');
+lmb_require('limb/macro/src/lmbMacroParser.class.php');
+
+Mock::generate('lmbMacroParserListener', 'MockMacroParserListener');
+
+class lmbMacroParserTest extends UnitTestCase
+{
+  protected $parser;
+  protected $listener;
+
+  function setUp()
+  {
+    $this->listener = new MockMacroParserListener();
+    $this->parser = new lmbMacroParser($this->listener);
+  }
+
+  function testEmpty()
+  {
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('startElement');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('');
+  }
+
+  function testSimpledata()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->parser->parse('stuff');
+  }
+
+  function testPreservingWhiteSpace()
+  {
+    $this->listener->expectOnce('characters', array(" stuff\t\r\n "));
+    $this->parser->parse(" stuff\t\r\n ");
+  }
+
+  function testEmptyElement()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array()));
+    $this->listener->expectOnce('endElement', array('tag'));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<%tag%><%/tag%>');
+  }
+
+  function testEmptyElementSelfClose()
+  {
+    $this->listener->expectOnce('emptyElement', array('tag', array()));
+    $this->listener->expectNever('startElement');
+    $this->listener->expectNever('endElement');
+    $this->parser->parse('<%tag/%>');
+  }
+
+  function testElementWithContent()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array()));
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('endElement', array('tag'));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<%tag%>stuff<%/tag%>');
+  }
+
+  function testElementNestedSingleQuote()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => '\'')));
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<%tag attribute="\'"%>');
+  }
+
+  function testElementNestedDoubleQuote()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array('attribute' => '"')));
+    $this->listener->expectNever('characters');
+    $this->listener->expectNever('endElement');
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<%tag attribute=\'"\'%>');
+  }
+
+
+  function testEmptyClose()
+  {
+    $this->listener->expectOnce('endElement', array(''));
+    $this->listener->expectNever('characters');
+    $this->parser->parse('<%/%>');
+  }
+
+  function testElementWithPreContent()
+  {
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectOnce('startElement', array('br', array()));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('stuff<%br%>');
+  }
+
+  function testElementWithPostContent()
+  {
+    $this->listener->expectOnce('startElement', array('br', array()));
+    $this->listener->expectOnce('characters', array('stuff'));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<%br%>stuff');
+  }
+
+  function testExpressionAfterTag()
+  {
+    $this->listener->expectOnce('emptyElement', array('br', array()));
+    $this->listener->expectOnce('characters', array('{$str}'));
+    $this->parser->parse('<%br/%>{$str}');
+  }
+
+  function testExpressionAfterTagWithArguments()
+  {
+    $this->listener->expectOnce('emptyElement', array('tag', array('str' => 'abcdefgh')));
+    $this->listener->expectOnce('characters', array('{$str}'));
+    $this->parser->parse('<%tag str="abcdefgh" /%>{$str}');
+  }
+
+  function testMismatchedElements()
+  {
+    $this->listener->expectArgumentsAt(0, 'startElement', array('b', array()));
+    $this->listener->expectArgumentsAt(1, 'startElement', array('i', array()));
+    $this->listener->expectArgumentsAt(0, 'endElement', array('b'));
+    $this->listener->expectArgumentsAt(1, 'endElement', array('i'));
+    $this->listener->expectCallCount('startElement', 2);
+    $this->listener->expectCallCount('endElement', 2);
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<%b%><%i%>stuff<%/b%><%/i%>');
+  }
+
+  function testAttributes()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array("a" => "A", "b" => "B", "c" => "C")));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<%tag a="A" b=\'B\' c = "C"%>');
+  }
+
+  function testEmptyAttributes()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array("a" => NULL, "b" => NULL, "c" => NULL)));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse('<%tag a b c%>');
+  }
+
+  function testNastyAttributes()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array("a" => "&%$'?<>",
+                                                                   "b" => "\r\n\t\"",
+                                                                   "c" => "")));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse("<%tag a=\"&%$'?<>\" b='\r\n\t\"' c = ''%>");
+  }
+
+  function testAttributesPadding()
+  {
+    $this->listener->expectOnce('startElement', array('tag', array("a" => "A", "b" => "B", "c" => "C")));
+    $this->listener->expectNever('invalidAttributeSyntax');
+    $this->parser->parse("<%tag\ta=\"A\"\rb='B'\nc = \"C\"\n%>");
+  }
+}
+?>
\ No newline at end of file

Added: 3.x/trunk/limb/macro/tests/cases/lmbMacroTemplateTest.class.php
===================================================================
--- 3.x/trunk/limb/macro/tests/cases/lmbMacroTemplateTest.class.php	                        (rev 0)
+++ 3.x/trunk/limb/macro/tests/cases/lmbMacroTemplateTest.class.php	2007-05-02 21:03:38 UTC (rev 5788)
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Limb Web Application Framework
+ *
+ * @link http://limb-project.com
+ *
+ * @copyright  Copyright &copy; 2004-2007 BIT
+ * @license    LGPL http://www.gnu.org/copyleft/lesser.html
+ * @version    $Id: lmbPHPViewTest.class.php 5012 2007-02-08 15:38:06Z pachanga $
+ * @package    macro
+ */
+lmb_require('limb/macro/src/lmbMacroTemplate.class.php');
+lmb_require('limb/fs/src/lmbFs.class.php');
+
+class lmbMacroTemplateTest extends UnitTestCase
+{
+  function setUp()
+  {
+    lmbFs :: rm(LIMB_VAR_DIR . '/view');
+    lmbFs :: mkdir(LIMB_VAR_DIR . '/view');
+    lmbFs :: mkdir(LIMB_VAR_DIR . '/view/tpl');
+    lmbFs :: mkdir(LIMB_VAR_DIR . '/view/compiled');
+  }
+
+  function testRenderTemplateVar()
+  {
+    $view = $this->_createView('Hello, <?$name = "Jack"?><?=@$name?>');
+    $view->set('name', 'Bob');
+    $this->assertEqual($view->render(), 'Hello, Bob');
+  }
+
+  function testRenderLocalVar()
+  {
+    $view = $this->_createView('Hello, <?$name = "Jack"?><?=$name?>');
+    $view->set('name', 'Bob');
+    $this->assertEqual($view->render(), 'Hello, Jack');
+  }
+
+  function testEchoVarSyntaxSugar()
+  {
+    $view = $this->_createView('Hello, <?$name = "Jack"?>{$name}');
+    $view->set('name', 'Bob');
+    $this->assertEqual($view->render(), 'Hello, Jack');
+  }
+
+  function testEchoFunctionSyntaxSugar()
+  {
+    $rnd = mt_rand();
+    $view = $this->_createView("Hello, <?function f_$rnd(){return 'Jack';}?>{f_$rnd()}");
+    $this->assertEqual($view->render(), 'Hello, Jack');
+  }
+
+  function _createView($tpl)
+  {
+    $file = $this->_createTemplate($tpl);
+    $view = new lmbMacroTemplate($file, LIMB_VAR_DIR . '/view/compiled');
+    return $view;
+  }
+
+  function _createTemplate($tpl)
+  {
+    $file = LIMB_VAR_DIR . '/view/tpl/' . mt_rand() . '.phtml';
+    file_put_contents($file, $tpl);
+    return $file;
+  }
+}
+
+?>
\ No newline at end of file



More information about the limb-svn mailing list