soapvar soapclient ejemplo consumir php web-services soap error-handling

consumir - soapclient php ejemplo



Manejo de tiempos de espera de Soap en PHP (8)

Estoy trabajando en un proyecto en el que estoy verificando información de un usuario con un servicio web SOAP. Actualmente me estoy ocupando de los errores, suponiendo que recibo respuestas del servicio web, pero también tengo que manejar los casos extremos de un tiempo de espera o indisponibilidad del servicio.

En el caso de un tiempo de espera o falta de disponibilidad del servicio, debo pretender que la solicitud fue exitosa (que el servicio web aprobó la información), pero no tengo claro qué excepciones se lanzan.

Algunos pseudo-códigos:

// $client is PHP''s SoapClient class try { $response = $client->SomeSoapRequest(); } catch(SoapFault $e){ // handle issues returned by the web service } catch(Exception $e){ // handle PHP issues with the request }

Lo que no puedo encontrar es:

  1. ¿Los tiempos de espera son una SoapFault ? De ser así, ¿cuál es la mejor manera de distinguir entre un error de tiempo de espera y problemas del servicio web (como un error de tipo, etc.)? Encontré una página que mencionaba un error en el que el mensaje decía algo así como "Error al cargar encabezados", pero no mencionaba si se trataba de un error de Soap.
  2. ¿Cómo va a ocurrir la falta de disponibilidad de un servicio? Una excepción de PHP parece que tendría sentido (un SoapFault se devolverá desde el servicio web donde la falta de disponibilidad sería un problema de socket o similar)?
  3. ¿Existe un servicio existente (por ejemplo, un ejemplo) contra el que puedo poner a prueba un tiempo de espera? La mayoría de las discusiones relacionadas con el tiempo de espera parecen estar relacionadas con la prevención de tiempos de espera extendiendo la configuración de tiempo de espera predeterminado, que no es ideal en esta situación.

1) En caso de tiempo de espera, PHP lanza una excepción SoapFault con faultcode="HTTP" y faultstring="Error Fetching http headers" .

2) En mi opinión, la mejor manera de distinguir entre un error de tiempo de espera y problemas de servicio web es mirando los miembros de faultcode y faultstring de la clase SoapFault .
En particular, el elemento de faultcode está destinado a ser utilizado por el software para proporcionar un mecanismo algorítmico para identificar la falla.
Como también puede leer en un comentario del manual de PHP , no hay un método para leer la propiedad del faultcode , por lo que debe acceder directamente (por ejemplo, $e->faultcode ), porque el método getCode() no funciona.
La especificación SOAP 1.1 define cuatro posibles valores para el campo de faultcode :

  • VersionMismatch : la parte procesadora encontró un espacio de nombres no válido para el elemento SOAP Envelope
  • MustUnderstand : Un elemento hijo inmediato del elemento SOAP Header que no fue entendido o no obedecido por la parte procesadora contenía un atributo SOAP mustUnderstand con un valor de "1"
  • Cliente : la clase de error del Cliente indica que el mensaje se formó incorrectamente o no contenía la información adecuada para tener éxito. Por ejemplo, el mensaje podría carecer de la autenticación adecuada o la información de pago. En general, es una indicación de que el mensaje no debe reenviarse sin cambios.
  • Servidor : la clase de errores del servidor indica que el mensaje no pudo procesarse por razones que no son directamente atribuibles al contenido del mensaje en sí, sino al procesamiento del mensaje. Por ejemplo, el procesamiento podría incluir la comunicación con un procesador ascendente, que no respondió. El mensaje puede tener éxito en un momento posterior.

Además de esos códigos, PHP usa el código HTTP para identificar los errores que suceden a nivel de protocolo (p. Ej .: errores de socket); por ejemplo, si busca add_soap_fault en el código fuente ext/soap/php_http.c , puede ver cuándo se generan algunos de estos tipos de fallas.
Al buscar las funciones add_soap_fault y soap_server_fault en los archivos fuente de extensión SOAP de PHP, he creado la siguiente lista de excepciones PHP SoapFault :

HTTP ---- Unable to parse URL Unknown protocol. Only http and https are allowed. SSL support is not available in this build Could not connect to host Failed Sending HTTP SOAP request Failed to create stream?? Error Fetching http headers Error Fetching http body: No Content-Length: connection closed or chunked data Redirection limit reached: aborting Didn''t recieve an xml document Unknown Content-Encoding Can''t uncompress compressed response Error build soap request VersionMismatch --------------- Wrong Version Client ------ A SOAP 1.2 envelope can contain only Header and Body A SOAP Body element cannot have non Namespace qualified attributes A SOAP Envelope element cannot have non Namespace qualified attributes A SOAP Header element cannot have non Namespace qualified attributes Bad Request Body must be present in a SOAP envelope Can''t find response data DTD are not supported by SOAP encodingStyle cannot be specified on the Body encodingStyle cannot be specified on the Envelope encodingStyle cannot be specified on the Header Error cannot find parameter Error could not find "location" property Error finding "uri" property looks like we got "Body" with several functions call looks like we got "Body" without function call looks like we got no XML document looks like we got XML without "Envelope" element Missing parameter mustUnderstand value is not boolean SoapClient::__doRequest() failed SoapClient::__doRequest() returned non string value Unknown Data Encoding Style Unknown Error DataEncodingUnknown MustUnderstand -------------- Header not understood Server ------ Couldn''t find WSDL DTD are not supported by SOAP Unknown SOAP version WSDL generation is not supported yet

3) Para simular la condición de tiempo de espera, intente con el siguiente código:

soapclient.php

<?php ini_set(''default_socket_timeout'', 10); $client = new SoapClient(null, array( ''location'' => "http://localhost/soapserver.php", ''uri'' => "http://localhost/soapserver.php", ''trace'' => 1 ) ); try { echo $return = $client->__soapCall("add",array(41, 51)); } catch (SoapFault $e) { echo "<pre>SoapFault: ".print_r($e, true)."</pre>/n"; //echo "<pre>faultcode: ''".$e->faultcode."''</pre>"; //echo "<pre>faultstring: ''".$e->getMessage()."''</pre>"; } ?>

soapserver.php

<?php function add($a, $b) { return $a + $b; } sleep(20); $soap = new SoapServer(null, array(''uri'' => ''http://localhost/soapserver.php'')); $soap->addFunction("add"); $soap->handle(); ?>

Observe la llamada de SoapServer.php en el script SoapServer.php con un tiempo (20) más largo que el tiempo (10) especificado para el parámetro default_socket_timeout en el script SoapClient.php .
Si desea simular una indisponibilidad del servicio, podría, por ejemplo, cambiar el protocolo de location de http a https en el script soapclient.php , suponiendo que su servidor web no está configurado para SSL; Al hacer esto, PHP debería arrojar un SoapFault "Could not connect to host".


Desde mi experiencia, si $e->getMessage es "Error al obtener encabezados http", se trata de un tiempo de espera de red.

Si $e->getMessage es algo así como "No se puede conectar con el host", el servicio que está tratando de alcanzar está inactivo.

Luego aparece "Parece que no tenemos ningún documento XML", que es más críptico y puede significar cosas diferentes.


Para lidiar con los tiempos de espera en el servicio

$client = new SoapClient($wsdl, array("connection_timeout"=>10)); // SET SOCKET TIMEOUT if(defined(''RESPONSE_TIMEOUT'') && RESPONSE_TIMEOUT != '''') { ini_set(''default_socket_timeout'', RESPONSE_TIMEOUT); }


Parece que default_socket_timeout no se tiene en cuenta cuando se realizan llamadas SOAP a través de HTTPS:

Error abierto al momento de escribir. Como señala un comentario en la publicación del blog Robert Ludwick en una respuesta eliminada Timing Out PHP Soap Calls (21 de octubre de 2009, publicado por Robert F. Ludwick) , la solución discute la publicación (anulando SoapClient::__doRequest() con un curl request) también funciona alrededor de este error.

Otro error relacionado es:

El código mencionado en la publicación del blog ha sufrido algunos cambios y se puede encontrar en su último formulario con soporte de autenticación HTTP aquí en Github:

En cualquier caso, la solución alternativa no debería ser necesaria ya que este problema se ha solucionado en la extensión PHP SOAPClient.


Simplemente establecer default_socket_timeout globalmente a través de ini puede no hacer lo que desee. Esto afectaría las solicitudes SOAP, pero también afectaría a otras conexiones salientes, incluidas las conexiones DB. En cambio, anule el método __doRequest () de SoapClient para hacer la conexión HTTP usted mismo. Luego puede establecer su propio tiempo de espera en el socket, detectarlo y lanzar excepciones que pueda atrapar y manejar.

class SoapClientWithTimeout extends SoapClient { public function __construct ($wsdl, $options = null) { if (!$options) $options = []; $this->_connectionTimeout = @$options[''connection_timeout''] ?: ini_get (''default_socket_timeout''); $this->_socketTimeout = @$options[''socket_timeout''] ?: ini_get (''default_socket_timeout''); unset ($options[''socket_timeout'']); parent::__construct($wsdl, $options); } /** * Override parent __doRequest to add a timeout. */ public function __doRequest ( $request, $location, $action, $version, $one_way = 0 ) { // Extract host, port, and scheme. $url_parts = parse_url ($location); $host = $url_parts[''host'']; $port = @$url_parts[''port''] ?: ($url_parts[''scheme''] == ''https'' ? 443 : 80); $length = strlen ($request); // Form the HTTP SOAP request. $http_req = "POST $location HTTP/1.0/r/n"; $http_req .= "Host: $host/r/n"; $http_req .= "SoapAction: $action/r/n"; $http_req .= "Content-Type: text/xml; charset=utf-8/r/n"; $http_req .= "Content-Length: $length/r/n"; $http_req .= "/r/n"; $http_req .= $request; // Need to tell fsockopen to use SSL when requested. if ($url_parts[''scheme''] == ''https'') $host = ''ssl://''.$host; // Open the connection. $socket = @fsockopen ( $host, $port, $errno, $errstr, $this->_connectionTimeout ); if (!$socket) throw new SoapFault ( ''Client'', "Failed to connect to SOAP server ($location): $errstr" ); // Send the request. stream_set_timeout ($socket, $this->_socketTimeout); fwrite ($socket, $http_req); // Read the response. $http_response = stream_get_contents ($socket); // Close the socket and throw an exception if we timed out. $info = stream_get_meta_data ($socket); fclose ($socket); if ($info[''timed_out'']) throw new SoapFault ( ''Client'', "HTTP timeout contacting $location" ); // Extract the XML from the HTTP response and return it. $response = preg_replace ( ''/ /A # Start of string .*? # Match any number of characters (as few as possible) ^ # Start of line /r # Carriage Return $ # End of line /smx'', '''', $http_response ); return $response; } }


Supongo que llego un poco tarde, pero en caso de que alguien todavía esté buscando una solución a los tiempos de espera en el cliente de php soap, esto es lo que funcionó para mí:

Reemplazando Basic PHP SoapClient con cURL con set timeout. Solo tenga en cuenta que, a veces, WS espera que se especifique una acción en el encabezado HTTP. La solución original publicada en ese sitio web no incluye eso (ver comentarios).


Usé dos factores para hacer que mi extensión de SoapClient lanzara una buena excepción. El mensaje y el tiempo que tomó la solicitud para regresar. Creo que el mensaje de error "Error al recuperar encabezados http" también puede aparecer en algunos otros casos, por lo tanto, la verificación de tiempo.

El siguiente código debería ser correcto

class SoapClientWithTimeout extends SoapClient { public function __soapCall ($params, ---) { $time_start = microtime(true); try { $result = parent::__soapCall ($params, ---); } catch (Exception $e) { $time_request = (microtime(true)-$time_start); if( $e->getMessage() == ''Error Fetching http headers'' && ini_get(''default_socket_timeout'') < $time_request ) { throw new SoapTimeoutException( ''Soap request most likly timed out.''. '' It took ''.$time_request. '' and the limit is ''.ini_get(''default_socket_timeout'') ); } // E: Not a timeout, let''s rethrow the original exception throw $e; } // All good, no exception from the service or PHP return $result; } } class SoapTimeoutException extends Exception {}

Luego uso SoapClientWithTimeout

$client = new SoapClientWithTimeout(); try { $response = $client->SomeSoapRequest(); var_dump($response); } catch(SoapTimeoutException $e){ echo ''We experienced a timeout! ''. $e->getMessage(); } catch(Exception $e) { echo ''Exception: ''.$e->getMessage(); }

Para depurar el tiempo de espera de su servicio. Agregue la siguiente línea antes de llamar al servicio

ini_set(''default_socket_timeout'', 1);


solo use el "stream_context" para establecer la configuración de tiempo de espera también para la carga de WSDL (debe configurar las opciones $ de SoapClient [''connection_timeout''] antes):

class SoapClient2 extends SoapClient { public function __construct($wsdl, $options=null) { if(isset($options[''connection_timeout''])) { $s_options = array( ''http'' => array( ''timeout'' => $options[''connection_timeout''] ) ); $options[''stream_context''] = stream_context_create($s_options); } parent::__construct($wsdl, $options); } }