PHP Gotchas!
Call them obscure, call them pointless, call them "newb mistakes." Whatever you call them, you've more than likely been tripped up at some point in your PHP coding journey by seemingly odd or illogical behaviors of the language. With PHP being a loosely-typed language, funny things are bound to happen. PHP is an easy language to pick up for the casual coder--things should "just work." But not everyone comes into PHP development with a strong programming background, so here are some charming examples of ways PHP can trip you up if you aren't careful. Put on your thinking caps--here comes the science!
Needles and Haystacks
Say you want to test for the existence of a string inside another string--find the needle in the haystack. It's a pretty common task. Let's let use the strpos() function to knock it out:
if (strpos('abcdefg','abc')){
echo "found it!";
}else{
echo "didn't find it..";
}
Wait a sec! That needle is definitely in that haystack. Why is the strpos() function returning false? Let's peek at the manual entry:
strpos -- Find position of first occurrence of a string
int strpos ( string haystack, mixed needle [, int offset] )
Ah! The strpos() function returns an integer indicating the position (hence the name)! Since our needle is found at the beginning position of the haystack, that function returns 0, which evaluates to false. In addition, the manual states:
If needle is not found, strpos() will return boolean FALSE.
Great, so we'll just patch up the example like so:
if (strpos('abcdefg','abc') != false){
echo "found it!";
}else{
echo "didn't find it..";
}
Huh!? Our 0 return value is not equal to false... or is it? Yes, the integer 0 evaluates to false. We need to check the value as well as the type:
if (strpos('abcdefg','abc') !== false){
echo "found it!";
}else{
echo "didn't find it..";
}
Finally, we get the desired result. The == operator compares value while === (and its inverse !==) compare value as well as type. If you browse the PHP bug tracker, you'll see that this particular gotcha has been submitted many, many times, and marked as a bogus bug report on each occasion.
Cephalopods in my code?
Here's a fun PHP gotcha that I discovered when reading about the Zend PHP Certification:
var_dump(0123 == 123); //outputs bool(false)
This made absolutely no sense to me. Then I discovered that the leading zero made the first integer an octal number, which evaluates to the decimal number 83. Octal notation was included in PHP to make it easy to work with file modes. See the manual page for chmod() for a related gotcha.
While we're at it, don't confuse the first example with:
var_dump("0123" == 123); //outputs bool(true)
The string "0123" is converted to the integer 123 in the comparison.
Consistent Constants
Pop quiz. What will the following code print?
//MY_UNDEFINED_CONSTANT really is undefined
if (MY_UNDEFINED_CONSTANT){
echo "Kelly Clarkson";
}else{
echo "Kellie Pickler";
}
If you picked Pickler, you'd be wrong in this case. PHP sets an E_NOTICE error here, but if your configuration disregards errors at that level, you should remember undefined constants are treated as the name of the constant, as if you were using the name as a string. And the string "MY_UNDEFINED_CONSTANT" evaluates to true when cast to a boolean.
My Math Teacher Was Lying?
Put a third-grader in a room with computer science major and let them duke this one out:
var_dump(0.7 + 0.1 == 0.8); //outputs bool(false)
Floating point numbers are definitely not what they seem. Precision becomes a factor with floating point numbers. PHP doesn't seem to do the logical thing when comparing two floats, and this is due to the internal representation of the numbers. The solution is simply never compare floats for equality!
My "Intro to Logic Systems" Professor Was Lying, Too?!
I really started second guessing my student loans when I stumbled across this gotcha:
$x = true and false;
var_dump($x); //outputs bool(true)
Why is 'and' acting like an 'or' operator? Stumped? Maybe this will help:
$y = ($x = true and false);
var_dump($x); //outputs bool(true)
var_dump($y); //outputs bool(false)
This is a case of mistaken operator precedence. In this example, the assignment operator (=) has higher precedence than the 'and' operator, so $x is assigned the value true. The second example clears things up a bit. After the first assignment to $x, the return value (true) is anded with false, yielding false, which is assigned to $y.
__toString() or not to String
PHP version 5 introduced a handy new magic method, __toString(), which lets you do something to the tune of:
class Person {
private $name;
function __construct($name="John Doe"){
$this->name = $name;
}
function __toString(){
return (string) $this->name;
}
}
$bob = new Person('Bob');
echo $bob; //prints Bob
That's nice and convenient. But what if I want to add a little punch:
echo $bob . "!"; //prints Object id #1!
What happened to Bob? Turns out the concatenation operation (.) takes precedence, and my __toString() function is never called. What if I cast my object to a string:
$name = (string) $bob;
echo $name . "!"; //prints Object id #1!
Still no dice. The manual tells us that __toString is only called when it is directly combined with echo() or print(). So let's try sending our object, and our emphatic exclamation point as parameters to echo().
echo $bob , "!"; //prints Bob!
Viola! Finally, our __toString() method is called.
Got More Gotchas?
These PHP gotchas are not only informative, but lots of fun to pick apart and share with your programming buddies. If you have any personal horror stories and examples, please share them with in the post comments.

Comments
var_dump((string)(0.7 + 0.1) == (string)(0.8)); //outputs bool(true)
Weird thing though. Should be fixed if it is a bug or removed when it is a feature.
Sorry pal, this is well known and accepted behaviour in most programming languages under the sun.
Try it out in C or Java to see what I mean.
java link: http://java.sun.com/docs/books/tutorial/java/problems/index.html
c++ link, with some detailed explanation: http://www.cprogramming.com/tutorial/floating_point/understanding_floating_point_representation.html
In short: the problem does not lie within PHP itself, but with the internal representation of a floating point number inside your computer as a 32/64/128 bit binary entity.
You need a special lib to do real flotaing point math.
No go back to class, please ;)
One of the points I was trying to make in the article is that PHP developers come from many different backgrounds. Those with formal CS education can take things like knowledge of floating point representation for granted, but for a casual coder it can be a real head scratcher. Anything a language can provide to fill the gap is helpful IMO, otherwise it's up to documentation and community to make these behaviors apparent.
In my various encounters with PHP, I've run into a couple head-scratchers. Please note that while I use double-quotes (") for clarity in this document, I would normally use single quotes for performance ('):
------------------------------------------------------------------------------
if (FALSE);
{
//This code should not run
echo "Nothing";
}
Output>>> Nothing
Notice the semicolon at the end of the if() statement? PHP sees that as an empty statement, and then continues on the execute the block of code following it. This is not a PHP gotchya as much as a user error, but it happens.
------------------------------------------------------------------------------
$a = array(10.2 => "Something", "11.2" => "Anything");
var_dump($a);'
Will output:
array(2) {
[10]=>
string(9) "Something"
["11.2"]=>
string(8) "Anything"
}
Note that the 10.2 was floored into 10, but "11.2" was left alone because it was a string.
BEWARE of floats as array keys - they will produce unexpected results. From the manual: "[array] index may be of type string or numeric."
Consider this:
$aRadio = array(100.1 => "WPRR - Best Hits", 100.9 => "FRGY - Country Radio");
var_dump($aRadio);
Will output:
array(1) {
[100]=>
string(20) "FRGY - Country Radio"
}
Where did the first entry go? It was converted to (int) 100, and the second entry was also converted to (int) 100, overwriting it. The correct usage would be:
$aRadio = array("100.1" => "WPRR - Best Hits", "100.9" => "FRGY - Country Radio");
var_dump($aRadio);'
Will output:
array(2) {
["100.1"]=>
string(16) "WPRR - Best Hits"
["100.9"]=>
string(20) "FRGY - Country Radio"
}
------------------------------------------------------------------------------
Watch out for PHP types - it is not a strongly typed language!
class something
{
public function __construct($eArg)
{
switch(gettype($eArg))
{
case "integer":
//Load something by ID
break;
case "array":
//Create someting using an array of data
break;
default:
//throw an exception (invalid parameter)
}
}
}
Through the course of the application, this class would sometimes work, and othertimes fail. You see, any input from _GET, _POST and friends is of type "string". Database queries normally return integer values as strings. So, sometimes this class was being passed a numeric ID that was in a string format...
$o = new something($_POST['ID']); -> would fail because input was a string
$a = $oResult->fetch_assoc();
$o = new something($a["ID"]); -> would fail because input was a string
$o = new something(123) -> would succeed because input was an int
So, for this given application, the correct syntax to use was:
class something
{
public function __construct($eArg)
{
if(is_numeric($eArg))
{
//Load something by ID
break;
}
elseif(is_array($eArg))
{
//Create someting using an array of data
break;
}
else
{
//throw an exception (invalid parameter)
}
}
}
The function is_numeric() checks the input to see if it "looks" like a number (ie. string representation of a number). So, unless you are writing code with a *VERY-SPECIFIC* need, do not rely on the the different scalar types within PHP.
Hope this helps someone :)
--
Jason Garber
IonZoft, Inc.
var_dump('0000010.0000' == '10')
?>
output >> true
Consider:
$b = 19;
$str = 'I am 18 years old';
$str = ereg_replace('18',$b,$str);
echo $str;
Echoes:
I am [funky character here] years old.
Instead, if you're not sure what the value of $b is:
$str = ereg_replace('18',"$b",$str);
protected $x = 1;
function __get($name) {
return $this->x;
}
function echoX() {
echo $this->x;
}
}
$c = new Class2();
$c->X = $c->X + 1;
echo $c->X;
$c->echoX();
Why this code prints 21 instead of 22?
The answer is a reason for not just defining ReadOnly property with __get, but for disabling assignment in __set as well...
best wishes,
Brett
Not everyone has classes, I for one have never gone to one on comp sci. Maybe I could say that I never really saw the object of a class? However, that has not prevented me from understanding floating point maths (aka math). More over, in invoice calculations in the UK there are rules as to how accurate maths has to be (7 sig fig decimal for sales tax for example). Maybe PHP, as it does try and support a broad range of skill sets, should introduce an appox equal opperator for floating point maths. This would default to the maximum inaccuracy caused by the native floating point decimal <-> binary conversion on the system. This would not be 100% as errors can accumulate in some sorts of calculation - but it would be a start. The newtonean molecular dynamic package CHARMM has exactly this opperator (.AE. if memory serves) and it has been very useful to me in the past. Maybe =~= or >=< ?
AJ
<?php
$x = ('string' == 0);
var_dump($x); // Outputs bool(true)
?>
The string is internally converted to 0, and thus matches. You should, instead, use the type included comparison operator ===.
<?php
$x = ('string' === 0);
var_dump($x); // Outputs bool(false)
?>