- трудно использовать повторно : если вы хотите повторно использовать замыкание или анонимную функцию в двух разных местах, вам придется делиться кодом создания, помещая его в общий класс или метод. У нас нет разделения между кодом создания (который для объекта будет новым оператором и кодом самого замыкания. Это немного похоже на eval ().
- Трудно заставить контракты на . Вы не можете ввести подсказку о закрытии: даже если замыкание принимает пять аргументов, а другое вообще не имеет аргументов, вы не можете различить их как параметры в сигнатуре метода.
В последнем случае вы можете просто использовать недокументированную подсказку типа Closure, но она будет просто отлавливать, если объект передается рассматриваемому методу, а не неправильному замыканию.
function takesAsInputAnotherFunction(Closure $closure)
Этот хак явно указан как деталь реализации, на которую не следует полагаться в руководстве по PHP
__invoke () на помощь
Что, если бы мы вместо этого хотели, замыкание, которое мы можем создать даже несколько раз (возможно, с разными переменными), и что мы могли бы напечатать подсказку?
В этом случае нам нужно вернуться к объектно-ориентированной парадигме, определив класс, а затем использовать __invoke (), чтобы сохранить синтаксический сахар возможности вызывать его очень просто:
$object($argument1, $argument2);$object($argument1, $argument2);
Этот вид объекта называется функтором .
The definition of a class is longer than the creation code of a closure, so there is a trade-off: we have better specified code but we lose conciseness. Type hints are not only a defensive programming construct: they serve also as documentation as the next programmer reading a method’s signature would learn what he can pass in just from the parameters hints instead of going reading the tests or grepping for the method in his working copy:
grep -r '->methodName(' .
becomes just a quick look at the definition
public function methodName(AdderCallback $callback) { // ...
However the expressing power of an object implementing __invoke() and of a closure are equivalent, and when you find yourself rewriting the same closure over and over, you may want to keep it in only one place. You’ll have to create a class to hold the creation code anyway, so why not making it a class of its own?
For the PHP interpreter, closure and functors are really the same thing, and are even more swappable than callbacks built with array($object, ‘methodName’). is_callable() returns true for both of them, and you can write $closure() as well as $functor().
Example
Here is some code that you can hack if you want to play with closures and functors.
<?php
$square = function($value) {
return $value * $value;
};
var_dump($square(3));
/**
* a little more noisy, but equivalent in usage and functionality-
*/
class SquareCallback
{
public function __invoke($value)
{
return $value * $value;
}
}
$squareObject = new SquareCallback;
var_dump($squareObject(3));
// using PHP utilities which works on callback: total equivalency
var_dump(is_callable($squareObject));
$array = array(0, 1, 2);
var_dump(array_map($square, $array));
var_dump(array_map($squareObject, $array));
// currying vs collaborators: total equivalency
$toAdd = 5;
$adder = function($value) use ($toAdd) {
return $value + $toAdd;
};
var_dump($adder(3));
/**
* A LOT more noisy
*/
class AdderCallback
{
private $numberToAdd;
public function __construct($numberToAdd)
{
$this->numberToAdd = $numberToAdd;
}
public function __invoke($value)
{
return $value + $this->numberToAdd;
}
};
$adder = new AdderCallback(5);
var_dump($adder(3));
// but only classes can do this
function someMethodOrFunction(AdderCallback $adder)
{
// ...
}