Closure test

Замыкания в 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 будут позже):

//*** Пример 1j *** function outer(x) //Определение внешней функции < var y=2; //Локальная переменная внешней функции function inner(a) //Определение внутренней функции < var b=4; //Локальная переменная внутренней функции /* А дальше складываются переменные внутренней и * внешней функций, как будто все они локальные * переменные внутренней функции */ var res=x+y+a+b; alert(res); //Результат 10 в нашем примере. >inner(3); //Вызов внутренней функции > outer(1); //Вызов внешней функции, а она вызовет внутреннюю. 

Итак, функция inner, без какого-то специального объявления свободно использует переменные внешней функции outer. Но это еще не замыкание.

Во многих языках программирования функция (имеется в виду не результат выполнения функции, а сама функция как исполняемый код, указатель на функцию), это некий объект особого типа. Как любой другой объект (строка, число, массив), функция может быть присвоена переменной, передана в другую функцию в качестве параметра и возвращена как результат выполнения другой функции. С этим связана и еще одна важная вещь: Функции при ее определении, можно не присваивать имени, а присвоить эту функцию переменной и вызывать ее через эту переменную. Сама же функция не имеет собственного имени, и поэтому остается «анонимной«. Такие функции называют так же лямбда функциями.

// *** Пример 2j *** adder=function(a,b) //У функции нет имени, но она присваивается переменной < return a+b; //вернуть сумму >subber=function(a,b) < return a-b; >//То же самое, просто в одну строку /* * Прошу обратить внимание - тут переменным adder и subber присваивается не * результат вычисления суммы или разности каких то чисел, а, грубо говоря, * сам код функции. Т.е. в переменной adder и subber сейчас не число, а код, * который можно вызвать так: x=adder(1,3); и вот x уже будет числом. */ function performAction(action, a, b) //Обычная функция с именем performAction < var result=action(a,b); //предполагается что параметр action - это функция return result; >function makeDivider() //Обычная функция с именем makeDivider < return function (a,b) //Возвращает безымянную функцию < return a/b; >> r1=adder(1,2); //Вызываем безымянную функцию через переменную. r1=1+2; r2=performAction(subber,6,4); //Передаем функцию в другую функцию. r2=6-4; r3=performAction(function(a,b) ,5,6); //То же самое прямо на лету. r3=5*6; divider=makeDivider(); //Вызываем функцию, которая возвращает функцию, сохраняем результат r4=divider(16,4); //Вызываем функцию возвращенную функцией через переменную: r4=16/4; r5=makeDivider()(32,16);//То же самое, но без промежуточной переменной: r5=32/16; alert([r1,r2,r3,r4,r5]); //3,2,30,4,2 
  • Анонимная функция определенная внутри любой другой функции, имеет доступ к переменным родителя.
  • Если родительская функция вернет дочернюю функцию в качестве результата своей работы, переменные родительской функции останутся доступны в дочерней, несмотря на то, что родительская уже отработала и ее локальные переменные должны удалиться.
      Нажми И меня нажми 
Скрой меня скорее
И меня тоже скрой

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

Читайте также:  Где взять php файл

Вернемся к PHP. В нем все не так красиво как в Javascript. Во-первых, для того чтобы функция могла использовать переменные родительской функции, надо это явно указать в ее определении (это следует из идеологии языка), во-вторых работает это только с анонимными функциями и работает это все только с PHP 5.3. Пример 1j в исполнении PHP будет выглядеть как-то так:

// *** Пример 1p *** function outer($x) //Определение внешней функции < $y=2; //Локальная переменная внешней функции $inner=function ($a) use ($x, $y) //Определение внутренней функции < $b=4; //Локальная переменная внутренней функции /* А дальше складываются переменные внутренней и * внешней функций, как будто все они локальные * переменные внутренней функции */ $res=$x+$y+$a+$b; echo $res; //Результат 10 в нашем примере. >; $inner(3); //Вызов внутренней функции > outer(1); 

А наш пример 2j как то так:

// *** Пример 2p *** $adder=function($a,$b) //У функции нет имени, но она присваивается переменной < return $a+$b; //вернуть сумму >; $subber=function($a,$b) < return $a-$b; >; //То же самое, просто в одну строку function performAction($action, $a, $b) //Обычная функция с именем performAction < $result=$action($a,$b); //предполагается что параметр action - это функция return $result; >function makeDivider() //Обычная функция с именем makeDivider < return function ($a,$b) //Возвращает безымянную функцию < return $a/$b; >; > $r1=$adder(1,2); //Вызываем безымянную функцию через переменную. r1=1+2; $r2=performAction($subber,6,4); //Передаем функцию в другую функцию. r2=6-4; $r3=performAction(function($a,$b) ,5,6); //То же самое прямо на лету. r3=5*6; $divider=makeDivider(); //Вызываем функцию, которая возвращает функцию, сохраняем результат $r4=$divider(16,4); //Вызываем функцию возвращенную функцией через переменную: r4=16/4; //А такие вещи как в r5 в PHP вообще не прокатывают. //$r5=makeDivider()(32,16);//То же самое, но без промежуточной переменной: r5=32/16; $r5='php fail'; echo "$r1,$r2,$r3,$r4,$r5"; //3,2,30,4,php fail 

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

//По старинке: function cmp($a, $b) < return($a >$b); > //тут еще всякий код uasort($array, 'cmp'); //А тут использование этой функции //По новому: uasort($array, function($a, $b) < return($a >$b);>); 

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

Может показаться, что анонимные функции уже были в предыдущих версиях PHP. Разве create_function, это не оно — то самое? И да, и нет. create_function при каждом вызове создает новую настоящую именованную функцию в глобальном пространстве имен(sic!), но ее имя начинается с \0 (нулевого байта) и поэтому не может вступить в конфликт с обычными функциями. Но разве можно назвать такую функцию действительно анонимной? Кстати, create_function возвращает строку с именем этой «анонимной» функции. В остальном, использование действительно похоже на использование анонимных функций в PHP 5.3:

//Новый старый лад, работает даже в PHP 4!: uasort($array, create_function('$a, $b','return $a > $b;')); 

Зачем нужны замыкания в PHP я понимаю слабо. Для простого сохранения состояния функции между вызовами? Например так:

function getModernIncrementer() < $x=0; return function() use(&$x) //Обязательно указать передачу по ссылке! < return $x++; >; > $incrementer2=getModernIncrementer(); echo $incrementer2(), $incrementer2(), $incrementer2();//012 

Но для этого существует static переменные. Их использование проще, удобнее и требует меньше кода:

function incrementer() < static $x=0; return $x++; >echo incrementer(),incrementer(),incrementer(); //012 

Для сложного сохранения состояния между вызовами существует ООП. Кстати, анонимные функции в PHP 5.3 все же не совсем честные функции! На поверку эта функция оказывается… оказывается… Внимание, фокус:

var_dump(function()); // object(Closure)#1 (0)

Функция оказывается объектом класса Closure — и вот оно это ваше ООП. А чтобы объект всячески корчил из себя функцию, разработчики PHP придумали специальный «магический» метод __invoke():

class Test < public function __invoke () < return 123; >> $func = new Test(); echo $func(); //123 

Подводя итоги: Анонимные функции и замыкания — очень мощный инструмент. И как любой мощный инструмент, требуют очень аккуратного применения. Они могут существенно упростить код программы, сделать его красивее и повысить его читаемость, а могут ровно наоборот — сделать его абсолютно нечитаемым. Замыкания очень часто применяются в качестве функций обратного вызова при программировании графических интерфейсов. Очень удобно вешать их в качестве обработчиков нажатий на разные кнопочки. Но на PHP практически никто не делает программы с GUI (хотя умельцы существуют), и на это есть некоторые причины — PHP все же язык веб-сценариев, а не десктоп приложений. Поэтому анонимные функции хороши в preg_replace_callback, array_filter и тому подобных функциях, а замыкания следует оставить для Javascript, Python и других языков, где они реализованы действительно хорошо и где реально нужны для использования в GUI.

В заключение: Хочу привести законченный пример использования замыканий с JQuery (при работе с этой библиотекой они используются широко). В примере много вложенных функций, обработчиков, и в них, чтобы избежать постоянных (хоть и быстрых, но некрасивых) поисков DOM-объектов по параметрам, всюду используются переменные уже содержащие нужный объект, доставшиеся от внешней функции (это и есть замыкание). Аналогичный код используется в админке проекта hi-tech.mail.ru, и позволяет динамически добавить возможность редактирования и сохранения через AJAX отдельных блоков страницы (наподобие того, что делает плагин Editable), при этом изначально HTML код страницы не содержит никакой особой разметки для этого.

    .msg, .edt .msg .edt    

Это первое сообщение
Его можно редактировать!

Это второе сообщение
Его тоже можно редактировать!
P.S. Just 4 lulz

Я админ и хочу редактировать!

С уважением,
Отдел исследований и разработки Mail.Ru

Источник

Как сделать вызов функции из другой функции php?

Вызов функции из другой функции в PHP можно сделать с помощью круглых скобок:

 function firstFunction()  echo 'This is the first function!'; > function secondFunction()  firstFunction(); > secondFunction(); // => This is the first function! 

А также можно сделать с помощью функции call_user_func()

Вызывает callback-функцию, переданную первым параметром, и передаёт остальные параметры в качестве аргументов.

 function firstFunction()  echo 'This is the first function!'; > function secondFunction()  call_user_func('firstFunction'); > secondFunction(); // => This is the first function! 

Источник

Оцените статью