Огляд ядра
Пакет php-concept/core-2 (Concept\Core) — мінімальний HTTP
runtime для PHP 8.4+. Він свідомо не тягне View, Session чи БД:
ядро відповідає лише за «отримати request → знайти маршрут → викликати
handler → повернути response». Усе інше підключається extensions і
збіркою додатку (Stack / Bootstrap), коли воно справді потрібне.
Навіщо так тонко: щоб можна було зібрати API без Twig, CLI без HTTP, тести без «повного фреймворку», і щоб межа між runtime і предметною логікою була видимою. Core дає DI (League Container), маршрутизацію (League Route), dispatch і контракти для кастомізації — не «фреймворк із коробки».
Структура пакета
Дерево нижче — не «все, що є в додатку», а лише те, без чого не стартує HTTP-цикл. Якщо файлу немає в цьому списку, його шукайте в extension або у своєму App-шарі.
src/
├── App.php
├── Container/
│ ├── ContainerDependency.php
│ └── Exception/ContainerDependencyException.php
├── Events/Http/
│ ├── RequestHandled.php
│ ├── RouteHandlerInvoked.php
│ └── RouteInterceptorInvoked.php
├── Http/
│ ├── Contracts/
│ │ ├── ArgumentResolverInterface.php
│ │ ├── RequestContextInterface.php
│ │ └── RouteInterceptorInterface.php
│ ├── Request/RequestContext.php
│ └── Routing/
│ ├── RouteStrategy.php
│ └── Resolvers/
│ ├── ServerRequestArgumentResolver.php
│ └── RouteParameterArgumentResolver.php
└── Providers/Http/HttpKernelServiceProvider.php
Точка входу
App::create() піднімає контейнер; providers реєструєте ви —
ядро не «вгадує» стек. handle() — єдиний публічний крок
«обробити HTTP»: взяти request і Router з контейнера, викликати
Router::dispatch(), повернути ResponseInterface.
Emit у браузер лишається за entry point (SapiEmitter тощо),
щоб той самий App можна було ганяти в тестах без SAPI.
use Concept\Core\App;
$app = App::create();
$app->registerServiceProviders([/* ... */]);
$response = $app->handle();
HttpKernelServiceProvider
Єдиний вбудований service provider — і це навмисно. Без нього в контейнері немає Router і ServerRequest: HTTP з’являється лише коли ви явно підключили kernel. Він реєструє:
-
ServerRequestInterface— з globals черезServerRequestFactory::fromGlobals(): entry point дляApp::handle(), не «магічний» request у кожному сервісі -
RequestContextInterface— scoped enriched request під час handler: коли сервісу потрібен поточний request без прокидання параметром у кожен метод -
Router— League Route зRouteStrategy: маршрути підключаєте шляхами до файлів, без прихованих auto-routes
RouteStrategy
Розширює League ApplicationStrategy, щоб після middleware
не було «чорної скриньки»: видно, що саме відбувається перед
викликом handler-а. Порядок фіксований — це контракт життєвого циклу:
- route interceptors (якщо передані) — fail-fast за метаданими маршруту
prepareRequest()— route vars → PSR-7 attributes- argument resolvers → invoke handler
- перевірка, що результат —
ResponseInterface
Коли розширюєте поведінку (FormRequest, casting), додаєте resolver
або interceptor у chain — не патчите RouteStrategy.
Залежності
League Container, League Route, Laminas Diactoros (PSR-7 factory), PSR Event Dispatcher (опційно). Жодних додаткових пакетів Concept: Core не тягне extensions «транзитивно». Якщо потрібен View чи Session — це окремий пакет і явний крок збірки.
Практичний старт: Мінімальний додаток — повний приклад лише на core-2, без Stack і extensions. Як зібрати «справжній» додаток покроково — Огляд.