php 闭包
创始人
2024-02-11 22:55:44
0

一、概念说明

通常定义php函数时,都会指定一个函数名,这样的函数可以称为具名函数,但实际上PHP也支持定义没有函数名的函数,这类函数被称为闭包,也叫匿名函数,其本质是 Closure 类对象,类摘要如下

// 类用final修饰,防止定义子类
final class Closure {// 构造函数被定义为私有,防止匿名函数被实例化private __construct ( void )public static bind ( Closure $closure , object $newthis [, mixed $newscope = "static" ] ) : Closurepublic bindTo ( object $newthis [, mixed $newscope = "static" ] ) : Closurepublic call ( object $newthis [, mixed $... ] ) : mixedpublic static fromCallable ( callable $callable ) : Closurepublic __invoke( ...$values): mixed
}

二、函数说明

2.1 __invoke

当以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用,直接调用__invoke 也是一样会执行编写的函数体,下面是示例

function callInvoke(){$func = function ($name,$age){echo __FUNCTION__," name=$name,age=$age\n";};$func->__invoke("kate",100);
}callInvoke();

输出结果

{closure} name=kate,age=100

所以,编写的匿名函数函数体可以被认为是 __invoke 函数的函数体。

2.2 bindTo

返回一个新的闭包,新的闭包绑定了指定的 this对象和类作用域。也就是匿名函数中this 对象和类作用域。也就是匿名函数中this对象和类作用域。也就是匿名函数中this表示的是被绑定的对象,可以直接使用其成员变量和成员函数,反过来,相当于指定的对象临时增加了一个新的方法,只不过只能用普通的函数的调用方式来调用,某种程度上扩展了对象的功能。

// 定义商品类
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(); // 报错

2.3 bind

该函数是 bindTo 方法的静态版本,有两种用法:
用法一:实现与 bindTo 方法同样的效果

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

用法二:将匿名函数与类(而不是对象)绑定,此时第二个参数需要设置为 null

// 商品库存为 10
class Good {static $num = 10;
}// 每次销售后返回当前库存
$sell = static function() {echo "当前库存为". --static::$num,"\n" ;
};// 将静态匿名函数绑定到 Good 类中
$sold = \Closure::bind($sell, null, Good::class);$sold(); 
$sold();

输出如下

当前库存为 9
当前库存为 8

2.4 call

call 方法是PHP 7 新增的,可以实现绑定并调用匿名函数,语法更加简洁,性能更高。下面是两种方式的示例代码。

// call 版本
$addDiscount->call($good, 0.5);  // 绑定并传入参数 0.5,结果为 50// bindTo 版本
$count = $addDiscount->bindTo($good, Good::class); 
$count(0.5); // 50

2.5 fromCallable

将给定的 callable 函数转化成匿名函数

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 等价于
$reflexion = new ReflectionFunction('addDiscount');
$closure = $reflexion->getClosure();

有一点需要特别注意,无论是 fromCallable 转化成的闭包,还是使用反射得到的闭包,在使用 bindTo 时,如果第二个参数指定绑定类,就会报下面的错错误

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

三、使用

匿名函数的本质是对象,因此可将匿名函数赋值给某一变量。

3.1 使用外部变量

通过 use 声明的变量可以在匿名函数内部使用,如果声明的是引用,那么在匿名函数中修改变量后,外部变量会同步修改。

$num = 1;
$func = function() use($num){$num = $num + 1;echo $num;
}
$func();  // 2
echo $num;  // 还是 1// 要让匿名函数中对外部变量的修改在闭包结束后仍然生效,需要使用引用传值$num = 1;
$funcRef = function() use(&$num){$num = $num + 1;echo $num;
}
$funcRef();  // 2
echo $num;  // 2

3.2 自动绑定$this到当前类

从 PHP 5.4 开始,在类里面使用匿名函数时,匿名函数的 $this 将自动绑定到当前类

class Foo {public function bar(){return function() {return $this;};}public function getAge(){return 100;}
}function testThis(){$foo = new Foo();$obj = $foo->bar();var_dump($obj);//var_dump($obj()->getAge());
}testThis();

输出结果如下

object(Closure)#2 (1) {["this"]=>object(Foo)#1 (0) {}
}
int(100)

如果不想让自动绑定生效,可以使用静态匿名函数,此时返回的闭包中不能使用 $this,否则一调用就会报下面的 Fatal error

class FooStatic
{public function bar(){return static function () {return $this;};}public function getAge(){return 100;}}function testStaticThis()
{$foo = new FooStatic();$obj = $foo->bar(); // Closure()var_dump($obj);$obj(); // Fatal error: Using $this when not in object context
}
testStaticThis();

因此静态匿名函数实际上是绑定了整个类,可以通过static关键字访问类静态变量,示例代码如下

class FooStatic
{static $quantity = 10;public function bar(){return static function () {echo static::$quantity,"\n";};}
}$foo = new FooStatic();
$obj = $foo->bar(); // Closure()
var_dump($obj);$obj();

输出如下

object(Closure)#2 (0) {
}
10

参考

php手册 https://www.php.net/manual/zh/class.closure.php
心智极客 https://learnku.com/articles/35863

相关内容

热门资讯

台积电:今年全球新建9座厂,年... 5月15日,台积电营运副总经理张宗生在其技术论坛上表示,台积电2017年到2020年平均一年建置3座...
山东16市全部出台“好房子”相... 格隆汇5月15日丨据大众日报,从5月14日召开的山东省“好房子”建设工作会上获悉,当前,山东正加快推...
中国石化相关公司新增一项123... (转自:快查一企业中标了)快查APP显示,中国石化相关公司中国石化上海石油化工股份有限公司于2025...
【光明时评】农家院落也可变成增... 近期,一种新型农村经济发展模式正在全国各地兴起。昔日普通的农家院落,如今摇身一变成为带动村民增收的“...
伊朗官员发声,事关核武器!伊总... 每经编辑|杜宇 据央视新闻5月15日消息,美国全国广播公司新闻台5月14日报道,伊...
有人顶风作案!中央指导组进驻后... 按照中央党的建设工作领导小组统一部署,5月10日,深入贯彻中央八项规定精神学习教育中央第三指导组指导...
急转!央视曝光新骗术遥控取钱 【急转!#央视曝光新骗术遥控取钱#】#男子被骗要给陌生人22万现金# 前不久,北京的马先生被拉入了一...
数聚北疆 智启未来——国际媒体... 转自:环球网  【环球时报-环球网 记者 史洁瑞】5月13日至14日,由中国公共外交协会与环球网联合...
青海省药检院开展技术帮扶工作   中国食品药品网讯 5月6日至5月10日,青海省药品检验检测院(以下简称青海省药检院)派出由结对帮...
中金公司相关公司新增一项300...   炒股就看金麒麟分析师研报,权威,专业,及时,全面,助您挖掘潜力主题机会! (转自:快查一企业中...
俄媒:俄罗斯谈判代表团已抵达土... 中新网5月15日电 据塔斯社援引消息人士的话报道,俄罗斯谈判代表团已经抵达土耳其伊斯坦布尔。据此前报...
闻泰科技跌2.01%,成交额1... 5月15日,闻泰科技(维权)盘中下跌2.01%,截至14:00,报34.09元/股,成交1.85亿元...
架起“云端丝路” 共绘航旅新篇... 上月15日,2025年“引客入克”暨沪克航旅推介会在上海成功举办,克拉玛依将增开两个航班:东方航空在...
民进党当局将“汉人”表述改为“... 转自:环球网据台湾联合新闻网5月15日报道,台行政机构网站有关台湾人口情况介绍中删除“汉人”表述,改...
路透社:美国今年麻疹病例破千例... 来源:海外网海外网5月15日电据路透社5月14日报道,路透社和益普索集团联合展开的一项最新民调显示,...
苏试试验跌2.04%,成交额1... 5月15日,苏试试验盘中下跌2.04%,截至13:38,报15.40元/股,成交1.02亿元,换手率...
吉利汽车:2025年第一季度营...   Hehson科技讯 5月15日下午消息,吉利汽车发布2025年第一季度财报。财报显示,第一季度,...
广电电气跌2.11%,成交额4... 5月15日,广电电气盘中下跌2.11%,截至13:44,报3.71元/股,成交4401.54万元,换...
黄仁勋将亮相台北Compute... 一年一度的Computex 2025电脑展将于下周在台北开幕。英伟达CEO黄仁勋将亮相展会并进行主题...
冯喜运:早间黄金原油价格涨跌解... 5月15日,周四,亚市早盘,现货黄金低位徘徊,目前交投于3183.50美元/盎司附近。金价周三下跌逾...