Commit | Line | Data |
---|---|---|
6855525e JL |
1 | <?php |
2 | ||
3 | /** | |
4 | * | |
5 | * Parse structured wiki text and render into arbitrary formats such as XHTML. | |
6 | * | |
7 | * @category Text | |
8 | * | |
9 | * @package Text_Wiki | |
10 | * | |
11 | * @author Paul M. Jones <pmjones@php.net> | |
12 | * | |
13 | * @license LGPL | |
14 | * | |
15 | * @version $Id: Wiki.php,v 1.29 2005/02/24 17:26:29 pmjones Exp $ | |
16 | * | |
17 | */ | |
18 | ||
19 | /** | |
20 | * The baseline abstract parser class. | |
21 | */ | |
22 | ||
23 | require_once 'Text/Wiki/Parse.php'; | |
24 | ||
25 | /** | |
26 | * The baseline abstract render class. | |
27 | */ | |
28 | ||
29 | require_once 'Text/Wiki/Render.php'; | |
30 | ||
31 | ||
32 | /** | |
33 | * | |
34 | * Parse structured wiki text and render into arbitrary formats such as XHTML. | |
35 | * | |
36 | * This is the "master" class for handling the management and convenience | |
37 | * functions to transform Wiki-formatted text. | |
38 | * | |
39 | * @category Text | |
40 | * | |
41 | * @package Text_Wiki | |
42 | * | |
43 | <<<<<<< Wiki.php | |
44 | * @version 0.25.0 | |
45 | * | |
46 | * @license LGPL | |
47 | ======= | |
48 | * @author Paul M. Jones <pmjones@php.net> | |
49 | * | |
50 | * @version @package_version@ | |
51 | >>>>>>> 1.28 | |
52 | * | |
53 | */ | |
54 | ||
55 | class Text_Wiki { | |
56 | ||
57 | /** | |
58 | * | |
59 | * The default list of rules, in order, to apply to the source text. | |
60 | * | |
61 | * @access public | |
62 | * | |
63 | * @var array | |
64 | * | |
65 | */ | |
66 | ||
67 | var $rules = array( | |
68 | 'Prefilter', | |
69 | 'Delimiter', | |
70 | 'Code', | |
71 | 'Function', | |
72 | 'Html', | |
73 | 'Raw', | |
74 | 'Include', | |
75 | 'Embed', | |
76 | 'Anchor', | |
77 | 'Heading', | |
78 | 'Toc', | |
79 | 'Horiz', | |
80 | 'Break', | |
81 | 'Blockquote', | |
82 | 'List', | |
83 | 'Deflist', | |
84 | 'Table', | |
85 | 'Image', | |
86 | 'Phplookup', | |
87 | 'Center', | |
88 | 'Newline', | |
89 | 'Paragraph', | |
90 | 'Url', | |
91 | 'Freelink', | |
92 | 'Interwiki', | |
93 | 'Wikilink', | |
94 | 'Colortext', | |
95 | 'Strong', | |
96 | 'Bold', | |
97 | 'Emphasis', | |
98 | 'Italic', | |
99 | 'Tt', | |
100 | 'Superscript', | |
101 | 'Subscript', | |
102 | 'Revise', | |
103 | 'Tighten' | |
104 | ); | |
105 | ||
106 | ||
107 | /** | |
108 | * | |
109 | * The list of rules to not-apply to the source text. | |
110 | * | |
111 | * @access public | |
112 | * | |
113 | * @var array | |
114 | * | |
115 | */ | |
116 | ||
117 | var $disable = array( | |
118 | 'Html', | |
119 | 'Include', | |
120 | 'Embed' | |
121 | ); | |
122 | ||
123 | ||
124 | /** | |
125 | * | |
126 | * Custom configuration for rules at the parsing stage. | |
127 | * | |
128 | * In this array, the key is the parsing rule name, and the value is | |
129 | * an array of key-value configuration pairs corresponding to the $conf | |
130 | * property in the target parsing rule. | |
131 | * | |
132 | * For example: | |
133 | * | |
134 | * <code> | |
135 | * $parseConf = array( | |
136 | * 'Include' => array( | |
137 | * 'base' => '/path/to/scripts/' | |
138 | * ) | |
139 | * ); | |
140 | * </code> | |
141 | * | |
142 | * Note that most default rules do not need any parsing configuration. | |
143 | * | |
144 | * @access public | |
145 | * | |
146 | * @var array | |
147 | * | |
148 | */ | |
149 | ||
150 | var $parseConf = array(); | |
151 | ||
152 | ||
153 | /** | |
154 | * | |
155 | * Custom configuration for rules at the rendering stage. | |
156 | * | |
157 | * Because rendering may be different for each target format, the | |
158 | * first-level element in this array is always a format name (e.g., | |
159 | * 'Xhtml'). | |
160 | * | |
161 | * Within that first level element, the subsequent elements match the | |
162 | * $parseConf format. That is, the sub-key is the rendering rule name, | |
163 | * and the sub-value is an array of key-value configuration pairs | |
164 | * corresponding to the $conf property in the target rendering rule. | |
165 | * | |
166 | * @access public | |
167 | * | |
168 | * @var array | |
169 | * | |
170 | */ | |
171 | ||
172 | var $renderConf = array( | |
173 | 'Docbook' => array(), | |
174 | 'Latex' => array(), | |
175 | 'Pdf' => array(), | |
176 | 'Plain' => array(), | |
177 | 'Rtf' => array(), | |
178 | 'Xhtml' => array() | |
179 | ); | |
180 | ||
181 | ||
182 | /** | |
183 | * | |
184 | * Custom configuration for the output format itself. | |
185 | * | |
186 | * Even though Text_Wiki will render the tokens from parsed text, | |
187 | * the format itself may require some configuration. For example, | |
188 | * RTF needs to know font names and sizes, PDF requires page layout | |
189 | * information, and DocBook needs a section hierarchy. This array | |
190 | * matches the $conf property of the the format-level renderer | |
191 | * (e.g., Text_Wiki_Render_Xhtml). | |
192 | * | |
193 | * In this array, the key is the rendering format name, and the value is | |
194 | * an array of key-value configuration pairs corresponding to the $conf | |
195 | * property in the rendering format rule. | |
196 | * | |
197 | * @access public | |
198 | * | |
199 | * @var array | |
200 | * | |
201 | */ | |
202 | ||
203 | var $formatConf = array( | |
204 | 'Docbook' => array(), | |
205 | 'Latex' => array(), | |
206 | 'Pdf' => array(), | |
207 | 'Plain' => array(), | |
208 | 'Rtf' => array(), | |
209 | 'Xhtml' => array() | |
210 | ); | |
211 | ||
212 | ||
213 | /** | |
214 | * | |
215 | * The delimiter for token numbers of parsed elements in source text. | |
216 | * | |
217 | * @access public | |
218 | * | |
219 | * @var string | |
220 | * | |
221 | */ | |
222 | ||
223 | var $delim = "\xFF"; | |
224 | ||
225 | ||
226 | /** | |
227 | * | |
228 | * The tokens generated by rules as the source text is parsed. | |
229 | * | |
230 | * As Text_Wiki applies rule classes to the source text, it will | |
231 | * replace portions of the text with a delimited token number. This | |
232 | * is the array of those tokens, representing the replaced text and | |
233 | * any options set by the parser for that replaced text. | |
234 | * | |
235 | * The tokens array is sequential; each element is itself a sequential | |
236 | * array where element 0 is the name of the rule that generated the | |
237 | * token, and element 1 is an associative array where the key is an | |
238 | * option name and the value is an option value. | |
239 | * | |
240 | * @access private | |
241 | * | |
242 | * @var array | |
243 | * | |
244 | */ | |
245 | ||
246 | var $tokens = array(); | |
247 | ||
248 | ||
249 | /** | |
250 | * | |
251 | * The source text to which rules will be applied. | |
252 | * | |
253 | * This text will be transformed in-place, which means that it will | |
254 | * change as the rules are applied. | |
255 | * | |
256 | * @access private | |
257 | * | |
258 | * @var string | |
259 | * | |
260 | */ | |
261 | ||
262 | var $source = ''; | |
263 | ||
264 | ||
265 | /** | |
266 | * | |
267 | * Array of rule parsers. | |
268 | * | |
269 | * Text_Wiki creates one instance of every rule that is applied to | |
270 | * the source text; this array holds those instances. The array key | |
271 | * is the rule name, and the array value is an instance of the rule | |
272 | * class. | |
273 | * | |
274 | * @access private | |
275 | * | |
276 | * @var array | |
277 | * | |
278 | */ | |
279 | ||
280 | var $parseObj = array(); | |
281 | ||
282 | ||
283 | /** | |
284 | * | |
285 | * Array of rule renderers. | |
286 | * | |
287 | * Text_Wiki creates one instance of every rule that is applied to | |
288 | * the source text; this array holds those instances. The array key | |
289 | * is the rule name, and the array value is an instance of the rule | |
290 | * class. | |
291 | * | |
292 | * @access private | |
293 | * | |
294 | * @var array | |
295 | * | |
296 | */ | |
297 | ||
298 | var $renderObj = array(); | |
299 | ||
300 | ||
301 | /** | |
302 | * | |
303 | * Array of format renderers. | |
304 | * | |
305 | * @access private | |
306 | * | |
307 | * @var array | |
308 | * | |
309 | */ | |
310 | ||
311 | var $formatObj = array(); | |
312 | ||
313 | ||
314 | /** | |
315 | * | |
316 | * Array of paths to search, in order, for parsing and rendering rules. | |
317 | * | |
318 | * @access private | |
319 | * | |
320 | * @var array | |
321 | * | |
322 | */ | |
323 | ||
324 | var $path = array( | |
325 | 'parse' => array(), | |
326 | 'render' => array() | |
327 | ); | |
328 | ||
329 | ||
330 | ||
331 | /** | |
332 | * | |
333 | * The directory separator character. | |
334 | * | |
335 | * @access private | |
336 | * | |
337 | * @var string | |
338 | * | |
339 | */ | |
340 | ||
341 | var $_dirSep = DIRECTORY_SEPARATOR; | |
342 | ||
343 | ||
344 | /** | |
345 | * | |
346 | * Constructor. | |
347 | * | |
348 | * @access public | |
349 | * | |
350 | * @param array $rules The set of rules to load for this object. | |
351 | * | |
352 | */ | |
353 | ||
354 | function Text_Wiki($rules = null) | |
355 | { | |
356 | if (is_array($rules)) { | |
357 | $this->rules = $rules; | |
358 | } | |
359 | ||
360 | $this->addPath( | |
361 | 'parse', | |
362 | $this->fixPath(dirname(__FILE__)) . 'Wiki/Parse/Default/' | |
363 | ); | |
364 | ||
365 | $this->addPath( | |
366 | 'render', | |
367 | $this->fixPath(dirname(__FILE__)) . 'Wiki/Render/' | |
368 | ); | |
369 | ||
370 | } | |
371 | ||
372 | ||
373 | /** | |
374 | * | |
375 | * Set parser configuration for a specific rule and key. | |
376 | * | |
377 | * @access public | |
378 | * | |
379 | * @param string $rule The parse rule to set config for. | |
380 | * | |
381 | * @param array|string $arg1 The full config array to use for the | |
382 | * parse rule, or a conf key in that array. | |
383 | * | |
384 | * @param string $arg2 The config value for the key. | |
385 | * | |
386 | * @return void | |
387 | * | |
388 | */ | |
389 | ||
390 | function setParseConf($rule, $arg1, $arg2 = null) | |
391 | { | |
392 | $rule = ucwords(strtolower($rule)); | |
393 | ||
394 | if (! isset($this->parseConf[$rule])) { | |
395 | $this->parseConf[$rule] = array(); | |
396 | } | |
397 | ||
398 | // if first arg is an array, use it as the entire | |
399 | // conf array for the rule. otherwise, treat arg1 | |
400 | // as a key and arg2 as a value for the rule conf. | |
401 | if (is_array($arg1)) { | |
402 | $this->parseConf[$rule] = $arg1; | |
403 | } else { | |
404 | $this->parseConf[$rule][$arg1] = $arg2; | |
405 | } | |
406 | } | |
407 | ||
408 | ||
409 | /** | |
410 | * | |
411 | * Get parser configuration for a specific rule and key. | |
412 | * | |
413 | * @access public | |
414 | * | |
415 | * @param string $rule The parse rule to get config for. | |
416 | * | |
417 | * @param string $key A key in the conf array; if null, | |
418 | * returns the entire conf array. | |
419 | * | |
420 | * @return mixed The whole conf array if no key is specified, | |
421 | * or the specific conf key value. | |
422 | * | |
423 | */ | |
424 | ||
425 | function getParseConf($rule, $key = null) | |
426 | { | |
427 | $rule = ucwords(strtolower($rule)); | |
428 | ||
429 | // the rule does not exist | |
430 | if (! isset($this->parseConf[$rule])) { | |
431 | return null; | |
432 | } | |
433 | ||
434 | // no key requested, return the whole array | |
435 | if (is_null($key)) { | |
436 | return $this->parseConf[$rule]; | |
437 | } | |
438 | ||
439 | // does the requested key exist? | |
440 | if (isset($this->parseConf[$rule][$key])) { | |
441 | // yes, return that value | |
442 | return $this->parseConf[$rule][$key]; | |
443 | } else { | |
444 | // no | |
445 | return null; | |
446 | } | |
447 | } | |
448 | ||
449 | ||
450 | /** | |
451 | * | |
452 | * Set renderer configuration for a specific format, rule, and key. | |
453 | * | |
454 | * @access public | |
455 | * | |
456 | * @param string $format The render format to set config for. | |
457 | * | |
458 | * @param string $rule The render rule to set config for in the format. | |
459 | * | |
460 | * @param array|string $arg1 The config array, or the config key | |
461 | * within the render rule. | |
462 | * | |
463 | * @param string $arg2 The config value for the key. | |
464 | * | |
465 | * @return void | |
466 | * | |
467 | */ | |
468 | ||
469 | function setRenderConf($format, $rule, $arg1, $arg2 = null) | |
470 | { | |
471 | $format = ucwords(strtolower($format)); | |
472 | $rule = ucwords(strtolower($rule)); | |
473 | ||
474 | if (! isset($this->renderConf[$format])) { | |
475 | $this->renderConf[$format] = array(); | |
476 | } | |
477 | ||
478 | if (! isset($this->renderConf[$format][$rule])) { | |
479 | $this->renderConf[$format][$rule] = array(); | |
480 | } | |
481 | ||
482 | // if first arg is an array, use it as the entire | |
483 | // conf array for the render rule. otherwise, treat arg1 | |
484 | // as a key and arg2 as a value for the render rule conf. | |
485 | if (is_array($arg1)) { | |
486 | $this->renderConf[$format][$rule] = $arg1; | |
487 | } else { | |
488 | $this->renderConf[$format][$rule][$arg1] = $arg2; | |
489 | } | |
490 | } | |
491 | ||
492 | ||
493 | /** | |
494 | * | |
495 | * Get renderer configuration for a specific format, rule, and key. | |
496 | * | |
497 | * @access public | |
498 | * | |
499 | * @param string $format The render format to get config for. | |
500 | * | |
501 | * @param string $rule The render format rule to get config for. | |
502 | * | |
503 | * @param string $key A key in the conf array; if null, | |
504 | * returns the entire conf array. | |
505 | * | |
506 | * @return mixed The whole conf array if no key is specified, | |
507 | * or the specific conf key value. | |
508 | * | |
509 | */ | |
510 | ||
511 | function getRenderConf($format, $rule, $key = null) | |
512 | { | |
513 | $format = ucwords(strtolower($format)); | |
514 | $rule = ucwords(strtolower($rule)); | |
515 | ||
516 | if (! isset($this->renderConf[$format]) || | |
517 | ! isset($this->renderConf[$format][$rule])) { | |
518 | return null; | |
519 | } | |
520 | ||
521 | // no key requested, return the whole array | |
522 | if (is_null($key)) { | |
523 | return $this->renderConf[$format][$rule]; | |
524 | } | |
525 | ||
526 | // does the requested key exist? | |
527 | if (isset($this->renderConf[$format][$rule][$key])) { | |
528 | // yes, return that value | |
529 | return $this->renderConf[$format][$rule][$key]; | |
530 | } else { | |
531 | // no | |
532 | return null; | |
533 | } | |
534 | ||
535 | } | |
536 | ||
537 | /** | |
538 | * | |
539 | * Set format configuration for a specific rule and key. | |
540 | * | |
541 | * @access public | |
542 | * | |
543 | * @param string $format The format to set config for. | |
544 | * | |
545 | * @param string $key The config key within the format. | |
546 | * | |
547 | * @param string $val The config value for the key. | |
548 | * | |
549 | * @return void | |
550 | * | |
551 | */ | |
552 | ||
553 | function setFormatConf($format, $arg1, $arg2 = null) | |
554 | { | |
555 | if (! is_array($this->formatConf[$format])) { | |
556 | $this->formatConf[$format] = array(); | |
557 | } | |
558 | ||
559 | // if first arg is an array, use it as the entire | |
560 | // conf array for the format. otherwise, treat arg1 | |
561 | // as a key and arg2 as a value for the format conf. | |
562 | if (is_array($arg1)) { | |
563 | $this->formatConf[$format] = $arg1; | |
564 | } else { | |
565 | $this->formatConf[$format][$arg1] = $arg2; | |
566 | } | |
567 | } | |
568 | ||
569 | ||
570 | ||
571 | /** | |
572 | * | |
573 | * Get configuration for a specific format and key. | |
574 | * | |
575 | * @access public | |
576 | * | |
577 | * @param string $format The format to get config for. | |
578 | * | |
579 | * @param mixed $key A key in the conf array; if null, | |
580 | * returns the entire conf array. | |
581 | * | |
582 | * @return mixed The whole conf array if no key is specified, | |
583 | * or the specific conf key value. | |
584 | * | |
585 | */ | |
586 | ||
587 | function getFormatConf($format, $key = null) | |
588 | { | |
589 | // the format does not exist | |
590 | if (! isset($this->formatConf[$format])) { | |
591 | return null; | |
592 | } | |
593 | ||
594 | // no key requested, return the whole array | |
595 | if (is_null($key)) { | |
596 | return $this->formatConf[$format]; | |
597 | } | |
598 | ||
599 | // does the requested key exist? | |
600 | if (isset($this->formatConf[$format][$key])) { | |
601 | // yes, return that value | |
602 | return $this->formatConf[$format][$key]; | |
603 | } else { | |
604 | // no | |
605 | return null; | |
606 | } | |
607 | } | |
608 | ||
609 | ||
610 | /** | |
611 | * | |
612 | * Inserts a rule into to the rule set. | |
613 | * | |
614 | * @access public | |
615 | * | |
616 | * @param string $name The name of the rule. Should be different from | |
617 | * all other keys in the rule set. | |
618 | * | |
619 | * @param string $tgt The rule after which to insert this new rule. By | |
620 | * default (null) the rule is inserted at the end; if set to '', inserts | |
621 | * at the beginning. | |
622 | * | |
623 | * @return void | |
624 | * | |
625 | */ | |
626 | ||
627 | function insertRule($name, $tgt = null) | |
628 | { | |
629 | $name = ucwords(strtolower($name)); | |
630 | if (! is_null($tgt)) { | |
631 | $tgt = ucwords(strtolower($tgt)); | |
632 | } | |
633 | ||
634 | // does the rule name to be inserted already exist? | |
635 | if (in_array($name, $this->rules)) { | |
636 | // yes, return | |
637 | return null; | |
638 | } | |
639 | ||
640 | // the target name is not null, and not '', but does not exist | |
641 | // in the list of rules. this means we're trying to insert after | |
642 | // a target key, but the target key isn't there. | |
643 | if (! is_null($tgt) && $tgt != '' && | |
644 | ! in_array($tgt, $this->rules)) { | |
645 | return false; | |
646 | } | |
647 | ||
648 | // if $tgt is null, insert at the end. We know this is at the | |
649 | // end (instead of resetting an existing rule) becuase we exited | |
650 | // at the top of this method if the rule was already in place. | |
651 | if (is_null($tgt)) { | |
652 | $this->rules[] = $name; | |
653 | return true; | |
654 | } | |
655 | ||
656 | // save a copy of the current rules, then reset the rule set | |
657 | // so we can insert in the proper place later. | |
658 | // where to insert the rule? | |
659 | if ($tgt == '') { | |
660 | // insert at the beginning | |
661 | array_unshift($this->rules, $name); | |
662 | return true; | |
663 | } | |
664 | ||
665 | // insert after the named rule | |
666 | $tmp = $this->rules; | |
667 | $this->rules = array(); | |
668 | ||
669 | foreach ($tmp as $val) { | |
670 | $this->rules[] = $val; | |
671 | if ($val == $tgt) { | |
672 | $this->rules[] = $name; | |
673 | } | |
674 | } | |
675 | ||
676 | return true; | |
677 | ||
678 | } | |
679 | ||
680 | ||
681 | /** | |
682 | * | |
683 | * Delete (remove or unset) a rule from the $rules property. | |
684 | * | |
685 | * @access public | |
686 | * | |
687 | * @param string $rule The name of the rule to remove. | |
688 | * | |
689 | * @return void | |
690 | * | |
691 | */ | |
692 | ||
693 | function deleteRule($name) | |
694 | { | |
695 | $name = ucwords(strtolower($name)); | |
696 | $key = array_search($name, $this->rules); | |
697 | if ($key !== false) { | |
698 | unset($this->rules[$key]); | |
699 | } | |
700 | } | |
701 | ||
702 | ||
703 | /** | |
704 | * | |
705 | * Change from one rule to another in-place. | |
706 | * | |
707 | * @access public | |
708 | * | |
709 | * @param string $old The name of the rule to change from. | |
710 | * | |
711 | * @param string $new The name of the rule to change to. | |
712 | * | |
713 | * @return void | |
714 | * | |
715 | */ | |
716 | ||
717 | function changeRule($old, $new) | |
718 | { | |
719 | $old = ucwords(strtolower($old)); | |
720 | $new = ucwords(strtolower($new)); | |
721 | $key = array_search($old, $this->rules); | |
722 | if ($key !== false) { | |
723 | $this->rules[$old] = $new; | |
724 | } | |
725 | } | |
726 | ||
727 | ||
728 | /** | |
729 | * | |
730 | * Enables a rule so that it is applied when parsing. | |
731 | * | |
732 | * @access public | |
733 | * | |
734 | * @param string $rule The name of the rule to enable. | |
735 | * | |
736 | * @return void | |
737 | * | |
738 | */ | |
739 | ||
740 | function enableRule($name) | |
741 | { | |
742 | $name = ucwords(strtolower($name)); | |
743 | $key = array_search($name, $this->disable); | |
744 | if ($key !== false) { | |
745 | unset($this->disable[$key]); | |
746 | } | |
747 | } | |
748 | ||
749 | ||
750 | /** | |
751 | * | |
752 | * Disables a rule so that it is not applied when parsing. | |
753 | * | |
754 | * @access public | |
755 | * | |
756 | * @param string $rule The name of the rule to disable. | |
757 | * | |
758 | * @return void | |
759 | * | |
760 | */ | |
761 | ||
762 | function disableRule($name) | |
763 | { | |
764 | $name = ucwords(strtolower($name)); | |
765 | $key = array_search($name, $this->disable); | |
766 | if ($key === false) { | |
767 | $this->disable[] = $name; | |
768 | } | |
769 | } | |
770 | ||
771 | ||
772 | /** | |
773 | * | |
774 | * Parses and renders the text passed to it, and returns the results. | |
775 | * | |
776 | * First, the method parses the source text, applying rules to the | |
777 | * text as it goes. These rules will modify the source text | |
778 | * in-place, replacing some text with delimited tokens (and | |
779 | * populating the $this->tokens array as it goes). | |
780 | * | |
781 | * Next, the method renders the in-place tokens into the requested | |
782 | * output format. | |
783 | * | |
784 | * Finally, the method returns the transformed text. Note that the | |
785 | * source text is transformed in place; once it is transformed, it is | |
786 | * no longer the same as the original source text. | |
787 | * | |
788 | * @access public | |
789 | * | |
790 | * @param string $text The source text to which wiki rules should be | |
791 | * applied, both for parsing and for rendering. | |
792 | * | |
793 | * @param string $format The target output format, typically 'xhtml'. | |
794 | * If a rule does not support a given format, the output from that | |
795 | * rule is rule-specific. | |
796 | * | |
797 | * @return string The transformed wiki text. | |
798 | * | |
799 | */ | |
800 | ||
801 | function transform($text, $format = 'Xhtml') | |
802 | { | |
803 | $this->parse($text); | |
804 | return $this->render($format); | |
805 | } | |
806 | ||
807 | ||
808 | /** | |
809 | * | |
810 | * Sets the $_source text property, then parses it in place and | |
811 | * retains tokens in the $_tokens array property. | |
812 | * | |
813 | * @access public | |
814 | * | |
815 | * @param string $text The source text to which wiki rules should be | |
816 | * applied, both for parsing and for rendering. | |
817 | * | |
818 | * @return void | |
819 | * | |
820 | */ | |
821 | ||
822 | function parse($text) | |
823 | { | |
824 | // set the object property for the source text | |
825 | $this->source = $text; | |
826 | ||
827 | // reset the tokens. | |
828 | $this->tokens = array(); | |
829 | ||
830 | // apply the parse() method of each requested rule to the source | |
831 | // text. | |
832 | foreach ($this->rules as $name) { | |
833 | // do not parse the rules listed in $disable | |
834 | if (! in_array($name, $this->disable)) { | |
835 | ||
836 | // load the parsing object | |
837 | $this->loadParseObj($name); | |
838 | ||
839 | // load may have failed; only parse if | |
840 | // an object is in the array now | |
841 | if (is_object($this->parseObj[$name])) { | |
842 | $this->parseObj[$name]->parse(); | |
843 | } | |
844 | } | |
845 | } | |
846 | } | |
847 | ||
848 | ||
849 | /** | |
850 | * | |
851 | * Renders tokens back into the source text, based on the requested format. | |
852 | * | |
853 | * @access public | |
854 | * | |
855 | * @param string $format The target output format, typically 'xhtml'. | |
856 | * If a rule does not support a given format, the output from that | |
857 | * rule is rule-specific. | |
858 | * | |
859 | * @return string The transformed wiki text. | |
860 | * | |
861 | */ | |
862 | ||
863 | function render($format = 'Xhtml') | |
864 | { | |
865 | // the rendering method we're going to use from each rule | |
866 | $format = ucwords(strtolower($format)); | |
867 | ||
868 | // the eventual output text | |
869 | $output = ''; | |
870 | ||
871 | // when passing through the parsed source text, keep track of when | |
872 | // we are in a delimited section | |
873 | $in_delim = false; | |
874 | ||
875 | // when in a delimited section, capture the token key number | |
876 | $key = ''; | |
877 | ||
878 | // load the format object | |
879 | $this->loadFormatObj($format); | |
880 | ||
881 | // pre-rendering activity | |
882 | if (is_object($this->formatObj[$format])) { | |
883 | $output .= $this->formatObj[$format]->pre(); | |
884 | } | |
885 | ||
886 | // load the render objects | |
887 | foreach (array_keys($this->parseObj) as $rule) { | |
888 | $this->loadRenderObj($format, $rule); | |
889 | } | |
890 | ||
891 | // pass through the parsed source text character by character | |
892 | $k = strlen($this->source); | |
893 | for ($i = 0; $i < $k; $i++) { | |
894 | ||
895 | // the current character | |
896 | $char = $this->source{$i}; | |
897 | ||
898 | // are alredy in a delimited section? | |
899 | if ($in_delim) { | |
900 | ||
901 | // yes; are we ending the section? | |
902 | if ($char == $this->delim) { | |
903 | ||
904 | // yes, get the replacement text for the delimited | |
905 | // token number and unset the flag. | |
906 | $key = (int)$key; | |
907 | $rule = $this->tokens[$key][0]; | |
908 | $opts = $this->tokens[$key][1]; | |
909 | $output .= $this->renderObj[$rule]->token($opts); | |
910 | $in_delim = false; | |
911 | ||
912 | } else { | |
913 | ||
914 | // no, add to the dlimited token key number | |
915 | $key .= $char; | |
916 | ||
917 | } | |
918 | ||
919 | } else { | |
920 | ||
921 | // not currently in a delimited section. | |
922 | // are we starting into a delimited section? | |
923 | if ($char == $this->delim) { | |
924 | // yes, reset the previous key and | |
925 | // set the flag. | |
926 | $key = ''; | |
927 | $in_delim = true; | |
928 | } else { | |
929 | // no, add to the output as-is | |
930 | $output .= $char; | |
931 | } | |
932 | } | |
933 | } | |
934 | ||
935 | // post-rendering activity | |
936 | if (is_object($this->formatObj[$format])) { | |
937 | $output .= $this->formatObj[$format]->post(); | |
938 | } | |
939 | ||
940 | // return the rendered source text. | |
941 | return $output; | |
942 | } | |
943 | ||
944 | ||
945 | /** | |
946 | * | |
947 | * Returns the parsed source text with delimited token placeholders. | |
948 | * | |
949 | * @access public | |
950 | * | |
951 | * @return string The parsed source text. | |
952 | * | |
953 | */ | |
954 | ||
955 | function getSource() | |
956 | { | |
957 | return $this->source; | |
958 | } | |
959 | ||
960 | ||
961 | /** | |
962 | * | |
963 | * Returns tokens that have been parsed out of the source text. | |
964 | * | |
965 | * @access public | |
966 | * | |
967 | * @param array $rules If an array of rule names is passed, only return | |
968 | * tokens matching these rule names. If no array is passed, return all | |
969 | * tokens. | |
970 | * | |
971 | * @return array An array of tokens. | |
972 | * | |
973 | */ | |
974 | ||
975 | function getTokens($rules = null) | |
976 | { | |
977 | if (is_null($rules)) { | |
978 | return $this->tokens; | |
979 | } else { | |
980 | settype($rules, 'array'); | |
981 | $result = array(); | |
982 | foreach ($this->tokens as $key => $val) { | |
983 | if (in_array($val[0], $rules)) { | |
984 | $result[] = $val; | |
985 | } | |
986 | } | |
987 | return $result; | |
988 | } | |
989 | } | |
990 | ||
991 | ||
992 | /** | |
993 | * | |
994 | * Add a token to the Text_Wiki tokens array, and return a delimited | |
995 | * token number. | |
996 | * | |
997 | * @access public | |
998 | * | |
999 | * @param array $options An associative array of options for the new | |
1000 | * token array element. The keys and values are specific to the | |
1001 | * rule, and may or may not be common to other rule options. Typical | |
1002 | * options keys are 'text' and 'type' but may include others. | |
1003 | * | |
1004 | * @param boolean $id_only If true, return only the token number, not | |
1005 | * a delimited token string. | |
1006 | * | |
1007 | * @return string|int By default, return the number of the | |
1008 | * newly-created token array element with a delimiter prefix and | |
1009 | * suffix; however, if $id_only is set to true, return only the token | |
1010 | * number (no delimiters). | |
1011 | * | |
1012 | */ | |
1013 | ||
1014 | function addToken($rule, $options = array(), $id_only = false) | |
1015 | { | |
1016 | // increment the token ID number. note that if you parse | |
1017 | // multiple times with the same Text_Wiki object, the ID number | |
1018 | // will not reset to zero. | |
1019 | static $id; | |
1020 | if (! isset($id)) { | |
1021 | $id = 0; | |
1022 | } else { | |
1023 | $id ++; | |
1024 | } | |
1025 | ||
1026 | // force the options to be an array | |
1027 | settype($options, 'array'); | |
1028 | ||
1029 | // add the token | |
1030 | $this->tokens[$id] = array( | |
1031 | 0 => $rule, | |
1032 | 1 => $options | |
1033 | ); | |
1034 | ||
1035 | // return a value | |
1036 | if ($id_only) { | |
1037 | // return the last token number | |
1038 | return $id; | |
1039 | } else { | |
1040 | // return the token number with delimiters | |
1041 | return $this->delim . $id . $this->delim; | |
1042 | } | |
1043 | } | |
1044 | ||
1045 | ||
1046 | /** | |
1047 | * | |
1048 | * Set or re-set a token with specific information, overwriting any | |
1049 | * previous rule name and rule options. | |
1050 | * | |
1051 | * @access public | |
1052 | * | |
1053 | * @param int $id The token number to reset. | |
1054 | * | |
1055 | * @param int $rule The rule name to use. | |
1056 | * | |
1057 | * @param array $options An associative array of options for the | |
1058 | * token array element. The keys and values are specific to the | |
1059 | * rule, and may or may not be common to other rule options. Typical | |
1060 | * options keys are 'text' and 'type' but may include others. | |
1061 | * | |
1062 | * @return void | |
1063 | * | |
1064 | */ | |
1065 | ||
1066 | function setToken($id, $rule, $options = array()) | |
1067 | { | |
1068 | // reset the token | |
1069 | $this->tokens[$id] = array( | |
1070 | 0 => $rule, | |
1071 | 1 => $options | |
1072 | ); | |
1073 | } | |
1074 | ||
1075 | ||
1076 | /** | |
1077 | * | |
1078 | * Load a rule parser class file. | |
1079 | * | |
1080 | * @access public | |
1081 | * | |
1082 | * @return bool True if loaded, false if not. | |
1083 | * | |
1084 | */ | |
1085 | ||
1086 | function loadParseObj($rule) | |
1087 | { | |
1088 | $rule = ucwords(strtolower($rule)); | |
1089 | $file = $rule . '.php'; | |
1090 | $class = "Text_Wiki_Parse_$rule"; | |
1091 | ||
1092 | if (! class_exists($class)) { | |
1093 | $loc = $this->findFile('parse', $file); | |
1094 | if ($loc) { | |
1095 | // found the class | |
1096 | include_once $loc; | |
1097 | } else { | |
1098 | // can't find the class | |
1099 | $this->parseObj[$rule] = null; | |
1100 | return false; | |
1101 | } | |
1102 | } | |
1103 | ||
1104 | $this->parseObj[$rule] =& new $class($this); | |
1105 | ||
1106 | } | |
1107 | ||
1108 | ||
1109 | /** | |
1110 | * | |
1111 | * Load a rule-render class file. | |
1112 | * | |
1113 | * @access public | |
1114 | * | |
1115 | * @return bool True if loaded, false if not. | |
1116 | * | |
1117 | */ | |
1118 | ||
1119 | function loadRenderObj($format, $rule) | |
1120 | { | |
1121 | $format = ucwords(strtolower($format)); | |
1122 | $rule = ucwords(strtolower($rule)); | |
1123 | $file = "$format/$rule.php"; | |
1124 | $class = "Text_Wiki_Render_$format" . "_$rule"; | |
1125 | ||
1126 | if (! class_exists($class)) { | |
1127 | // load the class | |
1128 | $loc = $this->findFile('render', $file); | |
1129 | if ($loc) { | |
1130 | // found the class | |
1131 | include_once $loc; | |
1132 | } else { | |
1133 | // can't find the class | |
1134 | return false; | |
1135 | } | |
1136 | } | |
1137 | ||
1138 | $this->renderObj[$rule] =& new $class($this); | |
1139 | } | |
1140 | ||
1141 | ||
1142 | /** | |
1143 | * | |
1144 | * Load a format-render class file. | |
1145 | * | |
1146 | * @access public | |
1147 | * | |
1148 | * @return bool True if loaded, false if not. | |
1149 | * | |
1150 | */ | |
1151 | ||
1152 | function loadFormatObj($format) | |
1153 | { | |
1154 | $format = ucwords(strtolower($format)); | |
1155 | $file = $format . '.php'; | |
1156 | $class = "Text_Wiki_Render_$format"; | |
1157 | ||
1158 | if (! class_exists($class)) { | |
1159 | $loc = $this->findFile('render', $file); | |
1160 | if ($loc) { | |
1161 | // found the class | |
1162 | include_once $loc; | |
1163 | } else { | |
1164 | // can't find the class | |
1165 | return false; | |
1166 | } | |
1167 | } | |
1168 | ||
1169 | $this->formatObj[$format] =& new $class($this); | |
1170 | } | |
1171 | ||
1172 | ||
1173 | /** | |
1174 | * | |
1175 | * Add a path to a path array. | |
1176 | * | |
1177 | * @access public | |
1178 | * | |
1179 | * @param string $type The path-type to add (parse or render). | |
1180 | * | |
1181 | * @param string $dir The directory to add to the path-type. | |
1182 | * | |
1183 | * @return void | |
1184 | * | |
1185 | */ | |
1186 | ||
1187 | function addPath($type, $dir) | |
1188 | { | |
1189 | $dir = $this->fixPath($dir); | |
1190 | if (! isset($this->path[$type])) { | |
1191 | $this->path[$type] = array($dir); | |
1192 | } else { | |
1193 | array_unshift($this->path[$type], $dir); | |
1194 | } | |
1195 | } | |
1196 | ||
1197 | ||
1198 | /** | |
1199 | * | |
1200 | * Get the current path array for a path-type. | |
1201 | * | |
1202 | * @access public | |
1203 | * | |
1204 | * @param string $type The path-type to look up (plugin, filter, or | |
1205 | * template). If not set, returns all path types. | |
1206 | * | |
1207 | * @return array The array of paths for the requested type. | |
1208 | * | |
1209 | */ | |
1210 | ||
1211 | function getPath($type = null) | |
1212 | { | |
1213 | if (is_null($type)) { | |
1214 | return $this->path; | |
1215 | } elseif (! isset($this->path[$type])) { | |
1216 | return array(); | |
1217 | } else { | |
1218 | return $this->path[$type]; | |
1219 | } | |
1220 | } | |
1221 | ||
1222 | ||
1223 | /** | |
1224 | * | |
1225 | * Searches a series of paths for a given file. | |
1226 | * | |
1227 | * @param array $type The type of paths to search (template, plugin, | |
1228 | * or filter). | |
1229 | * | |
1230 | * @param string $file The file name to look for. | |
1231 | * | |
1232 | * @return string|bool The full path and file name for the target file, | |
1233 | * or boolean false if the file is not found in any of the paths. | |
1234 | * | |
1235 | */ | |
1236 | ||
1237 | function findFile($type, $file) | |
1238 | { | |
1239 | // get the set of paths | |
1240 | $set = $this->getPath($type); | |
1241 | ||
1242 | // start looping through them | |
1243 | foreach ($set as $path) { | |
1244 | $fullname = $path . $file; | |
1245 | if (file_exists($fullname) && is_readable($fullname)) { | |
1246 | return $fullname; | |
1247 | } | |
1248 | } | |
1249 | ||
1250 | // could not find the file in the set of paths | |
1251 | return false; | |
1252 | } | |
1253 | ||
1254 | ||
1255 | /** | |
1256 | * | |
1257 | * Append a trailing '/' to paths, unless the path is empty. | |
1258 | * | |
1259 | * @access private | |
1260 | * | |
1261 | * @param string $path The file path to fix | |
1262 | * | |
1263 | * @return string The fixed file path | |
1264 | * | |
1265 | */ | |
1266 | ||
1267 | function fixPath($path) | |
1268 | { | |
1269 | $len = strlen($this->_dirSep); | |
1270 | ||
1271 | if (! empty($path) && | |
1272 | substr($path, -1 * $len, $len) != $this->_dirSep) { | |
1273 | return $path . $this->_dirSep; | |
1274 | } else { | |
1275 | return $path; | |
1276 | } | |
1277 | } | |
1278 | } | |
1279 | ||
1280 | ?> |