views:

4224

answers:

38

In the spirit of Common programming mistakes for .NET developers to avoid?, what are common mistakes PHP developers make?

Using == when === should be used is a common one. What are some others (preferably from your own experience)?

Community wiki'ed for complete satisfaction.

+10  A: 

Putting everything in one file

This is really easy to do, but after a while you realize that the file is too complicated and will easily break. If you can, split the file into several files, with functions in each file. The best method is to go object oriented (I assume you are using PHP5, its 2009 for gods sake!) and create classes with functions. This might slow down your script a bit, and will make a lot more data, but everything gets separated into nice objects.

Marius
i actually found that classifying and functionalizing my code resulted in a redesign rather than refactoring. The end result was efficient and a lot faster.
dassouki
Does using OOP really affect script performance? If anything, it would n't it speed up complicated scripts?
Alex L
OOP in PHP encourages code re-use, which done correctly, which decrease codebase size and thus memory usage.
staticsan
+10  A: 

I'd say the most common error in php is not checking user-input (and as it is a language which is mostly used for the web, there's almost always some user-input somewhere), making way for (My)SQL-Injections or XSS. I've even seen people passing user-input directly to eval()...

x3ro
Not checking user input is great if you like to shoot yourself in the foot. But it is not limited to PHP.
Gamecat
Agreed. All the times you see people just chucking user input directly into SQL without any checking at all. Hopefully the increasing popularity of frameworks and ORMs will help this. On the other hand - if you're skilled enough to use these components, won't you know to escape the input? :)
phidah
The problem with PHP is that its lax nature almost encourages people to skip input validation. It's so easy to just do `$sql = "UPDATE users SET something = 1 WHERE user_id = $_POST[user]";`
Blixt
+3  A: 

Data Access must be done right. By "right" I mean proper encapsulation of data-access logic, no SQL concatenation and please-please do use parametrized queries: this will help make world a better place.

Anton Gogolev
Can you expand on these points please? Specifically the parametrized queries.
Artem Russakovskii
I think Artem means using queries that have placeholders for the variables that should go in them. This allows the DB subsystem to properly escape those variables and insert them later. Every time. PDO prepared statements do this. http://php.net/pdo
Aaron
+36  A: 

SQL-injection vulnerabilities due to incorrect usage of mysql_query()

This might be a controversial opinion, but I belive that it's a mistake to use the old family of mysql functions. These are the family of functions prefixed with mysql_. There isn't really anything wrong with them as long as you use them correctly, but unfortunately I've found that most of the time they just aren't used correctly. The end result is that we have sites all over the internet vulnerable to SQL injection.

A better choice is to use an API that supports prepared statements, which solves this problem completely. MySQLi is such a family of functions, and for security purposes it's fine. However, I believe that the API is a bit of a pain to use. The main basis for that opinion is the fact that you simply cannot retrieve the result of a prepared statement as an array.

The best option is to use PDO. It's a modern database agnostic wrapper that supports flexible prepared statements and results in modern, safe and readable code. It's a joy to work with.

We should all stop teaching beginners the old functions and show them how to use and become comfortable with PDO from the start.

Emil H
I agree: PDO is the best option for all DB interaction in PHP, and MySQLi's API is a pain.
Justin Johnson
I used to like MySQLi, until I learned that its features are rather variable depending on what version of it you've got. I went from developing on a very modern environment to deploying to a system that couldn't even deal with the prepared statements I'd used. PDO!
The Wicked Flea
+7  A: 

Not turning on strict and reporting all errors, and you miss out on typos mistakes.

And there's one in PHP4 where foreach return value, not reference.

Some PHP bugs due to form processing are actually due to bad form HTML.

And here's my nemesis. Using of headers. They are not function calls! I had see code like this:

if ($bUserValid)
{
    header("Location: login-success.php");
}

header("Location: login-error.php");

The last header sent is the last header to be executed. So no matter what happens, you will always be redirected to login-error.php

Extrakun
Yeah, header() only sets header location, but doesn't outputs it right away. die() or exit() must be called after header to terminate script, and after that headers are sent :)
usoban
Also keep in mind that some spiders/bots IGNORE the "Location" headers completely, and no client *has* to follow them. That means if you use header redirection for security without any kinda of fallback, your secure data will be displayed in some search results and someone that disables header redirection in their browser can access it too.
tj111
A: 

I don't use PHP very often, so the "forget the $ variable prefix" trap often gets me.

Gamecat
Actually, I find $ great, you can seen what is variable right away. I can imagine if you come from other languages that you forget dollar sign, I have the same, but reverse problem with Java, sometimes I put dollar in front of variable and look like a retard what's wrong with compiler :D
usoban
Adding on to this, forgetting the $this :D
Extrakun
Or adding the $ when in $this context, i.e. $this->$var rather than $this->var
Colonel Sponsz
Just learn to get used to it.
drikoda
+56  A: 

Writing code without using the maximum level of error reporting

All development should be done with:

error_reporting(E_ALL | E_STRICT);

All notices and warning should be eliminated during development. It makes the code incredibly much more stable. (Of course all errors should then be hidden on production setups.)

Emil H
+1 for forgetting to hide errors and warnings on production.
Artem Russakovskii
Agreed. Ever tried that with Typo3? It's quite amusing actually.
x3ro
It doesn't help that the default php.ini file in the distribution has Notices disabled. I even filed a bug about that, but it was closed.
staticsan
on production you can make a script that sends you the errors by mail. It's a nice way to be notified as soon as something goes wrong.
mnml
A: 

forgetting to use e.g. mysql_escape_string() and similar

second
Please note that this function throws an E_DEPRECATED notice on PHP 5.3. Use mysql_real_escape_string() instead. Hey, I should post this one as a comment mistake. =p
Randell
And I'll post using the word "comment" when you mean "common." And then we'll get into recursion and infinite loops.
Lance Kidwell
Yeah, saw that. LOL. Comments should have edits.
Randell
+7  A: 

Using $var inside a class when what I really mean is self::$var or $this->var. With warnings off, this doesn't raise any flags.

Artem Russakovskii
+8  A: 

Forgetting the ; at the end of a line :-)

phidah
Oy. That bites me after I come back to PHP after I've been doing a lot of Icon. :-)
staticsan
+42  A: 

Unsafe usage of include

You often see things like:

include("pages/" . $_GET["pg"]);

It's a mistake of incredible proportions to not check the pg variable here.

Emil H
This problem has caused Google to flag several of our all-ages sites due to unscrupulous spammers linking to poorly designed pages (created by outside developers, of course ;) and stuffing malicious JS code into the querystring parameters. In the worst cases, these parameters have allowed full domain path includes (not even restricted to a subdir on our servers).
shawnr
Does this still happen?? Wow
AntonioCS
Ye gods, I never knew people did this.
abeger
A: 

Not being prudent when writing code :)

Jeffrey Boolos
This isn't PHP specific.
Randell
+23  A: 

I've got a list for this one from an old blog post I wrote awhile ago.

Unquoted array indexes:

//This is *WRONG* (but will work):
echo $array[my_key];
//This is correct
echo $array['my_key'];

PHP considers the unquoted index as a "bare" string, and considers it a defined constant. When it can't find a matching symbol for this constant in the symbol table however, it converts it to a real string, which is why your code will work. Quoting the index prevents PHP from having to check for the defined constant, and makes it safer in case someone defines a future constant with the same name. I've also heard that it is up to seven times faster than referencing an unquoted index for this reason.

zombat
Wouldn't it be better to add these as separate answers so that people can upvote them separately?
Blixt
Hmm, probably. I'll break it up.
zombat
You don't obey your own rules ;)Following your second rule, in 3. you'd have to write:echo 'Hi my name is '.$a.'. I am '.$b;
x3ro
@x3ro - I broke it into a separate answer. I know what you mean though. I used double-quotes in all those strings just so everything would be equal between the comparisons except the factor under discussion.
zombat
+3  A: 

Using literals as strings

While it's fortunately disappearing from more recent PHP versions, one thing I always hated was how PHP tried to do the best of any developer mistake instead of throwing an error. One such example is that trying to get an undefined constant would return the constant name as a string instead, and unknowing people would "abuse" that:

$user = $_POST[user]; // $_POST['user'], unless user is a defined constant
Blixt
+4  A: 

Using mysql_escape_string() instead of mysql_real_escape_string().

Randell
What I don't get is: why don't they just make it so the library always uses the safe one? Retain the syntax so you won't break anything, but for goodness sake, why keep in the broken and misleading function?
Dinah
@Dinah: Because the point isn't actually the escaping done by function itself, it's having a database connection for the escaping. `mysql_real_escape_string()` will actually *attempt to connect to the database* unless an active connection is available, which is *significantly* different functionality from the older function. Anyway, `mysql_escape_string()` is marked as deprecated, so it's not like they're keeping it.
Ilari Kajaste
+7  A: 

Use single quotes instead of double quotes for strings when possible.

When you surround a PHP string in double quotes, it is subsequently parsed by the PHP interpreter for variables and special characters, such as "\n". If you just want to output a basic string, use single quotes! There is a marginal performance benefit, since the string does not get parsed. If you have variables or special characters, then by all means use double-quotes, but pick single quotes when possible.

echo 'Do this when possible.';
echo "Not this.";
zombat
Have you got any details on the performance benefits? I was always told it really doesn't matter which you use, just be consistent with my use of quotes and as such have usually stuck with double-quotes.
Peter Spain
I've read that single quotes are about twice as fast as double quotes - however, it's more like 2us vs 1us so it's pretty negligible.
DisgruntledGoat
Also, if you're echo-ing HTML, single quotes are nicer since you don't need to keep escaping the double quotes. And even using double quotes, you need to break out of them for functions, so it's easier to move all variables outside of strings to be consistent.
DisgruntledGoat
The performance benefit is definitely marginal, and it's not worth re-writing your app over. If you're going to pick a "quotes habit" however, single quotes have the advantage. @DisgruntledGoat: Completely agree.
zombat
PHP team says: "Benchmarks run against PHP 5.2 and 5.3 show that parsing double-quoted strings with interpolation is no slower (and often faster) than single-quoted strings using concatenation." http://groups.google.com/group/make-the-web-faster/browse_thread/thread/ddfbe82dd80408cc
porneL
On PHP4, single quotes were as much as ten times faster.
staticsan
If you're worried about the performance difference as that which results between single and double quotes -- PHP is NOT the right choice.
Dinah
+14  A: 

Forgetting to learn the standard library, first.

Much wheel-reinvention is caused by this! (This applies to any programming environment, really)

xtofl
I can't remember how many times I've said this to my colleague: "Ever heard of function x ?"
andyk
+76  A: 

I personally enjoy writing in PHP. The fun stops when I have to do code reviews. Here's why:

  • Don't use regex (preg_match, etc) for simple string searches.
  • Turn on error_reporting(E_ALL | E_STRICT); in development.
  • Separate your damn presentation from your damn logic, better yet, use a standard templating system.
  • Weak-typing is a double-edged sword. Avoid exploiting it liberally.
  • Clean all user input via casting, escaping, appropriate functions etc.
  • Did I say clean all user input? Really, it boggles me how often this gets passed over.
  • Check the library before writing ridiculous functions. It's full of its own ridiculous functions.
  • If writing anything resembling a library, be consistent with parameter order ("is it haystack/needle or needle/haystack?").
  • define is your friend. Please don't make me hunt for silly literals in your code.
  • Unless you're using register_shutdown_function, die / exit is usually a very bad and ungraceful way of handling errors, especially in production. Even still, you're probably making it more complex than it needs to be.
  • You're writing a wrapper class for an existing library compiled as part of PHP (i.e.: db interaction). Stop. You should probably use a Pear class.
  • Format your code (yes, really).
  • Format your code consistently.
  • Don't write code like this: if ( condition ) { return true; } else { return false; } or $variable = condition ? true : false; (this applies to any language).
  • Use an opcode cache (APC or an equivalent thereof).
  • Use a distributed object cache when appropriate (ala memcached).
  • Be aware of type coercion: it will bite you. For example, "-" == (int)"-" is true.
  • POST when you should POST, GET when you should GET.

Maybe more, that's it for now.

Edit:

  • Using magic quotes.
  • Not upgrading (when possible).
  • Using $_REQUEST out of laziness instead of the appropriate $_GET and $_POST.
Justin Johnson
*Check the library before writing ridiculous functions. It's full of its own ridiculous functions* haha, great! :D
alex
Agree with all but the ternary operator "don't". It cant be incredibly useful for checking / setting default $_GET or $_POST vars, like $aInput['something_id'] = isset($_POST['something_id']) ? $_POST['something_id'] : 0. A block of those at the top of a script is a fine way of [pre]defining all incoming variables in an easy-to-reference tabular format. That way you KNOW all your vars are already defined and at the very least have a default value that can be checked, rather than relying on isset($_POST['...']) throughout the code.
enobrev
That's not what I'm saying. I'm arguing against the redundancy of literally assigning a true or false value after evaluating a condition when you can just use the result of the conditional evaluation as the assignment value.
Justin Johnson
+18  A: 

I think a very typical mistake, not even for a beginner is to have some kind of chars before the tag

<?php

Which makes the script thow an error I you want to modify the header. This is also the case if you end you php scripts with a php-tag an then these files get included/required. Nothing complicated, but sometimes hard to find.

Hippo
Just to explicitly throw it out there, this also breaks sessions for the same reason.
Justin Johnson
Yes, yes, yes, and yes. When I dev'ed PHP full time I spent a lot of time in PHP forums and it's astounding how many problems people had that was nothing more than this.
Dinah
+11  A: 

This is more of a best practice, but it's definitely the answer to much frustration for new developers - omit the closing tag ("?>") for PHP Files. It is not required by PHP, and omitting it prevents trailing whitespace from being accidentally injected into the output.

Jonathan Kushner
Omitting it can cause its own problems [plenty on SO about it]. Making sure you actively prevent typing extra whitespace etc after the closing tag would be much better practice.
Peter Spain
@peter I've never had such a problem.
Justin Johnson
this is strongly recommended by CodeIgniter framework
andyk
When people suggest things like this, it just feels like a hack. What if the behaviour differs in a future version of PHP. Follow the PHP documentation!
Sohnee
@Sohnee, here's what the PHP documentation says about the optional closing tag - http://php.net/manual/en/language.basic-syntax.instruction-separation.php : "The closing tag of a PHP block at the end of a file is optional, and in some cases omitting it is helpful when using include() or require(), so unwanted whitespace will not occur at the end of files, and you will still be able to add headers to the response later. It is also handy if you use output buffering, and would not like to see added unwanted whitespace at the end of the parts generated by the included files."
d__
The IDE's I've used go crazy if you omit the closing tag. Some of them even highlight the entire file in the same color making it unreadable. I've yet to see anyone editing the files accidentally inject whitespace yet...
Lotus Notes
+9  A: 

Aware of uninitialized arrays

foreach($customers as $customer) {
    $customerIds[] = $customer->getId();
}

Always do

$customerIds = Array();

Before loop, it saves you a lot of debugging time.

mindwork
Actually, I've found some problems doing this, as the templating engine I was using would treat an empty array as if it had one empty element in it. I agree with the principle, but in this case I would prefer $customerIds = null;
Jon Grant
While instantiating as null works, I would say that templating system is wrong.
Justin Johnson
$customerIds = Array() - defines that variable must be treaded like an array. while ... = null is variable. imho array is more readable
mindwork
A: 
  • create a secure_str function that you can use throughout the site
  • create a template system. the whole idea behind php, imo, is to develop a cms that you can use on any site.
  • I personally have blocked exec() and such functions
  • php server can only use certain folders
  • put my /source folder in a secure location
  • thrive to learn the best practices and refactor your code accordingly. i.e., i'm in the process of PDO-ing my code
  • i'm a big fan of modularizing code, it's easier when you update your client's websites
dassouki
A: 

Having all the page functions return a string, which is concatenated and then printed from a massive if-then-if-else-else-if..... block, rather than splitting it up properly and dropping out of PHP when possible (Portable Hypertext Preprocessor.)

Richard Whitty
+2  A: 

Not escaping the output of any variable that was brought over get POST or GET using htmlspecialchars(). This is to prevent XSS.

Knix
+2  A: 

Using json_encode() prior to version 5.2.0.

Randell
+2  A: 

This one

var_dump(0123 == 123);

outputs false because 0123 in the comparison is translated to octal because of the leading zero, while

var_dump("0123" == 123);

outputs true because "0123" is converted to integer 123

Randell
+7  A: 

Never compare floats for inequality.

var_dump(0.7 + 0.1 == 0.8);

outputs false. No kidding.

This is due to the fact that it is impossible to express some fractions in decimal notation with a finite number of digits. For instance, 1/3 in decimal form becomes 0.3.

If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.

Source: PHP: Floating point numbers check out the warning part

Randell
Wow, crazy. Do you know the exact reason?
DisgruntledGoat
This is not PHP-specific. Computers do floating point math completely differently than humans.
Alex
usually, you can compare this with:var_dump((0.8 - (0.7 + 0.1) < 0.001);
mfolnovich
+9  A: 

Incorrect testing of strpos / stripos

if (strpos('Needle in a haystack', 'Needle')) {
    echo "There's a needle in the haystack!";
} else {
    echo "There's no needle...";
}

This will output "There's no needle...", as strpos will return 0, which is interpreted as false.

Should be:

if (strpos('Needle in a haystack', 'Needle') !== false) {
Jrgns
+2  A: 

Googling for every answer

One of the first things everyone should learn is to read the documentation, it's not that hard. The docs are excellent and answer almost every question.

I heard from several newbies independently that the docs are hard to understand, apparently the function signatures look too confusing. I half blame this on the dynamically typed nature of PHP, which makes people not think about variable types and gets them confused when confronted with something like:

string chunk_split ( string $body [, int $chunklen [, string $end ]] )

When all they are used to see is:

chunk_split('my text');
deceze
I actually find it easier to search the doc than google. Especially if I have the php search installed in firefox's search bar.
Justin Johnson
How about "Posting on SO for every question"?
Artem Russakovskii
I find that Google brings php.net to the top of the search results for many PHP queries. If you type "php <function>" into Firefox's address bar, you're almost guaranteed to be taken to the correct page on php.net.
DisgruntledGoat
Have to disagree here -- google is still usually the best choice.
Dinah
@Dinah: Not if you're using it the newbie way: Finding usage examples for functions because you're trying to avoid reading the documentation.
deceze
A: 

Forgetting to add break; at the end of each case for your switch statement (assuming, of course, that you did not intend to execute case after case).

Knix
Nothing to do with PHP really, is it?
Artem Russakovskii
I'm not quite sure what you mean by that. Switch statements are a part of PHP.
Knix
+3  A: 

I often find that the biggest mistake with PHP is worrying too much about the difference between single and double quotes, or the difference between print and echo when you should be worring about the loop within a loop that's actually taking up most of the processing time!

PHP coders are experts as the incredible detail of a tip or trick (how many discussions have we seen about ++i instead of i++, while at the same time, they're counting the length of the array in each loop, which takes more time than ++i and i++ put together!)

Sohnee
+3  A: 

PHP references != C pointers

With PHP reference passing, what f() is passing to g() is a handle that will let g() change the assignment of one of f()'s local variables. This sounds almost the same as a C pointer, but in C, g() doesn't change the assignment of f's local variable, it changes the contents of the memory location it points to.

I found this quite disturbing when I got it for the first time, firstly because my local variables were a lot less local than I thought they were; and secondly I had trouble with the very idea of functions having access to each others' local variable scopes, which I wasn't familiar with from other languages I'd used... what languages do have that feature?

Another C habit which doesn't apply in PHP is passing pointers around to avoid memory being duplicated. As far as I understand, this isn't necessary, as the PHP interpreter uses copy-on-write tricks to avoid reassigning memory until it has to.

There was a very good article on this, maybe in the PHP Architect magazine, with boxes and arrows explaining step-by-step what was going on in a program execution. If somebody can remember where that is and can find a URL for it, can they append it here?

d__
+1  A: 

Saving as UTF-8 (with BOM)

PHP will not throw any warnings or errors but you will have strange "markup" objects (appearing as white lines in the DOM tree inside Firebug) on the page. If you construct the page using multiple templates they appear at the places where your template starts. Random margins and whatnot will haunt your dreams...

Spaces before <?php

If you have spaces before any <php tag in one of your files, you will not be able to send headers or set cookies. You will however get a decent warning if you have the correct error display settings on your development system.

kbeyer
+1  A: 

Use a Framework. There are a lot of very good and excelent php application frameworks out there, to name a few YII, PRADO, LIMONADE, SOLAR, CakePHP, Symfony, CodeIgnite .... [PLACE YOURS HERE] ... do not reinvent the wheel (again ... and again ... and again).

Do your homework and identify which one better suits your requirements/likes and go start using it. It will save you tons of time.

+6  A: 

Calling a method just because method_exists() returned true. Note that just because a method exists does not mean it is callable. method_exists returns true even if the method is protected or private.

Randell
A: 
  • Writing functions that duplicate built-in functionality instead of learning the language.
  • Attempting to write a CMS or Blog Engine or whatever and creating your own mini-framework instead of using something like Zend Framework, Code Igniter, CakePHP or any of the others available.
  • Not escaping user input.
  • Bad, or inconsistent, code formatting.
  • Constantly trying to support PHP4 or refusing to upgrade to PHP5.
  • Mixing design and business logic.
  • Improper use of OOP.
  • Setup error logs. If it breaks, log it!
  • Setup error emails. If it breaks, email it to your self, especially if it breaks on the production environment!
  • Profile, profile, profile, profile and then profile some more.
Steven Surowiec
A: 

Copying online examples like:

$res = mysql_query( "select from persons where name = $name and password = $password" );

Or things line:

$output = "<b>$name</b>";

Or even:

$email = "From: <$from>\n"
       . "To: <$to>\n"
       . "Subject: $subject"

Basically, everywhere you combine strings from multiple sources, the variables need to be escaped. That includes:

  • SQL queries
  • HTML strings
  • url parameters
  • regexps
  • generated JavaScript
  • shell commands
  • email headers
  • outputted http headers,

and so on.

Otherwise, You can easily login with strings like 'password' or 1 --, inject HTML to read user login cookies, or add new SMTP/HTTP headers by adding a newline character in the input..

As solution, there is:

mysql_real_escape_string()
htmlentities()
urlencode()
preg_quote()
json_encode()
escapeshellarg()

Ideally, for database queries I strongly suggest to avoid the mysql_...() functions directly, and use - or build - a wrapper library around it. Something which offers an API line:

$rs = $db->select( "select from persons where name = ? and password = ?", array( $name, $password ) )
foreach( $rs as $record )   // $rs is an object which implements the Iterator  interface
{
  ...
}
vdboor
A: 

If you use a reference in a foreach loop, then you need to unset it afterwards.

Ie.

foreach($results as &$result)
{
   ...
}

unset($result);
Dan