Preloading

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

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

Замечание:

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

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

opcache.preload=preload.php

preload.php - это обязательный файл, который будет запущен один раз при старте сервера (PHP-FPM, mod_php, etc.) и который загрузит код в постоянную память. Если PHP будет запущен под пользователем 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 может быть более надёжным.