00001 <?php
00002
00003
00004
00005
00017 class XMLSchema extends nusoap_base {
00018
00019
00020 var $schema = '';
00021 var $xml = '';
00022
00023 var $enclosingNamespaces;
00024
00025 var $schemaInfo = array();
00026 var $schemaTargetNamespace = '';
00027
00028 var $attributes = array();
00029 var $complexTypes = array();
00030 var $currentComplexType = false;
00031 var $elements = array();
00032 var $currentElement = false;
00033 var $simpleTypes = array();
00034 var $currentSimpleType = false;
00035
00036 var $imports = array();
00037
00038 var $parser;
00039 var $position = 0;
00040 var $depth = 0;
00041 var $depth_array = array();
00042 var $message = array();
00043 var $defaultNamespace = array();
00044
00053 function XMLSchema($schema='',$xml='',$namespaces=array()){
00054
00055 $this->debug('xmlschema class instantiated, inside constructor');
00056
00057 $this->schema = $schema;
00058 $this->xml = $xml;
00059
00060
00061 $this->enclosingNamespaces = $namespaces;
00062 $this->namespaces = array_merge($this->namespaces, $namespaces);
00063
00064
00065 if($schema != ''){
00066 $this->debug('initial schema file: '.$schema);
00067 $this->parseFile($schema, 'schema');
00068 }
00069
00070
00071 if($xml != ''){
00072 $this->debug('initial xml file: '.$xml);
00073 $this->parseFile($xml, 'xml');
00074 }
00075
00076 }
00077
00086 function parseFile($xml,$type){
00087
00088 if($xml != ""){
00089 $xmlStr = @join("",@file($xml));
00090 if($xmlStr == ""){
00091 $msg = 'Error reading XML from '.$xml;
00092 $this->setError($msg);
00093 $this->debug($msg);
00094 return false;
00095 } else {
00096 $this->debug("parsing $xml");
00097 $this->parseString($xmlStr,$type);
00098 $this->debug("done parsing $xml");
00099 return true;
00100 }
00101 }
00102 return false;
00103 }
00104
00112 function parseString($xml,$type){
00113
00114 if($xml != ""){
00115
00116
00117 $this->parser = xml_parser_create();
00118
00119 xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0);
00120
00121
00122 xml_set_object($this->parser, $this);
00123
00124
00125 if($type == "schema"){
00126 xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement');
00127 xml_set_character_data_handler($this->parser,'schemaCharacterData');
00128 } elseif($type == "xml"){
00129 xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement');
00130 xml_set_character_data_handler($this->parser,'xmlCharacterData');
00131 }
00132
00133
00134 if(!xml_parse($this->parser,$xml,true)){
00135
00136 $errstr = sprintf('XML error parsing XML schema on line %d: %s',
00137 xml_get_current_line_number($this->parser),
00138 xml_error_string(xml_get_error_code($this->parser))
00139 );
00140 $this->debug($errstr);
00141 $this->debug("XML payload:\n" . $xml);
00142 $this->setError($errstr);
00143 }
00144
00145 xml_parser_free($this->parser);
00146 } else{
00147 $this->debug('no xml passed to parseString()!!');
00148 $this->setError('no xml passed to parseString()!!');
00149 }
00150 }
00151
00160 function schemaStartElement($parser, $name, $attrs) {
00161
00162
00163 $pos = $this->position++;
00164 $depth = $this->depth++;
00165
00166 $this->depth_array[$depth] = $pos;
00167 $this->message[$pos] = array('cdata' => '');
00168 if ($depth > 0) {
00169 $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]];
00170 } else {
00171 $this->defaultNamespace[$pos] = false;
00172 }
00173
00174
00175 if($prefix = $this->getPrefix($name)){
00176
00177 $name = $this->getLocalPart($name);
00178 } else {
00179 $prefix = '';
00180 }
00181
00182
00183 if(count($attrs) > 0){
00184 foreach($attrs as $k => $v){
00185
00186 if(ereg("^xmlns",$k)){
00187
00188
00189 if($ns_prefix = substr(strrchr($k,':'),1)){
00190
00191 $this->namespaces[$ns_prefix] = $v;
00192 } else {
00193 $this->defaultNamespace[$pos] = $v;
00194 if (! $this->getPrefixFromNamespace($v)) {
00195 $this->namespaces['ns'.(count($this->namespaces)+1)] = $v;
00196 }
00197 }
00198 if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema'){
00199 $this->XMLSchemaVersion = $v;
00200 $this->namespaces['xsi'] = $v.'-instance';
00201 }
00202 }
00203 }
00204 foreach($attrs as $k => $v){
00205
00206 $k = strpos($k,':') ? $this->expandQname($k) : $k;
00207 $v = strpos($v,':') ? $this->expandQname($v) : $v;
00208 $eAttrs[$k] = $v;
00209 }
00210 $attrs = $eAttrs;
00211 } else {
00212 $attrs = array();
00213 }
00214
00215 switch($name){
00216 case 'all':
00217 case 'choice':
00218 case 'sequence':
00219
00220 $this->complexTypes[$this->currentComplexType]['compositor'] = $name;
00221 if($name == 'all' || $name == 'sequence'){
00222 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
00223 }
00224 break;
00225 case 'attribute':
00226
00227 $this->xdebug("parsing attribute " . $this->varDump($attrs));
00228 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
00229 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
00230 if (!strpos($v, ':')) {
00231
00232 if ($this->defaultNamespace[$pos]) {
00233
00234 $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
00235 }
00236 }
00237 }
00238 if(isset($attrs['name'])){
00239 $this->attributes[$attrs['name']] = $attrs;
00240 $aname = $attrs['name'];
00241 } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){
00242 if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) {
00243 $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
00244 } else {
00245 $aname = '';
00246 }
00247 } elseif(isset($attrs['ref'])){
00248 $aname = $attrs['ref'];
00249 $this->attributes[$attrs['ref']] = $attrs;
00250 }
00251
00252 if(isset($this->currentComplexType)){
00253 $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs;
00254 } elseif(isset($this->currentElement)){
00255 $this->elements[$this->currentElement]['attrs'][$aname] = $attrs;
00256 }
00257
00258 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){
00259 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
00260 $prefix = $this->getPrefix($aname);
00261 if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
00262 $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'];
00263 } else {
00264 $v = '';
00265 }
00266 if(strpos($v,'[,]')){
00267 $this->complexTypes[$this->currentComplexType]['multidimensional'] = true;
00268 }
00269 $v = substr($v,0,strpos($v,'['));
00270 if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){
00271 $v = $this->XMLSchemaVersion.':'.$v;
00272 }
00273 $this->complexTypes[$this->currentComplexType]['arrayType'] = $v;
00274 }
00275 break;
00276 case 'complexType':
00277 if(isset($attrs['name'])){
00278 $this->xdebug('processing named complexType '.$attrs['name']);
00279 $this->currentElement = false;
00280 $this->currentComplexType = $attrs['name'];
00281 $this->complexTypes[$this->currentComplexType] = $attrs;
00282 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
00283 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
00284 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
00285 } else {
00286 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
00287 }
00288 }else{
00289 $this->xdebug('processing unnamed complexType for element '.$this->currentElement);
00290 $this->currentComplexType = $this->currentElement . '_ContainedType';
00291 $this->currentElement = false;
00292 $this->complexTypes[$this->currentComplexType] = $attrs;
00293 $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType';
00294 if(isset($attrs['base']) && ereg(':Array$',$attrs['base'])){
00295 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
00296 } else {
00297 $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct';
00298 }
00299 }
00300 break;
00301 case 'element':
00302
00303
00304
00305 if(isset($attrs['type'])){
00306 $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']);
00307 $this->currentElement = $attrs['name'];
00308 $this->elements[ $attrs['name'] ] = $attrs;
00309 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
00310 if (!isset($this->elements[ $attrs['name'] ]['form'])) {
00311 $this->elements[ $attrs['name'] ]['form'] = $this->schemaInfo['elementFormDefault'];
00312 }
00313 $ename = $attrs['name'];
00314 } elseif(isset($attrs['ref'])){
00315 $ename = $attrs['ref'];
00316 } else {
00317 $this->xdebug("processing untyped element ".$attrs['name']);
00318 $this->currentElement = $attrs['name'];
00319 $this->elements[ $attrs['name'] ] = $attrs;
00320 $this->elements[ $attrs['name'] ]['typeClass'] = 'element';
00321 $this->elements[ $attrs['name'] ]['type'] = $this->schemaTargetNamespace . ':' . $attrs['name'] . '_ContainedType';
00322 if (!isset($this->elements[ $attrs['name'] ]['form'])) {
00323 $this->elements[ $attrs['name'] ]['form'] = $this->schemaInfo['elementFormDefault'];
00324 }
00325 }
00326 if(isset($ename) && $this->currentComplexType){
00327 $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs;
00328 }
00329 break;
00330
00331
00332
00333 case 'import':
00334 if (isset($attrs['schemaLocation'])) {
00335
00336 $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false);
00337 } else {
00338
00339 $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true);
00340 if (! $this->getPrefixFromNamespace($attrs['namespace'])) {
00341 $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace'];
00342 }
00343 }
00344 break;
00345 case 'restriction':
00346
00347 if($this->currentElement){
00348 $this->elements[$this->currentElement]['type'] = $attrs['base'];
00349 } elseif($this->currentSimpleType){
00350 $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base'];
00351 } elseif($this->currentComplexType){
00352 $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base'];
00353 if(strstr($attrs['base'],':') == ':Array'){
00354 $this->complexTypes[$this->currentComplexType]['phpType'] = 'array';
00355 }
00356 }
00357 break;
00358 case 'schema':
00359 $this->schemaInfo = $attrs;
00360 $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix);
00361 if (isset($attrs['targetNamespace'])) {
00362 $this->schemaTargetNamespace = $attrs['targetNamespace'];
00363 }
00364 if (!isset($attrs['elementFormDefault'])) {
00365 $this->schemaInfo['elementFormDefault'] = 'unqualified';
00366 }
00367 break;
00368 case 'simpleType':
00369 if(isset($attrs['name'])){
00370 $this->xdebug("processing simpleType for name " . $attrs['name']);
00371 $this->currentSimpleType = $attrs['name'];
00372 $this->simpleTypes[ $attrs['name'] ] = $attrs;
00373 $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType';
00374 $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar';
00375 } else {
00376
00377
00378 }
00379 break;
00380 default:
00381
00382 }
00383 }
00384
00392 function schemaEndElement($parser, $name) {
00393
00394 $this->depth--;
00395
00396 if(isset($this->depth_array[$this->depth])){
00397 $pos = $this->depth_array[$this->depth];
00398 }
00399
00400 if($name == 'complexType'){
00401 $this->currentComplexType = false;
00402 $this->currentElement = false;
00403 }
00404 if($name == 'element'){
00405 $this->currentElement = false;
00406 }
00407 if($name == 'simpleType'){
00408 $this->currentSimpleType = false;
00409 }
00410 }
00411
00419 function schemaCharacterData($parser, $data){
00420 $pos = $this->depth_array[$this->depth - 1];
00421 $this->message[$pos]['cdata'] .= $data;
00422 }
00423
00429 function serializeSchema(){
00430
00431 $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion);
00432 $xml = '';
00433
00434 if (sizeof($this->imports) > 0) {
00435 foreach($this->imports as $ns => $list) {
00436 foreach ($list as $ii) {
00437 if ($ii['location'] != '') {
00438 $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n";
00439 } else {
00440 $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n";
00441 }
00442 }
00443 }
00444 }
00445
00446 foreach($this->complexTypes as $typeName => $attrs){
00447 $contentStr = '';
00448
00449 if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){
00450 foreach($attrs['elements'] as $element => $eParts){
00451 if(isset($eParts['ref'])){
00452 $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n";
00453 } else {
00454 $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\"/>\n";
00455 }
00456 }
00457 }
00458
00459 if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){
00460 foreach($attrs['attrs'] as $attr => $aParts){
00461 $contentStr .= " <$schemaPrefix:attribute ref=\"".$this->contractQName($aParts['ref']).'"';
00462 if(isset($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType'])){
00463 $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl'];
00464 $contentStr .= ' wsdl:arrayType="'.$this->contractQName($aParts['http://schemas.xmlsoap.org/wsdl/:arrayType']).'"';
00465 }
00466 $contentStr .= "/>\n";
00467 }
00468 }
00469
00470 if( isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){
00471 $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n";
00472 }
00473
00474 if(isset($attrs['compositor']) && ($attrs['compositor'] != '')){
00475 $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n";
00476 }
00477
00478 elseif((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){
00479 $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n";
00480 }
00481
00482 if($contentStr != ''){
00483 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n";
00484 } else {
00485 $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n";
00486 }
00487 $xml .= $contentStr;
00488 }
00489
00490 if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){
00491 foreach($this->simpleTypes as $typeName => $attr){
00492 $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <restriction base=\"".$this->contractQName($eParts['type'])."\"/>\n </$schemaPrefix:simpleType>";
00493 }
00494 }
00495
00496 if(isset($this->elements) && count($this->elements) > 0){
00497 foreach($this->elements as $element => $eParts){
00498 $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n";
00499 }
00500 }
00501
00502 if(isset($this->attributes) && count($this->attributes) > 0){
00503 foreach($this->attributes as $attr => $aParts){
00504 $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>";
00505 }
00506 }
00507
00508 $el = "<$schemaPrefix:schema targetNamespace=\"$this->schemaTargetNamespace\"\n";
00509 foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) {
00510 $el .= " xmlns:$nsp=\"$ns\"\n";
00511 }
00512 $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n";
00513 return $xml;
00514 }
00515
00522 function xdebug($string){
00523 $this->debug('<' . $this->schemaTargetNamespace . '> '.$string);
00524 }
00525
00537 function getPHPType($type,$ns){
00538 if(isset($this->typemap[$ns][$type])){
00539
00540 return $this->typemap[$ns][$type];
00541 } elseif(isset($this->complexTypes[$type])){
00542
00543 return $this->complexTypes[$type]['phpType'];
00544 }
00545 return false;
00546 }
00547
00564 function getTypeDef($type){
00565
00566 if(isset($this->complexTypes[$type])){
00567 $this->xdebug("in getTypeDef, found complexType $type");
00568 return $this->complexTypes[$type];
00569 } elseif(isset($this->simpleTypes[$type])){
00570 $this->xdebug("in getTypeDef, found simpleType $type");
00571 if (!isset($this->simpleTypes[$type]['phpType'])) {
00572
00573
00574 $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1);
00575 $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':'));
00576 $etype = $this->getTypeDef($uqType);
00577 if ($etype) {
00578 if (isset($etype['phpType'])) {
00579 $this->simpleTypes[$type]['phpType'] = $etype['phpType'];
00580 }
00581 if (isset($etype['elements'])) {
00582 $this->simpleTypes[$type]['elements'] = $etype['elements'];
00583 }
00584 }
00585 }
00586 return $this->simpleTypes[$type];
00587 } elseif(isset($this->elements[$type])){
00588 $this->xdebug("in getTypeDef, found element $type");
00589 if (!isset($this->elements[$type]['phpType'])) {
00590
00591 $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1);
00592 $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':'));
00593 $etype = $this->getTypeDef($uqType);
00594 if ($etype) {
00595 if (isset($etype['phpType'])) {
00596 $this->elements[$type]['phpType'] = $etype['phpType'];
00597 }
00598 if (isset($etype['elements'])) {
00599 $this->elements[$type]['elements'] = $etype['elements'];
00600 }
00601 } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') {
00602 $this->elements[$type]['phpType'] = 'scalar';
00603 }
00604 }
00605 return $this->elements[$type];
00606 } elseif(isset($this->attributes[$type])){
00607 $this->xdebug("in getTypeDef, found attribute $type");
00608 return $this->attributes[$type];
00609 }
00610 $this->xdebug("in getTypeDef, did not find $type");
00611 return false;
00612 }
00613
00621 function serializeTypeDef($type){
00622
00623 if($typeDef = $this->getTypeDef($type)){
00624 $str .= '<'.$type;
00625 if(is_array($typeDef['attrs'])){
00626 foreach($attrs as $attName => $data){
00627 $str .= " $attName=\"{type = ".$data['type']."}\"";
00628 }
00629 }
00630 $str .= " xmlns=\"".$this->schema['targetNamespace']."\"";
00631 if(count($typeDef['elements']) > 0){
00632 $str .= ">";
00633 foreach($typeDef['elements'] as $element => $eData){
00634 $str .= $this->serializeTypeDef($element);
00635 }
00636 $str .= "</$type>";
00637 } elseif($typeDef['typeClass'] == 'element') {
00638 $str .= "></$type>";
00639 } else {
00640 $str .= "/>";
00641 }
00642 return $str;
00643 }
00644 return false;
00645 }
00646
00656 function typeToForm($name,$type){
00657
00658 if($typeDef = $this->getTypeDef($type)){
00659
00660 if($typeDef['phpType'] == 'struct'){
00661 $buffer .= '<table>';
00662 foreach($typeDef['elements'] as $child => $childDef){
00663 $buffer .= "
00664 <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td>
00665 <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>";
00666 }
00667 $buffer .= '</table>';
00668
00669 } elseif($typeDef['phpType'] == 'array'){
00670 $buffer .= '<table>';
00671 for($i=0;$i < 3; $i++){
00672 $buffer .= "
00673 <tr><td align='right'>array item (type: $typeDef[arrayType]):</td>
00674 <td><input type='text' name='parameters[".$name."][]'></td></tr>";
00675 }
00676 $buffer .= '</table>';
00677
00678 } else {
00679 $buffer .= "<input type='text' name='parameters[$name]'>";
00680 }
00681 } else {
00682 $buffer .= "<input type='text' name='parameters[$name]'>";
00683 }
00684 return $buffer;
00685 }
00686
00727 function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){
00728 $this->complexTypes[$name] = array(
00729 'name' => $name,
00730 'typeClass' => $typeClass,
00731 'phpType' => $phpType,
00732 'compositor'=> $compositor,
00733 'restrictionBase' => $restrictionBase,
00734 'elements' => $elements,
00735 'attrs' => $attrs,
00736 'arrayType' => $arrayType
00737 );
00738
00739 $this->xdebug("addComplexType $name: " . $this->varDump($this->complexTypes[$name]));
00740 }
00741
00752 function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar') {
00753 $this->simpleTypes[$name] = array(
00754 'name' => $name,
00755 'typeClass' => $typeClass,
00756 'phpType' => $phpType,
00757 'type' => $restrictionBase
00758 );
00759
00760 $this->xdebug("addSimpleType $name: " . $this->varDump($this->simpleTypes[$name]));
00761 }
00762 }
00763
00764
00765
00766
00767 ?>