Пишем свой web-сервис на PHP и XML-RPC
Автор: Harry Fuecks
Перевод: Данил Миронов
Источник: detail.phpclub.net
Если вы следили
за новостями на данную тему, у вас могло сложиться
впечатление, что это всё - технологии высшего порядка, для
больших компаний с немалым же бюджетом. Если вы так подумали,
то вы ошиблись.
Принцип работы web-сервисов достаточно прост и в нашей
сегодняшней статье мы довольно подробно рассмотрим все
задействованные в этом технологии. Затем, с помощью нашего
общего друга PHP мы напишем
свой первый web-сервис.
Статья написана с расчётом на то, что читатели хоть немного
знакомы с PHP, MySQL и XML, если вы не уверены в ваших
познаниях, рекомендуем подчитать Building your own Database Driven Website using
PHP & MySQL и XML - An Introduction.
Итак, что мы можем вам предложить:
- Основы технологии web-сервисов: для начала
краткий обзор принципов работы web-сервисов.
- Введение в XML-RPC: затем поговорим об
использовании XML в обмене данными между системами и
приведём наши примеры в соответствие с понятиями, описанными
в статье Кевина.
- PHP-разработки XML-RPC: конечно, затем мы
проведём обзор некоторых PHP-разработок Open Source для
XML-RPC: этот код вы сможете использовать для разработки
ваших собственных web-сервисов или для доступа к
web-сервисам с вашего сайта.
- Ваш первый web-сервис: если вам не
терпится сразу заняться делом, то вам сюда. Здесь мы возьмём
одну из разработок, описанных в предыдущем разделе, и
напишем в PHP свой web-сервис.
- На что мы способны с XML-RPC: спрашиваете,
что вам делать дальше? Мы предложим вам несколько идей
использования web-сервисов и XML-RPC.
Основы технологии
web-сервисов
Первое, что вам необходимо усвоить, - это то, что концепция
web-сервисов совсем не нова. Если вы когда-либо использовали
фиды RSS для получения новостей из другого сайта и размещения
их на своём, то уже имеете неплохое представление о том, как
работают web-сервисы (см. статью Кевина Йанка PHP and XML: Parsing RSS 1.0).
Web-сервисы применяются для обмена данными между сервером и
клиентом; формат XML используется для "упаковывания" запросов
и данных в целях взаимопонимания между обоими участниками
общения. И сервер и клиент могут быть web-серверами или любыми
другими электронными устройствами, которые вам
понадобятся.
С точки зрения работы сети обмен данными с web-сервисами
проходит по TCP через 80 порт и с использованием стандартного
метода POST протокола http. Иными словами, работа web-сервисов
- это почти отправка html-форм из браузера (методом POST) и
получение странички в ответ. Единственная существенная разница
- это то, что вместо HTML web-сервисы используют XML. А это
всё означает, что web-сервисы доступны по всей Сети и через
firewall-ы проходят так же, как обычные web-странички. Обмен
данными проходит на уровне пакетирования.
Кроме обмена данными вам также может понадобиться
информация, описывающая интерфейс сервиса (или API -
программный интерфейс приложения). Эта информация делает
web-сервис полезным для всего остального Интернета, позволяя
другим разработчикам создавать приложения для доступа к вашему
web-сервису. Этот называется уровнем описания, и
стандарт WSDL (Web Services Description Language - Язык
Описания Web-Сервисов), который этим займётся, в настоящее
время разрабатывается.
На верхнем уровне располагается информация о типе и
свойствах web-сервиса как такового (можно сравнить с
META-тэгами в HTML) для категоризации и размещения в каталогах
web-сервисов. Это уровень поиска, который находится под
управлением реестра UDDI (Universal Description, Discovery,
and Integration - Универсальное Описание, Поиск и
Взаимодействие).
И уровень описания, и уровень поиска представляют собой
обычный XML, управляемый специальным форматом для выявления
релевантной информации обо всех web-сервисах в Инете.
Возможно, причиной возросшего ажиотажа вокруг web-сервисов
(ну, не считая поддержки благодаря пристрастиям Microsoft и
IBM) является как раз разработка вышеупомянутых стандартов
WSDL и UDDI. С их помощью будет возможна общая индексация всех
web-сервисов Интернета и поддержка средствами разработки
простого и предсказуемого доступа к сервисам. Не мешает
напомнить, однако, что всё, что сегодня делают web-сервисы (с
точки зрения обмена данными), было возможно и 5, а то и 10 лет
назад с применением HTTP и XML любого существующего или
разработанного вами формата (ближайший пример тому - фиды
RSS). Единственная "горячая новость" на сегодня - это то, что
теперь разработать и распространить web-сервис намного проще,
чем было тогда.
В любом случае, если вы ищете объективную и свежую
информацию по разработке web-сервисов, читайте XML
Hack(Узрев слово оное да не возликует бес в душе твоей,
ибо "hack" по-англицки есть не "систему взломать" но многия
другия; здесь же больше "В ладах с XML"// прим. толмача
:-)).
Прочую теорию мы пропустим, но достаточно сказать, что в
нашей статье мы будем рассматривать уровень
пакетирования, то есть способы построения web-сервиса и
доступа к нему.
Введение в XML-RPC
В своей статье "Web Services Demystified" Кевин пишет о
стандарте SOAP, который применяется для "упаковывания"
данных для обмена оными между двумя системами. В настоящее
время технологии SOAP пытаются придать статус W3C-стандарта
для web-сервисов, и если учесть, что этому процессу помогают
Microsoft и IBM, есть все основания полагать, что заветный
"Одобрям" будет скоро поставлен.
Однако для реализации тех же задач существует и другой
стандарт: XML-RPC. Он
был разработан около 1998, и хотя и не стал W3C-стандартом,
нашёл себе широкое применение и мощную поддержку со стороны
Open Source Projects. XML-RPC был разработан корпорацией Useful совместно с Microsoft и во многих
отношениях может считаться предшественником SOAP, хотя шествие
своё ещё не закончил.
Как мы убедимся по мере прочтения этой статьи, основная
причина популярности XML-RPC - его простота. Это становится
очевидным уже при одном взгляде на спецификацию XML-RPC (1 500 слов) в
сравнении с талмудом по SOAP (11 000 слов и продолжает расти).
Ещё одна хорошая новость: XML-RPC поддерживается в PHP и в разработках почти на всех языках, которые
только можно припомнить.
Для детального сравнения стандартов XML-RPC и SOAP,
рекомендуем вам почитать XML-RPC vs. SOAP. А если вкратце, то SOAP
разрабатывался чтобы залатать дыры в XML-RPC, представляя
собой тем самым разработку корпоративного уровня для обмена
данными между системами. К примеру, многомерные массивы проще
передавать через SOAP, нежели чем через XML-RPC. Но это вовсе
не значит, что XML-RPC - это не прикольно и использовать его
не стОит. Особенно если учесть, что XML-RPC - это стабильный
стандарт, а SOAP в скором времени может ожидать серьёзная
переработка.
Вы можете задаться вопросом: а стОит ли так напрягаться и
изучать стандарт, который вот-вот будет вытеснен конкурирующей
разработкой? Не волнуйтесь: с помощью XSLT вы без проблем сможете преобразовать SOAP-запрос в запрос XML-RPC,
что и поможет вашему XML-RPC выжить в этой интернетовской
грязи, где каждый убить готов ради этого обмылка SOAP. (прошу
прощения, но один каламбур я себе таки позволил) (// можешь не
извиняться, дружище, я твой каламбур всё равно в переводе
похерил :) - прим. перев.)
Итак, как же работает XML-RPC? Как вы поймёте по мере
прочтения этой статьи, вам не так уж и важно много знать о
принципах работы стандарта XML-RPC, чтобы его использовать. Но подобной информации полно, рекомендую
ознакомиться с XML-RPC для чайников. Сейчас мы вкратце
рассмотрим, как работает XML-RPC, и потом научимся применять
его в своей работе.
Две основных составляющих XML-RPC-"сообщения" - это методы и параметры. Методам мы можем поставить в
примерное соответствие функции, которые мы определяем в PHP, а
параметрам - переменные, которые мы нашим функциям передаём.
Параметры могут быть разного типа: строки, целые числа,
массивы, - всё очень похоже на то, к чему мы уже привыкли с
PHP. Кроме того, в XML-RPC определены другие тэги для
различных целей, таких как обработка ошибок, но мы оставим вам
возможность всё это узнать из спецификаций, ибо, в большинстве случаев,
беспокоиться нам об этом не придётся.
XML-RPC-"общение" между двумя системами начинается с запроса (request) от XML-RPC-клиента, которому сервер
высылает ответ (response). Запрос содержит название
метода, и, возможно, необходимые параметры. В ответе мы
получаем набор параметров, в которых и содержатся
запрашиваемые данные. Весь процесс очень напоминает
использование функций в PHP-скриптах: вызываем функцию и
передаём ей некоторые переменные. Потом функция в ответ
возвращает несколько переменных.
Вот так, например, мог бы выглядеть типичный XML-RPC-запрос
для сервера:
POST /xmlrpcInterface HTTP/1.0
User-Agent: Sitepoint XML-RPC Client 1.0
Host: xmlrpc.sitepoint.com
Content-type: text/xml
Content-length: 195
<?xml version="1.0"?>
<methodCall>
<methodName>forums.getNumTodaysThreads</methodName>
<params>
<param><value><string>PHP Development</string></value></param>
</params>
</methodCall>
А вот таким видит ваш XML-RPC-клиент ответ от
сервера.
HTTP/1.1 200 OK
Connection: close
Content-Length: 148
content-Type: text/xml
Date: Wed, Jul 28 1999 15:59:04 GMT
Server: Sitepoint XML-RPC Server 1.0
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><int>42</int></value>
</param>
</params>
</methodResponse>
Показанный в примере запрос может выяснить, сколько новых
тем было открыто сегодня на форуме PHP Development. В ответе
сервера содержится число, интересующее клиента. Но это пример
очень прост: в метод отправляется один параметр, один параметр
получаем при возврате. На самом же деле XML-RPC способен на
большее, позволяя вам передавать серверу более сложные наборы
данных (как, например, массивы) и получать очень подробные
ответы, содержащие самые разные типы данных.
Если кому-нибудь интересно, то "RPC" в аббревиатуре XML-RPC
расшифровывается как Remote Procedure Call (Удалённый Вызов
Процедуры), то есть клиентская система "вызывает" "процедуру"
(или метод) на удалённом сервере. Сама концепция Удалённого
Вызова Процедуры применима при разработке ПО вообще, и данная
технология может вам встретиться в самых различных областях
девелопперства. XML-RPC - это XML-стандарт для обмена данных
при Удалённом Вызове Процедуры.
XML-RPC находится на уровне пакетирования стека технологий,
описанных Кевином в статье "Web Services Demystified" (и, как
и было сказано выше, XML-RPC является альтернативой SOAP,
который Кевин использовал в качестве примера для уровня
пакетирования). Что будет происходить на уровне описания и
уровне поиска - решать вам. Некоторые XML-RPC-разработки имеют
дополнительные модули, которые позволяют проводить
интроспекцию, - "самоописание" - и тем самым подниматься до
уровня описания, предоставляя подробное описание методов и
параметров, которые вы можете отправлять XML-RPC-серверу.
Уровень поиска в XML-RPC отсутствует как таковой: не
существует централизованного реестра XML-RPC-серверов, но,
несомненно, при малейшей необходимости появятся расширения,
отвечающие требованиям стандарта
UDDI.
Ну хватит о самом XML-RPC! Если мы используем уже
существующие решения для PHP, в подробности работы стандарта
вдаваться не обязательно. Так что давайте посмотрим, что мы
можем бесплатно получить для XML-RPC и PHP...
PHP-разработки
XML-RPC
Чувствуется, у вас стали потеть ладони, значит, самая пора
провести небольшой обзор проектов Open Source, которые могут
облегчить вам жизнь, избавив от необходимости писать свой код
для генерирования XML-RPC-запросов и ответов. Список
кандидатов мы взяли со странички implementations сайта xmlrpc.com. Два основных "нагрузочных
испытания" в нашем тестировании были:
- скорость и лёгкость установки и использования;
- поддержка программы конфигурацией PHP, которую мы можем
обнаружить на среднестатистическом хосте.
XMLRPC-EPI изначально было разработан для внутреннего
пользования в epinions и
оказалось настолько успешным, что в настоящее время снабжает
сам PHP экспериментальными XML-RPC функциями. По сути XMLRPC-EPI -это
базовый класс, написанный в C++ (остальные написаны в PHP), а
поэтому просто так, без root-а, установить на сервер его не
удастся: потребуется перекомпилировать PHP.
Кроме того, приложение только лишь интерпретирует
содержание запросов и ответов XML-RPC, но не посылает и не
принимает их. Техруководство по использованию класса вы
найдёте здесь.
Разработка Бэрда Фастарда (чувак, наверное, прётся от такой
фамилии). Это XML-RPC класс, используемый в приложении ezPublish для получения запросов с её же локального клиента. Дизайн класса
превосходен интуитивностью управления, кроме того, прекрасно
описан в техруководстве, и позволяет проводить
интроспекцию.
Всё прекрасно, но в одном класс нехорош: для его работы
требуется, чтобы в конфигурации PHP был доступен и установлен xml parser (--with-qtdom). Для большинства
инсталляций PHP это не типично, чаще встречается expat XML parser (--with-xml). А посему, с
печалью в глазах, отправимся на дальнейшие поиски...
Документация на сайте неплоха, но при попытке установить, а
затем запустить демонстрационный скрипт "server.php" получаем
ошибку "class not found". Поскольку других кандидатур хватает,
без разговоров снимаем проект с дистанции.
Класс с большим потенциалом. Разработчики не остановились
на простой интерпретации и управлении запросами/ответами и
предоставили пользователю более широкую функциональность, как,
например, подключение к "абстрактной" базе данных. Последняя
доступная версия - альфа 0.9, так что сейчас проект мы
пропустим, но стоит быть в курсе его развития. Если у вас в
планах большие проекты, этот класс мог бы серьёзно сэкономить
ваше время.
Эта разработка корпорации Useful, самого создателя
стандарта XML-RPC, очевидно содержит много полезного. По
существу, приложение полностью поддерживает стандарт XML-RPC
и, кроме того, предоставляет возможность отладки, которая (как
мы увидим позже) может стать самой большой вашей проблемой при
разработке web-сервиса.
Документация хорошая, много примеров, как и в руководстве по разработке клиента в той же
системе. Однако для первого опыта в написании web-сервиса
система далека от идеала: её можно порекомендовать только
более опытным PHP-программерам, поэтому мы продолжаем
искать...
Посетив домашнюю страничку проекта, сразу понимаем, что это
лучшее начало. Код представляет собой не полный класс, а
простой набор определённых пользователем функций, которые
нужно инклюдить в скрипт.
В данной системе к XML-RPC-серверу и клиенту не
предъявляется требование самим определять тип принимаемых
XML-RPC параметров. Это значит, что XML-RPC-клиенту нужно
более внимательно проверять получаемые данные, однако здесь
вас будут беспокоить переменные PHP (а уж к ним то вы
привыкли) а не параметрами XML-RPC. Документация тоже неплоха.
А посему первый web-сервис мы напишем с помощью этого
проекта.
Ваш первый
web-сервис
Итак, начали. Напишем web-сервис для публикации новостей с
вашего сайта, а заодно и строчки для фида RSS. Но вместо того,
чтобы просто дать юзеру список заголовков и ссылок, которые
всё равно его притащат на исходный ресурс, наш web-сервис
будет делать всё так, как будто новости на самом деле живут на
сайте, где установлен XML-RPC-клиент.
Для нашего использования определим два XML-RPC-метода:
"news.getNewsList
", который выдаёт список
новостей, и "news.viewNewsItem
", который
отображает одну новость полностью. Также напишем метод
"method_not_found
", на всякий случай. Каждый
метод соответствует функции в PHP, и SQL-запросу, который
делает выборку новостей с вашего сайта.
Предположим, что у вас PHP 4.1.0 или выше и доступ к БД
MySQL (версия 3.23+). СтОит прочитать код и комментарии и
ознакомиться с документацией к функциям XML-RPC
Кейта.
Вроде бы всё. Закатали рукава и поехали...
Приготовления
- Скачайте исходники примера этой статьи - и избавьте
себя любимого от кропотливого "вырезать-вставить".
- Скачайте исходники из http://www.keithdevens.com/software/xmlrpc/source.php и сохраните их как "kd_xmlrpc.php".
- Теперь на вашем web-сервере в корне (там, где лежит ваша
"home page") создайте директорию kd_xmlrpc и положите туда
kd_xmlrpc.php. Все другие файлы, которые мы создадим в
процессе разработки, будем класть туда же.
- В БД MySQL создайте таблицу при помощи следующего
запроса:
CREATE TABLE kd_xmlrpc_news (
news_id mediumint(9) NOT NULL auto_increment,
title varchar(255) default NULL,
short_desc text,
full_desc text,
author varchar(100) default NULL,
date datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (news_id)
) TYPE=MyISAM COMMENT='XML RPC News';
- А теперь INSERT-ом вставьте в таблицу какие-нибудь
липовые новости, или если воображение не сработает,
запустите запрос из "kd_xmlrpc_news.sql", который вы найдёте
в архиве с исходниками к статье (скачанном в п. 1).
XML-RPC-сервер
БД готова к действию. Теперь пишем скрипт сервера и
называем его "server.php":
<?php
/* server.php */
/* Переменные
для доступа к MySQL */
$dbserver= "localhost"; // Имя хоста MySQL-сервера
$db = "database_name"; // Имя БД
$dbuser = "username"; // Имя пользователя для доступа $db
$dbpassword = "password"; // Пароль пользователя
/*
Подключаемся к MySQL-серверу */
$link = @mysql_connect ($dbserver, $dbuser, $dbpassword);
if (! $link){
echo ( "Unable to connect to db" );
exit();
}
/* Выбор БД */
if (!mysql_select_db ($db, $link) ){
exit ();
}
/* Инклюдим
xml-rpc-библиотеку Кейта */
include("kd_xmlrpc.php");
/* Инклюдим файл,где определены
все xml-rpc "методы" */
include("web_service_api.php");
/* Теперь используем функцию
XMLRPC_parse чтобы забрать POST
данные,
которые положил туда xml-rpc клиент, и преобразуем их
в нормальные PHP-переменные */
$xmlrpc_request = XMLRPC_parse($GLOBALS['HTTP_RAW_POST_DATA']);
/* Из полученных переменных PHP
вычленим имя метода, то есть
как будто
сервер спрашивает: "Чем могу помочь?" */
$methodName = XMLRPC_getMethodName($xmlrpc_request);
/* Получаем параметры, привязанные к
этому методу,
т. е.: "Итак, вы желаете
получить одну новость.
Какую же? Какой
id#?" */
$params = XMLRPC_getParams($xmlrpc_request);
/* Отлов ошибок - если запрашиваемый
метод не
существует, клиенту возвращаем
сообщение об ошибке */
if(!isset($xmlrpc_methods[$methodName])){
$xmlrpc_methods['method_not_found']($methodName);
/* В противном случае передаём
управление соответствующей
данному
методу PHP-функции - заметьте, что функции сами
возвращают клиенту правильно
отформатированный
xml-rpc-ответ */
}else{
/* Вызов метода - заметьте, что здесь
$params[0], а не просто
$params,
как указано в документации. */
$xmlrpc_methods[$methodName]($params[0]);
}
?>
Краткое описание того, что происходит в server.php:
- Подключаемся к MySQL и предоставляем скрипту доступ к
БД.
- Подключаем kd_xmlrpc.php: теперь мы можем использовать
функции Кейта.
- Подключаем web_service_api.php, где определяются
XML-RPC-методы.
- Для того чтобы забрать содержимое
$GLOBALS['HTTP_RAW_POST_DATA']
, где хранится
запрос от XML-RPC-клиента, и переконвертировать параметры
XML в PHP-переменные используем функцию XMLRPC_parse()
.
- Теперь определим, какой метод вызывается функцией
XMLRPC_getMethodName()
. Мы хотим, чтобы наш
XML-RPC-сервер мог выполнять больше чем одну задачу (здесь
задача=метод=функция) и как раз здесь-то мы и определяем,
какой из методов будет обрабатывать текущий запрос
(например, "news.getNewsList
" или
"news.viewNewsItem
").
- Теперь проверим, какие параметры передавал клиент в
своём запросе (например,
id
новости).
- Наконец, мы проверяем, существует ли метод (а это
описано в web_service_api.php), и если всё в порядке, то
этот метод (он же PHP-функция) и выполняется. Если метод не
найден, передаём управление в метод по умолчанию
"
method_not_found
".
Специфические XML-RPC-функции, задействованные в этом
скрипте:
XMLRPC_parse()
: эта функция получает
XML-данные и преобразует их в переменные PHP - то есть XML
из запроса преобразован в удобоваримую для PHP форму. На
данном этапе нет ничего, взятого непосредственно из
спецификации стандарта XML-RPC: любые полученные XML-данные
преобразуются в переменные PHP.
XMLRPC_getMethodName()
: определяет, какой
метод нужно задействовать, то есть мы узнаём, какую
PHP-функцию вызывать.
XMLRPC_getParams()
: получает
XML-RPC-параметры и преобразует их в переменные PHP.
Вот, в принципе, и всё: XML-RPC-сервер готов. В общей
сложности, это всё, что вам нужно сделать при разработке
любого нового XML-RPC-сервера. Несложно, да? Теперь всё, что
нам нужно - это те PHP-функции, используемые в качестве
XML-RPC-методов...
Методы
Теперь напишем PHP-функции, соответствующие
XML-RPC-методам, и складываем их все в
web_service_api.php:
<?php
/* web_service_api.php */
/*
Определяем массив с именами xmlrpc-методов и
соответствующими им PHP-функциям */
$xmlrpc_methods = array();
$xmlrpc_methods['news.getNewsList'] = news_getNewsList;
$xmlrpc_methods['news.viewNewsItem'] = news_viewNewsItem;
$xmlrpc_methods['method_not_found'] = XMLRPC_method_not_found;
/* Теперь очень полезная функция для
преобразования MySQL-поля дата-время
в
UNIX timestamp, который потом будет использован
функцией
XMLRPC_convert_timestamp_to_iso8601($timestamp).
Это не метод!
Взято из:
http://www.zend.com/codex.php?id=176&single=1 */
function mysql_datetime_to_timestamp($dt) {
$yr=strval(substr($dt,0,4));
$mo=strval(substr($dt,5,2));
$da=strval(substr($dt,8,2));
$hr=strval(substr($dt,11,2));
$mi=strval(substr($dt,14,2));
$se=strval(substr($dt,17,2));
return mktime($hr,$mi,$se,$mo,$da,$yr);
}
/* Функция для
печати списка новостей, соответствует
методу news.getNewsList Допускает
сортировку по
имени столбца и
ограничение выдаваемых строк до 20 */
function news_getNewsList ( $query_info=0 ) {
/* Определяем массив с именами
столбцов, по которым
допускается
сортировка ORDER BY в запросе */
$order_fields = array ( "author", "title" );
/* Теперь смотрим,имеет ли
$query_info['order']
допустимое
значение и приписываем корректное значение
переменной $order */
if ( ISSET ( $query_info['order'] ) &&
in_array ( $query_info['order'], $order_fields ) ) {
$order = "ORDER BY " . $query_info['order'] . ", date DESC ";
} else {
$order = "ORDER BY date DESC ";
}
/* Смотрим в $query_info['limit'] и
определяем
количество новостей,
которые отправим в ответ,
приписываем корректное значение
переменной $limit */
if ( ISSET ( $query_info['limit'] ) && $query_info['limit'] < 20 ) {
$limit = "LIMIT 0, " . $query_info['limit'] . " ";
} else {
$limit = "LIMIT 0, 5 ";
}
/* Генерим запрос */
$query = "SELECT * FROM kd_xmlrpc_news " . $order . $limit;
$sql = mysql_query ( $query );
if ( $sql ) {
$news_items = array();
while ( $result = mysql_fetch_array ( $sql ) ) {
/* Из выборки вытягиваем нужные нам
данные */
$news_item['news_id'] = $result['news_id'];
$news_item['date'] = XMLRPC_convert_timestamp_to_iso8601(
mysql_datetime_to_timestamp( $result['date'] )
);
$news_item['title'] = $result['title'];
$news_item['short_desc'] = $result['short_desc'];
$news_item['author'] = $result['author'];
/* Дополняем массив $news_items */
$news_items[] = $news_item;
}
/* Конвертим массив $news_items в набор
параметров
XML-RPC и отвечаем в формате XML. */
XMLRPC_response(XMLRPC_prepare($news_items), KD_XMLRPC_USERAGENT);
} else {
/* Если была ошибка, шлём
сообщение
об ошибке */
XMLRPC_error("1", "news_getNewsList() error: Unable to
read news:". mysql_error() . "\nQuery was: " . $query, KD_XMLRPC_USERAGENT);
}
}
/* Функция для
просмотра одной новости целиком, соответствует
методу news.viewNewsItem */
function news_viewNewsItem ( $news_id ) {
/* Определяем запрос одной новости */
$query = "SELECT * FROM kd_xmlrpc_news WHERE
news_id = '" . $news_id . "'";
$sql = mysql_query ( $query );
if ( $result = mysql_fetch_array ( $sql ) ) {
/* Вычленяем переменные и
шлём их в
ответ сервера */
$news_item['news_id'] = $result['news_id'];
$news_item['date'] = XMLRPC_convert_timestamp_to_iso8601(mysql_datetime_to_timestamp( $result['date'] ) );
$news_item['title'] = $result['title'];
$news_item['full_desc'] = $result['full_desc'];
$news_item['author'] = $result['author'];
/* Отвечаем клиенту, высылая новость */
XMLRPC_response(XMLRPC_prepare($news_item), KD_XMLRPC_USERAGENT);
} else {
/* Если была ошибка,
вместо
ответа шлём сообщение о ней */
XMLRPC_error("1", "news_viewNewsItem() error: Unable to
read news:". mysql_error(), KD_XMLRPC_USERAGENT);
}
}
/* Функция на
случай, если запрашиваемый
мметод не существует */
function XMLRPC_method_not_found($methodName){
XMLRPC_error("2", "The method you requested, " . $methodName
. ", was not found.", KD_XMLRPC_USERAGENT);
}
?>
Итак, что здесь, собственно, происходит:
- Сначала мы создали PHP-массив
$xmlrpc_methods
, где хранится список всех
доступных XML-RPC-методов и соответствующих им функций PHP.
- Функция
mysql_datetime_to_timestamp()
по
сути не является XML-RPC-методом; эта функция нужна для
конвертирования MySQL-поля "дата-время" в формат, с которым
сможет работать PHP.
- Затем определяем функцию
news_getNewsList()
для отправки запросов к таблице "kd_xmlrpc_news". Функция
может принимать в качестве аргумента массив $query_info
, с параметрами, которые может
послать XML-RPC-клиент. Как вы видите, эти параметры могут
повлиять на запрос к БД, изменяя опции "ORDER
BY
" и "LIMIT
".
- Функция
news_viewNewsItem()
используется
для того, чтобы выдернуть из БД одну новость и принимает в
аргумент только одну переменную/параметр: $news_id
, содержащий id новости в базе данных.
- Последняя функция
XMLRPC_method_not_found()
- это функция по умолчанию, которая генерит ответ в том
случае, когда клиент запрашивает несуществующий
XML-RPC-метод.
Специфические XML-RPC-функции, задействованные в этом
скрипте:
XMLRPC_convert_timestamp_to_iso8601()
:
XML-RPC использует особенный формат обозначения даты и
времени. Данная функция просто берёт timestamp в PHP и
преобразует его в формат, применяемый в XML-RPC.
XMLRPC_prepare()
: эта функция берёт нобор
переменных PHP и преобразует их в параметры XML-RPC. Функция
определяет тип переменных и поэтому правильно преобразует в
XML-RPC. Именно эта особенность делает интерфейс проекта
Кевина Девенса дружественным: в большинстве случаев другие
приложения требуют при генерации ответа на XML-RPC-запрос
отдельных функций для массивов, целых чисел, строк и т. д.
XMLRPC_response()
: данная функция
используется для отправки ответа клиенту. Функция принимает
два аргумента: XML-RPC-данные (сгенерённые функцией XMLRPC_prepare()
),. и факультативно имя
XML-RPC-сервера или user agent, например, KD_XMLRPC_USERAGENT
- здесь можно поставить всё
что угодно.
XMLRPC_error()
: генерирует ответ
XML-RPC-сервера с применением специальных тэгов с сообщением
об ошибке, которые определены в спецификации стандарта. Если
пожелаете, можете настроить ваш собственный набор кодов
ошибок; очень помогает при отладке сервера.
Готово! Мы написали PHP-функции, у нас есть таблица их
соответствия XML-RPC-методам: наш XML-RPC-сервер готов к
работе! Теперь, на десерт, напишем простенький клиент для
доступа к новостям.
XML-RPC-клиент
Поскольку это пример, скрипт клиента будем хранить в той же
директории, где и сервер, но если у вас есть другой
web-сервер, вы можете разместить клиента там, главное,
убедитесь в том, что скрипт имеет доступ к библиотеке функций
"kd_xmlrpc.php".
<?php
/* Подключаем библиотеку */
include ( "kd_xmlrpc.php" );
/* Определяем путь к xml-rpc серверу */
$site = "www.yourdomain.com";
$location = "/kd_xmlrpc/server.php";
/* Функция, возвращающая красивую дату
*/
function convert_date ( $date ) {
$date = date ( "D M y H:i:s",
XMLRPC_convert_iso8601_to_timestamp ( $date ) );
return
( $date );
}
?>
<!doctype html
public "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title> KD XML RPC
News Client </title>
<meta name="Generator"
content="EditPlus">
<meta name="Author"
content="HarryF">
<meta name="Keywords" content="XML
RPC">
<meta name="Description" content="Gets news
form server.php">
</head>
<body>
<?php
/* client.php */
/* Если юзер хочет
видеть только одну новость, делаем это */
if ( ISSET ( $_GET['news_id'] ) ) {
/* $success равен 0 (неуспешно) / 1 (
успешно ).
XMLPRC_request
шлёт скрипту сервера XML POST,
вызывая метод и
отправляя корректные параметры
с помощью
XMLRPC_prepare */
list($success, $response) = XMLRPC_request(
$site,
$location,
'news.viewNewsItem',
array(XMLRPC_prepare($_GET['news_id']),
'HarryFsXMLRPCClient')
);
/* Если всё хорошо, показываем эту
новость */
if ($success) {
?>
<table
align="center" width="600">
<tr valign="top">
<th colspan="2"><b><?php echo ( $response['title'] );?></b></th>
</tr>
<tr valign="top">
<th><?php echo ( $response['author'] );?></th>
<th><?php echo ( convert_date ( $response['date'] ) );?></th>
</tr>
<tr valign="top">
<td colspan="2">
<?php echo ( nl2br ( $response['full_desc'] ) );?>
</th>
</tr>
</table>
<?php
/* В противном случае поведаем юзеру об
ошибке */
} else {
echo ( "<p>Error: " . nl2br ( $response['faultString'] ) );
}
} else {
/* Определяем параметры, передаваемые в
XML-RPC метод как массив PHP */
$query_info['limit'] = 10;
$query_info['order'] = "author";
/* XMLRPC_prepare обрабатывает массив и
преобразует в параметры XML-RPC */
list($success, $response) = XMLRPC_request(
$site,
$location,
'news.getNewsList',
array(XMLRPC_prepare($query_info),
'HarryFsXMLRPCClient')
);
/* При успешном завершении показываем
список в виде HTML-таблицы */
if ($success) {
echo ( "<table
align=\"center\" width=\"600\">\n" );
$count = 0;
while (
list ( $key, $val ) = each ( $response ) ) {
?>
<tr
valign="top">
<td colspan="2">
<a
href="<?php echo ( $_SERVER['PHP_SELF'] );?>?news_id=<?php echo ( $response[$count]['news_id'] ); ?>">
<?php echo ( $response[$count]['title'] ); ?>
</a>
</td>
</tr>
<tr valign="top">
<td colspan="2">
<?php echo ( $response[$count]['short_desc'] ); ?>
</td>
</tr>
<tr valign="top">
<td>
<?php echo ( $response[$count]['author'] ); ?>
</td>
<td>
<?php echo ( convert_date ( $response[$count]['date'] ) ); ?>
</td>
</tr>
<?php
$count++;
}
echo ( "</table>\n" );
/* Если ошибка */
} else {
echo ( "<p>Error: " . nl2br ( $response['faultString'] ) );
}
}
;?>
</body>
</html>
Что происходит в скрипте? Сам по себе, PHP-код - это
простое ветвление if/else: то есть если мы решим просмотреть
одну конкретную новость и используем переменную $_GET['news_id']
, то скрипт покажет эту новость.
В противном случае будет показан список всех новостей.
Для просмотра списка новостей мы используем метод news.getNewsList
. Заметьте, что $query_info['limit']
выставлен на 10 (то есть нам
нужно максимум 10 новостей), а $query_info['order']
- на значение "author" (то
есть сортировка новостей по имени автора). Эти две переменные
станут параметрами, которые отправит XML-RPC-клиент в своём
запросе.
При просмотре одной новости целиком, в параметр мы
преобразуем $_GET['news_id']
; по этому параметру
сервер сможет определить, какую новость мы желаем
получить.
Специфические XML-RPC-функции, задействованные в этом
скрипте:
XMLRPC_prepare()
: раньше мы использовали её
в сервере, и здесь также конвертируем переменные PHP в
XML-RPC-параметры. Именно здесь мы фиксируем данные, которые
понадобятся серверу для работы запрашиваемого метода.
XMLRPC_request()
: данная функция принимает
в качестве аргумента пять переменных и возвращает массив.
Это, собственно, то, что делает клиента клиентом.
Принимаются:
$site
- доменное имя сервера
$location
- путь к скрипту сервера от
web-корня
$methodName
- имя запрашиваемого
XML-RPC-метода
$params
- любые необходимые для работы
метода переменные
$user_agent
- необязательный аргумент, и
здесь может быть всё, что вам заблагорассудится (например,
HarryFsXMLRPCClient).
Возвращаемый массив содержит
две переменные. Первая принимает значение 0 в случае
отрицательного ответа от сервера, 1 в случае положительного,
и это есть наша обработка ошибок. Вторая переменная - это
многомерный массив, в котором содержатся данные из ответа
сервера.
XMLRPC_convert_iso8601_to_timestamp
: нашей
функцией convert_date()
мы преобразуем дату и
время из формата XML-RPC в нечто более приглядное, а
timestamp от PHP используем в качестве перевалочного пункта.
Подсказка: при написании XML-RPC-клиентов полезно
рассматривать XML-RPC-сервер как базу данных - и то, и другое
лишь источник данных. В ваших скриптах код обращений к серверу
будет иметь структуру кода с запросами к MySQL.
Итак, всё готово! Направили браузер на
http://www.yourdomain.com/kd_xmlrpc/client.php, и читаем себе
новости.
Скачайте код: все используемые в данной статье скрипты и
таблица MySQL находятся в этом .zip файле.
Ну и в заключение
Итак, как вы уже убедились, используя XML-RPC-код Кейта
Девенса, создать свой web-сервис достаточно просто. То есть
можно сказать, что это и есть прекрасное начало нашего
близкого знакомства с миром web-сервисов. Возможно, в качестве
"домашнего задания" вы могли бы добавить функциональности
клиенту, позволив пользователям выбирать способ сортировки
отображаемых новостей (то есть по автору, теме или дате). Или,
если у вас большие амбиции, вы могли бы расширить интерфейс
клиента, позволив пользователям редактировать
(UPDATE
) и добавлять (INSERT
)
новости.
Если у вас далеко идущие планы по работе с web-сервисами,
вам может быть небезынтересно взглянуть и на другие проекты,
например, класс phpxmlrpc
от Userland,
оптимизации кода ради. Но для и выполнения тех же задач вам
вполне хватит разработки Кейта Девенса.
Но о двух небольших недоработках в коде Кейта вы должны
знать.
Во-первых, функция XMLRPC_request()
"стирает"
всю передаваемую в ответе XML-RPC информацию о типе каждого
параметра, что может привести к лишнему коду в клиенте.
Например, вы можете ждать массив, а получить скалярную (с
одним значением) переменную - клиент должен учитывать оба
варианта.
Кроме того, нет возможности интроспекции, которая позволяет
другим разработчикам получить ваши API (то есть список
допустимых методов и параметров вашего сервера). Впрочем,
добавить возможность интроспекции будет несложно: просто надо
использовать массив из web_service_api.php.
И, напоследок, это достаточно ограниченные средства
отладки. Когда ваш единственный интерфейс - это клиент,
выяснить, что именно там делает XML-RPC-сервер будет
проблематично. Здесь вы, вероятно, захотите завести нечто
вроде лога для сервера и можете задействовать ваш собственный
отладчик ошибок в PHP; для ознакомления с этим аспектом,
прочитайте эту статью про определённые пользователем
обработчики ошибок.
Но, конечно, мы благодарим Кейта за замечательную
разработку и содействие в удачном старте при освоении новой
для нас технологии.
В статье мы не рассматривали два момента: систему
безопасности и обработку транзакций. Если вам хочется чего-то
"великого и ужасного", то можете прочитать это. В системе безопасности нужно
предусмотреть три момента:
- методы авторизации XML-RPC-клиентов. Если ваш
сервер поддерживает методы добавления (
INSERT
)
и редактирования (UPDATE
) новостей, то вам
нужно будет найти способы авторизации удалённого сайта и/или
пользователей этого сайта. И здесь вам решать, как вы
поступите. Удалённый сайт вы можете проверить по имени хоста
или IP-адресу. Для авторизации пользователей можно
порекомендовать воспользоваться XML-RPC-сервером ещё как и
средством проверки пользователей по базе данных: перед тем,
как предоставить доступ к потенциально опасным методам,
посредством специального метода (написанного вами и
используемого только для целей авторизации) затребуйте
логин-пассворд. В идеале, если вы так и поступите,
соединение клиента с сервером необходимо будет закодировать
через SSL, но это требует использования CURL-функций PHP при приёме/отправке
данных (некоторые части приложения придётся переписать).
- Как бороться с DoS-атаками? Более общий вопрос:
что случится, если клиент флудит ваш сервер своими запросами
и тем самым не даёт серверу отреагировать на запросы других
пользователей?
- Как осуществлять контроль над предоставлением
информации по API сервера (интроспекция/WSDL) на уровне
описания? Насколько хороша затея с предоставлением
списка всех допустимых методов вашего XML-RPC-сервера, то
есть идея неограниченного доступа к этой информации? Как
контролировать предоставление доступа к таким данным? Если
какой-нибудь метод даёт доступ к уязвимым данным, которые
обычно прячут за firewall-ом, нужно думать очень аккуратно.
При обработке транзакций задумайтесь, что будет, если вы
потеряете соединение между клиентом и сервером в процессе
XML-RPC-общения? Какие механизмы вы внедрите для обслуживания
повторных попыток (повторная отправка запроса или ответа)?
Может случиться так, что потеря данных будет недопустима для
вашего web-сервиса, в этом случае необходимо предусмотреть
механизм создания/управления очередью запросов и ответов.
Но прочь страхи, вот вы и написали ваш первый web-сервис и,
будем надеяться, неплохо разобрались с принципами работы
технологии. Возникает закономерный вопрос: ну и что же делать
с этим добром? Ставить новости - это здорово, но это делали
уже и раньше. Что-нибудь ещё? Читаем следующую главу...
На что мы способны с
XML-RPC?
Вопрос немного из категории "на что мы способны с
web-страничкой?". Единственное ограничение здесь - ваша
фантазия. Максимально абстрактно можем сказать, что с XML-RPC
и PHP вы можете вывести ваш ресурс за границы простой отправки
документов пользователю. Ваш сайт будет в состоянии
обмениваться данными с любой системой, а вы, принимая на
вооружение столь распространённый стандарт, предоставите
другим разработчикам интерфейс, с которым они будут рады
работать.
Web-сервисы в самом широком смысле могут принести доход
тем, кто владеет "ценной информацией". Например, если вы
имеете прямой доступ к курсам обмена валют, то с PHP и XML-RPC
вы можете предоставить другим сайтам услугу конвертирования
валют. А вот ещё несколько идей и примеров:
- Альтернативный клиент для обновления вашего сайта.
Сколько раз вы кричали от отчаяния, когда вы забили длинный
код в TEXTAREA html-странички, нажали "отправить" - и всё
потеряли, потому что вышло время сессии или прервалась связь
с сайтом? XML-RPC может:
- предоставить интерфейс для обновления вашего сайта,
- позволить внести изменения посредством стандартного
windows-клиента и сохранить их локально, а потом
- перенести изменения на удалённый сервер в удобное для
вас время.
- Именно это и сделали в ez Systems,
разработав локальный клиент для системы CMS ezPublish.
- Правильная орфография на сайте при помощи XML-RPC-проверкой орфографии (по-русски
spellchecker// Прим. перев.) от Stuffed
Dog.
- Распределение нагрузки сервера и использования полосы
пропускания: БД на одном сервере, и несколько других
серверов с интерфейсом доступа к БД через XML-RPC. Для такой
MMOG (massive multyplayer online game, массовая
многопользовательская онлайн игра) как Planetarion возможности просто
безграничны.
- Общение вашего сайта на PHP с сайтами на CGI/Perl или
ASP посредством одной из многочисленных XML-RPC-разработок, что позволит вам не
переписывать все скрипты при апгрейде до PHP.
- Можно воспользоваться услугами Google Search Gateway (поисковый шлюз
Google), с помощью которого результаты поиска появляются
непосредственно на вашем сайте.
- Можно скачать почту с разных аккаунтов, а потом
прочитать всё из одного XML-RPC-ресурса с CGI-сервером xr2pop (ну ладно, пусть это не PHP, а
Perl, всё равно хороший пример).
- Получение самых различных новостей от News is Free при помощи их XML-RPC-сервера.
Возможности безграничны...