Migracja

Ten przewodnik przenosi istniejącą aplikację PHP na OxPHP. Zacznij od sekcji dla tego, czego używasz dziś — nginx + PHP-FPM albo FrankenPHP i RoadRunnera — a zanim zdecydujesz się na przejście, przeczytaj Czego nie da się przenieść.

Z nginx + PHP-FPM

Większość aplikacji przechodzi bez zmian w kodzie. Tryb domyślny wykonuje każde żądanie od czystego stanu PHP — to ten sam cykl życia, który daje PHP-FPM — więc praca sprowadza się do przeniesienia konfiguracji. Przykładowe wdrożenia to kompletne przepisy dla Laravela, Symfony, WordPressa, Drupala, Magento i innych.

1. Wybierz tryb routingu pasujący do Twojego try_files

nginx OxPHP
try_files $uri /index.php?$query_string (Laravel, Symfony, Yii) ENTRY_FILE=index.php (Framework)
try_files $uri $uri/ /index.php z kilkoma punktami wejścia PHP (WordPress, OpenCart) ENTRY_FILE nieustawione (Tradycyjny)
try_files $uri /index.html dla aplikacji jednostronicowej ENTRY_FILE=index.html (SPA)

2. Zbuduj obraz

Zacznij od dwuwierszowego pliku Dockerfile z Szybkiego startu. Rozszerzenia muszą być zbudowane dla wielowątkowego (thread-safe) PHP, w etapie php:*-zts-alpine z tą samą wersją minor PHP co obraz OxPHP; zobacz Instalowanie rozszerzeń PHP na produkcji.

Warning

Sprawdź, czy każde używane przez Ciebie rozszerzenie PECL obsługuje ZTS. OxPHP uruchamia wyłącznie wielowątkowe PHP, więc rozszerzenia bez obsługi ZTS nie da się załadować.

3. Przenieś konfigurację

Obraz nie ładuje żadnego php.ini. Trzymaj swoje ustawienia PHP (memory_limit, upload_max_filesize, OPcache, …) we własnym pliku w dowolnym miejscu projektu, na przykład docker/php.ini, i skopiuj go do katalogu konfiguracji PHP w obrazie. Ten katalog to ścieżka wewnątrz kontenera, a nie w Twoim projekcie:

Dockerfile
FROM ghcr.io/oxphp/oxphp:0.13.0 COPY docker/php.ini /usr/local/etc/php/conf.d/zz-app.ini COPY --chown=www-data:www-data . /var/www/html/public

PHP odczytuje każdy plik .ini z tego katalogu w kolejności alfabetycznej, a wygrywa ostatnia wartość, więc prefiks zz- sprawia, że Twój plik zostanie zastosowany po własnych plikach obrazu. Zamontowanie pliku jako wolumenu też działa; zobacz Konfiguracja PHP.

Reszta mapuje się na zmienne środowiskowe:

nginx / PHP-FPM OxPHP
listen 443 ssl, ssl_certificate TLS_CERT, TLS_KEY, TLS_MIN_VERSION; zobacz TLS
pm = static / pm = dynamic, pm.max_children PHP_WORKERS=N / PHP_WORKERS=MIN:MAX
php_admin_value[...], php.ini własny plik .ini skopiowany do /usr/local/etc/php/conf.d/ w obrazie (zobacz wyżej)
gzip on, brotli on włączone domyślnie, konfigurowane przez COMPRESSION_ENCODINGS; zobacz Kompresja
expires 30d dla plików statycznych STATIC_MAX_AGE (domyślnie 30d)
limit_req RATE_LIMIT, RATE_WINDOW_SECONDS (stałe okno na adres IP); zobacz Ograniczanie liczby żądań
set_real_ip_from, real_ip_header TRUSTED_PROXIES; zobacz Zaufane proxy
error_page 404 /404.html ERROR_PAGES_DIR z plikami {status}.html; zobacz Niestandardowe strony błędów
access_log ACCESS_LOG=all lub error (linie JSON); zobacz Rejestrowanie dostępu
location ~ ^/uploads/.*\.php$ { deny all; } PHP_DENY_PATHS=/uploads/**; zobacz Lista blokowania wykonywania PHP
pm.status_path, ping.path INTERNAL_ADDR z /health i /metrics; zobacz Kontrole stanu
fastcgi_finish_request() oxphp_finish_request(); zobacz Wczesna odpowiedź

4. Sprawdź różnice w zachowaniu

  • max_execution_time liczy czas zegarowy. Pod PHP-FPM czas oczekiwania na bazę danych lub wywołanie HTTP nie jest wliczany; w wielowątkowym PHP jest. Powolny endpoint, który wcześniej nigdy nie dobił do limitu, może zacząć zwracać 504. Zobacz Limity czasu.
  • php_sapi_name() zwraca cli-server. Kod, który rozgałęzia się na podstawie fpm-fcgi, potrzebuje innego sprawdzenia: function_exists('oxphp_request_id').
  • Kilka kluczy $_SERVER się różni. SERVER_ADDR, PATH_TRANSLATED i REDIRECT_STATUS nie są ustawiane, a PHP_AUTH_USER / PHP_AUTH_PW nie są wyodrębniane; zamiast nich odczytuj HTTP_AUTHORIZATION. Zobacz Różnice względem PHP-FPM.
  • Osadzanie w ramkach jest domyślnie ograniczone. Odpowiedzi niosą X-Frame-Options: SAMEORIGIN i dopasowaną politykę frame-ancestors. Ustaw FRAME_OPTIONS=off, jeśli zarządzasz osadzaniem gdzie indziej lub osadzasz aplikację w innym pochodzeniu; zobacz Ochrona przed clickjackingiem.

5. Wdrażaj obok starego stosu

Uruchom OxPHP obok nginx + PHP-FPM, skieruj na niego część ruchu przez istniejący load balancer, używaj /health/readiness do sond i porównaj odsetek błędów oraz opóźnienia w /metrics, zanim przeniesiesz resztę.

6. Tryb worker włącz na końcu

Najpierw przeprowadź migrację w trybie domyślnym. Potem ustaw WORKER_MODE_ENABLED=true, a ENTRY_FILE skieruj na skrypt workera. Stan przechowywany we właściwościach statycznych i długo żyjące połączenia przetrwają teraz między żądaniami, więc przed włączeniem tego trybu przeczytaj Co jest resetowane, a co zachowywane.

Z FrankenPHP lub RoadRunnera

Skrypty workerów zachowują swój kształt: jednorazowa inicjalizacja, a potem obsługa żądań. Zastąp pętlę frankenphp_handle_request() pojedynczym wywołaniem oxphp_worker($handler). OxPHP sam prowadzi pętlę i wywołuje handler dla każdego żądania; wewnątrz niego odczytuj żądanie z superglobalnych lub z oxphp_http_request(). Pętla PSR-7 waitRequest() / respond() z RoadRunnera nie ma odpowiednika: przenieś handler aplikacji do tego samego callbacku. Zobacz oxphp_worker() i Tryb worker.

Porzuć licznik MAX_REQUESTS

Nie ma odpowiednika i nie jest Ci potrzebny: worker nie powinien być restartowany co N żądań, a takie restarty jedynie ukrywają wyciek. Jeśli podejrzewasz wyciek, śledź go:

  • Rysuj wykres oxphp_worker_memory_bytes dla każdego workera i obserwuj, czy wartość stale rośnie.
  • Ustaw WORKER_MAX_MEMORY_MIB jako zabezpieczenie. Worker, który przekroczy limit, jest wymieniany, zanim w kontenerze skończy się pamięć, a każdy taki recykling jest liczony w oxphp_worker_recycles_by_reason_total{reason="max_memory"}.
  • Ten limit mierzy tylko własną stertę PHP. Przy stosach intensywnie korzystających z rozszerzeń porównuj Worker::current()->rss() z własnym progiem i wywołuj Worker::scheduleExit().

Czego nie da się przenieść

  • OxPHP nie obsługuje odwrotnego proxy ani upstreamów: nie ma proxy_pass ani serwera WebSocket. Jeśli nginx obsługuje też inne usługi, zostaw dla tych tras proxy przed OxPHP.
  • Instancja ma jeden port i jeden protokół. Obsługuje zwykły HTTP albo HTTPS, nie oba naraz, więc przekierowanie z portu 80 na HTTPS wymaga proxy lub drugiej instancji. Certyfikat jest wczytywany przy starcie, więc jego odnowienie wymaga restartu; zobacz TLS.
  • HTTP/3 i 103 Early Hints nie są jeszcze dostępne; zobacz plan rozwoju.
  • OxPHP działa wyłącznie na Linuksie i jest dystrybuowany jako obraz Docker. Nie ma pakietów apt ani brew. Obsługiwane są PHP 8.4 i 8.5.

Prompty dla Twojego agenta

Te prompty zakładają agenta z podłączonym serwerem MCP dokumentacji OxPHP — sam przeczyta strony, których potrzebuje.

prompt
Przenieś tę aplikację z nginx i PHP-FPM na OxPHP. Przeczytaj stronę `getting-started/migration` przez serwer MCP dokumentacji OxPHP. Przełóż bloki server nginx, ustawienia puli PHP-FPM i nadpisania php.ini z tego repozytorium na plik Dockerfile i zmienne środowiskowe OxPHP, wybierz tryb routingu na podstawie reguł `try_files` i wypisz każdą różnicę w zachowaniu oraz każdą nieobsługiwaną funkcję, która dotyczy tego kodu, razem z plikiem, na który wpływa.
prompt
Przenieś workera FrankenPHP lub RoadRunnera z tego repozytorium na OxPHP. Przeczytaj stronę `getting-started/migration` przez serwer MCP dokumentacji OxPHP. Przepisz skrypt workera wokół pojedynczego wywołania `oxphp_worker()`, usuń każdy licznik, który restartuje workera co N żądań, i powiedz mi, jaką metrykę i jaki limit pamięci ustawić w jego miejsce.

Co dalej

  • Routing — tryby routingu Tradycyjny, Framework, SPA i Worker w szczegółach
  • Przewodnik po Dockerze — deweloperskie i produkcyjne pliki Dockerfile, konfiguracja Compose, montowanie plików ini PHP
  • Konfiguracja — pełna dokumentacja zmiennych środowiskowych
  • Tryb worker — trwałe workery PHP, które inicjalizują się raz i obsługują wiele żądań
  • Przykładowe wdrożenia — kompletne przepisy dla Laravela, Symfony, WordPressa, Drupala, Magento i innych
Znalazłeś błąd? Zgłoś go →