From 66f29dc2d17fabeeb035443edb6f5007da1e5e51 Mon Sep 17 00:00:00 2001 From: stronk7 Date: Mon, 22 Jun 2009 18:35:56 +0000 Subject: [PATCH] MDL-19579 code coverage - add PEAR/XML lib, required by Spike PHP Coverage. New BSD license. --- lib/pear/XML/Parser.php | 768 +++++++++++++++++++++++++++++++++ lib/pear/XML/Parser/Simple.php | 326 ++++++++++++++ 2 files changed, 1094 insertions(+) create mode 100644 lib/pear/XML/Parser.php create mode 100644 lib/pear/XML/Parser/Simple.php diff --git a/lib/pear/XML/Parser.php b/lib/pear/XML/Parser.php new file mode 100644 index 0000000000..12d91d799a --- /dev/null +++ b/lib/pear/XML/Parser.php @@ -0,0 +1,768 @@ + + * @author Tomas V.V.Cox + * @author Stephan Schmidt + * @copyright 2002-2008 The PHP Group + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/XML_Parser + */ + +/** + * uses PEAR's error handling + */ +require_once 'PEAR.php'; + +/** + * resource could not be created + */ +define('XML_PARSER_ERROR_NO_RESOURCE', 200); + +/** + * unsupported mode + */ +define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201); + +/** + * invalid encoding was given + */ +define('XML_PARSER_ERROR_INVALID_ENCODING', 202); + +/** + * specified file could not be read + */ +define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203); + +/** + * invalid input + */ +define('XML_PARSER_ERROR_INVALID_INPUT', 204); + +/** + * remote file cannot be retrieved in safe mode + */ +define('XML_PARSER_ERROR_REMOTE', 205); + +/** + * XML Parser class. + * + * This is an XML parser based on PHP's "xml" extension, + * based on the bundled expat library. + * + * 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. + * - different parsing modes + * + * @category XML + * @package XML_Parser + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Stephan Schmidt + * @copyright 2002-2008 The PHP Group + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_Parser + * @todo create XML_Parser_Namespace to parse documents with namespaces + * @todo create XML_Parser_Pull + * @todo Tests that need to be made: + * - mixing character encodings + * - a test using all expat handlers + * - options (folding, output charset) + */ +class XML_Parser extends PEAR +{ + // {{{ properties + + /** + * XML parser handle + * + * @var resource + * @see xml_parser_create() + */ + var $parser; + + /** + * File handle if parsing from a file + * + * @var resource + */ + var $fp; + + /** + * Whether to do case folding + * + * If set to true, all tag and attribute names will + * be converted to UPPER CASE. + * + * @var boolean + */ + var $folding = true; + + /** + * Mode of operation, one of "event" or "func" + * + * @var string + */ + 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' + ); + + /** + * source encoding + * + * @var string + */ + var $srcenc; + + /** + * target encoding + * + * @var string + */ + var $tgtenc; + + /** + * handler object + * + * @var object + */ + var $_handlerObj; + + /** + * valid encodings + * + * @var array + */ + var $_validEncodings = array('ISO-8859-1', 'UTF-8', 'US-ASCII'); + + // }}} + // {{{ php4 constructor + + /** + * Creates an XML parser. + * + * This is needed for PHP4 compatibility, it will + * call the constructor, when a new instance is created. + * + * @param string $srcenc source charset encoding, use NULL (default) to use + * whatever the document specifies + * @param string $mode how this parser object should work, "event" for + * startelement/endelement-type events, "func" + * to have it call functions named after elements + * @param string $tgtenc a valid target encoding + */ + function XML_Parser($srcenc = null, $mode = 'event', $tgtenc = null) + { + XML_Parser::__construct($srcenc, $mode, $tgtenc); + } + // }}} + // {{{ php5 constructor + + /** + * PHP5 constructor + * + * @param string $srcenc source charset encoding, use NULL (default) to use + * whatever the document specifies + * @param string $mode how this parser object should work, "event" for + * startelement/endelement-type events, "func" + * to have it call functions named after elements + * @param string $tgtenc a valid target encoding + */ + function __construct($srcenc = null, $mode = 'event', $tgtenc = null) + { + $this->PEAR('XML_Parser_Error'); + + $this->mode = $mode; + $this->srcenc = $srcenc; + $this->tgtenc = $tgtenc; + } + // }}} + + /** + * Sets the mode of the parser. + * + * Possible modes are: + * - func + * - event + * + * You can set the mode using the second parameter + * in the constructor. + * + * This method is only needed, when switching to a new + * mode at a later point. + * + * @param string $mode mode, either 'func' or 'event' + * + * @return boolean|object true on success, PEAR_Error otherwise + * @access public + */ + function setMode($mode) + { + if ($mode != 'func' && $mode != 'event') { + $this->raiseError('Unsupported mode given', + XML_PARSER_ERROR_UNSUPPORTED_MODE); + } + + $this->mode = $mode; + return true; + } + + /** + * Sets the object, that will handle the XML events + * + * This allows you to create a handler object independent of the + * parser object that you are using and easily switch the underlying + * parser. + * + * If no object will be set, XML_Parser assumes that you + * extend this class and handle the events in $this. + * + * @param object &$obj object to handle the events + * + * @return boolean will always return true + * @access public + * @since v1.2.0beta3 + */ + function setHandlerObj(&$obj) + { + $this->_handlerObj = &$obj; + return true; + } + + /** + * Init the element handlers + * + * @return mixed + * @access private + */ + function _initHandlers() + { + if (!is_resource($this->parser)) { + return false; + } + + if (!is_object($this->_handlerObj)) { + $this->_handlerObj = &$this; + } + switch ($this->mode) { + + case 'func': + xml_set_object($this->parser, $this->_handlerObj); + xml_set_element_handler($this->parser, + array(&$this, 'funcStartHandler'), array(&$this, 'funcEndHandler')); + break; + + case 'event': + xml_set_object($this->parser, $this->_handlerObj); + xml_set_element_handler($this->parser, 'startHandler', 'endHandler'); + break; + default: + return $this->raiseError('Unsupported mode given', + XML_PARSER_ERROR_UNSUPPORTED_MODE); + break; + } + + /** + * set additional handlers for character data, entities, etc. + */ + foreach ($this->handler as $xml_func => $method) { + if (method_exists($this->_handlerObj, $method)) { + $xml_func = 'xml_set_' . $xml_func; + $xml_func($this->parser, $method); + } + } + } + + // {{{ _create() + + /** + * create the XML parser resource + * + * Has been moved from the constructor to avoid + * problems with object references. + * + * Furthermore it allows us returning an error + * if something fails. + * + * NOTE: uses '@' error suppresion in this method + * + * @return bool|PEAR_Error true on success, PEAR_Error otherwise + * @access private + * @see xml_parser_create + */ + function _create() + { + if ($this->srcenc === null) { + $xp = @xml_parser_create(); + } else { + $xp = @xml_parser_create($this->srcenc); + } + if (is_resource($xp)) { + if ($this->tgtenc !== null) { + if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING, + $this->tgtenc) + ) { + return $this->raiseError('invalid target encoding', + XML_PARSER_ERROR_INVALID_ENCODING); + } + } + $this->parser = $xp; + $result = $this->_initHandlers($this->mode); + if ($this->isError($result)) { + return $result; + } + xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding); + return true; + } + if (!in_array(strtoupper($this->srcenc), $this->_validEncodings)) { + return $this->raiseError('invalid source encoding', + XML_PARSER_ERROR_INVALID_ENCODING); + } + return $this->raiseError('Unable to create XML parser resource.', + XML_PARSER_ERROR_NO_RESOURCE); + } + + // }}} + // {{{ reset() + + /** + * Reset the parser. + * + * This allows you to use one parser instance + * to parse multiple XML documents. + * + * @access public + * @return boolean|object true on success, PEAR_Error otherwise + */ + function reset() + { + $result = $this->_create(); + if ($this->isError($result)) { + return $result; + } + return true; + } + + // }}} + // {{{ setInputFile() + + /** + * Sets the input xml file to be parsed + * + * @param string $file Filename (full path) + * + * @return resource fopen handle of the given file + * @access public + * @throws XML_Parser_Error + * @see setInput(), setInputString(), parse() + */ + function setInputFile($file) + { + /** + * check, if file is a remote file + */ + if (eregi('^(http|ftp)://', substr($file, 0, 10))) { + if (!ini_get('allow_url_fopen')) { + return $this-> + raiseError('Remote files cannot be parsed, as safe mode is enabled.', + XML_PARSER_ERROR_REMOTE); + } + } + + $fp = @fopen($file, 'rb'); + if (is_resource($fp)) { + $this->fp = $fp; + return $fp; + } + return $this->raiseError('File could not be opened.', + XML_PARSER_ERROR_FILE_NOT_READABLE); + } + + // }}} + // {{{ setInputString() + + /** + * XML_Parser::setInputString() + * + * Sets the xml input from a string + * + * @param string $data a string containing the XML document + * + * @return null + */ + function setInputString($data) + { + $this->fp = $data; + return null; + } + + // }}} + // {{{ setInput() + + /** + * Sets the file handle to use with parse(). + * + * You should use setInputFile() or setInputString() if you + * pass a string + * + * @param mixed $fp Can be either a resource returned from fopen(), + * a URL, a local filename or a string. + * + * @return mixed + * @access public + * @see parse() + * @uses setInputString(), setInputFile() + */ + function setInput($fp) + { + if (is_resource($fp)) { + $this->fp = $fp; + return true; + } elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) { + // see if it's an absolute URL (has a scheme at the beginning) + return $this->setInputFile($fp); + } elseif (file_exists($fp)) { + // see if it's a local file + return $this->setInputFile($fp); + } else { + // it must be a string + $this->fp = $fp; + return true; + } + + return $this->raiseError('Illegal input format', + XML_PARSER_ERROR_INVALID_INPUT); + } + + // }}} + // {{{ parse() + + /** + * Central parsing function. + * + * @return bool|PEAR_Error returns true on success, or a PEAR_Error otherwise + * @access public + */ + function parse() + { + /** + * reset the parser + */ + $result = $this->reset(); + if ($this->isError($result)) { + return $result; + } + // if $this->fp was fopened previously + if (is_resource($this->fp)) { + + while ($data = fread($this->fp, 4096)) { + if (!$this->_parseString($data, feof($this->fp))) { + $error = &$this->raiseError(); + $this->free(); + return $error; + } + } + } else { + // otherwise, $this->fp must be a string + if (!$this->_parseString($this->fp, true)) { + $error = &$this->raiseError(); + $this->free(); + return $error; + } + } + $this->free(); + + return true; + } + + /** + * XML_Parser::_parseString() + * + * @param string $data data + * @param bool $eof end-of-file flag + * + * @return bool + * @access private + * @see parseString() + **/ + function _parseString($data, $eof = false) + { + return xml_parse($this->parser, $data, $eof); + } + + // }}} + // {{{ parseString() + + /** + * XML_Parser::parseString() + * + * Parses a string. + * + * @param string $data XML data + * @param boolean $eof If set and TRUE, data is the last piece + * of data sent in this parser + * + * @return bool|PEAR_Error true on success or a PEAR Error + * @throws XML_Parser_Error + * @see _parseString() + */ + function parseString($data, $eof = false) + { + if (!isset($this->parser) || !is_resource($this->parser)) { + $this->reset(); + } + + if (!$this->_parseString($data, $eof)) { + $error = &$this->raiseError(); + $this->free(); + return $error; + } + + if ($eof === true) { + $this->free(); + } + return true; + } + + /** + * XML_Parser::free() + * + * Free the internal resources associated with the parser + * + * @return null + **/ + function free() + { + if (isset($this->parser) && is_resource($this->parser)) { + xml_parser_free($this->parser); + unset( $this->parser ); + } + if (isset($this->fp) && is_resource($this->fp)) { + fclose($this->fp); + } + unset($this->fp); + return null; + } + + /** + * XML_Parser::raiseError() + * + * Throws a XML_Parser_Error + * + * @param string $msg the error message + * @param integer $ecode the error message code + * + * @return XML_Parser_Error reference to the error object + **/ + function &raiseError($msg = null, $ecode = 0) + { + $msg = !is_null($msg) ? $msg : $this->parser; + $err = &new XML_Parser_Error($msg, $ecode); + return parent::raiseError($err); + } + + // }}} + // {{{ funcStartHandler() + + /** + * derives and calls the Start Handler function + * + * @param mixed $xp ?? + * @param mixed $elem ?? + * @param mixed $attribs ?? + * + * @return void + */ + function funcStartHandler($xp, $elem, $attribs) + { + $func = 'xmltag_' . $elem; + $func = str_replace(array('.', '-', ':'), '_', $func); + if (method_exists($this->_handlerObj, $func)) { + call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs); + } elseif (method_exists($this->_handlerObj, 'xmltag')) { + call_user_func(array(&$this->_handlerObj, 'xmltag'), + $xp, $elem, $attribs); + } + } + + // }}} + // {{{ funcEndHandler() + + /** + * derives and calls the End Handler function + * + * @param mixed $xp ?? + * @param mixed $elem ?? + * + * @return void + */ + function funcEndHandler($xp, $elem) + { + $func = 'xmltag_' . $elem . '_'; + $func = str_replace(array('.', '-', ':'), '_', $func); + if (method_exists($this->_handlerObj, $func)) { + call_user_func(array(&$this->_handlerObj, $func), $xp, $elem); + } elseif (method_exists($this->_handlerObj, 'xmltag_')) { + call_user_func(array(&$this->_handlerObj, 'xmltag_'), $xp, $elem); + } + } + + // }}} + // {{{ startHandler() + + /** + * abstract method signature for Start Handler + * + * @param mixed $xp ?? + * @param mixed $elem ?? + * @param mixed &$attribs ?? + * + * @return null + * @abstract + */ + function startHandler($xp, $elem, &$attribs) + { + return null; + } + + // }}} + // {{{ endHandler() + + /** + * abstract method signature for End Handler + * + * @param mixed $xp ?? + * @param mixed $elem ?? + * + * @return null + * @abstract + */ + function endHandler($xp, $elem) + { + return null; + } + + + // }}}me +} + +/** + * error class, replaces PEAR_Error + * + * An instance of this class will be returned + * if an error occurs inside XML_Parser. + * + * There are three advantages over using the standard PEAR_Error: + * - All messages will be prefixed + * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' ) + * - messages can be generated from the xml_parser resource + * + * @category XML + * @package XML_Parser + * @author Stig Bakken + * @author Tomas V.V.Cox + * @author Stephan Schmidt + * @copyright 2002-2008 The PHP Group + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_Parser + * @see PEAR_Error + */ +class XML_Parser_Error extends PEAR_Error +{ + // {{{ properties + + /** + * prefix for all messages + * + * @var string + */ + var $error_message_prefix = 'XML_Parser: '; + + // }}} + // {{{ constructor() + /** + * construct a new error instance + * + * You may either pass a message or an xml_parser resource as first + * parameter. If a resource has been passed, the last error that + * happened will be retrieved and returned. + * + * @param string|resource $msgorparser message or parser resource + * @param integer $code error code + * @param integer $mode error handling + * @param integer $level error level + * + * @access public + * @todo PEAR CS - can't meet 85char line limit without arg refactoring + */ + function XML_Parser_Error($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:%d', + xml_error_string($code), + xml_get_current_line_number($msgorparser), + xml_get_current_column_number($msgorparser)); + } + $this->PEAR_Error($msgorparser, $code, $mode, $level); + } + // }}} +} +?> diff --git a/lib/pear/XML/Parser/Simple.php b/lib/pear/XML/Parser/Simple.php new file mode 100644 index 0000000000..7d9bb8bc8b --- /dev/null +++ b/lib/pear/XML/Parser/Simple.php @@ -0,0 +1,326 @@ + + * @copyright 2004-2008 Stephan Schmidt + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version CVS: $Id$ + * @link http://pear.php.net/package/XML_Parser + */ + +/** + * built on XML_Parser + */ +require_once 'XML/Parser.php'; + +/** + * Simple XML parser class. + * + * This class is a simplified version of XML_Parser. + * In most XML applications the real action is executed, + * when a closing tag is found. + * + * XML_Parser_Simple allows you to just implement one callback + * for each tag that will receive the tag with its attributes + * and CData. + * + * + * require_once '../Parser/Simple.php'; + * + * class myParser extends XML_Parser_Simple + * { + * function myParser() + * { + * $this->XML_Parser_Simple(); + * } + * + * function handleElement($name, $attribs, $data) + * { + * printf('handle %s
', $name); + * } + * } + * + * $p = &new myParser(); + * + * $result = $p->setInputFile('myDoc.xml'); + * $result = $p->parse(); + *
+ * + * @category XML + * @package XML_Parser + * @author Stephan Schmidt + * @copyright 2004-2008 The PHP Group + * @license http://opensource.org/licenses/bsd-license New BSD License + * @version Release: @package_version@ + * @link http://pear.php.net/package/XML_Parser + */ +class XML_Parser_Simple extends XML_Parser +{ + /** + * element stack + * + * @access private + * @var array + */ + var $_elStack = array(); + + /** + * all character data + * + * @access private + * @var array + */ + var $_data = array(); + + /** + * element depth + * + * @access private + * @var integer + */ + var $_depth = 0; + + /** + * Mapping from expat handler function to class method. + * + * @var array + */ + var $handler = array( + 'default_handler' => 'defaultHandler', + 'processing_instruction_handler' => 'piHandler', + 'unparsed_entity_decl_handler' => 'unparsedHandler', + 'notation_decl_handler' => 'notationHandler', + 'external_entity_ref_handler' => 'entityrefHandler' + ); + + /** + * Creates an XML parser. + * + * This is needed for PHP4 compatibility, it will + * call the constructor, when a new instance is created. + * + * @param string $srcenc source charset encoding, use NULL (default) to use + * whatever the document specifies + * @param string $mode how this parser object should work, "event" for + * handleElement(), "func" to have it call functions + * named after elements (handleElement_$name()) + * @param string $tgtenc a valid target encoding + */ + function XML_Parser_Simple($srcenc = null, $mode = 'event', $tgtenc = null) + { + $this->XML_Parser($srcenc, $mode, $tgtenc); + } + + /** + * inits the handlers + * + * @return mixed + * @access private + */ + function _initHandlers() + { + if (!is_object($this->_handlerObj)) { + $this->_handlerObj = &$this; + } + + if ($this->mode != 'func' && $this->mode != 'event') { + return $this->raiseError('Unsupported mode given', + XML_PARSER_ERROR_UNSUPPORTED_MODE); + } + xml_set_object($this->parser, $this->_handlerObj); + + xml_set_element_handler($this->parser, array(&$this, 'startHandler'), + array(&$this, 'endHandler')); + xml_set_character_data_handler($this->parser, array(&$this, 'cdataHandler')); + + /** + * set additional handlers for character data, entities, etc. + */ + foreach ($this->handler as $xml_func => $method) { + if (method_exists($this->_handlerObj, $method)) { + $xml_func = 'xml_set_' . $xml_func; + $xml_func($this->parser, $method); + } + } + } + + /** + * Reset the parser. + * + * This allows you to use one parser instance + * to parse multiple XML documents. + * + * @access public + * @return boolean|object true on success, PEAR_Error otherwise + */ + function reset() + { + $this->_elStack = array(); + $this->_data = array(); + $this->_depth = 0; + + $result = $this->_create(); + if ($this->isError($result)) { + return $result; + } + return true; + } + + /** + * start handler + * + * Pushes attributes and tagname onto a stack + * + * @param resource $xp xml parser resource + * @param string $elem element name + * @param array &$attribs attributes + * + * @return mixed + * @access private + * @final + */ + function startHandler($xp, $elem, &$attribs) + { + array_push($this->_elStack, array( + 'name' => $elem, + 'attribs' => $attribs + )); + $this->_depth++; + $this->_data[$this->_depth] = ''; + } + + /** + * end handler + * + * Pulls attributes and tagname from a stack + * + * @param resource $xp xml parser resource + * @param string $elem element name + * + * @return mixed + * @access private + * @final + */ + function endHandler($xp, $elem) + { + $el = array_pop($this->_elStack); + $data = $this->_data[$this->_depth]; + $this->_depth--; + + switch ($this->mode) { + case 'event': + $this->_handlerObj->handleElement($el['name'], $el['attribs'], $data); + break; + case 'func': + $func = 'handleElement_' . $elem; + if (strchr($func, '.')) { + $func = str_replace('.', '_', $func); + } + if (method_exists($this->_handlerObj, $func)) { + call_user_func(array(&$this->_handlerObj, $func), + $el['name'], $el['attribs'], $data); + } + break; + } + } + + /** + * handle character data + * + * @param resource $xp xml parser resource + * @param string $data data + * + * @return void + * @access private + * @final + */ + function cdataHandler($xp, $data) + { + $this->_data[$this->_depth] .= $data; + } + + /** + * handle a tag + * + * Implement this in your parser + * + * @param string $name element name + * @param array $attribs attributes + * @param string $data character data + * + * @return void + * @access public + * @abstract + */ + function handleElement($name, $attribs, $data) + { + } + + /** + * get the current tag depth + * + * The root tag is in depth 0. + * + * @access public + * @return integer + */ + function getCurrentDepth() + { + return $this->_depth; + } + + /** + * add some string to the current ddata. + * + * This is commonly needed, when a document is parsed recursively. + * + * @param string $data data to add + * + * @return void + * @access public + */ + function addToData($data) + { + $this->_data[$this->_depth] .= $data; + } +} +?> -- 2.39.5