The haters always trod on PHP for a variety of reasons, but one of the easiest targets is how ugly the code can be. Some people may not agree that code cleanliness and “prettiness” matter, but when it comes to maintaining codebases for the long term, you can save your team a lot of time by having code that is easy to read.
While at Apple, I developed an ecosystem of finance applications used by internal stakeholders. Over the years, I was constantly onboarding new developers into the tools to assist with general maintenance or enhancements. One of the major takeaways from that time is how important it is to be able to read your code with natural language.
It’s interesting to find that most code is written in such a way that you have to read through a significant chunk of it, then go back to the start to fully understand what is going on. Using more expressive language or APIs while writing code can greatly reduce the onboarding and ramp-up times of new developers.
This is where Collections shine. Laravel Collections take some complicated array traversals and make them incredibly easy to read and understand. Your code becomes a chain of commands read like “Filter this array to only words starting with a, then map the cost amount to the row, and lastly reduce this to a sum of all costs.” It is very easy to undersell the importance of writing your code with more expressive language, but once you’ve made it a habit, it’s hard to imagine how anything else is acceptable.
Of course, readability cannot come at the expense of performance so it’s important to ensure that we’re always making sound choices and only prioritizing readability where it’s appropriate. We should always understand what is happening behind the scenes and be fully aware of what tradeoffs we are encountering with the way that we approach our solutions. It does no good for our code to be readable if it takes a full second longer than a less readable solution.
Let’s dive in and have an in-depth look at PHP arrays vs. Collections and compare performance between the two.
One of the biggest advantages of using Laravel Collections is their fluent syntax, which allows for chaining methods. This results in clean and readable code, as you can perform multiple operations in a sequence without needing temporary variables or complex nested function calls.
Let’s compare some of the commonly used array manipulation functions below to see how PHP compares to Collections in a very simple example.
PHP
array_filter($data, function($row) {
return substr($row, 0, 1) === "a";
});
Collections
$data->filter(function($row) {
return substr($row, 0, 1) === "a";
});
PHP
array_search(function($row) {
return substr($row, 0, 1) === "a";
}, $data);
Collections
$data->search(function($row) {
return substr($row, 0, 1) === "a";
});
PHP
array_map(function($row) {
return "test";
}, $data);
Collections
$data->map(function($row) {
return "test";
});
PHP
sort($data);
Collections
$data->sort();
PHP
foreach($loop as $item) {
$doSomething = true;
}
Collections
$data->each(function($row) {
return "test";
});
PHP
array_reduce($data, function($carry, $row) {
return $carry + strlen($row);
});
Collections
$data->reduce(function($carry, $row) {
return $carry + strlen($row);
});
PHP
array_splice($data, count($data)/2);
Collections
$data->splice(count($data)/2);
All Together (PHP)
$data = array_filter($data, function($row) {
return substr($row, 0, 1) === "a";
});
$data = array_search(function($row) {
return substr($row, 0, 1) === "a";
}, $data);
$data = array_map(function($row) {
return "test";
}, $data);
sort($data);
foreach($loop as $item) {
$doSomething = true;
}
$sum = array_reduce($data, function($carry, $row) {
return $carry + strlen($row);
});
All Together (Collections)
$sum = $data->filter(function($row) {
return substr($row, 0, 1) === "a";
})->search(function($row) {
return substr($row, 0, 1) === "a";
})->map(function($row) {
return "test";
})->sort()
->each(function($row) {
return "test";
})->reduce(function($carry, $row) {
return $carry + strlen($row);
});
Comparison
With an approach this simple, there doesn’t seem to be a huge tradeoff in readability for each individual function, although when you consider the example where you need them all to be applied to a single array, you can clearly see that it is more concise and easier to read when using the chained-methods in a collection.
Instead of constantly having to overwrite your variable and then set a new variable for output at the end, we can simply chain every command until we get to our desired output. Collections are definitely easier to read the more complex your code becomes.
I took the above examples and generated some test data to attempt to determine if collections were more or less performant than standard PHP functions.
Each array had 100,000 random strings as items, and I ran each function 100 times. In the end, we calculated the average of all of the response times.
The final results are below:
========== Tests Complete (ms) ==========
php filter: 5.07
collect filter: 6.49
=======================
php search: 0.79
collect search: 0
=======================
php map: 3.45
collect map: 4.18
=======================
php sort: 25.27
collect sort: 11.18
=======================
php each: 1.03
collect each: 6.96
=======================
php reduce: 2.78
collect reduce: 7.75
=======================
php splice: 1
collect splice: 0.74
=======================
As you can clearly see, though we gain a large amount of readability from Collections, we lose a significant amount of performance in some key areas.
Filter, Map, Foreach, and Reduce are all faster with the standard PHP functions. Foreach and Reduce are actually incredibly significant differences. Search, Sort, and Splice all show Collections as the winner, and Sort is actually a massive time saver.
It also is important to note that you would have to create each collection out of a built array which adds a tiny bit of overhead to setting up the collection to begin with, but even with that small amount of extra work and time, the results are pretty clear.
In my opinion (and it is just an opinion based on these results), if performance is a huge concern, I would stick with standard PHP functionality for Foreach loops for sure and likely the same for any Reduce needs. If you need to do any sorting on large datasets, clearly Collections is the right way to go. The rest are so close that it really feels like a personal preference.
At that point, I’d say collections are always easier to read and maintain.
Obviously, you should take this information and make your own informed decision, however, if you’re like me, I think you’ll find yourself slipping collections in for many of these functions above. But I do think I’ll dial back my use of →each
and →reduce
where appropriate going forward!