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.