20.08.2026 · PHP · Symfony · OpenAPI · ~10 мин чтения

Современный API Reference в Symfony через Scalar

Обновление 18.09.2026: бандл принят в организацию Scalar и стал официальной интеграцией для Symfony — github.com/scalar/symfony, на Packagist scalar/symfony. Ставить нужно его: composer require scalar/symfony. Прежний пакет alex-frolov/scalar-symfony помечен abandoned, репозиторий заархивирован. Текст статьи ниже — о том, как бандл создавался; команды установки в нём обновлены на официальный пакет.

1. OpenAPI в реальных проектах

Работая с PHP, испытываешь постоянную необходимость поддерживать документацию в актуальном состоянии. В последнее время PHP-проекты — это API-first: бэкенд на PHP, а фронт собирается отдельно.

Тут у нас два пути:

  • Spec-first — сначала готовится спецификация всех возможных маршрутов с входными/выходными параметрами, ошибками и пр. Часто это YAML-файл OpenAPI;
  • OpenAPI-файл генерируется на основе атрибутов и аннотаций в коде.

В первом случае мы делаем то, что прописано в спецификации, и имеем возможность сравнить, верно ли у нас всё сделано. Во втором случае у нас всегда актуальная схема (после автоматического обновления).

Стандартом в Symfony считается NelmioApiDocBundle. Вплоть до 4-й версии в качестве UI он всё ещё отдаёт Swagger UI или Redoc. Если поискать, какие ещё есть варианты UI, находишь свежий и современный Scalar — open-source-рендерер API Reference (в NelmioApiDocBundle 5+ заявлена поддержка). Но NelmioApiDocBundle генерирует OpenAPI из атрибутов — это эталонный представитель второго типа.

Если посмотреть на Laravel, там вариантов больше: Scribe, Scramble и официальная интеграция со Scalar.

Хотелось бы лёгкий, современный UI для отрисовки готового OpenAPI YAML-файла — для первого типа работы с документацией API. И, кажется, Scalar для этого отлично подходит, но вот интеграции из коробки у Symfony со Scalar нет. Чтобы подключить Scalar в Symfony, приходится копировать HTML-страницу с <script>-тегом из доков Scalar и настраивать конфигурацию руками.

В самом Scalar официальный список интеграций покрывает 30+ фреймворков ( Express, FastAPI, NestJS, Spring Boot, Laravel), но нет Symfony. На Packagist не было ни одного пакета, который подключал Scalar в Symfony.

В одном своём проекте у меня подход OpenAPI-first: сначала спецификация, потом реализация. Мощный NelmioApiDocBundle тянуть в проект не хотелось. Выбор пал на Scalar. Но одно плохо — нет интеграции. Можно было просто интегрировать своими силами в проекте, но я понял, что это можно оформить в виде бандла и в дальнейшем переиспользовать в других проектах. За один вечер я написал первую версию Symfony-бандла, тесты и CI-матрицу. Протестировал на своём проекте — после чего бандл был опубликован.

2. Разберёмся подробнее, что такое Scalar

Scalar — open-source API-платформа для работы с OpenAPI-документами.

Две части:

  • API Reference — интерактивный рендерер OpenAPI 3.x; загружает спеку на клиенте, со встроенным API-клиентом (возможность делать тестовые запросы), сниппетами кода на разных языках (curl, PHP, Python, Go), разными темами оформления и разными схемами авторизации;
  • API client — десктопный клиент уровня Postman: работает офлайн и читает те же OpenAPI-файлы.

На данный момент на GitHub: около 16 тыс. звёзд, создан в 2023 году, насчитывает более 100 релизов — живой проект.

Есть интеграция для Laravel («scalar/laravel», официальная, в org scalar) — это основа, на которую стоит смотреть при реализации своего бандла. Что там есть? «Тонкий пакет», который отдаёт одну страницу со Scalar, указывая на любой OpenAPI-документ.

3. Бандл: что он делает

scalar/symfony рендерит Scalar API Reference в Symfony из любого OpenAPI-документа. Один маршрут, нет привязки к тому, каким образом сгенерировалась спецификация: может быть статичный openapi.yaml, swagger-php, NelmioApiDocBundle или API Platform.

Для установки и настройки нужно отредактировать два файла.

Подключаем бандл:

composer require scalar/symfony

Редактируем настройки:

# config/packages/scalar_symfony.yaml
scalar_symfony:
    url: '/openapi.yaml'          # ваш OpenAPI-документ (обязательно)

    path: '/scalar'               # маршрут (по умолчанию: /scalar)
    cdn: 'https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.65.1'

    configuration:
        theme: 'default'
        metaData:
            title: 'API Reference'

    scalar_options:               # любая опция Scalar, пробрасывается как есть
        darkMode: false
        layout: 'modern'

    access_control:
        mode: public              # или 'attribute' + security-атрибут
# config/routes.yaml
scalar_symfony:
    resource: '@ScalarSymfonyBundle/config/routes.php'

Всё — reference открывается по адресу /scalar. Страница — небольшой HTML с CDN-скриптом и Scalar.createApiReference('#scalar-api-reference', {...}), где конфиг сериализуется XSS-безопасно (JSON_HEX_TAG/APOS/AMP/QUOT).

4. Настоящий запрос, настоящий 201

Лучше всего показать это на живом примере, а не на мокапе. Бандл обслуживает API Reference платформы Tender (Symfony 8.1, highload-аукционный API; спека — OpenAPI 3.1). Через «Test Request» открываем эндпоинт POST /auth/register — «Регистрация компании (создаётся компания pending + первый пользователь admin)» — заполнил JSON-тело, нажал Send и получил:

HTTP/1.1 201 Created  (794 ms)
{
  "company_id": "0c7702c6-9667-4ea3-8caa-df4990522ee7",
  "user_id": "86a40d70-5ef9-44fd-882b-8f703e10df7e",
  "verification_status": "pending"
}

Реальный ответ с реальными данными. Вариантов для тестирования здесь больше, чем в Swagger UI: страница работает как интерактивный клиент.

Scalar API Reference в Symfony: живой ответ 201 от POST /auth/register в Test Request — реальные UUID, 794 ms
Живой ответ 201 в Scalar API Reference: POST /auth/register на платформе Tender, реальный бэкенд

5. Контроль качества

Итоговое состояние:

  • 16 функциональных тестов, 46 проверок;
  • статический анализ — PHPStan level max;
  • стиль кода — PHP-CS-Fixer, правила @Symfony;
  • CI-проверки: PHP 8.2/8.3/8.5 со Symfony 6.4/7.2/7.4/8.0, включая опции --prefer-lowest (5 job), no-dev smoke-тест и composer validate --strict;
  • валидация конфига на этапе компиляции: режим attribute без Symfony Security роняет cache:clear с внятной ошибкой вместо HTTP 500 на первом запросе; пустой cdn, путь без ведущего / и пустой attribute тоже блокируются на уровне конфига;
  • добавлена документация по безопасности: SRI (SHA-384 для указанного файла CDN), рекомендации по CSP/nonce, рецепт self-hosting.

По дороге нашлись два артефакта.

Первый: PHPUnit 11.5 помечает тесты как risky, когда ErrorHandler Symfony идёт после boot ядра; фикс — восстановление exception-handler в tearDown().

Второй: контейнер кэшируется по классу ядра и окружению, из-за этого функциональные тесты с разными конфигами бандла должны использовать уникальный cache-dir на каждый конфиг — иначе тесты запускаются из устаревшего контейнера с битым состоянием.

6. CI и проблемы

После пуша в репозиторий GitHub запустился CI. Половина задач из GitHub Actions начала падать со случайной ошибкой:

Your github oauth token for github.com contains invalid characters

Как выяснилось, причина была в setup-php: он пишет Actions-токен GITHUB_TOKEN с префиксом ghs_ в глобальный auth.json composer, а Composer 2.8 принимает только ghp_/gho_/github_pat_. Падение тасков было плавающим — зависело от того, заставил ли rate-limit реально использовать токен или нет, поэтому часть тасков проходила. Даже composer config --unset падал с той же ошибкой. Пришлось делать фикс: удалять auth.json на виртуальном раннере перед проверкой composer validate --no-check-publish, а токен оставлять для установки зависимостей, где он защищает от rate-limit.

7. Roadmap и предложение

Я открыл предложение в организации Scalar:

Discussion #9920 — «Proposal: official Symfony integration (scalar/symfony)»
github.com/scalar/scalar/discussions/9920

Суть: принять бандл в org scalar как scalar/symfony, повторив ровно путь scalar/laravel — перенос или форк, добавить трекинг _integration: symfony, занести Symfony в официальные интеграции. Бандл опубликован, протестирован, работает в проде — усилия на принятие почти нулевые, а разработчики Symfony получают то, что у Laravel уже есть.

Обсуждение набрало 38 голосов. 18 сентября 2026 года мейнтейнеры Scalar перенесли бандл в организацию: репозиторий scalar/symfony, пакет scalar/symfony на Packagist, авторство сохранено в composer.json. От публикации предложения до принятия прошёл месяц.

8. Как начать использовать?

  1. composer require scalar/symfony
  2. Укажите scalar_symfony.url на любой OpenAPI-документ (или сгенерируйте через NelmioApiDocBundle / swagger-php)
  3. Импортируйте маршруты, откройте /scalar

В README описано всё подробно.

9. Итог

У Scalar не было официальной интеграции с Symfony, была потребность — и был сделан бандл.

  • Официальная интеграция scalar/symfony (бандл начинался как alex-frolov/scalar-symfony v0.1.0);
  • Поддерживаемые версии: PHP >= 8.2, Symfony 6.4 / 7.2+ / 8.x;
  • Использовано на платформе Tender Platform.

Код: github.com/scalar/symfony, MIT. История принятия: discussion #9920.

Следующий шаг

Бандл поставить одной командой: composer require scalar/symfony (MIT, исходники). На проекте с уже настроенным NelmioApiDocBundle документация поднимается без дополнительной конфигурации.

Если что-то не заработало на вашей версии Symfony — это полезнее любого отзыва: заведите issue в репозитории Scalar.

Обсудить вашу задачу

Готовите API на Symfony и хотите современную документацию? Помогу настроить Scalar API Reference и NelmioApiDocBundle, спроектировать OpenAPI-спеку и встроить проверки документации в CI. Отвечаю в течение 24–48 часов.