Best way to check if variable is set and can be 'foreach'ed

Viewed 55

I've taken over a larger code base, that has several warnings and errors.

I keep running into statements like this:

foreach( $object->keys => $key ){
  ...
}

Where I can see in the error log, that $object is null.

What is the simplest way, tot check that above-written statement won't break?


To be 100% sure, I would do something like this:

if( isset( $object ) && is_array( $object->keys ) ){
  foreach( $object->keys => $key ){
    ...
  }
}

But the readability decreases significantly.

I could also make a helper function, that did it, like so:

if( myHelper::canBeForeached( $object, 'keys' ) ){ // naming would obviously be changed
  foreach( $object->keys => $key ){
    ...
  }
}

But that too seems bad.

What is the simplest/prettiest way to achieve this, to also maintain readability of the code?

1 Answers

There are a number of ways to approach this. Here's the shortest approach.

If you use the ?? null-coalescing operator for default value, in the event that either $object or ->keys is undefined, then [] or an empty array will be passed for iteration, and obviously nothing happens. This is one way to avoid wrapping your foreach loop inside a condition.

foreach($object->keys ?? [] as $key) {
    // If $object or ->keys is undefined, nothing happens.
}

Now, some may object that this is unclear or unreadable; but at least it's not a ton of boilerplate, and it's very quick to add in. This approach assumes that if $object exists and has the property keys, then keys will be iterable. If not, it will result in a warning on must be of type array|object.

Other than that, you'd have to wrap all your loops with a condition check, isset($object), or alternatively, gettype($object) === 'object' if you want to ensure that it's an object. For some reason, is_object() will give a warning for an undefined variable. Again, is_null() returns true if the variable is undefined, essentially behaving like !isset() when negated.

As noted above, if your legacy code has objects with plural-named properties that are not iterable but also not null (e.g. ->keys = 'duck'), you'll have to do a check on your $object->keys with is_iterable() before it enters the loop for an attempt at iteration.

If you pass an undefined object into is_iterable, it's a warning again. You can, however, use the power of null-coalescing once more, where if either $object or ->keys is undefined, the check below reads as is_iterable(false) (or any other non-iterable value to evaluate):

if(is_iterable($object->keys ?? false)) { 
    foreach($object->keys as $key) {
    //...
    }
}

...and that could be the sole condition that wraps your loop.

P.S. On the possible solutions in OP: A helper function results in warnings if used with an undefined variable. Also, your if( isset( $object ) && is_array( $object->keys ) ) will result in a warning if object is defined but doesn't have the ->keys property.

Related