![]() |
![]() |
| http://kurepin.ru/php/zametki/add_str/ |
|
Довольно популярный вопрос среди начинающих программистов: "А как записать данные не в конец, а в начало файла?". Вопрос хороший. Подобная задача рано или поздно встает перед каждым web-программистом. Обычно — раньше. Надо сказать, что ответов на этот вопрос довольно много. Каждый программист решает эту популярную задачу по-разному. Я бы даже на месте разработчиков PHP ввел отдельную функцию, дописывающую строку с начало файла, чтобы избавить людей от этого вопроса. Даже не от вопроса, а от неграмотных его решений. Не смотря на разнообразные подходы к решению этой задачи, сам алгоритм решения, как правило, общий для всех: прочесть файл, дописать к нему новые данные, сохранить файл. Но, не смотря на кажущуюся простоту, в таком решении заложен огромный подводный камень, на который натыкается, чуть ли не 99% малоопытных программистов. Суть ошибки заключается в том, что программист использует для временного сохранения данных оперативную память: читает файл в память, дописывает к нему новые данные, благо оперативка позволяет дописывать данные куда угодно, после чего сохранят данные в старый файл. "Чем же плох такой метод?", -- спросит любой из вас. А вот чем. Попробуйте мысленно растянуть во времени всю эту процедуру: вот данные прочтены в оперативную память... вот вы закрыли файл для чтения... вновь открыли его, но уже для перезаписи... [fatal error] ...вот тут у вас по какой-то причине оборвалось выполнение скрипта. Сбой в питании, перезагрузка, не хватило квоты на диске или случилась любая другая ошибка. Экземпляр скрипта уничтожается вместе с прочтенными в оперативку данными, а открытый для перезаписи файл автоматически закрывается девственно чистым или с некоторой частью данных, которые успели записаться. Все, данные теперь можно вытащить только из backup. Хорошо, если он у вас окажется актуальным. Что же произошло, неужели ошибка в алгоритме? Нет, никакой ошибки в алгоритме нет. Но давайте все по порядку... Предположим, что аварийного завершения работы скрипта у вас никогда не произойдет, хотя я в это и не верю, но пройдет какое-то время и файл с данными (предположим, что это гостевая книга) раздуется до такого размера, что просто перестанет влезать в отведенную для данного скрипта оперативную память. Вы полагаете, что можете распоряжаетесь всей свободной оперативной памятью сервера? Ничего подобного! Каждому скрипту обычно выделяется какой-то фиксированный объем памяти, которым он может распоряжаться. Как правило, это несколько мегабайт. Надеюсь, дальше эту проблему можно не описывать. "Ну хорошо", -- скажете вы, -- "а если я обеспечу своевременное дробление файла на куски, чтобы не вылезать, скажем, за пределы 1 мегабайта в каждом куске?". Что ж, это ход, но я бы его тоже назвал полумерой. А если к скрипту обратится сразу много запросов, что тогда произойдет? А произойдет вполне очевидное — запустится такое количество экземпляров скрипта, какое может разместиться в оперативной памяти. И если каждый экземпляр скрипта будет читать в память хотя бы по мегабайту, то не трудно посчитать, сколько человек одновременно смогут одновременно прочесть вашу гостевую. Все дело в том, что описанные проблемы возникают далеко не сразу. Они могут вообще не возникнуть, но где гарантия, что это будет именно так, как вы хотите? Полагаю, я достаточно привел аргументов в пользу того, что нельзя использовать оперативную базу в качестве временного хранилища файлов. "Как же так", — продолжите удивляться вы, — "а зачем тогда в PHP существуют операторы, позволяющие прочесть файл в массив строк и т.п.? Зачем они тогда существуют?". А я вам с удовольствием отвечу на этот вопрос. Существуют они для работы с файлами более-менее фиксированного объема. Самым удобным примером тут может быть файл настроек вашей программы. Конфигурационные файлы обычно имеют более-менее фиксированный размер, и сроки в них представляют собой или директивы или комментарии. Такой файл очень удобно читать в строковый массив, производить над ним действия и записывать данные обратно в файл. Вот так. Но давайте-ка, вернемся к нашим баранам. Как же все-таки записывать новые данные в начало файла? А вот как:
$file_gb="gb.txt"; // файл гостевой книги
$file_tmp="gb_tmp.txt"; // временный файл
$str="новая строка текста";
// проверяем, не было ли сбоя в предыдущем запуске скрипта
if(file_exists($file_tmp)) die("fatal error, call administrator!");
// копируем содержимое файла в tmp
if(copy($file_gb, $file_tmp))
{
// удачно скопировался, можно перезаписывать основной файл
if($w=fopen($file_gb,"w"))
{
flock($w,2); // локируем файл
fwrite($w,$str."\n"); // записываем первую строку
if(!$r=fopen($file_tmp,"r")) die("can't open file");
flock($r,1);
while($str=fgets($r,10240)) // читаем построчно
{
fputs($w,$str); // пишем построчно
}
flock($r,3);
fclose($r);
flock($w,3);
fclose($w);
unlink($file_tmp);
}
}
Вот так. А теперь попробуйте представить себе ситуацию, когда данные могут быть испорчены работой этого скрипта. И не пытайтесь! На каком этапе работы не произошел бы сбой, всегда сохранится актуальная информация: или в основном файле, или во временном. Причем, если информация осталась во временном файле, то следующий экземпляр скрипта не станет работать, чтобы не затереть данные в tmp-файле. Теперь вы можете быть спокойны за свою гостевую книгу, форум, сбор подписей, хранилище заказов и т.п. — в худшем случае программа просто перестанет принимать новые данные, надежно сохранив существующие. Надеюсь, мне удалось доказать, что работать с файлами надо как с файлами, а не как с переменными, которые на то и переменные, чтобы часто рождаться и часто умирать. Только не надо думать, что "от этих предосторожностей" скрипт раздуется в размерах. Просто оформите этот код в виде отдельной функции, назовите его удобным словосочетанием и используйте в своих программах наряду с другими функциями PHP. Желаю удачи! 10.01.03
|
| реклама на сайте |
|
…
|
| copyright ©2000-2002 Ruslan Kurepin |