I have a method that accepts a callback as a parameter. I would like to provide a signature in the PHPDoc for the class method that outlines the parameters for the callback function to be passed to that method so that my IDE (PHPStorm) can produce valid type hints for functions that are passed to my method, or at least someone looking at the code can determine the signature of the callback they're intended to provide.
For example:
class Foo {
public $items = [];
/**
* @param Callable(
* @param ArrayObject $items The list of items that bar() will return
* ) $baz A callback to receive the items
**/
public function bar(Callable $baz) {
$items = new ArrayObject($this->items);
$baz($items);
}
}
The method bar
has one parameter, $baz
, which is a callback function. Any function passed as an parameter to bar()
must accept an ArrayObject
as its only parameter.
Ideally, it should be possible to include multiple parameters for the Callable
, just like for any other method.
When I write the following code:
$foo = new Foo();
$foo->bar(function(
...I should then receive a parameter list that correctly hints the type (ArrayObject
) of the accepted parameter for this function call.
Is such a thing possible? Does PHPStorm or another IDE support it? Is there a recommended/standard way of documenting this even if there is no IDE support?
PHP 7+:
Using an interface for the callable combined with anonymous classes will do the trick. It's not very handy and leads to bit too complex code for the class-consumer, but currently it's the best solution in terms of static code-analysis.
/**
* Interface MyCallableInterface
*/
interface MyCallableInterface{
/**
* @param Bar $bar
*
* @return Bar
*/
public function __invoke(Bar $bar): Bar;
}
/**
* Class Bar
*/
class Bar{
/**
* @var mixed
*/
public $data = null;
}
/**
* Class Foo
*/
class Foo{
/**
* @var Bar
*/
private $bar = null;
/**
* @param MyCallableInterface $fn
*
* @return Foo
*/
public function fooBar(MyCallableInterface $fn): Foo{
$this->bar = $fn(new Bar);
return $this;
}
}
/**
* Usage
*/
(new Foo)->fooBar(new class implements MyCallableInterface{
public function __invoke(Bar $bar): Bar{
$bar->data = [1, 2, 3];
return $bar;
}
});
If you're using PhpStorm it will even auto-generate the __invoke
-Method's signature & body within the anonymous class.
I overcame this problem by defining a static function
within the class using the callable
. That function has its own doc-block and I just refer to it in the method that's requiring my callable using PHPDoc's @see
tag.
class Foo
{
/**
* Description of the "bar" callable. Used by {@see baz()}.
*
* @param int $index A 1-based integer.
* @param string $name A non-empty string.
* @return bool
* @see baz()
* @throws \Exception This is a prototype; not meant to be called directly.
*/
public static barCallable($index, $name)
{
throw new \Exception("barCallable prototype called");
}
/**
* Description of the baz() method, using a {@see barCallable()}.
*
* @param callable $bar A non-null {@see barCallable()}.
* @see barCallable()
*/
public function baz(callable $bar)
{
// ...
call_user_func($bar, 1, true);
// ...
}
}
This works well in PhpStorm 10. the Quick Documentation allows to navigate from the method documentation to the prototype documentation easily.
I make my prototype function throw an exception to make it clear it's not meant to be called. I could use a protected
or private
scope, but then PHPDoc would not always pick the doc-block for documentation generation.
Unfortunately PhpStorm can't track usage of callbacks. It doesn't provide Parameter Info either when using the method requiring a callback, but the callback is at least formally documented.
This approach also has the added benefit of validating a callback definition from the reflection of the prototype at run-time.
It is not possible in PhpStorm by now. I can't even think of other solution which do relatively the same by other means.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With