Commit | Line | Data |
---|---|---|
696f20d5 MS |
1 | <?php |
2 | ||
3 | ||
4 | ||
5 | ||
6 | /** | |
7 | * parses an XML Schema, allows access to it's data, other utility methods. | |
8 | * imperfect, no validation... yet, but quite functional. | |
9 | * | |
10 | * @author Dietrich Ayala <dietrich@ganx4.com> | |
11 | * @author Scott Nichol <snichol@users.sourceforge.net> | |
12 | * @version $Id: class.xmlschema.php,v 1.53 2010/04/26 20:15:08 snichol Exp $ | |
13 | * @access public | |
14 | */ | |
15 | class nusoap_xmlschema extends nusoap_base { | |
16 | ||
17 | // files | |
18 | var $schema = ''; | |
19 | var $xml = ''; | |
20 | // namespaces | |
21 | var $enclosingNamespaces; | |
22 | // schema info | |
23 | var $schemaInfo = array(); | |
24 | var $schemaTargetNamespace = ''; | |
25 | // types, elements, attributes defined by the schema | |
26 | var $attributes = array(); | |
27 | var $complexTypes = array(); | |
28 | var $complexTypeStack = array(); | |
29 | var $currentComplexType = null; | |
30 | var $elements = array(); | |
31 | var $elementStack = array(); | |
32 | var $currentElement = null; | |
33 | var $simpleTypes = array(); | |
34 | var $simpleTypeStack = array(); | |
35 | var $currentSimpleType = null; | |
36 | // imports | |
37 | var $imports = array(); | |
38 | // parser vars | |
39 | var $parser; | |
40 | var $position = 0; | |
41 | var $depth = 0; | |
42 | var $depth_array = array(); | |
43 | var $message = array(); | |
44 | var $defaultNamespace = array(); | |
45 | ||
46 | /** | |
47 | * constructor | |
48 | * | |
49 | * @param string $schema schema document URI | |
50 | * @param string $xml xml document URI | |
51 | * @param string $namespaces namespaces defined in enclosing XML | |
52 | * @access public | |
53 | */ | |
54 | function nusoap_xmlschema($schema='',$xml='',$namespaces=array()){ | |
55 | parent::nusoap_base(); | |
56 | $this->debug('nusoap_xmlschema class instantiated, inside constructor'); | |
57 | // files | |
58 | $this->schema = $schema; | |
59 | $this->xml = $xml; | |
60 | ||
61 | // namespaces | |
62 | $this->enclosingNamespaces = $namespaces; | |
63 | $this->namespaces = array_merge($this->namespaces, $namespaces); | |
64 | ||
65 | // parse schema file | |
66 | if($schema != ''){ | |
67 | $this->debug('initial schema file: '.$schema); | |
68 | $this->parseFile($schema, 'schema'); | |
69 | } | |
70 | ||
71 | // parse xml file | |
72 | if($xml != ''){ | |
73 | $this->debug('initial xml file: '.$xml); | |
74 | $this->parseFile($xml, 'xml'); | |
75 | } | |
76 | ||
77 | } | |
78 | ||
79 | /** | |
80 | * parse an XML file | |
81 | * | |
82 | * @param string $xml path/URL to XML file | |
83 | * @param string $type (schema | xml) | |
84 | * @return boolean | |
85 | * @access public | |
86 | */ | |
87 | function parseFile($xml,$type){ | |
88 | // parse xml file | |
89 | if($xml != ""){ | |
90 | $xmlStr = @join("",@file($xml)); | |
91 | if($xmlStr == ""){ | |
92 | $msg = 'Error reading XML from '.$xml; | |
93 | $this->setError($msg); | |
94 | $this->debug($msg); | |
95 | return false; | |
96 | } else { | |
97 | $this->debug("parsing $xml"); | |
98 | $this->parseString($xmlStr,$type); | |
99 | $this->debug("done parsing $xml"); | |
100 | return true; | |
101 | } | |
102 | } | |
103 | return false; | |
104 | } | |
105 | ||
106 | /** | |
107 | * parse an XML string | |
108 | * | |
109 | * @param string $xml path or URL | |
110 | * @param string $type (schema|xml) | |
111 | * @access private | |
112 | */ | |
113 | function parseString($xml,$type){ | |
114 | // parse xml string | |
115 | if($xml != ""){ | |
116 | ||
117 | // Create an XML parser. | |
118 | $this->parser = xml_parser_create(); | |
119 | // Set the options for parsing the XML data. | |
120 | xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, 0); | |
121 | ||
122 | // Set the object for the parser. | |
123 | xml_set_object($this->parser, $this); | |
124 | ||
125 | // Set the element handlers for the parser. | |
126 | if($type == "schema"){ | |
127 | xml_set_element_handler($this->parser, 'schemaStartElement','schemaEndElement'); | |
128 | xml_set_character_data_handler($this->parser,'schemaCharacterData'); | |
129 | } elseif($type == "xml"){ | |
130 | xml_set_element_handler($this->parser, 'xmlStartElement','xmlEndElement'); | |
131 | xml_set_character_data_handler($this->parser,'xmlCharacterData'); | |
132 | } | |
133 | ||
134 | // Parse the XML file. | |
135 | if(!xml_parse($this->parser,$xml,true)){ | |
136 | // Display an error message. | |
137 | $errstr = sprintf('XML error parsing XML schema on line %d: %s', | |
138 | xml_get_current_line_number($this->parser), | |
139 | xml_error_string(xml_get_error_code($this->parser)) | |
140 | ); | |
141 | $this->debug($errstr); | |
142 | $this->debug("XML payload:\n" . $xml); | |
143 | $this->setError($errstr); | |
144 | } | |
145 | ||
146 | xml_parser_free($this->parser); | |
147 | } else{ | |
148 | $this->debug('no xml passed to parseString()!!'); | |
149 | $this->setError('no xml passed to parseString()!!'); | |
150 | } | |
151 | } | |
152 | ||
153 | /** | |
154 | * gets a type name for an unnamed type | |
155 | * | |
156 | * @param string Element name | |
157 | * @return string A type name for an unnamed type | |
158 | * @access private | |
159 | */ | |
160 | function CreateTypeName($ename) { | |
161 | $scope = ''; | |
162 | for ($i = 0; $i < count($this->complexTypeStack); $i++) { | |
163 | $scope .= $this->complexTypeStack[$i] . '_'; | |
164 | } | |
165 | return $scope . $ename . '_ContainedType'; | |
166 | } | |
167 | ||
168 | /** | |
169 | * start-element handler | |
170 | * | |
171 | * @param string $parser XML parser object | |
172 | * @param string $name element name | |
173 | * @param string $attrs associative array of attributes | |
174 | * @access private | |
175 | */ | |
176 | function schemaStartElement($parser, $name, $attrs) { | |
177 | ||
178 | // position in the total number of elements, starting from 0 | |
179 | $pos = $this->position++; | |
180 | $depth = $this->depth++; | |
181 | // set self as current value for this depth | |
182 | $this->depth_array[$depth] = $pos; | |
183 | $this->message[$pos] = array('cdata' => ''); | |
184 | if ($depth > 0) { | |
185 | $this->defaultNamespace[$pos] = $this->defaultNamespace[$this->depth_array[$depth - 1]]; | |
186 | } else { | |
187 | $this->defaultNamespace[$pos] = false; | |
188 | } | |
189 | ||
190 | // get element prefix | |
191 | if($prefix = $this->getPrefix($name)){ | |
192 | // get unqualified name | |
193 | $name = $this->getLocalPart($name); | |
194 | } else { | |
195 | $prefix = ''; | |
196 | } | |
197 | ||
198 | // loop thru attributes, expanding, and registering namespace declarations | |
199 | if(count($attrs) > 0){ | |
200 | foreach($attrs as $k => $v){ | |
201 | // if ns declarations, add to class level array of valid namespaces | |
202 | if(preg_match('/^xmlns/',$k)){ | |
203 | //$this->xdebug("$k: $v"); | |
204 | //$this->xdebug('ns_prefix: '.$this->getPrefix($k)); | |
205 | if($ns_prefix = substr(strrchr($k,':'),1)){ | |
206 | //$this->xdebug("Add namespace[$ns_prefix] = $v"); | |
207 | $this->namespaces[$ns_prefix] = $v; | |
208 | } else { | |
209 | $this->defaultNamespace[$pos] = $v; | |
210 | if (! $this->getPrefixFromNamespace($v)) { | |
211 | $this->namespaces['ns'.(count($this->namespaces)+1)] = $v; | |
212 | } | |
213 | } | |
214 | if($v == 'http://www.w3.org/2001/XMLSchema' || $v == 'http://www.w3.org/1999/XMLSchema' || $v == 'http://www.w3.org/2000/10/XMLSchema'){ | |
215 | $this->XMLSchemaVersion = $v; | |
216 | $this->namespaces['xsi'] = $v.'-instance'; | |
217 | } | |
218 | } | |
219 | } | |
220 | foreach($attrs as $k => $v){ | |
221 | // expand each attribute | |
222 | $k = strpos($k,':') ? $this->expandQname($k) : $k; | |
223 | $v = strpos($v,':') ? $this->expandQname($v) : $v; | |
224 | $eAttrs[$k] = $v; | |
225 | } | |
226 | $attrs = $eAttrs; | |
227 | } else { | |
228 | $attrs = array(); | |
229 | } | |
230 | // find status, register data | |
231 | switch($name){ | |
232 | case 'all': // (optional) compositor content for a complexType | |
233 | case 'choice': | |
234 | case 'group': | |
235 | case 'sequence': | |
236 | //$this->xdebug("compositor $name for currentComplexType: $this->currentComplexType and currentElement: $this->currentElement"); | |
237 | $this->complexTypes[$this->currentComplexType]['compositor'] = $name; | |
238 | //if($name == 'all' || $name == 'sequence'){ | |
239 | // $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; | |
240 | //} | |
241 | break; | |
242 | case 'attribute': // complexType attribute | |
243 | //$this->xdebug("parsing attribute $attrs[name] $attrs[ref] of value: ".$attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']); | |
244 | $this->xdebug("parsing attribute:"); | |
245 | $this->appendDebug($this->varDump($attrs)); | |
246 | if (!isset($attrs['form'])) { | |
247 | // TODO: handle globals | |
248 | $attrs['form'] = $this->schemaInfo['attributeFormDefault']; | |
249 | } | |
250 | if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { | |
251 | $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; | |
252 | if (!strpos($v, ':')) { | |
253 | // no namespace in arrayType attribute value... | |
254 | if ($this->defaultNamespace[$pos]) { | |
255 | // ...so use the default | |
256 | $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'] = $this->defaultNamespace[$pos] . ':' . $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; | |
257 | } | |
258 | } | |
259 | } | |
260 | if(isset($attrs['name'])){ | |
261 | $this->attributes[$attrs['name']] = $attrs; | |
262 | $aname = $attrs['name']; | |
263 | } elseif(isset($attrs['ref']) && $attrs['ref'] == 'http://schemas.xmlsoap.org/soap/encoding/:arrayType'){ | |
264 | if (isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])) { | |
265 | $aname = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; | |
266 | } else { | |
267 | $aname = ''; | |
268 | } | |
269 | } elseif(isset($attrs['ref'])){ | |
270 | $aname = $attrs['ref']; | |
271 | $this->attributes[$attrs['ref']] = $attrs; | |
272 | } | |
273 | ||
274 | if($this->currentComplexType){ // This should *always* be | |
275 | $this->complexTypes[$this->currentComplexType]['attrs'][$aname] = $attrs; | |
276 | } | |
277 | // arrayType attribute | |
278 | if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']) || $this->getLocalPart($aname) == 'arrayType'){ | |
279 | $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; | |
280 | $prefix = $this->getPrefix($aname); | |
281 | if(isset($attrs['http://schemas.xmlsoap.org/wsdl/:arrayType'])){ | |
282 | $v = $attrs['http://schemas.xmlsoap.org/wsdl/:arrayType']; | |
283 | } else { | |
284 | $v = ''; | |
285 | } | |
286 | if(strpos($v,'[,]')){ | |
287 | $this->complexTypes[$this->currentComplexType]['multidimensional'] = true; | |
288 | } | |
289 | $v = substr($v,0,strpos($v,'[')); // clip the [] | |
290 | if(!strpos($v,':') && isset($this->typemap[$this->XMLSchemaVersion][$v])){ | |
291 | $v = $this->XMLSchemaVersion.':'.$v; | |
292 | } | |
293 | $this->complexTypes[$this->currentComplexType]['arrayType'] = $v; | |
294 | } | |
295 | break; | |
296 | case 'complexContent': // (optional) content for a complexType | |
297 | $this->xdebug("do nothing for element $name"); | |
298 | break; | |
299 | case 'complexType': | |
300 | array_push($this->complexTypeStack, $this->currentComplexType); | |
301 | if(isset($attrs['name'])){ | |
302 | // TODO: what is the scope of named complexTypes that appear | |
303 | // nested within other c complexTypes? | |
304 | $this->xdebug('processing named complexType '.$attrs['name']); | |
305 | //$this->currentElement = false; | |
306 | $this->currentComplexType = $attrs['name']; | |
307 | $this->complexTypes[$this->currentComplexType] = $attrs; | |
308 | $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; | |
309 | // This is for constructs like | |
310 | // <complexType name="ListOfString" base="soap:Array"> | |
311 | // <sequence> | |
312 | // <element name="string" type="xsd:string" | |
313 | // minOccurs="0" maxOccurs="unbounded" /> | |
314 | // </sequence> | |
315 | // </complexType> | |
316 | if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){ | |
317 | $this->xdebug('complexType is unusual array'); | |
318 | $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; | |
319 | } else { | |
320 | $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; | |
321 | } | |
322 | } else { | |
323 | $name = $this->CreateTypeName($this->currentElement); | |
324 | $this->xdebug('processing unnamed complexType for element ' . $this->currentElement . ' named ' . $name); | |
325 | $this->currentComplexType = $name; | |
326 | //$this->currentElement = false; | |
327 | $this->complexTypes[$this->currentComplexType] = $attrs; | |
328 | $this->complexTypes[$this->currentComplexType]['typeClass'] = 'complexType'; | |
329 | // This is for constructs like | |
330 | // <complexType name="ListOfString" base="soap:Array"> | |
331 | // <sequence> | |
332 | // <element name="string" type="xsd:string" | |
333 | // minOccurs="0" maxOccurs="unbounded" /> | |
334 | // </sequence> | |
335 | // </complexType> | |
336 | if(isset($attrs['base']) && preg_match('/:Array$/',$attrs['base'])){ | |
337 | $this->xdebug('complexType is unusual array'); | |
338 | $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; | |
339 | } else { | |
340 | $this->complexTypes[$this->currentComplexType]['phpType'] = 'struct'; | |
341 | } | |
342 | } | |
343 | $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'false'; | |
344 | break; | |
345 | case 'element': | |
346 | array_push($this->elementStack, $this->currentElement); | |
347 | if (!isset($attrs['form'])) { | |
348 | if ($this->currentComplexType) { | |
349 | $attrs['form'] = $this->schemaInfo['elementFormDefault']; | |
350 | } else { | |
351 | // global | |
352 | $attrs['form'] = 'qualified'; | |
353 | } | |
354 | } | |
355 | if(isset($attrs['type'])){ | |
356 | $this->xdebug("processing typed element ".$attrs['name']." of type ".$attrs['type']); | |
357 | if (! $this->getPrefix($attrs['type'])) { | |
358 | if ($this->defaultNamespace[$pos]) { | |
359 | $attrs['type'] = $this->defaultNamespace[$pos] . ':' . $attrs['type']; | |
360 | $this->xdebug('used default namespace to make type ' . $attrs['type']); | |
361 | } | |
362 | } | |
363 | // This is for constructs like | |
364 | // <complexType name="ListOfString" base="soap:Array"> | |
365 | // <sequence> | |
366 | // <element name="string" type="xsd:string" | |
367 | // minOccurs="0" maxOccurs="unbounded" /> | |
368 | // </sequence> | |
369 | // </complexType> | |
370 | if ($this->currentComplexType && $this->complexTypes[$this->currentComplexType]['phpType'] == 'array') { | |
371 | $this->xdebug('arrayType for unusual array is ' . $attrs['type']); | |
372 | $this->complexTypes[$this->currentComplexType]['arrayType'] = $attrs['type']; | |
373 | } | |
374 | $this->currentElement = $attrs['name']; | |
375 | $ename = $attrs['name']; | |
376 | } elseif(isset($attrs['ref'])){ | |
377 | $this->xdebug("processing element as ref to ".$attrs['ref']); | |
378 | $this->currentElement = "ref to ".$attrs['ref']; | |
379 | $ename = $this->getLocalPart($attrs['ref']); | |
380 | } else { | |
381 | $type = $this->CreateTypeName($this->currentComplexType . '_' . $attrs['name']); | |
382 | $this->xdebug("processing untyped element " . $attrs['name'] . ' type ' . $type); | |
383 | $this->currentElement = $attrs['name']; | |
384 | $attrs['type'] = $this->schemaTargetNamespace . ':' . $type; | |
385 | $ename = $attrs['name']; | |
386 | } | |
387 | if (isset($ename) && $this->currentComplexType) { | |
388 | $this->xdebug("add element $ename to complexType $this->currentComplexType"); | |
389 | $this->complexTypes[$this->currentComplexType]['elements'][$ename] = $attrs; | |
390 | } elseif (!isset($attrs['ref'])) { | |
391 | $this->xdebug("add element $ename to elements array"); | |
392 | $this->elements[ $attrs['name'] ] = $attrs; | |
393 | $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; | |
394 | } | |
395 | break; | |
396 | case 'enumeration': // restriction value list member | |
397 | $this->xdebug('enumeration ' . $attrs['value']); | |
398 | if ($this->currentSimpleType) { | |
399 | $this->simpleTypes[$this->currentSimpleType]['enumeration'][] = $attrs['value']; | |
400 | } elseif ($this->currentComplexType) { | |
401 | $this->complexTypes[$this->currentComplexType]['enumeration'][] = $attrs['value']; | |
402 | } | |
403 | break; | |
404 | case 'extension': // simpleContent or complexContent type extension | |
405 | $this->xdebug('extension ' . $attrs['base']); | |
406 | if ($this->currentComplexType) { | |
407 | $ns = $this->getPrefix($attrs['base']); | |
408 | if ($ns == '') { | |
409 | $this->complexTypes[$this->currentComplexType]['extensionBase'] = $this->schemaTargetNamespace . ':' . $attrs['base']; | |
410 | } else { | |
411 | $this->complexTypes[$this->currentComplexType]['extensionBase'] = $attrs['base']; | |
412 | } | |
413 | } else { | |
414 | $this->xdebug('no current complexType to set extensionBase'); | |
415 | } | |
416 | break; | |
417 | case 'import': | |
418 | if (isset($attrs['schemaLocation'])) { | |
419 | $this->xdebug('import namespace ' . $attrs['namespace'] . ' from ' . $attrs['schemaLocation']); | |
420 | $this->imports[$attrs['namespace']][] = array('location' => $attrs['schemaLocation'], 'loaded' => false); | |
421 | } else { | |
422 | $this->xdebug('import namespace ' . $attrs['namespace']); | |
423 | $this->imports[$attrs['namespace']][] = array('location' => '', 'loaded' => true); | |
424 | if (! $this->getPrefixFromNamespace($attrs['namespace'])) { | |
425 | $this->namespaces['ns'.(count($this->namespaces)+1)] = $attrs['namespace']; | |
426 | } | |
427 | } | |
428 | break; | |
429 | case 'include': | |
430 | if (isset($attrs['schemaLocation'])) { | |
431 | $this->xdebug('include into namespace ' . $this->schemaTargetNamespace . ' from ' . $attrs['schemaLocation']); | |
432 | $this->imports[$this->schemaTargetNamespace][] = array('location' => $attrs['schemaLocation'], 'loaded' => false); | |
433 | } else { | |
434 | $this->xdebug('ignoring invalid XML Schema construct: include without schemaLocation attribute'); | |
435 | } | |
436 | break; | |
437 | case 'list': // simpleType value list | |
438 | $this->xdebug("do nothing for element $name"); | |
439 | break; | |
440 | case 'restriction': // simpleType, simpleContent or complexContent value restriction | |
441 | $this->xdebug('restriction ' . $attrs['base']); | |
442 | if($this->currentSimpleType){ | |
443 | $this->simpleTypes[$this->currentSimpleType]['type'] = $attrs['base']; | |
444 | } elseif($this->currentComplexType){ | |
445 | $this->complexTypes[$this->currentComplexType]['restrictionBase'] = $attrs['base']; | |
446 | if(strstr($attrs['base'],':') == ':Array'){ | |
447 | $this->complexTypes[$this->currentComplexType]['phpType'] = 'array'; | |
448 | } | |
449 | } | |
450 | break; | |
451 | case 'schema': | |
452 | $this->schemaInfo = $attrs; | |
453 | $this->schemaInfo['schemaVersion'] = $this->getNamespaceFromPrefix($prefix); | |
454 | if (isset($attrs['targetNamespace'])) { | |
455 | $this->schemaTargetNamespace = $attrs['targetNamespace']; | |
456 | } | |
457 | if (!isset($attrs['elementFormDefault'])) { | |
458 | $this->schemaInfo['elementFormDefault'] = 'unqualified'; | |
459 | } | |
460 | if (!isset($attrs['attributeFormDefault'])) { | |
461 | $this->schemaInfo['attributeFormDefault'] = 'unqualified'; | |
462 | } | |
463 | break; | |
464 | case 'simpleContent': // (optional) content for a complexType | |
465 | if ($this->currentComplexType) { // This should *always* be | |
466 | $this->complexTypes[$this->currentComplexType]['simpleContent'] = 'true'; | |
467 | } else { | |
468 | $this->xdebug("do nothing for element $name because there is no current complexType"); | |
469 | } | |
470 | break; | |
471 | case 'simpleType': | |
472 | array_push($this->simpleTypeStack, $this->currentSimpleType); | |
473 | if(isset($attrs['name'])){ | |
474 | $this->xdebug("processing simpleType for name " . $attrs['name']); | |
475 | $this->currentSimpleType = $attrs['name']; | |
476 | $this->simpleTypes[ $attrs['name'] ] = $attrs; | |
477 | $this->simpleTypes[ $attrs['name'] ]['typeClass'] = 'simpleType'; | |
478 | $this->simpleTypes[ $attrs['name'] ]['phpType'] = 'scalar'; | |
479 | } else { | |
480 | $name = $this->CreateTypeName($this->currentComplexType . '_' . $this->currentElement); | |
481 | $this->xdebug('processing unnamed simpleType for element ' . $this->currentElement . ' named ' . $name); | |
482 | $this->currentSimpleType = $name; | |
483 | //$this->currentElement = false; | |
484 | $this->simpleTypes[$this->currentSimpleType] = $attrs; | |
485 | $this->simpleTypes[$this->currentSimpleType]['phpType'] = 'scalar'; | |
486 | } | |
487 | break; | |
488 | case 'union': // simpleType type list | |
489 | $this->xdebug("do nothing for element $name"); | |
490 | break; | |
491 | default: | |
492 | $this->xdebug("do not have any logic to process element $name"); | |
493 | } | |
494 | } | |
495 | ||
496 | /** | |
497 | * end-element handler | |
498 | * | |
499 | * @param string $parser XML parser object | |
500 | * @param string $name element name | |
501 | * @access private | |
502 | */ | |
503 | function schemaEndElement($parser, $name) { | |
504 | // bring depth down a notch | |
505 | $this->depth--; | |
506 | // position of current element is equal to the last value left in depth_array for my depth | |
507 | if(isset($this->depth_array[$this->depth])){ | |
508 | $pos = $this->depth_array[$this->depth]; | |
509 | } | |
510 | // get element prefix | |
511 | if ($prefix = $this->getPrefix($name)){ | |
512 | // get unqualified name | |
513 | $name = $this->getLocalPart($name); | |
514 | } else { | |
515 | $prefix = ''; | |
516 | } | |
517 | // move on... | |
518 | if($name == 'complexType'){ | |
519 | $this->xdebug('done processing complexType ' . ($this->currentComplexType ? $this->currentComplexType : '(unknown)')); | |
520 | $this->xdebug($this->varDump($this->complexTypes[$this->currentComplexType])); | |
521 | $this->currentComplexType = array_pop($this->complexTypeStack); | |
522 | //$this->currentElement = false; | |
523 | } | |
524 | if($name == 'element'){ | |
525 | $this->xdebug('done processing element ' . ($this->currentElement ? $this->currentElement : '(unknown)')); | |
526 | $this->currentElement = array_pop($this->elementStack); | |
527 | } | |
528 | if($name == 'simpleType'){ | |
529 | $this->xdebug('done processing simpleType ' . ($this->currentSimpleType ? $this->currentSimpleType : '(unknown)')); | |
530 | $this->xdebug($this->varDump($this->simpleTypes[$this->currentSimpleType])); | |
531 | $this->currentSimpleType = array_pop($this->simpleTypeStack); | |
532 | } | |
533 | } | |
534 | ||
535 | /** | |
536 | * element content handler | |
537 | * | |
538 | * @param string $parser XML parser object | |
539 | * @param string $data element content | |
540 | * @access private | |
541 | */ | |
542 | function schemaCharacterData($parser, $data){ | |
543 | $pos = $this->depth_array[$this->depth - 1]; | |
544 | $this->message[$pos]['cdata'] .= $data; | |
545 | } | |
546 | ||
547 | /** | |
548 | * serialize the schema | |
549 | * | |
550 | * @access public | |
551 | */ | |
552 | function serializeSchema(){ | |
553 | ||
554 | $schemaPrefix = $this->getPrefixFromNamespace($this->XMLSchemaVersion); | |
555 | $xml = ''; | |
556 | // imports | |
557 | if (sizeof($this->imports) > 0) { | |
558 | foreach($this->imports as $ns => $list) { | |
559 | foreach ($list as $ii) { | |
560 | if ($ii['location'] != '') { | |
561 | $xml .= " <$schemaPrefix:import location=\"" . $ii['location'] . '" namespace="' . $ns . "\" />\n"; | |
562 | } else { | |
563 | $xml .= " <$schemaPrefix:import namespace=\"" . $ns . "\" />\n"; | |
564 | } | |
565 | } | |
566 | } | |
567 | } | |
568 | // complex types | |
569 | foreach($this->complexTypes as $typeName => $attrs){ | |
570 | $contentStr = ''; | |
571 | // serialize child elements | |
572 | if(isset($attrs['elements']) && (count($attrs['elements']) > 0)){ | |
573 | foreach($attrs['elements'] as $element => $eParts){ | |
574 | if(isset($eParts['ref'])){ | |
575 | $contentStr .= " <$schemaPrefix:element ref=\"$element\"/>\n"; | |
576 | } else { | |
577 | $contentStr .= " <$schemaPrefix:element name=\"$element\" type=\"" . $this->contractQName($eParts['type']) . "\""; | |
578 | foreach ($eParts as $aName => $aValue) { | |
579 | // handle, e.g., abstract, default, form, minOccurs, maxOccurs, nillable | |
580 | if ($aName != 'name' && $aName != 'type') { | |
581 | $contentStr .= " $aName=\"$aValue\""; | |
582 | } | |
583 | } | |
584 | $contentStr .= "/>\n"; | |
585 | } | |
586 | } | |
587 | // compositor wraps elements | |
588 | if (isset($attrs['compositor']) && ($attrs['compositor'] != '')) { | |
589 | $contentStr = " <$schemaPrefix:$attrs[compositor]>\n".$contentStr." </$schemaPrefix:$attrs[compositor]>\n"; | |
590 | } | |
591 | } | |
592 | // attributes | |
593 | if(isset($attrs['attrs']) && (count($attrs['attrs']) >= 1)){ | |
594 | foreach($attrs['attrs'] as $attr => $aParts){ | |
595 | $contentStr .= " <$schemaPrefix:attribute"; | |
596 | foreach ($aParts as $a => $v) { | |
597 | if ($a == 'ref' || $a == 'type') { | |
598 | $contentStr .= " $a=\"".$this->contractQName($v).'"'; | |
599 | } elseif ($a == 'http://schemas.xmlsoap.org/wsdl/:arrayType') { | |
600 | $this->usedNamespaces['wsdl'] = $this->namespaces['wsdl']; | |
601 | $contentStr .= ' wsdl:arrayType="'.$this->contractQName($v).'"'; | |
602 | } else { | |
603 | $contentStr .= " $a=\"$v\""; | |
604 | } | |
605 | } | |
606 | $contentStr .= "/>\n"; | |
607 | } | |
608 | } | |
609 | // if restriction | |
610 | if (isset($attrs['restrictionBase']) && $attrs['restrictionBase'] != ''){ | |
611 | $contentStr = " <$schemaPrefix:restriction base=\"".$this->contractQName($attrs['restrictionBase'])."\">\n".$contentStr." </$schemaPrefix:restriction>\n"; | |
612 | // complex or simple content | |
613 | if ((isset($attrs['elements']) && count($attrs['elements']) > 0) || (isset($attrs['attrs']) && count($attrs['attrs']) > 0)){ | |
614 | $contentStr = " <$schemaPrefix:complexContent>\n".$contentStr." </$schemaPrefix:complexContent>\n"; | |
615 | } | |
616 | } | |
617 | // finalize complex type | |
618 | if($contentStr != ''){ | |
619 | $contentStr = " <$schemaPrefix:complexType name=\"$typeName\">\n".$contentStr." </$schemaPrefix:complexType>\n"; | |
620 | } else { | |
621 | $contentStr = " <$schemaPrefix:complexType name=\"$typeName\"/>\n"; | |
622 | } | |
623 | $xml .= $contentStr; | |
624 | } | |
625 | // simple types | |
626 | if(isset($this->simpleTypes) && count($this->simpleTypes) > 0){ | |
627 | foreach($this->simpleTypes as $typeName => $eParts){ | |
628 | $xml .= " <$schemaPrefix:simpleType name=\"$typeName\">\n <$schemaPrefix:restriction base=\"".$this->contractQName($eParts['type'])."\">\n"; | |
629 | if (isset($eParts['enumeration'])) { | |
630 | foreach ($eParts['enumeration'] as $e) { | |
631 | $xml .= " <$schemaPrefix:enumeration value=\"$e\"/>\n"; | |
632 | } | |
633 | } | |
634 | $xml .= " </$schemaPrefix:restriction>\n </$schemaPrefix:simpleType>"; | |
635 | } | |
636 | } | |
637 | // elements | |
638 | if(isset($this->elements) && count($this->elements) > 0){ | |
639 | foreach($this->elements as $element => $eParts){ | |
640 | $xml .= " <$schemaPrefix:element name=\"$element\" type=\"".$this->contractQName($eParts['type'])."\"/>\n"; | |
641 | } | |
642 | } | |
643 | // attributes | |
644 | if(isset($this->attributes) && count($this->attributes) > 0){ | |
645 | foreach($this->attributes as $attr => $aParts){ | |
646 | $xml .= " <$schemaPrefix:attribute name=\"$attr\" type=\"".$this->contractQName($aParts['type'])."\"\n/>"; | |
647 | } | |
648 | } | |
649 | // finish 'er up | |
650 | $attr = ''; | |
651 | foreach ($this->schemaInfo as $k => $v) { | |
652 | if ($k == 'elementFormDefault' || $k == 'attributeFormDefault') { | |
653 | $attr .= " $k=\"$v\""; | |
654 | } | |
655 | } | |
656 | $el = "<$schemaPrefix:schema$attr targetNamespace=\"$this->schemaTargetNamespace\"\n"; | |
657 | foreach (array_diff($this->usedNamespaces, $this->enclosingNamespaces) as $nsp => $ns) { | |
658 | $el .= " xmlns:$nsp=\"$ns\""; | |
659 | } | |
660 | $xml = $el . ">\n".$xml."</$schemaPrefix:schema>\n"; | |
661 | return $xml; | |
662 | } | |
663 | ||
664 | /** | |
665 | * adds debug data to the clas level debug string | |
666 | * | |
667 | * @param string $string debug data | |
668 | * @access private | |
669 | */ | |
670 | function xdebug($string){ | |
671 | $this->debug('<' . $this->schemaTargetNamespace . '> '.$string); | |
672 | } | |
673 | ||
674 | /** | |
675 | * get the PHP type of a user defined type in the schema | |
676 | * PHP type is kind of a misnomer since it actually returns 'struct' for assoc. arrays | |
677 | * returns false if no type exists, or not w/ the given namespace | |
678 | * else returns a string that is either a native php type, or 'struct' | |
679 | * | |
680 | * @param string $type name of defined type | |
681 | * @param string $ns namespace of type | |
682 | * @return mixed | |
683 | * @access public | |
684 | * @deprecated | |
685 | */ | |
686 | function getPHPType($type,$ns){ | |
687 | if(isset($this->typemap[$ns][$type])){ | |
688 | //print "found type '$type' and ns $ns in typemap<br>"; | |
689 | return $this->typemap[$ns][$type]; | |
690 | } elseif(isset($this->complexTypes[$type])){ | |
691 | //print "getting type '$type' and ns $ns from complexTypes array<br>"; | |
692 | return $this->complexTypes[$type]['phpType']; | |
693 | } | |
694 | return false; | |
695 | } | |
696 | ||
697 | /** | |
698 | * returns an associative array of information about a given type | |
699 | * returns false if no type exists by the given name | |
700 | * | |
701 | * For a complexType typeDef = array( | |
702 | * 'restrictionBase' => '', | |
703 | * 'phpType' => '', | |
704 | * 'compositor' => '(sequence|all)', | |
705 | * 'elements' => array(), // refs to elements array | |
706 | * 'attrs' => array() // refs to attributes array | |
707 | * ... and so on (see addComplexType) | |
708 | * ) | |
709 | * | |
710 | * For simpleType or element, the array has different keys. | |
711 | * | |
712 | * @param string $type | |
713 | * @return mixed | |
714 | * @access public | |
715 | * @see addComplexType | |
716 | * @see addSimpleType | |
717 | * @see addElement | |
718 | */ | |
719 | function getTypeDef($type){ | |
720 | //$this->debug("in getTypeDef for type $type"); | |
721 | if (substr($type, -1) == '^') { | |
722 | $is_element = 1; | |
723 | $type = substr($type, 0, -1); | |
724 | } else { | |
725 | $is_element = 0; | |
726 | } | |
727 | ||
728 | if((! $is_element) && isset($this->complexTypes[$type])){ | |
729 | $this->xdebug("in getTypeDef, found complexType $type"); | |
730 | return $this->complexTypes[$type]; | |
731 | } elseif((! $is_element) && isset($this->simpleTypes[$type])){ | |
732 | $this->xdebug("in getTypeDef, found simpleType $type"); | |
733 | if (!isset($this->simpleTypes[$type]['phpType'])) { | |
734 | // get info for type to tack onto the simple type | |
735 | // TODO: can this ever really apply (i.e. what is a simpleType really?) | |
736 | $uqType = substr($this->simpleTypes[$type]['type'], strrpos($this->simpleTypes[$type]['type'], ':') + 1); | |
737 | $ns = substr($this->simpleTypes[$type]['type'], 0, strrpos($this->simpleTypes[$type]['type'], ':')); | |
738 | $etype = $this->getTypeDef($uqType); | |
739 | if ($etype) { | |
740 | $this->xdebug("in getTypeDef, found type for simpleType $type:"); | |
741 | $this->xdebug($this->varDump($etype)); | |
742 | if (isset($etype['phpType'])) { | |
743 | $this->simpleTypes[$type]['phpType'] = $etype['phpType']; | |
744 | } | |
745 | if (isset($etype['elements'])) { | |
746 | $this->simpleTypes[$type]['elements'] = $etype['elements']; | |
747 | } | |
748 | } | |
749 | } | |
750 | return $this->simpleTypes[$type]; | |
751 | } elseif(isset($this->elements[$type])){ | |
752 | $this->xdebug("in getTypeDef, found element $type"); | |
753 | if (!isset($this->elements[$type]['phpType'])) { | |
754 | // get info for type to tack onto the element | |
755 | $uqType = substr($this->elements[$type]['type'], strrpos($this->elements[$type]['type'], ':') + 1); | |
756 | $ns = substr($this->elements[$type]['type'], 0, strrpos($this->elements[$type]['type'], ':')); | |
757 | $etype = $this->getTypeDef($uqType); | |
758 | if ($etype) { | |
759 | $this->xdebug("in getTypeDef, found type for element $type:"); | |
760 | $this->xdebug($this->varDump($etype)); | |
761 | if (isset($etype['phpType'])) { | |
762 | $this->elements[$type]['phpType'] = $etype['phpType']; | |
763 | } | |
764 | if (isset($etype['elements'])) { | |
765 | $this->elements[$type]['elements'] = $etype['elements']; | |
766 | } | |
767 | if (isset($etype['extensionBase'])) { | |
768 | $this->elements[$type]['extensionBase'] = $etype['extensionBase']; | |
769 | } | |
770 | } elseif ($ns == 'http://www.w3.org/2001/XMLSchema') { | |
771 | $this->xdebug("in getTypeDef, element $type is an XSD type"); | |
772 | $this->elements[$type]['phpType'] = 'scalar'; | |
773 | } | |
774 | } | |
775 | return $this->elements[$type]; | |
776 | } elseif(isset($this->attributes[$type])){ | |
777 | $this->xdebug("in getTypeDef, found attribute $type"); | |
778 | return $this->attributes[$type]; | |
779 | } elseif (preg_match('/_ContainedType$/', $type)) { | |
780 | $this->xdebug("in getTypeDef, have an untyped element $type"); | |
781 | $typeDef['typeClass'] = 'simpleType'; | |
782 | $typeDef['phpType'] = 'scalar'; | |
783 | $typeDef['type'] = 'http://www.w3.org/2001/XMLSchema:string'; | |
784 | return $typeDef; | |
785 | } | |
786 | $this->xdebug("in getTypeDef, did not find $type"); | |
787 | return false; | |
788 | } | |
789 | ||
790 | /** | |
791 | * returns a sample serialization of a given type, or false if no type by the given name | |
792 | * | |
793 | * @param string $type name of type | |
794 | * @return mixed | |
795 | * @access public | |
796 | * @deprecated | |
797 | */ | |
798 | function serializeTypeDef($type){ | |
799 | //print "in sTD() for type $type<br>"; | |
800 | if($typeDef = $this->getTypeDef($type)){ | |
801 | $str .= '<'.$type; | |
802 | if(is_array($typeDef['attrs'])){ | |
803 | foreach($typeDef['attrs'] as $attName => $data){ | |
804 | $str .= " $attName=\"{type = ".$data['type']."}\""; | |
805 | } | |
806 | } | |
807 | $str .= " xmlns=\"".$this->schema['targetNamespace']."\""; | |
808 | if(count($typeDef['elements']) > 0){ | |
809 | $str .= ">"; | |
810 | foreach($typeDef['elements'] as $element => $eData){ | |
811 | $str .= $this->serializeTypeDef($element); | |
812 | } | |
813 | $str .= "</$type>"; | |
814 | } elseif($typeDef['typeClass'] == 'element') { | |
815 | $str .= "></$type>"; | |
816 | } else { | |
817 | $str .= "/>"; | |
818 | } | |
819 | return $str; | |
820 | } | |
821 | return false; | |
822 | } | |
823 | ||
824 | /** | |
825 | * returns HTML form elements that allow a user | |
826 | * to enter values for creating an instance of the given type. | |
827 | * | |
828 | * @param string $name name for type instance | |
829 | * @param string $type name of type | |
830 | * @return string | |
831 | * @access public | |
832 | * @deprecated | |
833 | */ | |
834 | function typeToForm($name,$type){ | |
835 | // get typedef | |
836 | if($typeDef = $this->getTypeDef($type)){ | |
837 | // if struct | |
838 | if($typeDef['phpType'] == 'struct'){ | |
839 | $buffer .= '<table>'; | |
840 | foreach($typeDef['elements'] as $child => $childDef){ | |
841 | $buffer .= " | |
842 | <tr><td align='right'>$childDef[name] (type: ".$this->getLocalPart($childDef['type'])."):</td> | |
843 | <td><input type='text' name='parameters[".$name."][$childDef[name]]'></td></tr>"; | |
844 | } | |
845 | $buffer .= '</table>'; | |
846 | // if array | |
847 | } elseif($typeDef['phpType'] == 'array'){ | |
848 | $buffer .= '<table>'; | |
849 | for($i=0;$i < 3; $i++){ | |
850 | $buffer .= " | |
851 | <tr><td align='right'>array item (type: $typeDef[arrayType]):</td> | |
852 | <td><input type='text' name='parameters[".$name."][]'></td></tr>"; | |
853 | } | |
854 | $buffer .= '</table>'; | |
855 | // if scalar | |
856 | } else { | |
857 | $buffer .= "<input type='text' name='parameters[$name]'>"; | |
858 | } | |
859 | } else { | |
860 | $buffer .= "<input type='text' name='parameters[$name]'>"; | |
861 | } | |
862 | return $buffer; | |
863 | } | |
864 | ||
865 | /** | |
866 | * adds a complex type to the schema | |
867 | * | |
868 | * example: array | |
869 | * | |
870 | * addType( | |
871 | * 'ArrayOfstring', | |
872 | * 'complexType', | |
873 | * 'array', | |
874 | * '', | |
875 | * 'SOAP-ENC:Array', | |
876 | * array('ref'=>'SOAP-ENC:arrayType','wsdl:arrayType'=>'string[]'), | |
877 | * 'xsd:string' | |
878 | * ); | |
879 | * | |
880 | * example: PHP associative array ( SOAP Struct ) | |
881 | * | |
882 | * addType( | |
883 | * 'SOAPStruct', | |
884 | * 'complexType', | |
885 | * 'struct', | |
886 | * 'all', | |
887 | * array('myVar'=> array('name'=>'myVar','type'=>'string') | |
888 | * ); | |
889 | * | |
890 | * @param name | |
891 | * @param typeClass (complexType|simpleType|attribute) | |
892 | * @param phpType: currently supported are array and struct (php assoc array) | |
893 | * @param compositor (all|sequence|choice) | |
894 | * @param restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) | |
895 | * @param elements = array ( name = array(name=>'',type=>'') ) | |
896 | * @param attrs = array( | |
897 | * array( | |
898 | * 'ref' => "http://schemas.xmlsoap.org/soap/encoding/:arrayType", | |
899 | * "http://schemas.xmlsoap.org/wsdl/:arrayType" => "string[]" | |
900 | * ) | |
901 | * ) | |
902 | * @param arrayType: namespace:name (http://www.w3.org/2001/XMLSchema:string) | |
903 | * @access public | |
904 | * @see getTypeDef | |
905 | */ | |
906 | function addComplexType($name,$typeClass='complexType',$phpType='array',$compositor='',$restrictionBase='',$elements=array(),$attrs=array(),$arrayType=''){ | |
907 | $this->complexTypes[$name] = array( | |
908 | 'name' => $name, | |
909 | 'typeClass' => $typeClass, | |
910 | 'phpType' => $phpType, | |
911 | 'compositor'=> $compositor, | |
912 | 'restrictionBase' => $restrictionBase, | |
913 | 'elements' => $elements, | |
914 | 'attrs' => $attrs, | |
915 | 'arrayType' => $arrayType | |
916 | ); | |
917 | ||
918 | $this->xdebug("addComplexType $name:"); | |
919 | $this->appendDebug($this->varDump($this->complexTypes[$name])); | |
920 | } | |
921 | ||
922 | /** | |
923 | * adds a simple type to the schema | |
924 | * | |
925 | * @param string $name | |
926 | * @param string $restrictionBase namespace:name (http://schemas.xmlsoap.org/soap/encoding/:Array) | |
927 | * @param string $typeClass (should always be simpleType) | |
928 | * @param string $phpType (should always be scalar) | |
929 | * @param array $enumeration array of values | |
930 | * @access public | |
931 | * @see nusoap_xmlschema | |
932 | * @see getTypeDef | |
933 | */ | |
934 | function addSimpleType($name, $restrictionBase='', $typeClass='simpleType', $phpType='scalar', $enumeration=array()) { | |
935 | $this->simpleTypes[$name] = array( | |
936 | 'name' => $name, | |
937 | 'typeClass' => $typeClass, | |
938 | 'phpType' => $phpType, | |
939 | 'type' => $restrictionBase, | |
940 | 'enumeration' => $enumeration | |
941 | ); | |
942 | ||
943 | $this->xdebug("addSimpleType $name:"); | |
944 | $this->appendDebug($this->varDump($this->simpleTypes[$name])); | |
945 | } | |
946 | ||
947 | /** | |
948 | * adds an element to the schema | |
949 | * | |
950 | * @param array $attrs attributes that must include name and type | |
951 | * @see nusoap_xmlschema | |
952 | * @access public | |
953 | */ | |
954 | function addElement($attrs) { | |
955 | if (! $this->getPrefix($attrs['type'])) { | |
956 | $attrs['type'] = $this->schemaTargetNamespace . ':' . $attrs['type']; | |
957 | } | |
958 | $this->elements[ $attrs['name'] ] = $attrs; | |
959 | $this->elements[ $attrs['name'] ]['typeClass'] = 'element'; | |
960 | ||
961 | $this->xdebug("addElement " . $attrs['name']); | |
962 | $this->appendDebug($this->varDump($this->elements[ $attrs['name'] ])); | |
963 | } | |
964 | } | |
965 | ||
966 | /** | |
967 | * Backward compatibility | |
968 | */ | |
969 | class XMLSchema extends nusoap_xmlschema { | |
970 | } | |
971 | ||
972 | ||
973 | ?> |