PHP 閉包的理解

lxzoliver發表於2020-04-12

在官方文件定義中,匿名函式(Anonymous functions),也叫閉包函式(closures),允許臨時建立一個沒有指定名稱的函式。最經常用作回撥函式(callback)引數的值。

在laravel應用中,也是使用非常頻繁的語法特性
例如在定義路由時,通過閉包的方法,定義路由組

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // // 使用 first 和 second 中介軟體
    });

    Route::get('user/profile', function () {
        // // 使用 first 和 second 中介軟體
    });
});

laravel的許多集合函式也通過閉包來實現,例如

$collection = collect([1, 2, 3, 4]);

$filtered = $collection->filter(function ($value, $key) {
    return $value > 2;
});

$filtered->all();

// [3, 4]

以下示例來自官方文件匿名函式

匿名函式目前是通過 Closure 類來實現的。

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

閉包函式也可以作為變數的值來使用。PHP 會自動把此種表示式轉換成內建類 Closure 的物件例項。把一個 closure 物件賦值給一個變數的方式與普通變數賦值的語法是一樣的,最後也要加上分號:

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

$greet('World');//輸出Hello World
$greet('PHP');//輸出Hello PHP
?>

閉包可以從父作用域中繼承變數。 任何此類變數都應該用 use 語言結構傳遞進去。 PHP 7.1 起,不能傳入此類變數: superglobals$this 或者和引數重名。

<?php
$message = 'hello';

// 沒有 "use"
$example = function () {
    var_dump($message);
};
echo $example();//Notice: Undefined variable: message in /example.php on line 6
//輸出NULL

// 繼承 $message
$example = function () use ($message) {
    var_dump($message);
};
echo $example();
//輸出hello

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

// Reset message
$message = 'hello';

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

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

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

Closure::bind

官方文件給出定義如下:
Closure::bind — 複製一個閉包,繫結指定的$this物件和類作用域。
我的理解如下:
可通過預定義一個閉包,在後續中,通過bind方法傳入類物件和作用域,使閉包成為類的方法,laravel的巨集指令就有使用到此方法,後續會做相應研究。

根據官方給出的案例

<?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";//輸出1
echo $bcl2(), "\n";//輸出2
?>

Closure::bind( Closure $closure , object $newthis [, mixed $newscope = ‘static’)可傳入的引數三個引數中,其中$closure為匿名函式,主要討論後面兩個引數$newthis,$newscope為繫結指定的$this物件和類作用域。

在以上案例中,bind方法中$bcl1只傳入了類作用域,代表在$cl1閉包函式作用域中允許訪問類的靜態變數,但不能訪問類的變數和類的方法。

$bcl2傳入了類作用域和$this物件,代表在$cl2可以訪問類的所有變數和類的所有方法,此時可通過閉包完成對類方法的擴充套件。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章