views:

720

answers:

14

I've realized that, although most of my experience consists in writing PHP applications, I find myself making "beginner mistakes" from time to time. This because PHP is a language that has grown very organically and as such has some idiosyncrasies, quirks and pitfalls that I don't know about.

I'd like this question to be a wiki for all those who want to know PHP's pitfalls and exceptions from what we might believe are the rules. But please, don't write general responses like:

Some functions receive arguments as $needle, $haystack, while some as $haystack, $needle.

Tell the function names. You have a few answers from me as examples. Oh, and add one pitfall per answer. This way we'll see which is the most despised of them all (by voting).

I don't want to start a flame war, keep it to the subject. If you'd like to write some bad things about PHP then do it as a comment to the respective answer.

Hopefully this wiki will be of help for all of us, beginners and experts.

Update:

After Andrew Moore's comment I'm thinking that the answer should also contain a solution or workaround for the pitfall.

A: 

Although strings may be iterated using for loops and indexes, they can't be iterated using foreach loops. Example:

$str = 'foo';
$max = strlen($str);

for ($i=0; $i<$max; $i++)  {
    echo $str[$i];
}
// outputs: foo

foreach ($str as $char) {
    echo $char;
}
// Warning: Invalid argument supplied for foreach() ...
Ionuț G. Stan
foreach(str_split($str) AS $char) { echo $char; }
Andrew Moore
This is because strings are not arrays of characters in PHP. The for loop works only because the brackets [] are syntactic sugar for accessing a particular character. Unfortunately this also gives the appearance that strings can be manipulated as arrays of characters, which as you see in the foreach loop, is untrue. As Andrew noted, using str_split() is an easy way to break it up into an actual array if that's what you need.
Bob Somers
has nothing to do with the original post...
Dr. Hfuhruhurr
Dr. Hfuhruhurr, funny, because I wrote both the original post and this answer.
Ionuț G. Stan
+3  A: 

Serializing objects that process XML structures and then unserializing them does not restore the original XML structure:

$dom = new DOMDocument;
$dom->loadXML('<test/>');

$dom = serialize($dom);
$dom = unserialize($dom);

var_dump($dom->saveXML());
// $ Warning: DOMDocument::saveXML(): Couldn't fetch DOMDocument in ...
// $ NULL

Similarly for SimpleXML objects.

Ionuț G. Stan
+7  A: 

Non-industry-standard operator precedence.

$x = 1;
echo 'foo: ' . $x+1 . ' bar';

Will output: "1 bar", instead of the expected "foo: 2 bar". Solution: Use parenthesis.

spoulson
echo()'s possibility to take several arguments can be used here as well: echo 'foo: ', $x+1, ' bar';
Philippe Gerber
It's worth noting that there are *two* funky things about this: not only is the precedence not what you wanted, but also, ("foo: 1" + 1) doesn't concatenate; it apparently equals 1, not "foo: 11".
ojrac
@ojrac, that's because the concatenation operator in PHP is . (dot), not + (plus) as in many other languages. + is used for arithmetic addition and "foo: 1" is evaluated to 0 in order to add 1 to it, thus the final result which is 1.
Ionuț G. Stan
+5  A: 

Integer size is platform dependent. You can't normally use 64bit integers on a 32bit machine without any outside module. Additionally, you cannot declare unsigned integers.

Spidey
+1  A: 

my favorite:

<?php
$a = 0;
$b = 'x';
var_dump(FALSE == $a);
var_dump($a == $b);
var_dump($b == TRUE);

echo' <br />Conclusion: TRUE equals FALSE (at least in PHP)';
Itay Moav
This can be shown in other scripting languages as well, not just PHP. It has to do with how 'x' is converted to an integer for comparison.
Bob Somers
The == operator coerces data types. You should be using ===, anyway.
spoulson
"The == operator coerces data..." not always, Some times I do want to treat null,0,'' as false and all other as true.
Itay Moav
A: 

Some newly introduced PHP features fall flat because there's no guarantee they'll be supported by default in hosting environments.

The biggest peeve of mine is the short_tags setting that enables <? foobar(); ?> and <?=$var ?> tag syntax. I argue PHP should've enabled this feature by default, rather than opt-in.

spoulson
I agree short tags should be the default.
tj111
short tags are evil. they mix up with XML declaration. you should not use it ...
Philippe Gerber
Irrational. The XML declaration is an edge case. And next you're going to tell me short tags are "hard to read" and some other nonsense. Simply put, "<?php echo" is excessive work compared to "<?=" once you've had to use it a dozen times in close proximity. It gets to the point for me that it's easier to give up on the templating and use "echo" for all HTML rendering, like the old Perl/CGI days.
spoulson
One more point, my argument is that short_tags should've been enabled by default. The unfortunate fact that PHP went with "<?=" as the open tag is beside the point. It's 9 characters shorter per tag than the old way. Additionally, "ini_set()" should've allowed this setting to be overriden. Flexibility would've been nice.
spoulson
i also like short tags and also think the argument made by Philippe is sophistry.
the0ther
+6  A: 

I hate how you can get issues if you close php files with a ?> closing tag (which seems like it should be the other way around). For example, include a file with some whitespace after the ?> and then try to change headers (assuming no output buffering). UGH. Took me way to long to learn to never close php files with ?>

tj111
+1  A: 

What I sometimes run into is the Fatal error: Can't use function return value in write context error when using the empty() construct. For example:

if (!empty(trim($_GET['s']))) {
    // ...
}

empty() needs a variable, anything else will result in the error.

The solution:

$s = trim($_GET['s']);
if (!empty($s)) {
    // ...
}
Philippe Gerber
Simpler still: `if (!trim($_GET['s'])) { ...`
troelskn
+1  A: 

Inconsistent naming conventions of built-in functions. For example this set of string processing functions:

 str_shuffle()
 str_split()
 str_word_count()
 strcasecmp()
 strchr()
 strcmp()
acrosman
Incidentally, part of the reason for that is that they were initially trying to emulate C with functions like `strstr()` and `strcmp()`.
Christian Mann
A: 
// this works
$_SESSION = array('username' => 'Martin', 'id' => 1, 'expires' => 230407297);

$arrayofsessiondata = array('username' => 'Martin', 'id' => 1, 'expires' => 230407297);

// this works
foreach($arrayofsessiondata as $k => $v)
{
    $_SESSION[$k] = $v;
}

// this DOESN'T WORK
$_SESSION = $arrayofsessiondata;

See my "bogus" bug report.

Martin
A: 
$x = "a string";

if ($x == 142)
{
    echo "lol, of course $x is a number";
}
else
{
    echo "$x is clearly not equal to 142";
}

When run:

lol, of course a string is a number

You must use ===

$x = "a string";

if ($x === 142)
{
    echo "lol, of course $x is a number";
}
else
{
    echo "$x is clearly not equal to 142";
}

When run:

a string is clearly not equal to 142
Tyler
I believe this example, although ordinary in the PHP community, is of great help for people coming from strict programming languages. Some of them don't even know there's such thing as triple equal.
Ionuț G. Stan
Yeah, took me a while to debug this issue in my code. I knew == coerced data types but I didn't know ("a string" == 234) would evaluate to true!!
Tyler
sorry, but that is not true. "a string" == 142 will evaluate to false. see the loose comparision table on http://ch.php.net/manual/en/types.comparisons.php
Philippe Gerber
Philippe is right, it goes"a string" == 142; /* false */ ... "a string" === 142; /* false */ ... "142" == 142; /* true */ ... "142" === 142; /* false */ ... Is there any version of php where ("a string" == 142) is true?
Deebster
+3  A: 

Multiple ways of doing truth tests (not operator, empty(), is_null(), isset()) + weak typing = this

With some discipline you can mostly avoid the need to refer to this table:

  • For general truth tests, you can use boolean comparison if ($) { ... } if (!$x) { ... }. It behaves the way boolean operators in most languages do.

  • Always use empty() if you want to test form input for falsy values (it treats "0" as false).

  • Always use isset() if you want to determine whethere a variable is set or not

  • Use is_null() or $x === NULL if you only need to check for NULL

Imran
+4  A: 

I've had all sorts of bother combining foreach with references

$testarray = array(1 => "one", 2 => "two");
$item = "three";
$testarray[3] =& $item;
foreach ($testarray as $key => $item) {
  // do nothing
}
echo $testarray[3]; // outputs "two"

This really threw me off during the PHP4 era, and although it's gotten better in PHP5 by having sane behavior if you don't use explicit references, I still manage to get caught by this one now and then.

Joeri Sebrechts
A: 

Having functions behave differently on different OS'es, and some functions are available only on specific OS'es.

For example, the mail() function on Windows cannot take sender name in the $to parameter. It can only contain email address. On Linux all works fine.

Another example, the function strptime() is only available on Linux, and not Windows (this got me in an existing project I wanted to run on my Windows box).

Sure, there are some functions that make sense only on certain OS'es (like Win32API functions), but many others seem like they should behave the same on all OS'es, when in fact they do not.

Vilx-