/**
  * 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:
  * - 'namespace'
  * - 'soapaction'
  * - '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
  *
  * @access public
  *
  * @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
  *                                 SOAP_Value instances.
  * @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->_raiseSoapFault($this->_soap_transport);
             $this->_soap_transport = null;
             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;
 }
 /**
  * SOAP_Client::call
  *
  * @param string method
  * @param array  params
  * @param string namespace  (not required if using wsdl)
  * @param string soapAction   (not required if using wsdl)
  *
  * @return array of results
  * @access public
  */
 function call($method, $params = array(), $namespace = false, $soapAction = false)
 {
     $this->fault = null;
     if ($this->endpointType == 'wsdl') {
         $this->setSchemaVersion($this->wsdl->xsd);
         // get portName
         if (!$this->portName) {
             $this->portName = $this->wsdl->getPortName($method);
             if (PEAR::isError($this->portName)) {
                 return $this->raiseSoapFault($this->portName);
             }
         }
         $namespace = $this->wsdl->getNamespace($this->portName, $method);
         // get endpoint
         $this->endpoint = $this->wsdl->getEndpoint($this->portName);
         if (PEAR::isError($this->endpoint)) {
             return $this->raiseSoapFault($this->endpoint);
         }
         $this->debug("endpoint: {$this->endpoint}");
         $this->debug("portName: {$this->portName}");
         // get operation data
         $opData = $this->wsdl->getOperationData($this->portName, $method);
         if (PEAR::isError($opData)) {
             return $this->raiseSoapFault($opData);
         }
         $soapAction = $opData['soapAction'];
         // set input params
         $nparams = array();
         if (count($opData['input']['parts']) > 0) {
             $i = 0;
             reset($params);
             foreach ($opData['input']['parts'] as $name => $type) {
                 if (isset($params[$name])) {
                     $nparams[$name] = $params[$name];
                 } else {
                     // XXX assuming it's an array, not a hash
                     // XXX this is pretty pethetic, but "fixes" a problem where
                     // paremeter names do not match correctly
                     $nparams[$name] = current($params);
                     next($params);
                 }
                 if (gettype($nparams[$name]) != 'object' && get_class($nparams[$name]) != 'soap_value') {
                     // type is a qname likely, split it apart, and get the type namespace from wsdl
                     $qname = new QName($type);
                     if ($qname->ns) {
                         $type_namespace = $this->wsdl->namespaces[$qname->ns];
                     } else {
                         $type_namespace = NULL;
                     }
                     $type = $qname->name;
                     $nparams[$name] = new SOAP_Value($name, $type, $nparams[$name], $namespace, $type_namespace, $this->wsdl);
                 }
             }
         }
         $params = $nparams;
     }
     $this->debug("soapAction: {$soapAction}");
     $this->debug("namespace: {$namespace}");
     // make message
     $soapmsg = new SOAP_Message($method, $params, $namespace, NULL, $this->wsdl);
     if ($soapmsg->fault) {
         return $this->raiseSoapFault($soapmsg->fault);
     }
     // serialize the message
     $soap_data = $soapmsg->serialize();
     $this->debug("soap_data " . $soap_data);
     if (PEAR::isError($soap_data)) {
         return $this->raiseSoapFault($soap_data);
     }
     $this->debug('<xmp>' . $soap_data . '</xmp>');
     // instantiate client
     $dbg = "calling server at '{$this->endpoint}'...";
     $soap_transport = new SOAP_Transport($this->endpoint, $this->debug_flag);
     if ($soap_transport->fault) {
         return $this->raiseSoapFault($soap_transport->fault);
     }
     $this->debug($dbg . 'instantiated client successfully');
     $this->debug("endpoint: {$this->endpoint}<br>\n");
     // send
     $dbg = "sending msg w/ soapaction '{$soapAction}'...";
     // send the message
     $this->response = $soap_transport->send($soap_data, $soapAction);
     if ($soap_transport->fault) {
         return $this->raiseSoapFault($this->response);
     }
     // parse the response
     $return = $soapmsg->parseResponse($this->response);
     $this->debug($soap_transport->debug_data);
     $this->debug($dbg . 'sent message successfully and got a(n) ' . gettype($return) . ' back');
     // check for valid response
     if (PEAR::isError($return)) {
         return $this->raiseSoapFault($return);
     } else {
         if (strcasecmp(get_class($return), 'SOAP_Value') != 0) {
             return $this->raiseSoapFault("didn't get SOAP_Value object back from client");
         }
     }
     // decode to native php datatype
     $returnArray = $return->decode();
     // fault?
     if (PEAR::isError($returnArray)) {
         return $this->raiseSoapFault($returnArray);
     }
     if (is_array($returnArray)) {
         if (isset($returnArray['faultcode']) || isset($returnArray['SOAP-ENV:faultcode'])) {
             foreach ($returnArray as $k => $v) {
                 if (stristr($k, 'faultcode')) {
                     $this->faultcode = $v;
                 }
                 if (stristr($k, 'faultstring')) {
                     $this->faultstring = $v;
                 }
                 if (stristr($k, 'faultdetail')) {
                     $this->faultdetail = $v;
                 }
                 if (stristr($k, 'faultactor')) {
                     $this->faultactor = $v;
                 }
             }
             return $this->raiseSoapFault($this->faultstring, $this->faultdetail, $this->faultactor, $this->faultcode);
         }
         // return array of return values
         if (count($returnArray) == 1) {
             return array_shift($returnArray);
         }
         return $returnArray;
     }
     return $returnArray;
 }