Kohana → Как убить время? Спросите меня =)

Читайте, *** (цензура), список изменений, при смене версии фреймворка или языка программирования! Еще раз повторю — ЧИТАЙТЕ ВНИМАТЕЛЬНО! Именно из-за моей невнимательности я вчера потратил больше 4-х часов на отладку простейшего модуля системы (модуль flash-сообщений). Вдумайтесь — больше 4 часов жизни на десяток простейших строк приложения!

Расскажу подробнее… Все началось с того, что в новом проекте решил использовать свой модуль notify из предыдущих проектов, на которых он отлично работал. Используется он примерно так:

Notify::error('Сообщение об ошибке');
$this -> request -> redirect($url);

или так:

Notify::error('Сообщение об ошибке');
$this -> content = View::factory('page');

Если происходит редирект, notify сохраняет flash-сообщения в сессию и редиректит. Если происходит вывод страницы — выводит flash-сообщения и забывает про них. Достигается подобное использованием модуля Kohana-Dispatcher: проставляем запуск определенных событий в определенных местах приложения. Вот тут-то я и нарвался на неприятности…

Для ko3.0/ko3.1, я устанавливал события на редирект и на вывод страницы:

Events::register('system.render', 'Notify::shutdown');
Events::register('request.redirect', 'Notify::shutdown');

В bootstrap.php был примерно такой код:

...
Route::set('default', '(<controller>(/<action>(/<id>)))')
	->defaults(array(
		'controller' => 'main',
		'action'     => 'index',
	));

Events::run('system.init');

if (!defined('SUPPRESS_REQUEST')) {
	$request = Request::instance() -> execute();
	Events::run('system.render');
	echo $request
		-> send_headers()
		-> response;
}

Events::run('system.shutdown');

Как видно по коду, перед запуском приложения запускался эвент system.init, потом происходит обработка, вызываем system.render перед выводом страницы и уже в самом конце system.shutdown. Напомню, по system.shurtdown у меня вызывается Notify::shurtdown.

Редирект обрабатывался так:

public function redirect($url = '', $code = 302) {
	Events::run('request.redirect');
	return parent::redirect($url, $code);
}

В нем, перед самим редиректом срабатывал эвент request.redirect, по которому так же вызывался Notify::shurtdown.
В результате всех этих действий все работало отлично — не важно, был ли редирект или вывод на страницу — flash-сообщения выводились только один раз и никогда не терялись.

А вот при использовании такого-же подхода в ko3.2 сообщения всегда пропадали при редиректе! Я убил уйму времени, пытаясь понять что не так. Основная проблема поиска бага была в том, что flash-сообщения вообще не попадали в сессию. Могу долго рассказывать, как и где искал проблемный код, но это не по теме поста, в данный момент…

В итоге-же оказалось, что при редиректе Notify::shurtdown обрабатывался 5 (ПЯТЬ) раз подряд, соответственно в первый раз notify сохранял в сессию flash-сообщения и удалял их из памяти, как использованные. Но следующий вызов Notify::shurtdown проверял, что flash-сообщений нет и сохранял поверх записанных пустой массив — вот так пропадали данные. Пошаговой проверкой выяснилось, что основная проблема именно в методе redirect объекта Request.

Вот так данный метод выглядит в ko3.1:

public function redirect($url = '', $code = 302)
{
	if (strpos($url, '://') === FALSE)
	{
		// Make the URI into a URL
		$url = URL::site($url, TRUE);
	}

	// Set the response status
	$this->status = $code;

	// Set the location header
	$this->headers['Location'] = $url;

	// Send headers
	$this->send_headers();

	// Stop execution
	exit;
}

а вот так, в ko3.2:

public function redirect($url = '', $code = 302)
{
	$referrer = $this->uri();

	if (strpos($referrer, '://') === FALSE)
	{
		$referrer = URL::site($referrer, TRUE, Kohana::$index_file);
	}

	if (strpos($url, '://') === FALSE)
	{
		// Make the URI into a URL
		$url = URL::site($url, TRUE, Kohana::$index_file);
	}

	if (($response = $this->response()) === NULL)
	{
		$response = $this->create_response();
	}

	echo $response->status($code)
		->headers('Location', $url)
		->headers('Referer', $referrer)
		->send_headers()
		->body();

	// Stop execution
	exit;
}

Принципиальная разница, как можно заметить, в том, что в ko3.2 вместе с заголовками формируется обычный вывод, а в ko3.1 только заголовки отправляются. Вот на этом я и попал, как говориться…

Решение проблемы простое — оставить эвент только на system.shurtdown, т.к. при редиректе все-равно он сработает в ko3.2. После небольших правок все заработало как надо, остался только осадок из-за собственной невнимательности =(.

Кстати, про изменение данного метода я нигде информации не встречал. Про некоторые изменения в ko3.2 относительно ko3.1 можно почитать на хабре. Из тех, что там указаны, для меня были актуальны лишь малая часть:

1. #4062 — Добавлен метод Validation::data(), вместо метода Validation::as_array(), который потом будет удален. Метод Validation::as_array() устарел и в последствии будет удален. Кому интересно, почему — идите по ссылке.

2. #3992 — Request::body() может принимать в качестве входящего параметра указатели файлов или ресурсов. Это позволит отправлять файлы сразу в браузер без необходимости считывать их содержимое в память.

3. #3942 — Стандартные контроллеры Controller_Template и Controller_Rest теперь вызывают parent::before() до каких-либо манипуляций.

4. #3536 — Удалена поддержка параметров в методах контроллеров. Я уже с 3.1 использовал только $this -> request -> param, в принципе, но считаю важным упомянуть этот момент.

5. #3183 — Kohana::modules() теперь выбрасывает исключение если вы пытаетесь инициализировать несуществующий модуль. На этом я уже успел споткнуться =))). Раньше, еще до создания какого-либо модуля, прописывал его подключение в bootstrap.php. Сильно удивился, когда в этот раз поймал исключение.

6. #3000 — Изменена система работы с конфигами. На данном моменте хочу остановиться чуть подробнее. Честно — не понимаю разработчиков фреймворка в данной ситуации. Ничего не мешало оставить предыдущий синтакцис, хотя бы в качестве совместимости: Kohana::config(‘global.email.notify’); Теперь же приходится делать примерно так: Kohana::$config -> load(‘global’) -> get(’email.notify’); Для меня это дико не удобно =(. «Прикрытие» разработчиков — «возможность не только читать конфиги, но и писать их». Ну если разработчики выносят из самого фреймворка работу с базой (хотя это один из основных функционалов), то для чего в нем запись конфигов??? Ну если это так необходимо — можно было сделать в виде модуля, а не внедрять в код фреймворка…

7. #2760 — Улучшение механизма кэширования запросов. Честно — до данного момента не использовал, сам формировал кеш. Надо будет поизучать возможности…

Собственно — все. Остальные, лично для меня, из данного списка — не существенны…

P.S.: вообще, kohana сильно расстраивает. Еще недавно я писал проект на ko3.0.x, а теперь даже половину предыдущего кода не могу использовать. По моему скромному мнению, разработчики фреймворка выбрали неверное направление развития. Надо было остановиться на ko3.0, избавиться от большинства глюков, вынести в отдельные модули все доп.возможности (fragments, например), создать нормальную полноценную документацию (нормально ли, что новички идут на сторонний вики, которая уже не актуальна, а не на офф.документацию? o_O), нарастить комьюнити разработчиков и только после этого менять версию на 3.1, да и то аккуратно.

Разработчики-же фреймворка, решили почаще обновлять релизы, вместо планомерного развития. Именно поэтому и возникают такие глюки, как в моем случае. Подумайте сами: вместо использования фреймворка (а именно в этом задумка всех фреймворков — облегчение и увеличение скорости разработки), программисты вынуждены каждые полгода полностью изучать новую версию фреймворка, т.к. через год текущая версия просто не будет поддерживаться!

Я уже полгода ищу альтернативу, но пока не могу найти… Yii слишком заумный (да и за C в начале каждого класса хочется убить разработчиков =)). Symfony, ZF, CakePHP — не приглянулись, как не пытался. Форк коханы, Fuel — не удобный. CI2 уже не мое — привык к некоторым плюшкам, которых нет в codeigniter. Если есть что-то на примете, что похоже на ko3.1, но при этом развивается стабильнее — дайте знать, plz.