includes/wikiformat.php
changeset 1027 98c052fc3337
parent 801 eb8b23f11744
child 1031 8a4b75e73137
equal deleted inserted replaced
1026:f0431eb8161e 1027:98c052fc3337
    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 ?>