Релизы

История изменений

Каждое значимое изменение сервера, PHP API и образов — избранные моменты по каждому релизу. Полная, неурезанная история — в CHANGELOG.md.

последняя v0.11.0 · 2026-08-20 · 11 релизов · AGPL-3.0 CHANGELOG.md →
oxphp — releases bash
$ docker pull ghcr.io/oxphp/oxphp:0.11.0
0.11.0: Pulling from oxphp/oxphp
Status: Image is up to date
# opt-in PHP 8.5 preview:
$ docker pull ghcr.io/oxphp/oxphp:php8.5
v0.11.0
2026-08-20
LATEST
⚠ breaking changes
Release notes →
BREAKING

FRAME_OPTIONS по умолчанию SAMEORIGIN — Межсайтовое встраивание остаётся заблокированным, а встраивание в рамках того же источника (предпросмотры в админке, виджеты дашбордов) работает из коробки; развёртывания, полагавшиеся на встроенный DENY, теперь должны задавать его явно.

Режим воркеров: без цикла событий внутри запроса — Запрос режима воркеров теперь является файбером, и Revolt отказывается запускать свой цикл внутри него: Revolt\EventLoop::run() в обработчике выбрасывает исключение. Перенесите его в бутстрап воркера; defer(), delay() и getSuspension() продолжают работать.

Режим воркеров: состояние запроса заканчивается вместе с запросом — ini_set(), set_time_limit() и error_reporting() откатываются к базовым значениям бутстрапа между запросами; filter_input() отвечает только за спрашивающий запрос; дескриптор php://input, удержанный дольше своего запроса, читается как закрытый ресурс, а не как тело другого запроса.

query() и cookie() декодируют свои значения — Код, компенсировавший это через rawurldecode(), теперь декодирует дважды и должен убрать лишний вызов. Cookie, отправленная пустой, читается как пустая строка, а не null, а query() разбирает не более 1000 параметров.

Строгий разбор MAX_CONNECTIONS / QUEUE_CAPACITY — Некорректные значения прерывают запуск вместо молчаливого отката к умолчаниям, а QUEUE_CAPACITY=0 теперь означает авто (воркеры × 128), а не очередь нулевой ёмкости.

ADDED

Бюджет ожидания очереди — Запрос, пришедший в заполненную очередь, теперь ждёт слота до QUEUE_WAIT_TIMEOUT_MS (по умолчанию 1 с) вместо мгновенного 529; один дедлайн, проставленный при прибытии, покрывает ожидание слота и ожидание в очереди. QUEUE_MAX_WAITING и QUEUE_MAX_WAITING_BYTES ограничивают множество ожидающих в запросах и в байтах тел.

RUNTIME_HOOKS=streams — Блокирующие чтения сокетов tcp:// и stream_select() приостанавливают файбер вместо потока воркера, покрывая mysqlnd (PDO_MySQL, mysqli), phpredis и HTTP-обёртки потоков без изменений кода. Соединения, разделяемые конкурентными файберами, занимаются на время обмена, поэтому запросы не могут читать ответы друг друга.

oxphp_admission_refused_total — Запросы, сброшенные до достижения воркера, теперь оставляют след на стороне сервера, а метка reason отделяет четыре причины перегрузки (529) от shutting_down (503) и pool_unavailable (500).

Автоматический захват исключений на корневом спане — Необработанное исключение или фатальная ошибка, отвечающая 5xx, прикрепляет событие exception (type, message, stacktrace, file, line) к корневому спану запроса во всех четырёх режимах обслуживания — без атрибута и без вызова oxphp_apm_error().

Атрибуты спанов базы данных — Спаны PDO и mysqli несут db.statement (литеральные значения обфусцированы в ?), db.operation, db.system, server.address/port, db.name и oxphp.db.slow; mysqli_stmt::prepare присоединяется к перехватываемому набору как функция номер 34.

CHANGED

oxphp_queue_wait_us достигает одной секунды — Четыре новых бакета (от 100 мс до 1 с) дают численную оценку ожиданиям около бюджета по умолчанию вместо сваливания их в +Inf; отклонённые запросы исключены из гистограммы.

Перезапуск воркеров считает то, что ломает воркер — Только фаталы, нехватка памяти и переполнения стека засчитываются в перезапуск по трём последовательным сбоям; необработанные исключения и отменённые запросы нейтральны. Предохранитель теперь срабатывает и на синхронном пути диспетчеризации, где он раньше не срабатывал никогда.

Рекомендация по размерам в config --check — Предупреждает, не завершаясь ошибкой, когда PHP_WORKERS + QUEUE_CAPACITY + QUEUE_MAX_WAITING достигает MAX_CONNECTIONS — точки, за которой перегрузка отвечает ничем вместо 529.

Документация выровнена — Документация репозитория переехала из docs/en/ на уровень docs/; русский и китайский переводы переехали на oxphp.dev, где уже живут французский, польский и японский.

FIXED

Режим воркеров: $_POST и $_FILES заполняются — Оба были пустыми для каждого запроса, который когда-либо обслуживал воркер, поэтому HTML-формы и загрузки файлов молча не работали; JSON-API, читающие php://input, затронуты не были — потому это и оставалось незамеченным.

Межзапросная утечка filter_input() — Собственная копия ввода расширения filter накапливалась всю жизнь воркера, отвечая одному запросу значениями строки запроса, сессионными cookies и полями тела другого клиента; теперь она возвращается в начале каждого запроса.

Всплески таймеров sleep — Более 32 таймеров, истекающих за один опрос планировщика, оставляли лишних спящих припаркованными навсегда; большой всплеск теперь пробуждается партиями по 32.

Закрытие общего соединения — fclose(), $mysqli->close() или помощник переподключения на соединении, на чтении которого стоял другой запрос, возобновляли тот запрос в освобождённую память; теперь он немедленно завершается с 500, называющим произошедшее.

Фаталы больше не стирают разделяемое состояние — Размотка прерванного запроса дважды освобождала include-переменные, молча сбрасывая массивы и объекты, в которых приложения режима воркеров хранят межзапросное состояние.

Темп асинхронного пула — Передача задач больше не задаётся фиксированным таймером простоя в 1 мс: передача через Shared\Channel упала с 0,69 мс до 0,14 мс на элемент, а пробуждения при простое — с 777/с до 92/с.

Приостановленные запросы сохраняют своё время старта — $request->startTime() и oxphp_server_info()[request_time] больше не читают 0 или значение конкурентного запроса после приостановки.

Предупреждения чтения тела достигают правильного запроса — Предупреждения post_max_size и лимитов загрузки теперь срабатывают внутри запроса, приславшего тело, поэтому обработчики ошибок видят его суперглобальные переменные, а обработчик, выбросивший исключение, отвечает 500 вместо закрытия соединения ни с чем.

v0.10.0
2026-07-12
⚠ breaking changes
Release notes →
BREAKING

Сброс привилегий по умолчанию — Запущенный от root при наличии учётной записи www-data, сервер привязывает свои слушающие сокеты и сбрасывает привилегии до www-data перед тем, как начать обслуживать трафик; официальный образ больше не работает от root. Передайте --user=root, чтобы сохранить прежнее поведение.

Fail-closed конфигурация TLS — Наполовину настроенная пара (TLS_CERT без TLS_KEY или наоборот) либо значение сертификата/ключа не в UTF-8 теперь прерывает запуск вместо того, чтобы молча обслуживать обычный HTTP, называя проблемную переменную.

Строгий разбор ASYNC_* / SUPERGLOBALS — Мусор в ASYNC_WORKERS, ASYNC_QUEUE_CAPACITY или ASYNC_MAX_FIBERS теперь приводит к ошибке запуска вместо молчаливого значения по умолчанию, а oxphp run больше не игнорирует некорректный SUPERGLOBALS_ENABLED.

QUERY без Content-Type возвращает 400, а не 415 — Единственный генерируемый сервером 415 исчез; пользовательский ERROR_PAGES_DIR/415.html для этого случая нужно переименовать в 400.html.

Определение конверта экспорта профилировщика — PROFILER_EXPORT_XHGUI=false теперь имеет приоритет даже на URL /run/import, а автоопределение конверта опирается только на канонический путь каждого инструмента (xhgui /run/import, Buggregator /api/profiler/store), а не на нечёткое вхождение подстроки "xhgui".

ADDED

Экспорт профилировщика в Buggregator — HTTP-push профилировщика оборачивает данные xhprof в конверт, который ожидает POST /api/profiler/store в Buggregator; PROFILER_EXPORT_BUGGREGATOR включает это, а tags и app_name задаются через переменные окружения.

Экспорт трассировки OTLP по TLS — https:// OTEL_EXPORTER_OTLP_ENDPOINT теперь экспортируется в зашифрованном виде как по gRPC, так и по http/protobuf, с проверкой по системному хранилищу доверенных сертификатов через rustls; образ среды выполнения должен содержать CA-бандл (официальный содержит).

Полные данные об исключении в спанах APM — Событие исключения в спане теперь несёт exception.message и exception.stacktrace наряду с exception.type, так что New Relic Errors Inbox, Jaeger и Tempo отображают сообщение и стек; ограничивается OTEL_APM_MESSAGE_MAX_BYTES / OTEL_APM_STACKTRACE_MAX_BYTES.

Корректный слив долгоживущих соединений — По SIGTERM сервер отправляет GOAWAY, закрывает простаивающие keep-alive-соединения и оперативно завершает открытые потоки SSE/flush; значение по умолчанию DRAIN_TIMEOUT_SECONDS снижено с 30 → 25, чтобы уложиться в 30-секундный grace-период Kubernetes.

TLS_MIN_VERSION — Минимальная допустимая версия протокола: 1.2 (по умолчанию) или 1.3. Всё остальное — включая чужеродный синтаксис вроде "TLSv1.2" — приводит к жёсткой ошибке запуска, проверяется даже при отключённом TLS и сообщается как tls_min_version в /config.

CHANGED

Дешёвый STATIC_REVALIDATE — stat() не чаще одного раза в 3 секунды на файл; попадание по статике обслуживается из памяти максимум одним системным вызовом, а изменения на диске становятся видны в течение 3 с. По умолчанию остаётся выключенным.

Более понятные ошибки запуска TLS — Путь с опечаткой завершается сообщением "TLS_CERT: cannot read /etc/ssl/cert.pem: No such file or directory" вместо голой ошибки ОС; ошибки разбора PEM и несоответствия ключа снабжаются таким же префиксом.

FIXED

Нацеливание отмены в режиме воркеров — Обрыв со стороны клиента или таймаут запроса мог отменить тот запрос, который был принят воркером последним, вместо того, который действительно отменялся, прерывая посторонний обрабатываемый запрос.

finish_request() против мультиплексированных потоков — Как только любой запрос на воркере вызывал oxphp_finish_request(), каждый другой поток, мультиплексированный на этот воркер, молча терял свой последующий вывод oxphp_stream_flush().

v0.9.0
2026-06-25
⚠ breaking changes
Release notes →
BREAKING

await по таймауту отменяет задачу — Брошенная задача принудительно отменяется: припаркованные задачи возобновляются уже в состоянии отмены, а задачи с нагрузкой на CPU прерываются на границе опкода. Раньше она выполнялась до конца запроса.

ADDED

Вложенный oxphp_async() — Асинхронная задача может порождать собственные задачи и приостанавливаться на await_all / await_any / await_race, не блокируя свой поток-воркер. Ограничение «без вложенного async» снято.

Лимит задач в обработке — ASYNC_MAX_FIBERS (по умолчанию 256) ограничивает число задач в очереди + выполняющихся; отправка сверх лимита немедленно отклоняется с AsyncException, так что fan-out не может привести к взаимоблокировке.

Список разрешений для внутреннего сервера — INTERNAL_ALLOW_IPS: список разрешений в формате CIDR для /metrics и /config; эндпоинты проверки работоспособности остаются доступными для оркестраторов.

Исключения для семплирования профилировщика — PROFILER_EXCLUDE_PATHS удерживает собственный трафик фреймворка (/_profiler, /_wdt) вне статистического семплирования; явные триггеры по-прежнему запускают профилирование.

FIXED

Пробуждение отправителя в Channel — Файбер, ожидающий отправки и припаркованный на заполненном Shared\Channel, пробуждается, когда recv_blocking освобождает слот — sendTimeout больше не срабатывает ложно при насыщении.

Крайние случаи async — Падение при корректном завершении работы; await на закрытом промисе зависал вместо отклонения; await_all оставлял промисы выполняться при раннем выходе.

v0.8.0
2026-06-13
⚠ breaking changes
Release notes →
BREAKING

Контракт маршрутизации воркеров — Статические ресурсы с диска, всё остальное — во входной файл воркера ENTRY_FILE; произвольные .php-файлы, индексы каталогов и запасной вариант с корневым index.php больше не разрешаются на каждый запрос.

Честный CGI PATH_INFO — В режиме Framework PATH_INFO задаётся только для явного запроса /index.php/extra; PHP_SELF сообщает фронт-контроллер. Маршрутизируйте по REQUEST_URI.

ADDED

Range-запросы (RFC 9110) — 206 Partial Content для статических файлов: перемотка видео, возобновляемые загрузки, частичная загрузка PDF; If-Range учитывается со строгими валидаторами.

Лимиты соединений HTTP/2 — Пять переменных окружения ограничивают использование ресурсов на соединение; итоговые значения логируются при запуске.

CHANGED

Строгие ETag для статики — "<size>-<mtime>" заменяет W/"…" — этого требует If-Range; сжатые представления сохраняют слабый тег.

FIXED

Заголовки приложения побеждают — X-Content-Type-Options, X-Frame-Options и CSP, заданные из PHP, больше не перезаписываются — значения сервера являются запасными и вставляются, только если заголовок отсутствует.

Стриминг против сжатия — Ответы, передаваемые через flush(), проходят без сжатия, а не буферизуются целиком слоем brotli до конца скрипта.

SECURITY

Усиление защиты HTTP/2 от DoS — Rapid Reset (CVE-2023-44487), HPACK-бомбы и атаки на зависание окна ограничены; мёртвые соединения высвобождаются через keepalive на PING/PONG.

v0.7.0
2026-06-05
⚠ breaking changes
Release notes →
BREAKING

remote_ip в логах доступа — JSON-поле remote_addr переименовано в remote_ip и содержит IP без порта — как в nginx/Apache/Caddy.

ADDED

CLI в стиле PHP — oxphp run script.php выполняет файл с семантикой CLI и полноценным движком под капотом; скрипты с #!/usr/bin/env oxphp запускаются напрямую; переопределения ini через -d принимаются.

Сброс привилегий через --user — Привязать привилегированные порты от root, затем необратимо сбросить привилегии до непривилегированного пользователя до того, как появится любой поток обработки запросов.

Проброшенный порт учитывается — За TRUSTED_PROXIES, SERVER_PORT учитывает X-Forwarded-Port; REMOTE_PORT заполняется из for= в RFC 7239, когда прокси его предоставляет.

FIXED

Падения декораторов — Два SIGSEGV в пути функциональных декораторов (освобождённый кэш экземпляров; висячий zval при изменении размера) устранены; переполнение вложенности теперь бросает StackOverflowException.

Атрибуты профилировщика учитывают аргументы — #[SlowThreshold(ms:)], #[MemoryThreshold(kb:)] и #[Mark(label:)] больше не схлопываются к общим значениям по умолчанию; события спанов APM доходят до OpenTelemetry.

API объектов загрузки подключён — Request::file() / files() возвращают объекты UploadedFile для любой формы $_FILES вместо null.

v0.6.0
2026-05-28
⚠ breaking changes
Release notes →
BREAKING

Трихотомический API ожидания — Channel и Mutex разбивают каждое ожидание на try* / голый глагол / *Timeout(int $ms), возвращая значения типов SendResult / RecvResult вместо mixed|null.

await_any означает Promise.any — Побеждает первый выполненный; при отклонении всех бросается AggregateAsyncException. Прежнее поведение «первый завершившийся» теперь называется oxphp_async_await_race().

Честные статусы отмены — Таймаут → 504, корректный слив → 503 + Retry-After, отключение клиента → 499 (только в лог). Убийства супервизором и отмены из пользовательского кода сохраняют 500.

Переименования без прослоек — Async\Exception → AsyncException, Shared\Exception → SharedException; Worker убирает префиксы get*; Counter становится минимальным (add/set); Flag повторяет Atomic; Once получает getOrInit()/status().

ADDED

Shared\Atomic + Ordering — Обобщённый int64-атомик: load/store/swap/CAS/fetch* с явными упорядочениями памяти, один в один с std::sync::atomic.

Shared\Registry — Процессно-глобальные хендлы с ключом по имени: каждый воркер сходится на одной и той же записи; фабрика выполняется максимум один раз.

REMOVED

Единый источник таймаута — REQUEST_TIMEOUT_SECONDS и oxphp_request_heartbeat() удалены — max_execution_time и set_time_limit() остаются.

FIXED

connection_aborted() в SSE — Возвращает true после разрыва посреди потока, так что циклы while (!connection_aborted()) завершаются корректно.

PERFORMANCE

Горячий путь Shared\* — Накладные расходы на операцию укладываются в шум criterion относительно чистой атомарной загрузки; ≈4.7× среднегеометрического на 8 потоках против прежней формы.

v0.5.0
2026-05-05
⚠ breaking changes
Release notes →
BREAKING

Строгий разбор булевых переменных окружения — Только on/true/1/yes и off/false/0/no; опечатка вроде ture приводит к отказу запуска вместо молчаливого значения по умолчанию.

ADDED

ENTRY_FILE + WORKER_MODE_ENABLED — Одна переменная выбирает режим маршрутизации по расширению; отдельный переключатель включает постоянные воркеры. INDEX_FILE / WORKER_FILE становятся устаревшими псевдонимами.

Класс OxPHP\Server\Worker — Хендл для интроспекции во время выполнения: id, число запросов, память, RSS — плюс управляемое приложением пересоздание через scheduleExit().

CHANGED

Переименования кэша статики — STATIC_CACHE_TTL → STATIC_MAX_AGE; STATIC_CACHE → STATIC_REVALIDATE с инвертированной полярностью. Значения по умолчанию не изменились.

DEPRECATED

WORKER_MAX_REQUESTS — Разбирается и игнорируется с предупреждением WARN при запуске — переходите на WORKER_MAX_MEMORY_MIB или scheduleExit().

v0.4.0
2026-05-02
Release notes →
ADDED

Образы PHP 8.5 — :php8.5, :php8.5-alpine и теги с фиксацией патча поставляются наряду с 8.4; latest остаётся на 8.4, пока не закроется окно обкатки.

FIXED

Чанкованный стриминг между запросами — Контекст моста, привязанный к запросу, утекал между запросами воркера, теряя чанкованный Transfer-Encoding после первого flush.

Shared\* внутри пользовательских файберов — Channel::recv()/send() внутри Fiber::start() больше не идут по пути приостановки oxphp-fiber и не падают с fiber_await rc=1.

Именованные аргументы для API плагинов — Классы и функции, определённые плагинами, теперь объявляют реальные имена параметров — вызовы с именованными аргументами работают.

Разделяемые хендлы в асинхронных замыканиях — Channel/Map/Pool, захваченные замыканием oxphp_async(), больше не приходят как null в принимающий поток.

v0.3.0
2026-04-22
⚠ breaking changes
Release notes →
BREAKING

Пространство имён Async — Async-классы перемещены в OxPHP\Async\; функции теперь поставляются в plugin-async.

ADDED

Семь примитивов Shared\* — Counter, Flag, Once, Mutex, Channel, Map, Pool — атомарное состояние, разделяемое между воркерами, с наблюдаемостью через реестр и детектором взаимоблокировок.

Профилировщик на каждый запрос — Вывод в xhprof / speedscope / pprof / collapsed; триггеры по cookie, заголовку, query-параметру и статистические; HTTP-push с конвертом xhgui.

Автоинструментирование APM — plugin-apm с SDK трассировки для PHP и захватом ошибок; подсистемы async и APM перенесены в плагины на Rust.

Интеграции для продакшена — TRUSTED_PROXIES (RFC 7239), Kubernetes /readyz + /livez, заголовки безопасности, ограничение частоты запросов на IP — всё настраивается через переменные окружения.

PERFORMANCE

Горячий путь Pool — Неконкурентные acquire/release ≈0.9 мкс в Docker, с привязкой к потоку.

v0.2.0
2026-03-27
Release notes →
ADDED

Мультиплексирование файберов — Конкурентный ввод-вывод в рамках одного потока-воркера; распределённая трассировка с W3C Trace Context и экспортом в OpenTelemetry.

Промисы oxphp_async() — Параллельное выполнение PHP на выделенном пуле потоков.

Объектный API HTTP — OxPHP\Http\Request с ленивыми аксессорами, плюс система декораторов на основе атрибутов.

CHANGED

Порт и противодавление (backpressure) — Порт по умолчанию 8080 → 80 (443 при TLS); ответ противодавления 503 → 529.

FIXED

Корректность CGI — SERVER_PROTOCOL отражает реальную версию HTTP; REQUEST_TIME — это начало запроса; разбор IPv6 в Host; 408 при таймауте запроса.

v0.1.0
2026-03-08
Release notes →
ADDED

Асинхронное ядро HTTP — Сервер на Hyper + Tokio с корректным завершением работы, собственный SAPI oxphp с полными суперглобальными переменными, пул воркеров ZTS с ограниченной очередью и противодавлением 529.

Три режима маршрутизации — Traditional, Framework и SPA; статические файлы с кэшем в памяти, ETag/304, brotli, TLS.

Режим воркеров — Постоянные воркеры через oxphp_worker(), раннее завершение ответа, стриминг SSE, кооперативные таймауты.

Наблюдаемость — /metrics, /health, /config, структурированные JSON-логи доступа, идентификаторы запросов.

Docker-first — Многоэтапная сборка на Alpine; образы amd64/arm64 на GHCR.