Short and safe array iteration

Short and safe array iteration

One reason to follow development mailing lists is you sometimes pick up on some very neat tricks. Here's one that I spotted on the PHP Internals list recently to simplify array iteration in PHP 7.
PHP's largely loose, dynamic typing has plenty of both pros and cons. One con in particular is that you don't always know for sure if a value you're trying to use has been set yet, or is non-null. PHP will dutifully whine at you if you try to use a null value, sometimes fatally. (Yet another reason to structure your code to avoid nulls, period.)
One place this comes up in particular is in foreach() loops, especially when working with nested array structures. (PHP lacks a struct type, but makes anonymous hash maps so easy that they get used as the uber data type, for better or worse.) Consider the following:
<?phpforeach ($definition['keys'] as $id => $val) {
  
// ...}?>
This not-at-all uncommon code has a problem: There's no guarantee that $definition['keys'] has been set. Ideally it has been, and if it's not there's a good chance there's a bug elsewhere, but it could be in user-supplied data, say, a user-supplied YAML or JSON file. To be safe, you really ought to check it:
<?phpIf (!empty($definition['keys']) {
  foreach (
$definition['keys'] as $id => $val) {
    
// ...
  
}
}
?>
That avoids any whining from PHP, but at the cost of more annoying boilerplate code. When you're parsing through a large and complex data structure (the aforementioned YAML or JSON data), that can add up to a lot of irritating extra code, especially if you also need to check even deeper array levels.
Fortunately, PHP 7 introduces a null-coalesce operator: ??. The ?? operator (pronounced "g'WHAT??") acts as a shorthand ternary for is_null(). That is, the following two statements are equivalent.
<?php
$a 
is_null($b) ? $default $b;$a $b ?? $default;?>
Note that ?? does require that the value being checked is defined; However, thanks to quirks of PHP an array key that is undefined evalues to null, not to, well, "undefined". Which means $definition['keys'] ?? $default will evaluate to $default if there is no "keys" index.
Which in turn means that this works:
<?phpforeach ($definition['keys'] ?? [] as $id => $val) {
  
// ...}?>
That is, if $definition['keys'] is null, or unset and PHP therefore casts to null, it will evaluate to an empty array. foreach() knows how to iterate an empty array (by doing nothing), and thus the code simply skips the foreach() entirely when "keys" is undefined. Just as we were trying to do in the longer version.
Unfortunately that doesn't work on a bare variable. This snippet will still throw a variable-undefined E_NOTICE if $definition is not set:
<?phpforeach ($definition ?? [] as $id => $val) {
  
// ...}?>
So no, it won't help in every situation; and favoring explicitly defined data structures over anonymous arrays is always a good idea when possible. But sometimes you just gotta iterate what you gotta iterate, and this is a neat trick to keep in your back pocket.
A major hat tip to Niklas Keller in this comment, who tipped me off to this technique.

Comments

Popular posts from this blog

A Tailwind CSS Preset for Laravel 5.5

PHPStorm's performance