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.
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
{
// ....
}
}