Pers.narod.ru. PHP. Статьи. Пример на работу с текстовым файлом в PHP |
Этот небольшой учебный пример иллюстрирует основные действия с так называемыми "плоскими" (то есть, обычными текстовыми) файлами. Даже не подключаясь к MySQL или другому серверу баз данных, на PHP легко (и главное, быстро) можно писать вполне полноценные приложения.
Пусть наш пример будет поддерживать простейшую текстовую "базу данных", в которой одна строка файла является одной записью. Запись будет состоять из 2 величин - имя (т.е., строка) и некое число, отделённое пробелом. Скрипт должен проверять существование файла (и при необходимости создавать новый), не позволять добавлять одинаковые записи, сортировать поддерживаемый список по алфавиту, уметь показать существующий список и форму для ввода новой записи.
Обычно подобные действия и требуются в реальных приложениях.
Сначала определим имя файла для наших данных, папка предполагается текущей:
$filename = 'data.txt';
Возможно, мы будем выходить из скрипта в нескольких местах кода (например, из-за ошибок доступа к файлу), так что сразу напишем функцию myexit
, "закрывающую" документ HTML и делающую выход. А вот "вход" в скрипт будет точно один, так что печать соответствующего заголовка документа HTML поставим первым действием.
function myexit($errorcode) { echo '</body></html>'; exit ($errorcode); } echo '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta content="text/html; charset=Windows-1251" http-equiv="content-type"> <title>Пример на работу с текстовым файлом</title> </head> <body>';
Проверим, что файл существует и доступен для записи, если нет - попытаемся создать пустой файл и проверим, что получилось. При неудаче - выводим сообщение об ошибке и выходим с кодом завершения 1
.
if (!is_writable($filename)) { if (!file_exists($filename)) { $f=@fopen($filename,"w"); if (!$f) { echo 'Не удалось открыть на запись новый файл данных '.$filename.', проверьте права!'; myexit(1); } fclose ($f); } }
Получим содержимое файла, разделим его на отдельные строки методом explode
(каждая запись будет в элементе массива $a
), элементы массива $a
, в свою очередь, разделим по пробелу на имена (после второго вызова explode
они будут содержаться в $fields[0]
) и числа ($fields[1]
). Но нам нужны пока только имена - чтобы сформировать из них массив всех имён $fio
. Ведь мы потом будем проверять имя на повторное добавление.
$data=file_get_contents ($filename); //можно сразу $a=file($filename); вместо этих 2 строк - $a=explode("\n",$data); //но нам потом понадобится $data как одна строка $fio=array(); foreach ($a as $item) { $fields = explode(" ",trim($item)); if (!empty($fields[0])) { array_push ($fio,$fields[0]); } }
Проверим и получим 2 внешних параметра - имя $name
и число $number
. Из имени мы просто удалили теги функцией htmlspecialchars
, а число преобразовали методом intval
, чтобы удалить нежелательные символы.
$name = $number = ''; if (isset($_GET['name'])) $name = htmlspecialchars(trim($_GET['name'])); if (isset($_GET['number'])) $number = intval(trim($_GET['number']));
Если обе переданных величины непусты, попытаемся добавить их в файл - но сначала проверим, что такого имени ещё нет (array_search
). Перед добавлением сортируем массив данных функцией sort
и объединяем его в строку функцией implode
, отдельные записи снова разделяем переводом строки. Конечно, такой подход нежелателен для больших объёмов данных, в этом случае лучше применять базы данных.
if (!empty($name) and !empty($number)) { if (array_search($name,$fio)===false) { array_push ($a,"$name $number"); sort ($a); $data=implode ("\n",$a); file_put_contents ($filename,$data); } }
Выводим форму для внесения новых данных, её поля, естественно, называются name
и number
, если соответствующие переменные были переданы - они выводятся как начальное содержимое полей. Конечно, неплохо было бы, если бы при совпадении поля имени и несовпадении числа скрипт умел ещё обновлять данные в файле - попробуйте "прикрутить" соответствующий код.
echo '<form action="index.php" method="get"> Имя: <input type="text" name="name" value="'.$name.'" size="30" maxlength="30"> <br>Число: <input type="text" name="number" value="'.$number.'" size="6" maxlength="6"> <br><input type="submit" value="Отправить"> </form>';
Остаётся вывести строку $data
и завершить скрипт. Если бы нам понадобилась печать с разделением данных - мы бы просканировали изменённый массив $a
циклом foreach
.
echo "$data"; myexit(0);
Ниже прикреплён архив ZIP с файлом этого примера. Предполагается, что файл скрипта называется index.php
, то есть, является "файлом по умолчанию" для своей папки.
Простейший пример на работу с текстовой "базой данных" средствами PHP (1 Кб)
P.S. На практике не следует забывать о теоретической возможности потери данных при работе с плоскими файлами, особенно при записи в них. Например, если два клиента пытаются записать файл одновременно, изменения, сделанные как минимум одним из них, могут пропасть... а в худшем случае пропадает весь файл!
Уменьшить вероятность "падения" может установка клиентом
исключительных прав на файл в то время, когда происходит запись.
Перед записью файла следует блокировать его стандартной функцией PHP
flock
, а по окончании записи разблокировать, например, так:
$fp = fopen("/tmp/lock.txt", "w+"); if (flock($fp, LOCK_EX)) { //Если удалось "запереть" файл fwrite($fp, "Что-нибудь пишем\n"); flock($fp, LOCK_UN); // "Отпираем" файл } else { echo "Ошибка блокировки файла на запись!"; } fclose($fp);
Однако, проблемы может создать и предшествующее записи чтение - представьте, что скрипт открывает файл, читает оттуда информацию в массив (точка 1), что-то добавляет в этот массив и пишет всё обратно в файл (точка 2). Блокируется только запись. Клиент 2 находится в точке 2 и собирается записать информацию. Клиент 1 из-за блокировки не смог получить содержимое файла, поэтому прочитал пустой массив. Но потом-то он тоже дойдёт до точки 2! Результат - содержимое файла потеряно.
В принципе, данной скользкой ситуации можно избежать, если написать функцию, которая "не пускает" пользователя дальше по скрипту до тех пор, пока файл не прочитается:
function read_file($path){ if (! is_file ($path)) {return false; } elseif (! filesize ($path)) {return array (); } elseif ($array= file ($path)) {return $array; } else { while (!$array= file ($path)){ sleep (1);} return $array; } }
Суть этого кода - пока файл блокирован на чтение,
пользователь, который хочет прочитать из него данные,
находится в цикле с функцией sleep
, то есть, как бы "стоит
на месте", ожидая, когда файл разблокируется на чтение.
Нужно заметить, что при действительно большой загруженности сервера эти приёмы могут как не сработать, так и создать большую нагрузку на сервер, которая, в конце концов, его "повесит". Но для больших сайтов и не подойдёт хранение информации в текстовых файлах, лучше поискать решение, основанное на базах данных.
гостевая; E-mail |