--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/lib/Zend/Markup/Parser/Bbcode.php Fri Mar 11 15:05:35 2011 +0100
@@ -0,0 +1,504 @@
+<?php
+/**
+ * Zend Framework
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * It is also available through the world-wide-web at this URL:
+ * http://framework.zend.com/license/new-bsd
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to license@zend.com so we can send you a copy immediately.
+ *
+ * @category Zend
+ * @package Zend_Markup
+ * @subpackage Parser
+ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ * @version $Id: Bbcode.php 21127 2010-02-21 15:35:03Z kokx $
+ */
+
+/**
+ * @see Zend_Markup_TokenList
+ */
+require_once 'Zend/Markup/TokenList.php';
+
+/**
+ * @see Zend_Markup_Parser_ParserInterface
+ */
+require_once 'Zend/Markup/Parser/ParserInterface.php';
+
+/**
+ * @category Zend
+ * @package Zend_Markup
+ * @subpackage Parser
+ * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ */
+class Zend_Markup_Parser_Bbcode implements Zend_Markup_Parser_ParserInterface
+{
+ const NEWLINE = "[newline\0]";
+
+ // there is a parsing difference between the default tags and single tags
+ const TYPE_DEFAULT = 'default';
+ const TYPE_SINGLE = 'single';
+
+ const NAME_CHARSET = '^\[\]=\s';
+
+ const STATE_SCAN = 0;
+ const STATE_SCANATTRS = 1;
+ const STATE_PARSEVALUE = 2;
+
+ /**
+ * Token tree
+ *
+ * @var Zend_Markup_TokenList
+ */
+ protected $_tree;
+
+ /**
+ * Current token
+ *
+ * @var Zend_Markup_Token
+ */
+ protected $_current;
+
+ /**
+ * Source to tokenize
+ *
+ * @var string
+ */
+ protected $_value = '';
+
+ /**
+ * Length of the value
+ *
+ * @var int
+ */
+ protected $_valueLen = 0;
+
+ /**
+ * Current pointer
+ *
+ * @var int
+ */
+ protected $_pointer = 0;
+
+ /**
+ * The buffer
+ *
+ * @var string
+ */
+ protected $_buffer = '';
+
+ /**
+ * Temporary tag storage
+ *
+ * @var array
+ */
+ protected $_temp;
+
+ /**
+ * Stoppers that we are searching for
+ *
+ * @var array
+ */
+ protected $_searchedStoppers = array();
+
+ /**
+ * Tag information
+ *
+ * @var array
+ */
+ protected $_tags = array(
+ 'Zend_Markup_Root' => array(
+ 'type' => self::TYPE_DEFAULT,
+ 'stoppers' => array(),
+ ),
+ '*' => array(
+ 'type' => self::TYPE_DEFAULT,
+ 'stoppers' => array(self::NEWLINE, '[/*]', '[/]'),
+ ),
+ 'hr' => array(
+ 'type' => self::TYPE_SINGLE,
+ 'stoppers' => array(),
+ ),
+ 'code' => array(
+ 'type' => self::TYPE_DEFAULT,
+ 'stoppers' => array('[/code]', '[/]'),
+ 'parse_inside' => false
+ )
+ );
+
+ /**
+ * Token array
+ *
+ * @var array
+ */
+ protected $_tokens = array();
+
+ /**
+ * State
+ *
+ * @var int
+ */
+ protected $_state = self::STATE_SCAN;
+
+
+ /**
+ * Prepare the parsing of a bbcode string, the real parsing is done in {@link _parse()}
+ *
+ * @param string $value
+ * @return Zend_Markup_TokenList
+ */
+ public function parse($value)
+ {
+ if (!is_string($value)) {
+ /**
+ * @see Zend_Markup_Parser_Exception
+ */
+ require_once 'Zend/Markup/Parser/Exception.php';
+ throw new Zend_Markup_Parser_Exception('Value to parse should be a string.');
+ }
+
+ if (empty($value)) {
+ /**
+ * @see Zend_Markup_Parser_Exception
+ */
+ require_once 'Zend/Markup/Parser/Exception.php';
+ throw new Zend_Markup_Parser_Exception('Value to parse cannot be left empty.');
+ }
+
+ $this->_value = str_replace(array("\r\n", "\r", "\n"), self::NEWLINE, $value);
+
+ // variable initialization for tokenizer
+ $this->_valueLen = strlen($this->_value);
+ $this->_pointer = 0;
+ $this->_buffer = '';
+ $this->_temp = array();
+ $this->_state = self::STATE_SCAN;
+ $this->_tokens = array();
+
+ $this->_tokenize();
+
+ // variable initialization for treebuilder
+ $this->_searchedStoppers = array();
+ $this->_tree = new Zend_Markup_TokenList();
+ $this->_current = new Zend_Markup_Token(
+ '',
+ Zend_Markup_Token::TYPE_NONE,
+ 'Zend_Markup_Root'
+ );
+
+ $this->_tree->addChild($this->_current);
+
+ $this->_createTree();
+
+ return $this->_tree;
+ }
+
+ /**
+ * Tokenize
+ *
+ * @param string $input
+ *
+ * @return void
+ */
+ protected function _tokenize()
+ {
+ $attribute = '';
+
+ while ($this->_pointer < $this->_valueLen) {
+ switch ($this->_state) {
+ case self::STATE_SCAN:
+ $matches = array();
+ $regex = '#\G(?<text>[^\[]*)(?<open>\[(?<name>[' . self::NAME_CHARSET . ']+)?)?#';
+ preg_match($regex, $this->_value, $matches, null, $this->_pointer);
+
+ $this->_pointer += strlen($matches[0]);
+
+ if (!empty($matches['text'])) {
+ $this->_buffer .= $matches['text'];
+ }
+
+ if (!isset($matches['open'])) {
+ // great, no tag, we are ending the string
+ break;
+ }
+ if (!isset($matches['name'])) {
+ $this->_buffer .= $matches['open'];
+ break;
+ }
+
+ $this->_temp = array(
+ 'tag' => '[' . $matches['name'],
+ 'name' => $matches['name'],
+ 'attributes' => array()
+ );
+
+ if ($this->_pointer >= $this->_valueLen) {
+ // damn, no tag
+ $this->_buffer .= $this->_temp['tag'];
+ break 2;
+ }
+
+ if ($this->_value[$this->_pointer] == '=') {
+ $this->_pointer++;
+
+ $this->_temp['tag'] .= '=';
+ $this->_state = self::STATE_PARSEVALUE;
+ $attribute = $this->_temp['name'];
+ } else {
+ $this->_state = self::STATE_SCANATTRS;
+ }
+ break;
+ case self::STATE_SCANATTRS:
+ $matches = array();
+ $regex = '#\G((?<end>\s*\])|\s+(?<attribute>[' . self::NAME_CHARSET . ']+)(?<eq>=?))#';
+ if (!preg_match($regex, $this->_value, $matches, null, $this->_pointer)) {
+ break 2;
+ }
+
+ $this->_pointer += strlen($matches[0]);
+
+ if (!empty($matches['end'])) {
+ if (!empty($this->_buffer)) {
+ $this->_tokens[] = array(
+ 'tag' => $this->_buffer,
+ 'type' => Zend_Markup_Token::TYPE_NONE
+ );
+ $this->_buffer = '';
+ }
+ $this->_temp['tag'] .= $matches['end'];
+ $this->_temp['type'] = Zend_Markup_Token::TYPE_TAG;
+
+ $this->_tokens[] = $this->_temp;
+ $this->_temp = array();
+
+ $this->_state = self::STATE_SCAN;
+ } else {
+ // attribute name
+ $attribute = $matches['attribute'];
+
+ $this->_temp['tag'] .= $matches[0];
+
+ $this->_temp['attributes'][$attribute] = '';
+
+ if (empty($matches['eq'])) {
+ $this->_state = self::STATE_SCANATTRS;
+ } else {
+ $this->_state = self::STATE_PARSEVALUE;
+ }
+ }
+ break;
+ case self::STATE_PARSEVALUE:
+ $matches = array();
+ $regex = '#\G((?<quote>"|\')(?<valuequote>.*?)\\2|(?<value>[^\]\s]+))#';
+ if (!preg_match($regex, $this->_value, $matches, null, $this->_pointer)) {
+ $this->_state = self::STATE_SCANATTRS;
+ break;
+ }
+
+ $this->_pointer += strlen($matches[0]);
+
+ if (!empty($matches['quote'])) {
+ $this->_temp['attributes'][$attribute] = $matches['valuequote'];
+ } else {
+ $this->_temp['attributes'][$attribute] = $matches['value'];
+ }
+ $this->_temp['tag'] .= $matches[0];
+
+ $this->_state = self::STATE_SCANATTRS;
+ break;
+ }
+ }
+
+ if (!empty($this->_buffer)) {
+ $this->_tokens[] = array(
+ 'tag' => $this->_buffer,
+ 'type' => Zend_Markup_Token::TYPE_NONE
+ );
+ }
+ }
+
+ /**
+ * Parse the token array into a tree
+ *
+ * @param array $tokens
+ *
+ * @return void
+ */
+ public function _createTree()
+ {
+ foreach ($this->_tokens as $token) {
+ // first we want to know if this tag is a stopper, or at least a searched one
+ if ($this->_isStopper($token['tag'])) {
+ // find the stopper
+ $oldItems = array();
+
+ while (!in_array($token['tag'], $this->_tags[$this->_current->getName()]['stoppers'])) {
+ $oldItems[] = clone $this->_current;
+ $this->_current = $this->_current->getParent();
+ }
+
+ // we found the stopper, so stop the tag
+ $this->_current->setStopper($token['tag']);
+ $this->_removeFromSearchedStoppers($this->_current);
+ $this->_current = $this->_current->getParent();
+
+ // add the old items again if there are any
+ if (!empty($oldItems)) {
+ foreach (array_reverse($oldItems) as $item) {
+ /* @var $token Zend_Markup_Token */
+ $this->_current->addChild($item);
+ $item->setParent($this->_current);
+ $this->_current = $item;
+ }
+ }
+ } else {
+ if ($token['type'] == Zend_Markup_Token::TYPE_TAG) {
+ if ($token['tag'] == self::NEWLINE) {
+ // this is a newline tag, add it as a token
+ $this->_current->addChild(new Zend_Markup_Token(
+ "\n",
+ Zend_Markup_Token::TYPE_NONE,
+ '',
+ array(),
+ $this->_current
+ ));
+ } elseif (isset($token['name']) && ($token['name'][0] == '/')) {
+ // this is a stopper, add it as a empty token
+ $this->_current->addChild(new Zend_Markup_Token(
+ $token['tag'],
+ Zend_Markup_Token::TYPE_NONE,
+ '',
+ array(),
+ $this->_current
+ ));
+ } elseif (isset($this->_tags[$this->_current->getName()]['parse_inside'])
+ && !$this->_tags[$this->_current->getName()]['parse_inside']
+ ) {
+ $this->_current->addChild(new Zend_Markup_Token(
+ $token['tag'],
+ Zend_Markup_Token::TYPE_NONE,
+ '',
+ array(),
+ $this->_current
+ ));
+ } else {
+ // add the tag
+ $child = new Zend_Markup_Token(
+ $token['tag'],
+ $token['type'],
+ $token['name'],
+ $token['attributes'],
+ $this->_current
+ );
+ $this->_current->addChild($child);
+
+ // add stoppers for this tag, if its has stoppers
+ if ($this->_getType($token['name']) == self::TYPE_DEFAULT) {
+ $this->_current = $child;
+
+ $this->_addToSearchedStoppers($this->_current);
+ }
+ }
+ } else {
+ // no tag, just add it as a simple token
+ $this->_current->addChild(new Zend_Markup_Token(
+ $token['tag'],
+ Zend_Markup_Token::TYPE_NONE,
+ '',
+ array(),
+ $this->_current
+ ));
+ }
+ }
+ }
+ }
+
+ /**
+ * Check if there is a tag declaration, and if it isnt there, add it
+ *
+ * @param string $name
+ *
+ * @return void
+ */
+ protected function _checkTagDeclaration($name)
+ {
+ if (!isset($this->_tags[$name])) {
+ $this->_tags[$name] = array(
+ 'type' => self::TYPE_DEFAULT,
+ 'stoppers' => array(
+ '[/' . $name . ']',
+ '[/]'
+ )
+ );
+ }
+ }
+ /**
+ * Check the tag's type
+ *
+ * @param string $name
+ * @return string
+ */
+ protected function _getType($name)
+ {
+ $this->_checkTagDeclaration($name);
+
+ return $this->_tags[$name]['type'];
+ }
+
+ /**
+ * Check if the tag is a stopper
+ *
+ * @param string $tag
+ * @return bool
+ */
+ protected function _isStopper($tag)
+ {
+ $this->_checkTagDeclaration($this->_current->getName());
+
+ if (!empty($this->_searchedStoppers[$tag])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Add to searched stoppers
+ *
+ * @param Zend_Markup_Token $token
+ * @return void
+ */
+ protected function _addToSearchedStoppers(Zend_Markup_Token $token)
+ {
+ $this->_checkTagDeclaration($token->getName());
+
+ foreach ($this->_tags[$token->getName()]['stoppers'] as $stopper) {
+ if (!isset($this->_searchedStoppers[$stopper])) {
+ $this->_searchedStoppers[$stopper] = 0;
+ }
+ ++$this->_searchedStoppers[$stopper];
+ }
+ }
+
+ /**
+ * Remove from searched stoppers
+ *
+ * @param Zend_Markup_Token $token
+ * @return void
+ */
+ protected function _removeFromSearchedStoppers(Zend_Markup_Token $token)
+ {
+ $this->_checkTagDeclaration($token->getName());
+
+ foreach ($this->_tags[$token->getName()]['stoppers'] as $stopper) {
+ --$this->_searchedStoppers[$stopper];
+ }
+ }
+
+}