PHP 核心特性之匿名函數

提出php

 

在匿名函數出現以前,全部的函數都須要先命名才能使用閉包

1函數

2性能

3flex

4this

5spa

function increment($value)code

{對象

    return $value + 1;ci

}

array_map('increment', [1, 2, 3]);

有的時候函數可能只須要使用一次,這時候使用匿名函數會使得代碼更加簡潔直觀,同時也避免了函數在其餘地方被使用

1

2

3

array_map(function($value){

    return $value + 1;

}, [1, 2, 3]);

定義和使用

PHP 將閉包和匿名函數視爲同等概念(本文統稱爲匿名函數),本質上都是假裝成函數的對象。

匿名函數的本質是對象,所以跟對象同樣可將匿名函數賦值給某一變量

1

2

3

4

$greet = function(string $name){

    echo "hello {$name}";

}

$greet("jack") // hello jack

全部的匿名函數都是 Closure 對象的實例

1

$greet instanceof Closure // true

對象並無什麼父做用域可言,因此須要使用 use 來手動聲明使用的變量,

1

2

3

4

5

6

7

$num = 1;

$func = function() use($num){

    $num = $num + 1;

    echo $num;

}

$func();  // 2

echo $num// 仍是 1

若是要讓匿名函數中的變量生效,須要使用引用傳值

1

2

3

4

5

6

7

$num = 1;

$func = function() use(&$num){

    $num = $num + 1;

    echo $num;

}

$func();  // 2

echo $num// 2

從 PHP 5.4 開始,在類裏面使用匿名函數時,匿名函數的 $this 將自動綁定到當前類

1

2

3

4

5

6

7

8

9

10

11

class Foo {

    public function bar()

    {  

        return function() {

            return $this;

        };

    }

}

$foo = new Foo();

$obj = $foo->bar(); // Closure()

$obj();   // Foo

若是不想讓自動綁定生效,可以使用靜態匿名函數

1

2

3

4

5

6

7

8

9

10

11

class Foo {

    public function bar()

    {  

        return static function() {

            return $this;

        };

    }

}

$foo = new Foo();

$obj = $foo->bar(); // Closure()

$obj();   // Using $this when not in object context

匿名函數的本質

匿名函數的本質是 Closure 對象,包括瞭如下五個方法

1

2

3

4

5

6

7

Closure {

    private __construct ( void )

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

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

    public call ( object $newthis [, mixed $... ] ) : mixed

    public static fromCallable ( callable $callable ) : Closure

}

__construct - 防止匿名函數被實例化

1

2

$closure = new \Closure();

// PHP Error:  Instantiation of 'Closure' is not allowed

Closure::bindTo - 複製當前匿名函數對象,綁定指定的 $this 對象和類做用域。通俗的說,就是手動將匿名函數與指定對象綁定,利用這點,能夠爲擴展對象的功能。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

// 定義商品類

class Good {

    private $price;

    public function __construct(float $price)

    {

        $this->price = $price;

    }

}

// 定義一個匿名函數,計算商品的促銷價

$addDiscount = function(float $discount = 0.8){

    return $this->price * $discount;

}

$good = new Good(100);

// 將匿名函數綁定到 $good 實例,同時指定做用域爲 Good

$count = $addDiscount->bindTo($good, Good::class);

$count(); // 80

// 將匿名函數綁定到 $good 實例,可是不指定做用域,將沒法訪問 $good 的私有屬性

$count = $addDiscount->bindTo($good);

$count(); // 報錯

Closure::bind - bindTo 方法的靜態版本,有兩種用法:

用法一:實現與 bindTo 方法一樣的效果

1

$count = \Closure::bind($addDiscount, $good, Good::class);

用法二:將匿名函數與類(而不是對象)綁定,記得要將第二個參數設置爲 null

1

2

3

4

5

6

7

8

9

10

11

12

// 商品庫存爲 10

class Good {

    static $num = 10;

}

// 每次銷售後返回當前庫存

$sell = static function() {

    return"當前庫存爲". --static::$num ;

};

// 將靜態匿名函數綁定到 Good 類中

$sold = \Closure::bind($sell, null, Good::class);

$sold(); // 當前庫存爲 9

$sold(); // 當前庫存爲 8

call - PHP 7 新增的 call 方法能夠實現綁定並調用匿名函數,除了語法更加簡潔外,性能也更高

1

2

3

4

5

// call 版本

$addDiscount->call($good, 0.5);  // 綁定並傳入參數 0.5,結果爲 50

// bindTo 版本

$count = $addDiscount->bindTo($good, Good::class, 0.5);

$count(); // 50

fromCallable - 將給定的 callable 函數轉化成匿名函數

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

class Good {

    private $price;

    public function __construct(float $price)

    {

        $this->price = $price;

    }

}

function addDiscount(float $discount = 0.8){

    return $this->price * $discount;

}

$closure = \Closure::fromCallable('addDiscount');

$good = new Good(100);

$count = $closure->bindTo($good); 

$count = $closure->bindTo($good, Good::class);   // 報錯,不能重複綁定做用域

$count(); // 報錯,沒法訪問私有屬性

fromCallable 等價於

1

2

$reflexion = new ReflectionFunction('addDiscount');

$closure = $reflexion->getClosure();

這裏有一點須要特別注意的是,不管是 fromCallable 轉化成的閉包,仍是使用反射獲得的閉包,在使用 bindTo 時,若是第二個參數指定綁定類,會報錯

1

Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure()

相關文章
相關標籤/搜索