Код ниже открывает файловый дескриптор ($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 все еще может отображать содержимое исходного файла, который уже был перезаписан! Не могу поверить, что PHP сделал полную копию файла, когда я позвонил fopen
.
Приведенный выше код отлично работает в Linux, однако в Windows он не будет работать, поскольку использование rename
в файле, уже открытом с помощью fopen
, приведет к ошибке - в Linux все в порядке, ошибок нет.
🤔 А знаете ли вы, что...
С PHP можно создавать красочные и интерактивные веб-формы.
Обратите внимание, что код (для вашего случая) в некоторой степени зависит от ОС.
Например, если мы немного изменим оператор переименования, чтобы отобразить результат функции переименования (например, как предложил комментатор):
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);
?>
тогда при запуске PHP-скрипта в ОС Windows результат (скажем, при запуске на XAMPP) будет:
Для того же кода, если запустить тот же PHP-скрипт в ОС Linux, результат будет:
Таким образом, для сценария PHP, работающего в ОС Win, поскольку ОС запрещает операцию переименования (когда файл «открыт»), операция перезаписи файла не выполняется при запуске функции переименования PHP, и, следовательно, система точно отображает содержимое исходный файл A.txt, который имеет вид «aaaaaaaaaaaaa111111». (да, я проверил, операция переименования не удалась, и там все еще есть два файла TXT)
Для сценария PHP, работающего в ОС Linux, операция переименования завершится успешно (даже если файл A.txt «открыт» с помощью fopen), поэтому PHP перезаписывает исходный файл A.txt содержимым файла B.txt с данными «bbbbbbbbbbbb222222». ".
Однако то, что вы наблюдаете, по-прежнему верно, потому что даже для PHP, работающего в ОС Linux, система будет отображать «aaaaaaaaaaaaaaaaaaaa111111», когда файл A.txt уже содержит данные «bbbbbbbbbbbbbb222222» после операции переименования.
Я проверил - это не имеет никакого отношения к тому, есть ли какая-либо задержка ввода-вывода в операции переименования перед операцией fread. Таким образом, даже если я добавлю строку «sleep(10)» перед оператором fread, система все равно будет отображать данные «aaaaaaaaaaaaaa111111», даже файл fileA.txt исчезнет и будет заменен на fileB.txt.
Таким образом, в этом случае дескриптор файла (в Linux) по-прежнему будет указывать на исходные данные файла, даже если вы используете функцию переименования для «перезаписи» данных исходного файла. Операция «перезаписи» (как указано в официальной документации PHP) сообщает вам результат операции. НО на самом деле дескриптор файла по-прежнему указывает на исходный поток данных (на вводе-выводе), поэтому система по-прежнему отображает старые данные.
Конечно, одним из способов решения проблемы будет обновление дескриптора файла, например:
Итак, для PHP, работающего в ОС Linux, подойдет следующее: (отобразится «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);
?>