[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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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 © 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