Что такое замыкание в php приведите пример
Анонимные функции
Анонимные функции, также известные как замыкания (closures), позволяют создавать функции, не имеющие определенных имен. Они наиболее полезны в качестве значений callback-параметров, но также могут иметь и множество других применений.
Пример #1 Пример анонимной функции
Замыкания также могут быть использованы в качестве значений переменных; PHP автоматически преобразует такие выражения в экземпляры внутреннего класса Closure. Присвоение замыкания переменной использует тот же синтаксис, что и для любого другого присвоения, включая завершающую точку с запятой:
Пример #2 Пример присвоения анонимной функции переменной
Замыкания могут также наследовать переменные из родительской области видимости. Любая подобная переменная должна быть объявлена в конструкции use.
Пример #3 Наследование переменных из родительской области видимости
// Сбросим message
$message = ‘hello’ ;
Результатом выполнения данного примера будет что-то подобное:
Пример #4 Замыкания и область видимости
// Базовая корзина покупок, содержащая список добавленных
// продуктов и количество каждого продукта. Включает метод,
// вычисляющий общую цену элементов корзины с помощью
// callback-замыкания.
class Cart
<
const PRICE_BUTTER = 1.00 ;
const PRICE_MILK = 3.00 ;
const PRICE_EGGS = 6.95 ;
Замыкания в PHP
или в PHP — это обычные функции, но без имени. Давайте рассмотрим пример такой функции:
В этом примере есть анонимная функция, но нет никакого смысла. Возникает вопрос — как использовать такие функции? Следующий пример поможет разобраться в этом:
Но этот пример не особо удобный для использования, ведь можно и простые функции использованть.
Как на практике используются замыкания
Обычно анонимные функции или замыкания в PHP используются чтобы передать их в качестве параметров другой функции. В PHP есть несколько встроенных функций, которые в качестве аргумента принимают замыкание, но об этом будет написано ниже.
Давайте ещё усложним наш пример.
Функция is_callable()
Анонимные функции в PHP реализованы с помощью встроенного класса Closure (PHP 5 >= 5.3.0, PHP 7). То есть каждая анонимная функция является объектом этого класса.
Конструкция use
При помощи ключевого слова use анонимной функции можно передать несколько переменных, они перечесляются в круглых скобках через запятую.
Также важно понимать, что конструкция use делает видимой именно переменные из родительской области видимости, а это не то же самое что и переменные из глобальной области видимости. Глобальная область видимости не меняется со сменой исполнения функций различной степени вложенности.
Аргументы в анонимных функциях
В анонимную функцию можно передать аргументы. Давайте для примера передадим один аргумент в нашу функцию.
С аргументами всё очень просто, тут анонимные функции ничем не отличаются от обычных.
Функция preg_replace_callback
Я обещал несколько встроенных в PHP функций, которые принимают в качестве аргумента замыкание, вот одна из них: preg_replace_callback
preg_replace_callback — выполняет поиск по регулярному выражению и замену с использованием callback-функции (замыкания).
Это краткий синтаксис, подробнее про возможности этой функции можно почитать на сайте мануала по PHP.
Функция call_user_func
Функция call_user_func — вызывает пользовательскую функцию, указанную в первом параметре. Возвращает результат функции, или FALSE в случае ошибки.
Примеры использования call_user_func :
Пример использования call_user_func в ООП.
Класс Closure
Также отмечу, что при вызове объекта как функции, вызывается магический метод __invoke (начиная с PHP5.3).
Применение замыканий в PHP
Введение в PHP 5.3 замыканий — одно из главных его новшеств и хотя после релиза прошло уже несколько лет, до сих пор не сложилось стандартной практики использования этой возможности языка. В этой статье я попробовал собрать все наиболее интересные возможности по применению замыканий в PHP.
Для начала рассмотрим, что же это такое — замыкание и в чем его особенности в PHP.
Как видим, замыкание как и лямбда-функция представляют собой объект класса Closure, коорый хранит переданные параметры. Для того, чтобы вызывать объект как функцию, в PHP5.3 ввели магический метод __invoke.
Используя конструкцию use мы наследуем переменную из родительской области видимости в локальную область видимости ламбда-функции.
Ситаксис прост и понятен. Не совсем понятно применение такого функционала в разработке web-приложений. Я просмотрел код нескольких совеременных фреймворков, использующих новые возможности языка и попытался собрать вместе их различные применения.
Функции обратного вызова
Самое очевидное применение анонимных функций — использование их в качестве функций обратного вызова (callbacks). В PHP имеется множество стандартных функций, принимающих на вход тип callback или его синоним callable введенный в PHP 5.4. Самые популярные из них array_filter, array_map, array_reduce. Функция array_map служит для итеративной обработки элементов массива. Callback-функция применяется к каждому элементу массива и в качестве результата выдается обработанный массив. У меня сразу возникло желание сравнить производительность обычной обработки массива в цикле с применением встроенной функции. Давайте поэкспериментируем.
Как видно, накладные расходы на большое количество вызовов функций дают ощутимый спад в производительности, чего и следовало ожидать. Хотя тест синтетический, задача обработки больших массивов возникает часто, и в данном случае применение функций обработки данных может стать тем местом, которе будет существенно тормозить ваше приложение. Будьте осторожны. Тем не менее в современных приложениях такой подход используется очень часто. Он позволяет делать код более лаконичным, особенно, если обработчик объявляется где-то в другом месте, а не при вызове.
По сути в данном контексте применение анонимных функций ничем не отличается от старого способа передачи строкового имени функции или callback-массива за исключением одной особенности — теперь мы можем использовать замыкания, то есть сохранять переменные из области видимости при создании функции. Рассмотрим пример обработки массива данных перед добавлением их в базу данных.
Очень удобно применять анонимные функции и для фильтрации
События.
Замыкания идеально подходят в качестве обработчиков событий. Например
Вынос логики в обработчики событий с одной стороны делает код более чистым, с другой стороны — усложняет поиск ошибок — поведение системы иногда становится неожиданным для человека, который не знает, какие обработчики навешаны в данный момент.
Валидация
Замыкания по сути сохраняют некоторую логику в переменной, которая может быть выполнена или не выполнена в по ходу работы скрипта. Это то, что нужно для реализации валидаторов:
В последнем случае мы применяем функцию высшего порядка, которая возвращает другую функцию — валидатор с предустановленными границами значений. Применять валидаторы можно, например, так.
Использование в формах классический пример. Также валидация может использоваться в сеттерах и геттерах обычных классов, моделях и т.д. Хорошим тоном, правда, считается декларативная валидация, когда правила описаны не в форме функций, а в форме правил при конфигурации, тем не менее, иногда такой подход очень кстати.
Выражения
В Symfony встречается очень интересное применение замыканий. Класс ExprBuilder опеделяет сущность, которая позволяет строить выражения вида
В Symfony как я понял это внутренний класс, который используется для создания обработки вложенных конфигурационных массивов (поправьте меня, если не прав). Интересна идея реализации выражений в виде цепочек. В принципе вполне можно реализовать класс, который бы описывал выражения в таком виде:
Применение, конечно, экспериментально. По сути — это запись некоторого алгоритма. Реализация такого функционала достаточно сложна — выражение в идеальном случае должно хранить дерево операций. Инетересна концепция, может быть где-то такая конструкция будет полезна.
Роутинг
Во многих мини-фреймворках роутинг сейчас работает на анонимных функциях.
Достаточно удобно и лаконично.
Кеширование
На хабре это уже обсуждалось, тем не менее.
Здесь метод get проверяет валидность кеша по ключу ‘users.list’ и если он не валиден, то обращается к функции за данными. Третий параметр определяет длительность хранения данных.
Инициализация по требованию
Допустим, у нас есть сервис Mailer, который мы вызываем в некоторых методах. Перед использованием он должен быть сконфигурирован. Чтобы не инициализировать его каждый раз, будем использовать ленивое создание объекта.
Инициализация объекта произойдет только перед самым первым использованием.
Изменение поведения объектов
Иногда бывает полезно переопределить поведение объектов в процессе выполнения скрипта — добавить метод, переопределить старый, и т.д. Замыкание поможет нам и здесь. В PHP5.3 для этого нужно было использовать различные обходные пути.
В принципе можно и переопределять старый метод, однако только в случае если он был определен подобным путем. Не совсем удобно. Поэтому в PHP 5.4 появилось возможность связать замыкание с объектом.
Конечно, модификации объекта не получилось, тем не менее замыкание получает доступ к приватным функциям и свойствам.
Передача как параметры по умолчанию в методы доступа к данным
Пример получения значения из массива GET. В случае его отсутствия значение будет получено путем вызова функции.
Функции высшего порядка
Здесь уже был пример создания валидатора. Приведу пример из фреймворка lithium
Метод возвращает замыкание, которое может быть использовано потом для записи сообщения в кеш.
Передача в шаблоны
Иногда в шаблон удобно передавать не просто данные, а, например, сконфигурированную функцию, которую можно вызвать из кода шаблона для получения каких либо значений.
В данном случае в шаблоне генерировалось несколько ссылок на сущности пользователя и в адресах этих ссылок фигурировал его логин.
Рекурсивное определение замыкания
Напоследок о том, как можно задавать рекурсивные замыкания. Для этого нужно передавать в use ссылку на замыкание, и вызывать ее в коде. Не забывайте об условии прекращения рекурсии
Многие из примеров выглядят натянуто. Сколько лет жили без них — и ничего. Тем не менее иногда применение замыкания достаточно естественно и для PHP. Умелое использование этой возможности позволит сделать код более читаемым и увеличить эффективность работы программиста. Просто нужно немного подстроить свое мышление под новую парадигму и все станет на свои места. А вообще рекомендую сравнить, как используются такие вещи в других языках типа Python. Надеюсь, что кто-нибудь нашел для себя здесь что-то новое. И конечно, если кто-то знает еще какие-нибудь интересные применения замыканий, то очень жду ваши комментарии. Спасибо!
Применение замыканий в PHP
Введение в PHP 5.3 замыканий — одно из главных его новшеств и хотя после релиза прошло уже несколько лет, до сих пор не сложилось стандартной практики использования этой возможности языка. В этой статье я попробовал собрать все наиболее интересные возможности по применению замыканий в PHP.
Для начала рассмотрим, что же это такое — замыкание и в чем его особенности в PHP.
Как видим, замыкание как и лямбда-функция представляют собой объект класса Closure, коорый хранит переданные параметры. Для того, чтобы вызывать объект как функцию, в PHP5.3 ввели магический метод __invoke.
Используя конструкцию use мы наследуем переменную из родительской области видимости в локальную область видимости ламбда-функции.
Ситаксис прост и понятен. Не совсем понятно применение такого функционала в разработке web-приложений. Я просмотрел код нескольких совеременных фреймворков, использующих новые возможности языка и попытался собрать вместе их различные применения.
Функции обратного вызова
Самое очевидное применение анонимных функций — использование их в качестве функций обратного вызова (callbacks). В PHP имеется множество стандартных функций, принимающих на вход тип callback или его синоним callable введенный в PHP 5.4. Самые популярные из них array_filter, array_map, array_reduce. Функция array_map служит для итеративной обработки элементов массива. Callback-функция применяется к каждому элементу массива и в качестве результата выдается обработанный массив. У меня сразу возникло желание сравнить производительность обычной обработки массива в цикле с применением встроенной функции. Давайте поэкспериментируем.
Как видно, накладные расходы на большое количество вызовов функций дают ощутимый спад в производительности, чего и следовало ожидать. Хотя тест синтетический, задача обработки больших массивов возникает часто, и в данном случае применение функций обработки данных может стать тем местом, которе будет существенно тормозить ваше приложение. Будьте осторожны. Тем не менее в современных приложениях такой подход используется очень часто. Он позволяет делать код более лаконичным, особенно, если обработчик объявляется где-то в другом месте, а не при вызове.
По сути в данном контексте применение анонимных функций ничем не отличается от старого способа передачи строкового имени функции или callback-массива за исключением одной особенности — теперь мы можем использовать замыкания, то есть сохранять переменные из области видимости при создании функции. Рассмотрим пример обработки массива данных перед добавлением их в базу данных.
Очень удобно применять анонимные функции и для фильтрации
События.
Замыкания идеально подходят в качестве обработчиков событий. Например
Вынос логики в обработчики событий с одной стороны делает код более чистым, с другой стороны — усложняет поиск ошибок — поведение системы иногда становится неожиданным для человека, который не знает, какие обработчики навешаны в данный момент.
Валидация
Замыкания по сути сохраняют некоторую логику в переменной, которая может быть выполнена или не выполнена в по ходу работы скрипта. Это то, что нужно для реализации валидаторов:
В последнем случае мы применяем функцию высшего порядка, которая возвращает другую функцию — валидатор с предустановленными границами значений. Применять валидаторы можно, например, так.
Использование в формах классический пример. Также валидация может использоваться в сеттерах и геттерах обычных классов, моделях и т.д. Хорошим тоном, правда, считается декларативная валидация, когда правила описаны не в форме функций, а в форме правил при конфигурации, тем не менее, иногда такой подход очень кстати.
Выражения
В Symfony встречается очень интересное применение замыканий. Класс ExprBuilder опеделяет сущность, которая позволяет строить выражения вида
В Symfony как я понял это внутренний класс, который используется для создания обработки вложенных конфигурационных массивов (поправьте меня, если не прав). Интересна идея реализации выражений в виде цепочек. В принципе вполне можно реализовать класс, который бы описывал выражения в таком виде:
Применение, конечно, экспериментально. По сути — это запись некоторого алгоритма. Реализация такого функционала достаточно сложна — выражение в идеальном случае должно хранить дерево операций. Инетересна концепция, может быть где-то такая конструкция будет полезна.
Роутинг
Во многих мини-фреймворках роутинг сейчас работает на анонимных функциях.
Достаточно удобно и лаконично.
Кеширование
На хабре это уже обсуждалось, тем не менее.
Здесь метод get проверяет валидность кеша по ключу ‘users.list’ и если он не валиден, то обращается к функции за данными. Третий параметр определяет длительность хранения данных.
Инициализация по требованию
Допустим, у нас есть сервис Mailer, который мы вызываем в некоторых методах. Перед использованием он должен быть сконфигурирован. Чтобы не инициализировать его каждый раз, будем использовать ленивое создание объекта.
Инициализация объекта произойдет только перед самым первым использованием.
Изменение поведения объектов
Иногда бывает полезно переопределить поведение объектов в процессе выполнения скрипта — добавить метод, переопределить старый, и т.д. Замыкание поможет нам и здесь. В PHP5.3 для этого нужно было использовать различные обходные пути.
В принципе можно и переопределять старый метод, однако только в случае если он был определен подобным путем. Не совсем удобно. Поэтому в PHP 5.4 появилось возможность связать замыкание с объектом.
Конечно, модификации объекта не получилось, тем не менее замыкание получает доступ к приватным функциям и свойствам.
Передача как параметры по умолчанию в методы доступа к данным
Пример получения значения из массива GET. В случае его отсутствия значение будет получено путем вызова функции.
Функции высшего порядка
Здесь уже был пример создания валидатора. Приведу пример из фреймворка lithium
Метод возвращает замыкание, которое может быть использовано потом для записи сообщения в кеш.
Передача в шаблоны
Иногда в шаблон удобно передавать не просто данные, а, например, сконфигурированную функцию, которую можно вызвать из кода шаблона для получения каких либо значений.
В данном случае в шаблоне генерировалось несколько ссылок на сущности пользователя и в адресах этих ссылок фигурировал его логин.
Рекурсивное определение замыкания
Напоследок о том, как можно задавать рекурсивные замыкания. Для этого нужно передавать в use ссылку на замыкание, и вызывать ее в коде. Не забывайте об условии прекращения рекурсии
Многие из примеров выглядят натянуто. Сколько лет жили без них — и ничего. Тем не менее иногда применение замыкания достаточно естественно и для PHP. Умелое использование этой возможности позволит сделать код более читаемым и увеличить эффективность работы программиста. Просто нужно немного подстроить свое мышление под новую парадигму и все станет на свои места. А вообще рекомендую сравнить, как используются такие вещи в других языках типа Python. Надеюсь, что кто-нибудь нашел для себя здесь что-то новое. И конечно, если кто-то знает еще какие-нибудь интересные применения замыканий, то очень жду ваши комментарии. Спасибо!
Замыкания в php
Не секрет, что в PHP 5.3 был введен ряд интересных новшеств. Разной степени полезности и скандальности. Возможно даже, что выпуск PHP 5.3 — хорошо спланированный PR-ход: самый большой список изменений за последние пять лет, оператор goto (sic!), пространства имен (namespaces) с синтаксисом «не как у всех», позднее статическое связывание (late static binding), более-менее честные анонимные (лямбда) функции (lambda functions), замыкания (closures).
О последних я и хочу рассказать. Справедливости ради хочу добавить, что в PHP 5.3 введено и много другого функционала, который делает этот релиз примечательным: модули Intl (интернационализация), Phar (PHP-архивы, наподобие JAR для JAVA), mysqlnd (новый драйвер для mysql), улучшения в SPL, общее увеличение производительности до 10% и много еще чего.
Замыкания (closures) и лямбда-функции (lambda functions) — нововведения PHP 5.3, вообще говоря, не являются передовым рубежом современных технологий. Удивительно, но и то и другое появилось в функциональном языке программирования LISP в конце 50-х, и было допилено напильником до состояния близкого к современному в 70-х годах. Таким образом, PHP подотстал с грамотной реализацией лет на 40.
Замыкания, если верить википедии — это процедуры, которые ссылаются на свободные переменные в своём лексическом контексте. Академически строго, но если бы я не знал о чем речь, никогда бы не понял из этого определения.
Во многих языках программирования, функция, чье определение вложено в другую функцию, так или иначе может иметь доступ не только к своим локальным переменным, но и к переменным родительской функции. Иногда для этого используется какой-то специальный синтаксис, иногда это работает без дополнительный усилий. Приведу пример на Javascript, потому что там все работает без какого-то особого синтаксиса (те же примеры, но на PHP будут позже):
Итак, функция inner, без какого-то специального объявления свободно использует переменные внешней функции outer. Но это еще не замыкание.
Во многих языках программирования функция (имеется в виду не результат выполнения функции, а сама функция как исполняемый код, указатель на функцию), это некий объект особого типа. Как любой другой объект (строка, число, массив), функция может быть присвоена переменной, передана в другую функцию в качестве параметра и возвращена как результат выполнения другой функции. С этим связана и еще одна важная вещь: Функции при ее определении, можно не присваивать имени, а присвоить эту функцию переменной и вызывать ее через эту переменную. Сама же функция не имеет собственного имени, и поэтому остается «анонимной«. Такие функции называют так же лямбда функциями.
Лямбда-функции (и замыкания, будучи ими) как бы откладывают выполнение некоторого кода на более поздний срок. Поэтому часто используются в разработке интерфейсов пользователя в качестве обработчиков событий, что и было показано выше.
Вернемся к PHP. В нем все не так красиво как в Javascript. Во-первых, для того чтобы функция могла использовать переменные родительской функции, надо это явно указать в ее определении (это следует из идеологии языка), во-вторых работает это только с анонимными функциями и работает это все только с PHP 5.3. Пример 1j в исполнении PHP будет выглядеть как-то так:
А наш пример 2j как то так:
Анонимные функции в PHP позволяют упростить и сделать нагляднее применение различных встроенных функций использующих callback, например:
В старом варианте код функции сравнения и код, где она используется, могут оказаться разнесены довольно далеко друг от друга, к тому же эта функция используется только один раз, но ей надо давать имя и оно может случайно вступить в конфликт с именем другой функции. В новом варианте, все гораздо приятнее, весь код размещен компактно и пространство имен тоже не засоряется.
Может показаться, что анонимные функции уже были в предыдущих версиях PHP. Разве create_function, это не оно — то самое? И да, и нет. create_function при каждом вызове создает новую настоящую именованную функцию в глобальном пространстве имен(sic!), но ее имя начинается с \0 (нулевого байта) и поэтому не может вступить в конфликт с обычными функциями. Но разве можно назвать такую функцию действительно анонимной? Кстати, create_function возвращает строку с именем этой «анонимной» функции. В остальном, использование действительно похоже на использование анонимных функций в PHP 5.3:
Зачем нужны замыкания в PHP я понимаю слабо. Для простого сохранения состояния функции между вызовами? Например так:
Но для этого существует static переменные. Их использование проще, удобнее и требует меньше кода:
Для сложного сохранения состояния между вызовами существует ООП. Кстати, анонимные функции в PHP 5.3 все же не совсем честные функции! На поверку эта функция оказывается… оказывается… Внимание, фокус:
Функция оказывается объектом класса Closure — и вот оно это ваше ООП. А чтобы объект всячески корчил из себя функцию, разработчики PHP придумали специальный «магический» метод __invoke():
Подводя итоги: Анонимные функции и замыкания — очень мощный инструмент. И как любой мощный инструмент, требуют очень аккуратного применения. Они могут существенно упростить код программы, сделать его красивее и повысить его читаемость, а могут ровно наоборот — сделать его абсолютно нечитаемым. Замыкания очень часто применяются в качестве функций обратного вызова при программировании графических интерфейсов. Очень удобно вешать их в качестве обработчиков нажатий на разные кнопочки. Но на PHP практически никто не делает программы с GUI (хотя умельцы существуют), и на это есть некоторые причины — PHP все же язык веб-сценариев, а не десктоп приложений. Поэтому анонимные функции хороши в preg_replace_callback, array_filter и тому подобных функциях, а замыкания следует оставить для Javascript, Python и других языков, где они реализованы действительно хорошо и где реально нужны для использования в GUI.