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