`rename`之前的`fopen`如何仍然使用PHP读取旧内容

问题描述 投票:0回答:1

下面的代码打开特定文件的文件描述符 (

$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 上它不起作用,因为在已经使用

rename
打开的文件上使用
fopen
会抛出错误 - 在 Linux 上这似乎很好,没有错误。

php file descriptor
1个回答
0
投票

请注意,代码(针对您的情况)在某种程度上取决于操作系统

例如,如果我们稍微更改一下 rename 语句以显示 rename 函数结果(如评论者所建议的):

echo "checking result of rename:" . rename($fileB,$fileA);

使代码变为:

<?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");

echo "checking result of rename:" . rename($fileB,$fileA);
echo "<br>";


echo fread($fp,10000);

?>

然后在 Windows 操作系统上运行 PHP 脚本时,结果(假设在 XAMPP 上运行)将是:

enter image description here

对于相同的代码,如果在linux操作系统上运行相同的PHP脚本,则会是:

enter image description here

因此,对于在Win操作系统上运行的PHP脚本,由于操作系统禁止重命名操作(当文件“打开”时),因此当触发PHP重命名功能时,不会进行文件覆盖操作,因此系统会忠实地显示内容原始文件A.txt,即“aaaaaaaaaaaaaa111111”。 (是的,我已经检查过,重命名操作失败,那里仍然有两个TXT文件)

对于在linux操作系统上运行的PHP脚本,重命名操作将会成功(即使fileA.txt被fopen“打开”,因此PHP会用fileB.txt的内容覆盖原始fileA.txt,数据为“bbbbbbbbbbbbbb222222” ”.

但是,您观察到的情况仍然是正确的,因为即使对于在 Linux 操作系统上运行的 PHP,当重命名操作后 fileA.txt 中已有数据“bbbbbbbbbbbbbb222222”时,系统也会显示“aaaaaaaaaaaaaaa111111”。

我测试过——这与fread操作之前的rename操作是否有I/O延迟无关。所以即使我在 fread 语句前添加一行“sleep(10)”,系统仍然会显示数据“aaaaaaaaaaaaaa111111”,即使 fileA.txt 消失并被 fileB.txt 取代

所以原因可以是(通俗地说):即使你使用重命名功能“覆盖”原始文件的数据,文件句柄仍然会指向原始文件数据,所以实际上这两个文件共存于文件系统。 “覆盖”操作(如官方 PHP 文档中所述)只是告诉您“看起来”是什么。实际上指向磁盘 I/O 的数据流仍然存在,因此系统仍然可以访问旧数据。

当然,解决方法之一是

  1. 重命名后关闭文件句柄
  2. 通过 fopen 重新打开文件A

因此,以下内容对于在 Linux 操作系统上运行的 PHP 会有好处:(它将显示“bbbbbbbbbbbbbb222222”)

<?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");

echo "checking result of rename:" . rename($fileB,$fileA);
echo "<br>";

fclose($fp);
$fp = fopen($fileA,"r");

echo fread($fp,10000);

?>

© www.soinside.com 2019 - 2024. All rights reserved.