/** * XML parsing recursive helper function * * @param string $xml * @param QuickBooks_XML_Node $root * @return void */ protected function _parseHelper($xml, &$Root, &$errnum, &$errmsg, $indent = 0) { $errnum = QuickBooks_XML::ERROR_OK; $errmsg = ''; $arr = array(); $xml = trim($xml); if (!strlen($xml)) { return false; } $data = ''; $vstack = array(); $dstack = array(); // Remove comments while (false !== strpos($xml, '<!--')) { $start = strpos($xml, '<!--'); $end = strpos($xml, '-->', $start); if (false !== $start and false !== $end) { $xml = substr($xml, 0, $start) . substr($xml, $end + 3); } else { break; } } $raw = $xml; $current = 0; $last = ''; $i = 0; // Parse while (false !== strpos($xml, '<')) { /* print('now examinging:'); print('--------------'); print($xml); print('-----------'); print("\n\n\n"); */ $opentag_start = strpos($xml, '<'); $opentag_end = strpos($xml, '>'); // CDATA check if (substr($xml, $opentag_start, 3) == '<![') { // Find the end of the CDATA section $cdata_end = strpos($xml, ']]>'); $opentag_start = strpos($xml, '<', $cdata_end + 3); $opentag_end = strpos($xml, '>', $cdata_end + 3); } //print('opentag start/end (' . $opentag_start . ', ' . $opentag_end . ') puts us at: {{' . substr($xml, $opentag_start, $opentag_end - $opentag_start) . '}}' . "\n\n"); $tag_w_attrs = trim(substr($xml, $opentag_start + 1, $opentag_end - $opentag_start - 1)); $tag = ''; $attributes = array(); $this->_extractAttributes($tag_w_attrs, $tag, $attributes); if (substr($tag_w_attrs, 0, 1) == '?') { // ignore } else { if (substr($tag_w_attrs, 0, 1) == '!') { // ignore } else { if (substr($tag_w_attrs, -1, 1) == '/') { // ***DO NOT*** completely ignore, auto-closed because it has no children // Completely ignoring causes some SOAP errors for requests like <serverVersion xmlns="http://developer.intuit.com/" /> //print('TAG: [' . substr($tag_w_attrs, 0, -1 . ']' . "\n"); //print('TWA: [' . $tag . ']' . "\n"); //$tag_w_attrs = substr($tag_w_attrs, 0, -1); //$tag = substr($tag, 0, -1); $tag_w_attrs = rtrim($tag_w_attrs, '/'); $tag = rtrim($tag, '/'); // Shove the item on to the stack array_unshift($vstack, array($tag, $tag_w_attrs, $current + $opentag_end)); array_unshift($dstack, array($tag, $tag_w_attrs, $current + $opentag_end)); $key = key($vstack); $tmp = array_shift($vstack); $pop = $tag; $gnk = $tag_w_attrs; $pos = $current + $opentag_end; // there is no data, so empty data and the length is 0 $length = 0; $data = null; if (count($vstack)) { array_shift($dstack); } else { $dstack[$key] = array($pop, $gnk, $pos, $length, $data); } } else { if (substr($tag_w_attrs, 0, 1) == '/') { // NOTE: If you change the code here, you'll likely have to // change it in the above else () section as well, as that // section handles data-less tags like <serverVersion /> $tag = substr($tag, 1); $key = key($vstack); $tmp = array_shift($vstack); $pop = $tmp[0]; $gnk = $tmp[1]; $pos = $tmp[2]; if ($pop != $tag) { $errnum = QuickBooks_XML::ERROR_MISMATCH; $errmsg = 'Mismatched tags, found: ' . $tag . ', expected: ' . $pop; return false; } $data = substr($raw, $pos, $current + $opentag_start - $pos); // Handle <![CDATA[ ... ]]> sections if (substr($data, 0, 9) == '<![CDATA[') { $cdata_end = strpos($data, ']]>'); // Set the data to the CDATA section... $data = QuickBooks_XML::encode(substr($data, 9, $cdata_end - 9)); // ... and remove the CDATA from the remaining XML string //$current = $current + strlen($data) + 12; } if (count($vstack)) { array_shift($dstack); } else { $dstack[$key] = array($pop, $gnk, $pos, $current + $opentag_start - $pos, $data); } } else { array_unshift($vstack, array($tag, $tag_w_attrs, $current + $opentag_end + 1)); array_unshift($dstack, array($tag, $tag_w_attrs, $current + $opentag_end + 1)); } } } } //print('stacks' . "\n"); //print_r($vstack); //print_r($dstack); $xml = substr($xml, $opentag_end + 1); $current = $current + $opentag_end + 1; } if (strlen($xml)) { $errnum = QuickBooks_XML::ERROR_GARBAGE; $errmsg = 'Found this garbage data at end of stream: ' . $xml; return false; } if (count($vstack)) { $errnum = QuickBooks_XML::ERROR_DANGLING; $errmsg = 'XML stack still contains this after parsing: ' . var_export($vstack, true); return false; } //print_r($dstack); //exit; $dstack = array_reverse($dstack); $last = ''; foreach ($dstack as $node) { $tag = $node[0]; $tag_w_attrs = $node[1]; $start = $node[2]; if (count($node) < 5) { continue; } $length = $node[3]; $payload = $node[4]; $tmp = ''; $attributes = array(); $this->_extractAttributes($tag_w_attrs, $tmp, $attributes); $Node = new QuickBooks_XML_Node($tag); foreach ($attributes as $key => $value) { $value = QuickBooks_XML::decode($value, true); $Node->addAttribute($key, $value); } if (false !== strpos($payload, '<')) { // The tag contains child tags $tmp = $this->_parseHelper($payload, $Node, $errnum, $errmsg, $indent + 1); if (!$tmp) { return false; } } else { // This tag has no child tags contained inside it // Make sure we decode any entities $payload = QuickBooks_XML::decode($payload, true); $Node->setData($payload); } $Root->addChild($Node); $last = $tag; } return $Root; }