Так исторически сложилось, что язык программирования PHP порой недолюбливают. Я не встречал ещё ни одного Java-программиста, который бы не смотрел на PHP свысока или хотя бы не ронял фразы типа: «К сожалению, практически вся e-commerce написана на PHP». Наверное, это происходит из-за того, что мы видим «плохой» код на PHP, иногда вынуждены поддерживать этот код и переносим негатив на сам язык. Но тем не менее нельзя отрицать, что PHP популярен — по данным на 2024 год, PHP используется на более чем 75% всех веб-сайтов, где язык программирования известен..
Надеюсь, эта статья может быть полезна не только тем, кто не работает с PHP постоянно, а вынужден лишь иногда что-то время от времени фиксить, но и тем, для кого PHP является «родным» языком. Я собрал некоторые анти-паттерны или плохие практики, из-за которых появляется плохой код. Возможно, вы узнаете здесь свои приёмы и подходы и пересмотрите их.
1. Хук — это не просто функция
Когда мы используем фреймворки или CMS, мы пользуемся специальными hook-функциями. Они могут называться по-разному, но смысл в том, что эта функция будет вызвана при определённом событии, произошедшем в системе. Например, «пользователь добавил товар в корзину» или «пользователь зашёл на определённую страницу». Это «событийно-ориентированная модель» или «event-driven programming» — парадигма программирования, основанная на обработке событий, сигналов или сообщений, возникающих в системе. Например, в Drupal такая функция может выглядеть так:
/** * Implements hook_form_alter(). */ function module_name_form_alter(&$form, FormStateInterface $form_state, $form_id) { }
Технически хук — это, конечно, функция, и проблема в том, что программист пытается поместить весь свой код между фигурными скобками этой функции. В итоге получается следующее:
Как результат мы видим очень длинную функцию. Мне приходится часто наблюдать подобные хуки, в пять экранов длиной и больше.
Какая в этом проблема:
-
Этот код трудно читать;
-
Трудно найти правильное место, если нужно что‑то изменить;
-
Реально трудно понять, что здесь происходит, потому что многие независимые части сайта изменяются этой одной функцией;
-
Это яркий пример «спагетти» кода;
Как это можно улучшить?
Необходимо использовать подход «разделяй и властвуй» (Divide and Conquer) или по другому: «модульность» (Modularity). Во‑первых использовать более специфические хуки взамен более общих:
Тогда у вас будет несколько небольших узкоспециализированных функций взамен одной общей.
Если более конкретных хуков не существует, все равно разделите код на отдельные функции. Тогда главная функция станет короче и будет похожа на некий контроллер.
Также следует использовать осмысленные имена для подфункций, тогда даже не понадобятся дополнительные комментарии;
Вывод: хук — это не просто функция, а точка входа в вашу программу!
Не бойтесь добавлять новые функции, объявлять файлы классов в своем коде, если вам это нужно. Не пытайтесь втиснуть всю необходимую логику между фигурными скобками функции‑события.
2. Используйте силу ООП
Довольно часто бывает нужно собрать и передать некоторый набор данных, и первая идея — использовать ассоциативный массив в качестве хранилища. Подумайте об использовании объектов (классов) вместо ассоциативных массивов, когда это имеет смысл.
В чем проблема массивов?
-
Ключи ассоциативных массивов не подсвечиваются IDE, поэтому вы не знаете, какие ключи там ожидать;
-
Вы не можете ограничить тип значения элемента ассоциативного массива;
-
Очевидно, что вам нужно проверить, существует ли ключ, прежде чем обращаться к нему, чтобы избежать ошибки;
Преимущества использования классов:
-
IDE показывает назначение и описание элемента класса;
-
Вы можете определить типы свойств;
-
Вы можете проверять и фильтровать значения в одном месте (сеттеры/геттеры);
Этот подход называется «Data Transfer Object (DTO)».
Другая ошибка: даже используя классы, некоторые программисты пишут код, подобный этому:
Class MyNodeProcessor { private function processNode($nid, $external_data) { $node_array_storage = getStorage(); $langcode = getLangcode(); $user = getCurrentUser(); $node = $this->changeTitleToExternal($node_array_storage, $nid, $langcode, $external_data->title, $user); $node = $this->setExternalPerson($node_array_storage, $nid, $langcode, $external_data->person, $user); $node = $this->publishIfNeeded($node_array_storage, $nid, $langcode, $external_data->person, $user); return $node; } }
Проблема здесь в том, что слишком много параметров нужно передавать в методы.
Как это улучшить:
Используйте свойства класса для хранения общих данных вместо передачи их в качестве параметров метода. Используйте конструктор для установки свойств класса. Этот подход называется «Инкапсуляция» (Encapsulation).
Вероятно, вашим методам вообще не нужно ничего получать или возвращать, если вы работаете с классами. Код должен стремиться к т.
class MyNodeProcessor { private $node; private $node_array_storage; private $langcode; private $current_user; private function processNode($nid) { $this->setNode($nid); $this->changeTitleToExternal(); $this->setExternalPerson(); $this->publishIfNeeded(); } }
3. Не упускайте новые возможности PHP 8
В последнее время язык обновляется очень интенсивно, новые версии выходят постоянно. Следите за обновлениями! Появились очень крутые и полезные инструменты. Вот некоторые из них:
Null coalescing operator
$username = $result['user'] ?? 'nobody'; // То же что и: $username = isset($result['user']) ? $result['user'] : 'nobody';
Некоторые подходы устаревают, например динамический свойства классов:
class Post { public string $title; } // … $post->name = 'Name'; // Dynamic properties are deprecated in PHP 8.2, and will throw an ErrorException in PHP 9.0
Вы знали, что теперь есть read-only классы?
readonly class Post { public function __construct( public string $title, public Author $author, public string $body, public DateTime $publishedAt, ) {} } $post->title = 'Other'; Error: Cannot modify readonly property Post::$title
Вместо этого кода, где мы проверяем значение на null:
$country = null; if ($session !== null) { $user = $session->user; if ($user !== null) { $address = $user->getAddress(); if ($address !== null) { $country = $address->country; } } }
Можно использовать «оператор безопасного обращения к null» (Null‑safe operator) или Null‑coalescing chaining:
$country = $session?->user?->getAddress()?->country;
Я не могу показывать все новые функции по очевидной причине:) вам придется гуглить и читать о новых функциях PHP8 самостоятельно.
И не бойтесь — попробуйте использовать их в своем коде! И конечно избегайте использования устаревших функций. Быть в курсе последних новшеств — это хороший способ повышать свои скилы как разработчика PHP.
4. Отформатируйте свой код
К сожалению не во всех наших проектах есть git‑линтеры, которые не позволяют коммитить неформатированный код. Выберите определенные правила форматирования и придерживайтесь их.
В чем проблема?
-
Неформатированный код трудно читать. Я видел классы, в которых свойства были объявлены между методами. Метод конструктора был где‑то в середине файла вместо того, чтобы быть перым методом класса;
-
В git будет мусор: среди хаотичных изменений в отступах и пробелах трудно найти настоящие функциональные изменения;
-
Неформатированный код является нарушением лучших практик PHP;
Для устранения лишнего “шума” в коде вы можете самостоятельно выполнить проверку форматирования. Вкратце продемонстрирую, как установить и использовать инструмент проверки кода на примере стандарта Drupal на Mac.
Устанавливаем composer глобально
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" php -r "if (hash_file('sha384', 'composer-setup.php') === 'e21205b207c3ff031906575712edab6f13eb0b361f2085f1f1237b7126d785e826a450292b6cfd1d64d92e6563bbde02') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" php composer-setup.php php -r "unlink('composer-setup.php');" sudo mv composer.phar /usr/local/bin/composer
Устанавливаем через composer Drupal Coder Sniffer, тоже глобально
composer global require drupal/coder Edit ~/.zshrc and add export PATH="$HOME/.composer/vendor/bin:<other path>"
Вот и все 🙂
Теперь можно проверить свой код на соответствие стандартам Drupal:
phpcs --extensions=theme,module,php --standard=Drupal,DrupalPractice web/modules/custom/module_name/*
Результатом будет что-то вроде этого:
Прелесть в том, что этот же инструмент может исправить ваш код автоматически:
phpcbf --extensions=theme,module,php --standard=Drupal,DrupalPractice web/modules/custom/module_name/*
Результатом будет:
Если вы раньше не использовали линтеры и вдруг начнете, то однозначно одним хорошим PHP программистом станет больше!
Ну и напоследок еще один антипаттерн и непрошеный совет:
Ниже будет частично псевдокод, но каждый PHP бэкендер может его узнать.
if (isset($node_ids)) { if (is_array($node_ids)) { foreach ($node_ids as $key => $node_id) { $node = LoadNode($node_id); if ($node instanceof Node) { if ($node->hasField('name')) { if (!$node->isEmpty('name')) { $result[$key] = $node->get('name')->toString(); } } } } } } return $result;
Проблема здесь в читаемости кода из‑за слишком большого количества вложенных условий. Представьте, если нужно еще больше ифов..
Как можно улучшить этот код?
В данном случае его можно улучшить, используя «ранний возврат» (early return), то есть, делая проверки на противоположные условия и останавливая выполнение программы, если условие срабатывает. Например вот так:
if (!isset($node_ids)) { return $result; } if (!is_array($node_ids)) { return $result; } foreach ($node_ids as $key => $node_id) { $node = LoadNode($node_id); if (!$node instanceof Node) { continue; } if (!$node->hasField('name')) { continue; } if ($node->isEmpty('name')) { continue; } $result[$key] = $node->get('name')->toString(); } return $result;
Меньше вложенных уровней делают код более читабельным. Как видите, также можно пропустить текущую итерацию цикла вместо проверки условий перед выполнением чего‑либо внутри цикла.
Заключение
Мои советы были о том как:
-
Не писать длинных функций (даже если это хук);
-
Использовать силу ООП;
-
Не упускать новые возможности PHP 8;
-
Отформатиовать наконец свой код;
-
Не делать много вложенных проверок;
Эти антипаттерны — часть того, с чем я постоянно сталкиваюсь в процессе работы и то, что портит представление о PHP как о языке в целом. В этот список можно еще много чего добавить, например использование побочных эффектов — когда для приведения типа переменной к числу используют умножение на единицу. Или если не умеют пользоваться авто‑загрузчиком классов и пространствами имен.
Да, на PHP можно писать отвратительный код. Но ведь можно писать и красиво! Этот язык прост для начинающих, что обманчиво, и даже многие состоявшиеся программисты «потрогав» PHP думаю что уже его умеют, но чувствуют, что получается какая то фигня, и тогда предполагают, что это язык какой-то не такой. Язык в порядке — совершенствуйте свои навыки, изучайте и применяйте лучшие практики и не используйте плохие.
Всем добра, и надеюсь, джависты не закидают меня тапками 😉
ссылка на оригинал статьи https://habr.com/ru/articles/871426/
Добавить комментарий