Приветствую всех. Хочу представить вам свой проект под названием Regenix.
Это новый MVC фреймворк для языка PHP, в рамках которого реализовано несколько интересных и уникальных идей, которые вы с малой вероятностью встретите в других PHP фреймворках. На проект большое влияние оказал Play! framework и язык Java.
В двух словах, Regenix это фреймворк, который ориентирован на контроль ошибок, на жесткие рамки, который часто не приемлет множества решений для одной задачи. Таким образом обеспечивается согласованность в большой команде разработчиков.
Основные качества фреймворка:
- Классическая MVC Архитектура
- Поддержка нескольких проектов на одном ядре без лишних манипуляций
- Гибкий роутинг на основе конфигурационных файлов
- Умный анализ кода для выявления runtime ошибок
- Приятный и подробный вывод ошибок
- Менеджер зависимостей для assets, модулей и зависимостей composer
- Ленивая загрузка классов, сканер классов
- Для моделей интегрирован Propel ORM
- CLI для управления проектами
- А также DI контейнер (похожий на Guice), i18n, логирование, свой простой шаблонизатор и многое другое ...
Так сложилось, что я работаю в компании где часто используют Java в качестве backend языка и PHP в качестве frontend языка. В мире PHP достаточно много известных фреймворков, однако многие из них не подходят нам по многим критериям. К тому же, после года программирования на Java и Play я сильно привык к другой идеологии и примерно 8 месяцев назад начал разработку Regenix в свободное время.
Далее я расскажу об особенностях фреймворка более подробнее…
Вступление
Regenix требует PHP 5.3+ и любой web сервер (Nginx+FastCGI, Apache + mod_rewrite, и др.), является
полностью Open Source проектом и размещен на GitHub'e (https://github.com/dim-s/regenix). Также частично доступна актуальная документация на английском языке, которую можно найти на гитхабе (русская версия уже устарела).
MVC Архитектура
В Regenix реализована классическая архитектура MVC — Модели, Представления и Контроллеры. Контроллер это класс унаследованный от базового класса regenixmvcController
, все его публичные нестатичные методы могут быть действиями (actions). Чтобы связать URL с методами контроллеров (routing) используется специальный файл конфигурации с легким для чтения синтаксисом, который очень похож на роутинги из Play framework.
Представление (или Шаблоны) реализованы через специальный шаблонизатор с простым синтаксисом, который частично похож на Smarty. Шаблонизатор по-умолчанию экранирует html символы.
Модели — реализованы с помощью стороннего проекта — Propel ORM, это достаточно известная и популярная ORM, с поддержкой миграций, генерацией схем и моделей и нескольких БД — Postgres, MySQL и т.д.
Контроль Качества — Runtime ошибки
PHP динамический язык программирования, а это означает, что часто ошибка возникает в момент выполнения. Однако, Regenix смог частично решить эту проблему. Во фреймворк встроен анализатор кода, который обнаруживает множество ошибок еще до выполнения самого кода. Для примера возьмем использование несуществующего класса в use (вывод ошибок во фреймворке):
Важно заметить, что фреймворк находит эти ошибки не в момент выполнения! Проверяются абсолютно все исходники проекта при каждом открытии страницы (конечно это происходит в DEV режиме), а также можно запустить анализ из CLI. В общем, фреймворк поддерживает следующие типы ошибок:
- Несуществующие классы — в new, use, аргументах функций и методов, в implements, extends и т.д.
- Проверка на корректность implements и extends
- Проверка на корректность синтаксиса (parse errors)
- Проверка на существование статических метод в местах их вызова
- Соблюдение PSR-0 стандарта именования пакетов и классов
- Возможность блокировать некоторые опасные фичи языка — goto, globals или даже объявление именованных функций
- Возможность блокировать использование супер-глобальных переменных ($_GET, $_POST, etc) и набор функций
При этом есть возможность писать свои анализаторы под свои нужды. Также планируется написать анализаторы для проверки совместимости исходников с разными версиями PHP. Как все это стало возможным? Для этого используется специальный PHP парсер (проект) и Class Scanner, о котором я расскажу ниже.
Class Сканер вместо Загрузчика
Regenix использует нестандартную модель загрузки классов, он не использует имена классов и их namespace для поиска их местоположения как сейчас принято делать в PHP. Class Scanner сканирует папки исходников на наличие в них классов. Что это означает? Для фреймворка есть понятия class paths (привет из Java), при добавлении нового источника классов (т.е. папки с исходниками), фреймворк производит сканирование и записывает всю найденную информацию о классах в кеш.
Особенности сканера классов:
- Нахождение классов вне зависимости от их именования
- Ленивая загрузка классов
- Возможность получить информацию о классе без его загрузки
- Возможность получить, например, всех наследников определенного класса (удобно для модулей)
Хочу заметить, что Class Scanner сохраняет всю найденную информацию в кеш и не сильно влияет на производительность. Вот так, например, можно найти всех наследников класса за достаточно быстрое время (стоимость этой функции доли миллисекунды):
// пример из шаблонизатора, который регистрирует все классы тегов
$meta = ClassScanner::find('regenixlibsRegenixTemplateTag');
foreach($meta->getChildrensAll() as $class){
if (!$class->isAbstract()){
$instance = $class->newInstance();
$this->registerTag($instance);
}
}
Данный подход освобождает разработчика от ручного регистрирования каких-то классов-расширений и это довольно удобно.
Роутинг, URL, ЧПУ
Еще одной важной особенностью фреймворка является роутинг. Regenix использует отдельный файл для настроек роутинга, давайте рассмотрим пример такого файла:
# comment
GET / Application.index
GET /{action} Application.{action}
POST /api/{method} api.Api.{method}
* /clients/{id<[0-9]+>} Clients.detail
Выше описывается 4 правила для ройтинга, первая колонка это метод HTTP (POST, GET, PUT, PATCH и т.д.), вторая — путь к странице, который может состоять из динамичных частей, третья — название контроллера и его метод (разделяется через точку, можно использовать namespaces). Как видно из примера, поддерживаются регулярные выражения, а все динамические части передаются в контроллер в виде аргументов методов, например контроллер Clients:
<?php namespace controllers;
use regenixmvcController;
class Clients extends Controller {
public function detail($id){
// $id придет из ройтинга
}
}
Кроме того, в фреймворке есть возможность писать шаблоны для роутинга в отдельных файлах, размещая их в папке /conf/routes/<name>.route
. Представим ситуацию, когда в REST архитектуре нам нужно постоянно объявлять похожие правила для роутинга, у нас имеются ресурсы с похожими по названию URL. Чтобы избавиться от дублирования кода, объявим новый шаблон для роутинга conf/routes/resource.route
:
GET / .index
POST /create .create
GET /{id} .show
PUT /{id}/update .update
DELETE /{id}/destroy .destroy
Далее, мы в главном файле роутинга можем использовать эти правила:
# PostApi и CommentApi это контроллеры
* /posts/ resource:PostApi
* /comments/ resource:CommentApi
Правила с префиксом resource
будут развернуты так как описано в шаблоне resource.route
.
Regenix также умеет проверять ошибки роутинга в случаях когда это явно можно проверить (например на существование класса и метода).
Контроллеры
Контроллеры в Regenix наследуются от общего класса regenixmvcController
. У всех контроллеров могут быть определены специальные методы для отлова событий: onBefore, onAfter, onFinally, onException, onHttpException, onReturn, onBindParams
. С помощью этого можно легко контролировать логику работы контроллеров. В добавок к этому, все контроллеры создаются с помощью DI. Давайте рассмотрим пример контроллера:
<?php namespace controllers;
use regenixmvcController;
class Clients extends Controller {
private $service;
public function __construct(MyService $service){ // внедрение зависимости через DI
$this->service = $service;
}
public function index(){
....
$this->put("var_name_for_template", $value); // добавляем переменную в шаблон
$this->render(); // рендерим шаблон, в данном случае будет рендерится "Clients/index.html"
}
public function detail($id){
// $id - придет из данных роутинга или из $_GET параметра
// также нам доступны: $this->request, $this->response, $this->body, $this->session, $this->flash, etc.
}
}
У базового класса контроллера есть набор методов render*
для вывода контента в различных форматах.
Интересная особенность этих методов в том, что они прерывают выполнения кода, использовать конструкцию return
нет необходимости.
Управление assets
А это отдельная история. Меня, как разработчика, утомляет постоянно искать различные клиентские библиотеки и постоянно их вставлять в каждый проект, запоминая их физический адрес. Поэтому в Regenix был встроен менеджер assets, чтобы избавить разработчика от этой рутины. Вы просто прописываете в конфигурации conf/deps.json список клиентских библиотек (jQuery, Angular, etc) и их версии, после чего набираете regenix deps update
и получаете все эти библиотеки. Вот пример файла deps.json:
{
"repository": "github:dim-s/regenix-repository/master",
"assets": {
"jquery": {"version": "1.*"},
"bootstrap": {"version": "2.*|3.*"}
},
"modules": {
},
"composer": {
"require": {
}
}
}
Здесь мы подключили jQuery и Bootstrap (хотя зависимость Bootstrap уже тянет jQuery). Как вы наверно заметили, в качестве источника зависимостей указывается репозитарий на github, да зависимости будут загружаться оттуда, а версии это регулярные выражения (всегда выбирается самая максимальная версия из возможных). Это довольно удобно, вы можете форкнуть официальный репозитарий и собрать свой набор клиентских библиотек, формат репозитария довольно прост. Также из примера видно, что есть возможность прописать конфигурацию для Composer, все его зависимости будут находиться в папке src/vendor
отдельного проекта.
Для того чтобы подключить assets-зависимости в шаблон, есть специальная конструкция:
<html>
<head>
{deps.asset 'jquery'} <!-- будет подключен jquery -->
{deps.asset 'bootstrap'} <!-- ... -->
</head>
</html>
При этом разрешаются конфликты и повторное подключение зависимостей. И это очень удобно!
Шаблонизатор
Шаблонизатор в Regenix имеет лаконичный синтаксис и компилируется в PHP код, при этом практически не происходит потери производительности. Основные фичи шаблонизатора Regenix:
- Экранирование HTML символов — любая динамическая вставка по-умолчанию экранирует вывод для безопастности
- Короткий и упрощенный синтаксис для PHP вставок — вместо
<?= ... ?>
->{ ... }
- Наследование, теги (html и php), различные include-подобные конструкции
- Фильтры (встроенные, возможность писать свои)
Приведу пример шаблона:
<!DOCTYPE HTML>
<html>
<head>
<title>{get 'title'}</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
{deps.asset 'jquery'}
{deps.asset 'bootstrap'}
{html.asset 'css/main.css'}
{html.asset 'js/main.js'}
</head>
<body>
<h1 class="title">{get 'subTitle'}</h1>
<div id="content">
{content}
</div>
<div class="language">
<a href="{path 'Application.index', _lang: 'ru'}">Russian Version</a>
/
<a href="{path 'Application.index'}">Default Version</a>
</div>
{debug.info}
</body>
</html>
Почти заключение
Все возможности фреймворка описать в рамках одной статьи невозможно, поэтому я описал самые интересные и необычные возможности Regenix. Поэтому, далее я опишу как создать тестовый проект, с помощью которого вы сможете узнать о других более стандартных возможностях фреймворка, к тому же можно почитать документацию.
Создаем первый проект
Небольшая инструкция — как быстро установить и попробовать фреймворк. На данный момент Regenix можно скачать только через git, поэтому перед инструкцией его необходимо установить. Скопируйте все исходники фреймворка Regenix из репозитария с помощью git-bash:
cd <root_of_your_server>
git clone https://github.com/dim-s/regenix.git ./
git submodule init
git submodule update
После этих действий, у вас в папке должны появится следующие директории: framework — ядро фреймворка, apps — директория для ваших приложений. Все остальное не так важно. Фреймворк не требует какой-либо установки. Далее зайдите через консоль в папку веб сервера и используйте CLI чтобы создать новый проект из шаблона:
cd <root_of_your_server>
# если вы под unix
chmod +x install.sh
./install.sh
####
regenix new
После выполнения regenix new
появится папка с вашим новым приложением в папке apps, оно будет открываться по адресу localhost/[name]
.
Структура проекта
Regenix предлагает свою структуру приложения, которую не всегда можно изменить. Да, в данном случае фреймворк главнее приложений, и не он встраивается в приложения, а наоборот. Это один из основных пунктов в идеологии Regenix.
Проекты находятся в папке /apps/
и каждая новая папка является новым проектом. Структура проекта следующая:
apps/<appName>/ *
src/* # исходный код приложения
src/controllers/ # контроллеры
src/models/ # модели
src/views/ # представления
src/* # любые другие исходники и пакеты
conf/ # папка с конфигурациями
conf/application.conf # главный конфигурационный файл
conf/route # конфигурация для роутинга
conf/deps.json # описание зависимостей assets, модулей и composer
conf/analyzer.conf # конфигурация анализатора исходного кода
conf/orm/* # конфигурации Propel ORM
assets/ # папка с клиентскими ресурсами css, js, images
vendor/ # папка с библиотеками composer
Bootstrap.php # файл bootstrap с классом Bootstrap, необязательный
Также за пределами папки проекта (в root директории) находятся другие директории:
/public/<appName>/* # папка с upload ресурсами проекта
/logs/ # папка с логами
/assets/ # assets зависимости
/modules/ # модули фреймворка
Заключение
Это первое публичное освещение данного фреймворка. Сам проект еще находится в стадии разработки, у него еще нет своего сайта и полной документации, но 90% задуманных вещей уже функционируют и проверены небольшим временем.
Приглашаю всех кому понравился фреймворк к участию в проекте на Github: https://github.com/dim-s/regenix.
Документация: https://github.com/dim-s/regenix-documentation/
P.S. В проекте используются некоторые другие внешние библиотеки (vendors) — Symfony (Console, Process), Doctrine (только Cache), PHP-Parser, KCaptcha, Imagine, Propel ORM. Авторам этих библиотек огромная благодарность.
Автор: dim_s