From ac5b5f924609fdbca2703ee0bd0965c5ad82fdcb Mon Sep 17 00:00:00 2001 From: pronchev Date: Thu, 23 Apr 2026 17:39:01 +0300 Subject: [PATCH] Remove FrankenPHP targeting --- .claude/architecture/overview.md | 8 +++---- .claude/architecture/worker.md | 40 ++++++++++++++++++++++---------- .claude/development/commands.md | 16 ++++++------- .claude/development/patterns.md | 2 +- .claude/development/testing.md | 9 ++----- CLAUDE.md | 2 +- composer.json | 2 +- src/Http/WorkerRunner.php | 26 ++++++++++++++------- 8 files changed, 62 insertions(+), 43 deletions(-) diff --git a/.claude/architecture/overview.md b/.claude/architecture/overview.md index af82c62..f978abd 100644 --- a/.claude/architecture/overview.md +++ b/.claude/architecture/overview.md @@ -3,13 +3,13 @@ ## Общая схема ``` -HTTP → Caddy (:80) → /worker.php → FrankenPHP worker loop +HTTP → /worker.php → worker loop Kernel::boot($basePath) [один раз при старте] → Environment::detect() // читает APP_ENV, дефолт 'dev' → Config::load($configDir) // config/*.php + config/env/{env}.php + config/env/local.php → ContainerFactory::build() // PHP-DI, в prod компилируется в var/cache/prod/ - loop (WorkerRunner::run()): - frankenphp_handle_request(fn() => + loop (WorkerRunner::run($loop)): + $loop(fn() => // адаптер приложения, возвращает keepRunning HttpApplication::handleRequest($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER) → Request::fromGlobals() // парсит метод, путь, заголовки, JSON body → OPTIONS? → CORS headers + 204 // preflight, без роутинга @@ -32,7 +32,7 @@ HTTP → Caddy (:80) → /worker.php → FrankenPHP worker loop | `src/Environment.php` | `Environment` | Читает `APP_ENV`, метод `isProd()` | | `src/Config.php` | `Config` | Загружает `config/*.php`, deep-merge с env-оверрайдами; `get('a.b.c')` | | `src/ContainerFactory.php` | `ContainerFactory` | Строит PHP-DI контейнер; в prod включает compilation | -| `src/Http/WorkerRunner.php` | `WorkerRunner` | Цикл FrankenPHP, MAX_REQUESTS, gc | +| `src/Http/WorkerRunner.php` | `WorkerRunner` | Worker-цикл, MAX_REQUESTS, gc | | `src/Http/HttpApplication.php` | `HttpApplication` | CORS, роутинг, dispatch, обработка исключений | | `src/Http/Request.php` | `Request` | Иммутабельный; `body()`, `get(key)`, `withContext()` | | `src/Http/Response.php` | `Response` | `json()`, `error()`, `withHeader()`, `emit()` | diff --git a/.claude/architecture/worker.md b/.claude/architecture/worker.md index f16b0ae..b974a79 100644 --- a/.claude/architecture/worker.md +++ b/.claude/architecture/worker.md @@ -2,7 +2,7 @@ ## Entrypoint (`worker.php` приложения) -FrankenPHP запускается как `frankenphp run --config Caddyfile` и сам стартует PHP-воркеры. +Фреймворк запускается удобным для пользователя способом. `WorkerRunner::run()` без аргументов обрабатывает один запрос и завершается (классический SAPI/FPM/CGI). Чтобы крутить worker-петлю, приложение передаёт свой адаптер: ```php use Pronchev\Pinecore\Kernel; @@ -11,10 +11,16 @@ use Pronchev\Pinecore\Http\WorkerRunner; require __DIR__ . '/vendor/autoload.php'; Kernel::boot(__DIR__); -Kernel::container()->get(WorkerRunner::class)->run(); +$runner = Kernel::container()->get(WorkerRunner::class); + +// Один запрос: +$runner->run(); + +// Или worker-петля (пример для FrankenPHP): +$runner->run(fn ($handler) => frankenphp_handle_request($handler)); ``` -`WorkerRunner` резолвится через DI autowiring — конфигурировать не нужно. +Адаптер получает `callable $handler` (обработать один запрос) и возвращает `bool` — продолжать ли цикл. `MAX_REQUESTS`, `$app->terminate()` и `gc_collect_cycles()` отрабатывает сам `WorkerRunner` между итерациями. `WorkerRunner` резолвится через DI autowiring — конфигурировать не нужно. ## WorkerRunner (`src/Http/WorkerRunner.php`) @@ -26,23 +32,33 @@ final class WorkerRunner private readonly ExceptionHandler $exceptionHandler, ) {} - public function run(): void + public function run(?callable $loop = null): void { + if ($loop === null) { + $this->handle(); + $this->app->terminate(); + return; + } + + $handler = fn () => $this->handle(); $maxRequests = (int) ($_SERVER['MAX_REQUESTS'] ?? 0); // 0 = без лимита for ($n = 0; !$maxRequests || $n < $maxRequests; ++$n) { - $keepRunning = frankenphp_handle_request(function (): void { - try { - $this->app->handleRequest($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); - } catch (\Throwable $e) { - $this->exceptionHandler->handleException($e); // critical лог - } - }); + $keepRunning = $loop($handler); $this->app->terminate(); // хук: закрытие ресурсов (сейчас пустой) gc_collect_cycles(); - if (!$keepRunning) break; // FrankenPHP сигнализирует об остановке + if (!$keepRunning) break; + } + } + + private function handle(): void + { + try { + $this->app->handleRequest($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } catch (\Throwable $e) { + $this->exceptionHandler->handleException($e); // critical лог } } } diff --git a/.claude/development/commands.md b/.claude/development/commands.md index 9f0c7fc..2d67981 100644 --- a/.claude/development/commands.md +++ b/.claude/development/commands.md @@ -2,20 +2,18 @@ ## Запуск worker -FrankenPHP стартует PHP-воркеры через Caddyfile: - -```bash -frankenphp run --config Caddyfile -``` - -Воркер-скрипт приложения подключается к фреймворку через `WorkerRunner`: +Фреймворк запускается удобным для пользователя способом — способ запуска выбирает само приложение. Воркер-скрипт приложения подключается к фреймворку через `WorkerRunner`: ```php -// worker.php -$runner = new WorkerRunner($kernel); +// worker.php — один запрос (классический SAPI) $runner->run(); + +// worker.php — worker-петля, адаптер инжектится приложением +$runner->run(fn ($handler) => frankenphp_handle_request($handler)); ``` +Адаптер получает `callable $handler` и возвращает `bool` (продолжать ли цикл). `MAX_REQUESTS`, `terminate()` и `gc_collect_cycles()` `WorkerRunner` делает сам между итерациями. + --- ## Console-команды diff --git a/.claude/development/patterns.md b/.claude/development/patterns.md index b7459fd..3799b9c 100644 --- a/.claude/development/patterns.md +++ b/.claude/development/patterns.md @@ -105,7 +105,7 @@ $value = Config::get('section.key', $default); ```php // Внутри worker.php $runner = new WorkerRunner($kernel); -$runner->run(); // FrankenPHP loop: запрос → роутинг → middleware → контроллер → ответ +$runner->run(); // worker loop: запрос → роутинг → middleware → контроллер → ответ ``` `WorkerRunner` перехватывает исключения и возвращает корректный HTTP-ответ даже при ошибке. diff --git a/.claude/development/testing.md b/.claude/development/testing.md index 5b94369..981b2e4 100644 --- a/.claude/development/testing.md +++ b/.claude/development/testing.md @@ -2,11 +2,7 @@ ## Запуск воркера локально -```bash -frankenphp run --config Caddyfile -``` - -Убедись, что переменные окружения заданы (`.env` или `environment.php`). +Запусти воркер удобным для тебя способом (worker-рантайм, FPM, CLI — по выбору приложения). Убедись, что переменные окружения заданы (`.env` или `environment.php`). --- @@ -26,8 +22,7 @@ Config::get('log.level') // DEBUG для максимального вывода ### Воркер не стартует -- Проверь синтаксис `Caddyfile` и путь до `worker.php` -- FrankenPHP требует `FRANKENPHP_CONFIG` или явного указания воркер-скрипта +- Проверь конфигурацию выбранного рантайма и путь до `worker.php` - Проверь, что `Kernel::init()` вызван до первого запроса ### Маршрут не найден (404) diff --git a/CLAUDE.md b/CLAUDE.md index 801c5fe..7a669a6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ Guidance for Claude Code when working with this repository. ## Package -`pronchev/pinecore` — minimal PHP framework for FrankenPHP long-running workers. +`pronchev/pinecore` — minimal PHP framework for long-running workers. **Namespace:** `Pronchev\Pinecore\` → `src/` (PSR-4) diff --git a/composer.json b/composer.json index a41cf74..c526b2f 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pronchev/pinecore", - "description": "Minimal PHP framework for FrankenPHP long-running workers", + "description": "Minimal PHP framework for long-running workers", "type": "library", "license": "MIT", "require": { diff --git a/src/Http/WorkerRunner.php b/src/Http/WorkerRunner.php index c270917..6e20b25 100644 --- a/src/Http/WorkerRunner.php +++ b/src/Http/WorkerRunner.php @@ -11,18 +11,19 @@ final class WorkerRunner private readonly ExceptionHandler $exceptionHandler, ) {} - public function run(): void + public function run(?callable $loop = null): void { + if ($loop === null) { + $this->handle(); + $this->app->terminate(); + return; + } + + $handler = fn () => $this->handle(); $maxRequests = (int) ($_SERVER['MAX_REQUESTS'] ?? 0); for ($n = 0; !$maxRequests || $n < $maxRequests; ++$n) { - $keepRunning = frankenphp_handle_request(function (): void { - try { - $this->app->handleRequest($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); - } catch (\Throwable $e) { - $this->exceptionHandler->handleException($e); - } - }); + $keepRunning = $loop($handler); $this->app->terminate(); gc_collect_cycles(); @@ -32,4 +33,13 @@ final class WorkerRunner } } } + + private function handle(): void + { + try { + $this->app->handleRequest($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER); + } catch (\Throwable $e) { + $this->exceptionHandler->handleException($e); + } + } }