. Подключаем SOAP веб-сервисы в Yii2
Подключаем SOAP веб-сервисы в Yii2

Подключаем SOAP веб-сервисы в Yii2

Многие сайты и некоторые серверные приложения позволяют обращаться к ним по сети посредством стандартизированного протокола SOAP. Они выкладывают открытый API, через который позволяют вызывать некоторые их методы с передачей параметров. При этом масштабы таких систем могут быть совершенно разными: получение прогноза погоды, сеть 1C крупной организации или система бронирования авиабилетов.

В веб-разработке часто приходится интегрировать сторонние сервисы в свой сайт. Например, предположим, что нам нужно получить время восхода солнца, время захода и долготу дня для определённого города с веб-сервиса GisMeteo.

Мы на адрес http://ws.gismeteo.ru/Weather/Weather.asmx посылаем POST-запрос на вызов метода GetSunInfo в виде XML:

И с сервера возвращается нужный нам ответ GetSunInfoResponse в XML-формате:

Аналогично представим, что некий магазин открыл SOAP-сервис базы 1С для своих филиалов по адресу http://api.site.ru/ws/webservice.1cws , закрыв его от посторонних глаз HTTP-Basic авторизацией. Теперь работники других филиалов могут подключить свои экземпляры программы к этому общему сервису и просматривать списки заказов или отгрузок из общей базы и создавать свои:

На стороне сервера нужно только запрограммировать нужные методы вроде GetOrders и опубликовать сервис.

Это даёт возможность сделать корпоративный веб-портал для работников организации, подключенный к сервису 1С. Или даже разработать мобильное приложение.

Действительно, по этому адресу мы можем послать POST-запрос GetOrders :

и получить список заказов, возвращаемый в виде вложенного XML-файла в поле return :

Мы просто отправляем запрос на адрес http://api.site.ru/ws/webservice.1cws для вызова метода с полным именем http://api.site.ru/#WebService:GetOrders и оформляем XML запрос с использованием соответствующего пространства имён http://api.site.ru/ .

Эти три параметра должны быть именно такими, какими их предоставляет сервер.

Смотря на такие «адские» XML-структуры вам наверняка покажется, что это ужасно сложно использовать. На самом деле, это только эмоциональное первое впечатление. Как бы это ни выглядело сложным, вам не придётся разбираться в формате и что-то парсить.

В PHP для работы с веб-сервисами имеется класс SoapClient. Если вы работаете в Windows и столкнулись с отсутствием этого класса, то подключите расширение php_soap.dll в файле php.ini .

Работать с этим компонентом можно в двух режимах. Различаются они только способом формирования запросов.

Работа в WSDL-режиме

Обычно на сервере имеется специально сгенерированный WSDL-файл c описанием всех доступных методов и адресов веб-сервиса. У GisMeteo он выглядит так.

Для уже знакомого нам метода GetSunInfo в нём имеется отдельная секция с определением полей запроса и ответа:

Ниже находится информация о необходимом для его вызова значении action и используемых режимах работы:

И в самом конце файла расположена секция для определения location для работы по протоколам SOAP 1.1 и SOAP 1.2 соответственно:

Аналогичные секции присутствуют в WSDL файле, генерируемом веб-сервисом 1С.

Формат запроса заказов может быть таким:

Полное имя метода с пространством имён таким:

А адреса доступа такими:

Теперь достаточно указать этот файл при создании объекта класса SoapClient :

или воспользоваться локальной копией файла:

Так как сервис может быть закрыт HTTP Basic авторизацией, мы можем указать данные для доступа:

В классе SoapClient реализован магический метод __call , который позволяет работать с удалёнными методами прямо по их имени:

Результат $result не надо парсить вручную, так как он уже будет представлять из себя объект с полями, повторяющими XML-структуру ответа. Например, долготу дня можно будет получить так:

Аргументы метода могут передаваться в виде массива или объекта. Попробуем, например, получить курсы валют на текущий день по протоколу SOAP с сайта Центробанка с помощью метода GetCursOnDate . В отличие от получения погоды, этот метод возвращает ответ с единственным полезным нам полем any , в котором содержится вложенный XML документ с валютами. Но ничего страшного, так как его легко будет превратить в объект с помощью SimpleXMLElement .

Создадим файл curs.php :

и запустим его в консоли:

Через некоторое время мы увидим результат:

Здесь мы WSDL-файл загружаем прямо с сервера и кешируем его в памяти на некоторое время. Но если в каком-либо сервисе этот файл занимает несколько мегабайт, то для экономии трафика его лучше скачать к себе и подключать из локальной папки.

Вот и всё. Создаём клиент и выполняем методы. Никаких мучений с составлением и парсингом SOAP-запросов и ответов.

Работа в ручном режиме

Если у вас есть необходимость подключаться к какому-то специфическому веб-сервису, у которого отсутствует WSDL-файл, либо если вы хотите указывать все параметры вручную, то вам будет нужен режим работы без WSDL.

В данном случае клиенту неоткуда будет брать информацию об адресе сервиса, пространствах имён и полных имён методов для SOAPAction . Поэтому адрес сервиса location и пространство имён uri надо будет указать вручную:

Параметром exceptions мы включаем механизм генерирования исключений, позволяющий обрабатывать их конструкцией try < . >catch (SoapFault $e) . Другими параметрами можно указать, например, используемый прокси-сервер.

Все методы класса SoapClient доступны извне. Послать команду на сервер теперь нужно непосредственным вызовом __soapCall , указав имя метода для построения XML-запроса и полное имя SOAPAction. Получим из 1С список заказов за последние 90 дней:

Метод __soapCall позаботится о построении XML-структуры запроса и вызовет метод __doRequest , который отправит запрос на сервер.

Но при желании мы можем опуститься ещё ниже и вызвать метод __doRequest . В этом случае XML-запрос нужно составить самому:

Смешанное использование

Иногда встречаются ситуации, когда нужно использовать нестандартные параметры запроса. Например, специфические заголовки. Рассмотрим пример получения информации о пользователе из eBay:

Можно заметить, что здесь есть WSDL файл, но для работы нам нужно передавать токен авторизованного пользователя в дополнительных заголовках. И в этом случае нам помог вызов метода __soapCall .

Интеграция с веб-сервисом в Yii2

Аналогично нашим предыдущим скриптам, мы можем производить операции в любом месте нашего приложения:

То есть для данного случая нам просто нужно поместить WSDL-файл в любое место и прописать авторизационные данные в конфигурационном файле.

Это простейший случай, но использование этого кода, например, в контроллере не очень уместно. Вместо этого клиент целесообразнее вынести в отдельный компонент. Этим мы сейчас и займёмся.

Вместо использования динамически создаваемых структур или ассоциативных массивов для передачи аргументов запроса:

лучше использовать одноимённые запросам классы:

Это удобнее тем, что в любом хорошем редакторе или IDE работает автоподстановка полей и подсветка опечаток. Кроме того, в своём классе мы можем релизовать любой дополнительный функционал.

Например, создав базовый класс для наших команд, наследуемый от класса yii\base\Model :

мы превращаем все структуры в модели и легко можем добавить в них правила валидации:

и проверять корректность заполненных данных перед отправкой запроса:

И ещё один нюанс. У нас сделано так, что имя метода и класса его параметров совпадают:

Соответственно, имя метода можно получить из имени класса:

и вызывать этот метод у клиента динамически любым способом из этих двух:

Теперь, пользуясь этими нюансами, мы можем создать компонент приложения с единственным методом send , который будет автоматически определять имя метода и вызывать его у клиента:

и подключить его в конфигурационном файле:

чтобы теперь обращаться к нему как к Yii::$app->webservice :

Этого уже вполне достаточно для нормальной работы.

Но если у вас очень сложный сервис, ответы которого нужно довольно рутинно парсить (или ответ приходит в нестандартной кодировке, которую надо всегда расшифровывать), то можно наделить логикой и ответы.

Создадим базовый класс для ответа:

Теперь отнаследуем от него класс, который будет обрабатывать ответ метода получения курса валют GetCursOnDate . Одноимённый класс, но в папке response :

Дополним наш компонент функционалом оборачивания пришедшего от клиента ответа в полученный из имени метода класс:

После этого вместо голого ответа от клиента в переменной $response мы уже получим наш объект и сможем использовать его методы.

Теперь весь код получения курса доллара через SOAP клиент занимает всего несколько строк:

и может полноценно использоваться в любом месте приложения. Всё связанное с SOAP мы полностью инкапсулируем внутри нашего компонента webservice . Теперь в тестовой конфигурации можно легко подменить класс WebService , чтобы заменить рабочий компонент на его заглушку.

После того, как оставите комментарий к данной статье, советую прочесть ещё пару интересных статей:

📎📎📎📎📎📎📎📎📎📎