В этой статье описано новое SOAP-расширение для PHP. Статья
предназначена для разработчиков, которые хотят создавать
собственные веб-сервисы, либо работать с уже существующими.
Статья предполагает, что читателю знакомы такие понятия как
веб-сервис, SOAP и WSDL (Web Services Description Language).
Введение
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-профиля
сервиса:
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)
<?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 по которому
находится сервис.
Замечание: по умолчанию кэширование WSDL включено. На время
разработки и отладки вашего WSDL, кэширование лучше отключить.
Теперь самое время приступить к созданию нашего сервера.
Прежде всего мы разработаем функцию getQuote(), которая
будет обрабатывать входящие запросы из веб. Далее мы создадим
объект класса SoapServer и присоединим к нему нашу функцию с
помощью метода SoapServer::addFunction(). Как вы увидите в
дальнейшем, у конструктора SoapServer() есть только один
параметр - путь к WSDL-документу, описывающему сервис.
Пример 4 (server1. php)
<?php
$quotes = array( "ibm" => 98.42 );
function getQuote($symbol) {
global $quotes;
return $quotes[$symbol];
}
SoapServer может работать и без WSDL, примерно также как
клиент, но у такого варианта нет никаких преимуществ, из-за
которых стоило бы его использовать. Если вы все же хотите
работать именно так, вы должны убедиться что возвращаемые
значения - это объекты классов SoapParam и SoapVar(как в
первом примере.
Вот клиент для доступа к нашему SOAP-серверу. По сравнению
с предыдущим примером добавилась только ссылка на
местонахождение WSDL. Предполагается, что файл
"stockquote1.wsdl" лежит в той же директории что и
SOAP-сервер.
Пример 5 (client3.php)
<?php
$client = new SoapClient("stockquote1.wsdl");
print($client->getQuote("ibm")); ?>
Какие основные проблемы есть в наших клиенте и сервере?
Для начала, они не обрабатывают ошибки. Что происходит,
когда сервер не находит подходящий результат для переданного
ему значения переменной symbol? В SOAP существует специальный
формат сообщений для сообщений об ошибках - SoapFault.Чтобы
сгенерировать такое сообщение, сервер должен вызвать
исключение с помощью объекта SoapFault. Первый параметр
конструктора SoapFault() это строка с кодом ошибки, второй -
строка с описанием ошибки. Клиент должен быть написан так,
чтобы обрабатывать исключения SoapFault.
Во вторых, функциональность веб-сервиса лучше
инкапсулировать в PHP-класс. В этом случае нам не нужно будет
использовать глобальные переменные и добавлять к серверу
каждый SOAP-метод по отдельности. Вместо этого мы сможем
добавить класс целиком, и все его методы станут доступны через
SOAP. Вот соответствующим образом модифицированные версии
клиента и сервера.
Пример 6 (server2.php)
<?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.
Если вы хотите разобраться в формате SOAPсообщений, или
хотите самостоятельно отлаживать SOAP-клиента, то этот раздел
для вас.
Как вы видели в первом примере, конструктор SoapClient()
принимает ассоциативный массив в качестве второго параметра. С
помощью этого массива мы можем вызывать различные опции на
сервере.
Посмотрим две из них:
trace - позволяет клиенту сохранять SOAP-запросы и
ответы (по умолчанию выключено).
exceptions - позволяет клиенту контролировать механизм
исключений (по умолчанию включено).
Посмотрим на следующий пример SOAP-клиента. Это
доработанный клиент из Примера 5, в точности показывающий, что
передается между клиентом и сервером. Для получения этой
информации используются методы __getLastRequest() и
__getLastResponse().
В этой статье я описал только основные функции
SOAP-расширения. На самом деле оно может гораздо больше, но
продемонстрировать все его возможности в рамках одной короткой
статьи попросту невозможно. Вот список главных из них:
Поддержка комплексных типов данных (массивов, объектов)
Поддержка SOAP - заголовков
Динамическая поддержка SOAP 1.1 и SOAP 1.2
Возможно они будут более подробно рассмотрены в последующих
статьях.
Разработка этого расширения находится на начальном этапе,
поэтому ваши отзывы помогут сделать его более стабильным,
надежным, удобным и быстрым. Пожалуйста сообщайте о всех
возникающих при его использовании проблемах по адресу http://bugs.php.net/.
Дмитрий Стогов - один из авторов SOAP-расширения. Он также
написал расширение PECL/perl и Turck MMCache. В настоящее
время он проживает в Санкт Петербурге с женой и ребенком.