phpsymfonysymfony-3.4type-hintingphpstan

Correct type hint error thrown by PHPStan on UserInterface in Symfony project


I've recently started using PHPStan (version 0.12.19) on a Symfony 3.4 project but I'm getting an error which seems like it should have been simple to resolve but I'm struggling to figure out.

Currently running at level 7. Here's the error I get when I run:

vendor/bin/phpstan analyse

------ --------------------------------------------------------------------------------------------------------------
  Line   src/AppBundle/Controller/MapController.php
 ------ --------------------------------------------------------------------------------------------------------------
  94     Parameter #1 $user of static method AppBundle\Entity\MapMarker::createMapMarker() expects
         Symfony\Component\Security\Core\User\UserInterface, object given.

This is the important parts of MapController.php:

    $user = $this->getUser();

     $mapMarker = MapMarker::createMapMarker(
            $user,
            $latitude,
            $longitude
        );

The getUser method is the Symfony one so I can't change this part: https://github.com/symfony/symfony/blob/3.4/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php#L444:

/**
     * Get a user from the Security Token Storage.
     *
     * @return UserInterface|object|null
     *
     * @throws \LogicException If SecurityBundle is not available
     *
     * @see TokenInterface::getUser()
     *
     * @final since version 3.4
     */
    protected function getUser()
    {
        if (!$this->container->has('security.token_storage')) {
            throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".');
        }

        if (null === $token = $this->container->get('security.token_storage')->getToken()) {
            return null;
        }

        if (!\is_object($user = $token->getUser())) {
            // e.g. anonymous authentication
            return null;
        }

        return $user;
    }

And the important parts of MapMarker.php:

/**
     * @param UserInterface $user
     * @param double $latitude
     * @param double $longitude
     */
    private function __construct(UserInterface $user, $latitude, $longitude) {
        $this->createdBy = $user;
        $this->latitude = $latitude;
        $this->longitude = $longitude;
    }

    /**
     * @param UserInterface $user
     * @param double $latitude
     * @param double $longitude
     * @return MapMarker
     */
    public static function createMapMarker(UserInterface $user, $latitude, $longitude): MapMarker
    {
        return new self($user, $latitude, $longitude);
    }

When I dump out $user instanceof UserInterface it returns true - so as far as I can tell it IS passing a UserInterface object, not just an "object" as PHPStan is indicating.

And finally, here's my phpstan.neon config file:

parameters:
    level: 7
    paths:
        - src
        - tests
    checkGenericClassInNonGenericObjectType: false
    checkMissingIterableValueType: false

What am I missing?


Solution

  • I think you at least got whats the proble: The return type of the method getUser is @return UserInterface|object|null while createMapMarker expects an instance of UserInterface.

    Before calling createMapMarker you should check that the return value of getUser is actually an instance of UserInterface and probably a simple /** @var UserInterface $user */ right on top the the $user variable declaration will fix.