PHP 开发者大会日本 2024

预加载

从 PHP 7.4.0 开始,PHP 可以配置为在引擎启动时将脚本预加载到 opcache 中。这些文件中的任何函数、类、接口或特性(但不包括常量)都将全局可用,所有请求都可以使用,无需显式包含。这以牺牲基本的内存使用为代价换取了便利性和性能(因为代码始终可用)。它还需要重新启动 PHP 进程才能清除预加载的脚本,这意味着此功能仅适用于生产环境,不适用于开发环境。

请注意,性能和内存之间的最佳平衡可能因应用程序而异。“预加载所有内容”可能是最简单的策略,但不一定是最佳策略。此外,只有当从一个请求到另一个请求存在持久进程时,预加载才有用。这意味着虽然它可以在启用 opcache 的 CLI 脚本中工作,但通常是没有意义的。例外情况是在 FFI 库 上使用预加载时。

注意:

Windows 不支持预加载。

配置预加载涉及两个步骤,并且需要启用 opcache。首先,在 php.ini 中设置 opcache.preload 值。

opcache.preload=preload.php

preload.php 是一个任意文件,它将在服务器启动时(PHP-FPM、mod_php 等)运行一次,并将代码加载到持久内存中。在作为 root 用户启动然后切换到非特权系统用户的服务器中,或者如果 PHP 将作为 root 用户运行(不推荐)时,opcache.preload_user 值可以指定运行预加载的系统用户。默认情况下不允许以 root 用户身份运行预加载。设置 opcache.preload_user=root 以显式允许它。

preload.php 脚本中,由 includeinclude_oncerequirerequire_onceopcache_compile_file() 引用的任何文件都将被解析到持久内存中。在下面的示例中,src 目录中的所有 .php 文件都将被预加载,除非它们是 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];
}
?>

includeopcache_compile_file() 都可以工作,但对代码的处理方式有所不同。

  • include 将执行文件中的代码,而 opcache_compile_file() 则不会。这意味着只有前者支持条件声明(在 if 块内声明的函数)。
  • 因为 include 将执行代码,所以嵌套的 include 文件也将被解析,并且它们的声明将被预加载。
  • opcache_compile_file() 可以按任何顺序加载文件。也就是说,如果 a.php 定义了类 A,而 b.php 定义了扩展 A 的类 B,那么 opcache_compile_file() 可以按任何顺序加载这两个文件。但是,当使用 include 时,必须首先包含 a.php
  • 无论哪种情况,如果后面的脚本包含一个已经预加载的文件,那么它的内容仍然会执行,但是它定义的任何符号都不会重新定义。使用 include_once 并不会阻止文件第二次被包含。为了包含在其中定义的全局常量,可能需要再次加载文件,因为预加载不处理这些常量。
因此,哪种方法更好取决于所需的行为。对于原本使用自动加载器的代码,opcache_compile_file() 提供了更大的灵活性。对于原本手动加载的代码,include 将更加健壮。

添加注释

用户贡献的注释 2 条注释

4
postmaster at greg0ire dot fr
2 年前
PHP 8.1 带有一个继承缓存,它与预加载已经执行的操作部分重叠。如果您在较低版本上启用了预加载,然后迁移到 PHP 8.1,您可能需要关闭预加载,并查看这是否会带来性能损失。
0
postmaster at greg0ire dot fr
2 年前
启用预加载时有一些注意事项,其中之一是应该通过 php.ini 文件启用它。例如,使用 php-fpm 池配置启用它将不起作用,因为预加载是全局的,而不是每个池的。要确保您已成功启用预加载,您应该检查 opcache_get_status() 输出中的 preload_statistics 键。应该已经有一个 opcache_statistics 键,但这完全是另一回事。
To Top