Search code examples
phplaravelphpstormlumen

How to correctly label call_user_func to prevent warning : Exception is never thrown in the corresponding 'try' block (PhpStorm / Lumen)?


I have Authenticate middleware in my Lumen app that looks like this:

class Authenticate
{
    public function handle(Request $request, Closure $next, string|null $guard = null): mixed
    {
        try {
            /** @var \Illuminate\Auth\RequestGuard $requestGuard */
            $requestGuard = $this->auth->guard($guard);
            $signedIn = $requestGuard->check();

            // ...

        } catch (NoUserIdProvidedException) {
            // ...
        }

        // ...
    }
}

It works fine, but PhpStorm reports that the exceptions (I removed most from the example, there are a few) are not thrown by the containing block, when they are.

Seems that deep in the RequestGuard it uses call_user_func

return $this->user = call_user_func(
    $this->callback, $this->request, $this->getProvider()
);

To call a closure set up in the AuthServiceProvider, which uses the middleware method on the custom Security class:

class AuthServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->app['auth']->viaRequest('api', function ($request) {
            $security = new Security();

            return $security->middleware($request);
        });
    }
}

The middleware looks to me docblocked correctly

    /**
     * @param Request $request
     * @return bool|object|null
     * @throws InvalidDomainUser
     * @throws NoDomainUserException
     * @throws NoTokenOnRecordException
     * @throws NoTokenProvidedException
     * @throws NoUserException
     * @throws NoUserIdProvidedException
     */
    public function middleware(Request $request): object|bool|null
    {

adding the docblocks, like:

/**
* @throws NoUserIdProvidedException
*/

in the closure, the auth provider or the handle code does not make the warning go away, is there a way to comment or type hint the code to avoid false positives? I don't want to just switch off the inspection.


Solution

  • It seems that the way the guards work is just a bit too convoluted for static analysis, so I refactored, moving the underlying custom code out of the guard, and directly into the middleware and this worked, the exceptions are now correctly detected.

    class Authenticate
    {
        public function handle(Request $request, Closure $next, string|null $guard = null): mixed
        {
            try {
                $security = new Security();
                $user = $security->middleware($request);
                $signedIn = !empty($user->id);
    
                // ...
    
            } catch (NoUserIdProvidedException) {
                // ...
            }
    
            // ...
        }
    }
    

    The security class is custom logic, the important bit is that the doc blocks with the @throws are close enough to be found by the IDE

    class Security{
        /**
         * @param Request $request
         * @return bool|object|null
         * @throws InvalidDomainUser
         * @throws NoDomainUserException
         * @throws NoTokenOnRecordException
         * @throws NoTokenProvidedException
         * @throws NoUserException
         * @throws NoUserIdProvidedException
         */
        public function middleware(Request $request): object|bool|null
        {
          // ....
        }
    }