bootId = bin2hex(random_bytes(6)); $this->database = new PDO('sqlite::memory:', null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); $this->database->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, price INTEGER)'); $this->database->exec("INSERT INTO products VALUES (1, 'Аудит', 5000), (2, 'Настройка', 12000), (3, 'Сопровождение', 25000)"); } public function handle(): void { ++$this->handled; $userBefore = $this->currentUser; $cacheBefore = count($this->requestCache); $requestId = bin2hex(random_bytes(6)); $base = ['mode'=>getenv('FP_MODE') ?: 'classic','bootId'=>$this->bootId,'handled'=>$this->handled,'requestId'=>$requestId,'userBefore'=>$userBefore,'cacheBefore'=>$cacheBefore,'php'=>PHP_VERSION]; try { $path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH); $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; if ($path === '/fail') throw new RuntimeException('Intentional training failure'); if ($path === '/health') { $this->json(200, $base + ['ok'=>true]); return; } if ($path === '/customer') { $user = $_GET['user'] ?? 'guest'; if (!is_string($user) || !in_array($user, ['anna','ivan','guest'], true)) { $this->json(422, $base + ['error'=>'INVALID_DEMO_USER']); return; } $this->currentUser = $user; $this->requestCache['greeting'] = 'Здравствуйте, '.$user; $this->json(200, $base + ['user'=>$this->currentUser,'greeting'=>$this->requestCache['greeting']]); return; } if ($path === '/orders') { if ($method !== 'POST') { header('Allow: POST'); $this->json(405, $base + ['error'=>'METHOD_NOT_ALLOWED']); return; } if (!preg_match('~^application/json(?:;|$)~i', $_SERVER['CONTENT_TYPE'] ?? '')) { $this->json(415, $base + ['error'=>'JSON_REQUIRED']); return; } $raw = file_get_contents('php://input', false, null, 0, 16385); if ($raw === false || strlen($raw) > 16384) { $this->json(413, $base + ['error'=>'BODY_TOO_LARGE']); return; } try { $body = json_decode($raw, false, 16, JSON_THROW_ON_ERROR); } catch (JsonException) { $this->json(400, $base + ['error'=>'INVALID_JSON']); return; } if (!$body instanceof stdClass || !isset($body->title) || !is_string($body->title) || trim($body->title) === '' || strlen($body->title) > 120) { $this->json(422, $base + ['error'=>'INVALID_TITLE']); return; } $this->json(201, $base + ['accepted'=>true,'title'=>$body->title,'persisted'=>false]); return; } if ($path === '/products') { $min = filter_var($_GET['min'] ?? '0', FILTER_VALIDATE_INT, ['options'=>['min_range'=>0,'max_range'=>1000000]]); if ($min === false) { $this->json(422, $base + ['error'=>'INVALID_MIN']); return; } $query = $this->database->prepare('SELECT id, name, price FROM products WHERE price >= :min ORDER BY price'); $query->bindValue(':min', $min, PDO::PARAM_INT); $query->execute(); $this->json(200, $base + ['storage'=>'sqlite-memory-demo','items'=>$query->fetchAll(PDO::FETCH_ASSOC)]); return; } $this->json(404, $base + ['error'=>'NOT_FOUND']); } catch (Throwable $error) { error_log(json_encode(['event'=>'request_failed','requestId'=>$requestId,'exception'=>$error::class], JSON_THROW_ON_ERROR)); $this->json(500, $base + ['error'=>'INTERNAL_ERROR']); } finally { if ($this->database->inTransaction()) $this->database->rollBack(); $this->currentUser = null; $this->requestCache = []; } } private function json(int $status, array $data): void { http_response_code($status); header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-store'); echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); } }