Основы PHP
  Что такое PHP?
  Возможности PHP
  Преимущества PHP
  История развития
  Что нового в PHP5?
  «Движок» PHP
  Переход на PHP 5.3
New Переход на PHP 5.6
  Введение в PHP
  Изучение PHP
  Основы CGI
  Синтаксис PHP
  Типы данных PHP
  Переменные в PHP
  Константы PHP
  Выражения PHP
  Операторы PHP
  Конструкции PHP
  Ссылки в PHP
  PHP и ООП
  Безопасность
  Функции PHP
  Функции по категориям
  Функции по алфавиту
  Стандартные функции
  Пользовательские
  PHP и HTTP
  Работа с формами
  PHP и Upload
  PHP и Cookies
  PHP и базы данных
  PHP и MySQL
  Документация MySQL
  Учебники
  Учебники по PHP
  Учебники по MySQL
  Другие учебники
  Уроки PHP
  Введение
  Самые основы
  Управление
  Функции
  Документация
  Математика
  Файлы
  Основы SQL
  Дата и время
  CURL
  Изображения
  Стили
  Безопасность
  Установка
  Проектирование БД
  Регулярные выражения
  Подготовка к работе
  Быстрый старт
  Установка PHP
  Установка MySQL
  Конфигурация PHP
  Download / Скачать
  Скачать Apache
  Скачать PHP
  Скачать PECL
  Скачать PEAR
  Скачать MySQL
  Редакторы PHP
  Полезные утилиты
  Документация
  PHP скрипты
  Скачать скрипты
  Инструменты
  PHP в примерах
  Новости портала
 Главная   »  Сборник статей
 
 

Пишем свой 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, 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-функции, задействованные в этом скрипте:

  • 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&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-функции, задействованные в этом скрипте:

  • 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 (
"&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-функции, задействованные в этом скрипте:

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

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

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

  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 вы можете предоставить другим сайтам услугу конвертирования валют. А вот ещё несколько идей и примеров:

  • Альтернативный клиент для обновления вашего сайта. Сколько раз вы кричали от отчаяния, когда вы забили длинный код в 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-сервера.

Возможности безграничны...

 
 » Обсудить эту статью на форуме

 
 Сборник статей 
 Содержание раздела 
Есть еще вопросы или что-то непонятно - добро пожаловать на наш  форум портала PHP.SU 
 

 
Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS