當 PHP 中不可能時 require 方法回傳 int
P粉883223328
P粉883223328 2023-09-02 09:55:14
<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

全部回覆(2)
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學習者快速成長!