Banner

Ultima revisión 25/06/2013

Zend Framework: Web Service SOAP con atributos en la petición o request

Como prometí en el anterior artículo, hoy mostraremos cómo leer atributos cuando nos los envían a nosotros y tenemos que procesarlos o interpretarlos para dar una respuesta.

Bueno lo primero que debemos hacer es crear un proyecto nuevo en NetBeans. Para ello, agregamos un nuevo proyecto pulsando en el botón de New Project, seleccionamos en PHP Application y pulsamos en Next.

En la pantalla que nos sale escribimos un Nombre de Proyecto en el campo Project Name y el directorio dónde queremos que se cree el proyecto en el campo Source Folder y le damos a Next hasta que nos pida qué Framework escoger.

Entonces pinchamos en Zend y después el en enlace que aparece que pone options...

Ya podemos decir que todo está OK por lo que pulsamos en Finish para crear el proyecto nuevo.

Ahora nos vamos al navegador y escribimos la ruta del proyecto para comprobar que todo está correcto. En nuestro ejemplo es http://localhost/ZendProject1/public

Si obtenemos un error Server 500 lo que debemos hacer es corregir el archivo .htaccess

Si el error que nos sale es como:

Warning: require_once(Zend/Application.php) [function.require-once]: failed to open stream: No such file or directory in D:\httpdocs\zfp1\public\index.php on line 18
Fatal error: require_once() [function.require]: Failed opening required 'Zend/Application.php' (include_path='D:\httpdocs\zfp1\library;.;c:\php\includes;C:\AppServ\httpdocs\library') in D:\httpdocs\zfp1\public\index.php on line 18

lo que debemos hacer es cambiar el código del public/index.php:

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
get_include_path(),
)));

Por este otro:

// Ensure library/ is on include_path
$siteRootDir = dirname($_SERVER['DOCUMENT_ROOT']).'\\httpdocs\\';
set_include_path(
    $siteRootDir . '/library' . PATH_SEPARATOR
    . $siteRootDir . '/application' . PATH_SEPARATOR
    . get_include_path()
);

Creación de los ficheros necesarios

Primero creamos el archivo servicio_schedule.php con el siguiente contenido:

<?php
/**
* @author Pablo Fernández Casado
*
* 21/06/2013
* http://www.islavisual.com
*
* Archivo para definir el servicio de un Web Service creado con Zend Framework
*/

class Events {
    /**
      * @var string
      */

    public $event;
}

class Meeting {
    /** @var Events[] */
    public $meeting;
}

//*************************************

class Schedule {
    /**
      * @property string
      * @return Meeting
      */

    public function setMeeting() {
        $schedulle = new Meeting();

        $event = new Events();
        $event->event   = new SoapVar('<event id="1" where="Príncipe de Vergara 10" time="'.$GLOBALS['time'].'">Zend Conference</event>', XSD_ANYXML);
        $schedulle->meeting[] = $event;

        return $schedulle;
    }
}
?>

Ahora creamos el archivo servicio_SOA.php con el siguiente contenido:

<?php
    /**
    * @author Pablo Fernández Casado
    *
    * 21/06/2013
    * http://www.islavisual.com
    *
    * Archivo que genera de forma automática un WSDL a partir de la clase Schedule
    * utilizando Zend Framework
    */

    // Se debe agregar al path el directorio raíz que contiene al framework
    defined('APPLICATION_ENV') || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
    $siteRootDir = dirname($_SERVER['DOCUMENT_ROOT']).'\\httpdocs\\';
    set_include_path($siteRootDir . '/library' . PATH_SEPARATOR . $siteRootDir . '/application' . PATH_SEPARATOR . get_include_path());

    // Se incluye la clase a generar
    require_once('servicio_schedule.php');

    // Si en la url está presente la entrada ?wsdl
    if (strtolower($_SERVER['QUERY_STRING']) == "wsdl") {
        // Se incluye la clase AutoDiscover que es la encargada de generar en forma automática el WSDL
        require_once('Zend/Soap/AutoDiscover.php');
        require_once('Zend/Soap/Wsdl/Strategy/ArrayOfTypeComplex.php');

        $autodiscover = new Zend_Soap_AutoDiscover();
        $autodiscover->setComplexTypeStrategy(new Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex());
        $autodiscover->setOperationBodyStyle(
            array('use' => 'literal',
            'namespace' => 'http://framework.zend.com')
        );
        $autodiscover->setClass('Schedule');
        $autodiscover->handle();
    }

    // Si no
    else {
        require_once('Zend/Soap/Server.php');
        $wsdl_url = sprintf('http://%s%s?wsdl', $_SERVER['HTTP_HOST'], $_SERVER['SCRIPT_NAME']);
        $server = new Zend_Soap_Server($wsdl_url);
        $server->setClass('Schedule');
        // Primero añadimos la etiqueta de que es un XML
        $xml = '<?xml version="1.0" encoding="UTF-8"?'.'>'.file_get_contents("php://input");
        // Eliminamos los caracteres especiales
        $xml = preg_replace("/(<\/?)(\w+):([^>]*>)/", "$1$2$3", $xml);
        // Lo convertimos a XML
        $xml = new SimplexmlElement($xml, TRUE);
        // Y lo recorremos
        foreach ($xml->soapenvBody as $body){
            foreach ($body->framsetMeeting as $setMeeting){
                // Esto gurada la hora en una variable global accesible desde la clase PHP del servicio
                    $GLOBALS['time'] = $setMeeting->attributes()->time;
            }
        }
        $server->handle();
    }
?>

NOTA: En el segundo foreach el objeto se llama framsetMeeting por el 'namespace' => 'http://framework.zend.com' que establecimos en el autodiscover. Si cambiamos el namespace, cambiará el prefijo fram. Si, por ejemplo, ponemos 'namespace' => 'http://islavisual.com' el resultado a buscar no será framsetMeeting sino islsetMeeting.

Simulación del Web Service con SoapUI

SoapUI es una aplicación muy versátil que nos permite probar, simular y generar código de servicios web de forma ágil, partiendo del contrato de los mismos en formato WSDL y con vínculo SOAP sobre HTTP. Es gratuita para uso no comercial y, para temas de desarrollo, nos permitirá realiar pruebas de forma sencilla.

Podemos descargarla de SoapUI.org

Una vez descargada e instalada, abrimos la aplicación y dónde pone Project, arriba a la izquierda, pinchamos el botón derecho del ratón y le damos a New soapUI Project.

Entonces nos saldrá una pantalla parecida a la que muestro e introducimos el nombre del proyecto y la URL del WSDL y, pulsamos OK.

Si ahora desplegamos todas las opciones que nos salen a la izquierda y pulsamos con el botón derecho del ratón en Request 1 nos creará un mensaje de petición similar a:

 

Que en modo texto es:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:fram="http://framework.zend.com">
    <soapenv:Header/>
    <soapenv:Body>
        <fram:setMeeting/>
    </soapenv:Body>
</soapenv:Envelope>

Bien, pues ahora vamos a editar ese mensaje para hacer una prueba. Le vamos a poner un atributo a setMeeting con la hora. Por lo que la petición quedaría:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:fram="http://framework.zend.com">
    <soapenv:Header/>
    <soapenv:Body>
        <fram:setMeeting time="26-06-2013 09:30"/>
    </soapenv:Body>
</soapenv:Envelope>

Y si ahora le damos al botón verde de la ventana Request 1, que es para enviar la petición al servidor, el resultado es:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://framework.zend.com" xmlns:ns2="http://localhost/zfp1/public/servicio_SOA.php">
    <SOAP-ENV:Body>
        <ns1:setMeetingResponse>
            <return>
                <meeting>
                    <ns2:Events>
                        <event id="1" where="Príncipe de Vergara 10" time="26-06-2013 09:30">Zend Conference</event>
                    </ns2:Events>
                </meeting>
            </return>
        </ns1:setMeetingResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Como se puede ver, en el Server, recuperamos el atributo time que, más tarde, estableceremos en la respuesta.