Использование __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);
}
}
Объект класса, который реализует __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'));
Хотя в этом примере требуется немного больше близости с лямбда-выражениями | затворы | анонимные функции гораздо более лаконичны, поскольку не создают целую структуру класса только для хранения внешнего значения.
person
BrunoRB
schedule
08.02.2016