亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

當(dāng) PHP 中不可能時(shí) require 方法回傳 int
P粉883223328
P粉883223328 2023-09-02 09:55:14
0
2
689
<p>我有以下程式碼,它將一些php程式碼儲存到檔案中,然後載入它,再次運(yùn)行,有時(shí)require方法傳回int,為什麼會(huì)發(fā)生這種情況? </p> <h1>示範(fàn).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>使用兩個(gè) php 行程執(zhí)行上面的程式碼</p> <pre class="brush:php;toolbar:false;">php demo.php</pre></p>
P粉883223328
P粉883223328

全部回覆(2)
P粉351138462

這是 require 的標(biāo)準(zhǔn)行為,與 include# 的文檔,這兩者之間的行為是相同的:

如您所見,當(dāng)回傳值未被覆寫時(shí),快樂路徑上會(huì)傳回整數(shù) (1)。

這對您的範(fàn)例來說是有意義的,到目前為止,檔案存在(因此沒有致命錯(cuò)誤),但由於檔案剛剛創(chuàng)建,因此它可能只是被截?cái)?,也就是說,它是空的。 p>

因此傳回值不會(huì)被覆寫,您可以看到 int(1)。

另一種解釋自然是您已經(jīng)用整數(shù)覆蓋,這也是可能的,因?yàn)槎鄠€(gè)進(jìn)程可以寫入同一個(gè)文件,但對於您編寫示例的方式來說,這種可能性較小。我只是提到它,因?yàn)檫@是另一個(gè)有效的解釋。

如果存在則包含

範(fàn)例如何在您尋找 $result 時(shí)懸浮競爭條件,而不是(僅)在檔案存在時(shí):

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

背後的想法是,我們只進(jìn)行很少的錯(cuò)誤處理,例如檢查文件是否存在,否則無法包含該文件(include() 只會(huì)發(fā)出警告並以$result = false 傳遞),然後如果$result載入確實(shí)適用於is_array() 測試。

這就是我們?yōu)殄e(cuò)誤而設(shè)計(jì)的,但我們知道我們在尋找什麼,即 $result 是一個(gè)陣列。

這通常稱為事務(wù)或事務(wù)操作。

在這個(gè)新範(fàn)例中,當(dāng) $result 陣列為空時(shí),我們甚至不會(huì)輸入 if-body,例如不包含任何資料。

在程式處理層級上,這可能是我們感興趣的,檔案存在或不存在、為空或不為空、甚至寫錯(cuò)都是錯(cuò)誤情況,它需要「吃掉」並使 $result 無效。

定義錯(cuò)誤不存在。

處理解析錯(cuò)誤(對於 Include-If-Exists)

自 PHP 7.0 起,我們可以使用 include(),如果不幸的是返回的包含檔案已寫入一半,我們將看到 PHP 解析錯(cuò)誤,該錯(cuò)誤可以捕獲

# 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 了解如何拋出異常/異常處理工作詳細(xì),assert()用於記錄範(fàn)例中輸入?yún)?shù)$cachePath的含義。

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

正如您可能已經(jīng)看到的,您了解的細(xì)節(jié)越多,就越難編寫盡可能具有前瞻性的程式碼。我們絕不能迷失在錯(cuò)誤處理本身的錯(cuò)誤處理中,而應(yīng)該專注於結(jié)果並定義這些錯(cuò)誤不存在。

也就是說,include() 仍然導(dǎo)致將資料載入到記憶體中,file_exists() 僅用於「抑制」警告,我們知道,儘管如此,include() 可能會(huì)發(fā)出警告並可能傳回一個(gè)整數(shù),而不是一個(gè)陣列。


現(xiàn)在,由於編程很困難:然後您可能會(huì)將其包裝在一個(gè)循環(huán)中,例如重試三次。為什麼不使用 for 迴圈 來計(jì)數(shù)並保護(hù)數(shù)字重試次數(shù)?

P粉550323338

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

如果您正在談?wù)搧K行運(yùn)行此腳本,那麼問題在於以獨(dú)佔(zhàn)模式寫入檔案並不能保護(hù)您稍後在寫入過程中讀取檔案。

進(jìn)程可能正在寫入檔案(並擁有鎖),但 require 不遵守該鎖(檔案系統(tǒng)鎖是建議性的,而不是強(qiá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();
}

請注意,寫入後不會(huì)釋放並重新取得鎖定,因?yàn)榱硪粋€(gè)進(jìn)程可能正在等待覆寫該檔案。

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

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

為什麼需要寫入一個(gè)檔案以便稍後require將其傳回?

最新下載
更多>
網(wǎng)站特效
網(wǎng)站源碼
網(wǎng)站素材
前端模板