root.elima.ru
Мертвечина
Статьи и книгиИнтернет-технологии и сетевое программирование

Пишем свой web-сервис на PHP и XML-RPC

Если вы следили за новостями на данную тему, у вас могло сложиться впечатление, что это всё – технологии высшего порядка, для больших компаний с немалым же бюджетом. Если вы так подумали, то вы ошиблись.

Принцип работы web-сервисов достаточно прост и в нашей сегодняшней статье мы довольно подробно рассмотрим все задействованные в этом технологии. Затем, с помощью нашего общего друга PHP мы напишем свой первый web-сервис.

Статья написана с расчётом на то, что читатели хоть немного знакомы с PHP, MySQL и XML, если вы не уверены в ваших познаниях, рекомендуем подчитать Building your own Database Driven Website using PHP & MySQL и XML – An Introduction.

Итак, что мы можем вам предложить:

Основы технологии 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. Два основных "нагрузочных испытания" в нашем тестировании были:

XMLRPC-EPI, http://xmlrpc-epi.sourceforge.net/

XMLRPC-EPI изначально было разработан для внутреннего пользования в epinions и оказалось настолько успешным, что в настоящее время снабжает сам PHP экспериментальнымиXML-RPC функциями. По сути XMLRPC-EPI -это базовый класс, написанный в C++ (остальные написаны в PHP), а поэтому просто так, без root-а, установить на сервер его не удастся: потребуется перекомпилировать PHP.

Кроме того, приложение только лишь интерпретирует содержание запросов и ответов XML-RPC, но не посылает и не принимает их. Техруководство по использованию класса вы найдёте здесь.

eZ xmlrpc, http://developer.ez.no/article/static/53/

Разработка Бэрда Фастарда (чувак, наверное, прётся от такой фамилии). Это XML-RPC класс, используемый в приложении ezPublish для получения запросов с её же локального клиента. Дизайн класса превосходен интуитивностью управления, кроме того, прекрасно описан в техруководстве, и позволяет проводить интроспекцию.

Всё прекрасно, но в одном класс нехорош: для его работы требуется, чтобы в конфигурации PHP был доступен и установлен xml parser (--with-qtdom). Для большинства инсталляций PHP это не типично, чаще встречается expat XML parser (--with-xml). А посему, с печалью в глазах, отправимся на дальнейшие поиски...

Fase 4 XML-RPC, http://www.fase4.com/xmlrpc/

Документация на сайте неплоха, но при попытке установить, а затем запустить демонстрационный скрипт "server.php" получаем ошибку "class not found". Поскольку других кандидатур хватает, без разговоров снимаем проект с дистанции.

phpRPC, http://sourceforge.net/projects/phprpc/

Класс с большим потенциалом. Разработчики не остановились на простой интерпретации и управлении запросами/ответами и предоставили пользователю более широкую функциональность, как, например, подключение к "абстрактной" базе данных. Последняя доступная версия – альфа 0.9, так что сейчас проект мы пропустим, но стоит быть в курсе его развития. Если у вас в планах большие проекты, этот класс мог бы серьёзно сэкономить ваше время.

phpxmlrpc, http://phpxmlrpc.sourceforge.net/

Эта разработка корпорации Useful, самого создателя стандарта XML-RPC, очевидно содержит много полезного. По существу, приложение полностью поддерживает стандарт XML-RPC и, кроме того, предоставляет возможность отладки, которая (как мы увидим позже) может стать самой большой вашей проблемой при разработке web-сервиса.

Документация хорошая, много примеров, как и в руководстве по разработке клиента в той же системе. Однако для первого опыта в написании web-сервиса система далека от идеала: её можно порекомендовать только более опытным PHP-программерам, поэтому мы продолжаем искать...

Keith Devens' XML-RPC Client/Server, http://www.keithdevens.com/software/xmlrpc/

Посетив домашнюю страничку проекта, сразу понимаем, что это лучшее начало. Код представляет собой не полный класс, а простой набор определённых пользователем функций, которые нужно инклюдить в скрипт.

В данной системе к 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 Кейта.

Вроде бы всё. Закатали рукава и поехали...

Приготовления

  1. Скачайте исходники примера этой статьи – и избавьте себя любимого от кропотливого "вырезать-вставить".
  2. Скачайте исходники из http://www.keithdevens.com/software/xmlrpc/source.php и сохраните их как "kd_xmlrpc.php".
  3. Теперь на вашем web-сервере в корне (там, где лежит ваша "home page") создайте директорию kd_xmlrpc и положите туда kd_xmlrpc.php. Все другие файлы, которые мы создадим в процессе разработки, будем класть туда же.
  4. В БД 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';

  5. А теперь 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:

  1. Подключаемся к MySQL и предоставляем скрипту доступ к БД.
  2. Подключаем kd_xmlrpc.php: теперь мы можем использовать функции Кейта.
  3. Подключаем web_service_api.php, где определяются XML-RPC-методы.
  4. Для того чтобы забрать содержимое $GLOBALS['HTTP_RAW_POST_DATA'], где хранится запрос от XML-RPC-клиента, и переконвертировать параметры XML в PHP-переменные используем функцию XMLRPC_parse().
  5. Теперь определим, какой метод вызывается функцией XMLRPC_getMethodName(). Мы хотим, чтобы наш XML-RPC-сервер мог выполнять больше чем одну задачу (здесь задача=метод=функция) и как раз здесь-то мы и определяем, какой из методов будет обрабатывать текущий запрос (например, "news.getNewsList" или "news.viewNewsItem").
  6. Теперь проверим, какие параметры передавал клиент в своём запросе (например, id новости).
  7. Наконец, мы проверяем, существует ли метод (а это описано в web_service_api.php), и если всё в порядке, то этот метод (он же PHP-функция) и выполняется. Если метод не найден, передаём управление в метод по умолчанию "method_not_found".

Специфические XML-RPC-функции, задействованные в этом скрипте:

Вот, в принципе, и всё: 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&amp;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'] ) &amp;&amp;
            
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'] ) &amp;&amp; $query_info['limit'] &lt; 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);
}
?>

Итак, что здесь, собственно, происходит:

  1. Сначала мы создали PHP-массив $xmlrpc_methods, где хранится список всех доступных XML-RPC-методов и соответствующих им функций PHP.
  2. Функция mysql_datetime_to_timestamp() по сути не является XML-RPC-методом; эта функция нужна для конвертирования MySQL-поля "дата-время" в формат, с которым сможет работать PHP.
  3. Затем определяем функцию news_getNewsList() для отправки запросов к таблице "kd_xmlrpc_news". Функция может принимать в качестве аргумента массив $query_info, с параметрами, которые может послать XML-RPC-клиент. Как вы видите, эти параметры могут повлиять на запрос к БД, изменяя опции "ORDER BY" и "LIMIT".
  4. Функция news_viewNewsItem() используется для того, чтобы выдернуть из БД одну новость и принимает в аргумент только одну переменную/параметр: $news_id, содержащий id новости в базе данных.
  5. Последняя функция XMLRPC_method_not_found() – это функция по умолчанию, которая генерит ответ в том случае, когда клиент запрашивает несуществующий XML-RPC-метод.

Специфические 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 (
"&lt;/table>\n" );

    
/* Если ошибка */
    
} else {
        echo (
"&lt;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-функции, задействованные в этом скрипте:


Подсказка: при написании 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; для ознакомления с этим аспектом, прочитайте эту статью про определённые пользователем обработчики ошибок.

Но, конечно, мы благодарим Кейта за замечательную разработку и содействие в удачном старте при освоении новой для нас технологии.

В статье мы не рассматривали два момента: систему безопасности и обработку транзакций. Если вам хочется чего-то "великого и ужасного", то можете прочитать это. В системе безопасности нужно предусмотреть три момента:

  1. методы авторизации XML-RPC-клиентов. Если ваш сервер поддерживает методы добавления (INSERT) и редактирования (UPDATE) новостей, то вам нужно будет найти способы авторизации удалённого сайта и/или пользователей этого сайта. И здесь вам решать, как вы поступите. Удалённый сайт вы можете проверить по имени хоста или IP-адресу. Для авторизации пользователей можно порекомендовать воспользоваться XML-RPC-сервером ещё как и средством проверки пользователей по базе данных: перед тем, как предоставить доступ к потенциально опасным методам, посредством специального метода (написанного вами и используемого только для целей авторизации) затребуйте логин-пассворд. В идеале, если вы так и поступите, соединение клиента с сервером необходимо будет закодировать через SSL, но это требует использования CURL-функций PHP при приёме/отправке данных (некоторые части приложения придётся переписать).
  2. Как бороться с DoS-атаками? Более общий вопрос: что случится, если клиент флудит ваш сервер своими запросами и тем самым не даёт серверу отреагировать на запросы других пользователей?
  3. Как осуществлять контроль над предоставлением информации по API сервера (интроспекция/WSDL) на уровне описания? Насколько хороша затея с предоставлением списка всех допустимых методов вашего XML-RPC-сервера, то есть идея неограниченного доступа к этой информации? Как контролировать предоставление доступа к таким данным? Если какой-нибудь метод даёт доступ к уязвимым данным, которые обычно прячут за firewall-ом, нужно думать очень аккуратно.

При обработке транзакций задумайтесь, что будет, если вы потеряете соединение между клиентом и сервером в процессе XML-RPC-общения? Какие механизмы вы внедрите для обслуживания повторных попыток (повторная отправка запроса или ответа)? Может случиться так, что потеря данных будет недопустима для вашего web-сервиса, в этом случае необходимо предусмотреть механизм создания/управления очередью запросов и ответов. Для ознакомления с принципами обработки транзакций, читайте эту статью.

Но прочь страхи, вот вы и написали ваш первый web-сервис и, будем надеяться, неплохо разобрались с принципами работы технологии. Возникает закономерный вопрос: ну и что же делать с этим добром? Ставить новости – это здорово, но это делали уже и раньше. Что-нибудь ещё? Читаем следующую главу...

На что мы способны с XML-RPC?

Вопрос немного из категории "на что мы способны с web-страничкой?". Единственное ограничение здесь – ваша фантазия. Максимально абстрактно можем сказать, что с XML-RPC и PHP вы можете вывести ваш ресурс за границы простой отправки документов пользователю. Ваш сайт будет в состоянии обмениваться данными с любой системой, а вы, принимая на вооружение столь распространённый стандарт, предоставите другим разработчикам интерфейс, с которым они будут рады работать.

Web-сервисы в самом широком смысле могут принести доход тем, кто владеет "ценной информацией". Например, если вы имеете прямой доступ к курсам обмена валют, то с PHP и XML-RPC вы можете предоставить другим сайтам услугу конвертирования валют. А вот ещё несколько идей и примеров: