XML.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. <?php
  2. Lib::loadClass('Core_XmlWriter');
  3. // $zxo = new XMLDiff\Memory;// @require xmldiff pecl package installed
  4. // TODO:TYPE_RECURCE: if type has local prefix - choose:
  5. // - find recurse typeName and restrictions
  6. // - find types and save in SystemObjectField table (if simpleType)
  7. // - ? find types and save in SystemObject table + SystemObjectField table (if complexType)
  8. class XML {
  9. public static function xmlToArray($xml) {
  10. }
  11. public static function readXmlFileToArray($filePath) {
  12. $z = new XMLReader();
  13. if (!$z) throw new HttpException("Class XMLReader not installed", 500);
  14. $fileName = basename($filePath);
  15. if (!$z->open($filePath)) throw new Exception("Failed to open ant schema file '{$fileName}'", 500);
  16. return self::xmlReadToArray($z);
  17. }
  18. public static function xmlReadToArray($z) {
  19. while ($z->read()) {
  20. if (XMLReader::ELEMENT !== $z->nodeType) continue;
  21. return self::xmlReadRecurse($z);
  22. }
  23. }
  24. public static function xmlReadRecurse($z) {
  25. $node = [ $z->name, [], [] ];// name, attrs, childrens
  26. $depth = $z->depth;
  27. $isEmpty = $z->isEmptyElement;
  28. if ($z->hasAttributes) {
  29. while ($z->moveToNextAttribute()) {
  30. $node[1][$z->name] = $z->value;
  31. }
  32. }
  33. if ($isEmpty) {
  34. $node[2] = null;
  35. return $node;
  36. }
  37. // switch ($z->name) {
  38. // case 'xsd:complexType': $node[1]['name'] = $z->getAttribute('name'); break;
  39. // case 'xsd:simpleType': $node[1]['name'] = $z->getAttribute('name'); break;
  40. // case 'xsd:restriction': $node[1]['base'] = $z->getAttribute('base'); break;
  41. // case 'xsd:enumeration': $node[1]['value'] = $z->getAttribute('value'); break;
  42. // case 'xsd:element': $node[1] = [
  43. // 'type' => $z->getAttribute('type')
  44. // ]; break;
  45. // case 'xsd:complexContent': break;
  46. // case 'xsd:sequence': break;
  47. // case 'xsd:attribute': $node[1]['base'] = $z->getAttribute('base'); break;
  48. // case 'xsd:extension': $node[1]['base'] = $z->getAttribute('base'); break;
  49. // default: UI::alert('warning', "TODO: read attributes from: d({$z->depth}) name($z->name)");
  50. // }
  51. while ($z->read() && $z->depth > $depth) {
  52. if (XMLReader::ELEMENT !== $z->nodeType) continue;
  53. // if ($z->depth == $depth + 2)
  54. $node[2][] = self::xmlReadRecurse($z);
  55. // else $node[2][] = "d({$z->depth}) name($z->name)";
  56. }
  57. return $node;
  58. }
  59. public static function printXmlFromArray($xml) {
  60. $xmlWriter = new Core_XmlWriter();
  61. $xmlWriter->openUri('php://output');
  62. $xmlWriter->setIndent(true);
  63. $xmlWriter->startDocument('1.0','UTF-8');
  64. $xmlWriter->h($xml[0], $xml[1], $xml[2]);
  65. $xmlWriter->endDocument();
  66. }
  67. public static function findTargetNamespace($docArray) {
  68. return V::get('targetNamespace', '', $docArray[1]);
  69. }
  70. public static function findSimpleTypeNode($docArray, $name) {
  71. foreach ($docArray[2] as $child) {
  72. if ('simpleType' === self::getTagName($child[0])) {
  73. if ($name === $child[1]['name']) {
  74. return $child;
  75. }
  76. }
  77. }
  78. }
  79. public static function findTargetNamespacePrefix($docArray) {
  80. $tns = self::findTargetNamespace($docArray);
  81. if (!$tns) throw new Exception("targetNamespace not defined");
  82. foreach ($docArray[1] as $attr => $val) {
  83. if ('xmlns:' !== substr($attr, 0, 6)) continue;
  84. if ($tns === $val) return substr($attr, 6);
  85. }
  86. throw new Exception("Missing targetNamespace xmlns:...");
  87. }
  88. public static function findElementName($docArray, $nodeArray) {
  89. if (!empty($nodeArray[1]['name'])) return $nodeArray[1]['name'];
  90. if (!empty($nodeArray[1]['ref'])) return $nodeArray[1]['ref'];
  91. throw new Exception("Missing xsd:element name");
  92. }
  93. public static function findElementType($docArray, $nodeArray) {
  94. $fieldName = self::findElementName($docArray, $nodeArray);
  95. if (!empty($nodeArray[1]['type'])) {
  96. // TODO:TYPE_RECURCE: if local ns prefix then find correct typeName?
  97. // TODO:TYPE_RECURCE:the same for restrictions
  98. $type = $nodeArray[1]['type'];
  99. DBG::log(['is_xs:' => ('xs:' === substr($type, 0, 3)), 'is_xsd:'=>('xsd:' === substr($type, 0, 4))], 'array', "DBG: findElementType field({$fieldName}) type({$type})");
  100. if ($fixedType = self::tryConvertXsdTypeToXsdPrefix($type)) {
  101. return $fixedType;
  102. }
  103. list($prefix, $name) = explode(':', $type);
  104. if ($prefix === self::findTargetNamespacePrefix($docArray)) {
  105. $simpleTypeNode = self::findSimpleTypeNode($docArray, $name);
  106. DBG::log($simpleTypeNode, 'array', "\$simpleTypeNode \$fieldName='{$fieldName}' type='{$type}'");
  107. if (!empty($simpleTypeNode[1]['type'])) {
  108. throw new Exception("TODO: findElementType node/@type => 'xsd:simpleType/@type' = '{$simpleTypeNode[1]['type']}'");
  109. }
  110. if (!empty($simpleTypeNode[2][0]) && self::isXsdTag($simpleTypeNode[2][0][0], 'restriction')) {
  111. $restrictionNode = $simpleTypeNode[2][0];
  112. if (empty($restrictionNode[1]['base'])) throw new Exception("Missing xsd:restriction/@base (node/@type => xsd:simpleType/[@type='{$simpleTypeNode[1]['type']}']/xsd:restriction')");
  113. $type = $restrictionNode[1]['base'];
  114. DBG::log($type, 'array', "findElementType \$fieldName='{$fieldName}' type='{$nodeArray[1]['type']}' => type='{$type}'");
  115. // check restrictions - if has enumeration then return 'p5:enum'
  116. $isEnum = false;
  117. foreach ($restrictionNode[2] as $restr) {
  118. if ('enumeration' === self::getTagName($restr[0])) {
  119. $isEnum = true;
  120. break;
  121. }
  122. }
  123. if ($isEnum) return 'p5:enum';
  124. // TODO: recurse with limit
  125. if ($fixedType = self::tryConvertXsdTypeToXsdPrefix($type)) {
  126. return $fixedType;
  127. }
  128. list($prefix, $name) = explode(':', $type);
  129. $simpleTypeNode = self::findSimpleTypeNode($docArray, $name);
  130. DBG::log($simpleTypeNode, 'array', "\$simpleTypeNode \$fieldName='{$fieldName}' ... type='{$type}'");
  131. if (!empty($simpleTypeNode[1]['type'])) {
  132. throw new Exception("TODO: findElementType node/@type => 'xsd:simpleType/@type' = '{$simpleTypeNode[1]['type']}'");
  133. }
  134. if (!empty($simpleTypeNode[2][0]) && self::isXsdTag($simpleTypeNode[2][0][0], 'restriction')) {
  135. $restrictionNode = $simpleTypeNode[2][0];
  136. if (empty($restrictionNode[1]['base'])) throw new Exception("Missing xsd:restriction/@base (node/@type => xsd:simpleType/[@type='{$simpleTypeNode[1]['type']}']/xsd:restriction')");
  137. $type = $restrictionNode[1]['base'];
  138. DBG::log($type, 'array', "findElementType \$fieldName='{$fieldName}' ... type='{$type}'");
  139. return self::convertXsdTypeToXsdPrefix($type);
  140. }
  141. // TODO: throw...
  142. }
  143. // TODO: throw...
  144. // 0 => 'xsd:simpleType',
  145. // 1 => [
  146. // 'name' => 'PROCES_INIT_Simple',
  147. // ],
  148. // 2 => [
  149. // 0 => [
  150. // 0 => 'xsd:restriction',
  151. // 1 => [
  152. // 'base' => 'default_db__x3A__CRM_PROCES:TYPE_Simple',
  153. // ],
  154. // 2 => [
  155. // 0 => [
  156. // 0 => 'xsd:enumeration',
  157. // 1 => [
  158. // 'value' => 'PROCES_INIT',
  159. // ],
  160. // 2 => NULL,
  161. } else {
  162. return $nodeArray[1]['type'];
  163. }
  164. }
  165. if (!empty($nodeArray[1]['ref'])) return 'ref:' . $nodeArray[1]['ref'];
  166. if (empty($nodeArray[2])) throw new Exception("Missing xsd:element childrens - cannot find type");
  167. // TODO: find in loop (xsd:annotation may accure)
  168. if (empty($nodeArray[2][0][0]) || !self::isXsdTag($nodeArray[2][0][0], 'simpleType')) throw new Exception("Missing 'xsd:simpleType' for field '{$fieldName}'");
  169. if (empty($nodeArray[2][0][2][0]) || !self::isXsdTag($nodeArray[2][0][2][0][0], 'restriction')) throw new Exception("Missing 'xsd:restriction' for field '{$fieldName}'");
  170. if (empty($nodeArray[2][0][2][0][1]['base'])) throw new Exception("Missing 'xsd:restriction/@base' for field '{$fieldName}'");
  171. $type = $nodeArray[2][0][2][0][1]['base'];
  172. if ($fixedType = self::tryConvertXsdTypeToXsdPrefix($type)) {
  173. return $fixedType;
  174. }
  175. DBG::log($nodeArray[2][0], 'array', "TODO:findElementType: local type with restriction field({$fieldName}) type({$type})");
  176. // TODO: element local type with restriction
  177. // <xsd:element name="TYPE">
  178. // <xsd:simpleType>
  179. // <xsd:restriction base="default_db__x3A__CRM_PROCES:TYPE_Simple">
  180. // <xsd:enumeration value="PROCES_BENEFIT_INFO"/>
  181. // </xsd:restriction>
  182. // </xsd:simpleType>
  183. // </xsd:element>
  184. return $type;
  185. }
  186. public static function tryConvertXsdTypeToXsdPrefix($type) {
  187. try {
  188. $type = self::convertXsdTypeToXsdPrefix($type);
  189. return $type;
  190. } catch (Exception $e) {
  191. DBG::log($e);
  192. }
  193. return false;
  194. }
  195. public static function convertXsdTypeToXsdPrefix($type) {
  196. // TODO: validate if type is supported in object engine and gui
  197. // TODO: prefix p5
  198. list($prefix, $name) = explode(':', $type);
  199. $prefix = ('xs' === $prefix) ? 'xsd' : $prefix;
  200. if ('xsd' === $prefix && 'int' === $name) return 'xsd:integer';
  201. if ('xsd' === $prefix && 'NCName' === $name) return 'xsd:string'; // TODO: restriction?
  202. if ('xsd' === $prefix && 'NMTOKEN' === $name) return 'xsd:string'; // TODO: restriction?
  203. if ('xsd' === $prefix) return implode(":", [ $prefix, $name ]);
  204. if ('p5' === $prefix) return implode(":", [ $prefix, $name ]);
  205. // if ('xs:' === substr($type, 0, 3)) return "xsd:" . substr($type, 3);
  206. // if ('xsd:' === substr($type, 0, 4)) return $type;
  207. throw new Exception("Not implemented type '{$type}'");
  208. }
  209. public static function findElementRestrictions($docArray, $nodeArray) {
  210. $restrictions = [];
  211. $fieldName = self::findElementName($docArray, $nodeArray);
  212. if (!empty($nodeArray[1]['nillable']) && 'true' === $nodeArray[1]['nillable']) $restrictions['nillable'] = true;
  213. if (!empty($nodeArray[2])) {
  214. foreach ($nodeArray[2] as $c) {
  215. switch (XML::getTagName($c[0])) {
  216. case 'annotation': break; // skip xsd:element/xsd:annotation @see findElementAppInfo
  217. case 'simpleType': { // xsd:element/xsd:simpleType
  218. if (empty($c[2][0]) || 'restriction' != XML::getTagName($c[2][0][0])) throw new Exception("Missing 'xsd:restriction' for field '{$fieldName}'");
  219. if (empty($c[2][0][1]['base'])) throw new Exception("Missing 'xsd:restriction/@base' for field '{$fieldName}'");
  220. // xsd:element/xsd:simpleType/xsd:restriction/xsd:string
  221. foreach ($c[2][0][2] as $tagRestriction) {
  222. // xsd:string/xsd:maxLength
  223. $val = $tagRestriction[1]['value'];
  224. if ('enumeration' == XML::getTagName($tagRestriction[0])) {
  225. $restrictions['enumeration'][$val] = $val;
  226. } else {
  227. $restrictions[XML::getTagName($tagRestriction[0])] = $val;
  228. }
  229. }
  230. } break;
  231. default: {
  232. DBG::log($c, 'array', "Not imeplemented element child '{$c[0]}'");
  233. }
  234. }
  235. }
  236. }
  237. if (empty($nodeArray[2]) && !empty($nodeArray[1]['type'])) {
  238. list($prefix, $name) = explode(':', $nodeArray[1]['type']);
  239. if ($prefix === XML::findTargetNamespacePrefix($docArray)) {
  240. $simpleTypeNode = self::findSimpleTypeNode($docArray, $name);
  241. return XML::findSimpleTypeRestrictions($docArray, $simpleTypeNode);
  242. }
  243. }
  244. return $restrictions;
  245. }
  246. public static function findSimpleTypeRestrictions($docArray, $nodeArray) { // $nodeArray must be 'simpleType' === XML::getTagName($nodeArray[0])
  247. DBG::log($nodeArray, 'array', "TODO: findElementRestrictions \$nodeArray");
  248. $restrictions = [];
  249. if (empty($nodeArray[2][0]) || 'restriction' != XML::getTagName($nodeArray[2][0][0])) throw new Exception("Missing 'xsd:restriction' for field '{$fieldName}'");
  250. if (empty($nodeArray[2][0][1]['base'])) throw new Exception("Missing 'xsd:restriction/@base' for field '{$fieldName}'");
  251. // xsd:element/xsd:simpleType/xsd:restriction/xsd:string
  252. foreach ($nodeArray[2][0][2] as $tagRestriction) {
  253. // xsd:string/xsd:maxLength
  254. $val = $tagRestriction[1]['value'];
  255. if ('enumeration' == XML::getTagName($tagRestriction[0])) {
  256. $restrictions['enumeration'][$val] = $val;
  257. } else {
  258. $restrictions[XML::getTagName($tagRestriction[0])] = $val;
  259. }
  260. }
  261. return $restrictions;
  262. }
  263. public static function findElementAppInfo($docArray, $nodeArray) {
  264. $appInfo = [];
  265. $fieldName = self::findElementName($docArray, $nodeArray);
  266. if (!empty($nodeArray[2])) {
  267. foreach ($nodeArray[2] as $c) {
  268. switch (XML::getTagName($c[0])) {
  269. case 'annotation': { // skip xsd:element/xsd:annotation
  270. DBG::log($c, 'array', "xsd:annotation/xsd:appinfo '{$fieldName}'");
  271. // <xsd:annotation>
  272. // <xsd:appinfo>
  273. // <system_cache__appinfo:flat_relation_cache>
  274. // <system_cache__appinfo:source system_cache__appinfo:name="ID"
  275. // system_cache__appinfo:xpath="default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK/ID_PROCES"/>
  276. $prefix = 'system_cache__appinfo';
  277. foreach ($c[2] as $cc) {
  278. if ('appinfo' == XML::getTagName($cc[0])) {
  279. foreach ($cc[2] as $appTag) {
  280. $appInfo[XML::getTagName($appTag[0])] = XML::readAppInfoRecurse($docArray, $appTag);
  281. }
  282. }
  283. }
  284. } break;
  285. case 'simpleType': break; // skip xsd:element/xsd:simpleType @see findElementRestrictions
  286. default: {
  287. DBG::log($c, 'array', "Not imeplemented element child '{$c[0]}'");
  288. }
  289. }
  290. }
  291. }
  292. DBG::log($fieldName, 'array', "findElementAppInfo fieldName='{$fieldName}'");
  293. return $appInfo;
  294. }
  295. public static function getTagName($xsdName) {
  296. return (false !== ($pos = strpos($xsdName, ':')))
  297. ? substr($xsdName, $pos + 1)
  298. : $xsdName;
  299. }
  300. public static function isXsdTag($xsdName, $expectedTagName) {
  301. list($xsdPrefix, $tagName) = explode(':', $xsdName);
  302. switch ($xsdPrefix) {
  303. case 'xs':
  304. case 'xsd': return ($tagName === $expectedTagName);
  305. }
  306. return false;
  307. }
  308. public static function readAppInfoRecurse($docArray, $nodeArray) {
  309. $appInfo = [];
  310. if (!empty($nodeArray[1])) foreach ($nodeArray[1] as $attrName => $attrVal) {
  311. $appInfo['@' . XML::getTagName($attrName)] = $attrVal;
  312. }
  313. if (!empty($nodeArray[2])) foreach ($nodeArray[2] as $appTag) {
  314. $appInfo[XML::getTagName($appTag[0])] = XML::readAppInfoRecurse($docArray, $appTag);
  315. }
  316. // TODO: text nodes
  317. return $appInfo;
  318. }
  319. public static function findFieldsFromSequence($docArray, $nodeArray) {
  320. if (!self::isXsdTag($nodeArray[0], 'sequence')) throw new Exception("Error Parsing Schema - expected 'sequence'");
  321. $fields = [];
  322. foreach ($nodeArray[2] as $f) {
  323. if (!self::isXsdTag($f[0], 'element')) {
  324. DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
  325. continue;
  326. }
  327. $fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
  328. if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
  329. if ('__' === substr($fieldName, 0, 2)) continue;
  330. if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
  331. UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
  332. DBG::log($f, 'array', "Skipping not implemented field structure '{$fieldName}'");
  333. continue;
  334. }
  335. $fields[$fieldName] = [
  336. 'type' => XML::findElementType($docArray, $f),
  337. 'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
  338. 'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
  339. 'restrictions' => XML::findElementRestrictions($docArray, $f),
  340. 'appInfo' => XML::findElementAppInfo($docArray, $f),
  341. ];
  342. }
  343. return $fields;
  344. }
  345. public static function findFieldsFromComplexContent($docArray, $nodeArray) {
  346. // xsd:complexType / xsd:complexContent / xsd:restriction [ @base = "default_db__x3A__CRM_PROCES:CRM_PROCES" ]
  347. // xsd:complexType / xsd:complexContent / xsd:extension [ @base = "default_db__x3A__CRM_PROCES:CRM_PROCES" ]
  348. switch (XML::getTagName($nodeArray[2][0][0])) {
  349. case 'extension': return XML::findFieldsFromExtension($docArray, $nodeArray[2][0]);
  350. case 'restriction': return XML::findFieldsFromRestriction($docArray, $nodeArray[2][0]);
  351. }
  352. // TODO:? $xsdType['extensionBase'] = V::get('base', '', $nodeArray[1]);
  353. // TODO:? $xsdType['restrictionBase'] = V::get('base', '', $nodeArray[1]);
  354. }
  355. public static function findFieldsFromExtension($docArray, $nodeArray) {
  356. $fields = [];
  357. if (!self::isXsdTag($nodeArray[2][0][0], 'sequence')) throw new Exception("Error Parsing Schema - expected 'complexType/complexContent/extension/sequence'");
  358. foreach ($nodeArray[2][0][2] as $f) {
  359. if (!self::isXsdTag($f[0], 'element')) {
  360. DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
  361. continue;
  362. }
  363. $fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
  364. if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
  365. if ('__' === substr($fieldName, 0, 2)) continue;
  366. if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
  367. UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
  368. continue;
  369. }
  370. $fields[$fieldName] = [
  371. 'type' => XML::findElementType($docArray, $f),
  372. 'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
  373. 'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
  374. 'restrictions' => XML::findElementRestrictions($docArray, $f),
  375. 'appInfo' => XML::findElementAppInfo($docArray, $f),
  376. ];
  377. }
  378. return $fields;
  379. }
  380. public static function findFieldsFromRestriction($docArray, $nodeArray) {
  381. $fields = [];
  382. if (!self::isXsdTag($nodeArray[2][0][0], 'sequence')) throw new Exception("Error Parsing Schema - expected 'complexType/complexContent/restriction/sequence'");
  383. foreach ($nodeArray[2][0][2] as $f) {
  384. if (!self::isXsdTag($f[0], 'element')) {
  385. DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
  386. continue;
  387. }
  388. $fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
  389. if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
  390. if ('__' === substr($fieldName, 0, 2)) continue;
  391. if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
  392. UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
  393. continue;
  394. }
  395. $fields[$fieldName] = [
  396. 'type' => XML::findElementType($docArray, $f),
  397. 'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
  398. 'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
  399. 'restrictions' => XML::findElementRestrictions($docArray, $f),
  400. 'appInfo' => XML::findElementAppInfo($docArray, $f),
  401. ];
  402. }
  403. return $fields;
  404. }
  405. public static function getXsdTypeFromXsdSchema($xsdFilePath, $namespace, $name) {
  406. $schema = XML::readXmlFileToArray($xsdFilePath);
  407. if (empty($schema)) throw new Exception("Missing schema file for '{$namespace}'");
  408. $xsdType = [ // find xsd:element with @name = $name
  409. 'nsPrefix' => null,
  410. 'name' => null,
  411. 'nsUri' => null,
  412. 'primaryKey' => null,
  413. 'targetNsUri' => V::get('targetNamespace', '', $schema[1])
  414. ];
  415. if (!$xsdType['targetNsUri']) throw new Exception("Missing schema target namespace declaration '{$name}'");
  416. foreach ($schema[2] as $n) {
  417. if (!XML::isXsdTag($n[0], 'element')) continue;
  418. if ($name != V::get('name', '', $n[1])) continue;
  419. list($xsdType['nsPrefix'], $xsdType['name']) = explode(':', V::get('type', '', $n[1]));
  420. }
  421. if (!$xsdType['nsPrefix'] || !$xsdType['name']) throw new Exception("Missing schema root element name = '{$name}'");
  422. $xsdType['nsUri'] = V::get("xmlns:{$xsdType['nsPrefix']}", '', $schema[1]);// find xmlns:default_objects = "https://biuro.biall-net.pl/wfs/default_objects"
  423. if (!$xsdType['nsUri']) throw new Exception("Missing schema root element namespace declaration = '{$name}'");
  424. if ($xsdType['nsUri'] != $xsdType['targetNsUri']) throw new Exception("TODO: type ns is not the same as targetNamespace '{$name}'");// TODO
  425. foreach ($schema[2] as $n) {
  426. if (!XML::isXsdTag($n[0], 'complexType')) continue;
  427. if ($xsdType['name'] != V::get('name', '', $n[1])) continue;
  428. DBG::log($n, 'array', "find system_cache__appinfo:primaryKey from complexType");
  429. if (array_key_exists('system_cache__appinfo:primaryKey', $n[1])) {
  430. $xsdType['primaryKey'] = V::get('system_cache__appinfo:primaryKey', '', $n[1]);
  431. }
  432. // complexType/sequence/element
  433. // complexType/complexContent/extension[base=...]/sequence/element
  434. switch ($n[2][0][0]) {
  435. case 'xs:sequence':
  436. case 'xsd:sequence': $xsdType['struct'] = XML::findFieldsFromSequence($schema, $n[2][0]); break;
  437. case 'xs:complexContent':
  438. case 'xsd:complexContent': $xsdType['struct'] = XML::findFieldsFromComplexContent($schema, $n[2][0]); break;
  439. case 'xs:annotation':
  440. case 'xsd:annotation': {
  441. switch ($n[2][1][0]) {
  442. case 'xs:sequence':
  443. case 'xsd:sequence': $xsdType['struct'] = XML::findFieldsFromSequence($schema, $n[2][1]); break;
  444. case 'xs:complexContent':
  445. case 'xsd:complexContent': $xsdType['struct'] = XML::findFieldsFromComplexContent($schema, $n[2][1]); break;
  446. }
  447. } break;
  448. }
  449. // if ($n[2][0][0] != 'xsd:complexContent') throw new Exception("Error Parsing Schema - expected 'complexType/complexContent'");
  450. }
  451. if (empty($xsdType['primaryKey'])) {
  452. foreach ($xsdType['struct'] as $fieldName => $field) {
  453. if ('ID' === strtoupper($fieldName)) {
  454. $xsdType['primaryKey'] = $fieldName;
  455. break;
  456. }
  457. }
  458. }
  459. if (empty($xsdType['primaryKey'])) throw new Exception("Missing primaryKey for schema '{$namespace}'");
  460. return $xsdType;
  461. }
  462. }