Задание 8. Установка и начальная настройка СУБД MySQL 1. Если на компьютере уже имеется установленная версия MySQL, ее следует деинсталлировать. 2. Скопируйте файл дистрибутива mysql-essential-4.1.14-win32.msi в каталог C:\install\. 3. Процесс установки: a. Запустите установку программы из скопированного дистрибутива, используя средства Windows. b. Приветствие инсталлятора («Welcome…») – нажмите кнопку Next. c. Выберите тип установки Typical. d. В окне Ready to Install – нажмите кнопку Next. e. По окончании процедуры установки выберите вариант Skip Sign-Up для отказа от подписки. f. Получив сообщение о завершении установки, нажмите кнопку Finish. g. В мастере конфигурации выберите тип Standard. h. В окне Опции Windows – кнопку Next. i. В окне Security Options снимите флажок Modify Secure Settings и нажмите кнопку Next. j. В окне Ready to Execute нажмите кнопку Execute. k. По окончании настройки нажмите кнопку Finish. 4. Выполните проверку работоспособности СУБД: Создайте и выполните из командной строки PHP-скрипт test_db.php следующего вида: <? $DN = "test"; $HN = "localhost"; $UL = "root"; $UP = ""; mysql_connect($HN, $UL, $UP); mysql_selectdb( $DN ); $rez = mysql_query("select 123;"); $row = mysql_fetch_array( $rez ); print_r( $row ); ?> При нормальной работе СУБД скрипт должен вывести следующий текст: Array ( [0] => 123 [123] => 123 ) Задание 9. Создание гостевой книги с применением базы данных 1. Создайте и выполните в каталоге C:\phpprog\ скрипт create_gb.php следующего вида: <? 1 $DN = "test"; $HN = "localhost"; $UL = "root"; $UP = ""; mysql_connect($HN, $UL, $UP); mysql_selectdb( $DN ); $rez = mysql_query("drop table if exists guestbook;"); $rez = mysql_query(" create table guestbook ( id int auto_increment, name varchar(255), message text, primary key (id) ); "); ?> Скрипт предназначен для создания таблицы MySQl, в которой будут размещаться данные гостевой книги. 2. Создайте каталог C:\webroot\exercises\, если он не существует. 3. В каталоге создайте PHP-скрипт gb2.php следующего вида: <?php header("Cache-Control: no-cache"); header("Pragma: no-cache"); header("Content-Type: text/html; charset=windows-1251"); $DN $HN $UL $UP = = = = "test"; "localhost"; "root"; ""; mysql_connect($HN, $UL, $UP); mysql_selectdb( $DN ); ?> <html> <body> <?php if( $_REQUEST['send'] ) { $name = $_REQUEST['name']; $message = $_REQUEST['message']; if( ! get_magic_quotes_gpc() ) { $name = addslashes( trim( $name ) ); $message = addslashes( trim( $message ) ); } if( strlen($message) > 0 ) { $rez = @mysql_query("insert into guestbook (name, message) values ('$name', '$message');"); } } ?> <center><h1>Гостевая книга</h1></center> 2 Добавление нового сообщения: <form method=post> <input type=hidden name=send value=1> Имя: <input type=text name=name value="Незнакомец" size=50><br> Текст сообщения: <textarea name=message cols=30 rows=4></textarea><br> <input type=submit value="Послать сообщение"> </form> Ранее оставленные сообщения: <table width=100%> <?php $rez = @mysql_query("select * from guestbook order by id desc;"); while( $row = @mysql_fetch_array( $rez ) ) { $name = nl2br( htmlspecialchars( $row['name'] ) ); $message = nl2br( htmlspecialchars( $row['message'] ) ); print "<tr><td>$name</td><td>$message</td></tr>\n"; } ?> </table> </body> </html> 4. 5. 6. 7. 8. При создании скрипта за основу был взят скрипт gb1.php. Новый скрипт отличается тем, что хранит данные не в файле, а в базе данных. Обратитесь к созданному скрипту через броузер (по адресу http://localhost/exercises/gb2.php). Заполните в форме поля Имя и Текст сообщения и нажмите кнопку Послать сообщение. В странице должно отобразиться введенное сообщение. Введите еще несколько сообщений. Внимательно изучите текст скрипта. Сравните его со скриптом gb1.php. Некоторые операции в данном скрипте не просто отличны от операций в gb1.php, но прямо противоположны им. Попытайтесь обнаружить такие случаи. Встройте созданную гостевую книгу в свой сайт. Для этого добавьте в сайт новую страницу, оформленную по общему шаблону, и скопируйте в нее необходимые элементы из созданного скрипта. Способ встраивания может различаться для шаблонов разных типов. Пример. Передача файлов клиенту В данном примере обеспечивается доступ клиенту к файлам из заданного списка. Доступ клиента к содержимому файлов осуществляется через скрипт «getfile.php». В данном примере файлы размещаются в подкаталоге «files», но клиенту их истинное местонахождение неизвестно (сами файлы можно разместить и за пределами каталога web-сервера, тогда обращение клиента к ним напрямую будет невозможно). Такой способ доступа позволяет скрипту контролировать загрузку файлов клиентом. Например, можно потребовать аутентификации пользователя или просто регистрировать некоторые статистические данные – какой файл, когда, и кем был загружен. 3 В листинге 10 показано содержимое файла «files.txt», который определяет список файлов и их медиа-типов, разделенных символом «|». img01.gif | image/gif zippedfile.zip | application/zip img02.jpg | image/jpeg notexists.gif | image/gif Листинг 10 (файл «files.txt») Скрипт «list.html» (листинг 11) создает HTML-таблицу, в которой отображаются данные о файлах из указанного списка, а также создаются гиперссылки (тег «a») для возможности открытия/сохранения файлов. Если файл содержит изображение (в названии его типа присутствует строка «image/»), это изображение также вставляется в таблицу. Для считывания списка файлов в массив «$fileslist» используется включаемый скрипт «loadlist.php» (листинг 12). <? // Загрузить список файлов include_once("loadlist.php"); // Начало таблицы print <<<ENDTEXT <table align=center border=1 width=95%> <tr align=center> <th>File name</th> <th>Media type</th> <th>Image</th> </tr> ENDTEXT; foreach($fileslist as $name => $type) { // Обращение к файлам - через скрипт // getfile.php $fullname = 'getfile.php/' . $name; $imagetag = ''; // Если картинка - показать ее if( strstr($type, 'image/') ) $imagetag = "<img src=\"$fullname\">"; // Строка таблицы print <<<ENDTEXT <tr align=center> <td><a href="$fullname">$name</a></td> <td>$type</td> <td>$imagetag</td> </tr> ENDTEXT; } 4 // Конец таблицы print <<<ENDTEXT </table> ENDTEXT; ?> Листинг 11 (файл «list.html») <? $fileslist = array(); // Прочитать строки из файла в массив $lines = file("files.txt"); foreach ($lines as $item) { // Разобрать строки на пары: // имя файла | тип list($name, $type) = explode("|", $item); // Убрать лишние пробелы $name = trim($name); $type = trim($type); // Занести в ассоциативный массив if( $name ) $fileslist[$name] = $type; } ?> Листинг 12 (файл «loadlist.php») В листинге 13 показан HTML-код, формируемый скриптом «list.html», а на рис. 8 – внешний вид этого документа при отображении броузером. Имена файлов в первой колонке таблицы служат гиперссылками для доступа к этим файлам. Для файлов, которые содержат изображения, эти изображения показываются в третьей колонке. <table align=center border=1 width=95%> <tr align=center> <th>File name</th> <th>Media type</th> <th>Image</th> </tr><tr align=center> <td><a href="getfile.php/img01.gif">img01.gif</a></td> <td>image/gif</td> <td><img src="getfile.php/img01.gif"></td> </tr><tr align=center> <td><a href="getfile.php/zippedfile.zip">zippedfile.zip</a></td> <td>application/zip</td> <td></td> </tr><tr align=center> <td><a href="getfile.php/img02.jpg">img02.jpg</a></td> <td>image/jpeg</td> <td><img src="getfile.php/img02.jpg"></td> </tr><tr align=center> <td><a href="getfile.php/notexists.gif">notexists.gif</a></td> <td>image/gif</td> <td><img src="getfile.php/notexists.gif"></td> 5 </tr></table> Листинг 13 (HTML-документ, сформированный скриптом «list.html») Рис. 8 Скрипт «getfile.php», отвечающий за выдачу клиенту содержимого файлов, приведен в листинге 14. Поскольку скрипт выдает только те файлы, которые содержатся в списке, несанкционированный доступ к другим файлам на сервере невозможен. <? // Загрузить список файлов include_once("loadlist.php"); // Определить имя файла // по виртуальному пути // (отсечь начальный слэш "/") $name = substr( getenv(PATH_INFO), 1 ); // Найти медиа-тип файла по его имени // в списке файлов // Если имени файла нет в списке, // его тип не определен $type = $fileslist[$name]; // Добавить имя каталога, в котором // реально находятся файлы $realpath = 'files/' . $name; // Если файл реально существует на сервере // и при этом содержится в списке... if( file_exists($realpath) && $type ) { // Выдать клиенту поле заголовка // с указанием медиа-типа файла header("Content-type: $type"); // Выдать содержимое файла в // стандартный выходной поток @readfile( $realpath ); } else { 6 // Сообщить клиенту печальную весть... header("HTTP/1.0 404 Not Found"); } ?> Листинг 14 (файл«getfile.php») Пример. Хранение файлов в базе данных В данном примере решаются следующие задачи: формирование таблицы базы данных MySQL для хранения файлов и информации о них, обеспечение загрузки файлов пользователем в базу данных через HTML-форму, отображение списка файлов и самих файлов (предполагается, что в базу данных будут загружаться файлы изображений). В листинге 15 показан SQL-скрипт, создающий в базе данных MySQL таблицу с именем «filetable». Назначение полей в таблице: id – уникальный числовой идентификатор файла; size – хранит размер загруженного файла; name – имя загруженного файла; body – содержимое файла; type – медиа-тип файла; updtime – время последнего обновления текущей записи. CREATE TABLE filetable ( id int unsigned NOT NULL auto_increment, size int, name varchar(255), body longblob, type varchar(50), updtime timestamp NOT NULL, PRIMARY KEY (id) ); Листинг 15 (файл «filetable.sql») Файл «dbinit.php» (листинг 16) – скрипт, используемый другими скриптами для подключения к базе данных. В нем следует установить корректные параметры подключения: имя компьютера, логин и пароль пользователя, а также имя базы данных. <? $DN="dbname"; // Имя базы данных $HN="localhost"; // Имя компьютера $UL="root"; // Имя пользователя MySQL $UP=""; // Пароль пользователя $link = mysql_connect($HN, $UL, $UP); mysql_selectdb($DN); ?> Листинг 16 (файл «dbinit.php») 7 Скрипт «upload.php» в листинге 17 отображает в окне броузера HTML-форму (рис. 9) для загрузки файлов на сервер. При получении формы, он заносит содержимое переданного файла и информацию о нем в таблицу базы данных. <? include_once("dbinit.php"); // Если прислана форма, то надо загрузить в базу. if ( isset($_REQUEST[upload]) ) { // Массив $_FILES хранит нужную нам информацию. // myfile - так называлось поле формы (см. ниже). $fname = $_FILES[myfile][name]; $fsize = $_FILES[myfile][size]; $ftype = $_FILES[myfile][type]; $err = $_FILES[myfile][error]; $tmp_name = $_FILES[myfile][tmp_name]; // Загрузка прошла без ошибок. if ( $err==0 ) { // Удалось открыть временный файл. if ( $file = @fopen( $tmp_name, "rb" ) ) { // Прочитать содержимое временного файла $filebody = fread($file, $fsize); fclose( $file ); // Удалить временный файл - сам сервер // может об этом забыть unlink( $tmp_name ); // Заэкранировать специальные символы MySQL $filebody = addslashes($filebody); // Вставить запись в таблицу $result = mysql_query( <<<ENDQUERY INSERT INTO filetable SET type='$ftype', name='$fname', size='$fsize', body='$filebody' ENDQUERY ); // Не получилось? if ( !$result ) { // print mysql_error()."<br>\n"; // Ошибка занесения в БД. } } else { // Ошибка открытия файла. } } else { 8 // Ошибка загрузки файла на сервер. // Код ошибки - в переменной $err. } } ?> <!-поле MAX_FILE_SIZE ограничивает допустимый размер файла (в байтах) --> <form METHOD="POST" ENCTYPE="multipart/form-data"> <input type="hidden" name="MAX_FILE_SIZE" value="1200000"> Select file: <input type=file name=myfile><br> <input type=submit name=upload value="Upload"> </form> Листинг 17 (файл «upload.php») Рис. 9 Скрипт «list.php» (листинг 18) отображает в окне броузера список файлов, содержащихся в базе данных, и сами файлы (предполагается, что это изображения – используется тег «IMG»). Извлечение файлов из базы данных выполняет скрипт «getfile.php». При ссылке на имя скрипта («getfile») можно опустить расширение («.php»), если в конфигурации Apache указан параметр «Options MultiViews». <? include_once("dbinit.php"); function getFileList() { $list = array(); $result = mysql_query( "SELECT name FROM filetable ORDER BY name" ); while ($row = mysql_fetch_array($result)) $list[] = $row[name]; return $list; } $list = getFileList(); ?> <ul> <? foreach($list as $filename) { print "<li> $filename <br>"; print "<img src=getfile/$filename> </li> \n";} ?> </ul> 9 Листинг 18 (файл «list.php») Имя запрашиваемого файла передается в качестве «виртуального» пути и поступает в скрипт в виде переменной окружения «PATH_INFO». Для получения имени файла, из переменной следует удалить начальный символ «/». <? include_once("dbinit.php"); function getFileByName( $fname ) { // Извлечение всех полей для файла с заданным именем. // При этом дополнительно преобразуем время обновления // из формата MySQL TIMESTAMP (YYYYMMDDhhmmss) // в число секунд от начала эры UNIX. $result = mysql_query( <<<ENDQUERY SELECT *, UNIX_TIMESTAMP(updtime) AS updtime1 FROM filetable WHERE name='$fname' ENDQUERY ); return mysql_fetch_array($result); } // Получить имя запрошенного файла. $fname = substr( getenv(PATH_INFO), 1 ); // Взять файл из базы. $row = getFileByName( $fname ); if ( !$row ) { // Если такого файла нет - возвращаем клиенту код ошибки. header("HTTP/1.0 404 Not Found"); } else { // Время, когда файл был загружен. // Преобразуется из формата UNIX_TIMESTAMP // в формат RFC 822, в котором дату должен // возвращать сервер. $lastmodif = date("r", $row[updtime1]); // Эти 2 поля позволяют кэшировать переданный файл. // Разрешить кэширование. header("Cache-Control: public"); // Передать время модификации. header("Last-Modified: ".$lastmodif); // Передать медиа-тип файла. header("Content-type: ".$row[type]); // Передать тело файла. print $row[body]; } ?> Листинг 19 (файл «getfile.php») 10