Mastering PHPStan: Common Errors and How to Fix Them

Mastering PHPStan: Common Errors and How to Fix Them

·

4 min read

PHPStan is a static analysis tool for PHP that focuses on finding bugs in your code without actually running it. By analyzing your codebase, PHPStan can detect potential issues such as type errors, undefined variables, and incorrect method calls, among others. It helps developers maintain high code quality and adhere to best practices by providing detailed feedback and suggestions for improvement. PHPStan integrates seamlessly with various development environments and CI/CD pipelines, making it an essential tool for PHP developers aiming to write robust and error-free code.

When running PHPStan, you will often encounter unclear errors. In this post, I'll show a series of common errors you may come across.

Type has no value type specified in iterable type array

public array $fields;

PHPStan needs to know what the array consists of. You can specify the types of values in the array. For example, an array with integer keys and string values:

/** @var array<int, string>*/
public array $fields;

Has parameter with no value type specified in iterable type array

I have a type hint of array for $items but PHPStan cannot tell what the array contains.

public function fields(array $items): array

Set the array type in a docblock. For example, if the array is made up of integers and strings, you can specify it as follows

/**
 * @param  array<int, string>  $items
*/
public function fields(array $items): array

Return type has no value type specified in iterable type array

In a docblock set the return type and specify the data type contains.

/**
 * @param  array<int, string>  $items
 * @return array<int, string>
*/
public function fields(array $items): array

Has parameter with no type specified

public function fields($items)

Parameters need to have a type hint to tell PHPStan what type of data, be it an int, string etc or even a class type hint.

public function fields(array $items)

Types: TModelClass

Return type with generic class Builder does not specify its types: TModelClass.

public function builder(): Builder
{
    return AuditTrail::orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc');
}

To avoid the error, you need to specify the type of model (AuditTrail in this case) in the return type, like this:

/**
 * @return Builder<AuditTrail>
 */
public function builder(): Builder
{
    return AuditTrail::orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc');
}

Types: TKey, TModel

Property $notifications with generic class.

Collection does not specify its types.

public Collection $notifications;

The Collection class expects two type parameters: TKey (the type of the keys) and TModel (the type of the models in the collection).

/**
 * @var Collection<int, Notification>
 */
public Collection $notifications;

Types: TFactory

Class AuditTrail uses generic trait HasFactory but does not specify its types: TFactory.

I need to tell PHPStan that the factory used a AuditTrailsFactory.

/** @use HasFactory<AuditTrailsFactory> */
use HasFactory;

Type: with generic interface

Method userLogs() return type with generic interface LengthAwarePaginator does not specify its types: TItem

Need to specify the builder typeof the model being used.

/**
 * @return LengthAwarePaginator<AuditTrail>
 */
public function userLogs(): LengthAwarePaginator

App\Models\User|null

Using $request->user()->email may return an error: Cannot access property $email on App\Models\User|null. Even through the user is logged in and its definitely not null.

The problem is that the type system does not know that the request will return a user object. To resolve this, we need to inform PHPStan by using @var followed by the class used and the reference.

Then use $user for the rest of the code.

/** @var User $user */
$user = $request->user();

$user->email

Laravel Resource: undefined property

When using a resource and calling properties directly:

public function toArray(Request $request): array
{
    return [
        'type' => 'user',
        'id' => $this->id,
    ];
}

PHPStan will inform you Access to an undefined property then the property. In this case $id

One way to resolve this is to use ->resource

This makes it clear to PHPStan that the resource you're accessing is an object with those properties.

public function toArray(Request $request): array
{
    return [
        'type' => 'user',
        'id' => $this->resource->id,
    ];
}

Alternatively use a mixin

Using a @mixin ModelName above the class means you don't have to do any extra work for the model properties to work so instead of $this->resource->id you can use $this->id

/**
 * @mixin User
 */
class UsersResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, array<string, Carbon|string|null>|int|string>
     */
    public function toArray(Request $request): array
    {
        return [
            'type' => 'user',
            'id' => $this->id,

Laravel Config set type

This is a great tip.

Did you find this article valuable?

Support David Carr by becoming a sponsor. Any amount is appreciated!