http://sulfurzona.ru/
News
Service
Magazine
Software (Battle City Game, Wallpaper manager, Superpad, VG-NOW, Puzzle Game, Netler Internet Browser, ..)
Wing-Thunder Game (fly simulator)
Dune Game (Dune III, Dune IV, Cheats, Forum, ..)
Games free
Turbo Pascal (Assembler, Docs, Sources, Debbugers, ..)
Books (Docs for developers)
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
Скачать игру Крыло-Гром (Wing-Thunder) бесплатно
 
 

PHP и WEB для новичков (часть 6)

 

Робота с файлами

 
Автор: Владислав Демьянишин
 
PHP и WEB для новичков
При помощи того, что мы усвоили в предыдущем материале, уже достаточно, чтобы написать простейшие скрипты. Однако остается нераскрытой проблема сохранения результатов работы скриптов на сервере и применение этих результатов самим этим скриптом или другими.
 
Например, если мы желаем разместить на сайте подсчет количества посетителей, то должны хранить это количество как число в каком-то отдельном месте, а при каждом визите считывать, наращивать на единицу и снова сохранять.
 
Но мы не можем пользоваться для этого, например, переменными PHP, потому что переменная "живет" лишь от начала ее использования в отдельном скрипте до его окончания, и увидеть значение этой переменной в другом скрипте не представляется возможным. Для решения этой проблемы существует несколько путей. Один из них - хранить данные в файлах.
 
Файл - это некоторая проименованная запись с информацией на носителе, разумеется, скрипты тоже файлы. Основные операции с файлами - это создание (открытие) файла, чтение из файла, запись в файл, переименование и удаление файла.
 
Для того, чтобы начать работу с файлом, его необходимо сначала открыть следующим образом:
 
$f = fopen("file.txt", "w");
 
Функция fopen() открывает файл. Первым параметром следует имя файла, а другим - режим доступа. Следующая таблица перечисляет возможные режимы доступа (см. таблицу 4):
 
Таблица 4. Режимы доступа к файлам
Режим
Описание
'r'
Открыть только для чтения, поместить указатель на начало файла. Если файл не существует, то fopen() вернет false и сгенерирует сообщение об ошибке.
'r+'
Открыть для чтения/записи, поместить указатель на начало файла. Если файл не существует, то fopen() вернет false и сгенерирует сообщение об ошибке.
'w'
Открыть только для записи, поместить указатель на начало файла, обрезать файл до нулевой длины (по сути перезаписывает файл). Если файл не существует, попробовать его создать.
'w+'
Открыть для чтения/записи, поместить указатель на начало файла, обрезать файл до нулевой длины. Если файл не существует, попробовать его создать.
'a'
Открыть только для записи, поместить указатель в конец файла. Если файл не существует, попробовать его создать. По сути, дописывает файл.
'a+'
Открыть для чтения/записи, поместить указатель на конец файла. Если файл не существует, попробовать его создать.
'x'
Создать файл и открыть его только для записи, поместить указатель на начало файла. Если файл уже существует, то fopen() вернет false и сгенерирует сообщение об ошибке. Если файл не существует, попробовать его создать.
'x+'
Создать файл и открыть его для чтения/записи, поместить указатель на начало файла. Если файл уже существует, то fopen() вернет false и сгенерирует сообщение об ошибке. Если файл не существует, попробовать его создать.
 
Значение, возвращаемое функцией fopen(), называется дескриптором (идентификатором) файла и используется далее для чтения, записи и всего того, для чего файл был открыт. После выполнения всех желаемых операций с файлом его необходимо закрыть функцией fclose().
 
Упомянутый выше "указатель" является позицией, с которой начинается операция с файлом. Т.е. если файл открыт для чтения (Read режим "r", указатель в начале файла), то чтение осуществляется с начала файла.
 
Если файл открыт для добавления (Append режим "a", указатель в конце файла), то добавление будет выполнятся в конец. Для получения текущей позиции указателя применяют функцию ftell(), для установки указателя в желаемую позицию - функцией fseek(). Сначала рассмотрим на примере функции для записи в файл.
 
$f = fopen("file.txt", "w");
fwrite($f, "test");
fclose($f);
 
Простой пример - открыли файл, записали в него строку "test" функцией fwrite(), закрыли файл. На самом деле нет необходимости закрывать файл - он автоматически закроется при завершении скрипта.
 
Тем не менее, желательно все-таки это делать - нет гарантии, что файл будет содержать все данные, которые в него записывались, если попытаться прочесть его в этом же скрипте не закрывая. Закрытие файла приводит к принудительному сбросу данных, застрявших в кэше в файл на носителе.
 
Теперь про чтение из файла. Наиболее часто применяют чтение из текстовых файлов, разбивая их на строки. Если это небольшой файл, то весьма просто прочесть его одним вызовом функции file(), и для этого даже не нужно открывать файл:
 
$a = file(basename($_SERVER["PHP_SELF"]));
echo "<pre>";
foreach($a as $str) echo htmlspecialchars($str);
echo "</pre>";
 
Этот скрипт распечатывает сам себя. Функция file() читает все содержимое файла в массив. При этом каждая строка файла становится элементом массива. Дальше в цикле foreach() по элементам массива $a просто выводим каждую строку массива.
 
$_SERVER["PHP_SELF"] - это глобальная переменная PHP, т.е. ее видно во всех скриптах, и она всегда содержит имя файла скрипта, который выполняется в данный момент. Имя содержит относительный путь к скрипту, например, если скрипт называется test.php в корне веб-сервера, то $_SERVER["PHP_SELF"] будет содержать "/test.php". Поэтому мы должны отделить имя файла от пути функцией basename().
 
Еще одна на данный момент новая функция - это htmlspecialchars(). Если ее не применить, а написать просто echo "$str ", то получим несколько странный результат (советую провести этот эксперимент самостоятельно).
 
Это происходит потому, что выданные в браузер строки будут восприняты как HTML-страничка и это вполне логично, что, например, напечатанное "<pre>" будет воспринято именно как тег форматирования, а не как строка "<pre>".
 
Функция htmlspecialchars() превращает теги HTML в вид, который адекватно отображается браузером. Например, тот самый "<pre>" превращается в " " и отображается как надо.
Организация цикла foreach() может показаться утомительной, тогда можно применить функцию join() для объединения всех элементов массива в единую строку. Следующий пример является аналогом предыдущего примера:
 
echo "<pre>";
$text = join('', file(basename($_SERVER["PHP_SELF"])));
echo htmlspecialchars($text);
echo "</pre>";
 
Если файл достаточно велик, и читать его целиком в один массив нецелесообразно, то применяют функцию fgets(), которая читает из файла последовательно. Для этого необходимо открыть файл и организовать цикл с fgets(), например, так:
 
$f = fopen(basename($_SERVER["PHP_SELF"]), "r");
echo "<pre>";
while (!feof($f)) {
          $buffer = fgets($f, 4096);
          echo htmlspecialchars($buffer);
         } // while
echo "</pre>";
fclose($f);
 
Этот пример делает то же, что и предыдущий - распечатывает сам себя. Сначала открываем файл скрипта с режимом доступа "r", т.е. только для чтения. Потом в цикле while() читаем строку из файла функцией fgets() и сразу выводим ее. После завершения цикла закрываем файл.
 
Условием выхода из цикла есть конструкция !feof($f). Функция feof() возвращает true, если указатель файла достиг конца. В данном случае выдаст true, когда из файла прочитаны все строки.
Функция fgets() получает два параметра. Первый - дескриптор файла, второй - максимальная длина строки, которая должна быть прочитана. Т.е. в данном случае принимаем, что строки в файле не могут быть длиннее 4096 символов.
 
Этот параметр можно не указывать, тогда строки будут читаться до конца, какой бы длины они ни были. Если строка была прочитана нормально, то fgets() вернет эту строку. Если нет (например, при попытке прочесть после конца файла), то fgets() вернет false.
 
Другой способ читать файлы - посимвольное чтение. Для этого применяют функцию fgetc(), которая читает из файла один символ, и возвращает его, или false при неудаче (например, конец файла):
 
$f = fopen(basename($_SERVER["PHP_SELF"]), "r");
echo "<pre>";
while (($char = fgetc($f)) !== false) echo htmlspecialchars($char);
echo "</pre>";
fclose($f);
 
Обратите внимания на то, как выполняется проверка того, что возвращается функцией fgetc(). Применяем не операцию неравенства "!=", а операцию неэквивалентности "!==". Делается это потому, что теоретически в файле возможно встречаются символы "0".
 
Символ "0" будет равен false, если их сравнивать конструкцией "!=". Если же применять операцию неэквивалентности "!==", то это сработает правильно, поскольку хоть false и 0 фактически одинаковые, но они имеют разный тип.
 
Существуют еще несколько функций для чтения из файла, такие как file_get_contents() - получает из файла содержимое определенной длины в виде одной строки с указанной позиции,
 
echo "<pre>";
$text = file_get_contents(basename($_SERVER["PHP_SELF"]));
echo htmlspecialchars($text);
echo "</pre>";
 
А также функция fread() - чтение файла порциями, fscanf() - чтение переменных из файла с определенным форматом, fpassthru() и readfile() - выдача файла сразу в страничку.
Более поднобно о функциях PHP можно узнать по адресу http://ru2.php.net/manual/ru/funcref.php (полная информация, но лишь частично на русском языке) или http://www.php.su/functions/ (полностью на русском).
 
Подводным камнем в работе с файлами в PHP есть одновременный доступ к файлу. Если несколько пользователей одновременно откроют страничку со скриптом, который записывает в файл, то возможна ситуация одновременной записи и путаницы в данных. Такого можно избежать, применяя функцию flock().
 
Функция flock() запрещает (блокирует, запирает) одновременный доступ к файлу во время выполнения операций с ними. Происходит это так: сначала в скрипте запрещается одновременный доступ, например, конструкцией flock($f, LOCK_EX), где $f - дескриптор открытого файла. Потом выполняется чтение, запись в файл, потом доступ открывается - flock($f, LOCK_UN). Например.
 
$f = fopen("file.txt", "a");
flock($f, LOCK_EX);
fwrite($f, "Нет повести печальнее на свете, ".
"чем повесть о Ромео и Джульете.");
flock($f, LOCK_UN);
fclose($f);
 
Проблема состоит лишь в том, что на большинстве систем (например, Windows с файловой системой FAT) flock() не работает. Поэтому, если созданные скрипты должны работать на большинстве систем, и одновременный доступ к файлам критичен, то применяют другие хитрости.
 
Следующий пример хронологически демонстрирует физику запирания файла. Следует открыть этот скрипт и сразу же в другом окне браузера открыть его снова.
Это приведет к тому, что первый вызов скрипта заблокирует файл, чтобы работе с ним не мешали другие вызовы скрипта. Тем временем будет вызван такой же скрипт, который попытается блокировать доступ к файлу, но не сможет.
 
Второй скрипт станет ждать, когда снова файл станет доступен, и выполнит работу с этим файлом. Обычно скрипту отводится 30 секунд на выполнение всех действий. Поэтому ждать слишком долго нельзя, но попытаться стоит, поскольку, обычно скрипты выполняются очень быстро.
 
echo "<BR>".date("H:i:s")." Пробуем открыть файл";
$file_handle = fopen('test.txt', 'r+');
echo "<BR>".date("H:i:s")." Пробуем запереть файл";
while (!flock($file_handle, LOCK_EX ¦ LOCK_NB)) {
          echo "<BR>".date("H:i:s")." Запирание не удалось. Ждем секунду и пробуем снова...";
          sleep(1); //Ждем
        } // while
echo "<BR>".date("H:i:s")." Работаем с файлом 10 секунд...";
sleep(10); //Ждем 10 секунд - имитируем длительную работу с файлом
echo "<BR>".date("H:i:s")." Отпираем файл";
fclose($file_handle); //Закрываем и отпираем
 
В итоге получим следующую хронологию событий (см. таблицу 5).
 
Таблица 5. Демонстрация работы функции flock()
 
Первый скрипт Второй скрипт
14:02:56 Пробуем открыть файл
14:02:56 Пробуем запереть файл
14:02:56 Работаем с файлом 10 секунд...
14:03:06 Отпираем файл.
14:02:58 Пробуем открыть файл
14:02:58 Пробуем запереть файл
14:02:58 Запирание не удалось. Ждем секунду и пробуем снова...
14:02:59 Запирание не удалось. Ждем секунду и пробуем снова...
14:03:00 Запирание не удалось. Ждем секунду и пробуем снова...
14:03:01 Запирание не удалось. Ждем секунду и пробуем снова...
14:03:02 Запирание не удалось. Ждем секунду и пробуем снова...
14:03:03 Запирание не удалось. Ждем секунду и пробуем снова...
14:03:04 Запирание не удалось. Ждем секунду и пробуем снова...
14:03:05 Запирание не удалось. Ждем секунду и пробуем снова...
14:03:06 Запирание не удалось. Ждем секунду и пробуем снова...
14:03:07 Работаем с файлом 10 секунд...
14:03:17 Отпираем файл.
 
И, наконец, приведу практический пример работы с файлами - счетчик посещаемости странички. Показания счетчика будем хранить в файле counter.txt. Сам код счетчика будет в файле counter.inc.php и включаться в основную страницу counter_test.php через include. Итак, скрипт counter_test.php:
 
<html>
<head>
<title>тест счетчика</title>
</head>
<body>
<?
    include_once "counter.inc.php";
?>
</body>
</html>
 
Ну и сам counter.inc.php:
 
<?
    echo "эту страницу просмотрели ";
    if(file_exists("counter.txt")) $f = fopen("counter.txt", "r+");
       else  $f = fopen("counter.txt", "w+");
    flock($f, LOCK_EX);
 
    $count = fgets($f);
    if(!$count) $count = 0;
    $count++;
    fseek($f, 0, SEEK_SET);
 
    fwrite($f, $count);
    flock($f, LOCK_UN);
    fclose($f);
 
    echo $count;
 
    $c = substr($count,strlen($count)-1,1);
    if( $c > "1" && $c < "5" ) echo " раза";
       else echo " раз";
?>
 
Сначала нам следует открыть файл counter.txt. Поскольку необходимо прочесть из файла старое показание счетчика, нарастить его на единицу и записать обратно, то файл следует открыть для чтения и записи (режимы r+ или w+).
 
Если файл counter.txt на момент выполнения скрипта не существует (т.е. страничка просматривается впервые), то его следует создать (режим w+). Если файл есть, то открываем для чтения (r+). Для проверки на наличие файла используется функция file_exists(), которая возвращает true, если файл существует.
 
После открытия запрещаем одновременный доступ функцией flock() и читаем записанное туда количество посетителей в переменную $count. Если посетителей еще не было, то fgets() вернет false, т.е. 0, тоже справедливо.
Потом увеличиваем $count на единицу, переходим в начало файла функцией fseek($f, 0, SEEK_SET) и записываем содержимое $count назад в файл. Открываем доступ - flock(), закрываем файл - fclose().
Теперь выводим в страничку показания счетчика из переменной $count. Остальной код скрипта пишет корректно слова "раз" и "раза", в зависимости от количества посетителей. Как он работает, предлагаю разобраться самостоятельно. Я лишь расскажу, как работает использованная тут функция fseek(), которая служит для установки указателя файла.
 
Первым параметром fseek() идет дескриптор файла. Вторым позиция в байтах. Третий параметр указывает, относительно чего будет происходить позиционирование.
Так, SEEK_SET означает начало файла, SEEK_CUR - текущую позицию в файле, SEEK_END - конец файла. Второй параметр при этом может быть как положительным, так и отрицательным, например, fseek($f, -10, SEEK_CUR) переместит указатель на 10 символов назад, относительно текущей позиции. Следует обратить внимание на то, что перемещение указателя за конец файла не вызовет ошибки.
 

Сканирование каталога

 
Напоследок хочу показать, как средствами языка PHP можно сканировать содержимое каталога.
 
function GetFoldersAndFiles( $currentFolder, &$aFiles ) {
 
  $oCurrentFolder = opendir( $currentFolder ) ;
  while ( $sFile = readdir( $oCurrentFolder ) )
            if ( $sFile != '.' && $sFile != '..' )
                if (!is_dir( $currentFolder ."/". $sFile ) ) {
                    $iFileSize = filesize( $currentFolder ."/". $sFile ) ;
                    $aFiles[] = $sFile;
                   }
  natcasesort( $aFiles ) ;  // Сортируем файлы
}
 
$root_dir = $_SERVER["DOCUMENT_ROOT"].'/';
$file_list  = array();
GetFoldersAndFiles( $root_dir, $file_list );
echo "<pre>";
print_r ($file_list);
echo "</pre>";
 
Функция получает два параметра: название папки, которую следует сканировать и пустой массив, в которые следует получить результат сканирования. Внутри функции выполняется открытие указанного каталога диска сервера функцией opendir().
 
Затем следует цикл, в котором за каждую итерацию выбирается очередная файловая запись функцией readdir(). Если запись окажется файлом, т.е. имя записи не равно точке или двум точкам и при этом не является папкой, то определяется размер файла функцией filesize() и название файла заносится в массив результата $aFiles.
 
Когда список файлов каталога получен, имеет смысл для удобства его отсортировать по алфавиту. Для получения типа файла можно воспользоваться функцией filetype($sFile). Но это вы уже сделаете без меня ;)
 
Для применения нашей функции GetFoldersAndFiles() достаточно получить путь корневого каталога сайта, подготовить массив и вызвать функцию. В данном случае, для демонстрации я применил функцию print_r() для вывода содержимого массива. Функция print_r() еще бывает удобна при отладке скриптов. Рекомендую запомнить ее.
 
Продолжение следует…
 
© Владислав Демьянишин
 
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ.
 

Журнал > Программирование > PHP и WEB для новичков (HTML, JavaScript, PHP, MySQL) > PHP и WEB для новичков (часть 6): Робота с файлами
 
 
 
 
 
 
На главную страницу На предыдущую страницу На начало страницы