Krótko: OPcache to wbudowana pamięć podręczna bajtkodu PHP. Według podręcznika przechowuje prekompilowany bajtkod we wspólnej pamięci, dzięki czemu PHP nie musi ładować i tłumaczyć skryptów przy każdym żądaniu. Wchodzi w skład dystrybucji od PHP 5.5.0. OPcache przyspiesza PHP, ale nie zastępuje aktualizacji: po poprawce PHP wszystkie procesy PHP muszą się zrestartować, a nie tylko pamięć podręczna zostać opróżniona.
Co dokładnie robi OPcache?
Przy każdym żądaniu PHP musi przeczytać skrypt, rozbić go i przetłumaczyć na opcody, czyli polecenia dla maszyny wirtualnej Zend Engine. W CMS z setkami plików to dużo pracy, która ciągle się powtarza.
OPcache zapisuje wynik tego tłumaczenia we wspólnej pamięci (shared memory). Korzystają z niej wszystkie workery usługi PHP-FPM albo wszystkie procesy Apache z mod_php. Jeśli plik się nie zmienia, jego bajtkod jest używany bezpośrednio ponownie.
OPcache ładuje się jako rozszerzenie Zend, a nie jako zwykłe rozszerzenie:
; Linux (ścieżka zależna od pakietu)
zend_extension=opcache.so
; Windows
; zend_extension=php_opcache.dll
Według podręcznika OPcache powinien być ładowany przed Xdebug, jeśli oba są aktywne.
Ważne jest, do kogo należy wspólna pamięć. W PHP-FPM tworzy ją proces główny, a korzystają z niej wszystkie workery wszystkich pul tej usługi. W mod_php należy do głównego procesu Apache. Restart usługi opróżnia więc pamięć podręczną całkowicie, natomiast pojedynczy nowy worker nie. W wierszu poleceń nie ma wspólnej pamięci między wywołaniami, ponieważ każde wywołanie to odrębny proces; tam OPcache jest domyślnie wyłączony.
Dla osób podejmujących decyzje wystarczy reguła kciuka: OPcache jest na serwerach produkcyjnych prawie zawsze sensowny, ale wymaga jasnej procedury wdrożeń i aktualizacji.
Które ustawienia OPcache mają znaczenie?
Poniższe wartości pochodzą ze strony konfiguracji w podręczniku PHP. Kolumna „Zmienialna” pokazuje, gdzie ustawienie wolno ustawiać: INI_SYSTEM tylko w php.ini lub konfiguracji serwera, INI_ALL wszędzie.
| Dyrektywa | Domyślnie | Zmienialna | Znaczenie |
|---|---|---|---|
opcache.enable |
1 | INI_ALL | pamięć podręczna włączona lub wyłączona, w czasie działania można ją tylko wyłączyć |
opcache.enable_cli |
0 | INI_SYSTEM | pamięć podręczna dla wiersza poleceń |
opcache.memory_consumption |
128 | INI_SYSTEM | rozmiar wspólnej pamięci w MB |
opcache.interned_strings_buffer |
8 | INI_SYSTEM | pamięć na wspólne ciągi znaków w MB |
opcache.max_accelerated_files |
10000 | INI_SYSTEM | maksymalna liczba buforowanych skryptów |
opcache.validate_timestamps |
1 | INI_ALL | sprawdza, czy pliki zostały zmienione |
opcache.revalidate_freq |
2 | INI_ALL | odstęp sprawdzania w sekundach, 0 = przy każdym żądaniu |
opcache.restrict_api |
puste | INI_SYSTEM | dopuszcza funkcje API tylko z jednej ścieżki |
opcache.validate_permission |
0 | INI_SYSTEM | sprawdza uprawnienia plików względem bieżącego użytkownika |
opcache.validate_root |
0 | INI_SYSTEM | zapobiega kolizjom nazw przy chroot |
opcache.file_cache |
puste | INI_SYSTEM | druga warstwa pamięci podręcznej na dysku |
Typowa konfiguracja produkcyjna mogłaby wyglądać tak. Proszę wcześniej sprawdzić wartości na swojej aplikacji:
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=20000
opcache.validate_timestamps=1
opcache.revalidate_freq=60
opcache.restrict_api=/var/www/admin-tools
Kto ustawi opcache.validate_timestamps=0, zyskuje nieco wydajności. Wtedy jednak OPcache w ogóle nie rozpoznaje zmienionych plików i każde wdrożenie wymaga resetu pamięci podręcznej albo restartu.
Obserwowanie pamięci podręcznej i celowe opróżnianie
OPcache ma własne funkcje, w tym opcache_get_status(), opcache_get_configuration(), opcache_invalidate() i opcache_reset(). Krótki, tylko odczytowy wgląd w stopień wypełnienia:
<?php
$status = opcache_get_status(false);
if ($status === false) {
exit("OPcache ist nicht aktiv.\n");
}
printf("Gecachte Skripte: %d\n", $status['opcache_statistics']['num_cached_scripts']);
printf("Freier Speicher: %.1f MB\n", $status['memory_usage']['free_memory'] / 1048576);
Na serwerach z wieloma stronami nie każda aplikacja powinna móc opróżniać lub odczytywać pamięć podręczną wszystkich pozostałych. Do tego służy opcache.restrict_api: funkcje API mogą wywoływać tylko skrypty znajdujące się poniżej podanej ścieżki.
JIT: kompilator kodu maszynowego w OPcache
Od PHP 8.0 OPcache zawiera kompilator JIT, który tłumaczy opcody na kod maszynowy. Według RFC dotyczącego JIT jest to niemal samodzielna część OPcache, która zapisuje wytworzony kod w osobnym obszarze wspólnej pamięci. Steruje się nim przez opcache.jit i opcache.jit_buffer_size. Od PHP 8.4.0 opcache.jit jest domyślnie ustawione na disable. Kiedy JIT się opłaca, wyjaśnia artykuł JIT i wydajność w PHP 8.
OPcache i bezpieczeństwo: co obowiązuje po PHP2Shell
OPcache nie jest narzędziem bezpieczeństwa, ale ma skutki dla bezpieczeństwa. Trzy punkty są ważne po PHP2Shell:
- Reset pamięci podręcznej nie jest aktualizacją.
opcache_reset()opróżnia tylko buforowane skrypty. Interpreter z lukami CVE-2026-109618 i CVE-2026-109619 pozostaje w pamięci, dopóki wszystkie procesy nie zostaną zrestartowane. Jak to zrobić, opisujemy na stronie Restart PHP-FPM i wszystkich workerów. - Wstrzyknięty kod może przetrwać w pamięci podręcznej. Jeśli plik został zmanipulowany, a później wymieniony, przy wyłączonym sprawdzaniu znaczników czasu może być nadal wykonywana stara wersja. Po oczyszczeniu należy więc wykonać restart.
- Wspólna pamięć wymaga rozdzielenia. Na serwerach z wieloma klientami pomagają osobne pule PHP-FPM dla każdej strony oraz
opcache.validate_permissioniopcache.restrict_api. Katalog dlaopcache.file_cachemoże być zapisywalny tylko dla użytkownika PHP.
Proszę po każdej aktualizacji sprawdzić, czy czas startu procesów PHP przypada po aktualizacji. W pełni wypełniony OPcache z wysokim współczynnikiem trafień nie jest dowodem, że działa nowy interpreter.
Jak konkretnie pomaga PHP SOS?
- Sprawdzenie konfiguracji: Odczytujemy aktywną konfigurację OPcache dla każdego trybu pracy i porównujemy ją z Państwa aplikacją.
- Zabezpieczenie wdrożeń: Ustanawiamy stałą procedurę resetu pamięci podręcznej i restartu, aby nie działała dalej stara wersja kodu.
- Rozdzielenie klientów: W ramach utwardzania serwera ustawiamy oddzielne pule,
restrict_apii odpowiednie uprawnienia. - Kontrola po aktualizacjach: Na podstawie czasów startu procesów dowodzimy, że zaktualizowany interpreter rzeczywiście jest aktywny.
Częste pytania
Czy OPcache jest domyślnie aktywny?
OPcache jest dostarczany z PHP od wersji 5.5.0 i według podręcznika działa domyślnie z opcache.enable=1, o ile rozszerzenie jest załadowane. To, czy jest załadowane, zależy od Państwa pakietu lub dostawcy hostingu. W wierszu poleceń OPcache jest natomiast domyślnie wyłączony (opcache.enable_cli=0).
Dlaczego po wdrożeniu nadal widzę stary kod?
Prawdopodobnie OPcache sprawdza zmiany plików tylko w odstępach albo wcale. Z opcache.revalidate_freq czeka domyślnie dwie sekundy, z opcache.validate_timestamps=0 nigdy. Wtedy pomaga tylko reset pamięci podręcznej albo restart PHP-FPM lub Apache.
Czy opcache_reset() wystarcza po aktualizacji PHP?
Nie. opcache_reset() opróżnia tylko pamięć podręczną z przetłumaczonymi skryptami. Aktualizacja PHP wymienia sam interpreter, a ten jest załadowany w każdym działającym procesie. W tym celu trzeba zrestartować wszystkie procesy PHP.
Czy JIT w PHP 8.4 jest włączony?
Według podręcznika opcache.jit jest od PHP 8.4.0 domyślnie ustawione na disable, a opcache.jit_buffer_size wynosi 64M. Wcześniej było odwrotnie: jit na tracing, bufor na 0. W obu przypadkach JIT bez świadomej konfiguracji nie jest aktywny.