Программист
0,1
рейтинг
7 октября 2013 в 14:01

Разработка → 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/.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>
         {html.asset 'dep:jquery'} <!-- будет подключен jquery -->
         {html.asset 'dep: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">

        {html.asset 'dep:jquery'}
        {html.asset 'dep: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
карма
97,0
рейтинг 0,1
Программист
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (129)

  • +19
    Я не знаток английского, но children множественное от child, то есть поэтому не может быть getChildrensAll(), должно быть getChildrenAll().

    P.S. Или вру?
    • –2
      Да все верно, нужно изменить, чтобы не вводить в заблуждение. На раннем этапе я исходил из того, что методы с буквой s на конце возвращают множественное значение и намеренно сделал эту ошибку.
      • 0
        Чтобы не было лишних раздумий, лучше просто использовать то, что правильно звучит: getAllChildren(). Ну и, конечно, избегать наличия классов, в которых необходимо группировать методы по некоему префиксу (getChildren, getChildrenAll, getChildrenBy). Это можно достичь с помощью отдельных классов, например, ClassMetadata\Children, ClassMetadata\Parent.

        В таком случае будете иметь, например, ClassMetadata\Children::getAll() и ClassMetadata\Parents::getAll(), соответственно, необходимость иметь разные имена для разных методов пропадёт: $meta->getChildren()->getAll(), $meta->getParents()->getAll().

        В последнее время наличие некоего длинного имени метода своём классе всегда расцениваю как звонок к тому, что пора декомпозировать класс.
    • 0
      Не врёте.
    • 0
      Можно просто getChildren().
      • 0
        С точки зрения понятности наименования, просто getChildren() может возвращать несколько наследников (наверное по какому-то критерию, чтобы убедиться — надо лезть в документацию), а вот getChildrenAll() вернет всех наследников, тут и в документацию лезть не надо.
        • +6
          Если критерий не указан, обычно возвращается всё. Ну и если всё-таки хочется длинных имён, правильно с точки зрения английского getAllChildren().
          • 0
            В исходниках фреймворка есть методы по типу: put, putAll и в этом духе, поэтому не хотелось менять порядок, т.к. суфикс all используется во многих местах.
            • 0
              глаголAll — это вполне нормально, а вот существительноеAll — нет.
              • +1
                Поддерживаю. метод надо называть как в разговорном языке. В другом случае получится YodaFramework
      • 0
        Есть уже метод без All, он возвращает всех детей только первого уровня, а с All возвращает абсолютно всех.
        • –1
          getChildren() и getChildrenAll()?
        • +1
          Дети и могут быть только первого уровня. А то, о чем вы говорите, называется «наследники» (descendants).
    • 0
      «class is not found» туда же…
      • +1
        Страдательный залог, можно опустить is для сокращения сообщения, но ошибки нет.
        • –1
          Если не опускать, тогда время нужно поправить на was.
          • 0
            Дословный перевод «class is not found» — «класс не найден», «class was not found» — «класс не был найден». Как в русском, так и в английском оба варианта являются равнозначными.
        • 0
          Да, как-то уже все привыкли к
          File not found.
  • 0
    ну сразу что бросилось в глаза new и подобных экшенов не видать
    • 0
      Я не вижу в этом проблем, вы можете прописать в роутинге правила так:

      * /api/{method} Api.action{method}

      И будут вам методы view, list и new и методы надо будет именовать как actionNew, actionList и т.д.
  • +10
    простите, но ничего нестандартного я тут и не увидел. А по поводу хранения классов в разнобойных файлах, по-моему плохая практика — при увеличении количества классов просто замучаешься их сам искать
    • –8
      Вы всё ещё не используете нормальную IDE?
      • +4
        иде-идеёй, но расположение то лучше знать интуитивно…
      • +10
        Вы предлагаете на любой говнокод любую непродуманную архитектуру, лапшу и прочее — отвечать: «Вы всё ещё не используете нормальную IDE?».
        • –5
          Эм, а каким образом архитектура проекта заключается в сортировке классов по файлам?
          Я не считаю что именно способ расположения классов является неудачным решением.
          На мой взгляд, подобная свобода (которая является на самом деле ограничением), заставит разработчика использовать IDE и поиск класса по названию, что в результате повысит его скорость нахождения нужного участка кода.

          А текущая практика раскидывания файлов по неймспейсам содинаковым наименованиям файлов, меня так вообще удручает.
          Привет вам от 20 файлов Data.php в проекте и поиску по названию файла.

          Что-то с этим делать надо в PHP, но что?
          • +2
            С этим надо делать в программисте, а не в PHP
            • +2
              С чем надо делать в программисте? Зачем вы хотите его препарировать?
          • 0
            А что, реально сложнее в поиске по имени класса написать MyNamespace\MyClassName, чем MyNamespace_MyClassName? Или вы при наличии двадцати классов Data в проекте предпочитаете использовать генератор случайных текстов для именования файлов этих классов?
            • 0
              Как бы про первый, из указанных вами способов изначально и говорю. В чём тогда вопрос?
      • +6
        Дело не в IDE, а в структуре классов. Не стоит хранить вилку в платяном шкафу, даже если у вас есть специальный человек, который всегда знает, где она лежит.
        • –4
          Какая прекрасная аналогия, но не по делу.
    • 0
      PHP не поддерживает классы внутри классов. Иногда это было бы полезно. Фреймворк позволяет объявлять несколько классов в одном файле, однако частично соблюдается стандарт PSR-0 (проверку анализатором можно отключить) — название первого класса в файле должно совпадать с названием файла.
      • +2
        не вижу практической ценности, те же «псевдо-внутренние» классы можно и в тот же файл с основным классом засунуть, и очевидно погрузка так и так будет
        • 0
          Regenix позволяет так делать, т.к. Class Scanner основывается не на названиях классов.
          • +2
            честно говоря все равно не вижу смысла, у меня на <5.3 был сканер классов (как часть автолоадера), не скажу, что он был быстрый
            • 0
              Возможно сама операция сканирования нагрузочная, но не более 1 сек при большом наборе файлов, а обычно 50-100 млсек, но когда данные берутся из кеша, то на это тратится около 0.3 млсекунды, если не меньше.
              • +1
                а если нет возможности врубить какой-либо кеш (например у прова)? то данная операция получается довольно энергоемкой
                • 0
                  Используется php файловый кеш. Но вообще фреймворк не ориентирован на среду без кеша. Я пришел к выводу, если нет кеш системы (которая хранит данные в RAM), невозможно поддержать быстродействие в PHP на должном уровне. Кеш используется буквально по-всюду.
      • +1
        PSR-0 как-бы не позволяет нескольких классов в одном файле.
        • –1
          Поэтому я написал частично.
      • +2
        Очень вредно делать «иногда так, а иногда так».
    • +1
      Хранение классов в разнобойных файлах может понадобиться при использовании различных сторонних библиотек. Но при наличии в проекте composer'а непонятно, зачем это дополнительно поддерживать средствами фреймворка.
  • +9
    Ещё один php-mvc-framework…
    Вот есть там метод такой loadDeps() который делает такие операции:
    $file = $this->getPath() . 'conf/deps.json';
    if (is_file($file))
    $this->deps = json_decode(file_get_contents($file), true);
    

    Метод вызывается внутри _registerDependencies(), который вызывается в register(), который в свою очередь содержит ещё пачку file_exists, который вызывается из _registerCurrentApp(), который вызывается в init(), который в initWeb($rootDir), который вызывается в index.php. Если я правильно все понял, куча файловых операций, включая чтение текстового файла и парсинг json, будет выполняться каждый запрос. Почему бы не подключать конфиг в виде нормально кешируемого байткод-кешерами php-файла с php-массивом?

    Вообще, код выглядит перемудрёным и переусложненным. Смысл делать java из php — сомнительная затея вообще, на мой взгляд, на php можно писать намного проще, сохраняя при этом гибкость и скорость работы и разработки.
    • –1
      Чтение JSON не происходит каждый заново, конкретно deps.json и остальные файлы конфигурации кешируются в APC или XCache и перезагружаются только при изменении этих файлов. В фреймворке я старался снизить количество операций по чтению файлов. На счет хранения конфигураций в PHP файлах, исходил из того, что PHP файлы не для конфигураций и при желании новичок может засунуть туда какую-нибудь логику.

      P.S. Я не отрицаю что фреймворк мудренный внутри, но код приложений достаточно простой и понятный.
      • 0
        Хорошо, значит я не докопал до места, где оно керишуется. А парсинг json и сборка конфигурации — происходит каждый запрос?
        • 0
          Есть два режима работы приложения DEV и PROD, в первом режиме (который включен по-умолчанию) почти ничего не кешируется таким образом. В режиме продакшена парсинг кешируется, вызываются лишь функции проверяющие время модифицирования файла, парсинг JSON и других конфигураций при каждом запросе не происходит если вы в продакшен режиме.
          • 0
            Ага, увидел, значит этой болезни там нет — это хорошо. Ещё по поводу самого кода — я бы все-таки разнёс все классы (включая статические, включая описанные в include.php, в разные файлы с именами классов (и директориями неймспейсов). Так гораздо удобнее с ними работать. И ещё, правила в .htaccess это конечно замечательно, но мне гораздо спокойнее когда в проекте вся публичная часть лежит в отдельном каталоге public (или htdocs).
            • 0
              Да я это понимаю (вначале так и было), сделал ради оптимизации. Возможно в будущем просто напишу утилиту, которая будет собирать из исходников один файл.

              htaccess прописан так, что прямого доступа к файлам нет, только к статичным. Фреймворк можно держать в отдельной папке, он подключается откуда угодно, главное подключить файл include. Но пока я над этим сильно не работал.
              • +1
                Сомнительная оптимизация, так как при использовании кэша байткода все файлы лежат в памяти и доступ к ним практически мгновенный. Я пришел к такому выводу, когда я оптимизировал через xhprof разные вещи. В итоге, при полном избавлении от файловых операций (включая file_exists) и после перехода на php array конфиги, самые большие затраты цпу пришлись на… вызовы microtime(), который вызывался в начале и конце каждого скрипта.
                • –2
                  Все же я иду больше от того, что PHP array это файлы исходного кода, а не файлы конфигурации. Пусть даже это и приводит к небольшому ухудшению производительности.

                  На счет кеша байткода, я так и предполагал, однако например APC мне не давал в этом смысле какого-то выигрыша. Возможно Zend Opcache ведет себя по-другому.
                  • 0
                    Нужно ставить на продакшене apc_stat 0, чтобы он не читал дату модификации каждого файла с целью перезагрузки, может про это забыли.
                • 0
                  Во «взрослых» фреймворках уже давно реализованы конфигураторы, компилирующие на продакшне различные форматы конфига в php-файлы.
                  • 0
                    Можно узнать, о каких взрослых фреймворках вы говорите?
                    • 0
                      Symfony компилирует, вроде, yaml в php.
                      • –1
                        Заранее извиняюсь, что рассматриваю Symfony в совокупности с Doctrine, но иначе не представляю зачем рассматривать отдельно их. Это как смотреть на абстрактного коня в вакууме. Да, можно поставить не Doctrine, но она идет как коробочное решение, так что не обессудьте.

                        Итак, используется Symfony 2 с Doctrine все конфиги в Yaml. Конфиги Doctrine после первого запуска кешируются в array(по умолчанию) Слева как есть, справа с изменением YamlParser на нативное расширение для PHP:


                        Это трейс небольшого проекта. В большом, доля парсинга Yaml у нас достигает 50% всего времени. Исследование профайлера показывает, что всё это хозяйство дёргается из bootstrap.php.cache

                        Так что у меня пока язык не повернется сказать, что:
                        Во «взрослых» фреймворках уже давно реализованы конфигураторы, компилирующие на продакшне различные форматы конфига в php-файлы.


                        p.s.
                        не отрицаю, что возможно это можно пофикить, но пока не до этого было.
                        • –1
                          Можно компилировать отдельной тулзой и юзать уже как php-конфиги (не знаю, есть ли там готовое). Я предпочитаю не париться и юзать php.
                          • +1
                            Всё это было бы разумно, если бы в yml конфигах использовались особенности yaml, типа якорей и алиасов, а так никаких преимуществ в сравнении с php конфигами нет. зато имеется дополнительный парсинг, который нужно кешировать, возможные проблемы из-за форматирования (лет пять назад из-за этого выбросил yml из фреймворка).

                            Понятно ещё, когда используют xml, его можно валидировать через схемы, можно трансформировать через xlst, делать удобные выборки через xpath. А yml и ini — бессмысленные дополнительные преобразования.
                            • –1
                              Я примерно по тем же причинам выкинул yml и ini из своих проектов. Не нашёл плюсов.
                          • 0
                            Мы так же перешли на php конфиги с тех пор как обнаружили данную фишку :)
                        • 0
                          Конфиги не кешируются в Array(). Они компилируются в классы. Парсинга yaml там быть не должно, т.к. вся конфигурация компилится в методы хардкодно инстанциирующие сервисы со всей конфигурацией:
                              protected function getDoctrine_Dbal_DefaultConnectionService()
                              {
                                  return $this->services['doctrine.dbal.default_connection'] = $this->get('doctrine.dbal.connection_factory')->createConnection(array('dbname' => 'test', 'host' => '127.0.0.1', 'port' => NULL, 'user' => 'root', 'password' => '', 'charset' => 'UTF8', 'driver' => 'pdo_mysql', 'driverOptions' => array()), new \Doctrine\DBAL\Configuration(), new \Symfony\Bridge\Doctrine\ContainerAwareEventManager($this), array());
                              }
                          


                          То, что где-то там у вас трейс длинный, а где-то нет — если вы нативное расширение PHP используете, то его выполнение для дебаггера прозрачно. Если контроллер пустой, то временные затраты на логику инстанциирования сервисов достигает не 50%, а всех 80-90.

                          P.S. Большой трейс — это не плохо, это хорошо, т.к. в данном случае показывается хорошую декомпозицию.
                          • 0
                            P.S. Большой трейс — это не плохо, это хорошо, т.к. в данном случае показывается хорошую декомпозицию.

                            Что за бред?

                            Конфиги не кешируются в Array(). Они компилируются в классы.

                            symfony.com/doc/master/reference/configuration/doctrine.html
                            Не знаю что да зачем и т.д. в конфигах по умолчанию в array

                            То, что где-то там у вас трейс длинный, а где-то нет — если вы нативное расширение PHP используете, то его выполнение для дебаггера прозрачно. Если контроллер пустой, то временные затраты на логику инстанциирования сервисов достигает не 50%, а всех 80-90.

                            Вы не на трейс смотрите а на время выполнения и сколько его занимает от всего проекта. И вообще тут трейс парсинга Yaml, а не инстанцирование сервисов.
              • +1
                Собирать в один файл не помогает в большинстве случаев. Правильно настроенный APC / Opcache это только замедляет.
                • 0
                  При отсутствии кеширования APC — помогает.
                  • 0
                    Конечно, если мы говорим про абстрактное «подключение всех классов».
                  • 0
                    Почему бы при отсутсвии кеширования не установить и включить его? Или опять «а как же совместимость с shared хостингами без нормальной техподдержки»?
                    • 0
                      Т.к. всё-таки, это оверхедное «подключение всех классов в вакууме». При правильной архитектуре проекта такого происходить не должно даже близко.
                      • 0
                        Не совсем понял, причем тут классы и их «оверхедное» подключение? Кэш байткода включается элементарной установкой кэшера, например php-apc, без вмешательства в код проекта, и может являться одним из условий запуска проекта, наряду с правильной версией php, бд, и вебсервером.
              • 0
                Вы строите свои оптимизации на тестах или только на предположениях, как это должно работать?
                Вот сравнение, например: blgo.ru/blog/2013/08/07/autoload-req-pha/
          • +1
            Из опыта скажу: пользователь который может запихнуть в plain-PHP конфиг логику — легко может забить на настройку DEV / PROD.
            • 0
              Это вопрос из категории — почему нужно использовать шаблонизатор, а не чистый PHP (вед он тоже шаблонизатор).
  • 0
    Можно узнать, а зачем ещё 1 framework, когда есть Yii и Symfony 2?
    Ои оба покрывают всё множество извращенцем своими различиями в подходах к разработке веб-приложений.
    • +6
      свой велосипед веселее едет
    • +1
      Если бы на PHP не было фреймворков, то я бы 100 раз подумал, есть ли смысл писать для этого языка фреймворк.
      • 0
        А я бы как раз в этом случае без сомнения написал бы на PHP фреймворк (да в общем, примерно это и делали лет 10 назад, как могли).
  • +9
    Так какие из идей уникальны?
  • 0
    Пардон, а в чем высший смысл deps.json? Разве просто composer.json (который, как я понимаю, в секции composer файла deps.json) не покрывает все нужды?
    • 0
      Там хранятся зависимости от assets библиотек — javascript, html, css библиотеки. Еще там хранятся зависимости к модулям фреймворка, но это еще под вопросом, возможно они будут хранится в composer.
      • 0
        Нууу, думаю, излишне городить лишнюю сущность, потенциально усложняющую и тормозящуюю систему, когда можно стандартным инструментом, который и так используется, задать местоположение для третьесторонних компонентов и, скажем, дернуть скрипт обновления ассетов в коллбеке, как это сделанно в Symfony с недавних (>= 2.3) пор. Там после «composer update» чистится кеш и обновляются ассеты.
      • +1
        Можно было бы просто воспользоваться bower-ом для управления assets-зависимости. Он удобнее и уже стал стандартным инструментом.
        • +2
          Далее заменить свой роутинг, i18n и другие компоненты на компоненты от symfony2, интегрировать Twig, убрать лишние шаги из ленивой загрузки классов, проверку кода перенести в build скрипт в CI (где ей и место) и будет yet-another-framework %)
          • +1
            Regenix позволяет интегрировать любые другие шаблонные движки. Скрипт build и так возможно запускать из CI. С таким же успехом можно заменить в любом другом фреймворке все на компоненты Symfony2.
            • 0
              Сифони тоже позволяет интегрировать любые шаблонизаторы. Более того, любые ORM, мэйлеры и т.п. В общем, за счёт конфигуратора и DI там можно внедрить, что хочешь, куда хочешь. Насколько знаю, единственное, что он не позволяет — так это использовать чужой http-kernel. В том смысле, что при использовании другого request-response ядра фреймворк уже не будет симфони.

              Fabien Potencier о Symfony2: fabien.potencier.org/article/49/what-is-symfony2
          • +1
            yet-another? Symfony и получится.
        • 0
          Он тянет за собой Node.js (лишняя зависимость), слишком перемудрен на первый взгляд.
  • 0
    Assets которых нет в репозитарии можно держать в папке проекта и подключать их напрямую, используя относительный путь в шаблоне. Я точно не уверен, но кажется в Symfony просто указывается папка с assets. В Regenix если вы подключаете bootstrap, с ним автоматически тянется и jQuery, той версии, которая с ним совместима. Далее, когда вы подключаете bootstrap в шаблоне, подключать jQuery не нужно (т.к. это его зависимость), он подключается автоматически.
  • +1
    Несуществующие классы — в new, use, аргументах функций и методов, в implements, extends и т.д.
    Проверка на корректность implements и extends
    Проверка на корректность синтаксиса (parse errors)
    Проверка на существование статических методов в местах их вызова
    Соблюдение PSR-0 стандарта именования пакетов и классов
    Возможность блокировать некоторые опасные фичи языка — goto, globals или даже объявление именованных функций
    Возможность блокировать использование супер-глобальных переменных ($_GET, $_POST, etc) и набор функций


    Имхо удобнее это делать не на стороне фреймворка, а на стороне IDE.
    PhpStorm умеет все из этого.
    • –2
      Не у всех есть лицензия на PhpStorm и не все ею пользуются. К тому же в IDE это все на уровне необязательных к исполнению подсказок. А так можно организовать проверку на стороне сервера интеграции, а это снижает риск возникновения ошибок и попадания в репозитарий «плохого» кода. Проверка во время открытия страницы сделана как полезное дополнение, для удобства разработчика.
      • +1
        PhpStorm EAP бесплатна.

        И еще все это можно сделать только через PHP_CodeSniffer в любом IDE и на стороне сервера с использованием вашего любимого фреймворка.

        Использовать другой фреймворк для таких задач не необходимо. Это лишнее. Это неэффективно.
        • 0
          Всегда будут противники коробочного решения. PHP-Parser (проект для парсинга php-кода) делает примерно тоже самое что и PHP_CodeSniffer. EAP работает не весь год.
          • +1
            EAP обновляется.
            А для проверки кода использование фреймворка — лишнее, когда ее можно приделать к коду на выбранном фреймворке.

            Теперь вопрос: почему я должен отказаться от Laravel в пользу вашего фреймворка, если я и так имею вышеперечисленные проверки?
            • 0
              Я не изучал досконально Laravel, поэтому ответить на вопрос не могу.

              Приделать можно, но используя фреймворк вам делать этого не нужно, вам много чего приделывать в этом фреймворке не нужно. Когда есть большое количество решений одной проблемы, каждый приделывает что-то свое. Таким образом размывается единый стандарт пользования фреймворка. И получается, что каждый использует один и тот же фреймворк, но с разными инструментами.
              • 0
                Приделать можно, но используя фреймворк вам делать этого не нужно, вам много чего приделывать в этом фреймворке не нужно.
                Вы так говорите, как будто добавление этого функционала займет длительное время.
                А вот цена перехода на ваше решение высока — это стоимость отказа от использования любимого фреймворка.

                Таким образом размывается единый стандарт пользования фреймворка. И получается, что каждый использует один и тот же фреймворк, но с разными инструментами.
                Это нормально. Некоторые фреймворки специально задумывались для этого (Symfony).
                • 0
                  Я никого не призываю переходить на этот фреймворк. Скорее сейчас я больше заинтересован в людях, кому будет интересно вложить свой вклад в проект в качестве разработчиков фреймворка.

                  Это нормально. Некоторые фреймворки специально задумывались для этого (Symfony).


                  Но есть фреймворки, архитектура которых иная. Play framework для Java и Scala как пример. В начале статьи я писал, что ориентировался на этот фреймворк.
                  • +1
                    Но есть фреймворки, архитектура которых иная.
                    Значит при использовании фреймворков с иной архитектурой, я должен от них отказаться в пользу вашего, вместо того, чтобы просто подключить скрипт проверки?

                    Вам самому такие мысли нелогичными не кажутся?
                    • 0
                      Да, но если оно удовлетворяет вашим требованиям, но никто не мешает вам подключить ваш скрипт проверки.
  • 0
    А где composer.json?
    • 0
      Внутри deps.json, секция «composer».
  • 0
    Как по мне то слишком много статики в ActiveRecord. Лучше бы разбить на классы.
    Во вторых нет документации для методов классов и тд.

    Router::reverse ( github.com/dim-s/regenix/blob/dev/framework/mvc/route/Router.php ) тихий ужас. Надо разбить на методы или хоть как-нибудь почистить, в методе не должно быть столько логики сразу, плохо для расширения.

    Класс сканнер это конечно интересно (привет Кохана? ), но есть ли для этого продуманный кеш? а то на каждом запросе такие штуки делать дорого.

    Стандарты, стандарты… То у вас есть пробел перед { то нет.
    Глаза плачут кровью
    • 0
      Надеюсь что мой коммент не будет воспринят сильно агрессивно. Ни в коем случае не хочу обидеть автора, просто указать на несколько замечаний =)
      • –1
        ActiveRecord это вы имеете ввиду Propel ORM? Я думаю при желании логику работы с моделями можно выносить в отдельные классы-сервисы и подключать их через DI в контроллеры.

        Конечно я осознавал, что делая что-то сложное, я теряю в производительности. Но я еще исходил из того, что если мне нужно писать что-то крупное и нагрузочное у меня для этого есть Java, а php можно использовать только как Frontend. Например для этих целей очень удобно использовать Apache Thrift.

        На счет скобочек, проект в разработке и думать о скобочках нет времени. Форматирование исправляется подходящими инструментами за несколько минут. Говоря про стандарты, я меньше всего имел ввиду такие мелочи в code-style.
        • 0
          Я имел ввиду класс github.com/dim-s/regenix/blob/dev/framework/mvc/AbstractActiveRecord.php (кстати при чем он к папке mvc? )
          Честно говоря не вижу большой надобности в фичах «Class Сканер», можно пример когда он был бы полезен?
          • 0
            Да все классы связанные с ActiveRecord устарели и не используются, их надо будет удалить. Они остались еще со времен, когда не планировалось внедрять стороннее решение по типу Propel.

            Сканер полезен тем, что хранит мета-информацию о файлах, с помощью которой можно сделать удобную авто-регистрацию классов. Например, в проекте вы объявляете новый класс Тега для шаблонизатора. То что вы наследуете его от абстрактного класса тега, уже дает знать фреймворку, что вы хотите его использовать. Не нужно думать о том, где прописывать регистрацию этих тегов. Это один из примеров.
        • +1
          Конечно я осознавал, что делая что-то сложное, я теряю в производительности. Но я еще исходил из того, что если мне нужно писать что-то крупное и нагрузочное у меня для этого есть Java, а php можно использовать только как Frontend.

          Вот это особенно пугает.
          • 0
            Из-за модели работы PHP — рождаться и умирать, всегда будут проблемы с производительностью. Поэтому PHP сейчас подходит для средних и небольших проектов, для крупных нестандартных проектов есть более подходящие языки и архитектуры, моё субъективное мнение.
            • 0
              То есть Yahoo или Wikipedia — средние или небольшие?
              • 0
                Есть более подходящие и более производительные решения на данный момент. Проект может быть высоко-нагруженным, но его логика может сводиться к простым вещам (к выводу некой информации из Базы данных). От этого он не становится сложнее.
                • 0
                  Как-бы в Yahoo совсем не простые вещи. И ничего, работает отлично.
            • 0
              Начитались про рождаться и умирать и туда же. При правильной настройке (быстро рождаться и правильно умирать) — это вполне можно считать плюсом.
  • 0
    Если выйдет более легковесный чем симфони (кнч с документацией и длительной поддержкой), то взял бы на вооружение нескольких проектов.
    • +4
      А разве мало легковесных фреймворков?
      • 0
        Включая легковесный Silex на базе Symfony.
        • +1
          Silex хоть и маленький, но стиль большого брата чувствуется сильно…
          • +2
            Всегда можно создать свой маленький фреймворк на базе симфони :) Главное, вовремя остановиться.
          • +1
            Не соглашусь. Из схожести только компонентность и DI. А в целом, Symfony2 — достаточно строгое и большое, а Silex — играй гармонь (что неплохо).
  • +5
    В мире PHP достаточно много известных фреймворков, однако многие из них не подходят нам по многим критериям.

    Каждый хотя бы раз в жизни говорил это. К чему это приводит — через пару лет «О, а давайте перейдём на <имя фреймворка>!»

    Regenix использует нестандартную модель загрузки классов, он не использует имена классов и их namespace для поиска их местоположения как сейчас принято делать в PHP. Class Scanner сканирует папки исходников на наличие в них классов. Что это означает? Для фреймворка есть понятия class paths (привет из Java), при добавлении нового источника классов (т.е. папки с исходниками), фреймворк производит сканирование и записывает всю найденную информацию о классах в кеш.

    Composer позволяет генерировать автозагрузчик различными способами, в том числе так, как говорите вы: маппинг классов, маппинг неймспейсов, PSR-0.

    Для примера возьмем использование несуществующего класса в use (вывод ошибок во фреймворке)

    Привет, нормальные IDE.

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

    Зачем может понадобиться поиск наследников класса при выполнении программы — я вообще мало представляю.

    Менеджер зависимостей для assets, модулей и зависимостей composer

    Наличие папки «framework/lib» намекает на то, что даже если composer где-то в репозитории и есть, то composer'а в репозитории нет.

    update: Оказалось, это врапперы. Тогда непонятно вынесение их в отдельную папку, а не размещение в папке тех компонентов, к которым они относятся.

    namespace controllers;

    class Clients extends Controller

    Непонятен выбор стиля именования классов и неймспейсов.

    Модели — реализованы с помощью стороннего проекта — Propel ORM

    Это не модели, это сущности. M в MVC — это не про базы данных.

    regenix\deps
    regenix\exceptions
    regenix\lang
    regenix\libs
    regenix\logger

    Не очень понятна структура неймспейсов. Что-то сгруппировано по компоненту (logger), что-то по некоему third-party признаку (libs), где-то вообще решили не запариваться и кинули всё в кучу (lang).

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

    А в чём конкретно нестандартность? MVC, роутинг, ORM, конфиги, консоль, i18n, шаблонизатор. Разве что что-то там про ошибки. С другой стороны, до профайлеров «больших» фреймворков ещё очень далеко.

    P.S. Как некая разработка и вклад в портфолио — ок. Но для публичного open-source релиза, имхо, сильно далеко. Да и смысла особого нет.
    • –1
      Частично ответы на ваши вопросы в статье, частично в комментариях.

      Стиль именования классов и неймспейсов как в Java, пакеты все с маленькой буквы, классы с большой.

      P.S. OpenSource для того и нужен, чтобы найти единомышленников еще до релиза и развивать проект. Многие известные Open-Source проекты до сих пор не имеют стабильных версий (т.е. нулевые версии.)
      • 0
        0-версия — это не только нестабильность. Версия 0.9 — это чаще всего очень стабильный продукт. Смена мажорной версии — это чаще всего добавление серьёзных фич, или смена подхода в целом, или потеря обратной совместимости. В принципе, можно было бы 0 сменить на 1. Но зачем? Чтобы заставить пользователя продукта думать, что теперь надо всё заново тестировать?

        С другой стороны, есть продукты, которые начинаются сразу с 1-х версий. Таким образом публичный релиз может выходить под, например, 3-й версией.
        • 0
          К слову сказать, есть продукты у которых после версии 0.9 идет версия 0.10 (что довольно неожиданно)… и так до бесконечности.
          • 0
            Бывает. Вообще, походов к именованию версий очень много. Многие просто используют добавление «stable», «lts» и т.п.
    • –1
      Очень интересные ответы, было бы интересно услышать ваши мысли о PHPixie ( о котором мои посты) =)
  • +1
    Возможность блокировать использование супер-глобальных переменных ($_GET, $_POST, etc)

    Стесняюсь спросить, но как вы предлагаете обрабатывать входящие параметры?
  • +3
    После обсуждений и критики в комментариях сделаны выводы, поэтому:

    1. Классы будут разнесены по разным файлам (те что были объединены в один).
    2. Анализатор для PSR-0 будет полностью следовать стандарту, а не частично
    3. Будут исправлены недочеты в именах методов (all и т.п.)
    4. Конфигурация composer возможно будет вынесена в отдельный файл composer.json

    Если есть другие предложения пишите сюда: github.com/dim-s/regenix/issues
    • 0
      Конфиг компоузера надо обязательно выносить отдельно. К этому привыкли, не стоит устраивать людям проблемы с установкой доп. компонентов.

      А в целом — хорошие выводы :)
      • 0
        1, 2, 3 пункты уже реализованы и в репозитарии. На счет 1 пункта, как дополнение я добавил возможность собрать фреймворк в один файл. В некоторых ситуациях это давало прирост в скорости инициализации на 10-20% (однако возрастало потребление памяти).
        • 0
          Имхо, смысла в этом нет. Лучше прикрутить APC.
          • 0
            Я проверял при включенном APC и XCache (с отключенным stat), все равно был профит в объединении, использовал полные пути для include и require.
  • +6
    Хочу отметить тот факт, что работа проделана не малая и, почти, доведена до логической точки.
    Да, возможно не все моменты вышли так как задумывал автор, или так как понравилось бы публике, однако Вы молодец что занимаетесь этим! Именно благодаря подобным проектам программисты и набираются опыта, и более детально понимают тонкие места реализации других проектов.

    Желаю Вам развития и не забрасывать данную затею!
    • 0
      Полностью согласен.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.