1

I have a factory method whose signature is similar to this:

/**
 * @param class-string $class
 * @param callable? $callback
 * @return static
 */
public function instance(string $class, callable? $callback);

From the first parameter, an instance is created. Rather than the instance being returned, $this is returned instead - so an optional callback can be provided and the factory invokes it, passing the new instance.

What I want to know is - given the string passed is literal, can phpstan/vscode be told that the argument passed to the callable is an instance of my specified class, so that when I refer to the object inside the callable it can make suggestions of available methods/props, and so that phpstan can not grumble about missing methods, without me also typehinting the argument of the callable?

The object created does extend a common base class, but child classes will have their own methods.

Maybe clearer with an example of a call:

$obj->factory(Foo::class, function ($foo) {
   //
}

And I’m hoping to avoid repeating the explicit hint:

$obj->factory(Foo::class, function (Foo $foo) {
   //
}
Mark G
  • 85
  • 1
  • 5

1 Answers1

1

You can use generics to achieve this.

Check out this example: https://phpstan.org/r/cf596842-d9cb-4996-b2af-08d6683dd366

Basically you'd need a declaration like this one:

/**
 * @template T of object
 * @param class-string<T> $class
 * @param (callable(T):mixed)|null $callback
 * @return static
 */
 public function instance(string $class, callable|null $callback)

Here we are saying that the callable has one required argument. And it's type is the object of the first parameter that is passed into the instance method.

You can read more about generics here and here.

Can Vural
  • 2,222
  • 1
  • 28
  • 43
  • If I remove all of the generics stuff and, before your ‘dump’ line add $foo->something(), it still appears to pass without error (though the output of the dump then says ‘mixed’ so it’s definitely recognising it WITH the genetics stuff) – Mark G Sep 26 '22 at 18:21
  • @MarkG I'm sorry. What is your point? If you remove the generic stuff, the callable parameter will be implicitly mixed so `$foo->something()` will not raise an error. That's expected. – Can Vural Sep 26 '22 at 18:32
  • Because in simpler situations, phpstan will moan at me unless it can be 100% sure that the variable has that method, even at much lower levels of checking. And ‘mixed’ type doesn’t seem to be a good guarantee - if the wrong type is passed then I’ll get a fatal. So I’m wondering why it’s not giving any sort of warning. – Mark G Sep 26 '22 at 18:37
  • From the docs it suggests: “ PHPStan’s rule level 6 isn’t satisfied with implicit mixed, but an explicit one is sufficient” but running at level 9 still doesn’t grumble. – Mark G Sep 26 '22 at 18:44
  • That issue sounds not related to your issue in the original post. Anyway you can try `checkImplicitMixed: true` in your config file. Maybe that'll report it. – Can Vural Sep 26 '22 at 18:48