source: branches/feature-module-update/data/module/SOAP/Base.php @ 16957

Revision 16957, 39.0 KB checked in by naka, 15 years ago (diff)

PEAR::SOAPモジュールアップ

Line 
1<?php
2/**
3 * This file loads all required libraries, defines constants used across the
4 * SOAP package, and defines the base classes that most other classes of this
5 * package extend.
6 *
7 * PHP versions 4 and 5
8 *
9 * LICENSE: This source file is subject to version 2.02 of the PHP license,
10 * that is bundled with this package in the file LICENSE, and is available at
11 * through the world-wide-web at http://www.php.net/license/2_02.txt.  If you
12 * did not receive a copy of the PHP license and are unable to obtain it
13 * through the world-wide-web, please send a note to license@php.net so we can
14 * mail you a copy immediately.
15 *
16 * @category   Web Services
17 * @package    SOAP
18 * @author     Dietrich Ayala <dietrich@ganx4.com> Original Author
19 * @author     Shane Caraveo <Shane@Caraveo.com>   Port to PEAR and more
20 * @author     Chuck Hagenbuch <chuck@horde.org>   Maintenance
21 * @author     Jan Schneider <jan@horde.org>       Maintenance
22 * @copyright  2003-2007 The PHP Group
23 * @license    http://www.php.net/license/2_02.txt  PHP License 2.02
24 * @link       http://pear.php.net/package/SOAP
25 */
26
27define('MAIL_MIMEPART_CRLF', "\r\n");
28require_once 'PEAR.php';
29
30if (!defined('INF')) {
31    define('INF', 1.8e307);
32}
33if (!defined('NAN')) {
34    define('NAN', 0.0);
35}
36
37define('SOAP_LIBRARY_VERSION', '0.11.0');
38define('SOAP_LIBRARY_NAME',    'PEAR-SOAP 0.11.0-beta');
39
40// Set schema version.
41define('SOAP_XML_SCHEMA_VERSION',  'http://www.w3.org/2001/XMLSchema');
42define('SOAP_XML_SCHEMA_INSTANCE', 'http://www.w3.org/2001/XMLSchema-instance');
43define('SOAP_XML_SCHEMA_1999',     'http://www.w3.org/1999/XMLSchema');
44define('SOAP_SCHEMA',              'http://schemas.xmlsoap.org/wsdl/soap/');
45define('SOAP_SCHEMA_ENCODING',     'http://schemas.xmlsoap.org/soap/encoding/');
46define('SOAP_ENVELOP',             'http://schemas.xmlsoap.org/soap/envelope/');
47
48define('SCHEMA_DISCO',             'http://schemas.xmlsoap.org/disco/');
49define('SCHEMA_DISCO_SCL',         'http://schemas.xmlsoap.org/disco/scl/');
50
51define('SCHEMA_SOAP',              'http://schemas.xmlsoap.org/wsdl/soap/');
52define('SCHEMA_SOAP12',            'http://schemas.xmlsoap.org/wsdl/soap12/');
53define('SCHEMA_SOAP_HTTP',         'http://schemas.xmlsoap.org/soap/http');
54define('SCHEMA_WSDL_HTTP',         'http://schemas.xmlsoap.org/wsdl/http/');
55define('SCHEMA_MIME',              'http://schemas.xmlsoap.org/wsdl/mime/');
56define('SCHEMA_WSDL',              'http://schemas.xmlsoap.org/wsdl/');
57define('SCHEMA_DIME',              'http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/');
58define('SCHEMA_CONTENT',           'http://schemas.xmlsoap.org/ws/2002/04/content-type/');
59define('SCHEMA_REF',               'http://schemas.xmlsoap.org/ws/2002/04/reference/');
60
61define('SOAP_DEFAULT_ENCODING',  'UTF-8');
62
63class SOAP_Base_Object extends PEAR
64{
65
66    /**
67     * Supported encodings, limited by XML extension.
68     *
69     * @var array $_encodings
70     */
71    var $_encodings = array('ISO-8859-1', 'US-ASCII', 'UTF-8');
72
73    /**
74     * Fault code.
75     *
76     * @var string $_myfaultcode
77     */
78    var $_myfaultcode = '';
79
80    /**
81     * Recent PEAR_Error object.
82     *
83     * @var PEAR_Error $fault
84     */
85    var $fault = null;
86
87    /**
88     * Constructor.
89     *
90     * @param string $faultcode  Error code.
91     */
92    function SOAP_Base_Object($faultcode = 'Client')
93    {
94        $this->_myfaultcode = $faultcode;
95        parent::PEAR('SOAP_Fault');
96    }
97
98    /**
99     * Raises a SOAP error.
100     *
101     * Please refer to the SOAP definition for an impression of what a certain
102     * parameter stands for.
103     *
104     * @param string|object $str  Error message or object.
105     * @param string $detail      Detailed error message.
106     * @param string $actorURI
107     * @param mixed $code
108     * @param mixed $mode
109     * @param mixed $options
110     * @param boolean $skipmsg
111     */
112    function &_raiseSoapFault($str, $detail = '', $actorURI = '', $code = null,
113                              $mode = null, $options = null, $skipmsg = false)
114    {
115        // Pass through previous faults.
116        $is_instance = isset($this) && is_a($this, 'SOAP_Base_Object');
117        if (is_object($str)) {
118            $fault =& $str;
119        } else {
120            if (!$code) {
121                $code = $is_instance ? $this->_myfaultcode : 'Client';
122            }
123            require_once 'SOAP/Fault.php';
124            $fault =& new SOAP_Fault($str,
125                                     $code,
126                                     $actorURI,
127                                     $detail,
128                                     $mode,
129                                     $options);
130        }
131        if ($is_instance) {
132            $this->fault =& $fault;
133        }
134
135        return $fault;
136    }
137
138    function _isfault()
139    {
140        return $this->fault != null;
141    }
142
143    function &_getfault()
144    {
145        return $this->fault;
146    }
147
148}
149
150/**
151 * Common base class of all SOAP classes.
152 *
153 * @access   public
154 * @package  SOAP
155 * @author   Shane Caraveo <shane@php.net> Conversion to PEAR and updates
156 */
157class SOAP_Base extends SOAP_Base_Object
158{
159    var $_XMLSchema = array('http://www.w3.org/2001/XMLSchema',
160                            'http://www.w3.org/1999/XMLSchema');
161    var $_XMLSchemaVersion = 'http://www.w3.org/2001/XMLSchema';
162
163    // load types into typemap array
164    var $_typemap = array(
165        'http://www.w3.org/2001/XMLSchema' => array(
166            'string' => 'string',
167            'boolean' => 'boolean',
168            'float' => 'float',
169            'double' => 'float',
170            'decimal' => 'float',
171            'duration' => 'integer',
172            'dateTime' => 'string',
173            'time' => 'string',
174            'date' => 'string',
175            'gYearMonth' => 'integer',
176            'gYear' => 'integer',
177            'gMonthDay' => 'integer',
178            'gDay' => 'integer',
179            'gMonth' => 'integer',
180            'hexBinary' => 'string',
181            'base64Binary' => 'string',
182            // derived datatypes
183            'normalizedString' => 'string',
184            'token' => 'string',
185            'language' => 'string',
186            'NMTOKEN' => 'string',
187            'NMTOKENS' => 'string',
188            'Name' => 'string',
189            'NCName' => 'string',
190            'ID' => 'string',
191            'IDREF' => 'string',
192            'IDREFS' => 'string',
193            'ENTITY' => 'string',
194            'ENTITIES' => 'string',
195            'integer' => 'integer',
196            'nonPositiveInteger' => 'integer',
197            'negativeInteger' => 'integer',
198            // longs (64bit ints) are not supported cross-platform.
199            'long' => 'string',
200            'int' => 'integer',
201            'short' => 'integer',
202            'byte' => 'string',
203            'nonNegativeInteger' => 'integer',
204            'unsignedLong' => 'integer',
205            'unsignedInt' => 'integer',
206            'unsignedShort' => 'integer',
207            'unsignedByte' => 'integer',
208            'positiveInteger'  => 'integer',
209            'anyType' => 'string',
210            'anyURI' => 'string',
211            'QName' => 'string'
212        ),
213        'http://www.w3.org/1999/XMLSchema' => array(
214            'i4' => 'integer',
215            'int' => 'integer',
216            'boolean' => 'boolean',
217            'string' => 'string',
218            'double' => 'float',
219            'float' => 'float',
220            'dateTime' => 'string',
221            'timeInstant' => 'string',
222            'base64Binary' => 'string',
223            'base64' => 'string',
224            'ur-type' => 'string'
225        ),
226        'http://schemas.xmlsoap.org/soap/encoding/' => array(
227            'base64' => 'string',
228            'array' => 'array',
229            'Array' => 'array',
230            'Struct' => 'array')
231    );
232
233    /**
234     * Default class name to use for decoded response objects.
235     *
236     * @var string $_defaultObjectClassname
237     */
238    var $_defaultObjectClassname = 'stdClass';
239
240    /**
241     * Hash with used namespaces.
242     *
243     * @array
244     */
245    var $_namespaces;
246
247    /**
248     * The default namespace.
249     *
250     * @string
251     */
252    var $_namespace;
253
254    var $_xmlEntities = array('&' => '&amp;',
255                              '<' => '&lt;',
256                              '>' => '&gt;',
257                              "'" => '&apos;',
258                              '"' => '&quot;');
259
260    var $_doconversion = false;
261
262    var $_attachments = array();
263
264    var $_wsdl = null;
265
266    /**
267     * True if we use section 5 encoding, or false if this is literal.
268     *
269     * @var boolean $_section5
270     */
271    var $_section5 = true;
272
273    // Handle type to class mapping.
274    var $_auto_translation = false;
275    var $_type_translation = array();
276
277    /**
278     * Constructor.
279     *
280     * @param string $faultcode  Error code.
281     */
282    function SOAP_Base($faultcode = 'Client')
283    {
284        parent::SOAP_Base_Object($faultcode);
285        $this->_resetNamespaces();
286    }
287
288    /**
289     * Sets the default namespace.
290     *
291     * @param string $namespace  The default namespace.
292     */
293    function setDefaultNamespace($namespace)
294    {
295        $this->_namespace = $namespace;
296    }
297
298    function _resetNamespaces()
299    {
300        $this->_namespaces = array(
301            'http://schemas.xmlsoap.org/soap/envelope/' => 'SOAP-ENV',
302            'http://www.w3.org/2001/XMLSchema' => 'xsd',
303            'http://www.w3.org/2001/XMLSchema-instance' => 'xsi',
304            'http://schemas.xmlsoap.org/soap/encoding/' => 'SOAP-ENC');
305    }
306
307    /**
308     * Sets the schema version used in the SOAP message.
309     *
310     * @access private
311     * @see $_XMLSchema
312     *
313     * @param string $schemaVersion  The schema version.
314     */
315    function _setSchemaVersion($schemaVersion)
316    {
317        if (!in_array($schemaVersion, $this->_XMLSchema)) {
318            return $this->_raiseSoapFault("unsuported XMLSchema $schemaVersion");
319        }
320        $this->_XMLSchemaVersion = $schemaVersion;
321        $tmpNS = array_flip($this->_namespaces);
322        $tmpNS['xsd'] = $this->_XMLSchemaVersion;
323        $tmpNS['xsi'] = $this->_XMLSchemaVersion . '-instance';
324        $this->_namespaces = array_flip($tmpNS);
325    }
326
327    function _getNamespacePrefix($ns)
328    {
329        if ($this->_namespace && $ns == $this->_namespace) {
330            return '';
331        }
332        if (isset($this->_namespaces[$ns])) {
333            return $this->_namespaces[$ns];
334        }
335        $prefix = 'ns' . count($this->_namespaces);
336        $this->_namespaces[$ns] = $prefix;
337        return $prefix;
338    }
339
340    function _getNamespaceForPrefix($prefix)
341    {
342        $flipped = array_flip($this->_namespaces);
343        if (isset($flipped[$prefix])) {
344            return $flipped[$prefix];
345        }
346        return null;
347    }
348
349    function _isSoapValue(&$value)
350    {
351        return is_a($value, 'SOAP_Value');
352    }
353
354    function _serializeValue(&$value, $name = '', $type = false,
355                             $elNamespace = null, $typeNamespace = null,
356                             $options = array(), $attributes = array(),
357                             $artype = '')
358    {
359        $namespaces = array();
360        $arrayType = $array_depth = $xmlout_value = null;
361        $typePrefix = $elPrefix = $xmlout_offset = $xmlout_arrayType = '';
362        $xmlout_type = $xmlns = $ptype = $array_type_ns = '';
363
364        if (!$name || is_numeric($name)) {
365            $name = 'item';
366        }
367
368        if ($this->_wsdl) {
369            list($ptype, $arrayType, $array_type_ns, $array_depth)
370                = $this->_wsdl->getSchemaType($type, $name, $typeNamespace);
371        }
372
373        if (!$arrayType) {
374            $arrayType = $artype;
375        }
376        if (!$ptype) {
377            $ptype = $this->_getType($value);
378        }
379        if (!$type) {
380            $type = $ptype;
381        }
382
383        if (strcasecmp($ptype, 'Struct') == 0 ||
384            strcasecmp($type, 'Struct') == 0) {
385            // Struct
386            $vars = null;
387            if (is_object($value)) {
388                $vars = get_object_vars($value);
389            } else {
390                $vars = &$value;
391            }
392            if (is_array($vars)) {
393                foreach (array_keys($vars) as $k) {
394                    // Hide private vars.
395                    if ($k[0] == '_') {
396                        continue;
397                    }
398                    if (is_object($vars[$k])) {
399                        if (is_a($vars[$k], 'SOAP_Value')) {
400                            $xmlout_value .= $vars[$k]->serialize($this);
401                        } else {
402                            // XXX get the members and serialize them instead
403                            // converting to an array is more overhead than we
404                            // should really do.
405                            $xmlout_value .= $this->_serializeValue(get_object_vars($vars[$k]), $k, false, $this->_section5 ? null : $elNamespace);
406                        }
407                    } else {
408                        $xmlout_value .= $this->_serializeValue($vars[$k], $k, false, $this->_section5 ? null : $elNamespace);
409                    }
410                }
411            }
412        } elseif (strcasecmp($ptype, 'Array') == 0 ||
413                  strcasecmp($type, 'Array') == 0) {
414            // Array.
415            $typeNamespace = SOAP_SCHEMA_ENCODING;
416            $orig_type = $type;
417            $type = 'Array';
418            $numtypes = 0;
419            $value = (array)$value;
420            // XXX this will be slow on larger arrays.  Basically, it flattens
421            // arrays to allow us to serialize multi-dimensional arrays.  We
422            // only do this if arrayType is set, which will typically only
423            // happen if we are using WSDL
424            if (isset($options['flatten']) ||
425                ($arrayType &&
426                 (strchr($arrayType, ',') || strstr($arrayType, '][')))) {
427                $numtypes = $this->_multiArrayType($value, $arrayType,
428                                                   $ar_size, $xmlout_value);
429            }
430
431            $array_type = $array_type_prefix = '';
432            if ($numtypes != 1) {
433                $arrayTypeQName = new QName($arrayType);
434                $arrayType = $arrayTypeQName->name;
435                $array_types = array();
436                $array_val = null;
437
438                // Serialize each array element.
439                $ar_size = count($value);
440                foreach ($value as $array_val) {
441                    if ($this->_isSoapValue($array_val)) {
442                        $array_type = $array_val->type;
443                        $array_types[$array_type] = 1;
444                        $array_type_ns = $array_val->type_namespace;
445                        $xmlout_value .= $array_val->serialize($this);
446                    } else {
447                        $array_type = $this->_getType($array_val);
448                        $array_types[$array_type] = 1;
449                        $xmlout_value .= $this->_serializeValue($array_val, 'item', $array_type, $this->_section5 ? null : $elNamespace);
450                    }
451                }
452
453                $xmlout_offset = ' SOAP-ENC:offset="[0]"';
454                if (!$arrayType) {
455                    $numtypes = count($array_types);
456                    if ($numtypes == 1) {
457                        $arrayType = $array_type;
458                    }
459                    // Using anyType is more interoperable.
460                    if ($array_type == 'Struct') {
461                        $array_type = '';
462                    } elseif ($array_type == 'Array') {
463                        $arrayType = 'anyType';
464                        $array_type_prefix = 'xsd';
465                    } else {
466                        if (!$arrayType) {
467                            $arrayType = $array_type;
468                        }
469                    }
470                }
471            }
472            if (!$arrayType || $numtypes > 1) {
473                // Should reference what schema we're using.
474                $arrayType = 'xsd:anyType';
475            } else {
476                if ($array_type_ns) {
477                    $array_type_prefix = $this->_getNamespacePrefix($array_type_ns);
478                } elseif (isset($this->_typemap[$this->_XMLSchemaVersion][$arrayType])) {
479                    $array_type_prefix = $this->_namespaces[$this->_XMLSchemaVersion];
480                }
481                if ($array_type_prefix) {
482                    $arrayType = $array_type_prefix . ':' . $arrayType;
483                }
484            }
485
486            $xmlout_arrayType = ' SOAP-ENC:arrayType="' . $arrayType;
487            if ($array_depth != null) {
488                for ($i = 0; $i < $array_depth; $i++) {
489                    $xmlout_arrayType .= '[]';
490                }
491            }
492            $xmlout_arrayType .= "[$ar_size]\"";
493        } elseif ($this->_isSoapValue($value)) {
494            $xmlout_value = $value->serialize($this);
495        } elseif ($type == 'string') {
496            $xmlout_value = htmlspecialchars($value);
497        } elseif ($type == 'rawstring') {
498            $xmlout_value =& $value;
499        } elseif ($type == 'boolean') {
500            $xmlout_value = $value ? 'true' : 'false';
501        } else {
502            $xmlout_value =& $value;
503        }
504
505        // Add namespaces.
506        if ($elNamespace) {
507            $elPrefix = $this->_getNamespacePrefix($elNamespace);
508            if ($elPrefix) {
509                $xmlout_name = "$elPrefix:$name";
510            } else {
511                $xmlout_name = $name;
512            }
513        } else {
514            $xmlout_name = $name;
515        }
516
517        if ($typeNamespace) {
518            $typePrefix = $this->_getNamespacePrefix($typeNamespace);
519            if ($typePrefix) {
520                $xmlout_type = "$typePrefix:$type";
521            } else {
522                $xmlout_type = $type;
523            }
524        } elseif ($type &&
525                  isset($this->_typemap[$this->_XMLSchemaVersion][$type])) {
526            $typePrefix = $this->_namespaces[$this->_XMLSchemaVersion];
527            if ($typePrefix) {
528                $xmlout_type = "$typePrefix:$type";
529            } else {
530                $xmlout_type = $type;
531            }
532        }
533
534        // Handle additional attributes.
535        $xml_attr = '';
536        if (count($attributes)) {
537            foreach ($attributes as $k => $v) {
538                $kqn = new QName($k);
539                $vqn = new QName($v);
540                $xml_attr .= ' ' . $kqn->fqn() . '="' . $vqn->fqn() . '"';
541            }
542        }
543
544        // Store the attachment for mime encoding.
545        if (isset($options['attachment']) &&
546            !PEAR::isError($options['attachment'])) {
547            $this->_attachments[] = $options['attachment'];
548        }
549
550        if ($this->_section5) {
551            if ($xmlout_type) {
552                $xmlout_type = " xsi:type=\"$xmlout_type\"";
553            }
554            if (is_null($xmlout_value)) {
555                $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
556                    "$xml_attr xsi:nil=\"true\"/>";
557            } else {
558                $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
559                    "$xmlout_offset$xml_attr>$xmlout_value</$xmlout_name>";
560            }
561        } else {
562            if (is_null($xmlout_value)) {
563                $xml = "\r\n<$xmlout_name$xmlns$xml_attr/>";
564            } else {
565                $xml = "\r\n<$xmlout_name$xmlns$xml_attr>" .
566                    $xmlout_value . "</$xmlout_name>";
567            }
568        }
569
570        return $xml;
571    }
572
573    /**
574     * Converts a PHP type to a SOAP type.
575     *
576     * @access   private
577     *
578     * @param string $value  The value to inspect.
579     *
580     * @return string  The value's SOAP type.
581     */
582    function _getType(&$value)
583    {
584        $type = gettype($value);
585        switch ($type) {
586        case 'object':
587            if (is_a($value, 'soap_value')) {
588                $type = $value->type;
589            } else {
590                $type = 'Struct';
591            }
592            break;
593
594        case 'array':
595            // Hashes are always handled as structs.
596            if ($this->_isHash($value)) {
597                $type = 'Struct';
598            } else {
599                $ar_size = count($value);
600                reset($value);
601                $key1 = key($value);
602                if ($ar_size > 0 && is_a($key1, 'SOAP_Value')) {
603                    // FIXME: for non-wsdl structs that are all the same type
604                    $key2 = key($value);
605                    if ($ar_size > 1 &&
606                        $this->_isSoapValue($key1) &&
607                        $this->_isSoapValue($key2) &&
608                        $key1->name != $key2->name) {
609                        // This is a struct, not an array.
610                        $type = 'Struct';
611                    } else {
612                        $type = 'Array';
613                    }
614                } else {
615                    $type = 'Array';
616                }
617            }
618            break;
619
620        case 'integer':
621        case 'long':
622            $type = 'int';
623            break;
624
625        case 'boolean':
626            break;
627
628        case 'double':
629            // double is deprecated in PHP 4.2 and later.
630            $type = 'float';
631            break;
632
633        case 'null':
634            $type = '';
635            break;
636
637        case 'string':
638        default:
639            break;
640        }
641
642        return $type;
643    }
644
645    function _multiArrayType($value, &$type, &$size, &$xml)
646    {
647        if (is_array($value)) {
648            // Seems we have a multi dimensional array, figure it out if we
649            // do.
650            for ($i = 0, $c = count($value); $i < $c; ++$i) {
651                $this->_multiArrayType($value[$i], $type, $size, $xml);
652            }
653
654            $sz = count($value);
655            if ($size) {
656                $size = $sz . ',' . $size;
657            } else {
658                $size = $sz;
659            }
660            return 1;
661        } elseif (is_object($value)) {
662            $type = $value->type;
663            $xml .= $value->serialize($this);
664        } else {
665            $type = $this->_getType($value);
666            $xml .= $this->_serializeValue($value, 'item', $type);
667        }
668        $size = null;
669
670        return 1;
671    }
672
673    /**
674     * Returns whether a type is a base64 type.
675     *
676     * @param string $type  A type name.
677     *
678     * @return boolean  True if the type name is a base64 type.
679     */
680    function _isBase64Type($type)
681    {
682        return $type == 'base64' || $type == 'base64Binary';
683    }
684
685    /**
686     * Returns whether an array is a hash.
687     *
688     * @param array $a  An array to check.
689     *
690     * @return boolean  True if the specified array is a hash.
691     */
692    function _isHash(&$a)
693    {
694        // I really dislike having to loop through this in PHP code, really
695        // large arrays will be slow.  We need a C function to do this.
696        $it = 0;
697        foreach ($a as $k => $v) {
698            // Checking the type is faster than regexp.
699            if (!is_int($k)) {
700                return true;
701            }
702            // If someone has a large hash they should really be defining the
703            // type.
704            if ($it++ > 10) {
705                $this->_raiseSoapFault('Large associative array passed where a SOAP_Value was expected');
706                return false;
707            }
708        }
709        return false;
710    }
711
712    function _un_htmlentities($string)
713    {
714        $trans_tbl = get_html_translation_table(HTML_ENTITIES);
715        $trans_tbl = array_flip($trans_tbl);
716        return strtr($string, $trans_tbl);
717    }
718
719    /**
720    *   Converts a SOAP_Value object into a StdClass PHP object
721    */
722    function &_decode(&$soapval)
723    {
724        if (!$this->_isSoapValue($soapval)) {
725            return $soapval;
726        } elseif (is_array($soapval->value)) {
727            if ($soapval->type != 'Array') {
728                $classname = $this->_defaultObjectClassname;
729                if (isset($this->_type_translation[$soapval->tqn->fqn()])) {
730                    // This will force an error in PHP if the class does not
731                    // exist.
732                    $classname = $this->_type_translation[$soapval->tqn->fqn()];
733                } elseif (isset($this->_type_translation[$soapval->type])) {
734                    // This will force an error in PHP if the class does not
735                    // exist.
736                    $classname = $this->_type_translation[$soapval->type];
737                } elseif ($this->_auto_translation) {
738                    if (class_exists($soapval->type)) {
739                        $classname = $soapval->type;
740                    } elseif ($this->_wsdl) {
741                        $t = $this->_wsdl->getComplexTypeNameForElement($soapval->name, $soapval->namespace);
742                        if ($t && class_exists($t)) {
743                            $classname = $t;
744                        }
745                    }
746                }
747                $return =& new $classname;
748            } else {
749                $return = array();
750            }
751
752            $counter = 1;
753            $isstruct = !is_array($return);
754            foreach ($soapval->value as $item) {
755                if (is_object($return)) {
756                    if ($this->_wsdl) {
757                        // Get this child's WSDL information.
758                        // /$soapval->ns/$soapval->type/$item->ns/$item->name
759                        $child_type = $this->_wsdl->getComplexTypeChildType(
760                            $soapval->namespace,
761                            $soapval->name,
762                            $item->namespace,
763                            $item->name);
764                        if ($child_type) {
765                            $item->type = $child_type;
766                        }
767                    }
768                    if (!$isstruct || $item->type == 'Array') {
769                        if (isset($return->{$item->name}) &&
770                            is_object($return->{$item->name})) {
771                            $return->{$item->name} =& $this->_decode($item);
772                        } elseif (isset($return->{$item->name}) &&
773                                  is_array($return->{$item->name})) {
774                            $return->{$item->name}[] = $this->_decode($item);
775                        } elseif (isset($return->{$item->name})) {
776                            $return->{$item->name} = array(
777                                $return->{$item->name},
778                                $this->_decode($item)
779                            );
780                        } elseif (is_array($return)) {
781                            $return[] =& $this->_decode($item);
782                        } else {
783                            $return->{$item->name} =& $this->_decode($item);
784                        }
785                    } elseif (isset($return->{$item->name})) {
786                        //$isstruct = false;
787                        if (count(get_object_vars($return)) == 1) {
788                            $d =& $this->_decode($item);
789                            $return = array($return->{$item->name}, $d);
790                        } else {
791                            $d =& $this->_decode($item);
792                            $return->{$item->name} = array($return->{$item->name}, $d);
793                        }
794                    } else {
795                        $return->{$item->name} =& $this->_decode($item);
796                    }
797                    // Set the attributes as members in the class.
798                    if (method_exists($return, '__set_attribute')) {
799                        foreach ($soapval->attributes as $key => $value) {
800                            call_user_func_array(array(&$return,
801                                                       '__set_attribute'),
802                                                 array($key, $value));
803                        }
804                    }
805                } else {
806                    if ($soapval->arrayType && $this->_isSoapValue($item)) {
807                        if ($this->_isBase64Type($item->type) &&
808                            !$this->_isBase64Type($soapval->arrayType)) {
809                            // Decode the value if we're losing the base64
810                            // type information.
811                            $item->value = base64_decode($item->value);
812                        }
813                        $item->type = $soapval->arrayType;
814                    }
815                    if (!$isstruct) {
816                        $return[] = $this->_decode($item);
817                    } elseif (isset($return[$item->name])) {
818                        $isstruct = false;
819                        $d =& $this->_decode($item);
820                        $return = array($return[$item->name], $d);
821                    } else {
822                        $return[$item->name] = $this->_decode($item);
823                    }
824                }
825            }
826
827            return $return;
828        }
829
830        if ($soapval->type == 'boolean') {
831            if ($soapval->value != '0' &&
832                strcasecmp($soapval->value, 'false') != 0) {
833                $soapval->value = true;
834            } else {
835                $soapval->value = false;
836            }
837        } elseif ($soapval->type &&
838                  isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type])) {
839            // If we can, set variable type.
840            settype($soapval->value,
841                    $this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type]);
842        }
843
844        return $soapval->value;
845    }
846
847    /**
848     * Creates the SOAP envelope with the SOAP envelop data.
849     *
850     * @param mixed $method
851     * @param array $headers
852     * @param string $encoding
853     * @param array $options
854     *
855     * @return string
856     */
857    function makeEnvelope(&$method, &$headers,
858                          $encoding = SOAP_DEFAULT_ENCODING,
859                          $options = array())
860    {
861        $smsg = $header_xml = $ns_string = '';
862
863        if ($headers) {
864            $c = count($headers);
865            for ($i = 0; $i < $c; $i++) {
866                $header_xml .= $headers[$i]->serialize($this);
867            }
868            $header_xml = "<SOAP-ENV:Header>\r\n$header_xml\r\n</SOAP-ENV:Header>\r\n";
869        }
870
871        if (!isset($options['input']) || $options['input'] == 'parse') {
872            if (is_array($method)) {
873                $c = count($method);
874                for ($i = 0; $i < $c; $i++) {
875                    $smsg .= $method[$i]->serialize($this);
876                }
877            } else {
878                $smsg = $method->serialize($this);
879            }
880        } else {
881            $smsg = $method;
882        }
883        $body = "<SOAP-ENV:Body>\r\n" . $smsg . "\r\n</SOAP-ENV:Body>\r\n";
884
885        foreach ($this->_namespaces as $k => $v) {
886            $ns_string .= " xmlns:$v=\"$k\"\r\n";
887        }
888        if ($this->_namespace) {
889            $ns_string .= " xmlns=\"{$this->_namespace}\"\r\n";
890        }
891
892        /* If 'use' == 'literal', we do not put in the encodingStyle.  This is
893         * denoted by $this->_section5 being false.  'use' can be defined at a
894         * more granular level than we are dealing with here, so this does not
895         * work for all services. */
896        $xml = "<?xml version=\"1.0\" encoding=\"$encoding\"?>\r\n\r\n".
897            "<SOAP-ENV:Envelope $ns_string".
898            ($this->_section5 ? ' SOAP-ENV:encodingStyle="' . SOAP_SCHEMA_ENCODING . '"' : '').
899            ">\r\n".
900            "$header_xml$body</SOAP-ENV:Envelope>\r\n";
901
902        return $xml;
903    }
904
905    function _makeMimeMessage($xml, $encoding = SOAP_DEFAULT_ENCODING)
906    {
907        if (!@include_once 'Mail/mimePart.php') {
908            return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
909        }
910
911        // Encode any attachments.  See http://www.w3.org/TR/SOAP-attachments
912        // Now we have to mime encode the message.
913        $params = array('content_type' => 'multipart/related; type="text/xml"');
914        $msg = new Mail_mimePart('', $params);
915
916        // Add the xml part.
917        $params['content_type'] = 'text/xml';
918        $params['charset'] = $encoding;
919        $params['encoding'] = 'base64';
920        $msg->addSubPart($xml, $params);
921
922        // Add the attachements
923        for ($i = 0, $c = count($this->_attachments); $i < $c; ++$i) {
924            $msg->addSubPart($this->_attachments[$i]['body'],
925                             $this->_attachments[$i]);
926        }
927
928        return $msg->encode();
929    }
930
931    // TODO: this needs to be used from the Transport system.
932    function _makeDIMEMessage($xml)
933    {
934        if (!@include_once 'Net/DIME.php') {
935            return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
936        }
937
938        // Encode any attachments.  See
939        // http://search.ietf.org/internet-drafts/draft-nielsen-dime-soap-00.txt
940        // Now we have to DIME encode the message
941        $dime = new Net_DIME_Message();
942        $msg = $dime->encodeData($xml, SOAP_ENVELOP, null, NET_DIME_TYPE_URI);
943
944        // Add the attachments.
945        $c = count($this->_attachments);
946        for ($i = 0; $i < $c; $i++) {
947            $msg .= $dime->encodeData($this->_attachments[$i]['body'],
948                                      $this->_attachments[$i]['content_type'],
949                                      $this->_attachments[$i]['cid'],
950                                      NET_DIME_TYPE_MEDIA);
951        }
952        $msg .= $dime->endMessage();
953
954        return $msg;
955    }
956
957    function _decodeMimeMessage(&$data, &$headers, &$attachments)
958    {
959        if (!@include_once 'Mail/mimeDecode.php') {
960            return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
961        }
962
963        $params['include_bodies'] = true;
964        $params['decode_bodies']  = true;
965        $params['decode_headers'] = true;
966
967        // Lame thing to have to do for decoding.
968        $decoder =& new Mail_mimeDecode($data);
969        $structure = $decoder->decode($params);
970
971        if (isset($structure->body)) {
972            $data = $structure->body;
973            $headers = $structure->headers;
974
975            return;
976        } elseif (isset($structure->parts)) {
977            $data = $structure->parts[0]->body;
978            $headers = array_merge($structure->headers,
979                                   $structure->parts[0]->headers);
980            if (count($structure->parts) > 1) {
981                $mime_parts = array_splice($structure->parts,1);
982                // Prepare the parts for the SOAP parser.
983
984                $c = count($mime_parts);
985                for ($i = 0; $i < $c; $i++) {
986                    $p =& $mime_parts[$i];
987                    if (isset($p->headers['content-location'])) {
988                        // TODO: modify location per SwA note section 3
989                        // http://www.w3.org/TR/SOAP-attachments
990                        $attachments[$p->headers['content-location']] = $p->body;
991                    } else {
992                        $cid = 'cid:' . substr($p->headers['content-id'], 1, -1);
993                        $attachments[$cid] = $p->body;
994                    }
995                }
996            }
997
998            return;
999        }
1000
1001        $this->_raiseSoapFault('Mime parsing error', '', '', 'Server');
1002    }
1003
1004    function _decodeDIMEMessage(&$data, &$headers, &$attachments)
1005    {
1006        if (!@include_once 'Net/DIME.php') {
1007            return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
1008        }
1009
1010        // This SHOULD be moved to the transport layer, e.g. PHP itself should
1011        // handle parsing DIME ;)
1012        $dime =& new Net_DIME_Message();
1013        $err = $dime->decodeData($data);
1014        if (PEAR::isError($err)) {
1015            $this->_raiseSoapFault('Failed to decode the DIME message!', '', '', 'Server');
1016            return;
1017        }
1018        if (strcasecmp($dime->parts[0]['type'], SOAP_ENVELOP) != 0) {
1019            $this->_raiseSoapFault('DIME record 1 is not a SOAP envelop!', '', '', 'Server');
1020            return;
1021        }
1022
1023        $data = $dime->parts[0]['data'];
1024        // Fake it for now.
1025        $headers['content-type'] = 'text/xml';
1026        $c = count($dime->parts);
1027        for ($i = 0; $i < $c; $i++) {
1028            $part =& $dime->parts[$i];
1029            // We need to handle URI's better.
1030            $id = strncmp($part['id'], 'cid:', 4)
1031                ? 'cid:' . $part['id']
1032                : $part['id'];
1033            $attachments[$id] = $part['data'];
1034        }
1035    }
1036
1037    /**
1038     * @deprecated Use setTypeTranslation().
1039     */
1040    function __set_type_translation($type, $class = null)
1041    {
1042        $this->setTypeTranslation($type, $class);
1043    }
1044
1045    /**
1046     * Explicitly sets the translation for a specific class.
1047     *
1048     * Auto translation works for all cases, but opens ANY class in the script
1049     * to be used as a data type, and may not be desireable.
1050     *
1051     * @param string $type   A SOAP type.
1052     * @param string $class  A PHP class name.
1053     */
1054    function setTypeTranslation($type, $class = null)
1055    {
1056        $tq = new QName($type);
1057        if (!$class) {
1058            $class = $tq->name;
1059        }
1060        $this->_type_translation[$type]=$class;
1061    }
1062
1063}
1064
1065/**
1066 * Class used to handle QNAME values in XML.
1067 *
1068 * @access   public
1069 * @package  SOAP
1070 * @author   Shane Caraveo <shane@php.net> Conversion to PEAR and updates
1071 */
1072class QName
1073{
1074    var $name = '';
1075    var $ns = '';
1076    var $namespace='';
1077
1078    function QName($name, $namespace = '')
1079    {
1080        if ($name && $name[0] == '{') {
1081            preg_match('/\{(.*?)\}(.*)/', $name, $m);
1082            $this->name = $m[2];
1083            $this->namespace = $m[1];
1084        } elseif (substr_count($name, ':') == 1) {
1085            $s = explode(':', $name);
1086            $s = array_reverse($s);
1087            $this->name = $s[0];
1088            $this->ns = $s[1];
1089            $this->namespace = $namespace;
1090        } else {
1091            $this->name = $name;
1092            $this->namespace = $namespace;
1093        }
1094
1095        // A little more magic than should be in a qname.
1096        $p = strpos($this->name, '[');
1097        if ($p) {
1098            // TODO: Need to re-examine this logic later.
1099            // Chop off [].
1100            $this->arraySize = explode(',', substr($this->name, $p + 1, -$p - 2));
1101            $this->arrayInfo = substr($this->name, $p);
1102            $this->name = substr($this->name, 0, $p);
1103        }
1104    }
1105
1106    function fqn()
1107    {
1108        if ($this->namespace) {
1109            return '{' . $this->namespace . '}' . $this->name;
1110        } elseif ($this->ns) {
1111            return $this->ns . ':' . $this->name;
1112        }
1113        return $this->name;
1114    }
1115
1116}
Note: See TracBrowser for help on using the repository browser.