Search code examples
phplaravelauthenticationstatic-analysisphpstan

Resolve Laravel Auth Authenticatable to User model to address static analysis issues


We have a Laravel 8 application.

We're using the standard Laravel Auth facade to retrieve the authenticated user.

Our User model has a few custom functions, the most important of which is a shorthand function, hasPermissionTo(). (The reason why is because we have a very custom RBAC setup.)

So in a lot of our controllers, we have something like this...

use Illuminate\Routing\Controller as BaseController;

class ExampleController extends BaseController
{

  public function index()
  {
    if (\Auth::user()->hasPermissionTo('Management:View Users')) {
      // do something.
    }
    // etc.
  }
}

That's all well and good until we start running static analysis. We're using Larastan, which is giving me these errors:

------ -------------------------------------------------------------------------------------------
 Line   Http/Controllers/ExampleController.php
------ -------------------------------------------------------------------------------------------
  48     Call to an undefined method Illuminate\Contracts\Auth\Authenticatable::hasPermissionTo().

This also makes sense because the Auth facade proxies Illuminate\Auth\AuthManager and Auth::user(), via __call() magic, normally resolves to Illuminate\Auth\SessionGuard::user() and that typehints this...

    /**
     * Get the currently authenticated user.
     *
     * @return \Illuminate\Contracts\Auth\Authenticatable|null
     */
    public function user()
    {
    ...

So finally, my question:

Where is the failure here? Do I need to a) configure my static analysis tool better, b) configure Laravel better to more accurately return a specific type, or c) do I need to add explicit if (Auth::user() instanceof User) { ... } clauses all throughout my code?

Is there a correct way to override one of the Laravel stock classes with a more specific one of my own to address more specific functionality? Is there way to type-hint the actual authenticated User into the function declaration so I can declare function index(User $authenticatedUser) and have Laravel autopopulate this in with a more specific type hint?

I understand that I could just add an exclusion for this particular issue in Larastan and move on with my life, but the error is designed to protect against a specific class of error--i.e. if I added Auth0 and replaced App\Model\User with Auth0\Login\User, then I would have an Authenticatable class that fails to run hasPermissionTo(), and I'd have to now fix a bunch of code.


Solution

  • Eventually, this is how we worked around the problem. We added a type-hint for Larastan, so it can infer that $user has this HasRolesContract trait which provides hasPermissionTo().

      public function index()
      {
            /** @var \App\Traits\HasRolesContract */
            $user = \Auth::user();
    
            if ($user->hasPermissionTo('Management:View Users')) {
    

    Hopefully this helps someone else!

    (Thanks for the nudge, @djjavo)