evgip/w3a-core

There is no license information available for the latest version (v0.2.9) of this package.

Ядро w3a

Maintainers

Package info

github.com/evgip/w3a-core

pkg:composer/evgip/w3a-core

Transparency log

Statistics

Installs: 23

Dependents: 1

Suggesters: 0

Stars: 1

Open Issues: 0

v0.2.9 2026-08-18 14:05 UTC

This package is auto-updated.

Last update: 2026-08-18 14:06:50 UTC


README

PHP Version License

Высокопроизводительное, модульное и строго типизированное ядро PHP-фреймворка. Реализует принципы чистой архитектуры и современные паттерны разработки, обеспечивая предсказуемость, тестируемость и отличный Developer Experience.

✨ Ключевые особенности

  • 🎯 Строгая типизация HTTP-ответов: Контроллеры возвращают явные объекты ViewResponse, RedirectResponse или JsonResponse. Никаких скрытых void или побочных эффектов.
  • 📨 Единый центр сообщений (MessageBag): Централизованное управление flash-сообщениями и сохранением данных форм (old_input) без прямого манипулирования сессией в контроллерах.
  • 🛠 Встроенные Коллекции (Collections): Мощный fluent-интерфейс для работы с массивами данных (collect()->map()->filter()->pluck()), избавляющий от громоздких array_* функций.
  • 📄 Пагинация из коробки (Paginator): Расчёт страниц, offset для SQL и видимый диапазон номеров — общий для всех проектов.
  • Декларативная валидация: Встроенный класс Validator с поддержкой правил (required, email, unique, min, regex и др.) и автоматической обработкой ошибок через validateRequest().
  • 🧩 Чистая архитектура (DIP): Ядро не содержит жестких ссылок на классы приложения (\App\...). Зависимости явно передаются через DI-контейнер и интерфейсы (Contracts).
  • 🛡️ Безопасность из коробки: Встроенная защита от CSRF, XSS (CSP-nonce), Rate Limiting и Firewall (бан IP).
  • 🗄 Готовые БД-реализации контрактов: DatabaseRateLimitStorage, DatabaseAuditStorage, DatabaseBannedIpRepository — приложению достаточно зарегистрировать их в AppServiceProvider, писать свои классы не нужно.
  • Ленивая загрузка конфигов: Файлы конфигурации читаются с диска только при первом обращении к их ключам через dot-нотацию.
  • 💾 Безопасный файловый кэш: FileCache использует serialize() + flock(LOCK_SH) вместо require — нет исполнения произвольного PHP, нет race condition при конкурентной записи. Атомарная запись через tmp + rename(), шардирование каталога (2-символьные подпапки) для масштабируемости.
  • 🔐 Ленивая сессия: Session стартует только при первом обращении к $_SESSION. Анонимные GET-запросы не создают session-файлы и не шлют PHPSESSID cookie ботам и гостям — экономия ресурсов на 90%+ трафика публичного сайта.

📋 Требования

  • PHP 8.1+ (используются union types, constructor property promotion, match expressions)
  • Расширения: pdo, mbstring, json
  • База данных: MySQL / MariaDB (или другая, поддерживаемая PDO)

📦 Установка

Для продакшена (через Packagist)

composer require evgip/w3a-core

Для локальной разработки (через symlink)

Добавьте в composer.json вашего основного проекта:

{
    "repositories": [
        {
            "type": "path",
            "url": "./w3a-core"
        }
    ],
    "require": {
        "evgip/w3a-core": "@dev"
    }
}

Затем выполните composer update evgip/w3a-core. Composer создаст символическую ссылку, и все изменения в ядре будут применяться мгновенно.

🚀 Быстрый старт

Точка входа (public/index.php) максимально чиста и явно объявляет зависимости:

<?php

declare(strict_types=1);

require_once __DIR__ . '/../vendor/autoload.php';

// 1. Загрузка переменных окружения
\W3a\Core\Foundation\Env::load(dirname(__DIR__) . '/.env');

// 2. Инициализация приложения с явной регистрацией провайдеров
$app = new \W3a\Core\Foundation\Application(dirname(__DIR__), [
    \W3a\Core\Foundation\CoreServiceProvider::class, // Сервисы ядра
    \App\AppServiceProvider::class,                  // Сервисы вашего приложения
]);

$app->bootstrap()->run();

💡 Современный стиль кода (Примеры)

1. Контроллеры с явными ответами и MessageBag

Забудьте о $this->session()->flash() и скрытых редиректах в контроллерах приложения:

public function store(): RedirectResponse
{
    $data = $this->request->getParams();
    
    // Автоматическая валидация с редиректом и сохранением old_input при ошибке
    $validation = $this->validateRequest([
        'email' => 'required|email|unique:users,email',
        'password' => 'required|min:6',
    ]);
    
    if ($validation !== true) {
        return $validation; // Возвращаем RedirectResponse
    }

    try {
        $this->userService->create($data);
        MessageBag::flashMessage('success', 'Пользователь успешно создан!');
        return $this->redirect('/users');
    } catch (\Throwable $e) {
        MessageBag::flashMessage('error', 'Ошибка при создании пользователя.');
        return $this->redirectBack();
    }
}

2. Использование Коллекций (Collections)

Замена громоздких array_map и array_filter на читаемый fluent-интерфейс:

// Получаем уникальные ID активных историй из массива комментариев
$storyIds = collect($comments)
    ->reject(fn($c) => !empty($c['deleted_at']))
    ->pluck('story_id')
    ->unique()
    ->values()
    ->toArray();

3. Пагинация (Paginator)

use W3a\Core\Support\Paginator;

$total = $itemModel->countList($filter);
$pager = new Paginator($total, 15, (int)$request->query('page', 1));

$items = $itemModel->getList(15, $pager->offset(), $filter);

$this->render('list', [
    'items' => $items,
    'pager' => $pager->toArray(), // currentPage, lastPage, total, range, hasPrev, hasNext
]);

📂 Структура приложения

your-app/
├── app/
│   ├── Config/                 # Конфигурация (app.php, database.php, ...)
│   ├── Lang/                   # Файлы локализации (ru.php, en.php)
│   ├── Modules/                # Бизнес-модули (Users, Stories, Admin, ...)
│   └── AppServiceProvider.php  # Связывание интерфейсов ядра с реализациями
├── storage/
│   ├── cache/                  # Кэш маршрутов, представлений и данных (шардированный)
│   │   └── data/
│   │       └── a1/app_*.cache  # 256 подпапок (00..ff) по первым 2 символам MD5
│   ├── sessions/               # Файлы сессий PHP (создаются только для авторизованных)
│   └── logs/                   # Логи приложения и PHP-ошибок
├── public/
│   └── index.php               # Точка входа (Front Controller)
└── composer.json

🔌 Ключевые интерфейсы (Contracts)

Ядро работает через инверсию зависимостей. Интерфейсы, зависящие от бизнес-логики, регистрируются в AppServiceProvider:

Интерфейс Назначение Готовая реализация в ядре
Contracts\RateLimitStorageInterface Хранилище лимитов запросов (Rate Limiter) Security\DatabaseRateLimitStorage
Contracts\UserIdProviderInterface Получение ID текущего авторизованного пользователя
Contracts\AuditStorageInterface Хранилище журнала аудита действий Audit\DatabaseAuditStorage
Contracts\BannedIpRepositoryInterface Проверка заблокированных IP-адресов (Firewall) Security\DatabaseBannedIpRepository
Contracts\ErrorHandlerInterface Обработка и рендеринг страниц ошибок (404, 500) Errors\DefaultErrorHandler (fallback)

Пример регистрации

<?php
// app/AppServiceProvider.php
namespace App;

use W3a\Core\Foundation\Container;
use W3a\Core\Database\Database;
use W3a\Core\Contracts\RateLimitStorageInterface;
use W3a\Core\Contracts\AuditStorageInterface;
use W3a\Core\Contracts\BannedIpRepositoryInterface;
use W3a\Core\Contracts\ErrorHandlerInterface;

class AppServiceProvider
{
    public function register(Container $container): void
    {
        // Готовые БД-реализации (таблицы создаются миграциями из database/migrations/)
        $container->singleton(RateLimitStorageInterface::class, fn($c) =>
            new \W3a\Core\Security\DatabaseRateLimitStorage($c->get(Database::class))
        );

        $container->singleton(AuditStorageInterface::class, fn($c) =>
            new \W3a\Core\Audit\DatabaseAuditStorage($c->get(Database::class))
        );

        $container->singleton(BannedIpRepositoryInterface::class, fn($c) =>
            new \W3a\Core\Security\DatabaseBannedIpRepository($c->get(Database::class))
        );

        // Ошибки: свой ErrorHandler со своим layout; DefaultErrorHandler — fallback
        $container->singleton(ErrorHandlerInterface::class, fn($c) =>
            new \App\Modules\Errors\Services\ErrorHandler($c)
        );

        // Регистрация групп middleware
        $router = $container->get(\W3a\Core\Http\Router::class);
        $router->addMiddlewareGroup('auth', [
            \App\Modules\Users\Middleware\AuthMiddleware::class,
            \App\Modules\Users\Middleware\BanCheckMiddleware::class,
        ]);
    }
}

⚙️ Конфигурация

Конфиги поддерживают dot-нотацию (config('database.host')) и загружаются лениво (файл database.php не будет прочитан, если вы обращаетесь только к config('app.name')).

// app/Config/app.php
return [
    'name' => 'my-app',
    'env' => \W3a\Core\Foundation\Env::get('APP_ENV', 'development'),
    'lang' => \W3a\Core\Foundation\Env::get('APP_LANG', 'ru'),
    'log_path' => dirname(__DIR__, 2) . '/storage/logs/app.log',
];

🧩 Основные компоненты ядра

Компонент Неймспейс Назначение
Application W3a\Core\Foundation Оркестратор: управление жизненным циклом (bootstrap)
Container W3a\Core\Foundation DI-контейнер (поддержка singleton, bind, авто-резолв через рефлексию)
ExceptionHandler W3a\Core\Exceptions Централизованная обработка ошибок
Router W3a\Core\Http Маршрутизация с поддержкой middleware-групп и индексацией по префиксу
MessageBag W3a\Core\Support Управление flash-сообщениями и данными форм (old_input)
Collection W3a\Core\Support Fluent-интерфейс для трансформации массивов данных
Paginator W3a\Core\Support Расчёт пагинации: страницы, offset, диапазон
PhpArrayFile W3a\Core\Support Атомарная запись/чтение PHP-массивов (единый кэш-слой)
Validator W3a\Core\Support Декларативная валидация входных данных с поддержкой БД (unique, exists)
Session W3a\Core\Http Ленивое управление PHP-сессиями, flash-сообщения, защита от Session Fixation
FileCache W3a\Core\Cache Файловый кэш с serialize, flock и шардированием каталога
Audit W3a\Core\Support Журнал аудита действий с ленивым чтением сессии
DatabaseRateLimitStorage W3a\Core\Security БД-реализация RateLimitStorageInterface (таблица rate_limits)
DatabaseBannedIpRepository W3a\Core\Security БД-реализация BannedIpRepositoryInterface (таблица banned_ips)
DatabaseAuditStorage W3a\Core\Audit БД-реализация AuditStorageInterface (таблица audit_logs)
DefaultErrorHandler W3a\Core\Errors Fallback-обработчик ошибок (минимальный HTML)

🧰 Хелперы

Ядро предоставляет глобальные функции (загружаются автоматически):

Функция Назначение
config($key, $default) Значение конфигурации (dot-нотация)
env($key, $default) Переменная окружения из .env
container($abstract) Сервис из DI-контейнера
route($name, $params) URL по имени маршрута
redirect($url, $code) Редирект
abort($code, $message) Прервать выполнение с HTTP-ошибкой (бросает исключение ядра)
e($value) HTML-экранирование (защита от XSS)
__($key, $replace) Перевод строки
dt($datetime, $format) Форматирование даты из БД / timestamp
old($key, $default) Старое значение поля после ошибки валидации
csrf_field() Скрытое поле с CSRF-токеном
csp_nonce() Nonce для Content Security Policy
collect($items) Создать коллекцию

💾 Работа с кэшем

FileCache безопасен для публичных сайтов: данные сериализуются через serialize() (нет исполнения PHP), а каталог шардируется по первым 2 символам MD5-хеша ключа, что исключает деградацию ФС при десятках тысяч ключей.

$cache = $container->get(\W3a\Core\Cache\FileCache::class);

// Запись (TTL в секундах, 0 = бессрочно)
$cache->set('user:123:profile', $userData, 3600);

// Чтение (null если нет или истёк)
$user = $cache->get('user:123:profile');

// Проверка и удаление
if ($cache->has('user:123:profile')) {
    $cache->delete('user:123:profile');
}

// Полная очистка
$cache->clear();

⚠️ Миграция со старых версий: при обновлении необходимо очистить storage/cache/data/* — старые .php кэш-файлы несовместимы с новым форматом .cache.

🔐 Работа с сессиями

Session стартует лениво — только при первом вызове get()/set()/flash(). Это значит:

  • Анонимные GET-запросы (главная, статьи, RSS, JSON-API) не создают session-файлы
  • Боты и гости не получают cookie PHPSESSID
  • Авторизованные действия (login, POST-формы, CSRF) стартуют сессию автоматически
$session = $container->get(\W3a\Core\Http\Session::class);

// Для гостей: сессия НЕ стартует
$session->isStarted(); // false, пока нет обращения к данным

// Первое обращение автоматически запускает сессию
$session->set('user_id', 123);
$session->isStarted(); // true

// Flash-сообщения (доступны только на следующем запросе)
$session->flash('success', 'Профиль сохранён');

// Защита от Session Fixation — вызывать при входе/выходе
$session->regenerate();

// Полное уничтожение
$session->destroy();

📄 Лицензия

Распространяется под лицензией MIT.