source: branches/version-2_12-dev/data/module/SOAP/Base.php @ 21461

Revision 21461, 39.4 KB checked in by Seasoft, 12 years ago (diff)

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

  • 0.12.0 -> 0.13.0
  • SOAP_WSDL により、「Yahoo!デベロッパーネットワーク」の「オークション SOAP」を取得できることを確認した。
  • 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 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
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', '@version@');
40define('SOAP_LIBRARY_NAME',    'PEAR-SOAP @version@-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     * Supported encodings, limited by XML extension.
72     *
73     * @var array $_encodings
74     */
75    var $_encodings = array('ISO-8859-1', 'US-ASCII', 'UTF-8');
76
77    /**
78     * Fault code.
79     *
80     * @var string $_myfaultcode
81     */
82    var $_myfaultcode = '';
83
84    /**
85     * Recent PEAR_Error object.
86     *
87     * @var PEAR_Error $fault
88     */
89    var $fault = null;
90
91    /**
92     * Constructor.
93     *
94     * @param string $faultcode  Error code.
95     */
96    function SOAP_Base_Object($faultcode = 'Client')
97    {
98        $this->_myfaultcode = $faultcode;
99        parent::PEAR('SOAP_Fault');
100    }
101
102    /**
103     * Raises a SOAP error.
104     *
105     * Please refer to the SOAP definition for an impression of what a certain
106     * parameter stands for.
107     *
108     * @param string|object $str  Error message or object.
109     * @param string $detail      Detailed error message.
110     * @param string $actorURI
111     * @param mixed $code
112     * @param mixed $mode
113     * @param mixed $options
114     * @param boolean $skipmsg
115     */
116    function &_raiseSoapFault($str, $detail = '', $actorURI = '', $code = null,
117                              $mode = null, $options = null, $skipmsg = false)
118    {
119        // Pass through previous faults.
120        $is_instance = isset($this) && is_a($this, 'SOAP_Base_Object');
121        if (is_object($str)) {
122            $fault = $str;
123        } else {
124            if (!$code) {
125                $code = $is_instance ? $this->_myfaultcode : 'Client';
126            }
127            require_once 'SOAP/Fault.php';
128            $fault = new SOAP_Fault($str, $code, $actorURI, $detail, $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     * @var array
244     */
245    var $_namespaces;
246
247    /**
248     * The default namespace.
249     *
250     * @var 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 SOAP-ENV prefix and returns the current value.
290     *
291     * @access public
292     *
293     * @param string SOAP-ENV prefix
294     *
295     * @return string current SOAP-ENV prefix.
296     */
297    function SOAPENVPrefix($prefix = null)
298    {
299        static $_soapenv_prefix = 'SOAP-ENV';
300        if (!is_null($prefix)) {
301            $_soapenv_prefix = $prefix;
302        }
303        return $_soapenv_prefix;
304    }
305
306    /**
307     * Sets the SOAP-ENC prefix and returns the current value.
308     *
309     * @access public
310     *
311     * @param string SOAP-ENC prefix
312     *
313     * @return string current SOAP-ENC prefix.
314     */
315    function SOAPENCPrefix($prefix = null)
316    {
317        static $_soapenv_prefix = 'SOAP-ENC';
318        if (!is_null($prefix)) {
319            $_soapenv_prefix = $prefix;
320        }
321        return $_soapenv_prefix;
322    }
323
324    /**
325     * Sets the default namespace.
326     *
327     * @param string $namespace  The default namespace.
328     */
329    function setDefaultNamespace($namespace)
330    {
331        $this->_namespace = $namespace;
332    }
333
334    function _resetNamespaces()
335    {
336        $this->_namespaces = array(
337            'http://schemas.xmlsoap.org/soap/envelope/' => SOAP_BASE::SOAPENVPrefix(),
338            'http://www.w3.org/2001/XMLSchema' => 'xsd',
339            'http://www.w3.org/2001/XMLSchema-instance' => 'xsi',
340            'http://schemas.xmlsoap.org/soap/encoding/' => SOAP_BASE::SOAPENCPrefix());
341    }
342
343    /**
344     * Sets the schema version used in the SOAP message.
345     *
346     * @access private
347     * @see $_XMLSchema
348     *
349     * @param string $schemaVersion  The schema version.
350     */
351    function _setSchemaVersion($schemaVersion)
352    {
353        if (!in_array($schemaVersion, $this->_XMLSchema)) {
354            return $this->_raiseSoapFault("unsuported XMLSchema $schemaVersion");
355        }
356        $this->_XMLSchemaVersion = $schemaVersion;
357        $tmpNS = array_flip($this->_namespaces);
358        $tmpNS['xsd'] = $this->_XMLSchemaVersion;
359        $tmpNS['xsi'] = $this->_XMLSchemaVersion . '-instance';
360        $this->_namespaces = array_flip($tmpNS);
361    }
362
363    function _getNamespacePrefix($ns)
364    {
365        if ($this->_namespace && $ns == $this->_namespace) {
366            return '';
367        }
368        if (isset($this->_namespaces[$ns])) {
369            return $this->_namespaces[$ns];
370        }
371        $prefix = 'ns' . count($this->_namespaces);
372        $this->_namespaces[$ns] = $prefix;
373        return $prefix;
374    }
375
376    function _getNamespaceForPrefix($prefix)
377    {
378        $flipped = array_flip($this->_namespaces);
379        if (isset($flipped[$prefix])) {
380            return $flipped[$prefix];
381        }
382        return null;
383    }
384
385    /**
386     * Serializes a value, array or object according to the rules set by this
387     * object.
388     *
389     * @see SOAP_Value
390     *
391     * @param mixed $value       The actual value.
392     * @param QName $name        The value name.
393     * @param QName $type        The value type.
394     * @param array $options     A list of encoding and serialization options.
395     * @param array $attributes  A hash of additional attributes.
396     * @param string $artype     The type of any array elements.
397     */
398    function _serializeValue($value, $name = null, $type = null,
399                             $options = array(), $attributes = array(),
400                             $artype = '')
401    {
402        $namespaces  = array();
403        $arrayType   = $array_depth = $xmlout_value = null;
404        $typePrefix  = $elPrefix = $xmlout_arrayType = '';
405        $xmlout_type = $xmlns = $ptype = $array_type_ns = '';
406
407        if (!$name->name || is_numeric($name->name)) {
408            $name->name = 'item';
409        }
410
411        if ($this->_wsdl) {
412            list($ptype, $arrayType, $array_type_ns, $array_depth)
413                = $this->_wsdl->getSchemaType($type, $name);
414        }
415
416        if (!$arrayType) {
417            $arrayType = $artype;
418        }
419        if (!$ptype) {
420            $ptype = $this->_getType($value);
421        }
422        if (!$type) {
423            $type = new QName($ptype);
424        }
425
426        if (strcasecmp($ptype, 'Struct') == 0 ||
427            strcasecmp($type->name, 'Struct') == 0) {
428            // Struct
429            $vars = is_object($value) ? get_object_vars($value) : $value;
430            if (is_array($vars)) {
431                foreach (array_keys($vars) as $k) {
432                    // Hide private vars.
433                    if ($k[0] == '_') {
434                        continue;
435                    }
436
437                    if (is_object($vars[$k])) {
438                        if (is_a($vars[$k], 'SOAP_Value')) {
439                            $xmlout_value .= $vars[$k]->serialize($this);
440                        } else {
441                            // XXX get the members and serialize them instead
442                            // converting to an array is more overhead than we
443                            // should really do.
444                            $xmlout_value .= $this->_serializeValue(get_object_vars($vars[$k]), new QName($k, $this->_section5 ? null : $name->namepace), null, $options);
445                        }
446                    } else {
447                        $xmlout_value .= $this->_serializeValue($vars[$k], new QName($k, $this->_section5 ? null : $name->namespace), false, $options);
448                    }
449                }
450            }
451        } elseif (strcasecmp($ptype, 'Array') == 0 ||
452                  strcasecmp($type->name, 'Array') == 0) {
453            // Array.
454            $type = new QName('Array', SOAP_SCHEMA_ENCODING);
455            $numtypes = 0;
456            $value = (array)$value;
457            // XXX this will be slow on larger arrays.  Basically, it flattens
458            // arrays to allow us to serialize multi-dimensional arrays.  We
459            // only do this if arrayType is set, which will typically only
460            // happen if we are using WSDL
461            if (isset($options['flatten']) ||
462                ($arrayType &&
463                 (strchr($arrayType, ',') || strstr($arrayType, '][')))) {
464                $numtypes = $this->_multiArrayType($value, $arrayType,
465                                                   $ar_size, $xmlout_value);
466            }
467
468            $array_type = $array_type_prefix = '';
469            if ($numtypes != 1) {
470                $arrayTypeQName = new QName($arrayType);
471                $arrayType = $arrayTypeQName->name;
472                $array_types = array();
473                $array_val = null;
474
475                // Serialize each array element.
476                $ar_size = count($value);
477                foreach ($value as $array_val) {
478                    if (is_a($array_val, 'SOAP_Value')) {
479                        $array_type = $array_val->type;
480                        $array_types[$array_type] = 1;
481                        $array_type_ns = $array_val->type_namespace;
482                        $xmlout_value .= $array_val->serialize($this);
483                    } else {
484                        $array_type = $this->_getType($array_val);
485                        $array_types[$array_type] = 1;
486                        if (empty($options['keep_arrays_flat'])) {
487                            $xmlout_value .= $this->_serializeValue($array_val, new QName('item', $this->_section5 ? null : $name->namespace), new QName($array_type), $options);
488                        } else {
489                            $xmlout_value .= $this->_serializeValue($array_val, $name, new QName($array_type), $options, $attributes);
490                        }
491                    }
492                }
493
494                if (!$arrayType) {
495                    $numtypes = count($array_types);
496                    if ($numtypes == 1) {
497                        $arrayType = $array_type;
498                    }
499                    // Using anyType is more interoperable.
500                    if ($array_type == 'Struct') {
501                        $array_type = '';
502                    } elseif ($array_type == 'Array') {
503                        $arrayType = 'anyType';
504                        $array_type_prefix = 'xsd';
505                    } else {
506                        if (!$arrayType) {
507                            $arrayType = $array_type;
508                        }
509                    }
510                }
511            }
512            if (!$arrayType || $numtypes > 1) {
513                // Should reference what schema we're using.
514                $arrayType = 'xsd:anyType';
515            } else {
516                if ($array_type_ns) {
517                    $array_type_prefix = $this->_getNamespacePrefix($array_type_ns);
518                } elseif (isset($this->_typemap[$this->_XMLSchemaVersion][$arrayType])) {
519                    $array_type_prefix = $this->_namespaces[$this->_XMLSchemaVersion];
520                } elseif (isset($this->_typemap[SOAP_SCHEMA_ENCODING][$arrayType])) {
521                    $array_type_prefix = SOAP_BASE::SOAPENCPrefix();
522                }
523                if ($array_type_prefix) {
524                    $arrayType = $array_type_prefix . ':' . $arrayType;
525                }
526            }
527
528            $xmlout_arrayType = ' ' . SOAP_BASE::SOAPENCPrefix()
529                . ':arrayType="' . $arrayType;
530            if ($array_depth != null) {
531                for ($i = 0; $i < $array_depth; $i++) {
532                    $xmlout_arrayType .= '[]';
533                }
534            }
535            $xmlout_arrayType .= "[$ar_size]\"";
536        } elseif (is_a($value, 'SOAP_Value')) {
537            $xmlout_value = $value->serialize($this);
538        } elseif ($type->name == 'string') {
539            $xmlout_value = htmlspecialchars($value);
540        } elseif ($type->name == 'rawstring') {
541            $xmlout_value = $value;
542        } elseif ($type->name == 'boolean') {
543            $xmlout_value = $value ? 'true' : 'false';
544        } else {
545            $xmlout_value = $value;
546        }
547
548        // Add namespaces.
549        if ($name->namespace) {
550            $elPrefix = $this->_getNamespacePrefix($name->namespace);
551            if ($elPrefix) {
552                $xmlout_name = $elPrefix . ':' . $name->name;
553            } else {
554                $xmlout_name = $name->name;
555            }
556        } else {
557            $xmlout_name = $name->name;
558        }
559
560        if ($type->namespace) {
561            $typePrefix = false;
562            if (empty($options['no_type_prefix'])) {
563                $typePrefix = $this->_getNamespacePrefix($type->namespace);
564            }
565            if ($typePrefix) {
566                $xmlout_type = $typePrefix . ':' . $type->name;
567            } else {
568                $xmlout_type = $type->name;
569            }
570        } elseif ($type->name &&
571                  isset($this->_typemap[$this->_XMLSchemaVersion][$type->name])) {
572            $typePrefix = $this->_namespaces[$this->_XMLSchemaVersion];
573            if ($typePrefix) {
574                $xmlout_type = $typePrefix . ':' . $type->name;
575            } else {
576                $xmlout_type = $type->name;
577            }
578        }
579
580        // Handle additional attributes.
581        $xml_attr = '';
582        if (count($attributes)) {
583            foreach ($attributes as $k => $v) {
584                $kqn = new QName($k);
585                $vqn = new QName($v);
586                $xml_attr .= ' ' . $kqn->fqn() . '="' . $vqn->fqn() . '"';
587            }
588        }
589
590        // Store the attachment for mime encoding.
591        if (isset($options['attachment']) &&
592            !PEAR::isError($options['attachment'])) {
593            $this->_attachments[] = $options['attachment'];
594        }
595
596        if ($this->_section5) {
597            if ($xmlout_type) {
598                $xmlout_type = " xsi:type=\"$xmlout_type\"";
599            }
600            if (is_null($xmlout_value)) {
601                $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
602                    "$xml_attr xsi:nil=\"true\"/>";
603            } else {
604                $xml = "\r\n<$xmlout_name$xmlout_type$xmlns$xmlout_arrayType" .
605                    "$xml_attr>$xmlout_value</$xmlout_name>";
606            }
607        } elseif ($type->name == 'Array' && !empty($options['keep_arrays_flat'])) {
608            $xml = $xmlout_value;
609        } else {
610            if (is_null($xmlout_value)) {
611                $xml = "\r\n<$xmlout_name$xmlns$xml_attr/>";
612            } else {
613                $xml = "\r\n<$xmlout_name$xmlns$xml_attr>" .
614                    $xmlout_value . "</$xmlout_name>";
615            }
616        }
617
618        return $xml;
619    }
620
621    /**
622     * Converts a PHP type to a SOAP type.
623     *
624     * @param mixed $value  The value to inspect.
625     *
626     * @return string  The value's SOAP type.
627     */
628    function _getType($value)
629    {
630        $type = gettype($value);
631        switch ($type) {
632        case 'object':
633            if (is_a($value, 'soap_value')) {
634                $type = $value->type;
635            } else {
636                $type = 'Struct';
637            }
638            break;
639
640        case 'array':
641            // Hashes are always handled as structs.
642            if ($this->_isHash($value)) {
643                $type = 'Struct';
644                break;
645            }
646            if (count($value) > 1) {
647                // For non-wsdl structs that are all the same type
648                reset($value);
649                $value1 = next($value);
650                $value2 = next($value);
651                if (is_a($value1, 'SOAP_Value') &&
652                    is_a($value2, 'SOAP_Value') &&
653                    $value1->name != $value2->name) {
654                    // This is a struct, not an array.
655                    $type = 'Struct';
656                    break;
657                }
658            }
659            $type = 'Array';
660            break;
661
662        case 'integer':
663        case 'long':
664            $type = 'int';
665            break;
666
667        case 'boolean':
668            break;
669
670        case 'double':
671            // double is deprecated in PHP 4.2 and later.
672            $type = 'float';
673            break;
674
675        case 'null':
676            $type = '';
677            break;
678
679        case 'string':
680        default:
681            break;
682        }
683
684        return $type;
685    }
686
687    function _multiArrayType($value, &$type, &$size, &$xml)
688    {
689        if (is_array($value)) {
690            // Seems we have a multi dimensional array, figure it out if we
691            // do.
692            for ($i = 0, $c = count($value); $i < $c; ++$i) {
693                $this->_multiArrayType($value[$i], $type, $size, $xml);
694            }
695
696            $sz = count($value);
697            if ($size) {
698                $size = $sz . ',' . $size;
699            } else {
700                $size = $sz;
701            }
702            return 1;
703        } elseif (is_object($value)) {
704            $type = $value->type;
705            $xml .= $value->serialize($this);
706        } else {
707            $type = $this->_getType($value);
708            $xml .= $this->_serializeValue($value, new QName('item'), new QName($type));
709        }
710        $size = null;
711
712        return 1;
713    }
714
715    /**
716     * Returns whether a type is a base64 type.
717     *
718     * @param string $type  A type name.
719     *
720     * @return boolean  True if the type name is a base64 type.
721     */
722    function _isBase64Type($type)
723    {
724        return $type == 'base64' || $type == 'base64Binary';
725    }
726
727    /**
728     * Returns whether an array is a hash.
729     *
730     * @param array $a  An array to check.
731     *
732     * @return boolean  True if the specified array is a hash.
733     */
734    function _isHash($a)
735    {
736        foreach (array_keys($a) as $k) {
737            // Checking the type is faster than regexp.
738            if (!is_int($k)) {
739                return true;
740            }
741        }
742        return false;
743    }
744
745    function _un_htmlentities($string)
746    {
747        $trans_tbl = get_html_translation_table(HTML_ENTITIES);
748        $trans_tbl = array_flip($trans_tbl);
749        return strtr($string, $trans_tbl);
750    }
751
752    /**
753     * Converts a SOAP_Value object into a PHP value.
754     */
755    function _decode($soapval)
756    {
757        if (!is_a($soapval, 'SOAP_Value')) {
758            return $soapval;
759        }
760
761        if (is_array($soapval->value)) {
762            $isstruct = $soapval->type != 'Array';
763            if ($isstruct) {
764                $classname = $this->_defaultObjectClassname;
765                if (isset($this->_type_translation[$soapval->tqn->fqn()])) {
766                    // This will force an error in PHP if the class does not
767                    // exist.
768                    $classname = $this->_type_translation[$soapval->tqn->fqn()];
769                } elseif (isset($this->_type_translation[$soapval->type])) {
770                    // This will force an error in PHP if the class does not
771                    // exist.
772                    $classname = $this->_type_translation[$soapval->type];
773                } elseif ($this->_auto_translation) {
774                    if (class_exists($soapval->type)) {
775                        $classname = $soapval->type;
776                    } elseif ($this->_wsdl) {
777                        $t = $this->_wsdl->getComplexTypeNameForElement($soapval->name, $soapval->namespace);
778                        if ($t && class_exists($t)) {
779                            $classname = $t;
780                        }
781                    }
782                }
783                $return = new $classname;
784            } else {
785                $return = array();
786            }
787
788            foreach ($soapval->value as $item) {
789                if ($isstruct) {
790                    if ($this->_wsdl) {
791                        // Get this child's WSDL information.
792                        // /$soapval->prefix/$soapval->type/$item->prefix/$item->name
793                        $child_type = $this->_wsdl->getComplexTypeChildType(
794                            $soapval->namespace,
795                            $soapval->name,
796                            $item->namespace,
797                            $item->name);
798                        if ($child_type) {
799                            $item->type = $child_type;
800                        }
801                    }
802                    if ($item->type == 'Array') {
803                        if (isset($return->{$item->name}) &&
804                            is_object($return->{$item->name})) {
805                            $return->{$item->name} = $this->_decode($item);
806                        } elseif (isset($return->{$item->name}) &&
807                                  is_array($return->{$item->name})) {
808                            $return->{$item->name}[] = $this->_decode($item);
809                        } elseif (isset($return->{$item->name})) {
810                            $return->{$item->name} = array(
811                                $return->{$item->name},
812                                $this->_decode($item)
813                            );
814                        } elseif (is_array($return)) {
815                            $return[] = $this->_decode($item);
816                        } else {
817                            $return->{$item->name} = $this->_decode($item);
818                        }
819                    } elseif (isset($return->{$item->name})) {
820                        $d = $this->_decode($item);
821                        if (count(get_object_vars($return)) == 1) {
822                            $isstruct = false;
823                            $return = array($return->{$item->name}, $d);
824                        } else {
825                            if (is_array($return->{$item->name})) {
826                                $return->{$item->name} = array_merge($return->{$item->name}, array($d));
827                            } else {
828                                $return->{$item->name} = array($return->{$item->name}, $d);
829                            }
830                        }
831                    } else {
832                        $return->{$item->name} = $this->_decode($item);
833                    }
834                    // Set the attributes as members in the class.
835                    if (method_exists($return, '__set_attribute')) {
836                        foreach ($soapval->attributes as $key => $value) {
837                            call_user_func_array(array(&$return,
838                                                       '__set_attribute'),
839                                                 array($key, $value));
840                        }
841                    }
842                } else {
843                    if ($soapval->arrayType && is_a($item, 'SOAP_Value')) {
844                        if ($this->_isBase64Type($item->type) &&
845                            !$this->_isBase64Type($soapval->arrayType)) {
846                            // Decode the value if we're losing the base64
847                            // type information.
848                            $item->value = base64_decode($item->value);
849                        }
850                        $item->type = $soapval->arrayType;
851                    }
852                    $return[] = $this->_decode($item);
853                }
854            }
855
856            return $return;
857        }
858
859        if ($soapval->type == 'boolean') {
860            if ($soapval->value != '0' &&
861                strcasecmp($soapval->value, 'false') != 0) {
862                $soapval->value = true;
863            } else {
864                $soapval->value = false;
865            }
866        } elseif ($soapval->type &&
867                  isset($this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type])) {
868            // If we can, set variable type.
869            settype($soapval->value,
870                    $this->_typemap[SOAP_XML_SCHEMA_VERSION][$soapval->type]);
871        } elseif ($soapval->type == 'Struct') {
872            $soapval->value = null;
873        }
874
875        return $soapval->value;
876    }
877
878    /**
879     * Creates the SOAP envelope with the SOAP envelop data.
880     *
881     * @param SOAP_Value $method  SOAP_Value instance with the method name as
882     *                            the name, and the method arguments as the
883     *                            value.
884     * @param array $headers      A list of additional SOAP_Header objects.
885     * @param string $encoding    The charset of the SOAP message.
886     * @param array $options      A list of encoding/serialization options.
887     *
888     * @return string  The complete SOAP message.
889     */
890    function makeEnvelope($method, $headers, $encoding = SOAP_DEFAULT_ENCODING,
891                          $options = array())
892    {
893        $smsg = $header_xml = $ns_string = '';
894
895        if ($headers) {
896            for ($i = 0, $c = count($headers); $i < $c; $i++) {
897                $header_xml .= $headers[$i]->serialize($this);
898            }
899            $header_xml = sprintf("<%s:Header>\r\n%s\r\n</%s:Header>\r\n",
900                                  SOAP_BASE::SOAPENVPrefix(), $header_xml,
901                                  SOAP_BASE::SOAPENVPrefix());
902        }
903
904        if (!isset($options['input']) || $options['input'] == 'parse') {
905            if (is_array($method)) {
906                for ($i = 0, $c = count($method); $i < $c; $i++) {
907                    $smsg .= $method[$i]->serialize($this);
908                }
909            } else {
910                $smsg = $method->serialize($this);
911            }
912        } else {
913            $smsg = $method;
914        }
915        $body = sprintf("<%s:Body>%s\r\n</%s:Body>\r\n",
916                        SOAP_BASE::SOAPENVPrefix(), $smsg,
917                        SOAP_BASE::SOAPENVPrefix());
918
919        foreach ($this->_namespaces as $k => $v) {
920            $ns_string .= "\r\n " . sprintf('xmlns:%s="%s"', $v, $k);
921        }
922        if ($this->_namespace) {
923            $ns_string .= "\r\n " . sprintf('xmlns="%s"', $this->_namespace);
924        }
925
926        /* If 'use' == 'literal', do not put in the encodingStyle.  This is
927         * denoted by $this->_section5 being false.  'use' can be defined at a
928         * more granular level than we are dealing with here, so this does not
929         * work for all services. */
930        $xml = sprintf('<?xml version="1.0" encoding="%s"?>%s<%s:Envelope%s',
931                       $encoding, "\r\n", SOAP_BASE::SOAPENVPrefix(),
932                       $ns_string);
933        if ($this->_section5) {
934            $xml .= "\r\n " . sprintf('%s:encodingStyle="%s"',
935                                      SOAP_BASE::SOAPENVPrefix(),
936                                      SOAP_SCHEMA_ENCODING);
937        }
938        $xml .= sprintf('>%s%s%s</%s:Envelope>' . "\r\n",
939                        "\r\n", $header_xml, $body, SOAP_BASE::SOAPENVPrefix());
940
941        return $xml;
942    }
943
944    function _makeMimeMessage($xml, $encoding = SOAP_DEFAULT_ENCODING)
945    {
946        if (!@include_once 'Mail/mimePart.php') {
947            return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
948        }
949
950        // Encode any attachments.  See http://www.w3.org/TR/SOAP-attachments
951        // Now we have to mime encode the message.
952        $params = array('content_type' => 'multipart/related; type="text/xml"');
953        $msg = new Mail_mimePart('', $params);
954
955        // Add the xml part.
956        $params['content_type'] = 'text/xml';
957        $params['charset'] = $encoding;
958        $msg->addSubPart($xml, $params);
959
960        // Add the attachements
961        for ($i = 0, $c = count($this->_attachments); $i < $c; ++$i) {
962            $msg->addSubPart($this->_attachments[$i]['body'],
963                             $this->_attachments[$i]);
964        }
965
966        return $msg->encode();
967    }
968
969    // TODO: this needs to be used from the Transport system.
970    function _makeDIMEMessage($xml)
971    {
972        if (!@include_once 'Net/DIME.php') {
973            return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
974        }
975
976        // Encode any attachments.  See
977        // http://search.ietf.org/internet-drafts/draft-nielsen-dime-soap-00.txt
978        // Now we have to DIME encode the message
979        $dime = new Net_DIME_Message();
980        $msg = $dime->encodeData($xml, SOAP_ENVELOP, null, NET_DIME_TYPE_URI);
981
982        // Add the attachments.
983        $c = count($this->_attachments);
984        for ($i = 0; $i < $c; $i++) {
985            $msg .= $dime->encodeData($this->_attachments[$i]['body'],
986                                      $this->_attachments[$i]['content_type'],
987                                      $this->_attachments[$i]['cid'],
988                                      NET_DIME_TYPE_MEDIA);
989        }
990        $msg .= $dime->endMessage();
991
992        return $msg;
993    }
994
995    function _decodeMimeMessage(&$data, &$headers, &$attachments)
996    {
997        if (!@include_once 'Mail/mimeDecode.php') {
998            return $this->_raiseSoapFault('MIME messages are unsupported, the Mail_Mime package is not installed');
999        }
1000
1001        $params['include_bodies'] = true;
1002        $params['decode_bodies']  = true;
1003        $params['decode_headers'] = true;
1004
1005        // Lame thing to have to do for decoding.
1006        $decoder = new Mail_mimeDecode($data);
1007        $structure = $decoder->decode($params);
1008
1009        if (isset($structure->body)) {
1010            $data = $structure->body;
1011            $headers = $structure->headers;
1012            unset($headers['']);
1013            return;
1014        } elseif (isset($structure->parts)) {
1015            $data = $structure->parts[0]->body;
1016            $headers = array_merge($structure->headers,
1017                                   $structure->parts[0]->headers);
1018            unset($headers['']);
1019            if (count($structure->parts) <= 1) {
1020                return;
1021            }
1022
1023            $mime_parts = array_splice($structure->parts, 1);
1024            // Prepare the parts for the SOAP parser.
1025            for ($i = 0, $c = count($mime_parts); $i < $c; $i++) {
1026                $p = $mime_parts[$i];
1027                if (isset($p->headers['content-location'])) {
1028                    // TODO: modify location per SwA note section 3
1029                    // http://www.w3.org/TR/SOAP-attachments
1030                    $attachments[$p->headers['content-location']] = $p->body;
1031                } else {
1032                    $cid = 'cid:' . substr($p->headers['content-id'], 1, -1);
1033                    $attachments[$cid] = $p->body;
1034                }
1035            }
1036
1037            return;
1038        }
1039
1040        $this->_raiseSoapFault('Mime parsing error', '', '', 'Server');
1041    }
1042
1043    function _decodeDIMEMessage(&$data, &$headers, &$attachments)
1044    {
1045        if (!@include_once 'Net/DIME.php') {
1046            return $this->_raiseSoapFault('DIME messages are unsupported, the Net_DIME package is not installed');
1047        }
1048
1049        // This SHOULD be moved to the transport layer, e.g. PHP itself should
1050        // handle parsing DIME ;)
1051        $dime = new Net_DIME_Message();
1052        $err = $dime->decodeData($data);
1053        if (PEAR::isError($err)) {
1054            $this->_raiseSoapFault('Failed to decode the DIME message!', '', '', 'Server');
1055            return;
1056        }
1057        if (strcasecmp($dime->parts[0]['type'], SOAP_ENVELOP) != 0) {
1058            $this->_raiseSoapFault('DIME record 1 is not a SOAP envelop!', '', '', 'Server');
1059            return;
1060        }
1061
1062        $data = $dime->parts[0]['data'];
1063        // Fake it for now.
1064        $headers['content-type'] = 'text/xml';
1065        $c = count($dime->parts);
1066        for ($i = 0; $i < $c; $i++) {
1067            $part =& $dime->parts[$i];
1068            // We need to handle URI's better.
1069            $id = strncmp($part['id'], 'cid:', 4)
1070                ? 'cid:' . $part['id']
1071                : $part['id'];
1072            $attachments[$id] = $part['data'];
1073        }
1074    }
1075
1076    /**
1077     * Explicitly sets the translation for a specific class.
1078     *
1079     * Auto translation works for all cases, but opens ANY class in the script
1080     * to be used as a data type, and may not be desireable.
1081     *
1082     * @param string $type   A SOAP type.
1083     * @param string $class  A PHP class name.
1084     */
1085    function setTypeTranslation($type, $class = null)
1086    {
1087        $tq = new QName($type);
1088        if (!$class) {
1089            $class = $tq->name;
1090        }
1091        $this->_type_translation[$type]=$class;
1092    }
1093
1094}
1095
1096/**
1097 * Class used to handle QNAME values in XML.
1098 *
1099 * @package  SOAP
1100 * @author   Shane Caraveo <shane@php.net> Conversion to PEAR and updates
1101 */
1102class QName
1103{
1104    var $name = '';
1105    var $prefix = '';
1106    var $namespace = '';
1107
1108    function QName($name, $namespace = '')
1109    {
1110        if ($name && $name[0] == '{') {
1111            preg_match('/\{(.*?)\}(.*)/', $name, $m);
1112            $this->name = $m[2];
1113            $this->namespace = $m[1];
1114        } elseif (substr_count($name, ':') == 1) {
1115            $s = explode(':', $name);
1116            $this->prefix = $s[0];
1117            $this->name = $s[1];
1118            $this->namespace = $namespace;
1119        } else {
1120            $this->name = $name;
1121            $this->namespace = $namespace;
1122        }
1123
1124        // A little more magic than should be in a qname.
1125        $p = strpos($this->name, '[');
1126        if ($p) {
1127            // TODO: Need to re-examine this logic later.
1128            // Chop off [].
1129            $this->arraySize = explode(',', substr($this->name, $p + 1, -$p - 2));
1130            $this->arrayInfo = substr($this->name, $p);
1131            $this->name = substr($this->name, 0, $p);
1132        }
1133    }
1134
1135    function fqn()
1136    {
1137        if ($this->namespace) {
1138            return '{' . $this->namespace . '}' . $this->name;
1139        } elseif ($this->prefix) {
1140            return $this->prefix . ':' . $this->name;
1141        }
1142        return $this->name;
1143    }
1144
1145}
Note: See TracBrowser for help on using the repository browser.