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

web - tec / PHP 5 для начинающи

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

Надежный и понятный код 233

Использование оператора echo()

С помощью оператора echo можно выводить на экран значение, присвоенное какой+ либо переменной. Это можно делать внутри функций, внутри циклов, во время обычной обработки ++++++ по сути, можно помещать оператор echo почти в любой участок кода. А если идентифицировать местоположения того или иного оператора echo (например, вставив в него текст вроде ‘‘внутри цикла for’’ или ‘‘внутри второго оператора if’’), то в коде можно использовать множество операторов echo и знать, что происходит в каж+ дый момент выполнения программы. Например, если есть сценарий, принимающий от пользователя имя и адрес электронной почты, а затем обрабатывающий эти данные, то все происходящее во время обработки можно выяснить с помощью подобного кода:

<?php

echo "Имя :" . $name . "<br>";

echo "Email:" . $email . "<br>"; // обработка

echo "Имя после обработки:" . $name . "<br>"; echo "Email после обработки:" . $email . "<br>"; // дополнительная обработка

echo "Имя после дополнительной обработки:" . $name . "<br>"; echo "Email после дополнительной обработки:" . $email . "<br>";

?>

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

Ошибки внутри HTML-кода

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

Предположим, например, что используется PHP+код для подключения к базе дан+ ных, получения некоторых записей и последующего использования этих записей в цик+ ле for для создания HTML+элемента <select>. Известно, что элемент <select> необхо+ димо завершать закрывающим тегом </select> и что между этими тегами присутствуют элементы <option>. Если один из элементов <option> записан неверно (например, в нем присутствует PHP+сообщение об ошибке), то браузер просто проигнорирует данный эле+ мент. На экране пользователь увидит выпадающий список и все ‘‘хорошие’’ элементы <option>, но в HTML+коде можно будет заметить что+нибудь подобное:

<select name="select_customer_id">

<option value="1">Джон Доу</option> PHP-сообщение об ошибке

<option value="3">Джейн Доу</option> </select>

В данном случае полностью теряется клиент номер 2, но на экране сообщение об ошибке не отображается, хотя оно и отчетливо видно в исходном HTML+коде.

234 Глава 5

Проверка данных форм

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

Один из способов сделать это заключается в ограничении допустимых значений для определенного текстового поля. Например, в рассмотренном ранее сценарии loan.php форма передает возраст пользователя в переменную $age, а PHP+код затем сверяет это значение с более реальным диапазоном возрастов:

if ($age < 1 or $age > 130)

{

echo "Введен некорректный возраст. Пожалуйста, введите возраст в пределах 1 и 130."; break;

}

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

Использование оператора exit

Проверка данных ++++++ хорошая практика, но что делать, если программа столкну+ лась с некорректными данными? Иногда необходимо прервать обработку, что и дела+ ет оператор exit. В отличие от оператора break, который прерывает текущую структуру, оператор exit останавливает всю работу программы. Можно использовать любой метод для окончания обработки, но оператор exit более жесткий, поэтому при+ менять его следует осторожно. После того как PHP+интерпретатор встречает оператор exit, ни HTML+, ни PHP+код не обрабатывается, и если программист был не особенно внимательным, то пользователь может увидеть неожиданный результат, например, час+ тично сформированную страницу. Можно переработать приложение для обработки заявок на получение ссуд и сделать его более устойчивым по отношению к пользова+ тельским ошибкам путем внедрения некоторой логики проверки HTML+формы.

Практика Проверка форм

1.Откройте файл loan.php (из главы 4) и сохраните его как loan_fv.php, а за+ тем внесите следующие изменения:

<html>

<head><title></title></head>

<body>

<b>Заявка на получение ссуды. Кредитный банк Namllu</b>

<?php

if (isset($_POST['posted'])) {

$age = $_POST['age'];

$first_name = $_POST['first_name']; $last_name = $_POST['last_name']; $address = $_POST['address']; $loan = $_POST['loan'];

$month = $_POST['month'];

Надежный и понятный код 235

//проверка данных формы

if ($age < 10 OR $age > 130)

{

echo "Введен некорректный возраст – нажмите кнопку Назад и заполните форму еще раз";

exit;

}

if ($first_name == "" or $last_name == "")

{

echo "Необходимо ввести имя – нажмите кнопку Назад и заполните форму еще раз";

exit;

}

if ($address == "")

{

echo " Необходимо ввести адрес – нажмите кнопку Назад и заполните форму еще раз";

exit;

}

if ($loan != 1000 and $loan != 5000 and $loan != 10000)

{

echo "Необходимо ввести сумму ссуды – нажмите кнопку Назад и заполните форму еще раз";

exit;

}

$duration = 0; switch ($loan) { case "1000";

$interest = 5; break;

case "5000"; $interest = 6.5; break;

case "10000"; $interest = 8; break;

default:

echo "Вы не выбрали вариант ссуды<hr>"; exit;

}

while ($loan > 0)

{

$duration = $duration + 1;

$monthly = $month - ($loan*$interest/100); if ($monthly <= 0)

{

echo "Чтобы погасить ссуду, требуются более крупные ежемесячные платежи<hr>";

exit;

}

$loan = $loan - $monthly;

}

echo "Для погашения ссуды при процентной ставке $interest процентов понадобится $duration месяцев.<hr>";

}

?>

<form method="POST" action="loan_fv.php"> <input type="hidden" name="posted" value="true"> <br>

236 Глава 5

Имя:

<input name="first_name" type="text">

Фамилия:

<input name="last_name" type="text">

Возраст:

<input name="age" type="text" size="3"> <br>

<br>

Адрес:

<textarea name="address" rows="4" cols="40"> </textarea>

<br>

<br>

Каков размер Вашей текущей заработной платы? <select name="salary">

<option value=0>До $10000</option>

<option value=10000>От $10000 до $25000</option> <option value=25000>От $25000 до $50000</option> <option value=50000>Свыше $50000</option> </select>

<br>

<br>

Выберите необходимую сумму ссуды<br><br>

<input name="loan" type="radio" value="1000">1000 долларов под 5,0% в месяц <br>

<input name="loan" type="radio" value="5000">5000 долларов под 6,5% в месяц <br>

<input name="loan" type="radio" value="10000">10000 долларов под 8,0% в месяц <br>

<br>

Введите сумму ежемесячного платежа

<input name="month" type="text" size="5"> <br>

<br>

<input type="submit" value="Рассчитать"> </form>

</body>

</html>

2.Сохраните данный файл как loan_fv.php и закройте его.

3.Откройте файл в браузере и введите несколько значений, которые выходят за рамки допустимых (иначе говоря, некорректные значения). На рис. 5.3 показа+ но одно из сообщений об ошибке, которое может получить пользователь.

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

Пользователь может ввести чужой адрес или указать другой возраст, и в PHP нет способа проверить корректность данной информации. Но что действительно можно сделать с помощью нового кода, это гарантировать, что пользователь не забыл ввести необходимые сведения и что он преднамеренно не ввел очевидно неверную инфор+ мацию о своем возрасте. Для этого используются операторы if..then..else. Пер+ вый оператор проверяет, попадает ли введенное пользователем значение в диапазон 10++++130, ведь в противном случае совершенно ясно, что пользователь лжет:

if ($age < 10 OR $age > 130)

{

echo "Введен некорректный возраст – нажмите кнопку Назад и заполните форму еще раз";

exit;

}

Надежный и понятный код 237

Рис. 5.3.

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

Второй оператор if проверяет присутствие имени и фамилии. Две двойные ка+ вычки "" означают пустую строку (строку без символов), и проверка выполняется так:

if ($first_name == "" or $last_name == "")

{

echo "Необходимо ввести имя – нажмите кнопку Назад и заполните форму еще раз"; exit;

}

Так же проверяется значение переменной $address:

if ($address == "")

{

echo "Необходимо ввести адрес – нажмите кнопку Назад и заполните форму еще раз"; exit;

}

238Глава 5

Азатем проверяются значения переключателей с тем, чтобы определить, какой из них был выбран:

if ($loan != 1000 and $loan != 5000 and $loan != 10000)

{

echo "Необходимо ввести сумму ссуды – нажмите кнопку Назад и заполните форму еще раз"; exit;

}

Если переменная $load не равна одному из данных значений, то, очевидно, что пользователь значения не выбрал.

Предотвращение ввода пользователем HTML-кода:

функция HTMLSpecialChars()

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

икогда он отправляется назад клиенту, то обрабатывается браузером как любые другие простые текстовые символы, из которых формируется HTML+код. Например, если создать приложение, принимающее от пользователя данные для вывода содержимого каталога, то знающий пользователь может подставить тег <b> перед своим именем

и</b> после имени, так чтобы его имя в листинге каталога выводилось жирным шриф+ том. Это хороший прием, но он непорядочен по отношению к другим пользователям.

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

$String = HTMLSpecialChars("<b>После обработки этот текст будет выведен обычным, а не жирным шрифтом</b>");

Функции также можно передать имя переменной:

$String = "<b>После обработки этот текст будет выведен обычным, а не жирным шрифтом</b>";

$String = HTMLSpecialChars($String);

Функция HTMLSpecialChars() преобразует все HTML+теги в так называемые специальные символы. Специальными символами в HTML являются последовательно+ сти символов, представляющие HTML+символы, из которых они были получены. Например, тег <b> преобразуется в символы < (знак ‘‘меньше’’), букву b и сим+ волы > (знак ‘‘больше’’). Когда браузер получает специальные символы, он ото+ бражает их на экране как представленные ими HTML+символы, не обрабатывая и не выводя их как обычные HTML+теги.

Эта возможность часто используется, когда необходимо создать Web+страницу, на которой описываются HTML+теги (когда требуется отображать теги простым текстом

ине давать браузеру возможность их обработать), кроме этого, данная функция по+ лезна, поскольку предотвращает ввод пользователями их собственного HTML+кода в PHP+приложение.

Вконечном итоге, степень проверки ошибок ограничивается только знаниями

ифантазией разработчика. Фактически можно было бы предустанавливать заранее значения всех переменных, но зачем тогда выводить форму? Можно все делать авто+

Надежный и понятный код 239

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

Проверка строк и регулярные выражения

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

Проверка строк

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

Использование функции strlen()

Некоторые строковые данные имеют определенную длину, например, почтовые коды США, которые всегда состоят из 5 цифр, или в случае ZIP+4 из 5 цифр, дефиса и четырех или более цифр. Поэтому один из способов проверки данных, введенных как почтовые коды, заключается в использовании функции strlen():

If (strlen($postal_code) == 5 or strlen($postal_code) == 10) { //проверить, находится ли дефис в шестой позиции

//другие операторы

} else {

//отправка сообщения об ошибке

}

Использование функции strstr()

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

if (strlen($postal_code) == 5

or strlen($postal_code) == 10) { if (strlen($postal_code) == 10) {

if (strstr($postal_code, "-")) { //другие операторы

240 Глава 5

}

}

} else {

//отправка сообщения об ошибке

}

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

Использование функции substr()

Продолжая тот же пример, предположим, что необходимо отделить часть +4 от строки ZIP+4. Известно, что эта часть начинается с седьмой позиции и должна быть длиной четыре символа. С этой целью можно задействовать функцию substr(). Данная функция принимает в качестве аргументов строку, целое число, представ+ ляющее позицию, с которой следует начать поиск, и необязательное целое число воз+ вращаемых символов, например:

If (strlen($postal_code) == 5 or strlen($postal_code) == 10) { If (strlen($postal_code) == 10) {

$plus4_portion = substr($postal_code,7,4)

}

}else {

//отправка сообщения об ошибке

}

Использование функций addslashes() и stripslashes()

Для приложений, позволяющих пользователю вводить информацию, которая может затем передаваться в базу данных, полезно использовать функцию addslashes(). Эта функция добавляет обратную косую черту всякий раз, когда встречает строковый символ, который может создать проблему в базе данных (символы ', ", \ и NULL). Позднее, когда данные будут выводиться пользователю, необходимо применять функцию stripslashes(), которая, как можно догадаться, удаляет обратные косые, вставленные функцией addslashes().

Зачем нужны функции, предназначенные для защиты записей баз данных? Тот, кто когда+либо составлял SQL+строку для вставки записи (или для любой другой функ+ ции базы данных), знает, что SQL не терпит неверного использования апострофов (SQL рассматривается в главах 9++++11).

Предположим, что SQL+строка выглядит так:

$query = "INSERT INTO clients (username) values('$username')"; mysql_query($query);

Если пользователь вводит строку joeblow в качестве имени пользователя, то дан+ ный запрос работает корректно, однако если пользователь вводит строку joe’blow, то запрос не будет выполнен, потому что база данных ‘‘увидит’’ следующий запрос:

$query = "INSERT INTO clients (username) values('joe'blow')";

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

$username = addslashes($username);

$query = "INSERT INTO clients (username) values('$username')"; mysql_query($query);

Надежный и понятный код 241

Во время выполнения данного кода функция addslashes превратит строку joe’blow в строку joe\’blow. Обратная косая черта экранирует апостроф и, таким об+ разом, заставляет базу данных принять оба символа (комбинацию обратной косой черты и апострофа) без сбоев.

Однако при выводе имени пользователя следует позаботиться об использовании функции stripslashes(). В противном случае пользователь увидит в качестве своего имени строку joe\’blow, и если он не удалит обратную косую черту, то следующий запрос запишет в базу данных строку joe\\\’blow, так как функция addslashes попытается эк+ ранировать оба символа обратной косой черты, а это может привести к проблемам.

Кроме того, необходимо убедиться, что функция addslashes применяется везде, где используется имя пользователя. Хотя на экране имя пользователя отображается корректно как joe’blow, в базе данных оно хранится как joe\’blow, таким образом, только применяя функцию addslashes() снова, можно быть уверенным, что данное значение будет корректно сравниваться со значением в базе данных.

Регулярные выражения

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

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

if (strstr($postal_code, "-")) {

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

<?php

$words = "you, should, vote, happily"; $wordarray = explode(",", $words); foreach ($wordarray as $word) {

if ($word == "vote") {

echo "Найдена строка 'vote'";

}

}

?>

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

Регулярные выражения (regular expressions), или regexps, очень напоминают мини+язык программирования для создания чрезвычайно эффективных образцов поиска. В ре+

242 Глава 5

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

Применение регулярных выражений не ограничивается PHP. Такие языки программирования, как Perl и Python, наряду с Unix*утилитами sed и egrep используют ту же нотацию для поиска образцов в тексте. PHP*функции регулярных выражений, в которых используется Perl*нотация, называются PCRE*функциями и их имена начинаются с preg (Perl Regular Expression ****** регулярные выражения языка Perl),

а обычные PHP*функции регулярных выражений называются POSIX*совместимыми функциями регулярных выражений. Обычные (POSIX*совместимые) PHP*функции регулярных выражений не следует использовать для обработки двоичных данных (они искажают двоичные данные); вместо них следует применять PCRE*функции.

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

Использование функции ereg()

Приведенный выше пример работает, но он громоздкий, сложный и жестко закоди+ рован (слово ‘‘vote’’ фактически является частью кода, а не поступает как входное зна+ чение; по сути, весь массив жестко закодирован), и что еще хуже, функция explode() фактически содержит всю пунктуацию ++++++ строку ‘‘you’’ найти невозможно, тогда как ‘‘you,’’ (с запятой) находится. На первый взгляд это может показаться сложной про+ блемой, но на самом деле все решается просто с помощью регулярного выражения:

<?php

$words = "you, should, vote, happily";

if (ereg("vote", $words)) { echo "Найдена строка 'vote'";

}

?>

В данном случае используется функция ereg() и указывается образец (слово, с ко+ торым необходимо сравнивать выражение) и строка для сравнения. Функция возвра+ щает True, если сравнение с образцом оказалось успешным (в данном случае когда в значении переменной $words функция находит последовательность символов ‘‘vote’’), и False, если совпадение не найдено.

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

<?php

$words = "you, should, vote, happily";

if (ereg("vote", $words, $reg)) echo "Найдена строка '$reg[0]'"; ?>

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

Соседние файлы в папке web - tec