Public feed #599722

Callback and udata references pr

public php v1 · immutable
Raw Edit
·published 2007-07-02 12:19 UTC
rendered paste body
<?/* * Simple HTML 4.01 parser class for PHP * * Copyright (c) 2007, Matthew Mondor * ALL RIGHTS RESERVED. */class HTMLParser{        private $str = '';        private $str_len = 0;        private $str_i = 0;        private $callback = NULL;        private $udata = NULL;        private $state = NULL;        private $tag = NULL;        private $tag_len = 0;        private $attribute = NULL;        private $attributes = array();        private $last_free = NULL;        public function __construct($str, $function, &$udata)        {                $this->str = $str;                $this->str_len = strlen($str);                $this->callback = $function;                $this->udata = &$udata;        }        public function parse()        {                $this->state = 'state_0';                for ($this->str_i = 0; $this->str_i < $this->str_len; ) {                        if (($x = $this->{$this->state}()) !== NULL)                                return $x;                }                /* Send any remaining data */                if ($this->last_free < $this->str_i) {                        $s = trim(substr($this->str, $this->last_free,                            $this->str_i - $this->last_free));                        if (strlen($s) > 0) {                                $o = array(                                    'type' => 'content',                                    'content' => $s);                                $f = &$this->callback;                                if (($r = $f($o, $this->udata)) != NULL)                                        return $r;                        }                }                /* NOTREACHED */                return NULL;        }        private function state_0()        {                $this->last_free = $this->str_i;                /* Free-run until '<', switch to state 1 if found */                for (; $this->str_i < $this->str_len; $this->str_i++) {                        if ($this->str{$this->str_i} == '<') {                                if ($this->last_free < $this->str_i) {                                        $s = trim(substr($this->str,                                            $this->last_free, $this->str_i -                                            $this->last_free));                                        if (strlen($s) > 0) {                                                $o = array(                                                    'type' => 'content',                                                    'content' => $s);                                                $f = &$this->callback;                                                if (($r = $f($o, $this->udata))                                                    != NULL)                                                        return $r;                                        }                                }                                $this->state = 'state_1';                                return NULL;                        }                }                return NULL;        }        private function state_1()        {                /*                 * Skip comment if any;                 * error if reaching EOF before comment ends                 */                if ($this->str_i <= $this->str_len - 4 &&                    ($i = strpos($this->str, '<!--', $this->str_i)) !== FALSE                    && $i == $this->str_i) {                        /* Was a comment, switch back to state 0 */                        $this->str_i += 4;                        if (($i = strpos($this->str, '-->', $this->str_i))                            === FALSE)                                return "Expected '-->' but reached EOF";                        $this->str_i = $i + 3;                        $this->state = 'state_0';                        return NULL;                }                /* Not a comment, switch to state 2 */                $this->state = 'state_2';                return NULL;        }        private function state_2()        {                /* Tag must start with '<' */                if ($this->str{$this->str_i} != '<')                        return "Expected '<' at position {$this->str_i}";                /* Tag start, switch to state 3 */                $this->str_i++;                $this->state = 'state_3';                return NULL;        }        private function state_3()        {                /* Load tag name */                for ($this->tag = '', $this->tag_len = 0;                    $this->str_i < $this->str_len &&                    !ctype_space($this->str{$this->str_i}) &&                    $this->str{$this->str_i} != '>';                    $this->str_i++, $this->tag_len++)                        $this->tag .= $this->str{$this->str_i};                if ($this->tag_len == 0)                        return "Expected tag name at position {$this->str_i}";                /* Non-empty tag name, switch to state 4 */                $this->attributes = array();                $this->state = 'state_4';                return NULL;        }        private function state_4()        {                /* Skip whitespaces */                for (; $this->str_i < $this->str_len &&                     ctype_space($this->str{$this->str_i}); $this->str_i++) ;                /*                 * Upon end of tag, call the user function with the tag                 * and switch to state 0.                 */                if ($this->str{$this->str_i} == '>') {                        $this->str_i++;                        $o = array(                            'type' => 'tag',                            'tag' => array(                                'name' => strtolower($this->tag),                                'attributes' => $this->attributes));                        $f = &$this->callback;                        if (($r = $f($o, $this->udata)) != NULL)                                return $r;                        $this->state = 'state_0';                        return NULL;                }                /* Load attribute name */                for ($this->attribute = '';                    $this->str_i < $this->str_len &&                    !ctype_space($this->str{$this->str_i}) &&                    $this->str{$this->str_i} != '>';                    $this->str_i++)                        $this->attribute .= $this->str{$this->str_i};                if (strlen($this->attribute) == 0)                        return 'Expected attribute name at position ' .                         "{$this->str_i}";                /* Skip whitespaces */                for (; $this->str_i < $this->str_len &&                     ctype_space($this->str{$this->str_i}); $this->str_i++) ;                /*                 * Nonempty attribute name, is it assigned a value?                 * Switch to state 4 if empty, or 5 to assign it.                 */                if ($this->str{$this->str_i} != '=') {                        $this->attributes[strtolower($this->attribute)] = NULL;                        $this->state = 'state_4';                        return NULL;                }                $this->str_i++;                $this->state = 'state_5';                return NULL;        }        private function state_5()        {                /* Skip whitespaces */                for (; $this->str_i < $this->str_len &&                     ctype_space($this->str{$this->str_i}); $this->str_i++) ;                /* Assignment must start with '"' */                if ($this->str{$this->str_i} != '"')                        return "Expected '\"' at position {$this->str_i}";                $this->str_i++;                /*                 * Find ending '"', error if EOF;  Assign attribute and switch                 * back to state 4.                 */                if (($i = strpos($this->str, '"', $this->str_i)) === FALSE) {                        --$this->str_i;                        return "Expected closing '\"' to match position " .                            "{$this->str_i} but reached EOF";                }                if ($i > $this->str_i) {                        $this->attributes[strtolower($this->attribute)] =                            substr($this->str, $this->str_i,                            ($i - $this->str_i));                        $this->str_i = $i + 1;                } else {                        $this->attributes[strtolower($this->attribute)] = NULL;                        $this->str_i++;                }                $this->state = 'state_4';                return NULL;        }}/* * Test the parser */function po_callback(&$o, &$udata){        print_r($o);        /*        if ($o['type'] == 'content')                print($o['content']);        else {                print("<{$o['tag']['name']}");                $a = $o['tag']['attributes'];                foreach($a as $k => $v) {                        if ($v != NULL)                                print(" {$k}=\"{$v}\"");                        else                                print(" {$k}");                }                print('>');                $udata['tags']++;        }        */        $udata['tags']++;        return NULL;}$p_udata = array();$p_udata['tags'] = 0;$po = new HTMLParser(file_get_contents('bookmarks.html'), 'po_callback',    $p_udata);print($po->parse() . "\n");print("Tags: {$p_udata['tags']}\n");?>