Як створити вед-сервіс SOAP у контролері Symfony

Дата оновлення перекладу 2023-05-27

Як створити вед-сервіс SOAP у контролері Symfony

Налаштування контролера таким чином, щоб він поводив себе як веб-сервіс SOAP - це легко за допомогою деяких інструментів. Авжеж, у вас повинно бути встановлене розширення PHP SOAP. Так як розширення PHP SOAP не може на даний момент згенерувати WSDL, то ви повинні або створити його з нуля, або використати сторонній генератор.

Note

Існує декілька доступних для використання реалізацій SOAP-сервера з PHP. Zend SOAP і NuSOAP є двома прикладами. Незважаючи на те, що у цих прикладах використовується розширення PHP SOAP, загальна ідея повинна бути застосовувана до інших реалізацій

SOAP працює шляхом розкриття методів PHP-обʼєкта зовнішньої сутності (тобто, користувача, що використовує SOAP-сервіс). Для початку, створіть клас HelloService, що надає функціональність, яку ви розкриєте у вашому SOAP-сервісі. В такому випадку, SOAP-сервіс дозволить клієнту викликати метод за іменем hello, який відправляє електронний лист:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// src/Service/HelloService.php
namespace App\Service;

use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

class HelloService
{
    private MailerInterface $mailer;

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

    public function hello(string $name): string
    {
        $email = (new Email())
            ->from('admin@example.com')
            ->to('me@example.com')
            ->subject('Hello Service')
            ->text($name.' says hi!');

        $this->mailer->send($email);

        return 'Hello, '.$name;
    }
}

Далі, переконайтеся в тому, що ваш новий клас зареєстрований як сервіс. Якщо ви використовуєте конфігурацию сервісів за замовчуванням , то вам нічого не потрібно робити!

Нарешті. нижче ви побачите приклад контролера, здатного обробити SOAP-запит. Так як index() доступний через /soap, документ WSDL може бути викликаний через /soap?wsdl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// src/Controller/HelloServiceController.php
namespace App\Controller;

use App\Service\HelloService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class HelloServiceController extends AbstractController
{
    #[Route('/soap')]
    public function index(HelloService $helloService)
    {
        $soapServer = new \SoapServer('/path/to/hello.wsdl');
        $soapServer->setObject($helloService);

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

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

        return $response;
    }
}

Відмітьте виклики до ob_start() та ob_get_clean(). Ці методи контролюють буферизацію виведення, що дозволяє вам "спіймати у пастку" відображений метод $server->handle(). Це необхідно, так як Symfony чекає, що ваш контролер поверне обʼєкт Response з виведенням в якості "змісту". Ви також повинні не забути встановити заголовок "Content-Type" (Тип змісту) як "text/xml", так як це те, чого очікуватиме клієнт. Таким чином, ви використовуєте ob_start(), щоб почати буферизацію STDOUT та використовуєте ob_get_clean(), щоб скинути відображене виведення у зміст Відповіді та очистити буфер виведення. Нарешті, ви готові повернути Response.

Нижче продемонстровано приклад виклику сервісу з використанням клієнту NuSOAP. Цей приклад припускає, що метод index() в контролері вище доступний за маршрутом /soap:

1
2
3
$soapClient = new \SoapClient('http://example.com/index.php/soap?wsdl');

$result = $soapClient->__soapCall('hello', ['name' => 'Scott']);

Приклад WSDL нижче.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?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:helloservicewsdl"
    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/index.php/soap"/>
        </port>
    </service>
</definitions>