__invoke
makes sense when you need one callable that can maintain an internal state. Say you want to sort an array:
$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]
];
The function usort allows you to sort an array using a simple function. However in this case we want to sort the array based on the internal key 'value'
, which can be done as follows:
$comparisonFn = function($a, $b) {
return $a['value'] < $b['value'] ? -1 : ($a['value'] > $b['value'] ? 1 : 0);
};
usort($arr, $comparisonFn);
// o resultado vai ser um array onde
// ['key' => 'w', 'value' => 2] é o primeiro elemento,
// ['key' => 'w', 'value' => 3] é o segundo, etc
Now you may need to reorder the array, this time based on the key key
, it would be necessary to rewrite the function:
usort($arr, function($a, $b) {
return $a['key'] < $b['key'] ? -1 : ($a['key'] > $b['key'] ? 1 : 0);
});
As you can see the function logic is identical, however we could not reuse the previous one due to the need to sort based on a different key. This problem can be solved with a class that encapsulates comparison logic in the method __invoke
and that defines the key to be used in the constructor, ex:
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);
}
}
An object of a class that implements __invoke
is a "callable", it can be used in any context that a function could, so now we can simply instantiate objects Comparator
and pass them as the comparison function to usort
:
usort($arr, new Comparator('key')); // ordena por 'key'
usort($arr, new Comparator('value')); // ordena por 'value'
usort($arr, new Comparator('weight')); // ordena por 'weight'
Stretch the front reflects my opinion, and as such, subjective, you can stop reading here if you want ;): Although this is an extremely interesting example of the use of __invoke
, such cases are rare and I would particularly stick with the understanding of the functioning for case cross with some code so, but would avoid its use since, although the example shown is simple, it can be done in very confusing ways and there are usually clearer alternatives to implementation (although not always so comprehensive). An example in the same previous comparison problem would be the use of a function that returns the comparator function:
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'));
Although this example requires a little familiarity with Standard | closures | Anonymous functions it is much more concise since it does not create the whole structure of a class just to store a simple external variable.
I think someone could give an "increment" in the answers, adding details such as: The instance of the class that implements invoke can be evaluated as
true
when we usecallable
oris_callable
.– Wallace Maxters
Hm, but this is already implicit in the excerpt of my reply that says that an object that implements __invoke is a callable.
– BrunoRB