0

I am currently upgrading a project to use phpstan to better catch type errors, @template and class-string have been particularly helpfull

However, I have a method on the projects router, that takes two string parameters for a controller action.

Those parameters map to a controller class name, and method, eg:

    /**
     * @param class-string<Controller> $controllerName
     * @param string $methodName
     * @return Route
     */
     public function toController(string $controllerName, string $methodName) : Route

I can tell phpstan that the first parameter $controllerName must be a class name, and a subtype of Controller. However the $methodName param is currently just an untyped string.

I would like to be able to say it must be a public method on the class $className, but cannot find anything in the docs, or google searching.

I did find key-of<Type::ARRAY_CONST> which seems close, but works on const arrays.

Does anything exist to handle this at present?

1 Answer 1

3

If, instead of two strings, you took one callable, then you could still use the same strings on invocation (i.e., the "class name and method name in an array" variant of callable) and PHPStan would enforce the actual callability of the args in a static context:

class Controller
{
    public function bar(): void
    {
    }
}

class Caller
{
    public static function toController(callable $thing): void
    {
        call_user_func($thing);
    }
}

class Main
{
    protected function test(): void
    {
        Caller::toController(['Controller', 'bar']); // valid class and method
        Caller::toController(['Controller', 'baq']); // bad method
        Caller::toController(['NotAController', 'foo']); // bad class
    }
}

PHPStan output:

 ------ ---------------------------------------------------------------------
  Line   Main.php
 ------ ---------------------------------------------------------------------
  8      Parameter #1 $thing of static method Caller::toController() expects
         callable(): mixed, array{'Controller', 'baq'} given.
  9      Parameter #1 $thing of static method Caller::toController() expects
         callable(): mixed, array{'NotAController', 'foo'} given.
 ------ ---------------------------------------------------------------------
Sign up to request clarification or add additional context in comments.

5 Comments

This is nice but only works correctly for static methods. For non-static methods, phpstan will detect that this is not callable.
This solution works with non-static methods for versions of PHPStan prior to v2.0.0, which was not available at the time of writing. Now you'd simply have to add the static keyword to your controller method definition. (One would presume it's being called statically because the parameter being passed is a string and not an object instance.)
Most of the time you don't want to add a static keyword just for that. And if you do, but then use $this anywhere in that method, PhpStan will complain again. In my case, these methods are really not static, and the [$class, $method] is not a callable, but rather, the $class refers to a service which is an instance of that class.
If you already have an instance then just pass that instead of the class name. [$class, $method] still works as a callable if $class is an object with a public method $method, and nothing has to be static.
I don't have an instance at that point in the process. I have a class name and a method name passed to an attribute like '#[MyAttr(MyClass::class, 'foo')]`, which is then collected in a discovery process and stored for later use. I definitely cannot and should not instantiate all of those classes during the discovery.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.