PHP is a popular scripting language that can be used to create dynamic web pages. One of the features of PHP is its ability to create arrays. An array is a collection of data that can be accessed using an index number. Arrays are useful for storing data in a way that makes it easy to access it. For example, you could use an array to store the names of all the employees at a company. You could also use an array to store the addresses of all the customers who have placed orders with your business. To create an array in PHP, you first need to declare it using the keyword “array” followed by the name of the array. For example, if you wanted to create an array called “employees”, you would type: array employees; Next, you need to fill in the data for the array using index numbers. The first element in an array is at index 0 and the last element is at index count - 1 (count being equal to the number of elements in the array). So, if you wanted to add an element called “John” into your employees array, you would type: employees[0] = “John”; employees[1] = “Doe”; employees[2] = “Smith”; ..


PHP doesn’t let you define typed arrays. Any array can contain any value, which makes it tricky to enforce consistency in your codebase. Here are a few workarounds to help you create typed collections of objects using existing PHP features.

Identifying the Problem

PHP arrays are a very flexible data structure. You can add whatever you like to an array, ranging from scalar values to complex objects:

In practice, it’s rare you’d actually want an array with such a varied range of values. It’s more likely that your arrays will contain multiple instances of the same kind of value.

You might then create a method which acts on all the values within your array:

This code iterates over the DateTimeInterface instances in $times. The Unix timestamp representation of the time (seconds measured as an integer) is then stored into $laps.

The trouble with this code is it makes an assumption that $times is comprised wholly of DateTimeInterface instances. There’s nothing to guarantee this is the case so a caller could still pass an array of mixed values. If one of the values didn’t implement DateTimeInterface, the call to getTimestamp() would be illegal and a runtime error would occur.

Adding Type Consistency with Variadic Arguments

Ideally the issue would be resolved by specifying that the $times array can only contain DateTimeInterface instances. As PHP lacks support for typed arrays, we must look to alternative language features instead.

The first option is to use variadic arguments and unpack the $times array before it’s passed to recordLaps(). Variadic arguments allow a function to accept an unknown number of arguments which are then made available as a single array. Importantly for our use case, you may typehint variadic arguments as normal. Each argument passed in must then be of the given type.

Variadic arguments are commonly used for mathematical functions. Here’s a simple example that sums every argument it’s given:

sumAll() is not passed an array. Instead, it receives multiple arguments which PHP combines into the $numbers array. The int typehint means each value must be an integer. This acts as a guarantee that $numbers will only consist of integers. We can now apply this to the stopwatch example:

It’s no longer possible to pass unsupported types into recordLaps(). Attempts to do so will be surfaced much earlier, before the getTimestamp() call is attempted.

If you’ve already got an array of times to pass to recordLaps(), you’ll need to unpack it with the splat operator (…) when you call the method. Trying to pass it directly will fail – it’d be treated as one of the variadic times, which are required to be an int and not an array.

Limitations of Variadic Arguments

Variadic arguments can be a great help when you need to pass an array of items to a function. However, there are some restrictions on how they can be used.

The most significant limitation is that you can only use one set of variadic arguments per function. This means each function can accept only one “typed” array. In addition, the variadic argument must be defined last, after any regular arguments.

By nature, variadic arguments can only be used with functions. This means they can’t help you out when you need to store an array as a property, or return it from a function. We can see this in the stopwatch code – the Stopwatch class has a laps array which is meant to store only integer timestamps. There’s currently no way we can enforce this is the case.

Collection Classes

In these circumstances a different approach must be selected. One way to create something close to a “typed array” in userland PHP is to write a dedicated collection class:

The UserCollection class can now be used anywhere you’d normally expect an array of User instances. UserCollection uses variadic arguments to accept a series of User instances in its constructor. Although the $Users property has to be typehinted as the generic array, it’s guaranteed to consist wholly of user instances as it’s only written to in the constructor.

It may seem tempting to provide a get() : array method which exposes all the collection’s items. This should be avoided as it brings us back to the vague array typehint problem. Instead, the collection is made iterable so consumers can use it in a foreach loop. In this way, we’ve managed to create a typehint-able “array” which our code can safely assume contains only users.

Making Collections More Array-Like

Collection classes solve the typehinting problem but do mean you lose some of the useful functionality of arrays. Built-in PHP functions like count() and isset() won’t work with your custom collection class.

Support for these functions can be added by implementing additional built-in interfaces. If you implement Countable, your class will be usable with count():

Implementing ArrayAccess lets you access items in your collection using array syntax. It also enables the isset() and unset() functions. You need to implement four methods so PHP can interact with your items.

You now have a class which can only contain User instances and which also looks and feels like an array. One point to note about ArrayAccess is the offsetSet implementation – as $value must be mixed, this could allow incompatible values to be added to your collection. We explicitly check the type of the passed $value to prevent this.

Conclusion

Recent PHP releases have evolved the language towards stronger typing and greater consistency. This doesn’t yet extend to array elements though. Typehinting against array is often too relaxed but you can circumvent the limitations by building your own collection classes.

When combined with variadic arguments, the collection pattern is a viable way to enforce the types of aggregate values in your code. You can typehint your collections and iterate over them knowing only one type of value will be present.