PHP允许变量保存如下函数:
$f = function($a,$b) {
print "$a $b";
};
$f("Hello","World!"); //prints 'Hello World!'
这对我来说很好。我试图将一个函数传递给一个类并设置一个实例变量来保存该函数,但运气不佳:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
我在这里做错了吗?错误是该函数不存在所以我的猜测是它寻找具有该名称的类函数(其中没有一个),然后放弃而不是寻找变量。如何让它将$ this-> distanceFunc视为变量?
编辑: 所以根据下面的答案的建议,我找到了一个解决方案,它是一个函数来包装调用。例如,我的班级现在是:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print $this->distanceFunc(1,7); //exceptions and errors abound
}
private function distanceFunc($a,$b) {
$holder = $this->distanceFunc;
return $holder($a,$b);
}
}
$func = function($a,$b) {
return abs($a-$b);
}
$c = new Clusterer($func);
这很有效。 Php首先查找函数,只能通过上下文判断它是否是变量我猜这是故事的道德。
答案 0 :(得分:3)
您的代码不起作用,因为PHP将$this->distanceFunc(1,7)
解释为类方法,但您可以执行以下操作:
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func_array($this->distanceFunc, array(1, 7));
// print $this->distanceFunc(1,7); //exceptions and errors abound
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
http://sandbox.onlinephpfunctions.com/code/cdc1bd6bd50f62d5c88631387ac9543368069310
答案 1 :(得分:3)
在PHP中,对象的方法和属性占用不同的命名空间。这与JavaScript不同,例如,foo.bar = function() {}
是定义方法的完全有效的方法。
因此,$this->distanceFunc(1,7);
在当前类中查找名为distanceFunc
的方法,以及它从中继承的类,但从不查找您碰巧遇到的属性同名。
一种解决方案是强制PHP查找属性,然后执行它,例如$foo = $this->distanceFunc; $foo(1,7)
或call_user_func($this->distanceFunc, 1, 7)
另一种方法是在类上定义魔术方法__call
,只要引用不存在的方法就会运行该方法。这样的事情应该有用(我现在没有简单的方法来证明):
function __call($func, $args) {
if ( property_exists($this, $func) && is_callable($this->$func) ) {
return call_user_func_array($this->$func, $args);
}
}
请注意,这仍然与真实方法不同,例如在访问私有属性方面。
答案 2 :(得分:2)
看起来你在这里想要一个战略模式。 IE你想能够注入不同的计算距离的方法吗?如果是这样,有一种更“理智”的方式来做到这一点。
您可以定义用于存储策略方法的类的接口,确保类始终具有方法calculate()
,例如,这将是您的距离计算功能。然后在Clusterer
类的构造函数中,对参数中的接口键入check,并对传入的对象调用calculate()。
看起来像这样:
interface Calculateable
{
public function calculate();
}
class MyDistanceCalculator implements Calculateable
{
public function calculate()
{
// Your function here
}
}
class Clusterer
{
protected $calc;
public function __construct(Calculateable $calc)
{
$this->calc = $calc;
$this->calc->calculate();
}
}
$myClusterer = new Clusterer(new MyDistanceCalculator());
因为您定义了一个接口,所以传入的任何对象都将具有calculate()函数
答案 3 :(得分:1)
PHP没有第一类功能。在JavaScript中,如果您返回了一个函数,则可以执行此操作:myFunctionThatReturnsAFunction()(1,2)
,但不能在PHP中执行此操作。
<?php
class Clusterer {
private $distanceFunc;
public function __construct(Closure $f) {
$this->distanceFunc = $f;
}
public function getDistFunc()
{
return $this->distanceFunc;
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
$a = $c->getDistFunc();
echo $a(1,2);
答案 4 :(得分:1)
class Clusterer {
private $distanceFunc;
public function __construct($f) {
$this->distanceFunc = $f;
print $f(1,7); //works
print call_user_func($this->distanceFunc, 1, 7); //works too ;)
}
}
$func = function($a,$b) {
return abs($a-$b);
};
$c = new Clusterer($func);
不要问我有什么区别,但它的工作方式是你想要的(我讨厌这种语言的原因之一)
答案 5 :(得分:1)
在HHVM中,您可以这样做:
<?php
class Foo
{
public function __construct()
{
$this->bar = function() { echo "Here\n"; };
($this->bar)();
}
}
new Foo();
但PHP尚未支持它。但是,它在PHP 7中will be(there will be no release named PHP 6)。