11 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
11 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. |
13 */ |
13 */ |
14 |
14 |
15 /** |
15 /** |
16 * Parse structured wiki text and render into arbitrary formats such as XHTML. |
16 * Framework for parsing and rendering various formats. In Enano by default, this is MediaWiki-style wikitext being |
|
17 * rendered to XHTML, but this framework allows other formats to be supported as well. |
17 * |
18 * |
18 * PHP versions 4 and 5 |
19 * @package Enano |
19 * |
20 * @subpackage Content |
20 * @category Text |
21 * @author Dan Fuhry <dan@enanocms.org> |
21 * @package Text_Wiki |
22 * @copyright (C) 2009 Enano CMS Project |
22 * @author Paul M. Jones <pmjones@php.net> |
23 * @license GNU General Public License, version 2 or later <http://www.gnu.org/licenses/gpl-2.0.html> |
23 * @license http://www.gnu.org/licenses/lgpl.html |
|
24 * @version CVS: $Id: Wiki.php,v 1.44 2006/03/02 04:04:59 justinpatrin Exp $ |
|
25 * @link http://wiki.ciaweb.net/yawiki/index.php?area=Text_Wiki |
|
26 * |
|
27 * This code was modified for use in Enano. The Text_Wiki engine is licensed |
|
28 * under the GNU Lesser General Public License; see |
|
29 * http://www.gnu.org/licenses/lgpl.html for details. |
|
30 * |
|
31 */ |
24 */ |
32 |
25 |
33 require_once ENANO_ROOT.'/includes/wikiengine/Parse.php'; |
26 class Carpenter |
34 require_once ENANO_ROOT.'/includes/wikiengine/Render.php'; |
27 { |
35 |
28 /** |
36 class Text_Wiki { |
29 * Parser token |
37 |
30 * @const string |
38 var $rules = array( |
31 */ |
39 'Prefilter', |
32 |
40 'Delimiter', |
33 const PARSER_TOKEN = "\xFF"; |
41 'Code', |
34 |
42 'Function', |
35 /** |
43 'Html', |
36 * Parsing engine |
44 'Raw', |
37 * @var string |
45 'Include', |
38 */ |
46 'Embed', |
39 |
47 'Anchor', |
40 private $parser = 'mediawiki'; |
48 'Heading', |
41 |
49 'Toc', |
42 /** |
50 'Horiz', |
43 * Rendering engine |
51 'Break', |
44 * @var string |
52 'Blockquote', |
45 */ |
53 'List', |
46 |
54 'Deflist', |
47 private $renderer = 'xhtml'; |
55 'Table', |
48 |
56 'Image', |
49 /** |
57 'Phplookup', |
50 * Rendering flags |
58 'Center', |
51 */ |
59 'Newline', |
52 |
60 'Paragraph', |
53 public $flags = RENDER_WIKI_DEFAULT; |
61 'Url', |
54 |
62 'Freelink', |
55 /** |
63 'Interwiki', |
56 * List of rendering rules |
64 'Wikilink', |
57 * @var array |
65 'Colortext', |
58 */ |
66 'Strong', |
59 |
67 'Bold', |
60 private $rules = array( |
68 'Emphasis', |
61 'lang', |
69 'Italic', |
62 'templates', |
70 'Underline', |
63 'tables', |
71 'Tt', |
64 'heading', |
72 'Superscript', |
65 // note: can't be named list ("list" is a PHP language construct) |
73 'Subscript', |
66 'multilist', |
74 'Revise', |
67 'bold', |
75 'Tighten' |
68 'italic', |
|
69 'underline', |
|
70 'externalwithtext', |
|
71 'externalnotext', |
|
72 'image', |
|
73 'internallink', |
|
74 'paragraph' |
76 ); |
75 ); |
77 |
76 |
78 var $disable = array( |
77 /** |
79 'Html', |
78 * List of render hooks |
80 'Include', |
79 * @var array |
81 'Embed', |
80 */ |
82 'Tighten', |
81 |
83 'Image', |
82 private $hooks = array(); |
84 'Wikilink' |
83 |
85 ); |
84 /* private $rules = array('prefilter', 'delimiter', 'code', 'function', 'html', 'raw', 'include', 'embed', 'anchor', |
86 |
85 'heading', 'toc', 'horiz', 'break', 'blockquote', 'list', 'deflist', 'table', 'image', |
87 var $parseConf = array(); |
86 'phplookup', 'center', 'newline', 'paragraph', 'url', 'freelink', 'interwiki', |
88 |
87 'wikilink', 'colortext', 'strong', 'bold', 'emphasis', 'italic', 'underline', 'tt', |
89 var $renderConf = array( |
88 'superscript', 'subscript', 'revise', 'tighten'); */ |
90 'Docbook' => array(), |
89 |
91 'Latex' => array(), |
90 /** |
92 'Pdf' => array(), |
91 * Render the text! |
93 'Plain' => array(), |
92 * @param string Text to render |
94 'Rtf' => array(), |
93 * @return string |
95 'Xhtml' => array() |
94 */ |
96 ); |
95 |
97 |
96 public function render($text) |
98 var $formatConf = array( |
97 { |
99 'Docbook' => array(), |
98 $parser_class = "Carpenter_Parse_" . ucwords($this->parser); |
100 'Latex' => array(), |
99 $renderer_class = "Carpenter_Render_" . ucwords($this->renderer); |
101 'Pdf' => array(), |
100 |
102 'Plain' => array(), |
101 // include files, if we haven't already |
103 'Rtf' => array(), |
102 if ( !class_exists($parser_class) ) |
104 'Xhtml' => array() |
103 { |
105 ); |
104 require_once( ENANO_ROOT . "/includes/wikiengine/parse_{$this->parser}.php"); |
106 var $delim = "\xFF"; |
105 } |
107 var $tokens = array(); |
106 |
108 var $_countRulesTokens = array(); |
107 if ( !class_exists($renderer_class) ) |
109 var $source = ''; |
108 { |
110 var $parseObj = array(); |
109 require_once( ENANO_ROOT . "/includes/wikiengine/render_{$this->renderer}.php"); |
111 var $renderObj = array(); |
110 } |
112 var $formatObj = array(); |
111 |
113 var $path = array( |
112 $parser = new $parser_class; |
114 'parse' => array(), |
113 $renderer = new $renderer_class; |
115 'render' => array() |
114 |
116 ); |
115 // run prehooks |
117 var $_dirSep = DIRECTORY_SEPARATOR; |
116 foreach ( $this->hooks as $hook ) |
118 function Text_Wiki($rules = null) |
117 { |
119 { |
118 if ( $hook['when'] === PO_FIRST ) |
120 if (is_array($rules)) { |
119 { |
121 $this->rules = $rules; |
120 $text = call_user_func($hook['callback'], $text); |
122 } |
121 if ( !is_string($text) || empty($text) ) |
123 |
122 { |
124 global $plugins; |
123 trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING); |
125 // This code can be run from the installer, so in some cases $plugins |
124 // *sigh* |
126 // isn't initted. (Bug in 1.1.2, fixed for 1.1.3) |
125 $text = ''; |
127 if ( is_object($plugins) ) |
126 } |
128 { |
127 } |
129 $code = $plugins->setHook('text_wiki_construct'); |
128 } |
130 foreach ( $code as $cmd ) |
129 |
|
130 // perform render |
|
131 foreach ( $this->rules as $rule ) |
|
132 { |
|
133 // run prehooks |
|
134 foreach ( $this->hooks as $hook ) |
|
135 { |
|
136 if ( $hook['when'] === PO_BEFORE && $hook['rule'] === $rule ) |
|
137 { |
|
138 $text = call_user_func($hook['callback'], $text); |
|
139 if ( !is_string($text) || empty($text) ) |
131 { |
140 { |
132 eval($cmd); |
141 trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING); |
|
142 // *sigh* |
|
143 $text = ''; |
133 } |
144 } |
134 } |
145 } |
135 |
146 } |
136 $this->addPath( |
147 |
137 'parse', |
148 // execute rule |
138 $this->fixPath(ENANO_ROOT) . 'includes/wikiengine/Parse/Default/' |
149 $text = $this->perform_render_step($text, $rule, $parser, $renderer); |
139 ); |
150 |
140 $this->addPath( |
151 // run posthooks |
141 'render', |
152 foreach ( $this->hooks as $hook ) |
142 $this->fixPath(ENANO_ROOT) . 'includes/wikiengine/Render/' |
153 { |
143 ); |
154 if ( $hook['when'] === PO_AFTER && $hook['rule'] === $rule ) |
144 |
155 { |
145 } |
156 $text = call_user_func($hook['callback'], $text); |
146 |
157 if ( !is_string($text) || empty($text) ) |
147 public static function singleton($parser = 'Default', $rules = null) |
158 { |
148 { |
159 trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING); |
149 static $only = array(); |
160 // *sigh* |
150 if (!isset($only[$parser])) { |
161 $text = ''; |
151 $ret = Text_Wiki::factory($parser, $rules); |
|
152 if (!$ret) { |
|
153 return $ret; |
|
154 } |
|
155 $only[$parser] =& $ret; |
|
156 } |
|
157 return $only[$parser]; |
|
158 } |
|
159 |
|
160 public static function factory($parser = 'Default', $rules = null) |
|
161 { |
|
162 $d=getcwd(); |
|
163 chdir(ENANO_ROOT); |
|
164 |
|
165 $class = 'Text_Wiki_' . $parser; |
|
166 $c2 = $parser; |
|
167 $file = ENANO_ROOT . '/includes/wikiengine/' . str_replace('_', '/', $c2).'.php'; |
|
168 if (!class_exists($class)) { |
|
169 $fp = @fopen($file, 'r', true); |
|
170 if ($fp === false) { |
|
171 die_semicritical('Wiki formatting engine error', '<p>Could not find file '.$file.' in include_path</p>'); |
|
172 } |
|
173 fclose($fp); |
|
174 include_once($file); |
|
175 if (!class_exists($class)) { |
|
176 die_semicritical('Wiki formatting engine error', '<p>Class '.$class.' does not exist after including '.$file.'</p>'); |
|
177 } |
|
178 } |
|
179 |
|
180 chdir($d); |
|
181 |
|
182 $obj = new $class($rules); |
|
183 return $obj; |
|
184 } |
|
185 |
|
186 function setParseConf($rule, $arg1, $arg2 = null) |
|
187 { |
|
188 $rule = ucwords(strtolower($rule)); |
|
189 |
|
190 if (! isset($this->parseConf[$rule])) { |
|
191 $this->parseConf[$rule] = array(); |
|
192 } |
|
193 |
|
194 if (is_array($arg1)) { |
|
195 $this->parseConf[$rule] = $arg1; |
|
196 } else { |
|
197 $this->parseConf[$rule][$arg1] = $arg2; |
|
198 } |
|
199 } |
|
200 |
|
201 function getParseConf($rule, $key = null) |
|
202 { |
|
203 $rule = ucwords(strtolower($rule)); |
|
204 |
|
205 if (! isset($this->parseConf[$rule])) { |
|
206 return null; |
|
207 } |
|
208 |
|
209 if (is_null($key)) { |
|
210 return $this->parseConf[$rule]; |
|
211 } |
|
212 |
|
213 if (isset($this->parseConf[$rule][$key])) { |
|
214 return $this->parseConf[$rule][$key]; |
|
215 } else { |
|
216 return null; |
|
217 } |
|
218 } |
|
219 |
|
220 function setRenderConf($format, $rule, $arg1, $arg2 = null) |
|
221 { |
|
222 $format = ucwords(strtolower($format)); |
|
223 $rule = ucwords(strtolower($rule)); |
|
224 |
|
225 if (! isset($this->renderConf[$format])) { |
|
226 $this->renderConf[$format] = array(); |
|
227 } |
|
228 |
|
229 if (! isset($this->renderConf[$format][$rule])) { |
|
230 $this->renderConf[$format][$rule] = array(); |
|
231 } |
|
232 |
|
233 if (is_array($arg1)) { |
|
234 $this->renderConf[$format][$rule] = $arg1; |
|
235 } else { |
|
236 $this->renderConf[$format][$rule][$arg1] = $arg2; |
|
237 } |
|
238 } |
|
239 |
|
240 function getRenderConf($format, $rule, $key = null) |
|
241 { |
|
242 $format = ucwords(strtolower($format)); |
|
243 $rule = ucwords(strtolower($rule)); |
|
244 |
|
245 if (! isset($this->renderConf[$format]) || |
|
246 ! isset($this->renderConf[$format][$rule])) { |
|
247 return null; |
|
248 } |
|
249 |
|
250 if (is_null($key)) { |
|
251 return $this->renderConf[$format][$rule]; |
|
252 } |
|
253 |
|
254 if (isset($this->renderConf[$format][$rule][$key])) { |
|
255 return $this->renderConf[$format][$rule][$key]; |
|
256 } else { |
|
257 return null; |
|
258 } |
|
259 |
|
260 } |
|
261 |
|
262 function setFormatConf($format, $arg1, $arg2 = null) |
|
263 { |
|
264 if (! is_array($this->formatConf[$format])) { |
|
265 $this->formatConf[$format] = array(); |
|
266 } |
|
267 |
|
268 if (is_array($arg1)) { |
|
269 $this->formatConf[$format] = $arg1; |
|
270 } else { |
|
271 $this->formatConf[$format][$arg1] = $arg2; |
|
272 } |
|
273 } |
|
274 |
|
275 function getFormatConf($format, $key = null) |
|
276 { |
|
277 if (! isset($this->formatConf[$format])) { |
|
278 return null; |
|
279 } |
|
280 |
|
281 if (is_null($key)) { |
|
282 return $this->formatConf[$format]; |
|
283 } |
|
284 |
|
285 if (isset($this->formatConf[$format][$key])) { |
|
286 return $this->formatConf[$format][$key]; |
|
287 } else { |
|
288 return null; |
|
289 } |
|
290 } |
|
291 |
|
292 function insertRule($name, $tgt = null) |
|
293 { |
|
294 $name = ucwords(strtolower($name)); |
|
295 if (! is_null($tgt)) { |
|
296 $tgt = ucwords(strtolower($tgt)); |
|
297 } |
|
298 if (in_array($name, $this->rules)) { |
|
299 return null; |
|
300 } |
|
301 |
|
302 if (! is_null($tgt) && $tgt != '' && |
|
303 ! in_array($tgt, $this->rules)) { |
|
304 return false; |
|
305 } |
|
306 |
|
307 if (is_null($tgt)) { |
|
308 $this->rules[] = $name; |
|
309 return true; |
|
310 } |
|
311 |
|
312 if ($tgt == '') { |
|
313 array_unshift($this->rules, $name); |
|
314 return true; |
|
315 } |
|
316 |
|
317 $tmp = $this->rules; |
|
318 $this->rules = array(); |
|
319 |
|
320 foreach ($tmp as $val) { |
|
321 $this->rules[] = $val; |
|
322 if ($val == $tgt) { |
|
323 $this->rules[] = $name; |
|
324 } |
|
325 } |
|
326 |
|
327 return true; |
|
328 } |
|
329 |
|
330 function deleteRule($name) |
|
331 { |
|
332 $name = ucwords(strtolower($name)); |
|
333 $key = array_search($name, $this->rules); |
|
334 if ($key !== false) { |
|
335 unset($this->rules[$key]); |
|
336 } |
|
337 } |
|
338 |
|
339 function changeRule($old, $new) |
|
340 { |
|
341 $old = ucwords(strtolower($old)); |
|
342 $new = ucwords(strtolower($new)); |
|
343 $key = array_search($old, $this->rules); |
|
344 if ($key !== false) { |
|
345 $this->deleteRule($new); |
|
346 $this->rules[$key] = $new; |
|
347 } |
|
348 } |
|
349 |
|
350 function enableRule($name) |
|
351 { |
|
352 $name = ucwords(strtolower($name)); |
|
353 $key = array_search($name, $this->disable); |
|
354 if ($key !== false) { |
|
355 unset($this->disable[$key]); |
|
356 } |
|
357 } |
|
358 |
|
359 function disableRule($name) |
|
360 { |
|
361 $name = ucwords(strtolower($name)); |
|
362 $key = array_search($name, $this->disable); |
|
363 if ($key === false) { |
|
364 $this->disable[] = $name; |
|
365 } |
|
366 } |
|
367 |
|
368 function transform($text, $format = 'Xhtml') |
|
369 { |
|
370 $this->parse($text); |
|
371 return $this->render($format); |
|
372 } |
|
373 |
|
374 function parse($text) |
|
375 { |
|
376 $this->source = $text; |
|
377 |
|
378 $this->tokens = array(); |
|
379 $this->_countRulesTokens = array(); |
|
380 |
|
381 foreach ($this->rules as $name) { |
|
382 if (! in_array($name, $this->disable)) { |
|
383 $this->loadParseObj($name); |
|
384 |
|
385 if (is_object($this->parseObj[$name])) { |
|
386 $this->parseObj[$name]->parse(); |
|
387 } |
162 } |
388 // For debugging |
163 } |
389 // echo('<p>' . $name . ':</p><pre>'.htmlspecialchars($this->source).'</pre>'); |
164 } |
390 } |
165 } |
391 } |
166 |
392 } |
167 // run posthooks |
393 |
168 foreach ( $this->hooks as $hook ) |
394 function render($format = 'Xhtml') |
169 { |
395 { |
170 if ( $hook['when'] === PO_LAST ) |
396 $format = ucwords(strtolower($format)); |
171 { |
397 |
172 $text = call_user_func($hook['callback'], $text); |
398 $output = ''; |
173 if ( !is_string($text) || empty($text) ) |
399 |
174 { |
400 $in_delim = false; |
175 trigger_error("Hook returned empty/invalid text: " . print_r($hook['callback'], true), E_USER_WARNING); |
401 |
176 // *sigh* |
402 $key = ''; |
177 $text = ''; |
403 |
178 } |
404 $result = $this->loadFormatObj($format); |
179 } |
405 if ($this->isError($result)) { |
180 } |
406 return $result; |
181 |
407 } |
182 return (( defined('ENANO_DEBUG') && isset($_GET['parserdebug']) ) ? '<pre>' . htmlspecialchars($text) . '</pre>' : $text) . "\n\n"; |
|
183 } |
|
184 |
|
185 /** |
|
186 * Performs a step in the rendering process. |
|
187 * @param string Text to render |
|
188 * @param string Rule to execute |
|
189 * @param object Parser instance |
|
190 * @param object Renderer instance |
|
191 * @return string |
|
192 * @access private |
|
193 */ |
|
194 |
|
195 private function perform_render_step($text, $rule, $parser, $renderer) |
|
196 { |
|
197 // First look for a direct function |
|
198 if ( function_exists("parser_{$this->parser}_{$this->renderer}_{$rule}") ) |
|
199 { |
|
200 return call_user_func("parser_{$this->parser}_{$this->renderer}_{$rule}", $text, $this->flags); |
|
201 } |
|
202 |
|
203 // We don't have that, so start looking for other ways or means of doing this |
|
204 if ( method_exists($parser, $rule) && method_exists($renderer, $rule) ) |
|
205 { |
|
206 // Both the parser and render have callbacks they want to use. |
|
207 $pieces = $parser->$rule($text); |
|
208 $text = call_user_func(array($renderer, $rule), $text, $pieces); |
|
209 } |
|
210 else if ( method_exists($parser, $rule) && !method_exists($renderer, $rule) && isset($renderer->rules[$rule]) ) |
|
211 { |
|
212 // The parser has a callback, but the renderer does not |
|
213 $pieces = $parser->$rule($text); |
|
214 $text = $this->generic_render($text, $pieces, $renderer->rules[$rule]); |
|
215 } |
|
216 else if ( !method_exists($parser, $rule) && isset($parser->rules[$rule]) && method_exists($renderer, $rule) ) |
|
217 { |
|
218 // The parser has no callback, but the renderer does |
|
219 $text = preg_replace_callback($parser->rules[$rule], array($renderer, $rule), $text); |
|
220 } |
|
221 else if ( isset($parser->rules[$rule]) && isset($renderer->rules[$rule]) ) |
|
222 { |
|
223 // This is a straight-up regex only rule |
|
224 $text = preg_replace($parser->rules[$rule], $renderer->rules[$rule], $text); |
|
225 } |
|
226 else |
|
227 { |
|
228 // Either the renderer or parser does not support this rule, ignore it |
|
229 } |
|
230 |
|
231 return $text; |
|
232 } |
|
233 |
|
234 /** |
|
235 * Generic renderer |
|
236 * @access protected |
|
237 */ |
|
238 |
|
239 protected function generic_render($text, $pieces, $rule) |
|
240 { |
|
241 foreach ( $pieces as $i => $piece ) |
|
242 { |
|
243 $replacement = $rule; |
408 |
244 |
409 if (is_object($this->formatObj[$format])) { |
245 // if the piece is an array, replace $1, $2, etc. in the rule with each value in the piece |
410 $output .= $this->formatObj[$format]->pre(); |
246 if ( is_array($piece) ) |
411 } |
247 { |
412 |
248 $j = 0; |
413 foreach (array_keys($this->_countRulesTokens) as $rule) { |
249 foreach ( $piece as $part ) |
414 $this->loadRenderObj($format, $rule); |
250 { |
|
251 $j++; |
|
252 $replacement = str_replace(array("\\$j", "\${$j}"), $part, $replacement); |
|
253 } |
|
254 } |
|
255 // else, just replace \\1 or $1 in the rule with the piece |
|
256 else |
|
257 { |
|
258 $replacement = str_replace(array("\\1", "\$1"), $piece, $replacement); |
415 } |
259 } |
416 |
260 |
417 $k = strlen($this->source); |
261 $text = str_replace(self::generate_token($i), $replacement, $text); |
418 for ($i = 0; $i < $k; $i++) { |
262 } |
419 |
263 |
420 $char = $this->source{$i}; |
264 return $text; |
421 |
265 } |
422 if ($in_delim) { |
266 |
423 |
267 /** |
424 if ($char == $this->delim) { |
268 * Add a hook into the parser. |
425 |
269 * @param callback Function to call |
426 $key = (int)$key; |
270 * @param int PO_* constant |
427 $rule = $this->tokens[$key][0]; |
271 * @param string If PO_{BEFORE,AFTER} used, rule |
428 $opts = $this->tokens[$key][1]; |
272 */ |
429 $output .= $this->renderObj[$rule]->token($opts); |
273 |
430 $in_delim = false; |
274 public function hook($callback, $when, $rule = false) |
431 |
275 { |
432 } else { |
276 if ( !is_int($when) ) |
433 |
277 return null; |
434 $key .= $char; |
278 if ( ($when == PO_BEFORE || $when == PO_AFTER) && !is_string($rule) ) |
435 |
279 return null; |
436 } |
280 if ( ( is_string($callback) && !function_exists($callback) ) || ( is_array($callback) && !method_exists($callback[0], $callback[1]) ) || ( !is_string($callback) && !is_array($callback) ) ) |
437 |
281 { |
438 } else { |
282 trigger_error("Attempt to hook with undefined function/method " . print_r($callback, true), E_USER_ERROR); |
439 |
283 return null; |
440 if ($char == $this->delim) { |
284 } |
441 $key = ''; |
285 |
442 $in_delim = true; |
286 $this->hooks[] = array( |
443 } else { |
287 'callback' => $callback, |
444 $output .= $char; |
288 'when' => $when, |
445 } |
289 'rule' => $rule |
446 } |
290 ); |
447 } |
291 } |
448 |
292 |
449 if (is_object($this->formatObj[$format])) { |
293 /** |
450 $output .= $this->formatObj[$format]->post(); |
294 * Generate a token |
451 } |
295 * @param int Token index |
452 |
296 * @return string |
453 return $output; |
297 * @static |
454 } |
298 */ |
455 |
299 |
456 function getSource() |
300 public static function generate_token($i) |
457 { |
301 { |
458 return $this->source; |
302 return self::PARSER_TOKEN . $i . self::PARSER_TOKEN; |
459 } |
303 } |
460 |
304 |
461 function getTokens($rules = null) |
305 /** |
462 { |
306 * Tokenize string |
463 if (is_null($rules)) { |
307 * @param string |
464 return $this->tokens; |
308 * @param array Matches |
465 } else { |
309 * @return string |
466 settype($rules, 'array'); |
310 * @static |
467 $result = array(); |
311 */ |
468 foreach ($this->tokens as $key => $val) { |
312 |
469 if (in_array($val[0], $rules)) { |
313 public static function tokenize($text, $matches) |
470 $result[$key] = $val; |
314 { |
471 } |
315 $matches = array_values($matches); |
472 } |
316 foreach ( $matches as $i => $match ) |
473 return $result; |
317 { |
474 } |
318 $text = str_replace_once($match, self::generate_token($i), $text); |
475 } |
319 } |
476 |
320 |
477 function addToken($rule, $options = array(), $id_only = false) |
321 return $text; |
478 { |
322 } |
479 static $id; |
|
480 if (! isset($id)) { |
|
481 $id = 0; |
|
482 } else { |
|
483 $id ++; |
|
484 } |
|
485 |
|
486 settype($options, 'array'); |
|
487 |
|
488 $this->tokens[$id] = array( |
|
489 0 => $rule, |
|
490 1 => $options |
|
491 ); |
|
492 if (!isset($this->_countRulesTokens[$rule])) { |
|
493 $this->_countRulesTokens[$rule] = 1; |
|
494 } else { |
|
495 ++$this->_countRulesTokens[$rule]; |
|
496 } |
|
497 |
|
498 if ($id_only) { |
|
499 return $id; |
|
500 } else { |
|
501 return $this->delim . $id . $this->delim; |
|
502 } |
|
503 } |
|
504 |
|
505 function setToken($id, $rule, $options = array()) |
|
506 { |
|
507 $oldRule = $this->tokens[$id][0]; |
|
508 $this->tokens[$id] = array( |
|
509 0 => $rule, |
|
510 1 => $options |
|
511 ); |
|
512 if ($rule != $oldRule) { |
|
513 if (!($this->_countRulesTokens[$oldRule]--)) { |
|
514 unset($this->_countRulesTokens[$oldRule]); |
|
515 } |
|
516 if (!isset($this->_countRulesTokens[$rule])) { |
|
517 $this->_countRulesTokens[$rule] = 1; |
|
518 } else { |
|
519 ++$this->_countRulesTokens[$rule]; |
|
520 } |
|
521 } |
|
522 } |
|
523 |
|
524 function loadParseObj($rule) |
|
525 { |
|
526 $rule = ucwords(strtolower($rule)); |
|
527 $file = $rule . '.php'; |
|
528 $class = "Text_Wiki_Parse_$rule"; |
|
529 |
|
530 if (! class_exists($class)) { |
|
531 $loc = $this->findFile('parse', $file); |
|
532 if ($loc) { |
|
533 include_once $loc; |
|
534 } else { |
|
535 $this->parseObj[$rule] = null; |
|
536 return $this->error( |
|
537 "Parse rule '$rule' not found" |
|
538 ); |
|
539 } |
|
540 } |
|
541 |
|
542 $this->parseObj[$rule] = new $class($this); |
|
543 |
|
544 } |
|
545 |
|
546 function loadRenderObj($format, $rule) |
|
547 { |
|
548 $format = ucwords(strtolower($format)); |
|
549 $rule = ucwords(strtolower($rule)); |
|
550 $file = "$format/$rule.php"; |
|
551 $class = "Text_Wiki_Render_$format" . "_$rule"; |
|
552 |
|
553 if (! class_exists($class)) { |
|
554 $loc = $this->findFile('render', $file); |
|
555 if ($loc) { |
|
556 include_once $loc; |
|
557 } else { |
|
558 return $this->error( |
|
559 "Render rule '$rule' in format '$format' not found" |
|
560 ); |
|
561 } |
|
562 } |
|
563 |
|
564 $this->renderObj[$rule] = new $class($this); |
|
565 } |
|
566 |
|
567 function loadFormatObj($format) |
|
568 { |
|
569 $format = ucwords(strtolower($format)); |
|
570 $file = $format . '.php'; |
|
571 $class = "Text_Wiki_Render_$format"; |
|
572 |
|
573 if (! class_exists($class)) { |
|
574 $loc = $this->findFile('render', $file); |
|
575 if ($loc) { |
|
576 include_once $loc; |
|
577 } else { |
|
578 return $this->error( |
|
579 "Rendering format class '$class' not found" |
|
580 ); |
|
581 } |
|
582 } |
|
583 |
|
584 $this->formatObj[$format] = new $class($this); |
|
585 } |
|
586 |
|
587 function addPath($type, $dir) |
|
588 { |
|
589 $dir = $this->fixPath($dir); |
|
590 if (! isset($this->path[$type])) { |
|
591 $this->path[$type] = array($dir); |
|
592 } else { |
|
593 array_unshift($this->path[$type], $dir); |
|
594 } |
|
595 } |
|
596 |
|
597 function getPath($type = null) |
|
598 { |
|
599 if (is_null($type)) { |
|
600 return $this->path; |
|
601 } elseif (! isset($this->path[$type])) { |
|
602 return array(); |
|
603 } else { |
|
604 return $this->path[$type]; |
|
605 } |
|
606 } |
|
607 |
|
608 function findFile($type, $file) |
|
609 { |
|
610 $set = $this->getPath($type); |
|
611 |
|
612 foreach ($set as $path) { |
|
613 $fullname = $path . $file; |
|
614 if (file_exists($fullname) && is_readable($fullname)) { |
|
615 return $fullname; |
|
616 } |
|
617 } |
|
618 |
|
619 return false; |
|
620 } |
|
621 |
|
622 function fixPath($path) |
|
623 { |
|
624 $len = strlen($this->_dirSep); |
|
625 |
|
626 if (! empty($path) && |
|
627 substr($path, -1 * $len, $len) != $this->_dirSep) { |
|
628 return $path . $this->_dirSep; |
|
629 } else { |
|
630 return $path; |
|
631 } |
|
632 } |
|
633 |
|
634 function &error($message) |
|
635 { |
|
636 die($message); |
|
637 } |
|
638 |
|
639 function isError(&$obj) |
|
640 { |
|
641 return ( @get_class($obj) == 'PEAR_Error' ); |
|
642 } |
|
643 } |
323 } |
644 |
324 |
645 ?> |
|