- BrainTools - https://www.braintools.ru -

Как я написал production-ready PHP-роутер за один вечер с помощью ИИ

Предыстория: зачем ещё один роутер?

Каждый PHP-разработчик хотя бы раз задавался вопросом: «А не написать ли свой роутер?» Обычно ответ — «не надо, возьми готовый». И это правильный совет. FastRoute, Symfony Routing, Laravel Router — все они проверены временем и боем.

Но у меня была другая цель. Я хотел проверить гипотезу: можно ли с помощью современных ИИ-инструментов создать production-ready библиотеку, которая не стыдно выложить на Packagist, за один вечер?

Не прототип. Не «MVP, который потом допилим». А полноценную библиотеку с:

  • Строгой типизацией (PHP 8.4, strict_types)

  • PHPStan level 9

  • Полным покрытием тестами

  • CI/CD пайплайном

  • Документацией

  • Публикацией на Packagist

Спойлер: получилось.


Что получилось: Waypoint

ascetic-soft/waypoint [1] — легковесный PSR-15 роутер со следующими возможностями:

  • PSR-15 совместимость — реализует RequestHandlerInterface, работает с любым PSR-7/PSR-15 стеком

  • Атрибутная маршрутизация — объявление маршрутов через PHP 8 #[Route] атрибуты прямо на контроллерах

  • Быстрый prefix-trie матчинг — статические сегменты за O(1), динамические — только когда нужно

  • Middleware-пайплайн — глобальные и per-route PSR-15 middleware

  • Группы маршрутов — общие префиксы и middleware

  • Кеширование — компиляция маршрутов в PHP-файл для OPcache

  • Автоматический DI — параметры маршрута, ServerRequestInterface, сервисы из контейнера

  • URL-генерация — обратная маршрутизация по именам

  • Диагностика — обнаружение конфликтов, дубликатов, затенённых маршрутов

Масштаб проекта

Метрика

Значение

Строк кода (src/)

~2 100

Строк тестов (tests/)

~2 650

Файлов в src/

18

Тестовых файлов

16

Коммитов

8

Время разработки

~5.5 часов

PHPStan

Level 9

Зависимости

Только PSR-интерфейсы


Как проходил процесс

Инструмент: Cursor IDE

Я использовал Cursor [2] — IDE на базе VS Code с глубокой интеграцией ИИ. Cursor позволяет вести диалог с ИИ прямо в контексте кодовой базы: он видит файлы проекта, понимает структуру, может читать и редактировать код.

Хронология (по git log)

Весь проект был создан 12 февраля 2026 года. Вот хронология коммитов:

17:16  init project           — каркас проекта, composer.json, базовые классы
17:22  phpstan level 9        — настройка статического анализа, фикс типов
17:25  makefile fixer          — Makefile для удобной разработки
17:44  tree match             — реализация prefix-trie для быстрого матчинга
17:57  docs && ci             — README, GitHub Actions CI/CD
18:14  tests                  — полный набор юнит-тестов
22:44  Url Generator          — обратная маршрутизация
22:56  Url Generator base url — поддержка абсолютных URL
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 1 [3]

Между первым и последним коммитом прошло 5 часов 40 минут. Это включая перерывы, обдумывание архитектуры и ревью сгенерированного кода.

Этап 1: Архитектура и каркас (17:16)

Я начал с описания того, что хочу получить. Примерно так:

«Мне нужен PSR-15 совместимый PHP-роутер. PHP 8.4, strict types. Поддержка атрибутов #[Route] на контроллерах. FastRoute-style плейсхолдеры {name} и {name:regex}. Middleware pipeline. Группы маршрутов. Минимум зависимостей — только PSR-интерфейсы.»

ИИ предложил архитектуру:

Router  (PSR-15 RequestHandlerInterface)
├── RouteCollection
│   ├── RouteTrie           — prefix-tree для быстрого матчинга
│   └── Route[]             — линейный fallback для сложных паттернов
├── AttributeRouteLoader    — чтение #[Route] атрибутов через Reflection
├── MiddlewarePipeline      — FIFO PSR-15 middleware
├── RouteHandler            — вызов контроллера с DI
├── RouteCompiler           — компиляция/загрузка кеша
└── RouteDiagnostics        — обнаружение конфликтов
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 2 [3]

Я одобрил структуру, и за несколько итераций диалога были созданы все базовые файлы: composer.json, атрибут #[Route], value-объекты Route и RouteMatchResult, коллекция маршрутов, загрузчик атрибутов.

Что важно: я не просто жал «принять всё». На каждом шаге я проверял:

  • Соответствует ли код PSR-стандартам

  • Правильно ли используются readonly-свойства PHP 8.4

  • Нет ли лишних зависимостей

  • Логична ли декомпозиция

Этап 2: Prefix-Trie — сердце роутера (17:44)

Самая интересная часть — алгоритм матчинга маршрутов. Вместо простого перебора всех регулярок (как в большинстве роутеров), Waypoint использует prefix-trie (префиксное дерево).

Идея:

  1. Каждый маршрут разбивается на сегменты по /

  2. Статические сегменты — ключи в hash-map (O(1) lookup)

  3. Динамические сегменты ({id}, {slug:w+}) — проверяются regex только когда нужно

  4. Если паттерн не совместим с trie (например, prefix-{name}.txt), маршрут попадает в линейный fallback

Вот ключевой метод матчинга:

public function match(
    string $method,
    array $segments,
    int $depth = 0,
    array $params = [],
    array &$allowedMethods = [],
): ?array {
    if ($depth === count($segments)) {
        foreach ($this->routes as $route) {
            if ($route->allowsMethod($method)) {
                return ['route' => $route, 'params' => $params];
            }
            foreach ($route->getMethods() as $m) {
                $allowedMethods[$m] = true;
            }
        }
        return null;
    }

    $segment = $segments[$depth];

    // 1. Статический потомок — O(1) hash-map lookup
    if (isset($this->staticChildren[$segment])) {
        $result = $this->staticChildren[$segment]->match(
            $method, $segments, $depth + 1, $params, $allowedMethods,
        );
        if ($result !== null) {
            return $result;
        }
    }

    // 2. Динамические потомки — в порядке приоритета
    foreach ($this->paramChildren as $child) {
        if (preg_match($child['regex'], $segment)) {
            $childParams = $params;
            $childParams[$child['paramName']] = $segment;
            $result = $child['node']->match(
                $method, $segments, $depth + 1, $childParams, $allowedMethods,
            );
            if ($result !== null) {
                return $result;
            }
        }
    }

    return null;
}
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 3 [3]

ИИ предложил эту структуру, но я попросил доработать несколько моментов:

  • Добавить бэктрекинг (если статическая ветка не нашла — пробуем динамическую)

  • Собирать allowedMethods для корректного 405-ответа

  • Проверку совместимости паттерна с trie (isCompatible())

Этап 3: Атрибутная маршрутизация

PHP 8 атрибуты — мощный инструмент для декларативного описания маршрутов:

#[Route('/api/users', middleware: [AuthMiddleware::class])]
class UserController
{
    #[Route('/', methods: ['GET'], name: 'users.list')]
    public function list(): ResponseInterface { /* ... */ }

    #[Route('/{id:d+}', methods: ['GET'], name: 'users.show')]
    public function show(int $id): ResponseInterface { /* ... */ }

    #[Route('/{id:d+}', methods: ['PUT'], name: 'users.update')]
    public function update(int $id, ServerRequestInterface $request): ResponseInterface { /* ... */ }
}
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 4 [3]

Атрибут #[Route] работает на двух уровнях:

  • На классе — задаёт префикс пути и общие middleware

  • На методе — определяет конкретный маршрут

Атрибут — IS_REPEATABLE, что позволяет одному методу обслуживать несколько маршрутов. AttributeRouteLoader использует Reflection API для извлечения метаданных.

Этап 4: Тесты (18:14)

Вот тут ИИ показал себя во всей красе. После формулировки «Напиши полный набор тестов для всех компонентов» я получил 2 650 строк тестового кода, покрывающего:

  • RouterTest — интеграционные тесты роутера

  • RouteCollectionTest — матчинг маршрутов, приоритеты, 404/405

  • RouteTrieTest — тесты prefix-trie: статические/динамические сегменты, бэктрекинг

  • AttributeRouteLoaderTest — загрузка атрибутов, классовые префиксы

  • MiddlewarePipelineTest — порядок выполнения middleware, FIFO

  • RouteHandlerTest — DI-инъекция параметров, приведение типов

  • RouteDiagnosticsTest — обнаружение конфликтов

  • RouteCompilerTest — компиляция и загрузка кеша

  • UrlGeneratorTest — генерация URL, query-параметры

При этом тесты были не «для галочки» — в них проверялись граничные случаи: trailing slashes, пустые пути, конфликтующие маршруты, nullable-параметры, приведение типов.

Конечно, я проверял тесты и при необходимости просил доработать покрытие для edge cases.

Этап 5: CI/CD и документация (17:57)

GitHub Actions pipeline с тремя параллельными job’ами:

jobs:
  code-style:     # PHP CS Fixer (dry-run)
  static-analysis: # PHPStan level 9
  tests:          # PHPUnit + coverage → Codecov
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 5 [3]

README получился обширный, с примерами для каждой фичи, таблицами параметров, диаграммой архитектуры, бейджами CI, покрытия и версий.

Этап 6: URL Generator (22:44–22:56)

Последний штрих — обратная маршрутизация. Именованные маршруты можно использовать для генерации URL:

$router->get('/users/{id:d+}', $handler, name: 'users.show');

$url = $router->generate('users.show', ['id' => 42]);
// => /users/42

$url = $router->generate('users.show', ['id' => 42], absolute: true);
// => https://example.com/users/42
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 6 [3]

Что ИИ делает хорошо

1. Шаблонный код

Роутер — это много однотипного кода: методы get(), post(), put(), delete() отличаются одним параметром. ИИ генерирует такой код мгновенно и безошибочно.

2. PHPDoc и типизация

Все @param, @return, @throws, generic-типы вроде list<array{type: 'static'|'param', value: string}> — ИИ выдаёт их правильно и полно. Это критично для PHPStan level 9.

3. Тесты

Написание тестов — рутина, от которой большинство разработчиков отлынивают. ИИ генерирует тесты с удовольствием и покрывает сценарии, о которых можно забыть.

4. Конфигурационные файлы

composer.json, phpunit.xml.dist, phpstan.neon.dist, .php-cs-fixer.dist.php, Makefile, .github/workflows/ci.yml — всё это ИИ создал правильно с первого-второго раза.

5. README

Документация получилась подробная, с примерами кода для каждой фичи, таблицами параметров, ASCII-диаграммой архитектуры.


Где ИИ нуждается в контроле

1. Архитектурные решения

ИИ предлагает решения, но окончательный выбор — за разработчиком. Например, решение разделить маршруты на trie-совместимые и линейный fallback — это архитектурное решение, которое нужно было осознанно принять.

2. Edge cases

ИИ может пропустить нетривиальные граничные случаи. Например, что происходит, когда regex параметра может матчить / (кросс-сегментный захват)? Или когда сегмент содержит смесь статического текста и параметра (prefix-{name}.txt)? Эти случаи нужно было явно продумать и указать ИИ.

3. Качество кода

Иногда ИИ генерирует «рабочий, но некрасивый» код. Нужно не стесняться просить рефакторинг: «Сделай это через readonly-свойства», «Используй named arguments», «Разбей на более мелкие методы».

4. Консистентность

При генерации большого объёма кода ИИ может забыть о решениях, принятых ранее. Важно следить за единообразием именования, обработки ошибок, порядка параметров.


Технические решения, которыми горжусь

Двухуровневый матчинг

Не все маршруты помещаются в trie. Паттерн /files/{path:.+} (где regex матчит /) или /report-{year}.pdf (смесь статики и параметра в одном сегменте) — не совместимы с посегментным поиском. Waypoint автоматически определяет это через RouteTrie::isCompatible() и отправляет такие маршруты в линейный fallback.

Lazy-инициализация

Trie строится только при первом запросе match(). Индекс имён для URL-генерации — только при первом вызове generate(). Это значит, что если вы загружаете 500 маршрутов, но обрабатываете только один запрос — вы не платите за построение всех индексов.

OPcache-дружественный кеш

Маршруты компилируются в обычный PHP-массив:

// cache/routes.php — загружается мгновенно через OPcache
return [
    ['pattern' => '/users/{id:d+}', 'methods' => ['GET'], 'handler' => [...], ...],
    // ...
];
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 7 [3]

Никакого serialize()/unserialize(), никакого JSON. Просто include + OPcache = нулевые накладные расходы.

DI с приведением типов

RouteHandler анализирует сигнатуру метода контроллера и автоматически приводит параметры маршрута к нужному типу:

// Маршрут: /users/{id:d+}
// Параметр из URL: string "42"
public function show(int $id) { /* $id === 42 (int) */ }
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 8 [3]

Порядок разрешения: ServerRequestInterface → route-параметры → контейнер → default values → nullable.


Статистика и цифры

  • 8 коммитов за 5 часов 40 минут

  • 18 файлов в src/, 16 тестовых файлов

  • ~4 800 строк PHP-кода суммарно

  • 0 внешних зависимостей (только PSR-интерфейсы)

  • PHPStan level 9 — максимальный уровень строгости

  • 3 параллельных CI job’а: code style, static analysis, tests + coverage


Выводы

ИИ — это мультипликатор, а не замена

ИИ не написал этот проект за меня. Я принимал архитектурные решения, ревьюил каждый файл, указывал на проблемы, просил доработки. Но ИИ колоссально ускорил процесс.

Без ИИ этот проект занял бы у меня 3–5 рабочих дней. С ИИ — один вечер. При этом качество кода не пострадало: PHPStan level 9 не прощает небрежности.

Что нужно от разработчика

  1. Чёткое видение — ИИ отлично выполняет задачи, но плохо ставит их сам себе

  2. Архитектурное мышление [4] — декомпозиция, выбор паттернов, trade-offs

  3. Code review — ИИ может ошибаться, и его код нужно проверять с тем же пристрастием, что и код коллеги

  4. Знание предметной области — PSR-стандарты, особенности PHP 8.4, best practices

Когда это работает лучше всего

  • Библиотеки с чёткой спецификацией (как роутер)

  • Проекты, где много шаблонного кода

  • Задачи с хорошо определёнными интерфейсами (PSR)

  • Написание тестов и документации

Когда стоит быть осторожным

  • Уникальная бизнес-логика

  • Код, зависящий от специфического контекста

  • Оптимизации производительности (нужны бенчмарки, а не интуиция [5] ИИ)

  • Безопасность (всегда проверяйте вручную)


Попробовать

composer require ascetic-soft/waypoint
Как я написал production-ready PHP-роутер за один вечер с помощью ИИ - 9 [3]

Код на GitHub: ascetic-soft/Waypoint [6]


Вопросы, критика, идеи — welcome в комментариях. Если вы тоже создавали проекты с помощью ИИ — делитесь опытом [7], инте��есно сравнить подходы.

Автор: kotafey

Источник [8]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/25686

URLs in this post:

[1] ascetic-soft/waypoint: https://packagist.org/packages/ascetic-soft/waypoint

[2] Cursor: https://cursor.com/

[3] Image: https://sourcecraft.dev/

[4] мышление: http://www.braintools.ru/thinking

[5] интуиция: http://www.braintools.ru/article/6929

[6] ascetic-soft/Waypoint: https://github.com/ascetic-soft/Waypoint

[7] опытом: http://www.braintools.ru/article/6952

[8] Источник: https://habr.com/ru/articles/996728/?utm_source=habrahabr&utm_medium=rss&utm_campaign=996728

www.BrainTools.ru

Rambler's Top100