Время на прочтение
3 мин
Количество просмотров 9.8K
Подборочка ошибок начинающих PHP разработчиков…
- Книга по PHP за 2002 год как источник знаний — это уже история, советую «PHP 5. Профессиональное программирование» — Э. Гутманс, С. Баккен — ISBN:5-93286-083-9, иль даже поновее…
- Использование web-сервера, где «всё включено» (Denwer и еже с ним) — научитесь сетапить сами, потом успеете перейти на полуфабрикаты
- Используем простенький редактор с подсветкой синтаксиса — пора взрослеть и переходить на IDE — с IDE увеличивается скорость разработки, особенно в больших проектах, где не один десяток классов.
- В php.ini включен параметр register_globals — отключаем это ЗЛО, по умолчанию отключен, но Вам он зачем-то понадобился
- Реализация «index.php» содержащая приблизительно следующий код: <?php require_once $_GET[‘mod’]; ?> — это уязвимость, зовется PHP инъекцией — всегда проверяйте входные данные.
- Реализация авторизации, когда строка запроса к БД содержит что-то типа: «SELECT * FROM users WHERE login=».$_GET[‘login’].» AND password=».$_GET[‘password’] — читаем что такое SQL инъекции (это относится ко всем входным данным и SQL запросам)
- Доверяем POST переменным больше чем GET? — Будьте уверены подделать так же легко, так что проверяем
- AJAX это хорошо, дырявый AJAX — плохо — не забывай проверять права доступа для функций вызываемых в AJAX’е
- Не проверяем залитые пользователями файлы — теперь и мы знаем что такое PHP Backdoor
- Присваивание в условии: <?php if ($auth = DEFINE_NAME) {… } ?> такую ошибку Вы будете долго искать — старайтесь избегать подобных конструкций (используйте конструкцию ввида <?php if (DEFINE_NAME == $auth) {… } ?> — если пропустите один знак «равно» — интерпретатор выдаст ошибку)
- «Cannot send session cookie — headers already sent by… » — пытаемся установить куки, когда заголовок уж послан браузеру — незаметили пустую строку или пробел перед первым тегом <?
- Переписываем функции PHP — воспользуйтесь поиском по manual’y
- Выражение <?php if (array_search(‘needle’, $array) != FALSE) {…} ?> — не сработает если искомый элемент доступен по ключу 0 либо «» — читаем внимательней manual по каждой функции
- Работаем под windows, хостинг на linux и сайт не поднимается — имя файла index.php, Index.php, INDEX.php и т.д. это все разные файлы для linux систем, а мы про это забыли
- Отключение вывода ошибок — код должен быть чистым — так что error_reporting(E_ALL) Вам в помощь, следите даже за Notic’ами
- Залив проект на хостинг — отключите вывод ошибок на экран — логируйте их в файл — не будете краснеть потом
- PHP файлы имеют не стандартное расширение (к примеру .inc вместо .php) — если не защитить такие файлы, то может быть очень стыдно
- В рассчете на short_tags, поленились писать польностью <?php… ?> — теперь на новом хостинге переписываем шаблоны
- Понадеялись на magic_quotes, теперь наше приложение надежно как швейцарский… сыр
- Соблюдай стандарты кодирования и пиши комментарии к коду — твои последователи скажут спасибо
- Не пиши маты в сообщениях об ошибках и комментариях — заказчик может просматривать и тогда прийдется долго оправдываться
- Читают статью http://php.spb.ru/php/speed.html и думают, что только так можно/нужно оптимизировать код — это «блошиная» оптимизация относится к PHP3
- Реализуем функционал БД средствами PHP — array_search вместо WHERE, ну и другие чудеса незнания SQL
- Стучимся к БД в рекурсии — стараемся обходить подобные реализации
- Не нужно забивать гвозди пасатижами — «Мне надо реализовать гостевую книгу — возьму-ка я свой любимый набор библиотек на 6 мегабайт»
- Узнали Smarty, и теперь уверены, что научились разделять логику и отображение
- Проверяем ВСЕ входные данные, XSS уязвимости нам не нужны
Если есть что добавить — пишем в комментариях…
P.S. Изначально было перечислено 25 ошибок, оттель и картинка такая…
Эта серия статей предназначена для тех программистов на языке PHP, которые хотят избежать наиболее общих ошибок в написании кода. Читатель, как минимум, должен знать общий синтаксис PHP, а также весьма желателен некоторый опыт использования языка на практике.
- Введение
- Об авторе
- О переводчике
Одна из наиболее сильных сторон PHP является, одновременно, и его слабой стороной: PHP очень прост в изучении. Это привлекает многих людей; однако, несмотря на его кажущуюся простоту, не так-то просто научиться использовать этот язык правильно и эффективно.
Как правило, дело в недостаточной практике программирования. Неопытные программисты становятся перед лицом необходимости создания сложных веб-приложений. Поэтому сплошь и рядом допускаются ошибки, которых избежал бы опытный программист, такие как необоснованное использование функции printf()или неправильное использование семантики PHP.
В этой серии из трех статей представлены наиболее, по нашему мнению, характерные ошибки. Эти ошибки можно классифицировать по нескольким категориям, от <некритических> до <смертельных>. Наряду с анализом этих ошибок, представлены способы их избежания, а также некоторые <маленькие хитрости>, накопленные за многие годы практики программирования.
Часть 1: Описываются 7 <детских> ошибок (№ 21-15, в обратном порядке, в соответствии со степенью серьезности по нашей классификации). Такие ошибки не вызывают серьезных проблем, но приводят к уменьшению эффективности работы программы, а также выражаются в громоздком трудночитаемом коде, в который, к тому же, трудно вносить изменения.
Часть 2: Следующие 7 ошибок (№ 14-8) относятся к <серьезным>. Они ведут к еще более значительному уменьшению скорости выполнения кода, уменьшению безопасности скриптов; код становится еще более запутанным.
Часть 3: Описания семи, последних, <смертельных> ошибок. Эти ошибки концептуальны по своей природе и являются причиной появления ошибок, описанных в 1-ой и 2-ой частях статьи. Они включают и такие ошибки, как недостаточное внимания, уделенное как проекту в целом, так и коду программы, в частности.
21. Неоправданное использование функции printf()
Функция printf() предназначена для вывода форматированных данных.
Например, ее следует использовать при необходимости вывода переменной в формате с плавающей запятой с определенной точностью, либо в любом другом случае, когда возникает необходимость изменения формата выводимых данных.
Ниже приведен пример обоснованного применения функции printf(). В данном случае она используется для форматированного вывода числа <пи>:
<?php printf ("Число Пи: %2fn<br>n", M_PI); printf ("Это тоже число Пи: %3fn<br>n", M_PI; printf ("И это Пи: %4fn<br>n", M_PI); ?>
Примечание: Наблюдаются случаи патологической боязни функции printf(), когда люди пишут свои функции форматированного вывода, порой по 30-40 строк, хотя все проблемы мог бы решить один-единственный вызов функции printf().
Многие программисты используют функцию printf() для вывода переменных, результатов вызова функций, а иногда даже обычных текстовых данных. Наиболее часто это происходит в следующих двух случаях:
* когда следовало бы использовать функцию print();
* при выводе результатов, возвращаемых функциями.
Когда следует использовать print()
Вызов функции printf() зачастую используется там, где следовало бы использовать print(). В следующем примере функция printf() используется для вывода четырех переменных:
<?php $name = 'Sterling Hughes'; $job = 'Senior Engineer'; $company = 'DesignMultimedia'; $email = 'shughes@designmultimedia.com'; printf ( "Меня зовут %sn<br>n Я работаю %s, %sn<br>n Мой адрес E-mail:%sn<br>n", $name, $job, $company, $email ); ?>
В данном случае возможно (и желательно!) применение print():
print «Меня зовут $namen<br>n
Я работаю в $company, $jobn<br>n
Мой адрес E-mail: $emailn<br>n»;
Использование print() вместо printf() в случаях, когда выводятся неформатированные данные, как в данном примере, дает следующие выгоды:
* Увеличение производительности: Функция printf() форматирует свои аргументы перед выводом. Таким образом, время ее выполнения больше, чем для функций print() или echo().
* Более ясный код: Все-таки, надо признать, что использование функции printf() затрудняет чтение кода (имеющих достаточный опыт программирования на C, это, конечно, касается в меньшей степени). Чтобы функция printf() не повела себя самым неожиданным для вас образом, требуется как знание синтаксиса данной функции, (т.е. %s определяет строковый формат вывода, тогда как %d — десятичный), так и знание типов переменных.
Использование функции printf() для вывода значения, возвращаемого функцией
Еще одна характерная ошибка использования функции printf() — вывод значения, возвращаемого функцией, как в следующем примере:
<?php printf ("Найдено %d вхождений строки %s", count ($result), $search_term); ?>
Наряду с функцией print(), при использовании ее в тех же целях, следует использовать оператор ‘.’ В данном случае этот оператор добавляет текст к результату вызова функции:
<?php print "Найдено " . count ($result) . " вхождений строки $search_term"; ?>
Использование оператора . в паре с функцией print() позволяет избежать использования более медленной функции printf().
20. Неверное применение семантики языка
Многие программисты используют в своей работе PHP , фактически не понимая тонкостей этого языка. Одна из тонкостей — разница между синтаксисом и семантикой PHP.
* Синтаксис PHP:Представляет собой набор правил для определения элементов языка. Например, как мы определяем переменную? Ставим знак $ перед ее именем. Как определяем функцию? В общем случае, используя скобки, аргументы и т.п.
* Семантика PHP: Представляет собой набор правил для применения синтаксиса. Например, возьмем функцию с двумя аргументами, что определяется ее синтаксисом. Причем в качестве аргументов ей следует передавать переменные строкового типа ?- это определяется семантикой.
Заметьте: <следует>. В языках с четким разделением типов (таких как Java или C) нет понятия <следует> (в общем случае, хотя бывают и исключения). В таком случае компилятор вынудит использовать переменные строго определенного типа.
Языки, в которых отсутствует само определение типов переменных, предоставляют больше гибкости в написании кода. Но, как бы то ни было, в случае неправильного использования семантики для большинства функций PHP следует ожидать появления сообщения об ошибке.
Возьмем кусок кода, который открывает файл и выводит его построчно:
<?php $fp = @fopen ( 'somefile.txt', 'r' ) or die ( 'Не могу открыть файл somefile.txt' ); while ($line = @fgets ( "$fp", 1024)) // Здесь ошибка! { print $line; } @fclose ("$fp") // И здесь тоже color or die( 'Не могу закрыть файл somefile.txt' ); ?>
В данном случае появится сообщение об ошибке типа:
«Warning: Supplied argument is not a valid File-Handle resource in tst.php on line 4»
(«Внимание: аргумент не может являться дескриптором файла»)
Это вызвано тем, что переменная $fp заключена в двойные кавычки, что однозначно определяет ее как строку, тогда как функция fopen() ожидает в качестве первого аргумента дескриптор, но не строку. Соответственно, вам следует использовать переменную, которая может содержать дескриптор.
Примечание: В данном случае, строковый тип допустим синтаксически.
Для решения проблемы следует просто убрать двойные кавычки:
<?php $fp = @fopen ( 'somefile.txt', 'r' ) or die ( 'Не могу открыть файл somefile.txt' ); while ( $line = @fgets ($fp, 1024) ) { print $line; } @fclose ($fp) or die ( 'Не могу закрыть файл somefile.txt' ); ?>
Как избежать неправильного приложения семантики?
В приведенном примере генерируется сообщение об ошибке. Но PHP предоставляет программисту больше свободы, чем другие, традиционные языки программирования. Это позволяет получать интересные результаты. Как минимум, теоретически возможно написать корректный код, неправильно используя семантику языка.
Но будьте осторожны, заигрывая с семантикой языка! Возможно появление трудноуловимых ошибок в программах. Если же вы все-таки решили поэкспериментировать, вам следует понимать три ключевых момента:
* Типы: В PHP каждая переменная в любой момент времени относится к определенному типу. И это несмотря на тот факт, что ее тип можно свободно изменять. Другими словами, в языке PHP переменная не может существовать, при этом не относясь к определенному типу (и, соответственно, не обладая характеристиками, присущих этому типу). В PHP есть 7 основных типов переменных: Boolean, resource, integer, double, string, array и object.
* Область видимости: В PHP переменные имеют область видимости, которая определяет то, откуда она может быть доступна, и насколько долго будет существовать. Недопонимание концепции <области видимости> может проявляться в виде различного рода <плавающих> ошибок.
* php.ini: При написании кода следует понимать, что не все пользователи имеют такую же конфигурацию программно-аппаратных средств, как и вы. Таким образом, совершенно необходимо лишний раз убедиться, сохраняется ли работоспособность вашего кода в той конфигурации, в которой программа должна работать, а не в той, в которой разрабатывалась.
19. Недостаточно либо излишне комментированный текст
Плохо документированный текст программы является признаком эгоистичного программиста. Результатом попытки анализа вашей программы с целью внесения улучшений будет только головная боль. Причем все программисты считают самодокументированный код хорошим тоном, но сами крайне редко пишут комментарии.
Следует также избегать избыточных комментариев. Это тоже встречается очень редко, и, опять же, создает трудно читаемый исходный код. Следующий пример это иллюстрирует:
<?php // Начало кода $age = 18; // Возраст равен 18 $age++; // Увеличим $age на один год // Напечатаем приветствие print "Вам сейчас 19 лет, и это значит, что Вам уже было:"; print "n<br>n<br>n"; // Цикл 'для' чтобы вывести все // предыдущие значения возраста for ($idx = 0; $idx < $age; $idx++) { // Напечатаем каждое значение возраста print "$idx летn<br>n"; } // Конец кода ?>
И все-таки: где золотая середина?
Итак, какой же объем комментариев следует помещать в скрипт?! Это зависит от многого: от времени, которым вы располагаете, от политики компании, сложности проекта и т.д. Тем не менее, запомните несколько основных принципов, которым надо следовать при написании программ вне зависимости от вашего решения:
* Перед телом функции всегда помещайте комментарий — назначение функции.
* Добавляйте комментарии в сомнительных участках кода, когда нет уверенности, что он будет работать как надо.
* Если назначение кода неочевидно, внесите информацию о предназначении этого участка. Вы же потом воспользуетесь этим комментарием.
* Избегайте комментарии вида # — используйте только /* */ либо //.
Следующий пример иллюстрирует хороший стиль комментариев:
<?php // Random_Numbers.lib // Генерация случайных чисел различного типа mt_srand((double)microtime()*1000000); // // mixed random_element(array $elements[, array weights]) // Возвращает случайный элемент массива-аргумента // Массив weights содержит относительные вероятности // выборки элементов // function random_element ($elements, $weights = array()) { // Для корректного функционирования этого алгоритма // количество элементов массива должно быть равным // количеству элементов массива относительных вероятностей if (count ($weights) == count ($elements)) { foreach ($elements as $element) { foreach ($weights as $idx) { // Примечание: мы не используем $idx, потому что // нам не нужен доступ к отдельным элементам // массива weights $randomAr[] = $element; } } } else { $randomAr = $elements; } $random_element = mt_rand (0, count ($randomAr) - 1); return $randomAr [$random_element]; } ?>
18. Слишком много переменных — слишком большое время выполнения
Некоторые прямо-таки страдают навязчивой идеей вводить временные переменные где надо и где не надо. Совершенно невозможно понять, чем руководствовался человек, написавший такой код:
<?php $tmp = date ("F d, h:i a"); // т.е. формат даты February 23, 2:30 pm print $tmp; ?>
Для чего здесь использована временная переменная?! Она просто не нужна:
<?php print date ("F d, h:i a"); ?>
К сожалению, многие программисты никак не могут избавиться от этой дурной привычки. Использование временных переменных замедляет выполнение программы. Для увеличения скорости кода, где это возможно, лучше воспользоваться вложением функций. Использование временных переменных зачастую увеличивают время выполнения скриптов почти на четверть.
Еще одна причина, по которой следует избегать использования излишнего количества временных переменных, это ухудшение читаемости кода. Сравните два приведенных примера. Какой из них выглядит более элегантно — с использованием временной переменной или без? Какой код проще прочесть? Использование лишних временных переменных ведет к написанию менее читаемого и ясного кода.
Плюсы использования временных переменных
Введение временных переменных позволяет упростить некоторые сложные выражения или вызовы функций. Еще они приносят пользу, когда позволяют избежать многократного вызова функции с одними и теми же аргументами.
Вот пример, в котором не используется лишних переменных:
<?php // string reverse_characters (string str) // Переворачивает строку символов function reverse_characters ($str) { return implode ("", array_reverse (preg_split ("//", $str))); } ?>
Вызову функции implode() в качестве одного из параметров передается результат выполнения вложенных функций, поэтому такой код трудно прочесть. В данном случае нам может здорово помочь использование временной переменной:
<?php // string reverse_characters (string str) // Переворачивает строку символов function reverse_characters ($str) { $characters = preg_split ("//", $str); $characters = array_reverse ($characters); return implode ("", $characters); } ?>
Золотое правило
Если вы думаете, ввести или нет еще одну временную переменную, задайте себе два вопроса:
* Будет ли эта переменная использована хотя бы дважды?
* Значительно ли улучшится с ее введением читаемость кода?
Если на любой из этих вопросов вы ответили <да>, тогда введите временную переменную. Иначе комбинируйте вызовы функций (если это необходимо) и обойдитесь без ее использования.
17. Переписываем стандартные функции
Кое-кто рекомендует переименовывать стандартные функции для того, чтобы программистам на Visual Basic’е проще было перейти к использованию языка PHP:
<?php function len ($str) { return strlen ($str); } ?>
Встречаются также рекомендации, приступая к программированию на PHP, первым делом заменять имена встроенных функций более привычными.
Существует, как минимум, две причины этого не делать. Во-первых и прежде всего, мы получаем менее читаемый код. Люди, читающие ваш код, увидят массу очевидно ненужных функций и будут сильно удивлены, почему же вами не использовались стандартные функции PHP.
Ну и, наконец, это замедляет программу. Дело не только в необходимости обработки большего объема кода, но и в том, что для вызова такой пользовательской функции, требуется больше времени, чем для прямого вызова стандартной функции.
Используйте стандартные функций языка!
Иногда так трудно устоять! Ведь программист редко знает сразу весь набор функций — у него обычно нет времени запомнить их все. Почему бы просто не переименовать функцию? Но, повторимся, этого не следует делать в силу изложенных выше причин.
Хорошо бы иметь под рукой справочник по функциям PHP (удобно использовать индексированную версию в формате PDF). И перед тем как написать какую-либо функцию, внимательно посмотреть — не существует ли она уже в списке стандартных функций.
Но следует заметить, что в кодах программ можно встретить пользовательские функции, написанные еще до их введения в качестве стандартных (например, функции сравнения двух массивов). Это не означает, что вы обязательно должны переписать код и заменять их стандартными функциями.
16. Клиентская часть программы не отделяется от серверной части
Многие программисты рекомендуют объединять код HTML (интерпретируемый на стороне клиента) и код PHP (выполняемый сервером) в один большой файл.
Для маленьких сайтов это, возможно, неплохо. Но, когда ваш сайт начнет расти, вы можете столкнуться с проблемами при необходимости добавить какие-либо новые функции. Такой стиль программирования приводит к очень <непослушному> и громоздкому коду.
API функций
Если вы собрались отделить код PHP от HTML кода, у вас есть два варианта. Один способ — создание функций динамического формирования вывода и поместить их в нужное место на веб-странице.
Например, так:
index.php — код страницы
<?php include_once ("site.lib"); ?> <html> <head> <title><?php print_header (); ?></title> </head> <body> <h1><?php print_header (); ?></h1> <table border="0" cellpadding="0" cellspacing="0"> <tr> <td width="25%"> <?php print_links (); ?> </td> <td> <?php print_body (); ?> </td> </tr> </table> </body> </html>
site.lib — Сам код программы
<?php $dbh = mysql_connect ("localhost", "sh", "pass") or die (sprintf ("Не могу открыть соединение с MySQL [%s]: %s", mysql_errno (), mysql_error ())); @mysql_select_db ("MainSite") or die (sprintf ("Не могу выбрать базу данных [%s]: %s", mysql_errno (), mysql_error ())); $sth = @mysql_query ("SELECT * FROM site", $dbh) or die (sprintf ("Не могу выполнить запрос [%s]: %s", mysql_errno (), mysql_error ())); $site_info = mysql_fetch_object ($sth); function print_header () { global $site_info; print $site_info->header; } function print_body () { global $site_info; print nl2br ($site_info->body); } function print_links () { global $site_info; $links = explode ("n", $site_info->links); $names = explode ("n", $site_info->link_names); for ($i = 0; $i < count ($links); $i++) { print "ttt <a href="$links[$i]">$names[$i]</a> n<br>n"; } } ?>
Очевидно, такой код лучше читаем. Еще одно преимущество использования этой концепции — возможность изменения дизайна без модификации самого кода программы.
Плюсы использования API функций
* Относительно чистый, ясный код
* Быстрый код
Минусы использования API функций
* Не настолько наглядно как система шаблонов
* Все-таки, для модификации дизайна требуется некоторое знание PHP
Система шаблонов
Второй способ, используемый для разделения PHP и HTML кода — использование шаблонов. В данном случае, некоторые элементы дизайна заменяются пользовательскими тегами, а сама программа сканирует файл на предмет их наличия и заменяет их необходимой информацией.
Пример использования шаблонов:
<html> <head> <title>%%PAGE_TITLE%%</title> </head> <body %%BODY_PROPERTIES%%> <h1>%%PAGE_TITLE%%</h1> <table border="0" cellpadding="0" cellspacing="0"> <tr> <td width="25%">%%PAGE_LINKS%%</td> <td>%%PAGE_CONTENT%%</td> </tr> </table> </body> </html>
Затем пишем программу, просматривающую код шаблона и при выводе заменяющую тэги вида %%:%% нужной информацией.
Примечание: неплохой класс для использования его в системе шаблонов — FastTemplate, его можно скачать с http://www.thewebmasters.net/.
Плюсы использования шаблонов:
* Предельно просто и ясно
* Для изменения шаблонов не требуется знание PHP
Минусы использования шаблонов:
* Более медленный способ — ведь надо сканировать весь шаблон и лишь потом выводить данные
* Сложнее внедрить на практике
15. Использование устаревшего синтаксиса и функций
Некоторые программисты вновь и вновь используют старые библиотеки и старые наработки. Например, код, написанный еще под PHP 2, до сих пор используется с PHP4, хотя уже начиная с версии PHP3 были добавлены стандартные функции, реализующие то же самое.
Использование устаревших функций и синтаксиса могут снизить скорость выполнения кода и, к тому же, сделать его нечитаемым. Другие программисты могут быть незнакомы со старыми функциями. Но тем не менее, если вы встретили участок старого кода, не обязательно его переписывать с учетом новых правил языка. Просто не надо его использовать при написании новых программ.
Пример использования старых языковых конструкций:
<?php // Старый стиль while (1): print "5"; if ( $idx++ == 5 ): break; endif; endwhile; // Лучше написать так // (впрочем, код можно оптимизировать) while (1) { print "5"; if ( $idx++ == 5 ) { break; } } ?>
Почему же следует следовать новым стандартам? Причины следующие:
* Использование старых конструкций не очень распространено и, таким образом, новички в PHP будут в замешательстве, увидев два разных варианта синтаксиса.
* Старый синтаксис отличается от синтаксиса других языков программирования, и, следовательно, при переходе с другого языка на PHP программисту будет сложнее понять и привыкнуть.
* Но самое главное — в одной из новой версий, возможно, будет исключена поддержка старого синтаксиса, тем самым это заставит вас переписать код заново. Как бы то ни было, скобки всегда останутся часть языка PHP.
Подобные участки кода можно встретить во многих программах. Вам, как правило, следует руководствоваться правилами, приведенными в документации по PHP, большей часть обновленной — в ней отражается развитие языка. Периодически просматривайте документацию, ведь язык развивается, добавляются новые функции. Таким образом, вам никогда не придется писать пользовательские функции, выполняющие ту же работу, что и стандартные.
Резюме
В этой статье мы рассмотрели первые 7 из 21 наиболее общих ошибок PHP программиста. Как правило, они не нарушают работоспособности программ, но, тем не менее, их следует избегать:
* Необоснованное применение функции printf(): Ее следует использовать только для вывода форматированных данных.
* Неправильное применение семантики языка: Многие программисты не имеют достаточно времени, чтобы разобраться во всех тонкостях языка, что впоследствии выражается в ошибочном коде.
* Плохо комментированный код: Всегда пишите комментарии! Перед каждой функцией указывайте, что делает данная функция, и какие аргументы она требует. Также комментируйте сложные участки кода и внесенные изменения.
* Слишком много временных переменных: Временные переменные хорошо использовать для предотвращения повторного вызова функций или последовательностей функций.
* Изобретаем велосипед — переписываем стандартную функцию: Сначала загляните в руководство по PHP — не описана ли там функция, которую вы собираетесь написать для, казалось бы, расширения набора стандартных функций PHP.
* Смешан PHP и HTML код: Попробуйте сделать код как можно более модульным. Потом вам (и другим тоже) можно будет сменить дизайн страницы без изменения кода PHP.
* Используются старые языковые конструкции и устаревшие функции: То, что вы можете сделать, не всегда следует делать. Загляните в документацию и литературу по PHP, как писать правильно. Отличные книги — «Разработка веб-приложений с использованием PHP (Web Application Development with PHP) и «Профессиональный программист PHP (Professional PHP). (Эх, где бы их еще найти! ;)) — прим. переводчика)
Стерлинг Хьюз (Sterling Hughes) — независимый разработчик веб-сайтов, занимается созданием динамических веб-приложений для некоторых крупнейших мировых компаний. Участвовал в создании cURL and SWF расширений PHP с открытым исходным кодом. Его книга, <Настольная книга программиста PHP> (The PHP Developer’s Cookbook), была издана в октябре 2000 года издательством .
Дмитрий Короленко. Ну что о нем много говорить? ;)) Благодаря ему люди, не знающие английского, или знающие, но очень ;)), смогут прочесть весьма познавательную статью Стерлинга Хьюза. О замеченных недостатках перевода просьба сообщать на мыло: lkx2@mail.ru Сразу хочется пояснить, что дословный перевод не был самоцелью, но, тем не менее, стиль и смысл оригинальной статьи по возможности ;)) сохранялись. Все замечания и пожелания приветствуются и будут учтены, ибо готовится перевод второй части этой статьи. Только просьба ногами не пинать ;)) Все же я надеюсь,что если вы до этого места дочитали, то не все так плохо ;))
Written on 20 Января 2007. Posted in PHP
Как правило, дело в недостаточной практике программирования. Неопытные программисты становятся перед лицом необходимости создания сложных веб-приложений. Поэтому сплошь и рядом допускаются ошибки, которых избежал бы опытный программист, такие как необоснованное использование функции printf()или неправильное использование семантики PHP.
В этой серии из трех статей представлены наиболее, по нашему мнению, характерные ошибки. Эти ошибки можно классифицировать по нескольким категориям, от некритических до смертельных. Наряду с анализом этих ошибок, представлены способы их избежания, а также некоторые маленькие хитрости, накопленные за многие годы практики программирования.
Целевая аудитория
Эта серия статей предназначена для тех программистов на языке PHP, которые хотят избежать наиболее общих ошибок в написании кода. Читатель, как минимум, должен знать общий синтаксис PHP, а также весьма желателен некоторый опыт использования языка на практике.
Введение
Одна из наиболее сильных сторон PHP является, одновременно, и его слабой стороной: PHP очень прост в изучении. Это привлекает многих людей; однако, несмотря на его кажущуюся простоту, не так-то просто научиться использовать этот язык правильно и эффективно.
Как правило, дело в недостаточной практике программирования. Неопытные программисты становятся перед лицом необходимости создания сложных веб-приложений. Поэтому сплошь и рядом допускаются ошибки, которых избежал бы опытный программист, такие как необоснованное использование функции printf()или неправильное использование семантики PHP.
В этой серии из трех статей представлены наиболее, по нашему мнению, характерные ошибки. Эти ошибки можно классифицировать по нескольким категориям, от «некритических» до «смертельных». Наряду с анализом этих ошибок, представлены способы их избежания, а также некоторые «маленькие хитрости», накопленные за многие годы практики программирования.
Часть 1: Описываются 7 «детских» ошибок (#21-15, в обратном порядке, в соответствии со степенью серьёзности по нашей классификации). Такие ошибки не вызывают серьёзных проблем, но приводят к уменьшению эффективности работы программы, а также выражаются в громоздком трудночитаемом коде, в который, к тому же, трудно вносить изменения.
Часть 2: Следующие 7 ошибок (#14-8) относятся к «серьёзным». Они ведут к ещё более значительному уменьшению скорости выполнения кода, уменьшению безопасности скриптов; код становится еще более запутанным.
Часть 3: Описания семи, последних, «смертельных» ошибок. Эти ошибки концептуальны по своей природе и являются причиной появления ошибок, описанных в 1-ой и 2-ой частях статьи. Они включают и такие ошибки, как недостаточное внимание, уделённое как проекту в целом, так и коду программы, в частности.
14. Пренебрежение правилами присвоения имён
Одна из наиболее серьёзных ошибок программиста — непродуманная система именования переменных проекта. Нередко приходится тратить уйму времени на разбор кода только потому, что автор вдруг решил ввести в программу переменные $fred и $barney вместо ожидаемых $email и $name. Речь ведётся о реальном проекте, где не менее реальный программист решил все переменные проекта назвать именами героев мультсериала «Flinstones» (Это не шутка).
То как вы назовёте переменные и функции программы, определит во многом читаемость её кода. Наиболее распространёнными ошибками являются имена:
- слишком короткие или наоборот, чрезмерно длинные;
- не связанные по смыслу с контекстом программы;
- не учитывающие регистрозависимость;
- замедляющие разбор и чтение кода (особенно это касается имён функций).
Именование переменных
Регистрозависимость
В PHP имена переменных регистрозависимы, то есть $user и $User — две записи в списке переменных скрипта. Однако некоторые программисты активно пользуются этим и производят на свет переменные с совершенно одинаковыми именами, но использующими буквы разных регистров. Это отвратительная привычка. Регистр букв никогда не должен быть единственным отличием двух переменных. Каждая переменная на своём поле действия должна иметь уникальное имя.
Слишком короткие имена
Для обозначения переменных многие программисты используют одним им понятные аббревиатуры. О чём впоследствии сильно жалеют, ибо смысл сокращения затерялся во времени своего создания. Имя переменной должно отражать характер её значения, то есть содержания и обозначаться полными словами или общепонятными сокращениями.
Слишком длинные имена
С другой стороны, наблюдаются случаи злоупотребления длинными именами. Наиболее общее правило: имя переменной должно состоять максимум из двух слов. Разделить эти два слова мы можем, поставив understrike (то есть «_») или написав второе слово с заглавной буквы.
Пример #1. Положительный.
Как правильно присваивать имена переменным:
<?php
$username = ‘sterling’;
$password = ‘secret’; $teachers = array (‘Sadlon’,
‘Lane’,
‘Patterson’,
‘Perry’,
‘Sandler’,
‘Mendick’,
‘Zung’);
foreach (
$teachers as $teacher);
?>
Пример #2. Отрицательный.
Теперь рассмотрим несколько преувеличенные примеры того, как не следует присваивать имена переменным:
<?php
$username_for_database = ‘sterling’;
$guMbi = ‘secret’; // for the $password $thelastnamesofteachers = array (‘Sadlon’,
‘Lane’,
‘Patterson’,
‘Perry’,
‘Sandler’,
‘Mendick’,
‘Zung’);
foreach (
$thelastnamesofteachers as
$TeaChER);
?>
Имена функций
Все правила, применяемые для имён переменных, годятся и для функций. Однако в случае с функциями, грамматические реалии имеют большее значение.
Помните, что в PHP все функции, встроенные или определённые разработчиком, — регистронезависимы.
Использование глаголов
Функции в PHP можно сравнить с какими-либо действиями, совершаемыми в реальном мире. Таким образом, имена функций должны отражать эту направленность на действие, то есть выражаться глаголами. Причём лучше в настоящем времени.
В качестве примера рассмотрим функцию, генерирующую Гауссовы случайные числа. Предполагается, что из её имени мы должны понять, какая именно формула используется в генерации числа. Вот так: generate_gaussian_rand().
Обратите внимание на использование глагола в имени функции. Именно глагол помещает функцию в правильный контекст:
<?php
list ($num1, $num2) = generate_gaussian_rand();
list ($num3, $num4) = generate_gaussian_rand();
?>
Для сравнения, другой пример:
<?php
list ($num1, $num2) = gaussian_rand_generator();
list ($num1, $num2) = gaussian_rand_generator();
?>
Видите разницу? Во втором примере для обозначения действия использовано существительное. И если назначение функции ещё прослеживается, название затрудняет чтение кода.
Мораль: используйте глаголы!
13. Непродуманная работа с данными: бд и sql
Забавно иногда наблюдать, сколько разных уловок находят люди для организации доступа к базам данных и получения выборки результатов. Среди прочих особенно выделяются комбинации из веток if, циклов do..while, множественных запросов и вызовов функции sql_result() внутри цикла for.
Чем, на их взгляд, они занимаются?
Код, основанный на методе научного тыка, говорит о недостаточно ясно определённой организации работы с БД. Те, кто прилагают все свои усилия на написание кода, а не на написание правильного кода, рискуют больше потерять, чем заработать. Некорректная выборка данных — яркий тому пример. Некоторые программисты не уделяют достаточно времени на тщательное продумывание этого момента. Естественно, в реальной жизни может и не оказаться того «единственно верного» способа выборки данных, но всегда найдётся тысяча «неверных», это точно.
Ошибки в организации выборки данным можно разделить на три класса:
- неправильное использование функций обращения к БД
- ошибки SQL: запрашивается не то, что нужно
- обработка результатов выборки средствами PHP
Неправильное использование функций обращения к БД
Один из PHP-исходников предлагал следующий способ получения выборки из БД (приведённый ниже код в проекте находится после сгенерированных SQL-запросов):
<?php
if (!($row = sql_fetch_row ($result))) {
print «Ошибка: не найдено ни одного ряда»;
exit;
}
do {
print
«$row[0]: $row[1]n<br>n»;
}
while ($row = sql_fetch_row ($result));
?>
Примечание: в данном и последующих примерах $result является дескриптором выборки или указателем на неё. Другими словами, был произведён запрос и получено определённое множество рядов. Примеры демонстрируют методы эффективной обработки этого множества.
В этом отрезке кода есть две ошибки:
- проверка на «ноль рядов» — это попытка получить хотя бы один.
- полученные данные не хранятся в ассоциативном массиве.
Проверка на «ноль рядов» ($result): неправильный подход
Задействовав функцию sql_fetch_row(), данный кусок кода предлагает косвенную проверку выборки на наличие хотя бы одного ряда данных. Но ведь существует прямой способ — это подсчёт количества рядов в выборке $result функцией sql_num_rows(), как показано ниже:
<?php if (sql_num_rows ($result) <= 0) {
print «Ошибка: не найдено ни одного ряда»;
exit;
}
while (
$row = sql_fetch_row ($result)){
print «$row[0]: $row[1]n<br>n»;
}
?>
Избавляемся от do..while
Прежде всего, исчезает необходимость в использовании давно уже поднадоевшего do..while, ибо для проверки на «ноль рядов» функция sql_num_row() не выдёргивает первый рядв $row, и указатель по-прежнему установлен на начало.
В PHP Source как-то был представлен подобный фрагмент кода. Если выборка не была нулевой, то функция sql_fetch_row() внутри условного блока доставляла первый ряд. Для получения остальных приходилось прибегать к do..while, потому что получение ряда из выборки («to fetch» — принести, доставить// Прим. перев.) смещает указатель в ней. Таким образом, сначала вам придётся обработать уже полученный ряд («do»), только потом получить второй ряд и так далее.
Так чем же do..while так провинился?
- в данном примере внутри цикла do..while помещён только один оператор: простой вывод. Теперь представим, что там может оказаться не один, а десять операторов. Тогда редактору кода придётся искать условие while после оператора do и целого блока действий внутри цикла. Занятие не из приятных.
- условие while обычно располагается в начале блока, а не в конце его. Поэтому редактору кода нужно будет уделять этому особое внимание при чтении, чтобы не спутать цикл do..while с предварительным условием while обычного цикла.
Делаем всё просто и понятно
В случае получения нулевой выборки, функция sql_num_row() в отличие от sql_fetch_row() делает именно то, что вам нужно сделать:
- действие sql_fetch_row(): «При попытке получить первый ряд не найдено ни одного ряда. Это может означать, что в данной выборке их нет«.
- Действие sql_num_row(): «Количество рядов в выборке равно нулю«.
Но как это отражается на написании кода?
Рассмотрим следующий пример, где операторы внутри условия записаны псевдокодом:
- if(!($row = sql_fetch_row($result))){Print Error}:
- Получаем первый ряд из выборки.
- Если выборка пустая, то переменной $row приписываем 0; ноль логически выражается False; отсюда !(0)=True; выводим сообщение об ошибке.
- Иначе, если выборка не пустая, получаем первый ряд, приписываем его переменной $row; $row не равно нулю, то есть True; !(True)=False; выходим на цикл do..while.
- If(sql_num_rows($result)<=0){Print Error}:
- Подсчёт рядов в выборке.
- Если их меньше или равно нулю, выводим сообщение об ошибке.
- Иначе — идём дальше.
Итак, какое из двух выражений проще и быстрее понять? Безусловно, подсчёт рядов — более прямой и короткий путь.
Каково всё же практическое преимущество второго способа? Невелика разница, что мы поместим внутри этого условия — многого тут не выиграть.
Однако на протяжении 10 000 строк вашего кода продуманные, а потому просто и ясно изложенные идеи сэкономят кучу времени редактору кода (вот и первое преимущество). Есть и другие преимущества: разработка скриптов заметно ускоряется и становится более размеренной.
Если ваша СУБД не поддерживает sql_num_row()
Действительно, некоторые СУБД могут не поддерживать эту функцию. Отнесёмся с сочувствием ко всем владельцам таких систем. Им придётся проверять выборки «на ноль рядов» путем запроса первого ряда. Однако и здесь, рекомендуем использовать булевские переменные:
<?php
$found = false;
while (
$row = sql_fetch_array($result)){
$found = true;
}
if (!
$found){
print «Ошибка»;
}
?>
Получение рядов данных: правила эффективной работы
Вторая проблема нашего кода — это использование функции sql_fetch_row() для получения рядов. Как результат своей работы эта функция возвращает лишь пронумерованный массив. Однако существует ещё и функция sql_fetch_array(), которая возвращает два массива: пронумерованный и ассоциативный:
<?php
$row = sql_fetch_array ($result);
print $row[1]; // Второй столбец
print $row[name]; // Столбец name — имя
?>
Примечание: Существуют разные точки зрения на целесообразность использования одинарных кавычек при вставке строковых аргументов. В приведённом примере (столбец name) и далее по статье они не используются.
Какая из функций более удобна для разработчика? Ассоциативные массивы позволяют редактору кода ясно и однозначно понять, какая именно выборка из БД будет осуществляться в каждом конкретном случае. Например:
<?php
if (sql_num_rows ($result) <= 0) {
print «Ошибка: не найдено ни одного ряда»;
exit;
}
while (
$row = sql_fetch_array ($result)) {
print «$row[name]: $row[phone_number]n<br>n»;
}
?>
Применение sql_fetch_row($result)
Итак, функция sql_fetch_row() имеет целую тонну недостатков. Однако, существует ситуация, где её можно поставить без всякого ущерба «прозрачности» кода: когда sql-запрос формируется пользователем.
До настоящего момента мы рассматривали примеры с заранее известными запросами и определёнными разработчиком. Но иногда возникает необходимость в запросе, сформированном самим пользователем. В таких случаях разработчику неизвестно количество столбцов в выборке.
Здесь для их эффективной обработки полезно использовать функцию sql_fetch_row() в сочетании с count():
<?php
for ($i = 0; $i < count($row); $i++){
print «Столбец». ($i + 1). $row[$i]. «n<BR>n»;
}
?>
Ошибки SQL: запрашивается не то, что нужно
Практика показывает, что обработка выборки из БД средствами PHP — тоже является ошибкой. Бывали случаи, когда для простого поиска по 2Мб БД программисты использовали PHP, а потом возмущались его медлительностью. А делать выборку «весом» в два метра занимает целую вечность.
Язык Структурированных Запросов (SQL) был специально разработан для запросов и получения данных из таблиц в БД. Идея языка заключается в отсеивании данных ненужных вам (средствами SQL) и получении только тех, которые вам действительно необходимы для дальнейшей обработки (например, средствами PHP).
Если вы заметили, что получаете в выборке данных, больше, чем вам нужно, это верный признак недоработанных SQL-запросов.
Условие WHERE
Классический пример эффективного применения SQL-запросов — использование условия WHERE в синтаксисе SQL.
Рассмотрим пример кода, производящего выборку и выводящего список имён и телефонов всех пользователей с id равным 5:
<?php
// В предыдущих строках
// устанавливается соединение, и $conn
// определяется как дескриптор соединения. $statement = «SELECT name, phone, id FROM samp_table»;
$result = @sql_query ($statement, $conn);
if (!
$result) {
die (sprintf («Ошибка [%d]: %s», sql_errno (), sql_error ()));
}
if (@
sql_num_rows ($result) <= 0) {
die («Получено ноль результатов»);
}
while (
$row = @sql_fetch_array ($result)){
if ($row[id] & 5) {
print «Имя: $row[name]n<br>n»;
print «Телефон: $row[phone]n<br>n»;
break;
}
}
?>
Данный код имеет следующие недоработки: для поиска по всей БД используется PHP; при работе с БД малого размера на это можно и не обращать внимания, но с ростом БД вы обязательно заметите резкое падение скорости работы скриптов.
Выход прост: включите в SQL-запрос условие WHERE:
<?php
$statement = «SELECT name, phone FROM samp_table»;
$statement .= » WHERE id=’5′»;
WHERE позволит применить более строгие критерии выборки. Фильтром в данном случае будет являться значение аргумента. В нашем примере это «id=5».
Получив нужную вам выборку, вы используете PHP для простого вывода результатов:
<?php
if (@sql_num_rows ($result) != 1) {
die («Получено неверное количество рядов»);
} $row = @sql_fetch_array ($result);
print «Имя: $row[name]n<br>n»;
print «Телефон: $row[phone]n<br>n»;
?>
Обработка результатов выборки средствами PHP
Нередко программист намеренно не сортирует выборку при запросе, перекладывая эту работу на PHP. Такой подход неэффективен, ибо сортировка средствами SQL проходит намного быстрее, чем в PHP.
Для сортировки результатов рекомендуем применять синтаксис SQL (ORDER BY), а не PHP-функцию ksort().
Рассмотрим пример использования ksort() для сортировки выборки по имени (name):
<?php
$statement = «SELECT name, email, phone FROM some_table «;
$statement .= «WHERE name IS LIKE ‘%baggins'»; $result = @sql_db_query ($statement, «samp_db», $conn);
if (!
$result) {
die (sprintf («Ошибка [%d]: %s», sql_errno (),sql_error ()));
}
while (
$row = @sql_fetch_array ($result)){
$matches[ $row[name] ] = array ($row[email], $row[phone]);
} ksort ($matches);
?>
Возникает вопрос: а почему бы ни провести сортировку результатов во время выборки? Это избавит нас от необходимости проходить по всему массиву с результатами дважды.
Итак, убираем ksort() и исправляем SQL-запрос, добавив ORDER BY:
<?php
$statement = «SELECT name, email, phone FROM some_table «;
$statement .= «WHERE name IS LIKE ‘%baggins’ ORDER BY name»;
?>
12. Слабая устойчивость к ошибкам
В природе существует огромное количество скриптов абсолютно не справляющихся с пользовательскими ошибками. Своим появлением такие скрипты обязаны программистам, которые не удосуживаются правильно распланировать будущий проект и определить все места возможных ошибок. Причём этим следует заняться до того, как скрипт был написан. Недоработки подобного рода приводят к сбоям программы, что чревато не только получением некорректных результатов, но и падением системы!
Предусмотреть худшее
Любой скрипт может «свалиться» при наступлении каких-либо «критичных» условий. Чтобы свести такой риск к минимуму всегда нужно:
- проверять результаты вызова функций:
- проверять результаты системных вызовов:
- в файле php.ini устанавливать уровень error_reporting на E_ALL
Проверка результатов вызова функций
При вызове функции, результаты которой подвергаются дальнейшей обработке, обязательно убедитесь, что возвращаемые данные находятся в интервале допустимых значений.
В приведённом ниже примере на шестом витке цикла возникает ошибка «деление на ноль», поскольку $i наращивается на 1, а $j уменьшается на 1. На шестом проходе $i=$j=1.
<?php
mt_srand((double)microtime() * 10000000);
function
do_math ($a, $b) {
return (($a — $b) * 2) / mt_rand();
}
for (
$i = 5, $j = —5; $i > —5; $i—, $j++){
print $j / do_math ($i, $j) . «n»;
}
?>
Проверка результатов системных вызовов
При обращении к внешним файлам или процессам всегда проверяйте, всё ли работает корректно.
Блестящий тому пример — проверка ответа системы при вызове функции sql_connect(). Стоит проверить этот ответ и убедиться, что подключение к БД действительно имело место. Если этого не сделать, то все запросы к БД могут не состояться, а некоторые данные могут быть утеряны; вы же будете пребывать в счастливом неведении.
<?php
$conn = @sql_connect ($host, $user, $pass);
if (!
$conn) {
die (sprintf («Ошибка [%d]: %s», sql_errno (), sql_error ()));
}
?>
Установка уровня error_reporting в файле php.ini на E_ALL
Убедитесь, что PHP правильно сконфигурирован, то есть уровень error_reporting (отображение сообщений об ошибках) выставлено на наивысший уровень. При другой конфигурации, по крайней мере, на время отладки скриптов, многие ошибки типа «неверное регулярное выражение», «недопустимое значение» ускользнут от вашего внимания.
Обратимся ещё раз к примеру, приведённому в части «Проверка результатов вызова функций». Предположим, что error_reporting выставлен не на максимум, а, скажем, на E_ERROR.
Обратите внимание на то, как скрипт выполняет функцию do_math, но не сообщает об ошибке «деление на ноль», которая, однако, имела место (при $i=$j=0 вывода результата просто не было).
<?php
error_reporting (E_ERROR); mt_srand ((double)microtime() * 1000000);
function
do_math ($a, $b) {
return (($a — $b) * 2) / mt_rand();
}
for (
$i = 5, $j = —5; $i > —5; $i—, $j++){
print $j / do_math ($i, $j) . «n»;
}
?>
Результат работы скрипта:-5148.25
-5271
-323.75
-4931
-7713.5-4702.5
-488.5
-928.5
-1394.75
Свои обработчики ошибок
Как правило, PHP выдаёт сообщения об ошибках непосредственно в браузер и не позволяет разработчику подавить или перехватить их. Однако в PHP4 у вас появилась возможность перехвата таких сообщений с помощью функции set_error_handler().
Функция set_error_handler() применяется для записи ошибок вашего скрипта. Теперь вы можете перехватывать все ошибки и программировать собственные обработчики — warning’и пользователей больше не побеспокоят.
В следующем примере set_error_handler() назначает обработчиком по умолчанию функцию error_handler(). В случае возникновения ошибки вызывается error_handler(), и встроенная функция error_log() регистрирует сбой в файле лога error_file.
Если происходит ошибка класса E_ERROR, работа скрипта прекращается и выводится сообщение об ошибке.
<?php // void error_handler(string type, string message, string file, int line)
// Индивидуальный обработчик ошибок, определён функцией
// set_error_handler() function error_handler ($type, $message, $file = __FILE__, $line = __LINE__) {
error_log(«$message, $file, $line», 3, ‘error_file’);
if ($type & E_ERROR) {
print ‘Произошла ошибка, зарегистирована.’;
exit;
}
} set_error_handler(‘error_handler’);
?>
11. Неоправданное использование ООП
Парадигма ООП — замечательный подход к написанию кода. У ООП есть множество неоспоримых преимуществ, самое значительное из которых — возможность использовать заново уже некогда написанный код. Однако все мы рано или поздно осознаём тот факт, что ‘PHP — не объектно-ориентированный язык’.
Несмотря на то, что PHP имеет корректно работающую поддержку объектов, использовать объекты там, где можно без них обойтись — недальновидно и неэффективно. Причина? Дело в том, что поддержка парадигмы ООП в PHP реализована не в полном объёме.
Несмотря на присутствие основных элементов, PHP всё-таки не хватает многих «продвинутых» функций (как защищённые члены или закрытые переменные), которые обязательны для «настоящих» объектно-ориентированных языков (например, Java, C++).
Кроме того, поддержка объектов в PHP недостаточно отработана и не очень эффективна. Это означает, что использование парадигмы ООП может существенно снизить скорость выполнения программы.
Примечание: Другими словами, скрипт, работающий на объектах будет исполняться медленнее, как код внутри eval() по сравнению с обычным кодом. Для более наглядных примеров, где использование ООП принимает какие-то уродливые формы, пришлось бы прибегнуть к продвинутым функциям концепциям PHP, некоторые из которых даже незадокументированы. Так что остановимся на этом.
А что же мы сможем без ООП?
Если вы пришли в PHP из Java или C++, где без объектов трудно создать что-либо более или менее серьёзное, то и в PHP вам будет трудно обходиться без них. Но будьте уверены, что серьёзные приложения могут быть написаны и методик и приёмов ООП (PHP был написан на C, а последний, как мы знаем, не поддерживает объектов).
Итак, для тех, кто не привык обходиться без ООП, приведём альтернативные технологии написания связных и расширяемых приложений вне парадигмы ООП:
- Создание API.
- Разработка концепции именования (и работа в её рамках).
- Группирование взаимосвязанных функций в один файл.
Создание API
Соотнесём код программы с тремя уровнями:
- Первый — собственно рабочие функции.
- Второй — API функции. Сюда входят функции для построения конкретного приложения.
- Третий — само приложение:
<?php
// MortgageRate.php (Ипотечный Кредит)
// Уровень первый — внутренние функции
// Внутренние функции для расчёта оптимальной процентной ставки исходя из времени и размера помесячных выплат
function _mort_find_interest_rate ($total) {
if ($total < 30000)
return (7.4);
elseif ($total > 30000)
return (3.2);
elseif ($total > 50000)
return (2.5);
else
return (1.7);
} // Уровень второй — API функции
// double calculate_mortgage_rate (int money, int time, int month)
// Рассчитывает процентную ставку исходя из суммы займа, времени погашения и интервала выплат
function calculate_mortgage_rate ($money, $time, $month) {
$rate = _mort_find_interest_rate ($money) / 100;
$money /= ($time / $month);
return ($rate * $money) + $money;
} ?>
<?php
// CalcMortgage.php
// Третий уровень — приложение
// $money, $time и $period получаем из формы
include_once ‘MortgageRate.php’; $price = calculate_mortgage_rate ($money, $time, $period);
«Ваша процентная ставка за $period составляет $price»;
?>
Разработка концепции именования и работа в её рамках.
Один из самых неприятных моментов при разработке больших приложений — это конфликты пространства имён. Классы его сегментируют. Таким образом, разные классы могут:
- иметь свойства с одинаковыми именами или
- содержать в себе методы с одинаковыми именами.
Например, класс Phillips и класс Normal могут одновременно содержать метод с именем screwdriver.
В общем, перед началом большого проекта рекомендуется продумать именование для всего, в частности, способы различия глобальных и регулярных переменных, определения библиотечных функций и т. д.
Группирование взаимосвязанных функций в один файл
Связанные API функции лучше всего собрать в один файл так же, как связанные методы объединяются в класс. Такие файлы можно представить классами, где каждая функция представляет собой как бы метод этого класса. Так, каждая функция будет иметь ясное определение и прозрачную структуру.
Например, можно было бы все функции, связанные с общением с БД, собрать в файл DB.php.
ООП, как и всё на свете, хорошо в меру
Небольшая оговорка: эта глава была написана не для того, чтобы отговорить вас от использования ООП вообще. Скорее, это была попытка убедить вас не работать с PHP в режиме Java или C++, где ООП — решение номер один.
Проведите тщательный анализ всех выгод и потерь, прежде чем применить объектный подход в PHP.
10. Неоправданное использование регулярных выражений
Регулярные выражения — мощный инструмент для поиска и организации данных, как, например, проверка адреса электронной почты на корректность или поиск URL. Но в то же время регулярные выражения работают медленнее других функций PHP, предназначенных для более простых задач.
Например, для перевода целой строки в заглавные буквы, новичок в PHP мог бы написать следующее:
<?php
$URL = «http://www.php.net»; $fp = @fopen ($URL, «r»);
if (!$fp) {
die («Сбой при открытии $URL!»);
}
while (
$line = @fgets ($fp, 1024)){
$data .= $line;
}
@
fclose ($fp) or warn («Сбой при закрытии дескриптора $URL»); $data = ereg_replace («[a-z]», «[A-Z]», $data);
$data;
?>
Однако, в этом случае, используя тяжеловесный и медленный ereg_replace(), он потратил бы кучу драгоценного времени выполнения на задачу, с которой более лёгкая функция strtoupper() справилась бы намного быстрее
$data = strtoupper ($data);
В общем, всегда следует искать более «лёгкую» замену регулярным выражениям, поскольку скорость выполнения вашего скрипта в таких случаях резко возрастает.
Эти функции должен знать каждый
Здесь будут перечислены несколько жизненно необходимых функций, сокращающих время выполнения вашего скрипта:
strtoupper(); strtolower(); ucfirst(); strtr(); str_replace(); trim(); explode(); implode(); substr(); strcmp()
При успешной замене замену регулярного выражения одной из этих функций вы можете рассчитывать на резкое повышение скорости, особенно если анализу подвергаются строки больших размеров.
9. Программирование на php как на другом языке
Многие приходят в PHP уже с большим опытом программирования в другом языке, как PERL, C, Java или (ну это ещё куда ни шло) ASP. И частенько «импортируют» техники и подходы, которые не всегда хорошо сочетаются с методиками PHP.
К сожалению, некоторые программисты как-то не удосуживаются поучиться PHP-программированию в стиле именно PHP. Вместо этого они обзаводятся минимальным набором новых для них концепций и «насилуют» PHP.
При таком подходе очень часто на выходе мы получаем медленный и трудно поддерживаемый код. И такие случаи не редкость:
- «однострочники» PERL. PHP — язык мало приспособленный к так называемому методу — «всё в одной строке». Рекомендуем разбивать сложные хитросплетения комбинированных функций и представлять их в более структурированном виде.
Perlwhile () {
@_ = split /:/;
$quotes{shift} = shift;
}
print map { "$_: ", reverse split //,$quotes->{$_},"n"; } keys %quotes;
PHP
<?php
$fp
= @fopen(‘php://stdin’, ‘r’);
if (!$fp) {
die (‘Сбой при открытии STDIN’);
}while (
$line = @fgets ($fp, 1024)){
list($name, $quote) = explode (‘:’, $line);
$quotes[ $name ] = $quote;
}foreach (
$quotes as $name => $quote){
print «$name: «;
print implode (» «, array_reverse (preg_split (‘//’, $quote)));
print «n»;
}@
fclose ($fp);
?> - Уклонение от встроенных функций: Многие PHP-программисты, пришедшие из C, кажется, не понимают того, что PHP содержит целую армию встроенных функций. Их цель — избавить вас от километровых скриптов. Если вы раньше писали на C, вам настоятельно рекомендуется изучить техническое описание PHP и узнать, какие функции PHP вам может предложить и тем самым облегчить вам жизнь.
- Переименование стандартных функций PHP: некоторые программисты переименовывают стандартные функции PHP только для того, чтобы легче их запоминать. Это не только снижает скорость выполнения скрипта, но и затрудняет чтение кода.
- Неоправданное использование ООП: PHP — не объектно-ориентированный язык, хотя некоторая поддержка объектов всё-таки имеется. И всегда стоит помнить, что использование функций поддержки ООП значительно снижает скорость выполнения скрипта.
Где можно почитать об «изящном стиле» программирования на PHP
На ваше счастье Инет просто забит статьями о том, как правильно программировать на PHP.
8. Недостаточное внимание к вопросам безопасности
Пользователи никогда не будут работать по нашим правилам. Поэтому создание системы устойчивой и терпимой к ошибкам — целиком и полностью ответственность разработчиков, то есть нас.
При разработке приложения вы должны влезть в шкуру обычного юзера. Внимательно изучите все места, где пользовательская ошибка может вскрыть брешь в безопасности. Затем исправьте код так, чтобы программа сама нейтрализовала все ошибки и тем самым избавилась от потенциальных «дыр». Также важно запомнить, что хотя все ошибки и атаки — вина пользователей, за «дыры» или за непроверенные данные на каком-либо уровне отвечаете только вы.
Пример: многие скрипты не используют встроенную PHP-функцию mail(), которая обеспечивает безопасную отправку почты. Вместо этого почта отправляется через программу sendmail с помощью popen(). Это делает код весьма неустойчивым к искажению данных и представляет собой «дыру» в безопасности системы (получателю можно отправить /etc/passwd).
Перечислим наиболее распространённые «дыры», позволяющие искажать данные:
- системные вызовы. Никогда нелишне повторить. Каждый раз нужно убедиться, сто пользователь отправляет вам «безопасные» данные для системного вызова. НИКОГДА НЕ ДОВЕРЯЙТЕ ДАННЫМ, ПОЛУЧЕННЫМ ОТ ПОЛЬЗОВАТЕЛЯ. И ПРЕЖДЕ ЧЕМ ПОДСТАВИТЬ ИХ В СИСТЕМНЫЙ ВЫЗОВ ПРОВЕРЬТЕ ИХ.
- регистрация пользователей. Если вы желаете получить корректные результаты, обязательно проверяйте полученные данные, причём, лучше если проверка будет состоять из нескольких этапов. Прежде всего, это проверка адреса электронной почты: убедитесь, что пользователь предоставил вам работающий аккаунт. Кроме того, стоит убедиться, что указанный возраст находится в определённых пределах. Вы можете быть совершенно уверены, что на нашей планете нет двухсотлетних товарищей, которые ещё способны сесть за компьютер.
- Приём номеров кредитных карточек. Некоторые программисты ограничиваются самыми простыми алгоритмами; их легко обмануть. Существуют крупные специализированные организации для проверки кредитных карточек; рекомендуем прибегать к их помощи или ресурсам и только потом решать, принята карточка или нет. НИКОГДА НЕ ПОЛАГАЙТЕСЬ ТОЛЬКО НА АЛГОРИТМЫ.
Безопасность системных вызовов.
Если возникает необходимость поместить полученные от пользователя данные в системный вызов, обязательно проверьте и перепроверьте их. Убедитесь, что данные, которые пользователь предоставил системе, не содержат «опасных» элементов и не взломают систему нежелательными командами. Для этого PHP предлагает специальную функцию EscapeShellCmd().
Каждый раз при системном вызове с потенциально небезопасными данными, дезактивируйте их с помощью EscapeShellCmd():
Примечание: дезактивация проходит путём подстановки бэкслэша («») перед потенциально небезопасными для системы символами (а именно: #&;?'»|*?~<>^()[]{}$\x0AxFF).
<html>
<head>
<title>Поиск аккаунта</title>
</head>
<body>
<h1>Поиск аккаунта</h1>
<?php
if ($name) {
system (EscapeShellCmd («lookup $name»));
print «<br>nn»;
}
?>
<form action=»<?php print $PHP_SELF; ?>» method=»GET»>
Введите аккаунт для поиска:
<input type=»text» name=»name»>
<input type=»submit» value=»Найти аккаунт»>
</form>
</body>
</html>
Примечание: хотя EscapeShellCmd() — функция чрезвычайно полезная, когда вам нужно убедиться в безопасности полученных данных, всегда стоит проводить специальные проверки в зависимости от вида данных. EscapeShellCmd() не проверит данные на корректность, она лишь предотвратит неавторизованные действия пользователя.
Усиленная проверка данных
Как правило, лучше проверять строку на наличие допустимых символов, нежели на отсутствие недопустимых.
Например, проверить, что переменная $name содержит только буквенные символы. При этом новые пути проникновения в систему имеют меньше шансов просочиться через ваш фильтр.
Проверка адресов электронной почты
Один из самых распространённых видов проверки данных — это валидация адреса электронной почты. Большинство новичков ограничиваются лишь фильтром из регулярных выражений, взятым с какого-нибудь кладбища бесплатных скриптов. Но одних регулярных выражений недостаточно, если вы претендуете на получение действительно корректных результатов. Для большей безопасности вы можете применить ещё парочку методов:
- проверка соединения
- интерактивная проверка
Проверка соединения
Один из способов проверки аккаунта на достоверность без привлечения самого пользователя — это попробовать открыть соединение с сервером, указанным в полученном адресе и проверить наличие аккаунта на данном сервере.
Плюсы
- Никаких действий со стороны пользователя, поскольку операция проводится на программном уровне.
- Выявляет несуществующие адреса, с чем никогда не справится регулярное выражение (например, Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в вашем браузере должен быть включен Javascript.).
Минусы
- Выявляются только несуществующие адреса. Например, если John Doe пошлёт e-mail автора этих строк (Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в вашем браузере должен быть включен Javascript.), данные будут приняты, несмотря на то, что был предоставлен неверный адрес.
- Данный метод работает медленнее, чем регулярные выражения.
- Почтовый сервер пользователя может временно не работать, при этом рабочий адрес будет восприниматься как некорректный.
Интерактивная проверка
Другой способ проверки — выслать на указанный адрес специальный ключ и попросить пользователя ввести этот ключ, чтобы продолжить процедуру регистрации. Данный метод позволяет проверить не только существование адреса, но и наличие у пользователя права доступа к нему.
Плюсы
- Лучший способ убедиться, что пользователь предоставил корректный и свой адрес (Пользователь должен иметь доступ к аккаунту, указанному при регистрации).
Минусы
- Требуются некоторые действия со стороны пользователя. Это раздражает тех, кто пытается вас одурачить.
- Как и любой другой, этот способ не даёт стопроцентной гарантии надёжности. Пользователь может создать временный аккаунт на Hotmail или Netaddress, а потом дать его при регистрации.
Резюме
В этой статье мы рассмотрели семь «серьёзных» из 21 наиболее общих ошибок PHP-программиста. Итак, вот то, чего следует избегать:
- Пренебрежение правилами присвоения имён: Правильно продуманная система именования переменных и функций проекта даст вам на выходе прозрачный и легко расширяемый код.
- Непродуманная работа с данными: БД и SQL: Некорректно работающая выборка данных говорит о недостаточно ясно определённой организации работы с БД. Да, в реальной жизни может и не оказаться того «единственно верного» способа выборки данных, но всегда найдётся тысяча «неверных».
- Слабая устойчивость к ошибкам: Код должен разрабатываться с нейтрализацией и учётом всех потенциальных ошибок и сбоев. Особенное внимание нужно уделять вызовам функций и системным вызовам.
- Злоупотребление ООП: PHP — язык, где поддержка объектов осуществляется не в полном объёме. Не стоит применять методы ООП также легко, как вы это делаете в Java или C++ (или даже Perl).
- Злоупотребление регулярными выражениями: Регулярные выражения медленно работают. Прежде чем вставить в код регулярное выражение, убедитесь, что более простого и быстрого решения нет.
- Программирование на PHP как на другом языке: Не стоит программировать на PHP как на C или Perl. PHP — состоявшийся язык программирования и существует сам по себе. Поэтому, обязательно научитесь программировать именно на PHP, а не так, как это было бы правильно для другого языка.
- Недостаточное внимание к вопросам безопасности: Всегда учитывайте «человеческий фактор». Не вставляйте в системные запросы непроверенные строки данных. Проверяйте адреса электронной почты. Не полагайтесь на алгоритмы при приёме кредитных карточек.
Автор: Sterling Hughes
Оригинал: www.zend.com
Перевод: Дмитрий Короленко
Часть первая — 7 «детских» ошибок
Целевая аудитория
Эта серия статей предназначена для тех программистов на языке PHP, которые
хотят избежать наиболее общих ошибок в написании кода. Читатель, как минимум,
должен знать общий синтаксис PHP, а также весьма желателен некоторый опыт
использования языка на практике.
Введение
Одна из наиболее сильных сторон PHP является, одновременно, и его слабой
стороной: PHP очень прост в изучении. Это привлекает многих людей; однако,
несмотря на его кажущуюся простоту, не так-то просто научиться использовать этот
язык правильно и эффективно.
Как правило, дело в недостаточной практике программирования. Неопытные
программисты становятся перед лицом необходимости создания сложных
веб-приложений. Поэтому сплошь и рядом допускаются ошибки, которых избежал бы
опытный программист, такие как необоснованное использование функции printf()или
неправильное использование семантики PHP.
В этой серии из трех статей представлены наиболее, по нашему мнению,
характерные ошибки. Эти ошибки можно классифицировать по нескольким категориям,
от до . Наряду с анализом этих ошибок, представлены
способы их избежания, а также некоторые , накопленные за
многие годы практики программирования.
Часть 1: Описываются 7 ошибок (№ 21-15, в обратном порядке,
в соответствии со степенью серьезности по нашей классификации). Такие ошибки не
вызывают серьезных проблем, но приводят к уменьшению эффективности работы
программы, а также выражаются в громоздком трудночитаемом коде, в который, к
тому же, трудно вносить изменения.
Часть 2: Следующие 7 ошибок (№ 14-8) относятся к . Они
ведут к еще более значительному уменьшению скорости выполнения кода, уменьшению
безопасности скриптов; код становится еще более запутанным.
Часть 3: Описания семи, последних, ошибок. Эти ошибки
концептуальны по своей природе и являются причиной появления ошибок, описанных в
1-ой и 2-ой частях статьи. Они включают и такие ошибки, как недостаточное
внимания, уделенное как проекту в целом, так и коду программы, в частности.
21. Неоправданное использование функции printf()
Функция printf()
предназначена для вывода форматированных
данных.
Например, ее следует использовать при необходимости вывода переменной в
формате с плавающей запятой с определенной точностью, либо в любом другом
случае, когда возникает необходимость изменения формата выводимых данных.
Ниже приведен пример обоснованного применения функции printf()
.
В данном случае она используется для форматированного вывода числа :
<?php printf ("Число Пи: %2fn<br>n", M_PI); printf ("Это тоже число Пи: %3fn<br>n", M_PI; printf ("И это Пи: %4fn<br>n", M_PI); ?>
Примечание: Наблюдаются случаи патологической боязни функции
printf()
, когда люди пишут свои функции форматированного вывода,
порой по 30-40 строк, хотя все проблемы мог бы решить один-единственный вызов
функции printf()
.
Многие программисты используют функцию printf()
для вывода
переменных, результатов вызова функций, а иногда даже обычных текстовых данных.
Наиболее часто это происходит в следующих двух случаях:
- когда следовало бы использовать функцию
print()
; - при выводе результатов, возвращаемых функциями.
Когда следует использовать print()
Вызов функции printf()
зачастую используется там, где следовало
бы использовать print()
. В следующем примере функция
printf()
используется для вывода четырех переменных:
<?php $name = 'Sterling Hughes'; $job = 'Senior Engineer'; $company = 'DesignMultimedia'; $email = 'shughes@designmultimedia.com'; printf ( "Меня зовут %sn<br>n Я работаю %s, %sn<br>n Мой адрес E-mail:%sn<br>n", $name, $job, $company, $email ); ?>
В данном случае возможно (и желательно!) применение print()
:
print "Меня зовут $namen<br>n Я работаю в $company, $jobn<br>n Мой адрес E-mail: $emailn<br>n";
Использование print()
вместо printf()
в случаях,
когда выводятся неформатированные данные, как в данном примере, дает
следующие выгоды:
- Увеличение производительности: Функция
printf()
форматирует
свои аргументы перед выводом. Таким образом, время ее выполнения
больше, чем для функцийprint()
илиecho()
. - Более ясный код: Все-таки, надо признать, что использование функции
printf()
затрудняет чтение кода (имеющих достаточный опыт
программирования на C, это, конечно, касается в меньшей степени). Чтобы
функцияprintf()
не повела себя самым неожиданным для вас
образом, требуется как знание синтаксиса данной функции, (т.е.%s
определяет строковый формат вывода, тогда как%d
— десятичный),
так и знание типов переменных.
Использование функции printf()
для вывода значения,
возвращаемого функцией
Еще одна характерная ошибка использования функции printf()
—
вывод значения, возвращаемого функцией, как в следующем примере:
<?php printf ("Найдено %d вхождений строки %s", count ($result), $search_term);
?>
Наряду с функцией print()
, при использовании ее в тех же целях,
следует использовать оператор ‘.
‘ В данном случае этот оператор
добавляет текст к результату вызова функции:
<?php print "Найдено " . count ($result) . " вхождений строки $search_term"; ?>
Использование оператора .
в паре с функцией print()
позволяет избежать использования более медленной функции
printf()
.
20. Неверное применение семантики языка
Многие программисты используют в своей работе PHP , фактически не понимая
тонкостей этого языка. Одна из тонкостей — разница между синтаксисом и
семантикой PHP.
- Синтаксис
PHP:Представляет собой набор правил для определения элементов языка.
Например, как мы определяем переменную? Ставим знак $ перед ее именем. Как
определяем функцию? В общем случае, используя скобки, аргументы и т.п. - Семантика
PHP: Представляет собой набор правил для применения синтаксиса.
Например, возьмем функцию с двумя аргументами, что определяется ее
синтаксисом. Причем в качестве аргументов ей следует передавать
переменные строкового типа ?— это
определяется семантикой.
Заметьте: . В языках с четким разделением типов (таких как Java или
C) нет понятия (в общем случае, хотя бывают и исключения). В таком
случае компилятор вынудит использовать переменные строго определенного
типа.
Языки, в которых отсутствует само определение типов переменных, предоставляют
больше гибкости в написании кода. Но, как бы то ни было, в случае неправильного
использования семантики для большинства функций PHP следует ожидать появления
сообщения об ошибке.
Возьмем кусок кода, который открывает файл и выводит его построчно:
<?php $fp = @fopen ( 'somefile.txt', 'r' ) or die ( 'Не могу открыть файл somefile.txt' ); while ($line = @fgets ( "$fp", 1024)) // Здесь ошибка! { print $line; } @fclose ("$fp") // И здесь тоже color or die( 'Не могу закрыть файл somefile.txt' ); ?>
В данном случае появится сообщение об ошибке типа:"Warning:
(«Внимание: аргумент не может являться дескриптором файла»)
Supplied argument is not a valid File-Handle resource in tst.php on line
4"
Это вызвано тем, что переменная $fp
заключена в двойные кавычки,
что однозначно определяет ее как строку, тогда как функция fopen()
ожидает в качестве первого аргумента дескриптор, но не строку.
Соответственно, вам следует использовать переменную, которая может
содержать дескриптор.
Примечание: В данном случае, строковый тип допустим синтаксически.
Для решения проблемы следует просто убрать двойные кавычки:
<?php $fp = @fopen ( 'somefile.txt', 'r' ) or die ( 'Не могу открыть файл somefile.txt' );
while ( $line = @fgets ($fp, 1024) )
{ print $line; } @fclose ($fp) or die ( 'Не могу закрыть файл somefile.txt' ); ?>
Как избежать неправильного приложения семантики?
В приведенном примере генерируется сообщение об ошибке. Но PHP предоставляет
программисту больше свободы, чем другие, традиционные языки программирования.
Это позволяет получать интересные результаты. Как минимум, теоретически
возможно написать корректный код, неправильно используя семантику языка.
Но будьте осторожны, заигрывая с семантикой языка! Возможно появление
трудноуловимых ошибок в программах. Если же вы все-таки решили
поэкспериментировать, вам следует понимать три ключевых момента:
- Типы: В PHP каждая переменная в любой момент времени относится к
определенному типу. И это несмотря на тот факт, что ее тип можно
свободно изменять. Другими словами, в языке PHP переменная не может
существовать, при этом не относясь к определенному типу (и, соответственно, не
обладая характеристиками, присущих этому типу). В PHP есть 7 основных типов
переменных:Boolean
,resource
,integer
,
double
,string
,array
и
object
. - Область видимости: В PHP переменные имеют область видимости,
которая определяет то, откуда она может быть доступна, и насколько долго будет
существовать. Недопонимание концепции может проявляться в
виде различного рода ошибок. - php.ini: При написании кода следует понимать, что не все
пользователи имеют такую же конфигурацию программно-аппаратных средств, как и
вы. Таким образом, совершенно необходимо лишний раз убедиться, сохраняется ли
работоспособность вашего кода в той конфигурации, в которой программа должна
работать, а не в той, в которой разрабатывалась.
19. Недостаточно либо излишне комментированный текст
Плохо документированный текст программы является признаком эгоистичного
программиста. Результатом попытки анализа вашей программы с целью внесения
улучшений будет только головная боль. Причем все программисты считают
самодокументированный код хорошим тоном, но сами крайне редко пишут
комментарии.
Следует также избегать избыточных комментариев. Это тоже встречается очень
редко, и, опять же, создает трудно читаемый исходный код. Следующий пример это
иллюстрирует:
<?php // Начало кода $age = 18; // Возраст равен 18 $age++; // Увеличим $age на один год // Напечатаем приветствие print "Вам сейчас 19 лет, и это значит, что Вам уже было:"; print "n<br>n<br>n"; // Цикл 'для' чтобы вывести все // предыдущие значения возраста for ($idx = 0; $idx < $age; $idx++) { // Напечатаем каждое значение возраста print "$idx летn<br>n"; } // Конец кода ?>
И все-таки: где золотая середина?
Итак, какой же объем комментариев следует помещать в скрипт?! Это зависит от
многого: от времени, которым вы располагаете, от политики компании, сложности
проекта и т.д. Тем не менее, запомните несколько основных принципов, которым
надо следовать при написании программ вне зависимости от вашего решения:
- Перед телом функции всегда помещайте комментарий — назначение функции.
- Добавляйте комментарии в сомнительных участках кода, когда нет
уверенности, что он будет работать как надо. - Если назначение кода неочевидно, внесите информацию о предназначении этого
участка. Вы же потом воспользуетесь этим комментарием. - Избегайте комментарии вида
#
— используйте только/*
либо
*///
.
Следующий пример иллюстрирует хороший стиль комментариев:
<?php // Random_Numbers.lib // Генерация случайных чисел различного типа mt_srand((double)microtime()*1000000); // // mixed random_element(array $elements[, array weights]) // Возвращает случайный элемент массива-аргумента // Массив weights содержит относительные вероятности // выборки элементов // function random_element ($elements, $weights = array()) { // Для корректного функционирования этого алгоритма // количество элементов массива должно быть равным // количеству элементов массива относительных вероятностей if (count ($weights) == count ($elements)) { foreach ($elements as $element) { foreach ($weights as $idx) { // Примечание: мы не используем $idx, потому что // нам не нужен доступ к отдельным элементам // массива weights $randomAr[] = $element; } } } else { $randomAr = $elements; } $random_element = mt_rand (0, count ($randomAr) - 1); return $randomAr [$random_element]; } ?>
18. Слишком много переменных — слишком большое время
выполнения
Некоторые прямо-таки страдают навязчивой идеей вводить временные переменные
где надо и где не надо. Совершенно невозможно понять, чем руководствовался
человек, написавший такой код:
<?php $tmp = date ("F d, h:i a"); // т.е. формат даты February 23, 2:30 pm print $tmp; ?>
Для чего здесь использована временная переменная?! Она просто не нужна:
<?php
print date ("F d, h:i a");
?>
К сожалению, многие программисты никак не могут избавиться от этой дурной
привычки. Использование временных переменных замедляет выполнение программы. Для
увеличения скорости кода, где это возможно, лучше воспользоваться вложением
функций. Использование временных переменных зачастую увеличивают время
выполнения скриптов почти на четверть.
Еще одна причина, по которой следует избегать использования излишнего
количества временных переменных, это ухудшение читаемости кода. Сравните два
приведенных примера. Какой из них выглядит более элегантно — с использованием
временной переменной или без? Какой код проще прочесть? Использование лишних
временных переменных ведет к написанию менее читаемого и ясного кода.
Плюсы использования временных переменных
Введение временных переменных позволяет упростить некоторые сложные выражения
или вызовы функций. Еще они приносят пользу, когда позволяют избежать
многократного вызова функции с одними и теми же аргументами.
Вот пример, в котором не используется лишних переменных:
<?php // string reverse_characters (string str) // Переворачивает строку символов function reverse_characters ($str) { return implode ("", array_reverse (preg_split ("//", $str))); } ?>
Вызову функции implode()
в качестве одного из параметров
передается результат выполнения вложенных функций, поэтому такой код трудно
прочесть. В данном случае нам может здорово помочь использование временной
переменной:
<?php // string reverse_characters (string str) // Переворачивает строку символов function reverse_characters ($str) { $characters = preg_split ("//", $str);
$characters = array_reverse ($characters);
return implode ("", $characters);
}
?>
Золотое правило
Если вы думаете, ввести или нет еще одну временную переменную, задайте себе
два вопроса:
- Будет ли эта переменная использована хотя бы дважды?
- Значительно ли улучшится с ее введением читаемость кода?
Если на любой из этих вопросов вы ответили , тогда введите временную
переменную. Иначе комбинируйте вызовы функций (если это необходимо) и обойдитесь
без ее использования.
17. Переписываем стандартные функции
Кое-кто рекомендует переименовывать стандартные функции для того, чтобы
программистам на Visual Basic’е проще было перейти к использованию языка
PHP:
<?php function len ($str) { return strlen ($str); } ?>
Встречаются также рекомендации, приступая к программированию на PHP, первым
делом заменять имена встроенных функций более привычными.
Существует, как минимум, две причины этого не делать. Во-первых и прежде
всего, мы получаем менее читаемый код. Люди, читающие ваш код, увидят массу
очевидно ненужных функций и будут сильно удивлены, почему же вами не
использовались стандартные функции PHP.
Ну и, наконец, это замедляет программу. Дело не только в необходимости
обработки большего объема кода, но и в том, что для вызова такой
пользовательской функции, требуется больше времени, чем для прямого вызова
стандартной функции.
Используйте стандартные функций языка!
Иногда так трудно устоять! Ведь программист редко знает сразу весь набор
функций — у него обычно нет времени запомнить их все. Почему бы просто не
переименовать функцию? Но, повторимся, этого не следует делать в силу изложенных
выше причин.
Хорошо бы иметь под рукой справочник по функциям PHP (удобно использовать
индексированную версию в формате PDF). И перед тем как написать какую-либо
функцию, внимательно посмотреть — не существует ли она уже в списке стандартных
функций.
Но следует заметить, что в кодах программ можно встретить пользовательские
функции, написанные еще до их введения в качестве стандартных (например,
функции сравнения двух массивов). Это не означает, что вы обязательно должны
переписать код и заменять их стандартными функциями.
16. Клиентская часть программы не отделяется от серверной
части
Многие программисты рекомендуют объединять код HTML (интерпретируемый на
стороне клиента) и код PHP (выполняемый сервером) в один большой файл.
Для маленьких сайтов это, возможно, неплохо. Но, когда ваш сайт начнет расти,
вы можете столкнуться с проблемами при необходимости добавить какие-либо новые
функции. Такой стиль программирования приводит к очень и
громоздкому коду.
API функций
Если вы собрались отделить код PHP от HTML кода, у вас есть два варианта.
Один способ — создание функций динамического формирования вывода и поместить их
в нужное место на веб-странице.
Например, так:
index.php - код страницы <?php include_once ("site.lib"); ?> <html> <head> <title><?php print_header (); ?></title> </head> <body> <h1><?php print_header (); ?></h1> <table border="0" cellpadding="0" cellspacing="0"> <tr> <td width="25%"> <?php print_links (); ?> </td> <td> <?php print_body (); ?> </td> </tr> </table> </body> </html> site.lib - Сам код программы <?php $dbh = mysql_connect ("localhost", "sh", "pass") or die (sprintf ("Не могу открыть соединение с MySQL [%s]: %s", mysql_errno (), mysql_error ())); @mysql_select_db ("MainSite") or die (sprintf ("Не могу выбрать базу данных [%s]: %s", mysql_errno (), mysql_error ())); $sth = @mysql_query ("SELECT * FROM site", $dbh) or die (sprintf ("Не могу выполнить запрос [%s]: %s", mysql_errno (), mysql_error ())); $site_info = mysql_fetch_object ($sth); function print_header () { global $site_info; print $site_info->header; } function print_body () { global $site_info; print nl2br ($site_info->body); } function print_links () { global $site_info; $links = explode ("n", $site_info->links); $names = explode ("n", $site_info->link_names); for ($i = 0; $i < count ($links); $i++) { print "ttt <a href="$links[$i]">$names[$i]</a> n<br>n"; } } ?>
Очевидно, такой код лучше читаем. Еще одно преимущество использования этой
концепции — возможность изменения дизайна без модификации самого кода
программы.
Плюсы использования API функций
- Относительно чистый, ясный код
- Быстрый код
Минусы использования API функций
- Не настолько наглядно как система шаблонов
- Все-таки, для модификации дизайна требуется некоторое знание PHP
Система шаблонов
Второй способ, используемый для разделения PHP и HTML кода — использование
шаблонов. В данном случае, некоторые элементы дизайна заменяются
пользовательскими тегами, а сама программа сканирует файл на предмет их наличия
и заменяет их необходимой информацией.
Пример использования шаблонов:
<html> <head> <title>%%PAGE_TITLE%%</title> </head> <body %%BODY_PROPERTIES%%> <h1>%%PAGE_TITLE%%</h1> <table border="0" cellpadding="0" cellspacing="0"> <tr> <td width="25%">%%PAGE_LINKS%%</td> <td>%%PAGE_CONTENT%%</td> </tr> </table> </body> </html>
Затем пишем программу, просматривающую код шаблона и при выводе заменяющую
тэги вида %%:%%
нужной информацией.
Примечание: неплохой класс для использования его в системе шаблонов —
FastTemplate, его можно скачать с http://www.thewebmasters.net/.
Плюсы использования шаблонов:
- Предельно просто и ясно
- Для изменения шаблонов не требуется знание PHP
Минусы использования шаблонов:
- Более медленный способ — ведь надо сканировать весь шаблон и лишь
потом выводить данные - Сложнее внедрить на практике
15. Использование устаревшего синтаксиса и функций
Некоторые программисты вновь и вновь используют старые библиотеки и старые
наработки. Например, код, написанный еще под PHP 2, до сих пор используется с
PHP4, хотя уже начиная с версии PHP3 были добавлены стандартные функции,
реализующие то же самое.
Использование устаревших функций и синтаксиса могут снизить скорость
выполнения кода и, к тому же, сделать его нечитаемым. Другие программисты могут
быть незнакомы со старыми функциями. Но тем не менее, если вы встретили участок
старого кода, не обязательно его переписывать с учетом новых правил языка.
Просто не надо его использовать при написании новых программ.
Пример использования старых языковых конструкций:
<?php // Старый стиль while (1): print "5"; if ( $idx++ == 5 ): break; endif; endwhile; // Лучше написать так
// (впрочем, код можно оптимизировать) while (1) { print "5"; if ( $idx++ == 5 ) { break; } } ?>
Почему же следует следовать новым стандартам? Причины следующие:
- Использование старых конструкций не очень распространено и, таким образом,
новички в PHP будут в замешательстве, увидев два разных варианта синтаксиса. - Старый синтаксис отличается от синтаксиса других языков программирования,
и, следовательно, при переходе с другого языка на PHP программисту будет
сложнее понять и привыкнуть. - Но самое главное — в одной из новой версий, возможно, будет исключена
поддержка старого синтаксиса, тем самым это заставит вас переписать код
заново. Как бы то ни было, скобки всегда останутся часть языка PHP.
Подобные участки кода можно встретить во многих программах. Вам, как правило,
следует руководствоваться правилами, приведенными в документации по PHP, большей
часть обновленной — в ней отражается развитие языка. Периодически просматривайте
документацию, ведь язык развивается, добавляются новые функции. Таким образом,
вам никогда не придется писать пользовательские функции, выполняющие ту же
работу, что и стандартные.
Резюме
В этой статье мы рассмотрели первые 7 из 21 наиболее общих ошибок PHP
программиста. Как правило, они не нарушают работоспособности программ, но, тем
не менее, их следует избегать:
- Необоснованное применение функции
printf()
: Ее следует
использовать только для вывода форматированных данных. - Неправильное применение семантики языка: Многие программисты не имеют
достаточно времени, чтобы разобраться во всех тонкостях языка, что
впоследствии выражается в ошибочном коде. - Плохо комментированный код: Всегда пишите комментарии! Перед каждой
функцией указывайте, что делает данная функция, и какие аргументы она требует.
Также комментируйте сложные участки кода и внесенные изменения. - Слишком много временных переменных: Временные переменные хорошо
использовать для предотвращения повторного вызова функций или
последовательностей функций. - Изобретаем велосипед — переписываем стандартную функцию: Сначала загляните
в руководство по PHP — не описана ли там функция, которую вы собираетесь
написать для, казалось бы, расширения набора стандартных функций PHP. - Смешан PHP и HTML код: Попробуйте сделать код как можно более модульным.
Потом вам (и другим тоже) можно будет сменить дизайн страницы без изменения
кода PHP. - Используются старые языковые конструкции и устаревшие функции: То, что вы
можете сделать, не всегда следует делать. Загляните в
документацию и литературу по PHP, как писать правильно. Отличные книги
— «Разработка веб-приложений с использованием PHP (Web Application Development
with PHP) и «Профессиональный программист PHP (Professional PHP). (Эх, где бы
их еще найти! ;)) — прим. переводчика)
Об авторе
Стерлинг Хьюз (Sterling Hughes) — независимый разработчик веб-сайтов,
занимается созданием динамических веб-приложений для некоторых крупнейших
мировых компаний. Участвовал в создании cURL and SWF расширений PHP с открытым
исходным кодом. Его книга, (The PHP
Developer’s Cookbook), была издана в октябре 2000 года издательством .
О переводчике
Дмитрий Короленко. Ну что о нем много говорить? ;)) Благодаря ему люди, не
знающие английского, или знающие, но очень ;)), смогут прочесть весьма
познавательную статью Стерлинга Хьюза. О замеченных недостатках перевода просьба
сообщать на мыло: lkx2@mail.ru Сразу хочется
пояснить, что дословный перевод не был самоцелью, но, тем не менее,
стиль и смысл оригинальной статьи по возможности ;)) сохранялись.
Все замечания и пожелания приветствуются и будут учтены, ибо готовится перевод
второй части этой статьи. Только просьба ногами не пинать ;)) Все же я
надеюсь,что если вы до этого места дочитали, то не все так плохо ;))
- PHP и MySQL
- Основы PHP
- Нарушения в работе системы PHP
Внимание! Данный курс устарел!
Переходите к новому курсу «PHP для начинающих».
В этой статье приведены сведения о некоторых из наиболее распространенных проблем возникающих при работе с PHP, классифицированные по признакам нарушений в работе, и предложены некоторые широко применяемые способы устранения этих проблем.
Обработка и отладка ошибок
В процессе функционирования интерпретатора PHP вырабатываются ошибки нескольких типов, которые классифицируются по степени серьезности. Четыре наиболее распространенных типа ошибок описаны ниже. Дополнительные сведения по этой теме приведены по адресу www.php.net/error_reporting.
- Извещение (Notice)
-
Ошибки такого типа не слишком серьезны и не создают существенных проблем. По умолчанию эти ошибки подавляются. Их появление становится возможным только после изменения степени серьезности регистрируемых в журнале ошибок в файле php.ini.
- Устаревшие функции и конструкции (Deprecated)
-
Ошибки использования устаревших конструкций. Включаются для того, чтобы получать предупреждения о коде, который не будет работать в следующих версиях PHP.
Ошибка использования устаревших функций и конструкций - Предупреждение (Warning)
-
Ошибки такого типа возникают, если в коде, выполняемом со сбоями, возникло нарушение в работе, но выполнение программы не прекратилось. Обычно сообщение о такой ошибке отображается, но сценарий продолжает работать.
Предупреждение - Неисправимая ошибка (Fatal Error)
-
Значимое условие возникновения ошибки, в силу которого продолжение работы сценария становится невозможным. После возникновения неисправимой ошибки работа сценария прекращается.
Неисправимая ошибка
Кроме того, ошибка каждого типа представлена с помощью константы, на которую можно ссылаться в коде: E_USER_NOTICE (извещение), E_USER_WARNING (предупреждение), E_USER_DEPRECATED (устаревшие функции и конструкции) и E_USER_ERROR (неисправимая ошибка). Степень серьезности регистрируемых ошибок можно задать в сценарии вручную, с помощью функции error_reporting(), как показано в следующих примерах:
Код PHP
// Формировать сообщения только о неисправимых ошибках
error_reporting(E_USER_ERROR);
// Формировать предупреждающие сообщения и сообщения о неисправимых ошибках
error_reporting(E_USER_WARNING | E_USER_ERROR);
// Формировать все сообщения, в том числе извещения и сообщения E_STRICT
error_reporting(E_ALL);
// Не показывать никакие сообщения
error_reporting(0);
Непродуманное подавление вывода средств формирования сообщений об ошибках значительно затрудняет процесс отладки. Вместо этого чаще всего следует использовать обработчики ошибок.
Извещения никогда не передаются в клиентскую программу и не отражаются отрицательно на функциональных возможностях программы, поэтому разработчик почти всегда может без опасений отказаться от использования извещений в целях обработки ошибок. И наоборот, специализированный обработчик ошибок не позволяет обрабатывать неисправимые ошибки; в интерпретаторе PHP такие ошибки рассматриваются как достаточно серьезные для того, чтобы прекратить выполнение сценария, не задавая дополнительных вопросов. Поэтому область применения специализированных функций обработки ошибок главным образом сводится к предупреждениям. Основное назначение таких функций заключается в том, что они позволяют избежать необходимости показывать конечному пользователю сообщения об ошибках, предназначенные для программиста, и нарушать ход выполнения приложения.
Определение обработчика ошибок
На этом этапе необходимо найти ответ на следующий важный вопрос: «Какая информация должна отображаться на экране браузера пользователя при возникновении ошибки?» Обычно считается нецелесообразным и даже не рекомендуется демонстрировать перед конечным пользователем какие-либо детали, касающиеся внутреннего функционирования приложения, не говоря уже о том, что сообщения об ошибках портят внешний вид веб-страницы. Создав функцию, формирующую специализированное сообщение об ошибках, а затем установив ее в качестве применяемого по умолчанию обработчика ошибок, можно избежать такой ситуации, в которой перед глазами пользователя появляются некрасивые и непрофессионально выполненные сообщения об ошибках.
Вначале определим, какая информация должна быть предоставлена пользователю, а затем создадим функцию, как показано ниже. Эта функция должна принимать такие входные параметры, как тип ошибки, сообщение, имя файла и номер строки:
Код PHP
<?php
function error_msg($err_type, $err_msg, $err_file, $err_line)
{
static $count = 0;
$count++;
echo "<div style="width:32px; height:32px; background:url(error.png); float:left; margin:0 12px 12px 0;"></div>"
."<b>Ошибка №$count:</b><p>Извините, но на этой странице возникла ошибка. "
."Пожалуйста, отправьте следующее сообщение администратору сайта на странице <a href='help.html'>help</a>.</p>"
."<p>Тип ошибки: <em>$err_type</em>, сообщение: <em>$err_msg</em>, файл: <em>$err_file</em>, номер строки: <em>$err_line</em>"
."<hr color='red'>";
}
// Регистрируем нашу функцию в качестве обработчика ошибок
set_error_handler("error_msg");
// Генерируем ошибку, чтобы проверить вызывается ли функция error_msg
include 'undefined.php';
?>
В данном случае было решено предоставлять информацию о конкретной ошибке и о том, где она возникла. В зависимости от ситуации может оказаться оправданным решение предоставлять пользователю минимальный объем информации, поскольку ему достаточно знать, что возникла ошибка, и как ему следует действовать в дальнейшем. После того как определен специализированный обработчик ошибок, достаточно указать эту функцию обработчика в коде с помощью функции set_error_handler().
После подготовки и выполнения такого кода все ошибки, которые разрешены в соответствии с установленной степенью серьезности регистрируемых ошибок, будут обрабатываться с помощью применяемой специализированной функции, за исключением неисправимых ошибок, которые, к сожалению, также неизбежны:
Инициирование пользовательской ошибки вручную
В PHP можно инициировать пользовательскую ошибку. Такая операция приблизительно эквивалентна операции активизации исключительной ситуации в версии PHP 5 (конструкция throw). Ошибка любого типа может быть инициирована путем передачи в функцию trigger_error() сообщения об ошибке и необязательной константы с обозначением степени серьезности ошибки, как в следующем примере:
Код PHP
trigger_error("Тестируем сообщение уровня 'Notice'", E_USER_NOTICE);
Средства инициирования ошибок лучше всего использовать в сочетании со специализированным обработчиком ошибок. Сразу после инициирования такой ошибки в программе вызывается определенный в ней обработчик ошибок, который предоставляет пользователю отформатированное сообщение об ошибке. Т.е. эту функцию удобно использовать для тестирования функций обработки ошибок.
Ведение журнала и отладка
Если средства обработки исключительных ситуаций и формирования сообщений об ошибках не применяются должным образом, то задача отладки существенно усложняется. Ошибка, которую и без того трудно выявить, может стать почти неуловимой, если программист непродуманно использует способы подавления или перенаправления сообщений об ошибках. Но достаточно лишь немного более тщательно продумать применение обработчиков ошибок, и сопровождение приложения существенно упрощается.
В приведенных выше примерах процесс обработки ошибок в основном рассматривался как средство создания интерфейса, в котором пользователь не обнаруживает следов возникших нарушений в работе. По такому же принципу можно обеспечить применение этих средств для упрощения регистрации ошибок и отладки в интересах программиста. Для этого достаточно включить вызов встроенной функции, такой как error_log(), и передать в эту функцию любую релевантную информацию, которая может помочь в процессе отладки, как в следующем коде. Следует учитывать, что в ходе разработки может потребоваться прибегнуть к использованию функции getTraceAsString(), позволяющей обеспечить ведение журналов:
Код PHP
function error_msg($type, $msg, $file, $line)
{
// Записать в журнал ошибок
$log_msg = "Тип ошибки: $type, сообщение: $msg, файл: $file, номер строки: $line, время возникновения: ".time();
$log_path = "tmp/php_errors.log";
error_log($log_msg, 3, $log_path);
}
set_error_handler("error_msg");
trigger_error("Тестируем сообщение уровня 'Notice'", E_USER_NOTICE);
Функция error_log() принимает в качестве второго параметра одно из четырех целых чисел, описание которых приведено ниже. Этот параметр задает тип сообщения в сочетании с третьим параметром, который указывает на местонахождение объекта — получателя сообщения об ошибке:
-
0 — сообщение, регистрируемое с использованием общесистемного механизма ведения журналов операционной системы.
-
1 — сообщение об ошибке, передаваемое по указанному адресу электронной почты (в качестве четвертого параметра можно ввести дополнительные заголовки).
-
2 — сообщение об ошибке, передаваемое через отладочное соединение PHP (должна быть разрешена дистанционная отладка).
-
3 — сообщение об ошибке, добавляемое в конец указанного файла журнала ошибок.
Ошибки, обнаруживаемые интерпретатором PHP
После того, как мы рассмотрели виды ошибок и способы их обработки, давайте рассмотрим наиболее распространенные нарушения при работе с PHP.
Проблемы, связанные с инсталляцией PHP
Я не стану читать мораль на тему, что не следует одним махом проходить все этапы инсталляции, не разобравшись в документации, а приведу описание нескольких часто встречающихся признаков нарушения в работе, которые обычно появляются после того, как инсталляция PHP выполняется впервые. Если вы устанавливали PHP не вручную, а например через WAMP-сервер, то приведенные ниже проблемы скорее всего у вас не возникнут.
Признак нарушения в работе: в окне браузера отображается текст файла
Если в окне браузера появляется текст сценария PHP вместо результирующего кода HTML, такая ситуация явно свидетельствует о том, что не происходит вызов машины PHP. Проверьте, что обращаетесь к сайту с помощью веб-сервера, а не файловой системы. Применяйте следующий вариант:
https://localhostpre>а не следующий, например:
file://home/httpd/htmlpre>Признак нарушения в работе - блоки PHP отображаются при их передаче по протоколу HTTP в виде текста или браузер выводит приглашение, согласно которому вам следует сохранить файл
Вызов машины PHP не происходит должным образом. Если запрос на получение файла передан правильно, по протоколу HTTP, как было указано в предыдущем подразделе, то наиболее распространенной причиной этой ошибки является то, что заданы не все расширения имен файлов, которые должны обслуживаться веб-сервером и распознаваться интерпретатором PHP. Вторая по степени распространенности причина состоит в том, что файл php.ini находится не в должном месте или содержит неправильную директиву настройки конфигурации.
Признак нарушения в работе - не найден сервер или хост либо страница не могут быть отображены
Если браузер не может найти используемый сервер, то, по-видимому, имеет место нарушение конфигурации DNS (Domain Name Service — служба доменных имен) или веб-сервера. Если доступ к сайту может быть получен с помощью IP-адреса (например, https://127.0.0.1p>
Если невозможно получить доступ к сайту с помощью IP-адреса после выполнения новой инсталляции, то, по всей видимости, пользователь не смог успешно выполнить привязку IP-адреса к сетевому интерфейсу или настроить конфигурацию домена httpd для обработки запросов, относящихся к конкретному домену. А если доступ к сайту не может быть получен с помощью IP-адреса, притом что применяемая инсталляция уже успешно работала, то наиболее вероятное объяснение состоит в том, что веб-сервер остановлен или недоступен по причине, не относящейся к PHP.
Проблемы формирования страницы
В данном разделе рассматриваются проблемы, при возникновении которых интерпретатор PHP, собственно говоря, не сообщает о каких-либо ошибках, но пользователь обнаруживает в окне браузера не те результаты, на получение которых он рассчитывал.
Признак нарушения в работе - полностью пустая страница
Появление пустой страницы может быть вызвано самыми разнообразными причинами. Одной из наиболее распространенных становится неисправимая ошибка в коде PHP, после обнаружения которой интерпретатор PHP не может возобновить нормальную работу. Начинайте отладку с верхней части файла PHP, который вы пытаетесь открыть в браузере, поместив вызов функции die() после открывающего дескриптора <?php:
Код PHP<?php die(print "hello"); ...
Если после обновления страницы вы увидите в браузере слово hello, это означает, что проблемы с веб-сервером и самим модулем PHP исключены. Продолжайте перемещать оператор вызова функции die() в коде PHP дальше, до тех пор, пока снова не появится ошибка, выражающаяся в появлении пустой страницы. Не забывайте, что неудачное завершение сценария может также происходить в результате обработки любых файлов, включенных с помощью require, require_once, include и тому подобных конструкций. Если после ввода оператора вызова функции die() непосредственно перед включенным файлом сценарий работает, а вслед за перемещением оператора вызова функции die() непосредственно после включенного файла перестает работать, то можно сделать вывод, что нарушение в работе возникает из-за включенного файла.
Безусловно, еще одной из возможных причин такой ситуации может явиться также то, что модуль PHP вообще не работает. Проверьте это предположение, просмотрев другую страницу из того же каталога, в отношении которой вы уже убедились, что она правильно обрабатывается интерпретатором PHP.
Наконец, пустое окно браузера обнаруживается в тех ситуациях, когда интерпретатор PHP сталкивается с достаточно серьезной ошибкой, а средства формирования сообщений об ошибках отключены. По-видимому, средства вывода сообщений об ошибках следует отключать на производственных серверах по соображениям безопасности, но на серверах, применяемых для разработки, средства вывода сообщений об ошибках в браузер оказывают огромную помощь. Проверьте в файле php.ini параметр display_errors и убедитесь в том, что все необходимые параметры заданы правильно. Если же пользователь действительно отвергает возможность вывода сообщений об ошибках в окно браузера, то ему придется широко использовать в составе средств обработки исключении функцию error_log() как показано выше.
Признак нарушения в работе - в окне веб-браузера обнаруживается код PHP
Если в окне браузера обнаруживается в буквальном виде код PHP, а не развертывается код HTML, который должен быть сформирован в этом фрагменте PHP, то, по-видимому, где-то пропущен начальный дескриптор PHP. (При этом предполагается, что интерпретатор PHP функционирует успешно, а в используемой инсталляции предусмотрены правильные форматы дескрипторов PHP.)
Можно легко забыть, что интерпретатор PHP рассматривает включаемые файлы как представленные в коде HTML, а не в коде PHP, если пользователь не укажет интерпретатору иное с помощью открывающего дескриптора в начале файла.
Ошибки при загрузке страницы
Если интерпретатору PHP не удается найти файл, запрос о загрузке которого получен от пользователя, то обнаруживается целый ряд разнообразных ошибок, описанных ниже.
Признак нарушения в работе — страница не может быть найдена
Если появляется сообщение о том, что страница не может быть найдена, притом, что другие файлы PHP загружаются без каких-либо нарушений, то, по-видимому, допущена ошибка при написании имени файла или определении пути доступа. Еще один вариант состоит в том, что могла возникнуть путаница в отношении того, где находится корневой каталог документов веб-сервера.
Признак нарушения в работе - сообщение Failed opening [file] for inclusion (He удалось открыть [файл] для включения)
Если в файле PHP предусмотрено включение других файлов PHP, иногда встречаются ошибки, подобные показанной ниже:
Warning Failed opening 'C:InetPubwwwrootasdf.php' for inclusion (ihclude_path='') in [no active file] on line 0Можно считать, что это сообщение представляет собой вариант сообщения Page cannot be found (He удается найти страницу), относящийся к включаемым файлам. Появление этого сообщения свидетельствует о том, что интерпретатору PHP не удалось загрузить даже первую строку активизированного файла. Активизированный файл отсутствует, поскольку не удается найти файл с указанным именем.
Появление такого сообщения возможно также в связи с тем, что права доступа к файлу, который вы пытаетесь загрузить, заданы неправильно.
Ошибки интерпретации
Наиболее распространенная категория ошибок возникает при обработке содержащего орфографические ошибки или синтаксически неправильного кода PHP, который вызывает сбои в работе машины интерпретации PHP.
Признак нарушения в работе — сообщение об ошибке интерпретации (Parse error)
Безусловно, количество причин возникновения проблем при интерпретации велико, но признак этого нарушения в работе почти всегда остается одинаковым — сообщение об ошибке интерпретации, Parse error:
Сообщение об ошибке интерпретации (Parse error) Все наиболее распространенные причины ошибок интерпретации, подробно описанные ниже, являются довольно незначительными и допускают несложное исправление, особенно если учесть, что интерпретатор PHP сам подсказывает способ их исправления. Тем не менее при любой ошибке интерпретации возвращается идентичное сообщение (в котором изменяются только имена файлов и номера строк) независимо от причины. Любой код HTML, который может находиться в файле с ошибками, не отобразится или не обнаружится в исходном коде, даже если этот код находится перед фрагментом PHP, вызвавшем появление ошибки.
Отсутствие точки с запятой
Ошибка интерпретации возникает, если точка с запятой не стоит, как положено, после каждого оператора PHP. В следующем примере фрагмента PHP в первой строке отсутствует точка с запятой, поэтому операция присваивания значения переменной так и не выполняется:
Код PHP
$count = 2 $level = 4
Отсутствие знаков доллара
Еще одна весьма распространенная проблема состоит в том, что пропущен знак доллара, предшествующий имени переменной. Если знак доллара отсутствует в операторе присваивания переменной начального значения, как в следующем примере:
Код PHP
count = 2;
то возникает сообщение об ошибке интерпретации. Но если вместо этого знак доллара отсутствует в применяемом впоследствии операторе вывода значения переменной, как в следующем примере:
Код PHP
$count = 2; echo "Количество: count";
то интерпретатор PHP не выводит сообщение об ошибке интерпретации. Вместо этого в окне отображается строка "Количество: count".
Это — превосходный пример, показывающий, почему не следует рассчитывать на то, что интерпретатор PHP сообщит вам обо всех ошибках на странице. Безусловно, сообщения об ошибках PHP являются более информативными по сравнению с большинством других средств динамического формирования информационного наполнения, но ошибки, подобные указанной, вполне могут остаться незамеченными, если усилия, предпринятые при проверке правильности кода, не будут соответствовать требованиям решаемой задачи.
Если вам приходится затрачивать значительную часть времени на отладку кода PHP, то для вас может оказаться буквально бесценным редактор текста, позволяющий переходить к строкам с указанными номерами и подсвечивающий все синтаксические ошибки. Я, например, использую удобную IDE Adobe Dreamweaver, которая динамически подсвечивает синтаксические ошибки, а также содержит нумерацию строк, чтобы можно было легко найти другие виды ошибок, которые отображаются в окне браузера:
Подсветка ошибки в Adobe Dreamweaver Подобные программы позволяют избежать большого количества синтаксических ошибок на этапе написания кода.
Проблемы, связанные со сменой режима
Еще одна разновидность нарушений в работе возникает в связи с неправильным переходом в режим PHP и из режима PHP. Если блок PHP не закрыт должным образом, как показано ниже, то возникает ошибка интерпретации:
Код PHP
<?php $count = 2 $level = 4; </body> </html>
Данная конкретная проблема переключения режима очень часто возникает при использовании коротких блоков PHP. И наоборот, если пользователь не обозначит должным образом начало блока PHP, то весь оставшийся блок, предназначенный для обработки, будет выглядеть как код HTML.
Кавычки, не обозначенные управляющими последовательностями
При использовании такого стиля разработки, в котором для выработки кода HTML в максимальной степени применяется код PHP, часто возникает еще одна ошибка интерпретации, связанная с тем, что кавычки в строках не обозначаются управляющими последовательностями:
Код PHP
// Неправильная строка echo "Он сказал, - "Что нам делать?""; // Правильная строка echo "Он сказал, - "Что нам делать?"";
Другие причины ошибок интерпретации
Проблемы, названные выше, не составляют исчерпывающий список источников ошибок интерпретации. Дело в том, что сбой в работе интерпретатора возникает под воздействием любых нарушений формата оператора PHP, включая незакрытые круглые и квадратные скобки, операции без операндов, не заключенные в круглые скобки выражения проверки в управляющих структурах и т.д. Иногда сообщение об ошибке интерпретации включает сведения о том, что ожидал встретить в коде интерпретатор PHP, но так и не смог обнаружить; такие сведения могут оказаться полезной подсказкой. Если номер строки, указанный в сообщении об ошибке интерпретации, относится к самой последней строке файла, это обычно означает, что какая-то программная конструкция, заключенная в парные символы (кавычки, круглые, фигурные скобки и т.д.), была открыта, но так и не закрыта, а интерпретатор PHP продолжал искать закрывающий символ до достижения самого конца файла.
Проблемы, связанные с использованием функций
Многие проблемы, касающиеся вызова функций, приводят к возникновению неисправимых ошибок, а это означает, что интерпретатор PHP отказывается от обработки оставшейся части сценария.
Признак нарушения в работе - сообщение Call to undefined function my_function()
В коде PHP предпринимается попытка вызвать функцию my_function(), которая еще не была определена. Такая ситуация может возникнуть просто в связи с тем, что допущена ошибка при написании имени функции (встроенной или определяемой пользователем), или лишь потому, что не дано определение функции. Если для загрузки определений пользовательских функций из файлов используется конструкция include или require, следует убедиться в том, что загружаются именно те файлы, которые требуются.
Если же рассматриваемая проблема касается весьма специализированной, встроенной функции (например, относится к средствам XML или к средствам математических вычислений произвольной точности), то причина может состоять в том, что при компиляции и сборке исполняемого файла интерпретатора PHP не было разрешено применение соответствующего семейства функций.
Признак нарушения в работе - сообщение call to undefined function ()
В данном случае интерпретатор PHP пытается вызвать некоторую функцию, но не имеет даже возможности определить ее имя. Такая ситуация возникает исключительно в тех случаях, если в коде применяется выражение в форме $my_function(), где само имя функции показано как переменная. Это означает, что разработчик, скорее всего, случайно поместил знак $ перед выражением вызова функции my_function(), имеющим смысл, или ошибся, специально используя средство задания имени функции с помощью переменной языка PHP. А поскольку $my_function представляет собой переменную с незаданным значением, то интерпретатор PHP подставляет в качестве значения этой переменной пустую строку (которая не может служить в качестве имени определенной функции), после чего выдает приведенное в названии этого подраздела маловыразительное сообщение об ошибке.
Признак нарушения в работе - сообщение Cannot redeclare my_function()
Причина этой проблемы проста — где-то в используемом коде имеется повторно заданное определение функции my_function(), а такая ситуация в языке PHP является недопустимой. Убедитесь в том, что в коде не применяется конструкция include для включения одного и того же файла с определениями функций больше одного раза. Чтобы предотвратить возникновение такой ошибки, необходимо использовать конструкцию include_once или require_once, но с учетом того предостережения, что ошибка, связанная с повторным включением, при этом не устраняется, а просто перестает обнаруживаться.
Почему указанную ситуацию следует рассматривать как потенциальную причину нарушений в работе? Дело в том, что вполне можно представить себе такую ситуацию, что разработчик определил две полностью разные функции, но непреднамеренно присвоил этим функциям одинаковые имена. Это связано с тем риском, что допущенная при этом ошибка проявится в самый неожиданный момент.
Признак нарушения в работе - сообщение Wrong parameter count
Функция, имя которой указано в сообщении об ошибке Wrong parameter count, вызвана с меньшим или большим количеством фактических параметров по сравнению с тем, на обработку которого она рассчитана. Если задано больше параметров, чем требуется, то никаких затруднений не возникает, но если используется меньше параметров по сравнению с ожидаемым, то возникает ошибка.
Обработка исключений
Отладка программ
Оценить статью: