/office/quickbooks/QuickBooks/XML/Backend/Builtin.php

https://bitbucket.org/211enterprises/laracing · PHP · 426 lines · 279 code · 87 blank · 60 comment · 51 complexity · 39d8e2bc57714e4b0bd209d89004186a MD5 · raw file

  1. <?php
  2. class QuickBooks_XML_Backend_BuiltIn implements QuickBooks_XML_Backend
  3. {
  4. protected $_xml;
  5. public function __construct($xml)
  6. {
  7. $this->_xml = $xml;
  8. }
  9. public function load($xml)
  10. {
  11. $this->_xml = $xml;
  12. return strlen($xml) > 0;
  13. }
  14. public function validate(&$errnum, &$errmsg)
  15. {
  16. if (!strlen($this->_xml))
  17. {
  18. return false;
  19. }
  20. $stack = array();
  21. $xml = $this->_xml;
  22. // Remove comments
  23. while (false !== strpos($xml, '<!--'))
  24. {
  25. $start = strpos($xml, '<!--');
  26. $end = strpos($xml, '-->', $start);
  27. if (false !== $start and false !== $end)
  28. {
  29. $xml = substr($xml, 0, $start) . substr($xml, $end + 3);
  30. }
  31. else
  32. {
  33. break;
  34. }
  35. }
  36. // Remove <![CDATA[ sections
  37. $has_cdata = false;
  38. while (false !== strpos($xml, '<![CDATA['))
  39. {
  40. $has_cdata = true;
  41. $start = strpos($xml, '<![CDATA[');
  42. $end = strpos($xml, ']]>', $start);
  43. if (false !== $start and false !== $end)
  44. {
  45. $xml = substr($xml, 0, $start) . substr($xml, $end + 3);
  46. }
  47. else
  48. {
  49. break;
  50. }
  51. }
  52. // Check well-formedness
  53. while (false !== strpos($xml, '<'))
  54. {
  55. $opentag_start = strpos($xml, '<');
  56. $opentag_end = strpos($xml, '>');
  57. $tag_w_attrs = trim(substr($xml, $opentag_start + 1, $opentag_end - $opentag_start - 1));
  58. $tag = '';
  59. $attributes = array();
  60. $this->_extractAttributes($tag_w_attrs, $tag, $attributes);
  61. if (substr($tag_w_attrs, 0, 1) == '?') // < ? x m l
  62. {
  63. // ignore
  64. }
  65. else if (substr($tag_w_attrs, 0, 1) == '!') // <!DOCTYPE
  66. {
  67. // ignore
  68. }
  69. else if (substr($tag_w_attrs, -1, 1) == '/')
  70. {
  71. // completely ignore, auto-closed because it has no children
  72. }
  73. else if (substr($tag_w_attrs, 0, 1) == '/') // close tag
  74. {
  75. $tag = substr($tag, 1);
  76. $pop = array_shift($stack);
  77. if ($pop != $tag)
  78. {
  79. $errnum = QuickBooks_XML::ERROR_MISMATCH;
  80. $errmsg = 'Mismatched tags, found: ' . $tag . ', expected: ' . $pop;
  81. return false;
  82. }
  83. }
  84. else // open tag
  85. {
  86. array_unshift($stack, $tag);
  87. }
  88. $xml = trim(substr($xml, $opentag_end + 1));
  89. }
  90. if (strlen($xml))
  91. {
  92. $errnum = QuickBooks_XML::ERROR_GARBAGE;
  93. $errmsg = 'Found this garbage data at end of stream: ' . $xml;
  94. return false;
  95. }
  96. if (count($stack))
  97. {
  98. $errnum = QuickBooks_XML::ERROR_DANGLING;
  99. $errmsg = 'XML stack still contains this after parsing: ' . var_export($stack, true);
  100. return false;
  101. }
  102. return true;
  103. }
  104. public function parse(&$errnum, &$errmsg)
  105. {
  106. $base = new QuickBooks_XML_Node('root');
  107. $this->_parseHelper($this->_xml, $base, $errnum, $errmsg);
  108. if ($errnum != QuickBooks_XML::ERROR_OK)
  109. {
  110. return false;
  111. }
  112. $tmp = $base->children();
  113. return new QuickBooks_XML_Document(current($tmp));
  114. }
  115. /**
  116. * XML parsing recursive helper function
  117. *
  118. * @param string $xml
  119. * @param QuickBooks_XML_Node $root
  120. * @return void
  121. */
  122. protected function _parseHelper($xml, &$Root, &$errnum, &$errmsg, $indent = 0)
  123. {
  124. $errnum = QuickBooks_XML::ERROR_OK;
  125. $errmsg = '';
  126. $arr = array();
  127. $xml = trim($xml);
  128. if (!strlen($xml))
  129. {
  130. return false;
  131. }
  132. $data = '';
  133. $vstack = array();
  134. $dstack = array();
  135. // Remove comments
  136. while (false !== strpos($xml, '<!--'))
  137. {
  138. $start = strpos($xml, '<!--');
  139. $end = strpos($xml, '-->', $start);
  140. if (false !== $start and false !== $end)
  141. {
  142. $xml = substr($xml, 0, $start) . substr($xml, $end + 3);
  143. }
  144. else
  145. {
  146. break;
  147. }
  148. }
  149. $raw = $xml;
  150. $current = 0;
  151. $last = '';
  152. $i = 0;
  153. // Parse
  154. while (false !== strpos($xml, '<'))
  155. {
  156. /*
  157. print('now examinging:');
  158. print('--------------');
  159. print($xml);
  160. print('-----------');
  161. print("\n\n\n");
  162. */
  163. $opentag_start = strpos($xml, '<');
  164. $opentag_end = strpos($xml, '>');
  165. // CDATA check
  166. if (substr($xml, $opentag_start, 3) == '<![')
  167. {
  168. // Find the end of the CDATA section
  169. $cdata_end = strpos($xml, ']]>');
  170. $opentag_start = strpos($xml, '<', $cdata_end + 3);
  171. $opentag_end = strpos($xml, '>', $cdata_end + 3);
  172. }
  173. //print('opentag start/end (' . $opentag_start . ', ' . $opentag_end . ') puts us at: {{' . substr($xml, $opentag_start, $opentag_end - $opentag_start) . '}}' . "\n\n");
  174. $tag_w_attrs = trim(substr($xml, $opentag_start + 1, $opentag_end - $opentag_start - 1));
  175. $tag = '';
  176. $attributes = array();
  177. $this->_extractAttributes($tag_w_attrs, $tag, $attributes);
  178. if (substr($tag_w_attrs, 0, 1) == '?') // xml declration
  179. {
  180. // ignore
  181. }
  182. else if (substr($tag_w_attrs, 0, 1) == '!')
  183. {
  184. // ignore
  185. }
  186. //else if (substr($tag_w_attrs, 0, 3) == '!--') // comment
  187. //{
  188. // // ignore
  189. //}
  190. else if (substr($tag_w_attrs, -1, 1) == '/')
  191. {
  192. // ***DO NOT*** completely ignore, auto-closed because it has no children
  193. // Completely ignoring causes some SOAP errors for requests like <serverVersion xmlns="http://developer.intuit.com/" />
  194. //print('TAG: [' . substr($tag_w_attrs, 0, -1 . ']' . "\n");
  195. //print('TWA: [' . $tag . ']' . "\n");
  196. //$tag_w_attrs = substr($tag_w_attrs, 0, -1);
  197. //$tag = substr($tag, 0, -1);
  198. $tag_w_attrs = rtrim($tag_w_attrs, '/');
  199. $tag = rtrim($tag, '/');
  200. // Shove the item on to the stack
  201. array_unshift($vstack, array( $tag, $tag_w_attrs, $current + $opentag_end ) );
  202. array_unshift($dstack, array( $tag, $tag_w_attrs, $current + $opentag_end ) );
  203. $key = key($vstack);
  204. $tmp = array_shift($vstack);
  205. $pop = $tag;
  206. $gnk = $tag_w_attrs;
  207. $pos = $current + $opentag_end;
  208. // there is no data, so empty data and the length is 0
  209. $length = 0;
  210. $data = null;
  211. if (count($vstack))
  212. {
  213. array_shift($dstack);
  214. }
  215. else
  216. {
  217. $dstack[$key] = array( $pop, $gnk, $pos, $length, $data );
  218. }
  219. }
  220. else if (substr($tag_w_attrs, 0, 1) == '/') // close tag
  221. {
  222. // NOTE: If you change the code here, you'll likely have to
  223. // change it in the above else () section as well, as that
  224. // section handles data-less tags like <serverVersion />
  225. $tag = substr($tag, 1);
  226. $key = key($vstack);
  227. $tmp = array_shift($vstack);
  228. $pop = $tmp[0];
  229. $gnk = $tmp[1];
  230. $pos = $tmp[2];
  231. if ($pop != $tag)
  232. {
  233. $errnum = QuickBooks_XML::ERROR_MISMATCH;
  234. $errmsg = 'Mismatched tags, found: ' . $tag . ', expected: ' . $pop;
  235. return false;
  236. }
  237. $data = substr($raw, $pos, $current + $opentag_start - $pos);
  238. // Handle <![CDATA[ ... ]]> sections
  239. if (substr($data, 0, 9) == '<![CDATA[')
  240. {
  241. $cdata_end = strpos($data, ']]>');
  242. // Set the data to the CDATA section...
  243. $data = QuickBooks_XML::encode(substr($data, 9, $cdata_end - 9));
  244. // ... and remove the CDATA from the remaining XML string
  245. //$current = $current + strlen($data) + 12;
  246. }
  247. if (count($vstack))
  248. {
  249. array_shift($dstack);
  250. }
  251. else
  252. {
  253. $dstack[$key] = array( $pop, $gnk, $pos, $current + $opentag_start - $pos, $data );
  254. }
  255. }
  256. else // open tag
  257. {
  258. array_unshift($vstack, array( $tag, $tag_w_attrs, $current + $opentag_end + 1 ) );
  259. array_unshift($dstack, array( $tag, $tag_w_attrs, $current + $opentag_end + 1 ) );
  260. }
  261. //print('stacks' . "\n");
  262. //print_r($vstack);
  263. //print_r($dstack);
  264. $xml = substr($xml, $opentag_end + 1);
  265. $current = $current + $opentag_end + 1;
  266. }
  267. if (strlen($xml))
  268. {
  269. $errnum = QuickBooks_XML::ERROR_GARBAGE;
  270. $errmsg = 'Found this garbage data at end of stream: ' . $xml;
  271. return false;
  272. }
  273. if (count($vstack))
  274. {
  275. $errnum = QuickBooks_XML::ERROR_DANGLING;
  276. $errmsg = 'XML stack still contains this after parsing: ' . var_export($vstack, true);
  277. return false;
  278. }
  279. //print_r($dstack);
  280. //exit;
  281. $dstack = array_reverse($dstack);
  282. $last = '';
  283. foreach ($dstack as $node)
  284. {
  285. $tag = $node[0];
  286. $tag_w_attrs = $node[1];
  287. $start = $node[2];
  288. if (count($node) < 5)
  289. {
  290. continue;
  291. }
  292. $length = $node[3];
  293. $payload = $node[4];
  294. $tmp = '';
  295. $attributes = array();
  296. $this->_extractAttributes($tag_w_attrs, $tmp, $attributes);
  297. $Node = new QuickBooks_XML_Node($tag);
  298. foreach ($attributes as $key => $value)
  299. {
  300. $value = QuickBooks_XML::decode($value, true);
  301. $Node->addAttribute($key, $value);
  302. }
  303. if (false !== strpos($payload, '<'))
  304. {
  305. // The tag contains child tags
  306. $tmp = $this->_parseHelper($payload, $Node, $errnum, $errmsg, $indent + 1);
  307. if (!$tmp)
  308. {
  309. return false;
  310. }
  311. }
  312. else
  313. {
  314. // This tag has no child tags contained inside it
  315. // Make sure we decode any entities
  316. $payload = QuickBooks_XML::decode($payload, true);
  317. $Node->setData($payload);
  318. }
  319. $Root->addChild($Node);
  320. $last = $tag;
  321. }
  322. return $Root;
  323. }
  324. protected function _extractAttributes($tag_w_attrs, &$tag, &$attributes)
  325. {
  326. $tag = '';
  327. $attributes = array();
  328. $tmp = QuickBooks_XML::extractTagAttributes($tag_w_attrs, true);
  329. $tag = array_shift($tmp);
  330. $attributes = $tmp;
  331. /*
  332. print('extracting attributes from: {{' . $tag_w_attrs . '}}' . "\n");
  333. print(' tag: [[' . $tag . ']]' . "\n");
  334. print(' attrs: ' . print_r($attributes, true) . "\n");
  335. print("\n");
  336. */
  337. return true;
  338. }
  339. }