提出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" )
|
全部的匿名函数都是 Closure 对象的实例
1 |
$greet instanceof Closure
|
对象并无什么父做用域可言,因此须要使用 use 来手动声明使用的变量,
1 2 3 4 5 6 7 |
$num = 1;
$func = function () use ( $num ){
$num = $num + 1;
echo $num ;
}
$func ();
echo $num ;
|
若是要让匿名函数中的变量生效,须要使用引用传值
1 2 3 4 5 6 7 |
$num = 1;
$func = function () use (& $num ){
$num = $num + 1;
echo $num ;
}
$func ();
echo $num ;
|
从 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();
$obj ();
|
若是不想让自动绑定生效,可以使用静态匿名函数
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();
$obj ();
|
匿名函数的本质
匿名函数的本质是 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();
|
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);
$count = $addDiscount ->bindTo( $good , Good:: class );
$count ();
$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 |
class Good {
static $num = 10;
}
$sell = static function () {
return "当前库存为" . -- static :: $num ;
};
$sold = \Closure::bind( $sell , null, Good:: class );
$sold ();
$sold ();
|
call - PHP 7 新增的 call 方法能够实现绑定并调用匿名函数,除了语法更加简洁外,性能也更高
1 2 3 4 5 |
$addDiscount ->call( $good , 0.5);
$count = $addDiscount ->bindTo( $good , Good:: class , 0.5);
$count ();
|
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()
|