Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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. */
28
define('MAIL_MIMEPART_CRLF', "\r\n");
29
 
30
require_once 'PEAR.php';
31
 
32
if (!defined('INF')) {
33
    define('INF', 1.8e307);
34
}
35
if (!defined('NAN')) {
36
    define('NAN', 0.0);
37
}
38
 
39
define('SOAP_LIBRARY_VERSION', '0.12.0');
40
define('SOAP_LIBRARY_NAME',    'PEAR-SOAP 0.12.0-beta');
41
 
42
// Set schema version.
43
define('SOAP_XML_SCHEMA_VERSION',  'http://www.w3.org/2001/XMLSchema');
44
define('SOAP_XML_SCHEMA_INSTANCE', 'http://www.w3.org/2001/XMLSchema-instance');
45
define('SOAP_XML_SCHEMA_1999',     'http://www.w3.org/1999/XMLSchema');
46
define('SOAP_SCHEMA',              'http://schemas.xmlsoap.org/wsdl/soap/');
47
define('SOAP_SCHEMA_ENCODING',     'http://schemas.xmlsoap.org/soap/encoding/');
48
define('SOAP_ENVELOP',             'http://schemas.xmlsoap.org/soap/envelope/');
49
 
50
define('SCHEMA_DISCO',             'http://schemas.xmlsoap.org/disco/');
51
define('SCHEMA_DISCO_SCL',         'http://schemas.xmlsoap.org/disco/scl/');
52
 
53
define('SCHEMA_SOAP',              'http://schemas.xmlsoap.org/wsdl/soap/');
54
define('SCHEMA_SOAP12',            'http://schemas.xmlsoap.org/wsdl/soap12/');
55
define('SCHEMA_SOAP_HTTP',         'http://schemas.xmlsoap.org/soap/http');
56
define('SCHEMA_WSDL_HTTP',         'http://schemas.xmlsoap.org/wsdl/http/');
57
define('SCHEMA_MIME',              'http://schemas.xmlsoap.org/wsdl/mime/');
58
define('SCHEMA_WSDL',              'http://schemas.xmlsoap.org/wsdl/');
59
define('SCHEMA_DIME',              'http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/');
60
define('SCHEMA_CONTENT',           'http://schemas.xmlsoap.org/ws/2002/04/content-type/');
61
define('SCHEMA_REF',               'http://schemas.xmlsoap.org/ws/2002/04/reference/');
62
 
63
define('SOAP_DEFAULT_ENCODING',  'UTF-8');
64
 
65
/**
66
 * @package SOAP
67
 */
68
class 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 <shane@php.net> Conversion to PEAR and updates
157
 */
158
class 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 <shane@php.net> Conversion to PEAR and updates
1097
 */
1098
class 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
}