Привет, Хабр! Меня зовут Виталий Киреев, я руководитель разработки 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 и упростить синтаксис. А еще они лучше подходят для работы с большим количеством переменных или с частой сменой в процессе.