Разработка на фреймворке fatfree

Недавно взялся за проект на микрофреймворке fatfree или как его ещё именуют F3. В целом, этой мой первый серьёзный опыт работы с такой крохой,  весит он всего 55k. При этом очень шустрый, имеет готовые решения:

  1. Роутинг
  2. Встроенное кэширование
  3. Active Record
  4. Шаблонизатор
  5. Расширяемость через плагины.

Выглядит всё это довольно внушительно, примеры уровня «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

Настроив конфигурацию и инициализировав нужные данные дошла очередь до написание первого контроллера.  И здесь случился самый толстый минус — невозможно работать нормально с классами для контроллеров. Контроллеры можно задавать кучей способов:

  1. Через замыкание F3::route(‘GET /’, function() { … })
  2. Через кэлбэк F3::route(‘GET /’, ‘function_name’);
  3. Через кэлбэк, но используя класс и статические методы F3::route(‘GET /’, ‘class_name::method_name’)
  4. Через объекты F3::route(‘GET /’, array(new class_name(), ‘method_name’))

Использование объектов было бы предпочтительнее, потому что поддерживает нормальное наследование, состояния и вызов через $this. Но в способе 4-ре нужно передавать именно объект, иначе будет статический вызов в обход конструктора класса. Но все роуты необходимо прописать до вызова диспетчеризации F3::run(). Естественно плодить объекты просто для роутеров не хочется и пришлось смириться со статикой, которая, как мы знаем, не является ООП. Я использовал вариант три, его очень удобно прописывать в ini конфиге.

Для того, чтобы вынести все контроллеры f3 в отдельную папку я задал F3::set(‘AUTOLOAD’, ‘application|autoload’); Подгрузка осуществляется примерно так:

  1. Сканируются папки и автозагрузки.
  2. Подключается файл согласно названию кэлбэка в нижнем регистре.

Я лишь в самом конце просёк фишку, что для автолода можно прописывать несколько папок, до этого всё складывал в одной папке и плагины, и контроллеры. «смешались в кучу люди, кони …». В примере ООП блога с их сайта делалось точно так же, в документации тоже нет намёка, спасло копание в коде.

В контроллере мы используем в основном 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»/>  позволила легко реализовать двухшаговую шаблонизацию с мастер и слейв шаблоном.
Но и он не без багов, у меня возникли проблемы при использовании {} в шаблонах для писем, которые брались из базы.

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

Разработка на фреймворке fatfree: 4 комментария

  1. Ярослав

    По мне проще юзать CodeIgniter или Yii
    Они ненамного тяжелее, а первый вообще по своей концепции ориентирован на скорость.

    1. AmdY Автор записи

      По мне тоже, он не много удобнее голого php, но далёк от нормального фреймворка.
      Здесь как бы сделана ставка на скорость, но с таким обилием регулярок php в реальных проектах будет при нагрузках падать в корку (предположительно). php не perl, расширение для регулярок страдает падениями. тот же Yii легко убивается из-за кривой регулярки для валидации url.

      Получается он и в скорости разработки проигрывает, и в скорости работы тоже не даёт никаких преимуществ. Опять же в Yii гораздо удобнее и продуманнее сделано кэширование моделей. А fatfree — просто забавка, на пощупать, я бы не брался свои проекты на нём делать.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *