В предыдущей статье я описал основные изменения ядра фреймворка, затронувшие механизм обработки запроса-ответа. Пришло время описать прочие новшества, достойные нашего внимания.
Правила именования файлов
- Поддержка стандарта PSR-0 привела к тому, что все имена файлов классов в Kohana 3.3 должны начинаться с большой буквы. В регистрочувствительных системах (т.е. все кроме Windows) это важно.
- Для корректной работы старых модулей, не приведенных к новому стандарту имен файлов, добавили метод
Kohana::auto_load_lowercase()
. Просто добавляете его к стандартному автозагрузчику в bootstrap.php и можно продолжать работать:spl_autoload_register(array('Kohana', 'auto_load_lowercase'));
- Добавилась поддержка namespace‘ов в именах классов. Правила все те же — namespace (если он есть) будет преобразован в путь к классу, в качестве разделителей используется стандартный разделитель в PHP — ‘\\’.
CLI
- Убрали класс CLI (был только метод
CLI::options()
, перенесен вMinion_CLI::options()
). #3989 - Из класса
Kohana
убрали статическое свойство$is_cli
. Оно теперь не требуется, т.к. вся обработка CLI идет через модуль Minion.
класс Kohana (Kohana_Core)
- Убрали свойство
Kohana::$log_errors
. Оно все равно не использовалось, при обработке исключений стандартным методомKohana_Exception::_handler()
лог теперь пишется. Осталось не забыть про логирование в своих обработчиках классовHTTP_Exception
. - При инициализации ядра (метод
Kohana::init()
) добавили проверку на наличие подключенных логов и конфигов в свойствахKohana::$log
иKohana::$config
. Теперь можно спокойно добавлять свои обработчики логов/конфигов ДО вызоваKohana::init()
и не бояться, что они пропадут. - Добавлен метод
Kohana::version()
, который просто возвращает строку с номером версии фреймворка и ее кодовым именем. Можно использовать для вывода информации где-нибудь в «подвале» сайта мааааленькими буквами.
Request
Request::factory()
поменял перечень принимаемых параметров. Теперь он выглядит следующим образом:/** * * @param string $uri URI of the request * @param array $client_params An array of params to pass to the request client * @param bool $allow_external Allow external requests? (deprecated in 3.3) * @param array $injected_routes An array of routes to use, for testing * @return void|Request */ public static function factory($uri = TRUE, $client_params = array(), $allow_external = TRUE, $injected_routes = array())
Исключен аргумент
$cache
, в котором можно было передать объектHTTP_Cache
для кеширования запроса, вместо него добавили параметры$client_params
и$allow_external
(который только появился и уже объявлен deprecated).- По названию
$client_params
можно догадаться, что это параметры, которые будут переданы вRequest_Client
. На данный момент есть ограничение — будут использованы только те параметры, для которых есть одноименный сеттер в объектеRequest_Client
. Это ‘cache‘ (передаем объектHTTP_Cache
), ‘follow‘ (булевский флаг, который разрешает или запрещает редирект), ‘follow_headers‘ (массив HTTP-заголовков, которые надо передать дальше после редиректа) и ‘strict_redirect‘ (если TRUE, то при 302 редиректе будет сохранен исходный метод HTTP-запроса, как того требует HTTP/1.1 — это поведение по умолчанию).Перечень параметров будет дорабатываться и расширяться. Например, сейчас в ветку
3.3/develop
добавленыheader_callbacks
(#4353). - Параметр
$allow_external
позволяет запретить использованиеRequest_Client_External
, т.е. все поступающие запросы будут обработаны черезRequest_Client_Internal
, как внутренние.
- По названию
- Убран метод
response()
, который позволял получить текущий объектResponse
. - Удален метод
generate_etag()
. В версии 3.2 это был дефолтный способ сгенерировать etag из содержимогоResponse
объектаRequest
. В 3.3 классRequest
уже не содержит свойство$_response
, поэтому данный метод доступен только из классаRequest
. - Методы
query()
иpost()
поддерживают многомерные массивы (применяетсяArr::path()
вместоArr::get()
). Т.е. теперь можно использовать$request->query('foo.bar')
, чтобы получить$_GET['foo']['bar']
.
Роутинг
- Фильтры. Одно из самых заметных нововведений. В 3.3 к каждому маршруту можно добавлять условия, влияющие на итоговые параметры маршрута (контроллер, экшен и т.д.). Более того, с помощью фильтров можно даже указать, что данный маршрут не подходит под предложенный URI. Приведу ниже несколько примеров, бессмысленных и беспощадных:
Используем анонимную функцию, чтобы запретить использование маршрута:
Route::set('default', '(<controller>(/<action>(/<id>)))') ->defaults(array( 'controller' => 'Welcome', 'action' => 'index', ))->filter(function(Route $route, $params) { if (Kohana::$environment === Kohana::PRODUCTION) return false; });
Если мы хотим указать, что данный маршрут не подходит, то надо вернуть FALSE из фильтра.
Вносим изменения в вычисленные параметры маршрута (меняем экшен в зависимости от используемого HTTP-метода):
Route::set('default', '(<controller>(/<action>(/<id>)))') ->defaults(array( 'controller' => 'Welcome', 'action' => 'index', ))->filter(function(Route $route, $params) { $method = Request::current() ? Request::current()->method() : Arr::get($_SERVER, 'REQUEST_METHOD', HTTP_Request::GET); switch($method) { case HTTP_Request::POST: $params['action'] = 'create'; break; case HTTP_Request::PUT: $params['action'] = 'change'; break; case HTTP_Request::DELETE: $params['action'] = 'delete'; break; default: $params['action'] = 'show'; } return $params; });
Обратите внимание, что необходимо вернуть массив параметров, если в него были внесены изменения. Если просто вернуть TRUE (или ничего не возвращать), то фильтр будет воспринят как прошедший проверку, но не поменявший параметры маршрута.
- Иногда бывает необходимо объединить кешированные маршруты с уже зарегистрированными в системе. Для этого в метод
Route::cache()
добавили второй параметр$append
. Если он выставлен в TRUE, то загруженные из кеша маршруты будет добавлены к существующим. В 3.2 они их полностью заменяли (впрочем, сейчас это осталось поведением по умолчанию).
Хэлперы
- Исправлена работа метода
Arr::get()
, теперь он нормально обрабатывает NULL-значения в массиве. #4012 - Добавлен метод
Arr::set_path()
для установки значения в многомерных массивах. Кроме того,Arr::extract()
теперь работает с многомерными массивами (используются путиArr::path()
вместо ключейArr::get()
). Arr::merge()
теперь объединяет многомерные массивы без замены старого значения на новое. Это в первую очередь полезно при работе с конфигами. Пример можно посмотреть в #3141.
Date::format_time()
теперь принимает в качестве третьего (опционального) параметра идентификатор времЕнной зоны (timezone).- В хэлпер
HTML
добавили статическое свойство$strict
. Если оно установлено в TRUE (по умолчанию), то сгенерированные элементы HTML будут содержать атрибуты в формате, принятом XHTML. Т.е. не<input type="checkbox" checked>
<input type="checkbox" checked="checked">
- При записи лог-сообщения автоматически будет добавлена отладочная информация (tracelog, имя файла, номер строки и т.д.). А в методе
Kohana::log()
появился четвертый параметр$additional
, в котором может содержаться различная полезная информация для записи в лог. В частности, если в$additional['exception']
сохранить объектException
, то трейслог будет взят из него. - Логирование сделано более настраиваемым. В классах-потомках
Log_Writer
можно переопределить методformat_message()
, чтобы установить свой формат текстовых сообщений в логах. Кроме того, добавились статические свойстваLog_Writer::$timestamp
иLog_Writer::$timezone
, позволяющие указать формат даты и временнУю зону для логов. - Можно указать свой метод для сериализации или кодирования (обфускации) содержимого сессии. Если раньше использовался
serialize()
иbase64_encode()
, то теперь они вынесены в методы_serialize()/_unserialize()/_encode()/_decode()
. То есть их можно переопределить, если вдруг понадобится. - Добавился метод
Text::user_agent($agent, $value)
. Точнее, не добавился, а был вынесен из методаRequest::user_agent($value)
. Он вычленял отдельный параметр из пользовательского User_Agent‘а (имя параметра передавалось в$value
), а значение User_Agent бралось изRequest
. Сейчас метод стал более универсальным, в него надо передавать User_Agent явно (например, братьRequest::$user_agent
). Возможные значения$value
:- ‘platform’ — имя операционной системы (‘Windows 7′, ‘Windows XP’, ‘FreeBSD’ и т.д.)
- ‘browser’ — имя семейства браузеров (‘Internet Explorer’, ‘Opera’, ‘Firefox’ и т.д.)
- ‘mobile’ — имя мобильного клиента (‘iPhone’, ‘Android’, ‘Opera mini’ и т.д.)
- ‘robot’ — имя бота (‘Googlebot’, ‘Yahoo’, ‘Facebook’ и т.д.)
Эти настройки берутся из конфиг-файла
user_agents.php
, и легко могут быть изменены под собственные нужды.Примеры использования:
$agent = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.18) Gecko/20110621 Fedora/3.6.18-1.fc14 Firefox/3.6.18'; $data = Text::user_agent($agent, 'browser'); // 'Firefox' $data = Text::user_agent($agent, array('version', 'mobile')); // array('3.6.18', false)
- В правило
Valid::range()
добавился 4-й параметр$step
(1 по умолчанию). Т.е. теперь можно проверять значение на нахождение в диапазоне значений с произвольным шагом (к примеру, [1,3,5,7,9,11]).
Исключения
- Добавлены классы для HTTP-кодов 300, 301, 302, 303, 304, 305, 307.
- Исключения семейства классов
HTTP_Exception
теперь могут сообщить, какойRequest
из «выбросил». Используйте методrequest()
для получения этого объекта. - Исключения
HTTP_Exception
имеют встроенный валидатор, проверяющий достаточность переданных данных для успешной обработки исключения. Для этого реализован методcheck()
, в котором мы можем для любого исключения добавить собственную проверку. Вот так, к примеру, проверяются исключения классаHTTP_Exception_Redirect
(коды 301-305, 307, 405):public function check() { if ($this->headers('location') === NULL) throw new Kohana_Exception('A \'location\' must be specified for a redirect'); return TRUE; }
Прочие изменения
- Поправили PHPDOC, особенно в части указания правильных типов возвращаемых объектов.
- Добавлены еще исключения в конфиг
inflector.php
.
UPD. Пока писал данную статью, вышел второй RC.
Спасибо за статью
надо посмотреть поподробнее на фильтры в роуте, до это статьи не знал про них.
Вот честно, если он так меняется каждые пол года, нужно ли оно?
Как-то меня это разочаровывает…
Огромное спасибо, Иван, что бы без тебя делали!
Но, вообще, ситуация не радует. Чтобы юзать Кохану у себя в проектах приходится становиться специалистом по Кохане нескольких версий. И апдейтить сложно, и поддерживать на старых версиях сложно — надо быть в курсе, как какая конструкция пишется в разных версиях, если потребуется что-то дописать.
@Vovochka
@medar
А вас кто-то заставляет переходить на новые версии? Для 3.2 и 3.1 также правятся баги. Не уверен, что новые релизы для этих веток будут к выходу 3.3.0, но к лету наверняка выпустят.
Ну тогда новые проекты придется тоже делать на 3.2 или 3.1, и вообще сидеть на этих ветках до скончания века. И через полгода или год, читая хэлп или какую-нить статью по кохане, вспоминать — есть ли такая функция в 3.2 или нет ?
Меня иногда спрашивают, какой фреймворк юзать для php, и как-то Кохану язык не поворачивается рекомендовать. Несмотря на удобную внутреннюю структуру и т.п. Подсаживать человека на такое существование.. Это какой-то неправильный путь.
ИМХО, не все так плохо. Во-первых, в 3.3 не так уж много серьезных изменений, и переход с 3.2 должен быть довольно-таки простым. Единственное, что с PHP <5.3 возникнут проблемы. Ну и с модулями вечная проблема, это наверное главный минус.
С другой стороны, если бы новые ветки релизились раз в пару лет, нашлись бы недовольные, что фреймворк плохо развивается.
В общем, было бы неплохо эту тему поднять на англоязычном форуме, наверняка много недовольных такой политикой. Обсудить, выслушать мнение разработчиков…
Тоже никому не советую уже кохану. Текущие изменения, не очень как-то и обоснованы. Развитие никакого, а по факту сплошная модификация одного и того же.
Сам же попробовал yii, сделал пол проекта на нем. По началу раза 3 хотел бросить и переделать на кохану, уж все казалось непривычно. И несмотря на наличие мануалов у уii многие детали не описаны. И сейчас уже понимаю, если писать что-то большое, что нужно постоянно поддерживать — определенно yii, для меленького проектика — kohana, и то очень спорный момент, так как yii и мелкие проекты переварит легко.
В перспективе, есть желание попробовать симфони 2.
А я тут недавно открыл для себя очень интересный фреймворк — laravel.com . Делает один человек, как и yii, только в отличие от yii он легкий и прозрачный. Меня он сразу подкупил.. наверное, это можно назвать здравым смыслом. Это как дух Рельсов на php. «Кто-то поработал головой». Миграции из коробки. Внятная валидация. Понятная короткая запись raw-запросов к базе, без «DB::query(Database::SELECT». Query Builder, ORM, но это у всех. Каталог бандлов-модулей, завязанный на гитхаб — прямо на официальном сайте. Сli-скрипт, для конфигурации, миграции, крона, юнит-тестов, установки и апгрейда бандлов и т.п. Как-то даже странно, что один человек за год сумел поднять все это с нуля, когда смотришь на то, что сделали разрабы Коханы за тот же год.
Фильтры это отлично. Надо бы скачать и попробовать
@bagir
Основные изменения в данной ветке больше похожи на рефакторинг, да. Правда, Minion весьма полезен, если говорить о новых фичах.
UPD. Да что уж говорить, развитие всей третьей ветки как один сплошной рефакторинг )) Но, по крайней мере, к чему-то более-менее стабильному мы приходим.
@medar
Насколько я знаю, Yii только в самом начале один человек делал. Кстати, 3.0 тоже практически в одиночку была написана Вуди.
А вот я, ребят, ну что греха таить, ну очень хотел бы, чтобы все мои конкуренты писали на YII… ну, в крайнем случае, на Laravel или Fuel
Мда, заглянул узнать что новенького в kohana, а в итоге ушел изучать laravel…
Кохана не торт, ухожу постепенно на django.
Хотя интересно что будет дальше, проекты поддерживать все-таки нужно
А мне нравится изменения) Люди не сидят сложа руки =)
А что с кэшированием роутов? Всё так же ошибки выдаёт о невозможности сериализации анонимных функций? Какой выход? Вообще роуты не кэшировать?
А какой может быть выход? Анонимки ведь не сериализуются. Если их отбрасывать, то кэширование получается ущербным. Правда, есть еще вот такой вариант — http://www.htmlist.com/development/extending-php-5-3-closures-with-serialization-and-reflection/
Ну хотя бы так:
Может это не самый красивый вариант, но думаю при желании можно что-то придумать
Ссылка интересная, спасибо!
Кстати, автор того класса Super_Closure пишет на Kohana. Сейчас он оформил класс в виде целой библиотеки — https://github.com/jeremeamia/super_closure.
Я ни где не могу найти примеры использования namespace в 3.3.
Не будет трудно описать?
Напоминаю о своей просьбе о namespace. Не могу разобраться как применить их в classes и использовать потом в контроллерах.
…А если никак, то в чем отличие от 3.2 ?..
Использовать в обычных классах проблем нет:
Потом просто \Foo\Bar::test().
С контроллерами и моделями будет сложнее, так как в них есть префикс. Не обойтись без не очень красивого хака:
Насколько я знаю, более красивого способа использовать namespace в контроллерах и моделях нет.