Подтвердить что ты не робот

Как я могу использовать PHPDoc для типа-намека на параметры вызываемого?

У меня есть метод, который принимает обратный вызов в качестве параметра. Я хотел бы предоставить сигнатуру параметра для обратного вызова как PHPDoc, чтобы моя IDE (PHPStorm) могла выдавать допустимые подсказки типа для функций, переданных моему методу, или, по крайней мере, кто-то, кто смотрит на код, может определить подпись обратного вызова, который они 'предназначен для предоставления.

Например:

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);
  }
}

Метод bar имеет один параметр $baz, который является функцией обратного вызова. Любая функция, передаваемая как параметр bar(), должна принимать ArrayObject как единственный параметр.

В идеале должно быть возможно включить несколько параметров для Callable, как и для любого другого метода.

Когда я пишу следующий код:

$foo = new Foo();
$foo->bar(function(

... Затем я должен получить список параметров, который правильно намекает тип (ArrayObject) принятого параметра для этого вызова функции.

Возможно ли такое? Поддерживает ли PHPStorm или другую среду IDE? Есть ли рекомендуемый/стандартный способ документирования этого, даже если нет поддержки IDE?

4b9b3361

Ответ 1

PHP 7 +:

Использование интерфейса для вызываемого в сочетании с анонимными классами сделает трюк. Это не очень удобно и приводит к слишком сложному коду для класса-потребителя, но в настоящее время это лучшее решение с точки зрения статического анализа кода.

/**
 * 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;
    }
});

Если вы используете PhpStorm, он даже автоматически сгенерирует подпись и тело __invoke -Method в теле анонимного класса.

Ответ 2

В PhpStorm это пока невозможно. Я даже не могу думать о другом решении, которое относительно одинаково с другими средствами.

Ответ 3

Я преодолел эту проблему, указав static function внутри класса с помощью callable. Эта функция имеет свой собственный doc-блок, и я просто ссылаюсь на нее в методе, который требует моего вызова с помощью тега PHPDoc @see.

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);
        // ...
    }
}

Это хорошо работает в PhpStorm 10. Quick Documentation позволяет легко перемещаться из документации метода в документацию прототипа.

Я заставляю свою прототипную функцию генерировать исключение, чтобы было ясно, что он не должен быть вызван. Я мог бы использовать область protected или private, но тогда PHPDoc не всегда выбирал блок doc для генерации документации.

К сожалению, PhpStorm не может отслеживать использование обратных вызовов. Он не предоставляет информацию о параметрах при использовании метода, требующего обратного вызова, но обратный вызов, по крайней мере, формально документирован.

Этот подход также имеет дополнительное преимущество для проверки определения обратного вызова из reflection прототипа во время выполнения.

Ответ 4

PHPDoc теперь допускает подсказку типа callable: @param callable $var_name.

class MyClass {
  /**
   * @param callable $func
   */
  public static function callme($func) {
    $func();
  }
}

MyClass::callme([MyClass::class, 'callme']); // Do not run this line! Test only!

PhpStorm (2019) предложил 'callme' в качестве элемента автозаполнения после того, как я набрал первый ', указывая, что он правильно понимает подсказку. Без подсказки написано "Нет предложений".