Cómo crear un servicio Web SOAP en un controlador de Symfony2

Configurar un controlador para que actúe como un servidor SOAP es muy sencillo con un par de herramientas. Debes, por supuesto, tener instalada la extensión SOAP de PHP. Debido a que la extensión SOAP de PHP, actualmente no puede generar un WSDL, debes crear uno desde cero o utilizar un generador de un tercero.

Nota

Hay disponibles varias implementaciones del servidor SOAP para usarlas con PHP. Zend SOAP y NuSOAP son dos ejemplos. A pesar de que usamos la extensión SOAP de PHP en estos ejemplos, la idea general debería seguir siendo aplicable a otras implementaciones.

SOAP trabaja explotando los métodos de un objeto PHP a una entidad externa (es decir, la persona que utiliza el servicio SOAP). Para empezar, crea una clase —HelloService— que representa la funcionalidad que vas a exponer en tu servicio SOAP. En este caso, el servicio SOAP debe permitir al cliente invocar a un método llamado hello, el cual sucede que envía un correo electrónico:

// src/Acme/SoapBundle/Services/HelloService.php
namespace Acme\SoapBundle\Services;

class HelloService
{
    private $mailer;

    public function __construct(\Swift_Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    public function hello($name)
    {

        $message = \Swift_Message::newInstance()
                                ->setTo('me@example.com')
                                ->setSubject('Hello Service')
                                ->setBody($name . ' says hi!');

        $this->cartero->send($mensaje);


        return 'Hello, '.$name;
    }
}

A continuación, puedes entrenar a Symfony para que sea capaz de crear una instancia de esta clase. Puesto que la clase envía un correo electrónico, se ha diseñado para aceptar una instancia de Swift_Mailer. Usando el contenedor de servicios, puedes configurar a Symfony para construir correctamente un objeto HelloService:

  • YAML
    # app/config/config.yml
    services:
        hello_service:
            class: Acme\SoapBundle\Services\HelloService
            arguments: ["@mailer"]
    
  • XML
    <!-- app/config/config.xml -->
        <services>
        <service id="hello_service" class="Acme\SoapBundle\Services\HelloService">
            <argument type="service" id="mailer"/>
        </service>
        </services>
    
  • PHP
    // app/config/config.php
    $container
        ->register('hello_service', 'Acme\SoapBundle\Services\HelloService')
        ->addArgument(new Reference('mailer'));
    

A continuación hay un ejemplo de un controlador que es capaz de manejar una petición SOAP. Si indexAction() es accesible a través de la ruta /soap, entonces puedes recuperar el documento WSDL a través de /soap?wsdl.

namespace Acme\SoapBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

class HelloServiceController extends Controller
{
    public function indexAction()
    {
        $server = new \SoapServer('/ruta/a/hello.wsdl');
        $server->setObject($this->get('hello_service'));

        $response = new Response();
        $response->headers->set('Content-Type', 'text/xml; charset=ISO-8859-1');

        ob_start();
        $server->handle();
        $response->setContent(ob_get_clean());

        return $response;
    }
}

Toma nota de las llamadas a ob_start() y ob_get_clean(). Estos métodos controlan la salida almacenada temporalmente lo cual te permite «atrapar» la salida difundida por $server->handle(). Esto es necesario porque Symfony espera que el controlador devuelva un objeto Respuesta con la salida como «contenido». También debes recordar establecer la cabecera Content-Type a text/xml, ya que esto es lo que espera el cliente. Por lo tanto, utiliza ob_start() para empezar a almacenar la STDOUT y usa ob_get_clean() para volcar la salida difundida al contenido de la Respuesta y limpiar la memoria de salida. Por último, estás listo para devolver la Respuesta.

A continuación hay un ejemplo de llamada al servicio usando el cliente NuSOAP. Este ejemplo asume que el indexAction en el controlador de arriba es accesible a través de la ruta /soap:

$client = new \Soapclient('http://example.com/app.php/soap?wsdl', true);

$result = $client->call('hello', array('name' => 'Scott'));

Abajo está un ejemplo de WSDL.

<?xml version="1.0" encoding="ISO-8859-1"?>
<definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:tns="urn:arnleadservicewsdl"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    targetNamespace="urn:helloservicewsdl">

    <types>
        <xsd:schema targetNamespace="urn:hellowsdl">
            <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" />
            <xsd:import namespace="http://schemas.xmlsoap.org/wsdl/" />
        </xsd:schema>
    </types>

    <message name="helloRequest">
        <part name="name" type="xsd:string" />
    </message>

    <message name="helloResponse">
        <part name="return" type="xsd:string" />
    </message>

    <portType name="hellowsdlPortType">
        <operation name="hello">
            <documentation>Hello World</documentation>
            <input message="tns:helloRequest"/>
            <output message="tns:helloResponse"/>
        </operation>
    </portType>

    <binding name="hellowsdlBinding" type="tns:hellowsdlPortType">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="hello">
            <soap:operation soapAction="urn:arnleadservicewsdl#hello" style="rpc"/>

            <input>
                <soap:body use="encoded" namespace="urn:hellowsdl"
                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
            </input>

            <output>
                <soap:body use="encoded" namespace="urn:hellowsdl"
                    encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
            </output>
        </operation>
    </binding>

    <service name="hellowsdl">
        <port name="hellowsdlPort" binding="tns:hellowsdlBinding">
            <soap:address location="http://example.com/app.php/soap" />
        </port>
    </service>
</definitions>
Bifúrcame en GitHub