Новости

17.09.2024

«Рецепты PHP. Для профессиональных разработчиков»

В этом сборнике рецептов разработчики на PHP найдут надежные и проверенные решения распространенных задач. PHP — удивительно простой язык программирования, что объясняет, почему на нем написано более 75% веб-сайтов в Интернете. Но он также невероятно терпим к ошибкам программирования, что может привести к тиражированию сомнительного кода.

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

 

Почему стоит прочитать «Рецепты PHP»?

• Вы знали, что на PHP написано боле 75% сайтов в интернете?
Если быть точнее, то 79% по данным SkillFactory, 90% по данным Яндекс Практикума. Если переводить на простой язык, получится «очень много» PHP-кода.
А что это означает? Тот, кто владеет языком, владеет веб-сайтами.

• Разработчики на PHP найдут надежные решения всех распространенных задач.
Список тем, поднимаемых книги, обширен, но каждая из них объяснена подробно и подкреплена примерами.

В книге «Рецепты PHP» вы найдете:
  • Переменные;
  • Операторы;
  • Функции;
  • Строки;
  • Числа;
  • Дата и время;
  • Массивы;
  • Классы и объекты;
  • Безопасность и шифрование;
  • Работа с файлами;
  • Потоки;
  • Обработка ошибок;
  • Отладка и тестирование;
  • Настройка производительности;
  • Пакеты и расширения;
  • Базы данных;
  • Асинхронный PH5P;
  • Командная строка PHP.

Паттерны и примеры, раскрывающиеся в книге, пригодятся любому программисту.

• Вы изучите систему типов современного PHP.
Сейчас технологии быстро меняются, системы обновляются, а знания актуализируются.
PHP-язык удивительно простой. Он интерпретируемый, очень снисходительный к ошибкам программирования, потому так удобен для новичков. Даже грубые ошибки приводят только к предупреждениям, в то время как PHP продолжит выполнять программу. Но такая снисходительность — это своего рода обоюдоострый меч, поскольку даже «плохой код» будет выполняться, а многие разработчики публикуют этот код, который затем зачастую используют ничего не подозревающие новички.
• Как итог — создание эффективных приложений.
Ознакомившись с задачами и предложенными решениями, отработав их на практике, читатель лучше понимает, как использовать PHP-язык. Готовые шаблоны и примеры распространённых задач помогут сократить время на «изобретение велосипедов», а также убережёт от копирования «плохого кода», который может попасться в процессе исследований.

Обработка ошибок


Лучшие планы мышей и людей часто идут вкривь и вкось.
Роберт Бернс

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

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

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

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


Задача

Компилятор PHP не смог проанализировать скрипт в вашем приложении; вы хотите быстро найти и устранить ошибку.

Решение

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

Обсуждение

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

В качестве наглядного примера перечислим западные штаты США:

$states = ['Вашингтон', 'Орегон', 'Калифорния'];
foreach $states as $state {
print("{$state} находится на западном побережье.") . PHP_EOL;
}

Интерпретатор PHP при запуске этого кода выдаст ошибку Parse error на второй строке:

PHP Parse error: syntax error, unexpected variable "$states", expecting "(" in php shell code on line 2

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

$states = ['Вашингтон', 'Орегон', 'Калифорния'];
foreach ($states as $state) {
print("{$state} находится на западном побережье.") . PHP_EOL;
}

Эта конкретная ошибка — пропуск скобок при использовании языковых конструкций — распространена среди разработчиков, часто переходящих с одного языка на другой. Например, тот же механизм в Python выглядит почти так же, но синтаксически корректен, если опустить круглые скобки в вызове foreach. Например:

states = ['Вашингтон', 'Орегон', 'Калифорния'].
for state in states:
print(f"{state} находится на западном побережье.")

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

Удобно, что такие IDE, как Visual Studio Code (https://oreil.ly/CkzbA), автоматически анализируют ваш скрипт и подсвечивают все синтаксические ошибки еще до запуска приложения (см. рис. 12.1).

image

Рис. 12.1. Visual Studio Code идентифицирует и выделяет синтаксические ошибки до запуска приложения

Читайте также

Список меток парсера PHP (https://oreil.ly/Zw_1I).

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


Задача

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

Решение

Расширьте базовый класс Exception, чтобы внедрить в него пользовательское поведение, а затем используйте блоки try/catch для захвата и обработки исключений.

Обсуждение

PHP определяет базовый интерфейс Throwable (https://oreil.ly/NkLuC), реализуемый любым видом ошибки или исключения в языке. Внутренние проблемы представляются классом Error (https://oreil.ly/eFMGz) и его потомками, а проблемы в пользовательской среде — классом Exception и его потомками.

Как правило, вы будете расширять только класс Exception в рамках своего приложения, но у вас есть возможность перехватывать любую реализацию Throwable в стандартном блоке try/catch.

Предположим, вы реализуете функцию деления с очень тонкой, пользовательской функциональностью:
  • деление на 0 не допускается;
  • все десятичные значения округляются в меньшую сторону;
  • целое число 42 не допускается в качестве числителя;
  • числитель должен представлять собой целое число, но знаменатель может быть числом с плавающей точкой.
Подобная функция допускает использование встроенных ошибок, например ArithmeticError или DivisionByZeroError. Однако в списке правил третье выделяется как требующее пользовательского исключения. Перед определением функции необходимо задать пользовательское исключение, как показано в примере 12.1.

Пример 12.1. Простое определение пользовательского исключения

class HitchhikerException extends Exception
{
public function __construct(int $code = 0, Throwable $previous = null)
{
parent::__construct('42 является неделимым.', $code, $previous);
}

public function __toString()
{
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
}

Когда пользовательское исключение создано, сгенерируйте его в своей пользовательской функции деления следующим образом:

function divide(int $numerator, float|int $denominator): int
{
if ($denominator === 0) {
throw new DivisionByZeroError;
} elseif ($numerator === 42) {
throw new HitchhikerException;
}

return floor($numerator / $denominator);
}

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

Пример 12.2. Обработка ошибок в пользовательском делении

$pairs = [
[10, 2],
[2, 5],
[10, 0],
[42, 2]
];

foreach ($pairs as $pair) {
try {
echo divide($pair[0], $pair[1]) . PHP_EOL;
} catch (HitchhikerException $he) { 1
echo 'Неверное деление на 42!' . PHP_EOL;
} catch (Throwable $t) { 2
echo 'Смотрите, бешеный сурок!' . PHP_EOL;
}
}
  1. Если в качестве числителя передается число 42, функция divide() сгенерирует исключение HitchhikerException и не сможет восстановиться. Перехват этого исключения, позволит вам дать обратную связь либо приложению, либо пользователю и двигаться дальше.
  2. Любая другая ошибка или исключение, сгенерированные функцией, будут перехвачены как реализация Throwable. В этом случае вы отклоняете ошибку и продолжаете выполнение.
Читайте также

Документация по следующим вопросам:
  • базовый класс Exception (https://oreil.ly/2s4mn);
  • список предопределенных исключений (https://oreil.ly/tdegn);
  • дополнительные исключения, определенные стандартной библиотекой PHP (SPL) (https://oreil.ly/gsdeg);
  • создание пользовательских исключений с помощью расширений (https://oreil.ly/-jrvt);
  • иерархия ошибок в PHP 7 (https://oreil.ly/KF1Zd).

Скрытие сообщений об ошибках от конечных пользователей


Задача

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

Решение

Чтобы полностью подавить ошибки в эксплуатационной среде, установите обе директивы error_reporting и display_errors в php.ini в положение Off:

; Disable error reporting
error_reporting = Off
display_errors = Off

Обсуждение

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

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

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

Возможно, существуют страницы с записями об уже известных ошибках (из-за устаревшего кода, плохо написанных зависимостей или известного «технического долга»), которые вы захотите пропустить. В таких ситуациях вы можете программно установить уровень отчетности об ошибках с помощью функции error_reporting(). Она принимает новый уровень сообщения об ошибках и возвращает тот уровень, который был установлен ранее (по умолчанию, если он прежде не был настроен).

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

$error_level = error_reporting(E_ERROR); 1

// ... Вызов другого кода вашего приложения.

error_reporting($error_level); 2
  1. Устанавливает уровень ошибок на абсолютный минимум, в том числе фатальные ошибки среды выполнения, которые останавливают выполнение сценария.
  2. Возврат уровня ошибок к предыдущему состоянию.
По умолчанию используется уровень ошибок E_ALL, который включает в себя все ошибки, предупреждения и уведомления. Допускается использовать целочисленные значения уровней, но PHP представляет несколько именованных констант, которые представляют каждую потенциальную настройку. Эти константы перечислены в табл. 12.1.

До PHP 8.0 уровень отчетности об ошибках по умолчанию начинался с E_ALL, а затем явно удалялись диагностические уведомления (E_NOTICE), строгие предупреждения о типах (E_STRICT) и уведомления об устаревании (E_DEPRECATED).

Таблица 12.1. Константы уровня сообщений об ошибках

image

image

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

error_reporting(E_ERROR | E_WARNING | E_PARSE);

Читайте также

Документация по функции error_reporting() (https://oreil.ly/b4eIH), директиве error_reporting (https://oreil.ly/t5IW2) и директиве display_errors (https://oreil.ly/lxXNs).

Использование пользовательского обработчика ошибок


Задача

Вы хотите настроить собственный способ обработки и отображения ошибок.

Решение

Определите свой обработчик как вызываемую функцию в PHP, а затем передайте эту функцию в set_error_handler() следующим образом:

function my_error_handler(int $num, string $str, string $file, int $line)
{
echo "Возникла ошибка $num в $file в строке $line: $str" . PHP_EOL;
}

set_error_handler('my_error_handler');

Обсуждение

PHP применит пользовательский обработчик в тех ситуациях, когда ошибка считается исправимой. Однако фатальные ошибки, ошибки ядра и проблемы во время компиляции (например, ошибки парсера) приводят к приостановке или полному прерыванию программы и не обрабатываются пользовательской функцией (к последнему относятся ошибки E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR и E_COMPILE_WARNING). Кроме того, большинство ошибок E_STRICT в файле, который вызывал функцию set_error_handler(), также не могут быть зафиксированы, так как они возникнут до регистрации пользовательского обработчика.

Если вы определите пользовательский обработчик ошибок, аналогичный тому, что представлен в «Решении», то при любых перехватываемых ошибках будут вызываться эта функция и выводиться данные на экран. Как показано в примере 12.3, попытка выдать echo неопределенной переменной приведет к ошибке E_WARNING.

Пример 12.3. Перехват восстановимых ошибок среды выполнения

echo $foo;

Если определить и зарегистрировать my_error_handler() из примера выше, то ошибочный код в примере 12.3 выведет на экран следующий текст, ссылающийся на целочисленное значение типа ошибки E_WARNING:

Возникла ошибка 2 в коде php shell в строке 1: Неопределенная переменная $foo

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

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

Аналогично PHP позволяет регистрировать (и восстанавливать) пользовательские обработчики исключений. Они работают так же, как и обработчики ошибок, только фиксируют любое исключение, брошенное вне блока try/catch. В этом случае выполнение программы будет остановлено после вызова пользовательского обработчика исключений.

Для более подробной информации об исключениях ознакомьтесь с рецептом 12.2 и документацией для функций set_exception_handler() (https://oreil.ly/_pf4H) и restore_exception_handler() (https://oreil.ly/TOEuz).

Читайте также

Документация по set_error_handler() (https://oreil.ly/IAh69) и restore_error_handler() (https://oreil.ly/SlT_d).

Регистрация ошибок во внешний поток


Задача

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

Решение

Используйте функцию error_log() для записи ошибок в стандартный файл журнала следующим образом:

$user_input = json_decode($raw_input);
if (json_last_error() !== JSON_ERROR_NONE) {
error_log('JSON Error #' . json_last_error() . ': ' . $raw_input);
}

Обсуждение

По умолчанию функция error_log()записывает ошибки в то место, которое указано в директиве error_log (https://oreil.ly/3lVPn) файла php.ini. В системах на базе Unix этот файл обычно располагается в каталоге /var/log, однако это можно изменить по вашему усмотрению.

Необязательный второй параметр error_log() позволяет маршрутизировать сообщения об ошибках при необходимости. Если сервер настроен на отправку электронной почты, вы можете указать тип сообщения 1 и предоставить адрес электронной почты в дополнительном третьем параметре для отправки ошибок по электронной почте:

error_log('Какое-то сообщение об ошибке', 1, 'developer@somedomain.tld');

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

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

error_log('Какое-то сообщение об ошибке', 3, 'error_log.txt');

При записи ошибок непосредственно в файл с помощью функции error_log() система не будет автоматически добавлять символ новой строки. Вам придется либо добавить PHP_EOL к любой строке, либо закодировать символы новой строки \r \n.

В главе 11 подробно рассматриваются файловый протокол, а также другие потоки, реализуемые PHP. Помните, что прямое обращение к файлу неявно использует протокол file://, так что в действительности вы регистрируете ошибки в файловом потоке с помощью предыдущего блока кода. С тем же успехом можно ссылаться на любой другой вид потока, если правильно указан его протокол. В следующем примере ошибки записываются непосредственно в стандартный поток ошибок консоли:

error_log('Какое-то сообщение об ошибке', 3, 'php://stderr');

Читайте также

Документация по функции error_log() (https://oreil.ly/QUQRH). Рецепт 13.5, в котором рассказывается о Monolog — более полной библиотеке протоколирования для PHP-приложений.
Об авторе
Эрик А. Манн работает инженером-программистом почти два десятилетия. Он создавал масштабируемые проекты как для стартапов, так и для компаний из списка Fortune 500. Эрик часто выступает с докладами по архитектуре программного обеспечения, основам компьютерной безопасности и лучшим практикам разработки. Он регулярно публикуется в журнале php[architect] и больше всего любит помогать начинающим разработчикам не совершать тех же профессиональных ошибок, что и он когда-то.

Подводя итог, какой рецепт успеха?

Много терпения, вдумчивого подхода. Щепотка находчивости, ломоть старания и настольная книга «Рецепты PHP. Для профессиональных разработчиков».

Более подробно с книгой можно ознакомиться на сайте издательства

Комментарии: 0

Пока нет комментариев


Оставить комментарий






CAPTCHAОбновить изображение

Наберите текст, изображённый на картинке

Все поля обязательны к заполнению.

Перед публикацией комментарии проходят модерацию.