PHP fwrite всегда возвращает количество записываемых байтов, даже если файл не существует, это ожидаемое поведение?

У меня есть следующий пример кода

<?php
`echo test > /tmp/test.txt`;
$f=fopen("/tmp/test.txt","w+");
`rm /tmp/test.txt`;
var_dump(fwrite($f,"test"));
fclose($f);
file_get_contents("/tmp/test.txt");

Который создает файл, открывает указатель на него, удаляет файл, а затем пытается записать в него. Теперь я ожидаю, что fwrite вернет false или 0, поскольку данные не будут записаны, однако он выдает следующий вывод

int(4)

Warning: file_get_contents(/tmp/test.txt): failed to open stream: No such file or directory in /Stuff/tmp/test3 on line 7

Таким образом, fwrite, по-видимому, преуспевает, но file_get_contents терпит неудачу, как и ожидалось.

Ожидается ли поведение fwrite для возврата количества записываемых байтов? Если да, то как я могу проверить, действительно ли запись была успешной?

Это с php 5.3.3


person Rwky    schedule 18.11.2010    source источник


Ответы (5)


Причина, по которой он возвращает false, заключается в том, что указатель файла находится в конце файла после fwrite... Вот что fgets должен делать, когда он достигает конца файла (он возвращает false, так как больше нет данных для получения)...

Что вам нужно сделать, так это добавить вызов fseek перед вызовом fgets:

var_dump(fwrite($f,"test"));
fseek($f, 0);
var_dump(fgets($f));

Изменить: Теперь, когда я понял ваш вопрос (и рассмотрел его):

Итак, вот что происходит. Чтобы понять, вы должны сначала узнать, как работают файловые системы Linux. Имя файла не имеет значения для операционной системы. Все, что он делает, это указывает на INODE файла. Индексный дескриптор хранит данные и т. д. Он также хранит номер ссылки на количество жестких ссылок. к этому файлу. Файл удаляется только тогда, когда номер ссылки падает до 0. Я подозреваю, что открытие указателя на файл (с использованием fopen или других системных вызовов) увеличивает счетчик ссылок.

По сути, это означает, что когда вы запускали rm для файла, он удалял жесткую ссылку. Но поскольку файл все еще был открыт, вы все еще могли получить доступ к индексному узлу через fwrite (поэтому запись прошла успешно). Вы не могли получить доступ к /tmp/test.txt, так как жесткой ссылки на этот файл больше не существовало. Таким образом, файл стал файлом-фантомом, доступным только для inode. Но как только вы закрыли дескриптор файла для этого файла (fclose или завершив скрипт), счетчик ссылок упал до 0, и индекс был освобожден...

Таким образом, файл существует, просто он недоступен по имени файла после вызова rm...

Это то, что я собираю, зная то, что я знаю о файловых системах. Я не говорю, что это на 100% точно, но это должна быть приличная гипотеза...

person ircmaxell    schedule 18.11.2010
comment
Я изменил вопрос, чтобы сделать его более ясным, речь идет не о fgets, а о отчете fwrite, который записывает 4 байта в несуществующий файл, не должен ли fwrite возвращать 0 или false? - person Rwky; 18.11.2010
comment
+1 перемотка назад ($f) также будет работать (относительно исходного вопроса). - person GZipp; 18.11.2010
comment
Ах, это имеет смысл, боль, но это имеет смысл. - person Rwky; 18.11.2010

Если это в системе Linux (и, возможно, других unix-подобных ОС), ссылка на файл в файловой системе удаляется rm, но сам файл все еще существует до тех пор, пока все программы, читающие/пишущие в него, не будут закрыты.

Это связано с тем, как работают жесткие ссылки; все записи в файловой системе являются жесткими ссылками, мягкими ссылками или устройствами.

person Powerlord    schedule 18.11.2010
comment
Кстати, Windows просто выдаст ошибку, если вы попытаетесь удалить файл, пока другой процесс читает или записывает его. - person Powerlord; 18.11.2010

Я считаю, что вы сначала должны сохранить файл

fclose($f);
person pestaa    schedule 18.11.2010
comment
Закрытие файла не равно сохранению. Он принудительно сбрасывает, но вам не нужно закрывать его, чтобы принудительно сбросить. А учитывая, что он буферизован, вам нужно только сбросить данные, чтобы увидеть изменения из другого процесса. Кроме того, вы можете просто использовать команду fflush вместо закрытия и повторного запуска. открытие файла... - person ircmaxell; 18.11.2010

Следующий код должен позволить вам проверить, была ли запись успешной.

 if (fwrite($handle, $somecontent) === FALSE) {
        echo "Cannot write to file ($filename)";
        exit;
 }
person Jose Vega    schedule 18.11.2010
comment
Но var_dump показывает, что было возвращено 4, указывая на то, что было записано 4 байта. Так что эта проверка не укажет на ошибку в этом случае (не говорю, что это плохо, просто не применимо к этой проблеме)... - person ircmaxell; 18.11.2010
comment
Это не означает, что var_dump показывает вывод fwrite как 4, что не является ложным. - person Rwky; 18.11.2010

Вашему коду нужны некоторые условия, обработка ошибок, например.

  • проверить существует ли файл
  • доступен ли файл для записи
  • были ли данные записаны в файл
  • читаются ли данные
person takeshin    schedule 18.11.2010
comment
Это всего лишь пример кода, мой вопрос касается fwrite: ожидается ли, что он вернет, что он записал 4 байта в несуществующий файл, не должен ли он возвращать 0 или false? - person Rwky; 18.11.2010