Принцип работы web-сервисов достаточно прост и в нашей сегодняшней статье мы довольно подробно рассмотрим все задействованные в этом технологии. Затем, с помощью нашего общего друга PHP мы напишем свой первый web-сервис. Статья написана с расчётом на то, что читатели хоть немного знакомы с PHP, MySQL и XML Итак, что мы можем вам предложить: Основы технологии 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-сервисы. 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-"сообщения" - это методы и параметры. Методам мы можем поставить в примерное соответствие функции, которые мы определяем в 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 Кейта. Вроде бы всё. Закатали рукава и поехали... Приготовления Скачайте исходники примера этой статьи - и избавьте себя любимого от кропотливого "вырезать-вставить". Скачайте исходники из 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-сервера. Возможности безграничны...
|