О проекте
Логотип основной

О проекте

                   ○○○○   ○○○○
                 ○○○○○  ○○○○○○
                ○○○    ○○○
    ○○○○○○○     ○○○○○○○○○○○○○○  ○○○○○○○      ○○○○○○○     ○○ ○○○   ○○○○○○○
  ○○○○   ○○○○   ○○○○○○○○○○○○  ○○○     ○○○  ○○○     ○○○   ○○○    ○○○     ○○○
 ○○○       ○○○  ○○○    ○○○   ○○           ○○         ○○  ○○    ○○         ○○
○○○○○○○○○○○○○○  ○○○    ○○○  ○○           ○○           ○○ ○○   ○○○○○○○○○○○○○○
 ○○○            ○○○    ○○○   ○○           ○○         ○○  ○○    ○○
  ○○○○   ○○○○   ○○○    ○○○    ○○○     ○○○  ○○○     ○○○   ○○     ○○○     ○○○
    ○○○○○○○     ○○○    ○○○      ○○○○○○○      ○○○○○○○     ○○       ○○○○○○○
              ○○○○   ○○○○
            ○○○○   ○○○○

Общая информация

  • Тип: система управления содержимым/фреймворк
  • Автор: Рысевец Максим Владимирович
  • Разработчик: Рысевец Максим Владимирович
  • Начало разработки: конец 2016 года
  • Первый выпуск: 01.01.2022
  • Написан на: PHP
  • Поддерживаемые ОС: UNIX, Linux, Microsoft Windows
  • Поддерживаемые веб-сервера: NGINX, Apache, IIS
  • Поддерживаемые БД: SQLite, MySQL
  • Проект с открытым исходным кодом: да
  • Лицензия: проприетарное программное обеспечение
  • Сайт: http://effcore.com
  • Репозиторий кода: https://github.com/effectivecore

EFFCORE — это мини-CMS (система управления содержимым) и мини-CMF (фреймворк управления содержимым) нового поколения. Была разработана в промежутке между 2016 и 2024 годам. Использует такие технологии как: HTML5, CSS3, IP v6, SVG, SMIL, UTF8, UTC, WAI-ARIA, NoSQL, Markdown, UML, Composer, Docker…

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

Название системы является акронимом и происходит от фразы effective core. Система была разработана с нуля. Её главный принцип — отсутствие стороннего кода (для исключения правовых претензий), а также отсутствие сторонних идей (для выработки инновационных решений).

Стимулом для создания системы послужила массовая деградация в развитии Open Source проектов. Первая причина — это распределённая команда разработки. По своей сути — это группа лиц с различным уровнем квалификации, различными взглядами и слабым уровнем координации в таких проектах. Вторая причина — это непосредственно подход к разработке. Вместо интенсивного пути развития они выбрали экстенсивный. Интенсивный путь развития подразумевает разработку собственного кода и его тесную интеграцию с другими компонентами системы. Экстенсивный путь — это использование готовых библиотек и сборка этих библиотек в единое целое. При этом из каждой такой библиотеки задействуется лишь часть функционала. Каждая такая библиотека была создана для абстрактных целей. В результате объём кода растёт, нагрузка на оборудование растёт, количество ошибок растёт, надёжность и безопасность падает, а функционал увеличивает лишь незначительно.

Основной упор в системе сделан на получение максимальной производительности. Оценочным критерием является простое и понятное условие: система, установленная на хостинге с самым дешёвым тарифным планом, должна генерировать главную страницу за 0,01 секунду. Это позволит обслуживать одновременно до ~100 запросов в секунду. Тарифный план должен соответствовать минимальным требованиям установки и включать в себя такие базовые возможности как OPCache + JIT и твёрдотельный накопитель. Примерная стоимость тарифного плана должна варьироваться в пределах 3-5$ в месяц.

Управление содержимым

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

Локализация

Интерфейс системы может быть переведён на любой язык. Система уже поставляется с некоторым числом переводов. Изменить язык интерфейса можно в любой момент времени при помощи раздела Управление → Локализации.

В свою очередь, любая страница (кроме административного интерфейса) может переопределить глобальные настройки и выбрать иной язык, что-бы соответствовать языку контента на этой странице. Настройка каждой страницы осуществляется в разделе Управление → Данные → Содержимое → Страницы.

В системе реализована более совершенная система "Plural". Это система позволяет выполнить вставку любого контента из Вашей функции в зависимости от значения переменной во фразе. Пример: %%_number секунд%%_plural(number|a-ji) при number = 1 будет возвращать 1 секунда, а при number = 10 будет возвращать 10 секунд.

Организовать мультиязычный веб-сайт/веб-портал можно двумя способами:

  1. в рамках одного домена организовать столько копий страниц и блоков, сколько языков необходимо поддерживать (например, http://example.com/EN/about);
  2. для каждого языкового домена организовать отдельную копию системы (например, http://EN.example.com/about).

Примечание: И первая и вторая версии требуют наличия дубликатов страниц и блоков на различных языках. Данный подход является оправданным т.к. часто разные языковые версии контента имеют отличия не только в содержании, но и в структуре. Например, главное меню на одной языковой версии может иметь одни пункты меню, а на другой — совсем иные — будут отличаться и названия этих пунктов и их адреса, и их количество.

В модуле "Профиль "Классический" Вы можете увидеть пример реализации мультиязычности по способу №1.

Оформление

В системе нет такого понятия как тема оформления. Вместо тем используется особый вид модуля под названием "Профиль". Любой профиль может содержать следующие виды элементов:

  • страницы;
  • макеты страниц;
  • блоки (меню, текст, аудио, видео, галереи, выборки, опросы и другие);
  • цвета и цветовые профили;
  • шаблоны в файлах *.tpl/*.data;
  • пользовательские стили в файлах *.css/*.cssd;
  • пользовательские скрипты в файлах *.js/*.jsd;
  • файлы robots.txt, sitemap.xml;
  • любые файлы, которые копируются в систему при развёртывании профиля;
  • и другие элементы, которые может генерировать типичный модуль.

Профиль устанавливается как обычный модуль. Если в файле профиля module.data указать enabled: yes тогда он появиться в списке профилей на странице установки системы. При включении профиля он может как переопределить какие-либо настройки оформления, так и не производить никаких изменений, а лишь добавлять дополнительные возможности которые пользователь должен активировать самостоятельно.

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

Для работы с оформлением в административном интерфейсе системы имеется раздел Управление → Вид, который включает в себя следующие подразделы:

  • Цвета → Управление: работа с цветами;
  • Цвета → Профили: работа с цветовыми профилями;
  • Макеты: просмотр доступных макетов страниц;
  • Глобальный CSS: добавление пользовательских CSS-директив;
  • Настройки: изменение минимальной и максимальной ширины всех страниц (данный параметр можно переопределить в настройках каждой страницы);

Все макеты, имеющиеся в системе (а также Выборки с типом оформления "Таблица (адаптивная)") уже способны адаптироваться к мобильной версии.

Файловая организация

Интуитивно-понятное расположение файлов в системе позволяет определить их назначение, не прибегая к документации:

  • в директориях вида module_*/frontend располагается всё необходимое для frontend-разработки;
  • в директориях вида module_*/backend располагается всё необходимое для backend-разработки;
  • в директориях вида module_*/data располагаются NoSQL-данные.

Система имеет встроенный парсер и загрузчик классов. Работа файлов не зависит от их расположения и при необходимости все файлы будут найдены и обработаны если они располагаются в директориях modules и system.

Размещение в файловой системе основных модулей системы, сторонних модулей и пакетов библиотек можно представить следующей схемой:

├─ modules
│  ├─ module_custom_0
│  ├─ module_custom_1 …
│  ├─ module_custom_N
│  │  ├─ backend
│  │  ├─ data
│  │  └─ frontend
│  └─ vendors
│     └─ packages
│        ├─ package_0
│        ├─ package_1 …
│        └─ package_N
└─ system
   ├─ module_0
   ├─ module_1 …
   └─ module_N
      ├─ backend
      ├─ data
      └─ frontend

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

Сторонние модули следует размещать в директории modules. Пакеты библиотек следует размещать в директории modules\vendors\packages.

Информация о модулях и пакетах библиотек хранится в кэше поэтому при изменении состояния их файлов, следует сбрасывать кэш. Для сброса кэша необходимо перейти в административный интерфейс в раздел Управление → Модули → Установить и нажать кнопку .

Архитектура

Архитектура выполнена по классической схеме MVC. Хранение данных осуществляется в SQL и NoSQL-хранилищах. Для структурированных данных используется SQL-хранилище. Для неструктурированных данных (например, формы, цветовые профили, тесты, настройки модулей) используется NoSQL-хранилище. Описание SQL и NoSQL приводится в соответствующих разделах.

Система оперирует двумя типами классов:

  1. статические классы-фабрики;
  2. классы-паттерны. По количественному составу, классов-паттернов намного больше чем статических фабрик.

Описание экземпляров классов-паттернов (будущих PHP-объектов) храниться в NoSQL. В момент выборки этого описания и происходит создание экземпляра (объекта).

Код системы следует принципу DRY (Don’t Repeat Yourself; с англ. — "не повторяйтесь") и KISS (Keep It Short and Simple; с англ. — "будьте краткими и простыми"). Каждый класс состоит из небольшого числа методов, состоящих из небольшого числа строк. Код адаптирован для повторного использования, а такой подход существенно облегчает восприятие кода.

Процесс разработки следует следующим правилам:

  • всё что кажется сложным, должно быть переделано (каждый метод может быть итеративно переписан от 3 до 10 раз);
  • для функционального тестирования необходимо перебрать все возможные комбинации аргументов/параметров (полное множество комбинаторных перестановок), чтобы проверить как правильную работу системы и её компонентов, так и работу при заведомо ложных значениях (для поиска простых ошибок и преднамеренных попыток взлома).

В разделе Разработка → Структуры → Диаграмма генерируется диаграмма классов, зарегистрированных в системе. Здесь же имеется и ссылка для загрузки этой диаграммы в виде JSON-файла, для программы StarUML. Данный раздел станет доступен после включения модуля "Разработка".

В разделе Разработка → Консоль можно включить отображение консоли для разработчиков. В консоли показывается порядок действий при генерации страницы (вызов классов, подключение файлов, запуск событий, запросы к БД и другие). Данный раздел станет доступен после включения модуля "Разработка".

В разделе Разработка → Тесты имеются базовые тесты. Тест "Безопасность настроек сервера" позволяет проверить серверное ПО на безопасность. Тест "Безопасность ролей" позволяет проверить безопасность ролевой системы проекта. Данный раздел станет доступен после включения модуля "Тест".

Формат *.data. NoSQL

Одной из сильных сторон проекта является формат *.data. Он схож с форматом YAML, однако имеет следующие преимущества:

  • более простое описание;
  • более строгий и однозначный синтаксис;
  • более быстрый парсер;
  • позволяет описывать будущие объекты любых классов-паттернов;
  • каждая строка формата описывает только одно свойство и его значение, например, таким образом: ключ: значение;
  • изменение одной строки в файле *.data приводит к изменению одной строки в git diff.

Пример файла *.data:

example
  string: text
  string_empty: 
  integer: 123
  float: 0.000001
  boolean: true
  null: null
  array
  - key_1: value 1
  - key_2: value 2
  - key_3: value 3
  array_empty|_empty_array
  object
    property_name_1: value 1
    property_name_2: value 2
    property_name_3: value 3
  object_empty
  object_text|Text
    text: some translated text

Такой файл будет преобразован в PHP-файл /dynamic/cache/data--example.php с примерно следующим содержанием:

Cache::$data['example'] = new \stdClass;
Cache::$data['example']->string = 'text';
Cache::$data['example']->string_empty = '';
Cache::$data['example']->integer = 123;
Cache::$data['example']->float = 0.000001;
Cache::$data['example']->boolean = true;
Cache::$data['example']->null = null;
Cache::$data['example']->array['key_1'] = 'value 1';
Cache::$data['example']->array['key_2'] = 'value 2';
Cache::$data['example']->array['key_3'] = 'value 3';
Cache::$data['example']->array_empty = [];
Cache::$data['example']->object = new \stdClass;
Cache::$data['example']->object->property_name_1 = 'value 1';
Cache::$data['example']->object->property_name_2 = 'value 2';
Cache::$data['example']->object->property_name_3 = 'value 3';
Cache::$data['example']->object_empty = new \stdClass;
Cache::$data['example']->object_text = new \effcore\Text;
Cache::$data['example']->object_text->text = 'some translated text';

При вызове Cache::select('example') этот файл будет загружен в память и данные станут доступны без задержки.

Если используется PHP OPCache, тогда все данные будут скомпилированы в оп-код и станут храниться в разделяемой памяти, что не потребует времени на загрузку PHP-файла и его анализ при каждом запросе. Если используется PHP JIT то части кода преобразуются уже в байт-код и выполняются ещё быстрее.

С помощью такого формата разработчик может описать любую структуру с любым уровнем вложенности — он ограничен только своей фантазией. Для каждой строки в таком формате существуют лишь следующие вариаций записи:

  • имя_сущности
  • имя_сущности|Имя_класса
  • имя_свойства_обьекта: значение
  • - имя_ключа_массива: значение

Каждый модуль может иметь любое число файлов *.data и располагать их где угодно, но традиционно все такие файлы будут храниться в директории data каждого модуля.

Набор таких файлов *.data, а также механизм их парсинга, хранения и выборки, по своей сути и является NoSQL-хранилищем. Фактически это гибрид из документ-ориентированной, объектно-ориентированной и иерархической модели БД.

Парсинг всех файлов *.data происходит один раз — при очистке кэша. Очистка кэша — это очень редкая процедура, которая требуется лишь после обновления модулей в системе. После парсинга всё содержимое помещается в файл dynamic/cache/data_original.php. Также для каждого вида сущности создаётся отдельный файл. Вот пример такой организации файлов:

  • dynamic/cache/data--forms.php
  • dynamic/cache/data--pages.php
  • dynamic/cache/data--menus.php

Когда необходимо внести изменение в дерево NoSQL, применяется механизм "Changes". Он описывает то, какие изменения следует внести в это дерево. Процесс внесения изменений можно описать следующей процедурой: производится полная загрузка дерева NoSQL из файла data_original.php, далее в это дерево вносятся изменения, далее производится пофайловое сохранение каждой сущности дерева NoSQL (пример выше). Если для какой-либо сущности не было изменений — файл не перезаписывается. Механизм "Changes" также применяется редко — главным образом при сохранении системных настроек через административный интерфейс.

Примечание: пример внесения изменений показан в файле test--data--changes.data модуля "Тест".

Скорость обновления кэша в среднем не превышает одной секунды. Скорость обновления через "Changes" выполняется за десятые доли секунды. Таким образом NoSQL работает в основном в режиме чтения и выдаёт максимально возможную производительность при использовании OPCache + JIT.

SQL

В качестве SQL-хранилища могут быть использованы MySQL или SQLite. Требуемые версии можно узнать в файле docs/software.md. Основной упор в системе сделан на ANSI SQL и кроссплатформенность.

Поддерживаются следующие возможности:

  • проверки check (SQLite, MySQL v.8+);
  • транзакции transaction (begin, rollback, commit);
  • сопоставления collate (nocase, binary);
  • ограничения constraint (primary, unique, foreign с каскадным действием);
  • простые и уникальные индексы (index, unique index);
  • подключение к дополнительным хранилищам через процесс ручной инициализации;
  • префиксы таблиц.

Поддерживаются каскадные действия внешнего ключа:

  • на обновление: cascade (restrict и no action не были протестированы);
  • на удаление: cascade (restrict и no action не были протестированы).

Поддерживаются кроссплатформенные типы полей:

  • autoincrement
  • varchar (MySQL: varchar | SQLite: text)
  • integer (MySQL: int | SQLite: integer)
  • real (MySQL: double | SQLite: real)
  • time (MySQL: time | SQLite: text)
  • date (MySQL: date | SQLite: text)
  • datetime (MySQL: datetime | SQLite: text)
  • boolean (MySQL: tinyint | SQLite: integer)
  • blob (MySQL: blob | SQLite: blob)

Набор указанных типов достаточен для большинства задач. Другие типы допускаются, но они не были протестированы. Рекомендуется использовать только данные типы для обеспечения кроссплатформенности.

Хранение дат в любой СУРБД имеет свои особенности. Так, тип timestamp хранит дату в виде числа, поэтому имеет малый диапазон значений — от 1970-01-01 00:00:01 UTC до 2038-01-19 03:14:07 UTC. Также дополнительной проблемой является его преобразование к часовому поясу. Поэтому в системе не используется данный тип, а вместо него рекомендуется использовать тип integer. Однако есть идеальное решение и для полноценных дат — это использование типов time, date и datetime. Так, типы date и datetime имеют широкие диапазоны значений от 0001-01-01 до 9999-12-31 и не привязаны к часовому поясу. При добавлении значений в поля таких типов их следует приводить к часовому поясу UTC±0:00.

Примечание: SQLite поддерживает только 4 типа данных: integer, real, text и blob. Если используется иной тип, то его значение приводится к этим базовым типам. Так, тип datetime приводится к text, а boolean к integer и так далее. Такая реализация не производит контроля целостности домена данных, однако данная система осуществляет такой контроль на стороне полей форм и внести недействительные значения будет невозможно.

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

Примечание: В процессе разработки был исключён из поддержки PostgreSQL как СУРБД, наименее соответствующая ANSI-стандартам и имеющая особенности в работе со счётчиками autoincrement.

Динамические файлы

В системе имеются особые типы файлов — это *.cssd, *.jsd, *.svgd, *.htmld, *.txtd, *.xmld и возможно другие если в file_types.data они были описаны как kind: dynamic.

Данные файлы не кэшируются и имеют динамическую обработку — на каждый запрос такого типа файла происходит его обработка на стороне PHP (если в системе была зарегистрирована функция-обработчик такого типа или же функция-обработчик всех подобных типов).

Динамическая обработка даёт возможность организовать в таких файлах вывод переменных системы или произвести любую другую операцию с их контентом.

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

%%_color(main)
%%_color(main|profile=dark|r=10|g=20|b=30|o=.5)
%%_return_if_scope_is_dark(text|#000|#fff)
%%_return_if_token(color(text)|#fff|1|0)
%%_avatar_path
%%_page_width_min(context)
%%_page_width_max(context)
%%_request(scheme)
%%_request(host)
%%_translation(simple string)

По факту:

  • *.cssd — это классический CSS-файл, содержащий каскадные таблицы стилей, но который может содержать динамический контент;
  • *.jsd — это классический JS-файл, содержащий код JavaScript, но который может содержать динамический контент;
  • *.svgd — это классический SVG, содержащий векторную графику в XML формате, но который может содержать динамический контент.

Примечание: больше примеров можно увидеть в файле develop.cssd.

Событийная модель

Система имеет прозрачную и предсказуемую событийную модель. Достаточно найти или создать файл events.data в пользовательском модуле, указать в нём функцию-обработчик события и её вес, а далее сбросить кэш и событие станет обрабатываться.

В административном интерфейсе системы в разделе Разработка → NoSQL данные → События можно просмотреть все зарегистрированные события. Данный раздел станет доступен после включения модуля "Разработка".

Кэширование

Архитектура системы выполнена таким образом, чтобы работать максимально быстро и не требовать кэширования страниц. На проектах любого уровня система будет работать также быстро как и другие системы с включенным кэшированием. Для сильно нагруженных проектов можно организовать кэширование страниц средствами веб-сервера NGINX, Apache или IIS, при этом никаких дополнительных действий со стороны системы не потребуется.

Примечание: Динамические файлы не кэшируются (*.cssd, *.jsd, *.svgd и другие). При организации их кэширования разработчик берёт всю ответственность на себя по обновлению такого кэша.

Улучшение производительности

Для повышения производительности следует:

  • в PHP v.7+ включить OPCache;
  • в PHP v.8+ включить OPCache + JIT;
  • перейти на использование твёрдотельных накопителей (англ. Solid-State Drive, SSD);
  • перенести директории dynamic/cache и dynamic/tmp в ОЗУ при этом для повышения уровня надёжности веб-сервера такая ОЗУ должна поддерживать коды исправления ошибок (англ. error-correcting code, ECC), а сам сервер использовать источник бесперебойного питания (англ. Uninterruptible Power Supply, UPS).

Лучший способ увеличения производительности — это грамотное каскадирование стилей. Такой подход позволяет обойтись без препроцессоров SAS и LESS, основной задачей которых является копирование существующих стилей на множество новых элементов в результате чего объём каждого CSS-файла начинает кратно увеличиваться, а сама идея каскадности нивелируется.

Хороший способ увеличения производительности — это минификация JS-файлов путём редукционной реорганизации кода сторонними программами или сервисами. Также хорошим решением является отказ от "тяжёлых" библиотек подобных jQuery и переход на CSS3-анимацию, SMIL-анимацию, современные возможности JavaScript и HTML5.

Спорный способ увеличения производительности — это включить технологию потокового сжатия GZIP. Сделать это можно средствами веб-сервера NGINX, Apache, IIS. Однако следует помнить, что сжатие и распаковка GZIP-трафика приводит к увеличению нагрузки на процессор, создаёт задержку при загрузке и распаковке сжатого трафика, а также снижает время автономной работы мобильного устройства.

Обновление

Обновление системы или модулей предоставляет:

  • новый функционал;
  • исправления ошибок;
  • улучшение производительности;
  • улучшение безопасности.

Для обновления файлов системы в ручном режиме необходимо зайти на её официальный сайт effcore.com и загрузить текущую версию дистрибутива в виде архива. Далее следует распаковать загруженный архив локально. Важно: Среди распакованных файлов имеются директории modules и dynamic. В эти директории необходимо перенести файлы из существующего проекта. Далее следует скопировать полученные файлы на веб-сервер. Обновление сторонних модулей выполняется отдельно.

Для обновления файлов системы или модуля через Git-репозиторий необходимо в административном интерфейсе системы перейти в раздел Управление → Модули → Обновить → Файлы из репозитория и нажать кнопку "обновить". Если кнопка "обновить" недоступна, но доступна кнопка "восстановить репозиторий", тогда необходимо выполнить процедуру восстановления репозитория. Если недоступны кнопки "обновить" и "восстановить репозиторий" значит система или модуль не имеет собственного репозитория и его обновление через Git невозможно.

Обновление файлов системы через Git можно выполнить из командной строки если зайти на веб-сервер через SSH соединение, перейти в директорию shell и выполнить команду ./update.sh. Такое обновление возможно только в случае, если в веб-корне имеется директория .git. Обновление сторонних модулей выполняется отдельно.

Обновление файлов системы и модулей через Composer можно выполнить из командной строки если зайти на веб-сервер через SSH соединение, перейти в веб-корень и выполнить команду composer update. Такое обновление возможно только в случае, если на веб-сервере установлен Composer. Все модули, которые не были добавлены через composer require или не были зарегистрированы в composer.json или composer.lock не будут обновлены.

После обновления файлов системы или модуля следует выполнить обновление данных. Такое обновление производится в разделе Управление → Модули → Обновить → Данные.

Лицензирование

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

Лицензионное соглашение является простым и минималистичным и не ограничивает права добропорядочных пользователей.

Согласно лицензионному соглашению злонамеренные пользователи, которые хотели бы выдать данное произведение как своё собственное, уже будут находиться вне рамок правового поля.