Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Пишем SOAP заказчик-серверное приложение на PHP

Anna | 31.05.2014 | нет комментариев

Всем здравствуй!
Так случилось, что в последнее время я стал заниматься разработкой веб-сервисов. Но сегодня топик не обо мне, а о том, как нам написать свой XML Web Service учрежденный на протоколе SOAP 1.2.

Я верю, что позже прочтения топика вы сумеете самосильно:

  • написать свою собственную серверную реализацию веб-приложения;
  • написать свою собственную клиентскую реализацию веб-приложения;
  • написать свое собственное изложение веб-обслуживания (WSDL);
  • отправлять заказчиком массивы однотипных данных на сервер.

Как вы могли додуматься, каждая магия будет твориться с применением PHP и встроенных классов SoapClient и SoapServer. В качестве кролика у нас будет выступать сервис по отправке смс-сообщений.

1 Постановка задачи

 

1.1 Границы

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

1.2 Какими данными будем меняться?

Отменно, с границами мы определились! Дальнейшим шагом, тот, что нужно сделать – решить какими данными мы будем обмениваться между сервером и заказчиком. На эту тему предлагаю длинно не мудрить и сразу для себя ответить на основные вопросы:

  • Какой минимум данных нужно посылать на сервер, Дабы отправить смс-сообщение абоненту?
  • Какой минимум данных нужно посылать с сервера, Дабы удовлетворить спросы заказчика?

Что-то мне подсказывает, что для этого нужно посылать следующее:

  • номер мобильного телефона, а также
  • текст смс-сообщения.

В тезисе, 2-х этих колляций довольно для отправки, но мне сразу представляется случай, как смс-ка с поздравлением о дне рождения приходит вам в 3 часа утра, либо 4! В данный момент я буду каждому дюже признателен за то, что про меня не позабыли! Следственно, мы также будем посылать на сервер и

  • дату отправки смс-сообщения.

Следующее, что я бы хотел отправлять на сервер, так это

  • Тип сообщения.

Данный параметр не является непременным, но он может нам дюже крепко сгодиться в случае, если стремительно потребуется сказать шефу о том, скольких из наших заказчиков мы «обрадовали» своим известием, а также нарисовать какую-нибудь прекрасную статистику на данный счет.

И все же, я что-то позабыл! Если еще немножко порефлексировать, то стоит подметить, что заказчик за раз может отправить на сервер как одно смс-сообщение, так и некоторое их число. Другими словами, в одном пакете данных может быть от одного до бесконечности сообщений.

В итоге мы получаем, что для отправки смс-сообщения нам нужны следующие данные:

  • номер мобильного телефона,
  • текст смс-сообщения,
  • время отправки смс-сообщения абоненту,
  • тип сообщения.

На 1-й вопрос мы ответили, сейчас нужно ответить на 2-й вопрос. И вероятно, я дозволю себе немножко с халтурить. Следственно, с сервера мы будем присылать только булевы данные, значение которых имеет дальнейший толк:

  • TRUE – пакет удачно дошел до сервера, прошел аутентификацию и встал в очередь для отправки смс-провайдеру
  • FALSE – во всех остальных случаях

На этом мы завершили изложение постановки задачи! И наконец-то приступим к самому увлекательному – будем пытаюсь описать его своими словами!

WSDL предуготовлен для того, Дабы наши заказчики могли типично общаться с сервером. Для этого в файле с растяжением «*.wsdl» описывается дальнейшая информация:

  • Какие пространства имен применялись,
  • Какие схемы данных применялись,
  • Какие типы сообщений веб-сервис ожидает от заказчиков,
  • Какие данные принадлежат каким процедурам веб-обслуживания,
  • Какие процедуры содержит веб-сервис,
  • Каким образом заказчик должен вызывать процедуры веб-обслуживания,
  • На какой адрес обязаны отправляться вызовы заказчика.

Как видно, данный файл и есть каждый веб-сервис. Указав в заказчике адрес WSDL-файла мы будем знать об любом веб-сервисе все! В итоге, нам не нужно безусловно ничего знать о том, где размещен сам веб-сервис. Довольно знать адрес расположения его WSDL-файла! Скоро мы узнаем, что не так ужасен SOAP как его малюют (с) русская поговорки.

3 Вступление в XML-Schema

Сейчас мы много чего знаем о то, что такое SOAP, что находится у него внутри и имеем обзорное представление о том, какой стек спецтехнологий его окружает. От того что, раньше каждого SOAP представляет собой метод взаимодействия между заказчиком и сервером, и в качестве транспорта для него применяется язык разметки XML, то в данном разделе мы немножко разберемся каким образом происходит механическая валидация данных посредством XML-схем.

Основная задачи схемы – описать конструкцию данных которые мы собираемся обрабатывать. Все данные в XML-схемах делятся на примитивные (скалярные) и коплексные (конструкции) типы. К простым типам относятся такие типы как:

  • строка,
  • число,
  • булево значение,
  • дата.

Что-то дюже примитивное, у чего внутри нет растяжений. Их антиподом являются трудные комплексные типы. Самый примитивный пример комплексного типа, тот, что приходит каждому в голову – объекты. Скажем, книга. Книга состоит из свойств: авторнаименованиеценаISBN номер и т.д. И эти свойства, в свою очередь, могут быть как примитивными типами, так и комплексными. И задача XML-схемы это описать.

Предлагаю вдалеке не ходить и написать XML-схему для нашего смс-сообщения! Ниже представлено xml-изложение смс-сообщения:

<message>
	<phone>71239876543</phone>
	<text>Тестовое сообщение</text>
	<date>2013-07-20T12:00:00</date>
	<type>12</type>
</message>

Схема нашего комплексного типа будет выглядеть дальнейшим образом:

<element name="message" type="Message" />
<complexType name="Message">
	<sequence>
		<element name="phone" type="string" />
		<element name="text" type="string" />
		<element name="date" type="dateTime" />
		<element name="type" type="decimal" />
	</sequence>
</complexType>

Эта запись читается дальнейшим образом: у нас есть переменная «message» типа «Message» и есть совокупный тип с именем «Message», тот, что состоит из последовательного комплекта элементов «phone» типа string, «text» типа string, «date» типа dateTime, «type» типа decimal. Эти типы примитивные и теснее определены в изложении схемы. Поздравляю! Мы только что написали нашу первую XML-схему!

Думаю, что значение элементов «element» и «complexType» вам стало все больше-менее ясно, следственно не будем на них огромнее останавливаться внимание и переключимся сразу же на элемент-композитор «sequence». Когда мы используем элемент-композитор «sequence» мы уведомляем о том, что элементы включенные в него обязаны неизменно располагаться в указанной в схеме последовательности, а также все из них являются непременными. Но не стоит отчаиваться! В XML-схемах есть еще два элемента-композитора: «choice» и «all». Композитор «choice» уведомляет о том, что должен быть какой-то один из перечисленных в нем элементов, а композитор «all» – любая комбинация перечисленных элементов.

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

<messageList>
	<message>
		<phone>71239876543</phone>
		<text>Тестовое сообщение 1</text>
		<date>2013-07-20T12:00:00</date>
		<type>12</type>
	</message>
	<!-- ... -->
	<message>
		<phone>71239876543</phone>
		<text>Тестовое сообщение N</text>
		<date>2013-07-20T12:00:00</date>
		<type>12</type>
	</message>
</messageList>

Схема для такого комплексного типа будет выглядеть так:

<complexType name="Message">
	<sequence>
		<element name="phone" type="string" minOccurs="1" maxOccurs="1" />
		<element name="text" type="string" minOccurs="1" maxOccurs="1" />
		<element name="date" type="dateTime" minOccurs="1" maxOccurs="1" />
		<element name="type" type="decimal" minOccurs="1" maxOccurs="1" />
	</sequence>
</complexType>

<element name="messageList" type="MessageList" />
<complexType name="MessageList">
	<sequence>
		<element minOccurs="1" maxOccurs="unbounded" name="message" type="Message"/>
	</sequence>
</complexType>

В первом блоке идет знакомое нам декларирование комплексного типа «Message». Если вы подметили, то в всяком простом типе, входящем в «Message», были добавлены новые уточняющие признаки «minOccurs» и «maxOccurs». Как не сложно додуматься из наименования, 1-й (minOccurs) информирует о том, что в данной последовательности должно быть минимум по одному элементу типа «phone», «text», «date» и «type», в то время как дальнейший (maxOccurs) признак нам декларирует, что таких элементов в нашей последовательности максимум по-одному. В итоге, когда мы пишем свои схемы для каких-либо данных, нам предоставляется широчайший выбор по их настройке!

2-й блок схемы декларирует элемент «messageList» типа «MessageList». Видно, что «MessageList» представляет собой совокупный тип, тот, что включает минимум один элемент «message», но наивысшее число таких элементов не ограничено!

На этом будем считать, что ЛикБез по схемам закончен и дальше нас ожидает еще одно не менее интересное приключение – мы будем писать свой личный WSDL!

4 Пишем свой WSDL

Вы помните о том, что WSDL и есть наш веб-сервис? Верю, что помните! Как мы его напишем, так на нем наш небольшой веб-сервис и поплывет. Следственно, предлагаю не халтурить.

Вообще, для того, Дабы у нас все работало верно нам нужно передавать заказчику WSDL-файл с верным MIME-типом. Для этого нужно настроить ваш веб-сервер соответствующим образом, а именно – установить для файлов с растяжением «*.wsdl» MIME-тип равный дальнейшей строке:

application/wsdl xml

Но на практике, я обыкновенно отправлял посредством PHP HTTP-заголовок«text/xml»:

header("Content-Type: text/xml; charset=utf-8");

и все восхитительно работало!

Хочу сразу предупредить, наш простенький веб-сервис будет иметь достаточно значительное изложение, следственно не пугайтесь, т.к. огромная часть текста является непременной водой и написав ее один раз дозволено непрерывно копировать от одного веб-обслуживания к иному!

От того что WSDL – это XML, то в самой первой строке нужно прямо об этом и написать. Корневой элемент файла неизменно должен именоваться «definitions»:

<?xml version="1.0" encoding="utf-8"?>
<definitions>
</definitions>

Обыкновенно, WSDL состоит из 4-5 основных блоков. Самый 1-й блок – определение веб-обслуживания либо другими словами – точки входа.

<?xml version="1.0" encoding="utf-8"?>
<definitions>
    <!—Определение обслуживания -->
    <service name="SmsService">
        <port name="SmsServicePort" binding="tns:SmsServiceBinding">
            <soap:address location="http://localhost:80/smsservice.php" />
        </port>
    </service>
</definitions>

Тут написано, что у нас есть сервис, тот, что именуется – «SmsService». В тезисе, все имена в WSDL-файле могут быть вами изменены на какие только пожелаете, т.к. они не играют безусловно никакой роли.

Позже этого мы объявляем о том, что в нашем веб-сервисе «SmsService» есть точка входа («port»), которая именуется «SmsServicePort». Именно в эту точку входа и будут отправляться все запросы от заказчиков к серверу. И указываем в элементе «address» ссылку на файл-обработчик, тот, что будет принимать запросы.

Позже того, как мы определили веб-сервис и указали для него точку входа – нужно привязать к нему поддерживаемые процедуры:

<?xml version="1.0" encoding="utf-8"?>
<definitions>
    <!—Формат процедур веб-обслуживания -->
    <binding name="SmsServiceBinding" type="tns:SmsServicePortType">
        <soap:binding style=”rpc” transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="sendSms">
            <soap:operation soapAction="" />
            <input>
                <soap:body use="literal" />
            </input>
            <output>
                <soap:body use="literal" />
            </output>
        </operation>
    </binding>
    <!—Определение обслуживания -->
    <service name="SmsService">
        <port name="SmsServicePort" binding="tns:SmsServiceBinding">
            <soap:address location="http://localhost:80/smsservice.php" />
        </port>
    </service>
</definitions>

Для этого перечисляется какие операции и в каком виде у будут вызываться. Т.е. для порта «SmsServicePort» определена привязка под именем «SmsServiceBinding», которая имеет тип вызова «rpc» и в качестве протокола передачи (транспорта) применяется HTTP. Т.о., мы тут указали, что будем осуществлять RPC вызов поверх HTTP. Позже этого мы описываем какие процедуры (operation) поддерживаются в веб-сервисе. Мы будем поддерживать каждого одну процедуру – «sendSms». Через эту процедуру будут отправляться на сервер наши восхитительные сообщения! Позже того, как была объявлена процедура, нужно указать в каком виде будут передаваться данные. В данном случае указано, что будут применяться типовые SOAP-конверты.

Позже этого нам нужно привязать процедуру к сообщениям:

<?xml version="1.0" encoding="utf-8"?>
<definitions>
    <!— Привязка процедуры к сообщениям -->
    <portType name="SmsServicePortType">
        <operation name="sendSms">
            <input message="tns:sendSmsRequest" />
            <output message="tns:sendSmsResponse" />
        </operation>
    </portType>
    <!—Формат процедур веб-обслуживания -->
    <binding name="SmsServiceBinding" type="tns:SmsServicePortType">
        <soap:binding style=”rpc” transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="sendSms">
            <soap:operation soapAction="" />
            <input>
                <soap:body use="literal" />
            </input>
            <output>
                <soap:body use="literal" />
            </output>
        </operation>
    </binding>
    <!— Определение обслуживания -->
    <service name="SmsService">
        <port name="SmsServicePort" binding="tns:SmsServiceBinding">
            <soap:address location="http://localhost:80/smsservice.php" />
        </port>
    </service>
</definitions>

Для этого мы указываем, что наша привязка («binding») имеет тип «SmsServicePortType» и в элементе «portType» с одноименным типу именем указываем привязку процедур к сообщениям. И так, входящее сообщение (от заказчика к серверу) будет именоваться «sendSmsRequest», а исходящее (от сервера к заказчику) «sendSmsResponse». Как и все имена в WSDL, имена входящих и исходящих сообщения – произвольные.

Сейчас нам нужно описать сами сообщения, т.е. входящие и исходящие:

<?xml version="1.0" encoding="utf-8"?>
<definitions>
    <!-- Сообщения процедуры sendSms -->
    <message name="sendSmsRequest">
        <part name="Request" element="tns:Request" />
    </message>
    <message name="sendSmsResponse">
        <part name="Response" element="tns:Response" />
    </message>
    <!-- Привязка процедуры к сообщениям -->
    <portType name="SmsServicePortType">
        <operation name="sendSms">
            <input message="tns:sendSmsRequest" />
            <output message="tns:sendSmsResponse" />
        </operation>
    </portType>
    <!-- Формат процедур веб-обслуживания -->
    <binding name="SmsServiceBinding" type="tns:SmsServicePortType">
        <soap:binding style=”rpc” transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="sendSms">
            <soap:operation soapAction="" />
            <input>
                <soap:body use="literal" />
            </input>
            <output>
                <soap:body use="literal" />
            </output>
        </operation>
    </binding>
    <!-- Определение обслуживания -->
    <service name="SmsService">
        <port name="SmsServicePort" binding="tns:SmsServiceBinding">
            <soap:address location="http://localhost:80/smsservice.php" />
        </port>
    </service>
</definitions>

Для этого мы добавляем элементы «message» с именами «sendSmsRequest» и «sendSmsResponse» соответственно. В них мы указываем, что на вход должен прийти конверт, конструкция которого соответствует типу данных «Request». Позже чего с сервера возвращается конверт содержащий тип данных – «Response».

Сейчас нужно сделать самую малость – добавить изложение данных типов в наш WSDL-файл! И как вы думаете, как описываются в WSDL входящие и исходящие данные? Думаю, что вы теснее все давным-давно осознали и сказали сами себе, что при помощи XML-схем! И вы будете безусловно правы!

<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
             xmlns:tns="http://localhost/"
             xmlns:xs="http://www.w3.org/2001/XMLSchema"
             xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
             xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
             name="SmsWsdl"
             xmlns="http://schemas.xmlsoap.org/wsdl/">
    <types>
        <xs:schema xmlns:tns="http://schemas.xmlsoap.org/wsdl/"
                   xmlns="http://www.w3.org/2001/XMLSchema"
                   xmlns:xs="http://www.w3.org/2001/XMLSchema"
                   elementFormDefault="qualified"
                   targetNamespace="http://localhost/">
            <complexType name="Message">
                <sequence>
                    <element name="phone" type="string" minOccurs="1" maxOccurs="1" />
                    <element name="text" type="string" minOccurs="1" maxOccurs="1" />
                    <element name="date" type="dateTime" minOccurs="1" maxOccurs="1" />
                    <element name="type" type="decimal" minOccurs="1" maxOccurs="1" />
                </sequence>
            </complexType>
            <complexType name="MessageList">
<sequence>
                <element minOccurs="1" maxOccurs="unbounded" name="message" type="Message"/>
                </sequence>
            </complexType>
            <element name="Request">
                <element name="messageList" type="MessageList" />
            </element>
            <element name="Response">
                <complexType>
                    <sequence>
                        <element name="status" type="boolean" />
                    </sequence>
                </complexType>
            </element>
        </xs:schema>
    </types>
    <!-- Сообщения процедуры sendSms -->
    <message name="sendSmsRequest">
        <part name="Request" element="tns:Request" />
    </message>
    <message name="sendSmsResponse">
        <part name="Response" element="tns:Response" />
    </message>
    <!-- Привязка процедуры к сообщениям -->
    <portType name="SmsServicePortType">
        <operation name="sendSms">
            <input message="tns:sendSmsRequest" />
            <output message="tns:sendSmsResponse" />
        </operation>
    </portType>
    <!-- Формат процедур веб-обслуживания -->
    <binding name="SmsServiceBinding" type="tns:SmsServicePortType">
        <soap:binding style=”rpc” transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="sendSms">
            <soap:operation soapAction="" />
            <input>
                <soap:body use="literal" />
            </input>
            <output>
                <soap:body use="literal" />
            </output>
        </operation>
    </binding>
    <!-- Определение обслуживания -->
    <service name="SmsService">
        <port name="SmsServicePort" binding="tns:SmsServiceBinding">
            <soap:address location="http://localhost:80/smsservice.php" />
        </port>
    </service>
</definitions>

Дозволено нас поздравить! Наш 1-й WSDL был написан! И мы еще на один шаг приблизились к достижению поставленной цели.
Дальше мы разберемся с тем, что нам предоставляет PHP для разработки собственных распределенных приложений.

5 Наш 1-й SOAP-сервер

Ранее я писал, что для создания SOAP-сервера на PHP мы будем применять встроенный класс SoapServer. Для того, Дабы все последующие действия происходили также как и у меня, вам потребоваться немножко подкрутить свой PHP. Если быть еще вернее, то нужно удостовериться, что у вас установлено растяжение «php-soap». Как его поставить на ваш веб-сервере отменнее каждого прочитать на официальном сайте PHP (см. список литературы).

Позже того, как все было установлено и настроено нам нужно будет сделать в корневой папке вашего хостинга файл «smsservice.php» со дальнейшим оглавлением:

<?php
/**
 * smsservice.php
 */
header("Content-Type: text/xml; charset=utf-8");
header('Cache-Control: no-store, no-cache');
header('Expires: '.date('r'));

/**
 * Пути по-умолчанию для поиска файлов
 */
set_include_path(get_include_path()
    .PATH_SEPARATOR.'classes'
    .PATH_SEPARATOR.'objects');

/**
 * Путь к конфигурационному файлу
 */
const CONF_NAME = "config.ini";

/**
 ** Функция для автозагрузки нужных классов
 */
function __autoload($class_name){
    include $class_name.'.class.php';
}

ini_set("soap.wsdl_cache_enabled", "0"); // отключаем кеширование WSDL-файла для тестирования

//Создаем новейший SOAP-сервер
$server = new SoapServer("http://{$_SERVER['HTTP_HOST']}/smsservice.wsdl.php");
//Регистрируем класс обработчик
$server->setClass("SoapSmsGateWay");
//Запускаем сервер
$server->handle();

То, что находится выше строчки с функцией «ini_set», верю, что пояснять не нужно. Т.к. там определяется какие HTTP-заголовки мы будем отправлять с сервера заказчику и настраивается окружение. В строчке с «ini_set» мы отключаем кеширование WSDL-файла для того, Дабы наши метаморфозы в нем сразу же вступали в действие на заказчике.

Сейчас мы подошли к серверу! Как видим, каждый SOAP-сервер занимает каждого лишь три строки! В первой строке мы создаем новейший экземпляр объекта SoapServer и передаем ему в конструктор адрес нашего WSDL-изложения веб-обслуживания. Сейчас мы знаем, что он будет располагаться в корне хостинга в файле с говорящим именем «smsservice.wsdl.php». Во 2-й строке мы уведомляем SOAP-серверу какой класс нужно дергать для того, Дабы обработать поступивший с заказчика конверт и воротить конверт с результатом. Как вы могли додуматься, именно в этом классе будет описан наш исключительный способ sendSms. В третьей строке мы запускаем сервер! Все, наш сервер готов! С чем я нас всех и поздравляю!

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

<?php
/**
 * smsservice.wsdl.php
 */
header("Content-Type: text/xml; charset=utf-8");
echo "<?xml version="1.0" encoding="utf-8"?>";
?>
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
             xmlns:tns="http://<?=$_SERVER['HTTP_HOST']?>/"
             xmlns:xs="http://www.w3.org/2001/XMLSchema"
             xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
             xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
             name="SmsWsdl"
             xmlns="http://schemas.xmlsoap.org/wsdl/">
    <types>
        <xs:schema elementFormDefault="qualified"
                   xmlns:tns="http://schemas.xmlsoap.org/wsdl/"
                   xmlns:xs="http://www.w3.org/2001/XMLSchema"
                   targetNamespace="http://<?=$_SERVER['HTTP_HOST']?>/">
            <xs:complexType name="Message">
                <xs:sequence>
                    <xs:element name="phone" type="xs:string" minOccurs="1" maxOccurs="1" />
                    <xs:element name="text" type="xs:string" minOccurs="1" maxOccurs="1" />
                    <xs:element name="date" type="xs:dateTime" minOccurs="1" maxOccurs="1" />
                    <xs:element name="type" type="xs:decimal" minOccurs="1" maxOccurs="1" />
                </xs:sequence>
            </xs:complexType>
            <xs:complexType name="MessageList">
                <xs:sequence>
                    <xs:element name="message" type="Message" minOccurs="1" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:complexType>
            <xs:element name="Request">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="messageList" type="MessageList" />
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
            <xs:element name="Response">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="status" type="xs:boolean" />
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        </xs:schema>
    </types>

    <!-- Сообщения процедуры sendSms -->
    <message name="sendSmsRequest">
        <part name="Request" element="tns:Request" />
    </message>
    <message name="sendSmsResponse">
        <part name="Response" element="tns:Response" />
    </message>

    <!-- Привязка процедуры к сообщениям -->
    <portType name="SmsServicePortType">
        <operation name="sendSms">
            <input message="tns:sendSmsRequest" />
            <output message="tns:sendSmsResponse" />
        </operation>
    </portType>

    <!-- Формат процедур веб-обслуживания -->
    <binding name="SmsServiceBinding" type="tns:SmsServicePortType">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
        <operation name="sendSms">
            <soap:operation soapAction="" />
            <input>
            <soap:body use="literal" />
            </input>
            <output>
                <soap:body use="literal" />
            </output>
        </operation>
    </binding>

    <!-- Определение обслуживания -->
    <service name="SmsService">
        <port name="SmsServicePort" binding="tns:SmsServiceBinding">
            <soap:address location="http://<?=$_SERVER['HTTP_HOST']?>/smsservice.php" />
        </port>
    </service>
</definitions>

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

6 SOAP-заказчик на подходе

Раньше каждого нам нужно сделать файл, в котором будем писать заказчик. Как обыкновенно, мы его сделаем в корне хоста и назовем «client.php», а внутри напишем следующее:

<?php
/**
 * /client.php
 */
header("Content-Type: text/html; charset=utf-8");
header('Cache-Control: no-store, no-cache');
header('Expires: '.date('r'));

/**
 * Пути по-умолчанию для поиска файлов
 */
set_include_path(get_include_path()
    .PATH_SEPARATOR.'classes'
    .PATH_SEPARATOR.'objects');

/**
 ** Функция для автозагрузки нужных классов
 */
function __autoload($class_name){
    include $class_name.'.class.php';
}

ini_set('display_errors', 1);
error_reporting(E_ALL & ~E_NOTICE);

// Заготовки объектов
class Message{
    public $phone;
    public $text;
    public $date;
    public $type;
}

class MessageList{
    public $message;
}

class Request{
    public $messageList;
}

// создаем объект для отправки на сервер
$req = new Request();
$req->messageList = new MessageList();
$req->messageList->message = new Message();
$req->messageList->message->phone = '79871234567';
$req->messageList->message->text = 'Тестовое сообщение 1';
$req->messageList->message->date = '2013-07-21T15:00:00.26';
$req->messageList->message->type = 15;

$client = new SoapClient(   "http://{$_SERVER['HTTP_HOST']}/smsservice.wsdl.php",
                            array( 'soap_version' => SOAP_1_2));
var_dump($client->sendSms($req));

Опишем наши объекты. Когда мы писали WSDL в нем для входящего на сервер конверта описывались три сущности: RequestMessageList и Message. Соответственно классы RequestMessageList и Message являются отражениями этих сущностей в нашем PHP-скрипте.

Позже того, как мы определили объекты, нам нужно сделать объект ($req), тот, что будем отправлять на сервер. Позже чего идут две самые сокровенные для нас строки! Наш SOAP-заказчик! Верите либо нет, но этого довольно для того, Дабы на наш сервер начали сыпаться сообщения от заказчика, а также для того, Дабы наш сервер удачно их принимал и обрабатывал! В первой из них мы создаем экземпляр класса SoapClient и передаем в его конструктор адрес расположения WSDL-файла, а в параметрах очевидно указываем, что трудиться мы будем по протоколу SOAP версии 1.2. В дальнейшей строке мы вызываем способ sendSmsобъекта $client и сразу же выводим в браузере итог.
Давайте запусти и посмотрим что-же у нас наконец-то получилось!

Мне с сервера возвратился дальнейший объект:

object(stdClass)[5]
  public 'status' => boolean true

И это восхитительно, т.к. сейчас мы верно знаем о том, что наш сервер работает и не легко работает, но еще и может возвращать на заказчик какие-то значения!

Сейчас посмотрим на лог, тот, что мы предусмотрительно ведем на серверной стороне! В первой его части мы видим необработанные данные, которые поступили на сервер:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://sms-service/">
	<env:Body>
		<ns1:Request>
			<ns1:messageList>
				<message>
					<phone>79871234567</phone>
					<text>Тестовое сообщение 1</text>
					<date>2013-07-21T15:00:00.26</date>
					<type>15</type>
				</message>
			</ns1:messageList>
		</ns1:Request>
	</env:Body>
</env:Envelope>

Это и есть конверт. Сейчас вы знаете как он выглядит! Но непрерывно на него любоваться нам вряд ли будет увлекательно, следственно давайте десереализуем объект из лог-файла и посмотрим все ли у нас отлично:

object(stdClass)[4]
  public 'messageList' => 
    object(stdClass)[5]
      public 'message' => 
        object(stdClass)[6]
          public 'phone' => string '79871234567' (length=11)
          public 'text' => string 'Тестовое сообщение 1' (length=37)
          public 'date' => string '2013-07-21T15:00:00.26' (length=22)
          public 'type' => string '15' (length=2)

Как видим, объект десериализовался верно, с чем я нас всех хочу поздравить! Дальше нас ожидает что-то больше увлекательно! А именно – мы будем отправлять заказчиком на сервер не одно смс-сообщение, а целую пачку (если быть вернее, то целых три)!

7 Отправляем трудные объекты

Давайте подумаем над тем, как же нам передать целую пачку сообщений на сервер в одном пакете? Вероятно, самым простым методом будет организация массива внутри элемента messageList! Давайте это сделаем:

// создаем объект для отправки на сервер
$req = new Request();
$req->messageList = new MessageList();

$msg1 = new Message();
$msg1->phone = '79871234567';
$msg1->text = 'Тестовое сообщение 1';
$msg1->date = '2013-07-21T15:00:00.26';
$msg1->type = 15;

$msg2 = new Message();
$msg2->phone = '79871234567';
$msg2->text = 'Тестовое сообщение 2';
$msg2->date = '2014-08-22T16:01:10';
$msg2->type = 16;

$msg3 = new Message();
$msg3->phone = '79871234567';
$msg3->text = 'Тестовое сообщение 3';
$msg3->date = '2014-08-22T16:01:10';
$msg3->type = 17;

$req->messageList->message[] = $msg1;
$req->messageList->message[] = $msg2;
$req->messageList->message[] = $msg3;

В наших логах числится, что пришел дальнейший пакет от заказчика:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:enc="http://www.w3.org/2003/05/soap-encoding" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://sms-service/">
	<env:Body>
		<ns1:Request>
			<ns1:messageList>
				<message>
					<SOAP-ENC:Struct>
						<phone>79871234567</phone>
						<text>Тестовое сообщение 1</text>
						<date>2013-07-21T15:00:00.26</date>
						<type>15</type>
					</SOAP-ENC:Struct>
					<SOAP-ENC:Struct>
						<phone>79871234567</phone>
						<text>Тестовое сообщение 2</text>
						<date>2014-08-22T16:01:10</date>
						<type>16</type>
					</SOAP-ENC:Struct>
					<SOAP-ENC:Struct>
						<phone>79871234567</phone>
						<text>Тестовое сообщение 3</text>
						<date>2014-08-22T16:01:10</date>
						<type>17</type>
					</SOAP-ENC:Struct>
				</message>
			</ns1:messageList>
		</ns1:Request>
	</env:Body>
</env:Envelope>

Что за галиматья, скажете вы? И будете правы в некотором смысле, т.к. только что мы узнали о том, что какой объект ушел от заказчика, то безусловно в том же виде он пришел к нам на сервер в виде конверта. Правда, смс-сообщения сериализовались в XML не так, как нам было нужно – они обязаны были быть обернуты в элементы message, а не в Struct. Сейчас посмотрим в каком виде приходит такой объект в способ sendSms:

object(stdClass)[6]
  public 'messageList' => 
    object(stdClass)[7]
      public 'message' => 
        object(stdClass)[8]
          public 'Struct' => 
            array (size=3)
              0 => 
                object(stdClass)[9]
                  public 'phone' => string '79871234567' (length=11)
                  public 'text' => string 'Тестовое сообщение 1' (length=37)
                  public 'date' => string '2013-07-21T15:00:00.26' (length=22)
                  public 'type' => string '15' (length=2)
              1 => 
                object(stdClass)[10]
                  public 'phone' => string '79871234567' (length=11)
                  public 'text' => string 'Тестовое сообщение 2' (length=37)
                  public 'date' => string '2014-08-22T16:01:10' (length=19)
                  public 'type' => string '16' (length=2)
              2 => 
                object(stdClass)[11]
                  public 'phone' => string '79871234567' (length=11)
                  public 'text' => string 'Тестовое сообщение 3' (length=37)
                  public 'date' => string '2014-08-22T16:01:10' (length=19)
                  public 'type' => string '17' (length=2)

Что нам дает это познание? Только то, что выбранный нами путь не является правильным и мы не получили результата на вопрос – «Как нам на сервере получить положительную конструкцию данных?». Но я предлагаю не отчаиваться и испробовать привести наш массив к типу объект:

$req->messageList->message = (object)$req->messageList->message;

В этом случае, нам придет теснее иной конверт:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://sms-service/">
	<env:Body>
		<ns1:Request>
			<ns1:messageList>
				<message>
					<BOGUS>
						<phone>79871234567</phone>
						<text>Тестовое сообщение 1</text>
						<date>2013-07-21T15:00:00.26</date>
						<type>15</type>
					</BOGUS>
					<BOGUS>
						<phone>79871234567</phone>
						<text>Тестовое сообщение 2</text>
						<date>2014-08-22T16:01:10</date>
						<type>16</type>
					</BOGUS>
					<BOGUS>
						<phone>79871234567</phone>
						<text>Тестовое сообщение 3</text>
						<date>2014-08-22T16:01:10</date>
						<type>17</type>
					</BOGUS>
				</message>
			</ns1:messageList>
		</ns1:Request>
	</env:Body>
</env:Envelope>

Пришедший в способ sendSms объект имеет следующую конструкцию:

object(stdClass)[7]
  public 'messageList' => 
    object(stdClass)[8]
      public 'message' => 
        object(stdClass)[9]
          public 'BOGUS' => 
            array (size=3)
              0 => 
                object(stdClass)[10]
                  public 'phone' => string '79871234567' (length=11)
                  public 'text' => string 'Тестовое сообщение 1' (length=37)
                  public 'date' => string '2013-07-21T15:00:00.26' (length=22)
                  public 'type' => string '15' (length=2)
              1 => 
                object(stdClass)[11]
                  public 'phone' => string '79871234567' (length=11)
                  public 'text' => string 'Тестовое сообщение 2' (length=37)
                  public 'date' => string '2014-08-22T16:01:10' (length=19)
                  public 'type' => string '16' (length=2)
              2 => 
                object(stdClass)[12]
                  public 'phone' => string '79871234567' (length=11)
                  public 'text' => string 'Тестовое сообщение 3' (length=37)
                  public 'date' => string '2014-08-22T16:01:10' (length=19)
                  public 'type' => string '17' (length=2)

Как по мне, то «от перемены мест слагаемых – сумма не меняется» (с). Что BOGUS, что Struct – цель нами до сих пор не достигнута! А для ее достижения нам нужно сделать так, Дабы взамен этих непонятных наименований отображалось наше родное message. Но как этого добиться, автору пока не вестимо. Следственно исключительное, что мы можем сделать – избавить от лишнего контейнера. Другими словами, мы теперь сделаем так, Дабы взамен message стал BOGUS! Для этого изменим объект дальнейшим образом:

// создаем объект для отправки на сервер
$req = new Request();

$msg1 = new Message();
$msg1->phone = '79871234567';
$msg1->text = 'Тестовое сообщение 1';
$msg1->date = '2013-07-21T15:00:00.26';
$msg1->type = 15;

$msg2 = new Message();
$msg2->phone = '79871234567';
$msg2->text = 'Тестовое сообщение 2';
$msg2->date = '2014-08-22T16:01:10';
$msg2->type = 16;

$msg3 = new Message();
$msg3->phone = '79871234567';
$msg3->text = 'Тестовое сообщение 3';
$msg3->date = '2014-08-22T16:01:10';
$msg3->type = 17;

$req->messageList[] = $msg1;
$req->messageList[] = $msg2;
$req->messageList[] = $msg3;
$req->messageList = (object)$req->messageList;

Внезапно нам повезет и из схемы подтянется положительное наименование? Для этого посмотрим на пришедший конверт:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://sms-service/">
	<env:Body>
		<ns1:Request>
			<ns1:messageList>
				<BOGUS>
					<phone>79871234567</phone>
					<text>Тестовое сообщение 1</text>
					<date>2013-07-21T15:00:00.26</date>
					<type>15</type>
				</BOGUS>
				<BOGUS>
					<phone>79871234567</phone>
					<text>Тестовое сообщение 2</text>
					<date>2014-08-22T16:01:10</date>
					<type>16</type>
				</BOGUS>
				<BOGUS>
					<phone>79871234567</phone>
					<text>Тестовое сообщение 3</text>
					<date>2014-08-22T16:01:10</date>
					<type>17</type>
				</BOGUS>
			</ns1:messageList>
		</ns1:Request>
	</env:Body>
</env:Envelope>

Да, Дива не случилось! BOGUS – не поборем! Пришедший в sendSms объект в этом случае будет выглядеть дальнейшим образом:

object(stdClass)[6]
  public 'messageList' => 
    object(stdClass)[7]
      public 'BOGUS' => 
        array (size=3)
          0 => 
            object(stdClass)[8]
              public 'phone' => string '79871234567' (length=11)
              public 'text' => string 'Тестовое сообщение 1' (length=37)
              public 'date' => string '2013-07-21T15:00:00.26' (length=22)
              public 'type' => string '15' (length=2)
          1 => 
            object(stdClass)[9]
              public 'phone' => string '79871234567' (length=11)
              public 'text' => string 'Тестовое сообщение 2' (length=37)
              public 'date' => string '2014-08-22T16:01:10' (length=19)
              public 'type' => string '16' (length=2)
          2 => 
            object(stdClass)[10]
              public 'phone' => string '79871234567' (length=11)
              public 'text' => string 'Тестовое сообщение 3' (length=37)
              public 'date' => string '2014-08-22T16:01:10' (length=19)
              public 'type' => string '17' (length=2)

Как говорится – «Примерно»! На этой (немножко грустной) ноте предлагаю потихонечку закругляться и сделать некоторые для себя итоги.

8 Завершение

Наконец-то мы добрались сюда! Давайте определимся с тем, что вы сейчас умеете делать:

  • вам по силам написать нужный для вашего веб-обслуживания WSDL-файл;
  • вы без каждых задач можете написать свой личный заказчик способный общаться с сервером по протоколу SOAP;
  • вы можете написать свой личный сервер общающийся с окружающим миром по SOAP;
  • вы можете отправлять массивы однотипных объектов на сервер со своего заказчика (с некоторыми ограничениями).

Также, мы сделали для себя некоторые открытия в ходе нашего небольшого изыскания:

  • нативный класс SoapClient не может верно сериализовывать однотипные конструкции данных в XML;
  • при сериализации массива в XML он создает ненужный элемент с именем Struct;
  • при сериализации объекта в XML он создает ненужный элемент с именем BOGUS;
  • BOGUS меньшее зло чем Struct из-за того, что конверт получается суперкомпактнее (не добавляются лишние namespace’ы в XML заголовке конверта);
  • к сожалению, класс SoapServer механически не валидирует данные конверта нашей XML-схемой (допустимо, и другие сервера этого не делают).

9 Список литературы

 

P.S. Автор статьи хотел осветить авторизацию пакетов посредством встроенных в SOAP вероятностей, но ему не удалось этого сделать по средством классов SoapServer и SoapClient. Следственно, если у вас есть правильный навык применения встроенной в SOAP авторизации посредством PHP, то умоляю об этом написать в комментариях к статье, либо мне в личку :)

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB