Хорошие привычки в PHP
Как и любой другй язык программирования, пхп предоставляет возможность записать одни и те же действия по-разному. Один способ записи получается красивее, другой - не очень. Для начинающего веб-разработчика может быть не очевидно, как лучше описать какое-либо действие в PHP, поэтому некоторые из приёмов я продемонстрирую здесь.
Проход по массивам/спискам
Для прохода по массивам и спискам, где это возможно, пользуемся foreach, а не for($i=0; $i<$count; $i++):
$arr = array(1, 2, 3, 4); foreach($arr as $number) { echo $number; } $arr2 = array('name' => 'John', 'email' => 'jon@smith'); foreach($arr2 as $key => $value) { echo "\n $key = $value"; }
Не злоупотреблять префиксом @
Префикс @ перед выражением в PHP подавляет вывод предупреждений, даже если в скрипте на самом деле логическая ошибка, например обращение к объекту как к массиву:
$data = new stdClass(); $data->x = 1; echo @$data['x'];
В этом примере на третьей строчке будет Fatal error, но сообщения о нём вы не увидите, так что найти такую ошибку в большом проекте может быть сложно. Поэтому, использовать @ стоит только там, где это действительно необходимо.
Инициализация переменных
Всегда следует инициализировать переменные. То есть, присваивать начальное значение перед тем, как считывать его из переменной.
Язык PHP родился в то время, когда о культуре программирования обычно задумывались не сильно, поэтому PHP позволяет различные вольности, вроде неинициализированных переменных. Программа, обращающаяся к неинициализированной переменно будет работать, но в ней может скрываться скрытый дефект, о котором разработчик не догадывается.
Поэтому необходимо всегда инициализировать переменные перед обращением к ним:
$users = getUsers(); $search = 'Ivan'; // Плохо: foreach($users as $user) { if($user->name == $search) { $found = $user->id; } } if($found) { echo "$search = $found"; } // Лучше: $found = null; foreach($users as $user) { if($user->name == $search) { $found = $user->id; } } if($found) { echo "$search = $found"; }
Обращение к элементам массива, которые могут отсутствовать
Обращение к отсутствующему элементу массива порождает замечание (Notice). Чтобы этого не произошло, пользуемся isset() для проверки наличия элемента в массиве или члена данных в объекте. Это предпочтительнее, чем пользоваться префиксом @, поскольку @ не предотвращает Notice, а лишь подавляет сообщение о нём, что ведёт к расходу вычисительной мощности впустую.
На машине разработчика обязательно должен быть включен вывод Notice на страницу и в лог, чтобы обращение к несуществующим переменным не осталось незамеченным.
$userId = isset($_GET['userId'])? $_GET['userId']: null; if($userId) { echo 'Hello, user nr. ', $userId; } $email = isset($user->params->email)? $user->params->email: null;
Повторения похожего кода
Любых повторений следует избегать. Если вы смотрите на программу и видите несколько похожих строк или блоков, это почти наверняка означает избыточное копирование (копипаст), что является злейшим врагом разработчика.
В примере ниже ясно видно, что 5 раз повторяется один и тот же кусок при обращении к списку параметров. Этот кусок переписан так, чтобы не было повторяющегося фрагмента. Теперь, если придётся менять код, изменения надо будет внести только в одном месте.
Ещё страшнее повторения крупных блоков кода. В этом случае, чтобы вспомнить, чем они друг от друга отличаются, приходится внимательно изучать каждое из повторений, чтобы "найти 10 отличий", а уже после этого приступить к изменениям, которые опять же, придется вносить в каждую копию блока. Те, кто сталкивался с такими повторяющимися фрагментами, в которые надо было внести изменения, знают, что это может быть адским кошмаром по сравнению с остальными проблемами в коде.
В общем, правило такое: никаких повторений! Вместо повторяющегося кода должны стоять вызовы функции или метода, реализующего эти действия. Или, как в простом примере ниже - заведена дополнительная переменная.
// Плохо: if (isset($search['zipcode'])) { $request['FindArticles']['request']['search_params']['address']['zip_code'] = $search['zipcode']; } if (isset($search['zipcodeRadius'])) { $request['FindArticles']['request']['search_params']['address']['radius'] = $search['zipcodeRadius']; } if (isset($search['fuel'])) { $request['FindArticles']['request']['search_params']['fuel_types']['fuel_type_id'] = $search['fuel']; } if (isset($search['minPowerAsArray'])) { $request['FindArticles']['request']['search_params']['kilowatt']['from'] = $search['minPowerAsArray']; } if (isset($search['maxPowerAsArray'])) { $request['FindArticles']['request']['search_params']['kilowatt']['to'] = $search['maxPowerAsArray']; } // Лучше: $params = array(); if (isset($search['zipcode'])) { $params['address']['zip_code'] = $search['zipcode']; } if (isset($search['zipcodeRadius'])) { $params['address']['radius'] = $search['zipcodeRadius']; } if (isset($search['fuel'])) { $params['fuel_types']['fuel_type_id'] = $search['fuel']; } if (isset($search['minPowerAsArray'])) { $params['kilowatt']['from'] = $search['minPowerAsArray']; } if (isset($search['maxPowerAsArray'])) { $params['kilowatt']['to'] = $search['maxPowerAsArray']; } $request['FindArticles']['request']['search_params'] = $params;
Комментируй там, где надо
Комментировать код - вообще-то хорошая привычка. При работе в команде разработчиков, комментариями должны быть снабжены все внутренние API, которыми всем постоянно приходится пользоваться. Так же в обязательном порядке комментировать нужно сложные участки кода: реализации хитрых алгоритмов, и другие места, где без поллитры не разберёшься.
А вот ставить комментарий там, где всё очевидно - тратить время своё, время тех, кто будет читать этот код, и место на диске. Например, в примере ниже комментарии совершенно ни к чему, поскольку код и без них легко читается и говорит сам за себя:
// Инициализация переменных:$totalTeethCount = 0; $totalWeight = 0;// Проход по пользователям:foreach($users as $user) { $totalTeethCount += $user->teethCount; $totalWeight += $user->weight; }
Понятные имена переменных, функций, методов
Достингуть хорошей читаемости программы можно даже просто выбирая имена переменных и функций, которые говорят сами за себя. Следует избегать различных неочевидных сокращений в именах переменных, если на то нет особых причин.
Вывод фрагментов HTML из PHP
Когда в PHP-скрипте надо вывести фрагмент HTML, делать это следует не через echo, а в блоках ?>....<?php. Практика показывает, что в большинстве случаев, удобнее работать с выводимым HTML, когда он находится вне PHP кода (и это правильно, так как PHP - это один язык, HTML - совсем другой). Вывод HTML через echo заставляет писать длинные цепочки из конкатенации строк через оператор ".", добавляя обратный слеш перед кавычками. Записанный таким образом HTML выглядит как месиво из кавычек, в котором сложно найти и исправить нужное место.
Сравните два фрагмента кода, в которых список объектов отображется в HTML. Какой из них понятнее? Я думаю, что второй.
// Плохо: foreach($items as $item) { echo '<tr><td>' . $item->id . '</td><td><a href="item/' . $item->id . '">' . $item->title . '</a></td></tr>'; } // Лучше: foreach($items as $item) { ?> <tr> <td><?php echo $item->id ?></td> <td><a href="item/<?php echo $item->id ?>"><?php echo $item->title ?></a></td> </tr> <?php }
Обработка запроса и вывод HTML
Скрипты, которые выдают HTML, могут одновременно содержать и реализацию логики приложения. Например, обработку запроса пользователя и вывод результата - HTML-страницы. В таких страницах следует четко разделить приём входных параметров, обработку запроса и вывод результата, не перемешивая между собой код обработки и вывода HTML. В больших программных проектах, как правило, это разделение ещё сильнее: за обработку и за отображение графического интерфейса отвечают разные компоненты, или классы (модель MVC, Model / View / Controller).
В маленьких скриптах создавать разные классы для обработки и отображения нет смысла, но разделить обработку и вывод необходимо хотя-бы визуально: в начале PHP-скрипта входная обработка параметров, далее - обработка собственно запроса пользователя, далее - вывод HTML. Эти части можно разделить визуально комментариями "///////....." или как нравится :).
Закрывающий маркер ?>
В конце PHP-файла, если он заканчивается PHP-кодом, а не HTML, ставить закрывающий маркер ?> не нужно. Более того: наличие этого маркера в самом конце файла может при некоторых условиях привести к ошибке, которую будет очень сложно обнаружить.