Viewing file: Client.php (27.75 KB) -rw-rw-rw- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php /** * This file contains the code for the SOAP client. * * PHP versions 4 and 5 * * LICENSE: This source file is subject to version 2.02 of the PHP license, * that is bundled with this package in the file LICENSE, and is available at * through the world-wide-web at http://www.php.net/license/2_02.txt. If you * did not receive a copy of the PHP license and are unable to obtain it * through the world-wide-web, please send a note to license@php.net so we can * mail you a copy immediately. * * @category Web Services * @package SOAP * @author Dietrich Ayala <dietrich@ganx4.com> Original Author * @author Shane Caraveo <Shane@Caraveo.com> Port to PEAR and more * @author Chuck Hagenbuch <chuck@horde.org> Maintenance * @author Jan Schneider <jan@horde.org> Maintenance * @copyright 2003-2005 The PHP Group * @license http://www.php.net/license/2_02.txt PHP License 2.02 * @link http://pear.php.net/package/SOAP */
require_once 'SOAP/Value.php'; require_once 'SOAP/Base.php'; require_once 'SOAP/Transport.php'; require_once 'SOAP/WSDL.php'; require_once 'SOAP/Fault.php'; require_once 'SOAP/Parser.php';
// Arnaud: the following code was taken from DataObject and adapted to suit
// this will be horrifically slow!!!! // NOTE: Overload SEGFAULTS ON PHP4 + Zend Optimizer // these two are BC/FC handlers for call in PHP4/5
if (!class_exists('SOAP_Client_Overload')) { if (substr(zend_version(), 0, 1) > 1) { class SOAP_Client_Overload extends SOAP_Base { function __call($method, $args) { $return = null; $this->_call($method, $args, $return); return $return; } } } else { if (!function_exists('clone')) { eval('function clone($t) { return $t; }'); } eval(' class SOAP_Client_Overload extends SOAP_Base { function __call($method, $args, &$return) { return $this->_call($method, $args, $return); } }'); } }
/** * SOAP Client Class * * This class is the main interface for making soap requests. * * basic usage:<code> * $soapclient = new SOAP_Client( string path [ , boolean wsdl] ); * echo $soapclient->call( string methodname [ , array parameters] ); * </code> * or, if using PHP 5+ or the overload extension:<code> * $soapclient = new SOAP_Client( string path [ , boolean wsdl] ); * echo $soapclient->methodname( [ array parameters] ); * </code> * * Originally based on SOAPx4 by Dietrich Ayala * http://dietrich.ganx4.com/soapx4 * * @access public * @package SOAP * @author Shane Caraveo <shane@php.net> Conversion to PEAR and updates * @author Stig Bakken <ssb@fast.no> Conversion to PEAR * @author Dietrich Ayala <dietrich@ganx4.com> Original Author */ class SOAP_Client extends SOAP_Client_Overload { /** * Communication endpoint. * * Currently the following transport formats are supported: * - HTTP * - SMTP * * Example endpoints: * http://www.example.com/soap/server.php * https://www.example.com/soap/server.php * mailto:soap@example.com * * @see SOAP_Client() * @var string */ var $_endpoint = '';
/** * The SOAP PORT name that is used by the client. * * @var string */ var $_portName = '';
/** * Endpoint type e.g. 'wdsl'. * * @var string */ var $_endpointType = '';
/** * The received xml. * * @var string */ var $xml;
/** * The outgoing and incoming data stream for debugging. * * @var string */ var $wire;
/** * The outgoing data stream for debugging. * * @var string */ var $_last_request = null;
/** * The incoming data stream for debugging. * * @var string */ var $_last_response = null;
/** * Options. * * @var array */ var $_options = array('trace' => false);
/** * The character encoding used for XML parser, etc. * * @var string */ var $_encoding = SOAP_DEFAULT_ENCODING;
/** * The array of SOAP_Headers that we are sending. * * @var array */ var $headersOut = null;
/** * The headers we recieved back in the response. * * @var array */ var $headersIn = null;
/** * Options for the HTTP_Request class (see HTTP/Request.php). * * @var array */ var $_proxy_params = array();
/** * The SOAP_Transport instance. * * @var SOAP_Transport */ var $_soap_transport = null;
/** * Constructor. * * @access public * * @param string $endpoint An URL. * @param boolean $wsdl Whether the endpoint is a WSDL file. * @param string $portName The service's port name to use. * @param array $proxy_params Options for the HTTP_Request class * @see HTTP_Request * @param boolean|string $cache Use WSDL caching? The cache directory if * a string. */ function SOAP_Client($endpoint, $wsdl = false, $portName = false, $proxy_params = array(), $cache = false) { parent::SOAP_Base('Client');
$this->_endpoint = $endpoint; $this->_portName = $portName; $this->_proxy_params = $proxy_params;
// This hack should perhaps be removed as it might cause unexpected // behaviour. $wsdl = $wsdl ? $wsdl : strtolower(substr($endpoint, -4)) == 'wsdl';
// make values if ($wsdl) { $this->_endpointType = 'wsdl'; // instantiate wsdl class $this->_wsdl =& new SOAP_WSDL($this->_endpoint, $this->_proxy_params, $cache); if ($this->_wsdl->fault) { $this->_raiseSoapFault($this->_wsdl->fault); } } }
function _reset() { $this->xml = null; $this->wire = null; $this->_last_request = null; $this->_last_response = null; $this->headersIn = null; $this->headersOut = null; }
/** * Sets the character encoding. * * Limited to 'UTF-8', 'US_ASCII' and 'ISO-8859-1'. * * @access public * * @param string encoding * * @return mixed SOAP_Fault on error. */ function setEncoding($encoding) { if (in_array($encoding, $this->_encodings)) { $this->_encoding = $encoding; return; } return $this->_raiseSoapFault('Invalid Encoding'); }
/** * Adds a header to the envelope. * * @access public * * @param SOAP_Header $soap_value A SOAP_Header or an array with the * elements 'name', 'namespace', * 'mustunderstand', and 'actor' to send * as a header. */ function addHeader(&$soap_value) { // Add a new header to the message. if (is_a($soap_value, 'SOAP_Header')) { $this->headersOut[] =& $soap_value; } elseif (is_array($soap_value)) { // name, value, namespace, mustunderstand, actor $this->headersOut[] =& new SOAP_Header($soap_value[0], null, $soap_value[1], $soap_value[2], $soap_value[3]);; } else { $this->_raiseSoapFault('Invalid parameter provided to addHeader(). Must be an array or a SOAP_Header.'); } }
/** * Calls a method on the SOAP endpoint. * * The namespace parameter is overloaded to accept an array of options * that can contain data necessary for various transports if it is used as * an array, it MAY contain a namespace value and a soapaction value. If * it is overloaded, the soapaction parameter is ignored and MUST be * placed in the options array. This is done to provide backwards * compatibility with current clients, but may be removed in the future. * The currently supported values are:<pre> * namespace * soapaction * timeout (HTTP socket timeout) * transfer-encoding (SMTP, Content-Transfer-Encoding: header) * from (SMTP, From: header) * subject (SMTP, Subject: header) * headers (SMTP, hash of extra SMTP headers) * </pre> * * @access public * * @param string $method The method to call. * @param array $params The method parameters. * @param string|array $namespace Namespace or hash with options. * @param string $soapAction * * @return mixed The method result or a SOAP_Fault on error. */ function &call($method, &$params, $namespace = false, $soapAction = false) { $this->headersIn = null; $this->_last_request = null; $this->_last_response = null; $this->wire = null; $this->xml = null;
$soap_data =& $this->_generate($method, $params, $namespace, $soapAction); if (PEAR::isError($soap_data)) { $fault =& $this->_raiseSoapFault($soap_data); return $fault; }
// _generate() may have changed the endpoint if the WSDL has more // than one service, so we need to see if we need to generate a new // transport to hook to a different URI. Since the transport protocol // can also change, we need to get an entirely new object. This could // probably be optimized. if (!$this->_soap_transport || $this->_endpoint != $this->_soap_transport->url) { $this->_soap_transport =& SOAP_Transport::getTransport($this->_endpoint); if (PEAR::isError($this->_soap_transport)) { $fault =& $this->_soap_transport; $this->_soap_transport = null; $fault =& $this->_raiseSoapFault($fault); return $fault; } } $this->_soap_transport->encoding = $this->_encoding;
// Send the message. $transport_options = array_merge_recursive($this->_proxy_params, $this->_options); $this->xml = $this->_soap_transport->send($soap_data, $transport_options);
// Save the wire information for debugging. if ($this->_options['trace']) { $this->_last_request = $this->_soap_transport->outgoing_payload; $this->_last_response = $this->_soap_transport->incoming_payload; $this->wire = $this->getWire(); } if ($this->_soap_transport->fault) { $fault =& $this->_raiseSoapFault($this->xml); return $fault; }
if (isset($this->_options['result']) && $this->_options['result'] != 'parse') { return $this->xml; }
$this->__result_encoding = $this->_soap_transport->result_encoding;
$result = &$this->parseResponse($this->xml, $this->__result_encoding, $this->_soap_transport->attachments); return $result; }
/** * Sets an option to use with the transport layers. * * For example: * <code> * $soapclient->setOpt('curl', CURLOPT_VERBOSE, 1) * </code> * to pass a specific option to curl if using an SSL connection. * * @access public * * @param string $category Category to which the option applies or option * name. * @param string $option An option name if $category is a category name, * an option value if $category is an option name. * @param string $value An option value if $category is a category * name. */ function setOpt($category, $option, $value = null) { if (!is_null($value)) { if (!isset($this->_options[$category])) { $this->_options[$category] = array(); } $this->_options[$category][$option] = $value; } else { $this->_options[$category] = $option; } }
/** * Call method supporting the overload extension. * * If the overload extension is loaded, you can call the client class with * a soap method name: * <code> * $soap = new SOAP_Client(....); * $value = $soap->getStockQuote('MSFT'); * </code> * * @access public * * @param string $method The method to call. * @param array $params The method parameters. * @param mixed $return_value Will get the method's return value * assigned. * * @return boolean Always true. */ function _call($method, $params, &$return_value) { // Overloading lowercases the method name, we need to look into the // WSDL and try to find the correct method name to get the correct // case for the call. if ($this->_wsdl) { $this->_wsdl->matchMethod($method); }
$return_value =& $this->call($method, $params);
return true; }
/** * @deprecated Use getLastRequest(). */ function &__getlastrequest() { $request = $this->getLastRequest(); return $request; }
/** * Returns the XML content of the last SOAP request. * * @return string The last request. */ function getLastRequest() { return $this->_last_request; }
/** * @deprecated Use getLastResponse(). */ function &__getlastresponse() { $response =& $this->getLastResponse; return $response; }
/** * Returns the XML content of the last SOAP response. * * @return string The last response. */ function getLastResponse() { return $this->_last_response; }
/** * @deprecated Use setUse(). */ function __use($use) { $this->setUse($use); }
/** * Sets the SOAP encoding. * * @param string $use Either 'literal' or 'encoded' (section 5). */ function setUse($use) { $this->_options['use'] = $use; }
/** * @deprecated Use setStyle(). */ function __style($style) { $this->setStyle($style); }
/** * Sets the SOAP encoding style. * * @param string $style Either 'document' or 'rpc'. */ function setStyle($style) { $this->_options['style'] = $style; }
/** * @deprecated Use setTrace(). */ function __trace($level) { $this->setTrace($level); }
/** * Sets whether to trace the traffic on the transport level. * * @see getWire() * * @param boolean $trace */ function setTrace($trace) { $this->_options['trace'] = $trace; }
function &_generate($method, &$params, $namespace = false, $soapAction = false) { $this->fault = null; $this->_options['input'] = 'parse'; $this->_options['result'] = 'parse'; $this->_options['parameters'] = false;
if ($params && gettype($params) != 'array') { $params = array($params); }
if (gettype($namespace) == 'array') { foreach ($namespace as $optname => $opt) { $this->_options[strtolower($optname)] = $opt; } if (isset($this->_options['namespace'])) { $namespace = $this->_options['namespace']; } else { $namespace = false; } } else { // We'll place $soapAction into our array for usage in the // transport. $this->_options['soapaction'] = $soapAction; $this->_options['namespace'] = $namespace; }
if ($this->_endpointType == 'wsdl') { $this->_setSchemaVersion($this->_wsdl->xsd);
// Get port name. if (!$this->_portName) { $this->_portName = $this->_wsdl->getPortName($method); } if (PEAR::isError($this->_portName)) { $fault =& $this->_raiseSoapFault($this->_portName); return $fault; }
// Get endpoint. $this->_endpoint = $this->_wsdl->getEndpoint($this->_portName); if (PEAR::isError($this->_endpoint)) { $fault =& $this->_raiseSoapFault($this->_endpoint); return $fault; }
// Get operation data. $opData = $this->_wsdl->getOperationData($this->_portName, $method);
if (PEAR::isError($opData)) { $fault =& $this->_raiseSoapFault($opData); return $fault; } $namespace = $opData['namespace']; $this->_options['style'] = $opData['style']; $this->_options['use'] = $opData['input']['use']; $this->_options['soapaction'] = $opData['soapAction'];
// Set input parameters. if ($this->_options['input'] == 'parse') { $this->_options['parameters'] = $opData['parameters']; $nparams = array(); if (isset($opData['input']['parts']) && count($opData['input']['parts'])) { $i = 0; foreach ($opData['input']['parts'] as $name => $part) { $xmlns = ''; $attrs = array(); // Is the name a complex type? if (isset($part['element'])) { $xmlns = $this->_wsdl->namespaces[$part['namespace']]; $part = $this->_wsdl->elements[$part['namespace']][$part['type']]; $name = $part['name']; } if (isset($params[$name]) || $this->_wsdl->getDataHandler($name, $part['namespace'])) { $nparams[$name] =& $params[$name]; } else { // We now force an associative array for // parameters if using WSDL. $fault =& $this->_raiseSoapFault("The named parameter $name is not in the call parameters."); return $fault; } if (gettype($nparams[$name]) != 'object' || !is_a($nparams[$name], 'SOAP_Value')) { // Type is likely a qname, split it apart, and get // the type namespace from WSDL. $qname =& new QName($part['type']); if ($qname->ns) { $type_namespace = $this->_wsdl->namespaces[$qname->ns]; } elseif (isset($part['namespace'])) { $type_namespace = $this->_wsdl->namespaces[$part['namespace']]; } else { $type_namespace = null; } $qname->namespace = $type_namespace; $type = $qname->name; $pqname = $name; if ($xmlns) { $pqname = '{' . $xmlns . '}' . $name; } $nparams[$name] =& new SOAP_Value($pqname, $qname->fqn(), $nparams[$name], $attrs); } else { // WSDL fixups to the SOAP value. } } } $params =& $nparams; unset($nparams); } } else { $this->_setSchemaVersion(SOAP_XML_SCHEMA_VERSION); }
// Serialize the message. $this->_section5 = (!isset($this->_options['use']) || $this->_options['use'] != 'literal');
if (!isset($this->_options['style']) || $this->_options['style'] == 'rpc') { $this->_options['style'] = 'rpc'; $this->docparams = true; $mqname =& new QName($method, $namespace); $methodValue =& new SOAP_Value($mqname->fqn(), 'Struct', $params); $soap_msg = $this->makeEnvelope($methodValue, $this->headersOut, $this->_encoding, $this->_options); } else { if (!$params) { $mqname =& new QName($method, $namespace); $mynull = null; $params =& new SOAP_Value($mqname->fqn(), 'Struct', $mynull); } elseif ($this->_options['input'] == 'parse') { if (is_array($params)) { $nparams = array(); $keys = array_keys($params); foreach ($keys as $k) { if (gettype($params[$k]) != 'object') { $nparams[] =& new SOAP_Value($k, false, $params[$k]); } else { $nparams[] =& $params[$k]; } } $params =& $nparams; } if ($this->_options['parameters']) { $mqname =& new QName($method, $namespace); $params =& new SOAP_Value($mqname->fqn(), 'Struct', $params); } } $soap_msg = $this->makeEnvelope($params, $this->headersOut, $this->_encoding, $this->_options); } unset($this->headersOut);
if (PEAR::isError($soap_msg)) { $fault =& $this->_raiseSoapFault($soap_msg); return $fault; }
// Handle MIME or DIME encoding. // TODO: DIME encoding should move to the transport, do it here for // now and for ease of getting it done. if (count($this->_attachments)) { if ((isset($this->_options['attachments']) && $this->_options['attachments'] == 'Mime') || isset($this->_options['Mime'])) { $soap_msg =& $this->_makeMimeMessage($soap_msg, $this->_encoding); } else { // default is dime $soap_msg =& $this->_makeDIMEMessage($soap_msg, $this->_encoding); $this->_options['headers']['Content-Type'] = 'application/dime'; } if (PEAR::isError($soap_msg)) { $fault =& $this->_raiseSoapFault($soap_msg); return $fault; } }
// Instantiate client. if (is_array($soap_msg)) { $soap_data =& $soap_msg['body']; if (count($soap_msg['headers'])) { if (isset($this->_options['headers'])) { $this->_options['headers'] = array_merge($this->_options['headers'], $soap_msg['headers']); } else { $this->_options['headers'] = $soap_msg['headers']; } } } else { $soap_data =& $soap_msg; }
return $soap_data; }
/** * @deprecated Use parseResponse(). */ function &__parse(&$response, $encoding, &$attachments) { return $this->parseResponse($response, $encoding, $attachments); }
/** * Parses a SOAP response. * * @see SOAP_Parser:: * * @param string $response XML content of SOAP response. * @param string $encoding Character set encoding, defaults to 'UTF-8'. * @param array $attachments List of attachments. */ function &parseResponse(&$response, $encoding, &$attachments) { // Parse the response. $response =& new SOAP_Parser($response, $encoding, $attachments); if ($response->fault) { $fault =& $this->_raiseSoapFault($response->fault); return $fault; }
// Return array of parameters. $return =& $response->getResponse(); $headers =& $response->getHeaders(); if ($headers) { $this->headersIn =& $this->_decodeResponse($headers, false); }
$decoded = &$this->_decodeResponse($return); return $decoded; }
function &_decodeResponse(&$response, $shift = true) { if (!$response) { $decoded = null; return $decoded; }
// Check for valid response. if (PEAR::isError($response)) { $fault =& $this->_raiseSoapFault($response); return $fault; } elseif (!is_a($response, 'soap_value')) { $fault =& $this->_raiseSoapFault("Didn't get SOAP_Value object back from client"); return $fault; }
// Decode to native php datatype. $returnArray =& $this->_decode($response);
// Fault? if (PEAR::isError($returnArray)) { $fault =& $this->_raiseSoapFault($returnArray); return $fault; }
if (is_object($returnArray) && strcasecmp(get_class($returnArray), 'stdClass') == 0) { $returnArray = get_object_vars($returnArray); } if (is_array($returnArray)) { if (isset($returnArray['faultcode']) || isset($returnArray['SOAP-ENV:faultcode'])) { $faultcode = $faultstring = $faultdetail = $faultactor = ''; foreach ($returnArray as $k => $v) { if (stristr($k, 'faultcode')) $faultcode = $v; if (stristr($k, 'faultstring')) $faultstring = $v; if (stristr($k, 'detail')) $faultdetail = $v; if (stristr($k, 'faultactor')) $faultactor = $v; } $fault =& $this->_raiseSoapFault($faultstring, $faultdetail, $faultactor, $faultcode); return $fault; } // Return array of return values. if ($shift && count($returnArray) == 1) { $decoded = array_shift($returnArray); return $decoded; } return $returnArray; } return $returnArray; }
/** * @deprecated Use getWire(). */ function __get_wire() { return $this->getWire(); }
/** * Returns the outgoing and incoming traffic on the transport level. * * Tracing has to be enabled. * * @see setTrace() * * @return string The complete traffic between the client and the server. */ function getWire() { if ($this->_options['trace'] && ($this->_last_request || $this->_last_response)) { return "OUTGOING:\n\n" . $this->_last_request . "\n\nINCOMING\n\n" . preg_replace("/></",">\r\n<", $this->_last_response); }
return null; }
}
|