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

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

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

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

PHP 7.x - P35: функции, определяемые пользователем
PHP 7.x - P36: аргументы функций
PHP 7.x - P37: функции, возвращающие значения
PHP 7 .x - P38: Функции с переменными
PHP 7.x - P39: Анонимные функции
PHP 7.x - P40: используйте ключевое слово
PHP 7.x - P41: Стрелочные функции

Я кратко резюмирую несколько концепций, чтобы общая картина имела больше смысла. Мы создадим функцию add_one_to_x (), которая принимает один аргумент. Затем функция добавит 1 к аргументу и выведет результат.

Пока что довольно стандартно. Мы передали целое число в качестве аргумента. Вы можете думать об этом так, что значение 5 было присвоено переменной (параметру) $ x. Только представьте, что мы переместили $ x в тело функции. Результат будет тот же.

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

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

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

Знаете ли вы, что в PHP есть еще один способ вызова функций?

Вы, вероятно, вызывали функции, как я в строке 7 выше. Но в PHP есть встроенная функция call_user_func (), которая принимает строку в качестве аргумента. Эта строка - имя функции, которую вы хотите вызвать. Оператор call_user_func («say_hello») в строке 8 делает то же самое, что и say_hello () в строке 7. Здесь нет никакого обмана: это буквально еще один способ вызвать функцию.

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

В приведенном выше примере два аргумента передаются функции animal_says (). Затем эта функция эхо выведет Кот говорит, что собаки отстой. Я создал два оператора echo, поскольку в следующий раз мы будем изменять эту функцию.

Давайте изменим второй оператор echo. Вместо того, чтобы напрямую повторять аргумент $ animal_saying, мы вызовем другую функцию, которая сделает это за нас.

Начнем с создания новой функции cat_says (). Эта функция принимает один аргумент и отображает значение при вызове. Функция cat_says () вызывается из функции animal_says (). Это были изменения. Давайте рассмотрим этот пример и посмотрим, что происходит. Мы еще не добрались до концепции функций обратного вызова, но мы очень близки.

  1. PHP достигает строки 12. Вызывается функция animal_says () и передаются два аргумента: строки Cat и Dogs suck.
  2. PHP входит в функцию animal_says () в строке 3. Первый аргумент присоединяется к строке «говорит» и выводится.
  3. Второй аргумент, Собаки отстой, передается в качестве аргумента функции cat_says ().
  4. PHP входит в функцию cat_says (). Аргумент Собаки - отстой повторяется.
  5. PHP выходит из функции cat_says () и возвращается к функции animal_says ().
  6. PHP выходит из функции animal_says () и завершает выполнение программы после строки 12.
  7. Итоговый результат: Кот говорит, что собаки отстой.

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

Собаки обиделись. Они также хотят иметь свою собственную функцию. Давайте создадим функцию dog_says (). Они хотят всегда говорить, что Кошки - отстой.

Представим, что у нас нет прямого доступа к функциям cat_says () и dog_says () и что мы только имеем доступ к animal_says () (вы увидите эту концепцию в объектно-ориентированном программировании, о котором мы расскажем в следующих статьях).

Если мы вызовем функцию animal_says () и передадим ей аргумент Cat, мы получим следующий вывод: Cat говорит, что собаки отстой. Если мы вызовем функцию animal_says () и передадим ей аргумент Dog, мы получим следующий результат: Собака говорит, что собаки отстой. Это не так. что мы хотим, однако; мы хотим, чтобы он сказал: Собака говорит, что кошки отстой. В настоящее время функция cat_says () жестко запрограммирована в строке 5. Мы могли бы выполнить оператор if / else и вызвать функцию cat_says (), если Cat передается аргумент и вызывает функцию dog_says (), если передан аргумент Dog.

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

Давайте сейчас избавимся от этой головной боли. Разве не было бы неплохо просто передать нужную функцию и вызвать ее автоматически без оператора if / else? Помните функцию call_user_func ()? Если мы передадим ему имя функции, мы сможем вызвать конкретную функцию.

Наша функция animal_says () должна будет принять второй аргумент. Этот второй аргумент будет именем функции, которую мы хотим вызвать. Затем мы передадим этот строковый аргумент функции call_user_func (), и PHP вызовет эту функцию.

Давайте рассмотрим этот пример и убедимся, что получаем желаемые результаты.

  1. Функция animal_says () вызывается в строке 16, и ей передаются два аргумента: строка Cat и строка cat_says.
  2. PHP входит в функцию animal_says в строке 3. Он входит в тело функции и выполняет инструкцию echo в строке 4: Cat говорит.
  3. Затем он переходит к следующей строке (5) и передает аргумент $ animal_function функции call_user_func (). Строка «cat_says» передается в call_user_func (). Сама функция call_user_func () вызывает функцию cat_says (), которая, в свою очередь, выводит эхо из Dogs suck.
  4. PHP выходит из функции cat_says () и возвращается к функции animal_says ().
  5. PHP выходит из функции animal_says () и возвращается к строке 16. Оператор в строке 16 завершается, и PHP переходит к строке 17.
  6. PHP вызывает функцию animal_says () и передает ей два аргумента: строку Dog и строку dog_says.
  7. PHP входит в функцию animal_says в строке 3. Он входит в тело функции и выполняет инструкцию echo в строке 4: Dog говорит.
  8. Затем он переходит к следующей строке (5) и передает строку dog_says в call_user_func (). Сама функция call_user_func () вызывает функцию dog_says (), которая, в свою очередь, эхо из Кошки сосут.
  9. PHP выходит из функции dog_says () и возвращается к функции animal_says ().
  10. PHP выходит из функции animal_says () и возвращается к строке 17. Оператор в строке 17 завершается, и PHP завершает программу.

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

Наши кошки и собаки сели и выработали некоторые отличия. Оказывается, они хотят иметь возможность говорить разные вещи, а не только Собаки - отстой или Кошки - отстой. Функции cat_says () и dog_says () необходимо изменить, чтобы они могли принимать аргумент и выводить этот аргумент. call_user_func () имеет второй необязательный параметр, который позволяет пользователю передавать аргумент функции.

call_user_func( $function_name, $function_arg = "" )

Вывести call_user_func («dog_says», «Hello») будет то же самое, что вызвать функцию dog_says () следующим образом: dog_says («Hello») . Давайте применим эти принципы на практике.

Функция animal_says () теперь принимает три аргумента:

  1. строка $ animal_type, которая будет отображена в строке 4.
  2. строка $ animal_function, которая будет передана в функцию call_user_func (), чтобы она могла выполнить вызов функции.
  3. строка $ say, которая также будет передана в функцию call_user_func (). На этот раз он будет передан в качестве аргумента функции, которую требуется выполнить функции call_user_func ().

Итак, в строке 16 вызывается функция animal_says (), которой передаются три аргумента: строки Cat, cat_says, и Я люблю собак. Cat выводится эхом в строке 4, а два других аргумента передаются в функцию call_user_func () в строке 5. call_user_func () выполняет вызов функции. cat_say ($ say) и передает ему аргумент Я люблю собак. PHP выполняет инструкцию echo в строке 9, Мяу: я люблю собак. Процесс повторяется в строке 17 с новыми аргументами, которые запускают dog_says () вызываемая функция.

Нам не нужно использовать функцию call_user_func () для вызова нашей функции. Помните переменные функции? Функции переменных могут быть полезны, когда вы хотите вызывать функцию динамически на основе значения, хранящегося внутри переменной. Вкратце, вы можете сохранить имя функции в виде строки внутри переменной и вызвать функцию, добавив к переменной пару круглых скобок.

Мы можем использовать эту концепцию для замены нашего call_user_func (), поскольку имя функции хранится внутри нашего параметра $ animal_function. Мы просто заменим call_user_func ($ animal_function, $ say) на $ animal_function ($ say); мы просто изменяем строку 5.

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

Затем единственное изменение, которое мы собираемся сделать, - это изменить имя параметра с $ animal_function на $ callback. Этот параметр обычно вызывается $ callback, чтобы явно указать, что функция будет вызываться внутри этой функции.

Мы здесь? Возможно ли, что мы наконец-то займемся функциями обратного вызова? да. До сих пор мы передавали имя (строку) функции, которую хотим вызвать, внутри функции animal_says (). Но помните, что функция обратного вызова - это функция, переданная как аргумент, а не строка, переданная как аргумент.

Давайте заменим функции cat_says () и dog_says () на анонимные. Мы присвоим их переменным $ cat_says и $ dog_says. Когда мы их изменяем, мы можем передать переменную $ cat_says и переменную $ dog_says в качестве аргументов вместо строк cat_says и dog_says. Прежде чем мы это сделаем, давайте кратко рассмотрим анонимные функции и способы их использования.

В приведенном выше примере мы создаем переменную $ cat_says и назначаем ей анонимную функцию (закрытие). Это закрытие имеет один параметр, $ cat_saying, и echoes out Meow:, за которым следует значение параметра (значение $ cat_saying ). Замыкание вызывается в строке 7 путем добавления круглых скобок к функции $ cat_says () и передачи аргумента Я люблю собак. После выполнения функции вы получите следующий результат: Мяу: я люблю собак.

Мы взяли на себя одну функцию; теперь давайте просто изменим вторую функцию, dog_says (), и передадим две переменные функции animal_says (). Нам не нужно ничего изменять внутри, поскольку функции переменных и анонимные функции технически имеют одинаковый синтаксис.

Глядя на код, мы изменили функцию cat_says () на закрытие в строке 8. Мы также изменили функцию dog_says () на закрытие в строке 12. Мы ' re затем передает закрытие $ cat_says и закрытие $ dog_says в качестве аргументов в строках 16 и 17. Это то, что разработчики, похоже, не могут понять, поэтому я ' м собираюсь вытянуть это. Давайте посмотрим на строку 16 и сосредоточимся на втором аргументе. Если вам нужно напомнить о передаче строк в качестве аргументов, вы можете взглянуть на первую иллюстрацию в начале этой статьи.

Поскольку $ cat_says равно закрытию, мы можем просто заменить аргумент закрытием. Так что просто представьте, что мы засунули всю функцию в аргумент.

Как видите, закрытие передается как аргумент функции animal_says (). Замыкание назначается параметру $ callback. Теперь мы знаем, что содержит переменная $ callback; он просто держит закрытие. Как мы называем закрытие? Добавляя круглые скобки к имени переменной и передавая ей любые аргументы. Давайте теперь расширим это и посмотрим, как действует третий аргумент.

  1. Аргумент передается при вызове функции animal_says ().
  2. Он назначается как значение третьего параметра.
  3. Он передается в вызов функции $ callback ().
  4. Перешло на закрытие.
  5. Он эхом изнутри крышки.

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

Давайте посмотрим на передачу анонимных функций в качестве аргументов. На этот раз мы не будем создавать закрытие $ dog_says и $ cat_says. Мы просто передадим эти замыкания напрямую в качестве аргументов.

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

  1. В строке 8 выполняется вызов функции animal_says ().
  2. Первый аргумент - это строка Cat. Эта строка назначается параметру $ animal_type.
  3. Второй аргумент - это функция: function ($ cat_saying) {…};. Эта функция назначается параметру $ callback.
  4. Третий аргумент - строка Я люблю собак. Эта строка присваивается параметру $ say.
  5. Внутри функции animal_says () первый аргумент отображается в строке 4.
  6. В строке 5 мы просто вызываем закрытие, присвоенное параметру $ callback, добавляя к нему пару круглых скобок и передавая ему значение параметра $ say. .

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

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

В строке 8 в качестве второго аргумента передается стрелочная функция. Функция стрелки примет аргумент, как только он будет вызван в строке 5. Это аргумент Я люблю собак. Он вернет Мяу: Я люблю собак. Вся строка будет отображена в строке 5.

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