21 ошибка программиста PHP
Эта серия статей предназначена для тех программистов на языке PHP, которые хотят избежать наиболее общих ошибок в написании кода.
Читатель, как минимум, должен знать общий синтаксис PHP, а также весьма желателен некоторый опыт использования языка на практике.
Введение
Одна из наиболее сильных сторон PHP является, одновременно, и его слабой стороной: PHP очень прост в изучении. Это привлекает многих людей; однако, несмотря на его кажущуюся простоту, не так-то просто научиться использовать этот язык правильно и эффективно.
Как правило, дело в недостаточной практике программирования. Неопытные программисты становятся перед лицом необходимости создания сложных веб-приложений. Поэтому сплошь и рядом допускаются ошибки, которых избежал бы опытный программист, такие как необоснованное использование функции printf() или неправильное использование семантики PHP.
В этой серии из трех статей представлены наиболее, по нашему мнению, характерные ошибки. Эти ошибки можно классифицировать по нескольким категориям, от «некритических» до «смертельных». Наряду с анализом этих ошибок представлены способы их избежания, а также некоторые «маленькие хитрости», накопленные за многие годы практики программирования.
Часть 1: Описываются 7 «детских» ошибок (21-15 в обратном порядке, в соответствии со степенью серьезности по нашей классификации). Такие ошибки не вызывают серьезных проблем, но приводят к уменьшению эффективности работы программы, а также выражаются в громоздком трудночитаемом коде, в который, к тому же, трудно вносить изменения.
Часть 2: Следующие 7 ошибок (14-8) относятся к «серьезным». Они ведут к еще более значительному уменьшению скорости выполнения кода, уменьшению безопасности скриптов; код становится еще более запутанным.
Часть 3: Описания семи последних, «смертельных» ошибок. Эти ошибки концептуальны по своей природе и являются причиной появления ошибок, описанных в 1-ой и 2-ой частях статьи. Они включают и такие ошибки, как недостаточное внимание, уделенное как проекту в целом, так и коду программы, в частности.
21. Неоправданное использование функции printf()
Функция printf() предназначена для вывода форматированных данных.
Например, ее следует использовать при необходимости вывода переменной в формате с плавающей запятой с определенной точностью, либо в любом другом случае, когда возникает необходимость изменения формата выводимых данных.
Ниже приведен пример обоснованного применения функции printf(). В данном случае она используется для форматированного вывода числа «пи»:
<?php
Примечание: Наблюдаются случаи патологической боязни функции printf(), когда люди пишут свои функции форматированного вывода, порой по 30-40 строк, хотя все проблемы мог бы решить один-единственный вызов функции printf().
printf ("Число Пи: %2f\n
\n", M_PI);
printf ("Это тоже число Пи: %3f\n
\n", M_PI);
printf ("И это Пи: %4f\n
\n", M_PI);
?>
Многие программисты используют функцию printf() для вывода переменных, результатов вызова функций, а иногда даже обычных текстовых данных. Наиболее часто это происходит в следующих двух случаях:
• когда следовало бы использовать функцию print();
• при выводе результатов, возвращаемых функциями.
Когда следует использовать print()
Вызов функции printf() зачастую используется там, где следовало бы использовать print(). В следующем примере функция printf() используется для вывода четырех переменных:
<?php
В данном случае возможно (и желательно!) применение print():
$name = 'Sterling Hughes';
$job = 'Senior Engineer';
$company = 'DesignMultimedia';
$email = 'shughes@designmultimedia.com';
printf ( "Меня зовут %s\n
\n
Я работаю %s, %s\n
\n
Мой адрес E-mail:%s\n
\n",
$name, $job, $company, $email );
?>
print «Меня зовут $name\n
\n
Я работаю в $company, $job\n
\n
Мой адрес E-mail: $email\n
\n»;
Использование print() вместо printf() в случаях, когда выводятся неформатированные данные, как в данном примере, дает следующие выгоды:
• Увеличение производительности: Функция printf() форматирует свои аргументы перед выводом. Таким образом, время ее выполнения больше, чем для функций print() или echo().
• Более ясный код: Все-таки надо признать, что использование функции printf() затрудняет чтение кода (имеющих достаточный опыт программирования на C это, конечно, касается в меньшей степени). Чтобы функция printf() не повела себя самым неожиданным для вас образом, требуется как знание синтаксиса данной функции, (т.е. %s определяет строковый формат вывода, тогда как %d — десятичный), так и знание типов переменных.
Использование функции printf() для вывода значения, возвращаемого функцией
Еще одна характерная ошибка использования функции printf() — вывод значения, возвращаемого функцией, как в следующем примере:
<?php
Наряду с функцией print() при использовании ее в тех же целях, следует использовать оператор «.» В данном случае этот оператор добавляет текст к результату вызова функции:
printf ("Найдено %d вхождений строки %s", count ($result), $search_term);
?>
<?php
Использование оператора . в паре с функцией print() позволяет избежать использования более медленной функции printf().
print "Найдено " .
count ($result) .
" вхождений строки $search_term";
?>
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 заключена в двойные кавычки, что однозначно определяет ее как строку, тогда как функция fgets() ожидает в качестве первого аргумента дескриптор, но не строку. Соответственно, вам следует использовать переменную, которая может содержать дескриптор.
Примечание: В данном случае строковый тип допустим синтаксически.
Для решения проблемы следует просто убрать двойные кавычки:
<?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
\n
\n";
// Цикл "для" чтобы вывести все
// предыдущие значения возраста
for ($idx = 0; $idx < $age; $idx++)
{
// Напечатаем каждое значение возраста
print "$idx лет\n
\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
Вызову функции implode() в качестве одного из параметров передается результат выполнения вложенных функций, поэтому такой код трудно прочесть. В данном случае нам может здорово помочь использование временной переменной:
// string reverse_characters (string str)
// Переворачивает строку символов
function reverse_characters ($str)
{
return implode ("", array_reverse (preg_split ("//", $str)));
}
?>
<?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
Встречаются также рекомендации приступая к программированию на PHP, первым делом заменять имена встроенных функций более привычными.
function len ($str) {
return strlen ($str);
}
?>
Существует, как минимум, две причины этого не делать. Во-первых, и прежде всего, мы получаем менее читаемый код. Люди, читающие ваш код, увидят массу очевидно ненужных функций и будут сильно удивлены, почему же вами не использовались стандартные функции PHP.
Ну и, наконец, это замедляет программу. Дело не только в необходимости обработки большего объема кода, но и в том, что для вызова такой пользовательской функции, требуется больше времени, чем для прямого вызова стандартной функции.
Используйте стандартные функций языка!
Иногда так трудно устоять! Ведь программист редко знает сразу весь набор функций — у него обычно нет времени запомнить их все. Почему бы просто не переименовать функцию? Но, повторимся, этого не следует делать в силу изложенных выше причин.
Хорошо бы иметь под рукой справочник по функциям PHP (удобно использовать индексированную версию в формате PDF). И перед тем как написать какую-либо функцию, внимательно посмотреть — не существует ли она уже в списке стандартных функций.
Но следует заметить, что в кодах программ можно встретить пользовательские функции, написанные еще до их введения в качестве стандартных (например, функции сравнения двух массивов). Это не означает, что вы обязательно должны переписать код и заменять их стандартными функциями.
16. Клиентская часть программы не отделяется от серверной части
Многие программисты рекомендуют объединять код HTML (интерпретируемый на стороне клиента) и код PHP (выполняемый сервером) в один большой файл.
Для маленьких сайтов это, возможно, неплохо. Но, когда ваш сайт начнет расти, вы можете столкнуться с проблемами при необходимости добавить какие-либо новые функции. Такой стиль программирования приводит к очень «непослушному» и громоздкому коду.
API функций
Если вы собрались отделить код PHP от HTML кода, у вас есть два варианта. Один способ — — создать функции динамического формирования вывода и поместить их в нужное место на веб-странице.
Например, так:
index.php — код страницы
<?php include_once ("site.lib"); ?>
Очевидно, такой код лучше читаем. Еще одно преимущество использования этой концепции — возможность изменения дизайна без модификации самого кода программы.
<?php print_header (); ?>
<?php print_links (); ?> <?php print_body (); ?>
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 "\t\t\t
\"$links[$i]\">$names[$i]
\n
\n";
}
}
?>
Плюсы использования API функций
• Относительно чистый, ясный код
• Быстрый код
Минусы использования API функций
• Не настолько наглядно как система шаблонов
• Все-таки для модификации дизайна требуется некоторое знание PHP
Система шаблонов
Второй способ, используемый для разделения PHP и HTML кода, — использование шаблонов. В данном случае некоторые элементы дизайна заменяются пользовательскими тегами, а сама программа сканирует файл на предмет их наличия и заменяет их необходимой информацией.
Пример использования шаблонов:
<BODY< font> %%BODY_PROPERTIES%%>
Затем пишем программу, просматривающую код шаблона и при выводе заменяющую тэги вида %%SOME%% нужной информацией.
%%PAGE_TITLE%%
%%PAGE_LINKS%% %%PAGE_CONTENT%%
Примечание: неплохой класс для использования его в системе шаблонов — 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)». (Эх, где бы их еще найти! ;)) — прим. переводчика)
14. Пренебрежение правилами присвоения имён
Одна из наиболее серьёзных ошибок программиста — непродуманная система именования переменных проекта. Нередко приходится тратить уйму времени на разбор кода только потому, что автор вдруг решил ввести в программу переменные $fred и $barney вместо ожидаемых $email и $name. Речь ведётся о реальном проекте, где не менее реальный программист решил все переменные проекта назвать именами героев мультсериала «Flinstones» (Это не шутка).
То как вы назовёте переменные и функции программы, определит во многом читаемость её кода. Наиболее распространёнными ошибками являются имена:
• слишком короткие или наоборот, чрезмерно длинные;
• не связанные по смыслу с контекстом программы;
• не учитывающие регистрозависимость;
• замедляющие разбор и чтение кода (особенно это касается имён функций).
Именование переменных
Регистрозависимость
В PHP имена переменных регистрозависимы, то есть $user и $User — две записи в списке переменных скрипта. Однако некоторые программисты активно пользуются этим и производят на свет переменные с совершенно одинаковыми именами, но использующими буквы разных регистров. Это отвратительная привычка. Регистр букв никогда не должен быть единственным отличием двух переменных. Каждая переменная на своём поле действия должна иметь уникальное имя.
Слишком короткие имена
Для обозначения переменных многие программисты используют одним им понятные аббревиатуры. О чём впоследствии сильно жалеют, ибо смысл сокращения затерялся во времени своего создания. Имя переменной должно отражать характер её значения, то есть содержания и обозначаться полными словами или общепонятными сокращениями.
Слишком длинные имена
С другой стороны, наблюдаются случаи злоупотребления длинными именами. Наиболее общее правило: имя переменной должно состоять максимум из двух слов. Разделить эти два слова мы можем, поставив understrike (то есть «_») или написав второе слово с заглавной буквы.
Пример #1. Положительный.
Как правильно присваивать имена переменным:
$username = ‘sterling’;
$password = ‘secret’;
$teachers = array (‘Sadlon’,
‘Lane’,
‘Patterson’,
‘Perry’,
‘Sandler’,
‘Mendick’,
‘Zung’);
foreach ($teachers as $teacher);
?>
Пример #2. Отрицательный.
Теперь рассмотрим несколько преувеличенные примеры того, как не следует присваивать имена переменным:
$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().
Обратите внимание на использование глагола в имени функции. Именно глагол помещает функцию в правильный контекст:
list ($num1, $num2) = generate_gaussian_rand();
list ($num3, $num4) = generate_gaussian_rand();
?>
Для сравнения, другой пример:
list ($num1, $num2) = gaussian_rand_generator();
list ($num1, $num2) = gaussian_rand_generator();
?>
Видите разницу? Во втором примере для обозначения действия использовано существительное. И если назначение функции ещё прослеживается, название затрудняет чтение кода.
Мораль: используйте глаголы!
13. Непродуманная работа с данными: бд и sql
Забавно иногда наблюдать, сколько разных уловок находят люди для организации доступа к базам данных и получения выборки результатов. Среди прочих особенно выделяются комбинации из веток if, циклов do..while, множественных запросов и вызовов функции sql_result() внутри цикла for.
Чем, на их взгляд, они занимаются?
Код, основанный на методе научного тыка, говорит о недостаточно ясно определённой организации работы с БД. Те, кто прилагают все свои усилия на написание кода, а не на написание правильного кода, рискуют больше потерять, чем заработать. Некорректная выборка данных — яркий тому пример. Некоторые программисты не уделяют достаточно времени на тщательное продумывание этого момента. Естественно, в реальной жизни может и не оказаться того «единственно верного» способа выборки данных, но всегда найдётся тысяча «неверных», это точно.
Ошибки в организации выборки данным можно разделить на три класса:
• неправильное использование функций обращения к БД
• ошибки SQL: запрашивается не то, что нужно
• обработка результатов выборки средствами PHP
Неправильное использование функций обращения к БД
Один из PHP-исходников предлагал следующий способ получения выборки из БД (приведённый ниже код в проекте находится после сгенерированных SQL-запросов):
if (!($row = sql_fetch_row ($result))) {
print «Ошибка: не найдено ни одного ряда»;
exit;
}
do {
print «$row[0]: $row[1]\n
\n»;
}
while ($row = sql_fetch_row ($result));
?>
Примечание: в данном и последующих примерах $result является дескриптором выборки или указателем на неё. Другими словами, был произведён запрос и получено определённое множество рядов. Примеры демонстрируют методы эффективной обработки этого множества.
В этом отрезке кода есть две ошибки:
• проверка на «ноль рядов» — это попытка получить хотя бы один.
• полученные данные не хранятся в ассоциативном массиве.
Проверка на «ноль рядов» ($result): неправильный подход
Задействовав функцию sql_fetch_row(), данный кусок кода предлагает косвенную проверку выборки на наличие хотя бы одного ряда данных. Но ведь существует прямой способ — это подсчёт количества рядов в выборке $result функцией sql_num_rows(), как показано ниже:
if (sql_num_rows ($result) <= 0) {
print «Ошибка: не найдено ни одного ряда»;
exit;
}
while ($row = sql_fetch_row ($result)){
print «$row[0]: $row[1]\n
\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()
Действительно, некоторые СУБД могут не поддерживать эту функцию. Отнесёмся с сочувствием ко всем владельцам таких систем. Им придётся проверять выборки «на ноль рядов» путем запроса первого ряда. Однако и здесь, рекомендуем использовать булевские переменные:
$found = false;
while ($row = sql_fetch_array($result)){
$found = true;
}
if (!$found){
print «Ошибка»;
}
?>
Получение рядов данных: правила эффективной работы
Вторая проблема нашего кода — это использование функции sql_fetch_row() для получения рядов. Как результат своей работы эта функция возвращает лишь пронумерованный массив. Однако существует ещё и функция sql_fetch_array(), которая возвращает два массива: пронумерованный и ассоциативный:
$row = sql_fetch_array ($result);
print $row[1]; // Второй столбец
print $row[name]; // Столбец name — имя
?>
Примечание: Существуют разные точки зрения на целесообразность использования одинарных кавычек при вставке строковых аргументов. В приведённом примере (столбец name) и далее по статье они не используются.
Какая из функций более удобна для разработчика? Ассоциативные массивы позволяют редактору кода ясно и однозначно понять, какая именно выборка из БД будет осуществляться в каждом конкретном случае. Например:
if (sql_num_rows ($result) <= 0) {
print «Ошибка: не найдено ни одного ряда»;
exit;
}
while ($row = sql_fetch_array ($result)) {
print «$row[name]: $row[phone_number]\n
\n»;
}
?>
Применение sql_fetch_row($result)
Итак, функция sql_fetch_row() имеет целую тонну недостатков. Однако, существует ситуация, где её можно поставить без всякого ущерба «прозрачности» кода: когда sql-запрос формируется пользователем.
До настоящего момента мы рассматривали примеры с заранее известными запросами и определёнными разработчиком. Но иногда возникает необходимость в запросе, сформированном самим пользователем. В таких случаях разработчику неизвестно количество столбцов в выборке.
Здесь для их эффективной обработки полезно использовать функцию sql_fetch_row() в сочетании с count():
for ($i = 0; $i < count($row); $i++){
print «Столбец». ($i + 1). $row[$i]. «\n
\n»;
}
?>
Ошибки SQL: запрашивается не то, что нужно
Практика показывает, что обработка выборки из БД средствами PHP — тоже является ошибкой. Бывали случаи, когда для простого поиска по 2Мб БД программисты использовали PHP, а потом возмущались его медлительностью. А делать выборку «весом» в два метра занимает целую вечность.
Язык Структурированных Запросов (SQL) был специально разработан для запросов и получения данных из таблиц в БД. Идея языка заключается в отсеивании данных ненужных вам (средствами SQL) и получении только тех, которые вам действительно необходимы для дальнейшей обработки (например, средствами PHP).
Если вы заметили, что получаете в выборке данных, больше, чем вам нужно, это верный признак недоработанных SQL-запросов.
Условие WHERE
Классический пример эффективного применения SQL-запросов — использование условия WHERE в синтаксисе SQL.
Рассмотрим пример кода, производящего выборку и выводящего список имён и телефонов всех пользователей с id равным 5:
// В предыдущих строках
// устанавливается соединение, и $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
\n»;
print «Телефон: $row[phone]\n
\n»;
break;
}
}
?>
Данный код имеет следующие недоработки: для поиска по всей БД используется PHP; при работе с БД малого размера на это можно и не обращать внимания, но с ростом БД вы обязательно заметите резкое падение скорости работы скриптов.
Выход прост: включите в SQL-запрос условие WHERE:
$statement = «SELECT name, phone FROM samp_table»;
$statement .= » WHERE id=’5′»;
WHERE позволит применить более строгие критерии выборки. Фильтром в данном случае будет являться значение аргумента. В нашем примере это «id=5».
Получив нужную вам выборку, вы используете PHP для простого вывода результатов:
if (@sql_num_rows ($result) != 1) {
die («Получено неверное количество рядов»);
}
$row = @sql_fetch_array ($result);
print «Имя: $row[name]\n
\n»;
print «Телефон: $row[phone]\n
\n»;
?>
Обработка результатов выборки средствами PHP
Нередко программист намеренно не сортирует выборку при запросе, перекладывая эту работу на PHP. Такой подход неэффективен, ибо сортировка средствами SQL проходит намного быстрее, чем в PHP.
Для сортировки результатов рекомендуем применять синтаксис SQL (ORDER BY), а не PHP-функцию ksort().
Рассмотрим пример использования ksort() для сортировки выборки по имени (name):
$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:
$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.
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(). Стоит проверить этот ответ и убедиться, что подключение к БД действительно имело место. Если этого не сделать, то все запросы к БД могут не состояться, а некоторые данные могут быть утеряны; вы же будете пребывать в счастливом неведении.
$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 вывода результата просто не было).
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, работа скрипта прекращается и выводится сообщение об ошибке.
// 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 функции. Сюда входят функции для построения конкретного приложения.
• Третий — само приложение:
// 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;
}
?>
// CalcMortgage.php
// Третий уровень — приложение
// $money, $time и $period получаем из формы
include_once ‘MortgageRate.php’;
$price = calculate_mortgage_rate ($money, $time, $period);
print «Ваша процентная ставка за $period составляет $price»;
?>
Разработка концепции именования и работа в её рамках.
Один из самых неприятных моментов при разработке больших приложений — это конфликты пространства имён. Классы его сегментируют. Таким образом, разные классы могут:
• иметь свойства с одинаковыми именами или
• содержать в себе методы с одинаковыми именами.
Например, класс Phillips и класс Normal могут одновременно содержать метод с именем screwdriver.
В общем, перед началом большого проекта рекомендуется продумать именование для всего, в частности, способы различия глобальных и регулярных переменных, определения библиотечных функций и т. д.
Группирование взаимосвязанных функций в один файл
Связанные API функции лучше всего собрать в один файл так же, как связанные методы объединяются в класс. Такие файлы можно представить классами, где каждая функция представляет собой как бы метод этого класса. Так, каждая функция будет иметь ясное определение и прозрачную структуру.
Например, можно было бы все функции, связанные с общением с БД, собрать в файл DB.php.
ООП, как и всё на свете, хорошо в меру
Небольшая оговорка: эта глава была написана не для того, чтобы отговорить вас от использования ООП вообще. Скорее, это была попытка убедить вас не работать с PHP в режиме Java или C++, где ООП — решение номер один.
Проведите тщательный анализ всех выгод и потерь, прежде чем применить объектный подход в PHP.
10. Неоправданное использование регулярных выражений
Регулярные выражения — мощный инструмент для поиска и организации данных, как, например, проверка адреса электронной почты на корректность или поиск URL. Но в то же время регулярные выражения работают медленнее других функций 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);
print $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 — язык мало приспособленный к так называемому методу — «всё в одной строке». Рекомендуем разбивать сложные хитросплетения комбинированных функций и представлять их в более структурированном виде.
Perl
• while () {
• @_ = split /:/;
• $quotes{shift} = shift;
• }
• print map { «$_: «, reverse split //,$quotes->{$_},»\n»; } keys %quotes;
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, вот лучшие из них:
• РАЗДЕЛ КНИГИ на PHPCLub.NET:
• Professional PHP: одна из лучших книг по PHP; годится как для настоящих профи, так и начинающих.
• Web Application Development with PHP: замечательная книга; в ней описаны как концепции web-программирования в целом, так и самые продвинутые функции PHP. Кроме всего прочего, содержит официальную информацию по Zend API.
• The PHP Developer’s Cookbook: книга рассчитана на помощь в решении конкретных задач, с которыми PHP-программисты сталкиваются чаще всего (Время издания: ноябрь 2000; авторы: ваш покорный слуга и Андрей Змиевский).
8. Недостаточное внимание к вопросам безопасности
Пользователи никогда не будут работать по нашим правилам. Поэтому создание системы устойчивой и терпимой к ошибкам — целиком и полностью ответственность разработчиков, то есть нас.
При разработке приложения вы должны влезть в шкуру обычного юзера. Внимательно изучите все места, где пользовательская ошибка может вскрыть брешь в безопасности. Затем исправьте код так, чтобы программа сама нейтрализовала все ошибки и тем самым избавилась от потенциальных «дыр». Также важно запомнить, что хотя все ошибки и атаки — вина пользователей, за «дыры» или за непроверенные данные на каком-либо уровне отвечаете только вы.
Пример: многие скрипты не используют встроенную PHP-функцию mail(), которая обеспечивает безопасную отправку почты. Вместо этого почта отправляется через программу sendmail с помощью popen(). Это делает код весьма неустойчивым к искажению данных и представляет собой «дыру» в безопасности системы (получателю можно отправить /etc/passwd).
Перечислим наиболее распространённые «дыры», позволяющие искажать данные:
• системные вызовы. Никогда нелишне повторить. Каждый раз нужно убедиться, сто пользователь отправляет вам «безопасные» данные для системного вызова. НИКОГДА НЕ ДОВЕРЯЙТЕ ДАННЫМ, ПОЛУЧЕННЫМ ОТ ПОЛЬЗОВАТЕЛЯ. И ПРЕЖДЕ ЧЕМ ПОДСТАВИТЬ ИХ В СИСТЕМНЫЙ ВЫЗОВ ПРОВЕРЬТЕ ИХ.
• регистрация пользователей. Если вы желаете получить корректные результаты, обязательно проверяйте полученные данные, причём, лучше если проверка будет состоять из нескольких этапов. Прежде всего, это проверка адреса электронной почты: убедитесь, что пользователь предоставил вам работающий аккаунт. Кроме того, стоит убедиться, что указанный возраст находится в определённых пределах. Вы можете быть совершенно уверены, что на нашей планете нет двухсотлетних товарищей, которые ещё способны сесть за компьютер.
• Приём номеров кредитных карточек. Некоторые программисты ограничиваются самыми простыми алгоритмами; их легко обмануть. Существуют крупные специализированные организации для проверки кредитных карточек; рекомендуем прибегать к их помощи или ресурсам и только потом решать, принята карточка или нет. НИКОГДА НЕ ПОЛАГАЙТЕСЬ ТОЛЬКО НА АЛГОРИТМЫ.
Безопасность системных вызовов.
Если возникает необходимость поместить полученные от пользователя данные в системный вызов, обязательно проверьте и перепроверьте их. Убедитесь, что данные, которые пользователь предоставил системе, не содержат «опасных» элементов и не взломают систему нежелательными командами. Для этого PHP предлагает специальную функцию EscapeShellCmd().
Каждый раз при системном вызове с потенциально небезопасными данными, дезактивируйте их с помощью EscapeShellCmd():
Примечание: дезактивация проходит путём подстановки бэкслэша («\») перед потенциально небезопасными для системы символами (а именно: #&;?’\»|*?~<>^()[]{}$\\\x0A\xFF).
Поиск аккаунта
if ($name) {
system (EscapeShellCmd («lookup $name»));
print «
\n\n»;
}
?>
print $PHP_SELF; ?>» method=»GET»>
Введите аккаунт для поиска:
Примечание: хотя EscapeShellCmd() — функция чрезвычайно полезная, когда вам нужно убедиться в безопасности полученных данных, всегда стоит проводить специальные проверки в зависимости от вида данных. EscapeShellCmd() не проверит данные на корректность, она лишь предотвратит неавторизованны
7. Программирование методом «Вырезал и Вставил»: Неправильный Способ
Я видел, как программисты-новички копируют код, который, к примеру, проверяет верность адреса e-mail, добавляют функцию, посылающую письмо и строки, использующие значения формы для создания почтового сообщения. Они вставляют его целиком с свою программу, что приводит к работающей смеси функций, которая ненадежно отправляет форму. Хотя код будет работать в оптимальных условиях, он не пройдет никакой реальной проверки на качественность. Эта мешанина не будет:
Дорабатываемой: Код будет выглядеть, как множество отрывков, слепленных вместе. Попросите опытного программиста изменить скрипт и он предпочтет все переписать с нуля. Нечитаемый код это недорабатываемый код.
Безопасной: Вы будете помещать код других людей, не понимая полностью, что он делает. Подумайте об этом. Что, если бы в этом коде был бы фиктивный системный вызов, который бы снес все файлы с вашего диска? Более того, тот же самый код не всегда окажется одинаково безопасным на разных системах и конфигурациях. В заключение, в вашей системе будут глюки, унаследованные из чужой программы.
Быстрой: Когда код слепляют вместе, он, вероятно, окажется не особо быстрым, поскольку он не последователен — а это самая важная вещь, когда доходит до создания быстрых скриптов.
Делайте Правильно: Сначала изучите, Потом Копируйте
Внимательно изучите код другого программиста перед его копированием. Проанализируйте, что было сделано. Только если код читабелен, согласуется с логикой вашей программы и в нем нет ошибок, его можно рассматривать, как кандидат на копирование. Интегрирование кода таким образом позволит легче совместить его с остатками своего кода.
Библиотеки — это хорошо
Используйте библиотеки PHP лишь из проверенных источников, таких как PEAR, или PHP Classes Repository. Для предварительно запакованных API, хорошо использовать дополнительную функциональность, которую эти API предоставляют вашей программе. На самом деле, если вы можете найти написанные библиотеки в источниках, вызывающих доверие, обычно лучше использовать их в своем коде.
6. Нет принципов написания кода
Раньше, когда я только начал программировать, я работал над простым проектом (на Perl’е) с тремя другими программистами. Поскольку я был молодом (и не был ведущим разработчиком), мы не договорились о принципах написания кода. Каждому из нас была поручена часть проекта, мы разошлись и стали работать раздельно друг от друга. Когда мы наконец собрались, чтобы собрать законченный продукт (связать все части вместе), каждый кусок работал по разному. К примеру, один из программистов предпочитал studlyCaps стиль именования функций и переменных. Я, например, предпочитал использовать подчеркивания для выделения названий функций и переменных. У главы проекта был еще более интересный стиль программирования. По настроению он пользовался то одним, то другим способом (что привело к некоторым конфликтам названий и головной боли). Если коротко, получилась фигня. Нам понадобилось где-то на 20 часов больше, чем пришлось бы потратить, если бы мы обо все договорились заранее и договорились о принципах написания кода.
Эти принципы — способ определения структуры и внешнего вида вашего кода, они описывают методы и стиль, который вы должны использовать при разработке проекта.
Каждый проект должен следовать набору таким принципов. Они должны включать все, начиная от глобальных моментов, например, как разделить исходный код (например, файловая структура), до более специфических, таких как правила именования переменных (например, префиксы и суффиксы, написание глобальных переменных заглавными буквами). Принципы определяют общепризнанные стандарты, которым следуют все участники проекта независимо от собственного стиля программирования. Они могут служить для определения всех спорных моментов, таких как:
Какие переменные глобальные и как они обозначаются, как глобальные.
Структура документа, обозначающая какой файл должен быть помещен в lib, а какой в src каталоги.
Стиль комментариев и их написание.
Написание документации.
Длина строки.
Принципы написания кода должны быть оформлены, как официальный документ их должен придерживаться каждый программист, задействованный в проекте. Когда проект завершен, документация сохраняется на будущее, как справочная информация.
Пример Документа по принципам написания кода
Давайте создадим очень короткий документ по принципам написания кода. Мы не будем углубляться в детали, просто напишем каркас. Настоящий документ обычно достигает размера в 10-40 листов. Обратите внимание, что вам не понадобиться каждый раз писать его заново. Старый вариант всегда можно изменить по необходимости и использовать в новом проекте.
Основные параметры стиля DesignMultimedia.com
Вот пример такого документа. Как только он завершен, вы можете в одиночку сосредоточится на его реализации и не беспокоиться о несовместимости исходного кода, метода именования переменных или структуре сайта.
Вступление
Этот документ содержит следующую инфомацию:
Файловая Структура
Заголовки и Подвалы
Документация по коду
Стиль комментирования, назначение и определение комментариев
Основные моменты разработки
Именование переменных
Файловая Структура
Приложение DesignMultimedia.com имеет следующую структуру:
<Описание, как будут храниться файлы вашего приложения (например, что куда разместить), также как правила именования каждого файла.>
Объяснение структуры:
<Объясните каждый момент, чтобы не было разночтений, что именно вы имели ввиду>
Заголовки и Подвалы
Каждая страница программы должна содержать следующий заголовок:
<Здесь описывается заголовок. Он может быть чем угодно, от куска кода до простого copyright’а>
К примеру, все файлы .c и .h PHP4 имеют следующий стандартный заголовок:
<?php
/*
+————————————————————————+
| PHP version 4.0 |
+————————————————————————+
| Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
+————————————————————————+
| This source file is subject to version 2.02 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| http://www.php.net/license/2_02.txt. |
| If you did not receive a copy of the PHP license and are unable |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+————————————————————————+
| Authors: Sterling Hughes |
+————————————————————————+
*/
?>
И следующий подвал:
<Здесь вы описываете стандартный подвал вашего сайта, к примеру, это можно найти в завершении всех .c и .h файлов PHP:>
<?php
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
?>
Объяснения если необходимы:
<Расскажите про размещение заголовков и подвалов, а также объясните, зачем они нужны (в зависимости от их содержания) >
Документация по Коду
<Здесь вы описываете специфику представления кода в приложении, где он пишется по стилю документации javadoc, а где по документации к XML с использованием таблиц стилей Docbook.>
Стиль комментирования
<Здесь описываются различные типы комментариев, в дополнении к описанию значений любых используемых аббревиатур или «фраз».>
Основные моменты разработки
<Здесь описывается, что можно и что нельзя делать. К примеру, не собирайте разные классы в один файл или помещайте каждый класс в отдельный файл.>
Именование Переменных
<К примеру, вы можете написать следующее:>
Этот сайт следует следующим принципам присвоения имен:
[1] Названия все классов любыми буквами.
[2] Названия всех функции строчными буквами со словами, разделенными подчеркиванием (_).
[3] Дополнительные специфические правила.
5. Не Проверяется Код
Я понял, что этот пункт надо включить в эту статью, когда провел проверку кода для моего друга. Кода я читал его код, я смог сократить число переменных на одну треть, что дало прирост скорости в 400% при обращениях к базе данных. Более того, я сократил число строк кода наполовину, что привело к ускорению на 1000% (в 10 раз). Какова мораль сей басни? Если другой опытный программист пройдется по вашему коду мелкой гребенкой, качество, скорость и безопасность кода значительно возрастет. Он сможет найти глюки, о существовании которых вы даже нег догадывались и предложит сделать что-то более простым способом. Более того, он сможет определить места, которые замедляют PHP или приводят к появлению серьезных дыр в системе безопасности.
Одна из причин, по которой PHP, как система с открытым исходным кодом, занимает значительную долю рынка, заключается в том, что другие разработчики могут изучить исходный код, пишущийся для PHP. Тысячи людей участвуют в поиске ляпов, глюков, утечек памяти, ошибок совместимости и неоптимальности. К моменту выпуска новой версии PHP, по меньшей мере 2 или 3 программиста-эксперта просмотрели исходный код.
В идеале, скрипты для средне- или крупноразмерного проекта должны проверять два разных программиста, не участвовавших в написании исходного кода. Во время написания, комментарии постороннего человека также могут оказаться полезными. Впрочем, в большинстве случаев вам должно хватить одного программиста-эксперта, проверяющего код. Квалифицированный проверяющий — это тот, кто может быстро понять работу кода и предложить конструктивные советы и по его содержанию и по исполнению.
Часто, оказывается полезным сделать небольшой вопросник для проверяющего. Вот некоторые примеры, которые я счел полезными:
Каково назначение XXX кода?
Какая связь файла XXX с остальным проектом?
Каков стандартный механизм обработки ошибок в программе?
Можем мы ли мы отследить действия стандартного пользователя, работающего с программой?
Где пользователь может столкнуться с ошибками?
4. Затыкание дыр в проекте
Вы создаете приложение и затем понимаете, что некоторые вещи были сделаны не так, как их надо было сделать. Затыкание дыр в проекте — это создание временных заплаток вместо устранения основной, более серьезной проблемы. Когда вы совершаете эту ошибку, она может привести к работающему коду с серьезными заплатками, что приведет к снижению скорости и надежности приложения.
Показатели Упущений в Проекте
Обычно, когда вы первоначально планируете свой проект, вы думаете, что выполняете все в нужном порядке. Вы можете не осознать, что идете по неправильному пути, пока не получите специфику структуры приложения (или его частей).
Вот показатели, что план проекта сбился с пути:
Чрезмерное Затыкание дыр: Вы «затыкаете дыры» в коде. «Затыкание дыр» — это решение, которое заставляет код работать, но не подходит к структуре программы. Аналогично, это может быть не оптимальным решением, но единственным, применимой в текущей структуре.
Слишком-Сложные Решения. Вы можете обнаружить, что делать сложные операции для выполнения простых действий. Посмотрите на следующий пример, использующий цикл for для вывода строки:
<?php
$GeorgeBush = «If you look at the trends, I think you’ll see that most of our
imports come from other countries»;
for ($idx = 0; $idx < strlen($GeorgeBush); $idx++)
{
print $GeorgeBush[$idx];
}
?>
Код сверху написан хорошо; он пробегает по строке и выводит цитату Джорджа Буша. Он правильно написан и внешне и по синтаксису. Тем не менее, той же цели можно было достичь, применив функцию print на всю строку целиком.
Исправление Упущений Проекта
Когда вы понимаете, что ваша программа была написана с ошибками или не-оптимальной структурой, шаги, необходимые для исправления этого, могут быть различными, от оставления программы такой, какая она есть, изменения частей структуры программы, или полной переработки всего приложения. В большинстве случаев, лучшим вариантом будет, если кто-нибудь не участвующий в процессе разработки программы осмотрит ее и прикинет, что нужно сделать.
Давайте рассмотрим три категории ошибок:
Мелкие упущения местного масштаба: Иногда упущения в проекте программ могут быть не критичными и могут не стоить времени и денег, которые понадобятся на переработку структуры.
Исправление: В этом случае, вы должны записать информацию о недостатке на будущее. Если в будущем будет производиться реструтуризация, вы сможет заодно произвести любые необходимые изменения. Примером такого может быть использование неверного типа структуры данных для определенной части программы (обычный массив, где больше бы подошел ассоциативный, или стека, где больше бы подошло дерево).
Значительное упущение местного масштаба: В других случаях выясняется, что нужно переделать лишь часть приложения. Например, вы пишите Операционную Систему и есть недостатки в построении окон, но весь предшествующий код вполне хорош.
Исправление: Все, что на самое деле нужно переделать — это Менеджер Окон (а не вся ОС). Это, пожалуй, самый распространенный случай, в котором часть структуры неверна, а общая структура правильна.
ЗНАЧИТЕЛЬНОЕ, глобальное упущение в проекте: В большинстве таких случаях, вся инфраструктура приложения нарушена.
Исправление: Когда инфраструктура нарушена, обычно требуется полная реорганизация кода и взаимодействия различных частей программы. Это наиболее сложный и долгий случай, и редкий проект доходит до такого состояния. Примером может быть хранение всей информации поисковой системы типа Яndex’а в текстовых файлах.
3. Исключение Пользователя из Разработки Дизайна
С вами когда-нибудь случалось такое? Вас назначили разработать и произвести корпоративное приложение для внутреннего пользования именно вашей организацией. Вы провели часы, находя и документируя требования. Вы рассчитали проект, назначили задания и сделали его. Три месяца спустя вы представляется работающую модель лишь для того, чтобы получить следующие ответы пользователей:
«Это не то, что мы хотели»
«Требования изменились»
«Хорошо, но:»
«Э: какое приложение?» (первоначальный пользователь уволился!!!!)
Финальным судьей качества приложения будут пользователи. По определению, они будут использовать ваше приложение (и пытаться обругать его разными способами). Многие программисты создают очень крутые приложения, и все равно ожидания пользователей не оправдываются. Обычно это связано с одним или более «непониманиями».
Непонимания возникают, когда вы отстраняется пользователя от разработки структуры. Когда создается приложения, всегда думайте о них. Всегда держите в голове, чего хотят они и как приложение должно достичь поставленной цели. Наиболее важно, впрочем, уделять время на общение с ними:
Постоянный опрос мнения пользователей
Создание Прототипов
Бета Тестирование
Постоянный Опрос Мнения Пользователей
Как написал Бенджамин Франклин в альманахе Бедного Ричарда «Стежок, сделанный вовремя, спасает девятерых.» (Прим.пер.- Не силен я в американской истории) Тоже верно и для приложений. Если вы хотите поберечь свое время, постоянно спрашивайте их мнения. Чего они хотят? Чего нет? Что улучшит приложение?
Создание Прототипов
Создание Прототипов — это структурированный процесс тестирования и требования мнения пользователя по мере разработки приложения. Тестирование веб-приложения во время его разработки также важно. Начните с определение тестеров.
Проверяйте, насколько приложение соответствует требованиям пользователей, но также требуйте и прямых личных мнений. Многие программисты совершают ошибку — тестирует приложение только, когда оно завершено. Это рецепт провала, поскольку то, чего хочет пользователь, и то, чего сделает вы, скорее всего, будет различаться. Более того, пользователи лучше поймут, чего они хотят, когда увидят ясный пример. В кратце, нельзя заставить пользователя сразу четко сформулировать свои требования, хотя каждый программист и хочет этого.
Я предлагаю, чтобы вы определили путевые столбы по ходу разработки приложения. В конце каждого такого отрезка обдумайте следующие моменты:
Приносит пользу пользователю работа, которую вы проделали?
Чтобы они хотели видеть в приложении того, что еще не реализовано?
Будут ли они использовать добавленную вами функциональность?
Что дает приложение?
Ваши улучшения сделали все лучше или хуже?
Когда нужно размещать путевые столбы?
Обычно грамотно размещать путевые столбы в те моменты, когда завершена очередная значительная часть пользовательского интерфейса. К примеру, я часто размещаю первый столб когда завершается работа над интерфейсом приложения. Это момент, когда дизайнеры примерно представляют, как будет выглядеть сайт. Следующую проверку нужно будет провести, когда будет готова простейшая демо-версия, демонстрирующая функциональность приложения.
Таким образом, я проверю пользовательский интерфейс по завершению каждого модуля или компонента приложения. Модуль или компонент может быть, например, системы управления пользователями приложения, или, возможно, поисковой системой сайта. В эти моменты я возьму мою первоначальную группу тестеров (а также тех новых, которые имеют особое отношение к объекту тестирования) и предложу ответить на поставленные вопросы. Это позволяет увидеть «общий» эффект изменений, которые вы только что сделали. Вы можете определить дополнительные наборы вопрос на каждый «путевой столб» или использовать стандартный набор.
Бета Тестирование
Это обычная форма тестирования, которая похожа на создание прототипов, но обычно оставляется на время окончания работ. Выбранные клиенты получают возможность проверить приложение и сообщить свои комментарии и отчеты об ошибках. Оно не такое интерактивное, как создание прототипов, и должно быть произведено как можно раньше. Тем не менее, это необходимый шаг, который разработчики обязательно должны произвести перед выпуском приложения.
2. Отступление от Технического Задания
Во многие проектах сейчас все заканчиваются тем, что ТЗ пишется по мере работы. К примеру, в одном из моих первых проектов, я создал приложение, основанное на 40-минутном телефонном разговоре. Хотя все получилось нормально, риск провала был намного больше, чем если бы я потратил время на то, чтобы спланировать и представить себе приложение до начала его разработки. К примеру, когда я сделал проект, ничего не было абстрактным. Это означает, что если бы пользователь захотел использовать MySQL вместо MSSQL, приложение пришлось бы переписывать. Более того, там была туча ляпов безопасности. Я хранил все данные приложения в cookie. Было еще несколько недостатков — некоторые слишком обидные, чтобы их упоминать.
Когда Фред Брукс описал процесс разработки приложения в Месяце Мифического Человека (The Mythical Man Month), он определил следующее оптимальное расписание:
1/3 Планирование: Здесь вы описываете, как ваше приложение будет работать, каковы различные компоненты (и компоненты компонентов) приложения и как они будут связаны. Что должно делать приложение? Ответив на эти фундаментальные вопросы вы положите базис планирования приложения.
1/6 Кодирование: То, что мы любим делать. Превращать дизайн в реальность.
1/4 Тестирование компонентов и раннее тестирование системы: При разработке больших приложений наступает момент, когда, хотя приложение еще не готово, но базовую функциональность уже можно проверить.
1/4 Тестирование всех компонентов вместе: Это заключительная стадия, когда у нас уже есть законченное приложение, теперь его нужно проверять и перепроверять, чтобы убедиться, что оно настолько избавлено от глюков, насколько возможно.
Сегодня нам повезет, если хотя бы 1/6 времени, отведенного на разработку проекта, уйдет на планирование. Программисты начнут работать прямо сейчас, вообще не представляя, какие требования у задачи, в чем суть проблемы, и как ее решить. Этот сценарий аналогичен написанию статьи без плана.
Кодирование должно быть процессом размещения того, что уже было спланировано. Множество программистов (и я один из них) пишут все приложение (или наиболее сложные моменты приложения) перед тем, как начать непосредственное написание на настоящем языке программирования, таком, как PHP.
Обратите внимание: Процесс планирования внешнего вида, особенностей и функциональных требований приложения известен, как информационная архитектура.
Стадии Проекта
О стадиях проектного плана может быть сказано много. К примеру, можно упомянуть отладку, моделирование, разработку проекта и планирование времени. Тем не менее, я только набросаю простенький план. Обычный План Проекта включает:
Стадия Анализа Требований
Стадия разработки Дизайна Программы
Стадия Тестирования
Стадия Анализа Требований
Первая часть планирования проекта — создание анализа требований: здесь вы точно определяет, что требуется. Вы точно решает, что должна делать программа и как она будет работать. Это одна из важнейших стадий в разработке веб-приложения.
Определение Требований Пользователя
Так каким же образом вы определяете, что именно пользователь хочет от приложения? Возьмите на себя роль консультанта, чтобы определить их потребности, а также получше узнать своих клиентов. К примеру:
Что они делают?
Что делает их продукт лучшим (или уникальным)?
Как они представляют себя потребителям?
Как особенности сайта позволят им выйти на рынок?
Последнее, как я выяснил, отличный способ сделать проект больше. Можете ли вы найти, что можно добавить к функциональности сайта, чтобы помочь им? Если да, вы получить довольного заказчика, а заодно и больший (и высокоопличиваемый) проект.
Методы исследования могут варьироваться от обзора рынка или вопросников до разговора с ключевыми людьми компании. Каким бы способом вы не воспользовались для сбора нужно информации, крайне важно подумать о вышеупомянутых пунктах.
Определение Технологических Требований
Здесь вы решаете, какие технологии будут нужны вашему приложению и как вы будете использовать их. Первостепенный вопрос, есть ли у нас возможности и знаем ли мы, как выполнить требования пользователя? Технологии может включать, к примеру, языки программирования (в дополнение к PHP), ОС, скорость сервера, скорость коннекта, и т.д.
Стадия разработки Дизайна Программы
У вас есть спецификация. Теперь вы решаете, как написать приложение, что когда произходи; может даже пишите какой-то псевдо-код если встретите сложные для выполнения места. Вкратце, вам понадобиться:
Смоделировать
Проиллюстрировать
Набросать псевдо-код
Смоделировать. Перед написанием приложения, поймите, как разные части приложения будут взаимодействовать друг с другом.
К примеру, давайте разработаем простую форму. Мы разрабатываем возможные действия, которые может произвести пользовать при обращении к простому скрпиту, рассылающему почту. При самом абстрактом подходе, возможны два варианта. Они отослали данные формы, или нет. Если они не отослали данные формы, мы должны показать первую страницу формы. В противном случае, мы должны (еще раз) сделать одну из следующих двух вещей.
Если посланные данные верны (подразумевается, что они удовлетворяют всем критериям истинных данных), то отослать письмо пользователю и выдать сообщение с благодарностью.
В противном случае выдать сообщение об ошибки и дать возможность исправить ошибку.
Проиллюстрировать Их . Очень простая диаграмма показывает работу диаграммы, описанную выше. Она была сделана в Microsoft Visio и прекрасно подходит для простого приложения, отсылающего письмо.
Впрочем, когда доходить до более сложных приложений, часто используется специализированный инструмент, который позволит создать диаграмму ваших приложений. Чтобы получить информацию об этих популярных инструментах, посетите Dia, Visio, и UML.
Набросать Псевдо Код. Написание псевдо кода, реального кода, описывающего работу приложения, но не работающего — распространенная практика, используемая многими разработчиками. Обычно к ней прибегают, когда они упираются во что-то сложное или не могут полностью понять как впишется в систему какой-то определенный аспект приложения. Я также обнаружил, что псевдо код полезен при описании интерфейса приложения, когда его будут использовать другие программисты. Это помогает понять, что нужно сделать, и какой для этого самый простой способ.
К примеру, псевдо код снизу может быть набросан при разработке нашего простого приложения, отсылающего письмо.
<?php
if (formSubmitted)
{
valid_name(name) or error() and output_form();
valid_email(email) or error() and output_form();
valid_message(message) or error() and output_form();
message = name & email;
send_mail(email, message);
print «Thank you for your feedback»;
}
else
{
print «Send us feedback»;
formelement(name);
formelement(email);
formelement(message);
}
?>
Псевдо код выше определяет общую структуру скрипта и показывает все требуемые специфические элементы. Код не должен быть верным работающим PHP кодом (хотя и может быть). Что должен делать псевдо код — это определять различные задачи приложения и, возможно, теорию за этими задачами. Уровень абстракции вашего псевдо кода зависит только от вам. Лично я предпочитаю писать менее абстрактный вид псевдо кода, чем большинство людей. Все зависит от того, насколько вы знакомы с программированием. Наконец, когда вы спланировали все приложение, вы можете начать кодировать ваше приложение зная все шаги, которые нужно будет предпринять, и представляя, что именно вы будете создавать.
Стадия Тестирования
Одна из важнейших стадии разработки приложения, про которую часто забывают это стадия (заключительного) тестирования. Часто из-за за давления по времени и/или со стороны менеджеров, эту стадию сокращают или пропускают, а приложение считают завершенным.
Будем честны, программисты ненавидят тестирование. Это, пожалуй, одна из наиболее скучных и раздражающих стадий разработки приложения. Часто она состоит из погонь за диким гусем (? — wild goose chases), часов обезглючивания, и тестирования различных вариантов, чтобы понять, будет ли код работать в различных условиях. Если вы пропустите это, у вас никогда не будет обезглюченного кода! Вы всегда можете быть уверены, что что-то пропустили. Знаете, то, про что вы думаете, что оно никогда не случится, все равно произойдет.
Тестирование на Возвращение к предыдущему Состоянию. Приложения бесконечно дорабатываются. Важно быть уверенным, что когда вы добавляете новые функции, вы не сломаете то, что было раньше и то, на что рассчитывают пользователи. Такми образом, вы должны проделать Тестирование на Возвращение к предыдущему Состоянию. Это набор тестов, который дает уверенность, что имеющаяся функциональность не сломается с учетом произведенных изменений.
PHP сам по себе тестируется таким образом, чтобы убедиться что все функции и процессы, верно работающие в нем, когда вы проведете изменения в нем, не затронут другую часть PHP. Это помогает PHP не только поддерживать обратную совместимость (например, когда добавляются новые функции, старые скрипты продолжают работать). Но также это способ убедиться, что ни одно из совершенных изменений не испортит функции (не просто изменит их поведение).
Стресс-Тесты. ОК, значит ваше приложение отлично работает с 1 пользователем — все идет прекрасно и с огромной скоростью. Но что, если ваше приложение начнут использовать 20 пользователей? 30 пользователей? Или даже 100 пользователей одновременно? Как быстро тогда оно будет работать? Не начнет ли оно внезапно выдавать непонятные ошибки? Когда вы тестируете приложение вы обязательно провести стресс-тест, чтобы убедиться, что приложение выдержит большие нагрузки и будет нормально работать в различных условиях.
Это не означает, что не нужно отдать программу на тестирование пользователю. Выдайте ему ее и ждите отчетов об ошибках и мнений. Проведите бета тест (как упоминается в предыдущей ошибке). Более того, если специальные автоматические инструменты, эмулирующие тестирование толпой пользователей. Первый приходящий в голову, это Apache’s ab tool. AB, или Apache Benchmark произведет определенное число запросов к вашей Web-странице и вернет число удачных, неудачных, среднее время ожидания и т.д. Я советую использовать AB на всех ваших веб-страницах (в случае, если вы примите бесконечно мудрое решение пользоваться апачем). Так вы сможете обнаружить и оптимизировать страницы, которые требуют уйму памяти или просто очень долго грузятся. (Также, подумайте о покупке мощной утилиты кэширования скриптов — The Zend’s Cache).
1. Потеря Во Времени
Ошибка номер один, с которой сталкиваются все программисты — это потеряться во времени. Посмотрите правде в глаза, мы все оптимисты. Для нас нормально предполагать, что проект займет ровно столько времени, сколько он должен. Мы не учитываем все, что может пойти наперекосяк. К примеру, нам не приходит в голову, что выполнение чего-то может вызвать у нас проблемы.
Так, мы должны пресекать свои чувства и подредактировать цифры. Как правило, каждый раз, когда я беру проект, я учитываю каждый фактор, который приходит мне в голову и рассчитываю требуемое время, а затем удваиваю его. Компании редко бывают недовольны, когда вы завершаете проект раньше времени (и совсем наоборот обстоят дела, когда вы не укладываетесь в сроки). Такой метод позволяет получить достаточно времени, чтобы сделать работу качественно и не опоздать. Но, честно говоря, он зависит от точности моего первоначального расчета.
Каждый программист недооценивает (или в редких случаях, переоценивает) время, необходимое для завершения проекта на определенную величину, для меня в два раза, для кого-то в полтора, для третьего в три. Задача в том, чтобы выяснить среднюю ошибку в расчете требуемого времени и потом постоянно вносить эту поправку во все проекты.
Недостаточно время на разработку проекта приводит к следующим эффектам (которые я слишком хорошо знаю):
Вы забудете о своей личной жизни до тех пор, пока не сдадите проект. Все дни и ночи будут посвящены программированию, оставляя мало времени на отдых (не то, чтобы было неинтересно программировать, но программировать без остановки — это ужасно).
Вас будут торопить, что означает, что вы потратите меньше времени на создание надежного, читабельного и быстрого кода. Вместо этого, вы сосредоточитесь лишь на том, чтобы успеть ко времени сдачи проекта.
Вы можете пропустить важнейшие шаги, такие как проверка или отладка кода, которые, в свою очередь, рассердят клиентов и приведут к еще большей работе.
Вам придется бросать на проект больше людей, что будет стоить больших денег, а может и не помочь (по законам Брука, особенно на больших проектах).
Всегда уделяйте достаточно времени для долгого процесса отладки (такого же долгого, или даже более длинного, чем процесс разработки сам по себе), и 1/3 всего времени проекта на планирование. Это две важнейшие стадии производственного процесса.
SterlingWonderfulToyland.com: Пример
Давайте рассмотрим пример сайта, SterlingWonderfulToyland.com. Этот простой электронный магазин предназначен для продажи ограниченного выбора Playstation 2 и Видео Игр по демпинговым ценам. Владелец Джон Гиусепп также хочет убедить клиентов зайти в настоящий магазин и купить товары в нем. Таким образом, в сайте есть два основных раздела:
Интернет-магазин.
Обычная часть сайта — указывает, где находится магазин и что он собой представляет.
Во время планирования, я придумаю несколько вариантов каждого раздела веб-сайта. Я затем совмещу время на разработку той и другой, добавлю неделю на то, чтобы страстить их, а затем сообщу клиенту время на разработку всего сайта (словно он бы состоял из одного объединенного раздела).
Обычная часть сайта
Для простой, не-динамической части сайта, я прикину, сколько времени понадобиться на разработку каждой страницы в таком редакторе, как Macromedia Dreamweaver. Поскольку ничего динамического там нет, я умножу полученное время на количество страниц (с учетом того, что дизайн уже разработан). Затем я удвою результат (моя ошибка обычно 2x). Для этого раздела не нужно составлять никаких серьезных планов (как только пришла диаграмма, как должна выглядеть страница, все, что останется — слепить ее в Dreamweaver).
Обратите Внимание: Учтите дополнительные 1/3 времени от разработки приложения на планирование. К примеру, если вы ожидаете, что на разработку системы проверки кредитной карты понадобиться 9 дней, добавьте дополнительные 2-3 дня.
Интернет-Магазин
Для Интернет-магазина расчет времени произвести сложнее. Проще всего разбить задачу на меньшие подзадачи (такие, как транзакцией кредитных карточек, заказ через один клик, информация о продуктах, управление продуктами и т.д.), и рассчитать различные компоненты не учитывая поправку на ошибку. Я затем совмещу их, умножу результат на величину ошибки. После этого я добавлю дополнительные 1/3 времени, которое мне понадобиться на завершение проекта, на тестирование и исправление глюков, чтобы быть уверенным, что клиент получит работающий продукт.
Обратите Внимание: Положите дополнительные 1/3 времени на планирование, как в статической части сайта.
Продажа времени сдачи проекта
Даже если вы можете примерно установить время сдачи проекта, ваш босс или клиент может не быть так доволен этим, как вы. Иногда слишком оттянутое время сдачи может сорвать сделку. Конкуренты предложат сделать все быстрее за время, за которое они это сделать не смогут, и просто опоздают со сдачей проекта. Так как можно установить время сдачи такое, чтобы босс/клиент были бы довольны и в которое вы бы уложились?
Вот здесь вам предстоит продать себя и время сдачи проекта. Что вы можете предложить клиенту? Почему требуемое вам время правдоподобное и такое, какое оно есть? Представьте себе, что вы — клиент, и подумайте, чего вы ожидаете от потенциального программиста?
Некоторые моменты, которые мне показались важными:
Вы сделаете все правильно. Другие программисты могут сделать немного быстрее, но вы сделаете все правильно, что сэкономит деньги и время клиента в будущем (особенно верно это если вам платят за время).
Вы придерживаетесь своего слова. Время, которое требуется вам — это время, за которое вы все сделаете. Другие программисты могут пообещать сделать все быстрее, зная, что они не успеют.
Приведите рекомендации клиентов. Ваши предыдущие работы говорят сами за себя, довольные клиенты могут дать рекомендации, которые вы сможете показать своему боссу/клиентам.
Будьте внимательны! Прогоните свои предложения через проверку орфографии и рамматики. Добавьте диаграммы и четко объясните все, что собираетесь делать и сколько займет каждая часть работы.
Сообщите разброс. Не просто говорите клиентам, сколько по вашему мнению это займет времени — сообщите им о разных вариантах. Сколько в лучшем случае? Сколько в худшем
Когда вы получаете недостаточного времени
Иногда вы не получаете достаточного времени на проект: Когда такое случается, у вас по прежнему есть множество вариантов (не столь очевидных на этот раз).
Общайтесь! Общение между вами и клиентом — наиболее важная возможная вещь. Постоянно информируйте их о том, что вы сейчас делаете. Если вы поймете, что нужно немного отодвинуть дату сдачи, в случае если вы общались с клиентом, это может оказаться не так сложно.
Покажите боссу/клиенту незавершенную версию веб-сайта. Часто, когда вы просите дать побольше времени, бывает полезно продемонстрировать уже проделанную работу, показать, что вы работаете над проектом.
Во всем виновата Канада: Примите на себя полную ответственность за задержку, но не забудьте упомянуть все внешние факторы, которые заставили вас опоздать.
Срезать углы: Это отвратительно, но если вы действительно опаздываете со сдачей проекта срежьте несколько углов, чтобы получить работающую версию приложения клиенту, с четко обозначенными недоработками, а затем, во время тестирования, вернитесь и доделайте их.
Используйте GUI: Если я создаю приложение я предпочитаю писать HTML вручную, я не доверяю HTML, сгенеренному редакторами WYSIWYG. Тем не менее, если времени не хватает, можно рассмотреть и такой вариант.
Обобщение
Программирование методом «вырезал и вставил» — неправильный метод: Обычно «вырезать и вставлять» чужой код в свои приложения — не очень хорошая идея. Хорошо, если вы возьмете из их кода общие идеи и алгоритмы и интегрируете их в свой. Аналогично, можно использовать их код, как «библитеки». Но не копируйте чужой код вслепую.
Нет принципов написания кода: Создание принципов написания кода важно для любого проекта. Это позволяет привести к единому стилю код программ, организацию и документацию. Это помогает избежать дальнейшего непонимания.
Не Проверяется Код: Всегда кто-нибудь должен посмотреть на ваш код. Другой человек может заметить такие вещи, как неоптимизированные SQL-запросы, неоптимальный код, дыры в системе безопасности и другие ошибки.
Затыкание дыр в проекте: Не затыкайте дыры. Если видите, что что-то (если не все) изначально спроектировано неправильно, лучше все переделать. В будущем вы будете вознаграждены.
Исключение Пользователя Из Разработки Дизайна: Никогда не исключайте пользователя из разработки дизайна. После всего, что сказано и сделано, они будут теми судьями, которые оценят полезность вашего приложения. Работайте с ними во время разработки приложения и вы избежите будущего непонимания.
Отступление от Технического Задания: Не делайте «все сразу». Заложите время на планирование приложение. Пример простого плана проекта включает Стадию Анализа Требований, Стадию разработки Дизайна Программы и Стадию Тестирования.
Потеря Во Времени: Выделяйте достаточно времени на проекты! Необходимость торопиться перед сдачей проекта приведет к тому, что вы совершите какую-то из предыдущих 20 ошибок. Не потеряйтесь во времени.
Автор: Стерлинг Хьюз