Не буду останавливаться на вопросе, что такое веб-сервисы и зачем они нужны. В сети очень много статей на эту тему. Просто постараюсь вкратце показать, каким простым способом возможно создание клиента к любому веб-сервису на 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)
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/">
Замечание: по умолчанию кэширование 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" ;\n" ;
print($client -> getQuote ("ibm" ));
echo "\n" ;
print($client -> getQuote ("microsoft" ));
echo "\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/">
Ответ:
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/">
Другие реализации SOAP для PHP
Все они написаны на PHP, а не на Си.
Резюме
В этой статье я описал только основные функции SOAP-расширения. На самом деле оно может гораздо больше, но продемонстрировать все его возможности в рамках одной короткой статьи попросту невозможно. Вот список главных из них:
- Поддержка комплексных типов данных (массивов, объектов)
- Поддержка SOAP - заголовков
- Динамическая поддержка SOAP 1.1 и SOAP 1.2
Возможно они будут более подробно рассмотрены в последующих статьях.
Подробная документация по SOAP-расширению расположена по адресу http://www.php.net/manual/en/ref.soap.php .
Разработка этого расширения находится на начальном этапе, поэтому ваши отзывы помогут сделать его более стабильным, надежным, удобным и быстрым. Пожалуйста сообщайте о всех возникающих при его использовании проблемах по адресу http://bugs.php.net/ .