XML.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  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 findElementName($docArray, $nodeArray) {
  68. if (!empty($nodeArray[1]['name'])) return $nodeArray[1]['name'];
  69. if (!empty($nodeArray[1]['ref'])) return $nodeArray[1]['ref'];
  70. throw new Exception("Missing xsd:element name");
  71. }
  72. public static function findElementType($docArray, $nodeArray) {
  73. $fieldName = self::findElementName($docArray, $nodeArray);
  74. if (!empty($nodeArray[1]['type'])) {
  75. // TODO:TYPE_RECURCE: if local ns prefix then find correct typeName?
  76. // TODO:TYPE_RECURCE:the same for restrictions
  77. return $nodeArray[1]['type'];
  78. }
  79. if (!empty($nodeArray[1]['ref'])) return 'ref:' . $nodeArray[1]['ref'];
  80. if (empty($nodeArray[2])) throw new Exception("Missing xsd:element childrens - cannot find type");
  81. // TODO: find in loop (xsd:annotation may accure)
  82. if (empty($nodeArray[2][0][0]) || 'xsd:simpleType' != $nodeArray[2][0][0]) throw new Exception("Missing 'xsd:simpleType' for field '{$fieldName}'");
  83. if (empty($nodeArray[2][0][2][0]) || 'xsd:restriction' != $nodeArray[2][0][2][0][0]) throw new Exception("Missing 'xsd:restriction' for field '{$fieldName}'");
  84. if (empty($nodeArray[2][0][2][0][1]['base'])) throw new Exception("Missing 'xsd:restriction/@base' for field '{$fieldName}'");
  85. return $nodeArray[2][0][2][0][1]['base'];
  86. }
  87. public static function findElementRestrictions($docArray, $nodeArray) {
  88. $restrictions = [];
  89. $fieldName = self::findElementName($docArray, $nodeArray);
  90. // TODO:TYPE_RECURCE: if type has local prefix then find retriction recurse?
  91. if (!empty($nodeArray[1]['nillable']) && 'true' === $nodeArray[1]['nillable']) $restrictions['nillable'] = true;
  92. if (!empty($nodeArray[2])) {
  93. foreach ($nodeArray[2] as $c) {
  94. switch (XML::getTagName($c[0])) {
  95. case 'annotation': break; // skip xsd:element/xsd:annotation @see findElementAppInfo
  96. case 'simpleType': { // xsd:element/xsd:simpleType
  97. if (empty($c[2][0]) || 'restriction' != XML::getTagName($c[2][0][0])) throw new Exception("Missing 'xsd:restriction' for field '{$fieldName}'");
  98. if (empty($c[2][0][1]['base'])) throw new Exception("Missing 'xsd:restriction/@base' for field '{$fieldName}'");
  99. // xsd:element/xsd:simpleType/xsd:restriction/xsd:string
  100. foreach ($c[2][0][2] as $tagRestriction) {
  101. // xsd:string/xsd:maxLength
  102. $val = $tagRestriction[1]['value'];
  103. if ('enumeration' == XML::getTagName($tagRestriction[0])) {
  104. $restrictions['enumeration'][$val] = $val;
  105. } else {
  106. $restrictions[XML::getTagName($tagRestriction[0])] = $val;
  107. }
  108. }
  109. } break;
  110. default: {
  111. DBG::log($c, 'array', "Not imeplemented element child '{$c[0]}'");
  112. }
  113. }
  114. }
  115. }
  116. return $restrictions;
  117. }
  118. public static function findElementAppInfo($docArray, $nodeArray) {
  119. $appInfo = [];
  120. $fieldName = self::findElementName($docArray, $nodeArray);
  121. if (!empty($nodeArray[2])) {
  122. foreach ($nodeArray[2] as $c) {
  123. switch (XML::getTagName($c[0])) {
  124. case 'annotation': { // skip xsd:element/xsd:annotation
  125. DBG::nicePrint($c, "TODO: xsd:annotation/xsd:appinfo '{$fieldName}'");
  126. // <xsd:annotation>
  127. // <xsd:appinfo>
  128. // <system_cache__appinfo:flat_relation_cache>
  129. // <system_cache__appinfo:source system_cache__appinfo:name="ID"
  130. // system_cache__appinfo:xpath="default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK/ID_PROCES"/>
  131. $prefix = 'system_cache__appinfo';
  132. foreach ($c[2] as $cc) {
  133. if ('appinfo' == XML::getTagName($cc[0])) {
  134. foreach ($cc[2] as $appTag) {
  135. $appInfo[XML::getTagName($appTag[0])] = XML::readAppInfoRecurse($docArray, $appTag);
  136. }
  137. }
  138. }
  139. } break;
  140. case 'simpleType': break; // skip xsd:element/xsd:simpleType @see findElementRestrictions
  141. default: {
  142. DBG::log($c, 'array', "Not imeplemented element child '{$c[0]}'");
  143. }
  144. }
  145. }
  146. }
  147. DBG::log($fieldName, 'array', "findElementAppInfo fieldName='{$fieldName}'");
  148. return $appInfo;
  149. }
  150. public static function getTagName($xsdName) {
  151. return (false !== ($pos = strpos($xsdName, ':')))
  152. ? substr($xsdName, $pos + 1)
  153. : $xsdName;
  154. }
  155. public static function readAppInfoRecurse($docArray, $nodeArray) {
  156. $appInfo = [];
  157. if (!empty($nodeArray[1])) foreach ($nodeArray[1] as $attrName => $attrVal) {
  158. $appInfo['@' . XML::getTagName($attrName)] = $attrVal;
  159. }
  160. if (!empty($nodeArray[2])) foreach ($nodeArray[2] as $appTag) {
  161. $appInfo[XML::getTagName($appTag[0])] = XML::readAppInfoRecurse($docArray, $appTag);
  162. }
  163. // TODO: text nodes
  164. return $appInfo;
  165. }
  166. public static function findFieldsFromSequence($docArray, $nodeArray) {
  167. if ($nodeArray[0] != 'xsd:sequence') throw new Exception("Error Parsing Schema - expected 'sequence'");
  168. $fields = [];
  169. foreach ($nodeArray[2] as $f) {
  170. if ($f[0] !== 'xsd:element') {
  171. DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
  172. continue;
  173. }
  174. $fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
  175. if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
  176. if ('__' === substr($fieldName, 0, 2)) continue;
  177. if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
  178. UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
  179. DBG::log($f, 'array', "Skipping not implemented field structure '{$fieldName}'");
  180. continue;
  181. }
  182. $fields[$fieldName] = [
  183. 'type' => XML::findElementType($docArray, $f),
  184. 'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
  185. 'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
  186. 'restrictions' => XML::findElementRestrictions($docArray, $f),
  187. 'appInfo' => XML::findElementAppInfo($docArray, $f),
  188. ];
  189. }
  190. return $fields;
  191. }
  192. public static function findFieldsFromComplexContent($docArray, $nodeArray) {
  193. // xsd:complexType / xsd:complexContent / xsd:restriction [ @base = "default_db__x3A__CRM_PROCES:CRM_PROCES" ]
  194. // xsd:complexType / xsd:complexContent / xsd:extension [ @base = "default_db__x3A__CRM_PROCES:CRM_PROCES" ]
  195. switch (XML::getTagName($nodeArray[2][0][0])) {
  196. case 'extension': return XML::findFieldsFromExtension($docArray, $nodeArray[2][0]);
  197. case 'restriction': return XML::findFieldsFromRestriction($docArray, $nodeArray[2][0]);
  198. }
  199. // TODO:? $xsdType['extensionBase'] = V::get('base', '', $nodeArray[1]);
  200. // TODO:? $xsdType['restrictionBase'] = V::get('base', '', $nodeArray[1]);
  201. }
  202. public static function findFieldsFromExtension($docArray, $nodeArray) {
  203. $fields = [];
  204. if ($nodeArray[2][0][0] != 'xsd:sequence') throw new Exception("Error Parsing Schema - expected 'complexType/complexContent/extension/sequence'");
  205. foreach ($nodeArray[2][0][2] as $f) {
  206. if ($f[0] !== 'xsd:element') {
  207. DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
  208. continue;
  209. }
  210. $fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
  211. if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
  212. if ('__' === substr($fieldName, 0, 2)) continue;
  213. if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
  214. UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
  215. continue;
  216. }
  217. $fields[$fieldName] = [
  218. 'type' => XML::findElementType($docArray, $f),
  219. 'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
  220. 'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
  221. 'restrictions' => XML::findElementRestrictions($docArray, $f),
  222. 'appInfo' => XML::findElementAppInfo($docArray, $f),
  223. ];
  224. }
  225. return $fields;
  226. }
  227. public static function findFieldsFromRestriction($docArray, $nodeArray) {
  228. $fields = [];
  229. if ($nodeArray[2][0][0] != 'xsd:sequence') throw new Exception("Error Parsing Schema - expected 'complexType/complexContent/restriction/sequence'");
  230. foreach ($nodeArray[2][0][2] as $f) {
  231. if ($f[0] !== 'xsd:element') {
  232. DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
  233. continue;
  234. }
  235. $fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
  236. if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
  237. if ('__' === substr($fieldName, 0, 2)) continue;
  238. if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
  239. UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
  240. continue;
  241. }
  242. $fields[$fieldName] = [
  243. 'type' => XML::findElementType($docArray, $f),
  244. 'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
  245. 'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
  246. 'restrictions' => XML::findElementRestrictions($docArray, $f),
  247. 'appInfo' => XML::findElementAppInfo($docArray, $f),
  248. ];
  249. }
  250. return $fields;
  251. }
  252. }