| // +----------------------------------------------------------------------+ // // $Id$ cInclude('pear', 'PEAR.php'); /** * XML Parser class. This is an XML parser based on PHP's "xml" extension, * based on the bundled expat library. * * @author Stig Bakken * @todo Tests that need to be made: * - error class * - mixing character encodings * - a test using all expat handlers * - options (folding, output charset) * - different parsing modes * * @notes - It requires PHP 4.0.4pl1 or greater * - From revision 1.17, the function names used by the 'func' mode * are in the format "xmltag_$elem", for example: use "xmltag_name" * to handle the tags of your xml file. */ class XML_Parser extends PEAR { // {{{ properties /** * @var resource XML parser handle */ var $parser; /** * @var resource File handle if parsing from a file */ var $fp; /** * @var boolean Whether to do case folding */ var $folding = true; /** * @var string Mode of operation, one of "event" or "func" */ var $mode; /** * Mapping from expat handler function to class method. * * @var array */ var $handler = array( 'character_data_handler' => 'cdataHandler', 'default_handler' => 'defaultHandler', 'processing_instruction_handler' => 'piHandler', 'unparsed_entity_decl_handler' => 'unparsedHandler', 'notation_decl_handler' => 'notationHandler', 'external_entity_ref_handler' => 'entityrefHandler' ); /** * @var string source encoding */ var $srcenc; /** * @var string target encoding */ var $tgtenc; /* * Use call_user_func when php >= 4.0.7 * @var boolean * @see setMode() */ var $use_call_user_func = true; // }}} // {{{ constructor /** * Creates an XML parser. * * @param string source charset encoding, use NULL (default) to use * whatever the document specifies * @param string how this parser object should work, "event" for * startelement/endelement-type events, "func" * to have it call functions named after elements * * @see xml_parser_create */ function __construct($srcenc = null, $mode = "event", $tgtenc = null) { $this->PEAR('XML_Parser_Error'); if ($srcenc === null) { $xp = @xml_parser_create(); } else { $xp = @xml_parser_create($srcenc); } if (is_resource($xp)) { if ($tgtenc !== null) { if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING, $tgtenc)) { return $this->raiseError("invalid target encoding"); } } $this->parser = $xp; $this->setMode($mode); xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding); } $this->srcenc = $srcenc; $this->tgtenc = $tgtenc; } // }}} // {{{ setMode() /** * Sets the mode and all handler. * * @param string * @see $handler */ function setMode($mode) { $this->mode = $mode; xml_set_object($this->parser, $this); switch ($mode) { case "func": // use call_user_func() when php >= 4.0.7 // or call_user_method() if not if (version_compare(phpversion(), '4.0.7', 'lt')) { $this->use_call_user_func = false; } else { $this->use_call_user_func = true; } xml_set_element_handler($this->parser, "funcStartHandler", "funcEndHandler"); break; case "event": xml_set_element_handler($this->parser, "startHandler", "endHandler"); break; } foreach ($this->handler as $xml_func => $method) if (method_exists($this, $method)) { $xml_func = "xml_set_" . $xml_func; $xml_func($this->parser, $method); } } // }}} // {{{ setInputFile() /** * Defines * * @param string Filename (full path) * @return resource fopen handle of the given file * @throws XML_Parser_Error * @see setInput(), parse() * @access public */ function setInputFile($file) { $fp = @fopen($file, "rb"); if (is_resource($fp)) { $this->fp = $fp; return $fp; } return $this->raiseError($php_errormsg); } // }}} // {{{ setInput() /** * Sets the file handle to use with parse(). * * @param resource fopen * @access public * @see parse(), setInputFile() */ function setInput($fp) { if (is_resource($fp)) { $this->fp = $fp; return true; } return $this->raiseError("not a file resource"); } // }}} // {{{ parse() /** * Central parsing function. * * @throws XML_Parser_Error * @return boolean true on success * @see parseString() * @access public */ function parse() { if (!is_resource($this->fp)) { return $this->raiseError("no input"); } while ($data = fread($this->fp, 2048)) { $err = $this->parseString($data, feof($this->fp)); if (PEAR::isError($err)) { fclose($this->fp); return $err; } } fclose($this->fp); return true; } // }}} // {{{ parseString() /** * Parses a string. * * @param string XML data * @param boolean ??? * @throws XML_Parser_Error * @return mixed true on success or a string with the xml parser error */ function parseString($data, $eof = false) { if (!xml_parse($this->parser, $data, $eof)) { $err = $this->raiseError($this->parser); xml_parser_free($this->parser); return $err; } return true; } // }}} // {{{ funcStartHandler() function funcStartHandler($xp, $elem, $attribs) { $func = 'xmltag_' . $elem; if (method_exists($this, $func)) { if ($this->use_call_user_func) { call_user_func(array(&$this, $func), $xp, $elem, $attribs); } else { call_user_method($func, $this, $xp, $elem, $attribs); } } } // }}} // {{{ funcEndHandler() function funcEndHandler($xp, $elem) { $func = 'xmltag_' . $elem . '_'; if (method_exists($this, $func)) { if ($this->use_call_user_func) { call_user_func(array(&$this, $func), $xp, $elem); } else { call_user_method($func, $this, $xp, $elem); } } } // }}} // {{{ startHandler() /** * * @abstract */ function startHandler($xp, $elem, &$attribs) { return NULL; } // }}} // {{{ endHandler() /** * * @abstract */ function endHandler($xp, $elem) { return NULL; } // }}} } class XML_Parser_Error extends PEAR_Error { // {{{ properties var $error_message_prefix = 'XML_Parser: '; // }}} // {{{ constructor() function __construct($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE) { if (is_resource($msgorparser)) { $code = xml_get_error_code($msgorparser); $msgorparser = sprintf("%s at XML input line %d", xml_error_string($code), xml_get_current_line_number($msgorparser)); } $this->PEAR_Error($msgorparser, $code, $mode, $level); } // }}} } ?>