当您需要一个必须维护某些内部状态的可调用时,使用__invoke
是有意义的。假设您要对以下数组进行排序:
$arr = [
['key' => 3, 'value' => 10, 'weight' => 100],
['key' => 5, 'value' => 10, 'weight' => 50],
['key' => 2, 'value' => 3, 'weight' => 0],
['key' => 4, 'value' => 2, 'weight' => 400],
['key' => 1, 'value' => 9, 'weight' => 150]
];
usort函数允许您使用某些函数对数组进行排序,非常简单。但是,在这种情况下,我们希望使用其内部数组键对数组进行排序,可以这样完成:'value'
$comparisonFn = function($a, $b) {
return $a['value'] < $b['value'] ? -1 : ($a['value'] > $b['value'] ? 1 : 0);
};
usort($arr, $comparisonFn);
// ['key' => 'w', 'value' => 2] will be the first element,
// ['key' => 'w', 'value' => 3] will be the second, etc
现在,也许您需要再次对数组进行排序,但这次使用作为目标键,有必要重写函数:'key'
usort($arr, function($a, $b) {
return $a['key'] < $b['key'] ? -1 : ($a['key'] > $b['key'] ? 1 : 0);
});
如您所见,该函数的逻辑与前一个函数相同,但是由于需要使用不同的键进行排序,因此我们无法重用前一个函数。这个问题可以通过一个类来解决,该类封装了方法中的比较逻辑,并定义了要在其构造函数中使用的键:__invoke
class Comparator {
protected $key;
public function __construct($key) {
$this->key = $key;
}
public function __invoke($a, $b) {
return $a[$this->key] < $b[$this->key] ?
-1 : ($a[$this->key] > $b[$this->key] ? 1 : 0);
}
}
实现它的 Class 对象是“可调用的”,它可以在函数可能的任何上下文中使用,所以现在我们可以简单地实例化对象并将它们传递给函数:__invoke
Comparator
usort
usort($arr, new Comparator('key')); // sort by 'key'
usort($arr, new Comparator('value')); // sort by 'value'
usort($arr, new Comparator('weight')); // sort by 'weight'
以下段落反映了我的主观意见,所以如果你愿意,你可以停止阅读现在的答案;):尽管前面的例子显示了非常有趣的用法,但这种情况很少见,我会避免使用它,因为它可以以非常混乱的方式完成,并且通常有更简单的实现替代方案。在同一排序问题中,替代方法的一个示例是使用返回比较函数的函数:__invoke
function getComparisonByKeyFn($key) {
return function($a, $b) use ($key) {
return $a[$key] < $b[$key] ? -1 : ($a[$key] > $b[$key] ? 1 : 0);
};
}
usort($arr, getComparisonByKeyFn('weight'));
usort($arr, getComparisonByKeyFn('key'));
usort($arr, getComparisonByKeyFn('value'));
虽然此示例需要与 lambdas |闭包|匿名函数更加紧密,但它更加简洁,因为它不会创建整个类结构,只是为了存储外部值。