国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

當 PHP 中不可能時 require 方法返回 int
P粉883223328
P粉883223328 2023-09-02 09:55:14
0
2
692
<p>我有以下代碼,它將一些php代碼保存到文件中,然后加載它,再次運行,有時require方法返回int,為什么會發(fā)生這種情況?</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>如何重現(xiàn)?</h1> <p>使用兩個 php 進程運行上面的代碼</p> <pre class="brush:php;toolbar:false;">php demo.php</pre></p>
P粉883223328
P粉883223328

全部回復(2)
P粉351138462

這是 require 的標準行為,與 include 的文檔,這兩者之間的行為是相同的:

如您所見,當返回值未被覆蓋時,快樂路徑上會返回一個整數(shù) (1)。

這對于您的示例來說是有意義的,到目前為止,文件存在(因此沒有致命錯誤),但由于文件剛剛創(chuàng)建,因此它可能只是被截斷,也就是說,它是空的。 p>

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

另一種解釋自然是您已經用整數(shù)覆蓋,這也是可能的,因為多個進程可以寫入同一個文件,但對于您編寫示例的方式來說,這種可能性較小。我只是提到它,因為這是另一個有效的解釋。

如果存在則包含

示例如何在您查找 $result 時懸浮競爭條件,而不是(僅)在文件存在時:

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

其背后的想法是,我們只進行很少的錯誤處理,例如檢查文件是否存在,否則無法包含該文件(include() 只會發(fā)出警告并以 $result = false 傳遞),然后如果 $result 加載確實適用于 is_array() 測試。

這就是我們?yōu)殄e誤而設計的,但我們知道我們在尋找什么,即 $result 是一個數(shù)組。

這通常稱為事務或事務操作。

在這個新示例中,當 $result 數(shù)組為空時,我們甚至不會輸入 if-body,例如不包含任何數(shù)據(jù)。

在程序處理級別上,這可能是我們感興趣的,文件存在或不存在、為空或不為空、甚至寫錯都是錯誤情況,它需要“吃掉”并使 $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()用于記錄示例中輸入參數(shù)$cachePath的含義。

第二個示例不使用抑制操作“@”,原因是如果像前面的示例一樣使用它,并且要包含的文件將包含真正的致命錯誤,則該致命錯誤將被靜音。如今,在現(xiàn)代 PHP 中,這不再是一個大問題,但是使用 file_exists() + include() – 雖然由于檢查時間與使用時間而存在競爭條件 – 對于不存在的文件是安全的(僅警告)并且致命錯誤不會被隱藏。

正如您可能已經看到的,您了解的細節(jié)越多,就越難編寫盡可能具有前瞻性的代碼。我們決不能迷失在錯誤處理本身的錯誤處理中,而應該關注結果并定義這些錯誤不存在。

也就是說,include() 仍然導致將數(shù)據(jù)加載到內存中,file_exists() 僅用于“抑制”警告,我們知道,盡管如此,include() 可能會發(fā)出警告并可能返回一個整數(shù),而不是一個數(shù)組。


現(xiàn)在,由于編程很困難:然后您可能會將其包裝在一個循環(huán)中,例如重試三次。為什么不使用 for 循環(huán) 來計數(shù)并保護數(shù)字重試次數(shù)?

P粉550323338

如果腳本始終只有一個執(zhí)行者,則此問題無法重現(xiàn)。

如果您正在談論并行運行此腳本,那么問題在于以獨占模式寫入文件并不能保護您稍后在寫入過程中讀取文件。

進程可能正在寫入文件(并擁有鎖),但 require 不遵守該鎖(文件系統(tǒng)鎖是建議性的,而不是強制執(zhí)行的)。

所以正確的解決方案是:

<?php

$f = function()  use($a){
    $cachePath = '/tmp/t.php';

    /* Open the file for writing only. If the file does not exist, it is created.
       If it exists, it is neither truncated (as opposed to 'w'), nor the call to this function fails (as is the case with 'x').
       The file pointer is positioned on the beginning of the file. 
       This may be useful if it's desired to get an advisory lock (see flock()) before attempting to modify the file, as using 'w' could truncate the file before the lock was obtained (if truncation is desired, ftruncate() can be used after the lock is requested). */
    $fp = fopen($cachePath, "c");

    $code = '<?php';
    $code .= "\n\n";
    $code .= 'return ' . var_export([], true) . ';';
 
    // acquire exclusive lock, waits until lock is acquired
    flock($fp, LOCK_EX);
    // clear the file
    ftruncate($fp, 0);
    // write the contents
    fwrite($fp, $code);

    //wrong (see my answer as to why)
    //file_put_contents($cachePath, $code, LOCK_EX);

    //not needed
    //if (file_exists($cachePath)) {
        // Lock is held during require
        $result = require($cachePath);
        if (!is_array($result)) {
            var_dump($result, $cachePath, file_get_contents($cachePath));
            exit("ok");
        }
        var_dump($result);
    //}
    // closing the file implicitly releases the lock
    fclose($fp);
};


for($i=0;$i<1000000;$i++) {
    $f();
}

請注意,寫入后不會釋放并重新獲取鎖,因為另一個進程可能正在等待覆蓋該文件。

不發(fā)布它是為了確保編寫的同一段代碼也是 required。

然而,這整件事從一開始就值得懷疑。

為什么需要寫入一個文件以便稍后require將其返回?

最新下載
更多>
網站特效
網站源碼
網站素材
前端模板