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

Обычные функции

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

function hello() {
	return 'Hello World!';
}

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

function createMessage($verb, $subject) {
    return "$verb $subject!";
}

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

function createMessage(string $verb, string $subject): string {
	return "$verb $subject!";
}

Получаем вот такие результаты вызова:

createMessage('Hello', 'World'); // 'Hello World!'
createMessage(123, false); // '123 !'

В функцию все еще можно передать параметры неверного типа. Спасибо приведению типов, которое преобразует и числовое и булево значение в строковое. Но можно включить режим строгой типизации, добавив в начало файла строку declare(strict_types=1):

declare(strict_types=1);

function createMessage(string $verb, string $subject): string {
	return "$verb $subject!";
}

createMessage('Hello', 'World'); // 'Hello World!'
createMessage(123, false); //Fatal error: Uncaught TypeError...

Вот теперь все будет точно работать как надо. Только объявление строгой типизации нужно добавлять в каждый файл – компромисс для обратной совместимости PHP с предыдущими версиями.

Это все база, от которой будем далее отталкиваться. Но если хочется проверить свои знания функций в PHP, то можно обратиться к разделу в официальной документации (EN/RU)

Вызываемый класс – Обращение к объекту, как к функции

Хотя в этой статье мы больше говорим о процедурном подходе, стоит упомянуть, что и экземпляры классов можно вызывать как обычные функции. Для этого в классе должен быть реализован метод __invoke:

class Message {
    public function __invoke(string $verb, string $subject): string {
		return "$verb $subject!";
	}
}

$message = new Message();

$message('Hello', 'World'); // Hello World!

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

Анонимные функции (Замыкания/Closures)

У функций описанных в предыдущем разделе есть важный общий момент – у них всех есть имя. Но мы можем создать и вариант функции без имени:

function(string $verb, string $subject): string {
	return "$verb $subject!";
};

Такая функция называется анонимной ведь у нее нет имени. Еще в PHP такие функции называют замыканиями или closure. Это может сбить с толку людей, знакомых с JS, где замыкание – это про получение доступа к переменной вне функции. Но разработчики PHP не решили нас запутать, а просто взяли концепцию из C подобных языков, в которых замыкания – синтаксический сахар для создания анонимных функций. И в PHP тоже есть класс Closure, и анонимная функция под капотом создает экземпляр этого класса, но о нем позднее, а пока вернемся к нашему примеру.

Хотя в нем синтаксически все верно, есть проблема – анонимную функцию никак не вызвать поэтому давайте запишем ее в переменную:

$createMessage = function(string $verb, string $subject): string {
	return "$verb $subject!";
};

И теперь, хотя функция анонимна и не может быть вызвана самостоятельно, у нас есть переменная, которая указывает на функцию и дает возможность с ней работать:

$createMessage('Hello', 'World'); // 'Hello World!'

И самый частый способ использования анонимных функций – это их передача, в качестве параметра, в другие функции.

Callback функции

В PHP есть специальный тип callable, который обозначает что-то, что можно вызвать как функцию. Он объединяет в себя различные типы, не все из которых выглядят очевидно:

Обычные функции / Строки

Функции созданные обычным способом можно вызвать, а значит они соответствуют типу callable. Давайте создадим такую:

function add(int $a, int $b): int {
	return $a + $b;
}

И передадим их в функцию array_reduce, которая преобразует массив данных в соответствии с правилами, описанными в переданной callback функции:

array_reduce([3,2,1], add, 0);

Иииии получим ошибку Undefined constant "add", а все, потому что в PHP строка вне кавычек и без доллара в начале считается за константу. Но есть решение этой проблемы – нужно всего лишь обернуть имя функции в кавычки и она заработает:

array_reduce([3,2,1], 'add', 0); //6

В целом любую функцию в PHP можно вызывать используя кавычки: 'add'(1,2), но это только для тех, кому платят за каждый символ в коде.

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

array_map('trim', [' foo ', 'bar ', ' baz']); //['foo', 'bar', 'baz']

Анонимные функции

В примере выше имя функции использовалось для указания на нее, но гораздо удобнее использовать анонимные функции, как callback функции. Доработаем предыдущий пример:

$add = function (int $a, int $b): int {
	return $a + $b;
};

И теперь воспользуемся ей при вызове функции array_reduce:

array_reduce([3,2,1], $add, 0); //6

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

array_reduce([3,2,1], function($a, $b) {
	return $a + $b;
}, 0); //6

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

Массивы

Точнее один вид массива, состоящий из двух элементов, где первый элемент – это экземпляр класса, а второй – публичный метод класса:

class Math {
	public function add (int $a, int $b): int {
		return $a + $b;
	}
}

В примере все та же функция add, только теперь это уже метод класса Math. Создадим экземпляр класса Math и воспользуемся методом add как callback функцией:

array_reduce([3,2,1], [new Math, 'add'], 0); //6

Проблема такой записи в том, что на ней может ломаться статический анализ кода текстового редактора и вместе с ним быстрый поиск метода. Хотя такое сейчас встречаю уже редко

Таким же способом можно вызывать и статические методы, только вместо экземпляра класса, нужно указать сам класс:

class Math {
	public static function add (int $a, int $b): int {
		return $a + $b;
	}
}

array_reduce([3,2,1], [Math::class, 'add'], 0); //6

First class callable syntax

"Зачем нам вызывать функции из строк, если мы можем явно создавать ссылки на функции?" подумали разработчики PHP, готовя релиз версии 8.1 и добавили фичу со странным названием из заголовка, но оно имеет смысл. В программировании "Первоклассными сущностями" называются те элементы программы, которые могут быть присвоены переменной, переданы как параметр, возвращены из функции. То есть First class callable - это ничто иное как уже известная нам анонимная функция (Closure/Замыкание).

Сам синтаксис же состоит из конструкции (...), которую нужно использовать после callable сущности для создания анонимной функции на ее основе:

class Math {  
	public function add(int $a, int $b): int {}  
	public static function addStatic(int $a, int $b): int {}  
	public function __invoke(int $a, int $b): int {}  
}  
  
$math = new Math();  
  
//Встроенные функция
strlen(...);
'strlen'(...);  

//Массивы
[$math, 'add'](...);  
[Math::class, 'addStatic'](...);
  
//Invokable объекты
$math(...);

//Методы объекта и класса
$math->add(...);  
Math::addStatic(...);

В итоге если вы работаете с версией PHP 8.1+, то вместо оборачивания функций в кавычки и создания массивов для передачи метода, можно использовать единый синтаксис (...)

Use - получение данных из родительского контекста

Попробуем умножить массив чисел на переменную определенную вне анонимной функции. По умолчанию анонимные функции в PHP не имеют доступа к внешним переменным, поэтому следующий пример работать не будет:

$multiplier = 2;  
  
array_map(function ($num) {  
    return $num * $multiplier; // Warning: Undefined variable $multiplier
}, [1, 2, 3]); // [0,0,0]

Анонимная функция должна захватить переменную из внешнего пространства имен в свое, но PHP в отличие от JS автоматически не захватывает переменные из родительских пространств имен. Поэтому переменные для захвата нужно указать вручную, воспользовавшись инструкцией use, и указать в ней требуемые переменные, в нашем случае переменную $multiplier:

$multiplier = 2;  
  
array_map(function ($num) use ($multiplier) {  
    return $num * $multiplier;
}, [1, 2, 3]); // [2,4,6]

Но на самом деле неверно про такой код говорить, что он захватывает переменную. Нет, он захватывает лишь значение переменной, что демонстрирует следующий пример:

  
$multiplier = 2;  
  
$func = function ($num) use ($multiplier) {  
    return $num * $multiplier;  
};  
  
array_map($func, [1, 2, 3]); // Ожидаемые [2,4,6]  
  
$multiplier = 4;  
  
array_map($func, [1, 2, 3]); // Все еще [2,4,6]

Здесь работают такие же правила, как и с передачей аргументов в функцию. По умолчанию примитивные типы и массивы передаются по значению (опустим детали оптимизации), и только экземпляры классов передаются по ссылке. Это легко продемонстрировать, заменим значение $multiplier на класс:

final class Multiplier {  
    public function __construct(  
        private int $multiplier  
    ) {}  
  
    public function getMultiplier(): int {  
        return $this->multiplier;  
    }  
  
    public function setMultiplier(int $multiplier): void {  
        $this->multiplier = $multiplier;  
    }  
}  
  
$multiplier = new Multiplier(2);  
  
$func = function ($num) use ($multiplier) {  
    return $num * $multiplier->getMultiplier();  
};  
  
array_map($func, [1, 2, 3]); // Умножаем на 2: [2,4,6]  
  
$multiplier->setMultiplier(4);  
  
array_map($func, [1, 2, 3]); // А теперь на 4: [4,8,12]

Для того чтобы в функцию передавать аргументы по ссылке, требуется чтобы перед именем параметра в теле функции стоял знак &. То же самое работает и с use, вернемся к последнему примеру с неверной логикой и починим его:

$multiplier = 2;  
  
$func = function ($num) use (&$multiplier) {  
    return $num * $multiplier;  
};  
  
array_map($func, [1, 2, 3]); // Ожидаемые [2,4,6]  
  
$multiplier = 4;  
  
array_map($func, [1, 2, 3]); // Тоже ожидаемые [4,8,12]

Теперь захватывается ссылка на переменную и ее изменения попадают в анонимную функцию.

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

class Multiplier {  
    public function __construct(  
        private int $multiplier  
    ) {}  

    public function getMultipliedArray(array $array): array {  
        return array_map(  
            function ($num) {  
                return $num * $this->multiplier; // Используем $this без use  
            },  
            $array  
        );  
    }  
}  
  
(new Multiplier(2))->getMultipliedArray([1,2,3]); // Результат - уже знакомый массив [2,4,6]

Если же внутри анонимной функции не предполагается использование переменной $this, то функцию стоит объявить статической. Тогда автоматический захват $this будет отключен. Это предотвращает потенциальную утечку памяти, в случае если указатель на экземпляр класса останется существовать внутри замыкания. Объявляется статическая анонимная функция следующим образом:

$f = static function() {}; // Больше никакого $this

Стрелочные функции

В PHP 7.4 появился способ автоматически захватывать внешние переменные в анонимных функциях. Для этого вместо привычного синтаксиса создания функции, нужно применить "стрелочный" или "сокращенный" вариант: fn (argument_list) => expr. Вот так выглядит применение стрелочной функции в примере с умножением массива:

$multiplier = 2;  
  
array_map(  
    fn($num) => $num * $multiplier,  
    [1, 2, 3]  
); // [2,4,6]

Конструкция use в данном случае не нужна. Захват происходит автоматически и только по значению. А также пропал оператор return, значение вычисленное в функции автоматически возвращается из нее.

А что делать если нужно выполнить несколько действий в одной стрелочной функции? Не использовать стрелочную функцию! В стрелочной может быть только одно выражение, И хотя это является ограничением, оно становится менее серьезным если уметь в функциональный подход, использовать тернарный оператор, использовать функции для преобразования данных (array_map вместо foreach цикла, например)

Попытка снятия ограничения была в 2022 году. Тогда было создано RFC Short Closures 2.0 по внедрению многострочных стрелочных функций в PHP 8.2, но ему не хватило перевеса в 1-2 голоса из 43 полученных для принятия в стандарт языка. Первая же версия этого RFC от 2015 года провалилась полностью. Возможно в будущем будет и третья уже удачная попытка.

Closure - как создается анонимная функция

Как я уже упоминал, анонимные функции – это синтаксический сахар для создания экземпляра класса Closure. Вот только создать экземпляр этого класса напрямую с помощью оператора new не получится, ведь у него приватный конструктор и сам класс финальный. Это подтверждает структура класса из официальной документации:

final class Closure {
	private __construct()
	
	public static bind(Closure $closure, ?object $newThis, object|string|null $newScope = "static"): ?Closure
	
	public bindTo(?object $newThis, object|string|null $newScope = "static"): ?Closure
	
	public call(object $newThis, mixed ...$args): mixed
	
	public static fromCallable(callable $callback): Closure
}

Но есть фабричный метод fromCallable, который из callable типа создает замыкание, например вот так:

function greet($name) { 
	return "Hello, " . $name; 
} 

$closure = Closure::fromCallable('greet'); 

$closure instanceof Closure; // true

На самом деле мы уже обсудили все варианты создание Closure из callable, когда говорили про First class callable syntax, ведь конструкция (...) – это тоже синтаксический сахар, только уже для метода Closure::fromCallable.

BindTo, Bind и Call - указание контекста

В структуре класса Closure еще осталось еще три метода. Все они нужны для изменения контекста вызова анонимной функции. Все три функции используются для подмены $this на требуемый объект, а bindTo и bind еще могут менять статическую область видимости.

bindTo

Начнем с метода bindTo. Так как Closure – это класс с методами, а анонимная функция – это экземпляр класса Closure, то у этой функции можно вызывать методы, как у обычного объекта. И метод bind мы как раз можем вызвать для изменения контекста:

class Num5 {  
    public int $num = 5;  
}  
  
class Num10 {  
    public int $num = 10;  
}  
  
$func = function (int $a): int {
    return $a * $this->num;  
};  
  
$func5  = $func->bindTo(new Num5());  
$func10 = $func->bindTo(new Num10());  
  
$func5(5); // 25  
$func10(5); // 50

Функция $func внутри себя обращается к объекту через $this, но сразу этот объект не определен. Требуется с помощью метода bindTo указать на требуемый $this. В примере выше на место $this становятся экземпляры классов Num5 и Num10.

То же самое можно провернуть и со статическими свойствами и методами. Только теперь вместо передачи экземпляра класса в первый параметр, нужно передать сам класс во второй параметр:

class Num5 {  
    static int $num = 5;  
}  
  
class Num10 {  
    static int $num = 10;  
}  
  
$func = function (int $a): int {
    return $a * static::$num;  
};  
  
$func5  = $func->bindTo(null, Num5::class);  
$func10 = $func->bindTo(null, Num10::class);  
  
$func5(5); // 25  
$func10(5); // 50

Но что самое интересное, таким образом можно получить доступ и к приватным свойствам класса. Для этого нужно использовать сразу оба параметра. Первый укажет на конкретный экземпляр класса, а второй изменит видимость private и protected методов:

class Num5 {  
    private int $num = 5;  
}  
  
class Num10 {  
    protected int $num = 10;  
}  
  
$func = function (int $a): int {  
    return $a * $this->num;  
};  
  
$func5  = $func->bindTo(new Num5(), Num5::class);  
$func10 = $func->bindTo(new Num10(), Num10::class);  
  
$func5(5); // 25  
$func10(5); // 50

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

bind

Все что было рассказано про метод bindTo верно и для bind. Только вызывается он статически на классе Closure и callable переменная передается в него первым параметром, а $this и контекст – вторым и третьим:

class Num5 {  
    private int $num = 5;  
}  
  
class Num10 {  
    protected int $num = 10;  
}  
  
$func = function (int $a): int {  
    return $a * $this->num;  
};  
  
$func5  = Closure::bind($func, new Num5(), Num5::class);  
$func10 = Closure::bind($func, new Num10(), Num10::class);  
  
$func5(5); // 25  
$func10(5); // 50

call

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

class Num5 {  
    private int $num = 5;  
}  
  
class Num10 {  
    protected int $num = 10;  
}  
  
$func = function (int $a): int {  
    return $a * $this->num;  
};  
  
$func->call(new Num5(), 2); //10  
$func->call(new Num10(), 2); //20

Также защищенные данные сразу станут доступны внутри функции вызванной с помощью call

Подводя итоги

В PHP уже с 7 версии есть множество способов работы с функциями. С версии 8 этих способов стало еще больше. Работая с современным PHP, не стоит везде использовать обычные (глобальные) функции. Нужно использовать еще анонимные и стрелочные, работать с областями видимости.

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

А как у вас с функциями в PHP? Какие подходы используете для работы в процедурном и функциональном стилях?

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


  1. JhaoDa
    25.07.2024 10:38
    +2

    есть проблема – анонимную функцию никак не вызвать

    Серьёзно?


    1. Daniel217D Автор
      25.07.2024 10:38

      Серьёзно. В примере с этим комментарием функция бесполезна


      1. arokettu
        25.07.2024 10:38
        +4

        а как же?

        (function ($s) { 
            echo $s;
        })('123');
        


        1. Daniel217D Автор
          25.07.2024 10:38

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

          И в целом абзац про iife стоит добавить. Спасибо за комментарий)


  1. bolk
    25.07.2024 10:38
    +3

    1) У вас форматирование не по PSR.
    2) Никакой путаницы с замыканиями нет. Посмотрите что такое замыкание: https://ru.wikipedia.org/wiki/Замыкание_(программирование)
    3) стрелочные функции тоже бывают static


    1. Daniel217D Автор
      25.07.2024 10:38
      +1

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

      Путаница есть, среди тех, кто считает, что у замыкания только одно определение. В статье, как и в википедии этот непонимание разрушается


      1. bolk
        25.07.2024 10:38

        Оно непривычное, поэтому неудобное.


        1. Daniel217D Автор
          25.07.2024 10:38
          +3

          Они неудобны для чтения на мобильных устройствах, так как одна кавычка занимает целую строку, а на мобильных устройствах строк мало.

          Я пишу код под разные стандарты и поддерживаю единообразие с помощью инструментов стат анализа.

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


  1. aleksejs1
    25.07.2024 10:38
    +2

    Очень крутая статья.

    Правда много объектных нюансов.

    Я бы ещё добавил сюда пару абзацов про области видимости (namespace), и что с ними тоже можно играться при построении грамотного процедурного кода.

    Вспомнился небольшой забавный проектик, когда я пробовал переписать Symfony на процедурный стиль с целью более глубокого изучения symfony и попыткой доказать, что без ООП тоже можно жить: git


    1. Daniel217D Автор
      25.07.2024 10:38

      Спасибо!

      А можете подробнее расписать про комбинацию функций и областей видимости? Мне только обычный импорт вспоминается


      1. aleksejs1
        25.07.2024 10:38
        +1

        Возможно я употребил неправильную терминологию. В доках на русском это называется пространства имён.

        Ну и суть в изоляции имён функций, что бы небыло конфликтов, наприер с импортируемыми библиотеками.

        Например a.php:

        <?php
        
        namespace my\functions;
        
        function hello() {
          echo "world";
        }

        b.php:

        <?php
        
        use function \my\functions\hello as privet;
        
        include("a.php");
        
        \my\functions\hello(); //world
        
        privet(); //world
        
        hello(); //PHP Fatal error:  Uncaught Error: Call to undefined function hello() in b.php:11


  1. FanatPHP
    25.07.2024 10:38
    +9

    Статья, на мой взгляд в целом хорошая и нужная.

    Вот только я бы написал про про строгую типизацию поподробнее. Она всё-таки, целиком относится именно к функциям. Тем более что запустив пример createMessage(123, false)читатель не получит никакого InvalidArgumentException, а в статье не объясняется, как его получить.

    Заголовок "Вызываемый класс" я бы поменял на "Объект как функция" или "Обращение к объекту, как к функции", поскольку в примере у вас не класс, а объект, и непонятно, что имеется в виду под словом "вызываемый". Плюс обычно говорят "экземпляр" класса, а не "сущность".


    1. Rsa97
      25.07.2024 10:38
      +1

      Тем более что запустив пример createMessage(123, false) читатель не получит никакого InvalidArgumentException

      Читатель получит syntax error, unexpected end of file, поскольку отсутствует точка с запятой.


    1. Daniel217D Автор
      25.07.2024 10:38

      Спасибо за содержательный комментарий!

      Типизацию изначально вообще не думал упоминат, но в моих примерах она есть, поэтому затронута в статей, но без подробностей. Про типизацию в PHP планирую рассказать отдельно в будущем и сослаться из этой статьи для тех, кому интересно

      Про точность терминов - согласен. Поправлю


      1. FanatPHP
        25.07.2024 10:38

        Окей, не хотите писать про типизацию - дело ваше. Но оставлять в статье неверную информацию тоже не стоит. Надо или дописать одну строчку, поясняющую, как получить эту ошибку, или не писать про неё вовсе. И поправить синтаксис.


      1. supercat1337
        25.07.2024 10:38

        Полезная публикация! Спасибо.

        И про типизацию я бы почитал с удовольствием. Думаю многим будет интересно когда целесообразно использовать phpdoc.


        1. Rsa97
          25.07.2024 10:38
          +2

          Типизация в современном PHP не имеет никакого отношения к phpdoc.
          При включённом strict_types заданные в контракте функции типы аргументов и результата контролируются в момент исполнения кода и их несоответствие приводит к ошибке.
          Комментарии phpdoc нужны для разработчика. Они общепринятым способом описывают не только типы, но и назначение аргументов и результата функции и могут содержать информацию о поведении функции. Эта информация может использоваться IDE для показа подсказок. При этом несоответствие типов в phpdoc самим PHP не контролируется и проверяется, как правило, статическим анализатором (возможно, встроенным в IDE).


          1. supercat1337
            25.07.2024 10:38

            Спасибо, полностью согласен с вами. Конечно, в первую очередь интересует контроль типов статическим анализатором из IDE (пользуемся VSCode). Ловить исключение после запуска - это не так интересно) А что касается phpdoc, то там есть где можно мысли развернуться, например, описание возвращаемой структуры данных из функции. В общем, пусть автор знает, что мне это интересно))


            1. Daniel217D Автор
              25.07.2024 10:38

              PHPdoc круто работает в связке со стат анализом от phpstan или psalm. Можно описывать типы более подробно (non-empty-string вместо string например), использовать дженерики. И проверять код на ошибки во время CI


              1. Kahelman
                25.07.2024 10:38

                Чего только люди не придумают, лишь бы компиляторами не пользоваться…. Может пора уже завезти в язык фичу, которая будет эмулировать компилятор и проверку типов делать вместо всех этих костылей?


                1. Daniel217D Автор
                  25.07.2024 10:38
                  +1

                  Язык скриптовый, поэтому компиляции нет и не будет, иначе полная потеря обратной совместимости.

                  Но если очень хочется, то есть KPHP от VK - эта версия компилируемая, по набору фич соответствует 8.1 версии


                1. FanatPHP
                  25.07.2024 10:38
                  +1

                  А почему обязательно в язык? В чем принципиальная разница со сторонним инструментом, который прекрасно "эмулирует компилятор и делает проверку типов"?


  1. milkground
    25.07.2024 10:38

    Такая функция называется анонимной ведь у нее нет имени. Еще в PHP такие функции называют замыканиями или closure.

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


    1. Rsa97
      25.07.2024 10:38
      +1

      В PHP это одно и то же. Из официальной документации:

      Anonymous functions, also known as closures, allow the creation of functions which have no specified name.


      1. milkground
        25.07.2024 10:38

        Ну я же говорю - это отличный вопрос для собеседования, чтобы понять уровень разработчика)) Погуглите, что такое замыкание и почему оно так называется. После этого погуглите ключевое слово, которое в PHP из анонимной функции делает замыкание. После этого вы поймёте, что нельзя все анонимные функции называть замыканиями, так как они не работают как замыкания по умолчанию.

        А отличие от других языков в том, что в PHP по умолчанию в замыкание передаётся копия переменной, а не ссылка на неё. Второе отличие в том, что для импорта нужно явно указывать переменные.


        1. Rsa97
          25.07.2024 10:38
          +1

          После этого вы поймёте, что нельзя все анонимные функции называть замыканиями, так как они не работают как замыкания по умолчанию.

          Ну, это вы авторам PHP скажите, что нельзя. Они решили, что в PHP все анонимные функции являются замыканиями. А уж привязывать к ним переменные или нет и как именно привязывать - по значению или по ссылке - это дело разработчика.


          1. milkground
            25.07.2024 10:38

            Ну, это вы авторам PHP скажите, что нельзя. Они решили, что в PHP все анонимные функции являются замыканиями.

            Мне кажется, что это какая-то путаница, связанная с тем, что все анонимные функции являются экземплярами класса, который называется Closure (замыкание). То есть они как бы замыкания, но по факту не замыкания, если не использовать use.


        1. farrow
          25.07.2024 10:38

          для импорта нужно явно указывать переменные

          Не обязательно. Есть вариант со стрелочными функциями: using variables from the parent scope is always automatic