Привет, Хабр! Меня зовут Виталий Киреев, я руководитель разработки SpaceWeb. В статье расскажу, как мы с командой проводили рефакторинг кода при переходе с PHP 7.4 на PHP 8 и на что заменили одну из самых популярных функций — create_function. 

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

Контекст: когда стало ясно, что нужен рефакторинг

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

В части проектов SpaceWeb много legacy-кода. Мы поддерживаем проекты с версии PHP 5.6, которая появилась в 2014 году. С некоторой периодичностью переходим на новые версии PHP, постоянно обновляя код.  

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

Когда вышла версия PHP 7.4, функция create_function, которая часто использовалась в нашем коде, стала deprecated, то есть перестала поддерживаться и работать. Из-за этого стали появляться ошибки в коде. Тогда и стало ясно, что нужен рефакторинг.

Подсветить часть проблем в коде нам помогли статические анализаторы кода — PHP Code Style Fixer и PHPstan. Но большую часть кода мы проверяли и обновляли вручную. В первую очередь нужно было придумать, на что заменить  популярную create_function. Сперва начали переход на анонимную функцию и замыкание. А спустя время внедрили и стрелочные функции, чтобы повысить читаемость кода и решить дополнительные задачи.

Анонимные функции: как работают и как применить замыкание

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

На самом деле, create_function, которую мы обычно использовали до PHP 8 — это классический пример использования анонимной функции. Ее часто применяли для обработки данных в массиве. Функция содержит два аргумента — массив аргументов функции и сам код.

create_function('$v', 'return $v > 1;')

Когда эта функция стала deprecated в PHP 8, мы начали рефакторинг кода. Анонимные функции стали выглядеть так:

$filterValue = 20;
$newArray = array_filter($oldArray, function($v) use ($filterValue)  { return $v > $filterValue;});

Но что, если нам потребуется вызвать эту функцию в нескольких разных местах? Тогда можно использовать замыкание. Это позволит вызывать функцию, используя ее как переменную.

$filterValue = 20;
$func = function($v) use ($filterValue) { return $v > $filterValue;};
$func(23); // true
$func(19); // false

А как быть, если нам нужно использовать переменные из окружения, и значения переменных  будут меняться в процессе выполнения программы? Здесь нам помогут стрелочные функции.

Стрелочные функции: в чем отличие от анонимных и где лучше использовать

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

$inn = $this->getContractInn();
$newArray = array_filter($oldArray, fn($v) => $v === $ inn);

В этом примере была задача отфильтровать контракты и соотнести их с нужным ИНН. Если бы решали ее с помощью анонимных функций, то под каждый ИНН пришлось бы писать отдельную функцию. А еще везде использовать use. 

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

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

Итоги

Для замены create_function в PHP 8 можно использовать как анонимные, так и стрелочные функции.

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

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

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


  1. SerafimArts
    11.07.2024 11:01
    +15

    Простите за циничность, но это рофл такой? Со всем уважением к труду, т.к. написать статью - это время и этот самый труд, но...

    Если не путаю, то create_function - это шляпа, которая могла использоваться (но не рекомендовалась) во времена PHP 5.2. Про неё забыли перестали использовать уже во времена PHP 5.3 (около 15 лет назад).

    Если мы говорим о новичках, как вы позиционируете статью, то они даже о ней не узнают никогда, т.к. о таком костыле помнят только бородатые скуфы, заставшие ещё PHP 4. Следовательно, непонятно вообще зачем про это говорить?

    Ну разве что если хотите придумать подводку/аргумент к статье зачем в очередной раз писать про анонимки, которым как раз почти 15лет уже. Других причин я не могу понять.


    1. FanatPHP
      11.07.2024 11:01
      +1

      Это ж spaceweb. У них все статьи такие. Довольно странная маркетинговая политика. Зачем это всё писать, а потом втихаря подправлять голоса после разгромной критики - для меня загадка. Комментарии вида "Ну поставили вы джуна руководить разработкой, бывает, но зачем позориться-то на весь интернет?" и "Это писал человек, который не смыслит в веб-уязвимостях вообще ничего" им божья роса.


      1. murzix
        11.07.2024 11:01

        Так для них любой коммент это тоже активность и отклик. Так скоро будем читать о пользе GOTO и превосходстве процедурной парадигмы. Чисто ради бурлений =)


  1. tommyangelo27
    11.07.2024 11:01
    +2

    Версия PHP 7.4 "внезапно" вышла пять лет назад... У вас статья с тех пор в черновиках висела?


  1. Ukrainskiy
    11.07.2024 11:01

    Казалось бы, при чем тут PHP 8


  1. Ukrainskiy
    11.07.2024 11:01
    +1

    Даже интересно, кто это плюсует, с учетом того, что за сутки компания не удасужилась исправить в статье такое.
    Даже интересно, кто это плюсует, с учетом того, что за сутки компания не удасужилась исправить в статье такое.