что такое анонимная функция 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 скриптам, которые Вы сможете использовать на своих ресурсах.
Фильтрация данных с помощью zend-filter
Когда речь идёт о безопасности веб-сайта, то фраза «фильтруйте всё, экранируйте всё» всегда будет актуальна. Сегодня поговорим о фильтрации данных.
Контекстное экранирование с помощью zend-escaper
Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.
Подключение Zend модулей к Expressive
Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.
Совет: отправка информации в Google Analytics через API
Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.
Подборка PHP песочниц
Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.
Совет: активация отображения всех ошибок в PHP
При поднятии PHP проекта на новом рабочем окружении могут возникнуть ошибки отображение которых изначально скрыто базовыми настройками. Это можно исправить, прописав несколько команд.
Агент
PHP парсер юзер агента с поддержкой Laravel, работающий на базе библиотеки Mobile Detect.
Анонимные функции в PHP: сеанс чёрной магии с разоблачением
Начать, наверное, следует с того, что анонимная функция(замыкание) в PHP — это не функция, а объект класса Closure. Собственно, на этом статью можно было бы и закончить, но если кому-то интересны подробности — добро пожаловать под кат.
Дабы не быть голословным:
Забегая вперёд, скажу, что на самом деле это не совсем обычный объект. Давайте разберёмся.
Например, такой код
компилируется в такой набор опкодов:
Блок с описанием тела функции нам не особо интересен, а вот в первом блоке присутствуют два интересных нам опкода: DECLARE_LAMBDA_FUNCTION и INIT_DYNAMIC_CALL. Начнём со второго.
INIT_DYNAMIC_CALL
Этот опкод используется в случае, когда компилятор видит вызов функции на переменной или массиве. Т.е.
В случае замыкания нас интересует вызов на переменной с объектом, что логично.
Углубляясь в код VM, обрабатывающий этот опкод, мы дойдём до функции zend_init_dynamic_call_object, в которой увидим следующее (нарезка):
Забавно, что привычный всем вызов метода __invoke в терминах VM является попыткой вызова замыкания — get_closure.
Собственно, на этом месте начинается разница в обработке вызова анонимной функции и метода __invoke обычного объекта.
В PHP у каждого объекта существует набор различных обработчиков, определяющий его служебные и магические методы.
Кроме методов, описанных здесь, этот класс также имеет метод __invoke. Данный метод необходим только для совместимости с другими классами, в которых реализован магический вызов, так как этот метод не используется при вызове функции.
И это таки правда. Функция get_closure для замыкания возвращает не __invoke, а вашу функцию, из которой создавалось замыкание.
Более подробно можете изучить исходники сами — файл zend_closure.c, а мы перейдём к следующему опкоду.
DECLARE_LAMBDA_FUNCTION
И вот на этом месте начинаются не очень приятные новости.
Так что же не так с анонимными функциями?
Создание замыкания — операция более тяжёлая, чем создание обыкновенного объекта. Мало того что вызывается стандартный механизм создания объекта, к нему ещё добавляется и некоторое количество логики, самым неприятным из которой является копирование всего массива опкодов вашей функции в тело объекта замыкания. Само по себе это не то чтобы страшно, но ровно до того момента, пока вы не начинаете его использовать «неправильно».
Чтобы понять, где именно поджидают проблемы, разберём случаи, когда происходит создание замыкания.
Замыкание создаётся заново:
а) при каждой обработке опкода DECLARE_LAMBDA_FUNCTION.
Интуитивно — ровно тот кейс, где замыкание смотрится хорошо, но на самом деле новый объект замыкания будет создаваться на каждой итерации цикла.
б) при каждом вызове методов bind и bindTo:
Тут замыкание будет создаваться заново также на каждой итерации.
с) при каждом вызове метода call, если в качестве функции используется генератор. А если не генератор, а обычная функция, то выполняется только часть с копированием массива опкодов. Такие дела.
Выводы
Если вам не важна производительность любой ценой, то анонимные функции удобны и приятны. А если важна, то, наверное, не стоит.
В любом случае теперь вы знаете, что замыкания и циклы, если их готовить неправильно, — такая себе комбинация.
Что такое анонимная функция php
So you will need to explicitly pass them in by reference if your closure cares about their contents over time:
//set up variable in advance
$myInstance = null ;
//$myInstance might be instantiated, might not be
if( SomeBusinessLogic :: worked () == true )
<
$myInstance = new myClass ();
>
Every instance of a lambda has own instance of static variables. This provides for great event handlers, accumulators, etc., etc.
In case you were wondering (cause i was), anonymous functions can return references just like named functions can. Simply use the & the same way you would for a named function. right after the `function` keyword (and right before the nonexistent name).
As of PHP 7.0, you can use IIFE(Immediately-invoked function expression) by wrapping your anonymous function with ().
PERFORMANCE BENCHMARK 2017!
I decided to compare a single, saved closure against constantly creating the same anonymous closure on every loop iteration. And I tried 10 million loop iterations, in PHP 7.0.14 from Dec 2016. Result:
a single saved closure kept in a variable and re-used (10000000 iterations): 1.3874590396881 seconds
new anonymous closure created each time (10000000 iterations): 2.8460240364075 seconds
In other words, over the course of 10 million iterations, creating the closure again during every iteration only added a total of «1.459 seconds» to the runtime. So that means that every creation of a new anonymous closure takes about 146 nanoseconds on my 7 years old dual-core laptop. I guess PHP keeps a cached «template» for the anonymous function and therefore doesn’t need much time to create a new instance of the closure!
So you do NOT have to worry about constantly re-creating your anonymous closures over and over again in tight loops! At least not as of PHP 7! There is absolutely NO need to save an instance in a variable and re-use it. And not being restricted by that is a great thing, because it means you can feel free to use anonymous functions exactly where they matter, as opposed to defining them somewhere else in the code. 🙂
public function __destruct ()
<
echo «destructed\n» ;
>
>
new Test ;
echo «finished\n» ;
public static function createClosure ()
<
return function () <
>;
>
When using anonymous functions as properties in Classes, note that there are three name scopes: one for constants, one for properties and one for methods. That means, you can use the same name for a constant, for a property and for a method at a time.
Since a property can be also an anonymous function as of PHP 5.3.0, an oddity arises when they share the same name, not meaning that there would be any conflict.
Consider the following example:
class MyClass <
const member = 1 ;
public function member () <
return «method ‘member'» ;
>
header ( «Content-Type: text/plain» );
$myObj = new MyClass ();
/*
* Establish mock authorisation, call the service; should get
* ‘Service returns: test 1’.
*/
$_SESSION [ ‘is_authorised’ ] = true ;
$service ( ‘test 1’ );
/*
* Remove mock authorisation, call the service; should get ‘Access Denied’.
*/
$_SESSION [ ‘is_authorised’ ] = false ;
$service ( ‘test 2’ );
Применение замыканий в 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. Надеюсь, что кто-нибудь нашел для себя здесь что-то новое. И конечно, если кто-то знает еще какие-нибудь интересные применения замыканий, то очень жду ваши комментарии. Спасибо!