21 ошибка программиста PHP
Написав    Понеділок, 03 вересня 2007, 03:57    PDF Друк e-mail
Розробник

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

Введение

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

Как правило, дело в недостаточной практике программирования. Неопытные программисты становятся перед лицом необходимости создания сложных веб-приложений. Поэтому сплошь и рядом допускаются ошибки, которых избежал бы опытный программист, такие как необоснованное использование функции printf() или неправильное использование семантики PHP.

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

Часть 1: Описываются 7 "детских" ошибок (21-15 в обратном порядке, в соответствии со степенью серьезности по нашей классификации). Такие ошибки не вызывают серьезных проблем, но приводят к уменьшению эффективности работы программы, а также выражаются в громоздком трудночитаемом коде, в который, к тому же, трудно вносить изменения.

Часть 2: Следующие 7 ошибок (14-8) относятся к "серьезным". Они ведут к еще более значительному уменьшению скорости выполнения кода, уменьшению безопасности скриптов; код становится еще более запутанным.

Часть 3: Описания семи последних, "смертельных" ошибок. Эти ошибки концептуальны по своей природе и являются причиной появления ошибок, описанных в 1-ой и 2-ой частях статьи. Они включают и такие ошибки, как недостаточное внимание, уделенное как проекту в целом, так и коду программы, в частности.

{mospagebreak title=7 "детских" ошибок}

21. Неоправданное использование функции printf()

Функция printf() предназначена для вывода форматированных данных.

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

Ниже приведен пример обоснованного применения функции printf(). В данном случае она используется для форматированного вывода числа "пи":

<?php
printf ("Число Пи: %2f\n<br>\n", M_PI);
printf ("Это тоже число Пи: %3f\n<br>\n", M_PI);
printf ("И это Пи: %4f\n<br>\n", M_PI);
?>

Примечание: Наблюдаются случаи патологической боязни функции printf(), когда люди пишут свои функции форматированного вывода, порой по 30-40 строк, хотя все проблемы мог бы решить один-единственный вызов функции printf().

Многие программисты используют функцию printf() для вывода переменных, результатов вызова функций, а иногда даже обычных текстовых данных. Наиболее часто это происходит в следующих двух случаях:

  • когда следовало бы использовать функцию print();
  • при выводе результатов, возвращаемых функциями.

Когда следует использовать print()

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

<?php
$name = 'Sterling Hughes';
$job = 'Senior Engineer';
$company = 'DesignMultimedia';
$email = ' Ця електронна адреса захищена від спам-ботів, Вам потрібно включити JavaScript для перегляду ';
printf ( "Меня зовут %s\n<br>\n
Я работаю %s, %s\n<br>\n
Мой адрес E-mail:%s\n<br>\n",
$name, $job, $company, $email );
?>

В данном случае возможно (и желательно!) применение print():

 print "Меня зовут $name\n<br>\n
Я работаю в $company, $job\n<br>\n
Мой адрес E-mail: $email\n<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 заключена в двойные кавычки, что однозначно определяет ее как строку, тогда как функция 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<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 "\t\t\t
<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>

Затем пишем программу, просматривающую код шаблона и при выводе заменяющую тэги вида %%SOME%% нужной информацией.

Плюсы использования шаблонов:

  • Предельно просто и ясно
  • Для изменения шаблонов не требуется знание 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)". (Эх, где бы их еще найти! ;)) - прим. переводчика)
{mospagebreak title=7 ошибок (14-8) относятся к "серьезным"}

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.

Чем, на их взгляд, они занимаются?

Код, основанный на методе научного тыка, говорит о недостаточно ясно определённой организации работы с БД. Те, кто прилагают все свои усилия на написание кода, а не на написание правильного кода, рискуют больше потерять, чем заработать. Некорректная выборка данных - яркий тому пример. Некоторые программисты не уделяют достаточно времени на тщательное продумывание этого момента. Естественно, в реальной жизни может и не оказаться того "единственно верного" способа выборки данных, но всегда найдётся тысяча "неверных", это точно.

Ошибки в организации выборки данным можно разделить на три класса:

Неправильное использование функций обращения к БД

Один из 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] &amp; 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. Слабая устойчивость к ошибкам

В природе существует огромное количество скриптов абсолютно не справляющихся с пользовательскими ошибками. Своим появлением такие скрипты обязаны программистам, которые не удосуживаются правильно распланировать будущий проект и определить все места возможных ошибок. Причём этим следует заняться до того, как скрипт был написан. Недоработки подобного рода приводят к сбоям программы, что чревато не только получением некорректных результатов, но и падением системы!

Предусмотреть худшее

Любой скрипт может "свалиться" при наступлении каких-либо "критичных" условий. Чтобы свести такой риск к минимуму всегда нужно:

Проверка результатов вызова функций

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

В приведённом ниже примере на шестом витке цикла возникает ошибка "деление на ноль", поскольку $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);

print
"Ваша процентная ставка за $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);

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

    <?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():

Примечание: дезактивация проходит путём подстановки бэкслэша ("\") перед потенциально небезопасными для системы символами (а именно: #&;?'\"|*?~<>^()[]{}$\\\x0A\xFF).

 

<html>
<head>
<title>Поиск аккаунта</title>
</head>
<body>
<h1>Поиск аккаунта</h1>
<?php
if ($name) {
    
system (EscapeShellCmd ("lookup $name"));
    print
"<br>\n\n";
}
?>
<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, а не так, как это было бы правильно для другого языка.
  • Недостаточное внимание к вопросам безопасности: Всегда учитывайте "человеческий фактор". Не вставляйте в системные запросы непроверенные строки данных. Проверяйте адреса электронной почты. Не полагайтесь на алгоритмы при приёме кредитных карточек.
{mospagebreak title=7 последних, "смертельных" ошибок}

7. Программирование методом "вырезать-вставить": неверный подход

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

И если при всех оптимальных условиях код и будет работать, то при настоящей проверке обязательно свалится. Причём латание "дыр" никогда не сделает ваш код:

  • расширяемым: программа будет выглядеть как набор обрывков кода сляпанных вместе. Попросите опытного программиста что-либо изменить в таком скрипте, и он почти наверняка предпочтёт написать свой. Нечитаемый код - нерасширяемый код.
  • безопасным: вы вставляете в свой проект код чужого человека; при этом вы точно не знаете, что же именно код делает. Задумайтесь над этим. А что если код содержи подложный системный вызов, который сносит все файлы с вашего жёсткого диска? Кроме того, один и тот же код не может быть одинаково защищён и безопасен для разных систем. Ну и, наконец, вы просто копируете чужие ошибки.
  • быстрым: если вы собираете код из кусочков разных скриптов, не ждите от него быстрой работы. Ибо в этом случае логическое развитие скрипта попросту отсутствует; а всем известно, что в основе быстродействия скрипта лежит его логика.

Правильный подход: изучить, потом скопировать

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

Библиотеки: то, что вам нужно

Используйте библиотеки PHP-функций только из надёжных источников, как, например, PEAR или PHP Classes Repository. Дополнительная функциональность, которую предоставляют вам API из этих библиотек, пойдут вашему проекту только на пользу. Итак, если вы нашли уже готовую библиотеку нужных вам функций (из надёжного источника), то её использование только приветствуется.

6. Отсутствие в проекте технических директив

Как-то раз, в самом начале моей карьеры, я работал над одним капитальным проектом (на PERL) в команде с тремя другими программистами. Поскольку я был ещё молод (и к тому же не был руководителем проекта), никаких технических директив (ТД) у нас не было. Каждый взял свою часть проекта и работал над ней в одиночку. Когда же мы стали собирать проект, наши части сильно разнились по своему стилю.

Например, один из моих коллег в названии переменных и функций использовал БиКапитализацию. Я использовал underscore ("_"). Руководитель проекта избрал самый оригинальный стиль и использовал оба метода в зависимости от настроения (что, собственно, и вызвало конфликты в пространстве имён; всем известно, какая это головная боль для разработчика).

В общем, это был не проект, а свалка. Понадобилось 20 часов дополнительного времени; хотя ничего бы подобного не случилось, если бы мы удосужились составить чёткие ТД для своего проекта.

ТД описывают структуру и внешнее представление исходного кода проекта, определяют методы и стратегию реализации конечного продукта.

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

ТД - это пакет соглашений, которые должны выполняться независимо от личных предпочтений программистов. В ТД фиксируются решения по всем спорным моментам, как например:

  • какие переменные объявлять глобальными, и как они будут дифференцироваться от локальных переменных.
  • Структуру хранения документов, то есть правила, в какую директорию поместить тот или иной файл: src или lib.
  • Стиль и содержание комментариев.
  • Правила документирования.
  • Ширина строк.

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

Пример ТД проекта

Для лучшего понимания, попробуем составить образец ТД: без подробностей, только основные пункты. Естественно, все необходимые элементы будут представлены, но представлены скорее схематично: настоящие ТД могут свободно занимать 10-40 страниц. Стоит заметить, что для каждого нового проекта не обязательно писать новые ТД с нуля: достаточно просто обзавестись готовым шаблоном и при необходимости вносить в него изменения.

ТД по представлению проекта DesignMultimedia.com

Итак, приведём пример ТД по внешнему представлению проекта. Разработав подобный документ, вы можете сосредоточиться только на непосредственной реализации проекта и не беспокоиться более о несвязности исходников, конфликтов именования или о структуре сайта, если вы разрабатываете свой сайт.

Введение

Эта часть определяет следующие моменты:

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

Файловая структура (на примере DesignMultimedia.com)

<Здесь описываются правила хранения файлов в приложении (то есть какой файл куда идёт), а также соглашения по именованию файлов.>;
Пояснения по структуре:
<Здесь приводятся пояснения по всем указанным выше правилам и соглашениям, чтобы исключить любое неверное истолкование.>;

Колонтитулы

Каждая страница исходного текста проекта содержит следующий заголовок:
<Здесь указывается заголовок. Он может включать в себя всё что угодно: от образца кода до простого копирайта.>;
Например, все .c и .h файлы проекта PHP4 имели следующий стандартный заголовок:

/*
+-----------------------------------------------------------------------+
| PHP версия 4.0 |
+-----------------------------------------------------------------------+
| Copyright (c) 1997, 1998, 1999, 2000 The PHP Group |
+-----------------------------------------------------------------------+
| данный файл с исходным кодом подпадает под действие лицензии PHP |
| версии 2.02, копия которой хранится в данном пакете программ в файле |
| LICENSE; также данную лицензию вы можете получить по адресу в WWW |
| http://www.php.net/license/2_02.txt. |
| Если вы не получили копию лицензии PHP и не имеете возможности |
| получить её через WWW, просьба сообщить об этом по адресу |
| Ця електронна адреса захищена від спам-ботів, Вам потрібно включити JavaScript для перегляду и лицензия будет незамедлительно выслана. |
+-----------------------------------------------------------------------+
| Авторы: Sterling Hughes < Ця електронна адреса захищена від спам-ботів, Вам потрібно включити JavaScript для перегляду > |
+-----------------------------------------------------------------------+
*/

И нижний колонтитул:
<Здесь помещаете свой стандартный нижний колонтитул; в проекте PHP4 во всех файлах .c и .h можно увидеть следующее:>;

/* 
* Локальные переменные:
* ширина табуляции: 4
* основной отступ C: 4
* Окончание:
*/

И если потребуется, пояснения.
<Здесь приводятся пояснения по содержанию верхнего и нижнего колонтитулов, возможно, причины их наличия (всё это зависит от содержания колонтитулов).>;

Документация

<В этой части определяется стиль документирования кода в приложении: будет ли он оформлен в виде javadoc или XML Docbook.>;

Стиль комментариев

<Здесь описывается стиль комментариев с разъяснением сокращений или тех или иных "фраз".>;

Правила сборки проекта

<Здесь даются правила в виде запретов или директив, например: "Не хранить разные классы в одном файле" или "Хранить каждый класс в отдельном файле".>;

Именование переменных

<Здесь вы можете написать примерно следующее:>;
В именовании переменных данного проекта должны соблюдаться следующие правила:
[1] В именах классов используется бикапитализация.
[2] Имена функций пишутся строчными буквами, для разделения слов используется underscore ("_").
[3] Прочие, более конкретные правила.

5. Отсутствие экспертной оценки программы

Идея добавить этот пункт появилась у меня во время подобной проверки, о которой попросил меня друг. Изучив его код, я был в состоянии сократить количество переменных на треть (что давало 400% увеличение скорости обращения к базе данных), количество строк в программе примерно вдвое, кроме того, внести ещё множество улучшений с общим результатом 1000% увеличение скорости (то есть в десять раз быстрее).

Мораль? Мораль в том, что качество, скорость и защищённость вашего кода возрастёт неимоверно, если у вас под рукой всегда есть другой опытный программист, который изучит ваш код. Другой человек заметит ошибки, о которых вы даже и не знали, найдёт более простые пути решения той или иной задачи. Кроме того, ему легче найти в вашем коде места, сильно замедляющие работу PHP или представляющие собой потенциальные "дыры".

Одна из причин небывалого успеха проекта PHP, языка с открытыми исходниками, - это то, что код видело множество людей. Тысячи людей бесплатно проверяли PHP на наличие ошибок, на потенциальные сбои, на несовместимость, на скорость работы и т. д. Таким образом, к появлению новой версии PHP, код просмотрело как минимум 2-3 очень опытных программиста.

В идеале, проекты среднего/крупного масштаба просматривают, по меньшей мере, два "приглашённых со стороны" программиста. Как и в любом другом виде творчества, свежий взгляд всегда очень кстати. Однако в большинстве случаев люд обходятся одним проверяющим.

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

Нередко бывает полезно составить небольшой список вопросов к человеку, просматривающему код. Вот несколько возможных вопросов:

  • Какова цель кода XXX?
  • Как файл XXX вписывается в остальную часть проекта?
  • Каков алгоритм нейтрализации ошибок?
  • Можем ли мы отследить действия среднего пользователя данной программы?
  • Где пользователь программы может ошибиться?

4. "Латание" проекта

Допустим, вы написали приложение и приходите к мысли, что некоторые вещи стоит переделать. "Латать" - значит писать патчи ("заплатки") вместо того, чтобы изучить причину возникновения ошибки и устранить её.
Если вы допустили подобную ошибку и понаписали патчей, то получите криво работающий код, компромисс и в скорости и в безопасности.

Показатели недоработок проекта

Естественно, при первоначальном проектировании приложения, вы считаете, что ваш ход мыслей - самый правильный. И понять, что что-то пошло не так, вы можете в самый последний момент, когда приложение (а также его части) написано почти до конца.
Существуют два показателя того, что план проекта не состоялся:

  • Слишком частая реанимация кода: программа постоянно "сваливается" и вы находите различные способы заставить её работать. Однако это никак не вписывается в изначальный план проекта. С другой стороны, хотя это и не лучший выход из положения, но в рамках уже составленного вами плана это лучшее, что можно найти.
  • Слишком сложная реализация. Для решения простых задач выполняются сложные операции. В приведённом ниже примере для вывода строки используется цикл for:

    <?php
    $GeorgeBush
    = "Если вы посмотрите на рыночные тенденции, я думаю, вы увидите, что большая часть импорта в США приходит из-за рубежа";
    for (
    $idx = 0; $idx < strlen($GeorgeBush); $idx++) {
        print
    $GeorgeBush[$idx];
    }
    ?>

    Этот код хорошо написан; он циклом проходит по строке и правильно выводит знаменитую фразу Джорджа Буша. Правильный синтаксис, правильная вложенность. И, тем не менее, тот же результат мы могли бы получить при простом выводе целой строки (print()).

    Исправление недостатков программы

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

    В большинстве случаев бывает весьма полезно получить мнение независимого эксперта: просмотрев и изучив код, он сможет адекватно оценить масштаб недоработок.

    Рассмотрим три категории ошибок:

    • Небольшие, локализованные ошибки: иногда ошибки в вашей программе не так критичны для её функионирования и внесение исправлений не оправдывает затраченные деньги и время.
    • Как исправить: в этом случае необходимо как-либо задокументировать факт наличия ошибки. Когда вы решите изменить или обновить ваше приложение, заодно вы исправите и эту ошибку. Примером ошибки подобного класса может послужить неправильная организация данных в отдельной части приложения (простой массив там, где логичнее было бы использовать ассоциативный; стек там, где дерево более уместно).
    • Значительные, но локализованные ошибки: иногда же получается так, что вы действительно вынуждены переписать часть приложения. Например, если вы пишете свою ОС, в Диспетчере Окон может быть уйма ошибок, в то время как остальной код в полном порядке.
    • Как исправить: всё, что вам нужно - это перепроектировать именно Диспетчер Окон (а не всю ОС). Это, наверное, самый распространённый случай в практике программирования: отдельные модули содержат ошибки, но в целом структура проекта выполнена корректно.
    • Значительные, обширные ошибки: наконец, крайний случай, когда ошибки содержатся в реализации самой инфраструктуры проекта.
    • Как исправить: если неверно спроектирована сама инфраструктура приложения, обычно необходима переорганизация на уровне взаимодействия различных модулей программы. Это, безусловно, самая трудоёмкая работа, отнимающая уйму времени, и к ней редко прибегают, если речь идёт о проектах, находящихся на последних стадиях реализации. Примером таких ошибок могло бы стать использование простых файлов для хранения всей информации в такой огромной поисковой системе как Yahoo.

    3. Исключение конечного пользователя из процесса разработки

    Ситуация: вы получили задание по разработке корпоративного приложения специально для вашей компании. Вы потратили часы на поиск и документирование тонны требований, определили стоимость проекта, поставили задачи и выполнили их. Через три месяца вы приносите законченный проект только чтобы услышать в ответ:

    • "Это не то, чего бы мы хотели".
    • "Наши требования изменились".
    • "Да, но...".
    • "Ммм, какая программа?" (Ваш изначальный заказчик уже давно уволился из компании!!!!).

    Случалось ли подобное с вами?

    Последнее слово в оценке вашего приложения всегда остаётся за пользователем. По определению именно он будет использовать ваше приложение (и, конечно, много раз пытаться использовать его криво). Многие программисты пишут вещи, которые сами по себе являются шедеврами, но, тем не менее, не отвечают требованиям пользователей. И часто это случается из-за одного или нескольких "недопониманий".

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

    • непрерывная обратная связь
    • создание макетов
    • бета-тестирование

    Непрерывная обратная связь

    Бенджамин Франклин в своём "Альманахе Бедного Ричарда" писал: "Один стежок вовремя девяти стоит". То же правило применимо и в разработке приложений. Если вы действительно хотите завершить проект как можно раньше, очень важно поддерживать связь с пользователем и интересоваться его мнением о вашей программе. Что им нравится? Что им не нравится? Как можно улучшить приложение?

    Создание макетов

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

    Проверяйте приложение на соответствие требованиям, но не обделяйте вниманием и прямые отзывы самих пользователей. Кроме того, многие программисты тестируют приложение только после того, как оно уже было написано и собрано. И совершают ошибку, ибо это может провалить вам весь проект: слишком велики расхождения между тем, чего хотелось бы пользователю и тем, что вы сделали. К тому же, очень часто пользователи только и понимают, что им действительно нужно, только имея перед глазами уже сколько-нибудь осязаемый образец. Короче говоря, требования пользователя к проекту постоянно меняются в ходе разработки (хотя многим программистам больше понравилось бы обратное).

    Есть способ избавить себя от этой головной боли: разбить весь процесс разработки приложения на несколько этапов. По завершении каждого этапа обдумайте следующее:

    • Работа, которую вы проделали на данном этапе, приносит ли она какой-либо полезный для пользователя результат?
    • Остались ли какие-нибудь вещи, которые пользователь хотел бы увидеть в вашем приложении, но до сих пор в проекте не представленные?
    • Будет ли задействована дополнительная функциональность, которая была добавлена на данном этапе?
    • Какова "чистая прибыль" в функциональности вашего приложения?
    • Проделанная вами работа улучшила или ухудшила приложение?

    Как разбивать работу на этапы?

    Обычно хорошей практикой считается разграничение этапов, когда завершена какая-либо значительная порция пользовательского интерфейса.

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

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

    Бета-тестирование

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

    2. Отсутствие плана

    В наше время многие проекты пишутся по принципу "главное начать, дальше само пойдёт". Например, один из моих первых проектов я написал, основываясь на 40-минутном разговоре по телефону. И хотя всё работало, риск того, что оно могло "упасть", был очень велик. Естественно, если бы я всё продумал и распланировал до того, как сел кодировать, риск появления сбоев в работе был бы намного меньше.

    Например, абстракция в проекте была почти нулевая. То есть, если бы заказчик пожелал вместо MSSQL использовать MySQL, пришлось бы переписывать весь проект целиком. Кроме того, вся система защиты была сделана кое-как. Все данные хранились в "куке". Была ещё парочка недочётов, слишком незначительных, чтобы описывать их подробно...

    В своей работе "The Mythical Man Month", посвящённой процессу разработки программного обеспечения, Фред Брукс описал оптимальный план работы над приложением следующим образом:

      1/3 Проектирование: вы разрабатываете концепцию функционирования. Что включают в себя разные модули приложения (или части этих модулей)? Как они взаимодействуют? Что должно делать приложение в целом? Ответы на эти вопросы служат основой для всей разработки системы.
    • 1/6 Кодирование: это то, что мы все просто обожаем делать. Проводим концепцию в жизнь.
    • 1/4 Тестирование отдельных модулей и общее тестирование на ранней стадии разработки. При создании больших систем наступает такой момент: приложение ещё не готово, но уже доведено до той стадии, когда можно уже тестировать его основную функциональность.
    • 1/4 Тестирование всей системы: это последняя стадия, где мы уже имеем на руках готовое приложение: теперь его надо проверять и перепроверять, чтобы оставить в нём как можно меньше "багов".

    Однако на сегодняшний день мы уже счастливы, если первой части (проектированию) посвящается ну от силы 1/6 всего времени. Чаще всего программисты яростно бросаются набивать код, даже не имея в голове ясной идеи, что им нужно написать, без взвешенной оценки задачи и уж конечно без готовых путей её решения. Такое их поведение можно сравнить с попытками написания курсовой работы без составления плана.

    Кодирование должно быть простым изложением того, что вы уже спроектировали, не более того. Многие программисты доходят до того, что пишут всё приложение (или наиболее мудрёные места) в псевдокоде и только потом кодируют проект на реальном языке, как, например, PHP.

    Примечание: проектирование того, как будет выглядеть, работать и "думать" ваше приложение, также известно как информационная архитектура.

    Этапы разработки проекта

    На эту тему можно говорить бесконечно много. Достаточно перечислить: отладка, составление блок-схем, макетирование, управление проектом и определение сроков. Однако ограничимся минимальным набором общих рекомендаций.

    Итак, образцовый план проекта включает в себя:

    • этап изучения требований
    • этап разработки
    • этап тестирования

    Этап изучения требований

    Полный анализ требований к программе - это первый шаг в планировании приложения; вы определяете, что именно вам нужно сделать. Вы подробно и точно расписываете, что будет делать программа и как она будет работать. Это один из принципиальных моментов разработки.

    Определение пользовательских требований

    Так как же выяснить, что именно ваш заказчик хочет от приложения? Станьте на время консультантом по определению потребностей заказчика и, в частности, постарайтесь получше узнать, что он собой представляет. Например, вас должно интересовать следующее:

    • Чем они занимаются?
    • Что делает их продукт лучше других (или уникальным)?
    • Как они хотели бы представить себя целевой аудитории?
    • Какие особенности/свойства их сайта помогут им достичь своего рынка?

    Последний из перечисленных вопросов может помочь вам в расширении проекта. Вы можете предложить дополнительную функциональность, и таким образом сделать их сайт лучше? Если да, то ваш заказчик будет просто счастлив, а вы получите на руки больший проект (а он и стоит больше).

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

    Определение технологических требований

    На данном этапе вам необходимо определить, какие технологии будет использовать ваше приложение. Ключевой вопрос: сможем ли мы выполнить требования заказчика, располагаем ли мы техническими и технологическими возможностями для этого? При этом вы должны учитывать язык, на котором реализовано приложение, ОС платформы, скорость сервера и соединения с ним и многое другое.

    Этап разработки

    Итак, спецификации готовы. На данном этапе вы должны решить, как будет реализовано приложение, что когда в нём будет происходить. Можете сделать даже несколько набросков в псевдокоде, если какие-либо места покажутся вам слишком запутанными. Другими словами, вам будет нужно:

    • смоделировать приложение
    • проиллюстрировать его
    • сделать наброски в псевдокоде

    Смоделировать

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

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

    Если данные не отправлены, то нам нужно отправить пользователю страницу с пустой формой. В противном случае, необходимо (ещё раз) произвести одно из двух следующих действий:

    • если полученные данные корректны (т. е. соответствуют критериям корректных данных), то нужно отослать сообщение адресату, а пользователю отправить страничку с благодарностями.
    • Иначе выдаём сообщение об ошибке с указанием того, что было не так и предоставляем возможность всё исправить.

    Проиллюстрировать

    Ниже представлена очень несложная диаграмма, иллюстрирующая работу приложения. Диаграмма выполнена в Microsoft Visio(r) и идеально подходит для приложения по простой отправке сообщений.

    Псевдокод

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

    Для примера вернёмся к нашему скрипту для отправки сообщений:

    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 (хотя и мог бы им быть). Миссия псевдокода - определить задачи и, возможно, подвести под них теоретическую основу. Уровень абстракции псевдокода зависит только от вас. Лично я склоняюсь в сторону менее абстрактного кода. Всё зависит от того, насколько комфортно вы чувствуете себя в программировании.

    Наконец, когда вы распланировали всё приложение, вы можете начать кодировать: вам уже известны все шаги, которые будет нужно предпринять; известно и то, что в конечном счёте нужно получить.

    Этап тестирования

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

    Скажем честно: программисты ненавидят тестирование. Это, наверное, самое нудное и раздражающее занятие в программировании. Тестирование - это часы и дни погони за неизвестными багами, отладка, проверка всех возможных комбинаций и ситуаций, - и всё для того, чтобы убедиться, что программа в большинстве случаев работает правильно. И в довершение всего, код без багов вы всё равно НЕ ПОЛУЧИТЕ! Всё равно, что-нибудь да ускользнёт от вашего внимания. И вещи, которые (вы уверены!) не должны случиться, обязательно случаются.

    Регрессивные испытания

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

    Нагрузочные испытания

    Ура, ваше приложение отлично работает с одним пользователем: все вроде бы получается и происходит с огромной скоростью. А что будет, если с приложением одновременно заработают 20 пользователей? А 30? Или даже 100? Насколько быстро работает ваш проект теперь? Не стал ли выдавать странные ошибки?

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

    Но это не означает, что всю работу по тестированию можно отфутболить пользователям (просто выложить всё на сайт и ждать отзывов и писем о найденных ошибках, ну вы знаете...). Проведите бета-тестирование (как описано в предыдущей главе). Кроме того, существуют специальные сервисные программы, эмулирующие большой наплыв пользователей. На ум сразу приходит AB от Apache. AB или Apache Benchmark (Анализатор Производительности Apache) делает заданное количество запросов к странице и выдаёт количество успешных запросов, количество неудавшихся запросов, среднюю скорость и т.д.

    Настоятельно рекомендуется проверять ваши web-проекты с помощью AB (если, конечно, вы приняли бесконечно верное решение использовать Apache-сервер). Так вы сможете обнаружить и оптимизировать страницы, "съедающие" память, или загружающиеся слишком долго. (Также, подумайте о приобретении замечательной утилиты по кэшированию от Zend - The Zend Cache).

    1. Сорванные сроки

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

    Сильно не пугайтесь - все мы оптимисты. Вполне естественно полагать, что реализация проекта займёт ровно столько времени, сколько она должна занять. И ни один сбой в работе не берётся в расчёт. Более того, нам и в голову зачастую не приходит, что у нас могут возникнуть ну хоть какие-нибудь проблемы с реализацией чего-либо.

    Итак, давайте возьмём себя в руки, подавим этот инстинкт и рассчитаем временные затраты по-другому. Из моей личной практики: когда я берусь за новый проект, я учитываю все факторы, которые только могу вспомнить, оцениваю время для каждого, всё суммирую. Затем полученный результат я удваиваю. Неаккуратно? Я бы так не сказал: заказчики очень довольны, когда вы приносите готовый проект до положенного срока (и наоборот: если вы опаздываете, они совсем не рады этому).

    Такой "перестраховочный" метод определения временных затрат помогает мне проделать качественную работу по проекту и уложиться в указанные мной сроки. Но, конечно, здесь всё зависит от точности первоначальных расчётов.

    Каждый программист недооценивает (намного реже - переоценивает) время, необходимое на разработку проекта. При этом каждый имеет свой постоянный коэффициент допуска возможных ошибок. Для меня это 2, для кого-то это 1.5, а у кого-то и все 3. Вся хитрость состоит в том, чтобы найти свой личный коэффициент и впоследствии вносить поправки в расчёты.

    Недооценка временных затрат на разработку проекта чревато следующими последствиями (слишком хорошо знакомыми мне):

    • Вы принесёте в жертву проекту всё выше личное время. День и ночь вы будете проводить в работе над проектом, а для отдыха времени не останется (не то чтобы я хочу сказать, что девелоперство это плохо, но программирование в режиме нон-стоп - это невыносимо).
    • Вы будете торопиться. Это означает, что вы будете заботиться не столько о читаемости, защищённости и скорости работы проекта, сколько о том, чтобы выложить готовый проект в срок.
    • В спешке вы пропустите такие важные этапы, как проверка кода или отладка. Это может не понравиться заказчику и добавить вам работы.
    • Может быть, вам придётся прибегнуть к сторонней помощи. Это стоит денег и далеко не всегда помогает (согласно закону Брукса, особенно при разработке капитальных проектов).

    Обязательно выделяйте достаточно времени на длительную отладку (столько же, сколько вы выделяете на весь этап кодирования или даже больше) и не менее 1/3 всего времени на планирование. Эти два этапа являются определяющими этапами для всей разработки проекта.

    SterlingWonderfulToyland.com: Пример

    Рассмотрим в качестве примера простой проект: сайт SterlingWonderfulToyland.com. Это самый обычный интернет-коммерческий сайт. Его задача - продажа через интернет игровых приставок и видеоигр по сниженным ценам; количество наименований ограничено.

    Будучи приверженцем старых и проверенных способов продаж, владелец сайта Джон Джузеппе хочет пригласить посетителей зайти на склад и самим выбрать товар. Таким образом, сайт можно логически разделить на две части:

    • сама система заказов;
    • html-часть сайта, приглашающая посетителей зайти на склад и дизайн самого склада.

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

    Html-часть

    Для простой, статической части сайта я бы взял за основу время, необходимое для вёрстки одной страницы в каком-нибудь редакторе, например Macromedia Dreamweaver(c). И поскольку динамические элементы в данной части отсутствуют, для определения общих временных затрат можно просто перемножить время вёрстки страницы на количество страниц (при условии, что дизайн страниц уже разработан). Затем я бы удвоил полученный результат (поскольку мой личный коэффициент 2). Вообще, этой части не требуется действительно тщательное планирование (посудите сами: дизайнер даёт вам схему того, как всё должно выглядеть, а ваша задача - наверстать страницы в Dreamweaver).

    Примечание: добавьте ещё интервал примерно с 1/3 всего времени, оно уйдёт на сборку проекта. Например, если вы запланировали разработку скрипта для проверки кредиток за 9 дней, то добавьте ещё 2-3 дня.

    Система заказов

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

    Примечание: добавьте ещё 1/3 всего времени на планирование, как и в части простого html.

    Как преподнести такие сроки заказчику

    То, что вы сумели правильно рассчитать сроки реализации проекта, вовсе не означает, что заказчику они понравятся также как и вам. Иногда, указав слишком большие сроки, вы теряете заказ. Ваши конкуренты дадут более привлекательные сроки лишь для того, чтобы получить этот заказ, а потом просто-напросто не выдержат их. Так что же нужно сделать, чтобы и заказчик был доволен, и вы могли честно выдержать указанные сроки?

    Здесь наступает момент, когда нужно правильно преподнести заказчику свои услуги и свои сроки... Какие сроки вы можете предложить? Что делает ваши сроки выполнимыми и почему поставлен именно такой срок, а не какой-либо другой? Нередко помогает мысленный разговор с заказчиком: представьте себя на его месте и подумайте, чего было бы справедливо ожидать от потенциального исполнителя заказа?

    Вот некоторые из аспектов, которые показались мне наиболее важными:

    • Делайте качественную работу. Другие могут выполнить работу немного быстрее вас, но ваш козырь в том, что вы сделаете всё правильно и корректно, что, в конечном счете, сэкономит заказчику время и деньги (особенно сильный аргумент, если вы получаете почасовую оплату).
    • Выдерживайте сроки. Вы даёте сроки и собираетесь выдержать их. Другие могут дать более привлекательные сроки, но далеко не собираются их выдерживать.
    • Предоставьте рекомендации. Ваши предыдущие проекты говорят сами за себя. Соберите с ваших довольных заказчиков различные отзывы и рекомендательные письма с тем, чтобы впоследствии предъявить их потенциальному начальнику/заказчику.
    • Будьте тщательны! Проверьте орфографию и грамматику во всех ваших выкладках. Добавьте схемы и графики, подробно и ясно опишите всё, что вы планируете сделать и сколько времени займёт выполнение каждой из поставленных задач.
    • Предлагайте несколько вариантов. Не ограничивайтесь простым заявлением о том, сколько времени, по вашему мнению, займёт выполнение какой-либо задачи. Дайте несколько возможных вариантов. Как будут развиваться события по самому оптимистичному сценарию? А как по самому пессимистичному?
    • Указывайте наикратчайшие возможные сроки. Чем больше срок выполнения, тем больше вероятность того, что он не будет выдержан.

    Если вы всё же указали слишком короткие сроки

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

    • Поддерживайте связь! Постоянная связь программиста с заказчиком - вряд ли можно придумать что-либо важнее. Постоянно информируйте заказчика о том, что вы в данный момент делаете по проекту. Если вдруг вам понадобится увеличить срок, сделать это будет намного легче, чем, если бы от вас всё это время не было никаких новостей.
    • Продемонстрируйте заказчику/начальнику готовую часть сайта. Демонстрация уже сделанной работы нередко облегчает вам переговоры по выделению дополнительного времени. Покажите, что вы работаете над проектом.
    • Найдите стрелочника. Возьмите всю ответственность за опоздание на себя, но не забудьте указать различные внешние факторы, помешавшие выполнить заказ в срок.
    • Срезайте углы. Это отвратительно и неприятно, но если вы действительно не укладываетесь, придётся срезать несколько углов и выдать заказчику рабочую версию продукта с подробным списком всех багов. Потом, во время тестирования или позже вы вернётесь к ним и исправите.
    • WYSIWYG. При работе над web-проектами я предпочитаю писать html-код руками, ибо очень не доверяю графическим html-кодерам. Однако если ваш проект трещит, то стоит рассмотреть и такой вариант.

    Резюме

    • ПРОГРАММИРОВАНИЕ МЕТОДОМ "ВЫРЕЗАТЬ-ВСТАВИТЬ": НЕВЕРНЫЙ ПОДХОД. Обычно копирование чужого кода - не очень хорошая затея. Здорово будет изучить техники и алгоритмы чужого кода и при случае внедрить в свой проект. Или же использовать готовые библиотеки. Но ни в коем случае не просто слепо копировать чужой код.
    • ОТСУТСТВИЕ В ПРОЕКТЕ ТЕХНИЧЕСКИХ ДИРЕКТИВ. Составление подобных директив необходимо при разработке любого проекта. Директивы позволяют вести проект организованно, в единых для всех стиле и документировании и таким образом, позволит избежать многих возможных "недопониманий" в будущем.
    • ОТСУТСТВИЕ ЭКСПЕРТНОЙ ОЦЕНКИ ПРОГРАММЫ. Пусть кто-то обязательно просмотрит ваш код. Свежий взгляд на проект обязательно выявит такие вещи как неоптимальные SQL-запросы, неправильное применение синтаксиса языка, слишком сложные решения, "дыры" и другие недостатки.
    • "ЛАТАНИЕ ДЫР". Серьёзные недостатки "патчами" не исправить. Если вы видите, что что-то спроектировано неправильно (или не совсем правильно), то в большинстве случаев намного лучше перепроектировать и переписать этот модуль. "И да воздастся вам" впоследствии.
    • ИСКЛЮЧЕНИЕ КОНЕЧНОГО ПОЛЬЗОВАТЕЛЯ ИЗ ПРОЦЕССА РАЗРАБОТКИ. Никогда не делайте этого. Именно пользователи будут судить о пригодности вашего приложения, когда всё будет сказано и сделано. Привлечение пользователей в сам процесс разработки приложения опять же позволит избежать многих возможных "недопониманий".
    • ОТСУТСТВИЕ ПЛАНА. Забудьте про "главное начать, дальше само пойдёт". Выделите время для тщательного планирования. Каждый план проекта должен обязательно включать в себя этапы Изучения Требований, Разработки и этап Тестирования.
    • СОРВАННЫЕ СРОКИ. Выделяёте достаточно времени на реализацию проекта! В своём стремлении уложиться в нереальные сроки, вы только повышаете свои шансы допустить 20 предыдущих ошибок. Не позволяйте себе потеряться во времени.
Останнє оновлення ( Четвер, 03 грудня 2009, 14:43 )
 

Відвідування

mod_vvisit_countermod_vvisit_countermod_vvisit_countermod_vvisit_countermod_vvisit_countermod_vvisit_counter
mod_vvisit_counterСьогодні210
mod_vvisit_counterВчора409
mod_vvisit_counterЗа цей тиждень210
mod_vvisit_counterЗа минулий тиждень2355
mod_vvisit_counterЦього місяця4833
mod_vvisit_counterПопереднього місяця6078
mod_vvisit_counterВсього635080

Кількість користувачів: 5
Ваш IP: 54.91.203.233
,
Сьогодні: лист. 18, 2018