image Привет, Хаброжители!

Важнейшая книга на кухне разработчиков на PHP!
В ней нет ни слова о том, как разделывать фазана, зачем солить воду и сколько в минутах будет «до готовности». Лишь практическое руководство, как использовать современную версию 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. Для профессиональных разработчиков».

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

» Оглавление
» Отрывок

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Для Хаброжителей скидка 25% по купону — PHP

Комментарии (7)


  1. delphinpro
    03.09.2024 15:26
    +4

    Приведённые выдержки из книжки не побудят профессиональных разработчиков ее купить =)
    Надо название поменять - «Рецепты PHP. Для желающих стать профессиональным разработчиком»


    1. MyraJKee
      03.09.2024 15:26

      Это точно... Явно не стоило начинать с примера со скобками...


  1. Darksynx
    03.09.2024 15:26
    +1

    "Компилятор PHP не смог проанализировать скрипт..."

    компилятор... ага

    Почитав статью создалось впечатление, что автор начал писать книгу лет 15 назад и только сейчас закончил.

    Проще всего узнать КАК пользоваться php через доку (особенно про те вещи, которые привели). Другой вопрос КОГДА и ЗАЧЕМ их использовать, а ещё лучше почему лучше НЕ ИСПОЛЬЗОВАТЬ - вот это было бы как раз что-то чем можно поделиться профессионалу с другими профессионалами. Сейчас рассматривать задачу для ПХП без связки с окружением где он запускается - можно очень сильно ошибиться. Такие книжки скорее действительно не для профессиональных разработчиков, как правильно заметили выше.


    1. SerafimArts
      03.09.2024 15:26
      +2

      компилятор... ага

      Ну, если без шуток, то в PHP три разных компилятора (constexpr, основной: опкоды + оптимизрующий + опкеш, jit). А интерпретатор уже воспроизводит результат компиляции или просто отправляет сгенерированный машинный код (если это возможно и такой есть после JIT) на исполнение. Так что технически назвать это компилятором с натяжкой возможно.

      P.S. Но это так, просто примечание к сарказму) Понятно, что в данном случае будет ругаться вообще парсер, т.к. это ошибка в грамматике. Чтобы ругался компилятор - надо написать:

      <?php
      
      function example(): void
      {
          return 42;
      }
      

      В этому случае будет ругаться именно компилятор уже после разбора в AST и ещё до запуска кода (интерпретации то бишь) т.к. не осилил генерацию опкодов из-за ошибок в типах.


  1. FanatPHP
    03.09.2024 15:26
    +3

    Я даже не знаю, кто здесь прекраснее - никому неизвестный Эрик А. Манн, который пишет книги "на отвали", или обкурившийся школьник, который его переводил.

    Этот "инженер-программист" выступает в роли прапорщика из анекдота, "Летают, только низенько-низенько". Сначала он пишет рецепт, что на боевом сервере все ошибки надо подавить. А потом при обсуждении вдруг вспоминает, что вообще-то не надо. Так надо или нет? Вроде не мальчик, мог бы определиться уже, за столько-то лет разработки.

    Или совершенно бессмысленные примеры кода, на которые смотришь и думаешь - "а нафига вообще в язык завезли такой дурацкий функционал? Я что - сам не выведу сообщение об ошибке, без всяких исключений?"

    Ну и самая знаменитая бессмыслица, куда же без неё: "Вы знаете, что функция может выдать ошибку, поэтому важно обернуть любой ее вызов в оператор try". Ага, то-то я вижу каждый вызов require, или любой оператор деления обёрнутый в try... А обрабатывать пойманное исключение автор рекомендует фразой "у вас тут бешеный суслик". Просто прелестно. представляю код, написанный по этим рецептам.

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

    There might be specific pages with known errors

    превратить в нелепую отсебятину

    Возможно, существуют страницы с записями об уже известных ошибках

    ?

    Кто должен следить, чтобы из текста не пропадало форматирование, и непереводимые термины

    Any other Error or Exception thrown by the function

    не стали простым текстом, полностью меняющим смысл,

    Любая другая ошибка или исключение будут перехвачены

    поскольку ошибки этот код ловить не будет?


    1. FanatPHP
      03.09.2024 15:26
      +2

      Кстати, кто догадается, что имеется в виду в нижеприведенном сложносочиненном предуведомлении - тому приз

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

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


  1. SidVisceos
    03.09.2024 15:26
    +1

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

    Это даже на пособие "для чайников" не тянет. Просто распечатанная дока с кривым местами переводом.