El uso de __invoke
tiene sentido cuando necesita un callable que tiene que mantener un cierto estado interno. Digamos que desea ordenar la siguiente matriz:
$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]
];
La función usort le permite ordenar una matriz mediante una función, muy simple. Sin embargo, en este caso queremos ordenar la matriz usando sus paneles interiores 'value'
clave, lo que podría hacerse de esta manera:
$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
Ahora tal vez lo que necesita para ordenar de nuevo la matriz, pero esta vez utilizando 'key'
como la clave de destino , sería necesario volver a escribir la función:
usort($arr, function($a, $b) {
return $a['key'] < $b['key'] ? -1 : ($a['key'] > $b['key'] ? 1 : 0);
});
Como se puede ver la lógica de la función es idéntica a la anterior, sin embargo, no podemos volver a utilizar el anterior, debido a la necesidad de clasificar con una diferente llave. Este problema puede abordarse con una clase que encapsula la lógica de comparación en el método __invoke
y que definen la clave para ser utilizado en su constructor:
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);
}
}
un objeto de clase que implementa __invoke
se trata de un "exigible", se puede ser utilizado en cualquier contexto que una función podría ser, por lo que ahora podemos simplemente crear una instancia Comparator
objetos y pasarlos a la función 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'
los siguientes párrafos reflejan mi opinión subjetiva, por lo que si se desea se puede dejar de leer la respuesta ahora;): Aunque el ejemplo anterior mostró un uso muy interesante de __invoke
, estos casos son raros y evitaría su uso, ya que se puede hacer de maneras realmente confusas y, en general, hay alternativas de implementación más simples. Un ejemplo de una alternativa en el mismo problema de clasificación sería el uso de una función que devuelve una función de comparación:
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'));
Aunque este ejemplo se requiere un poco más de intimidad con lambdas | closures | anonymous functions es mucho más concisa, ya que no crea una estructura de clase completa solo para almacenar un valor externo.
no lo entiendo ... ¿qué funciones de primera clase exactamente si no es $ a? $ a = function ($ x, $ y) {return ($ x <$ y? $ x: $ y;}; $ min = array_reduce (array (1,2,3), $ a); – stefs
oooooh! okay , perdón por mezclar funciones anónimas/first class/lambda todo el tiempo. – stefs
funciones de primera clase/cierres en PHP 5.3: función multiplyBy ($ x) {return function ($ y) use ($ x) {return $ x * $ y;};}.$ mulby3 = multiplyBy (3); $ twentyone = $ mulby3 (7); - __invoke es> 5.3 también, por lo que usar __invoke como reemplazo de pseudo-first-class no siempre tiene sentido. – stefs