Частина перша – 7 "дитячих" помилок

Цільова аудиторія


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


Введення


Одна з найбільш сильних сторін PHP є, одночасно, і його слабкою
стороною: PHP дуже простий у вивченні. Це приваблює багатьох людей, а проте,
незважаючи на його уявну простоту, не так-то просто навчитися використовувати цей
мова правильно і ефективно.


Як правило, справа в недостатній практиці програмування. Недосвідчені
програмісти стають перед обличчям необхідності створення складних
веб-додатків. Тому часто-густо припускаються помилок, яких уникнув би
досвідчений програміст, такі як необгрунтоване використання функції printf () або
неправильне використання семантики PHP.


У цій серії з трьох статей подано найбільш, на нашу думку,
характерні помилки. Ці помилки можна класифікувати по декількох категоріях,
від <некритичних> до <смертельних>. Поряд з аналізом цих помилок,
представлені способи їх уникнення, а також деякі <маленькі хитрощі>,
накопичені за багато років практики програмування.


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


Частина 2: Наступні 7 помилок (№ 14-8) відносяться до <серйозним>.
Вони ведуть до ще більш значного зменшення швидкості виконання коду,
зменшення безпеки скриптів; код стає ще більш заплутаним.


Частина 3: Описи семи, останніх, <смертельних> помилок. Ці
помилки концептуальними за своєю природою і є причиною появи помилок,
описаних у 1-ій і 2-ий частинах статті. Вони включають і такі помилки, як
недостатнє уваги, приділену як проекту в цілому, так і кодом програми, в
зокрема.


21. Невиправдане використання функції printf ()


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


Наприклад, її слід використовувати при необхідності виведення змінної в
форматі з плаваючою комою з певною точністю, або в будь-якому іншому
випадку, коли виникає необхідність зміни формату даних, що виводяться.


Нижче наведено приклад обгрунтованого застосування функції printf().
У даному випадку вона використовується для форматованого виведення числа
<Пі>:

<?php

printf ("Число Пі:% 2f
<br>
", M_PI);
printf ("Це теж число Пі:% 3f
<br>
", M_PI;
printf ("І це Пі:% 4f
<br>
", M_PI);

?>


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

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



Коли слід використовувати print()


Виклик функції printf() найчастіше використовується там, де слід було
б використовувати print(). У наступному прикладі функція
printf() використовується для виведення чотирьох змінних:

<?php

$name = “Sterling Hughes”;
$job = “Senior Engineer”;
$company = “DesignMultimedia”;
$email = “shughes@designmultimedia.com”;

printf ("Мене звати% s
<br>

Я працюю% s,% s
<br>

Моя адреса E-mail:% s
<br>
",
$name, $job, $company, $email );
?>


У даному випадку можливо (і бажано!) Застосування print():

 print "Мене звуть $ name
<br>

Я працюю в $ company, $ job
<br>

Моя адреса E-mail: $ email
<br>
";


Використання print() замість printf() у випадках,
коли виводяться неформатовані дані, як в даному прикладі, дає
наступні вигоди:



Використання функції printf() для виведення значення,
повертається функцією


Ще одна характерна помилка використання функції printf()
висновок значення, що повертається функцією, як у наступному прикладі:

<?php

printf ("Знайдено% d входжень рядка% s", count ($ result), $ search_term);

?>


Поряд з функцією print(), При використанні її в тих же цілях,
слід використовувати оператор "."У даному випадку цей оператор
додає текст до результату виклику функції:

<?php

print "Знайдено".
count ($result) .
"Входжень рядка $ search_term";

?>


Використання оператора . в парі з функцією print()
дозволяє уникнути використання більш повільної функції
printf().


20. Неправильне застосування семантики мови


Багато програмісти використовують у своїй роботі PHP, фактично не розуміючи
тонкощів цієї мови. Одна з тонкощів – різниця між синтаксисом і
семантикою PHP.



Зауважте: <слід>. У мовах з чітким поділом типів (таких як
Java або C) немає поняття <слід> (у загальному випадку, хоча бувають і
винятки). У такому випадку компілятор змусить використовувати змінні
строго певного типу.


Мови, в яких відсутнє саме визначення типів змінних, надають
більше гнучкості в написанні коду. Але, як би там не було, у разі неправильного
використання семантики для більшості функцій PHP слід очікувати появи
повідомлення про помилку.


Візьмемо шматок коду, який відкриває файл і виводить його підрядник:

<?php

$fp = @fopen ( “somefile.txt”, “r” )
or die ("Не можу відкрити файл somefile.txt");

while ($ line = @ fgets ("$ fp", 1024)) / / Тут помилка!
{
print $line;
}

@ Fclose ("$ fp") / / І тут теж color
or die ("Не можу закрити файл somefile.txt");

?>


У даному випадку з'явиться повідомлення про помилку типу:
“Warning:
Supplied argument is not a valid File-Handle resource in tst.php on line
4”
("Увага: аргумент не може бути дескриптором файлу")


Це викликано тим, що змінна $fp у подвійні лапки,
що однозначно визначає її як рядок, тоді як функція fopen()
очікує в якості першого аргументу дескриптор, але НЕ рядок.
Відповідно, вам слід використовувати змінну, яка може
містити дескриптор.


Примітка: У даному випадку, рядковий тип допустимо синтаксично.


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

<?php
$fp = @fopen ( “somefile.txt”, “r” )
or die ("Не можу відкрити файл somefile.txt");

while ( $line = @fgets ($fp, 1024) )
{
print $line;
}

@fclose ($fp)
or die ("Не можу закрити файл somefile.txt");
?>


Як уникнути неправильного застосування семантики?


У наведеному прикладі генерується повідомлення про помилку. Але PHP надає
програмісту більше свободи, ніж інші, традиційні мови програмування.
Це дозволяє отримувати цікаві результати. Як мінімум, теоретично
можливо написати коректний код, неправильно використовуючи семантику мови.


Але будьте обережні, заграючи з семантикою мови! Можлива поява
важковловимий помилок в програмах. Якщо ж ви все-таки вирішили
поекспериментувати, вам слід розуміти три ключових моменти:



  • Типи: У PHP кожна змінна в будь-який момент часу відноситься до
    певному типу. І це незважаючи на той факт, що її тип можна вільно
    змінювати. Іншими словами, в мові PHP змінна не може існувати, при
    цьому не ставлячись до певного типу (і, відповідно, не володіючи
    характеристиками, властивих цьому типу). У PHP є 7 основних типів змінних:
    Boolean, resource, integer,
    double, string, array і
    object.
  • Область видимості: У PHP змінні мають область видимості, яка
    визначає те, звідки вона може бути доступна, і наскільки довго буде
    існувати. Непорозуміння концепції <області видимості> може
    проявлятися у вигляді різного роду <плаваючих> помилок.
  • php.ini: При написанні коду, варто розуміти, що не всі користувачі
    мають таку ж конфігурацію програмно-апаратних засобів, як і ви. Таким
    чином, абсолютно необхідно зайвий раз переконатися, чи зберігається
    працездатність вашого коду в тій конфігурації, в якій програма повинна
    працювати, а не в тій, в якій розроблялася.

19. Недостатньо або надмірно коментований текст


Погано документований текст програми є ознакою егоїстичного
програміста. Результатом спроби аналізу вашої програми з метою внесення
поліпшень буде лише головний біль. Причому всі програмісти вважають
самодокументірованний код гарним тоном, але самі вкрай рідко пишуть
коментарі.


Слід також уникати надмірних коментарів. Це теж зустрічається дуже
рідко, і, знову ж таки, створює важко читається вихідний код. Наступний приклад це
ілюструє:

 <? Php / / Початок коду

$ Age = 18; / / Вік дорівнює 18
$ Age + +; / / Збільшимо $ age на один рік

/ / Надрукуємо привітання
print "Вам зараз 19 років, і це значить, що Вам уже було:";
print "
<br>
<br>
";

/ / Цикл "для" щоб вивести всі
/ / Попередні значення віку
for ($idx = 0; $idx < $age; $idx++)
{
/ / Надрукуємо кожне значення віку
print "$ idx років
<br>
";
}
/ / Кінець коду
?>


І все-таки: де золота середина?


Отже, який же обсяг коментарів слід поміщати в скрипт?! Це залежить від
багато чого: від часу, яким ви володієте, від політики компанії, складності
проекту і т.д. Тим не менш, запам'ятаєте декілька основних принципів, яким
треба дотримуватися при написанні програм незалежно від вашого рішення:



  • Перед тілом функції завжди розміщайте коментар – призначення функції.
  • Додавайте коментарі в сумнівних ділянках коду, коли немає впевненості,
    що він буде працювати як треба.
  • Якщо призначення коди неочевидно, внесіть інформацію про призначення цього
    ділянки. Ви ж потім скористаєтеся цим коментарем.
  • Уникайте коментарі виду # – Використовуйте лише /*
    */
    або //.

Наступний приклад ілюструє гарний стиль коментарів:

<?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 1923, 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 ("
", $site_info->links);
$names = explode ("
", $site_info->link_names);

for ($i = 0; $i < count ($links); $i++)
{
print "
<a href="$links[$i]"> $ names [$ i] </ a>

<br>
";
}
}
?>


Очевидно, такий код краще читаємо. Ще одна перевага використання цієї
концепції – можливість зміни дизайну без модифікації самого коду
програми.


Плюси використання API функцій



  • Щодо чистий, ясний код
  • Швидкий код

Мінуси використання API функцій



  • Не настільки наочно як система шаблонів
  • Все-таки, для модифікації дизайну потрібно деяке знання PHP

Система шаблонів


Другий спосіб, використовуваний для поділу PHP і HTML коду – використання
шаблонів. У даному випадку, деякі елементи дизайну замінюються
користувацькими тегами, а сама програма сканує файл на предмет їх наявності
і замінює їх необхідною інформацією.


Приклад використання шаблонів:


<html>

<head>
<title>%%PAGE_TITLE%%</title>
</head>

<body %%BODY_PROPERTIES%%>
<h1>%%PAGE_TITLE%%</h1>
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td width="25%">%% PAGE_LINKS%% </ td>
<td>%%PAGE_CONTENT%%</td>
</tr>
</table>
</body>

</html>


Потім пишемо програму, переглядала код шаблону і при виведенні заменяющую
теги виду %%:%% потрібною інформацією.


Примітка: непоганий клас для використання його в системі шаблонів –
FastTemplate, його можна завантажити з http://www.thewebmasters.net/.


Плюси використання шаблонів:



  • Гранично просто і ясно
  • Для зміни шаблонів не потрібне знання PHP

Мінуси використання шаблонів:



  • Більш повільний спосіб – адже треба сканувати весь шаблон і лише
    потім виводити дані
  • Складніше впровадити на практиці

15. Використання застарілого синтаксису і функцій


Деякі програмісти знову і знову використовують старі бібліотеки і старі
напрацювання. Наприклад, код, написаний ще під PHP 2, до цих пір використовується з
PHP4, хоча вже починаючи з версії PHP3 були додані стандартні функції,
реалізують те ж саме.


Використання застарілих функцій і синтаксису можуть знизити швидкість
виконання коду і, до того ж, зробити його нечитабельним. Інші програмісти можуть
бути незнайомі зі старими функціями. Але тим не менш, якщо ви зустріли ділянку
старого коду, не обов'язково його переписувати з урахуванням нових правил мови.
Просто не треба його використовувати при написанні нових програм.


Приклад використання старих мовних конструкцій:


<?php

/ / Старий стиль

while (1):
print "5";
if ( $idx++ == 5 ):
break;
endif;
endwhile;

/ / Краще написати так
/ / (Втім, код можна оптимізувати)

while (1)
{
print "5";
if ( $idx++ == 5 ) {
break;
}
}

?>


Чому ж слід дотримуватися нових стандартів? Причини наступні:



  • Використання старих конструкцій не дуже поширене і, таким чином,
    новачки в PHP будуть в замішанні, побачивши два різні варіанти синтаксису.
  • Старий синтаксис відрізняється від синтаксису інших мов програмування, і,
    отже, при переході з іншої мови на PHP програмісту буде складніше
    зрозуміти і звикнути.
  • Але найголовніше – в одній з новою версією, можливо, буде виключена
    підтримка старого синтаксису, тим самим це змусить вас переписати код заново.
    Як би там не було, дужки завжди залишаться частина мови PHP.

Подібні ділянки коду можна зустріти в багатьох програмах. Вам, як правило,
слід керуватися правилами, наведеними в документації по PHP, більшою
частину оновленої – в ній відбивається розвиток мови. Періодично переглядайте
документацію, адже мова розвивається, додаються нові функції. Таким чином,
вам ніколи не доведеться писати користувальницькі функції, що виконують ту ж
роботу, що і стандартні.


Резюме


У цій статті ми розглянули перші 7 з 21 найбільш загальних помилок PHP
програміста. Як правило, вони не порушують працездатності програм, але, тим
Проте, їх слід уникати:



  • Необгрунтоване застосування функції printf(): Її слід
    використовувати тільки для виведення форматованих даних.
  • Неправильне застосування семантики мови: Багато програмісти не мають
    достатньо часу, щоб розібратися у всіх тонкощах мови, що згодом
    виражається в помилковому коді.
  • Погано коментований код: Завжди пишіть коментарі! Перед кожною функцією
    вказуйте, що робить ця функція, і які аргументи вона вимагає. Також
    коментуйте складні ділянки коду і внесені зміни.
  • Занадто багато тимчасових змінних: Тимчасові змінні добре використовувати
    для запобігання повторного виклику функцій або послідовностей функцій.
  • Винаходимо велосипед – переписуємо стандартну функцію: Спочатку загляньте в
    керівництво по PHP – не описана чи там функція, яку ви збираєтеся написати
    для, здавалося б, розширення набору стандартних функцій PHP.
  • Змішаний PHP і HTML код: Спробуйте зробити код як можна більш модульним.
    Потім вам (і іншим теж) можна буде змінити дизайн сторінки без зміни коду
    PHP.
  • Використовуються старі мовні конструкції і застарілі функції: Те, що ви
    можете зробити, не завжди слід робити. Загляньте в документацію
    і літературу по PHP, як писати правильно. Відмінні книги – "Розробка
    веб-додатків з використанням PHP (Web Application Development with PHP) і
    "Професійний програміст PHP (Professional PHP). (Ех, де б їх ще знайти!
    ;)) – прим. перекладача)

Про автора


Стерлінг Хьюз (Sterling Hughes) – незалежний розробник веб-сайтів,
займається створенням динамічних веб-додатків для деяких найбільших
світових компаній. Брав участь у створенні cURL and SWF розширень PHP з відкритим
вихідним кодом. Його книга, <Настільна книга програміста PHP> (The PHP
Developer "s Cookbook), була видана в жовтні 2000 року видавництвом .


Про перекладача


Дмитро Короленко. Ну що про нього багато говорити? ;)) Завдяки йому люди, не
знають англійської, або знають, але дуже;)), зможуть прочитати вельми
пізнавальну статтю Стерлінга Хьюза. Про помічені недоліки перекладу прохання
повідомляти на мило: lkx2@mail.ru Відразу хочеться
пояснити, що дослівний переклад не був самоціллю, але, тим не менш,
стиль і сенс оригінальної статті по можливості;)) зберігалися.
Всі зауваження та побажання вітаються і будуть враховані, тому що готується переклад
другій частині цієї статті. Тільки прохання ногами не штовхати;)) Все ж таки я
сподіваюся, що якщо ви до цього місця дочитали, то не все так погано;))

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*