Лямбда-функции и замыкания
Конечно многие из нас знакомы с этим понятием, однако данная статья рассчитана на новичков. В данном посте постараюсь рассмотреть данный феномен и привести примеры использования. Для начала необходимо понять что же такое лямбда-функция. Итак, лямбда-функция, часто ее называют анонимной, т. е. функция при определении которой не нужно указывать ее имя. Возвращаемое значение такой функцией присваивается переменной, через которую в последствие эту функцию можно вызывать.
До выхода PHP 5.3 определять лямбда-функции было возможно, но их нельзя было назвать полноценными. Сейчас я приведу пару примеров и продолжим рассматривать данные понятия.
$/\',$match[1],$m) ? "cms_$m[1]" : $match[1]);'); $query = 'SELECT * FROM '; $regExp = '/([^<"\']+|\'(?:\\\\\'.|[^\'])*\'|"(?:\\\\"|[^"])*"|<[^>)/'; echo preg_replace_callback($regExp, $temp, $query); //Выведет SELECT * FROM cms_documents ?>
Конечно динамическое создание функций не решает всех проблем, однако порой написание такой одноразовой функции может быть полезным. Можно расширить наш пример:
query = $query; $this->prefix = $prefix; > public function replaceCallback( $match ) < return ( preg_match('/^<(.*)>$/',$match[1],$m) ? ( empty($this->prefix) ? $m[1] : "prefix>_$m[1]" ) : $match[1] ); > public function build($query) < static $regExp = '/([^<"\']+|\'(?:\\\\\'.|[^\'])*\'|"(?:\\\\"|[^"])*"|<[^>)/'; return preg_replace_callback($regExp, array(&$this, "replaceCallback"), $query); > >; $builder = new Builder(‘cms’); echo $builder->build(“SELECT * FROM ”); //Выведет SELECT * FROM cms_documents ?>
Понятие замыкания наверняка знакомо программистам на JavaScript, а так же программистам на многих других языках. Замыкание — это функция, охватывающая или замыкающая текущую область видимости. Что бы понять все это, рассмотрим пример:
; echo $x(8); // Выведет 80 ?>
Как вы уже могли заметить, функция не имеет имени и результат присваивается переменной. Лямбда-функция, созданная таким образом, возвращает значение в виде объекта типа closure.
В PHP 5.3 стало возможно вызывать объекты как если бы они были функциями. А именно магический метод __invoke() вызывается каждый раз, когда класс вызывается как функция.
Переменные недоступны внутри функции, если они не объявлены глобальными, так же переменные из дочернего контекста недоступны если только не используется зарезервированное слово use. Обычно в PHP переменные передаются в замыкание значением, это поведение можно изменить с помощью ампермсанда перед переменной в выражении use. Рассмотрим пример:
multiplier = $multilier; > public function getClosure() < $mul = &$this->multiplier; return function( $number ) use( &$mul ) < return $mul * $number; >; > > $test = new ClosureTest(10); $x = $test->getClosure(); echo $x(8); // Выведет 80 $test->multiplier = 2; echo $x(8); // Выведет 16 ?>
Если убрать амперсанды то оба раза выведется 80, т. к. переменная $mul внутри замыкания будет копией, а не ссылкой.
Итак, осталось только выяснить как это можно применить на практике.
Рассмотрим пример:
$arg ) $argv[$i] = mysql_escape_string($arg); array_unshift($argv, $self->build($query)); return call_user_func_array( “sprintf”, $argv); >; > >; $builder = new QueryBuilder(); $deleteBook = $builder->getQueryObject(“DELETE FROM WHERE $deleteBook( $_GET[‘id’] ); ?>
Этот пример уже можно использовать для достаточно гибкого прототипирования. Достаточно объявить методы для всех SQL-операций с объектом.
Автор не призывает всех придерживаться такой практики, равно как и не считает что так лучше, все вышеописанное лишь пример использования, причем возможно не самый техничный и интересный, и не более того.
UPD Говоря о том самом длинном регулярном выражении, я не стал подписывать его в комментариях и решил вынести сюда. Оно лишь ищет строки в одинарных и двойных кавычках, а так же имена таблиц и экранирует их.
Анонимные функции PHP в хуках WordPress
Анонимные PHP функции, также известные как замыкания (closures) или лямбда-функции, позволяют создавать функции не имеющие определенных имен. В WP они наиболее удобны в качестве значений callback-функций в хуках (фильтрах и событиях). Появились в PHP они начиная с версии 5.3, немного доработаны в 5.4. Получается, что появились они давно, а если заглянуть в код WordPress плагинов, то там их практически нет. Например, в том же javascript, они используются повсюду. Почему так? Для начала давайте посмотрим, как выглядит хук с обычной функцией и замыканием:
// 1. хук с обычной функцией add_filter( 'the_content', 'filter_the_content'); function filter_the_content( $content ) < // изменяем контент return $content; >// 2. хук с анонимной функцией add_filter( 'the_content', function( $content )< // изменяем контент return $content; >);
У первого способа фактически одно преимущество: так как функция имеет имя, хук при необходимости можно удалить, используя функцию remove_filter() или remove_action() . Это тем, кто будет пользоваться вашим кодом. Поэтому в плагинах и темах анонимные функции редкие гости. А вот если хук создан с помощью замыкания, то удалить его уже никак нельзя.
Преимущества анонимных функций в WP
- Короткий синтаксис (удобнее писать).
- Более чистое глобальное пространство имен и не нужно переживать за конфликты имен функций.
- Не нужно придумывать названия для callback функций (иногда это отнимает немало времени).
Недостатки анонимных функций в WP
- Невозможно удалить хук, созданный на базе анонимной функций.
- Невозможно использовать одну и туже функцию в нескольких хуках — для каждого нужно писать отдельную анонимную функцию. Пример:
add_filter( 'the_content', 'filter_the_content' ); add_filter( 'widget_text', 'filter_the_content' ); function filter_the_content( $content ) < // Изменяем контент return $content; >// или add_filter('the_content', function( $content )< // что-то делаем return $content; >); add_filter('widget_text', function( $content )< // что-то делаем return $content; >);
Впрочем, этот недостаток неоднозначный, потому что в PHP (с версии 5.3) как и в JS анонимную функцию можно поместить в переменную и затем использовать переменную в качестве коллбек-функции хука:
$my_callback = function( $content )< // что-то делаем return $content; >; add_filter( 'the_content', $my_callback ); add_filter( 'widget_text', $my_callback );
use в анонимных функциях
Еще одна фишка лямбда-функций — это использование переменных из текущей области видимости, с помощью оператора use:
$var = 'Привет мир!'; $func = function() use ( $var ) < echo $var; >; $func(); //> Привет мир!
Переменные передаются как значение, но можно передать и ссылку на переменную, указав & :
$var = 'Привет мир!'; $func = function() use ( & $var ) < $var = $var .' Мы в гости!'; >; $func(); // вызовем echo $var; //> Привет мир! Мы в гости!
Когда стоит применять анонимные функции в WordPress?
- Если код не предназначен для широкого использования.
- Возможность удаления callback функции не нужна и не будет нужна.
- Не нужна поддержка PHP версий ниже 5.3.
Теперь, немного разобравшись что к чему, можно ответить на вопрос, — «Почему в WP не используются анонимные функции?» — Потому что код WP должен быть максимально гибким, а замыкания как раз не про это.
Подробнее про анонимные PHP функции читайте в документации.
Замыкание — PHP: Функции
Каждая функция, в своем теле, задает так называемую область видимости. Внутри этой области доступны только те переменные, которые были определены там же. Переменные определённые вне функции, не видимы внутри неё.
$age = 5; function generate() print_r($age); > generate();
Этот код выдаст предупреждение PHP Notice: Undefined variable: age. Переменная $age определена вне контекста функции и невидима внутри. Точно такое же поведение и у анонимных функций.
$age = 5; $generate = function () print_r($age); >; $generate(); // PHP Notice: Undefined variable: age
Но передача аргументов все же возможна, и делается она благодаря «замыканию». С помощью этого механизма можно «захватить» переменные из внешнего окружения и использовать их внутри. Правда, только для анонимных функций. Термином «замыкание» нередко называют и саму функцию, которая осуществила захват внешних переменных.
$age = 5; $generate = function () use ($age) print_r($age); >; $generate(); // 5
Захват переменных осуществляется с помощью ключевого слова use и списка переменных, который идёт после этого слова. Крайне важно осознать, что use используется при определении функции, а не её вызове.
Может возникнуть закономерный вопрос, зачем это делать, если переменные можно передать в сам вызов.
$age = 5; $generate = function ($age) print_r($age); >; $generate($age); // 5
Замыкания полезны в тех случаях, когда функция определяется в одном месте, а используется в совершенно другом. Замыкание позволяет не таскать за собой гору переменных. А в некоторых ситуациях без них просто не обойтись. Вспомните функцию without() из пакета Funct . Эта функция принимает на вход массив и значение, а возвращает новый массив полученный фильтрацией старого по переданному значению. Его реализация, построенная на функциях высшего порядка, подразумевает фильтрацию. Сложность возникает при описании предиката, ведь внутри анонимной функции нужно сравнивать текущее значение и переданный элемент. Замыкание позволяет решить эту задачу просто.
function without(array $items, $value) $filtered = array_filter($items, function ($item) use ($value) return $item !== $value; >); // Сбрасываем ключи return array_values($filtered); > without([3, 4, 10, 4, 'true'], 4); // [3, 10, 'true']
Без добавления use ($value) ничего не получится — $value не виден внутри анонимной функции.
В замыкание можно передать данные по ссылке, что позволит их менять внутри. Но делать так никогда не стоит. Подобное использование создаёт невероятно сложный в отладке и сопровождении код. Единственная причина, по которой можно использовать передачу по ссылке — рекурсия внутри анонимной функции. Подробнее об этом в другом курсе.
Стрелочные функции
Главная цель введения стрелочных функций – создание более лаконичного кода. Поэтому кроме сокращенного синтаксиса, они получили автоматический захват внешних переменных. Другими словами, при использовании стрелочных функций нам не нужен use .
$age = 5; $generate = fn() => print_r($age);
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Анонимные функции PHP
Анонимная функция — это функция без какого-либо определенного пользователем имени. Такая функция также называется замыканием или лямбда-функцией. Иногда вам может понадобиться функция для одноразового использования. Замыкание — это анонимная функция, которая закрывается над средой, в которой она определена. В ней нужно указать ключевое слово use. Наиболее распространено использование анонимной функции для создания встроенной функции обратного вызова.
$var = function ($arg1, $arg2) < return $val; >;
Между ключевым словом function и открывающей скобкой нет имени функции.
После определения функции ставится точка с запятой, поскольку анонимные определения функций являются выражениями.
Функция присваивается переменной и вызывается позже, используя ее имя.
Когда она передается другой функции, которая может вызвать его позже, он называется обратным вызовом.
Верните его из внешней функции, чтобы он мог получить доступ к переменным внешней функции. Это называется закрытием.
Пример анонимной функции
; echo "cube of 4 prettyprint"> cube of 4 = 64
Анонимная функция обратного вызова
В следующем примере анонимная функция используется в качестве аргумента для встроенной функции usort(). Функция usort() сортирует данный массив с помощью функции сравнения
$y; >); foreach ($arr as $x) < echo $x . "\n"; >?>
Это приведет к следующему результату. −
3 10 21 54 70
Анонимная функция как замыкание
Замыкание также является анонимной функцией, которая может получить доступ к переменным вне своей области действия с помощью ключевого слова use
; echo "marks=285 percentage prettyprint"> marks=285 percentage=95
Автор этого материала — я — Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML — то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.