Еще одна характерная ошибка использования функции printf() - вывод значения, возвращаемого функцией, как в следующем примере:
printf ("Найдено %d вхождений строки %s", count ($result), $search_term);
?>
Наряду с функцией print() при использовании ее в тех же целях, следует использовать оператор "." В данном случае этот оператор добавляет текст к результату вызова функции:
print "Найдено " .
count ($result) .
" вхождений строки $search_term";
?>
Использование оператора . в паре с функцией print() позволяет избежать использования более медленной функции printf().
20. Неверное применение семантики языка
Многие программисты используют в своей работе PHP, фактически не понимая тонкостей этого языка. Одна из тонкостей - разница между синтаксисом и семантикой PHP.
Синтаксис PHP: Представляет собой набор правил для определения элементов языка. Например, как мы определяем переменную? Ставим знак $ перед ее именем. Как определяем функцию? В общем случае, используя скобки, аргументы и т.п.
Семантика PHP: Представляет собой набор правил для применения синтаксиса. Например, возьмем функцию с двумя аргументами, что определяется ее синтаксисом. Причем в качестве аргументов ей следует передавать переменные строкового типа - это определяется семантикой.
Заметьте: "следует". В языках с четким разделением типов (таких как Java или C) нет понятия "следует" (в общем случае, хотя бывают и исключения). В таком случае компилятор вынудит использовать переменные строго определенного типа.
Языки, в которых отсутствует само определение типов переменных, предоставляют больше гибкости в написании кода. Но, как бы то ни было, в случае неправильного использования семантики для большинства функций 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() ожидает в качестве первого аргумента дескриптор, но не строку. Соответственно, вам следует использовать переменную, которая может содержать дескриптор.
Примечание: В данном случае строковый тип допустим синтаксически.
Для решения проблемы следует просто убрать двойные кавычки:
$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. Недостаточно либо излишне комментированный текст
Плохо документированный текст программы является признаком эгоистичного программиста. Результатом попытки анализа вашей программы с целью внесения улучшений будет только головная боль. Причем все программисты считают самодокументированный код хорошим тоном, но сами крайне редко пишут комментарии.
Следует также избегать избыточных комментариев. Это тоже встречается очень редко, и, опять же, создает трудно читаемый исходный код. Следующий пример это иллюстрирует:
$age = 18; // Возраст равен 18
$age++; // Увеличим $age на один год
// Напечатаем приветствие
print "Вам сейчас 19 лет, и это значит, что Вам уже было:";
print "n
n
n";
// Цикл "для" чтобы вывести все
// предыдущие значения возраста
for ($idx = 0; $idx < $age; $idx++)
{
// Напечатаем каждое значение возраста
print "$idx летn
n";
}
}
// Конец кода
?>
И все-таки: где золотая середина?
Итак, какой же объем комментариев следует помещать в скрипт?! Это зависит от многого: от времени, которым вы располагаете, от политики компании, сложности проекта и т.д. Тем не менее, запомните несколько основных принципов, которым надо следовать при написании программ вне зависимости от вашего решения:
Перед телом функции всегда помещайте комментарий - назначение функции.
Добавляйте комментарии в сомнительных участках кода, когда нет уверенности, что он будет работать как надо.
Если назначение кода неочевидно, внесите информацию о предназначении этого участка. Вы же потом воспользуетесь этим комментарием.
Избегайте комментарии вида # - используйте только /* */ либо //.
Следующий пример иллюстрирует хороший стиль комментариев:
// 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. Слишком много переменных - слишком большое время выполнения
Некоторые прямо-таки страдают навязчивой идеей вводить временные переменные где надо и где не надо. Совершенно невозможно понять, чем руководствовался человек, написавший такой код:
$tmp = date ("F d, h:i a"); // т.е. формат даты February 23, 2:30 pm
print $tmp;
?>
Для чего здесь использована временная переменная?! Она просто не нужна:
print date ("F d, h:i a");
?>
К сожалению, многие программисты никак не могут избавиться от этой дурной привычки. Использование временных переменных замедляет выполнение программы. Для увеличения скорости кода, где это возможно, лучше воспользоваться вложением функций. Использование временных переменных зачастую увеличивают время выполнения скриптов почти на четверть.
Еще одна причина, по которой следует избегать использования излишнего количества временных переменных, это ухудшение читаемости кода. Сравните два приведенных примера. Какой из них выглядит более элегантно - с использованием временной переменной или без? Какой код проще прочесть? Использование лишних временных переменных ведет к написанию менее читаемого и ясного кода.
Плюсы использования временных переменных
Введение временных переменных позволяет упростить некоторые сложные выражения или вызовы функций. Еще они приносят пользу, когда позволяют избежать многократного вызова функции с одними и теми же аргументами.
Вот пример, в котором не используется лишних переменных:
// string reverse_characters (string str)
// Переворачивает строку символов
function reverse_characters ($str)
{
return implode ("", array_reverse (preg_split ("//", $str)));
}
?>
Вызову функции implode() в качестве одного из параметров передается результат выполнения вложенных функций, поэтому такой код трудно прочесть. В данном случае нам может здорово помочь использование временной переменной:
// string reverse_characters (string str)
// Переворачивает строку символов
function reverse_characters ($str)
{
$characters = preg_split ("//", $str);
$characters = array_reverse ($characters);
return implode ("", $characters);
}
?>
Золотое правило
Если вы думаете, ввести или нет еще одну временную переменную, задайте себе два вопроса:
Будет ли эта переменная использована хотя бы дважды?
Значительно ли улучшится с ее введением читаемость кода?
Если на любой из этих вопросов вы ответили "да", тогда введите временную переменную. Иначе комбинируйте вызовы функций (если это необходимо) и обойдитесь без ее использования.
17. Переписываем стандартные функции
Кое-кто рекомендует переименовывать стандартные функции для того, чтобы программистам на Visual Basic"е проще было перейти к использованию языка PHP:
function len ($str) {
return strlen ($str);
}
?>
Встречаются также рекомендации приступая к программированию на PHP, первым делом заменять имена встроенных функций более привычными.
Существует, как минимум, две причины этого не делать. Во-первых, и прежде всего, мы получаем менее читаемый код. Люди, читающие ваш код, увидят массу очевидно ненужных функций и будут сильно удивлены, почему же вами не использовались стандартные функции PHP.
Ну и, наконец, это замедляет программу. Дело не только в необходимости обработки большего объема кода, но и в том, что для вызова такой пользовательской функции, требуется больше времени, чем для прямого вызова стандартной функции.
Используйте стандартные функций языка!
Иногда так трудно устоять! Ведь программист редко знает сразу весь набор функций - у него обычно нет времени запомнить их все. Почему бы просто не переименовать функцию? Но, повторимся, этого не следует делать в силу изложенных выше причин.
Хорошо бы иметь под рукой справочник по функциям PHP (удобно использовать индексированную версию в формате PDF). И перед тем как написать какую-либо функцию, внимательно посмотреть - не существует ли она уже в списке стандартных функций.
Но следует заметить, что в кодах программ можно встретить пользовательские функции, написанные еще до их введения в качестве стандартных (например, функции сравнения двух массивов). Это не означает, что вы обязательно должны переписать код и заменять их стандартными функциями.
16. Клиентская часть программы не отделяется от серверной части
Многие программисты рекомендуют объединять код HTML (интерпретируемый на стороне клиента) и код PHP (выполняемый сервером) в один большой файл.
Для маленьких сайтов это, возможно, неплохо. Но, когда ваш сайт начнет расти, вы можете столкнуться с проблемами при необходимости добавить какие-либо новые функции. Такой стиль программирования приводит к очень "непослушному" и громоздкому коду.
API функций
Если вы собрались отделить код PHP от HTML кода, у вас есть два варианта. Один способ - - создать функции динамического формирования вывода и поместить их в нужное место на веб-странице.
Например, так:
index.php - код страницы
<html>
<head>
<title></title>
</head>
<body>
<h1></h1>
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="25%">
</td>
<td>
</td>
</tr>
</table>
</body>
</html>
site.lib - Сам код программы
$dbh = mysql_connect ("localhost", "sh", "pass")
or die (sprintf ("Не могу открыть соединение с MySQL [%s]: %s",
mysql_errno (), mysql_error ()));
@mysql_select_db ("MainSite")
or die (sprintf ("Не могу выбрать базу данных [%s]: %s",
mysql_errno (), mysql_error ()));
$sth = @mysql_query ("SELECT * FROM site", $dbh)
or die (sprintf ("Не могу выполнить запрос [%s]: %s",
mysql_errno (), mysql_error ()));
$site_info = mysql_fetch_object ($sth);
function print_header ()
{
global $site_info;
print $site_info->header;
}
function print_body ()
{
global $site_info;
print nl2br ($site_info->body);
}
function print_links ()
{
global $site_info;
$links = explode ("n", $site_info->links);
$names = explode ("n", $site_info->link_names);
for ($i = 0; $i < count ($links); $i++)
{
print "ttt
$names[$i] n
n";
}
}
?>
Очевидно, такой код лучше читаем. Еще одно преимущество использования этой концепции - возможность изменения дизайна без модификации самого кода программы.