+ /** @var array $parsing_options */
+ protected $parsing_options = array();
+ /** @var int $accept self::ACCEPT_REQUEST | self::ACCEPT_RESPONSE by default */
+ protected $accept = 3;
+ /** @var int $maxChunkLength 4 MB by default. Any value below 10MB should be good */
+ protected $maxChunkLength = 4194304;
+
+ /**
+ * @param array $options passed to the xml parser
+ */
+ public function __construct(array $options = array())
+ {
+ $this->parsing_options = $options;
+ }
+
+ /**
+ * @param string $data
+ * @param string $returnType
+ * @param int $accept a bit-combination of self::ACCEPT_REQUEST, self::ACCEPT_RESPONSE, self::ACCEPT_VALUE
+ * @param array $options
+ */
+ public function parse($data, $returnType = self::RETURN_XMLRPCVALS, $accept = 3, $options = array())
+ {
+ $this->_xh = array(
+ 'ac' => '',
+ 'stack' => array(),
+ 'valuestack' => array(),
+ 'isf' => 0,
+ 'isf_reason' => '',
+ 'value' => null,
+ 'method' => false, // so we can check later if we got a methodname or not
+ 'params' => array(),
+ 'pt' => array(),
+ 'rt' => '',
+ );
+
+ $len = strlen($data);
+
+ // we test for empty documents here to save on resource allocation and simply the chunked-parsing loop below
+ if ($len == 0) {
+ $this->_xh['isf'] = 3;
+ $this->_xh['isf_reason'] = 'XML error 5: empty document';
+ return;
+ }
+
+ $parser = xml_parser_create();
+
+ foreach ($this->parsing_options as $key => $val) {
+ xml_parser_set_option($parser, $key, $val);
+ }
+ foreach ($options as $key => $val) {
+ xml_parser_set_option($parser, $key, $val);
+ }
+ // always set this, in case someone tries to disable it via options...
+ xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 1);
+
+ xml_set_object($parser, $this);
+
+ switch($returnType) {
+ case self::RETURN_PHP:
+ xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
+ break;
+ case self::RETURN_EPIVALS:
+ xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_epi');
+ break;
+ default:
+ xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
+ }
+
+ xml_set_character_data_handler($parser, 'xmlrpc_cd');
+ xml_set_default_handler($parser, 'xmlrpc_dh');
+
+ $this->accept = $accept;
+
+ // @see ticket #70 - we have to parse big xml docks in chunks to avoid errors
+ for ($offset = 0; $offset < $len; $offset += $this->maxChunkLength) {
+ $chunk = substr($data, $offset, $this->maxChunkLength);
+ // error handling: xml not well formed
+ if (!xml_parse($parser, $chunk, $offset + $this->maxChunkLength >= $len)) {
+ $errCode = xml_get_error_code($parser);
+ $errStr = sprintf('XML error %s: %s at line %d, column %d', $errCode, xml_error_string($errCode),
+ xml_get_current_line_number($parser), xml_get_current_column_number($parser));
+
+ $this->_xh['isf'] = 3;
+ $this->_xh['isf_reason'] = $errStr;
+ break;
+ }
+ }
+
+ xml_parser_free($parser);
+ }
+