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.