I came across some search and replace PHP code that was working correctly if the search string sat in the middle or towards the end of the input string, but failed to perform if the search string was positioned at the front of the string.
Taking a look at the function in question, I quickly spotted the mistake.
The coder had put down:
if (!strpos($inputstring,$searchstring) === false){ //search string located, so do something! }
Now we know strpos returns the integer index of the starting point of the first instance of the search string if it locates the search string in the input string, or a boolean FALSE if it can’t locate it.
Based on this, the developer was essentially trying to say that IF (NOT ({strpos result} === FALSE)) (in other words the result is anything but FALSE) then we’ve found what we are looking for.
Unfortunately what tripped him up here was classic operator order – essentially his line of code means that the ! (NOT) is applied first to the result of strpos, before the result of that operation is compared to FALSE.
In other words, he wouldn’t suffer from the problem had he simply introduced some more brackets into his statement:
if (!(strpos($inputstring,$searchstring) === false)){ //search string located, so do something! }
With these brackets in place, the function now basically gets the result of the strpos function and checks if it is equivalent to false, before flipping the answer by applying NOT (!) to it.
But why did the original approach find the search string as long as it wasn’t right at the start of the input string then?
Well the reason lies in PHP’s unfortunate ability to represent the boolean FALSE as the integer 0.
So, if the search string existed in the input string, but not at the start of the input string, the strpos function would return an integer greater than 0. This integer once flipped with NOT would result in a boolean FALSE, which would then satisfy the === FALSE check at the end, thus telling us the search string was present. If the search string wasn’t found, strpos would return FALSE, which would be flipped to become TRUE which then wouldn’t match the final check against FALSE, in other words correctly telling us that the search string does not exist.
However, if the search string was at the start of the input string, then strpos returned 0, which is as far a PHP is concerned, equal to FALSE. And flipping FALSE with NOT of course results in TRUE, which as expected, does not satisfy the final FALSE check and thus incorrect tells us that the search string is not present in the input string.
Now with the newly added brackets in place, the solution correctly first evaluates the strpos result against FALSE before flipping it with NOT, meaning that the function now works consistentl in terms of it doesn’t matter where the search string exists within the input string.
All of this said and done though, of course our dear developer could have completely sidestepped this mess had he simply evaluated the strpos result using !=== FALSE in the first place!