viernes, 14 de diciembre de 2012

Accediendo a web services desde PHP vía socket

Según la Wikipedia, un web service (o servicio web) es una tecnología que utiliza un conjunto de protocolos y estándares que sirven para intercambiar datos entre aplicaciones que pueden estar desarrolladas en lenguajes de programación diferentes, y ejecutadas sobre plataforma distintas.

El lenguaje PHP tiene diversas formas de acceder a un web service, siendo la más usada SOAP (Simple Object Access Protocol). Para tener una visión completa recomiendo el repaso de la sección Web Services del manual de PHP. Pero hay otra forma de acceder a un servicio web, una de muy bajo nivel. Al fin y al cabo un web service no es más que un protocolo basado en direcciones web (URLs), por lo tanto si conocemos los parámetros del servicio podemos acceder al servicio web usando un simple socket y la siguiente secuencia de comandos:

  • Abrir el socket con el servidor (host).
  • Enviar una petición HTTP a través del socket.
  • Leer la respuesta del servidor en un buffer.
  • Extraer el mensaje del buffer.

Algunos de los puristas de los estándares dirán que esa no es la forma adecuada de acceder a un web service, pero eso no quiere decir que no se pueda hacer. En cualquier caso voy a poner un ejemplo de como hacerlo.

function readWebService($url) {

  // Se elimina de la URL el nombre del protocolo
  $url = str_replace("http://", "", $url);

  // Se extrae el host de la URL
  $pos = strpos($url, "/");
  if($pos === false) {
    $host = $url;
    $page = "/";
  }
  else {
    $page = substr($url, $pos);
    $host = substr($url, 0, $pos);
  }

  // Se obtiene la IP del host
  $ip = gethostbyname($host);

  // Se abre un socket de comunicación con el host
  $socket = @fsockopen($ip, 80, $errno, $errstr, 60);
  if($socket === false) {
    return false;
  }

  // Preparación de la petición HTTP a enviar al host
  $send = "GET " . $page . " HTTP/1.0\r\n";
  $send .= "Host: " . $host . "\r\n";
  $send .= "Accept-Language: es-ES\r\n";
  $send .= "Content-Type: application/json; charset=UTF-8\r\n";
  $send .= "User-Agent: " . $_SERVER["HTTP_USER_AGENT"] . "\r\n";
  $send .= "Connection: Close\r\n\r\n";

  // Se envía la petición HTTP al host
  fputs($socket, $send);

  // Se lee la respuesta del host
  $buffer = "";
  while(!feof($socket)) {
    $buffer .= fgets($socket, 4096);
  }

  // Se cierra el socket
  fclose($socket);

  // Separación de la cabecera HTTP y el contenido.
  $response = @explode("\r\n\r\n", $buffer, 2);

  // Se devuelve el resultado
  return $response;
}

Si la función falla, bien sea porque no encuentra el host o porque expira el tiempo máximo de 60 segundos que hemos puesto, devuelve FALSE. Si la función se ejecuta correctamente devuelve un array donde la primera posición contendrá la respuesta HTTP del servidor y la segunda el contenido de la respuesta. La cabecera HTTP a enviar variará dependiendo de las necesidades del momento. Es recomendable entender bien el protocolo HTTP para hacer uso de esta función. Además la respuesta HTTP del servidor también debería ser interpretada para ver lo que ha ocurrido.

Para probarlo podemos usar un servicio web para desarrolladores de Yahoo que permite obtener el timestamp (hora actual en formato UNIX).

$url = "http://developer.yahooapis.com/TimeService/V1/getTime/";
$url .= "?appid=YahooDemo";
$url .= "&output=json";
$response = readWebService($url);
if(!$response)
  echo "ERROR";
else {
  $header = $response[0];
  $content = $response[1];
  echo $content;
}
El resultado será:
{"Result":{"Timestamp":1355482897}} 

Esta técnica la podemos usar tanto para obtener datos de un web service, ya sea en formato JSON, XML u otro, como para cargar una página HTML y explorar su contenido.

No hay comentarios:

Publicar un comentario