Anonymous functions

Оновлено: 11.05.2023

Анонімні функції, також відомі як closures, дозволяють створювати функції, які не мають визначеного імені. Вони найбільш корисні як значення параметрів, що викликаються, але мають багато інших застосувань.

Анонімні функції реалізуються за допомогою класу Closure.

Приклад #1 Приклад анонімної функції

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

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

Приклад #2 Приклад присвоєння анонімної функціональної змінної

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

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

Закриття також можуть успадковувати змінні з батьківської області видимості. Будь-які такі змінні повинні бути передані в конструкцію мови використання. Починаючи з версії PHP 7.1, ці змінні не повинні містити суперглобалів, $this або змінних з однаковими іменами в якості параметрів. Оголошення типу повернення функції повинно бути розміщено після інструкції use.

Приклад #3 Успадкування змінних з батьківської області видимості

<?php
$message = 'hello';

// No "use"
$example = function () {
    var_dump($message);
};
$example();

// Inherit $message
$example = function () use ($message) {
    var_dump($message);
};
$example();

// Inherited variable's value is from when the function
// is defined, not when called
$message = 'world';
$example();

// Reset message
$message = 'hello';

// Inherit by-reference
$example = function () use (&$message) {
    var_dump($message);
};
$example();

// The changed value in the parent scope
// is reflected inside the function call
$message = 'world';
$example();

// Closures can also accept regular arguments
$example = function ($arg) use ($message) {
    var_dump($arg . ' ' . $message);
};
$example("hello");

// Return type declaration comes after the use clause
$example = function () use ($message): string {
    return "hello $message";
};
var_dump($example());
?>

У наведеному вище прикладі буде виведено щось подібне до

Починаючи з версії PHP 8.0.0, список успадкованих змінних може містити кінцеву кому, яка буде проігнорована.

Успадкування змінних з батьківської області видимості - це не те саме, що використання глобальних змінних. Глобальні змінні існують у глобальній області видимості, яка є однаковою незалежно від того, яка функція виконується. Батьківською областю видимості для закриття є функція, в якій було оголошено закриття (не обов'язково функція, з якої його було викликано). Дивіться наступний приклад:

Приклад #4 Закриття та визначення обсягу

<?php
// A basic shopping cart which contains a list of added products
// and the quantity of each product. Includes a method which
// calculates the total price of the items in the cart using a
// closure as a callback.
class Cart
{
    const PRICE_BUTTER  = 1.00;
    const PRICE_MILK    = 3.00;
    const PRICE_EGGS    = 6.95;

    protected $products = array();
    
    public function add($product, $quantity)
    {
        $this->products[$product] = $quantity;
    }
    
    public function getQuantity($product)
    {
        return isset($this->products[$product]) ? $this->products[$product] :
               FALSE;
    }
    
    public function getTotal($tax)
    {
        $total = 0.00;
        
        $callback =
            function ($quantity, $product) use ($tax, &$total)
            {
                $pricePerItem = constant(__CLASS__ . "::PRICE_" .
                    strtoupper($product));
                $total += ($pricePerItem * $quantity) * ($tax + 1.0);
            };
        
        array_walk($this->products, $callback);
        return round($total, 2);
    }
}

$my_cart = new Cart;

// Add some items to the cart
$my_cart->add('butter', 1);
$my_cart->add('milk', 3);
$my_cart->add('eggs', 6);

// Print the total with a 5% sales tax.
print $my_cart->getTotal(0.05) . "\n";
// The result is 54.29
?>

Приклад #5 Автоматичне зв'язування $this

<?php

class Test
{
    public function testing()
    {
        return function() {
            var_dump($this);
        };
    }
}

$object = new Test;
$function = $object->testing();
$function();
    
?>

Вищенаведений приклад виведе:

При оголошенні в контексті класу поточний клас автоматично зв'язується з ним, роблячи $this доступним всередині області видимості функції. Якщо автоматичне зв'язування поточного класу не потрібне, замість нього можна використовувати статичні анонімні функції.

Анонімні функції можна оголошувати статично. Це запобігає автоматичному зв'язуванню з поточним класом. Об'єкти також не можуть бути зв'язані з ними під час виконання.

Приклад #6 Спроба використання $this всередині статичної анонімної функції

<?php

class Foo
{
    function __construct()
    {
        $func = static function() {
            var_dump($this);
        };
        $func();
    }
};
new Foo();

?>

Вищенаведений приклад виведе:

Приклад #7 Спроба зв'язати об'єкт зі статичною анонімною функцією

<?php

$func = static function() {
    // function body
};
$func = $func->bindTo(new stdClass);
$func();

?>

Вищенаведений приклад виведе:

Version Description 7.1.0 Anonymous functions may not close over superglobals, $this, or any variable with the same name as a parameter.

Зауваження: Ви можете використовувати func_num_args(), func_get_arg() та func_get_args() всередині закриття.