Yii фреймворк: создание XML-RPC сервера

Владимир | | PHP, Web разработка, Yii.

yii xml rpc

На мой взгляд, XML-RPC протокол является одним из наиболее удобных способов передачи данных между Интернет-ресурсами. Можно, конечно, поспорить, но на изучение XML-RPC требуется минимальное количество времени, есть множество готовых библиотек, да и используется он очень широко. Поэтому я никогда не понимал, почему разработчики отличного фреймворка Yii решили не включать библиотеку для работы с XML-RPC в дистрибутив. Хотя, возможно, это вопрос времени 🙂

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

Примечание. Если вас интересует создание XML-RPC клиента, почитайте статью XML-RPC и Yii фреймворк.

Немного теории.

Мы можем использовать два основных подхода при создании XML-RPC сервера.

1) Отдельный скрипт. В этом случае создаём файл с именем вроде xmlrpc.php и помещаем его в корень сайта. В нём будет находиться код обработки XML-RPC запросов. Такой подход используется, например, в WordPress. Достоинство в том, что вам не нужно вносить никаких изменений в код вашего проекта, т.е. XML-RPC интерфейс получается независимым. С другой стороны, независимость приводит к тому, что вам будет гораздо сложнее работать со встроенными библиотеками фреймворка.

2) Использование действий контроллера. Этот вариант гораздо интереснее. Во-первых, вы автоматически получаете доступ ко всем возможностям Yii. Во-вторых, в этом случае XML-RPC методы можно разделить по модулям. Т.е. подключение / отключение модуля будет автоматически подключать / отключать соответствующие XML-RPC методы.

Т.к. реализация первого подхода никакой сложности не представляет (фактически вы можете использовать примеры из документации к вашей библиотеке для работы с XML-RPC), мы рассмотрим второй вариант.

Какие библиотеки можно использовать?

По большому счёту – любые, т.к. подключение сторонних библиотек к Yii выполняется без особых проблем. Я обычно использую библиотеку Incutio XML-RPC Library. Она достаточно простая, работает без проблем и, кроме того, используется в WordPress, а эта CMS используется очень часто.

Переходим к практике.

Шаг 1. Подключаем библиотеку.

Для этого скачиваем библиотеку и сохраняем файл IXR_Library.php в папку protected/vendors. Теперь подключить библиотеку можно так:

Yii::import('application.vendors.*');
require_once ('IXR_Library.php');

Этот код можно добавить в любое действие (action) контроллера.

Шаг 2. Создаём точку входа.

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

class MyController extends Controller {
	...
	public function actionXmlrpc() {
		Yii::import('application.vendors.*');
		require_once ('IXR_Library.php');

		$server = new IXR_Server(array(
			'my.hello' => array($this,'hello'),
		));
	}
	...
}

В этом методе мы просто создаём объект типа IXR_Server, конструктору которого нужно передать массив с названиями XML-RPC методов. В данном случае в массиве только один элемент. Ключ этого элемента является названием XML-RPC метода, а значение – именем функции, которую нужно вызвать.

array($this,'hello') означает, что будет вызван метод hello данного контроллера.

Тут нужно помнить об одном нюансе. Обычно в XML-RPC запросах не передаются данные сессий. Логин и пароль отправляются в каждом запросе в качестве параметров. Поэтому необходимо, обеспечить доступ к методу actionXmlrpc() без авторизации. Для этого немного изменим метод accessRules

public function accessRules() {
	return array(
		array('allow',  // allow all users to perform 'index' and 'view' actions
			'actions'=>array('index','view','xmlrpc'),
			'users'=>array('*'),
		),
		…,
	);
}

Шаг 3. Добавляем методы.

Теоретически, можно ограничиться просто добавлением метода hello, который просто вернет «Hello, world!».

public function hello($args) {
	return 'Hello, world!';
}

Но в 99% нужно аутентифицировать пользователя. Добавим метод

protected function xmlrpcLogin($username, $password) {
	$identity = new UserIdentity($username, $password);
	$identity->authenticate();
	if ($identity->errorCode === UserIdentity::ERROR_NONE) {
		Yii::app()->user->login($identity, 0);
		return true;
	}
	else {
		return false;
	}
}

Здесь мы используем класс UserIdentity, который автоматически создаётся фреймворком Yii при создании приложения. В параметрах этого метода нужно передать логин и пароль. В случае успешной аутентификации метод вернёт TRUE, в противном случае – FALSE. Кроме того, данные пользователя будут доступны через Yii::app()->user, т.е. точно также как и при обычной работе с фреймворком.

Теперь перепишем код метода hello.

public function hello($args) {
	if (!$this->xmlrpcLogin($args[0], $args[1])) {
		return new IXR_Error(-1, 'You did not provide the correct password');
	}
return 'Hello, world!';
}

Здесь предполагается, что логин и пароль будут переданы в первом и втором параметрах XML-RPC запроса.

Шаг 4. Проверяем работу.

Для проверки достаточно создать обычный PHP скрипт со следующим кодом.

require_once ('IXR_Library.php');

$client = new IXR_Client('http://my_site.domen/my/xmlrpc');
$client->debug = true;

$args = array();
$args[0] = 'login';
$args[1] = 'password';

if (!$client->query('my.hello', $args)) {
    die('Something went wrong - '.$client->getErrorCode().' : '.$client->getErrorMessage());
}
echo $client->getResponse();

Как видите, создание XML-RPC сервера особой сложности не представляет. Конечно, самая сложная работа – это написание самих методов. Но не забывайте, что если у вас уже готов web интерфейс и логика по максимуму вынесена в модели, то вы сможете спокойно использовать её и в XML-RPC методах.

Успехов!

Полезная информация

Новогодний подарок от Inferno Solutions — всем новым клиентам панель ISP бесплатно и 30$ в подарок при заказе VPS в Германии, Украине или Голландии! Укажите при заказе в примечании ISP+30.

  • Igor_tmb

    Большое спасибо!

  • Spot

    У клиента обязательно должна быть подключена IXR_Library.php?
    А если клиент не на PHP?

    • Подойдёт любая библиотека для отправки XML-RPC запросов. Вообще эти запросы можно сформировать и без библиотеки, т.к. они представляют собой данные в XML формате.

  • Spot

    Как передавать клиенту идентификатор сессии, чтоб отследить состояние клиента (онлайн/офлайн)

    • Я не совсем понимаю. Если клиент офлайн, то он вам не ответит не зависимо от того, передаёте вы ему идентификатор сессии или что-либо другое.
      А вообще идентификатор сессии можно передать вместе со всеми остальными данными запроса.

  • Spot

    Хотелось бы посмотреть пример метода получения данных из модели и передачи результата клиенту

    • Можно просто передать массив

      $file = Media::model()->findByPk($id);
      return $file->getAttributes();

  • Spot

    Как вызвать метод из другой модели

    Допустим я хочу передать клиенту результат метода getall() из модели Rayon

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

      $data = Rayon::model()->getall();

  • Spot

    POST /vrpc/xmlrpc HTTP/1.0
    Host: yii1.localhost
    Content-Type: text/xml
    User-Agent: The Incutio XML-RPC PHP Library
    Content-Length: 183

    vrpc.getkuzov

    HTTP/1.1 200 OK
    Date: Wed, 22 May 2013 23:40:48 GMT
    Server: Apache/2.2.4 (Win32) mod_ssl/2.2.4 OpenSSL/0.9.8k PHP/5.2.12
    X-Powered-By: PHP/5.2.12
    Connection: close
    Content-Length: 506
    Content-Type: text/xml;charset=utf-8

    1Седан
    2Универсал
    3Хэтчбек
    4Микроавтобус

    Something went wrong — -32700 : parse error. not well formed

    Почему? Ведь все данные в UTF-8

    • Spot

      оказывается ошибку показывает когда включен yii-debug-toolbar, после его отключения ошибок нет

      • Вообще странно. Если yii-debug-toolbar добавил свою разметку, то
        естественно xml стал не валидным. Но тогда не понятно почему эту
        разметка не выводится в режиме отладки.

  • Spot

    У меня щас всего 2 метода, и объект класса IXR_Client создается внутри метода, если допустим у меня будет 10-20 методов, то каждый раз будет создан новый экземпляр. Так и должно быть?
    Или как сделать так, чтобы экземпляр класса IXR_Client создавался один раз, а в методах только будет только формирование и отправка запроса