home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / SOAP / Base.php next >
Encoding:
PHP Script  |  2006-04-07  |  41.1 KB  |  1,246 lines

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