Php soap запросы для начинающих. Посылка SOAP-запросов

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

Настройка

Для использования SOAP в php необходимо подключить модуль SOAP (входит в дистрибутив php5). Под windows это делается просто – необходимо дописать (именно дописать, так как эта строка там не просто закомментирована, она отсутствует вообще) в php.ini:
extension=php_soap.dll

Не забудьте перезапустить сервер, если php у вас установлен как модуль.

Создание SOAP-клиента по WSDL-документу

Создание SOAP-клиента обычно происходит по WSDL-документу, который представляет собой XML-документ в определенном формате, полностью описывающий тот или иной веб-сервис. За подробностями по поводу WSDL – отправляю Вас на сайт консорциума W3C - www.w3.org/TR/2005/WD-wsdl20-soap11-binding-20050510 .

Главное же, что необходимо знать для того, чтобы построить клиента к веб-сервису – это знать URL его WSDL-документа.
Для примера возьмем веб-сервис «Currency Exchange Rate» от xmethods.com. Адрес этого веб-сервиса, который позволяет получать курсы валют в режиме онлайн - www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl .

Второй важный момент – из описания веб-сервиса необходимо получить информацию о том, какие методы этот сервис предоставляет, и какие параметры мы должны передавать ему в качестве входных значений (очень похоже на вызов обычной функции php или метода класса). Обычно эта информация содержится в описании сервиса на его сайте. Наш веб-сервис для получения курса валют предоставляет метод getRate(), которому в качестве аргументов передаются коды валют.

И последнее – важно знать, что ожидать в качестве ответа: сколько значений, какого типа и т.п. Это также можно получить из описания.
А в результате код получается очень простым и компактным, почти элементарным:

// Использование Web-сервиса
// "Currency Exchange Rate" от xmethods.com

// Создание SOAP-клиента по WSDL-документу
$client = new SoapClient("http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl");

// Поcылка SOAP-запроса и получение результата
$result = $client->getRate("us", "russia");

Echo ‘Текущий курс доллара: ’, $result, ‘ рублей’;
?>

Как видно из кода в конструктор класса SoapClient необходимо передать URL WSDL-документа и получить объект для работы с нужным веб-сервисом. Затем вызывается метод этого объекта, имя которого совпадает с именем самого метода веб-сервиса. Возвращает же этот метод желаемый нами результат.

Итак, этот простой пример иллюстрирует нам принцип построения SOAP-клиента для веб-сервисов на php. Однако в реальном приложении еще о многом придется позаботиться, в частности о том, что в момент обращения к веб-сервису он может быть временно недоступен или возвращать ошибку. Явно напрашивается использование блока try/catch/throw:)

Задача
Необходимо послать SOAP-запрос. Создание SOAP-клиентов позволяет собирать информацию с SOAP-серверов независимо от их операционных систем и связующего программного обеспечения.

Решение
Применяем SOAP-классы PEAR. Ниже показана клиентская программа, использующая SOAP-сервис GoogleSearch:

Require "SOAP/Client.php";
$query = "php"; // ваши условия поиска в Google

$params = array(










$hits = $soap->
foreach ($hits->resultElements as $hit) {
printf("
", $hit->URL, $hit->title);
}

Обсуждение
Простой протокол доступа к объектам (Simple Object Access Protocol, SOAP) – это способ обмена информацией, подобно XML-RPC реализованный поверх протокола HTTP.

В качестве формата сообщений он также использует XML, что облегчает процесс их создания и анализа.

В результате, так как SOAP не зависит от платформы и языка, он доступен на многих платформах и во многих языках, включая PHP. Чтобы сделать SOAP-запрос, нужно создать новый объект SOAP_Client и передать конструктору адрес страницы, на которую посылается этот запрос:

$soap = new SOAP_Client("http://api.google.com/search/beta2");

В настоящее время поддерживается два различных метода взаимодействия: HTTP и SMTP. Допускается также защищенный режим HTTP, если в вашей версии PHP встроен SSL. Для выбора используемого метода укажите его в качестве префикса вашего URL (http, https или
mailto).

После создания объекта SOAP_Client используйте его метод call() для вызова удаленной функции:

$query = "php";
$params = array(
new SOAP_Value("key", "string", "your google key"),
new SOAP_Value("q", "string", $query),
new SOAP_Value("start", "int", 0),
new SOAP_Value("maxResults", "int", 10),
new SOAP_Value("filter", "boolean", false),
new SOAP_Value("restrict", "string", ""),
new SOAP_Value("safeSearch", "boolean", false),
new SOAP_Value("lr", "string", "lang_en"),
new SOAP_Value("ie", "string", ""),
new SOAP_Value("oe", "string", ""));
$hits = $soap->call("doGoogleSearch", $params, "urn:GoogleSearch");

Массив $params содержит совокупность объектов SOAP_Value.

Объект SOAP_Value создается с тремя аргументами: именем, типом и значением параметра, которое передается SOAP-серверу. Они меняются от сообщения к сообщению в зависимости от SOAP-функции, доступной на сервере.

Реальные события происходят в методе SOAP_Client::call(), который принимает несколько аргументов. Первый – это метод, который должен выполнить сервер; в данном случае это doGoogleSearch. Второй аргумент – это массив параметров, передаваемых функции на SOAP-сервере. Третий аргумент, urn:GoogleSearch, – это пространство имен SOAP; он позволяет серверу узнать, что doGoogleSearch принадлежитк пространству имен GoogleSearch. С помощью пространства имен разрешается возможный конфликт между одноименными методами.

Есть и четвертый параметр, не используемый в данном случае – soap-Action. Этот параметр можно добавить, если требуется сообщить SOAP-серверу URI, представляющий цель запроса. К сожалению, определение слова «intent» трактуется от реализации к реализации по-разному. В данный момент действует соглашение о том, чтобы не использовать soapAction без дальнейшего его уточнения. SOAP-сервер PEAR не использует это поле, но другие поставщики услуг могут присвоить ему свое собственное значение.

В случае успешного выполнения функция возвращает объект, содержащий ответ сервера.

При возникновении ошибки функция возвращает объект PEAR_Error. Google возвращает информацию разных типов, но в данном случае мы выполняем цикл по массиву $resultElements и извлекаем для вывода URL и название каждой найденной ссылки:

Foreach ($hits->resultElements as $hit) {
printf("
", $hit->URL, $hit->title);
}

В результате получим:

(a href="http://www.php.net/">PHP : Hypertext Preprocessor(/a)
(a href="http://www.php.net/downloads.php">PHP : Downloads(/a)
(a href="http://phpnuke.org/">PHP -Nuke(/a)
(a href="http://www.phpbuilder.com/">PHPBuilder.com(/a)
(a href="http://php.resourceindex.com/">The PHP Resource Index(/a)
(a href="http://www.php.com/">PHP .com: Home(/a)
(a href="http://www.php.org/">PHP .org(/a)
(a href="http://php.weblogs.com/">PHP Everywhere:(/a)
(a href="http://www.php3.org/">(/a)
(a href="http://gtk.php.net/">PHP -GTK(/a)

Для выполнения запросов можно также использовать язык определения веб-сервисов (WSDL, Web Services Definition Language). При использовании WSDL нет необходимости перечислять ключи параметров или пространство имен SOAP:

Require "SOAP/Client.php";
$wsdl_url = "http://api.google.com/GoogleSearch.wsdl";
$WSDL = new SOAP_WSDL($wsdl_url);
$soap = $WSDL->getProxy();
$hits = $soap->doGoogleSearch("your google key",$query,0,10,
true,"",false,"lang_en","","");

Этот код эквивалентен самому длинному предыдущему примеру. Объект SOAP_WSDL принимает URL для WSDL-файла GoogleSearch и автоматически загружает спецификацию с этого URL. Вместо создания иприсвоения переменной $soap нового объекта SOAP_Client вызывается метод SOAP_WSDL::getProxy(), создающий объект GoogleSearch
.
Этот новый объект имеет методы с теми же именами, что и имена SOAP-методов GoogleSearch. Поэтому, вместо того чтобы передавать doGoogleSearch функции SOAP_Client::call() в качестве первого параметра, мы вызываем метод $soap->doGoogleSearch(). Значения массива $params становятся аргументами метода, при этом нет необходимости в инкапсуляции массива или создании объектов SOAP_Value. Кроме того, поскольку объект находится в WSDL-файле, то нет необходимости в указании пространства имен.

SOAP (Simple Object Access Protocol) представляет из себя основанный на XML протокол, предназначенный для обмена структурированной информацией между распределенными приложениями поверх существующих в веб протоколов, например HTTP. Спецификация SOAP определяет формат, используемый XML-сообщениями, то, как они должны обрабатываться набор правил кодирования для стандарта, типы данных а также соглашения для вызова удаленных процедур и ответы на вызовы.

Веб-сервисы - это модная и современная технология. Список технологий, относящихся к веб-сервисам увеличивается практически ежедневно, но SOAP является, вероятно, наиболее важной из них. Он стремительно становится стандартным протоколом доступа к веб-сервисам. Он использует XML-сообщения для обмена информацией между конечными точками, и в тоже время предоставляет некоторые преимущества бинарных протоколов. Поддержка RPC (Remote Procedure Calls) в начале была одной из незначительных возможностей протокола SOAP, но сейчас она превратилась в одну из наиболее часто используемых возможностей.

SOAP-расширение для PHP 5 - это первая попытка организовать поддержку SOAP в PHP на Си. У нее есть несколько преимуществ перед существующими реализациями SOAP, написанными на PHP, и самое важное из них - скорость. В данный момент расширение считается экспериментальным, но постепенно оно будет становиться все боле надежным и стабильным.

В расширении SOAP реализованы большие подмножества спецификаций SOAP 1.1, SOAP 1.2 и WSDL 1.1. Главная цель - максимально использовать RPC-возможности SOAP. Везде, где это возможно используется WSDL, чтобы сделать реализацию веб-сервисов более простой.

Первый SOAP-клиент

Чтобы продемонстрировать создание простого SOAP-клиента используем демонстрационный сервис "Delayed Stock Quote" с сайта XMethods. Перед тем как мы начнем писать PHP-код, необходимо собрать следующую информацию о данном конкретном сервисе:

  • Имя метода
  • URL по которому расположен этот сервис
  • Значение заголовка SOAPAction метода
  • Пространство имен метода
  • Имена и типы входных и выходных параметров метода

К счастью, вся эта информация доступна на сайте XMethods по адресу http://www.xmethods.com/ из RPC-профиля сервиса:

Имя метода getQuote
URL сервиса http://66.28.98.121:9090/soap
SOAPAction urn:xmethods-delayed-quotes#getQuote
Пространство имен метода urn:xmethods-delayed-quotes
Входные параметры Symbol (String)
Выходные параметры Result (float)

Пример 1 (client1.php)

$client = new SoapClient (NULL ,
array(
"location" => "http://66.28.98.121:9090/soap" ,
"uri" =>
"style" => SOAP_RPC ,
"use" => SOAP_ENCODED
));

Print($client -> __call (
/* Имя SOAP-метода */
"getQuote" ,
/* Параметры */
array(
new SoapParam (
/* Значение параметра */
"ibm" ,
/* Имя параметра */
"symbol"
)),
/* Опции */
array(
/* Пространство имен SOAP-метода */
"uri" => "urn:xmethods-delayed-quotes" ,
/* HTTP-заголовок SOAPAction для SOAP-метода */
"soapaction" => "urn:xmethods-delayed-quotes#getQuote"
)). "\n" );
?>

Как видите, решение этой простой задачи потребовало довольно много работы.

К счастью веб-сервисы могут описывать себя клиентам с помощью WSDL, в целом это довольно удобно. WSDL для сервиса "Delayed Stock Quote" представлен на его информационной страничке на сайте xmethods.com - http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl .

Вот вариант этого же клиента, переписанный для работы с помощью этого WSDL-документа. Здесь нам уже не нужно указывать URI-сервера, пространство имен, заголовок SOAPAction, способ кодирования и типы параметров. Вся эта информация берется из WSDL файла.

Пример 2 (client2.php)

$client = new
SoapClient (
"http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl"
);

Print($client -> getQuote ("ibm" ));
?>

Так несколько проще, правда?

Какие проблемы возникают при использовании WSDL? Единственный аргумент против его использования состоит в том, что клиент должен прочитать WSDL с сервера до того, как можно будет вызвать какую-нибудь процедуру, а в веб это может занять довольно много времени. Для того, чтобы ускорить работу в SOAP-расширении предусмотрены следующие параметры конфигурации: soap.wsdl_cache_enabled, soap.wsdl_cache_dir and soap.wsdl_cache_ttl. Их можно задать в файле php.ini или с помощью ini_set()(см. Пример 4). По умолчанию кэширование WSDL включено и WSDL-файлы кэшируются на 1 день.

Вот раздел SOAP файла php.ini с значениями по умолчанию. Вы можете скопировать их в свой php.ini.

[ soap ]

Soap . wsdl_cache_enabled = "1"
; включает или выключает кэширование WSDL

Soap . wsdl_cache_dir = "/tmp"
; задает имя директории в которой SOAP - расширение будет хранить кэшированные файлы

Soap . wsdl_cache_ttl = "86400"
; (время жизни ) устанавливает время (в секундах ) которое файлы из кэша могут использоваться

Первый SOAP-сервер

Попробуем написать собственный SOAP веб-сервис, который будет делать тоже, что и сервис "Delayed Stock Quote" с XMethods.

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

Раздел message определяет два сообщения. Первое - getQuoteRequest, которое передает сообщение getQuote и принимает одностроковый параметр с именем symbol. Второе сообщение - getQuoteResponse, ответ на запрос getQuote, передающий одно значение типа float с именем Result.

Раздел portType определяет единственную операцию getQuote, которая указывает, какое из описанных в разделе message будет использоваться для запроса, а какое для ответа.

В разделе binding определяется как сообщение должно кодироваться и передаваться. В данном случае в нем указано, что мы пошлем RPC-запрос через HTTP, используя SOAP-кодирование. Здесь также определены пространство имен и значение заголовка SOAPAction для метода getQuote.

И наконец в разделе service определяется URL по которому находится сервис.

Пример 3 (stockquote.wsdl)


targetNamespace="http://example.org/StockQuote"
xmlns:tns=" http://example.org/StockQuote "
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">












transport="http://schemas.xmlsoap.org/soap/http"/>



encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>


encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>








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

Теперь самое время приступить к созданию нашего сервера.

Прежде всего мы разработаем функцию getQuote(), которая будет обрабатывать входящие запросы из веб. Далее мы создадим объект класса SoapServer и присоединим к нему нашу функцию с помощью метода SoapServer::addFunction(). Как вы увидите в дальнейшем, у конструктора SoapServer() есть только один параметр - путь к WSDL-документу, описывающему сервис.

Пример 4 (server1. php)

$quotes = array(
"ibm" => 98.42
);


global $quotes ;
return $quotes [ $symbol ];
}

Ini_set ("soap.wsdl_cache_enabled" , "0" ); // отключаем кэширование WSDL
$server = new SoapServer ("stockquote1.wsdl" );
$server -> addFunction ("getQuote" );
$server -> handle ();
?>

SoapServer может работать и без WSDL, примерно также как клиент, но у такого варианта нет никаких преимуществ, из-за которых стоило бы его использовать. Если вы все же хотите работать именно так, вы должны убедиться что возвращаемые значения - это объекты классов SoapParam и SoapVar(как в первом примере.

Вот клиент для доступа к нашему SOAP-серверу. По сравнению с предыдущим примером добавилась только ссылка на местонахождение WSDL. Предполагается, что файл "stockquote1.wsdl" лежит в той же директории что и SOAP-сервер.

Пример 5 (client3.php)

$client = new SoapClient ("stockquote1.wsdl" );
print($client -> getQuote ("ibm" ));
?>

Какие основные проблемы есть в наших клиенте и сервере?

Для начала, они не обрабатывают ошибки. Что происходит, когда сервер не находит подходящий результат для переданного ему значения переменной symbol? В SOAP существует специальный формат сообщений для сообщений об ошибках - SoapFault.Чтобы сгенерировать такое сообщение, сервер должен вызвать исключение с помощью объекта SoapFault. Первый параметр конструктора SoapFault() это строка с кодом ошибки, второй - строка с описанием ошибки. Клиент должен быть написан так, чтобы обрабатывать исключения SoapFault.

Во вторых, функциональность веб-сервиса лучше инкапсулировать в PHP-класс. В этом случае нам не нужно будет использовать глобальные переменные и добавлять к серверу каждый SOAP-метод по отдельности. Вместо этого мы сможем добавить класс целиком, и все его методы станут доступны через SOAP. Вот соответствующим образом модифицированные версии клиента и сервера.

Пример 6 (server2.php)

class QuoteService {
private $quotes = array("ibm" => 98.42 );

Function getQuote ($symbol ) {
if (isset($this -> quotes [ $symbol ])) {
return $this -> quotes [ $symbol ];
} else {
throw new
SoapFault ("Server" , "Unknown Symbol "$symbol"." );
}
}
}

$server = new SoapServer ("stockquote2.wsdl" );
$server -> setClass ("QuoteService" );
$server -> handle ();
?>

Как видите, я использовал метод SoapServer::setClass() для соединения объекта SoapServer с классом QuoteService.

Пример 7 (client4.php)

$client = new SoapClient ("stockquote2.wsdl" );
try {
echo "

\n"
; 
print($client -> getQuote ("ibm" ));
echo "\n" ;
print($client -> getQuote ("microsoft" ));
echo "\n
\n" ;
} catch (SoapFault $exception ) {
echo $exception ;
}
?>

А что внутри?

Если вы хотите разобраться в формате SOAPсообщений, или хотите самостоятельно отлаживать SOAP-клиента, то этот раздел для вас.

Как вы видели в первом примере, конструктор SoapClient() принимает ассоциативный массив в качестве второго параметра. С помощью этого массива мы можем вызывать различные опции на сервере.

Посмотрим две из них:

  • trace - позволяет клиенту сохранять SOAP-запросы и ответы (по умолчанию выключено).
  • exceptions - позволяет клиенту контролировать механизм исключений (по умолчанию включено).

Посмотрим на следующий пример SOAP-клиента. Это доработанный клиент из Примера 5, в точности показывающий, что передается между клиентом и сервером. Для получения этой информации используются методы __getLastRequest() и __getLastResponse().

Пример 8 (client5.php)

$client = new SoapClient ("stockquote1.wsdl" ,array(
"trace" => 1 ,
"exceptions" => 0 ));
$client -> getQuote ("ibm" );
print "

\n"
; 
print "Запрос:\n" . htmlspecialchars ($client -> __getLastRequest ()) . "\n" ;
print "Ответ:\n" . htmlspecialchars ($client -> __getLastResponse ()). "\n" ;
print "
" ;
?>

Вот вывод скрипта. Он немного изменен, для упрощения понимания.

Запрос:



xmlns:xsd="http://www.w3.org/2001/XMLSchema"

SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">


ibm


Ответ:


xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:xmethods-delayed-quotes"
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/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">


98.42


Другие реализации SOAP для PHP

Все они написаны на PHP, а не на Си.

Резюме

В этой статье я описал только основные функции SOAP-расширения. На самом деле оно может гораздо больше, но продемонстрировать все его возможности в рамках одной короткой статьи попросту невозможно. Вот список главных из них:

  1. Поддержка комплексных типов данных (массивов, объектов)
  2. Поддержка SOAP - заголовков
  3. Динамическая поддержка SOAP 1.1 и SOAP 1.2

Возможно они будут более подробно рассмотрены в последующих статьях.

Подробная документация по SOAP-расширению расположена по адресу http://www.php.net/manual/en/ref.soap.php .

Разработка этого расширения находится на начальном этапе, поэтому ваши отзывы помогут сделать его более стабильным, надежным, удобным и быстрым. Пожалуйста сообщайте о всех возникающих при его использовании проблемах по адресу http://bugs.php.net/ .

Ссылки

Об авторе