PHP能否像Python一样在类对象中存储函数并传参调用?
修复PHP中存储函数引用并调用的无限循环问题
首先,你的代码出现无限循环有两个核心原因:
1. 错误地执行了函数而非传递引用
在_create_couplet函数里,你写的$pairing->function1()是直接执行了function1方法(因为加了括号),而不是把方法本身的引用传给_Mapper。由于你没传参数,function1会返回null(因为$value默认是null,null * 2还是null),所以_Mapper的构造函数其实接收的是null,而不是函数引用。
2. 属性名与方法名冲突导致递归调用
你的_Mapper类里,属性名和方法名都是estimator。当你在estimator方法里写$this->estimator($value)时,PHP会优先把$this->estimator解析为当前的方法,而不是你想访问的属性,这就导致了无限递归调用estimator方法,最终引发无限循环。
修正后的代码
下面是修复后的完整代码,我会标注关键改动:
class Pairings { public function function1($value = null) { return $value * 2; } public function function2($value = null) { return $value * 4; } } class _Mapper { // 改动1:修改属性名,避免和方法名冲突 private $estimatorFunc; function __construct($estimatorFunc) { $this->estimatorFunc = $estimatorFunc; } function estimator($value) { // 改动2:调用存储的回调函数,这里用call_user_func处理类方法回调 return call_user_func($this->estimatorFunc, $value); } } function _create_couplet($pairing, $A, $B) { if ($A < $B) { // 改动3:传递类方法的回调引用,格式为[对象实例, '方法名'],不要加括号! return new _Mapper([$pairing, 'function1']); } else { return new _Mapper([$pairing, 'function2']); } } // 测试代码 $oldValue = 10; $A = 10; $B = 5; $pairing = new Pairings; $couplet = _create_couplet($pairing, $A, $B); $newValue = $couplet->estimator($oldValue); echo "New value: $newValue"; // 输出:New value: 40
其他可选实现方式:用闭包包裹方法
如果你觉得类方法的数组回调不够直观,也可以用闭包来包裹方法调用,这样代码可读性可能更好:
function _create_couplet($pairing, $A, $B) { if ($A < $B) { // 用闭包捕获$pairing,直接包裹方法调用 return new _Mapper(function($value) use ($pairing) { return $pairing->function1($value); }); } else { return new _Mapper(function($value) use ($pairing) { return $pairing->function2($value); }); } } // 对应的_Mapper的estimator方法可以简化,因为闭包可以直接调用 class _Mapper { private $estimatorFunc; function __construct($estimatorFunc) { $this->estimatorFunc = $estimatorFunc; } function estimator($value) { // 闭包可以直接像普通函数一样调用 return $this->estimatorFunc($value); } }
关键知识点总结
- 传递函数/方法引用时,不要加括号:加括号会立即执行函数并返回结果,而不是传递引用。PHP中类方法的回调格式是
[对象实例, '方法名'],普通函数直接传函数名字符串即可。 - 避免属性名与方法名同名:这会导致PHP解析时的歧义,很容易引发递归或者意外行为。
- 调用回调的方式:可以用
call_user_func()/call_user_func_array()处理所有类型的回调,闭包则可以直接像普通函数一样调用。
内容的提问来源于stack exchange,提问作者Philip Lee
相关产品推荐
相关产品推荐

