Regenix: Новый нестандартный MVC фреймворк для PHP

в 10:01, , рубрики: mvc, php, rest, Веб-разработка, фреймворк, метки: , ,

Приветствую всех. Хочу представить вам свой проект под названием 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 — Модели, Представления и Контроллеры. Контроллер это класс унаследованный от базового класса regenixmvcController, все его публичные нестатичные методы могут быть действиями (actions). Чтобы связать URL с методами контроллеров (routing) используется специальный файл конфигурации с легким для чтения синтаксисом, который очень похож на роутинги из Play framework.

Представление (или Шаблоны) реализованы через специальный шаблонизатор с простым синтаксисом, который частично похож на Smarty. Шаблонизатор по-умолчанию экранирует html символы.

Модели — реализованы с помощью стороннего проекта — Propel ORM, это достаточно известная и популярная ORM, с поддержкой миграций, генерацией схем и моделей и нескольких БД — Postgres, MySQL и т.д.

Контроль Качества — Runtime ошибки

PHP динамический язык программирования, а это означает, что часто ошибка возникает в момент выполнения. Однако, Regenix смог частично решить эту проблему. Во фреймворк встроен анализатор кода, который обнаруживает множество ошибок еще до выполнения самого кода. Для примера возьмем использование несуществующего класса в use (вывод ошибок во фреймворке):

Regenix: Новый нестандартный MVC фреймворк для PHP

Важно заметить, что фреймворк находит эти ошибки не в момент выполнения! Проверяются абсолютно все исходники проекта при каждом открытии страницы (конечно это происходит в 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('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:

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

Автор: dim_s

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js