在PHP中持有函数的类变量

时间:2014-08-08 21:52:28

标签: php class function-pointers instance-variables

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首先查找函数,只能通过上下文判断它是否是变量我猜这是故事的道德。

6 个答案:

答案 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)

查看call_user_func

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 bethere will be no release named PHP 6)。