All pastes #468377 Raw Edit

k4st

public php v1 · immutable
·published 2007-05-03 02:06 UTC
rendered paste body
<?phpclass TemplateParser{	// the prefix on a template tag, <prefix:foo>	protected $tag_prefix;		// an associative array to handle tag names and	// their compiling class names	protected $tag_handlers = array();		// a timer	protected $start_time = 0;	protected $end_time = 0;		// construct	public function __construct()	{		// add the supported tags in		$this->addTag('for');		$this->addTag('foreach');		$this->addTag('print');		$this->addTag('import');				// set the tag prefix		$this->setPrefix('fa:');				// begin a timer		$this->start_time = microtime();	}		// set tag prefix	final public function setPrefix($prefix = 'fa:')	{		$this->tag_prefix = preg_quote($prefix);	}		// add tag handlers given a tag name	final public function addTag($tag_name)	{		// figure out the class name		$class_name = ucfirst($tag_name) .'Node';				// make sure the class exists		if(!class_exists($class_name))			throw new Exception("Tag handler class for [{$class_name}] does not exist or cannot be included.");				// add the class to the tag handlers under the		// tag name		$this->tag_handlers[$tag_name] = $class_name;	}		// get a tag handler class name given the lower	// case tag name	final public function getTagHandler($tag_name)	{		// default to the unknown node tag handler		$ret = 'UnknownNode';				// check if this tag is set		if(isset($this->tag_handlers[$tag_name]))			$ret = $this->tag_handlers[$tag_name];				return $ret;	}		// parse some text	final public function parse($buffer)	{		$buffer = $this->parseTags($buffer);		$buffer = $this->parseVariables($buffer);				$this->end_time = microtime();				return $buffer;	}		// get how long it took to parse	final public function getParseTime()	{		list($start_sec, $start_micro) = explode(" ", $this->start_time);		list($end_sec, $end_micro) = explode(" ", $this->end_time);		$start = (float)$start_sec .".". $start_micro;		$end = (float)$end_sec .".". $end_micro;		return (float)round(($end - $start), 4);	}		// parse the tags	final protected function parseTags($buffer = '')	{		// split up the buffer into tags and text		$parts = preg_split("~<(/?)". $this->tag_prefix ."([^>]*)>~i", $buffer, -1, PREG_SPLIT_DELIM_CAPTURE);						// create a node stack, this will keep track of		// non-text nodes, and push our root node onto		// it as the first node.		$stack = new NodeStack;		$stack->push(new RootNode(''));				// note: when the buffer is split up, it will		// end up in this format: (do a print_r($parts))		//		//[0] text node		//[1] / or nothing		//[2] tag and arguments		// .. repeat this process ..		//		// observations we can make:		// every 0th key will be a text node.		// the 1st key will tell us if it's a closing tag		// the 2nd key will have the extra info				// store the parsed buffer		$parsed_buffer = '';				// keep track of whether we are looping over a closing		// tag or not		$closing = FALSE;				//print_r($parts); exit;				// loop through the split up parts of the buffer and		// also increment a tracker		for($i = 0; $i < count($parts); $i++)		{			// get the last node added to the stack, the first			// iteration will always have this as the root node			$parent = $stack->last();						// the results of this will tell us what type of 			// node we are on			$key = $i % 3;						// text nodes			if($key == 0)			{				// add this text to the parent nodes buffer				$parent->addTextToBuffer($parts[$i]);								// reset closing to false so that future tags				// don't get popped out for no reason.				$closing = FALSE;			}						// normal tags			else			{				// split the tag up into its arguments				$tag_parts = preg_split("~([^\W]*)(=(\"|')?([a-z0-9\s.-_]*)(\"|')?)~i", $parts[$i+1], -1, PREG_SPLIT_DELIM_CAPTURE);								// try to get the tag name				$tag_name = trim(strtolower($tag_parts[0]));				// parse out the arguments from the split up tag parts				$arguments = $this->parseTagArguments($tag_parts);				// a closing tag has been found, pop it out of 				// the stack				if($parts[$i] == '/')				{					// tell the next iteration that this tag was					// a closing tag					$closing = TRUE;										// need to pop the current node off the stack					// otherwise we will end up adding text to itself					$node = $stack->pop();										// the new parent is the new last node in the stack					$parent = $stack->last();										// whoops, we have a finishing tag without a starting					// one or a malformed one!					if($node->getName() != $tag_name)					{						// reset the stack to where it was						$stack->push($node);						$parent = $stack->last();												// add in a HTML error						$parent->addTextToBuffer('<!-- BAD CLOSING TAG FOR ['. $this->tag_prefix . $tag_name .'] -->');					}					else {												$parent->addTextToBuffer($node->parseBuffer());					}				}								// and opening tag, push it onto the stack				if(!$closing)				{					// get the tag handler class name given the tag name					$class_name = $this->getTagHandler($tag_name);										// instanciate the appropriate node class					$node = new $class_name($tag_name);										$node->addArguments($arguments);										// is this a non-closing tag? Parse it without ever					// putting it on the stack										if(trim($tag_parts[count($tag_parts)-1]) == '/')						$parent->addTextToBuffer($node->parseBuffer());										// normal opening tag					else						$stack->push($node);				}								$i++;			}		}				$temp_buffer = '';		while($node = $stack->pop())					// is this the root node?			if($node instanceof RootNode)			{				// add any text back into it from malformed				// tags then parse it.				$node->addTextToBuffer($temp_buffer);				$parsed_buffer .= $node->parseBuffer();			}						// extract the buffers from the malformed tags			// and put them back into the root node later.			else				$temp_buffer .= $node->extractBuffer();						return $parsed_buffer;	}		// parse variables	final protected function parseVariables($buffer)	{		$buffer = preg_replace('~{\$([a-zA-Z_-][a-zA-Z0-9_-]*)}~', '<?php echo $scope->pullVar("$1"); ?>', $buffer);		$buffer = preg_replace('~{\@([a-zA-Z_-][a-zA-Z0-9_-]*)}~', '$scope->pullVar("$1")', $buffer);				return $buffer;	}		// parse the tag arguments from a split up array	final public function parseTagArguments($parts = array())	{		unset($parts[0]);				$parts = array_values($parts);		$arguments = array();					for($i = 0; $i < count($parts); $i++)		{			if(isset($parts[$i+3]))				$arguments[$parts[$i]] = $parts[$i+3];						$i += 5;		}				return $arguments;	}}// a stack to keep track of non-text nodesclass NodeStack{	protected $stack = array();	protected $key = -1;		public function push(ClosingNode $node)	{		$this->stack[] = $node;		$this->key++;	}	public function pop()	{				$ret = FALSE;		$this->key--;		return array_pop($this->stack);	}	public function last()	{		return $this->seek($this->key);	}	public function seek($index = 0)	{		$ret = FALSE;		if(isset($this->stack[$index]))		{			$ret = $this->stack[$index];			$this->key = $index;		}				return $ret;	}}// our base node class, all node compiling classes// must extend this class.abstract class ClosingNode{	protected $buffer = '';	protected $name = '';	protected $arguments = array();		public function __construct($name = '')	{		$this->name = $name;	}	final public function addArguments($array)	{		$this->arguments = array_merge($this->arguments, $array);	}	final public function addTextToBuffer($buffer = '')	{		$this->buffer .= $buffer;	}	final public function parseBuffer()	{		return $this->parseOpen($this->arguments) . $this->buffer . $this->parseClose();	}	final public function extractBuffer()	{		return $this->buffer;	}	final public function getName()	{		return $this->name;	}		abstract public function parseOpen($args = array());	abstract public function parseClose();}// non-closing tags only require parseOpen functionsabstract class NonClosingNode extends ClosingNode{	final public function parseClose()	{		return '';	}}// the root nodeclass RootNode extends ClosingNode{	public function parseOpen($args = array())	{		return '';	}	public function parseClose()	{			return '';	}}// an unknown nodeclass UnknownNode extends ClosingNode{	public function parseOpen($args = array())	{		return '<!-- UNKNOWN NODE -->';	}	public function parseClose()	{			return '<!-- / UNKNOWN NODE -->';	}}?>