Новости
17.09.2024
«Рецепты PHP. Для профессиональных разработчиков»
В этом сборнике рецептов разработчики на PHP найдут надежные и проверенные решения распространенных задач. PHP — удивительно простой язык программирования, что объясняет, почему на нем написано более 75% веб-сайтов в Интернете. Но он также невероятно терпим к ошибкам программирования, что может привести к тиражированию сомнительного кода.
Эрик Манн предлагает собственные рецепты использования современных версий PHP для задач, встречающихся в повседневной практике программиста. Вы познакомитесь с паттернами и примерами, которые пригодятся любому разработчику, и сможете быстро находить и решать сложные задачи, не изобретая велосипед.
Почему стоит прочитать «Рецепты PHP»?
Если быть точнее, то 79% по данным SkillFactory, 90% по данным Яндекс Практикума. Если переводить на простой язык, получится «очень много» 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).
Рис. 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 не допускается в качестве числителя;
- числитель должен представлять собой целое число, но знаменатель может быть числом с плавающей точкой.
Пример 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;
}
}
- Если в качестве числителя передается число 42, функция divide() сгенерирует исключение HitchhikerException и не сможет восстановиться. Перехват этого исключения, позволит вам дать обратную связь либо приложению, либо пользователю и двигаться дальше.
- Любая другая ошибка или исключение, сгенерированные функцией, будут перехвачены как реализация 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
- Устанавливает уровень ошибок на абсолютный минимум, в том числе фатальные ошибки среды выполнения, которые останавливают выполнение сценария.
- Возврат уровня ошибок к предыдущему состоянию.
До PHP 8.0 уровень отчетности об ошибках по умолчанию начинался с E_ALL, а затем явно удалялись диагностические уведомления (E_NOTICE), строгие предупреждения о типах (E_STRICT) и уведомления об устаревании (E_DEPRECATED).
Таблица 12.1. Константы уровня сообщений об ошибках
Обратите внимание, что 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-приложений.
Подводя итог, какой рецепт успеха?
Много терпения, вдумчивого подхода. Щепотка находчивости, ломоть старания и настольная книга «Рецепты PHP. Для профессиональных разработчиков».
Более подробно с книгой можно ознакомиться на сайте издательства
Комментарии: 0
Пока нет комментариев