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 (Login to leave 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)
?>
<?php
var_dump('0000010.0000' == '10')
?>
output >> true
<?php
$x = ('string' == 0);
var_dump($x); // Outputs bool(true)
?>
Both of these scenarios are as intended--and documented!
In the first example, the documentation clearly states that if both strings can be interpreted as numbers, they will be compared as numbers so that this very effect IS possible. If for example, you are comparing a calculated total (as an integer) to another stored in a database (as a string), this becomes the desired effect. See the explanation for the is_numeric() function above. If both values return true for is_numeric(), then they are compared as numbers.
In the second example you see much the same effect. The point being that the == operator is DESIGNED to perform interpreted comparisons between values of different types. That's what the operator is for.
In both examples, the author should have been using ===. This is the operator provided for performing true equality comparisons as it requires that two pieces of data are equal in both value and type.
So please, do not complain about PHP working as it's intended and documented. Learn the language properly and make sure you're using the right operators.
Also, some of the other examples here fall back to order of operations. This too is documented. Any time you are using multiple operators you should be grouping your expressions. Just because PHP allows you to create expressions without grouping doesn't mean that it's a healthy coding practice.
Finally, people keep making the complaint that PHP is not a strictly typed language. It's designed NOT to be! That's one of its advantages. It enables you to use data provided by different sources without having heavy restrictions. If you're sanitizing and properly handling input data, this should never be a problem anyway.
In other words, virtually every case on this page can be very easily averted by avoiding bad coding practices.
$str = 'I am 18 years old';
$str = ereg_replace('18',$b,$str);
echo $str;
Echoes:
I am [funky character here] years old.
Here again. Why would you write this code? Knowing that the ereg_replace function requires a string input, you should be providing a string. You're solution was close, but still not completely correct. This is the proper, and safe approach:
$str = ereg_replace('18',"{$b}",$str);
or even:
$str = ereg_replace('18',(string)$b,$str);
Here's what's happening. PHP expects $b to be a string. If it isn't, it kindly attempts to use it as one. However, you need to understand this process. Before it uses the numbers in a literal string, it first checks whether they can be interpreted as a character code. In this case, decimal 19 equates to hex 13. A closer examination shows the ereg is replacing the string with character 13.
$b = hexdec(13); // decimal 19 = hex 13 (0x13)
$str = 'I am 18 years old';
$str = ereg_replace('18',$b,$str);
echo $str;
$b = 0x13;
$str = 'I am 18 years old';
$str = ereg_replace('18',$b,$str);
echo $str;
Notice how the above examples also produce the same unwanted result.
Bottom line, if a function requires a string attribute you should designate it as a string, even if that value is provided in the form of a variable.
Your explanation is a bit off here. The real reason your code echos "21" instead of "22" is because:
$c->X
is NOT the same as:
$c->x
If you correct that typo you'll get a fatal error because you're trying to set $c->x which is protected.
The only reason your code is working at all is because you have another bug in your __get() method. The correct way to use __get() would be to change:
return $this->x;
to:
return $this->{$name}