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

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

codeigniter-livejournal-xml-rpc

Недавно ко мне обратился читатель с просьбой помочь отправить сообщение в 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.

Постовой

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

  • be3

    CI не заброшен, просто его разрабатываю с учетом нужд самой компании. Это и есть огромнейший минус, но плюсов на мой взгляд много больше. Хотя поддержка php 4 версии, имхо, бред, слишком много ограничений.

    • Такой минус есть у многих open source продуктов. С другой стороны раз в нужды компании входит разработка CMS (ExpressionEngine), то и CI будет скорее всего развиваться в правильном направлении 🙂

  • be3

    CI не заброшен, просто его разрабатываю с учетом нужд самой компании. Это и есть огромнейший минус, но плюсов на мой взгляд много больше. Хотя поддержка php 4 версии, имхо, бред, слишком много ограничений.

    • Такой минус есть у многих open source продуктов. С другой стороны раз в нужды компании входит разработка CMS (ExpressionEngine), то и CI будет скорее всего развиваться в правильном направлении 🙂

  • Владимир

    По поводу «не забросили CI» — я тут посмотрел в сторону Yii и должен сказать, что Yii намного «вкуснее». Да, порог вхождения вышем чем у CI, но оно того стоит.

    • Я тоже понемногу пробую работать с Yii, выглядит все очень интересно. Порог вхождения может быть и выше, но скорее всего только в том случае если человек не занимался ООП.
      В любом случае Yii у меня на первом месте в качестве альтернативы CI 🙂

      • be3

        Yii — зачем изучать еще один фреймворк?
        Мне кажется лучше начать учить еще один язык или совершенствовать основы, а с задачами php и codeigniter справится, слегка подпиленный.
        В замен codeigniter я выбрал python + django.

        • Дело в том, что с новым фреймворком разобраться проще и быстрее, чем с новым языком 🙂
          Учить Yii — громко сказано. При средних знаниях PHP и ООП через 3-4 дня можно уже нормально работать.
          Я python — другое дело. Сам язык, библиотеки, может быть какие-нибудь особенности в настройке сервера, все это требует времени. Занятие, конечно, очень перспективное… язык перспективный.
          Кстати, я хотел спросить. Есть сейчас заказы на разработку на питоне (для фрилансеров)?

        • be3

          Не так много как бы хотелось. Но, питон можно использовать и при работе с пыхой, решать серверные задачи +) Здесь скорее можно рассчитывать на парочку постоянных заказчиков.
          Если говорить о потоке заказов, то тут лучше всего подходит zend и drupal, вот уж где очень много проектов и оплата соответствующая.

          А по части настройки сервера и вообще, все очень просто +) По крайне мере в linux, поставить питон и написать первый скрипт, заняло порядка 5 минут +)
          А пхп все равно устает, ну представь, на скольких C подобнымх языках ты писал? Один и тот же синтаксис, просто морально устаешь.

        • по части настройки сервера и вообще

          Это понятно, что «hello,world» можно за 5 минут запустить. Я имел ввиду, например, какие-то нюансы в настройке, которые могут проявиться при высокой нагрузке или что-нибудь подобное… не знаю точно, может быть действительно все просто и очевидно.

          Устать, ИМХО можно от любого синтаксиса. Просто пока учишь новый язык, есть энтузиазм, а потом, когда пишешь примерно один и тот же код в 10 раз — энтузиазма нет 😉

  • Владимир

    По поводу «не забросили CI» — я тут посмотрел в сторону Yii и должен сказать, что Yii намного «вкуснее». Да, порог вхождения вышем чем у CI, но оно того стоит.

    • Я тоже понемногу пробую работать с Yii, выглядит все очень интересно. Порог вхождения может быть и выше, но скорее всего только в том случае если человек не занимался ООП.
      В любом случае Yii у меня на первом месте в качестве альтернативы CI 🙂

      • be3

        Yii — зачем изучать еще один фреймворк?
        Мне кажется лучше начать учить еще один язык или совершенствовать основы, а с задачами php и codeigniter справится, слегка подпиленный.
        В замен codeigniter я выбрал python + django.

        • Дело в том, что с новым фреймворком разобраться проще и быстрее, чем с новым языком 🙂
          Учить Yii — громко сказано. При средних знаниях PHP и ООП через 3-4 дня можно уже нормально работать.
          Я python — другое дело. Сам язык, библиотеки, может быть какие-нибудь особенности в настройке сервера, все это требует времени. Занятие, конечно, очень перспективное… язык перспективный.
          Кстати, я хотел спросить. Есть сейчас заказы на разработку на питоне (для фрилансеров)?

        • be3

          Не так много как бы хотелось. Но, питон можно использовать и при работе с пыхой, решать серверные задачи +) Здесь скорее можно рассчитывать на парочку постоянных заказчиков.
          Если говорить о потоке заказов, то тут лучше всего подходит zend и drupal, вот уж где очень много проектов и оплата соответствующая.

          А по части настройки сервера и вообще, все очень просто +) По крайне мере в linux, поставить питон и написать первый скрипт, заняло порядка 5 минут +)
          А пхп все равно устает, ну представь, на скольких C подобнымх языках ты писал? Один и тот же синтаксис, просто морально устаешь.

        • по части настройки сервера и вообще

          Это понятно, что «hello,world» можно за 5 минут запустить. Я имел ввиду, например, какие-то нюансы в настройке, которые могут проявиться при высокой нагрузке или что-нибудь подобное… не знаю точно, может быть действительно все просто и очевидно.

          Устать, ИМХО можно от любого синтаксиса. Просто пока учишь новый язык, есть энтузиазм, а потом, когда пишешь примерно один и тот же код в 10 раз — энтузиазма нет 😉

  • GiN

    Владимир, огромное спасибо, что помогли )
    Как я только не мучался с этим исходным массивом для отправки.
    Теперь все понятно =)
    Теперь буду экспериментировать дальше с xml-rpc и ЖЖ.

  • GiN

    Владимир, огромное спасибо, что помогли )
    Как я только не мучался с этим исходным массивом для отправки.
    Теперь все понятно =)
    Теперь буду экспериментировать дальше с xml-rpc и ЖЖ.

  • GiN

    Владимир, не сочтите за дерзость, но у меня есть маленькие заметки-наблюдения =):

    1. event — текст самой записи, а subject — тема. но это мелочь.
    2. ЖЖ отлично «кушает» формат 'ver'=>array(2,'int') вместо 'ver'=>array('1','string');
    3. самое интересное:
    3.1 CI 1.7.2 на локальной машине под Денвером получает в ответ от сервера LJ ошибку «Application failed during request deserialization: unclosed token at line 107, column 0, byte 1490 at /usr/lib/perl5/XML/Parser.pm line 187» если хоть что-то из «event«, «subject» или «taglist» написано по-русски!. Может только у меня.
    3.2. как только выложил на хостинг под CI 1.7.1 с русскими словами ошибки не возникло никаких проблем.

    до этого я пытался исключительно на локальной машине это запустить и ошибка из п.3.1 у меня уже была. поэтому, я немного удивился когда даже Ваш код у меня не отработал сразу. На локальной машине под Денвером все отправляется отлично, если написано по-английски. (UTF-8 использую всегда и повсюдю, так что не в кодировке дело). Идентичный код, но на сервере, отработал на «ура».

    спасибо Вам огромное еще раз =)

    • 1) Согласен, я ошибся. С ЖЖ не работал (и в общем-то не планирую), поэтому внимания не обратил 😉

      2) К сожалению это один из сложных моментов в работе с XML-RPC. Тут все зависит от разработчиков сервера. Можно написать код так, что он будет принимать оба типа параметров и правильно их обрабатывать, а можно так, что ошибка будет возникать если тип не соответствует указанному. В любом случае лучше всегда ориентироваться на документацию. Если там сказано int, то и отправлять нужно int. (Я сам типы параметров ЖЖ не проверял, ориентировался на статью Константина).

      3) я тестировал именно с версией 1.7.2. Использовал WAMPSERVER на локальной машине. Посоветовать могу только сравнить конфиги PHP и Apache, ну и еще раз проверить кодировку (все-таки когда речь идет о русских буквах, то на 99,9% проблема с кодировкой, кстати, можно посмотреть в php.ini параметр default_charset).

      • GiN

        пожалуй вы правы. по умолчанию у меня в php.ini (который от Денвера) default_charset вообще закоментирован. Так что, наверно, либо в php.ihi, либо в .htaccess, либо напрямую прописывать header('Content-Type: text/html; charset=utf8'); и будет счастье.

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

  • GiN

    Владимир, не сочтите за дерзость, но у меня есть маленькие заметки-наблюдения =):

    1. event — текст самой записи, а subject — тема. но это мелочь.
    2. ЖЖ отлично «кушает» формат 'ver'=>array(2,'int') вместо 'ver'=>array('1','string');
    3. самое интересное:
    3.1 CI 1.7.2 на локальной машине под Денвером получает в ответ от сервера LJ ошибку «Application failed during request deserialization: unclosed token at line 107, column 0, byte 1490 at /usr/lib/perl5/XML/Parser.pm line 187» если хоть что-то из «event«, «subject» или «taglist» написано по-русски!. Может только у меня.
    3.2. как только выложил на хостинг под CI 1.7.1 с русскими словами ошибки не возникло никаких проблем.

    до этого я пытался исключительно на локальной машине это запустить и ошибка из п.3.1 у меня уже была. поэтому, я немного удивился когда даже Ваш код у меня не отработал сразу. На локальной машине под Денвером все отправляется отлично, если написано по-английски. (UTF-8 использую всегда и повсюдю, так что не в кодировке дело). Идентичный код, но на сервере, отработал на «ура».

    спасибо Вам огромное еще раз =)

    • 1) Согласен, я ошибся. С ЖЖ не работал (и в общем-то не планирую), поэтому внимания не обратил 😉

      2) К сожалению это один из сложных моментов в работе с XML-RPC. Тут все зависит от разработчиков сервера. Можно написать код так, что он будет принимать оба типа параметров и правильно их обрабатывать, а можно так, что ошибка будет возникать если тип не соответствует указанному. В любом случае лучше всегда ориентироваться на документацию. Если там сказано int, то и отправлять нужно int. (Я сам типы параметров ЖЖ не проверял, ориентировался на статью Константина).

      3) я тестировал именно с версией 1.7.2. Использовал WAMPSERVER на локальной машине. Посоветовать могу только сравнить конфиги PHP и Apache, ну и еще раз проверить кодировку (все-таки когда речь идет о русских буквах, то на 99,9% проблема с кодировкой, кстати, можно посмотреть в php.ini параметр default_charset).

      • GiN

        пожалуй вы правы. по умолчанию у меня в php.ini (который от Денвера) default_charset вообще закоментирован. Так что, наверно, либо в php.ihi, либо в .htaccess, либо напрямую прописывать header('Content-Type: text/html; charset=utf8'); и будет счастье.

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

  • GiN

    Владимир, уж простите, что достаю, но просто хотел кое что отметить. Опция opt_backdated, если установлена в true, еще и запрещает показ сообщения во френдлентах друзей. Так что пусть если что никто не удивляется — почему пост есть, а френды его не видят через ленту.
    =)

    • Я никогда не считаю комментарии попыткой меня достать 🙂
      А чем больше полезной информации вы приведете, тем лучше, ценность этой статьи только повысится. Может кому-то поможет.

      Сам я разбираться с LJ не буду (во всяком случае не планирую).

  • GiN

    Владимир, уж простите, что достаю, но просто хотел кое что отметить. Опция opt_backdated, если установлена в true, еще и запрещает показ сообщения во френдлентах друзей. Так что пусть если что никто не удивляется — почему пост есть, а френды его не видят через ленту.
    =)

    • Я никогда не считаю комментарии попыткой меня достать 🙂
      А чем больше полезной информации вы приведете, тем лучше, ценность этой статьи только повысится. Может кому-то поможет.

      Сам я разбираться с LJ не буду (во всяком случае не планирую).

  • Стоит посмотреть в сторону Recess фреймворка, молодой и без болезней присущих CI

    • Спасибо за совет. Я посмотрел парочку их скринкастов, выглядит действительно очень неплохо.
      Recess

  • Стоит посмотреть в сторону Recess фреймворка, молодой и без болезней присущих CI

    • Спасибо за совет. Я посмотрел парочку их скринкастов, выглядит действительно очень неплохо.
      Recess

  • Подскажите элементарный CMS для фотогалереи без базы данных
    чтоб можнонужно было просто залить изображения в папку по FTP
    и они автоматически добавились в галерею, желательно с простой админкой
    разрешающей массово редактировать фотки, прописать к ним описание
    и управлять коментариями, возможно так-же и функция оценки изображений.
    В итоге должно получится что-то вроде того что есть на фотогалерееhttp://dyon.ru/
    Только там отсутствует админка.

    • К сожалению, я с такими не работал. Поэтому подсказать не могу.

  • Подскажите элементарный CMS для фотогалереи без базы данных
    чтоб можнонужно было просто залить изображения в папку по FTP
    и они автоматически добавились в галерею, желательно с простой админкой
    разрешающей массово редактировать фотки, прописать к ним описание
    и управлять коментариями, возможно так-же и функция оценки изображений.
    В итоге должно получится что-то вроде того что есть на фотогалерееhttp://dyon.ru/
    Только там отсутствует админка.

    • К сожалению, я с такими не работал. Поэтому подсказать не могу.

  • Cykooz

    Скажите, а кто нибудь потом побывал редактировать добавленный пост через веб-интерфейс ЖЖ?
    У меня выдаёт ошибку на странице редактирования:
    ——
    Client error: Invalid text encoding: Cannot display this post. Please see http://www.livejournal.com/support/encodings.bml for more information.
    ——

    При этом пост нормально отображается на других страницах и ни каких проблем с кодировками нету.
    Правда скрипт делал я на Python и использовал для отправки стандартный модуль xmlrpclib, но мне кажется что дело не в этом. Все тексты передавал в кодировке UTF-8.

  • Cykooz

    Скажите, а кто нибудь потом побывал редактировать добавленный пост через веб-интерфейс ЖЖ?
    У меня выдаёт ошибку на странице редактирования:
    ——
    Client error: Invalid text encoding: Cannot display this post. Please see http://www.livejournal.com/support/encodings.bml for more information.
    ——

    При этом пост нормально отображается на других страницах и ни каких проблем с кодировками нету.
    Правда скрипт делал я на Python и использовал для отправки стандартный модуль xmlrpclib, но мне кажется что дело не в этом. Все тексты передавал в кодировке UTF-8.

  • 20 минут в 2-3 часа — это точно.. 🙂 Сколько раз сам так накалывался.. и всё равно продолжаю… Казалось бы мелочь, а нет-нет да и наткнешься.. зацепишься.. и пошло-поехало..

  • Kubig

    А подскажите пожалуйста по поводу примеры XML запросов для постинга в сообщество, Я что то в документации нечего подобного не нашёл.

  • К сожалению, у меня таких примеров нет.
    Вариант, указать в username имя сообщества, не проходит?

  • Kubig

    В принципе Я уже разобрался но у меня проблема с кодировкой, Я делал несколько иначе на cURL с функцией xmlrpc_encode_request();
    если не сложно глянь пожалуйста http://goo.gl/uvg4

  • На форуме, похоже, правильно ответили.
    В третьем параметре функции xmlrpc_encode_request() можно указать кодировку.

    xmlrpc_encode_request(…, …, array('encoding'=>'utf-8'))

    По-умолчанию установлена iso-8859-1.

  • Kubig

    Я не нашёл полной документации на эту функцию. Если сработате то буду очень признателен.

  • Kubig

    блин так и не получается, когда делаю так как ты написал нечего не отправляется вообще, хотя var_dump показывает что массив сформирован. Можно Я тебе весь скрипт отправлю ты сам попробуешь?

  • Можно 🙂 Отправляй.

    Описание функции <a
    href=»http://php.net/manual/en/function.xmlrpc-encode-request.php»>здесь.

  • Kubig

    Отправил… буду рад помощи.

  • Проблема действительно оказалась в функции xmlrpc_encode_request.
    Все нормально работает если использовать библиотеку The Incutio XML-RPC Library

  • Kubig

    Это так печально =((((((

  • Mewspambox

    Подскажите, как отправить xml-запрос в контроллер на CodeIgniter'а? Шлю например так:
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_HEADER, array(«Content-Type: text/xml; charset=utf-8», «Content-Length: «.strlen($xml)));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml );
    $res = curl_exec($ch);
    До контроллера запрос не доходит, CI возвращает ответ:
    Disallowed Key Characters
    Уже пробовал разные комбинации опций curl, ничего не помогает. Вне CI запрос принимается без проблем. Очень нужно!

  • Посмотрите в файле config.php параметр
    $config['permitted_uri_chars']

    Кстати, есть обсуждение этих проблем на форуме.

  • Прежде всего попробуйте
    $config['permitted_uri_chars'] = «»;

  • Mewspambox

    Не помогло… С URI никаких проблем нет, по всей видимости CI стопорится при разборе POST сообщения… Вот например если шлю POST в контроллер CI:
    $xml = «<?xml version='1.0' encoding='UTF-8'?><item name='aaa'>bbb</item>»;
    $url = «http://test.lan/index.php/send»;
    то до контроллера даже не доходит, сам CI возвращает ошибку «Disallowed Key Characters».
    а если в простой php (напр. $url = «http://test.lan/test.php» — то все нормально, test.php принимает запрос, отвечает, и ответ я получаю.

  • Mewspambox

    Решил. Создал тему — http://code-igniter.ru/forum/topic1949.html
    Присоединяйтесь 🙂

  • Спасибо за статью. Очень пригодилась.

    Только в строках 38-39 кажется перепутали: 
    ,'event'=>array('Текст записи','string'),'subject'=>array('А тут заголово …','string')

    • Может быть. Но это не принципиально 😉

  • Tester_grep

    Пользуюсь этим фреймворком уже несколько лет. Удобная справка и вообще хорошо документирован.
    Кстати, появилось руководство пользователя на русском для версии 2.0.0
    http://codeigniter.su