XML-RPC, CodeIgniter, LiveJournal и куча проблем :)

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

Недавно ко мне обратился читатель с просьбой помочь отправить сообщение в LiveJournal через XML-RPC протокол. При этом использовать нужно было библиотеку фреймворка CodeIgniter.

Честно говоря, когда я вижу такие вопросы, то сразу пробую найти готовое решение. Этот случай исключением не был и подходящая инструкция быстро нашлась. XML-RPC и кросспостинг в ЖЖ. Константин Лихачев в ней подробно рассказывает об отправке сообщений, единственное но – используется Incutio XML-RPC Library, а не встроенная библиотека CI.

Раз работает код с использованием Incutio XML-RPC, то сервис 100% рабочий и нужно просто правильно передать параметры в библиотеке CodeIgniter'а.

Думаю: «Почему бы не помочь человеку? Опыт работы с CI у меня есть, с документацией JiveJournal разбираться не нужно, т.к. список всех нужных параметров есть в статье Константина. Минут за 20 сделаю…» 🙂

Мне пора бы привыкнуть, что когда я так думаю, эти 20 минут часто превращаются в 2-3 часа. 🙂

Проблема была такая. Встроенная библиотека CodeIgniter требует, чтобы все параметры запроса были заданы в виде массива. При этом если нужно передавать структуры данных, получается куча вложенных друг в друга массивов.

И пропустить один из них (что я и сделал) или вставить лишний очень легко.

По-идее, именно для таких ситуаций предусмотрен режим отладки. Но в этом режиме библиотека CI выводит только XML ответ сервера, а мне нужно было посмотреть на XML строку, которая отправляется в запросе.

Примечание. Ответ сервера:

Can't use string ("Array") as a HASH ref while "strict refs" in use at /home/lj/cgi-bin/Apache/LiveJournal.pm line 1779.

лично мне ни о чем не говорит (на perl я не программирую). Кстати, тот же WordPress в таких случаях возвращает «parse error. not well formed» — на мой взгляд, более понятный ответ, хоть и не очень информативный.

В документации LiveJournal есть примеры XML запросов, которые нужно отправить для добавления записи, т.е. достаточно просто сравнить их со своими и все проблемы будут видны.

После нескольких неудачных попыток узнать что все-таки отправляется серверу, пришлось лезть в исходники библиотеки.

Чтобы вывести запрос я добавил строку

echo '<pre>'.htmlspecialchars($op).'</pre>';

в методе sendPayload перед

if ( ! fputs($fp, $op, strlen($op)))

Не очень элегантное решение, но зато сразу стало ясно, в чем проблема.

Привожу рабочий код отправки сообщения в livejournal.

class LiveJournal extends Controller {

	private $LJ_url = 'http://www.livejournal.com/interface/xmlrpc';
	private $LJ_login = 'your login';
	private $LJ_pass = 'your pass';
	
	function LiveJournal()
	{
		parent::Controller();
		
		//подключаем XML-RPC библиотеку
		$this->load->library('xmlrpc');
	}
	
	function index()
	{
		$this->xmlrpc->server($this->LJ_url);

		//отправляем challange-запрос
		$this->xmlrpc->method('LJ.XMLRPC.getchallenge');
		
		if (!$this->xmlrpc->send_request()) {
			echo $this->xmlrpc->display_error();
		}
		else {
			//если получен ответ с challenge-строкой, читаем его и отправляем запись 
			$challenge_response = $this->xmlrpc->display_response();
			
			//формируем массив с данными для создания записи
			$request = array(
			array(
				array(
					'username'=>array($this->LJ_login,'string')
					,'auth_method'=>array('challenge','string')
					,'auth_challenge'=>array($challenge_response['challenge'],'string')
					,'auth_response'=>array(md5($challenge_response['challenge'].md5($this->LJ_pass)),'string')
					,'ver'=>array('1','string')
					,'event'=>array('Заголовок записи','string')
					,'subject'=>array('Текст записи ...','string')
					,'year'=>array(2009,'int')
					,'mon'=>array(11,'int')
					,'day'=>array(12,'int')
					,'hour'=>array(5,'int')
					,'min'=>array(27,'int')
					,'props'=>array(
						array(
							'opt_backdated'=>array(true,'boolean')
							,'taglist'=>array('tag 1, tag 2, tag 3','string')
						)
						,'struct')
					,'security'=>array('public','string')
				)
				,'struct'
			)
			);

			//отправляем запрос серверу
			$this->xmlrpc->method('LJ.XMLRPC.postevent');
			$this->xmlrpc->request($request);
			if (!$this->xmlrpc->send_request()) {
				echo $this->xmlrpc->display_error();
			}
			else {
				$postData = $this->xmlrpc->display_response();
				//$postData['url'] - содержит адрес созданной записи
			}
		}
	}
}

Несколько пояснений. Публикация сообщения выполняется в 2 этапа.

1) Нужно получить challenge строку, которая хешируется (строка 36) вместе с вашим паролем и используется вместо него. Это сделано для того, чтобы не передавать пароль в открытом виде.

2) Отправка самого сообщения. Как раз тут и нужно формировать структуру с данными. Как видите, мы везде используем массивы с двумя элементами. Первый элемент содержит передаваемое значение, второй – тип этого значения. Если нужно передать структуру, то первый элемент должен быть массивом.

Выглядит такая конструкция громоздкой и заполнять ее нужно внимательно, особенно если вы привыкли к библиотеке вроде Incutio XML-RPC. Кстати, эту библиотеку не сложно подключить и использовать вместо стандартной CodeIgniter’а.

Вообще в последнее время складывается впечатление, что разработчики забросили фреймворк. Хотя, возможно, я не прав, а Ellislab просто все силы тратит на свою CMS, вторую версию ExpressionEngine, которая вроде бы будет работать на основе CodeIgniter.

Очень не хочется, чтобы они забросили CI.

Постовой

Что выгоднее, услуги приходящего бухгалтера или ведение бухгалтерии у профессионалов? Ответ здесь