open($filePath)) throw new Exception("Failed to open ant schema file '{$fileName}'", 500);
return self::xmlReadToArray($z);
}
public static function xmlReadToArray($z) {
while ($z->read()) {
if (XMLReader::ELEMENT !== $z->nodeType) continue;
return self::xmlReadRecurse($z);
}
}
public static function xmlReadRecurse($z) {
$node = [ $z->name, [], [] ];// name, attrs, childrens
$depth = $z->depth;
$isEmpty = $z->isEmptyElement;
if ($z->hasAttributes) {
while ($z->moveToNextAttribute()) {
$node[1][$z->name] = $z->value;
}
}
if ($isEmpty) {
$node[2] = null;
return $node;
}
// switch ($z->name) {
// case 'xsd:complexType': $node[1]['name'] = $z->getAttribute('name'); break;
// case 'xsd:simpleType': $node[1]['name'] = $z->getAttribute('name'); break;
// case 'xsd:restriction': $node[1]['base'] = $z->getAttribute('base'); break;
// case 'xsd:enumeration': $node[1]['value'] = $z->getAttribute('value'); break;
// case 'xsd:element': $node[1] = [
// 'type' => $z->getAttribute('type')
// ]; break;
// case 'xsd:complexContent': break;
// case 'xsd:sequence': break;
// case 'xsd:attribute': $node[1]['base'] = $z->getAttribute('base'); break;
// case 'xsd:extension': $node[1]['base'] = $z->getAttribute('base'); break;
// default: UI::alert('warning', "TODO: read attributes from: d({$z->depth}) name($z->name)");
// }
while ($z->read() && $z->depth > $depth) {
if (XMLReader::ELEMENT !== $z->nodeType) continue;
// if ($z->depth == $depth + 2)
$node[2][] = self::xmlReadRecurse($z);
// else $node[2][] = "d({$z->depth}) name($z->name)";
}
return $node;
}
public static function printXmlFromArray($xml) {
$xmlWriter = new Core_XmlWriter();
$xmlWriter->openUri('php://output');
$xmlWriter->setIndent(true);
$xmlWriter->startDocument('1.0','UTF-8');
$xmlWriter->h($xml[0], $xml[1], $xml[2]);
$xmlWriter->endDocument();
}
public static function findTargetNamespace($docArray) {
return V::get('targetNamespace', '', $docArray[1]);
}
public static function findSimpleTypeNode($docArray, $name) {
foreach ($docArray[2] as $child) {
if ('simpleType' === self::getTagName($child[0])) {
if ($name === $child[1]['name']) {
return $child;
}
}
}
}
public static function findTargetNamespacePrefix($docArray) {
$tns = self::findTargetNamespace($docArray);
if (!$tns) throw new Exception("targetNamespace not defined");
foreach ($docArray[1] as $attr => $val) {
if ('xmlns:' !== substr($attr, 0, 6)) continue;
if ($tns === $val) return substr($attr, 6);
}
throw new Exception("Missing targetNamespace xmlns:...");
}
public static function findElementName($docArray, $nodeArray) {
if (!empty($nodeArray[1]['name'])) return $nodeArray[1]['name'];
if (!empty($nodeArray[1]['ref'])) return $nodeArray[1]['ref'];
throw new Exception("Missing xsd:element name");
}
public static function findElementType($docArray, $nodeArray) {
$fieldName = self::findElementName($docArray, $nodeArray);
if (!empty($nodeArray[1]['type'])) {
// TODO:TYPE_RECURCE: if local ns prefix then find correct typeName?
// TODO:TYPE_RECURCE:the same for restrictions
$type = $nodeArray[1]['type'];
DBG::log(['is_xs:' => ('xs:' === substr($type, 0, 3)), 'is_xsd:'=>('xsd:' === substr($type, 0, 4))], 'array', "DBG: findElementType field({$fieldName}) type({$type})");
if ($fixedType = self::tryConvertXsdTypeToXsdPrefix($type)) {
return $fixedType;
}
list($prefix, $name) = explode(':', $type);
if ($prefix === self::findTargetNamespacePrefix($docArray)) {
$simpleTypeNode = self::findSimpleTypeNode($docArray, $name);
DBG::log($simpleTypeNode, 'array', "\$simpleTypeNode \$fieldName='{$fieldName}' type='{$type}'");
if (!empty($simpleTypeNode[1]['type'])) {
throw new Exception("TODO: findElementType node/@type => 'xsd:simpleType/@type' = '{$simpleTypeNode[1]['type']}'");
}
if (!empty($simpleTypeNode[2][0]) && self::isXsdTag($simpleTypeNode[2][0][0], 'restriction')) {
$restrictionNode = $simpleTypeNode[2][0];
if (empty($restrictionNode[1]['base'])) throw new Exception("Missing xsd:restriction/@base (node/@type => xsd:simpleType/[@type='{$simpleTypeNode[1]['type']}']/xsd:restriction')");
$type = $restrictionNode[1]['base'];
DBG::log($type, 'array', "findElementType \$fieldName='{$fieldName}' type='{$nodeArray[1]['type']}' => type='{$type}'");
// check restrictions - if has enumeration then return 'p5:enum'
$isEnum = false;
if (!empty($restrictionNode[2])) foreach ($restrictionNode[2] as $restr) {
if ('enumeration' === self::getTagName($restr[0])) {
$isEnum = true;
break;
}
}
if ($isEnum) return 'p5:enum';
// TODO: recurse with limit
if ($fixedType = self::tryConvertXsdTypeToXsdPrefix($type)) {
return $fixedType;
}
list($prefix, $name) = explode(':', $type);
$simpleTypeNode = self::findSimpleTypeNode($docArray, $name);
DBG::log($simpleTypeNode, 'array', "\$simpleTypeNode \$fieldName='{$fieldName}' ... type='{$type}'");
if (!empty($simpleTypeNode[1]['type'])) {
throw new Exception("TODO: findElementType node/@type => 'xsd:simpleType/@type' = '{$simpleTypeNode[1]['type']}'");
}
if (!empty($simpleTypeNode[2][0]) && self::isXsdTag($simpleTypeNode[2][0][0], 'restriction')) {
$restrictionNode = $simpleTypeNode[2][0];
if (empty($restrictionNode[1]['base'])) throw new Exception("Missing xsd:restriction/@base (node/@type => xsd:simpleType/[@type='{$simpleTypeNode[1]['type']}']/xsd:restriction')");
$type = $restrictionNode[1]['base'];
DBG::log($type, 'array', "findElementType \$fieldName='{$fieldName}' ... type='{$type}'");
return self::convertXsdTypeToXsdPrefix($type);
}
// TODO: throw...
}
// TODO: throw...
// 0 => 'xsd:simpleType',
// 1 => [
// 'name' => 'PROCES_INIT_Simple',
// ],
// 2 => [
// 0 => [
// 0 => 'xsd:restriction',
// 1 => [
// 'base' => 'default_db__x3A__CRM_PROCES:TYPE_Simple',
// ],
// 2 => [
// 0 => [
// 0 => 'xsd:enumeration',
// 1 => [
// 'value' => 'PROCES_INIT',
// ],
// 2 => NULL,
} else {
return $nodeArray[1]['type'];
}
}
if (!empty($nodeArray[1]['ref'])) return 'ref:' . $nodeArray[1]['ref'];
if (empty($nodeArray[2])) throw new Exception("Missing xsd:element childrens - cannot find type");
// TODO: find in loop (xsd:annotation may accure)
if (empty($nodeArray[2][0][0]) || !self::isXsdTag($nodeArray[2][0][0], 'simpleType')) throw new Exception("Missing 'xsd:simpleType' for field '{$fieldName}'");
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}'");
if (empty($nodeArray[2][0][2][0][1]['base'])) throw new Exception("Missing 'xsd:restriction/@base' for field '{$fieldName}'");
$type = $nodeArray[2][0][2][0][1]['base'];
if ($fixedType = self::tryConvertXsdTypeToXsdPrefix($type)) {
return $fixedType;
}
DBG::log($nodeArray[2][0], 'array', "TODO:findElementType: local type with restriction field({$fieldName}) type({$type})");
// TODO: element local type with restriction
//
//
//
//
//
//
//
return $type;
}
public static function tryConvertXsdTypeToXsdPrefix($type) {
try {
$type = self::convertXsdTypeToXsdPrefix($type);
return $type;
} catch (Exception $e) {
DBG::log($e);
}
return false;
}
public static function convertXsdTypeToXsdPrefix($type) {
// TODO: validate if type is supported in object engine and gui
// TODO: prefix p5
list($prefix, $name) = explode(':', $type);
$prefix = ('xs' === $prefix) ? 'xsd' : $prefix;
if ('xsd' === $prefix && 'int' === $name) return 'xsd:integer';
if ('xsd' === $prefix && 'NCName' === $name) return 'xsd:string'; // TODO: restriction?
if ('xsd' === $prefix && 'NMTOKEN' === $name) return 'xsd:string'; // TODO: restriction?
if ('xsd' === $prefix) return implode(":", [ $prefix, $name ]);
if ('p5' === $prefix) return implode(":", [ $prefix, $name ]);
// if ('xs:' === substr($type, 0, 3)) return "xsd:" . substr($type, 3);
// if ('xsd:' === substr($type, 0, 4)) return $type;
throw new Exception("Not implemented type '{$type}'");
}
public static function findElementRestrictions($docArray, $nodeArray) {
$restrictions = [];
$fieldName = self::findElementName($docArray, $nodeArray);
DBG::log([ 'is_empty($nodeArray[2])' => empty($nodeArray[2]), 'is_empty_type' => empty($nodeArray[1]['type']) ], 'array', "DBG: findElementRestrictions field({$fieldName})");
if (!empty($nodeArray[1]['nillable']) && 'true' === $nodeArray[1]['nillable']) $restrictions['nillable'] = true;
if (!empty($nodeArray[2])) {
foreach ($nodeArray[2] as $c) {
switch (XML::getTagName($c[0])) {
case 'annotation': break; // skip xsd:element/xsd:annotation @see findElementAppInfo
case 'simpleType': $restrictions = XML::parseSimpleTypeRestrictions($docArray, $c, $fieldName);
default: {
DBG::log($c, 'array', "Not imeplemented element child '{$c[0]}'");
}
}
}
}
if (!empty($nodeArray[1]['type'])) {
list($prefix, $name) = explode(':', $nodeArray[1]['type']);
if ($prefix === XML::findTargetNamespacePrefix($docArray)) {
$simpleTypeNode = self::findSimpleTypeNode($docArray, $name);
return XML::parseSimpleTypeRestrictions($docArray, $simpleTypeNode, $fieldName);
}
}
return $restrictions;
}
public static function parseSimpleTypeRestrictions($docArray, $simpleTypeNode, $fieldName) { // $simpleTypeNode must be 'simpleType' === XML::getTagName($nodeArray[0]), expected 'xsd:restriction' child
DBG::log($simpleTypeNode, 'array', "DBG: parseSimpleTypeRestrictions \$simpleTypeNode for field '{$fieldName}'");
if (empty($simpleTypeNode[2])) return []; // Missing childrens in simpleType definition
$restrictionNode = null;
foreach ($simpleTypeNode[2] as $c) {
if ('restriction' == XML::getTagName($c[0])) {
$restrictionNode = $c;
break;
}
}
DBG::log($restrictionNode, 'array', "DBG: parseSimpleTypeRestrictions \$restrictionNode for field '{$fieldName}'");
if (empty($restrictionNode)) return []; // Missing xsd:restriction in simpleType definition
$restrictions = [];
if (empty($restrictionNode[1]['base'])) throw new Exception("Missing 'xsd:restriction/@base' for field '{$fieldName}'");
// xsd:simpleType/xsd:restriction/xsd:string
if (!empty($restrictionNode[2])) foreach ($restrictionNode[2] as $tagRestriction) {
// xsd:string/xsd:maxLength
$val = $tagRestriction[1]['value'];
if ('enumeration' == XML::getTagName($tagRestriction[0])) {
$restrictions['enumeration'][$val] = $val;
} else {
$restrictions[ XML::getTagName($tagRestriction[0]) ] = $val;
}
}
return $restrictions;
}
public static function findElementAppInfo($docArray, $nodeArray) {
$appInfo = [];
$fieldName = self::findElementName($docArray, $nodeArray);
if (!empty($nodeArray[2])) {
foreach ($nodeArray[2] as $c) {
switch (XML::getTagName($c[0])) {
case 'annotation': { // skip xsd:element/xsd:annotation
DBG::log($c, 'array', "xsd:annotation/xsd:appinfo '{$fieldName}'");
//
//
//
//
$prefix = 'system_cache__appinfo';
foreach ($c[2] as $cc) {
if ('appinfo' == XML::getTagName($cc[0])) {
foreach ($cc[2] as $appTag) {
$appInfo[ XML::getTagName($appTag[0]) ] = XML::readAppInfoRecurse($docArray, $appTag);
}
}
}
DBG::log($appInfo, 'array', "xsd:annotation/xsd:appinfo '{$fieldName}' \$appInfo");
//
//
//
//
} break;
case 'simpleType': break; // skip xsd:element/xsd:simpleType @see findElementRestrictions
default: {
DBG::log($c, 'array', "Not imeplemented element child '{$c[0]}'");
}
}
}
}
DBG::log($fieldName, 'array', "findElementAppInfo fieldName='{$fieldName}'");
return $appInfo;
}
public static function getTagName($xsdName) {
return (false !== ($pos = strpos($xsdName, ':')))
? substr($xsdName, $pos + 1)
: $xsdName;
}
public static function isXsdTag($xsdName, $expectedTagName) {
list($xsdPrefix, $tagName) = explode(':', $xsdName);
switch ($xsdPrefix) {
case 'xs':
case 'xsd': return ($tagName === $expectedTagName);
}
return false;
}
public static function readAppInfoRecurse($docArray, $nodeArray) {
$appInfo = [];
if (!empty($nodeArray[1])) foreach ($nodeArray[1] as $attrName => $attrVal) {
$appInfo['@' . XML::getTagName($attrName)] = $attrVal;
}
if (!empty($nodeArray[2])) foreach ($nodeArray[2] as $appTag) {
$appInfo[ XML::getTagName($appTag[0]) ] = XML::readAppInfoRecurse($docArray, $appTag);
}
// TODO: text nodes
return $appInfo;
}
public static function findFieldsFromSequence($docArray, $nodeArray) {
if (!self::isXsdTag($nodeArray[0], 'sequence')) throw new Exception("Error Parsing Schema - expected 'sequence'");
$fields = [];
foreach ($nodeArray[2] as $f) {
if (!self::isXsdTag($f[0], 'element')) {
DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
continue;
}
$fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
if ('__' === substr($fieldName, 0, 2)) continue;
if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
DBG::log($f, 'array', "Skipping not implemented field structure '{$fieldName}'");
continue;
}
$fields[$fieldName] = [
'type' => XML::findElementType($docArray, $f),
'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
'restrictions' => XML::findElementRestrictions($docArray, $f),
'appInfo' => XML::findElementAppInfo($docArray, $f),
];
}
return $fields;
}
public static function findFieldsFromComplexContent($docArray, $nodeArray) {
// xsd:complexType / xsd:complexContent / xsd:restriction [ @base = "default_db__x3A__CRM_PROCES:CRM_PROCES" ]
// xsd:complexType / xsd:complexContent / xsd:extension [ @base = "default_db__x3A__CRM_PROCES:CRM_PROCES" ]
switch (XML::getTagName($nodeArray[2][0][0])) {
case 'extension': return XML::findFieldsFromExtension($docArray, $nodeArray[2][0]);
case 'restriction': return XML::findFieldsFromRestriction($docArray, $nodeArray[2][0]);
}
// TODO:? $xsdType['extensionBase'] = V::get('base', '', $nodeArray[1]);
// TODO:? $xsdType['restrictionBase'] = V::get('base', '', $nodeArray[1]);
}
public static function findFieldsFromExtension($docArray, $nodeArray) {
$fields = [];
if (!self::isXsdTag($nodeArray[2][0][0], 'sequence')) throw new Exception("Error Parsing Schema - expected 'complexType/complexContent/extension/sequence'");
foreach ($nodeArray[2][0][2] as $f) {
if (!self::isXsdTag($f[0], 'element')) {
DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
continue;
}
$fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
if ('__' === substr($fieldName, 0, 2)) continue;
if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
continue;
}
$fields[$fieldName] = [
'type' => XML::findElementType($docArray, $f),
'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
'restrictions' => XML::findElementRestrictions($docArray, $f),
'appInfo' => XML::findElementAppInfo($docArray, $f),
];
}
return $fields;
}
public static function findFieldsFromRestriction($docArray, $nodeArray) {
$fields = [];
if (!self::isXsdTag($nodeArray[2][0][0], 'sequence')) throw new Exception("Error Parsing Schema - expected 'complexType/complexContent/restriction/sequence'");
foreach ($nodeArray[2][0][2] as $f) {
if (!self::isXsdTag($f[0], 'element')) {
DBG::log($n, 'array', "Schema xsd parse error - Not implemented node type '{$f[0]}'");
continue;
}
$fieldName = XML::findElementName($docArray, $f); // V::get('name', '', $f[1]);
if (!$fieldName) throw new Exception("Error Parsing Schema - expected 'element[@name]'");
if ('__' === substr($fieldName, 0, 2)) continue;
if (!V::get('type', '', $f[1]) && !V::get('ref', '', $f[1]) && empty($f[2])) {
UI::alert('danger', "Skipping not implemented field structure '{$fieldName}'");
continue;
}
$fields[$fieldName] = [
'type' => XML::findElementType($docArray, $f),
'minOccurs' => V::get('minOccurs', 0, $f[1], 'int'),
'maxOccurs' => V::get('maxOccurs', '1', $f[1]),
'restrictions' => XML::findElementRestrictions($docArray, $f),
'appInfo' => XML::findElementAppInfo($docArray, $f),
];
}
return $fields;
}
public static function getXsdTypeFromXsdSchema($xsdFilePath, $namespace, $name) {
$schema = XML::readXmlFileToArray($xsdFilePath);
if (empty($schema)) throw new Exception("Missing schema file for '{$namespace}'");
$xsdType = [ // find xsd:element with @name = $name
'nsPrefix' => null,
'name' => null,
'nsUri' => null,
'primaryKey' => null,
'targetNsUri' => V::get('targetNamespace', '', $schema[1])
];
if (!$xsdType['targetNsUri']) throw new Exception("Missing schema target namespace declaration '{$name}'");
foreach ($schema[2] as $n) {
if (!XML::isXsdTag($n[0], 'element')) continue;
if ($name != V::get('name', '', $n[1])) continue;
list($xsdType['nsPrefix'], $xsdType['name']) = explode(':', V::get('type', '', $n[1]));
}
if (!$xsdType['nsPrefix'] || !$xsdType['name']) throw new Exception("Missing schema root element name = '{$name}'");
$xsdType['nsUri'] = V::get("xmlns:{$xsdType['nsPrefix']}", '', $schema[1]);// find xmlns:default_objects = "https://biuro.biall-net.pl/wfs/default_objects"
if (!$xsdType['nsUri']) throw new Exception("Missing schema root element namespace declaration = '{$name}'");
if ($xsdType['nsUri'] != $xsdType['targetNsUri']) throw new Exception("TODO: type ns is not the same as targetNamespace '{$name}'");// TODO
foreach ($schema[2] as $n) {
if (!XML::isXsdTag($n[0], 'complexType')) continue;
if ($xsdType['name'] != V::get('name', '', $n[1])) continue;
DBG::log($n, 'array', "find system_cache__appinfo:primaryKey from complexType");
if (array_key_exists('system_cache__appinfo:primaryKey', $n[1])) {
$xsdType['primaryKey'] = V::get('system_cache__appinfo:primaryKey', '', $n[1]);
}
// complexType/sequence/element
// complexType/complexContent/extension[base=...]/sequence/element
switch ($n[2][0][0]) {
case 'xs:sequence':
case 'xsd:sequence': $xsdType['struct'] = XML::findFieldsFromSequence($schema, $n[2][0]); break;
case 'xs:complexContent':
case 'xsd:complexContent': $xsdType['struct'] = XML::findFieldsFromComplexContent($schema, $n[2][0]); break;
case 'xs:annotation':
case 'xsd:annotation': {
switch ($n[2][1][0]) {
case 'xs:sequence':
case 'xsd:sequence': $xsdType['struct'] = XML::findFieldsFromSequence($schema, $n[2][1]); break;
case 'xs:complexContent':
case 'xsd:complexContent': $xsdType['struct'] = XML::findFieldsFromComplexContent($schema, $n[2][1]); break;
}
} break;
}
// if ($n[2][0][0] != 'xsd:complexContent') throw new Exception("Error Parsing Schema - expected 'complexType/complexContent'");
}
if (empty($xsdType['primaryKey'])) {
foreach ($xsdType['struct'] as $fieldName => $field) {
if ('ID' === strtoupper($fieldName)) {
$xsdType['primaryKey'] = $fieldName;
break;
}
}
}
if (empty($xsdType['primaryKey'])) throw new Exception("Missing primaryKey for schema '{$namespace}'");
return $xsdType;
}
}