Typed Arrays in PHP

As a community, we can only make progress when somebody pushes the boundaries. This can be done by questioning established best practices, or by coming up with new ideas. Both are the result of a trait that every good developer should have: continuously asking yourself if things can be done even better.

Recently, when we reviewed code that some of our friends had written for a stealth-mode startup, we saw such a different approach that we had never seen in PHP before. We are glad that Tim Bezhashvyly agreed to share his experience with us, and write an article about it.


Typed Arrays in PHP

by Tim Bezhashvyly

Even though Rasmus Lerdorf denies the fact, typed arrays exist in PHP. At least to some degree. This wonderful feature sneaked in as a side-effect of variadic functions that were added to the language in PHP 5.6. As you certainly know, PHP functions can receive more arguments than defined as parameters in their signature. An array comprising the full argument list can be obtained using the func_get_args() function. In a nutshell, variadics were introduced to replace ​func_get_args() and improve access to variable function arguments:

1 2 3 4 5
<?php
function foo ( ... $args )
{
/* ... */
}

Regardless how many arguments have been passed to the function above, they are all accessible as elements of the ​$args array. This is nothing special really, until you realize that variadics can be augmented with type declarations:

1 2 3 4 5
<?php
function foo ( Product ... $products )
{
/* ... */
}

So now an array passed to a function can be strictly checked to only contain elements of a specific type. If not, a fatal error will be triggered just like it would in the case of a type mismatch for a regular argument. So from the parameter's point of view it is a typed array! Think about how many input parameter validations can be dropped, how many named constructors can become a thing of the past! How many collection objects whose sole purpose was to carry the set of similar objects will become obsolete.

With the scalar type declarations of PHP 7, the number of possibilities grows:

1 2 3 4 5
<?php
function foo ( string ... $arrayOfStrings )
{
/* ... */
}

When it comes to passing an array to such a function, another feature introduced in PHP 5.6 comes in handy: argument unpacking can convert the array into list of arguments. The function defined above can be called in the following manner:

1 2
<?php
foo ( ... $products ) ;

In the example ​above, $products is an array containing (or not) elements of a required type.

There are two limitations however. Only one variadic is allowed in a function signature and it has to be the last parameter. While the parameters order can hardly be an issue, the limitation of one typed array per function is a bit unpleasant.

It is also possible to check the type of elements while iterating through an array. The way to do it is just to use higher-order functions such as array_map(), array_reduce(), or array_filter(), for instance, ​instead of the rather procedural ​foreach statement:

1 2 3 4 5 6 7 8
<?php
array_map (
function ( Product $product )
{
/* ... */
} ,
$products
) ;

As you can see in the example above, the type declaration for a closure argument will ensure the type of each array element. A mismatch will lead to a fatal error.

Using higher-order functions instead of regular loops can be even nicer from a readability point of view and safer from an immutability standpoint. However this is already a subject for another article.


Tim Bezhashvyly

Tim is a passionate software craftsman with decades of PHP experience. He is working on enterprise e-commerce systems, where his main focus areas are clean code, performance, scalability and testing. Tim currently works on a promising e-commerce platform which is still in stealth-mode but will be open-sourced quite soon.

Questioning PHPUnit Best Practices ConFoo: Combining Tech like PHP and Node.js