当 PHP 中不可能时 require 方法返回 int
P粉883223328
P粉883223328 2023-09-02 09:55:14
0
2
391
<p>我有以下代码,它将一些php代码保存到文件中,然后加载它,再次运行,有时require方法返回int,为什么会发生这种情况?</p> <h1>演示.php</h1> <pre class="brush:php;toolbar:false;"><?php $f = function() use($a){ $cachePath = '/tmp/t.php'; $code = '<?php'; $code .= "\n\n"; $code .= 'return ' . var_export([], true) . ';'; file_put_contents($cachePath, $code, LOCK_EX); if (file_exists($cachePath)) { // Sometime the following line returns int,why? $result = require($cachePath); if (!is_array($result)) { var_dump($result, $cachePath, file_get_contents($cachePath)); exit("ok"); } var_dump($result); } }; for($i=0;$i<1000000;$i++) { $f(); }</pre> <h1>如何重现?</h1> <p>使用两个 php 进程运行上面的代码</p> <pre class="brush:php;toolbar:false;">php demo.php</pre></p>
P粉883223328
P粉883223328

全部回复(1)
P粉351138462

这是 require 的标准行为,与 include 的文档,这两者之间的行为是相同的:

如您所见,当返回值未被覆盖时,快乐路径上会返回一个整数 (1)。

这对于您的示例来说是有意义的,到目前为止,文件存在(因此没有致命错误),但由于文件刚刚创建,因此它可能只是被截断,也就是说,它是空的。 p>

因此返回值不会被覆盖,您可以看到 int(1)。

另一种解释自然是您已经用整数覆盖,这也是可能的,因为多个进程可以写入同一个文件,但对于您编写示例的方式来说,这种可能性较小。我只是提到它,因为这是另一个有效的解释。

如果存在则包含

示例如何在您查找 $result 时悬浮竞争条件,而不是(仅)在文件存在时:

if (($result = @include($cachePath)) &&
    is_array($result)    
) {
   # $result is array, which is required
   # ...
}

其背后的想法是,我们只进行很少的错误处理,例如检查文件是否存在,否则无法包含该文件(include() 只会发出警告并以 $result = false 传递),然后如果 $result 加载确实适用于 is_array() 测试。

这就是我们为错误而设计的,但我们知道我们在寻找什么,即 $result 是一个数组。

这通常称为事务或事务操作。

在这个新示例中,当 $result 数组为空时,我们甚至不会输入 if-body,例如不包含任何数据。

在程序处理级别上,这可能是我们感兴趣的,文件存在或不存在、为空或不为空、甚至写错都是错误情况,它需要“吃掉”并使 $result 无效。

定义错误不存在。

处理解析错误(对于 Include-If-Exists)

自 PHP 7.0 起,我们可以使用 include(),如果不幸的是返回的包含文件已写入一半,我们将看到 PHP 解析错误,该错误可以捕获

# start the transaction
$result = null;
assert(
    is_string($cachePath) &&           # pathnames are strings,
    '' !== $cachePath &&               # never empty,
    false === strpos($cachePath, "rrreee") # and must not contain null-bytes
);
try {
    if (file_exists($cachePath)) {
        $result = include($cachePath);
    }
    # invalidate $result in case include() did technically work.
    if (!$result || !is_array($result) {
        $result = null;
    }
} catch (Throwable $t) {
    # catch all errors and exceptions,
    # the fall-through is intended to invalidate $result.
    $result = null;
} finally {
    # $result is not null, but a non-empty array if it worked.
    # $result is null, if it could not be acquired.
}

请参阅 PHP try-catch-finally 了解如何抛出异常/异常处理工作详细,assert()用于记录示例中输入参数$cachePath的含义。

第二个示例不使用抑制操作“@”,原因是如果像前面的示例一样使用它,并且要包含的文件将包含真正的致命错误,则该致命错误将被静音。如今,在现代 PHP 中,这不再是一个大问题,但是使用 file_exists() + include() – 虽然由于检查时间与使用时间而存在竞争条件 – 对于不存在的文件是安全的(仅警告)并且致命错误不会被隐藏。

正如您可能已经看到的,您了解的细节越多,就越难编写尽可能具有前瞻性的代码。我们决不能迷失在错误处理本身的错误处理中,而应该关注结果并定义这些错误不存在。

也就是说,include() 仍然导致将数据加载到内存中,file_exists() 仅用于“抑制”警告,我们知道,尽管如此,include() 可能会发出警告并可能返回一个整数,而不是一个数组。


现在,由于编程很困难:然后您可能会将其包装在一个循环中,例如重试三次。为什么不使用 for 循环 来计数并保护数字重试次数?

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!