PageRenderTime 48ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/script/lib/Text/Highlighter.php

https://bitbucket.org/chamilo/chamilo-dev/
PHP | 447 lines | 221 code | 50 blank | 176 comment | 35 complexity | cf88dddc6c2a829235a34e6f57068602 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Highlighter base class
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * LICENSE: This source file is subject to version 3.0 of the PHP license
  9. * that is available through the world-wide-web at the following URI:
  10. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  11. * the PHP License and are unable to obtain it through the web, please
  12. * send a note to license@php.net so we can mail you a copy immediately.
  13. *
  14. * @category Text
  15. * @package Text_Highlighter
  16. * @author Andrey Demenev <demenev@gmail.com>
  17. * @copyright 2004-2006 Andrey Demenev
  18. * @license http://www.php.net/license/3_0.txt PHP License
  19. * @version CVS: $Id: Highlighter.php,v 1.1 2007/06/03 02:35:28 ssttoo Exp $
  20. * @link http://pear.php.net/package/Text_Highlighter
  21. */
  22. require_once 'PEAR.php';
  23. // {{{ BC constants
  24. // BC trick : define constants related to default
  25. // renderer if needed
  26. if (! defined('HL_NUMBERS_LI'))
  27. {
  28. /**#@+
  29. * Constant for use with $options['numbers']
  30. * @see Text_Highlighter_Renderer_Html::_init()
  31. */
  32. /**
  33. * use numbered list
  34. */
  35. define('HL_NUMBERS_LI', 1);
  36. /**
  37. * Use 2-column table with line numbers in left column and code in right column.
  38. * Forces $options['tag'] = HL_TAG_PRE
  39. */
  40. define('HL_NUMBERS_TABLE', 2);
  41. /**#@-*/
  42. }
  43. // }}}
  44. // {{{ constants
  45. /**
  46. * for our purpose, it is infinity
  47. */
  48. define('HL_INFINITY', 1000000000);
  49. // }}}
  50. /**
  51. * Text highlighter base class
  52. *
  53. * @author Andrey Demenev <demenev@gmail.com>
  54. * @copyright 2004-2006 Andrey Demenev
  55. * @license http://www.php.net/license/3_0.txt PHP License
  56. * @version Release: 0.7.1
  57. * @link http://pear.php.net/package/Text_Highlighter
  58. */
  59. // {{{ Text_Highlighter
  60. /**
  61. * Text highlighter base class
  62. *
  63. * This class implements all functions necessary for highlighting,
  64. * but it does not contain highlighting rules. Actual highlighting is
  65. * done using a descendent of this class.
  66. *
  67. * One is not supposed to manually create descendent classes.
  68. * Instead, describe highlighting rules in XML format and
  69. * use {@link Text_Highlighter_Generator} to create descendent class.
  70. * Alternatively, an instance of a descendent class can be created
  71. * directly.
  72. *
  73. * Use {@link Text_Highlighter::factory()} to create an
  74. * object for particular language highlighter
  75. *
  76. * Usage example
  77. * <code>
  78. *require_once 'Text/Highlighter.php';
  79. *$hlSQL =& Text_Highlighter::factory('SQL',array('numbers'=>true));
  80. *echo $hlSQL->highlight('SELECT * FROM table a WHERE id = 12');
  81. * </code>
  82. *
  83. * @author Andrey Demenev <demenev@gmail.com>
  84. * @package Text_Highlighter
  85. * @access public
  86. */
  87. class Text_Highlighter
  88. {
  89. // {{{ members
  90. /**
  91. * Syntax highlighting rules.
  92. * Auto-generated classes set this var
  93. *
  94. * @access protected
  95. * @see _init
  96. * @var array
  97. */
  98. var $_syntax;
  99. /**
  100. * Renderer object.
  101. *
  102. * @access private
  103. * @var array
  104. */
  105. var $_renderer;
  106. /**
  107. * Options. Keeped for BC
  108. *
  109. * @access protected
  110. * @var array
  111. */
  112. var $_options = array();
  113. /**
  114. * Conditionds
  115. *
  116. * @access protected
  117. * @var array
  118. */
  119. var $_conditions = array();
  120. /**
  121. * Disabled keywords
  122. *
  123. * @access protected
  124. * @var array
  125. */
  126. var $_disabled = array();
  127. /**
  128. * Language
  129. *
  130. * @access protected
  131. * @var string
  132. */
  133. var $_language = '';
  134. // }}}
  135. // {{{ _checkDefines
  136. /**
  137. * Called by subclssses' constructors to enable/disable
  138. * optional highlighter rules
  139. *
  140. * @param array $defines Conditional defines
  141. *
  142. * @access protected
  143. */
  144. function _checkDefines()
  145. {
  146. if (isset($this->_options['defines']))
  147. {
  148. $defines = $this->_options['defines'];
  149. }
  150. else
  151. {
  152. $defines = array();
  153. }
  154. foreach ($this->_conditions as $name => $actions)
  155. {
  156. foreach ($actions as $action)
  157. {
  158. $present = in_array($name, $defines);
  159. if (! $action[1])
  160. {
  161. $present = ! $present;
  162. }
  163. if ($present)
  164. {
  165. unset($this->_disabled[$action[0]]);
  166. }
  167. else
  168. {
  169. $this->_disabled[$action[0]] = true;
  170. }
  171. }
  172. }
  173. }
  174. // }}}
  175. // {{{ factory
  176. /**
  177. * Create a new Highlighter object for specified language
  178. *
  179. * @param string $lang language, for example "SQL"
  180. * @param array $options Rendering options. This
  181. * parameter is only keeped for BC reasons, use
  182. * {@link Text_Highlighter::setRenderer()} instead
  183. *
  184. * @return mixed a newly created Highlighter object, or
  185. * a PEAR error object on error
  186. *
  187. * @static
  188. * @access public
  189. */
  190. function &factory($lang, $options = array())
  191. {
  192. $lang = strtoupper($lang);
  193. @include_once 'Text/Highlighter/' . $lang . '.php';
  194. $classname = 'Text_Highlighter_' . $lang;
  195. if (! class_exists($classname))
  196. {
  197. return PEAR :: raiseError('Highlighter for ' . $lang . ' not found');
  198. }
  199. $obj = & new $classname($options);
  200. return $obj;
  201. }
  202. // }}}
  203. // {{{ setRenderer
  204. /**
  205. * Set renderer object
  206. *
  207. * @param object $renderer Text_Highlighter_Renderer
  208. *
  209. * @access public
  210. */
  211. function setRenderer(&$renderer)
  212. {
  213. $this->_renderer = & $renderer;
  214. }
  215. // }}}
  216. /**
  217. * Helper function to find matching brackets
  218. *
  219. * @access private
  220. */
  221. function _matchingBrackets($str)
  222. {
  223. return strtr($str, '()<>[]{}', ')(><][}{');
  224. }
  225. function _getToken()
  226. {
  227. if (! empty($this->_tokenStack))
  228. {
  229. return array_pop($this->_tokenStack);
  230. }
  231. if ($this->_pos >= $this->_len)
  232. {
  233. return NULL;
  234. }
  235. if ($this->_state != - 1 && preg_match($this->_endpattern, $this->_str, $m, PREG_OFFSET_CAPTURE, $this->_pos))
  236. {
  237. $endpos = $m[0][1];
  238. $endmatch = $m[0][0];
  239. }
  240. else
  241. {
  242. $endpos = - 1;
  243. }
  244. preg_match($this->_regs[$this->_state], $this->_str, $m, PREG_OFFSET_CAPTURE, $this->_pos);
  245. $n = 1;
  246. foreach ($this->_counts[$this->_state] as $i => $count)
  247. {
  248. if (! isset($m[$n]))
  249. {
  250. break;
  251. }
  252. if ($m[$n][1] > - 1 && ($endpos == - 1 || $m[$n][1] < $endpos))
  253. {
  254. if ($this->_states[$this->_state][$i] != - 1)
  255. {
  256. $this->_tokenStack[] = array($this->_delim[$this->_state][$i], $m[$n][0]);
  257. }
  258. else
  259. {
  260. $inner = $this->_inner[$this->_state][$i];
  261. if (isset($this->_parts[$this->_state][$i]))
  262. {
  263. $parts = array();
  264. $partpos = $m[$n][1];
  265. for($j = 1; $j <= $count; $j ++)
  266. {
  267. if ($m[$j + $n][1] < 0)
  268. {
  269. continue;
  270. }
  271. if (isset($this->_parts[$this->_state][$i][$j]))
  272. {
  273. if ($m[$j + $n][1] > $partpos)
  274. {
  275. array_unshift($parts, array($inner,
  276. substr($this->_str, $partpos, $m[$j + $n][1] - $partpos)));
  277. }
  278. array_unshift($parts, array($this->_parts[$this->_state][$i][$j], $m[$j + $n][0]));
  279. }
  280. $partpos = $m[$j + $n][1] + strlen($m[$j + $n][0]);
  281. }
  282. if ($partpos < $m[$n][1] + strlen($m[$n][0]))
  283. {
  284. array_unshift($parts, array($inner,
  285. substr($this->_str, $partpos, $m[$n][1] - $partpos + strlen($m[$n][0]))));
  286. }
  287. $this->_tokenStack = array_merge($this->_tokenStack, $parts);
  288. }
  289. else
  290. {
  291. foreach ($this->_keywords[$this->_state][$i] as $g => $re)
  292. {
  293. if (isset($this->_disabled[$g]))
  294. {
  295. continue;
  296. }
  297. if (preg_match($re, $m[$n][0]))
  298. {
  299. $inner = $this->_kwmap[$g];
  300. break;
  301. }
  302. }
  303. $this->_tokenStack[] = array($inner, $m[$n][0]);
  304. }
  305. }
  306. if ($m[$n][1] > $this->_pos)
  307. {
  308. $this->_tokenStack[] = array($this->_lastinner,
  309. substr($this->_str, $this->_pos, $m[$n][1] - $this->_pos));
  310. }
  311. $this->_pos = $m[$n][1] + strlen($m[$n][0]);
  312. if ($this->_states[$this->_state][$i] != - 1)
  313. {
  314. $this->_stack[] = array($this->_state, $this->_lastdelim, $this->_lastinner, $this->_endpattern);
  315. $this->_lastinner = $this->_inner[$this->_state][$i];
  316. $this->_lastdelim = $this->_delim[$this->_state][$i];
  317. $l = $this->_state;
  318. $this->_state = $this->_states[$this->_state][$i];
  319. $this->_endpattern = $this->_end[$this->_state];
  320. if ($this->_subst[$l][$i])
  321. {
  322. for($k = 0; $k <= $this->_counts[$l][$i]; $k ++)
  323. {
  324. if (! isset($m[$i + $k]))
  325. {
  326. break;
  327. }
  328. $quoted = preg_quote($m[$n + $k][0], '/');
  329. $this->_endpattern = str_replace('%' . $k . '%', $quoted, $this->_endpattern);
  330. $this->_endpattern = str_replace('%b' . $k . '%', $this->_matchingBrackets($quoted), $this->_endpattern);
  331. }
  332. }
  333. }
  334. return array_pop($this->_tokenStack);
  335. }
  336. $n += $count + 1;
  337. }
  338. if ($endpos > - 1)
  339. {
  340. $this->_tokenStack[] = array($this->_lastdelim, $endmatch);
  341. if ($endpos > $this->_pos)
  342. {
  343. $this->_tokenStack[] = array($this->_lastinner, substr($this->_str, $this->_pos, $endpos - $this->_pos));
  344. }
  345. list($this->_state, $this->_lastdelim, $this->_lastinner, $this->_endpattern) = array_pop($this->_stack);
  346. $this->_pos = $endpos + strlen($endmatch);
  347. return array_pop($this->_tokenStack);
  348. }
  349. $p = $this->_pos;
  350. $this->_pos = HL_INFINITY;
  351. return array($this->_lastinner, substr($this->_str, $p));
  352. }
  353. // {{{ highlight
  354. /**
  355. * Highlights code
  356. *
  357. * @param string $str Code to highlight
  358. * @access public
  359. * @return string Highlighted text
  360. *
  361. */
  362. function highlight($str)
  363. {
  364. if (! ($this->_renderer))
  365. {
  366. include_once ('Text/Highlighter/Renderer/Html.php');
  367. $this->_renderer = & new Text_Highlighter_Renderer_Html($this->_options);
  368. }
  369. $this->_state = - 1;
  370. $this->_pos = 0;
  371. $this->_stack = array();
  372. $this->_tokenStack = array();
  373. $this->_lastinner = $this->_defClass;
  374. $this->_lastdelim = $this->_defClass;
  375. $this->_endpattern = '';
  376. $this->_renderer->reset();
  377. $this->_renderer->setCurrentLanguage($this->_language);
  378. $this->_str = $this->_renderer->preprocess($str);
  379. $this->_len = strlen($this->_str);
  380. while ($token = $this->_getToken())
  381. {
  382. $this->_renderer->acceptToken($token[0], $token[1]);
  383. }
  384. $this->_renderer->finalize();
  385. return $this->_renderer->getOutput();
  386. }
  387. // }}}
  388. }
  389. // }}}
  390. /*
  391. * Local variables:
  392. * tab-width: 4
  393. * c-basic-offset: 4
  394. * c-hanging-comment-ender-p: nil
  395. * End:
  396. */
  397. ?>