I've learned from Stack Overflow that some of the code I've been writing so far is considered "bad practice". Like using tons of global variables, create_function()
etc. I'm curious to know more about this.
Post examples of bad practices in PHP and why. Please only post one bad practice per answer, this way the worst can get the most votes.
for($i = 0; $i < count($array); $i++)
God kills a kitten every time you call count()
inside a loop.
foreach
. - NikiC
foreach
normally is even faster than for
loops, because it can make use of PHP's internal implementation of hashes fat better. - NikiC
foreach
isn't the issue here--count()
inside of a loop condition is. - Jeff Hubbard
foreach
is slower. In any reasonable real-life case, it will always be faster. But yes, counting in a loop's bad too, you got my upvote on that ;) - NikiC
for(var i = 0; i < somearray.length; ++i)
? - Alex
$arr = array_fill(0, 1000000, 1); $count = count($arr); $time = microtime(true); for($i = 0; $i < $count; $i++) $arr[$i]; $end = microtime(true) - $time; echo "Using \$count took $end microseconds\n"; $time = microtime(true); for($i = 0; $i < count($arr); $i++) $arr[$i]; $end = microtime(true) - $time; echo "Using count() took $end microseconds\n";
. On my system, I get an order of magnitude of difference, and it only increases as I increase the size. - Jeff Hubbard
for (var i=0, ii=somearray.length; i<ii; i++) {}
- Mark Eirich
Failing to filter/escape database input or screen output when submitted by users:
mysql_query("INSERT INTO table (name, email) VALUES ({$_POST['name']}, {$_POST['email']})");
This is downright dangerous.
pg_query_params()
](php.net/pg_query_params). - ThiefMaster
Using the @ modifier to suppress warnings & errors, particularly when talking to a database. The proper way is to make sure errors aren't displaying on screen, but are being logged where you address them.
@mysql_connect(...)
@
is a valid and good thing to do. - NikiC
@
is not only valid, but good (as you say it)? I wasn't aware that it's ever good to outright suppress errors... - ircmaxell
file_get_contents
throws an error in case it gets back a header like 404 or 500 (for I don't know what reason). I obviously don't wont my error log to be cluttered with these messages, so I suppress the error and use $http_response_header
to handle each error appropriately. - NikiC
@
there and using $http_response_header
is a much better approach to the problem, than registering an error handler, executing the statement and then unregistering it again. Or is there any hidden benefit of doing this? - NikiC
@
there and hope that everything will be fine. Registering a handler that will convert all errors to exception is not an option for a production site (if I correctly understand what you're doing). I don't want my clients to get white pages, just because some silly variable is not initialized. I want that variable logged, so I can fix it, but I don't want an Exception to be thrown. - NikiC
@
: Ashley at semantic dot org had this to say in the PHP documentation comments for unlink
: "you should use @unlink
rather than testing with file_exists
. The former is atomic, whereas the latter can break if you can't guarantee only one process will try to delete a given file at a time." - Stephen
fopen()
is the worst culprit. - staticsan
@unlink
looks like a viable use-case, IMO. - Stephen
trigger_error()
it for example). And for mysql_query()
you want special handling anyway so you can log the offending query. - ThiefMaster
Not indenting, in my opinion.
function awesome($someone){
if($someone == 'you')
{
echo 'it is you';
}
else
{
switch($someone){
case 'him':
echo 'that guy';
break;
case 'her':
echo 'that girl';
break;
default:
echo 'someone else';
break
}
}
}
It's just impossible to read if you have to update someone's old code as I do all the time in my horrible job.
break
without semicolon? - Benoit
Mixing business logic with presentation logic. This has its roots in PHP being in the beginning pretty much a templating system for HTML. Unfortunately you still see a lot of code like:
<?php session_start(); ?>
<html>
<?php connectToDataBase() ?>
<head>
<?php doSomeStuffNotRelatedToDisplayingHtmlAtAll() ?>
<table>
<?php $result = mysql_query()
while($row = mysql_fetch_assoc($result)) {
if($row['condition']) {?> <td></td> <?php }
}
?>
</table>
...
Using register_globals = On
. This results in big headache. http://php.net/manual/en/security.globals.php
register_globals = On
for your code to function. - Michael
This *feature* has been DEPRECATED
- Josh
Worst practice ever?
echo "$var";
It really hurts me when I see it somewhere.
echo "this " . $var . " that";
- Surreal Dreams
echo "Blah: {$var}";
I think it makes the code prettier :) - Alex
echo "this $var that";
? - gnur
echo "this $var that";
bad? - Kenny Cason
echo 'this ', $var, ' that';
- gnur
echo "this " . htmlspecialchars($var, ENT_QUOTES) . " that";
;) - MiffTheFox
$var
has already been preapred to use within HTML? Or that the string will not actually be used as HTML output at all? ;) - Mchl
' . . '
will take more time than it will save during entire lifetime of that code is pretty convincing argument for me not to do it. - Mchl
print "Here it gets {$slightly} worse";
- twmulloy
print("{$var}");
? - twmulloy
"$var"
, always to make sure it was a string (though I prefer (string)$var
). I always left a comment on why I did this. - staticsan
print
+ braces + interpolated variable + pointless interpolated variable... - NikiC
echo "$var is blabla and $ho is something else";
is just as fast as echo $var . ' is blabla and ' . $ho . ' is something else';
See the results at gnur.nl/speed.php - gnur
echo $var;
works just as well and looks much better. - gnur
Simple:
magic_quotes
[1] being turned on on.
Why? Because escaping cannot be done automatically. People think they are safe since it's on, and wind up with \
in their database, or major vulnerabilities because it doesn't work how they think it does...
commenting the obvious
// return false
return false;
using eval()
too many arguments, just pass in an object or array
// too many arguments
public function doSomething($arg1, $arg2, $arg3, $arg4, $arg5, $arg6)
{
...
}
// better, also put type and maybe some comments on usage for others.
public function doSomething(array $args)
{
...
}
not quoting array keys
//bad
echo $yourArray[someKey];
//good
echo $yourArray['someKey'];
functions that do too much, "Swiss Army function (?)"
//bad
public function someFunction(string $arg)
{
switch($arg)
{
case 'update':
...
break;
case 'delete':
...
break;
default:
...
break;
}
}
// good, separate functions-- keep it clean and simple
public function update(){...}
public function delete(){...}
public function someFunction(){...}
doSomething(array(1, 2, 3));
and doSomething(array('some str!1'));
- which one is valid? I would not recommend replacing
method params with an array. - fwielstra
doSomething($arg1, $arg2, $arg3 = true, $arg4 = null)
. where you're having to set defaults to the argument to not break any reference to the function when it had 2 arguments. in this case, should really revisit the function and consider maybe passing certain arguments as an array. the point i would like to make is that when you're reaching the point where you have many arguments that you should reconsider rewriting your function, ~6+ arguments is my threshold. - twmulloy
I can't stand it when people abbreviate variables.... avoid abbreviating to make your code more readable.
$ml = 123;
for($x = 0; $x < $ml; $x++) {
//code here
}
You have NO IDEA what $ml is... why not just called it $minimumLines
or something where a developer can look at your code and know what that value is.
$ml
I immediately though of minimum lines :D But generally, you raise a valid issue. Though one should not exaggerate. $i
definitely is a far better variable name than $iterationKeyStorage_integer
. +1 - NikiC
Some of the things you have been told to be "bad practices" are just memes gone awry and overgeneralized on Stackoverflow. Here are a few antithesis:
Effective PHP Cargo Cult Programming [1]
(I'm so going to get downvoted.)
[1] http://web135.srv3.sysproserver.de/milki.erphesfurt.de./EPCCP.pdfFailing to escape user submitted data:
echo $_POST['key'];
htmlspecialchars()
. - Nyuszika7H
Believe the user is your friend and he'll never try to hack you.
Remember:
You HAVE to assume your visitor is a maniac serial killer, out to destroy your application. And you have to prevent it
Debugging and writing code without
E_NOTICE
[1] active.
Why: A fair number of developers (including those who should know better) think that notices are not important and hence can be ignored. However, a fair number of bugs sneak through because of miss-spellings or typos. Undefined variables are errors and should be treated as such.
Better: Always write and debug code with all errors levels running (E_ALL
). Only on production servers should you limit the error level as appropriate.
[1] http://www.php.net/manual/en/errorfunc.constants.phpMario's edit to add a counter-argument: That's plain wrong. PHP is a dynamic language and dynamically typed. Handling undefined variables is one of the core features, as it's optimized for receiving forms. Notices are debug messages and not errors (potential problems != faults). They should be turned on in the development stage. They should neither be ignored, nor habitually suppressed, but evaluated in each specific case (there are syntax notices and informational notices, should not be lumped together). Using
isset($_GET["var"]) ? $_GET["var"] : NULL
is functionally eqivalent to@$_GET["var"]
- everything else is a lie. Or a micro optimization (which are only ever appropriate in loops).
php.ini
has E_NOTICE disabled and has so for a long time. This leads to a lot of PHP code that throws many E_NOTICE errors because the developers never turned it on. - staticsan
goto
is supported by the language as a core-feature). Plus, I'd argue you shouldn't be doing $foo = isset($_GET...
or accessing request variables directly in any case. Instead, you should use some kind of request class/object that abstracts away things like magic_quotes handling and character set conversion of the input stream... - ircmaxell
if (variable === undefined)
. It's practically a datatype. - Stephen
When i see this i wish i never started using PHP :
function something ($optional_var1 = 1, $optional_var2 = 2, $must_provide_var) {
...
}
Or another thing is when people abuse dynamic typing : $a = "1" + "2"; and expect integer
E_STRICT
barfs on that though. - Mchl
something(,,$someVar)
? - mc10
Parse error: parse error, expecting ')'
- Martin Dimitrov
call_user_func
accepts a list of params. how can you skip the first 2? Not possible. call_user_func_array
expects the parameters to be passed to the function, as an indexed array. So we might think we can do call_user_func_array("something", array(2 => 3));
to provide just the third argument but actually the keys of the array are ignored and reordered starting from index 0. - Martin Dimitrov
something(null, null, $someVar)
? - mc10
null
is an absolutely valid value, so basically your code is calling the function something
with three arguments. This means that $optional_var1
and $optional_var2
will end up with value null
and not use their default values :) - Martin Dimitrov
Whoa, this list could grow really really long as ways of screwing things up are vast.
From those commonly encountered:
... I could go on and on. I've seen terrible things :/. Just use your brain and get to know your tools and you are going to be OK
while ($row = mysql_fetch_assoc($query)): [do another query here]
all the time, when logical. Or am I misunderstanding? - Andrew
Using eval() [1].
Always remember: eval() is evil()
.
Just like this guy:
[1] http://php.net/manual/en/function.eval.phpFunction evil() doesn't exist
so I guess you're wrong about this alias. - Mchl
sizeof()
is an alias for count()
) Apparently to see it as funny, one needs to have his brain as screwed up as I do) - Mchl
function evil($code_str) { return eval($code_str); }
- Nemoden
Passing every input inside a 'sanitizer' function, without care about what that input mean.
$an_aspected_integer_id = my_super_mega_iper_sanitizer_function($_GET['id']);
// (int)$_GET['id']; looks bad!?
Using a lot of if-else short declaration
$myvar = $x == $y ? $y <= $z ? $y : $x != $j ? $k >= $j ? $k : $j : $x : "omg_i_did_again" ;
Writing a 30,000 line Web application, with:
I could go on and on and on.
Maybe I could sum it up simply and generically: Writing it ugly, bad. Not bothering to incrementally improve: just plain offensive.
People should stop using die()
like, a typical example die("MySQL error:".mysql_error())
use PDO
and try/catch exceptions
trigger_error()
, since it's too easy to ignore and doesn't actually interrupt the execution flow. Instead, use Exceptions or another inline error paradigm... - ircmaxell
Writing all your strings like:
'foo'.$bar.'baz'.$biz.'buz'."\n"
Because '
quoted strings are faster* than "
quoted strings.
The bottom line, write readable code. Write appropriate code. Don't worry about premature optimizations and micro-optimizations.
* Note: They aren't really faster. The benchmarks between them are quite inconsistent and both are well within the margin of error of each other.
'<a href="' . $url . '" rel="nofollow">' . $text . '</a>'
is "cleaner" than writing "<a href=\"$url\" rel=\"nofollow\">$text</a>"
. Though it is already very rare that I need to write something like this. I use a Template Engine and that normally makes any manual work with strings unnecessary (well, apart from extensions for the TE maybe...) - NikiC
Really, I'm not making this up...
if($_SESSION['username']=='name-of-lead-developer')echo "<a href='".$URlBuildString."' target='_blank'>Here it is</a>";
This wasn't git-stashed, this was live, in production, code.
echo
, return
, ... with braces, like return(5);
eval
instead of PHP dynamic language features, like $function()
or new $class
(eval
is okay to use, but no if there is a native possibility to do it.)if ($a == $b) { return true } else { return false }
$funcion
and $$variable
are just as bad as eval
. - Billy ONeal
switch
/case
list. - NikiC
$function
is okay if you're using it as a callback (and nowhere else!). But $$variable
almost always needs to go. Never said anything about new
being bad. $$variable
is bad simply because you're encoding the name of a variable as a requirement of your program logic. Therefore instead of being able to use a simple rename tool (or even find and replace), if you pick a bad name you're stuck with it. - Billy ONeal
${'_'.!$_=getArray()}[0]
as the shortest way to dereference a function. Admittedly I can't even remember when I used $$variable
the last time, but I'm very confident that I have used it several times and did so for a reason. - NikiC
$$
ever crosses one's mind, that's a code smell in and of itself. - Billy ONeal
$function
where the variable is a generic object that implements __invoke()
? - ircmaxell
$$variable
is good for, but am confident that there are good use cases. - NikiC
$
, but apart from some edge-case php-preprocessing unit tests I found only two usages in Doctrine. And they didn't seem very legitimate. I will remove the $$variable
example from my post. - NikiC
__invoke
is) Only place I see that making sense as a callback, but I can't say I've ever used __invoke
(no PHP 5.3 features in anything I've done as of yet). - Billy ONeal
Not understanding the different ways
PHP handles references
[1] and blindly using &
for creating references with objects.
Reason: passing around variable references can get quite messy and lead to very hard-to-debug errors.
Instead, just let PHP internally handle the reference for you. It's far cleaner, and far easier to debug when something goes wrong...
[1] http://stackoverflow.com/questions/3611986/in-php-can-someone-explain-cloning-vs-pointer-reference/3612129#3612129Not PHP specific, but uninitialized variables:
if ($foo) {
$bar = $foo;
}
echo $bar;
Why: it makes debugging much harder while making the code less clear.
Instead, always pre-declare your variables (where you're going to conditionally assign them):
$bar = '';
if ($foo) {
$bar = $foo;
}
echo $bar;
Writing PHP4 style classes - no visibility modifiers so all member variables and functions are public.
Not writing unit (or other types of) tests. That usually also means little refactoring (it's hard to refactor code without tests).
Overusing magic methods - e.g. __get(), __call(). These methods can do cool tricks but also obfuscate class.
Premature sanitization of input values:
$i = $_POST['key']
if($i == '') $i = $_GET['key']
I would have to say ... almost anything on http://php.net (the "documentation section in particular).
While some people claim it's a great source for information (and there is lots of information) -- the problem is there is lots of bad information mixed in with the good -- but unlike SO, where the same applies, there is no real moderation. This site has done as much to pollute and destroy new developers -- if not more -- as it has done to encourage good programming. None of the "competing" languages (Perl, PHP, Ruby, etc.) can claim the same.
Reading the "accepted" user comments (often complete with code to avoid) makes me cringe.
Open Source libraries without PHPDoc (or equivalent) tags, so there's no API documentation and you have to wade through the code itself to find out what it actually does... only marginally worse is PHPDoc tags that don't match the actual code.
I know it's hard work keeping tags up to date, and boring as well; but it's a necessary part of maintaining any open source library.
Using variable variables [1] in all cases:
Why? You should use an array instead:
for ($i = 0; $i < 10; $i++) {
$var = "foo" . $i;
$$var = $i;
}
[1] http://php.net/manual/en/language.variables.variable.phpIt's not the worst thing ever, but I really do hate it when people put the "then" part of an if
statement without braces. Let me just show an example.
if($this == $that)
doSomething();
I hate this. It bugs me. Please use a set of braces...
if($this == $that)
{
doSomething();
}
It's small, but it really makes a difference to me. I go hunting for a closing brace that's not there. Put an else
in the mix and it's actually worse.
if($this == $that)
doSomething();
else
doSomethingElse();
//this drives me nuts
if($condition) continue;
. - Mchl
if
-else
version where they hide the else
from sight. - larsmans
{
i put it on the same line as the if
statement, but other than that i still like the brackets. And plus, when you want to debug, it's easier to add extra code to the "then" block if the brackets are already there - Kenny Cason
if(true == $var)
unset()
enough select * from table
when only need id $object =& new Something()
in 5.x<div><?php $rows = $DB->query('....'); foreach.... ?></div>
data handling in the viewwrapping lots of code in ifs like
if($true){..code...}else{error(); return;}
could be
if(!$true){something(); return;} ...code...
the list of bad practices is 100x bigger that best practice :)
unset
? - Billy ONeal
unset()
slows down your execution time. it's more a convenience for removing a key=>value pair from an existing array where array keys or values matter... like when using Zend Framework database $this->update($data, $id);
where $data is an array and the key=>value must precisely match your db schema - twmulloy
select *
is a good thing to consider, but that's more an SQL consideration than PHP. in its defense though, select *
is acceptable depending on its usage (mostly if you're return all fields anyway). Definitely do want to restrict the results of a query for frontend queries (for obvious performance and security reasons) but for backend (eg. content management system) it's not always convenient to list out some large number of table fields to return, granted more secure but it's generally the case you need all field's data in a backend type system. - twmulloy
unset()
isn't a huge hit on performance, but if you have an array with some x (large) number of post variables and you only need to remove certain keys, it's surely more convenient to unset($post['action'], $post['id']);
than to essentially dupe the array with x - 2 keys. - twmulloy
unset
is implicit whenever: 1. you return from a function (local variables get unset), 2. your script terminates. Unless you have long running PHP scripts somewhere you're going to hurt performance using unset
, not help it. - Billy ONeal
Using a CMS with deprecated functions in it...
I feel a little bad about submitting this as it is a real example that I found in a project; however, hopefully this will help someone else...
// accept DateTime or String as date
$this->birthDate = is_object($birthDate) ? $birthDate : (!empty($birthDate) ? new \DateTime($birthDate) : null);
Problems with the above code:
the & before variable in function parameters list is one of those - when you call it without variable it produces error
&
is for references and it is sometimes desired to pass a variable by reference? Normally people don't use it, just to suppress error messages. - NikiC
&
with the @
operator. - eykanal
foreach($myArray as &$value){...}
is better than the alternative-- creating another array. - twmulloy
$GLOBALS = $_REQUEST
(Yes, that's from production code) - Thomas Ahle