The difference between or and || are not well understood by many PHP developers. On the surface, the operators appear to be interchangeable but this is not correct. Understanding the difference between these operators will make you a better developer.

Breaking Our Codebase

Blogs.ed.ac.uk is the University of Edinburgh's Academic Blogging platform. It is a multisite WordPress instance that hosts blogs to support academic activities.

The service uses a plugin that allows you to add mulitple users to a blog in a single operation. It needed an update to check that the users added have valid usernames by cross-referencing them with our central LDAP server.

To connect to LDAP, we added the following code to our plugin:

$ldapConn  = ldap_connect(  $host,  $port ) or wp_die( "Could not connect to {$host}", 500 ); 

During peer review of this code, the reviewer made a suggestion to change or to ||. The code was changed and the plugin broke. In this blog post I'd like to take a quick look at why this was the case, to help dispel the confusion around these seemingly similar operators.

It's All About Precedence

While or and || both check that at least one condition is true, their execution order can be different. This is because or and || operate at different precedences.

Operator precedence determines how two expressions are bound together. The PHP manual gives the following example:

$value = 1 + 5 * 3;

echo $value; // 16

You'd be forgiven for thinking that 1 + 5 * 3 would give you 18 as 1 + 5 = 6 and 6 * 3 = 18 but this is not the way PHP executes this statement.

Instead, PHP executes 5 * 3 first, which gives us 15. It then executes 1 + 15 to get the final result of 16.

The multiplication operator has a higher precedence than the addition operator so it will be evaluated first.

Back to our OR Replacement Scenario

Our original statement works as expected:

$ldapConn = ldap_connect( $host, $port ) or wp_die( "Could not connect to {$host}", 500 );

But when following the reviewers comment and replacing or with || as in the statement below, our code fails.

$ldapConn = ldap_connect( $host, $port ) || wp_die( "Could not connect to {$host}", 500 );

Again, this is because of operator precedence. The three operators we are concerned with are =, ||, or. Their ordering is as follows:

  1. ||
  2. =
  3. or

or has a lower precedence than both = and || so in our original statement, it would be actioned last.

|| has a higher precedence than = so it will be actioned first in our modified statement based on the reviewers comments.

This subtle change alters what is stored in the variable, $ldapConn. We can visualise this by adding additional brackets to each statement to make it clear what the order of execution is for each statement.

// Assume that the LDAP connection will always be successful

// Correct statement
(($ldapConn  = ldap_connect(  $host,  $port )) or wp_die( "Could not connect to {$host}", 500 ))

// Incorrect Statement
($ldapConn  = (ldap_connect(  $host,  $port ) || wp_die( "Could not connect to {$host}", 500 ))) 

In the first statement, the result of ldap_connect() is assigned to the variable $ldapConn before the or check is executed. This is because = has a higher precedence than or.

If $ldapConn contains a truthy value, execution halts as the or condition will be satisfied and the wp_die() function is never called. If the syntactic check for LDAP connection fails, the function will return false and wp_die() will be executed as expected.

In the second statement, because = has a lower precedence than ||, the || operator is resolved first. If the ldap_connect() call is successful, the || check would result in true. This boolean value is then assigned to $ldapConn.

Later function calls expect  $ldapConn to be a LDAP link identifier. Because it was a boolean value, our plugin broke and brought down the site.

It Pays to Understand Your Code

As developers we should take time to understand why our code functions the way it does. The || bug was quickly fixed by reverting  the change but we didn't understand why this fixed the issue.

As a team, we investigated the problem in depth and made sure we understood why our code wasn't working. By doing this, we have improved our knowledge of PHP and made ourselves better developers.

A good peer review process and testing strategy should surface issues like these. When you find a similar error, ask yourself why  this is happening. It will make you a better programmer in the long term, and your work  will improve because of it.

Photo by Markus Spiske on Unsplash