Source for file Client.php
Documentation is available at Client.php
* This file contains the code for the SOAP client.
* 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.
* @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
function __call($method, $args)
$this->_call ($method, $args, $return);
eval ('function clone($t) { return $t; }');
class SOAP_Client_Overload extends SOAP_Base {
function __call($method, $args, &$return)
return $this->_call($method, $args, $return);
* This class is the main interface for making soap requests.
* $soapclient = new SOAP_Client( string path [ , boolean wsdl] );
* echo $soapclient->call( string methodname [ , array parameters] );
* or, if using PHP 5+ or the overload extension:<code>
* $soapclient = new SOAP_Client( string path [ , boolean wsdl] );
* echo $soapclient->methodname( [ array parameters] );
* Originally based on SOAPx4 by Dietrich Ayala
* http://dietrich.ganx4.com/soapx4
* @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
* Communication endpoint.
* Currently the following transport formats are supported:
* http://www.example.com/soap/server.php
* https://www.example.com/soap/server.php
* mailto:soap@example.com
* The SOAP PORT name that is used by the client.
* Endpoint type e.g. 'wdsl'.
* The outgoing and incoming data stream for debugging.
* The outgoing data stream for debugging.
var $_last_request = null;
* The incoming data stream for debugging.
var $_last_response = null;
var $_options = array ('trace' => false );
* The character encoding used for XML parser, etc.
var $_encoding = SOAP_DEFAULT_ENCODING;
* The array of SOAP_Headers that we are sending.
* The headers we recieved back in the response.
* Options for the HTTP_Request class (see HTTP/Request.php).
var $_proxy_params = array ();
* The SOAP_Transport instance.
var $_soap_transport = null;
* @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
* @param boolean|string$cache Use WSDL caching? The cache directory if
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
$this->_endpointType = 'wsdl';
// instantiate wsdl class
$this->_wsdl = new SOAP_WSDL($this->_endpoint,
if ($this->_wsdl->fault ) {
$this->_raiseSoapFault ($this->_wsdl->fault );
$this->_last_request = null;
$this->_last_response = null;
* Sets the character encoding.
* Limited to 'UTF-8', 'US_ASCII' and 'ISO-8859-1'.
* @return mixed SOAP_Fault on error.
if (in_array($encoding, $this->_encodings)) {
$this->_encoding = $encoding;
return $this->_raiseSoapFault ('Invalid Encoding');
* Adds a header to the envelope.
* @param SOAP_Header $soap_value A SOAP_Header or an array with the
* elements 'name', 'namespace',
* 'mustunderstand', and 'actor' to send
// Add a new header to the message.
if (is_a($soap_value, 'SOAP_Header')) {
// name, value, namespace, mustunderstand, actor
$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:
* - 'timeout': HTTP socket timeout
* - 'transfer-encoding': SMTP transport, Content-Transfer-Encoding: header
* - 'from': SMTP transport, From: header
* - 'subject': SMTP transport, Subject: header
* - 'headers': SMTP transport, hash of extra SMTP headers
* - 'attachments': what encoding to use for attachments (Mime, Dime)
* - 'trace': whether to trace the SOAP communication
* - 'style': 'document' or 'rpc'; when set to 'document' the parameters
* are not wrapped inside a tag with the SOAP action name
* - 'use': 'literal' for literal encoding, anything else for section 5
* encoding; when set to 'literal' SOAP types will be omitted.
* - 'keep_arrays_flat': use the tag name multiple times for each element
* when passing in an array in literal mode
* - 'no_type_prefix': supress adding of the namespace prefix
* @param string $method The method to call.
* @param array $params The method parameters.
* @param string|array$namespace Namespace or hash with options. Note:
* most options need to be repeated for
* @param string $soapAction
* @return mixed The method result or a SOAP_Fault on error.
function call($method, $params, $namespace = false , $soapAction = false )
$this->_last_request = null;
$this->_last_response = null;
$soap_data = $this->_generate ($method, $params, $namespace, $soapAction);
if (PEAR ::isError ($soap_data)) {
$fault = $this->_raiseSoapFault ($soap_data);
// _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) {
if (PEAR ::isError ($this->_soap_transport)) {
$fault = $this->_raiseSoapFault ($this->_soap_transport);
$this->_soap_transport = null;
$this->_soap_transport->encoding = $this->_encoding;
$this->xml = $this->_soap_transport->send($soap_data, $transport_options);
// Save the wire information for debugging.
if ($this->_options['trace']) {
if ($this->_soap_transport->fault) {
$fault = $this->_raiseSoapFault ($this->xml);
if (isset ($this->_options['result']) &&
$this->_options['result'] != 'parse') {
* Sets an option to use with the transport layers.
* $soapclient->setOpt('curl', CURLOPT_VERBOSE, 1)
* to pass a specific option to curl if using an SSL connection.
* @param string $category Category to which the option applies or option
* @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
function setOpt($category, $option, $value = null )
if (!isset ($this->_options[$category])) {
$this->_options[$category] = array ();
$this->_options[$category][$option] = $value;
$this->_options[$category] = $option;
* Call method supporting the overload extension.
* If the overload extension is loaded, you can call the client class with
* $soap = new SOAP_Client(....);
* $value = $soap->getStockQuote('MSFT');
* @param string $method The method to call.
* @param array $params The method parameters.
* @param mixed $return_value Will get the method's return value
* @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
$this->_wsdl->matchMethod ($method);
$return_value = $this->call($method, $params);
* Returns the XML content of the last SOAP request.
* @return string The last request.
return $this->_last_request;
* Returns the XML content of the last SOAP response.
* @return string The last response.
return $this->_last_response;
* Sets the SOAP encoding.
* The default encoding is section 5 encoded.
* @param string $use Either 'literal' or 'encoded' (section 5).
$this->_options['use'] = $use;
* Sets the SOAP encoding style.
* The default style is rpc.
* @param string $style Either 'document' or 'rpc'.
$this->_options['style'] = $style;
* Sets whether to trace the traffic on the transport level.
$this->_options['trace'] = $trace;
* Generates the complete XML SOAP message for an RPC call.
* @param string $method The method to call.
* @param array $params The method parameters.
* @param string|array$namespace Namespace or hash with options. Note:
* most options need to be repeated for
* @param string $soapAction
* @return string The SOAP message including envelope.
function _generate ($method, $params, $namespace = false ,
$this->_options['input'] = 'parse';
$this->_options['result'] = 'parse';
$this->_options['parameters'] = false;
$params = array ($params);
// Options passed as a hash.
foreach ($namespace as $optname => $opt) {
// We'll place $soapAction into our array for usage in the
$this->_options['soapaction'] = $soapAction;
$this->_options['namespace'] = $namespace;
if (isset ($this->_options['namespace'])) {
$namespace = $this->_options['namespace'];
if ($this->_endpointType == 'wsdl') {
$this->_setSchemaVersion ($this->_wsdl->xsd );
$this->_portName = $this->_wsdl->getPortName ($method);
if (PEAR ::isError ($this->_portName)) {
return $this->_raiseSoapFault ($this->_portName);
$this->_endpoint = $this->_wsdl->getEndpoint ($this->_portName);
if (PEAR ::isError ($this->_endpoint)) {
return $this->_raiseSoapFault ($this->_endpoint);
$opData = $this->_wsdl->getOperationData ($this->_portName, $method);
if (PEAR ::isError ($opData)) {
return $this->_raiseSoapFault ($opData);
$namespace = $opData['namespace'];
$this->_options['style'] = $opData['style'];
$this->_options['use'] = $opData['input']['use'];
$this->_options['soapaction'] = $opData['soapAction'];
if ($this->_options['input'] == 'parse') {
$this->_options['parameters'] = $opData['parameters'];
if (isset ($opData['input']['parts']) &&
count($opData['input']['parts'])) {
foreach ($opData['input']['parts'] as $name => $part) {
// Is the name a complex type?
if (isset ($part['element'])) {
$xmlns = $this->_wsdl->namespaces [$part['namespace']];
$part = $this->_wsdl->elements [$part['namespace']][$part['type']];
if (isset ($params[$name]) ||
$this->_wsdl->getDataHandler ($name, $part['namespace'])) {
$nparams[$name] = & $params[$name];
// We now force an associative array for
// parameters if using WSDL.
return $this->_raiseSoapFault (" The named parameter $name is not in the call parameters." );
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']);
$type_namespace = $this->_wsdl->namespaces [$qname->ns ];
} elseif (isset ($part['namespace'])) {
$type_namespace = $this->_wsdl->namespaces [$part['namespace']];
$qname->namespace = $type_namespace;
$pqname = '{' . $xmlns . '}' . $name;
// WSDL fixups to the SOAP value.
// 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';
$mqname = new QName($method, $namespace);
$methodValue = new SOAP_Value($mqname->fqn (), 'Struct', $params,
array (), $this->_options);
$mqname = new QName($method, $namespace);
$params = new SOAP_Value($mqname->fqn (), 'Struct', null );
} elseif ($this->_options['input'] == 'parse') {
if (gettype($params[$k]) != 'object') {
$nparams[] = & $params[$k];
if ($this->_options['parameters']) {
$mqname = new QName($method, $namespace);
if (PEAR ::isError ($soap_msg)) {
return $this->_raiseSoapFault ($soap_msg);
// 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);
$soap_msg = $this->_makeDIMEMessage ($soap_msg, $this->_encoding);
$this->_options['headers']['Content-Type'] = 'application/dime';
if (PEAR ::isError ($soap_msg)) {
return $this->_raiseSoapFault ($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']);
$this->_options['headers'] = $soap_msg['headers'];
* Parses a SOAP response.
* @param string $response XML content of SOAP response.
* @param string $encoding Character set encoding, defaults to 'UTF-8'.
* @param array $attachments List of attachments.
$response = new SOAP_Parser($response, $encoding, $attachments);
$fault = $this->_raiseSoapFault ($response->fault );
// Return array of parameters.
$return = $response->getResponse ();
$headers = $response->getHeaders ();
$this->headersIn = $this->_decodeResponse ($headers, false );
$decoded = $this->_decodeResponse ($return);
* Converts a complex SOAP_Value into a PHP Array
* @param SOAP_Value $response Value object.
function _decodeResponse ($response, $shift = true )
// Check for valid response.
if (PEAR ::isError ($response)) {
$fault = $this->_raiseSoapFault ($response);
} elseif (!is_a($response, 'soap_value')) {
$fault = $this->_raiseSoapFault ("Didn't get SOAP_Value object back from client");
// Decode to native php datatype.
$returnArray = $this->_decode ($response);
if (PEAR ::isError ($returnArray)) {
$fault = $this->_raiseSoapFault ($returnArray);
if (isset ($returnArray['faultcode']) ||
isset ($returnArray[SOAP_BASE ::SOAPENVPrefix (). ':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 array of return values.
if ($shift && count($returnArray) == 1 ) {
* Returns the outgoing and incoming traffic on the transport level.
* Tracing has to be enabled.
* @return string The complete traffic between the client and the server.
if ($this->_options['trace'] &&
($this->_last_request || $this->_last_response)) {
Documentation generated on Mon, 04 Aug 2008 20:00:14 -0400 by phpDocumentor 1.4.0. PEAR Logo Copyright © PHP Group 2004.
|