Примеры

Реальный код Universal Engine, а не псевдокод: страница, маршрут, работа с universal table, идемпотентный upsert, свой скин и подключение эффектов. Всё то, из чего собран этот сайт.

Минимальная страница page/hello.php
php

Полноценная страница в одном файле: выбор скина, заголовок, header/mainmenu/footer. Инфраструктура живёт в ядре, а не в странице.

Минимальная страница page/hello.php php
<?php
declare(strict_types=1);

// Один вызов задаёт скин - дальше вся страница рисуется его контрактом.
Sogerien::Page()->set_templates([TemplateBlog::class]);

// Единый экранировщик вывода на страницу.
function h(mixed $v): string
{
    return htmlspecialchars((string)$v, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

$name = trim((string)($_GET['name'] ?? 'мир'));

Sogerien::Page()->title = 'Hello - Sogerien';
Sogerien::Page()->header();
Sogerien::Page()->mainmenu();
?>
<section class="container py-5">
    <h1>Привет, <?= h($name) ?></h1>
    <p>Вся страница - это один файл page/hello.php. Инфраструктуры в нём ноль.</p>
</section>
<?php
Sogerien::Page()->footer();
BOM перед <?php ломает declare(strict_types=1) - файл строго UTF-8 без BOM. Любой вывод наружу гони через h(), сырые данные в HTML не попадают.
Регистрация маршрута в корневом index.php
php

Тонкий front controller: карта «URL-сегмент -> файл страницы», один foreach и финальный template().

Регистрация маршрута в корневом index.php php
<?php
declare(strict_types=1);

// ... выше: require Sogerien.php, загрузка config, DbController()->connect() ...

$routes = Sogerien::Routes();

// Карта маршрутов: сегмент URL -> файл страницы в /page. '' - это корень сайта.
$map = [
    ''         => '/page/home.php',
    'hello'    => '/page/hello.php',
    'examples' => '/page/examples.php',
    'docs'     => '/page/docs.php',
];

foreach ($map as $url => $templatePath) {
    $routes->add_template($url, $templatePath);
}

$routes->template();   // выбирает страницу по текущему URL и исполняет её
Sogerien::markDone();  // помечаем запрос завершённым
add_template(url, path) принимает путь к файлу в /page, а не имя класса. После template() обязателен Sogerien::markDone(), иначе движок считает запрос незавершённым.
Чтение из universal table sogerien
php

SELECT по природному ключу (entity, item_key) с именованными params и разбором JSON-ответа sql_request.

Чтение из universal table sogerien php
<?php
declare(strict_types=1);

$dbAlias = 'front';

// sql_request ВСЕГДА возвращает JSON-строку, а не массив - её декодируем.
$json = Sogerien::DbController()->sql_request($dbAlias, [
    'sql' => "SELECT item_key, title, data
                FROM sogerien
               WHERE entity = :entity AND status = :status
               ORDER BY updated_at DESC
               LIMIT :limit",
    'params' => [
        ':entity' => 'landing_feature',
        ':status' => 'published',
        ':limit'  => 20,
    ],
]);

$res  = json_decode($json, true);
$rows = (is_array($res) && ($res['result'] ?? false) === true)
    ? ($res['rows'] ?? [])
    : [];   // result=false -> причина в $res['error']['message']

foreach ($rows as $row) {
    // jsonb-колонка может прийти массивом или строкой - нормализуем перед доступом.
    $data  = is_array($row['data'] ?? null)
        ? $row['data']
        : (array)json_decode((string)($row['data'] ?? '[]'), true);
    $title = (string)($row['title'] ?? '');
    $icon  = (string)($data['icon'] ?? '');
}
sql_request отдаёт JSON-строку, а не массив - сначала json_decode и проверка result. jsonb-колонка может прийти строкой, нормализуй её перед доступом по ключу.
Идемпотентный upsert через ON CONFLICT
sql

INSERT ... ON CONFLICT (entity, item_key) DO UPDATE: повторный вызов не плодит дубли, а обновляет запись и updated_at.

Идемпотентный upsert через ON CONFLICT sql
-- Идемпотентная запись в universal table sogerien.
-- Природный уникальный ключ таблицы - пара (entity, item_key).
INSERT INTO sogerien (entity, item_key, title, data, status)
VALUES (:entity, :item_key, :title, CAST(:data AS jsonb), :status)
ON CONFLICT (entity, item_key) DO UPDATE
   SET title      = EXCLUDED.title,
       data       = EXCLUDED.data,
       status     = EXCLUDED.status,
       updated_at = now()
RETURNING id;
Значение :data передавай обычным PHP-массивом - санитайзер сам свернёт его в jsonb. Каст только CAST(:data AS jsonb); запись :data::jsonb ломает разбор именованных параметров, потому что :: конфликтует с :name.
Свой скин-класс TemplateLanding
php

Duck-typed контракт скина: head-CSS/JS, класс и атрибуты <body>, хуки открытия/закрытия. Эффекты подмешиваются из Affects.

Свой скин-класс TemplateLanding php
<?php

declare(strict_types=1);

/**
 * Скин лендинга sogerien.top. Лёгкий холст: свой CSS/JS страницы плюс
 * эффекты из Affects. Контракт duck-typed - Template зовёт эти методы сам.
 */
final class TemplateLanding
{
    use SogerienClassHelp;

    public function __construct(private Template $template)
    {
    }

    /**
     * @return array<int,string>
     */
    public function get_head_css_urls(): array
    {
        $domain = (string)Sogerien::InputRequest()->sogerien_domain . '/sogerien';

        return array_merge([
            $domain . '/page/css/landing/landing.css',
        ], Affects::get_head_css_urls($domain));
    }

    /**
     * @return array<int,array{src:string,defer:bool}>
     */
    public function get_head_js_urls(): array
    {
        $domain = (string)Sogerien::InputRequest()->sogerien_domain . '/sogerien';

        return array_merge([
            ['src' => $domain . '/page/js/landing/landing.js', 'defer' => true],
        ], Affects::get_head_js_urls($domain));
    }

    public function get_body_class(): string
    {
        return 'sg-landing';
    }

    /**
     * @return array<string,string>
     */
    public function get_body_attributes(): array
    {
        return ['data-theme' => 'dark'];
    }

    public function render_body_open(): void
    {
    }

    public function render_body_close(): void
    {
    }
}
Ничего не наследуешь и не регистрируешь - Template зовёт методы по имени через method_exists. Свои ассеты сливай с Affects через array_merge, а не дублируй руками.
Подключение библиотеки-эффекта через Affects
php

Сторонний эффект (AOS) добавляется одной строкой в css- и js-массивы Affects и появляется на всех страницах через скин.

Подключение библиотеки-эффекта через Affects php
<?php

declare(strict_types=1);

/**
 * Единственная точка подключения сторонних библиотек и эффектов.
 * Ни один <link>/<script> не пишется в разметку страницы - только сюда.
 */
final class Affects
{
    use SogerienClassHelp;

    /**
     * @return array<int,string>
     */
    public static function get_head_css_urls(string $domain): array
    {
        return [
            $domain . '/page/effects/aos/aos.css',
            $domain . '/page/effects/proxymint-background-kit/proxymint-background-kit.css',
        ];
    }

    /**
     * @return array<int,array{src:string,defer:bool}>
     */
    public static function get_head_js_urls(string $domain): array
    {
        return [
            ['src' => $domain . '/page/effects/aos/aos.js', 'defer' => true],
            ['src' => $domain . '/page/effects/aos/aos.init.js', 'defer' => true],
        ];
    }
}
Инлайн <script>/<link> прямо в разметке запрещён - это ключевой принцип лендинга. Даже инициализацию (AOS.init) выносишь в отдельный файл aos.init.js, а не в тег на странице.
Документация