Changelog
Każda istotna zmiana w serwerze, API PHP i obrazach — wyselekcjonowane najważniejsze punkty każdego wydania. Pełna, nieskrócona historia znajduje się w CHANGELOG.md.
Automatyczne przechwytywanie nieobsłużonych wyjątków w spanie głównym — Żądanie, które kończy się nieprzechwyconym wyjątkiem lub błędem krytycznym i zwraca 5xx, dołącza teraz do głównego spanu SERVER zdarzenie wyjątku OpenTelemetry (type, message, stacktrace, file, line) bez atrybutu #[OxPHP\Apm\Trace] i bez wywołania oxphp_apm_error(), więc 500 sam siebie opisuje w backendach błędów. Aplikacje frameworkowe, które renderują własną stronę błędu, nadal zapisują je jawnie.
FRAME_OPTIONS domyślnie ma teraz wartość SAMEORIGIN, a nie DENY — Osadzanie w ramkach w obrębie tego samego źródła (podglądy panelu administracyjnego, widżety dashboardu, komponenty płatności z tego samego źródła) działa od razu, podczas gdy clickjacking między źródłami pozostaje zablokowany, zgodnie z nginx i Rails; ustaw FRAME_OPTIONS=DENY, aby zabronić wszelkiego osadzania w ramkach.
Domyślne zrzucanie uprawnień — Uruchomiony jako root, gdy istnieje konto www-data, serwer wiąże swoje gniazda nasłuchujące i zrzuca uprawnienia do www-data przed rozpoczęciem obsługi jakiegokolwiek ruchu; oficjalny obraz nie działa już jako root. Przekaż --user=root, aby zachować dawne zachowanie.
Konfiguracja TLS w trybie fail-closed — Połowicznie skonfigurowana para (TLS_CERT bez TLS_KEY lub odwrotnie) albo wartość certyfikatu/klucza spoza UTF-8 przerywa teraz uruchamianie, zamiast po cichu obsługiwać zwykły HTTP, wskazując z nazwy problematyczną zmienną.
Ścisłe parsowanie ASYNC_* / SUPERGLOBALS — Śmieci w ASYNC_WORKERS, ASYNC_QUEUE_CAPACITY lub ASYNC_MAX_FIBERS to teraz błąd uruchamiania zamiast cichej wartości domyślnej, a oxphp run nie ignoruje już nieprawidłowego SUPERGLOBALS_ENABLED.
QUERY bez Content-Type zwraca 400, a nie 415 — Jedyny generowany przez serwer 415 zniknął; własny plik ERROR_PAGES_DIR/415.html dla tego przypadku trzeba zmienić na 400.html.
Ustalanie koperty eksportu profilera — PROFILER_EXPORT_XHGUI=false ma teraz priorytet nawet dla adresu URL /run/import, a automatyczne wykrywanie koperty opiera się wyłącznie na kanonicznej ścieżce każdego narzędzia (xhgui /run/import, Buggregator /api/profiler/store), a nie na nieprecyzyjnym dopasowaniu podłańcucha "xhgui".
Eksport profilera do Buggregatora — HTTP push profilera opakowuje dane xhprof w kopertę, której oczekuje POST /api/profiler/store Buggregatora; PROFILER_EXPORT_BUGGREGATOR to włącza, a tags i app_name ustawia się przez zmienne środowiskowe.
Eksport śladów OTLP przez TLS — Endpoint OTEL_EXPORTER_OTLP_ENDPOINT z https:// jest teraz eksportowany w postaci zaszyfrowanej zarówno przez gRPC, jak i http/protobuf, z weryfikacją względem systemowego magazynu zaufanych certyfikatów za pomocą rustls; obraz środowiska uruchomieniowego musi zawierać pakiet certyfikatów CA (oficjalny zawiera).
Pełne dane o wyjątku w spanach APM — Zdarzenie wyjątku w spanie niesie teraz exception.message i exception.stacktrace obok exception.type, więc New Relic Errors Inbox, Jaeger i Tempo renderują komunikat i stos; ograniczane przez OTEL_APM_MESSAGE_MAX_BYTES / OTEL_APM_STACKTRACE_MAX_BYTES.
Łagodne opróżnianie długotrwałych połączeń — Po SIGTERM serwer wysyła GOAWAY, zamyka bezczynne połączenia keep-alive i niezwłocznie kończy otwarte strumienie SSE/flush; domyślna wartość DRAIN_TIMEOUT_SECONDS obniżona z 30 → 25, aby zmieścić się w 30-sekundowym okresie karencji Kubernetesa.
TLS_MIN_VERSION — Minimalna akceptowana wersja protokołu: 1.2 (domyślnie) lub 1.3. Wszystko inne — w tym obca składnia w rodzaju "TLSv1.2" — to twardy błąd uruchamiania, walidowany nawet przy wyłączonym TLS i raportowany jako tls_min_version w /config.
Tani STATIC_REVALIDATE — stat() najwyżej raz na 3 sekundy na plik; trafienie w statykę jest obsługiwane z pamięci najwyżej jednym wywołaniem systemowym, a zmiany na dysku stają się widoczne w ciągu 3 s. Domyślnie pozostaje wyłączone.
Czytelniejsze błędy uruchamiania TLS — Ścieżka z literówką kończy się komunikatem "TLS_CERT: cannot read /etc/ssl/cert.pem: No such file or directory" zamiast surowego błędu systemu operacyjnego; błędy parsowania PEM i niedopasowania klucza otrzymują taki sam prefiks.
Celowanie anulowania w trybie worker — Przerwanie po stronie klienta lub przekroczenie limitu czasu żądania mogło anulować to żądanie, które worker przyjął jako ostatnie, zamiast tego, które faktycznie było anulowane, przerywając niezwiązane, przetwarzane właśnie żądanie.
finish_request() a strumienie multipleksowane — Gdy tylko jakiekolwiek żądanie na workerze wywołało oxphp_finish_request(), każdy inny strumień multipleksowany na tego workera po cichu porzucał swoje kolejne dane z oxphp_stream_flush().
await po przekroczeniu limitu czasu anuluje zadanie — Porzucone zadanie jest anulowane przymusowo: zaparkowane zadania wznawiają się już w stanie anulowania, a te obciążające CPU są przerywane na granicy opkodu. Wcześniej działało do końca żądania.
Zagnieżdżony oxphp_async() — Zadanie asynchroniczne może rozdzielać własne zadania i zawieszać się na await_all / await_any / await_race bez blokowania swojego wątku worker. Ograniczenie „bez zagnieżdżonego async” zniknęło.
Limit zadań w toku — ASYNC_MAX_FIBERS (domyślnie 256) ogranicza liczbę zadań w kolejce + wykonywanych; wysłanie ponad limit jest natychmiast odrzucane z AsyncException, więc fan-out nie może się zakleszczyć.
Lista dozwolonych dla serwera wewnętrznego — INTERNAL_ALLOW_IPS: lista dozwolonych w formacie CIDR dla /metrics i /config; endpointy kontroli stanu pozostają osiągalne dla orkiestratorów.
Wykluczenia próbkowania profilera — PROFILER_EXCLUDE_PATHS utrzymuje własny ruch frameworka (/_profiler, /_wdt) poza próbkowaniem statystycznym; jawne wyzwalacze nadal uruchamiają profilowanie.
Wybudzanie nadawcy w Channel — Fiber czekający na wysłanie, zaparkowany na pełnym Shared\Channel, wybudza się, gdy recv_blocking zwolni slot — sendTimeout nie wyzwala się już fałszywie przy nasyceniu.
Przypadki brzegowe async — Awaria podczas łagodnego zamknięcia; await na zamkniętym promisie zawieszał się zamiast odrzucać; await_all pozostawiał promisy działające przy wczesnym przerwaniu.
Kontrakt routingu workerów — Zasoby statyczne z dysku, wszystko inne do pliku wejściowego workera ENTRY_FILE — dowolne pliki .php, indeksy katalogów i awaryjny root index.php nie są już rozwiązywane per żądanie.
Uczciwe CGI PATH_INFO — W trybie Framework PATH_INFO jest ustawiane tylko dla jawnego żądania /index.php/extra; PHP_SELF podaje front controller. Routing opieraj na REQUEST_URI.
Żądania Range (RFC 9110) — 206 Partial Content dla plików statycznych: przewijanie wideo, wznawiane pobierania, częściowe ładowanie PDF; If-Range uwzględniane z silnymi walidatorami.
Limity połączeń HTTP/2 — Pięć zmiennych środowiskowych ogranicza zużycie zasobów na połączenie; ustalone wartości są logowane przy uruchomieniu.
Silne ETagi dla statyki — "<size>-<mtime>" zastępuje W/"…" — wymaga tego If-Range; reprezentacje skompresowane zachowują słaby tag.
Nagłówki aplikacji wygrywają — X-Content-Type-Options, X-Frame-Options i CSP ustawione z PHP nie są już nadpisywane — wartości serwera są używane awaryjnie, tylko gdy nagłówek jest nieobecny.
Strumieniowanie a kompresja — Odpowiedzi strumieniowane przez flush() przechodzą bez kompresji, zamiast być w całości buforowane przez warstwę brotli do końca skryptu.
Utwardzenie HTTP/2 przeciw DoS — Rapid Reset (CVE-2023-44487), bomby HPACK i ataki zawieszające okno są ograniczone; martwe połączenia odzyskiwane przez keepalive PING/PONG.
remote_ip w logach dostępu — Pole JSON remote_addr zostało przemianowane na remote_ip i zawiera adres IP bez portu — zgodnie z nginx/Apache/Caddy.
CLI w stylu PHP — oxphp run script.php wykonuje plik z semantyką CLI, mając pod spodem pełny silnik; skrypty z #!/usr/bin/env oxphp uruchamiają się bezpośrednio; przyjmowane są nadpisania ini przez -d.
Zrzucanie uprawnień przez --user — Zwiąż uprzywilejowane porty jako root, a następnie nieodwracalnie zrzuć uprawnienia do nieuprzywilejowanego użytkownika, zanim powstanie jakikolwiek wątek obsługujący żądania.
Przekazany port uwzględniany — Za TRUSTED_PROXIES SERVER_PORT uwzględnia X-Forwarded-Port; REMOTE_PORT jest wypełniane z for= z RFC 7239, gdy proxy je dostarcza.
Awarie dekoratorów — Dwa SIGSEGV w ścieżce dekoratora funkcji (zwolniony cache instancji; wiszący zval przy zmianie rozmiaru) zniknęły; przepełnienie zagnieżdżenia rzuca teraz StackOverflowException.
Atrybuty profilera uwzględniają argumenty — #[SlowThreshold(ms:)], #[MemoryThreshold(kb:)] i #[Mark(label:)] nie sprowadzają się już do wspólnych wartości domyślnych; zdarzenia spanów APM docierają do OpenTelemetry.
Podłączone API obiektów przesyłania — Request::file() / files() zwracają obiekty UploadedFile dla każdej postaci $_FILES zamiast null.
Trychotomiczne API oczekiwania — Channel i Mutex dzielą każde oczekiwanie na try* / goły czasownik / *Timeout(int $ms), zwracając typowane wartościami SendResult / RecvResult zamiast mixed|null.
await_any oznacza Promise.any — Wygrywa pierwszy spełniony; odrzucenie wszystkich rzuca AggregateAsyncException. Dawne zachowanie „pierwszy rozstrzygnięty” to teraz oxphp_async_await_race().
Uczciwe statusy anulowania — Limit czasu → 504, łagodne opróżnianie → 503 + Retry-After, rozłączenie klienta → 499 (tylko log). Zabicia przez nadzorcę i anulowania z kodu użytkownika zachowują 500.
Zmiany nazw bez warstw pośrednich — Async\Exception → AsyncException, Shared\Exception → SharedException; Worker porzuca prefiksy get*; Counter staje się minimalny (add/set); Flag odwzorowuje Atomic; Once zyskuje getOrInit()/status().
Shared\Atomic + Ordering — Ogólny atomik int64: load/store/swap/CAS/fetch* z jawnymi porządkami pamięci, jeden do jednego ze std::sync::atomic.
Shared\Registry — Globalne dla procesu uchwyty kluczowane nazwą: każdy worker zbiega się na tym samym wpisie; fabryka wykonuje się najwyżej raz.
Jedno źródło limitu czasu — REQUEST_TIMEOUT_SECONDS i oxphp_request_heartbeat() zniknęły — max_execution_time i set_time_limit() pozostają.
connection_aborted() w SSE — Zwraca true po rozłączeniu w środku strumienia, więc pętle while (!connection_aborted()) kończą się czysto.
Gorąca ścieżka Shared\* — Narzut na operację mieści się w szumie criterion względem surowego atomowego odczytu; ≈4.7× średniej geometrycznej przy 8 wątkach względem poprzedniej postaci.
Ścisłe parsowanie logicznych zmiennych środowiskowych — Tylko on/true/1/yes oraz off/false/0/no; literówka w rodzaju ture powoduje odmowę uruchomienia zamiast cichego przyjęcia wartości domyślnej.
ENTRY_FILE + WORKER_MODE_ENABLED — Jedna zmienna wybiera tryb routingu na podstawie rozszerzenia; osobny przełącznik włącza trwałe workery. INDEX_FILE / WORKER_FILE stają się przestarzałymi aliasami.
Klasa OxPHP\Server\Worker — Uchwyt do introspekcji w czasie działania: id, liczba żądań, pamięć, RSS — plus sterowany przez aplikację recykling przez scheduleExit().
Zmiany nazw cache statyki — STATIC_CACHE_TTL → STATIC_MAX_AGE; STATIC_CACHE → STATIC_REVALIDATE z odwróconą polaryzacją. Wartości domyślne bez zmian.
WORKER_MAX_REQUESTS — Parsowane i ignorowane z ostrzeżeniem WARN przy uruchomieniu — przejdź na WORKER_MAX_MEMORY_MIB lub scheduleExit().
Obrazy PHP 8.5 — :php8.5, :php8.5-alpine oraz tagi przypięte do wersji łatki są dostarczane obok 8.4; latest pozostaje na 8.4, dopóki nie zamknie się okno wygrzewania.
Chunkowane strumieniowanie między żądaniami — Kontekst mostka przypisany do żądania przeciekał między żądaniami workera, gubiąc chunkowane Transfer-Encoding po pierwszym flush.
Shared\* wewnątrz Fiberów użytkownika — Channel::recv()/send() wewnątrz Fiber::start() nie idą już ścieżką zawieszenia oxphp-fiber i nie padają z fiber_await rc=1.
Argumenty nazwane dla API wtyczek — Klasy i funkcje zdefiniowane przez wtyczki ogłaszają teraz prawdziwe nazwy parametrów — wywołania z argumentami nazwanymi działają.
Współdzielone uchwyty w domknięciach async — Channel/Map/Pool przechwycone przez domknięcie oxphp_async() nie przychodzą już jako null w wątku odbierającym.
Przestrzeń nazw Async — Klasy Async przeniesione do OxPHP\Async\; funkcje są teraz dostarczane w plugin-async.
Siedem prymitywów Shared\* — Counter, Flag, Once, Mutex, Channel, Map, Pool — stan atomowy współdzielony między workerami, z obserwowalnością przez rejestr i detektorem zakleszczeń.
Profiler na żądanie — Wyjścia xhprof / speedscope / pprof / collapsed; wyzwalacze przez cookie, nagłówek, query oraz statystyczne; HTTP push z kopertą xhgui.
Automatyczne instrumentowanie APM — plugin-apm z SDK do śledzenia dla PHP i przechwytywaniem błędów; podsystemy async i APM przeniesione do wtyczek w Rust.
Integracje produkcyjne — TRUSTED_PROXIES (RFC 7239), Kubernetes /readyz + /livez, nagłówki bezpieczeństwa, ograniczanie liczby żądań na IP — wszystko konfigurowane przez zmienne środowiskowe.
Gorąca ścieżka Pool — Bezkonfliktowe acquire/release ≈0.9 µs w Dockerze, z powinowactwem per wątek.
Multipleksowanie Fiberów — Współbieżne I/O w obrębie jednego wątku workera; śledzenie rozproszone z W3C Trace Context i eksportem do OpenTelemetry.
Promisy oxphp_async() — Równoległe wykonanie PHP na dedykowanej puli wątków.
Obiektowe API HTTP — OxPHP\Http\Request z leniwymi akcesorami, plus system dekoratorów oparty na atrybutach.
Port i przeciwciśnienie — Domyślny port 8080 → 80 (443 z TLS); odpowiedź przeciwciśnienia 503 → 529.
Poprawność CGI — SERVER_PROTOCOL odzwierciedla rzeczywistą wersję HTTP; REQUEST_TIME to początek żądania; parsowanie IPv6 w Host; 408 przy przekroczeniu limitu czasu żądania.
Asynchroniczne jądro HTTP — Serwer na Hyper + Tokio z łagodnym zamknięciem, własny SAPI oxphp z pełnymi superglobalnymi, pula workerów ZTS z ograniczoną kolejką i przeciwciśnieniem 529.
Trzy tryby routingu — Traditional, Framework i SPA; pliki statyczne z cache w pamięci, ETag/304, brotli, TLS.
Tryb worker — Trwałe workery przez oxphp_worker(), wczesne zakończenie odpowiedzi, strumieniowanie SSE, kooperatywne limity czasu.
Obserwowalność — /metrics, /health, /config, ustrukturyzowane logi dostępu JSON, ID żądań.
Docker-first — Wieloetapowa kompilacja na Alpine; obrazy amd64/arm64 na GHCR.