下面的代码打开特定文件的文件描述符 (
$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 上这似乎很好,没有错误。
请注意,代码(针对您的情况)在某种程度上取决于操作系统
例如,如果我们稍微更改一下 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 上运行)将是:
对于相同的代码,如果在linux操作系统上运行相同的PHP脚本,则会是:
因此,对于在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 的数据流仍然存在,因此系统仍然可以访问旧数据。
当然,解决方法之一是
因此,以下内容对于在 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);
?>