Preloading

Начиная с PHP 7.4.0 можно настроить предзагрузку скриптов в opcache в момент старта PHP. Любые функции, классы, интерфейсы или трейты (но не константы) в этих файлах будут глобально доступны для всех запросов без необходимости их явной загрузки. Такая предзагрузка позволяет добиться большего удобства и производительности (потому что код всегда доступен) за счёт использования большего количества памяти. Также, при внесении изменений в предзагруженные скрипты, чтобы эти изменения стали доступны, придётся перезагрузить PHP. Из этого следует, что предзагрузку имеет смысл использовать только в производственном окружении, но не в окружении разработки.

Обратите внимание, что баланс повышения производительности и потребления памяти сильно зависит от приложения. «Предзагрузка всего на свете» может быть простейшей стратегией, но совсем не обязательно лучшей. Также, предзагрузка будет работать только в случае, когда PHP работает в режиме обслуживания запросов без перезагрузки. Таким образом, хоть предзагрузку и можно использовать в режиме CLI с включённым opcache, но в большинстве случаев бессмысленно. Исключением является использование предзагрузки с библиотеками FFI.

Замечание:

Предзагрузка не поддерживается в Windows.

Настройка предзагрузки состоит из двух этапов и требует включённого opcache. Для начала, настройте opcache.preload в php.ini:

opcache.preload=preload.php

Файл preload.php — это обязательный файл, который будет запущен один раз при старте сервера (PHP-FPM, mod_php, etc.) и который загрузит код в постоянную память. В серверах, которые запускаются от имени root перед переключением на непривилегированного пользователя системы или если PHP запускается от имени root (не рекомендуется), значение opcache.preload_user может указывать системного пользователя для запуска предварительной загрузки. Запуск предварительной загрузки от имени root по умолчанию запрещён. Установите opcache.preload_user=root, чтобы явно разрешить это.

В скрипте preload.php любой файл, указанный в выражениях include, include_once, require, require_once или opcache_compile_file(), будет загружен в постоянную память. В следующем примере будут загружены все файлы .php в директории src, если они не содержат Test в имени.

<?php
$directory
= new RecursiveDirectoryIterator(__DIR__ . '/src');
$fullTree = new RecursiveIteratorIterator($directory);
$phpFiles = new RegexIterator($fullTree, '/.+((?<!Test)+\.php$)/i', RecursiveRegexIterator::GET_MATCH);

foreach (
$phpFiles as $key => $file) {
require_once
$file[0];
}
?>

И include и opcache_compile_file() будут работать, но при этом будут немного по разному обработаны.

  • Выражение include запустит код из файла, а opcache_compile_file() нет. Это повлияет только на условные декларации (функции объявленные в блоках if).
  • Из за того, что include запустит код, вложенные include также будут обработаны и предзагружены.
  • Функция opcache_compile_file() может загружать файлы в любом порядке. То есть, если файл a.php определяет класс A и b.php определяет класс B, который является наследником A, то функция opcache_compile_file() может загрузить эти два файла в любом порядке. При использовании выражения include, с другой стороны, файл a.php должен быть загружен первым.
  • В любом случае, если какой-то скрипт в последствии запросит включение уже предзагруженного скрипта, то он будет выполнен, но сущности пересоздаваться не будут. Использование выражения include_once не предотвратит повторное включение файла. Может потребоваться загрузить файл снова, чтобы включить в него определённые глобальные константы, поскольку они не обрабатываются предварительной загрузкой.
Какой подход использовать — зависит от желаемого поведения. Для кода, который использует автозагрузчик, подход с вызовом функции opcache_compile_file() даст больше гибкости. С кодом, который будет загружаться вручную, вариант с выражением include может быть более надёжным.

add a note add a note

User Contributed Notes 2 notes

up
2
postmaster at greg0ire dot fr
2 years ago
There are caveats when enabling preloading, one of them being that it should be enabled via a php.ini file. Enabling it with a php-fpm pool configuration won't work, for instance, since preloading is global and not per-pool. To make sure that you successfully enabled preloading, you should check for a preload_statistics key in the output of opcache_get_status(). There should already be an opcache_statistics key, but that's something else entirely.
up
2
postmaster at greg0ire dot fr
2 years ago
PHP 8.1 comes with an inheritance cache that partially overlaps with what the preloading already does. If you enabled preloading on lower versions then migrated to PHP 8.1, you might want to turn off preloading and see if that comes with a performance penalty or not.
To Top