Всем доброго.
Хочу представить вам результат своего вот уже как почти 9-ти месячного кодинга очередного MVC-фреймворка на PHP.
Зачем? Изначально писалось для одного проекта, готовое решение использовать было нежелательно, да и времени разбираться в громадинах вроде Zend Framework, Yii и прочих совсем не было. Релиз делаю ради «просто поделиться». Заодно и послушать критику в адрес моего кода :)
Итак, кого заинтересовал новый (и, возможно, интересный) продукт, прошу за мной.
В первую очередь, хотелось бы прояснить ситуацию насчет названия фреймворка. Да, сравнив его с моим ником, можно понять, что автором являюсь я. Почему такое название? Нет, не нарциссизм, а простая нехватка хороших идей в голове. Вот и всё. Если есть идеи — с удовольствием выслушаю.
Features
Как и большинство PHP фреймворков, мой написан с использованием паттерна MVC, используется система компонентов для удобного обмена сообщениями между классами, i18n, PDO для БД, Query Builder ООП-слои для сессий, cookies и т.д.
Работает всё это достаточно быстро, в основном — за счет ленивой подгрузки файлов компонентов. Вчера провел тесты ApacheBench, результаты (количество Requests Per Second) следующие:
Zend Framework 1 тоже тестировался, но в его результаты что-то лично мне слабо верится, поэтому в график включать не стал — 1324 RPS. Логи ab доступны здесь, конфигурация машины:
OS: Microsoft Windows 7 Ultimate (x64)
CPU: AMD Athlon 64x2 4200+
Memory: 4GB
Web: Apache 2.4.1
PHP: 5.4
Ab: ab -t 30 -c 10 URL
Компоненты
Компонент — это обычный PHP класс. В нём нет кучи разных файлов, множества конфигураций на разных языках и т.д. Только один файл, только один класс.
Все компоненты наследуются от базового родительского класса Component и именуются по шаблону «class <Name>_[Type]_Component extends Component», где Name — имя компонента (например, ImageUploader), а Type — его тип (или группа). В случае, если тип не задан, компонент помещается в папку app/components/, иначе — в app/components/<TypeName/), где TypeName — тот же самый Type.
Таким образом, компонент ImageUploader_Component можно будет найти в файле app/components/Imageuploader.php, а компонент Users_Model_Component — в app/components/model/Users.php.
Модели
По умолчанию модели определяют принадлежность к конкретной таблице конкретной БД — описывают поля, алиасы, информацию для генерации веб-форм. Кроме того, могут выполнять CRUD-операции.
Получить данные модели можно таким образом:
// this->i() возвращает новый инстанс компонента $name (с типом $type, если указан)
// почти аналогичная функция this->c() возвращает уже ранее созданный объект компонента (для использования одиночек)
$model = $this->i('SomeModel', 'Model');
// загружаем запись с ID == 1
$model->load(array(':id' => 1));
echo 'field1: ' . $model->field1 . ', field2: ' . $model->field2;
$model->field1 = 'New Value';
$model->save(); // Update
$model->delete(); // Delete from DB
Более подробно — в документации.
Контроллеры
Каждый контроллер наследуется от класса Controller_Component, в котором реализованы все базовые функции абстрактного контроллера.
Работа контроллера заключается в следующем:
- Поиск и вызов необходимого действия (action)
- Хранение блоков представления
- Инициализация бизнес-логики приложения
При создании у каждого контроллера будут вызваны следующие методы:
- Controller_Component::run() — используется для выполнения повторяющихся действий в пределах одного контроллера
- Controller_Component::action<действие>() — в зависимости от запроса вызывается конкретное действие (например, при запросе /docs/controllers у контроллера Docs запускается метод actionControllers). Если действие не задано, вызывается метод actionIndex
- Controller_Component::finish() — по умолчанию используется для вызова компонента представления (рендеринг регионов и блоков)
Поиск действия базовый класс контроллера выполняет самостоятельно, а вот блоки придется создавать самому. Но ничего сложного в этом нет — блок цепляется к какому-либо региону, содержимое региона отображается в шаблонах. Ниже объясню подробнее, а сейчас — пример контроллера.
<?php
class Example_Controller_Component extends Controller_Component
{
// Номер индекса массива-запроса (URL), в котором нужно искать действие
// http://example.org/ru/example/info - индекс у "info" равен 1 (нумерация идет с 0)
// Язык из URL-запроса за действие не считается и в массив-запрос не добавляется
protected $m_webActionIndex = 1;
protected function actionIndex()
{
// задаем заголовок страницы (будет иметь вид "Action Index :: Website Title")
$this->m_pageTitle = 'Action Index';
// задаем блок, который будет рендериться
$this->m_blocks = 'index';
}
protected function actionInfo()
{
$this->m_pageTitle = 'Action Info';
$this->m_blocks = array('info', 'footer');
}
protected function finish()
{
$this->c('View')
// строим необходимые блоки
->buildBlocks($this->m_blocks)
// строим страницу с шаблоном layout.tpl из папки templates/application/layout
->buildPage('layout', 'application.layout');
}
public function block_index()
{
return $this->i('Block')
->setName('index') // задаем имя блока
->setTemplate('index', 'application.contents') // задаем шаблон и путь к нему
->setRegion('pagecontent'); // задаем регион блока
}
public function block_info()
{
return $this->i('Block')
->setName('info')
->setTemplate('info', 'application.contents')
->setRegion('pagecontent');
}
public function block_footer()
{
return $this->i('Block')
->setName('footer')
->setTemplate('footer', 'application.contents')
->setRegion('footer');
}
};
Код должен находиться в файле app/components/controller/Example.php.
Забыл упомянуть, что роутинг работает на mod_rewrite.
Более подробно — в документации.
Представление
В данном фреймворке к представлению относятся следующие вещи:
- i18n/l10n
- Выдача ссылок на клиентские файлы (CSS/JS)
- Выдача мета-тегов
- «Рендеринг» (по сути — простой require_once, т.к. файл шаблона — такой же PHP скрипт, только с расширением tpl) и отображение регионов и блоков
Первые 3 пункта должны быть понятны (надеюсь), поэтому здесь пока что расскажу о блоках и регионах.
Регион — конкретное место в шаблоне страницы (header, footer, menu, etc.). Блоки являются содержимым регионов. Их можно создавать либо вручную для каждого контроллера, либо задать их конфигурацию в файле настроек.
Вывод определенного региона выглядит следующим образом:
echo $this->getRegionContents('pagecontent'); // напрямую из шаблона
// либо
echo $this->c('View')->getRegionContents('pagecontent'); // из компонента
// либо
echo $this->c('View')->getRegion('pagecontent')->getRegionHTML(); // обращение непосредственно к самому региону
// либо HTML одного блока (в случае, если к региону подключено несколько блоков)
echo $this->c('View')->getRegion('pagecontent')->getBlock('info')->getBlockHTML();
Подробности — как обычно в документации.
Заключение
Если копнуть глубже, вести речь об отдельных компонентах и возможностях можно достаточно долго. В ядре есть реализации генератора запросов (QueryBuilder_Db_Component), поддержки локализаций (I18n_Component), журналирования (Log_Component), сервера API приложения (Api_Component и SiteApi_Component) и ещё много чего интересного.
На выпуске в паблик разработка не останавливается, проект так или иначе будет развиваться, даже если успеха и не будет — как минимум, он нужен мне для работы :)
Буду рад, если кто-то захочет попробовать поработать с фреймворком, в случае затруднений помогу разобраться.
Ссылки:
Сайт с документацией (основная часть написана, сейчас готовятся примеры использования отдельных компонентов) — www.phpmvc.ru
Github-репозиторий проекта — www.github.com/Shadez/Framework
Форум — community.phpmvc.ru
Спасибо за внимание!
Автор: Shadez