Three Tips for Writing Airtight PHP Conditions
In PHP, conditions are the core of conditional statements such as
if-statements, and of certain other control structures like
Surprises are great in some areas of life, but rarely so in PHP development, and knowing how to write clear conditional statements will dramatically cut down on surprises in your code. That all comes down to writing clean, precise conditions.
This article defines PHP conditions, and then offers three practical tips on writing better conditions for more predictable, less error-prone code.
What Conditions Are in PHP
We’ve introduced PHP
while loops, and conditional statements like
if... else statements, in our introduction to PHP control flow. These structures dictate “what code executes, when,” in PHP, and they have something in common:
// if-statement if ( #condition# ) : // Action endif; // if... elseif... else statement if ( #condition# ) : // Action elseif ( #condition# ) : // Action else : // Action endif; // while() loop while( #condition# ) : // Action endif;
What these structures have in common are conditions: statements that evaluate to either
false, thereby dictating what happens in other code. These conditions occupy the
#condition# parts in the code above (which is definitely not real code, so don’t run it live on the NASA mainframe).
And did you know you can just write conditions out in the wild, too, outside of parentheses? It’s less common, but it’s perfectly valid PHP:
// A wild condition appears! $boolean = 2 === 3; // This evaluates to false, so the value of $boolean is now false
Which lets us use our control structures in a more verbose way, if we want to:
$first_boolean = 2 === 3; // This evaluates to false, so the value of $first_boolean is now false if ( $first_boolean === true ) : echo 'I will not execute'; // That's true, you won't endif; $second_boolean = 1 === 1; // This evaluates to true, so the value of $second_boolean is now true if ( $second_boolean === true ) : echo 'I will execute'; // Prints "I will execute" to the page endif;
When we say “PHP conditions” in this article, we’re talking about these
1 === 1 or
2 === 3 (or
typeof( $var ) === 'array' or anything else) statements themselves: statements that have a defined truth value, and that help other code decide whether to run.
And so, “writing good conditions” is essentially the same thing as “writing good conditional statements,” or even “writing good
if-statements,” which is why both we in this article and real humans tend to use those related terms fairly closely.
Why We Need to Worry About How We Write PHP Conditional Statements
PHP is a loosely typed language. You can give me a variable,
$var, that could be of any data type. It could be an integer, or a boolean, or a float, or even a huge associative array. And then I can be like , “Oh now
$var = 'Hello';” and all of a sudden
$var‘s data type changes instantly to a string, without me even having to think about it.
Well, sort of.
Loose typing makes writing PHP easier and less verbose, but it also gives you a lot of freedom to mess yourself up. It’s kind of like an older car that doesn’t ding when you don’t wear your seatbelt. That gives you greater freedom—sort of. But freedom to do what? Not wear your seatbelt?
The solution is to always make sure you stay buckled up yourself. In PHP, that means writing code that eliminates variable typing confusion, and other possible sources of confusion.
That confusion often shows up most harmfully in the PHP conditions that are the engine of your
if-statements and other conditional statements,
for loops, and so on. Why is it such a problem in that environment ? Because the whole point of conditions is that they’re clear-cut: they return either
false, perfect opposites. Mess up what a condition returns and you’re in trouble.
The rest of this article is practical advice for how to accomplish this goal of writing crisp PHP conditions that always return what they’re supposed to.
How to Write Airtight PHP Conditions
The tips below all teach the same overall lessons:
- Be specific. Write conditions that test for exactly—not approximately—what you expect to be the case.
- Don’t assume. Don’t think you understand the exact statement your condition is testing: know you are.
Carry those lessons in mind as we look at code examples.
1. Be Verbose
Write out the fullness of what you’re trying to test for in your PHP conditions. Don’t cut corners.
Example with a Badly Written Conditional
If we want to do something to a variable, we need to make sure that variable exists, right? So I guess we can use an
if-statement to make sure we’re not talking to nothing.
$give_me_a_number = 2; // Let's make sure we got something for $give_me_a_number if ( $give_me_a_number ) : $new_number = $give_me_a_number + 2; echo 'Thanks for the number. It plus 2 is ' . $new_number . '.'; endif;
The code above will run and print out “Thanks for the number. It plus 2 is 4.”
But it’s extremely fragile. Let’s see why:
$give_me_a_number = 0; if ( $give_me_a_number ) : $new_number = $give_me_a_number + 2; echo 'Thanks for the number. It plus 2 is ' . $new_number . '.'; endif;
The code above won’t print out anything.
Why not? Because 0 is very similar, in PHP’s mind, to
false. It thinks of numbers above 0 as “true,” which is why the first example ran. In this case, though, 0 is “false” and the
echo statement never even runs.
Again, this isn’t because
$give_me_a_number isn’t defined or doesn’t exist. It does exist—it’s an
int with value
0—but what happens to the number 0 inside a PHP condition is that PHP looks at it and says, “That’s false.”
This isn’t the behavior we want: our little sentence should add 2 to all numbers, not “all numbers other than 0.”
What did we do wrong? We were too concise—not verbose enough—and that led us to violate both of the rules listed above.
I won’t even get into all the other ways you could break this statement, such as:
$give_me_a_number = new stdClass; if ( $give_me_a_number ) : $new_number = $give_me_a_number + 2; echo 'Thanks for the number. It plus 2 is ' . $new_number . '.'; // Fatal error time! endif;
So the PHP condition above is bad. How can we do better?
Example with a Well-Written Conditional
Let’s take a step back: What are we actually asking in our
What we really want is to make sure that:
$give_me_a_numberis defined, and:
$give_me_a_numberis actually a number.
That second bit is important. If it’s not a number, why are we trying to add 2 to it?
Here’s how we ask that in PHP:
if ( isset( $give_me_a_number) && is_numeric( $give_me_a_number ) ) : $new_number = $give_me_a_number + 2; echo 'Thanks for the number. It plus 2 is ' . $new_number . '.'; endif;
if-statement asks exactly the two things we want to know: first, that
$give_me_a_number is defined and non-null; and, second, that
$give_me_a_number is numeric.
With that knowledge, no matter what we throw at our PHP condition—numbers, strings,
null, objects, arrays—it will always run exactly as intended. If you can meaningfully add 2 to something, the code inside will run, and if you can’t, it won’t.
We did this with the magic of being verbose: stating everything we mean, thing by thing, rather than relying on assumptions about one thing implying another.
In other words, always work to clearly and fully state all your expectations, not just some of them, in the conditions you write.
2. Check Data Types First
Because PHP is loosely typed, your PHP conditions need to be especially clear about the data types you expect to be working with. If you don’t write this way, bad things can happen. Here’s an example.
Example with a Badly Written Conditional
If we want to combine two arrays, we need to make sure that the second array has stuff in it, or it’s not worth adding to. We can find out how many elements an array has using
count(), so I guess we can just write that into our
$shopping_list = array( 'oranges', 'bananas' ); $new_list = array( 'strawberries' ); // We'll just make sure $new_list has elements before trying to combine it in if ( count( $new_list ) ) : $shopping_list = array_merge( $shopping_list, $new_list ); endif; // Great, now we're ready to print out our shopping list! foreach ( $shopping_list as $list_item ) : echo $list_item . ' '; endforeach;
The code above will print out “oranges bananas strawberries ”—so it’s working, right?
Not really. It overrelies on assumptions—particularly about data types—and so it’s extremely fragile:
$shopping_list = array( 'oranges', 'bananas' ); // $new_list is an empty string, which is a fine way to indicate an "empty" thing... right? $new_list = ''; // We'll just make sure $new_list has elements before trying to combine it in if ( count( $new_list ) ) : $shopping_list = array_merge( $shopping_list, $new_list ); // Bad news: this code will run endif; // So now we're ready to print out our shopping list foreach ( $shopping_list as $list_item ) : echo $list_item; endforeach; // Wait nothing at all printed to the page // Wait what happened? var_dump( $shopping_list ); // This prints out NULL // Uh oh
What happened here? For what must have felt like good reasons at the time,
count( '' ); in PHP returns
0 or an error. Our
if-statement will run, and inside it we’re going to try the never-fun job of
array_merge()ing two things that are not both arrays.
So what happens when you
array_merge() an array with a string? You get
null. In other words, we’ve ended up destroying the original array we were trying to merge with.
Example with a Well-Written Conditional
Here is better code:
$shopping_list = array( 'oranges', 'bananas' ); $new_list = ''; // Let's go crazy // Are we actually getting an array with more than 0 elements? if ( is_array( $new_list ) && count( $new_list ) > 0 ) : $shopping_list = array_merge( $shopping_list, $new_list ); endif; // Great, now we're ready to print out our shopping list! foreach ( $shopping_list as $list_item ) : echo $list_item . ' '; endforeach;
The result of the above will be “oranges bananas ”—not
null. More generally, this code will always behave as expected, including when
$new_list isn’t the array it should be.
Why is this code better? Because of the
is_array() check that leaves absolutely nothing to chance about the type of thing
$new_list might be.
It’s also better, in my opinion, because of the verbose
count( $new_list ) > 0 check rather than just
count( $new_list ). Those things behave similarly, but in one you’re saying what you mean—“I want to make sure that this array has more than 0 elements”—and in another you’re relying on slightly cocky shorthand. Don’t do that in PHP conditionals.
3. Don’t Mess Around with Loose Equivalence
If something equals another thing, then it definitely equals that thing, right?
Ha! The answer is of course no, and we PHP programmers are now quietly snorting into our Hawaiian Punch.
$integer = 5; // I'm the integer 5. $string = '5'; // I'm the string "5". $bool = $integer == $string; // This is PHP's loose equivalence test. Are these two things equal? // $bool is now true
== operator tests loose equivalence: are two things equal, once you ignore their data type? There’s also
!=, loose not-equals: are two things unequal, once you ignore their data type?
If that sounds like fuzzy reasoning to you, then we agree—and it can lead to all kinds of potentially weird results, as we’ll now demonstrate.
Example of Loose Equivalence (Avoid)
Here are two failing
if checks that use loose equivalence, plus ensuing hijinks.
$integer = 5; $string = '00005'; if ( $integer == $string ) : echo 'Wait what'; // This code will run, which is bad echo strlen( $string ); // Prints out "5" echo strlen( $integer ); // Prints out "1" // I guess they weren't that equal. // And why are we running strlen() on an integer? endif; // Maybe they're not equal after all. Let's check again. if ( $integer != $string ) : echo 'Okay, good'; // This code won't run, which is bad else : echo 'Wait what'; // This code will run, which is bad endif;
Example of Strict Equivalence (Use)
Here are two much-improved PHP conditionals:
$integer = 5; $string = '00005'; if ( $integer === $string ) : echo 'Wait what'; // This code won't run, because integers aren't strings endif; // So they're not equal? Let's make sure if ( $integer !== $string ) : echo 'Okay, good'; // This code will run, which is good else : echo 'Wait what'; // This code won't run, which is good endif;
Do you see the actual code difference? It’s the extra
= in our conditions—from
As it turns out, one extra
= makes all the difference, because it denotes strict equivalence: whether two things are identical in value and type.
I have never found a good reason to use loose equivalence in my code. Instead, write code that gets exactly what you’re looking for, data type included. Accept no substitutes.
We’re Done, On One Condition
If you have a better sense of how PHP conditionals work, and how to write airtight
while() loops, and so on in your code, then thank you for reading!
Else, you might want to take a closer read, or contact us for further information.
In either case, we’d love to hear from you in the comments below, or in our Facebook group.