root/trunk/lib/class.xmlschema.php

Revision 61, 32.8 kB (checked in by yann, 5 years ago)

use nusoap library for SOAP access (can be included in plugin package)

Line 
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.39 2005/08/04 01:27:42 snichol 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 ?>
Note: See TracBrowser for help on using the browser.