PageRenderTime 62ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/quicky/classes/Quicky_bbcode.class.php

https://bitbucket.org/seyar/startech.local
PHP | 413 lines | 400 code | 0 blank | 13 comment | 133 complexity | 0d6041a6dc6b967ef413c643fd6f1e42 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**************************************************************************/
  3. /* Quicky: smart and fast templates
  4. /* ver. 0.5.0.0
  5. /* ===========================
  6. /*
  7. /* Copyright (c)oded 2007-2008 by WP
  8. /* http://quicky-tpl.net
  9. /*
  10. /* Quicky_BBCode.class.php: BB-code class
  11. /**************************************************************************/
  12. class Quicky_BBcode
  13. {
  14. public $smiles = array(
  15. 'acute',
  16. 'angry',
  17. 'big_boss',
  18. 'glare',
  19. 'good',
  20. 'happy',
  21. 'hi',
  22. 'huh',
  23. 'lol',
  24. 'not_i',
  25. 'ohmy',
  26. 'read',
  27. 'smile',
  28. 'smile3',
  29. 'taunt',
  30. 'to_keep_order',
  31. 'wink',
  32. 'yahoo',
  33. 'yes',
  34. 'rose'/*,
  35. 'banned',
  36. 'tema',
  37. 'offtopic',
  38. 'funny_post'*/,
  39. );
  40. public $source;
  41. public $blocks = array();
  42. public $tags = array();
  43. public $left_delimiter = '[';
  44. public $right_delimiter = ']';
  45. public $errors = array();
  46. public $allow_html_tags = FALSE;
  47. public $smiles_dir;
  48. public $smiles_url;
  49. private $_builtin_blocks = '[gm]|email|link|url|code|php|list|plain|literal';
  50. public $cast_unrecognized_tags = FALSE;
  51. public $stat = array();
  52. public $use_stat = TRUE;
  53. public $block_stacks = array();
  54. public $block_stack_n = 0;
  55. public $autourl = TRUE;
  56. public $allow_smiles = TRUE;
  57. public function __construct() {}
  58. public function load($string)
  59. {
  60. $this->source = $string;
  61. }
  62. public function safe_uri($uri)
  63. {
  64. $uri = trim($uri);
  65. if (preg_match('~^(?:java|vb)script:~i',preg_replace('~\s+~','',$uri))) {return FALSE;}
  66. return TRUE;
  67. }
  68. private function _error($msg)
  69. {
  70. $this->errors[] = $msg;
  71. return FALSE;
  72. }
  73. public function register_block($name,$callback) {$this->blocks[strtolower($name)] = $callback;}
  74. public function register_tag($name,$callback) {$this->tags[strtolower($name)] = $callback;}
  75. private function _parse_params($p)
  76. {
  77. $params = array();
  78. preg_match_all('~\w+\s*=|(([\'"]).*?(?<!\\\\)\2|\S+)~s',$p,$m,PREG_SET_ORDER);
  79. $lastkey = '';
  80. foreach ($m as $v)
  81. {
  82. if (trim($v[0]) === '') {continue;}
  83. if (sizeof($v) == 1) {$lastkey = ltrim(rtrim($v[0]," =\t")); continue;}
  84. if ($lastkey === '') {$params[] = $v[0];}
  85. else {$params[$lastkey] = $v[0];}
  86. $lastkey = '';
  87. }
  88. if ($this->use_stat) {$this->stat['numparams'] += sizeof($params);}
  89. return $params;
  90. }
  91. private function _dequote($string)
  92. {
  93. if ((substr($string,0,1) == '"' and substr($string,-1) == '"')
  94. or (substr($string,0,1) == '\'' and substr($string,-1) == '\''))
  95. {return substr($string,1,-1);}
  96. return $string;
  97. }
  98. private function _tag_token($mixed)
  99. {
  100. if (is_array($mixed))
  101. {
  102. if (sizeof($mixed) == 1)
  103. {
  104. if ($mixed[0] == "\n") {return '<br />'."\n";}
  105. elseif ($mixed[0] == "\r") {return '';}
  106. return $this->allow_html_tags?$mixed[0]:htmlspecialchars($mixed[0]);
  107. }
  108. if (isset($mixed[7]) and $mixed[7] !== '')
  109. {
  110. if (!$this->allow_smiles) {return $mixed[0];}
  111. $smile = substr($mixed[7],1,-1);
  112. if (file_exists($this->smiles_dir.$smile.'.gif')) {return '<img src="'.htmlspecialchars($this->smiles_url.$smile).'.gif">';}
  113. else {return $mixed[0];}
  114. }
  115. if ($mixed[1] !== '')
  116. {
  117. if ($this->use_stat)
  118. {
  119. ++$this->stat['numblocks'];
  120. if (substr($mixed[2],0,1) == '=') {++$this->stat['numparams'];}
  121. }
  122. $block_type = strtolower($mixed[1]);
  123. $block_content = $mixed[4];
  124. if ($block_type == 'img')
  125. {
  126. if (!$this->safe_uri($block_content)) {return $this->_error('Unsafe uri "'.$block_content.'" in tag '.$block_type);}
  127. return '<img src="'.htmlspecialchars($block_content,ENT_QUOTES).'" />';
  128. }
  129. elseif ($block_type == 'link' or $block_type == 'url')
  130. {
  131. if (substr($mixed[2],0,1) == '=') {$url = substr($mixed[2],1);}
  132. else
  133. {
  134. $block_params = $this->_parse_params($mixed[2]);
  135. $url = isset($block_params['src'])?$block_params['src']:'';
  136. }
  137. $url = $this->_dequote($url);
  138. if ($url === '') {$url = $block_content;}
  139. if (!$this->safe_uri($url)) {return $this->_error('Unsafe uri "'.$url.'" in tag '.$block_type);}
  140. return '<a href="'.htmlspecialchars($url).'">'.$this->_tag_token($block_content).'</a>';
  141. }
  142. elseif ($block_type == 'php')
  143. {
  144. $s = trim($block_content,"\r\n");
  145. if (!preg_match('~<\?php~i',$s)) {$s = str_replace("\r",'','<?php'."\n".$s.' ?>');}
  146. else {$s = str_replace("\r",'','<?php'."\n".$s.' ?>');}
  147. $s = @highlight_string($s,TRUE);
  148. $s = substr_replace($s,'',strpos($s,'&lt;?php'),8);
  149. $s = substr_replace($s,'',strrpos($s,'?&gt;'),5);
  150. $from = 0;
  151. $x = 0;
  152. while ($i = strpos($s,'<br />',$from))
  153. {
  154. $s = substr($s,0,$x == 0?$i:$i+6)."\n".
  155. '<font style="color:#000000;background-color:#eeeeee;">&nbsp;'.sprintf('%03d',$x+1).
  156. '&nbsp;</font>&nbsp;'.substr($s,$i+6);
  157. $from = $i+5;
  158. $x++;
  159. }
  160. return '<div style="background-color:#cccccc">'.$s.'</div>';
  161. }
  162. elseif ($block_type == 'code')
  163. {
  164. $s = trim($block_content);
  165. $r = '';
  166. $x = 0;
  167. $e = explode("\n",$s);
  168. for ($i = 0, $s = sizeof($e); $i < $s; ++$i)
  169. {
  170. $line = $e[$i];
  171. if ($x != 0 or strlen(trim(str_replace('<br />','',$line))) > 0)
  172. {
  173. $r .= '<font style="color:#000000;background-color:#eeeeee;">&nbsp;'.sprintf('%03d',$x+1).
  174. '&nbsp;</font>&nbsp;'.$line."\n";
  175. $x++;
  176. }
  177. }
  178. return '<div style="background-color:#cccccc">'.$r.'</div>';
  179. }
  180. elseif (in_array($block_type,array('b','i','u','s','p'))) {return '<'.$block_type.'>'.$this->_tag_token($block_content).'</'.$block_type.'>';}
  181. elseif ($block_type == 'email')
  182. {
  183. if (substr($mixed[2],0,1) == '=') {$email = substr($mixed[2],1);}
  184. else
  185. {
  186. $block_params = $this->_parse_params($mixed[2]);
  187. $email = isset($block_params['address'])?$this->_dequote($block_params['address']):'';
  188. }
  189. if ($email === '') {$email = $block_content;}
  190. return '<a href="mailto:'.htmlspecialchars($email).'" />'.$this->_tag_token($block_content).'</a>';
  191. }
  192. elseif ($block_type == 'm') {return '<a href="http://php.net/'.urlencode($block_content).'">'.htmlspecialchars($block_content).'</a>';}
  193. elseif ($block_type == 'g') {return '<a href="http://www.google.com/search?q='.urlencode($block_content).'">'.htmlspecialchars($block_content).'</a>';}
  194. elseif ($block_type == 'list')
  195. {
  196. if (substr($mixed[2],0,1) == '=') {$flag = substr($mixed[2],1);}
  197. else {$flag = '0';}
  198. return '<table border=0 width=100%><tr><td width=50></td><td width="95%">'
  199. .($flag == '0'?'<ol>':'<ul>')
  200. .$this->_tag_token($block_content)
  201. .($flag == '0'?'</ol>':'</ul>').'</td></tr></table>';
  202. }
  203. elseif ($block_type == 'plain' or $block_type == 'literal') {return htmlspecialchars($block_content);}
  204. elseif (isset($this->blocks[$block_type])) {return call_user_func($this->blocks[$block_type],$mixed[2],$block_content,$this);}
  205. else {return $this->cast_unrecognized_tags?$mixed[0]:$this->_error('Unrecognized block-type: \''.$block_type.'\'');}
  206. }
  207. elseif (isset($mixed[5]) && $mixed[5] !== '')
  208. {
  209. static $c_offsets = array();
  210. if (!isset($c_offsets[$this->block_stack_n])) {$c_offsets[$this->block_stack_n] = 0;}
  211. preg_match('~^\s*(/?)([^\s=]*)(.*?)$~s',$mixed[5],$m);
  212. if (!isset($m[0])) {$m[0] = '';}
  213. if (!isset($m[1])) {$m[1] = '';}
  214. if (!isset($m[2])) {$m[2] = '';}
  215. if (!isset($m[3])) {$m[3] = '';}
  216. $close = $m[1];
  217. $tag = strtolower($m[2]);
  218. $param = $m[3];
  219. if ($this->use_stat) {++$this->stat['numtags'];}
  220. $bs = &$this->block_stacks[$this->block_stack_n];
  221. if ($close and ($tag == ''))
  222. {
  223. $el = array_slice($bs,-$c_offsets[$this->block_stack_n],1);
  224. $tag = current($el);
  225. ++$c_offsets[$this->block_stack_n];
  226. if ($tag === FALSE or $tag[1]) {return '';}
  227. $tag = $tag[0];
  228. $bs[key($el)][1] = TRUE;
  229. }
  230. elseif ($close)
  231. {
  232. $found = FALSE;
  233. for ($i = sizeof($bs)-1; $i >= 0; --$i)
  234. {
  235. if ((!$bs[$i][1]) and ($bs[$i][0] == $tag))
  236. {
  237. $bs[$i][1] = TRUE;
  238. $found = TRUE; break;
  239. }
  240. }
  241. if (!$found) {return '';}
  242. }
  243. $return = $this->_exec_tag($close,$tag,$param);
  244. if (!$close and !in_array($tag,array('hr')) and ($return !== FALSE)) {$bs[] = array($tag,FALSE); $c_offsets[$this->block_stack_n] = 0;}
  245. return $return;
  246. }
  247. return;
  248. }
  249. ++$this->block_stack_n;
  250. if (!isset($this->block_stacks[$this->block_stack_n])) {$this->block_stacks[$this->block_stack_n] = array();}
  251. $bs = &$this->block_stacks[$this->block_stack_n];
  252. static $regexp;
  253. if ($regexp === NULL)
  254. {
  255. $ldelim = preg_quote($this->left_delimiter,'~');
  256. $rdelim = preg_quote($this->right_delimiter,'~');
  257. $blocks = array($this->_builtin_blocks);
  258. for ($i = 0,$s = sizeof($this->blocks),$v = array_keys($this->blocks); $i < $s; ++$i) {$blocks[] = preg_quote($v[$i],'~');}
  259. $regexp = '~'
  260. .$ldelim.'\s*('.implode('|',$blocks).')([\s=](?:[^'.$rdelim.'\'"]*([\'"]).*?(?<!\\\\)\3)*.*?)?'.$rdelim.'((?:(?R)|.)*?)'.$ldelim.'/\s*\1?\s*'.$rdelim
  261. .'|'.$ldelim.'(\\??(?:[^'.$rdelim.'\'"]*([\'"]).*?(?<!\\\\)\5)*.*?)'.$rdelim
  262. .'|(:\w+:)|[<>&\n\'"]'
  263. .'~si';
  264. }
  265. if ((strpos($mixed,$this->left_delimiter) !== FALSE) or (strpos($mixed,':') !== FALSE))
  266. {
  267. $return = preg_replace_callback($regexp,array($this,'_tag_token'),$mixed);
  268. for ($i = sizeof($bs)-1; $i >= 0; --$i) {if (!$bs[$i][1]) {$return .= $this->_exec_tag('/',$bs[$i][0]);}}
  269. return $return;
  270. }
  271. --$this->block_stack_n;
  272. return $mixed;
  273. }
  274. public function _exec_tag($close,$tag,$param = '')
  275. {
  276. $bs = &$this->block_stacks[$this->block_stack_n];
  277. if ($tag == 'li' or $tag == '*') {$return = $close?'':'<li>';}
  278. elseif (in_array($tag,array('b','i','u','s','p'))) {$return = '<'.$close.$tag.'>';}
  279. elseif ($tag == 'quote')
  280. {
  281. if ($close) {return '</td></tr><tr><td><hr size=1 nowhade></td></tr></table>';}
  282. if (substr($param,0,1) == '=') {$author = substr($param,1);}
  283. else {$author = '';}
  284. return '<table border=0 width=100%><tr><td width="10" rowspan=3>&nbsp;</td><td width=90%><hr size=1 noshade></td></tr><tr><td>'
  285. .($author != ''?'<i>Original by: '.htmlspecialchars($author).'</i><br />':'');
  286. }
  287. elseif ($tag == 'size')
  288. {
  289. if ($close) {return '</font>';}
  290. if (substr($param,0,1) == '=') {$size = substr($param,1);}
  291. else {$size = '10';}
  292. return '<font size="'.htmlspecialchars($this->_dequote($size)).'">';
  293. }
  294. elseif ($tag == 'color')
  295. {
  296. if (substr($param,0,1) == '=') {$color = substr($param,1);}
  297. else {$color = 'black';}
  298. if ($close) {return '</font>';}
  299. return '<font color="'.htmlspecialchars($this->_dequote($color)).'">';
  300. }
  301. elseif ($tag == 'sub')
  302. {
  303. if ($close) {return '</'.$tag.'>';}
  304. return '<'.$tag.'>';
  305. }
  306. elseif ($tag == 'hr')
  307. {
  308. return '<'.$tag.' />';
  309. }
  310. elseif ($tag == 'font')
  311. {
  312. if (substr($param,0,1) == '=') {$face = substr($param,1);}
  313. else {$face = 'Verdana';}
  314. if ($close) {return '</font>';}
  315. return '<font face="'.htmlspecialchars($this->_dequote($face)).'">';
  316. }
  317. elseif ($tag == 'table')
  318. {
  319. if ($close) {return '</table>';}
  320. return '<table border="1">';
  321. }
  322. elseif ($tag == 'row')
  323. {
  324. $found = FALSE;
  325. for ($i = sizeof($bs)-1; $i >= 0; --$i)
  326. {
  327. if ((!$bs[$i][1]) and ($bs[$i][0] == 'table'))
  328. {
  329. $found = TRUE; break;
  330. }
  331. }
  332. if (!$found) {$this->_error('Unexpected tag-type: \''.$tag.'\''); return FALSE;}
  333. if ($close) {return '</tr>';}
  334. return '<tr>';
  335. }
  336. elseif ($tag == 'col')
  337. {
  338. $found = FALSE;
  339. for ($i = sizeof($bs)-1; $i >= 0; --$i)
  340. {
  341. if ((!$bs[$i][1]) and ($bs[$i][0] == 'table'))
  342. {
  343. $found = TRUE; break;
  344. }
  345. }
  346. if (!$found) {return '';}
  347. if ($close) {return '</td>';}
  348. $p = $this->_parse_params($param);
  349. $s = '';
  350. static $col_params = array('width','height');
  351. foreach ($p as $k => $v)
  352. {
  353. if (in_array(strtolower($k),$col_params)) {$s .= ' '.$k.'="'.htmlspecialchars($this->_dequote($v)).'"';}
  354. }
  355. return '<td'.$s.'>';
  356. }
  357. elseif (isset($this->tags[$tag])) {$return = call_user_func($this->tags[$tag],$param,$close);}
  358. else {return $this->cast_unrecognized_tags?NULL:$this->_error('Unrecognized tag-type: \''.$tag.'\' ('.$close.')');}
  359. return $return;
  360. }
  361. public function build()
  362. {
  363. $this->source = &$this->text;
  364. return $this->result = $this->getHTML();
  365. }
  366. public function prepareblock_callback($m)
  367. {
  368. if (empty($m[1])) {return $m[0];}
  369. if (isset($m[7])) {return '[URL="'.$m[7].'"]'.$m[7].'[/URL]';}
  370. $blockname = $m[1];
  371. if ($blockname == 'url' or $blockname == 'php' or $blockname == 'code') {return $m[0];}
  372. else {return '['.$m[1].$m[2].']'.$this->prepareblock($m[4]).$m[5];}
  373. }
  374. public function prepareblock($source)
  375. {
  376. static $regexp = NULL;
  377. if ($regexp === NULL)
  378. {
  379. $ldelim = preg_quote($this->left_delimiter,'~');
  380. $rdelim = preg_quote($this->right_delimiter,'~');
  381. $blocks = array($this->_builtin_blocks);
  382. for ($i = 0,$s = sizeof($this->blocks),$v = array_keys($this->blocks); $i < $s; ++$i) {$blocks[] = preg_quote($v[$i],'~');}
  383. $regexp = '~'
  384. .$ldelim.'\s*('.implode('|',$blocks).')([\s=](?:[^'.$rdelim.'\'"]*([\'"]).*?(?<!\\\\)\3)*.*?)?'.$rdelim.'((?:(?R)|.)*?)('.$ldelim.'/\s*\1?\s*'.$rdelim.')'
  385. .'|'.$ldelim.'(\\??(?:[^'.$rdelim.'\'"]*([\'"]).*?(?<!\\\\)\6)*.*?)'.$rdelim.'\r?\n?'
  386. .'|([a-z\d]+://[^\s\]]+)'
  387. .'~si';
  388. }
  389. return preg_replace_callback($regexp,array($this,'prepareblock_callback'),$source);
  390. }
  391. public function getHTML()
  392. {
  393. $this->block_stacks = array();
  394. $this->errors = array();
  395. $this->block_stack_n = 0;
  396. $source = $this->source;
  397. $this->stat = array();
  398. if ($this->use_stat)
  399. {
  400. $this->stat = array(
  401. 'numblocks' => 0,
  402. 'numtags' => 0,
  403. 'numparams' => 0
  404. );
  405. }
  406. if ($this->autourl)
  407. {
  408. $source = $this->source = $this->prepareblock($source);
  409. }
  410. $source = $this->_tag_token($source);
  411. return $source;
  412. }
  413. }