Под катом делюсь обзором своего самописного PHP-фреймворка Gy — попытки сделать легковесного «убийцу» Битрикса весом 350 Кб. Расскажу, как я реализовал вызов компонентов, зачем написал кастомный SQL-движок на текстовых файлах PhpFileSql.

Костыли, велосипеды, 3 года разработки по выходным, 315 коммитов, 14232 строки кода, поддержка практически всех версий PHP и ровно 0 пользователей.


Зачем, цели и какая связь с Битриксом

Проект начался в 2018 году, тогда же были сделаны первые коммиты. В то время на основной работе я взаимодействовал исключительно с 1С-Битрикс (бэкенд-разработка), причем со старым его ядром. Новое ядро D7 тогда уже вышло, но мне не попадалось ни одного живого проекта, где бы оно использовалось.

Параллельно у меня периодически появлялась подработка — то одному знакомому, то другому нужно было сделать простенький сайт на пару страниц с информацией и удобной админкой для редактирования данных. На Битриксе я мог собрать такое очень быстро. Сам компонентный подход и код API казались мне простыми в освоении и использовании. Но было жирное «но», Битрикс стоил денег и был гигантского размера, и чтобы купить лицензию, нужно было заморачиваться через партнеров.

Примерно тогда же на Хабре под какой-то очередной статьей, яростно хающей Битрикс, я наткнулся на комментарий в духе: «А почему никто не сделалает бесплатный аналог Битрикса?». И я подумал, а почему бы и нет?

Изначально цель была грандиозной — написать PHP-фреймворк с похожими вызовами компонентов и архитектурой. Идея заключалась в том, чтобы можно было в будущем легко переносить проекты с Битрикса на мой движок, практически не меняя код самого сайта. Конечно, Битрикс огромный, в нем очень много функционала, поэтому написать его полный аналог в одиночку было нереально. Тогда я сузил задачу, сделать легковесный инструмент, на котором можно с той же «битриксовой» легкостью собирать маленькие сайты (лендинги или информационные визитки) и удобно управлять данными через админку.

Второй важной целью было желание иметь собственный пет-проект. Место, где я сам принимаю архитектурные решения, а не согласовываю каждую кнопочку и строчку кода с менеджерами. Полная свобода, делаю то, что захочу и как захочу.

Ну и третья цель — это изучение веб-программирования и вызов самому себе. Смогу ли я в одиночку написать что-то подобное и реально работающее? Мне нужен был собственный полигон для испытаний, чтобы пробовать новые подходы, внедрять изученные технологии и тестировать безумные идеи на практике.

Разработка и отрыв от Битрикса

Так как в то время я плотно работал с Битриксом, у меня не было особого выбора или альтернативного опыта, на который можно было опереться. Я каждый день видел вызовы компонентов, подключение пролога, работал со старым API. При этом внутрь самого ядра коммерческой CMS я никогда не заглядывал и не изучал, как там всё устроено под капотом.

Видя только внешнюю, интерфейсную часть, я просто предположил, как эта магия могла бы работать изнутри. И попытался воссоздать похожую логику с нуля, так появились механизмы подключения ядра и вызова компонентов.

Скорее всего, внутреннее устройство моего фреймворка кардинально отличается от Битрикса, а в коде скрываются те еще архитектурные костыли. Но главное — система работает.

Некоторые вещи в итоге получились похожими на оригинал, но многие концепции я пересмотрел и сделал совершенно иначе. Например, у меня появилось что-то вроде модели для работы с данными, что в старом Битриксе не было очевидным. Поэтому любые совпадения архитектуры «под капотом» — это чистая случайность. Да и вряд ли они там найдутся.

Особенности процесса разработки

В режиме активного кодинга проект находился около 3 лет, начиная с 2018 года. После этого наступило относительное затишье, занялся более крупным проектом в физическом мире, требующий кучу моего времени. Я выпускал лишь небольшие доработки с одной целью, доделать необходимое для целостности, что бы была возможность минимальные сайты сделать полностью.

Фреймворк создавался по вечерам после основной работы, на выходных и даже во время отпусков. Порой код писался откровенно криво и на скорую руку. Мне хотелось поскорее закончить текущий тяжелый кусок логики, чтобы со спокойной душой переключиться на следующую интересную задачу.

Поэтому код фреймворка далек от идеала. Однако в процессе разработки я постоянно учился. Как только я узнавал какую-то новую технологию или концепцию, я тут же шел внедрять её в Gy. Так в проекте появились стандарты PSR, документирование кода, слой моделей для работы с данными, кастомные механизмы для БД и многое другое. Каждое такое внедрение приводило к масштабному рефакторингу, когда приходилось пересматривать и переписывать вообще все файлы в репозитории.

Параллельно с кодом я старался развивать и экосистему вокруг него. Я вел Wiki прямо на GitHub ( https://github.com/ssv32/gy/wiki  ), развернул отдельный сайт ( https://asisg.ru/projects/gy/ ) с документацией и даже записал несколько видеоруководств по работе с фреймворком (https://rutube.ru/plst/466964?r=wd  https://www.youtube.com/watch?v=SqHj6PZ62OM&list=PLaa5NmVx2nGjhKgdeBImncsf\_oIRcsZ2l). Сейчас за те записи мне стыдно — они получились довольно медленными, так что смотреть их стоит разве что на скорости х2. Но главное, что они есть, и по ним действительно можно понять, как развернуть сайт на моем движке.

Весь код можно увидеть тут https://github.com/ssv32/gy опубликован под лицензией GPL-3.0. Было добавлено 37 441 строк и убрано 23 209 строк, получается весь проект это 14 232 строк, но это не только php а html и css может и отступы.

Анатомия фреймворка Gy. Что внутри?

Весь фреймворк вместе с административной панелью и демо-данными весит всего 350 Кб. Для сравнения, это вес одной картинки среднего качества. При этом движок успешно тестировался на самых разных версиях PHP — от древней 5.6 до 7.2 (перед публикацией статьи протестировал на 8.3 и закрыл пару багов). Такая легковесность позволяет запускать систему буквально на «утюге» или самом дешевом копеечном хостинге.

Структура папок и архитектура

Структура файлов
gy
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── gy
│   ├── 404.php
│   ├── admin
│   │   ├── add-user.php
│   │   ├── ajax.php
│   │   ...
│   │   ├── header-admin.php
│   │   ├── index.php
│   │   ├── modules.php
│   │   ├── options.php
│   │   └── users.php
│   ├── classes
│   │   ├── Gy
│   │   │   ├── Core
│   │   │   │   ├── AbstractClasses
│   │   │   │   │   ├── Cache.php
│   │   │   │   │   └── Db.php
│   │   │   │   ├── App.php
│   │   │   │   ├── Cache
│   │   │   │   │   └── CacheFiles.php
│   │   │   │   ├── Capcha.php
│   │   │   │   ├── Component
│   │   │   │   │   ├── Component.php
│   │   │   │   │   └── Mvc
│   │   │   │   │       ├── Controller.php
│   │   │   │   │       ├── Model.php
│   │   │   │   │       └── Template.php
│   │   │   │   ├── Crypto.php
│   │   │   │   ├── Db
│   │   │   │   │   ├── MySql.php
│   │   │   │   │   ├── PgSql.php
│   │   │   │   │   └── PhpFileSqlClientForGy.php
│   │   │   │   ├── Image.php
│   │   │   │   ├── Lang.php
│   │   │   │   ├── Module.php
│   │   │   │   ├── Security.php
│   │   │   │   ├── Url.php
│   │   │   │   └── User
│   │   │   │       ├── AccessUserGroup.php
│   │   │   │       ├── GeneralUsersPropertys.php
│   │   │   │       └── User.php
│   │   │   └── Tools
│   │   │       └── Pagination.php
│   │   └── Psr
│   │       ├── Log
│   │       │   ├── AbstractLogger.php
│   │       │   ├── Logger
│   │       │   │   ├── FileRoute.php
│   │       │   │   ├── Logger.php
│   │       │   │   └── Route.php
│   │       │   ├── LoggerInterface.php
│   │       │   └── LogLevel.php
│   │       └── Psr4
│   │           └── Psr4AutoloaderClass.php
│   ├── component
│   │   ├── add_user
│   │   │   ├── componentInfo.php
│   │   │   ├── controller.php
│   │   │   ├── lang_componentInfo.php
│   │   │   ├── lang_controller.php
│   │   │   ├── model.php
│   │   │   └── teplates
│   │   │       └── 0
│   │   │           ├── lang_template.php
│   │   │           └── template.php
│   │   ├── admin
│   │   │   ├── componentInfo.php
│   │   │   ├── controller.php
│   │   │   ├── lang_componentInfo.php
│   │   │   ├── lang_controller.php
│   │   │   └── teplates
│   │   │       └── 0
│   │   │           ├── lang_template.php
│   │   │           └── template.php
│   │   ├── admin-button-public-site
│   │   │   ├── componentInfo.php
│   │   │   ├── controller.php
│   │   │   ├── lang_componentInfo.php
│   │   │   └── teplates
│   │   │       └── 0
│   │   │           ├── lang_template.php
│   │   │           ├── style.css
│   │   │           └── template.php
│   │   ├── capcha
│   │   │   ├── componentInfo.php
│   │   │   ├── controller.php
│   │   │   ├── lang_componentInfo.php
│   │   │   └── teplates
│   │   │       └── 0
│   │   │           ├── lang_template.php
│   │   │           └── template.php
│   │   ...
│   │   └── users_group_manager
│   │       ├── componentInfo.php
│   │       ├── controller.php
│   │       ├── lang_componentInfo.php
│   │       ├── lang_controller.php
│   │       └── teplates
│   │           └── 0
│   │               ├── lang_template.php
│   │               └── template.php
│   ├── config
│   │   └── gy_config.php
│   ├── fonts
│   │   └── 18018.otf
│   ├── gy.php
│   ├── images
│   │   ├── fon.png
│   │   └── gy-icons.jpg
│   ├── index.php
│   ├── install
│   │   ├── consoleInstallOptions.php
│   │   ├── installDataBaseTable.php
│   │   └── installDemoSite1.php
│   ├── js
│   │   └── main.js
│   ├── lang
│   │   ├── lang_404.php
│   │   ├── lang_component.php
│   │   └── lang_header-admin.php
│   ├── modules
│   │   ├── containerdata
│   │   │   ├── admin
│   │   │   │   ├── container-data-add.php
│   │   │   │   ├── container-data-edit.php
│   │   │   │   ├── container-data-element-list.php
│   │   │   │   ├── container-data-element-property.php
│   │   │   │   ├── container-data.php
│   │   │   │   └── container-data-property-edit.php
│   │   │   ├── classes
│   │   │   │   └── ContainerData.php
│   │   │   ├── component
│   │   │   │   ├── containerdata
│   │   │   │   │   ├── componentInfo.php
│   │   │   │   │   ├── controller.php
│   │   │   │   │   ├── lang_componentInfo.php
│   │   │   │   │   └── teplates
│   │   │   │   │       └── 0
│   │   │   │   │           ├── lang_template.php
│   │   │   │   │           └── template.php
│	│	│	│	...
│	│	│	│
│   │   │   │   └── news
│   │   │   │       ├── componentInfo.php
│   │   │   │       ├── controller.php
│   │   │   │       ├── lang_componentInfo.php
│   │   │   │       └── teplates
│   │   │   │           └── 0
│   │   │   │               ├── lang_template.php
│   │   │   │               └── template.php
│   │   │   ├── init.php
│   │   │   ├── install
│   │   │   │   └── installDataBaseTable.php
│   │   │   └── lang_init.php
│   │   └── filemodule
│   │       ├── admin
│   │       │   └── work-page-site.php
│   │       ├── classes
│   │       │   ├── AppFromConstructorPageComponent.php
│   │       │   ├── Files.php
│   │       │   └── SitePages.php
│   │       ├── component
│   │       │   └── work_page_site
│   │       │       ├── componentInfo.php
│   │       │       ├── controller.php
│   │       │       ├── lang_componentInfo.php
│   │       │       └── teplates
│   │       │           └── 0
│   │       │               ├── lang_template.php
│   │       │               ├── style.css
│   │       │               └── template.php
│   │       ├── init.php
│   │       └── lang_init.php
│   ├── style
│   │   └── main.css
│   └── test
│       └── testLoger.php
├── LICENSE
├── README.md
└── SECURITY.md

Вся логика движка изолирована в одной папке /gy/. По названиям внутренних директорий сразу понятно, где что находится: компоненты, шаблоны, системные классы и модули.

Основная фишка такой структуры — юридическая и архитектурная независимость кода:

  • Лицензии подлежит только папка /gy/.

  • Код страниц самого сайта (например, index.php), а также все кастомные наработки выносятся в отдельный раздел.

  • В кастомном разделе (/customDir/ ) разработчик может переопределять стандартные классы, шаблоны, логику компонентов или создавать свои с нуля. При этом раскрывать этот код или отдавать его под лицензией движка не требуется.

 Административная панель сосредоточена в папке /gy/admin/. При этом архитектура позволяет модулям иметь собственные независимые разделы админки, если это необходимо для управления их специфическими данными.

Как выглядит админ панель
Как выглядит админ панель
Админ панель, скриншоты

Тестирование (точнее, его отсутствие)

В репозитории можно найти папку /tests/, но пугаться или радоваться не стоит — тесты там так и не были написаны. Всё тестирование фреймворка проводилось исключительно вручную по мере разработки. Конечно, было бы круто покрыть код юнит-тестами, но дойти до 100% покрытия в одиночку — это колоссальный объем работы, на который банально не хватило времени.

Фронтенд без зависимостей

Финально в ядре фреймворка вообще не используется JavaScript. У разработчика есть возможность подключить файлы стилей (CSS) и скриптов (JS) для компонентов, а также стандартными средствами подключать. На ранних этапах разработки я внедрял jQuery, но позже сознательно полностью вырезал его, чтобы избавить систему от лишних внешних зависимостей и сохранить минимальный вес.

Слой моделей у компонентов

Полноценная архитектура подразумевает наличие слоя моделей (Model) у компонентов. Механика для этого создана, но написана она была уже на поздних этапах разработки. В качестве эксперимента и рабочего примера внедрил её всего в один компонент — add_user. Во всех остальных компонентах модель до сих пор сделана в виде заглушки, так как переписывать все компоненты фреймворка ради этого я не стал.

Установка и деплой

Для фреймворка написан отдельный установщик, репозиторий доступен на https://github.com/ssv32/install-gy-php-framework . Там же лежит скрипт, который автоматически собирает сам установщик. Развернуть Gy на хостинге можно тремя способами: через графический веб-интерфейс, напрямую через консоль или просто скопировав файлы из основного репозитория.

Графическая установка
Графическая установка
Скриншоты
Демо сайт
Демо сайт
Ещё скриншоты

Работа с СУБД и кастомная PhpFileSql

Поддержка баз данных реализована через наследование абстрактного класса Classes/Gy/Core/AbstractClasses/Db.php. Взаимодействие с БД строится на простейших операциях CRUD (создание, чтение, обновление, удаление). Благодаря отсутствию сложных перегруженных механик, подключить новую СУБД можно очень быстро. Сейчас движок «из коробки» поддерживает:

·      MySQL / MariaDB

·      PostgreSQL (добавил её чисто на кураже, сразу после посещения одной из IT-конференций).

·      PhpFileSql

Отдельная фишка — поддержка кастомной мини-СУБД PhpFileSql (https://github.com/ssv32/PhpFileSql). Когда-то я наткнулся на Stack Overflow на обсуждение файловых БД и решил ради фана написать свою. Это один класс, данные хранятся в зашифрованном файле.

Когда я интегрировал её в Gy, сработал обратный эффект, СУБД пришлось экстренно дорабатывать под нужды фреймворка. Я добавил туда полноценный PRIMARY_KEY_AUTO_INCREMENT для автоматического увеличения номера строки и исправил пачку всплывших багов, чтобы обе системы работали корректно. Для маленьких лендингов это оказалось идеальным решением — база весит около 200 Кб и лежит прямо в папке проекта (на разделе выше публичного, для безопасности).

Пользователю Gy не нужно писать чистый SQL-код, в движке реализованы специальные выражения (выборки), через которые условия передаются в классы работы с БД. Это позволяет разворачивать проекты без глубоких знаний БД, достаточно один раз создать пустую базу. По такому же принципу абстракции заложена и система кэширования, хотя пока в ней реализован только файловый кэш.

// пример условия выборки данных
$dataElement3 = ContainerData::getElementContainerData(
    array(
        'AND' => array(
            array( '=' => array( 'id_container_data', $dataContentContainerData[0]['id']) ),
            array( '=' => array( 'code', "'title_h1'"))
        )
    )
);

Готовые модули

На текущий момент в системе работают два базовых модуля:

·      containerdata — отвечает за создание и управление «контейнерами данных» для хранения информации в админке.

·      filemodule — берет на себя управление страницами сайта напрямую из панели администратора (можно менять код файла или графически добавлять перемещать компоненты).

Также написан набор стандартных компонентов для публичной части: вывод новостей, постраничная навигация (пагинация) и вывод данных из админки.

Безопасность и архитектурная гигиена

·      Проверка окружения: Скрипты на страницах всегда проверяют, подключено ли ядро системы (defined("GY_CORE")), а в админке валидируются права текущего пользователя.

·      Фильтрация данных: Все входящие данные глобально очищаются. Единственное исключение, текстовые поля в админке, куда контент-менеджер сознательно может вводить чистый HTML-код для страниц.

·      Защита от брутфорса: Стандартный адрес админ-панели Gy можно скрыть за кастомным секретным URL-адресом.

 

Огромная часть фич так и осталась на стадии планов. Есть TODO-трекер проекта  https://github.com/users/ssv32/projects/2 но возможно уже не актуальный.

Пример использования Gy

Пример подключения gy php framework

<?php 
include $_SERVER["DOCUMENT_ROOT"]."/gy/gy.php"; // подключить ядро // include core

Пример проверки подключено ли ядро gy php framework

<?php
if (!defined("GY_CORE") && (GY_CORE !== true)) die( "gy: err include core" );

Пример вызова компонента

<?php 
include $_SERVER["DOCUMENT_ROOT"]."/gy/gy.php"; // подключить ядро // include core

// пример вызова компонента 
$APP->component(
    'form_auth',
    '0',
    array(
        'test' => 'asd',
        'idComponent' => 1,
    ),
);

Пример кода админ страницы, добавление пользователя

<?php
include "../../gy/gy.php"; // подключить ядро // include core

global $USER;

// проверим разрешено ли показывать админ панель текущему пользователю
if (Gy\Core\User\AccessUserGroup::accessThisUserByAction( 'show_admin_panel')) {

    include "../../gy/admin/header-admin.php";

    // Проверим разрешено ли работать с пользователями текущему пользователю
    if (Gy\Core\User\AccessUserGroup::accessThisUserByAction( 'edit_users')) {
        $APP->component(
            'add_user',
            '0',
            array(
                'back-url' => '/gy/admin/users.php'
            )
        );
    }
    
    include "../../gy/admin/footer-admin.php";

} else {
    header( 'Location: /gy/admin/' );
}

Код контроллера компонента add_user

Скрытый текст
<?php 
if (!defined("GY_CORE") && (GY_CORE !== true)) die( "gy: err include core" );

$data  = $_REQUEST;

// подключить модель (файл php model этого компонента) // include model this component
if (isset($this->model)) {
    $this->model->includeModel();
}

$this->model->backUrl = $this->arParam['back-url'];

$redirectUrl = str_replace('index.php', '', $_SERVER['SCRIPT_NAME']);

// взять все группы пользователей
$this->model->allUsersGroups = Gy\Core\User\AccessUserGroup::getAccessGroup();

function checkProperty($arr, $userProperty, $allUsersGroups){
    $result = true;
    foreach ($userProperty as $val) {
        if (empty($arr[$val])) {
            $result = false;
        }
    }

    if ($result) {
        foreach ($arr['groups'] as $value) {  // TODO протестировать

            if (empty($allUsersGroups[$value])) {
                $result = false;
            }

            if (!empty($arr['groups']['admins']) && !$USER->isAdmin()) { // TODO протестировать
                $result = false;
            }
        }
    }

    return $result;
}

if (!empty($data[$this->lang->getMessage('button')]) && ($data[$this->lang->getMessage('button')] == $this->lang->getMessage('button'))) {
    if (checkProperty($data, $this->model->userProperty, $this->model->allUsersGroups)) {
        // добавление пользователя
        global $USER;
        $arDaraUser = array();
        foreach ($this->model->userProperty as $val) {
            $arDaraUser[$val] = $data[$val];
        }

        // убрать группы из добавления
        unset($arDaraUser['groups']);

        if ($USER->addUsers($arDaraUser)) {
            // найти id добавленного пользователя
            global $DB;
            global $CRYPTO;
            $res = $DB->selectDb(
                $USER->tableName,
                array('*'),
                array(
                    'AND' => array(
                        array('=' => array('login', "'".$arDaraUser['login']."'")),
                        array('=' => array('pass', "'".md5($arDaraUser['pass'].$CRYPTO->getSole())."'") )
                    )
                )
            );
            $dataAddNewUser = $DB->fetch($res);

            // добавить пользователя к указанным группам
            Gy\Core\User\AccessUserGroup::deleteUserInAllGroups($dataAddNewUser['id']);
            foreach ($data['groups'] as $value) {
                Gy\Core\User\AccessUserGroup::addUserInGroup($dataAddNewUser['id'], $value);
            }

            $this->model->stat = 'ok';
        } else {
            $this->model->stat = 'err';
        }

    } else {
        $this->model->statText = $this->lang->getMessage('err_property') ; // ! Не все поля заполнены
        $this->model->stat = 'err';
    }

} elseif ((!empty($this->model->stat) && ($this->model->stat != 'err')) || empty($this->model->stat)) {
    $this->model->stat = 'add';
}

if (empty($data['stat'])) {
    header( 'Location: '.$redirectUrl.'?stat='.$this->model->stat );
} else {
    $this->model->stat = $data['stat'];
}

// установить модель этого компонента в шаблон (view)
$this->template->setModel($this->model);

// показать шаблон (view)
$this->template->show();

Код шаблона компонента add_user

Скрытый текст
<?php
if (!defined("GY_CORE") && (GY_CORE !== true)) die( "gy: err include core" );
?>
<h3><?=$this->lang->getMessage('title-add');?></h3>
<?php

if (!empty($this->model->backUrl)) {?>
    <br/>
    <br/>
    <a class="gy-admin-button" href="<?=$this->model->backUrl;?>"><?=$this->lang->getMessage('back');?></a>
    <br/>
    <br/>
<?php }?>
<?php if ($this->model->stat == 'add') {?>
    <form>
        <?php foreach ($this->model->userProperty as $key => $val) {?>
            <?=$this->lang->getMessage($val);?>:<br/>
            <?php if ($val != 'groups') {?>
                <input type="<?=(($val == 'pass')? 'password': 'text');?>" name="<?=$val;?>" />
            <?php } else {?>
                <select multiple name="groups[]">
                    <?php foreach ($this->model->allUsersGroups as $value) { ?>
                        <option value="<?=$value['code'];?>">
                            <?=$value['name']?> (<?=$value['code'];?>)
                        </option>
                    <?php }?>
                </select>
            <?php }?>
        <br/>
        <?php }?>
    <input class="gy-admin-button" type="submit" name="<?=$this->lang->getMessage('button');?>" value="<?=$this->lang->getMessage('button');?>" />

    </form>

<?php } elseif ($this->model->stat == 'ok') {?>
    <div class="gy-admin-good-message"><?=$this->lang->getMessage('add-ok');?></div>
    <br/>
    <a href="<?=$this->model->backUrl;?>" class="gy-admin-button"><?=$this->lang->getMessage('ok');?></a>
<?php } elseif ($this->model->stat == 'err') { ?>
    <div class="gy-admin-error-message"><?=$this->lang->getMessage('add-err');?></div>
    <?php if (!empty($this->model->statText)) {?>
        <br/> <?=$this->model->statText;?>
    <?php }?>
    <br/>
    <a href="<?=$this->model->backUrl;?>" class="gy-admin-button"><?=$this->lang->getMessage('ok');?></a>
<?php } 

Выводы

На разработку было потрачено прилично времени. Многое удалось реализовать, но еще больше так и осталось в планах. Чтобы три года работы не канули в лету окончательно, я и решил написать эту статью на Хабр — как минимум, чтобы зафиксировать этот опыт и узнать мнение сообщества.

Общий вывод: Проект может и не «взлететь», хотя полностью  пока не доделан. Да, всегда есть бесплатные альтернативы вроде Drupal, но они весят больше, и изучать их приходится дольше.

Вывод для себя: Это было отличное времяпрепровождение. Я решал задачи по собственному видению, написал кучу кода и прокачался в веб-разработке. Одиночка действительно может создать всё что угодно — вопрос лишь во времени. Ну и, возможно, проект кому-то понравится и появится желание поконтрибьютить в репозиторий.

Что касается будущего, есть понимание, куда двигаться дальше и что нужно переделать (я не боюсь перелопатить хоть весь проект заново). Но вот стоит ли оно затраченного времени — большой вопрос.

Если пофантазировать, путей развития несколько:

  • Интеграция сторонних решений: Подключить Composer и внедрить готовые компоненты от Symfony (например, для работы с Request/Response) или продолжать писать всё вручную.

  • Публикация пакета: Добить код до минимального стабильного состояния, чтобы на движке можно было гарантированно развернуть сайт с нуля, и выкатить его в Packagist.

  • Глубокий рефакторинг: Начать писать локальный аналог битриксового ядра D7 или просто переписать всё с нуля в Gy 2.0. Правда, если реализовать вообще все современные стандарты PSR, то на выходе получится аналог Symfony или Laravel, а делать их копии нет никакого смысла.

Также была забавная мысль, сделать на сайте фреймворка форму с тестом и в конце выдавать «Сертификат сертифицированного разработчика Gy». Который можно ради шутки добавить в свое резюме — пусть HR удивляются сертификату по абсолютно неизвестной CMS.


Опросы

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


  1. Mylistryx
    23.06.2026 06:52

    Это лютое дно! Сразу видно руку "мастера" и битриксоида.


    1. ssv32 Автор
      23.06.2026 06:52

      это старый код. И это как раз и была попытка сделать что-то типа бесплатного Битрикса, взять его главные преимущества и что-то доработать под себя. Но в одиночку написать полноценную CMS/Framework действительно очень сложно.


      1. Vadiok
        23.06.2026 06:52

        в одиночку написать полноценную CMS/Framework действительно очень сложно

        Не в одиночку тоже не так уж просто. Но тут самый главный вопрос - почему не пользоваться готовым всем, чем можно - ORM, шаблонизатором, макетами админок, Composer, да даже целый фреймворк (Laravel, Symfony, Yii) затащить в чем проблема?

        Вообще этот проект продолжать имеет смысл:

        • Если он нужен вам для ваших нужд, развития или развлечений, тогда его вообще можно сделать закрытым и никому не показывать.

        • Если вы можете ответить, кто ваш воображаемый пользователь, и какие ему ваша CMS дает конкурентные преимущества (да такие, чтобы эти преимущества перекрывали недостатки) по сравнению с аналогами, ради которых тратить время и его стоит изучать.


        1. ssv32 Автор
          23.06.2026 06:52

          Хороший комментарий и правильные вопросы.

          Про воображаемого пользователя: на тот момент (в 2018 году) он виделся мне вполне конкретным. Это Битрикс-разработчик, у которого уже есть опыт работы с этой CMS, и ему нужно прямо сейчас собирать мелкие сайты подработки. При этом он не хочет покупать коммерческую лицензию Битрикса и не готов тратить время на изучение условного Drupal или Laravel ради простого лендинга на пару страниц. Плюс к этому — ультрамалый вес движка и отсутствие жесткого требования к MySQL на сервере (что было актуально для самых дешевых хостингов).

          Актуально ли это для кого-то прямо сейчас? Мне уже сложно сказать (как раз для этого я и прикрутил опрос в конце статьи). Сам я с Битриксом сейчас работаю редко, да и с 2018 года на рынке появилось гораздо больше подробной документации и других бесплатных готовых решений.

          Про личные нужды: да, на тот момент фреймворк создавался под собственные задачи и, в первую очередь, для глубокого изучения веб-разработки изнутри.


          1. Vadiok
            23.06.2026 06:52

            на тот момент [2018 год] фреймворк создавался под собственные задачи и, в первую очередь, для глубокого изучения веб-разработки изнутри

            Для этого как раз стоило выбрать существующих гигантов.

            PHP 5.4 вышел в 2012, там уже синтаксис массивов [], а не array. Неймспейсы появились в PHP 5.3 в 2009, не говоря уже об автоимпортах, DI и прочем. Вы ничем этим не пользуетесь, есть подозрение, что как раз по причине того, что этой глубины веб-разработки в проекте нет, и учиться получается не на чем.


            1. ssv32 Автор
              23.06.2026 06:52

              Проект развивался 3 года, и стиль кодинга менялся вместе с моим обучением. Что-то могло быть не переписано, что-то исправлено. Конструкция array() использовалась для поддержки старых версий языка.  Просто тестировалось минимум на 5.6.

              Неймспейсы в проекте используются. (есть автоподключение классов, сам компосер не пробовал на тот момент подключать)

              Использовать это для обучения кому-то сейчас точно не стоит - скорее для обзора масштаба или, может быть, для мотивации. На тот момент это был инструмент именно для моего обучения, код постоянно писался, переписывался и переделывался.

              Это же процесс и состояние кода, сейчас такая стадия (зафиксированное состояние), завтра можно всё переписать, а вот нужно ли это уже другой вопрос. (или сделать всё только на php 8.4)


  1. Vitaly48
    23.06.2026 06:52

    Сила битрикса в его масштабах, есть много веб студий которые умеют с ним работать, под него много готовых модулей интеграций с разными платежными системами, с доставками, с CRM системами. У битрикса есть поддержка и сообщество.
    Но вот качество кода и его архитектура просто ужасны. Писать на нём код это боль и страдания. Документация оставляет желать лучшего. Паттерны проектирования там просто отсутствуют. Да и производительность не ахти.
    По сути вы своим фреймворком избавились от всех плюсов битрикса и взяли его худшие стороны пытаясь повторить его архитектуру


    1. ssv32 Автор
      23.06.2026 06:52

      Про бизнес-составляющую Битрикса (сообщество, готовые модули, интеграции) вы правы на 100% - именно поэтому он коммерческий гигант. И именно поэтому в одиночку повторить этот масштаб нереально.

      Но вот насчет «взял худшие стороны» я бы поспорил. Я взял из Битрикса только интерфейсную идею, изоляцию блоков в виде компонентов, которые вызываются одной строчкой и принимают массив параметров. Для битрикс разработчика в 2018 году это был очень понятный и быстрый способ собирать страницы.

      Архитектурно это скорее попытка взять простую и удобную концепцию компонентов, но реализовать её в ультралегком весе без коммерческого «монстра» под капотом.

      + это бесплатно. И порог входа, обучение как у битрикса, за счёт компонентов.

      Есть и куча минусов, недоделанного, например нельзя api написать или привычные роуты.