...
...

PHP и MySQL. Часть 2. Доступ к базам MySQL из Web с помощью PHP

PHP и MySQL. Часть 2. Доступ к базам MySQL из Web с помощью PHP

Для начала маленькое лирическое отступление.

Всем известно, что информацию можно хранить не только в базе данных, но еще и в двумерных файлах. Для того, чтобы как-то обосновать, почему необходимо использовать сервер MySQL, а не плоские файлы, приведем несколько часто возникающих проблем при работе с файлами. Первое, на что следует обратить внимание, это размер файла. Когда двумерные файлы становятся большими, работа с ними существенно замедляется. Поиск конкретной записи или группы записей в таком файле затруднителен. Если записи упорядочены, для поиска в ключевом поле можно использовать какой-либо из видов бинарного поиска в сочетании с применением записей фиксированной длины. Тогда, если нужно найти информацию, соответствующую определенному шаблону, придется прочесть и проверить каждую из записей в отдельности. Конкурирующий доступ также может порождать проблемы. Можно показать на примере, и это будет нетрудно, как блокируются файлы, но такой способ работы может привести к возникновению конфликтных ситуаций. При достаточно интенсивной работе большой группы пользователей на сайте им придется ожидать разблокировки файла, прежде чем они смогут разместить там свои заказы. А если ожидание слишком затянется, люди обратятся за покупкой в другое место.

До сих пор обработка данных в плоских файлах сводилась к последовательному перебору. То есть считывание начиналось с начала файла и выполнялось до конца. При необходимости вставить запись или удалить ее из середины придется перечитать весь файл в память, выполнить изменения и снова записать весь файл. Когда размер файла значителен, это вызывает перегрузку системы. Кроме ограничений, налагаемых правами доступа к файлам, не существует никакого способа обеспечения различных уровней доступа к данным.
С помощью СУБД эти проблемы решаются. И вот каким образом.
СУРБД могут обеспечить намного более быстрый доступ к базам данных, чем двумерные файлы. Среди всех СУРБД MySQL обладает одними из самых высоких показателей производительности. В СУБД можно легко отправлять запрос для извлечения наборов данных, отвечающих определенным критериям. Также СУБД обладают встроенными механизмами обработки конкурирующих обращений, что позволяет программисту не беспокоиться об этом, обеспечивают произвольный доступ к данным, обладают встроенными системами определения прав доступа. Причем MySQL имеет особенно большие возможности в этой области.
Главная же побудительная причина использования СУРБД заключается в том, что все (или, по крайней мере, большинство из них) функциональные возможности, требуемые от системы хранения данных, в ней уже реализованы. Конечно, можно было бы создать свою собственную библиотеку PHP-функций, но зачем же заново изобретать колесо?

Итак, думаю, выше было приведено достаточное количество преимуществ использования баз данных в интернет-проектах. Теперь мы знаем, что реляционные базы данных намного эффективнее и проще выполняют операции хранения и извлечения информации в web-приложениях.
Однако созданная в прошлой статье база данных, к сожалению, не имеет пока никакой ценности, так как не выполняет свою основную задачу — предоставление доступа к информации через Web. И по логике вещей мы должны после создания базы данных приступить к подключению этой базы к внешнему web-интерфейсу.
Постараемся показать, как получить доступ к базе данных из Web, используя PHP. На примерах будет также показано, как в простейшем варианте выбирать из базы и вносить в нее данные и как фильтровать потенциально опасную входную информацию.
Как получить доступ к БД и прочитать из нее данные, покажет следующий простой пример формы поиска материалов. Форма проще некуда, и код между тегами <body> </body> приведен ниже:

<body> <h1> Denver's Auto Ware Search</h1>
<from action="result.php" method="post">
Выберите тип поиска: <br>
<select name="searchtype">
<option value="matcode"> по коду
<option value="matname"> по названию
</select> <br>
Введите строку поиска:<br>
<input name="searchterm" type=text> <br>
<input type=submit value="Поиск">
</form> </body>

В процессе работы пользователь заходит на страницу поиска материала, выбирает тип поиска и вводит искомую строку. После того, как пользователь нажимает на кнопку ПОИСК, вызывается сценарий result.php, исходный код которого приведен ниже с комментариями:

<html>
<head>
<title> Denver's Auto Ware Search Results</title>
</head>
<body>
<h1> Denver's Auto Ware Search</h1>
<? trim($searchterm);

Поясним, какие операции выполняет этот сценарий. Сначала необходимо убрать все лишние пробелы по краям слова, которые мог случайно или преднамеренно набрать пользователь. Функция trim() обрезает пробелы вначале и в конце строки. На следующем этапе нам необходимо убедиться, что пользователь указал тип поиска и строку поиска. Эта проверка должна проводиться уже после того, как лишние пробелы удалены из параметров поиска. В случае если не выбран тип поиска или не введена строка поиска, сценарий выдаст сообщение об ошибке и завершит свою работу:
if (!searchtype || !searchterm)
{echo "Вы не заполнили все поля для поиска. Вернитесь обратно и заполните все поля.";
exit}

Входные данные, как известно, проверять необходимо всегда. Вводимая информация не должна содержать управляющих символов. Поэтому подвергаем переменные $searchtype и $searchterm обработке функцией addslashes(). К данным, извлекаемым из базы данных в этом случае также необходимо применить функцию stripslashes().
$searchtype = addslashes($searchtype);
$searchterm = addslashes($searchterm);

Подключиться к серверу MySQL из РНР-сценария несложно. Достаточно вызвать функцию mysql_pconnect(), как показано в следующем примере:
@ $db = mysql_pconnect("localhost","Denver","den001");
Также можно использовать функцию mysql_connect(): она тоже возвращает идентификатор соединения. Разница состоит только в том, что функция mysql_pconnect() создает постоянное подключение, которое "живет" все время работы сценария.
В этих функциях требуется указать имя узла, на котором помещен сервер MySQL, опционально, через двоеточие можно указать порт, по которому следует подключаться, имя пользователя и пароль, чтобы войти на сервер. Все эти параметры являются необязательными, и если какой-либо не указать, функция воспользуется значением по умолчанию. В случае успеха функция возвращает идентификатор сеанса связи с базой данных или значение false в случае неудачи. Следующий далее код проверяет, успешно ли прошло соединение с базой данных, и показывает предупреждение, если соединение установить не удалось.
if (!db)
{echo "Ошибка: не могу соединиться с базой данных. Попробуйте еще раз позднее.";
exit}

Затем выбираем базу данных, с которой будем работать. Эта функция аналогична консольному вводу "use auto;". В результате будет использоваться именно выбранная база данных при формировании запросов к таблицам.
mysql_select_db("auto");
Далее настраиваем текст запроса и помещаем его в переменную $query. После этого вызываем функцию mysql_query(), которая и осуществляет запрос. Результат запроса помещаем в переменную $result для дальнейшего использования. Не забывайте, что запрос, передаваемый в MySQL, не требует точки с запятой в конце, в отличие от запроса, который вводится в среде монитора MySQL.
$query = "select * from materials where ".searchtype." like '%"$searchterm"%'";
$result = mysql_query($query);
Разнообразие функций дает нам возможность получить результат различными способами. Идентификатор результата — это ключ доступа к строкам, возвращенным запросом, которых может быть ноль, одна или несколько. Применяемая ниже функция mysql_num_rows() сообщает количество строк, которые возвращает запрос.
$num_results = mysql_num_rows ($result);
echo "<p> Найдено материалов: ".$num_results."</p> ";

Это полезно знать, если планируется обрабатывать или отображать результаты. Зная их количество, можно организовать цикл по строкам результата запроса.
for ($I = 0, $I < $num_results, $I++)
{$row = mysql_fetch_array ($result);
В каждой итерации цикла происходит вызов функции mysql_fetch_array(). Эта функция берет каждую строку из списка результата и возвращает ее в виде ассоциативного массива с ключом как именем атрибута. Имея $row в массиве, можно пройти каждое поле и отобразить его должным образом.
echo "<p> <strong> ".($I + 1).".Наименование: ";
echo htmlspecialchars( stripslashes($row["matname"]));
echo "</strong> <br> Код материала: ";
echo htmlspecialchars( stripslashes($row["matcode"]));
echo "</strong> <br> Спецификация: ";
echo htmlspecialchars( stripslashes($row["matspec"]));
echo "<br> Цена: ";
echo htmlspecialchars( stripslashes($row["cost"]));
echo "</p> ";} ?>
</body>
</html>

Существует несколько вариантов получения результата. Вместо ассоциативного массива можно воспользоваться нумерованным массивом, воспользовавшись функцией mysql_fetch_row(). Тогда значения атрибутов будут храниться в каждом порядковом значении $row[0], $row[1] и так далее. С помощью функции mysql_fetch_object() можно выбрать строку внутрь объекта и получать доступ к атрибутам как к свойствам объекта $row-> matcode, $row-> matname и так далее.
Каждый из этих вариантов подразумевает выборку строки за раз. Другой вариант — получить доступ, используя mysql_result(). Для этого потребуется указать сроку (от нуля до количества строк минус один) и название поля:
%row = mysql_result(%result, $I, "matcode");

Однако не стоит смешивать при работе mysql_result() с другими функциями выборки. Строчно-ориентированные функции выборки намного более удобны и эффективны, нежели mysql_result(), так что лучше использовать одну из них.
В случае, если есть необходимость закрытия непостоянного соединения, применяется функция mysql_close(database_connection), однако применение ее спорно, так как с завершением выполнения сценария соединение закрывается автоматически.
Однако рассмотренный выше поиск представляет только одну функциональную единицу приложения. Кроме организации поиска по базе, должно существовать еще как минимум и добавление. Не стоит забывать и об изменении данных, и об их удалении. Эти операции не сильно отличаются друг от друга — только структурой запроса. Так что при их проведении не должно возникнуть трудностей, синтаксис операторов insert, update и delete довольно стандартен. Рассмотрим их все на примерах.

Внесение новой информации очень похоже на получение существующей. Нужно пройти те же шаги: установить соединение, отправить запрос и проверить результаты. Только в данном случае вместо select будет использоваться insert.
Для успешной работы все, что вам нужно, — это создать HTML-страницу с формой, свойство action которой указывает на файл insert_mat.php с методом post. На форме должны присутствовать все поля, которые необходимо будет заполнить пользователю для внесения нового материала: код материала, его название, спецификацию и цену. Результаты заполнения формы передаются в insert_mat.php, а сценарий, занимающийся деталями, выполняет аутентификацию и пытается записать данные в базу данных. Структура его такая же, как и у сценария поиска. Для начала производим проверку полей на непустые значения. Если проверка показала, что не все поля заполнены, показываем пользователю сообщение об ошибке и прекращаем выполнение сценария. После этого преобразовываем значения полей ввода функциями addslashes() и doubleval(). Последняя функция применяется для фильтрации всех неподходящих символов в числовом поле. Соединение с базой данных происходит по вызову функции mysql_pconnect(), и, если оно было успешным, формируется строка запроса вставки новой записи в БД. В противоположном случае пользователю выдается сообщение об ошибке. После того, как запрос выполнен, можно проверить изменения в базе данных путем вызова функции mysql_affected_rows(), которая возвращает количество строк, которые были подвергнуты изменениям. Необходимо заметить, что, когда используется оператор select, применяется функция mysql_num_rows(), возвращающая количество строк в результате запроса. В случае работы с операторами insert, delete и update надо вызывать функцию mysql_affected_rows().

Теперь поговорим о внесении данных. Эта процедура очень похожа на процесс получения существующих данных. Нужно пройти те же шаги: установить соединение, отправить запрос и проверить результаты. Только в данном случае вместо Select будет использоваться Insert. Хоть все, вроде, и просто, но на пример никогда взглянуть не помешает. Исходя из количества полей для материалов на складе, мы создадим обычную HTML-форму для добавления новых материалов на склад, то есть в базу данных. Код этой страницы приведен ниже:
<html> <head> <title> Denver's Auto Ware</title> </head>
<body>
<h1> Denver's Auto Ware — Insert Material</1>
<form action="insert_mat.php" method="post">
<table border=0>
<tr> <td> Код материала</td>
<td> <input type=text name=mat_code> <br> </td> </tr>
<tr> <td> Название</td>
<td> <input type=text name=mat_name> <br> </td> </tr>
<tr> <td> Применение</td>
<td> <input type=text name=mat_spec> <br> </td> </tr>
<tr> <td> Цена за ед.</td>
<td> <input type=text name=mat_cost> <br> </td> </tr>
<tr> <td colspan = 2 > <input type=submit value = Добавить> </td> </tr>
</table> </form>
</body> </html>

Результаты заполнения этой формы передаются в сценарий insert_mat.php, а сценарий, занимающийся вставкой материала, выполняет определенного вида аутентификацию и пытается записать данные в базу.
Код сценария без HTML представлен ниже:
<?
If (!$mat_code || !$mat_name || !$mat_spec || !$mat_cost)
{echo "вы не заполнили все требуемые поля! Вернитесь назад и попробуйте снова.";
exit;}
В этой части мы снова проверяем введенные пользователем данные. По нашим условиям, он обязательно должен заполнить все поля без исключения, иначе материал в базу добавлен не будет. Идем по сценарию далее:
$mat_code = addslashes($mat_code);
$mat_name = addslashes($mat_name);
$mat_spec = addslashes($mat_spec);
$mat_cost = doubleval($mat_cost);

Следующие четыре строки делают переданные нам значения безопасными для скрипта и базы данных. Очевидно, что цены хранятся в базе данных в виде числа с плавающей запятой, символы наклонной черты они содержать не должны. Поэтому еще раз повторюсь: для чисел, в отличие от строковых значений, это достигается использованием функции doubleval применительно к числовому значению. Эта функция отфильтровывает все неподходящие символы в числовом поле. Она же, функция, позаботится и о всех символах валюты, которые пользователь может ввести.
Далее мы снова соединяемся с базой данных функцией mysql_pconnect():
@ $db = mysql_pconnect("localhost","Denver","den001");
if (!$db)
{echo "ОШИБКА: не могу соединиться с базой данных!";
exit;}

mysql_select_db("auto");
$query = insert into materials values ('".$mat_code."', '".$mat_name."', '".$mat_spec."', '".$mat_cost."');
$result = mysql_query($query);
if ($result)
echo mysql_affected_rows()." Материалов добавлено в базу.";
?>

Одно существенное различие между операторами Select и Insert заключается в использовании mysql_affected_rows(). В предыдущем сценарии функция mysql_num_rows() применялась для определения количества строк, которые будет возвращать запрос Select. При написании запросов, которые изменяют базу данных, таких, как Insert, Delete, Update, следует использовать mysql_affected_rows(), которая возвращает количество строк в базе, подвергнутых изменениям.
Итак, как накапливать данные в памяти и как с ними работать, уже известно. А как освободить память от них, если такая необходимость возникнет? Для освобождения ресурсов, а такая ситуация может действительно понадобиться при выполнении сценария, пригодится функция mysql_free_result(int result), которая освобождает память, занимаемую результатом. Очевидно, что все действия с данными должны производиться до вызова этой функции.

Из сценария можно не только работать с базой данных, но и создавать и удалять их. Для этого существуют функции mysql_create_db() и mysql_drop_db(). Обе функции используют имя базы данных и соединение. Если соединения нет, будет использоваться последнее открытое. В случае успешного выполнения функций они возвращают true, а в случае неудачи — false.
Для ужесточения мер безопасности не стоит забывать, что запускать сервер MySQL — mysqld от имени привилегированного пользователя не рекомендуется. Используя полученные права, пользователь MySQL может записывать и читать файлы в любом месте системы.

Продолжение следует.

Денис "Denver" Мигачев, dtm@tut.by



© Компьютерная газета

полезные ссылки
Аренда ноутбуков