Not all Mixed Types are Equally Useless

Do you have a big project where you try to raise the PHPStan level as high as possible? Yet, you're stuck on level 4 or 5 with thousands of errors? We all have one and try to chip away a few errors now and then.

The mixed type is the worst of all of them. Fixing all mixed types to a specific type is a nightmare for the REST of your life (pun intended). But what if there are places where fixing mixed type brings much more value than in the others?

We're in a state where code runs, tests pass, and everything works. We can find snippets like this one:

function printNames(array $names)
{
    foreach ($names as $name)
    {
        echo $name;
    }
}

We know that the $names parameter is mixed[] type. It is probably a string[], int[] or StringableInterface type. We could run the code, write the test, detect the type and complete it.

But by adding a specific type here, we will not get much new information. The value will still be echo-able, and the code will still work. We will have one less ignored error in phpstan.neon.

Business-Focused Low Hanging Fruit

Maybe my boss would let me remove all the mixed types from the project, but it would make their expenses high enough to fire me. The coding must be economical and beneficial for the project's future.

Instead of looking for the origin of mixed with the attention of detective Colombo, we could do something better with our time. We could find those mixed type that is now blocking PHPStan from further analysis. We do not have to test manually a mixed type and hope for the user input.

In the same project, we find the following code:

function printNames(array $videos)
{
    foreach ($videos as $video)
    {
        echo $this->renderUrl($video->getUrl());
    }
}

From the PHPStan point of view, this snippet is identical to the previous one. There is a variable $videos with mixed[] type. PHPStan can't analyze the mixed type, as it can be anything from scalar, null, false, object, collection of objects and nested array of all above, etc.

What can we assume from this code?

$video->getUrl()

How can we deduct the type of object here?

PHPStorm Search to the Rescue

Here you could read a complex passage about types, abstract syntax tree, static analysis, etc.


Instead, we'll be KISSing PHPStorm:


We see the responsible type is Video object, and we can complete it to our code:

+/**
+ * @param Video[] $videos
+ */
 function printNames(array $videos)
 {
     foreach ($videos as $video)
     {
         echo $this->renderUrl($video->getUrl());
     }
 }

Thanks to this type the PHPStan now can analyze if:

That's how we can use a method call on mixed to our advantage.


The "Object mixed" > any mixed

The second use case for object mixed is similar. You can probably already guess it:

function collectIds(array $videos): array
{
    $ids = [];
    foreach ($videos as $video)
    {
        $ids[] = $video->id;
    }

    return $ids;
}

Yes, it's property fetch. We can fetch property only on a specific object. Well, except stdClass that is typical for json_decode() return values. But apart from that, property fetch is a sure sign of a known object.

Again, we'll be KISSing PHPStorm:


And complete the types based on found Video object:

+/**
+ * @param Video[] $video
+ * @return int[]
+ */
 function collectIds(array $videos): array
 {
     $ids = [];
     foreach ($videos as $video)
     {
         $ids[] = $video->id;
     }

     return $ids;
 }

Our code is now slightly more intelligent, and PHPStan now sees:

How detect Object mixed with PHPStan?

We can teach PHPStan to report these low-hanging fruit cases. The conditions are straightforward:

// method call → MethodCall node
$video->getUrl();

$video->id;
// property fetch → PropertyFetch node

You'd be surprised if you'd have to write those rules on your own.

They're freshly included in symplify/phpstan-rules


How did the rule perform on Symplify itself? The property rule passed without any report, but the method called one found over 20 cases of unknown type. Despite the fact we have PHPStan on level 8. Pretty neat, right?


How "Equal" is Your Project?

How many object mixed types do you have? Register rules and let PHPStan disclose the magic:

# phpstan.neon
rules:
    - Symplify\PHPStanRules\Rules\Explicit\NoMixedPropertyFetcherRule
    - Symplify\PHPStanRules\Rules\Explicit\NoMixedMethodCallerRule

Happy coding!




Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors. I'd really appreciate it!