source: branches/version-2_11-dev/data/module/SOAP/Base.php @ 21299

Revision 21299, 39.1 KB checked in by Seasoft, 15 years ago (diff)

#1521 (PEAR::SOAP 配布と異なる部分がある)

  • 新しいバージョンの配布ファイルを上書きすることで解決

#1522 (PEAR::SOAP をバージョンアップ)

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