Public feed
#599722
<?/* * 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");?>