Coresky планы и продукты - energy-coresky/air GitHub Wiki

SKY-приложения, папка wares и версирование

SKY-приложения могут свободно использовать код из папки vendor. А в папке wares, может храниться код продуктов, которые можно скачать из глобального репозитория, подобно пакетам composer. Продукты - это код для повторного использования, предназначенный для конкретных или любых SKY-приложений. Продукты типа prod, могут представлять собой полнофункциональные встраиваемые sub-приложения с контроллерами, настраиваемой адресацией, моделями и шаблонами представлений, что не предусмотрено системой контроля composer. Продукты типа dev, всегда предназначены для любого SKY-приложения, это, по сути, просто плагины для DEV-tools. Продукты не классифицируется по производителю (как код из папки vendor) и имеют имя в соответствии с регулярным выражением: \w+, например "mercury". Имя продукта определяет папка в которой он находится, например: wares/mercury.

Все SKY-приложения имеют имя с суффиксом "SKY." и версию, например "ἄλφα.0.7798.AB.SKY.". Если продукт предназначен для конкретного SKY-приложения, он содержит в своем плане app указатель target, например: 'target' => 'AB.SKY.'. Версия кода Coresky прописана в SKY::CORE и каждая версия SKY-приложений, зависит от конкретной версии Coresky. Некоторые отдельные классы Coresky, также могут иметь собственную версию, например: Jet::version.

Точный контроль версий продуктов отсутствует, в отличие от пакетов composer. Поэтому однажды скачанный и настроенный продукт для SKY-приложения, необходимо сохранять вместе с кодом приложения, как и код Coresky. Это не доставит неудобств, так как, размер дополнительного кода минимален. В глобальном репозитории продуктов, на сайте https://coresky.net/, хранится только самая свежая версия каждого продукта. Возможны специальные методы обновлений продуктов в SKY-приложениях, а условный контроль версий - по времени загрузки продукта в репозиторий. Также возможны различные оповещения о критических ошибках в старых продуктах и SKY-приложениях.

Код Coresky (и приложения), имеют собственную систему версирования на основе простого real-числа, но параллельно можно использовать и стандартную систему версирования. Если после запятой четыре цифры, - подразумевается альфа-версия (текущая разработка), если три - бета-версия. Релиз-кандидаты и стабильные версии - 1 или 2 чисел после запятой. Стабильные версии предлагается помечать, как "красивые номера" автомобилей, с помощью повторяющихся чисел, например 3.3 или 5.88, а релиз-кандидаты с нулём в конце, например 1.0 или 2.10. Если код имеет явные недоработки, версия должна быть менее единицы, а первый релиз-кандидат всегда - 1.0.

Структура размещения кода приложений

Выполнение web-скриптов начинается в файле index.php, который "лежит" в папке "public", но эта папка может иметь любое имя. Оно устанавливается в константе WWW, в файле var/cache/sky_plan.php. Эта папка может по разному называться на DEV и на PROD, инсталлятор moon.php учитывает это обстоятельство. В bootstrap.php, определяется константа DIR - главная директория приложения, указывающая на один уровень выше WWW. Все скрипты SKY-приложений, выполняются из этой директории и в дальнейшем все другие пути будут указаны относительно этой директории. Стандартный код файла public/index.php:

<?php

define('START_TS', microtime(true));
require __DIR__ . '/../bootstrap.php';
new HEAVEN; # write here `new SKY` for console scripts

Примерный код bootstrap.php:

<?php

define('DIR', __DIR__);
chdir(DIR);
define('EXTRA', 0); # 0 - off, 1 - on
define('DIR_S', 'vendor/energy/air');
define('DIR_M', 'main');
# for blue/green deploy:
# define('DIR_M', is_file('.blue') ? 'blue' : 'green');

require DIR_S . '/sky.php';

Стандартный код public/.htaccess для Apache:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php         [L,QSA]

Код приложений, обычно размещается в папке main, но эта папка, также может иметь любое имя. Можно легко организовать blue-green deploy для SKY-приложений, создав две копии кода приложения. Имя этой папки всегда содержится в DIR_M. План приложений app, всегда указывает на эту папку, а ware всегда называется main, несмотря на то что имя папки может отличаться от main.

Местоположение кода Coresky прописывается в DIR_S и он может располагаться в любом месте, например в vendor/energy/air. Папка DIR_S может также указывать на одно и то-же место с DIR_M. Это значит, что код приложений может располагаться в одной папке, вместе с кодом Coresky.

Продукты располагаются в папке wares - фиксированное имя. Но для продуктов предусмотрено две папки, вторая может располагаться где угодно. Её местоположение прописывается в инструментах разработчика. При установке пакета composer "coresky/hole", её первоначальное значение - vendor/coresky.

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

Планы Coresky

С помощью планов Coresky, можно изменять местоположение сущностей приложений, добавлять стили визуализаций, подключать продукты к приложениям или настраивать кэш. В качестве "движков" для планов, могут быть использованы: файловая система компьютера или базы данных NoSQL. В конфигурационном файле приложений main/config.yaml, помимо определения соединений с реляционными базами данных, также можно определить планы в core/plans, например:

core:
  plans:
    jet: {path: var/gate}
# Значения планов по умолчанию:
    app: {path: $DIR_M} # код приложения (для Ware main)
    view: {path: $DIR_M/mvc/view} # представления Jet
    cache: {path: var/cache} # очищаемый кэш
    mem: {path: var/mem} # хранимые генерируемые данные, например отчеты Globals
    jet: {path: var/jet} # компилированные шаблоны
    gate: {path: var/gate} # компилированные контроллеры
    sql: {path: var/sql} # компилированные кэшируемые запросы SQL

Для планов 'app', path всегда устанавливается автоматически, исходя из значения константы DIR_M для приложений, или указателя на папку для wares. Продукты, в своём файле config.yaml, могут иметь собственные определения перечисленных планов или наследовать планы приложения. И SKY-приложения и подключаемые продукты, могут иметь свои собственные имена планов, для работы их кода. В основном, это разного рода кэш. Для планов, драйвер для которых не указан, по умолчанию, используется dc_file. Поле path - обязательно для любого плана. В конфигурациях, могут присутствовать такие поля:

  • path - путь к файлам или записям в NoSQL DB;
  • pref - префикс для имени, по умолчанию пустая строка;
  • ttl - время жизни кэша в секундах, по умолчанию -1 (бесконечность);
  • driver - драйвер, по умолчанию dc_file;
  • dsn - для подключения драйверов, использующих dsn;
  • use - использовать соединение другого плана, в котором задействован нестандартный драйвер. По умолчанию - пустая строка, основной объект dc_file;

Дополнительные поля конфигураций для планов 'app':

  • type - view/dev/prod - обязательное поле для продуктов. Не используется в описании ware=main основного приложения;
  • require - требуемые классы. Ware не будет установлено, если не найдены указанные классы;
  • target - целевое приложение. Если отсутствует, то ware предназначено для всех SKY-приложений.
  • databases - поле для описания собственных соединений с реляционными базами данных в подключаемых продуктах;
  • flags - булевы свойства продуктов: массив идентификаторов, записанных через пробел;

Поля для планов 'app' в выбранных конфигурациях продуктов (файл main/wares.php):

  • tune - префикс настраиваемой адресации продуктов типа "prod". Может отсутствовать (использование без префикса). Если продукт поддерживает (необязательно обязан) настраиваемую адресацию, то flags обязан содержать идентификатор "tune"

В систематизированных wares/plans, в файле var/cache/sky_plan.php сохраняется ассоциативный массив, в котором ключами верхнего уровня являются имена продуктов. Основное приложение (ware=main), содержит и идентификаторы не являющиеся планами, которые содержат информацию для работы ядра Coresky:

  • rewrite - объединенный код реврайтов
  • class - экспортируемые классы, где ключ - имя класса, значение - имя ware, где он определен. Могут перекрестно использоваться продуктами. Обычно здесь указывается 0|1 класс из папки w3 на один продукт для его инициализирования;
  • ctrl - полный список прото-имен контроллеров (ключ) с указанием ware (значение), где они определены. Если prod-ware используется с префиксом, для настраиваемой адресации, ключ этого массива содержит "префикс/прото_имя_контроллера", например, 'my-board/topic' => 'forum', второй пример: 'u/*' => 'upload';

Coresky wares

Комбинированное использование кода

В пределах одного prod-ware имена контроллеров уникальны, но разные ware могут содержать совпадающие имена контроллеров. В Coresky, полная адресация основного действия, включает имя ware: ware.controller.action. Каждое протекание скрипта, может задействовать лишь один мастер-контроллер, а общий контроллер приложения main/mvc/common_c.php, задействован всегда. Поэтому, совпадающие имена контроллеров - не проблема. В такой ситуации, необходимо обязательно настроить префикс адресации для продукта и использовать реврайт, пример:

if ($cnt && 'my-board' == $surl[0]) {
    common_c::$tune = array_shift($surl);
    $cnt--;
}
# sample: http://example.com/my-board/topic?id=1
# where "my-board"-prefix, "topic"-controller-proto-name, "id"-action-proto-name

Для j-top-view действий контроллеров, префикс передается не в адресной части запроса (реврайт не используется), а в значении заголовка X-Action-J, который по умолчанию содержит пустую строку. При детектировании j-top-view действия, common_c::$tune заполняется автоматически.

Для использования кода ware другими ware или основным приложением, ware могут экспортировать классы.

Такая система распределения ролей, позволяет внедрить объемный prod-ware, например форум, в систему адресации (и основной layout) произвольного приложения. А также, на произвольных страницах приложения, внедрять части визуализаций поставляемые другими ware.

Типы продуктов и их инициация

У всех SKY-приложений, ware всегда называется main и является master-ware. Подключаемые ware всегда slave (ведомые), могут быть следующих типов:

  • view - альтернативный стиль для приложения
  • dev - расширения для разработки
  • prod - встраиваемое sub-приложение или просто экспортируемые классы (повторно-используемый код), может иметь один или несколько контроллеров. Такие ware, могут использоваться на production, но во время установки, можно выбрать "использование только для DEV"

Тем не менее, инициатором установки продукта может быть как приложение, так и сам продукт. Код приложения может подразумевать, что он работоспособен только при установленном продукте (prod или view), но это не всегда так. Инициатором установки DEV-продуктов, всегда являются сами продукты. Т.е. приложение, всегда работоспособно на продакшн, без установленного DEV-продукта.

В конфигурационных файлах любых продуктов, как правило, не нужно настраивать планы jet и gate, так как, например имена файлов компилированных шаблонов, содержат имя ware и эти имена никогда не пересекутся. Т.е. нужно наследовать планы основного приложения. Вот пример конфигурационного файла dev-продукта "Earth":

core:
  plans:
    app:
      type: dev
      require: SQLite3 Parsedown
    view: {path: $SELF/mvc}
    cfg: {path: $SELF/mvc}
  databases:
    driver: sqlite3
    dsn: $SELF/earth.base

Здесь планы возвращаются в массиве, а собственные соединения с БД указываются в ключе databases. DEV-продукты не могут экспортировать классы, а только импортировать.

Во время установки продуктов, выбранные конфигурации записываются в файл main/wares.php. А полный систематизированный SKY::$plans по всем установленным wares, кэшируется в файле var/cache/sky_plan.php. Во время операции "Drop all cache", он удаляется со всем кэшем, но генерируется заново, при первой необходимости.

Переключение Ware

Код приложения продуктов, так же как и код основного приложения, может располагаться в папках w3 и mvc. При этом, автозагрузчик Coresky иногда использует значение свойства Plan::$ware для поиска классов. Если загружается контроллер продукта, то в Plan::$warePlan::$view) автоматически записывается имя ware. А если необходимо использовать единичный класс ware, в контексте другого ware, то переключение ware не происходит автоматически. При этом, автозагрузка класса, может происходить с использованием реестра экспортированных классов или на основе пространства имен в имени класса, совпадающего с именем ware. Если, в последнем случае, все-таки необходимо явное переключение ware, из-за того, что инициированный класс использует много кода "своего" ware, можно использовать метод Plan::set(..) для временного переключение ware:

return Plan::set('warename', fn($oldware) => self::waremethod());

Такой вызов, вначале переключает ware, затем выполняет Closure и затем устанавливает "старый" ware, тот который был вначале обработки. Метод Plan::set(..) возвращает значение, возвращенное Closure.

Разрешение коллизий в именах классов

Как указано выше, имена классов любых контроллеров должны быть объявлены в глобальном пространстве имен. Коллизий не будет, так как каждый запрос может активировать только один мастер контроллер. Классы DEV-продуктов также, обычно, не используют namespace, так как они не запускают код приложения, а классы Coresky известны.

Модели в директории mvc и классы в w3 PROD-продуктов, могут использовать пространства имен, так как, продукты предназначенные для любых SKY-приложений не могут предполагать используемые имена классов в приложениях. Автозагрузчик Coresky, умеет работать с классами, где namespace соответствует имени продукта. Пространство имен и имя класса, должны быть объявлены в нижнем регистре, смотрите например объявление класса upload\ant продукта Upload:

<?php

namespace upload;
use Plan;

class ant
{
. . .
}

Использование таких классов, также, автоматически выбирает нужный ware, экспорт не требуется:

# в коде приложения (ware = 'main'):
$ant = new upload\ant; # использование кода ware = 'upload'

Для автоматического создания экземпляра моделей (смотрите код класса MVC_BASE), T и M модели, могут быть помещены в файл с префиксом x_, если эти модели, в своем объявлении используют namespace. Читайте об этом подробнее в статье "Модели в Coresky".

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

Методы класса Plan

Класс Plan, в некотором роде, - движок абстрактного кэша, использует драйверы с префиксом dc_, например смотрите w2/dc_file.php. Интерфейс, который имплементируют драйверы:

interface DriverCache
{
    function info();
    function setup($obj);
    function test($name); # для dc_file делается PHP is_file(..)
    function get($name);
    function run($name, $vars = false);
    function mtime($name);
    function append($name, $data);
    function put($name, $data, $ttl = false);
    function glob($mask = '*');
    function drop($name);
    function drop_all($mask = '*');
}

В классе Plan, определен магический метод __callStatic(..), с помощью которого делается основное управление планами, в соответствии с методами драйверов. До подчеркивания, указывается план, а после операция, план app можно не писать:

Plan::cache_g('name'); # выполнить get(..) плана cache текущего ware
Plan::cache_g(['main', 'name']); # то-же, но ware указано явно
Plan::gate_m($fn_dst); # выполнить mtime(..) плана gate текущего ware
Plan::_r("w3/$a0"); # выполнить run(..) плана app текущего ware

Основной список операций:

  • ::plan_t - выполнить test(..), - проверить наличие объекта
  • ::plan_s - выполнить set(..) - в dc_redis mtime будет неверно работать
  • ::plan_p - выполнить put(..) - в dc_redis возможно использование mtime
  • ::plan_a - выполнить append(..) - дописать в конец файла
  • ::plan_g - выполнить get(..)
  • ::plan_gq - выполнить get(..), но сначала проверить, если объекта нет - не генерировать ошибку, а вернуть пустую строку
  • ::plan_r - выполнить run(..), в драйвере dc_file выполняется require '..'
  • ::plan_rq - выполнить run(..), с проверкой на существование (quiet)
  • ::plan_m - выполнить mtime(..)
  • ::plan_mq - выполнить mtime(..) с quiet
  • ::plan_d - выполнить drop(..)
  • ::plan_dq - выполнить drop(..) с quiet
  • ::plan_da - drop all, удаление по маске
  • ::plan_b - выполнить glob(..) - выдать список объектов по маске
  • ::plan_obj - вернуть объект плана (основную конфигурацию)

Для методов с модификатором q (quiet), вторым или третьим (только для _rq) параметром, можно указать значение, которое будет возвращено, если объект не существует. Но если такое значение по умолчанию не указано, то для _gq вернется пустая строка, для _rq пустой массив, для _mq и _dq вернется ноль.

Имя текущего продукта всегда содержится в Plan::$ware, а имя продукта из которого берется текущий план view в Plan::$view. Первоначально, оба свойства содержат значение 'main'. Если шаблон не найден в плане 'view' текущего ware, который отличный от 'main', поиск шаблона продолжается в плане 'view' основного приложения. Это дает возможность, например, использовать общий layout для всех DEV-TOOLS в продуктах Mercury, Earth и т.д.

Использование Redis

Для более быстрого отклика, информацию планов можно поместить в оперативную память с помощью Redis. Параметры плана cache нужны для создания файла sky_plan.php, поэтому, значения планов следует указать в bootstrap.php, а не в config.yaml:

# use Redis
SKY::$plans['cache'] = ['path' => 'L/cache', 'driver' => 'redis', 'dsn' => 'localhost'];
SKY::$plans['gate'] = ['path' => 'L/gate', 'use' => 'cache']; # use the same single connection
SKY::$plans['jet'] = ['path' => 'L/jet', 'use' => 'cache']; # use the same single connection

Синтаксис DSN: hostname:port:password, но если порт стандартный и нет пароля, достаточно указать только имя хоста или IP. Для управления планами, предпочтительно использовать "обёртки" класса Plan в том числе для Redis. Но код приложения волен использовать все возможности классов модуля Redis для PHP, например:

# take Redis object via ::open(..) method:
$redis = Plan::open('cache', 'main')['dc']->conn;
# or this way, for current ware (shortest code):
$redis = Plan::cache_obj('dc')->conn;
# and for selected ware 'main':
$redis = Plan::cache_obj(['main', 'dc'])->conn;
# or this way
$redis = Plan::cache_obj(['main'])->dc->conn;
# use redis.so module Classes/Methods:
echo "This database has " . $redis->dbSize() . " keys\n";

Планы app могут использовать только драйвер dc_file, для них нельзя использовать Redis. Очередность использования планов в ядре такая: cache, gate, jet. Использование use возможно только для планов, которые ссылаются на "активированный" план к тому моменту, когда "активируется" план использующий use.

Замечание: планируется добавить возможность помещать данные таблиц memory и visitors реляционных БД, в NO-SQL базы данных.

Код продуктов

Продукты типа dev всегда содержат один контроллер с прототипом имени продукта и суффиксом "_c". Например, продукт Mercury содержит один контроллер mvc/mercury_c.php. Такие продукты не используют SKY-Gate. Продукты prod, могут содержать множество контроллеров с префиксом "c_", используют SKY-Gate, и могут подстраивать свои endpoints с помощью Coresky rewrites.

Все продукты, могут использовать автозагрузку классов, аналогичную основному приложению. Код может находиться в папках mvc и w3. Если некоторый класс не найден в коде продукта, поиск будет продолжен в коде основного приложения и экспортируемых классах. Так, например Earth-ware использует класс Parsedown.

Ware как средство дизайна

С помощью ware типа view, можно добавлять альтернативные стили для SKY-приложений. При этом, файлы Jet, могут располагаться в любом месте файловой системы. Список стилей можно получать так:

$styles = [];
foreach (SKY::$plans as $key => $val) {
    if ('main' == $key || 'view' == $val['app']['type'])
        $styles[] = $key;
}

Выбранный пользователем стиль, можно сохранять, например, в $user->v_style, а активировать выбранный стиль в системе так:

Plan::$view = $user->v_style ?: 'main';
# or.. (if production ware used):
if ('main' == Plan::$view && $user->v_style)
    Plan::$view = $user->v_style;

Метод Rare::cache

Метод Rare::cache(..), используется при работе оператора Jet @cache(..) .. ~cache, использует план 'cache'. В этом методе, ttl может быть параметром метода или использовать TTL указанный в БД: $sky->s_cache_sec. Для оператора Jet, также, автоматически добавляется префикс jet_, хотя в определении плана по умолчанию, указано: время жизни кэша - бесконечность и нет префикса. Пример использования по схеме @cache(..) .. ~cache в Jet:

<?php

if (Rare::cache('the_ob')): /* TTL will takes from $sky->s_cache_sec */ ?>

    This contents goes directly to output buffer

    <?php echo sqlf('+select txt from article where id=9') ?>

    This line after SQL query..

<?php Rare::cache(); endif ?>

Этот метод, также, можно использовать для обычной схемы кэширования. Пример использования метода Rare::cache(..) для кеширования SQL запроса:

$users = false;
$cfg = [
    'cache', # plan name
    'users', # cache name
    'sql_', # prefix for name (overwrite default plan's prefix)
];
# 600 - 10 minutes TTL
$serialized = Rare::cache($cfg, 600, function() use (&$users) {
    return serialize($users = sqlf('@select * from $_users'));
});

$users or $users = unserialize($serialized);

Автозагрузка из папки vendor

Автозагрузчик composer можно добавить в файл bootstrap.php:

require 'vendor/autoload.php';

Но лучше сделать вызов Plan::vendor() из ::head_y(..) контроллеров, моделей или общего контроллера. Это сделает код более производительным:

use Some\Namespace\Name; # vendor's classes

class common_c extends Controller
{
    function head_y($action) {
        Plan::vendor();
        . . .
        # in other controller you must return parent
        # return parent::head_y($action);
    }
    ...
}

Этот метод регистрирует автозагрузчик composer с помощью кода require 'vendor/autoload.php', затем отменяет регистрацию, но запоминает хендлер автозагрузчика. Всей автозагрузкой будет управлять автозагрузчик Coresky: если нужен класс в глобальной области видимости - приоритетно, поиск будет осуществлять автозагрузчик Coresky. А если имя класса не в глобальном namespace - приоритетно будет работать автозагрузчик composer.