15

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.

casperOne
  • 73,706
  • 19
  • 184
  • 253
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • As from php 5.3.3 you can no longer use the class name as the constructor name if you use namespaces. You must use '__construct'. No warnings will be issued, but calling 'new MyClass' will not execute the code in your constructor. Hurray for a nice pitfall. –  Dec 20 '14 at 00:26

14 Answers14

10

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
  • 176,118
  • 18
  • 189
  • 202
10

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 ?>

TJ L
  • 23,914
  • 7
  • 59
  • 77
9

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
  • 21,335
  • 15
  • 77
  • 102
  • 7
    echo()'s possibility to take several arguments can be used here as well: echo 'foo: ', $x+1, ' bar'; – Philippe Gerber Jul 09 '09 at 19:09
  • 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 Jul 09 '09 at 19:33
  • 4
    @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 Jul 09 '09 at 20:13
7

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.

MoshiBin
  • 3,107
  • 2
  • 21
  • 23
7

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
  • 87,203
  • 23
  • 98
  • 131
6

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
  • 11,012
  • 3
  • 35
  • 50
  • Do you know what's going on under the hood ? I am curious why php returns this. – Harijoe Aug 12 '15 at 13:20
  • 2
    The =& assignment makes the final entry in the array a reference to the $item variable. On each iteration of the foreach loop that means $testarray[3] changes to be the current value of $item. Since $testarray[2] is "two", that value persists when iterating over $testarray[3], and it remains the final value of $testarray[3] – Joeri Sebrechts Aug 14 '15 at 07:59
4

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()

Solution: Leave the manual open at all times.

acrosman
  • 12,814
  • 10
  • 39
  • 55
  • 1
    Incidentally, part of the reason for that is that they were initially trying to emulate C with functions like `strstr()` and `strcmp()`. – Christian Mann Oct 27 '10 at 03:09
3

Sometimes you like to pass a function-result to another function directly, which only accepts variables as input.

For example:

array_pop(  explode('\\', get_class($this))  )

…will result in an E_STRICT-error "Only variables should be passed by reference". To get get it working just add another pair of brackets around the inner (returned) statement:

array_pop( (explode('\\', get_class($this))) )

(If your are using namespaces and extended classes, this line returns only the class name (without namespace) from the calling object–not the parent-class which was extended and may hold this function.)

feeela
  • 29,399
  • 7
  • 59
  • 71
  • Just faced this error. Thank you for solution. By the way, additional brackets are not recognized as correct by PHPStorm. PS: This problem could also be solved using `\ReflectionObject` and `getName()` method. – Kirzilla Nov 26 '14 at 14:36
2

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.

== EDIT ==

In PHP >= 5.4 the short_tags setting is no longer taken into consideration for the shorthand echo statement , as such it will be available in every hosting environment that supports PHP 5.4 and up.

Kevin Op den Kamp
  • 565
  • 1
  • 8
  • 22
spoulson
  • 21,335
  • 15
  • 77
  • 102
  • I agree short tags should be the default. – TJ L Jul 09 '09 at 18:29
  • 7
    short tags are evil. they mix up with XML declaration. you should not use it ... – Philippe Gerber Jul 09 '09 at 19:04
  • 2
    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, " – spoulson Jul 09 '09 at 19:33
  • 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 Jul 09 '09 at 19:38
  • i also like short tags and also think the argument made by Philippe is sophistry. – Randy L Mar 06 '10 at 16:52
  • 1
    Embedded PHP is among the worse techniques to generate XML. – Álvaro González Jun 13 '11 at 08:46
2

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-
  • 104,512
  • 87
  • 279
  • 422
1

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
  • 176,118
  • 18
  • 189
  • 202
  • 4
    foreach(str_split($str) AS $char) { echo $char; } – Andrew Moore Jul 09 '09 at 17:51
  • 3
    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 Jul 09 '09 at 18:19
  • has nothing to do with the original post... – eddy147 Mar 26 '10 at 14:14
  • Dr. Hfuhruhurr, funny, because I wrote both the original post and this answer. – Ionuț G. Stan Mar 26 '10 at 14:42
1

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 -Malimovka
  • 52,579
  • 61
  • 190
  • 278
  • 2
    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 Jul 09 '09 at 18:23
  • 2
    The == operator coerces data types. You should be using ===, anyway. – spoulson Jul 09 '09 at 18:25
  • "The == operator coerces data..." not always, Some times I do want to treat null,0,'' as false and all other as true. – Itay Moav -Malimovka Jul 09 '09 at 18:42
  • @ItayMoav **Some times I do want to treat null,0,'' as false and all other as true.** - for this you should use dedicated functions is_null(), empty(), etc. Things are not done right using a type casting comparative operation, like ==, use === instead. – Starlays Oct 09 '12 at 10:03
  • 1
    @Starlays :-D I am three years more experienced today... – Itay Moav -Malimovka Oct 09 '12 at 12:56
1

== EDIT == This is no longer true for PHP >= 5.5!

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)) {
    // ...
}
Kevin Op den Kamp
  • 565
  • 1
  • 8
  • 22
Philippe Gerber
  • 17,457
  • 6
  • 45
  • 40
0
$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
  • 4,679
  • 12
  • 41
  • 60
  • 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 Jul 09 '09 at 19:50
  • 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 Jul 09 '09 at 20:06
  • 3
    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 Jul 10 '09 at 07:45
  • 1
    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 Jul 29 '10 at 12:08