Web Services: XML-RPC, SOAP, sobre PHP, Perl, y otros conceptos

A programar se aprende con una peque�a base te�rica, que ya la tenemos, y sobretodo jugando y practicando con las herramientas de programaci�n. De modo, que nos planteamos un proyecto relativamente sencillo para ver con m�s claridad como se crea un Servicio Web. Se trata de ofrecer la posibilidad de traducir un texto del Ingl�s al Castellano. Esto ser�a una versi�n muy "light" del Servicio Web que ofrece Babelfish de Altavista.

Hemos escogido PHP como lenguaje de programaci�n porque es una preferencia entre muchos programadores Linux, y adem�s es muy claro e intuitivo. El Servicio Web lo vamos a desarrollar paso a paso, porque requiere varias etapas:

  1. Buscamos un diccionario Ingl�s-Castellano.
  2. Implementamos un traductor de palabras individuales en PHP.
  3. Ampliamos el traductor para procesar textos completos.
  4. Ofrecemos en Internet el Servicio Web de forma elemental.
  5. Ampliamos el servicio, para que funcione bajo XML-RPC.

.�Buscamos un diccionario Ingl�s-Castellano

Si existiera para Liunx un buen programa traductor, podr�amos usarlo en nuestro desarrollo, invoc�ndolo desde l�nea de comando. Lamentablemente, por mucho que hemos buscado, parece que no hay tales programas a�n para nuestras plataforma favorita. Por lo que tendremos que conformarnos con un simple diccionario y efectuar una traducci�n completamente literal.

Diccionarios s� que podemos encontrar, aunque no muy completos. Aqui (317 Kb) tenemos el diccionario que utiliza wordtrans. Es un diccionario Ingl�s-Castellano. Lo descomprimimos como es habitual con un comando como �ste: "tar -zxvf i2edict_20010903-2.tar.gz". Obtendremos un fichero llamado i2e.dict. Examinamos la estructura del diccionario:

$more i2e.dict 
A (abbr. for ampere) : A (abrev. de amperio) 
abacterial : abacteriano 
abacus : �baco 
abaft : a popa 
abaft : en popa 
abaft : hacia popa 
abandoned : abandonado 
abandonee : abandonado 
abandonee : desamparado 
abandonment : abandono 
...

Podemos apreciar que la manipulaci�n de este diccionario va a resultar sencilla, ya que es un fichero de texto plano con una estructura muy clara.

.�Implementamos un traductor de palabras individuales en PHP

Suponiendo que tenemos unos conocimientos b�sicos de PHP, podemos r�pidamente desarrollar un script que carga en una tabla hash las palabras del diccionario Ingl�s => Castellano:

#!/usr/local/bin/php -q 

<? 
    // Se lee el fichero del diccionario 
    $filename = "i2edict-20010903/i2e.dict"; 
    $fp = @fopen($filename, "r"); if (!$fp) { die("ERROR: Imposible leer el 
fichero '$filename'\n");} 
    while (($linea = fgets($fp, 1024)) && (!feof($fp))) { 

        // Se eliminan caracteres residuales 
        $linea = eregi_replace("\([^)]*\)", "", $linea);  /* comentarios 
entre parentesis  */ 
        $linea = eregi_replace(" : ", ":", $linea);       /* separacion 
entre idiomas      */ 
        $linea = eregi_replace("[\n\r]*", "", $linea);    /* se elimina el 
cambio de linea */ 

        // Se separan los idiomas en 2 variables 
        $matches = split (":", $linea); 
        $palabraingles = $matches[0]; 
        $palabraespanol = $matches[1]; 

        // Se meten las palabras en la tabla hash 
        $ingles2espanol[$palabraingles] = $palabraespanol; 
        $espanol2ingles[$palabraespanol] = $palabraingles; 
    } 
    fclose ($fp); 

    // Se define una rutina que sepa traducir palabras sueltas del Castellano 
al Ingl�s 
    function palabra_espanol2ingles($espanol) { 
        global $espanol2ingles; 

        if ($espanol2ingles[$espanol] != '') { 
            return $espanol2ingles[$espanol]; 
        } else { 
            return $espanol; 
        } 
    } 

    // Se define una rutina que sepa traducir palabras sueltas del Ingl�s al 
Castellano 
    function palabra_ingles2espanol($ingles) { 
        global $ingles2espanol; 

        if ($ingles2espanol[$ingles] != '') { 
            return $ingles2espanol[$ingles]; 
        } else { 
            return $ingles; 
        } 
    } 

    // Se muestran dos resultados de ejemplo del traductor 
    print "�rbol => " . palabra_espanol2ingles("�rbol") . "\n"; 
    print "house => " . palabra_ingles2espanol("house") . "\n"; 
?>

El resultado de la ejecuci�n de este script, que hemos decidido llamar "dicionariohash.php", es esto:

$ ./dicionariohash.php 
�rbol => tree 
house => casa 

Bien, parece que traduce bien palabra por palabra, en un sentido, como en su opuesto. Para no complicarnos vamos a centrarnos s�lo en la traducci�n del Ingl�s al Castellano.

.�Ampliamos el traductor para procesar textos completos

El siguiente objetivo es que este script sea capaz de traducir textos completos, aunque lo haga palabra por palabra, literalmente. A modo de ejemplo, hemos escogido el siguiente texto en Ingl�s, para probar nuestro desarrollo:

"Linux is a UNIX-like operating system that was designed to provide personal computer users a free or very low-cost operating system comparable to traditional and usually more expensive UNIX systems. Linux has a reputation as a very efficient and fast-performing system. Linux's kernel (the central part of the operating system) was developed by Linus Torvalds at the University of Helsinki in Finland. To complete the operating system, Torvalds and other team members made use of system components developed by members of the Free Software Foundation for the GNU project."

Para pasar de traducir una sola palabra a traducir un texto completo, simplemente hay que considerar que un texto est� formado por palabras separadas entre s� por espacios en blanco o signos de puntuaci�n. Aplicamos esta idea sobre el c�digo, obteniendo el siguiente script:

<? 
    $texto='Linux is a UNIX-like operating system that ... etc.'; 

    // Se define una rutina que cargue en memoria un diccionario 
    function cargadiccionario($filename) { 

        global $ingles2espanol, $espanol2ingles; 

        // Se lee el fichero del diccionario 
        $fp = @fopen($filename, "r"); if (!$fp) { die("ERROR: Imposible leer 
el fichero '$filename'\n");} 
        while (($linea = fgets($fp, 1024)) && (!feof($fp))) { 

            // Se eliminan caracteres residuales 
            $linea = eregi_replace("\([^)]*\)", "", $linea);  /* comentarios 
entre parentesis  */ 
            $linea = eregi_replace(" : ", ":", $linea);       /* separacion 
entre idiomas      */ 
            $linea = eregi_replace("[\n\r]*", "", $linea);    /* se elimina el 
cambio de linea */ 

            // Se separan los idiomas en 2 variables 
            $matches = split (":", $linea); 
            $palabraingles = $matches[0]; 
            $palabraespanol = $matches[1]; 

            // Se meten las palabras en la tabla hash 
            $ingles2espanol[$palabraingles] = $palabraespanol; 
            $espanol2ingles[$palabraespanol] = $palabraingles; 
        } 
        fclose ($fp); 
    } 

    // Se carga el diccionario que trae la aplicaci�n libre i2edict 
    cargadiccionario("i2edict-20010903/i2e.dict"); 

    // Se define una rutina que sepa traducir palabras sueltas del ingl�s al 
espa�ol 
    function palabra_ingles2espanol($ingles) { 
        global $ingles2espanol; 

        if ($ingles2espanol[$ingles] != '') { 
            return $ingles2espanol[$ingles]; 
        } else { 
            return $ingles; 
        } 
    } 


    // Se lee un fichero con texto en ingles 
    $lineas = split ("\n", $texto); 
    while (list($key, $val) = each($lineas)) { 
        $linea = $val; 

        // Se reemplazan los signos de puntuaci�n, para preservarlos 
        $linea = eregi_replace("\.", " --PUNTO-- ", $linea); 
        $linea = eregi_replace("\,", " --COMA-- ", $linea); 
        $linea = eregi_replace("\;", " --PUNTOYCOMA-- ", $linea); 
        $linea = eregi_replace("\(", " --PARABR-- ", $linea); 
        $linea = eregi_replace("\)", " --PARCIE-- ", $linea); 

        // Se desglosa la linea en palabras individuales 
        $matches = split (" ", $linea); 

        // Se efectua la traduccion de cada palabra, recorriendo el array obtenido
        $linea_traducida = ''; 
        while (list($key, $val) = each($matches)) { 
            $linea_traducida .= palabra_ingles2espanol($val) . " "; 
        } 

        // Se reemplazan a la inversa los signos de puntuaci�n 
        $linea_traducida = eregi_replace(" --PUNTO-- ", ".", $linea_traducida); 
        $linea_traducida = eregi_replace(" --COMA-- ", ",", $linea_traducida); 
        $linea_traducida = eregi_replace(" --PUNTOYCOMA-- ", ";", $linea_traducida); 
        $linea_traducida = eregi_replace(" --PARABR-- ", "(", $linea_traducida); 
        $linea_traducida = eregi_replace(" --PARCIE-- ", ")", $linea_traducida); 

        // Se muestra en pantalla la linea traducida 
        print $linea_traducida . "\n"; 
    } 
?>

Si probamos el script encontraremos como resultado una traducci�n bastante inexacta, debida a la imprecis�n del diccionario con el que trabajamos. Con un peque�o truco podemos mejorar el diccionario agregando o suplantando algunos terminos. Para ello, incorporamos al c�digo esta dos l�neas:

// Se carga un diccionario con terminos adicionales que podemos personalizar 
cargadiccionario("mio.dict"); 

y creamos un fichero llamado "mio.dict" que contiene las siguientes palabras:

is : es 
the : el 
was : fue 
designed : dise�ado 
provide : proveer 
users : usuarios 
low-cost : bajo coste 
systems : sistemas 
has : tiene 
fast-performing : alto rendimiento 
developed : desarrollado 
by : por 
University : Universidad 
Finland : Finlandia 
members : miembros 
made : hecho 
components : componentes 
project : proyecto 
to : para 
that : que

Ahora, probamos nuevamente el script, y �ste el fant�stico resultado:

"Linux es un UNIX-like operativo sistema que fue dise�ado para proveer personal ordenador usuarios un libre o muy bajo coste operativo sistema comparable para tradicional y usualmente m�s caro UNIX sistemas. Linux tiene un reputaci�n como un muy eficaz y alto rendimiento sistema. Linux's n�cleo (el central pieza de el operativo sistema) fue desarrollado por Linus Torvalds a el Universidad de Helsinki en Finlandia. To completo el operativo sistema, Torvalds y otros equipo miembros hecho uso de sistema componentes desarrollado por miembros de el Free Software Foundation por el GNU proyecto."

.�Ofrecemos en Internet el Servicio Web de forma elemental

La forma m�s simple de que este script funcione en Internet y terceros usuarios o programadores puedan aprovecharse de �l es creando un sencillo formulario HTML donde se solciite el texto a traducir, y dar como respuesta una p�gina web din�mica cuyo contenido es la traducci�n.

El formulario HTML puede ser algo tan sencillo como esto:

<form name="fromulariotraduccion"
action="http://www.midominio.com/ingles2espanol.php" method="POST"> 
    <textarea name="texto" rows="7" cols="40"></textarea> 
    <input type="submit" value="Traducir >>"> 
</form> 

Lo llamamos "index.html" y lo colocamos en una carpeta del servidor web de http://www.midominio.com/

Por �ltimo, creamos el script "ingles2espanol.php" (en la misma carpeta del servidor web) que devuelve la traducci�n, a partir de la informaci�n extraida del formulario:

<? 
    // Se capturan las variables CGI a partir de PHP 4.2.0 
    function php420global($var) { 
        global $$var; 
        if (!isset($$var)) {$$var = $_GET["$var"];} 
        if (!isset($$var)) {$$var = $_POST["$var"];} 
        if (!isset($$var)) {$$var = $_COOKIE["$var"];} 
    } 

    php420global("texto"); 

    // Se define una rutina que cargue en memoria un diccionario 
    function cargadiccionario($filename) { 

        global $ingles2espanol, $espanol2ingles; 

        // Se lee el fichero del diccionario 
        $fp = @fopen($filename, "r"); if (!$fp) { die("ERROR: Imposible leer 
el fichero '$filename'\n");} 
        while (($linea = fgets($fp, 1024)) && (!feof($fp))) { 

            // Se eliminan caracteres residuales 
            $linea = eregi_replace("\([^)]*\)", "", $linea);  /* comentarios
entre parentesis  */ 
            $linea = eregi_replace(" : ", ":", $linea);       /* separacion 
entre idiomas      */ 
            $linea = eregi_replace("[\n\r]*", "", $linea);    /* se elimina el 
cambio de linea */ 

            // Se separan los idiomas en 2 variables 
            $matches = split (":", $linea); 
            $palabraingles = $matches[0]; 
            $palabraespanol = $matches[1]; 

            // Se meten las palabras en la tabla hash 
            $ingles2espanol[$palabraingles] = $palabraespanol; 
            $espanol2ingles[$palabraespanol] = $palabraingles; 
        } 
        fclose ($fp); 
    } 

    // Se carga el diccionario que trae la aplicaci�n libre i2edict 
    cargadiccionario("i2edict-20010903/i2e.dict"); 

    // Se carga un diccionario con terminos adicionales que podemos personalizar 
    cargadiccionario("mio.dict"); 

    // Se define una rutina que sepa traducir palabras sueltas del ingl�s al espa�ol 
    function palabra_ingles2espanol($ingles) { 
        global $ingles2espanol; 

        if ($ingles2espanol[$ingles] != '') { 
            return $ingles2espanol[$ingles]; 
        } else { 
            return $ingles; 
        } 
    } 


    // Se lee un fichero con texto en ingles 
    $lineas = split ("\n", $texto); 
    while (list($key, $val) = each($lineas)) { 
        $linea = $val; 

        // Se reemplazan los signos de puntuaci�n, para preservarlos 
        $linea = eregi_replace("\.", " --PUNTO-- ", $linea); 
        $linea = eregi_replace("\,", " --COMA-- ", $linea); 
        $linea = eregi_replace("\;", " --PUNTOYCOMA-- ", $linea); 
        $linea = eregi_replace("\(", " --PARABR-- ", $linea); 
        $linea = eregi_replace("\)", " --PARCIE-- ", $linea); 

        // Se desglosa la linea en palabras individuales 
        $matches = split (" ", $linea); 

        // Se efectua la traduccion de cada palabra, recorriendo el array obtenido 
        $linea_traducida = ''; 
        while (list($key, $val) = each($matches)) { 
            $linea_traducida .= palabra_ingles2espanol($val) . " "; 
        } 

        // Se reemplazan a la inversa los signos de puntuaci�n 
        $linea_traducida = eregi_replace(" --PUNTO-- ", ".", $linea_traducida); 
        $linea_traducida = eregi_replace(" --COMA-- ", ",", $linea_traducida); 
        $linea_traducida = eregi_replace(" --PUNTOYCOMA-- ", ";", $linea_traducida); 
        $linea_traducida = eregi_replace(" --PARABR-- ", "(", $linea_traducida); 
        $linea_traducida = eregi_replace(" --PARCIE-- ", ")", $linea_traducida); 

        // Se muestra en pantalla la linea traducida 
        print $linea_traducida . "\n"; 
    } 
?> 

Ya est�. Acabamos de crear un Servicio Web, que aunque no respeta ning�n est�ndar (XML-RPC o SOAP) funciona bajo la misma filosof�a. Podemos jugar con el ejemplo aqu� descrito en la siguiente URL de Internet

.�Ampliamos el servicio, para que funcione bajo XML-RPC

Retomamos el peque�o programa traductor Ingl�s => Castellano que dejamos a punto de convertirse en un servicio XML-RPC. Si queremos crear realmente un Servicio Web est�ndar, la comunicaci�n entre �ste y el cliente (normalmente el cliente es otro programa que nosotros u otros desarrolladores est�n realizando) debe llevarse a cabo a trav�s de un protocolo como XML-RPC o SOAP.

La implementaci�n de XML-RPC como servidor, se incorpora en PHP a trav�s de una herramienta ya creada (desarrollada por Ivan Ristic, [email protected]) y disponible en Internet gratuitamente llamada "XML-RPC Class Server". La podemos encontrar en esta web y descargarla.

Descomprimimos el fichero obtenido: "unzip xcs-1.2.zip" en la carpeta del servidor web, donde hemos colocado el script de traducci�n. Se crean varios ficheros, aunque s�lo nos vamos a centrar en uno de ellos, que es muy sencillo: "class.test.php"

En "class.test.php" se define la funci�n PHP a ejecutar por el Servicio Web, respetando la estructura inicial. De modo que la incorporaci�n de nuestro traductor ser�a algo as�:

<? 
class Test { 

    // funcion de pruebas, que nos permite ver el c�digo XML intercambiado 
    function Test($secret) { 
        if ($secret != '42') { 
            trigger_error("Secret does not match.", E_USER_ERROR); 
        } 
    } 

    // funcion respuesta del objeto invocado por XML-RPC 
    function ingles2espanol($texto) { 

        // Se define una rutina que cargue en memoria un diccionario 
        function cargadiccionario($filename) { 

            global $ingles2espanol, $espanol2ingles; 

            // Se lee el fichero del diccionario 
            $fp = @fopen($filename, "r"); if (!$fp) { die("ERROR: Imposible leer 
el fichero '$filename'\n");} 
            while (($linea = fgets($fp, 1024)) && (!feof($fp))) { 

                // Se eliminan caracteres residuales 
                $linea = eregi_replace("\([^)]*\)", "", $linea);  /* comentarios
entre parentesis  */ 
                $linea = eregi_replace(" : ", ":", $linea);       /* separacion 
entre idiomas      */ 
                $linea = eregi_replace("[\n\r]*", "", $linea);    /* se elimina el 
cambio de linea */ 

                // Se separan los idiomas en 2 variables 
                $matches = split (":", $linea); 
                $palabraingles = $matches[0]; 
                $palabraespanol = $matches[1]; 

                // Se meten las palabras en la tabla hash 
                $ingles2espanol[$palabraingles] = $palabraespanol; 
                $espanol2ingles[$palabraespanol] = $palabraingles; 
            } 
            fclose ($fp); 
        } 

        // Se define una rutina que sepa traducir palabras sueltas del ingl�s al espa�ol 
        function palabra_ingles2espanol($ingles) { 
            global $ingles2espanol; 

            if ($ingles2espanol[$ingles] != '') { 
                return $ingles2espanol[$ingles]; 
            } else { 
                return $ingles; 
            } 
        } 

        // Se carga el diccionario que trae la aplicaci�n libre i2edict 
        cargadiccionario("./i2edict-20010903/i2e.dict"); 

        // Se carga un diccionario con terminos adicionales que podemos personalizar 
        cargadiccionario("./mio.dict"); 

        // Se traduce el texto en ingles 
        $lineas = split ("\n", $texto); 
        while (list($key, $val) = each($lineas)) { 
            $linea = $val; 

            // Se reemplazan los signos de puntuaci�n, para preservarlos 
            $linea = eregi_replace("\.", " --PUNTO-- ", $linea); 
            $linea = eregi_replace("\,", " --COMA-- ", $linea); 
            $linea = eregi_replace("\;", " --PUNTOYCOMA-- ", $linea); 
            $linea = eregi_replace("\(", " --PARABR-- ", $linea); 
            $linea = eregi_replace("\)", " --PARCIE-- ", $linea); 

            // Se desglosa la linea en palabras individuales 
            $matches = split (" ", $linea); 

            // Se efectua la traduccion de cada palabra, recorriendo el array obtenido 
            $linea_traducida = ''; 
            while (list($key, $val) = each($matches)) { 
                $linea_traducida .= palabra_ingles2espanol($val) . " "; 
            } 

            // Se reemplazan a la inversa los signos de puntuaci�n 
            $linea_traducida = eregi_replace(" --PUNTO-- ", ".", $linea_traducida); 
            $linea_traducida = eregi_replace(" --COMA-- ", ",", $linea_traducida); 
            $linea_traducida = eregi_replace(" --PUNTOYCOMA-- ", ";", $linea_traducida); 
            $linea_traducida = eregi_replace(" --PARABR-- ", "(", $linea_traducida); 
            $linea_traducida = eregi_replace(" --PARCIE-- ", ")", $linea_traducida); 

            $lineas_traducidas .= $linea_traducida . "\n"; 
        } 

        return $lineas_traducidas; 
    } 
} 
?> 

�Ya est�! Ahora s� que hemos creado un Servicio Web completamente standard, bajo XML-RPC. Veamos si funciona. Ver si funciona un Servicio Web es complicado, ya que para invocarlo tenemos que enviar una solicitud web en lenguaje XML, y la respuesta es una p�gina web cuyo c�digo es XML. Por eso, com veis en el c�digo PHP anterior, se ha definido la funci�n "Test", dentro del objeto que representa al Servicio Web. Esta funci�n "Test" nos permite ejecutar el Servicio Web, sin necesidad de enviar una solicitud en formato XML, sino simplemente una solicitud GET corriente, como �sta:

http://www.midominio.com/xcs.php/test/42?_method=ingles2espanol&texto=tree

Si todo va bien, el resultado es �ste:

<?xml version='1.0' encoding="iso-8859-1" ?> 
<methodResponse> 
<params> 
 <param> 
  <value> 
   <string>�rbol</string> 
  </value> 
 </param> 
</params> 
</methodResponse> 

En fase de producci�n, un Servicio Web se invoca directamente desde el c�digo de programaci�n de la aplicaci�n cliente. Para efectuar dicha invocaci�n, se utilizan rutinas de cliente XML-RPC que traducen a XML nuestra llamada a rutina y sus variables, invocan por HTTP el Servicio Web, descargan el XML resultante, y lo procesan convirti�ndolo en variables resultado. A nivel de programador, un Servicio Web hace todo esto de forma transparente, por lo que es equivalente a ejecutar una rutina.

La solicitud de un Servicio Web tiene un aspecto similar a éste
<? 
include_once("./class.remotetest.php"); 
RemoteClassRegistry::addUrl('DEFAULT_URL', 'http://www.midominio.com/xcs.php'); 
$test = new Test(42); 
print $test->ingles2espanol('tree'); 
?> 

Este sencillo c�digo invoca la creaci�n de un objeto remoto de tipo "Test", y luego ejecuta su funci�n "ingles2espanol" con el par�metro de ejemplo "tree". El resultado, como podemos imaginar es "�rbol". De igual modo, podemos probar el sistema con un texto m�s largo.

La potencia de este esquema de programaci�n (los Servicios Web) est� en colocar el PHP del cliente en un servidor distinto al PHP del Servicio Web.

Ahora nos queda claro que la rutina de traducci�n s�lo reside en un servidor de Internet, y otros pueden invocar la funci�n tantas veces como quieran desde cualquier punto de Internet.

Hay un detalle que a�n no hemos comentado. Para que PHP pueda trabajar con el protocolo XML-RPC, hay que compilarlo con unas determinadas funciones especiales. Vemos a continuaci�n los pasos en la instalaci�n de PHP, para que todo funcione bien.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
SIGUIENTE ARTÍCULO