Введение в NuSOAP
NuSOAP - представляет из себя набор PHP-классов,
позволяющих разработчикам создавать и использовать веб-сервисы
на SOAP. NuSOAP не требует установки PHP-extensions. Текущая
стабильная версия NuSOAP (0.6.3 на момент написания этой
статьи - 23.04.2003) (уже вышла версия 0.6.7 - прим пер.),
поддерживает большое количество возможностей спецификации SOAP
1.1. Она также может генерировать WSDL 1.1 и использовать его.
Поддерживаются rpc/encoded и document/literal виды сервисов.
В то же время необходимо учитывать, что поддержка SOAP 1.1
и WSDL 1.1 в NuSOAP реализована не полностью по сравнению с
другими реализациями, например, .NET или Apache Axis.
В этой статье описано, как получить и установить NuSOAP, и
приведено несколько примеров, демонстрирующих ее возможности.
Эта статья ни в коей мере не претендует на полное описание
возможностей NuSOAP, но, надеюсь, что любому PHP-программисту
этой информации для начала хватит. Более подробно
программирование с помощью NuSOAP описано в следующих статьях.
Установка.
Вы можете взять NuSOAP из дерева CVS на странице проекта
NuSOAP на SourceForge. Существует веб-интерфейс к CVS.
Например, вы можете получить версию 1.20 файла nusoap.php,
набрав следующий URL: http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/*checkout*/nusoap/lib/nusoap.php?rev=1.20.
Вы можете начать работать с любой версией кода на ваш выбор. Я
проверял приведенные примеры на версии 1.20, исправив в ней
несколько ошибок.
После того, как вы скачали копию nusoap.php, вам необходимо
поместить его в дерево своей программы, чтобы можно было
подключать ее в PHP-код. Некоторые пользователи кладут ее в
отдельную директорию lib. Я в своих примерах поместил ее в ту
же директорию, что и сами примеры.
Hello, World
Демонстрируя полное отсутствие воображения, я начну с
популярнейшего примера "Hello, World". Это поможет
продемонстрировать основы создания клиентов и серверов с
помощью NuSOAP.
Начну с написания кода сервера, поскольку, если нет
сервера, то и клиент не нужен. Сервер реализует единственный
SOAP-метод с названием Hello, который принимает единственный
строковый параметр и возвращает строку. Надеюсь, что
комментариев в коде будет достаточно для объяснения. Для
создания самых простых веб-сервисов все, что понадобится - это
определить и зарегистрировать собственные функции.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр сервера
$server = new soap_server;
// Регистрируем предоставляемый метод
$server->register('hello');
// Определяем метод как функцию PHP
function hello($name) {
return 'Hello, ' . $name;
}
// Используем HTTP-запрос чтобы вызвать
сервис.
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
Код клиента для этого сервиса приведен ниже. Несколько
вещей необходимо отметить особо. Во-первых, при создании
экземпляра сервера параметр, передаваемый ему - это URL
сервера. В моем случае сервер расположен по адресу
http://localhost/phphack. Сервисы, с которыми вы будете
работать, будут, естественно, расположены по другим адресам.
Во-вторых, при вызове сервиса первый параметр - это его имя.
Он должен совпадать с регистрозависимым именем метода,
доступного на сервере. В этом примере он должен совпадать с
методом, зарегистрированном внутри helloworld.php. И наконец,
второй параметр - это массив параметров, которые будут
переданы методу SOAP-сервера. Так как метод hello сервера
helloworld.php принимает единственный параметр, в этом массиве
будет единственный элемент.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/helloworld.php');
// Вызываем SOAP-метод
$result = $client->call('hello', array('name' => 'Scott'));
// Отображаем результат
print_r($result);
?>
Отладка
Как и с любыми программами, бывает, что написанный код не
работает и его приходится отлаживать. NuSOAP предоставляет
несколько возможностей, полезных для отладки. В NuSOAP при
отладке обычно просматриваются посланный запрос и ответ
сервера. Класс soapclient содержит атрибуты request и
response, которые позволяют отобразить соответственно запрос и
ответ. Например, далее приведен листинг модифицированной
версии helloworldclient.php, отображающий запрос и ответ. В
следующем разделе я опишу структуру запроса и ответа,
отображаемых нашим клиентом.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/helloworld.php');
// Вызываем SOAP-метод
$result = $client->call('hello', array('name' => 'Scott'));
// Отображаем результат
print_r($result);
// Отображаем запрос и ответ
echo '<h2>Запрос</h2>';
echo '<pre>' . htmlspecialchars($client->request, ENT_QUOTES) . '</pre>';
echo '<h2>Ответ</h2>';
echo '<pre>' . htmlspecialchars($client->response, ENT_QUOTES) . '</pre>';
?>
Также NuSOAP предоставляет средства для просмотра
отладочной информации, полученной внутри его классов. Добавим
следующий код в клиента, чтобы он показывал эту дополнительную
отладочную информацию. К сожалению, разбираться в том, что эта
информация означает, вам придется самостоятельно.
//
Отображаем отладочные сообщения
echo '<h2>Отладка</h2>';
echo '<pre>' . htmlspecialchars($client->debug_str, ENT_QUOTES) . '</pre>';
Сервер предоставляет такую же отладочную информацию. На
самом деле, этот отладочный текст добавляется как
XML-комментарий в конец SOAP-ответа, так что его можно
посмотреть, отобразив ответ сервера на клиенте. Сервер с
включенной отладкой выглядит так:
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Включаем отладку *до* создания
экземпляра сервера
$debug = 1;
// Создаем экземпляр
сервера
$server = new soap_server;
// Регистрируем предоставляемый метод
$server->register('hello');
// Определяем метод как функцию PHP
function hello($name) {
return 'Hello, ' . $name;
}
// Используем HTTP-запрос чтобы вызвать
сервис.
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
Третье средство отладки, на самом деле, не столько средство
отладки, сколько хорошая программистская практика. В
предыдущих примерах мы не проверяли, возникают ли ошибки при
SOAP-вызовах. Устойчивый к ошибкам клиент будет выглядеть
следующим образом.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/helloworld.php');
// Проверяем, не произошла ли ошибка.
$err = $client->getError();
if ($err) {
// Отображаем ошибку
echo '<p><b>Ошибка в
конструкторе класса: ' . $err . '</b></p>';
// Теперь мы уже знаем, что следующий
вызов будет неудачным.
}
// Вызываем SOAP-метод
$result = $client->call('hello', array('name' => 'Scott'));
// Проверим, возник ли сбой
if ($client->fault) {
echo '<p><b>Сбой:
';
print_r($result);
echo '</b></p>';
} else {
// Проверяем, не произошла ли ошибка
$err = $client->getError();
if
($err) {
// Отображаем ошибку
echo '<p><b>Ошибка: ' . $err . '</b></p>';
} else {
// Отображаем результат
print_r($result);
}
}
?>
Чтобы проверить, как работает этот код, сделаем так,чтобы
произошла ошибка, например, изменим имя вызываемого метода с
'hello' на 'goodbye'.
Запрос и ответ
Выше я показал, как можно просто отобразить SOAP-запрос и
SOAP-ответ. Вот как выглядит запрос от helloworld2client.php.
POST /phphack/helloworld2.php
HTTP/1.0
User-Agent: NuSOAP/0.6.3
Host:
localhost
Content-Type: text/xml;
charset="ISO-8859-1"
Content-Length: 536
SOAPAction:
""
<?xml version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<ns1:hello
xmlns:ns1="http://testuri.org">
<name
xsi:type="xsd:string">Scott</name>
</ns1:hello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Посмотрев на заголовки HTTP, вы увидите, что поле
SOAPAction пустое. Это значение по умолчанию. Методы ваших
сервисов могут быть присвоены SOAPAction-у, и ваши клиенты
могут указывать SOAPAction как один из параметров вызываемого
метода.
В теле запроса вы увидите, что NuSOAP использует кодировку
ISO-8859-1, также называемую Latin-1. Пока что, по крайней
мере, в версии 0.6.3, нет возможности указать другую
кодировку, так как класс soap_transport_http наследует
параметр soap_defencoding от класса nusoap_base. В PHP есть
функция, конвертирующая текст в UTF-8, поэтому нетрудно
изменить NuSOAP, чтобы он позволял указывать кодировку UTF-8.
Более того, я полагаю, что кое-кто посылал в рассылку на
SourceForge патч для поддержки других кодировок, хотя, какие
кодировки поддерживаются, зависит от версии PHP, которой вы
пользуетесь, и от того, с какими ключами (features) он был
собран. И еше одна важная вещь - элемент, определяющий вызов
метода, элемент по имени hello, был помещен в пространство
имен http://tempuri.org. Это хорошая практика, и для многих
сервисов необходимо указать пространство имен, в котором
сервис был определен. Это будет продемонстрировано в
дальнейшем.
Ответ от сервера выглядит следующим образом.
HTTP/1.1 200 OK
Server:
Microsoft-IIS/5.0
Date: Thu, 24 Apr 2003 01:30:58
GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP Server
v0.6.3
Connection: Close
Content-Type: text/xml;
charset=UTF-8
Content-Length: 524
<?xml
version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<helloResponse>
<soapVal xsi:type="xsd:string">Hello,
Scott</soapVal>
</helloResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Здесь есть три очень интересных вещи, на которые нужно
обратить внимание. Во-первых, в объявлении XML, так же как и в
запросе, указана кодировка ISO-8859-1, тогда как в
HTTP-заголовке Content-Type стоит UTF-8. С технической точки
зрения, это проблема, так как ISO-8859-1 не является
подмножеством UTF-8, но при тестировании совместимости никаких
проблем из-за этого не возникало. В коде серверной части есть
один баг, связанный с кодировками. Сервер пытается
использовать кодировку входящего запроса, чтобы решить, какую
кодировку использовать в ответе. Но, по опыту, в некоторых
версиях PHP кодировка определяется некорректно, и ответное
сообщение кодируется в UTF-8. Если в сообщении используются
только символы ASCII, все отлично, но символы из второй
половины ISO-8859-1 передаются некорректно. Я исправил это в
моей локальной копии NuSOAP, но не знаю, когда это будет
исправлено в основной ветке.
Во-вторых, XML-элемент, содержащий ответ, это элемент
helloResponse, не находится ни в каком пространстве имен.
Обычно он находится в том же пространстве имен, что и запрос.
Некоторые SOAP-клиенты могут не воспринять этот ответ.
В-третьих, элемент с возвращаемым значением называется
soapVal. Это умолчальное значение, используемое в NuSOAP.
Лучше всего самостоятельно указывать, как должен называться
этот элемент, чтобы название отражало его конкретное
назначение. То, что NuSOAP принимает такой ответ, в некоторой
мере демонстрирует "лояльность" ее клиентской части.
Создание веб-сервисов с
помощью NuSOAP
NuSOAP - представляет из себя набор PHP-классов,
позволяющих разработчикам создавать и использовать веб-сервисы
на SOAP. NuSOAP не требует установки PHP-extensions. Текущая
стабильная версия NuSOAP (0.6.3 на момент написания этой
статьи - 23.04.2003) (уже вышла версия 0.6.7 - прим пер.),
поддерживает большое количество возможностей спецификации SOAP
1.1. Она также может генерировать WSDL 1.1 и использовать его.
Поддерживаются rpc/encoded и document/literal виды сервисов.
В то же время необходимо учитывать, что поддержка SOAP 1.1
и WSDL 1.1 в NuSOAP реализована не полностью по сравнению с
другими реализациями, например, .NET или Apache Axis.
Эта статья является продолжением статьи "Введение в
NuSOAP". В ней более подробно описоно то, как создавать и
использовать веб-сервисы с помощью NuSOAP.
Hello, World
возвращается
Демонстрируя полное отсутствие воображения, в прошлой
статье я использовал популярнейший пример "Hello, World".
Кроме того, там же я описал SOAP-запрос и SOAP-ответ, которыми
обменялись клиент и сервер. Поэтому эту статью я начну с того,
что покажу, как и до каких пределов этот пример может быть
улучшен.
В теле SOAP сообщения можно улучшить две вещи. Во-первых,
элемент response нужно поместить в какое-нибудь пространство
имен. Во-вторых, его первому дочернему узлу надо дать
нормальное имя, вместо умолчального soapVal.
Вы уже могли догадаться, что пространство имен элемента
response задается с помощью параметра $namespace метода
soap_server->register. Также, можно заметить, что имя
возвращаемого элемента можно задать через массив выходных
параметров. Тем не менее, код с этими изменениями, показанный
ниже, возвращает такой же SOAP-ответ, как и оригинальный
helloworld.php. Интересно, что, хотя пространство имен и
SOAPAction определены, клиенту не обязательно указывать их в
SOAP-запросе.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр сервера
$server = new soap_server;
// Регистрируем предоставляемый метод
// Замечание: в NuSOAP 0.6.3, без WSDL используется только
название метода
$server->register(
'hello', // название метода
array('name' => 'xsd:string'), // входные параметры
array('return' => 'xsd:string'), // выходные параметры
'uri:helloworld', // пространство имен
'uri:helloworld/hello', // SOAPAction
'rpc', // стиль
'encoded' // использование
);
// Определяем метод как функцию PHP
function hello($name) {
return 'Hello, ' . $name;
}
// Используем HTTP-запрос, чтобы
вызвать сервис.
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
В приведенной ниже реализации клиента указывается
пространство имен и SOAPAction для вызова. Хотя, как отмечено
выше, сервер на NuSOAP игнорирует эти значения, указывать их -
это хорошая тренировка, так как они могут понадобиться для
работы с большинством веб-сервисов, основанных на других
реализациях SOAP, например, .NET или Apache Axis.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/helloworld3.php');
// Проверяем, произошла ли ошибка
$err = $client->getError();
if ($err) {
// Отображаем ошибку
echo '<p><b>Ошибка конструктора
класса: ' . $err . '</b></p>';
// На этом этапе мы уже знаем, что
последующие вызовы будут неудачны.
}
// Вызываем SOAP-метод
$result = $client->call(
'hello', // имя метода
array('name' => 'Scott'), // входные параметры
'uri:helloworld', // пространство имен
'uri:helloworld/hello' // SOAPAction
);
// Странно, но такой код тоже работает:
//$result = $client->call('hello', array('name' =>
'Scott'));
// Проверим, возник ли сбой
if ($client->fault) {
echo '<p><b>Сбой:
';
print_r($result);
echo '</b></p>';
} else {
// Проверяем, произошла ли ошибка
$err = $client->getError();
if
($err) {
// Отображаем ошибку
echo '<p><b>Ошибка: ' . $err . '</b></p>';
} else {
// Отображаем результат
print_r($result);
}
}
?>
Итак, сервер использует только имя метода, которое мы
указали при его регистрации. Мы все же можем контролировать
название и тип возвращаемых данных. Вместо того, чтобы
возвращать простую строку, как в предыдущих примерах, мы можем
вернуть объект предоставляемого NuSOAP класса soapval. Вот
вариант оригинального helloworld.php, который это делает.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр сервера
$server = new soap_server;
// Регистрируем предоставляемый метод
// Замечание: в NuSOAP 0.6.3, без WSDL используется только
название метода
$server->register(
'hello', // название метода
array('name' => 'xsd:string'), // входные параметры
array('return' => 'xsd:string'), // выходные параметры
'uri:helloworld', // пространство имен
'uri:helloworld/hello', // SOAPAction
'rpc', // стиль
'encoded' // использование
);
// Определяем метод как функцию PHP
function hello($name) {
return
new soapval('return', 'xsd:string', 'Hello, ' . $name);
}
// Используем HTTP-запрос, чтобы
вызвать сервис.
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
Посмотрев на конструктор класса soapval, вы увидите что мы
также можем указать пространство имен для элемента. На самом
деле мы хотим указать пространство имен для элемента, в
котором находится результат, поэтому не надо указывать
пространство имен здесь
Ответ нового сервиса показан ниже. Заметьте, что дочерний
элемент элемента response теперь называется не soapVal, а
return.
HTTP/1.1 200 OK
Server:
Microsoft-IIS/5.0
Date: Thu, 24 Apr 2003 15:15:28
GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP Server
v0.6.3
Connection: Close
Content-Type: text/xml;
charset=UTF-8
Content-Length: 522
<?xml
version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<helloResponse>
<return xsi:type="xsd:string">Hello,
Scott</return>
</helloResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Простые типы данных
Наш пример Hello, World использовал строку как параметр и
строку как результат запроса. Но мы можем также использовать и
другие простые типы данных. PHP4 поддерживает следующие типы
данных: string, integer, float и boolean. В следующем примере
в методе сервера используются они все.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр сервера
$server = new soap_server;
// Регистрируем предоставляемый метод
$server->register('joinparams');
// Определяем метод как функцию PHP
function joinparams($s, $i, $f, $b) {
$ret = $s . '
имеет тип ' . gettype($s);
$ret .= ' ' . $i . '
имеет тип ' . gettype($i);
$ret .= ' ' . $f . '
имеет тип ' . gettype($f);
$ret .= ' ' . $b . '
имеет тип ' . gettype($b);
return new soapval('return', 'xsd:string', $ret);
}
// Используем HTTP-запрос, чтобы
вызвать сервис.
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
А вот клиент для этого сервиса.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/scalar.php');
// Вызываем SOAP-метод
$result = $client->call(
'joinparams',
array('s' => 'foo', 'i' => 21, 'f' => 43.21, 'b' => true)
);
// Проверяем, не
произошел-ли сбой
if
($client->fault) {
echo '<p><b>Сбой:
';
print_r($result);
echo '</b></p>';
} else {
// Проверяем, не возникла ли ошибка
$err = $client->getError();
if
($err) {
// Отображаем ошибку
echo '<p><b>Ошибка: ' . $err . '</b></p>';
} else {
// Отображаем результат
print_r($result);
}
}
?>
Вот что мы получим в результате:
foo имеет тип string
21
имеет тип integer
43.21 имеет тип double
1 имеет тип
integer
Результат вас удивил? Четвертый параметр, который мы
задавали как булев, сервису показался целым. Первое, что мы
должны проверить, это SOAP-запрос, который выглядит следующим
образом.
POST /phphack/scalar.php
HTTP/1.0
User-Agent: NuSOAP/0.6.3
Host:
localhost
Content-Type: text/xml;
charset="ISO-8859-1"
Content-Length: 630
SOAPAction:
""
<?xml version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<ns1:joinparams
xmlns:ns1="http://testuri.org">
<s
xsi:type="xsd:string">foo</s>
<i
xsi:type="xsd:int">21</i>
<f
xsi:type="xsd:float">43.21</f>
<b
xsi:type="xsd:boolean">1</b>
</ns1:joinparams>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Мы видим, что четвертый параметр был на самом деле задан
как булев в XML-схеме. Это одна из "причуд" NuSOAP. Когда
скалярные значения извлекаются из ответа, информация о их типе
игнорируется. Помимо булевых значений, это может плохо
повлиять на строки. Например, строка состоящая из одних цифр,
будет извлечена как целое. Это плохо в случае, когда строка
представляет собой что-то типа почтового индекса США, в
котором ведущие нули значащие. Также это плохо, если в строке
слишком много цифр, чтобы ее можно было нормально перевести в
целое. В этом случае возникнет ошибка переполнения.
Этот пример так же ведет к дискуссии о слабой типизации в
PHP. Мы не можем жестко задать типы данных для параметров
сервиса. (На самом деле, это можно сделать с помощью WSDL, но
мы до этого еще не дошли). Хотя, моё намерение состоит в том,
чтобы метод вызывался с параметрами типов string, integer,
float и boolean, и именно в таком порядке. PHP и NuSOAP не
отслеживают этого. Если типы данных для вас важны, вы должны
самостоятельно их проверять. В следующем разделе я покажу, как
это делается.
SOAP-исключения
Спецификация SOAP 1.1 определяет метод, как сообщить
клиенту об ошибках, возникших на сервере. Это называется
SOAP-исключения. Клиенты, которые мы использовали, проверяют
возникновение исключительных ситуаций и отображают их, если
они произошли. Серверный код NuSOAP будет автоматически
генерировать исключения в некоторых случаях, например, когда
клиент запрашивает несуществующий метод. Но он также может
генерировать исключения и в каких-то определенных вами
случаях.
Предположимм, что в предыдущем примере мы хотим жестко
задать типы данных входящих значений. Мы проверим типы данных
и вернем SOAP-исключение, если они некорректны.
Модифицированный код сервера приведен ниже. Заметьте, что
из-за того, как NuSOAP извлекает значения переменных из
SOAP-запроса, наш сервис должен проверять типы переменных
более свободно, чем мы могли ожидать. Например, поскольку
строковое значение '55.55' будет извлечено как float, мы
позволим строкам принимать значения float (и наш код затем
будет приводить float к типу string).
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр сервера
$server = new soap_server;
// Регистрируем
предоставляемый метод
$server->register('joinparams');
// Определяем метод как функцию PHP
function joinparams($s, $i, $f, $b) {
if
(!(is_string($s)
|| is_int($s)
|| is_float($s))) {
return new soap_fault('SERVER', '', 's должна иметь тип
string', $s);
}
if (!is_int($i)) {
return new soap_fault('SERVER', '', 'i должна иметь тип
integer', $i);
}
if (!(is_float($f)
|| is_int($f))) {
return new soap_fault('SERVER', '', 'f должна иметь тип
float', $f);
}
if (!(is_bool($b)
|| is_int($b))) {
return new soap_fault('SERVER', '', 'b должна иметь тип
boolean', $b);
}
$ret = $s . '
имеет тип ' . gettype($s);
$ret .= ' ' . $i . '
имеет тип ' . gettype($i);
$ret .= ' ' . $f . '
имеет тип ' . gettype($f);
$ret .= ' ' . $b . '
имеет тип ' . gettype($b);
return new soapval('return', 'xsd:string', $ret);
}
// Используем HTTP-запрос, чтобы
вызвать сервис.
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
Изменив клиента из предыдущего примера, чтобы он передавал
параметры не того типа, например, строку 'bar' для
целочисленной переменной $i, вы можете увидеть, что теперь
сервер возвращает клиенту SOAP-исключение. Вот как будет
выглядеть ответ сервера в этом случае.
HTTP/1.1 500 Internal Server
Error
Server: Microsoft-IIS/5.0
Date: Thu, 24 Apr 2003
16:08:50 GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP
Server v0.6.3
Connection: Close
Content-Type: text/xml;
charset=UTF-8
Content-Length: 620
<?xml
version="1.0"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>SERVER</faultcode>
<faultactor></faultactor>
<faultstring>i должна иметь тип
integer</faultstring>
<detail>
<soapVal
xsi:type="xsd:string">bar</soapVal>
</detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Создание веб-сервисов с
помощью NuSOAP, часть 2
NuSOAP - представляет из себя набор PHP-классов,
позволяющих разработчикам создавать и использовать веб-сервисы
на SOAP. NuSOAP не требует установки PHP-extensions. Текущая
стабильная версия NuSOAP (0.6.3 на момент написания этой
статьи - 23.04.2003) (уже вышла версия 0.6.7 - прим пер.),
поддерживает большое количество возможностей спецификации SOAP
1.1. Она может генерировать WSDL 1.1 и также использовать его.
Поддерживаются rpc/encoded и document/literal виды сервисов.
В то-же время необходимо учитывать, что поддержка SOAP 1.1
и WSDL 1.1 в NuSOAP реализована не полностью, по сравнению с
другими реализациями, например .NET или Apache Axis.
Эта статья является продолжением статьи "Введение в NuSOAP"
и "Создание веб-сервисов с помощью NuSOAP". В ней более
подробно описано то, как использовать массивы и структуры в
NuSOAP.
SOAP-массивы
Демонстрируя полное отсутствие воображения, в прошлых
статьях я использовал популярнейший пример "Hello, World".
Чтобы показать, как использоватть массивы я изменю этот
пример, чтобы он приветствовал не оного а несколько человек.
Индексами SOAP-массивов являются числа(т.е. массивы
не-ассоциативные), как во многих других языках
программирования, например в ФОРТРАН-е или Си. Поэтому, наш
сервис может получать доступ к элементам массива только
используя числовые индексы, а не ассоциативные ключи.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр сервиса
$server = new soap_server;
// Регистрируем предоставляемый метод
$server->register(
'hello' // название метода
);
// Определяем метод как функцию PHP
function hello($names) {
for
($i = 0; $i < count($names); $i++) {
$retval[$i]
= 'Hello, ' . $names[$i];
}
return $retval;
}
// Используем HTTP-запрос для вызова
функции
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
Все изменения в клиенте сводятся к тому, что вместо строки
он передает в качестве параметра массив имен.
<?php
// Подключаем библиотеку NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/helloworld5.php');
// Проверяем, возникла ли ошибка
$err = $client->getError();
if ($err) {
// Отображаем ошибку
echo '<p><b>Ошибка в
конструкторе: ' . $err . '</b></p>';
// Здесь мы уже знаем, что все
последующие вызовы закончатся неудачно
}
// Вызываем SOAP-метод
$names = array('Scott', 'Albert', 'Robert', 'Phyllis');
$result = $client->call(
'hello', // название метода
array('names' => $names) // входные параметры
);
// Проверяем, возник ли сбой
if ($client->fault) {
echo '<p><b>Сбой:
';
print_r($result);
echo '</b></p>';
} else {
// Проверяем, не возникла ли ошибка
$err = $client->getError();
if
($err) {
// Отображаем ошибку
echo '<p><b>Error: ' . $err . '</b></p>';
} else {
// Отображаем результат
print_r($result);
}
}
?>
Вот как выглядят запрос и ответ.
POST /phphack/helloworld5.php
HTTP/1.0
User-Agent: NuSOAP/0.6.3
Host:
localhost:80
Content-Type: text/xml;
charset="ISO-8859-1"
Content-Length: 736
SOAPAction:
""
<?xml version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<ns1:hello
xmlns:ns1="http://testuri.org">
<names
xsi:type="SOAP-ENC:Array"
SOAP-ENC:arrayType="xsd:string[4]">
<item
xsi:type="xsd:string">Scott</item>
<item
xsi:type="xsd:string">Albert</item>
<item
xsi:type="xsd:string">Robert</item>
<item
xsi:type="xsd:string">Phyllis</item>
</names>
</ns1:hello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
HTTP/1.1 200 OK
Server:
Microsoft-IIS/5.0
Date: Thu, 29 May 2003 18:46:12
GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP Server
v0.6.3
Connection: Close
Content-Type: text/xml;
charset=UTF-8
Content-Length: 743
<?xml
version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<helloResponse>
<soapVal
xsi:type="SOAP-ENC:Array"
SOAP-ENC:arrayType="xsd:string[4]">
<item
xsi:type="xsd:string">Hello,
Scott</item>
<item
xsi:type="xsd:string">Hello,
Albert</item>
<item
xsi:type="xsd:string">Hello,
Robert</item>
<item
xsi:type="xsd:string">Hello,
Phyllis</item>
</soapVal>
</helloResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP-структуры
До этого момента во всех примерах для параметров и
возвращаемых значений мы использовали простые типы и массивы
из значений простых типов. В этом разделе я покажу, как
использовать SOAP-структуры, которые более или менее
соответствуют комплексным типам XML-схем. Этот пример, еще
одна вариация на тему "Hello, World", на этот раз в качестве
параметра мы передаем структуру, которая предоставляет больше
информации о том, с кем мы здороваемся.
NuSOAP использует имеющуюся в PHP возможность работы с
ассоциативными массивами для представления SOAP-структур. В
этом примере мы используем массив, содержащий имя, возраст и
пол человека. В PHP это выглядит следующим образом:
$person = array(
'firstname' => 'Betty',
'age' => 32,
'gender' => 'female'
);
Сервис принимает этот ассоциативный массив в качестве
параметра, и возвращает следующий ассоциативный массив:
$return = array(
'greeting' => 'Hello...',
'winner' => false
);
Код самого сервиса приведен ниже:
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр сервера
$server = new soap_server;
// Регистрируем предоставляемый метод.
$server->register(
'hello' // название метода
);
// Определяем метод как функцию PHP
function hello($person) {
$greeting = 'Hello, ' . $person['firstname'] .
'. It is nice to meet a ' . $person['age'] .
' year old ' . $person['gender'] . '.';
$winner = $person['firstname'] == 'Scott';
$retval = array(
'greeting' => $greeting,
'winner' => $winner
);
return new soapval('return', 'ContestInfo', $retval, false, 'urn:MyURN');
}
// Используем запрос для вызова сервиса
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
Заметьте, что метод сервиса возвращает объект класса
soapval, для того чтобы можно было указать тип данных (в нашем
случае urn:MyURN:ContestInfo) для возвращаемого значения.
Вот клиент для этого сервиса.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/helloworld6.php');
// Проверяем, возникла ли ошибка
$err = $client->getError();
if ($err) {
// Отображаем ошибку
echo '<p><b>Ошибка в
конструкторе: ' . $err . '</b></p>';
// Здесь мы уже знаем, что все
последующие вызовы закончатся неудачно
}
// Вызываем SOAP-метод
$person = array('firstname' => 'Willi', 'age' => 22, 'gender' => 'male');
$result = $client->call(
'hello', // название метода
array('person' => new soapval('person', 'Person',
$person, false, 'urn:MyURN')) // входные параметры
);
// Проверяем, возник ли сбой
if ($client->fault) {
echo '<p><b>Сбой:
';
print_r($result);
echo '</b></p>';
} else {
// Проверяем, возникла ли ошибка
$err = $client->getError();
if
($err) {
// Отображаем ошибку
echo '<p><b>Ошибка: ' . $err . '</b></p>';
} else {
// Отображаем результат
print_r($result);
}
}
?>
Как и SOAP-сервер, этот клиент использует объект класса
soapval в качестве параметра, чтобы можно было указать XML-тип
данных для urn:MyURN:Person.
Запрос и ответ выглядят следующим образом.
POST /phphack/helloworld6.php
HTTP/1.0
User-Agent: NuSOAP/0.6.3
Host:
localhost:80
Content-Type: text/xml;
charset="ISO-8859-1"
Content-Length: 688
SOAPAction:
""
<?xml version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<ns1:hello
xmlns:ns1="http://testuri.org">
<person
xmlns:ns6678="urn:MyURN"
xsi:type="ns6678:Person">
<firstname
xsi:type="xsd:string">Willi</firstname>
<age
xsi:type="xsd:int">22</age>
<gender
xsi:type="xsd:string">male</gender>
</person>
</ns1:hello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
HTTP/1.1 200 OK
Server:
Microsoft-IIS/5.0
Date: Thu, 29 May 2003 19:50:30
GMT
X-Powered-By: PHP/4.0.6
Server: NuSOAP Server
v0.6.3
Connection: Close
Content-Type: text/xml;
charset=UTF-8
Content-Length: 679
<?xml
version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<helloResponse>
<return
xmlns:ns7437="urn:MyURN"
xsi:type="ns7437:ContestInfo">
<greeting
xsi:type="xsd:string">
Hello,
Willi. It is nice to meet a 22 year old
male.
</greeting>
<winner
xsi:type="xsd:boolean">0</winner>
</return>
</helloResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
В этом примере было продемонстрировано, как посылать
SOAP-структуру как параметр и возвращать ее как результат.
Также было показано, как указывать XML-тип данных для каждой
структуры. Без применения других инструментов, для обеспечения
взаимодействия клиента и сервиса с использованием этих типов,
им может потребоваться дополнительно "договариваться" между
собой о формате этих структур. Это одна из больших свобод,
заложенных в спецификации SOAP 1.1, но из-за нее возможны
"нестыковки" при взаимодействии. В следующей статье описано,
как использовать WSDL для описания веб-сервиса и используемых
им структур.
Создание веб-сервисов
использующих WSDL с помощью NuSOAP
NuSOAP - представляет из себя набор PHP-классов,
позволяющих разработчикам создавать и использовать веб-сервисы
на SOAP. NuSOAP не требует установки PHP-extensions. Текущая
стабильная версия NuSOAP (0.6.5 на момент написания этой
статьи - 20.10.2003) (уже вышла версия 0.6.7 - прим пер.),
поддерживает большое количество возможностей спецификации SOAP
1.1. Она может генерировать WSDL 1.1 и также использовать его.
Поддерживаются rpc/encoded и document/literal виды сервисов.
В то-же время необходимо учитывать, что поддержка SOAP 1.1
и WSDL 1.1 в NuSOAP реализована не полностью, по сравнению с
другими реализациями, например .NET или Apache Axis.
Эта статья является продолжением статьи "Введение в
NuSOAP", "Создание веб-сервисов с помощью NuSOAP" и "Создание
веб-сервисов с помощью NuSOAP, часть 2". В ней более подробно
описано то, как создавать веб-сервисы использующие WSDL с
помощью NuSOAP.
Hello, World
возвращается(снова)
Демонстрируя полное отсутствие воображения, в прошлых
статьях я использовал популярнейший пример "Hello, World". В
первой статье я показал обмен запрос-ответ между клиентом и
сервером. В этой ясатье я изменю этот пример, чтобы в нем
использовался WSDL.
WSDL-документ предоставляет метаданные, описывающие сервис.
NuSOAP позволяет автоматически генерировать WSDL для сервиса,
используя дополнительные аттрибуты и методы класса
soap_server.
Для того, чтобы сгенерировать правильный WSDL нужно сделать
несколько вещей. Информация о сервисе указывается при помощи
метода configureWSDL. Информация о каждом методе определяется
указанием дополнительных параметров методу register. Вот
пример сервиса, использующего WSDL.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр сервера
$server = new soap_server();
// Инициализируем поддержку WSDL
$server->configureWSDL('hellowsdl', 'urn:hellowsdl');
// Устанавливаем пространство имен с
префиксом tns для WSDL-схемы
$server->wsdl->schemaTargetNamespace = 'urn:hellowsdl';
// Регистрируем предоставляемый метод
$server->register('hello', // название метода
array('name' => 'xsd:string'), // входные параметры
array('return' => 'xsd:string'), // выходные параметры
'urn:hellowsdl', // пространство имен
'urn:hellowsdl#hello', // soapaction
'rpc', // стиль
'encoded', // использование
'Says hello to the
caller' // описание
);
// Определяем метод как функцию PHP
function hello($name) {
return 'Hello, ' . $name;
}
// Используем HTTP-запрос чтобы вызвать
сервис
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
Теперь
небольшой фокус. Вызовите в броузере ваш сервис, в моем случае
он расположен по адресу
http://localhost/phphack/hellowsdl.php. Вы увидите
веб-страницу, на которой будет ссылка на WSDL для сервиса, и
описания для каждого метода(в нашем случае для метода hello).
На экране должно появиться что то типа этого:
hellowsdl
View the WSDL
for the service. Click on an operation name to view it's
details.
hello
Displaying the details for the hello
operation looks something like this.
hellowsdl
View the WSDL for the service.
Click on an operation name to view it's
details.
hello
Close
Name: hello
Binding:
hellowsdlBinding
Endpoint:
http://localhost/phphack/hellowsdl.php
SoapAction:
urn:hellowsdl#hello
Style: rpc
Input:
use:
encoded
namespace:
urn:hellowsdl
encodingStyle:
http://schemas.xmlsoap.org/soap/encoding/
message:
helloRequest
parts:
name:
xsd:string
Output:
use:
encoded
namespace:
urn:hellowsdl
encodingStyle:
http://schemas.xmlsoap.org/soap/encoding/
message:
helloResponse
parts:
return:
xsd:string
Namespace: urn:hellowsdl
Transport:
http://schemas.xmlsoap.org/soap/http
Documentation: Says
hello to the caller
Таким образом, добавив всего немного кода, мы заставили
NuSOAP создать документацию для сервиса. Но это еще не все.
Нажав на ссылку "WSDL", или просто указав в броузере ссылку,
оканчивающуюся на ?wsdl (в моем случае
http://localhost/phphack/hellowsdl.php?wsdl), вы получите
следующий WSDL.
<?xml
version="1.0"?>
<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:si="http://soapinterop.org/xsd"
xmlns:tns="urn:hellowsdl"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="urn:hellowsdl">
<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>Says
hello to the
caller</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:hellowsdl#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://localhost/phphack/hellowsdl.php"/>
</port>
</service>
</definitions>
Новый клиент
Добавив несколько вызовов WSDL-методов в код сервиса мы
заставили его создавать WSDL и другую документацию. Для
сравнения, коиентская поддержка WSDL is anti-climactic, по
крайней мере для этого простого примера. Простой клиент,
показаный ниже не слишком отличается от не-WSDL клиента.
Единственное отличие в том, что в конструкторе класса
soapclient указан URL WSDL-я, а не просто сервиса.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/hellowsdl.php?wsdl', true);
// Проверяем, ни возникла ли ошибка
$err = $client->getError();
if ($err) {
// Отображаем ошибку
echo '<p><b>Ошибка в
конструкторе класса: ' . $err . '</b></p>';
// Теперь мы уже знаем, что следующие
вызовы будут неудачными
}
// Вызываем SOAP-метод
$result = $client->call('hello', array('name' => 'Scott'));
// Проверяем, ни возник-ли сбой
if ($client->fault) {
echo '<p><b>Сбой:
';
print_r($result);
echo '</b></p>';
} else {
// Проверяем, ни возникла ли ошибка
$err = $client->getError();
if
($err) {
// Оторажаем ошибку
echo '<p><b>Ошибка: ' . $err . '</b></p>';
} else {
// Отображаем результат
echo '<h2>Результат</h2><pre>';
print_r($result);
echo '</pre>';
}
}
// Отображаем запрос и
ответ
echo '<h2>Запрос</h2>';
echo '<pre>' . htmlspecialchars($client->request, ENT_QUOTES) . '</pre>';
echo '<h2>Отает</h2>';
echo '<pre>' . htmlspecialchars($client->response, ENT_QUOTES) . '</pre>';
// Отображаем отладочные сообщения
echo '<h2>Отладка</h2>';
echo '<pre>' . htmlspecialchars($client->debug_str, ENT_QUOTES) . '</pre>';
?>
Вот запрос и ответ для этой WSDL-реализации.
POST /phphack/hellowsdl.php
HTTP/1.0
User-Agent: NuSOAP/0.6.6
Host:
localhost
Content-Type: text/xml;
charset=ISO-8859-1
SOAPAction:
"urn:hellowsdl#hello"
Content-Length: 530
<?xml
version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd"
xmlns:tns="urn:hellowsdl">
<SOAP-ENV:Body>
<tns:hello>
<name
xsi:type="xsd:string">Scott</name>
</tns:hello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
HTTP/1.1 200 OK
Server:
Microsoft-IIS/5.0
Date: Tue, 21 Oct 2003 04:58:07
GMT
X-Powered-By: PHP/4.3.2
Server: NuSOAP Server
v0.6.6
Content-Type: text/xml;
charset=ISO-8859-1
Content-Length: 522
<?xml
version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd">
<SOAP-ENV:Body>
<helloResponse>
<return
xsi:type="xsd:string">Hello,
Scott</return>
</helloResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Определение новых
структур данных
Важной особенностью WSDL является то, чтоон может включать
в себя одну или несколько XML-схем, позволяя описывать
структуры данных, используемые веб-сервисом. Чтобы показать,
как это работает я добавлю поддержку WSDL в пример со
структурами из предыдущей статьи.
Внесем в код изменения, из предыдущего примера и добавим
код определяющий структуру данных Person .
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экщемпляр сервиса
$server = new soap_server();
// Инициализируем поддержку WSDL
$server->configureWSDL('hellowsdl2', 'urn:hellowsdl2');
// Устанавливаем пространство имен с
префиксом tns для WSDL-схемы
$server->wsdl->schemaTargetNamespace = 'urn:hellowsdl';
// Регистрируем используемые сервисом
структуы данных
$server->wsdl->addComplexType(
'Person',
'complexType',
'struct',
'all',
'',
array(
'firstname' => array('name' => 'firstname', 'type' => 'xsd:string'),
'age' => array('name' => 'age', 'type' => 'xsd:int'),
'gender' => array('name' => 'gender', 'type' => 'xsd:string')
)
);
$server->wsdl->addComplexType(
'SweepstakesGreeting',
'complexType',
'struct',
'all',
'',
array(
'greeting' => array('name' => 'greeting', 'type' => 'xsd:string'),
'winner' => array('name' => 'winner', 'type' => 'xsd:boolean')
)
);
// Регистрируем
предоставляемый метод
$server->register('hello', // название метода
array('person' => 'tns:Person'), // входные параметры
array('return' => 'tns:SweepstakesGreeting'), // выходные параметры
'urn:hellowsdl2', // пространство имен
'urn:hellowsdl2#hello', // soapaction
'rpc', // стиль
'encoded', // использование
'Greet a person entering the
sweepstakes' // документация
);
// Определяем метод как функцию PHP
function hello($person) {
$greeting = 'Hello, ' . $person['firstname'] .
'. It is nice to meet a ' . $person['age'] .
' year old ' . $person['gender'] . '.';
$winner = $person['firstname'] == 'Scott';
return array(
'greeting' => $greeting,
'winner' => $winner
);
}
// Используем HTTP-запрос чтобы вызвать
сервис
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$server->service($HTTP_RAW_POST_DATA);
?>
Кроме добавочного кода нужного для поддержки WSDL, немного
изменился и сам код сервиса. Теперь, когда мы используем WSDL
нам больше не нужно использовать объект soapval для указания
названия и типа данных возвращаемого значения.
<?php
// Подключаем код
NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/hellowsdl2.php?wsdl', true);
// Проверяем, ни возникла ли ошибка
$err = $client->getError();
if ($err) {
// Отображаем ошибку
echo '<p><b>Ошибка в
конструкторе класса: ' . $err . '</b></p>';
// Теперь мы уже знаем, что следующие
вызовы будут неудачными
}
// Вызываем SOAP-метод
$person = array('firstname' => 'Willi', 'age' => 22, 'gender' => 'male');
$result = $client->call('hello', array('name' => $person));
// Проверяем, ни возник-ли сбой
if ($client->fault) {
echo '<p><b>Сбой:
';
print_r($result);
echo '</b></p>';
} else {
// Проверяем, ни возникла ли ошибка
$err = $client->getError();
if
($err) {
// Оторажаем ошибку
echo '<p><b>Ошибка: ' . $err . '</b></p>';
} else {
// Отображаем результат
echo '<h2>Результат</h2><pre>';
print_r($result);
echo '</pre>';
}
}
// Отображаем запрос и
ответ
echo '<h2>Запрос</h2>';
echo '<pre>' . htmlspecialchars($client->request, ENT_QUOTES) . '</pre>';
echo '<h2>Ответ</h2>';
echo '<pre>' . htmlspecialchars($client->response, ENT_QUOTES) . '</pre>';
// Отображаем отладочные сообщения
echo '<h2>Отладка</h2>';
echo '<pre>' . htmlspecialchars($client->debug_str, ENT_QUOTES) . '</pre>';
?>
WSDL добавляет клиенту еще ону возможность. Можно
использовать прокси вместо вызова метода call. Класс proxy
создает локальное "зеркало" сервиса у которого будут
те-жеметоды и параметры, что и у оригинала. Некоторые
программисты предпочитают использовать прокси, так-как при
этом вместо вызова удаленных методов через call вызыватся
методы локального объекта. Клиент использующий прокси показан
ниже.
<?php
// Подключаем код NuSOAP
require_once('nusoap.php');
// Создаем экземпляр клиента
$client = new soapclient('http://localhost/phphack/hellowsdl2.php?wsdl', true);
// Проверяем, произошла ли ошибка
$err = $client->getError();
if ($err) {
// Отображаем ошибку
echo '<p><b>Ошибка конструктора
класса: ' . $err . '</b></p>';
// На этом тапи мы уже знаем, что
последующие вызовы будут неудачны
}
// Создаем прокси
$proxy = $client->getProxy();
// Вызываем SOAP-метод
$person = array('firstname' => 'Willi', 'age' => 22, 'gender' => 'male');
$result = $proxy->hello($person);
// Проверяем, ни возник-ли сбой
if ($proxy->fault) {
echo '<h2>Сбой</h2><pre>';
print_r($result);
echo '</pre>';
} else {
// Проверяем, произошла ли ошибка
$err = $proxy->getError();
if
($err) {
// Отображаем ошибку
echo '<h2>Ошибка</h2><pre>' . $err . '</pre>';
} else {
// Отображаем результат
echo '<h2>Результат</h2><pre>';
print_r($result);
echo '</pre>';
}
}
// Отображаем запрос и
ответ
echo '<h2>Запрос</h2>';
echo '<pre>' . htmlspecialchars($proxy->request, ENT_QUOTES) . '</pre>';
echo '<h2>Ответ</h2>';
echo '<pre>' . htmlspecialchars($proxy->response, ENT_QUOTES) . '</pre>';
// Отображаем отладочные сообщения
echo '<h2>Отладка</h2>';
echo '<pre>' . htmlspecialchars($proxy->debug_str, ENT_QUOTES) . '</pre>';
?>
Независимо от того, работаете ли вы через прокси, запрос и
ответ будут такими-же как при работе напрямую..
POST /phphack/hellowsdl2.php
HTTP/1.0
User-Agent: NuSOAP/0.6.6
Host:
localhost
Content-Type: text/xml;
charset=ISO-8859-1
SOAPAction:
"urn:hellowsdl2#hello"
Content-Length: 655
<?xml
version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd"
xmlns:tns="urn:hellowsdl2">
<SOAP-ENV:Body>
<tns:hello>
<person
xsi:type="tns:Person">
<firstname
xsi:type="xsd:string">Willi</firstname>
<age
xsi:type="xsd:int">22</age>
<gender
xsi:type="xsd:string">male</gender>
</person>
</tns:hello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
HTTP/1.1 200 OK
Server:
Microsoft-IIS/5.0
Date: Tue, 21 Oct 2003 14:21:38
GMT
X-Powered-By: PHP/4.3.2
Server: NuSOAP Server
v0.6.6
Content-Type: text/xml;
charset=ISO-8859-1
Content-Length: 687
<?xml
version="1.0"
encoding="ISO-8859-1"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
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:si="http://soapinterop.org/xsd"
xmlns:tns="urn:hellowsdl2">
<SOAP-ENV:Body>
<helloResponse>
<return
xsi:type="tns:SweepstakesGreeting">
<greeting
xsi:type="xsd:string">
Hello,
Willi. It is nice to meet a 22 year old
male.
</greeting>
<winner
xsi:type="xsd:boolean">0</winner>
</return>
</helloResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>