下面的代碼打開一個特定文件的文件描述符($fp
),然后我覆蓋該文件(使用rename
),稍后我使用fread
讀取該文件的內容。令我驚訝的是,當使用fread
讀取文件時,它仍然指向原始文件的內容(在被覆蓋之前)!這是怎么發生的?我認為只有當fopen
制作了一個完整的文件副本,以便以后可以讀取時,這才有可能,但我不敢相信fopen
制作了整個文件副本,因為這將是非常不夠的。
<?php
$fileA = "fileA.txt";
$fileB = "fileB.txt";
file_put_contents($fileA,"aaaaaaaaaaaaaa111111",LOCK_EX);
file_put_contents($fileB,"bbbbbbbbbbbbbb222222",LOCK_EX);
$fp = fopen($fileA,"r");
rename($fileB,$fileA);
echo fread($fp,10000);
?>
令人驚訝的是,上面的代碼輸出aaaaaaaaaaaaaa111111
,但它應該輸出bbbbbbbbbbbbbb222222
。如果在fread
之前,我使用fclose
并重新打開文件,它會按預期工作(顯示新內容)。
我的問題是,PHP如何仍然能夠顯示已被覆蓋的原始文件的內容!我簡直不敢相信,當我調用fopen
時,PHP竟然復制了整個文件。
上述代碼在Linux上運行良好,但在Windows上無法運行,因為在已使用fopen
打開的文件上使用rename
會拋出錯誤——在Linux上,這似乎沒問題,沒有錯誤。
請注意,代碼(針對您的情況)在一定程度上依賴于操作系統
例如,如果我們稍微更改重命名語句以顯示重命名函數結果(如評論員所建議的):
使代碼變為:
那么在Windows操作系統上運行PHP腳本時,結果(比如在XAMPP上運行)將是:
對于相同的代碼,如果在linux操作系統上運行相同的PHP腳本,結果將是:
因此,對于在Win操作系統上運行的PHP腳本,由于操作系統禁止重命名操作(當文件“打開”時),因此在觸發PHP重命名函數時不會進行文件覆蓋操作,因此系統會忠實地顯示原始fileA.txt的內容,即“aaaaaaaaaa 111111”。(是的,我已經檢查過了,重命名操作失敗,那里還有兩個TXT文件)
對于在linux操作系統上運行的PHP腳本,重命名操作將成功(即使當fileA.txt被fopen“打開”時),因此PHP會用數據“bbbbbbbbbb 222222”用fileB.txt的內容覆蓋原始的fileA.txt。
然而,您觀察到的仍然是正確的,因為即使對于在linux操作系統上運行的PHP,當重命名操作后fileA.txt已經具有數據“bbbbbbbbbb bbbbbb 222222”時,系統也會顯示“aaaaaaaaaa 111111”。
我已經測試過了——這與fread操作之前的重命名操作中是否存在I/O延遲無關。因此,即使我在fread語句前添加一行“sleep(10)”,系統仍將顯示數據“aaaaaaaaaa 111111”,即使fileA.txt消失并替換為fileB.txt
因此,對于這種情況,即使您使用重命名函數“覆蓋”原始文件的數據,文件句柄(在linux上)仍將指向原始文件數據。“overwrite”操作(如PHP官方文檔中所述)告訴您操作的結果。但實際上文件句柄仍然指向原始數據流(在I/O上),因此系統仍然顯示舊數據。
當然,解決方法之一是刷新文件句柄,例如:
因此,以下內容對于在Linux操作系統上運行的PHP是有益的:(它將顯示“bbbbbbbbbb 222222”)