Changeset 93101 in spip-zone


Ignore:
Timestamp:
Nov 23, 2015, 12:05:18 PM (5 years ago)
Author:
marcimat@…
Message:

Formattage et phpdoc de la lib safehtml.
On reprend le code de https://github.com/SlikNL/SafeHTML/blob/master/SafeHTML.php
et on y applique nos modifications.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • _core_/plugins/safehtml/lib/safehtml/classes/safehtml.php

    r93092 r93101  
    11<?php
     2
    23/**
    34 * SafeHTML Parser
    45 *
    56 * @note
    6  *    Attention : quelques modifications pour PHP 5.5
     7 *     Attention : Quelques modifications pour PHP 5.5 et 7
    78 *
    8  * @package SafeHTML
    9  * @author  Roman Ivanov <thingol@mail.ru>
     9 * @package    SafeHTML
     10 * @author     Roman Ivanov <thingol@mail.ru>
    1011 * @copyright  2004-2005 Roman Ivanov
    11  * @license http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
     12 * @license    http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
    1213 * @version    1.3.7
    13  * @link    http://pixel-apes.com/safehtml/
     14 * @link       http://pixel-apes.com/safehtml/
    1415 */
    1516
     
    1920require_once(XML_HTMLSAX3 . 'HTMLSax3.php');
    2021
     22/**
     23 *
     24 * SafeHTML Parser
     25 *
     26 * This parser strips down all potentially dangerous content within HTML:
     27 * <ul>
     28 * <li>opening tag without its closing tag</li>
     29 * <li>closing tag without its opening tag</li>
     30 * <li>any of these tags: "base", "basefont", "head", "html", "body", "applet",
     31 * "object", "iframe", "frame", "frameset", "script", "layer", "ilayer", "embed",
     32 * "bgsound", "link", "meta", "style", "title", "blink", "xml" etc.</li>
     33 * <li>any of these attributes: on*, data*, dynsrc</li>
     34 * <li>javascript:/vbscript:/about: etc. protocols</li>
     35 * <li>expression/behavior etc. in styles</li>
     36 * <li>any other active content</li>
     37 * </ul>
     38 * It also tries to convert code to XHTML valid, but htmltidy is far better
     39 * solution for this task.
     40 *
     41 * <b>Example:</b>
     42 * <pre>
     43 * $parser =& new SafeHTML();
     44 * $result = $parser->parse($doc);
     45 * </pre>
     46 *
     47 * @category   HTML
     48 * @package    SafeHTML
     49 * @author     Roman Ivanov <thingol@mail.ru>
     50 * @copyright  1997-2005 Roman Ivanov
     51 * @license    http://www.debian.org/misc/bsd.license  BSD License (3 Clause)
     52 * @version    Release: @package_version@
     53 * @link       http://pear.php.net/package/SafeHTML
     54 */
    2155class SafeHTML
    2256{
    23  var $_xhtml = '';
    24  
    25  var $_counter = array();
    26  
    27  var $_stack = array();
    28  
    29  var $_dcCounter = array();
    30  
    31  var $_dcStack = array();
    32  
    33  var $_listScope = 0;
    34  
    35  var $_liStack = array();
    36 
    37  var $_protoRegexps = array();
    38  
    39  var $_cssRegexps = array();
    40 
    41  var $singleTags = array('area', 'br', 'img', 'input', 'hr', 'wbr', );
    42 
    43  var $deleteTags = array(
    44   'applet', 'base',   'basefont', 'bgsound', 'blink',  'body',
    45   'embed',  'frame',  'frameset', 'head', 'html',   'ilayer',
    46   'iframe', 'layer',  'link',  'meta', 'object', 'style',
    47   'title',  'script',
    48   );
    49 
    50  var $deleteTagsContent = array('script', 'style', 'title', 'xml', );
    51 
    52  var $protocolFiltering = 'white';
    53 
    54  var $blackProtocols = array(
    55   'about',   'chrome',  'data',    'disk',  'hcp', 
    56   'help', 'javascript', 'livescript', 'lynxcgi',  'lynxexec',
    57   'ms-help', 'ms-its',  'mhtml',   'mocha', 'opera',   
    58   'res',  'resource',   'shell',   'vbscript', 'view-source',
    59   'vnd.ms.radio',    'wysiwyg',
    60   );
    61 
    62  var $whiteProtocols = array(
    63   'ed2k',   'file', 'ftp',  'gopher', 'http',  'https',
    64   'irc',    'mailto', 'news', 'nntp', 'telnet', 'webcal',
    65   'xmpp', 'callto',
    66   );
    67 
    68  var $protocolAttributes = array(
    69   'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc', 'src',
    70   );
    71 
    72  var $cssKeywords = array(
    73   'absolute', 'behavior',    'behaviour',   'content', 'expression',
    74   'fixed', 'include-source', 'moz-binding',
    75   );
    76 
    77  var $noClose = array();
    78 
    79  var $closeParagraph = array(
    80   'address', 'blockquote', 'center', 'dd',   'dir',    'div',
    81   'dl',   'dt',   'h1',  'h2',   'h3',  'h4',
    82   'h5',   'h6',   'hr',  'isindex', 'listing',   'marquee',
    83   'menu', 'multicol',   'ol',  'p',    'plaintext', 'pre',
    84   'table',   'ul',   'xmp',
    85   );
    86 
    87  var $tableTags = array(
    88   'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
    89   'thead',   'tr',
    90   );
    91 
    92  var $listTags = array('dir', 'menu', 'ol', 'ul', 'dl', );
    93 
    94  var $attributes = array('dynsrc', 'id', 'name', );
    95 
    96  var $attributesNS = array('xml:lang', );
    97 
    98  function SafeHTML()
    99  {
    100   //making regular expressions based on Proto & CSS arrays
    101   foreach ($this->blackProtocols as $proto) {
    102    $preg = "/[\s\x01-\x1F]*";
    103    for ($i=0; $i<strlen($proto); $i++) {
    104     $preg .= $proto{$i} . "[\s\x01-\x1F]*";
    105    }
    106    $preg .= ":/i";
    107    $this->_protoRegexps[] = $preg;
    108   }
    109 
    110   foreach ($this->cssKeywords as $css) {
    111    $this->_cssRegexps[] = '/' . $css . '/i';
    112   }
    113   return true;
    114  }
    115 
    116  function _writeAttrs ($attrs, $tag = null)
    117  {
    118   if (is_array($attrs)) {
    119    foreach ($attrs as $name => $value) {
    120 
    121     $name = strtolower($name);
    122 
    123     if (strpos($name, 'on') === 0) {
    124      continue;
    125     }
    126     if (strpos($name, 'data') === 0) {
    127      continue;
    128     }
    129     if ($tag !='a' AND in_array($name, $this->attributes)) {
    130      continue;
    131     }
    132     if (!preg_match("/^[a-z0-9-]+$/i", $name)) {
    133       if (!in_array($name, $this->attributesNS))
    134       {
    135        continue;
    136       }
    137     }
    138 
    139     if (($value === TRUE) || (is_null($value))) {
    140      $value = $name;
    141     }
    142 
    143     if ($name == 'style') {
     57    /**
     58     * Storage for resulting HTML output
     59     *
     60     * @var string
     61     * @access private
     62     */
     63    var $_xhtml = '';
     64   
     65    /**
     66     * Array of counters for each tag
     67     *
     68     * @var array
     69     * @access private
     70     */
     71    var $_counter = array();
     72   
     73    /**
     74     * Stack of unclosed tags
     75     *
     76     * @var array
     77     * @access private
     78     */
     79    var $_stack = array();
     80   
     81    /**
     82     * Array of counters for tags that must be deleted with all content
     83     *
     84     * @var array
     85     * @access private
     86     */
     87    var $_dcCounter = array();
     88   
     89    /**
     90     * Stack of unclosed tags that must be deleted with all content
     91     *
     92     * @var array
     93     * @access private
     94     */
     95    var $_dcStack = array();
     96   
     97    /**
     98     * Stores level of list (ol/ul) nesting
     99     *
     100     * @var int
     101     * @access private
     102     */
     103    var $_listScope = 0;
     104   
     105    /**
     106     * Stack of unclosed list tags
     107     *
     108     * @var array
     109     * @access private
     110     */
     111    var $_liStack = array();
     112
     113    /**
     114     * Array of prepared regular expressions for protocols (schemas) matching
     115     *
     116     * @var array
     117     * @access private
     118     */
     119    var $_protoRegexps = array();
     120   
     121    /**
     122     * Array of prepared regular expressions for CSS matching
     123     *
     124     * @var array
     125     * @access private
     126     */
     127    var $_cssRegexps = array();
     128
     129    /**
     130     * Should we perform UTF7 repacking or not?
     131     *
     132     * This repacking might replace completely normal strings such as "+31-" by illegal sequences,
     133     * which cause the document to be truncated on saving to MySQL
     134     *
     135     * @var boolean
     136     * @access public
     137     */
     138    var $repackUTF7 = true;
     139
     140    /**
     141     * List of single tags ("<tag />")
     142     *
     143     * @var array
     144     * @access public
     145     */
     146    var $singleTags = array('area', 'br', 'img', 'input', 'hr', 'wbr', );
     147
     148    /**
     149     * List of dangerous tags (such tags will be deleted)
     150     *
     151     * @var array
     152     * @access public
     153     */
     154    var $deleteTags = array(
     155        'applet', 'base',   'basefont', 'bgsound', 'blink',  'body',
     156        'embed',  'frame',  'frameset', 'head',    'html',   'ilayer',
     157        'iframe', 'layer',  'link',     'meta',    'object', 'style',
     158        'title',  'script',
     159        );
     160
     161    /**
     162     * List of dangerous tags (such tags will be deleted, and all content
     163     * inside this tags will be also removed)
     164     *
     165     * @var array
     166     * @access public
     167     */
     168    var $deleteTagsContent = array('script', 'style', 'title', 'xml', );
     169
     170    /**
     171     * Type of protocols filtering ('white' or 'black')
     172     *
     173     * @var string
     174     * @access public
     175     */
     176    var $protocolFiltering = 'white';
     177
     178    /**
     179     * List of "dangerous" protocols (used for blacklist-filtering)
     180     *
     181     * @var array
     182     * @access public
     183     */
     184    var $blackProtocols = array(
     185        'about',   'chrome',     'data',       'disk',     'hcp',     
     186        'help',    'javascript', 'livescript', 'lynxcgi',  'lynxexec',
     187        'ms-help', 'ms-its',     'mhtml',      'mocha',    'opera',   
     188        'res',     'resource',   'shell',      'vbscript', 'view-source',
     189        'vnd.ms.radio',          'wysiwyg',
     190        );
     191
     192    /**
     193     * List of "safe" protocols (used for whitelist-filtering)
     194     *
     195     * @var array
     196     * @access public
     197     */
     198    var $whiteProtocols = array(
     199        'ed2k',   'file', 'ftp',  'gopher', 'http',  'https',
     200        'irc',    'mailto', 'news', 'nntp', 'telnet', 'webcal',
     201        'xmpp',   'callto',
     202        );
     203
     204    /**
     205     * List of attributes that can contain protocols
     206     *
     207     * @var array
     208     * @access public
     209     */
     210    var $protocolAttributes = array(
     211        'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc', 'src',
     212        );
     213
     214    /**
     215     * List of dangerous CSS keywords
     216     *
     217     * Whole style="" attribute will be removed, if parser will find one of
     218     * these keywords
     219     *
     220     * @var array
     221     * @access public
     222     */
     223    var $cssKeywords = array(
     224        'absolute', 'behavior',       'behaviour',   'content', 'expression',
     225        'fixed',    'include-source', 'moz-binding',
     226        );
     227
     228    /**
     229     * List of tags that can have no "closing tag"
     230     *
     231     * @var array
     232     * @access public
     233     * @deprecated XHTML does not allow such tags
     234     */
     235    var $noClose = array();
     236
     237    /**
     238     * List of block-level tags that terminates paragraph
     239     *
     240     * Paragraph will be closed when this tags opened
     241     *
     242     * @var array
     243     * @access public
     244     */
     245    var $closeParagraph = array(
     246        'address', 'blockquote', 'center', 'dd',      'dir',       'div',
     247        'dl',      'dt',         'h1',     'h2',      'h3',        'h4',
     248        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee',
     249        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre',
     250        'table',   'ul',         'xmp',
     251        );
     252
     253    /**
     254     * List of table tags, all table tags outside a table will be removed
     255     *
     256     * @var array
     257     * @access public
     258     */
     259    var $tableTags = array(
     260        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
     261        'thead',   'tr',
     262        );
     263
     264    /**
     265     * List of list tags
     266     *
     267     * @var array
     268     * @access public
     269     */
     270    var $listTags = array('dir', 'menu', 'ol', 'ul', 'dl', );
     271
     272    /**
     273     * List of dangerous attributes
     274     *
     275     * @var array
     276     * @access public
     277     */
     278    var $attributes = array('dynsrc', 'id', 'name', );
     279
     280    /**
     281     * List of allowed "namespaced" attributes
     282     *
     283     * @var array
     284     * @access public
     285     */
     286    var $attributesNS = array('xml:lang', );
     287
     288    /**
     289     * Constructs class
     290     *
     291     * @access public
     292     */
     293    function SafeHTML()
     294    {
     295        //making regular expressions based on Proto & CSS arrays
     296        foreach ($this->blackProtocols as $proto) {
     297            $preg = "/[\s\x01-\x1F]*";
     298            for ($i=0; $i<strlen($proto); $i++) {
     299                $preg .= $proto{$i} . "[\s\x01-\x1F]*";
     300            }
     301            $preg .= ":/i";
     302            $this->_protoRegexps[] = $preg;
     303        }
     304
     305        foreach ($this->cssKeywords as $css) {
     306            $this->_cssRegexps[] = '/' . $css . '/i';
     307        }
     308        return true;
     309    }
     310
     311    /**
     312     * Handles the writing of attributes - called from $this->_openHandler()
     313     *
     314     * @param array $attrs array of attributes $name => $value
     315     * @return boolean
     316     * @access private
     317     */
     318    function _writeAttrs ($attrs, $tag = null)
     319    {
     320        if (is_array($attrs)) {
     321            foreach ($attrs as $name => $value) {
     322
     323                $name = strtolower($name);
     324
     325                if (strpos($name, 'on') === 0) {
     326                    continue;
     327                }
     328                if (strpos($name, 'data') === 0) {
     329                    continue;
     330                }
     331                if ($tag != 'a' and in_array($name, $this->attributes)) {
     332                    continue;
     333                }
     334                if (!preg_match("/^[a-z0-9]+$/i", $name)) {
     335                    if (!in_array($name, $this->attributesNS))
     336                    {
     337                        continue;
     338                    }
     339                }
     340
     341                if (($value === TRUE) || (is_null($value))) {
     342                    $value = $name;
     343                }
     344
     345                if ($name == 'style') {
    144346                   
    145347                   // removes insignificant backslahes
    146        $value = str_replace("\\", '', $value);
     348                   $value = str_replace("\\", '', $value);
    147349
    148350                   // removes CSS comments
     
    155357                   
    156358                   // replace all & to &amp;
    157        $value = str_replace('&amp;', '&', $value);
    158        $value = str_replace('&', '&amp;', $value);
    159 
    160        foreach ($this->_cssRegexps as $css) {
    161         if (preg_match($css, $value)) {
    162          continue 2;
    163         }
     359                   $value = str_replace('&amp;', '&', $value);
     360                   $value = str_replace('&', '&amp;', $value);
     361
     362                   foreach ($this->_cssRegexps as $css) {
     363                       if (preg_match($css, $value)) {
     364                           continue 2;
     365                       }
     366                   }
     367                   foreach ($this->_protoRegexps as $proto) {
     368                       if (preg_match($proto, $value)) {
     369                           continue 2;
     370                       }
     371                   }
     372                }
     373
     374                $tempval = preg_replace_callback('/&#(\d+);?/m', create_function('$m', 'return chr($m[1]);'), $value); //"'
     375                $tempval = preg_replace_callback('/&#x([0-9a-f]+);?/mi', create_function('$m', 'return chr(hexdec($m[1]));'), $tempval); //"'
     376
     377                if ((in_array($name, $this->protocolAttributes)) &&
     378                    (strpos($tempval, ':') !== false))
     379                {
     380                    if ($this->protocolFiltering == 'black') {
     381                        foreach ($this->_protoRegexps as $proto) {
     382                            if (preg_match($proto, $tempval)) continue 2;
     383                        }
     384                    } else {
     385                        $_tempval = explode(':', $tempval);
     386                        $proto = $_tempval[0];
     387                        if (!in_array($proto, $this->whiteProtocols)) {
     388                            continue;
     389                        }
     390                    }
     391                }
     392
     393                $value = str_replace("\"", "&quot;", $value);
     394                $this->_xhtml .= ' ' . $name . '="' . $value . '"';
     395            }
     396        }
     397        return true;
     398    }
     399
     400    /**
     401     * Opening tag handler - called from HTMLSax
     402     *
     403     * @param object $parser HTML Parser
     404     * @param string $name   tag name
     405     * @param array  $attrs  tag attributes
     406     * @return boolean
     407     * @access private
     408     */
     409    function _openHandler(&$parser, $name, $attrs)
     410    {
     411        $name = strtolower($name);
     412
     413        if (in_array($name, $this->deleteTagsContent)) {
     414            array_push($this->_dcStack, $name);
     415            $this->_dcCounter[$name] = isset($this->_dcCounter[$name]) ? $this->_dcCounter[$name]+1 : 1;
     416        }
     417        if (count($this->_dcStack) != 0) {
     418            return true;
     419        }
     420
     421        if (in_array($name, $this->deleteTags)) {
     422            return true;
     423        }
     424       
     425        if (!preg_match("/^[a-z0-9]+$/i", $name)) {
     426            if (preg_match("!(?:\@|://)!i", $name)) {
     427                $this->_xhtml .= '&lt;' . $name . '&gt;';
     428            }
     429            return true;
     430        }
     431
     432        if (in_array($name, $this->singleTags)) {
     433            $this->_xhtml .= '<' . $name;
     434            $this->_writeAttrs($attrs, $name);
     435            $this->_xhtml .= ' />';
     436            return true;
     437        }
     438
     439        // TABLES: cannot open table elements when we are not inside table
     440        if ((isset($this->_counter['table'])) && ($this->_counter['table'] <= 0)
     441            && (in_array($name, $this->tableTags)))
     442        {
     443            return true;
     444        }
     445
     446        // PARAGRAPHS: close paragraph when closeParagraph tags opening
     447        if ((in_array($name, $this->closeParagraph)) && (in_array('p', $this->_stack))) {
     448            $this->_closeHandler($parser, 'p');
     449        }
     450
     451        // LISTS: we should close <li> if <li> of the same level opening
     452        if ($name == 'li' && count($this->_liStack) &&
     453            $this->_listScope == $this->_liStack[count($this->_liStack)-1])
     454        {
     455            $this->_closeHandler($parser, 'li');
     456        }
     457
     458        // LISTS: we want to know on what nesting level of lists we are
     459        if (in_array($name, $this->listTags)) {
     460            $this->_listScope++;
     461        }
     462        if ($name == 'li') {
     463            array_push($this->_liStack, $this->_listScope);
     464        }
     465           
     466        $this->_xhtml .= '<' . $name;
     467        $this->_writeAttrs($attrs);
     468        $this->_xhtml .= '>';
     469        array_push($this->_stack,$name);
     470        $this->_counter[$name] = isset($this->_counter[$name]) ? $this->_counter[$name]+1 : 1;
     471        return true;
     472    }
     473
     474    /**
     475     * Closing tag handler - called from HTMLSax
     476     *
     477     * @param object $parsers HTML parser
     478     * @param string $name    tag name
     479     * @return boolean
     480     * @access private
     481     */
     482    function _closeHandler(&$parser, $name)
     483    {
     484
     485        $name = strtolower($name);
     486
     487        if (isset($this->_dcCounter[$name]) && ($this->_dcCounter[$name] > 0) &&
     488            (in_array($name, $this->deleteTagsContent)))
     489        {
     490           while ($name != ($tag = array_pop($this->_dcStack))) {
     491            $this->_dcCounter[$tag]--;
     492           }
     493
     494           $this->_dcCounter[$name]--;
     495        }
     496
     497        if (count($this->_dcStack) != 0) {
     498            return true;
     499        }
     500
     501        if ((isset($this->_counter[$name])) && ($this->_counter[$name] > 0)) {
     502           while ($name != ($tag = array_pop($this->_stack))) {
     503               $this->_closeTag($tag);
     504           }
     505
     506           $this->_closeTag($name);
     507        }
     508        return true;
     509    }
     510
     511    /**
     512     * Closes tag
     513     *
     514     * @param string $tag tag name
     515     * @return boolean
     516     * @access private
     517     */
     518    function _closeTag($tag)
     519    {
     520        if (!in_array($tag, $this->noClose)) {
     521            $this->_xhtml .= '</' . $tag . '>';
     522        }
     523
     524        $this->_counter[$tag]--;
     525
     526        if (in_array($tag, $this->listTags)) {
     527            $this->_listScope--;
     528        }
     529
     530        if ($tag == 'li') {
     531            array_pop($this->_liStack);
     532        }
     533        return true;
     534    }
     535
     536    /**
     537     * Character data handler - called from HTMLSax
     538     *
     539     * @param object $parser HTML parser
     540     * @param string $data   textual data
     541     * @return boolean
     542     * @access private
     543     */
     544    function _dataHandler(&$parser, $data)
     545    {
     546        if (count($this->_dcStack) == 0) {
     547            $this->_xhtml .= $data;
     548        }
     549        return true;
     550    }
     551
     552    /**
     553     * Escape handler - called from HTMLSax
     554     *
     555     * @param object $parser HTML parser
     556     * @param string $data   comments or other type of data
     557     * @return boolean
     558     * @access private
     559     */
     560    function _escapeHandler(&$parser, $data)
     561    {
     562        return true;
     563    }
     564
     565    /**
     566     * Returns the XHTML document
     567     *
     568     * @return string Processed (X)HTML document
     569     * @access public
     570     */
     571    function getXHTML ()
     572    {
     573        while ($tag = array_pop($this->_stack)) {
     574            $this->_closeTag($tag);
     575        }
     576       
     577        return $this->_xhtml;
     578    }
     579
     580    /**
     581     * Clears current document data
     582     *
     583     * @return boolean
     584     * @access public
     585     */
     586    function clear()
     587    {
     588        $this->_xhtml = '';
     589        return true;
     590    }
     591
     592    /**
     593     * Main parsing fuction
     594     *
     595     * @param string $doc HTML document for processing
     596     * @return string Processed (X)HTML document
     597     * @access public
     598     */
     599    function parse($doc)
     600    {
     601
     602       // Save all '<' symbols
     603       $doc = preg_replace("/<(?=[^a-zA-Z\/\!\?\%])/", '&lt;', $doc);
     604
     605       // Web documents shouldn't contains \x00 symbol
     606       $doc = str_replace("\x00", '', $doc);
     607
     608       // Opera6 bug workaround
     609       $doc = str_replace("\xC0\xBC", '&lt;', $doc);
     610
     611       if ($this->repackUTF7) {
     612           // UTF-7 encoding ASCII decode
     613           $doc = $this->repackUTF7($doc);
    164614       }
    165        foreach ($this->_protoRegexps as $proto) {
    166         if (preg_match($proto, $value)) {
    167          continue 2;
    168         }
    169        }
    170     }
    171 
    172     $tempval = preg_replace_callback('/&#(\d+);?/m',
    173         create_function('$m', 'return chr($m[1]);'), $value); //"'
    174     $tempval = preg_replace_callback('/&#x([0-9a-f]+);?/mi',
    175         create_function('$m', 'return chr(hexdec($m[1]));'), $tempval); //"'
    176 
    177     if ((in_array($name, $this->protocolAttributes)) &&
    178      (strpos($tempval, ':') !== false))
    179     {
    180      if ($this->protocolFiltering == 'black') {
    181       foreach ($this->_protoRegexps as $proto) {
    182        if (preg_match($proto, $tempval)) continue 2;
    183       }
    184      } else {
    185       $_tempval = explode(':', $tempval);
    186       $proto = $_tempval[0];
    187       if (!in_array($proto, $this->whiteProtocols)) {
    188        continue;
    189       }
    190      }
    191     }
    192 
    193     $value = str_replace("\"", "&quot;", $value);
    194     $this->_xhtml .= ' ' . $name . '="' . $value . '"';
    195    }
    196   }
    197   return true;
    198  }
    199 
    200  function _openHandler(&$parser, $name, $attrs)
    201  {
    202   $name = strtolower($name);
    203 
    204   if (in_array($name, $this->deleteTagsContent)) {
    205    array_push($this->_dcStack, $name);
    206    $this->_dcCounter[$name] = isset($this->_dcCounter[$name]) ? $this->_dcCounter[$name]+1 : 1;
    207   }
    208   if (count($this->_dcStack) != 0) {
    209    return true;
    210   }
    211 
    212   if (in_array($name, $this->deleteTags)) {
    213    return true;
    214   }
    215  
    216   if (!preg_match("/^[a-z0-9]+$/i", $name)) {
    217    if (preg_match("!(?:\@|://)!i", $name)) {
    218     $this->_xhtml .= '&lt;' . $name . '&gt;';
    219    }
    220    return true;
    221   }
    222 
    223   if (in_array($name, $this->singleTags)) {
    224    $this->_xhtml .= '<' . $name;
    225    $this->_writeAttrs($attrs);
    226    $this->_xhtml .= ' />';
    227    return true;
    228   }
    229 
    230   // TABLES: cannot open table elements when we are not inside table
    231   if ((isset($this->_counter['table'])) && ($this->_counter['table'] <= 0)
    232    && (in_array($name, $this->tableTags)))
    233   {
    234    return true;
    235   }
    236 
    237   // PARAGRAPHS: close paragraph when closeParagraph tags opening
    238   if ((in_array($name, $this->closeParagraph)) && (in_array('p', $this->_stack))) {
    239    $this->_closeHandler($parser, 'p');
    240   }
    241 
    242   // LISTS: we should close <li> if <li> of the same level opening
    243   if ($name == 'li' && count($this->_liStack) &&
    244    $this->_listScope == $this->_liStack[count($this->_liStack)-1])
    245   {
    246    $this->_closeHandler($parser, 'li');
    247   }
    248 
    249   // LISTS: we want to know on what nesting level of lists we are
    250   if (in_array($name, $this->listTags)) {
    251    $this->_listScope++;
    252   }
    253   if ($name == 'li') {
    254    array_push($this->_liStack, $this->_listScope);
    255   }
    256    
    257   $this->_xhtml .= '<' . $name;
    258   $this->_writeAttrs($attrs,$name);
    259   $this->_xhtml .= '>';
    260   array_push($this->_stack,$name);
    261   $this->_counter[$name] = isset($this->_counter[$name]) ? $this->_counter[$name]+1 : 1;
    262   return true;
    263  }
    264 
    265  function _closeHandler(&$parser, $name)
    266  {
    267 
    268   $name = strtolower($name);
    269 
    270   if (isset($this->_dcCounter[$name]) && ($this->_dcCounter[$name] > 0) &&
    271    (in_array($name, $this->deleteTagsContent)))
    272   {
    273      while ($name != ($tag = array_pop($this->_dcStack))) {
    274    $this->_dcCounter[$tag]--;
    275      }
    276 
    277      $this->_dcCounter[$name]--;
    278   }
    279 
    280   if (count($this->_dcStack) != 0) {
    281    return true;
    282   }
    283 
    284   if ((isset($this->_counter[$name])) && ($this->_counter[$name] > 0)) {
    285      while ($name != ($tag = array_pop($this->_stack))) {
    286       $this->_closeTag($tag);
    287      }
    288 
    289      $this->_closeTag($name);
    290   }
    291   return true;
    292  }
    293 
    294  function _closeTag($tag)
    295  {
    296   if (!in_array($tag, $this->noClose)) {
    297    $this->_xhtml .= '</' . $tag . '>';
    298   }
    299 
    300   $this->_counter[$tag]--;
    301 
    302   if (in_array($tag, $this->listTags)) {
    303    $this->_listScope--;
    304   }
    305 
    306   if ($tag == 'li') {
    307    array_pop($this->_liStack);
    308   }
    309   return true;
    310  }
    311 
    312  function _dataHandler(&$parser, $data)
    313  {
    314   if (count($this->_dcStack) == 0) {
    315    $this->_xhtml .= $data;
    316   }
    317   return true;
    318  }
    319 
    320  function _escapeHandler(&$parser, $data)
    321  {
    322   return true;
    323  }
    324 
    325  function getXHTML ()
    326  {
    327   while ($tag = array_pop($this->_stack)) {
    328    $this->_closeTag($tag);
    329   }
    330  
    331   return $this->_xhtml;
    332  }
    333 
    334  function clear()
    335  {
    336   $this->_xhtml = '';
    337   return true;
    338  }
    339 
    340  function parse($doc)
    341  {
    342 
    343     // Save all '<' symbols
    344     $doc = preg_replace("/<(?=[^a-zA-Z\/\!\?\%])/", '&lt;', $doc);
    345 
    346     // Web documents shouldn't contains \x00 symbol
    347     $doc = str_replace("\x00", '', $doc);
    348 
    349     // Opera6 bug workaround
    350     $doc = str_replace("\xC0\xBC", '&lt;', $doc);
    351 
    352     // UTF-7 encoding ASCII decode
    353     $doc = $this->repackUTF7($doc);
    354 
    355     // Instantiate the parser
    356     $parser= new XML_HTMLSax3();
    357 
    358     // Set up the parser
    359     $parser->set_object($this);
    360 
    361     $parser->set_element_handler('_openHandler','_closeHandler');
    362     $parser->set_data_handler('_dataHandler');
    363     $parser->set_escape_handler('_escapeHandler');
    364 
    365     $parser->parse($doc);
    366 
    367     return $this->getXHTML();
    368 
    369  }
    370 
     615
     616       // Instantiate the parser
     617       $parser = new XML_HTMLSax3();
     618
     619       // Set up the parser
     620       $parser->set_object($this);
     621
     622       $parser->set_element_handler('_openHandler','_closeHandler');
     623       $parser->set_data_handler('_dataHandler');
     624       $parser->set_escape_handler('_escapeHandler');
     625
     626       $parser->parse($doc);
     627
     628       return $this->getXHTML();
     629
     630    }
     631
     632
     633    /**
     634     * UTF-7 decoding fuction
     635     *
     636     * @param string $str HTML document for recode ASCII part of UTF-7 back to ASCII
     637     * @return string Decoded document
     638     * @access private
     639     */
    371640    function repackUTF7($str)
    372641    {
     
    374643    }
    375644
     645    /**
     646     * Additional UTF-7 decoding fuction
     647     *
     648     * @param string $str String for recode ASCII part of UTF-7 back to ASCII
     649     * @return string Recoded string
     650     * @access private
     651     */
    376652    function repackUTF7Callback($str)
    377653    {
     
    381657    }
    382658
     659    /**
     660     * Additional UTF-7 encoding fuction
     661     *
     662     * @param string $str String for recode ASCII part of UTF-7 back to ASCII
     663     * @return string Recoded string
     664     * @access private
     665     */
    383666    function repackUTF7Back($str)
    384667    {
     
    386669    }
    387670}
    388 
    389 ?>
Note: See TracChangeset for help on using the changeset viewer.