Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

PHP5_nachinayushim

.pdf
Скачиваний:
29
Добавлен:
20.03.2015
Размер:
26.79 Mб
Скачать

314 Глава 7

в начало файла (чтобы после этого можно было снова записывать в этот файл дан+ ные) и закрыть оператор if:

if(!($fp = fopen($counter_file, "r+"))){ die("Невозможно открыть файл $counter_file");

}

$counter = (int) fread($fp, filesize($counter_file)); $counter++;

echo "Вы – посетитель № $counter.";

echo "Последнее посещение страницы состоялось $date_str"; rewind($fp);

}

Далее следует оператор else; если файл count.dat отсутствует, то его необходимо создать с помощью функции fopen(), указав для нее режим только для записи. После этого счетчик инициализируется значением 1, которое выводится в сообщении:

else{

if(!($fp = fopen($counter_file, "w"))){ die("Невозможно открыть файл $counter_file");

}

$counter = 1;

echo "Вы – посетитель№ $counter.";

}

Наконец, в файл count.dat записывается новое значение счетчика, после чего файл закрывается с помощью вызова fclose():

fwrite($fp, $counter); fclose($fp);

Принадлежность и права доступа к файлам

Каким же образом можно получить информацию о принадлежности файла и правах доступа к нему? В Unix+подобных системах, таких как Linux, все файлы связываются с определенными пользователями и группами пользователей и им (файлам) назначают+ ся флаги, определяющие пользователей, которые имеют полномочия на чтение, запись или выполнение данных файлов. Некоторые файловые системы Windows (но не все) работают аналогично. Например, Windows NTFS (NT File System) позволяет назначать принадлежность и права доступа, тогда как в Windows 98 такой возможности нет.

В Unix определяются группы пользователей с тем, чтобы можно было легко рас+ ширять полномочия на определенные группы пользователей без необходимости на+ значать эти полномочия каждому пользователю системы по отдельности. Например, несколько пользователей, работающих над одним проектом, могут использовать фай+ лы совместно друг с другом, но только в пределах определенной группы.

Каждое из этих прав (чтение, запись и выполнение) может быть предоставлено (или наоборот отменено):

владельцу файла: по умолчанию владельцем файла является пользователь сис+ темы, создавший этот файл;

группе пользователей: по умолчанию это группа, к которой принадлежит вла+ делец файла;

всем пользователям: все пользователи, имеющие учетные записи в системе.

Файлы и каталоги 315

Пользователи и группы в Unix идентифицируются по идентификационным номе+ рам. Если в PHP+сценарии необходимо получить информацию о пользователе по его идентификатору, то это можно сделать с помощью функции posix_getpwuid(), ко+ торая возвращает ассоциативный массив со следующими данными:

Имя

Описание

 

 

name

Имя учетной записи shell данного пользователя

passwd

Зашифрованный пароль пользователя

uid

ID-номер пользователя

gid

Идентификатор группы данного пользователя

gecos

Перечень (поля разделены запятыми), содержащий полное имя пользователя,

 

рабочий телефон, номер офиса и домашний телефон. В большинстве систем

 

доступно только полное имя пользователя

dir

Абсолютный путь к начальному каталогу данного пользователя

shell

Абсолютный путь к пользовательской оболочке (shell) по умолчанию

 

 

Другая PHP+функция, posix_getgrgid(), возвращает ассоциативный массив ин+ формации о группе по идентификатору данной группы. В массиве содержатся сле+ дующие элементы:

Имя

Описание

 

 

name

Имя группы

gid

Идентификатор (ID) группы

members

Количество пользователей, принадлежащихданной группе

 

 

Для получения из PHP+сценариев полезной информации о файлах можно исполь+ зовать следующие три функции (каждая из них принимает единственный аргумент ++++++

имя файла):

fileowner(): возвращает пользовательский идентификатор владельца задан+ ного файла;

filegroup(): возвращает идентификатор группы владельца заданного файла;

filetype(): возвращает тип заданного файла (fifo, char, dir, block, link, file или unknown).

Например, чтобы проверить, является ли данный файл файлом или каталогом, можно использовать функцию filetype() (убедитесь, что файл counter.php суще+ ствует, а если нет, то создайте его):

<?php //file_type.php

$filename = "./counter.php"; $filegroup = filegroup($filename); $fileowner = fileowner($filename); $filetype = filetype($filename);

if ($filetype == 'dir') {

echo "$filename является каталогом.";

} else if ($filetype == 'file') {

echo "$filename является файлом.<br>"; } else {

316 Глава 7

echo "$filename не является ни файлом ни каталогом.<br>";

}

echo "$filename принадлежит пользователю № $fileowner и группе № $filegroup.<br>"; echo "<br>Информация о пользователе $fileowner<br>";

$user_info_array = posix_getpwuid($fileowner);

foreach ($user_info_array as $key => $val) { echo "$key => $val<br>";

}

echo "<br>Информация о группе $filegroup<br>"; $group_info_array = posix_getgrgid($filegroup);

foreach ($group_info_array as $key => $val) { echo "$key => $val<br>";

}

?>

На рис. 7.5 показан пример работы сценария на Windows+машине. Обратите вни+ мание на ошибку, возникающую при попытке использовать на Windows+машине функцию, которая работает только на Linux+машинах. Вообще данный сценарий луч+ ше запускать на Linux+машине, а для получения подобной информации о файлах на Windows+машинах можно использовать следующий сценарий.

Рис. 7.5.

Функции is_dir() и is_file()

Часто сценарий должен определить, является ли файл каталогом (как уже отмеча+ лось, каталоги ++++++ тоже файлы, но файлы особого типа). Предположим, например, что требуется создать сценарий, который проходит по иерархии каталогов. Для этого необ+

Файлы и каталоги 317

ходимо определить, является ли какой+либо файл каталогом, с тем, чтобы можно было войти в него и продолжить перемещение по иерархии. Точно так же, если требуется просматривать только файлы в каталоге, необходимо определить, какой файл действи+ тельно является файлом. Для решения обеих задач в PHP предусмотрены две функции:

is_dir() специально предназначена для работы с каталогами; она возвращает True, если заданное имя файла ссылается на какой+либо каталог;

is_file() возвращает True, если заданное имя файла ссылается на обыч+ ный файл.

Предыдущий сценарий очень легко переписать и заставить его выводить те же ре+ зультаты, но с помощью данных функций:

<?php

$filename = "./counter.php";

if(is_dir($filename)) { echo "$filename - каталог.";

}else if (is_file($filename)) { echo "$filename - файл.";

}else {

echo "$filename - не файл и не каталог.";

}

?>

Как уже отмечалось, функция fileatime() на Windows+машине возвращает дату последнего изменения файла; а так как Windows не поддерживает принадлежность файлов, обе функции filegroup() и fileowner() возвращают нуль.

Практика Получение информации о Windows-файлах

Следующий код сценария отображает некоторые свойства заданного файла.

<?php

function date_str($timestamp)

{

$date_str = getdate($timestamp); $year = $date_str["year"];

$mon = $date_str["mon"]; $mday = $date_str["mday"]; $hours = $date_str["hours"];

$minutes = $date_str["minutes"]; $seconds = $date_str["seconds"];

return "$hours:$minutes:$seconds $mday/$mon/$year";

}

function file_info($file)

{

$file_info_array["filesize"] = number_format(filesize($file)) . " байт."; $file_info_array["filectime"] = date_str(filectime($file)); $file_info_array["filemtime"] = date_str(filemtime($file)); if(!isset($_ENV[WINDIR])){

$file_info_array["fileatime"] = date_str(fileatime($file)); $file_info_array["filegroup"] = filegroup($file); $file_info_array["fileowner"] = fileowner($file);

}

$file_info_array["filetype"] = filetype($file); return $file_info_array;

}

318 Глава 7

$filename = "./count.dat"; $file_info_array = file_info($filename);

echo "<center>Свойства файла $filename</center>"; foreach($file_info_array as $key=>$val){

echo ucfirst($key) . "=>". $val . "<br>";

}

?>

На рис. 7.6 показана работа данного сценария на Windows+машине.

Рис. 7.6.

Как это работает

Несколько строк кода из последней версии счетчика можно поместить в функцию (назовем ее date_str()), которая возвращает отформатированную строку с датой:

function date_str($timestamp) { $date_str = getdate($timestamp); $year = $date_str["year"];

$mon = $date_str["mon"]; $mday = $date_str["mday"]; $hours = $date_str["hours"];

$minutes = $date_str["minutes"]; $seconds = $date_str["seconds"];

return "$hours:$minutes:$seconds $mday/$mon/$year";

}

Файлы и каталоги 319

В следующей функции, file_info(), вводится несколько новых функций, кото+ рые возвращают полезную информацию о заданных файлах:

function file_info($file)

{

$file_info_array["filesize"] = number_format(filesize($file)) . " байт."; $file_info_array["filectime"] = date_str(filectime($file)); $file_info_array["filemtime"] = date_str(filemtime($file)); if(!isset($_ENV[WINDIR])){

$file_info_array["fileatime"] = date_str(fileatime($file)); $file_info_array["filegroup"] = filegroup($file); $file_info_array["fileowner"] = fileowner($file);

}

$file_info_array["filetype"] = filetype($file); return $file_info_array;

}

Переменная среды $_ENV[WINDIR] устанавливается только на Windows+ платформах. К ней можно получить доступ через $_ENV и использовать как флаг для указания того, что сценарий выполняется в Windows. Если эта переменная установле+ на, то в результирующий массив информации не попадают поля, которые в Windows не поддерживаются. Обратите внимание, чтобы сделать эту переменную видимой внутри функции при условии использования ключевого слова global, также можно использовать старую ссылку на нее, $HTTP_ENV_VARS. Хотя этот способ работоспосо+ бен, он вытесняется использованием суперглобального массива $_ENV. Преимущест+ во использования суперглобального массива $_ENV заключается в том, что эту пере+ менную не требуется объявлять как глобальную, чтобы получить к ней доступ внутри какой+либо пользовательской функции.

Наконец, задается файл, вызывается функция file_info() и распечатывается содержимое каждого элемента возвращаемого ею массива:

$filename = "./count.dat"; $file_info_array = file_info($filename);

echo "<center>Свойства файла $filename</center>"; foreach($file_info_array as $key=>$val){

echo ucfirst($key) . "=>". $val . "<br>";

}

Пользовательские функции для работы с файлами

Теперь, зная, как определить владельца файла, можно выполнять некоторые про+ стейшие действия, такие как копирование, переименование и удаление файлов. Что+ бы выполнять эти операции, необходимо выяснить точный путь к файлу. Для этого PHP предоставляет несколько очень полезных функций, например, basename().

Разделение имени файла и пути

Часто полезной оказывается возможность отделять имя файла от пути к его ката+ логу; именно это и делает функция basename(), которая принимает полный путь к файлу и возвращает только его имя. Например, в следующем вызове переменной $filename присваивается значение "index.html":

$filename = basename("home/james/docs/index.html");

320 Глава 7

Можно указать путь к какому+либо каталогу. В таком случае функция возвращает самый правый каталог в пути. В следующем примере переменной $dirname присваи+ вается значение "docs":

$dirname = basename("home/james/docs");

В сущности, функция basename() представляет собой средство для получения по+ следней подстроки после крайнего правого символа косой черты.

Копирование, переименование и удаление файлов

PHP также позволяет копировать, переименовывать и удалять файлы. Для выпол+ нения этих операций предназначены функции copy(), rename() и unlink().

Функция copy() принимает два строковых аргумента, ссылающихся на исходный и конечный файл соответственно. Следующий вызов функции копирует исходный файл copyme.txt в конечный файл copied.txt:

if(!copy("./copyme.txt", "copied.txt")) die("Невозможно скопировать файл copyme.txt в copied.txt!");

Функция rename() используется для переименования файлов:

if(!rename ("./address.dat", "address.backup"))

die("Невозможно переименовать файл address.dat в address.backup!");

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

<?php //hit_counter12.php

$counter_file = "./count.dat"; $counterbackup_file = "./count.backup"; $backup_interval = 24*60*60;

if(file_exists($counter_file)) {

$date_str = getdate(fileatime($counter_file)); $year = $date_str["year"];

$mon = $date_str["mon"]; $mday = $date_str["mday"]; $hours = $date_str["hours"];

$minutes = $date_str["minutes"]; $seconds = $date_str["seconds"];

$date_str = "$hours:$minutes:$seconds $mday/$mon/$year";

if((time() - fileatime($counterbackup_file)) >= $backup_interval) { @copy($counter_file, $counterbackup_file);

}

if(!($fp = fopen($counter_file, "r+")))

die("Невозможно открыть файл $counter_file"); $counter = (int) fread($fp, filesize($counter_file));

$counter++;

echo "Вы – посетитель № $counter.

Последнее посещение страницы состоялось $date_str";

rewind($fp);

}

else {

if(!($fp = fopen($counter_file, "w")))

die("Невозможно открыть файл $counter_file");

$counter = 1;

echo "Вы – посетитель № $counter.";

}

Файлы и каталоги 321

fwrite($fp, $counter); fclose($fp);

?>

На рис. 7.7 показан результат (в Windows+системах корректное время доступа не отображается).

Рис. 7.7.

Переменной $backup_interval присваивается значение, равное количеству секунд в сутках (24*60*60). Во время работы сценарий проверяет время последнего доступа к файлу данных счетчика. Кроме того, сценарий проверяет текущее время с помощью функции time(), которая возвращает текущую временную метку Unix, т.е. время в секундах, истекшее с 00:00:00 1 января 1970 года.

Если последний доступ к файлу состоялся более $backup_interval секунд назад, то с помощью функций unlink() и copy() создается резервная копия существующе+ го файла данных (чтобы предотвратить удаление старых резервных копий файла данных, к имени каждого файла можно присоединить текущее время):

@copy($counter_file, $counterbackup_file);

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

Функция unlink() принимает единственный строковый аргумент ++++++ имя файла, который необходимо удалить. Например, если требуется удалить файл trash.txt в текущем каталоге, вызов может выглядеть так:

if(!unlink("./trash.txt")) die ("Невозможно удалить файл trash.txt!");

322 Глава 7

Почему эта функция называется unlink()? Использование такого имени для функции удаления может показаться странным, особенно для тех, кто не сталкивался с Unix+подобными системами. Почему эту функцию не назвали просто delete? В файловой системе Unix имеется различие между физическим расположением файлов на носителе и соответствующей структурой каталогов. Вполне возможно, а на практике в Unix это очень распространено, что различные части системы каталогов хранятся на физиче+ ски обособленных устройствах. При сохранении файла в определенной точке файло+ вой системы эта точка дерева каталогов (которая называется индексный узел (inode)) связывается с физическим местом хранения данных файла. В Unix путь к файлу фак+ тически представляет собой уникальный идентификатор для одного из этих узлов.

Примечательно то, что в Unix можно связать несколько таких точек (т.е. несколь+ ко индексных узлов) с одними и теми же данными. Это делается с помощью так назы+ ваемых жестких ссылок (hard link) (подробнее жесткие ссылки описываются в тексте man+страниц для команды ln). Эти данные можно получить до тех пор, пока сущест+ вует хотя бы одна ссылка на них. Но если все ссылки будут уничтожены, то будут так+ же уничтожены и сами данные. Таким образом, с помощью команды unlink() фак+ тически уничтожается одна из этих ссылок на данные.

Обычно существует только одна жесткая ссылка на данные файла, поэтому вызов упомянутой функции на самом деле удаляет файл. Если где+нибудь в системе существует другая ссылка, то файл фактически не удаляется. В Windows связать с одними и теми же данными несколько точек файловой системы невозможно, поэтому вызов unlink() для файла во всех случаях эквивалентен его удалению.

Многие версии PHP для Windows вообще не поддерживают функцию unlink(). Существуют различные пути решения данной проблемы, но они значительно отличают+ ся в зависимости от используемого Web+сервера. Например, при использовании Web+ сервера Apache можно передать команду del filename непосредственно Windows по+ средством функций system() или exec(). Поэтому в комбинации Windows/Apache можно имитировать функцию unlink() с помощью следующего кода:

if(!isset($_ENV[WINDIR])) {

$userfile = str_replace("/", "", $file);

exec("del $file");

if(file_exists("$file")) die("Невозможно удалить файл $file.");

}

else if(!@unlink($file)) { die("Невозможно удалить файл $file.");

}

Поскольку команда del ожидает присутствия в заданном пути символов обратной косой черты, в передаваемом пути с помощью функции str_replace() следует за+ менить все символы косой черты.

Работа с каталогами

PHP позволяет манипулировать каталогами почти так же, как файлами, и предостав+ ляет для этого множество эквивалентных функций. В некоторых из них используется дескриптор каталога, в других ++++++ строка, содержащая имя каталога. Дескриптор катало+ га подобен дескриптору файла; он представляет собой указывающее на каталог целое значение, которое можно получить, указав каталог в вызове функции opendir():

$dp = opendir ("/home/james/");

В случае возникновения ошибки данная функция возвращает False. Закрыть ката+ лог можно с помощью функции closedir(), передав ей соответствующий дескриптор:

closedir($dp);

Файлы и каталоги 323

Функция readdir() возвращает следующий пункт в списке открытого каталога. Этот список включает в себя такие пункты, как . (используется для указания текущего каталога) и .. (родительский по отношению к данному каталог). PHP+интерпретатор хранит внутренний указатель, связанный со следующим пунктом в списке. Этот указа+ тель подобен указателю позиции в файле, в которой должна быть выполнена следую+ щая файловая операция.

Практика Создание листинга каталога

Ниже показано, как создать цикл для получения всех пунктов в заданном каталоге:

<?php //dir_list.php

$default_dir ="x:/home/localhost/www/php5/Ch07";

if(!($dp = opendir($default_dir))) die("Невозможно открыть каталог $default_dir.");

while($file = readdir($dp))

if($file != '.' && $file != '..') echo "$file<br>"; closedir($dp);

?>

На рис. 7.8 показан пример выполнения данного сценария.

Рис. 7.8.

Как это работает

Сначала сценарий определяет дескриптор заданного каталога (в данном случае x:/home/localhost/www/php5/Ch07), а затем начинает цикл, который считывает записи из каталога и (если это не "." и не "..") распечатывает их. Цикл обусловлен

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]