Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- /* ubb.php
- A safe Ubbcode libary
- by Henrik Andersson aka henke37
- Free for noncommercial use as long as I get credit. I appertiate a notice about the code being used.
- Comercial use includes, but is not limited to, using it on a site that has ads.
- Exception, sites that I am an active member of have the rigths to use it,
- even for comercial uses.
- No fee is to be charged for getting the code,
- except a transfer fee to pay for the transfer, if needed.
- No waranties for quality, lack of defects or other problems.
- Classes:
- BadUbbCodeException - Thrown if any invalid code is found
- UbbDynData - Attached image filename, current time, userid of reader and things like that
- UbbSmiley - A smiley
- UbbTag - An ubbtag
- functions:
- nextchar - strpos that takes an array of needles and returns both position and what was matched in an array
- highlightPHPCode - A function that wraps the builtin PHP code highlighning
- */
- require dirname(__FILE__).'/function.nextchar.php';
- define('UBBCODE_DYNDATACHAR','$');
- define('UBBCODE_SMILEYCHAR',':');
- define('UBBCODE_TAGBEGIN','[');
- define('UBBCODE_TAGEND',']');
- class BadUbbCodeException extends Exception {
- var $offset;
- function __construct($error,$offset=0) {
- parent::__construct($error);
- $this->offset=$offset;
- }
- };
- class InvalidDynDataException extends BadUbbCodeException {};
- class UbbDynData {
- public $type;
- public static $callbacks=array();
- public static $types=array('time'=>true,'author'=>true);
- function __construct($type) {
- $this->type=$type;
- if(!isset(self::$types[$type])) {
- throw new InvalidDynDataException("There is no $type dyndata type");
- }
- }
- function getUbb() {
- return UBBCODE_DYNDATACHAR.$this->type.UBBCODE_DYNDATACHAR;
- }
- function getDynData() {
- //Do I have to say that you need to escape the input to any database queries?
- if(isset(self::$callbacks[$this->type])) {
- return call_user_func(self::$callbacks[$this->type]);
- }
- switch($this->type) {
- case 'time':
- return date('r');
- break;
- case 'author':
- return 'henke37';
- break;
- default:
- throw new Exception('getDynData somehow doesn\'t recognice the dyndata!');
- break;
- }
- }
- } #end of class UbDynData
- class UbbSmiley {
- public $type;
- public $originaltype;
- public $substMatch;
- public static $substitutionmap=array(';)'=>'wink',';-)'=>'wink');
- public static $smileymap=array('wink'=>'wink.png');
- public static $smileypath='/smilies/';
- function __construct($type,$substMatch=false) {
- $this->originaltype=$type;
- if($substMatch) {
- $type=self::$substitutionmap[$type];
- $this->substMatch=true;
- }
- if(!isset(self::$smileymap[$type])) {
- throw new BadUbbcodeException(
- "Undefined smiley \"$type\" used!"
- );
- }
- $this->type=$type;
- }
- function getUbb() {
- if($this->substMatch) {
- return $this->originaltype;
- }
- if(defined('UBBCODE_COMPLEX_SMILIES')) {
- return UBBCODE_SMILEYCHAR.$this->originaltype.UBBCODE_SMILEYCHAR;
- } else {
- return $this->originaltype;
- }
- }
- function getHtml() {
- $image=self::$smileymap[$this->type];
- if(substr($image,-4)=='.swf') {
- return 'Flash smilies is not yet implemented, please goto line number '.__LINE__ .' in file '.__FILE__.' and add the proper html for it';
- } else {
- return sprintf(
- '<img src="%s%s" alt="Smiley: %s"/>',
- self::$smileypath,
- $image,
- htmlentities($this->type)
- );
- }
- }
- }
- function highlightPHPCode($code,$language) {
- return highlight_string($code,true);
- }
- class UbbTag {
- public $type;
- public $usedname;
- //nested arrays that need access methods to be used as one
- public $contents;
- public $atteributes;
- //tag data
- static public $tagaliases=array();
- static protected $embedingtags=array();
- static protected $noendtagtags=array();
- static public $allowednesting=array();
- static public $forbidendeepnesting=array();
- static public $parserbypasstags=array();
- static public $urltags=array();
- static public $customtagCallback=array();
- //misc related data
- static public $allowedprotocols=array();
- static public $allowedembedprotocols=array();
- static public $highlightengines=array();
- //private stuff nobody should mess with
- static protected $tagID;//a counter for id values
- public static function initdata($extrablock,$extrainline,$extranesting,$extrainteractive) {
- self::$urltags=array(
- 'img'=>array('href',0,'url','src'),
- 'flash'=>array('href',0,'url','src'),
- 'url'=>array('href',0,'url')
- );
- self::$tagaliases=array(
- 'link'=>'url',
- 'image'=>'img',
- 'noparse'=>'noubb',
- 'raw'=>'noubb'
- );
- self::$embedingtags=array(
- 'img'=>true,
- 'flash'=>true,
- 'sound'=>true,
- 'video'=>true
- );
- self::$noendtagtags=array(
- '*'=>true,
- '_root'=>true
- );
- $blocktags=array(
- 'list'=>true,
- 'table'=>true,
- 'quote'=>true,
- 'code'=>true,
- 'spoiler'=>true,
- 'sql'=>true,
- 'html'=>true,
- 'indent'=>true
- );
- $inlinetags=array(
- 'url'=>true,
- 'google'=>true,
- 'img'=>true,
- 'b'=>true,
- 'i'=>true,
- 's'=>true,
- 'flash'=>true,
- 'noubb'=>true,
- 'comment'=>true,
- 'left'=>true,
- 'center'=>true,
- 'right'=>true,
- 'sub'=>true,
- 'sup'=>true,
- 'background'=>true,
- 'color'=>true,
- 'size'=>true,
- 'font'=>true,
- 'email'=>true,
- 'comment'=>true
- );
- $commontags=array_merge($inlinetags,$blocktags);
- self::$highlightengines=array(
- 'php'=>'highlightPHPCode'
- );
- self::$allowedprotocols=array(
- 'http',
- 'https',
- 'telnet',
- 'mailto',
- 'ftp',
- 'ftps',
- 'irc',
- 'ircs',
- 'news'
- );
- self::$allowedembedprotocols=array(
- 'http',
- 'https',
- 'ftp',
- 'ftps'
- );
- self::$allowednesting=array(
- '_root'=>$commontags,
- 'list'=>array('*'=>true,'comment'=>true),
- '*'=>$commontags,
- 'table'=>array('row'=>true,'comment'=>true),
- 'row'=>array('cell'=>true,'comment'=>true),
- 'cell'=>$commontags,
- 'quote'=>$commontags,
- 'spoiler'=>$commontags,
- 'b'=>$inlinetags,
- 'i'=>$inlinetags,
- 'sub'=>$inlinetags,
- 'google'=>$inlinetags,
- 'left'=>$inlinetags,
- 'center'=>$inlinetags,
- 'right'=>$inlinetags,
- 'sub'=>$inlinetags,
- 'sup'=>$inlinetags,
- 'background'=>$commontags,
- 'color'=>$inlinetags,
- 'size'=>$inlinetags,
- 'font'=>$inlinetags,
- 'indent'=>$commontags,
- 'email'=>$inlinetags
- );
- $interactivetags=array('spoiler','flash','url','google','email');
- self::$forbidendeepnesting=array(
- 'url'=>$interactivetags,
- 'flash'=>$interactivetags,
- 'google'=>$interactivetags,
- 'sub'=>array('sup'=>true),
- 'sup'=>array('sub'=>true)
- );
- self::$parserbypasstags=array(
- 'noubb'=>true,
- 'code'=>true
- );
- }
- public function __construct($type,$contents,$atteributes,$usedname='') {
- if(empty($type)) {
- throw new Exception('Ubbtag can not be constructed with an empty tag type!');
- }
- $this->type = $type;
- if(!is_array($contents)) {
- var_dump($contents);
- throw new Exception('Ubbtag can not be constructed when the content is not an array!');
- }
- $this->contents = $contents;
- if(!is_array($atteributes)) {
- throw new Exception('Ubbtag can not be constructed when the atteributes is not an array!');
- }
- $this->atteributes = $atteributes;
- if($usedname=='') {
- $this->usedname=$type;
- } else {
- $this->usedname=$usedname;
- }
- if(isset(self::$urltags[$this->type])) {
- if(!$this->hasatteribute(self::$urltags[$this->type])) {
- foreach($this->contents as $content) {
- if($content instanceof self or $content instanceof UbbSmiley) {
- throw new BadUbbCodeException(
- "{$this->type} tags that does not use an atteribute for the url may not contain any nested tags or smileys"
- );
- }
- }
- }
- $embeding=(isset(self::$embedingtags[$this->type]) and self::$embedingtags[$this->type]);
- $url= (
- $this->hasatteribute(self::$urltags[$this->type]) ?
- $this->getatteribute(self::$urltags[$this->type]) :
- self::handleDynData($this->contents)
- );
- self::checkvalidurl($url,$embeding);
- }
- switch($this->type) {
- }
- }
- static private function checkvalidurl($url,$isembeding) {
- //throws a BadUbbCodeException if the url is not ok
- if(preg_match('#^([a-z]+):#i',$url,$matches)) {
- $allowedprotocols=$isembeding ? self::$allowedembedprotocols :self::$allowedprotocols;
- if(!in_array($matches[1],$allowedprotocols)) {
- throw new BadUbbCodeException("The url $url is not using one of the allowed protocols");
- }
- } else {
- $url='http://'.$url;
- }
- }
- static private function ubbparse_commonend(&$state,$char) {//used by the state switcher in the parser
- switch($char) {
- case UBBCODE_TAGBEGIN:
- $state='tagbegining';
- break;
- case UBBCODE_DYNDATACHAR:
- $state='dyndatabegin';
- break;
- case UBBCODE_SMILEYCHAR:
- if(defined('UBBCODE_COMPLEX_SMILIES')) {
- $state='smileybegin';
- break;
- }
- //no break, fall trught
- default:
- $state='text';
- break;
- }
- }
- static function parse($input) {
- //throw new BadUbbCodeException('Can\'t parse without a parser implemention!');
- $contents=array(0=>array());
- $deep=0;
- $tagstack=array();
- $tagname='';
- $attvalue=array();
- if(defined('UBBCODE_DEBUG') && UBBCODE_DEBUG>=1) {
- ?><table class="ubbcodedebug">
- <caption><?php echo htmlentities($input);?></caption>
- <thead>
- <tr><th>Prev state</th><th>Character</th><th>New state</th>
- <?php if(UBBCODE_DEBUG>=2) { ?><th>$contents</th><th>$tagstack</th><th>$attvalue</th><th>$atts</th><th>$i</th><?php } ?></tr>
- </thead>
- <?php
- }
- $state='text';
- //Valid states:
- //text
- //tagbegining
- //tagname
- //tagend
- //tagattspace
- //taglongattname
- //taglongattequ
- //taglongattbeginquote
- //taglongattvalue
- //taglongattendquote
- //taglongattescape
- //taglongattescape2
- //taglongattdyndataescape
- //taglongattdyndatachar
- //taglongattdyndataend
- //dyndatabegin
- //dyndatachar
- //dyndataend
- //smileybegin
- //smileychar
- //smileyend
- //tagshortattequ
- //tagshortattchar
- //tagshortattcomma
- //tagshortattbeginquote
- //tagshortattquotechar
- //tagshortattendquote
- //tagshortattbegindyndata
- //tagshortattdyndatachar
- //tagshortattdyndataend
- //tagshortattquotedyndbeginescape
- //tagshortattdyndatachar
- //tagshortattdyndataend
- //tagshortattendquote
- for($i=0;$i<strlen($input);++$i) {
- $char=$input[$i];
- if(defined('UBBCODE_DEBUG') && UBBCODE_DEBUG>=1) {
- echo '<tr><td>'.htmlentities($state).'</td><td>'.htmlentities($char).'</td>';
- }
- //Chose the current state based on the previous state and the input
- switch($state) {
- case 'text';
- self::ubbparse_commonend($state,$char);
- break;
- case 'tagbegining':
- switch($char) {
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- case UBBCODE_TAGBEGIN:
- throw new BadUbbCodeException(
- 'A "'.UBBCODE_TAGEND.'" is needed before another "'.UBBCODE_TAGBEGIN.'".',
- $i
- );
- break;
- default:
- $state='tagname';
- break;
- }
- break;
- case 'tagname':
- switch($char) {
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- case '=':
- $state='tagshortattequ';
- break;
- case ' ':
- $state='tagattspace';
- break;
- case UBBCODE_TAGBEGIN:
- throw new BadUbbCodeException(
- 'A "'.UBBCODE_TAGEND.'" is needed before another "'.UBBCODE_TAGBEGIN.'".',
- $i
- );
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'tagend':
- self::ubbparse_commonend($state,$char);
- break;
- case 'tagattspace':
- switch($char) {
- case ' ':
- //no state change
- break;
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- case UBBCODE_TAGBEGIN:
- throw new BadUbbCodeException(
- 'It is invalid to begin a new tag inside the begining of another tag',
- $i
- );
- break;
- default:
- $state='taglongattname';
- break;
- }
- break;
- case 'taglongattname':
- switch($char) {
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- case '=':
- $state='taglongattequ';
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'taglongattequ':
- switch($char) {
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- case '"';
- $state='taglongattbeginquote';
- break;
- default:
- $state='taglongattvalue';
- break;
- }
- break;
- case 'taglongattbeginquote':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='taglongattdyndataescape';
- break;
- case '"':
- $state='taglongattendquote';
- break;
- default:
- $state='taglongattvalue';
- break;
- }
- break;
- case 'taglongattvalue':
- switch($char) {
- case '\\':
- $state='taglongescape';
- break;
- case UBBCODE_DYNDATACHAR:
- $state='taglongattdyndataescape';
- break;
- case '"':
- $state='taglongattendquote';
- break;
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'taglongattendquote':
- switch($char) {
- case ' ':
- $state='tagattspace';
- break;
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- default:
- $state='taglongattname';
- break;
- }
- break;
- case 'taglongattescape':
- $state='taglongattescape2';
- break;
- case 'taglongattescape2':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='taglongattdyndataescape';
- break;
- case '\\':
- $state='taglongattescape';
- break;
- default:
- $state='taglongattvalue';
- break;
- }
- break;
- case 'taglongattdyndataescape':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='taglongattvalue';
- break;
- default:
- $state='taglongattdyndatachar';
- break;
- }
- break;
- case 'taglongattdyndatachar':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='taglongattdyndataend';
- break;
- default:
- //no statechange
- break;
- }
- break;
- case 'taglongattdyndataend';
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='taglongattdyndataescape';
- break;
- case '\\':
- $state='taglongattescape';
- break;
- case '"':
- $state='taglongattendquote';
- break;
- default:
- $state='taglongattvalue';
- break;
- }
- break;
- case 'dyndatabegin':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='dyndataend';
- break;
- default:
- $state='dyndatachar';
- break;
- }
- break;
- case 'dyndatachar':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='dyndataend';
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'dyndataend':
- self::ubbparse_commonend($state,$char);
- break;
- case 'smileybegin':
- switch($char) {
- case UBBCODE_SMILEYCHAR:
- $state='smileyend';
- break;
- default:
- $state='smileychar';
- break;
- }
- break;
- case 'smileychar':
- switch($char) {
- case UBBCODE_SMILEYCHAR:
- $state='smileyend';
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'smileyend':
- self::ubbparse_commonend($state,$char);
- break;
- case 'tagshortattequ':
- switch($char) {
- case UBBCODE_TAGEND;
- $state='tagend';
- break;
- case ',':
- $state='tagshortattcomma';
- break;
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattbegindyndata';
- break;
- case '"':
- $state='tagshortattbeginquote';
- break;
- default:
- $state='tagshortattchar';
- break;
- }
- break;
- case 'tagshortattchar':
- switch($char) {
- case UBBCODE_TAGEND;
- $state='tagend';
- break;
- case ',':
- $state='tagshortattcomma';
- break;
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattbegindyndata';
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'tagshortattcomma':
- switch($char) {
- case UBBCODE_TAGEND;
- $state='tagend';
- break;
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattbegindyndata';
- break;
- case '"':
- $state='tagshortattbeginquote';
- break;
- case ',':
- //no state change
- break;
- case ' ':
- $state='tagshortattspace';
- break;
- default:
- $state='tagshortattchar';
- break;
- }
- break;
- case 'tagshortattbeginquote':
- switch($char) {
- case '"':
- $state='tagshortattendquote';
- break;
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattquotedyndatabeginescape';
- break;
- default:
- $state='tagshortattquotechar';
- break;
- }
- break;
- case 'tagshortattquotedyndatabeginescape':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattquotedyndataend';
- break;
- default:
- $state='tagshortattquotedyndatachar';
- break;
- }
- break;
- case 'tagshortattdyndatachar':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattdyndataend';
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'tagshortattquotedyndataend':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattquotedyndatabeginescape';
- break;
- case '"':
- $state='tagshortattendquote';
- break;
- default:
- $state='tagshortattquotechar';
- break;
- }
- break;
- case 'tagshortattbegindyndata':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattdyndataend';
- break;
- default:
- $state='tagshortattdyndatachar';
- break;
- }
- break;
- case 'tagshortattquotechar':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattquotedyndatabeginescape';
- break;
- case '"':
- $state='tagshortattendquote';
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'tagshortattendquote':
- switch($char) {
- case ' ':
- $state='tagshortattspace';
- break;
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- case ',':
- $state='tagshortattcomma';
- break;
- default:
- throw new BadUbbCodeException(
- "Parser too confused to continue at character $i,".
- " expecting a space, a comma or a tagend",
- $i
- );
- break;
- }
- break;
- case 'tagshortattquotedyndatachar':
- switch($char) {
- case UBBCODE_DYNDATACHAR:
- $state='tagshortattquotedyndataend';
- break;
- default:
- //no state change
- break;
- }
- break;
- case 'tagshortattdyndataend':
- switch($char) {
- case ',':
- $state='tagshortattcomma';
- break;
- case UBBCODE_TAGEND:
- $state='tagend';
- break;
- default:
- $state='tagshortattchar';
- break;
- }
- break;
- case 'tagshortattspace':
- switch($char) {
- case ' ':
- //no state change
- break;
- case ',':
- $state='tagshortattcomma';
- break;
- default:
- $state='taglongattname';
- break;
- }
- break;
- default:
- throw new Exception("Unknown state, $state");
- break;
- }
- if(defined('UBBCODE_DEBUG') && UBBCODE_DEBUG==true) {
- echo '<td>'.htmlentities($state)."</td>\r\n";
- }
- ################################################################################################
- //Now do the stuff in each state
- switch($state) {
- case 'text';
- $contents[$deep][]=$char;//we will merge the array later
- break;
- case 'tagbegining':
- $atts=array();
- $tagname="";
- break;
- case 'tagname':
- $tagname.=$char;
- break;
- case 'tagend':
- $tagname=strtolower($tagname);
- if($tagname=='') {
- throw new BadUbbcodeException(
- "A tag must have a name!",
- $i
- );
- } elseif($tagname=='_root') {
- throw new BadUbbcodeException(
- "The tag \"_root\" is a reservated tag, don't use it!",
- $i
- );
- }
- if(isset($attname) and ($attname or $attname===0) and count($attvalue)) {
- //type is important due to not allowing zero lenght strings
- $atts[$attname]=$attvalue;
- }
- $usedtagname=$tagname;
- while(isset(self::$tagaliases[$tagname])) {
- $tagname=self::$tagaliases[$tagname];
- }
- if(count($tagstack)) {
- $parrentname=$tagstack[count($tagstack)-1]['tagname'];
- } else {
- $parrentname='_root';
- }
- if(substr($tagname,0,1)=='/') {
- $stripedtagname=substr($tagname,1);
- if($parrentname=='*' and $tagstack[count($tagstack)-2]['tagname']=='list' and $stripedtagname=='list') {
- //close the * tag to allow for legacy syntax
- $deep--;
- $taginfo=array_pop($tagstack);
- $contents[$deep][]=new self($taginfo['tagname'],array_pop($contents),$taginfo['atts'],$taginfo['usedtagname']);
- }
- $deep--;
- if($deep<0) {
- throw new BadUbbCodeException(
- 'There is more closed tags than there was opened!',
- $i
- );
- }
- $taginfo=array_pop($tagstack);
- if(strlen($stripedtagname)>0) {
- if($stripedtagname!=$taginfo['usedtagname']) {
- throw new BadUbbcodeException(
- "Tag \"{$taginfo['tagname']}\" was ended with a \"$tagname\" instead of a \"/{$taginfo['usedtagname']}\" tag",
- $i
- );
- }
- }
- $contents[$deep][]=new self($taginfo['tagname'],array_pop($contents),$taginfo['atts'],$taginfo['usedtagname']);
- } else {
- if($tagname=='*' and $parrentname=='*') {
- //close the old * tag to allow legacy syntax
- $deep--;
- $taginfo=array_pop($tagstack);
- $contents[$deep][]=new self($taginfo['tagname'],array_pop($contents),$taginfo['atts'],$taginfo['usedtagname']);
- } elseif(!isset(self::$allowednesting[$parrentname][$tagname] ) ) {
- throw new BadUbbCodeException(
- "A \"$tagname\" tag may not be nested in a \"$parrentname\" tag",
- $i
- );
- } elseif( isset(self::$forbidendeepnesting[$tagname]) ) {
- foreach(array_slice($tagstack,0,-1) as $tagstackelement) {
- foreach(self::$forbidendeepnesting[$tagname] as $badtagname) {
- if($tagstackelement['tagname']==$badtagname) {
- throw new BadUbbcodeException(
- "A \"{$tagname}\" tag may not be nested in a \"{$tagstackelement['tagname']}\" tag!",
- $i
- );
- }
- }
- }
- }
- if(isset(self::$parserbypasstags[$tagname]) and self::$parserbypasstags[$tagname]==true) {
- $endtag=UBBCODE_TAGBEGIN.'/'.$usedtagname.UBBCODE_TAGEND;
- $endpos=@stripos($input,$endtag,$i+1);
- if($endpos===FALSE) {
- throw new BadUbbCodeException(
- 'Parser disabled tag not terminated properly',
- $i
- );
- }
- $tagcontents=array( substr($input,$i+1,$endpos-($i+1)) );
- $i=$endpos+strlen($endtag)-1;//the loop adds one at the end
- $contents[$deep][]=new self($tagname,$tagcontents,$atts,$usedtagname);
- } else {
- $tagstack[]=array('tagname'=>$tagname,'atts'=>$atts,'usedtagname'=>$usedtagname);
- $deep++;
- $contents[$deep]=array();
- }
- $atts=array();
- $attvalue=array();
- $attname='';
- } #end else (not / at beginging)
- break;
- case 'taglongattspace':
- $attname='';
- $attvalue=array();
- break;
- case 'taglongattname':
- if(!isset($attname) or is_numeric($attname)) {
- $attname='';
- }
- $attname.=$char;
- break;
- case 'taglongattequ':
- $attvalue=array();
- break;
- case 'taglongattbeginquote':
- break;
- case 'taglongattvalue':
- $attvalue[]=$char;
- break;
- case 'taglongattendquote':
- $atts[$attname]=$attvalue;
- $attvalue=array();
- $attname='';
- break;
- case 'taglongattescape':
- break;
- case 'taglongattescape2':
- switch($char) {
- case '"';
- $attvalue[]='"';
- break;
- case '\\':
- $attvalue[]='\\';
- break;
- default:
- throw new BadUbbcodeException(
- "Illegal escape character used $char",
- $i
- );
- break;
- }
- break;
- case 'taglongattdyndataescape':
- $dyndataname="";
- break;
- case 'taglongattdyndatachar':
- $dyndataname.=$char;
- break;
- case 'taglongattdyndataend':
- $attvalue[]=new UbbDynData($dyndataname);
- break;
- case 'dyndatabegin':
- $dyndataname="";
- break;
- case 'dyndatachar':
- $dyndataname.=$char;
- break;
- case 'dyndataend':
- if($dyndataname=='') {
- $contents[$deep][]=UBBCODE_DYNDATACHAR;
- } else {
- $contents[$deep][]=new UbbDynData($dyndataname);
- }
- break;
- case 'smileybegin':
- $smileyname="";
- break;
- case 'smileychar':
- $smileyname.=$char;
- break;
- case 'smileyend':
- if($smileyname=='') {
- $contents[$deep][]=UBBCODE_SMILEYCHAR;
- } else {
- $contents[$deep][]=new UbbSmiley($smileyname);
- }
- break;
- case 'tagshortattequ':
- $attname=0;
- break;
- case 'tagshortattchar':
- $attvalue[]=$char;
- break;
- case 'tagshortattcomma':
- $atts[$attname]=$attvalue;
- $attvalue=array();
- ++$attname;
- break;
- case 'tagshortattbegindyndata':
- $dyndataname='';
- break;
- case 'tagshortattdyndatachar':
- $dyndataname.=$char;
- break;
- case 'tagshortattdyndataend':
- $attvalue[]=new UbbDynData($dyndataname);
- break;
- case 'tagattspace':
- if(isset($attname) and $attname) {
- $atts[$attname]=$attvalue;
- }
- $attvalue=array();
- //nothing to do
- break;
- case 'tagshortattbeginquote':
- //already cleared the value before we got to this state
- break;
- case 'tagshortattquotechar':
- $attvalue[]=$char;
- break;
- case 'tagshortattendquote':
- $atts[$attname]=$attvalue;
- $attvalue=array();
- ++$attname;
- break;
- case 'tagshortattquotedyndatabeginescape':
- $dyndataname='';
- break;
- case 'tagshortattquotedyndatachar':
- $dyndataname.=$char;
- break;
- case 'tagshortattquotedyndataend':
- $attvalue[]=new UbbDynData($dyndataname);
- break;
- case 'tagshortattspace':
- //nothing to do?
- break;
- default:
- throw new Exception("Unhandled state: $state");
- break;
- }
- if(defined('UBBCODE_DEBUG') && ((UBBCODE_DEBUG&3)==3)) {//both flags are needed
- echo '<td><pre>';
- ob_start();
- var_dump($contents);
- $a=ob_get_contents();
- ob_end_clean();
- echo htmlspecialchars($a,ENT_QUOTES);
- echo '</pre></td><td><pre>';
- ob_start();
- var_dump($tagstack);
- $a=ob_get_contents();
- ob_end_clean();
- echo htmlspecialchars($a,ENT_QUOTES);
- echo '</pre></td><td><pre>';
- ob_start();
- var_dump($attvalue);
- $a=ob_get_contents();
- ob_end_clean();
- echo htmlspecialchars($a,ENT_QUOTES);
- echo '</pre></td><td><pre>';
- ob_start();
- var_dump($atts);
- $a=ob_get_contents();
- ob_end_clean();
- echo htmlspecialchars($a,ENT_QUOTES);
- echo "</pre></td><td>$i</td></tr>";
- }
- }# end of for i<strlen(input)
- if(defined('UBBCODE_DEBUG') && UBBCODE_DEBUG==true) {
- echo '</table>';
- }
- switch($state) {
- case 'tagend':
- case 'text':
- case 'smileyend':
- case 'dyndataend':
- $contents=array_pop($contents);
- break;
- default:
- throw new BadUbbCodeException("Parser ended in invalid state, $state",$i);
- break;
- }
- if($deep>0) {
- throw new BadUbbCodeException("There is $deep unclosed tag".($deep!=1?'s':'')." still open!",$i);
- }
- $tag=new self('_root',$contents,array());
- $tag->mergestrings();
- $tag->convertsmilies();
- $tag->converturls();
- return $tag;
- }# end of static function parse
- public function hasContents() {
- return isset($this->contents) and count($this->contents)>0;
- }
- static protected function getNewTagID() {
- //used for spoiler tags and other tags that need a known unique id
- //Nah, they don't need an unique id, they can use relative paths
- user_error('Depraciated method, "getNewTagID", called',E_USER_WARNING);
- return self::$tagID++;
- }
- public function getHtml($issig=false) {
- if(isset(self::$customtagCallback[$this->type])) {
- return call_user_func(self::$customtagCallback[$this->type],$this);
- }
- switch($this->type) {
- case 'b':
- return sprintf(
- '<strong>%s</strong>',
- $this->getHtmlForContents($issig)
- );
- break;
- case 'i':
- return sprintf(
- '<em>%s</em>',
- $this->getHtmlForContents($issig)
- );
- break;
- case 's':
- return sprintf(
- '<span style="text-decoration: line-through">%s</span>',
- $this->getHtmlForContents($issig)
- );
- break;
- case 'url':
- $url=$this->getatteribute(self::$urltags['url']);
- if(!$url) {
- $url=self::handleDynData($this->contents);
- }
- if(!preg_match('#^[a-z]+:#i',$url)) {
- $url='http://'.$url;
- }
- return sprintf(
- '<a href="%s">%s</a>',
- $url,
- (
- $this->hasContents() ?
- $this->getHtmlForContents($issig) :
- htmlentities($url)
- )
- );
- break;
- case 'email':
- $email=$this->getatteribute(array('email','to','address',0));
- if(!$email) {
- $email=$this->getHtmlForContents($issig);
- } else {
- $email=htmlentities($email);
- }
- return sprintf(
- '<a href="mailto:%s">%s</a>',
- urlencode($email),
- (
- $this->hasContents() ?
- $this->getHtmlForContents($issig) :
- htmlentities($email)
- )
- );
- break;
- case 'google':
- $name=self::getatteributename(array('query',0));
- //var_dump($name);
- $query=($name!==false?$this->atteributes[$name]:$this->contents);
- //var_dump($query);
- $query=self::handleDynData($query);
- //var_dump($query);
- return sprintf(
- '<a href="http://www.google.com/search?q=%s" class="google">%s</a>',
- htmlentities(
- urlencode(
- $query
- )
- ),
- (
- $this->hasContents() ?
- $this->getHtmlForContents($issig) :
- 'Google: '.htmlentities($query)
- )
- );
- break;
- case 'img':
- if($this->hasatteribute(self::$urltags['img'])) {
- return sprintf(
- '<img src="%s" alt="%s" />',
- htmlentities(
- $this->getatteribute(self::$urltags['img'])
- ),
- (
- $this->hasContents() ?
- self::handleDynData($this->contents) :
- 'User posted image'
- )
- );
- } else {
- return sprintf(
- '<img src="%s" alt="%s" />',
- self::handleDynData($this->contents),
- 'User posted image'
- );
- }
- break;
- case 'flash':
- //TODO: create non crappy html
- return sprintf(
- (
- '<embed allowscripting="never" src="%s" heigth="%u" width="%u" params="%s"'.
- ' loop="true" play="true" quality="%s" />'
- ),
- htmlentities(
- $this->hasatteribute(self::$urltags['flash']) ?
- $this->getatteribute(self::$urltags['flash']) :
- self::handleDynData($this->contents)
- ),
- $this->getatteribute('heigth'),//safe since %u is used
- $this->getatteribute('width'),
- htmlentities(
- $this->getatteribute('params')
- ),
- (
- $issig ?//yes, flash can change it at runtime, but it is the thought that counts
- 'low':
- 'high'
- )
- );
- break;
- case 'list':
- return sprintf(
- '<%s>%s</%1$s>',
- $this->hasatteribute(array('ordered',0)) ? 'ol' : 'ul',
- $this->getHtmlForContents($issig)
- );
- break;
- case '*':
- return sprintf(
- '<li>%s</li>',
- $this->getHtmlForContents($issig)
- );
- break;
- case 'code':
- $language=$this->getatteribute(array('lang',0));
- $gothighlighter=$language and
- isset(self::$highlightengines[$language]);
- if($gothighlighter) {
- $highlighter=self::$highlightengines[$language];
- }
- //TODO: write better html for the head
- return sprintf(
- '<div class="code"><div class="codetop">%s</div><div class="codemain">%s</div></div>',
- $language ? htmlentities("$language code") : 'code',
- (
- $gothighlighter ?
- call_user_func($highlighter,self::handleDynData($this->contents),$language) :
- htmlentities(self::handleDynData($this->contents))
- )
- );
- break;
- case 'table':
- return sprintf(
- '<table>%s</table>',
- $this->getHtmlForContents($issig)
- );
- break;
- case 'row':
- return sprintf(
- '<tr>%s</tr>',
- $this->getHtmlForContents($issig)
- );
- break;
- case 'cell':
- return sprintf(
- '<td>%s</td>',
- $this->getHtmlForContents($issig)
- );
- break;
- case 'noubb':
- case '_root':
- return $this->getHtmlForContents($issig);
- break;
- case 'spoiler':
- return 'Do not read this: '.$this->getHtmlForContents($issig);
- break;
- case 'comment':
- return '';
- break;
- case 'left':
- case 'center':
- case 'right':
- return sprintf(
- '<div style="text-align: %s">%s</div>',
- $this->type,
- $this->getHtmlForContents($issig)
- );
- break;
- case 'sub':
- case 'sup':
- return sprintf(
- '<%s>%s</%1$s>',
- $this->type,
- $this->getHtmlForContents($issig)
- );
- break;
- case 'background':
- $color=$this->getatteribute(array('color',0));
- if(preg_match('/^([a-z]+|#[0-f]{6})$/i', $color)) {
- return sprintf(
- '<span style="background-color: %s">%s</span>',
- $color,
- $this->getHtmlForContents($issig)
- );
- } else {
- return $this->getHtmlForContents($issig);
- }
- break;
- case 'color':
- $color=$this->getatteribute(array('color',0));
- if(preg_match('/^([a-z]+|#[0-f]{6})$/i', $color)) {
- return sprintf(
- '<span style="color: %s">%s</span>',
- $color,
- $this->getHtmlForContents($issig)
- );
- } else {
- return $this->getHtmlForContents($issig);
- }
- break;
- case 'size':
- $size=$this->getatteribute(array('size',0));
- return sprintf(
- '<span style="font-size: %u;">%s</span>',
- $size,
- $this->getHtmlForContents($issig)
- );
- break;
- case 'font':
- $font=$this->getatteribute(array('font',0));
- if(preg_match('#^[a-z-]+$#i',$font)) {
- return sprintf(
- '<span style="font-family %s;">%s</span>',
- $font,
- $this->getHtmlForContents($issig)
- );
- } else {
- return $this->getHtmlForContents($issig);
- }
- break;
- case 'indent':
- return sprintf(
- '<div style="margin-left: 2em;">%s</div>',
- $this->getHtmlForContents($issig)
- );
- break;
- default:
- throw new Exception("GetHtml somehow do not know the tag type {$this->type}");
- break;
- }
- }# end of function getHtml
- public function getHtmlForContents($isSig=false) {
- $o='';
- foreach($this->contents as $content) {
- if($content instanceof self) {
- $o.=$content->getHtml($isSig);
- } elseif($content instanceof UbbDynData) {
- $o.=htmlentities($content->getDynData());
- } elseif($content instanceof UbbSmiley) {
- $o.=$content->getHtml();
- } else {//must be a plain old string
- $o.=htmlentities($content);
- }
- }
- return $o;
- }# end of function getHtmlForContents
- protected function handleDynData($data) {
- //when smileys and tags wont be in it
- //var_dump($data);
- if(!is_array($data)) {
- throw new Exception('Handledyndata requires an array');
- }
- $o='';
- foreach($data as $chunk) {
- if($chunk instanceof UbbDynData) {
- $o.=$chunk->getDynData();
- } else {
- $o.=$chunk;
- }
- }
- return $o;
- }# end of function handleDynData
- public function getatteributename($names) {
- foreach($names as $candidate) {
- if(isset($this->atteributes[$candidate])) {
- return $candidate;
- }
- }
- return false;
- }
- public function getatteribute($name) {
- if(is_array($name)) {
- $name=self::getatteributename($name);
- if($name===false) {
- return $name;
- }
- } else {
- if(!isset($this->atteributes[$name])) {
- return false;
- }
- }
- return self::handleDynData($this->atteributes[$name]);
- }
- public function hasatteribute($name) {
- if(is_array($name)) {
- return self::getatteributename($name)!==false;
- } else {
- return isset($this->atteributes[$name]);
- }
- }
- protected function usesshortstyleattirbutes() {
- return isset($this->atteributes[0]);
- }
- public function getUbb() {
- $o='';
- if($this->type!='_root') {
- $o='['.$this->usedname;
- if(isset($this->atteributes[0])) {
- $o.='='.self::getUBBforatts($this->atteributes[0]);
- for($i=1;isset($this->atteributes[$i]);++$i) {
- $o.=','.self::getUBBforatts($this->atteributes[$i]);
- }
- }
- foreach($this->atteributes as $atteribute=>$value) {
- if(!is_numeric($atteribute)) {
- $o.=' '.$atteribute.'="'.self::getUBBforatts($value).'"';
- }
- }
- $o.=']';
- }
- foreach($this->contents as $content) {
- if($content instanceof self) {
- $o.=$content->getUbb();
- } elseif($content instanceof UbbDyndata) {
- $o.=$content->getUbb();
- } elseif($content instanceof UbbSmiley) {
- $o.=$content->getUbb();
- } elseif(isset(self::$parserbypasstags[$this->type]) and self::$parserbypasstags[$this->type]==true) {
- $o.=$content;
- } else {
- $o.=self::escapetext($content);
- }
- }#end of foreach
- if(!(isset(self::$noendtagtags[$this->type]) and self::$noendtagtags[$this->type])) {
- $o.='[/'.$this->usedname.']';
- }
- return $o;
- }#end of getUbb
- protected function getUBBforatts($atts) {
- if(!is_array($atts)) {
- throw new Exception('getUBBforatts requires an array');
- }
- $o='';
- foreach($atts as $att) {
- if($att instanceof UbbDynData) {
- $o.=$att->getUbb();
- } else {
- $o.=self::escapeattvalue($att);
- }
- }
- return $o;
- }
- public static function escapeattvalue($in) {
- return str_replace(array('\\','"',UBBCODE_DYNDATACHAR),array('\\\\','\\"',UBBCODE_DYNDATACHAR.UBBCODE_DYNDATACHAR),$in);
- }
- public static function escapetext($in) {
- $in=str_replace(UBBCODE_DYNDATACHAR,UBBCODE_DYNDATACHAR.UBBCODE_DYNDATACHAR,$in);
- if(defined('UBBCODE_COMPLEX_SMILIES')) {
- return str_replace(UBBCODE_SMILEYCHAR,UBBCODE_SMILEYCHAR.UBBCODE_SMILEYCHAR,$in);
- } else {
- return $in;
- }
- }
- protected function mergestrings() {
- $strbuffer='';
- $o=array();
- foreach($this->contents as $element) {
- if(is_string($element)) {
- $strbuffer.=$element;
- } else {
- if(strlen($strbuffer)>0) {
- $o[]=$strbuffer;
- $strbuffer='';
- }
- if($element instanceof self) {
- $element->mergestrings();
- }
- $o[]=$element;
- }
- }
- if(strlen($strbuffer)>0) {
- $o[]=$strbuffer;
- }
- $this->contents=$o;
- foreach($this->atteributes as $key=>$atteribute) {
- $o=array();
- $strbuffer='';
- foreach($atteribute as $element) {
- if(is_string($element)) {
- $strbuffer.=$element;
- } else {
- if(strlen($strbuffer)>0) {
- $o[]=$strbuffer;
- $strbuffer='';
- }
- $o[]=$element;
- }
- }
- if(strlen($strbuffer)>0) {
- $o[]=$strbuffer;
- }
- $this->atteributes[$key]=$o;
- }
- }
- protected function convertsmilies() {
- if(isset(self::$parserbypasstags[$this->type]) and self::$parserbypasstags[$this->type]) {
- return;
- }
- $substitutionneedles=array_keys(UbbSmiley::$substitutionmap);
- $o=array();
- foreach($this->contents as $content) {
- if(!is_string($content)) {
- if($content instanceof self) {
- $content->convertsmilies();
- }
- $o[]=$content;
- continue;
- }
- $pos=0;
- while($find=nextchar($content,$substitutionneedles,$pos)) {
- if($find['offset']>0) {
- $pre=substr($content,$pos,$find['offset']-$pos);
- $o[]=$pre;
- }
- $o[]=new UbbSmiley($find['needle'],true);
- $pos=$find['offset']+strlen($find['needle']);
- }
- if($pos!=strlen($content)) {
- $post=substr($content,$pos);
- $o[]=$post;
- }
- }
- $this->contents=$o;
- }
- protected function converturls() {
- //print_r(self::$urltags[$this->type]);
- if(isset(self::$parserbypasstags[$this->type]) and self::$parserbypasstags[$this->type]) {
- return;
- }
- //don't convert urls that are already going to be interpreted as urls
- if(isset(self::$urltags[$this->type])) {
- if(!$this->hasatteribute(self::$urltags[$this->type])) {
- return;
- }
- }
- $o=array();
- foreach($this->contents as $content) {
- if(!is_string($content)) {
- if($content instanceof self) {
- $content->converturls();
- }
- $o[]=$content;
- continue;
- }
- preg_match_all(
- '#'.
- '(www\\.|(ht|f)tps?://)[a-z0-9-]+(\\.[a-z0-9-]+)*(:[0-9]{1,6})?(/[a-z0-9\\.~/&_\\?%=!-]*(\\#[a-z0-9-]*)?)?'
- .'#iSXD',
- $content,
- $matches,
- PREG_PATTERN_ORDER
- );
- $substitutionneedles=$matches[0];
- unset($matches);
- //print_r($substitutionneedles);
- $pos=0;//TODO: move from the string substitution into a more exact position based solution.
- while($find=nextchar($content,$substitutionneedles,$pos)) {
- if($find['offset']>0) {
- $pre=substr($content,$pos,$find['offset']-$pos);
- $o[]=$pre;
- }
- $o[]=new self('url',array($find['needle']),array());
- $pos=$find['offset']+strlen($find['needle']);
- }
- if($pos!=strlen($content)) {
- $post=substr($content,$pos);
- $o[]=$post;
- }
- }
- $this->contents=$o;
- }
- } #end of UbbTag
- UbbTag::initdata(array(),array(),array(),array());//Load the data
- #end of ubb.php
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement