Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

My love-hate relationship with PHP Arrays

I’ve been programming PHP for most of my life, almost 20 years now. While PHP has many downsides, horrible history, and terrible reputation, it has improved a lot with PHP 7 and modern frameworks like Laravel that make it really enjoyable to develop.

However, I still wish that there would be a “PHP2", a rework of the original one boldly breaking the backward compatibility and fixing some of the underlying weirdness.

In the modern world, where JSON is the de facto format for passing data around in the web, PHP has one downside that makes me mad. Instead of having arrays and objects like Javascript or lists and dictionaries like Python, it only has arrays. Doesn’t sound too bad, right? Let’s dive in.

Array is an array

$array = [1,2,3,4,5];
echo json_encode($array);

// Echoes: [1,2,3,4,5]. Wonderful!

Since PHP 5.4, you’ve been able to create PHP arrays with a shorthand developers from other languages are able to understand immediately. It will create an array with values defined between [ and ] and when you encode it to JSON for passing through an API, you get a JSON array. Wonderful. (I started developing with PHP 4 so that was a very welcome improvement)

You can do all the basic array operations for these arrays: sorting, popping, mapping, reducing, etc. Life is good.

Array is an object

$object = [
    'name' => 'Jane Doe',
    'country' => 'US',
    'age' => 25

echo json_encode($object);

// Echoes: {"name": "Jane Doe", "country": "US", "age": 25}. Just like we love it !

You can also create key-value stores with the fat arrow notation. Using the same shorthand, you define a key and a value and life is good. You can sort the new array either by value or key, you can loop over it with foreach and so on. All the goodies you’re used to with maps, objects or dictionaries in other languages.

Array becomes an object

// Let's modify an array
$array = [1,2,3,4,5];

echo json_encode($array);
// Echoes: { "0": 1, "2": 3, "3": 4, "4": 5 }. Wait what. It's no longer an array

The trick here is that actually, all arrays in PHP are key-value stores. So when you initialize an array with [1, 2, 3, 4, 5], you actually create a key-value store of [0 => 1, 1 => 2, 2 => 3, 3 => 4, 4 => 5]. So when you use json_encode to do the encoding, it will return a JSON array if and only if:

  1. The keys of the array are 0 to n, AND
  2. The keys are in order from 0 to n

otherwise it will return a JSON object. So running operations to modify your array can cause the keys to mess up and you end up getting something you didn’t expect.

Array sometimes becomes an array and sometimes an object

$numbers = [0 => 100, 1 => 300, 3 => 400, 2 => 150];
$letters = ['a' => 10, 'b' => 200, 'd' => 300, 'c' => 100];

echo json_encode($numbers);

// Echoes { "0" => 100, "1" => 300, "3": 400, "2" => 150 }

// Let's sort the array by keys and try again
echo json_encode($numbers);

// Echoes [100, 300, 150, 400]

echo json_encode($letters);

// Echoes { "a": 100, "b": 200, "d": 300, "c": 100}

echo json_encode($letters);

// Echoes { "a": 100, "b": 200, "c": 100, "d": 300 }

The scary part of PHP, and a cause of a lot of bugs, is when you can run certain array operations to different arrays with similar structure and the type of the JSON encoded is different.

If you sort an array with numeric, sequential and unordered keys, you turn from object to an array. If you sort an array with numeric, sequential and ordered keys, it remains an array. Whatever you do for non-numeric or non-sequential array, it will stay an object.

I think that whenever a normal operation run on a dynamic data and the type of the end result (well, technically the return type of json_encode is always string but you know what I mean) is sometimes different, we’re in very murky waters.

Having different types for sequential arrays (like [1, 2, 3, 4] and associative arrays [0 => 1, 1 => 2, 2 => 3]) is definitely on my wishlist for Santa this year.

Note from 2019-02-09

I wrote a blog post about this topic suggesting an ES6-for-PHP type of solution.