当类实例像函数一样被调用时的魔术方法


Magic method for when a class instance is called like a function?

我想要做的是创建一个类似于.NET中的IComparableComparable类,这样您就可以像这样实例化它:

$cmp = new MultiArrayCompare(2);

然后你可以通过对数组进行排序

usort($myArray, $cmp);

它将对第二个索引上的数组进行排序。要做到这一点,我想usort会尝试像函数一样调用$cmp,所以我必须以某种方式覆盖这种行为。看起来__call并不像我想做的那样(我想了一会儿,它就像Python的__call__)。

如果这不可能。。。有没有另一种很好的方法来创建这个问题的通用解决方案?在哪里可以创建一个用户定义的排序函数,但给它一些值(在这种情况下为"2")?


使用__invoke,我能够创建以下类:

abstract class Comparable {
    abstract function Compare($a, $b);
    function __invoke($a, $b) {
        return $this->Compare($a, $b);
    }
}
class ArrayCompare extends Comparable {
    private $key;
    function __construct($key) {
        $this->key = $key;  
    }
    function Compare($a, $b) {
        if($a[$this->key] == $b[$this->key]) return 0;
        return $a[$this->key] < $b[$this->key] ? -1 : 1;
    }
}
class ArrayCaseCompare extends Comparable {
    private $key;
    function __construct($key) {
        $this->key = $key;  
    }
    function Compare($a, $b) {
        return strcasecmp($a[$this->key], $b[$this->key]);
    }
}

我可以用它对数组进行排序:

$arr = array(
    array(1,2,3),
    array(2,3,4),
    array(3,2,4),
)
usort($arr,new ArrayCompare(1));

您正在寻找__invoke:

当脚本试图将对象作为函数调用时,会调用__invoke()方法。

示例:

class SortAscending
{
    public function __invoke($a, $b)
    {
       return $a - $b;
    }
}
$numbers = array(4,7,2,3,9,1);
usort($numbers, new SortAscending);
print_r( $numbers );

输出(演示):

Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 7 [5] => 9 ) 

更苗条的选择是使用闭合。你的代码很简单:

$arr = array(
    array(1,2,3),
    array(2,3,4),
    array(3,2,4),
);
$key = 1;
usort($arr, function($a, $b) use ($key) {
    return $a[$key] - $b[$key];
});

或可重复使用和可配置(演示):

$ArrayCompare = function ($index) {
    return function($a, $b) use ($index) {
        return $a[$index] - $b[$index];
    };
};
usort($arr, $ArrayCompare(1));

除此之外,您基本上可以使用任何您想要的方法,并在回调中指定方法:

usort($arr, array(new ArrayCompare(1), 'Compare'));

如果没有魔法,以上内容也会实现。有关其他选项,请参阅有关回调的章节。

也许您正在寻找__invoke()?

手册示例:

class CallableClass
{
    public function __invoke($x)
    {
        var_dump($x);
    }
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));

为什么不创建一个提供一组静态compareXXX()方法的Comparer类?

对我来说,考虑到面向对象的方法,这看起来更干净。。。