PHP Array Unpacking

Chapter 30 7 mins

Learning outcomes:

  1. What is array unpacking
  2. How to unpack arrays (using ...)
  3. Examples of unpacking
  4. Unpacking associative arrays
  5. Things to note when unpacking arrays

What is unpacking?

For a long time, PHP has had the idea of argument unpacking built into it. With version 7.4, the language finally also got the provision of array unpacking — a very similar idea.

Array unpacking is to extract elements out of an array and dump them into another array.

Array unpacking is, more or less, a shortcut for array_merge(), a function used to merge two or more arrays together into a single array.

In PHP, array unpacking is done by the spread operator, denoted as ..., also referred to as the splat operator in some other languages.

Syntactically, we could express unpacking as follows:

[...$array_1, ...$array_2, ...]

The ... precede the array that we wish to unpack into the surrounding array.

Let's now consider an example of array unpacking.

Example of unpacking

Consider the following code:

<?php

$nums1 = [1, 5, 9];
$nums2 = [6, 0, -4];

We have two arrays, $nums1 and $nums2, holding two separate collections of numbers and want to merge them into one single array for processing them all together.

This ain't a challenging task at all. We can either use array_merge() or the power of array unpacking (which is essentially the same thing, albeit shorter to type and slightly faster to execute).

Here's how we can use array unpacking to consolidate both the arrays into one single array:

<?php

$nums1 = [1, 5, 9];
$nums2 = [6, 0, -4];

$nums = [...$nums1, ...$nums2];

Let's print $nums to see what it contains:

<?php

$nums1 = [1, 5, 9];
$nums2 = [6, 0, -4];

$nums = [...$nums1, ...$nums2];
print_r($nums);
Array ( [0] => 1 [1] => 5 [2] => 9 [3] => 6 [4] => 0 [5] => -4 )

As expected, it contains numbers from both the arrays. Perfect!

Note how in $nums we first have the elements of $nums1 and then the element of $nums2. This is because we first unpacked $nums1 and then unpacked $nums2.

If we switch the orders of these unpacking expressions, the content of $nums would change as well:

<?php

$nums1 = [1, 5, 9];
$nums2 = [6, 0, -4];

$nums = [...$nums2, ...$nums1];
print_r($nums);
Array ( [0] => 6 [1] => 0 [2] => -4 [3] => 1 [4] => 5 [5] => 9 )

Also note that it's possible to unpack the same array more than once, as shown below:

<?php

$nums1 = [1, 5, 9];
$nums2 = [6, 0, -4];

$nums = [...$nums1, ...$nums1];
print_r($nums);
Array ( [0] => 1 [1] => 5 [2] => 9 [3] => 1 [4] => 5 [5] => 9 )

This works because, when unpacking, PHP simply iterates over the given array and copies each of its elements and appends it to the underlying array (where the unpacking is done).

Henceforth, it shouldn't (and even doesn't) matter if we unpack the same array more than once.

Unpacking associative arrays

When array unpacking was introduced in PHP 7.4, it didn't unfortunately work on associative arrays.

Trying to unpack an associative array threw an error, as shown below:

<?php // PHP 7.4

$point = ['x' => 10, 'y' => 20];

$point_3d = [...$point, 'z' => -10];
print_r($point_3d);
Fatal error: Uncaught Error: Cannot unpack array with string keys in <file>

But after version 8.1, things changed. We're now able to unpack associative arrays as well.

Shown below is the same code above, run on PHP 8.1:

<?php // PHP 8.1

$point = ['x' => 10, 'y' => 20];

$point_3d = [...$point, 'z' => -10];
print_r($point_3d);
Array ( [x] => 10 [y] => 20 [z] => -10 )

Just as desired, $point_3d contains both the key-value pairs from $point in addition to the 'z' => -10 pair.

In case a similar key is encountered at a later stage while unpacking a given array, it's used to override the previous value of that key in the main array. This can be seen as follows:

<?php
// PHP 8.1

$point = ['x' => 10, 'y' => 20];

$point_3d = [...$point, 'z' => -10, 'x' => 500];
print_r($point_3d);
Array ( [x] => 500 [y] => 20 [z] => -10 )

$point already contains a key x in it and so when we unpack it, right at that point, the main array (where we unpack $point) gets the key x, with the value 10. But then, by virtue of 'x' => 500, this key gets replaced with the value 500.

Things to note

Unpacking literal arrays is pointless

Although PHP supports the idea of unpacking literal arrays, there's really no point in doing so. Instead, we must directly inline the contents in the main array.

For example, consider the following:

<?php

$arr = [1, 2];
$arr2 = [...$arr, ...[3, 4]];

We are trying to create an array $arr2 based on the contents of $arr, and then appending the numbers 3 and 4 to it.

The pointless thing in the code is ...[3, 4]. Because we already know of the contents of the array [3, 4] (i.e. it's fixed), there's absolutely no point of encapsulating them inside an array in the first place and then unpacking the array.

This code must be rewritten as follows:

<?php

$arr = [1, 2];
$arr2 = [...$arr, 3, 4];

The numbers 3 and 4 are specified directly within $arr2; no unpacking involved.

Unpacking by-reference is not supported

We might be tempted to think that it's possible to unpack an array by-reference, i.e. make every element of the unpacked array an alias of the corresponding element in the main array.

However, this ain't possible! We can NOT unpack an array by-reference.

For example, the following PHP code, where we precede $arr with & (in the unpacking expression ...&$arr) in hopes of unpacking it by-reference, is invalid:

<?php

$arr = [1, 2];
$arr2 = [...&$arr, 3, 4];

We can NOT precede an array in an unpacking expression with the & symbol.