PHP 8.4.0 RC4 available for testing

Предзагрузка

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

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

Замечание:

ОС Windows не поддерживает предзагрузку.

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

opcache.preload=preload.php

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

PHP проанализирует и загрузит в постоянную память каждый файл, который указали в выражениях include, include_once, require, require_once или в аргументе функции opcache_compile_file() в скрипте preload.php. В следующем примере загрузится каждый файл с расширением .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 выполняет код, PHP также разберёт и предзагрузит объявления вложенных файлов, которые включили выражением include.
  • Функция opcache_compile_file() загружает файлы в произвольном порядке. Даже если в файле a.php определили класс A, а в файле b.php класс B, который наследует класс A, это не изменит порядок загрузки файлов и функция opcache_compile_file() всё равно загрузит файлы в случайном порядке. А выражение include первым обязательно загрузит файл a.php.
  • В любом случае, если более поздний скрипт включает файл, который загрузился прежде, то PHP всё равно выполнит содержимое файла, но не переопределит символы, которые в нём определили. Выражение include_once не предотвратит повторное включение файла. Иногда требуется загрузить файл снова, чтобы включить глобальные константы, которые определили в файле, поскольку они не обрабатываются предварительной загрузкой.
Поведение, которое ждут от кода, определяет, какой подход лучше. Для кода, который иначе использовал бы автозагрузчик, функция opcache_compile_file() даёт больше гибкости. С кодом, который иначе загружался бы вручную, часто надёжнее выражение include.

Добавить

Примечания пользователей 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