Wiki.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Paul M. Jones <pmjones@ciaweb.net> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Wiki.php,v 1.4 2004/01/31 15:51:54 pmjones Exp $
  20. require_once 'Text/Wiki/Rule.php';
  21. /**
  22. *
  23. * This is the "master" class for handling the management and convenience
  24. * functions to transform Wiki-formatted text.
  25. *
  26. * @author Paul M. Jones <pmjones@ciaweb.net>
  27. *
  28. * @version 0.8 alpha
  29. *
  30. */
  31. class Text_Wiki {
  32. /**
  33. *
  34. * The array of rules to apply to the source text, in order.
  35. *
  36. * This is an associative array where the key is the token-name
  37. * to be used for the rule, and the value is the path to the
  38. * rule class file.
  39. *
  40. * @access public
  41. *
  42. * @var array
  43. *
  44. */
  45. var $rules = array(
  46. // pre-filters
  47. 'prefilter' => 'Text/Wiki/Rule/prefilter.php',
  48. 'delimiter' => 'Text/Wiki/Rule/delimiter.php',
  49. // capturing block elements
  50. 'code' => 'Text/Wiki/Rule/code.php',
  51. 'phpcode' => 'Text/Wiki/Rule/phpcode.php',
  52. 'html' => 'Text/Wiki/Rule/html.php',
  53. // non-capturing block elements
  54. 'heading' => 'Text/Wiki/Rule/heading.php',
  55. 'horiz' => 'Text/Wiki/Rule/horiz.php',
  56. 'blockquote' => 'Text/Wiki/Rule/blockquote.php',
  57. 'deflist' => 'Text/Wiki/Rule/deflist.php',
  58. 'table' => 'Text/Wiki/Rule/table.php',
  59. 'list' => 'Text/Wiki/Rule/list.php',
  60. 'toc' => 'Text/Wiki/Rule/toc.php',
  61. // mark paragraph blocks
  62. 'paragraph' => 'Text/Wiki/Rule/paragraph.php',
  63. // inline elements
  64. 'raw' => 'Text/Wiki/Rule/raw.php',
  65. 'phplookup' => 'Text/Wiki/Rule/phplookup.php',
  66. 'url' => 'Text/Wiki/Rule/url.php',
  67. 'interwiki' => 'Text/Wiki/Rule/interwiki.php',
  68. 'freelink' => 'Text/Wiki/Rule/freelink.php',
  69. 'wikilink' => 'Text/Wiki/Rule/wikilink.php',
  70. 'strong' => 'Text/Wiki/Rule/strong.php',
  71. 'bold' => 'Text/Wiki/Rule/bold.php',
  72. 'emphasis' => 'Text/Wiki/Rule/emphasis.php',
  73. 'italic' => 'Text/Wiki/Rule/italic.php',
  74. 'tt' => 'Text/Wiki/Rule/tt.php',
  75. 'superscript' => 'Text/Wiki/Rule/superscript.php',
  76. 'revise' => 'Text/Wiki/Rule/revise.php',
  77. // post-filters
  78. 'entities' => 'Text/Wiki/Rule/entities.php',
  79. 'tighten' => 'Text/Wiki/Rule/tighten.php'
  80. );
  81. /**
  82. *
  83. * An associative array of Interwiki mappings where the key is the
  84. * Interwiki name as entered on a wiki page, and the value is the
  85. * replacement URL for the target wiki web.
  86. *
  87. * @access public
  88. *
  89. * @var array
  90. *
  91. */
  92. var $interwiki = array(
  93. 'MeatBall' => 'http://www.usemod.com/cgi-bin/mb.pl?',
  94. 'Advogato' => 'http://advogato.org/',
  95. 'Wiki' => 'http://c2.com/cgi/wiki?'
  96. );
  97. /**
  98. *
  99. * An array of all pages that currently exist in the wiki. The source
  100. * of the pages does not matter (database, file system, whatever). All
  101. * that Text_Wiki needs to know is what pages are already in the system
  102. * so that it can decide what kind of link to show when that page name
  103. * appears in the source text.
  104. *
  105. * @access public
  106. *
  107. * @var array
  108. *
  109. */
  110. var $pages = array();
  111. /**
  112. *
  113. * The URL used to view an existing page in the wiki; the page name
  114. * will be appended to this base URL.
  115. *
  116. * @access public
  117. *
  118. * @var string
  119. *
  120. */
  121. var $view_url = 'http://example.com/index.php?page=';
  122. /**
  123. *
  124. * The URL used to create a page that does not exist in the wiki; the
  125. * page name will be appended to this base URL.
  126. *
  127. * @access public
  128. *
  129. * @var string
  130. *
  131. */
  132. var $new_url = 'http://example.com/new.php?page=';
  133. /**
  134. *
  135. * When a page does not exist in the wiki, this is the text for the
  136. * link to the "create" URL. (Typically, the page name itself is not
  137. * the linked text; instead, a question mark or an inline image is
  138. * used to indicate the page does not exist.)
  139. *
  140. * @access public
  141. *
  142. * @var string
  143. *
  144. */
  145. var $new_text = '?';
  146. /**
  147. *
  148. * The delimiter that surrounds a token number embedded in the source
  149. * wiki text.
  150. *
  151. * @access public
  152. *
  153. * @var string
  154. *
  155. */
  156. var $delim = "\xFF";
  157. /**
  158. *
  159. * An array of tokens generated by rules as the source text is
  160. * parsed.
  161. *
  162. * As Text_Wiki applies rule classes to the source text, it will
  163. * replace portions of the text with a delimited token number. This
  164. * is the array of those tokens, representing the replaced text and
  165. * any options set by the parser for that replaced text.
  166. *
  167. * The tokens array is seqential; each element is itself a sequential
  168. * array where element 0 is the name of the rule that generated the
  169. * token, and element 1 is an associative array where the key is an
  170. * option name and the value is an option value.
  171. *
  172. * @access private
  173. *
  174. * @var string
  175. *
  176. */
  177. var $_tokens = array();
  178. /**
  179. *
  180. * The source text to which rules will be applied. This text will be
  181. * transformed in-place, which means that it will change as the rules
  182. * are applied.
  183. *
  184. * @access private
  185. *
  186. * @var string
  187. *
  188. */
  189. var $_source = '';
  190. /**
  191. *
  192. * Text_Wiki creates one instance of every rule that is applied to
  193. * the source text; this array holds those instances. The array key
  194. * is the rule name, and the array value is an instance of the rule
  195. * class.
  196. *
  197. * @access private
  198. *
  199. * @var string
  200. *
  201. */
  202. var $_rule_obj = array();
  203. /**
  204. *
  205. * Constructor.
  206. *
  207. * @access public
  208. *
  209. * @param array $options An associative array of options where the
  210. * key is the option name and the value is the option value. Each
  211. * option key corresponds to a public property of Text_Wiki; e.g.,
  212. * 'rules', 'interwiki', 'pages', 'rule_dir', and so on.
  213. *
  214. */
  215. function Text_Wiki($options = array())
  216. {
  217. if (isset($options) && is_array($options)) {
  218. foreach ($options as $key => $val) {
  219. // don't override private properties
  220. if (substr($key, 0, 1) != '_') {
  221. $this->$key = $val;
  222. }
  223. }
  224. }
  225. }
  226. /**
  227. *
  228. * Transforms the source text in-place.
  229. *
  230. * First, the method parses the source text, applying rules to the
  231. * text as it goes. These rules will modify the source text
  232. * in-place, replacing some text with delimited tokens (and
  233. * populating the $this->_tokens array as it goes).
  234. *
  235. * Next, the method renders the in-place tokens into the requested
  236. * output format.
  237. *
  238. * Finally, the method returns the transformed text. Note that the
  239. * source text is transformed in place; once it is transformed, it is
  240. * no longer the same as the original source text.
  241. *
  242. * @access public
  243. *
  244. * @param string $text The source text to which wiki rules should be
  245. * applied, both for parsing and for rendering.
  246. *
  247. * @param string $format The target output format, typically 'xhtml'.
  248. * If a rule does not support a given format, the output from that
  249. * rule is rule-specific.
  250. *
  251. * @return string The transformed wiki text.
  252. *
  253. */
  254. function transform($text, $format = 'xhtml')
  255. {
  256. $this->parse($text);
  257. return $this->render($format);
  258. }
  259. /**
  260. *
  261. * Sets the $_source text property, then parses it in place and
  262. * retains tokens in the $_tokens array property.
  263. *
  264. * @access public
  265. *
  266. * @param string $text The source text to which wiki rules should be
  267. * applied, both for parsing and for rendering.
  268. *
  269. * @return void
  270. *
  271. */
  272. function parse($text)
  273. {
  274. // set the object property for the source text
  275. $this->_source = $text;
  276. // apply the parse() method of each requested rule to the source
  277. // text. load each rule class file as we go.
  278. foreach ($this->rules as $name => $file) {
  279. $this->_loadRule($name, $file);
  280. $this->_rule_obj[$name]->parse();
  281. }
  282. }
  283. /**
  284. *
  285. * Renders tokens back into the source text, based on the requested format.
  286. *
  287. * @access public
  288. *
  289. * @param string $format The target output format, typically 'xhtml'.
  290. * If a rule does not support a given format, the output from that
  291. * rule is rule-specific.
  292. *
  293. * @return string The transformed wiki text.
  294. *
  295. */
  296. function render($format = 'Xhtml')
  297. {
  298. // the rendering method we're going to use from each rule
  299. $method = "render$format";
  300. // the eventual output text
  301. $output = '';
  302. // when passing through the parsed source text, keep track of when
  303. // we are in a delimited section
  304. $in_delim = false;
  305. // when in a delimited section, capture the token key number
  306. $key = '';
  307. // pass through the parsed source text character by character
  308. $k = strlen($this->_source);
  309. for ($i = 0; $i < $k; $i++) {
  310. // the current character
  311. $char = $this->_source{$i};
  312. // are alredy in a delimited section?
  313. if ($in_delim) {
  314. // yes; are we ending the section?
  315. if ($char == $this->delim) {
  316. // yes, get the replacement text for the delimited
  317. // token number and unset the flag.
  318. $key = (int)$key;
  319. $rule = $this->_tokens[$key][0];
  320. $opts = $this->_tokens[$key][1];
  321. $output .= $this->_rule_obj[$rule]->$method($opts);
  322. $in_delim = false;
  323. } else {
  324. // no, add to the dlimited token key number
  325. $key .= $char;
  326. }
  327. } else {
  328. // not currently in a delimited section.
  329. // are we starting into a delimited section?
  330. if ($char == $this->delim) {
  331. // yes, reset the previous key and
  332. // set the flag.
  333. $key = '';
  334. $in_delim = true;
  335. } else {
  336. // no, add to the output as-is
  337. $output .= $char;
  338. }
  339. }
  340. }
  341. // return the rendered source text
  342. return $output;
  343. }
  344. /**
  345. *
  346. * Returns the parsed source text with delimited token placeholders.
  347. *
  348. * @access public
  349. *
  350. * @return string The parsed source text.
  351. *
  352. */
  353. function getSource()
  354. {
  355. return $this->_source;
  356. }
  357. /**
  358. *
  359. * Returns tokens that have been parsed out of the source text.
  360. *
  361. * @access public
  362. *
  363. * @param array $rules If an array of rule names is passed, only return
  364. * tokens matching these rule names. If no array is passed, return all
  365. * tokens.
  366. *
  367. * @return array An array of tokens.
  368. *
  369. */
  370. function getTokens($rules = null)
  371. {
  372. if (! is_array($rules)) {
  373. return $this->_tokens;
  374. } else {
  375. $result = array();
  376. foreach ($this->_tokens as $key => $val) {
  377. if (in_array($val[0], $rules)) {
  378. $result[] = $val;
  379. }
  380. }
  381. return $result;
  382. }
  383. }
  384. /**
  385. *
  386. * Loads a rule class file and creates an instance of the rule
  387. * object.
  388. *
  389. * @access private
  390. *
  391. * @param string $name The token name to use for the rule.
  392. *
  393. * @param string $file The file name of the rule class.
  394. *
  395. * @return void
  396. *
  397. */
  398. function _loadRule($name, $file)
  399. {
  400. // load the class definition.
  401. include_once($file);
  402. // dynamically determine the name of the class
  403. // we just loaded
  404. $tmp = get_declared_classes();
  405. $k = count($tmp) - 1;
  406. $class = $tmp[$k];
  407. unset($tmp);
  408. // instantiate the rule object and add to the set
  409. $this->_rule_obj[$name] =& new $class($this, $name);
  410. }
  411. }
  412. ?>