Использование __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 | крышки | анонимные функции гораздо более кратки, поскольку не создают целую структуру класса только для хранения внешнего значения.