Первый сайт на PHP

нлп

 

ФАЙЛ DO.PHP

Сценарий этого файла выполняет выбранное посетителем действие и перенаправляет его назад на основную страницу файлового менеджера.
На этой странице будет находиться только программный код. Поскольку пользователь после выполнения действия сразу будет перенаправлен на основную страницу, какой-либо дизайн данной страницы излишен.
Начнем сценарий. Точно так же в его начале запишем в переменную имя корневой папки аккаунта - дабы потом его использовать в сценариях:

<?php
$begin="files";

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

If
((strpos($folder,$begin)!=0)||(strpos($folder,"..")
!=False))
158
exit; }

Примечание:
Команда exi t полностью прекращает выполнение кода и выведение текста на странице, на которой она расположена - как если бы именно на ней страница заканчивалась. Обратите внимание, что эта команда завершает не выполнение РНР-программы, а вывод страницы - т. е. HTML-код после завершающего тэга РНР-сценария, если таковой есть, выводиться также не будет.

Хотя, бесспорно, если посетитель может загружать на свой аккаунт файлы с PHP-программами, и эти программы могут выполняться, никакие "защиты" в файловом менеджере не спасут сайт от взлома. Так что "защита" в этом сценарии является скорее демонстрацией.

УДАЛЕНИЕ, ВЫПОЛНЕНИЕ ДЕЙСТВИЯ

Для удаления файлов в РНР существует специальная команда - unlink.

Примечание:
Команда unlink (имя файла с полным путем к нему) удаляет файл, указанный в ее параметре. Если этого сделать не удастся - выводит на страницу сообщение об ошибке.
Команда rmdir (имя директории с полным путем к ней) удаляет указанную в ее параметре директорию, если она пустая. Если удаляемая папка не пуста или сценарий не имеет прав на удаление директории, то на страницу выводится сообщение об ошибке.

Удалить этой командой файлы, чьи имена переданы со страницы запроса подтверждения удаления - дело трех строк кода. Однако наш сценарий, если вы помните, позволяет удалять и целые директории. Пустую директорию можно удалить командой rmdir, - но вот как сделать директорию пустой, как ее очистить от содержимого? Ведь в директории могут быть и вложенные папки, а в них - тоже вложенные.
И снова используем рекурсию. Вот как выглядит рекурсивный алгоритм удаления всего содержимого папки:

function delfiles($fId)
$hdl=opendir($fld); while ($file = readdir($hdl)) {
if (($file!=".")&&($file!="..")) {
if (is_dir($fld."/".$file)==True) {
delfiles ($fId.»/".$file); rmdir ($fld."/".$file); }
else {
unlink ($fld."/".$file); } } }
closedir($hdl);
}

Проанализируйте его сами. Думаю, вы легко поймете логику его работы: указанная в параметре функции папка сканируется сценарием, разобранным в главе "Папкопотрошилка", и если очередной найденный в ней объект является файлом, то он удаляется, а если объект представляет собой папку, то вызывается эта же функция, но в качестве параметра ей на сей раз передается имя этой папки. Рано или поздно функция дойдет до папок, содержащих одни файлы, и рекурсия начнет прерываться. После удаления всех файлов в папке удаляется и сама папка, так как она уже пустая.
Сам код выполнения удаления выбранных пользователем файлов прост.
Если в качестве действия выбрано удаление...

if ($udal!="")

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

foreach ($fl as $i)

проверим на то, не является ли он директорией.

if (is_dir($folder."/".$i)==True)
{

Если является, то удалим все файлы в этой директории - рекурсивной функцией delf iles...

delfiles ($folder."/".$i); а затем и саму, уже опустошенную директорию - командой rmdir.
rmdir ($folder."/".$i); } else

А если этот объект, подлежащий удалению, является обычным файлом...
то для него припасена уже упомянутая выше команда unlink: unlink ($folder."/".$i); }
И так поступим с каждым объектом, имя которого передано сценарию через массив $fl, а путь к нему - через переменную Sfolder в адресной строке.
} Собственно, и все (рис. 11.10).

Рис. 11.10. Удаление выполнено

КОПИРОВАНИЕ, ВЫПОЛНЕНИЕ ДЕЙСТВИЯ

С копированием будет посложнее. Напомню, что из сценария запроса дополнительной информации передан массив Sfl, состоящий из имен копируемых файлов и папок, и переменная $rd, содержащая имя с полным путем к той папке, в которую планируется осуществить копирование. Ну и, разумеется, путь к текущей папке, в которой изначально и находятся копируемые файлы - $folder.
Сначала напишем функцию копирования целой папки со всеми вложенными в нее папками на новое место. Эта функция, как и функции удаления папки и вывода списка всех папок на аккаунте, будет рекурсивной - т. е. с вызовом самой себя из своего кода. Алгоритм ее прост:

  1. 1. Создать в папке назначения папку с тем же именем, что и у копируемой.
  2. 2. Копировать из копируемой папки в новосозданную папку все ее содержимое.
  3. 3. Если очередной копируемый объект - папка, то перейти к пункту 1, приняв в качестве папки назначения - новосозданную папку, а в качестве копируемой папки - этот самый очередной копируемый объект, оказавшийся папкой.

Рано или поздно функция дойдет до папок, содержащих только файлы, которые и станут прерывать рекурсию.
Функция будет получать полное имя (вместе с путем) папки, в которой находится копируемая папка, имя этой самой копируемой папки, и имя (вместе с путем) папки назначения копирования (т. е. той, в которую будет производиться копирование).
function copyfold ($fld, $nm, $tgt)
Внутри функции эти данные будут доступны в переменных $fld, $nm, $tgt соответственно.
Сначала создадим в папке назначения копирования папку с таким же именем, что и имя копируемой папки. Естественно, если там таковой еще нет:

if (file_exists($tgt."/".$nm)!=True)
mkdir ($tgt."/".$nm, 0666);

Примечание:
Команда mkdir (имя новой папки вместе с путем к ней, параметры доступа) создает новую папку. Если создать папку невозможно (например, уже есть папка с таким именем), то выдается сообщение об ошибке.

Параметры доступа, или атрибуты файла или папки - это восьмеричное число, сообщающее web-серверу о том, что можно делать с файлом, которому эти параметры установлены. Например позволить его читать только другим сценариям на том же аккаунте, но не посетителям из Сети. Узнать о соответствии значений параметров доступа их восьмеричному представлению можно, например в FTP-клиенте CuteFTP, воспользовавшись его окном выставления атрибутов файла (рис.11.11), доступном через пункт "Chmod" меню правой кнопки мыши любого файла на сайте. "Owner permissions" - это раз решения для других программ на том же аккаунте, a "Public permissions" - для посетителей из Интернета. Вы можете разрешить или запретить три вида действий: чтение, запись и запуск на исполнение (последнее имеет смысл только для программ).

Рис. 11.11 . "Калькулятор параметров доступа" из CuteFTP

Теперь начнем копировать файлы из исходной папки в новосоздан-ную (используем старый добрый сценарий "папкопотрошилки"): исходная папка при этом - $f Id. " /" . $шп, а новосозданная -

$tgt."/".$nm.
$hdl=opendir($fld."/".$nm) ; while ($file = readdir($hdl)) { if (($file!=" . .")&&($file! = "."))

Если очередной объект из "потрошимой" папки $f Id. " / " . $nm -директория...

if (is_dir($fld."/".$nm."/".$file)==True) {

о применим рекурсию - вновь вызовем функцию с ору fold, только араметры ей уже передадим несколько другие:

copyfold($fld."/".$nm, $file, tgt."/".$nm);

Другими словами, в качестве имени копируемой папки - указываем мя очередного обьекта, найденного в "потрошимой" папке. Остальные передаваемые функции параметры представляют собой соответственно имя "родительской" папки для копируемой (это имя "потрошимой" папки), имя папки назначения (составлено из исходного имени апки назначения и имени копируемой папки, эта папка, кстати, как вы, наверное, помните, была создана командой mkdir в начале работы функции).
Если же очередной объект из "потрошимой" папки $f Id." /" . $nm является обычным файлом...

}
else
{

то просто скопируем его из исходной папки в папку назначения -и дело с концом.
copy ($fld."/".$nm."/".$file, $tgt."/".$nm."/".$file);

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

Обратите внимание, что, во-первых, имена файлов нужно указывать вместе с путем к ним, а, во вторых, имя "файла на месте назначения" - это то имя, которое будет у копируемого файла после завершения копирования, а отнюдь не только имя папки, в которую он копируется:

copy ("/files/data/filel.htm", "/files/last/'filel.htm");

В качестве имен файлов могут выступать содержащие их строковые переменные.
Работа функции завершена. Закроем открытые циклы и условные операторы...

}
} }

и открытую "потрошимую" директорию:

closedir($hdl); }

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

if ($copy!="")
{

Переберем все объекты, подлежащие копированию: foreach ($fl as $i)
{ Если очередной объект - папка...

if (is_dir($folder."/".$i)==True)

то запустим функцию копирования папки, разобранную выше, передав ей нужные параметры.

copyfold($folder, $i, $rd);

Комментарий:
Как вы помните, на страницу запроса информации мы поместили специальный код, который составлял список папок, могущих быть пунктом назначения для копирования. В этом коде также отслеживалось, не является ли очередная папка из этого списка вложенной папкой какой-нибудь из копируемых папок, и таковые папки из списка удалялись. Это и понятно, копирование папки в свою же вложенную папку приведет либо к "подвисанию" web-сервера, если он не очень хорошо сконфигурирован, либо к забиванию аккаунта множеством папок и файлов (это связано с тем, что рекурсивная функция копирования в таком случае войдет в бесконечный цикл).

Однако исключение вложенных папок из списка возможных пунктов назначения на странице запроса дополнительной информации не гарантирует невозможности начала такого копирования! Злоумышленник может сохранить страницу файлового менеджера со списком пунктов назначения копирования на своем компьютере, а потом, исправив относительные ссылки на абсолютные, добавить в локальную копию страницы новый возможный пункт назначения копирования, т. е. имя папки, вложенной в копируемую папку, а затем, пометив именно эту папку, запустить копирование, тем самым навредив владельцу сайта, на котором расположен файловый менеджер.
Чтобы сделать такие действия невозможными, на странице выполнения действия можно проверять, не вложена ли папка назначения копирования в копируемую папку. Для этого проще всего сравнить полные имена этих папок. Если полное имя копируемой папки можно найти в начале полного имени папки назначения, то копирование выполнять нельзя. Для сравнения проще всего воспользоваться функцией strpos (справку по ней смотрите в начале этой главы):

if (!(strpos ($rd, $?older."/".$i)===0)) { copyfold($folder, $i, $rd);
}

Хотя, бесспорно, в данном сценарии такая "защита", скорее, является демонстрационной - если посетитель имеет возможность загружать на аккаунт любые файлы, то никто не помешает ему загрузить на него какую-нибудь вредоносную программу на РНР и выполнить ее.
Если же очередной объект - файл...

}
else {
то его скопируем.
copy ($folder."/".$i, $rd."/".$i);

Осталось закрыть фигурными скобками запущенные циклы и условные операторы.

} }
}

Процесс копирования завершен (рис. 11.12).

Рис. 11.12. Файлы скопированы

ПЕРЕИМЕНОВАНИЕ, ВЫПОЛНЕНИЕ ДЕЙСТВИЯ

В том случае, если посетитель выбрал в качестве требуемого действия переименование, то на странице запроса дополнительной информации ему пришлось ввести новые имена для тех файлов и папок, переименовать которые он пожелал. Сценарию же выполнения действия передалось два массива со старыми и новыми именами этих обьектов -$afl и $rfl соответственно.

if ($ren!="")
{

Переберем все элементы массива со старыми именами - $af 1. Поскольку для совершения собственно переименования для каждого элемента массива со старыми именами нам также потребуется элемент массива $rf 1 с новыми именами под тем же порядковым номером, то перебирать эти элементы будем подряд, по номерам - с помощью цикла for:

for ($i = 0; $i < sizeof($afl); $i++)

Если новое имя не совпадает со старым и не является пустой строкой...

if (($rfl[$i]!="")&($rfl[$i]!=$afl[$i]))

то можно переименовывать. Однако посетитель вполне может в новом имени указать символы, которые недопустимы для имен файлов - от пробелов до слэшей. Возвращать посетителя на этап ввода имени с выведением ему сообщения о недопустимых символах не будем - просто заменим все такие символы на знаки подчеркивания: "_". Тем более что такую замену можно совершить специальной командой PHP - strtr:

(Количество знаков подчеркивания в третьем параметре функции равно количеству указанных во втором недопустимых символов.)

Примечание:
Функция strtr("строка", "заменяемые символы", "заменяющие символы") заменяет в строке, указанной в ее первом параметре, символы, приведенные в строке в ее втором параметре, на символы из строки в ее третьем параметре, стоящие в этой строке на тех же местах, что и заменяемые символы в строке в ее втором параметре.

Если столь длинная фраза вас смутила, то можно сказать проще: во втором и в третьем параметрах функции приводятся строки, состоящие из определенных символов.
Функция просматривает строку в первом параметре символ за символом. Если очередной символ этой строки встречается и в строке во втором параметре, то функция:

1. Смотрит, какой символ стоит в строке в ее третьем параметре на том же самом месте от начала строки, что и встреченный символ в строке во втором параметре.
2. Заменяет в строке в первом параметре этот очередной символ на найденный в третьей строке. Например результатом выполнения функции

strtr ("Оабвапабаво", "ба", "ру")

будет строка

"Оурвупуруво"

Если строки во втором и третьем параметрах разной длины, то лишние символы в более длинной строке игнорируются.

Поскольку на всех местах в строке в третьем параметре этой функции стоит знак подчеркивания, то все недопустимые символы, указанные в строке в ее втором параметре, будут на него заменены.
И наконец, само переименование - командой rename:

rename ($folder."/".$af1[$i], $folder."/".$rfl[$i]) ;

Примечание:
Команда rename (имя переименовываемого файла вместе с путем, его новое имя вместе с путем) переименовывает файл. Если файл переименовать по какой-либо причине не удалось (скажем, переименовываемый файл не существует или в папке уже имеется файл с таким же именем, что и новое имя), то выводится сообщение об ошибке.

Вот и все (рис. 11.13).
} } }

Комментарий:
Если при переименовывании файла в качестве нового имени задать имя уже существующего файла, то команда rename выдаст сообщение об ошибке (рис. 11.14). Это и испортит внешний вид страницы, и затруднит действия пользователя. Можно, конечно, перед командой rename поставить знак @ - тогда сообщений об ошибке не будет (стандартный способ запрета на вывод такого сообщения), но тогда пользователь даже не узнает об ошибке.

Рис. 11.13. Файлы переименованы

Рис. 11.14. Ошибка при переименовании - сообщение РНР

А можно использовать несколько оригинальное решение - заранее посмотреть, есть ли в папке файл с таким же именем, как и то, которое пользователь желает дать переименовываемому файлу, и если есть, то добавить к новому имени файла спереди знак подчеркивания - "_". Код, реализующий это, прост:

while (file_exists($folder."/".$rfl[$i])==True)
{
$rfl[$i] = "_".$rfl[$i];
}

Как нетрудно понять, он добавляет в начало нового имени файла знак "_", если файл с таким именем уже существует в той же папке, куда предполагается копировать файл. Если же и таковой файл - со знаком "_" в начале -уже в папке имеется, то к новому имени добавляется еще один такой символ, и так до тех пор, пока новое имя не станет уникальным.

СОЗДАНИЕ НОВОЙ ПАПКИ, ВЫПОЛНЕНИЕ ДЕЙСТВИЯ

Создание новой папки выполняет самый маленький фрагмент кода во всем сценарии. Даже меньше того, что выводил запрос имени для этой новой папки.

if ($md!="") {

Как и в сценарии переименования файла, исключим из имени новой папки недопустимые символы...

$newname=strtr($newname, " []{},/\!@#$%л&*",

и создадим папку - командой mkdir, не забыв указать в ее параметрах полный путь к новой папке:

mkdir ($folder."/".$newname, 06 66);

Описание команды mkdir вы уже видели выше - в подразделе "Копирование, выполнение действия". Собственно, и все (рис. 11.15).

Рис. 11.15. Папка создана

ЗАВЕРШЕНИЕ ДЕЙСТВИЯ

Ну и по окончании выполнения действий перенаправим посетителя на основную страницу файлового менеджера - в ту же директорию, в которой он и находился, передав сценарию на основной странице путь к ней через переменную fold в адресной строке. Для этого воспользуемся командой Header, передающей браузеру заголовки.
Если на странице запроса дополнительной информации посетителем была нажата кнопка "Отмена", то ни один из вышеприведенных блоков исполнения действия выполнен не будет - ни одна из переменных Sudal, $copy, $ren, $md определена не будет. А эта команда выполнится - т. е. после нажатия кнопки "Отмена" на странице запроса дополнительной информации посетитель окажется на основной странице файлового менеджера.

Header ("Location: index.php?fold=$folder");

Примечание:
Заголовок - это данные, передаваемые браузеру до передачи самой web-страницы, сообщающие ему некоторые параметры передаваемого файла или определенные команды. Список всех возможных заголовков, которые обязаны поддерживать современные браузеры, можно найти в спецификациях протокола HTTP - они есть, например, на сайте http://www.w3.org. PHP-команда Header выполняет всего одно действие -она просто передает то, что указано в ее параметре, в браузер, запросивший страницу, на которой она находится, в качестве заголовка.
Заголовок "Location" осуществляет перенаправление браузера на страницу, указанную в его параметре.

Сценарий можно завершить - посетитель уже на другой странице...
В результате после того как посетитель на странице запроса дополнительной информации нажмет кнопку начала действия, после небольшой паузы (длящейся ровно столько времени, сколько надо web-серверу для совершения этого действия) он вновь окажется на главной странице файлового менеджера, а заказанное им действие будет выполнено.
Однако если в процессе выполнения сценария возникнет ошибка (скажем, новую папку создать не удастся, так как окажется, что папка с таким же именем уже существует, или возникла проблема при переименовании файла - см.рис.11.14), то на страницу будет выведено сообщение об ошибке, а команда Header не сработает - и тоже выдаст сообщение об ошибке (так как заголовок можно отправлять только до какого-либо вывода на страницу). Если вас это не устраивает - то можете либо предварять все команды символом "@" (он запрещает выводить сообщения об ошибках), либо снабдить страницу со сценариями выполнения действия в самом низу небольшим комментарием (с информацией вроде "Возникла ошибка, извините...") и ссылкой на главную страницу файлового менеджера.
Вот, собственно, и весь сценарий. Если желаете - можете его использовать в своих разработках. Или просто, просмотрев текст с комментариями, составить себе представление о том, как составлять программы на РНР.
Разумеется, сценарий можно улучшать. Первые два предложения по улучшению напрашиваются сразу - добавить в него возможность загрузки файлов и блок авторизации доступа. Код программ, реализующих эти предложения, уже рассматривался в предыдущих главах, так что вряд ли их реализация будет для вас сложной. (Не 174
забудьте разве что вставить код проверки содержимого авторизационных переменных на все страницы файлового менеджера, а не только на основную.) Можно также научить сценарий выдавать осмысленные сообщения об ошибках, если они возникнут, заставить его выдавать подтверждения, скажем, в случае существования в папке назначения копирования файлов и папок, одноименных копируемым - дабы предотвратить случайное их переписывание. Но это все остается уже на ваше усмотрение.

 
Назад Начало Вперед