Недавно взялся за проект на микрофреймворке fatfree или как его ещё именуют F3. В целом, этой мой первый серьёзный опыт работы с такой крохой, весит он всего 55k. При этом очень шустрый, имеет готовые решения:
- Роутинг
- Встроенное кэширование
- Active Record
- Шаблонизатор
- Расширяемость через плагины.
Выглядит всё это довольно внушительно, примеры уровня «Hello world» смотрятся потрасающе, но что будет, коль копнуть поглубже.
Начинаем. Нам нужен php 5.3. Вау, я ожидал массу фишек, а оказалось, что это скорее рекламный ход. Здесь мы встречаем первую неожиданность -неймспейсы ни то чтобы не используются, а даже наоборот, всё валится в единую кучу. Имеется базовый класс Core и все наши переменные сунутся через F3::set в self::$global. Понятное дело, что в серьёзных проектах большая вероятность переписать переменную и сломать всё к чертям.
Ладно, будем аккуратны. Начинаем писать основу для лёгкой конфигурации и какой-никакой MCV. Очень советую при скачивании fatfree скачать и примеры блога построенные на нём. Здесь мы обнаруживаем первый плюс, изначально продумано наличия dev и production версии, у нас есть переменные RELEASE и DEBUG, хотя ведут себя они странно.
Я работал с последней стабильной версией fatfree 1.4.4, в котором оказалось парочка критичных багов, особенно много проблем оставило решение для работы с базой данных. В трекере нашлось 2 серьёзные заплатки к данной версии и то, одну пришлось доделывать самому. Неприятная особенность, что при разработке с включённой отладкой ошибки не отображаются, если используется редирект F3::reroute.
Очень полезным явлется возможность использование ini файлов, значения из которых по сути являются аналогом F3::set($key, $value); Конфиг подключается через F3::config(‘config/config.ini’); В примере блога есть серьёзный баг и этот конфиг не закрыт от доступа по прямой ссылке, поэтому я создал отдельную директорию для конфигов и закрыл её через .htacccess
order deny,allow deny from all
Настроив конфигурацию и инициализировав нужные данные дошла очередь до написание первого контроллера. И здесь случился самый толстый минус — невозможно работать нормально с классами для контроллеров. Контроллеры можно задавать кучей способов:
- Через замыкание F3::route(‘GET /’, function() { … })
- Через кэлбэк F3::route(‘GET /’, ‘function_name’);
- Через кэлбэк, но используя класс и статические методы F3::route(‘GET /’, ‘class_name::method_name’)
- Через объекты F3::route(‘GET /’, array(new class_name(), ‘method_name’))
Использование объектов было бы предпочтительнее, потому что поддерживает нормальное наследование, состояния и вызов через $this. Но в способе 4-ре нужно передавать именно объект, иначе будет статический вызов в обход конструктора класса. Но все роуты необходимо прописать до вызова диспетчеризации F3::run(). Естественно плодить объекты просто для роутеров не хочется и пришлось смириться со статикой, которая, как мы знаем, не является ООП. Я использовал вариант три, его очень удобно прописывать в ini конфиге.
Для того, чтобы вынести все контроллеры f3 в отдельную папку я задал F3::set(‘AUTOLOAD’, ‘application|autoload’); Подгрузка осуществляется примерно так:
- Сканируются папки и автозагрузки.
- Подключается файл согласно названию кэлбэка в нижнем регистре.
Я лишь в самом конце просёк фишку, что для автолода можно прописывать несколько папок, до этого всё складывал в одной папке и плагины, и контроллеры. «смешались в кучу люди, кони …». В примере ООП блога с их сайта делалось точно так же, в документации тоже нет намёка, спасло копание в коде.
В контроллере мы используем в основном F3::set F3::get для работы с глобальными переменными, которые в последствии могут использоваться в других action и в шаблонах. F3::call позволяет вызвать другой action. Вот пример:
<?php class App_Article { static function show() { // получаем данные $article = new Axon('article'); $article->load('alias=\'{@PARAMS["articleId"]}\''); // устанавливаем значения переменных F3::set('pagetitle', $article->title); F3::set('content', $article->content); F3::set('template', 'article'); // это название подшаблона, который инклудится в основной F3::call('render'); // рендерим основной шаблон вызывая другой action } static function adminArticle() { F3::call('App_User::authAdmin'); // проверяем права $article = new Axon('article'); F3::set('entries', $article->find()); // выбираем список всех записей F3::set('pagetitle', 'Статьи'); F3::set('template', 'admin/article/list'); F3::call('render'); } // вызывается для GET метода static function adminArticleEdit() { F3::call('App_User::authAdmin'); $article = new Axon('article'); $article->load('id=\'{@PARAMS["articleId"]}\''); $article->content = preg_replace('~{(@[a-z0-9_]+)}~', "[$1]", $article->content); // костыль для шаблонизатора $article->copyTo('POST'); F3::set('pagetitle', 'Статьи::Редактирование'); F3::set('template', 'admin/article/edit'); F3::call('render'); } // вызывается для POST метода static function adminArticleEditDo() { F3::call('App_User::authAdmin'); F3::input('title', function($value) { if (!F3::exists('message')) { if (empty($value)) { F3::set('message', 'Заполните поле "Заголовок"'); } } } ); if (!F3::exists('message')) { if (!F3::get('POST.content')) { F3::set('message', 'Заполните поле "Содержание"'); } } // вот такая геморная валидация if (!F3::exists('message')) { $article = new Axon('article'); $article->load('id=\'{@PARAMS["articleId"]}\''); if (!$article->dry()) { $article->title = '{@POST.title}'; $article->content = '{@POST.content}'; $article->save(); F3::reroute('/admin/article'); } } F3::call(__CLASS__ . '::adminArticleEdit'); } } ?>
Очень неудобно делать валидацию и работать с переменными через set-get и особенно их скрытые варианты
$article->title = '{@POST.title}';
Но в целом реализация так себе, лучше чем без фреймворка, но хуже чем с нормальным.
Я уже показал работу с моделями, используется паттерн active record, миниманилистическая реализация, довольна удобная для простых операций, но имеет массу глюков. Сложилось впечатление, что это рудиментарный элемент, так как разработчики предпочитают MongoDb, класс для которого есть, а Axon даже не тестируют перед релизом. Подробнее можно посмотреть в документации на сайте, она довольно подробная и радует глаз.
Теперь шаблонизатор. Это наиболее понравившаяся вещь. Используется html синтаксис для тегов <F3:check if=»{preg_match(‘/404/’,@ERROR.code)}»> и {} для простой вставки переменных или вызова разрешённых функций. Но вот пропитанность регулярками это зло. Недавно на phpclub.ru пользователь grigori демонстрировал как можно заставить падать в корку php подсунув на валидацию строку из 180 символов с необязательным (.*).
Конструкция <F3:include href=»tpl/{@ERROR?’error’:@template}.htm»/> позволила легко реализовать двухшаговую шаблонизацию с мастер и слейв шаблоном.
Но и он не без багов, у меня возникли проблемы при использовании {} в шаблонах для писем, которые брались из базы.
На сим первая часть заканчивается. Общался с заказчиком и он разрешил использовать наработки по его проекту для статьи, так что в следующей части буду демонстрировать код и выложу исходники каркаса для быстрого старта.
По мне проще юзать CodeIgniter или Yii
Они ненамного тяжелее, а первый вообще по своей концепции ориентирован на скорость.
По мне тоже, он не много удобнее голого php, но далёк от нормального фреймворка.
Здесь как бы сделана ставка на скорость, но с таким обилием регулярок php в реальных проектах будет при нагрузках падать в корку (предположительно). php не perl, расширение для регулярок страдает падениями. тот же Yii легко убивается из-за кривой регулярки для валидации url.
Получается он и в скорости разработки проигрывает, и в скорости работы тоже не даёт никаких преимуществ. Опять же в Yii гораздо удобнее и продуманнее сделано кэширование моделей. А fatfree — просто забавка, на пощупать, я бы не брался свои проекты на нём делать.
«>test
XSS у вас нет ))