PHP 的 require() 函数是否将其结果缓存在 PHP 5.6 中?

Does PHP's require() function cache its results in PHP 5.6?

在旨在用于通用共享 LAMP 托管的 PHP 脚本中,我使用 require() 作为从文件读取数据的函数。数据文件如下所示:

<?php
# storage.php

return [
    // Rotating secrets
    'last_rand' => '532e89355b78aafdb85f5f01f0eed20440d6bd9e0a2d6ae1bd17be4e1d7d21c7bb7a822a2077e3f4',
];

我是这样读的:

$data = require($root . '/storage.php');
$lastRand = $data['last_rand'];

在我的脚本中,我将使用这种方法从配置文件中读取信息。在某些情况下,该操作将重新写入一个新的配置文件(使用 file_put_contents()),然后重新读取它,再次使用 require().

到目前为止,这在各种 LAMP 主机上运行良好,但今天我发现了一个 Web 主机,其中 require() 似乎没有注意到文件中的更改。该主机使用 PHP 5.6,而我测试过的所有其他主机都使用 7+。

非常奇怪的是 require() 获取文件的旧内容,即使 file_get_contents() 可以看到新版本,就好像 require() 正在做自己的内部缓存。

修复尝试失败

我什至尝试等待 require() 赶上,以防某些底层缓存需要时间过期:

$ok = true;
$t = microtime(true);
while ($oldRandom != $this->getFileService()->requirePhp($this->getStoragePath())['last_rand'])
{
    $elapsedTime = microtime(true) - $t;
    if ($elapsedTime > 4) // Wait 4 seconds
    {
        $ok = false;
        break;
    }
    usleep(1000);
}

这也没有用(超时刚刚到期,从 require() 返回的结果没有变化)。

然而,在 PHP 脚本的下一个 运行 时,require() 突然看到了新的文件内容。

我也试过删除storage.php文件再重读,也没用!我真的很期待这会有所帮助。

未来行动

所以,我完全不理解这种行为。要解决它,我可以:

然而,这些都感觉我忽略了一个错误,我应该调查原因。我碰巧也知道这个主机(一个免费的)几乎总是处于沉重的 CPU 负载之下。它是一个 64 位服务器 运行ning Linux 在相当老的 2.6 内核上,并且 phpinfo() 表示 CGI/FastCGI 服务器 API 生效。

有没有人以前遇到过这个问题,有什么办法可以缓解吗?

更多尝试

一些有帮助的评论者认为这个问题可能与 opcaching 有关,这似乎符合 require() 的一般目的。我在重写配置的 file_put_contents() 之后添加了这段代码:

    $reset = opcache_reset(); // Returns true
    $invalid = opcache_invalidate($this->getStoragePath()); // Returns false

然而,这没有什么区别 - require() 顽固地读取相同的值。我已经通过在文件写入前后执行 require() 以及 file_get_contents() 来获取真实内容来确认这一点。

当我最初遇到这个错误时,我对解决它犹豫不决,因为它感觉像是一个不应该被忽略的严重错误。但是,既然罪魁祸首很可能是 opcaching(因此与 require() 具体相关),我认为通过将值保存在内存中来解决这个问题并不是一个糟糕的解决方案。我必须记住 require() 仅适用于每个 HTTP 请求一次调用,即使这不适用于所有 PHP 安装。

我也意识到,如果我真的尝试解决这个问题,我将不得不应对许多 opcache 失效机制,这比我能接受的复杂得多。

我在配置文件中发现了同样的问题,目前唯一有效的解决方案是在每次修改时更改配置文件的名称:

1 - 加载配置文件

// get the config file name
$config_file=glob('config/config*.php'))[0] ?? null;

// load the settings
if($config_file) $settings=require_once($config_file);

2 - 在需要时保存配置文件

...

$data=['var'=>'value','other-var'=>'other value'];
$vars=var_export($data, TRUE);

// new file name
$file_name='config/config-'.time().'.php';
file_put_contents($file_name,"<?php\n return $vars;");

// delete old config file
if($config_file) @unlink($config_file);