One of my favorite optimization techniques is using the Identical operator. Why? Because it’s such an easy replacement for the Equality operator and it works much faster.
Wait, now you might be wondering “Equality Operator” ? … “Identical Operator” ? Aren’t those the same thing? Well, no. Let’s take a look.
PHP has lots of comparison operators. But it has two different sets of operators that are used to see if two things are equal or not equal to one another. These are called the Equal Operator: ==, != and the Identical Operator: ===, !==.
On the surface, these two operators do the same thing. But when you dig deeper an ugly truth emerges. The Equal operator does more work. Let’s look at some examples:
$x = 1024; $y = 'abc'; if ('1024' == $x) echo '<p>equal</p>'; if ('abc' == $y) echo '<p>equal</p>';
$x = 1024; $y = 'abc'; if ('1024' === $x) echo '<p>equal</p>'; if ('abc' === $y) echo '<p>equal</p>';
These two examples do not do the same thing. On line 4 of both examples, the value of the variable is compared against ‘1024’ — that’s a string.
PHP is a “loosely typed” language, which means you can assign any value you like to any variable at any time. So over it’s life, a variable can hold values of different types. That’s great for lazy programmers that don’t want to come up with new variable names all the time, but it’s not good for optimization.
This is different than a “strongly typed” language, such a Java and C++. In these languages, a variable can only be used for one type of data – the type that it’s declared to use. Any other type being assigned to the variable will be a compiler error.
So in strongly typed languages, a comparison operator only needs to compare the values of the two things it’s comparing. Because it already knows the type of variable, it doesn’t have to compare for type because the compiler won’t even generate code if the values are not of the same type. The drawback is that it forces you to come up with new variable names.
Back to PHP and it’s loosely typed variables. Because any two variables can be compared in PHP, the language needs to do extra work. If two values are not the same, the Equality Operator (==) will also convert one of the variables to the other’s type and compare them again. Yes, two comparisons. So if
'1024' == $xdoesn’t work, it will convert the string into an integer and compare again to see if
1024 == $x
Why is this an optimization issue? Most of the time we’re writing code, we know what the type of the data is that we’re working with. When we have a variable $x, we’ll know if it’s referring to an integer, float, string or an object. So when we write an if test to see if it’s equal to some constant value, like 0, we know that we’re comparing our variable that should hold an integer against the integer constant 0. Why not help the PHP compiler and let it know that we know it’s an integer? By writing
if (0 === $x)we tell the compiler that if the values are not initially equal, don’t bother converting them and comparing again, just resolve your analysis to FALSE.
To give you an idea of how much extra time this Equality vs. Identical operator difference can make, take a look at the following code:
define('ITERATIONS', 10000000); function test_equal_operator() { $a = FALSE; $b = '123'; $c = 456; $d = 'abc'; $start = microtime(TRUE); for ($i = 0; $i < ITERATIONS; $i++) { if ($a == TRUE) ; if ($a == FALSE) ; if ($b == 123) ; if ($b == 456) ; if ($c == 456) ; if ($c == 789) ; if ($d == 'abc') ; if ($d == 'xyz') ; } $end = microtime(TRUE); return $end - $start; } function test_identical_operator() { $a = FALSE; $b = '123'; $c = 456; $d = 'abc'; $start = microtime(TRUE); for ($i = 0; $i < ITERATIONS; $i++) { if ($a === TRUE) ; if ($a === FALSE) ; if ($b === '123') ; if ($b === 456) ; if ($c === 456) ; if ($c === 789) ; if ($d === 'abc') ; if ($d === 'xyz') ; } $end = microtime(TRUE); return $end - $start; } $elapsed = test_identical_operator(); echo 'test_identical_operator() ran in ', number_format($elapsed, 5), ' seconds', PHP_EOL; $elapsed = test_equal_operator(); echo 'test_equal_operator() ran in ', number_format($elapsed, 5), ' seconds', PHP_EOL; echo 'ran ', number_format(ITERATIONS, 0, '.', ','), ' iterations on tests', PHP_EOL;
Above you’ll see that I set up a few variables and then run them through a positive test, where I know that values are the same and a negative test, where I know the values are different. I do this with each operator, Equal == and Identical ===. The results are impressive. For only 10 million iterations through the test, the Identical operator finishes in about half the time. On average about 0.54 seconds for the Identical operator compared to 1.01 seconds for the Equal operator. This makes sense since it’s doing half the work. It only compares to see if the values are Identical. It doesn’t do any type casting and secondary compares.
So, whenever you know the types of your variables, which should be most of the time, you can use the Identical operator === to just check the value.
And what if you don’t know the type? I would say that good coding practices would tell us that we should force the type. So if you don’t know if you’re comparing an integer, force the variable to an integer and compare that: if ( 0 === (int) $x ). After all, you’re comparing the value to a known value of a known type (Integer 0) and not a known value of an unknown type (zero-ish?). Personally, I always force my variables to the type that I expect them to be. Maybe that’s a holdover from my C++ and Java coding days, but I think it’s a good policy from a code security point of view (never trust your inputs!), and it’s a good policy from a optimized coding point of view in a loosely typed language like PHP.
I don’t know about you, but I write a lot of comparisons in my code. So something simple like this can have a big impact.