Why you should be typing your arrays in PHP
We have been able to natively type parameters of methods and functions in PHP for quite some time. In basically any version of PHP you should be running we can do something like the following:
function getNames(array $input): array {
return array_map(function($item) {
return $item->getName();
}, $input)
}
Basic array types
This however tells us very little about the types that we are dealing with.
When specifying a type as just array
or iterable
, or something alike, all you are saying is that
it is an array, but you really want to specify that it is an array containing a specific type.
If we rewrite the previous example to be more specific, we can do the following:
/**
* @param array<User> $input
*
* @return array<string>
*/
function getNames(array $input): array {
return array_map(function($item) {
return $item->getName();
}, $input)
}
If we add types like this we now understand we are getting an array which contains User
objects, and
we are returning an array of string
. There are multiple benefits to doing so.
Benefits of typed arrays
IDE hints
The first benefit is the fact that your IDE will now understand what kind of array this is, and thus be able
to give you autocompletion inside the function. It will also help you when trying to find the usages of getName()
on the User
class, as now your IDE, knows it is that method that is being called.
This will also help you with refactor tools, for example if you are renaming the getName
method, an IDE like PHPStorm
will be able to also update this method call. You’ll also get errors in your IDE if you pass in the wrong type of array,
or if you try to do something that’s not possible with the returned array of strings.
Static analysis tooling
The second benefit comes from the usage of static analysis (SA) tooling (like PHPStan). SA tools will understand what you are doing, and give you error in your pipelines if you are doing something that shouldn’t be possible. The more types you use in your codebase, the better understanding it has of your code, the better it can help you detect issues before they hit production.
In PHPStan adding type declarations becomes a requirement on level 6. This may be hard to add everywhere in legacy code bases, but I would highly recommend upgrading to at least this level and generating a baseline. This will require you to at least add these types to any new code you are writing
Developer understanding
Most likely you aren’t the only one working on your code base. If the next developer comes along and sees the getNames
method, they first have to figure out what the input is, before they can figure out what they are supposed to do with the method.
I can’t count the amount of times I looked at a method and thought “I have no idea what’s going on here”, because there were
either no types, or just array
as a type. I would first have to search through the code base to see where the method is called,
before being able to understand what the parameters where and what the return values where.
Advanced array types
With modern PHP tooling, we can however, go much further than just doing @param array<Type> $value
.
We can do lists, specify array keys, or make sure it is not empty
The list type
We can for example specify something is a list
.
A list is an array, where the keys start at 0
, and each time they increment by one.
The list type can be especially useful when converting to JSON
for example, as that will make it an array, instead of an object.
If you create an array without keys, it is always a list, but you can also specify the keys if needed
$list = [1, 2, 3];
$alsoList = [
0 => 1,
1 => 2,
2 => 3,
];
// Keys start at 1, not at 0, so not a list.
$notList = [
1 => 1,
2 => 2,
3 => 3,
];
// Has a string key, so not a list.
$alsoNotList = [
'foo' => 'bar',
1,
2
];
The list
type doesn’t exist natively, but you can use it like so, and a tool like PHPStan will help tell you if
you aren’t passing a list
/**
* @param list<User> $input
*
* @return list<string>
*/
function getNames(array $input): array {
return array_map(function($item) {
return $item->getName();
}, $input)
}
Specifying keys
Nowadays, we can even specify that we expect a specific set of keys, or just what type the keys should be. With the following
notation we specify literal keys: array{key: Type, key2: Type}
.
This also comes with IDE autocompletion for those keys, and all other previously mentioned advantages.
If we want to specify just the general types, for example if the keys are strings, we can do that as follows:
array<string, Type>
. Generally the notation for arrays is array<KeyType, ValueType>
. If we only pass one type, it is the type of the value.
/**
* @param array{begin: int, interval: int} $data
*
* @return int
*/
function transform(array $data): int {
return $data['begin'] + $data['interval'];
}
/**
* @param array<string, mixed> $data
*
* @return string
*/
function concatKeys(array $data): string {
$output = '';
foreach($data as $key => $v) {
$output .= $key;
}
return $output;
}
Non-empty types
We can even go as far as to specify the array should not be empty. you can do that by using non-empty-array
(or non-empty-list
),
instead of just array
(or list
).
If we take the concatKeys
function as an example, we could define it like so, to make sure it is never empty. And we also specify
the returned string
will never be empty
.
/**
* @param non-emptyarray<string, mixed> $data
*
* @return non-empty-string
*/
function concatKeys(array $data): string {
$output = '';
foreach($data as $key => $v) {
$output .= $key;
}
return $output;
}
And once again, this provides better understanding for SA tooling, your IDE, and other developers.
Conclusion
Adding types to your arrays (and just adding types in general) helps you when using an IDE, or static analysis, and helps other developers in your team to understand your code. It should help you produce less bugs, and make your code cleaner and more readable.
These types help you document the intent of a function and how it should be used. And even better, with modern day tooling, it even shows you if you are doing something wrong.
Are you using (array) types in your PHP codebase, and do you think they are usefull? Are you already using more
advanced types like the list
and non-empty-array
. Let me know in the comments!
If you want to get notified of the next blog post, join the newsletter.