Анонимные функции

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

<?php
echo preg_replace_callback('~-([a-z])~', function ($match) {
    return strtoupper($match[1]);
}, 'hello-world');
// выведет helloWorld
?>

Замыкания также могут быть использованы в качестве значений переменных; PHP автоматически преобразует такие выражения в экземпляры внутреннего класса Closure. Присвоение замыкания переменной использует тот же синтаксис, что и для любого другого присвоения, включая завершающую точку с запятой:

<?php
$greet = function($name)
{
    printf("Hello %s\r\n", $name);
};

$greet('World');
$greet('PHP');
?>

Замыкания могут также наследовать переменные из родительской области видимости. Любая подобная переменная должна быть объявлена в конструкции use.

<?php
$message = 'hello';

// Без "use"
$example = function () {
    var_dump($message);
};
echo $example();

// Наследуем $message
$example = function () use ($message) {
    var_dump($message);
};
echo $example();

// Значение унаследованной переменной задано там, где функция определена, 
// но не там, где вызвана
$message = 'world';
echo $example();

// Сбросим message
$message = 'hello';

// Наследование по ссылке
$example = function () use (&$message) {
    var_dump($message);
};
echo $example();

// Измененное в родительской области видимости значение
// остается тем же внутри вызова функции
$message = 'world';
echo $example();

// Замыкания могут принимать обычные аргументы
$example = function ($arg) use ($message) {
    var_dump($arg . ' ' . $message);
};
$example("hello");
?>

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

Рекурсия в анонимных функциях

Следует передать в функцию ссылку на нее же.

$factorial = function( $n ) use ( &$factorial ) {
    if( $n == 1 ) return 1;
    return $factorial( $n - 1 ) * $n;
};
print $factorial( 5 );

Класс Closure

Класс используемый для создания анонимных функций.

Closure::bindTo ($newthis [,$newscope = "static" ] )

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

"Привязанный объект" определяет значение $this, которое будет доступно в теле функции, а "область видимости класса" представляет собой класс, который определяет к каким protected (защищенным) и private (закрытым) элементам этого объекта будет иметь доступ анонимная функция. Если точнее, то это те элементы, как если бы анонимная функция была бы методом класса, переданного в параметре newscope.

Статические замыкания не могут иметь привязанный объект (значение параметра newthis должно быть равно NULL), но эта функция может все равно использоваться для изменения его области видимости класса.

Данный метод гарантирует, что у нестатического замыкания с привязанным объектом будет задана область видимости и наоборот. Для выполнения этого условия применяются следующие правила: Для нестатического замыкания, с указанной областью видимости и с NULL вместо объекта, будет создано статическое замыкание. Для нестатического замыкания с незаданной областью видимости, но с указанием объекта, создается замыкание с неуказанной областью видимости.

Closure::bind ($closure , $newthis [,$newscope = "static" ] )

  • Дублирует замыкание с указанием связанного объекта и области видимости класса.
  • closure - Анонимная функция для привязывания к объекту.
  • newthis - Объект, к которому будет привязана переданная функция, или NULL для отсоединения функции от ее текущего объекта.
  • newscope - Область видимости класса, с которой ассоциируется замыкание, или 'static' для сохранения текущей области видимости. Если передан объект, то будет использован его класс. Этот параметр определяет видимость protected (защищенных) и private (закрытых) методов привязанного объекта.
  • Возвращает новый объект Closure или FALSE в случае возникновения ошибки.
<?php
class A {
    private static $sfoo = 1;
    private $ifoo = 2;
}
$cl1 = static function() {
    return A::$sfoo;
};
$cl2 = function() {
    return $this->ifoo;
};

$bcl1 = Closure::bind($cl1, null, 'A');
$bcl2 = Closure::bind($cl2, new A(), 'A');
echo $bcl1(), "\n";
echo $bcl2(), "\n";
/*
1
2
*/
?>

Присовение анонимной функции свойству объекта

При попытке присвоить анонимную функции свойству объекта, а потом ее вызвать, будет ошибка Call to undefined method. Т.к. PHP будет искать метод с таким именем в классе:

<?php
$closure = function()
{
    echo 'closure';
};

class My {
}

$a = new My();
$a->closure = $closure;
$a->closure(); // ошибка

Чтоб использовать замыкание в таких случая стоит либо у свойства вызвать метод __invoke(). Либо реализовать в классе магический метод __call:

$a->closure->__invoke();

class My {
    public function __call($name, $args)
    {
        return call_user_func_array($this->$name, $args);
    }
}