Практическое иcпользование NuSOAP

:

Перепечатка материалов разрешается при условии ссылки (для интернет-изданий - гиперссылки) на сайт phpclub.ru/detail

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->requestENT_QUOTES) . '</pre>';
echo 
'<h2>Ответ</h2>';
echo 
'<pre>' htmlspecialchars($client->responseENT_QUOTES) . '</pre>';
?>

Также NuSOAP предоставляет средства для просмотра отладочной информации, полученной внутри его классов. Добавим следующий код в клиента, чтобы он показывал эту дополнительную отладочную информацию. К сожалению, разбираться в том, что эта информация означает, вам придется самостоятельно.

 // Отображаем отладочные сообщения
echo '<h2>Отладка</h2>';
echo 
'<pre>' htmlspecialchars($client->debug_strENT_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 - представляет из себя набор 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 - представляет из себя набор 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'$retvalfalse'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',
$personfalse'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 для описания веб-сервиса и используемых им структур.

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->requestENT_QUOTES) . '</pre>';
echo 
'<h2>Отает</h2>';
echo 
'<pre>' htmlspecialchars($client->responseENT_QUOTES) . '</pre>';
// Отображаем отладочные сообщения
echo '<h2>Отладка</h2>';
echo 
'<pre>' htmlspecialchars($client->debug_strENT_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->requestENT_QUOTES) . '</pre>';
echo 
'<h2>Ответ</h2>';
echo 
'<pre>' htmlspecialchars($client->responseENT_QUOTES) . '</pre>';
// Отображаем отладочные сообщения
echo '<h2>Отладка</h2>';
echo 
'<pre>' htmlspecialchars($client->debug_strENT_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->requestENT_QUOTES) . '</pre>';
echo 
'<h2>Ответ</h2>';
echo 
'<pre>' htmlspecialchars($proxy->responseENT_QUOTES) . '</pre>';
// Отображаем отладочные сообщения
echo '<h2>Отладка</h2>';
echo 
'<pre>' htmlspecialchars($proxy->debug_strENT_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>


For comment register here