...

понедельник, 7 октября 2013 г.

[Из песочницы] Regenix: Новый нестандартный MVC фреймворк для PHP

Приветствую всех. Хочу представить вам свой проект под названием Regenix.

image

Это новый 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 — Модели, Представления и Контроллеры. Контроллер это класс унаследованный от базового класса regenix\mvc\Controller, все его публичные нестатичные методы могут быть действиями (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), при добавлении нового источника классов (т.е. папки с исходниками), фреймворк производит сканирование и записывает всю найденную информацию о классах в кеш.

Особенности сканера классов:



  1. Нахождение классов вне зависимости от их именования

  2. Ленивая загрузка классов

  3. Возможность получить информацию о классе без его загрузки

  4. Возможность получить, например, всех наследников определенного класса (удобно для модулей)


Хочу заметить, что Class Scanner сохраняет всю найденную информацию в кеш и не сильно влияет на производительность. Вот так, например, можно найти всех наследников класса за достаточно быстрое время (стоимость этой функции доли миллисекунды):



// пример из шаблонизатора, который регистрирует все классы тегов

$meta = ClassScanner::find('regenix\libs\RegenixTemplateTag');
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 regenix\mvc\Controller;

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 наследуются от общего класса regenix\mvc\Controller. У всех контроллеров могут быть определены специальные методы для отлова событий: onBefore, onAfter, onFinally, onException, onHttpException, onReturn, onBindParams. С помощью этого можно легко контролировать логику работы контроллеров. В добавок к этому, все контроллеры создаются с помощью DI. Давайте рассмотрим пример контроллера:

<?php namespace controllers;

use regenix\mvc\Controller;

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:

  1. Экранирование HTML символов — любая динамическая вставка по-умолчанию экранирует вывод для безопастности

  2. Короткий и упрощенный синтаксис для PHP вставок — вместо <?= ... ?> -> { ... }

  3. Наследование, теги (html и php), различные include-подобные конструкции

  4. Фильтры (встроенные, возможность писать свои)




Приведу пример шаблона:

<!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. Авторам этих библиотек огромная благодарность.


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий