Signs of Crappy PHP Software
phpfreaks.com
phpfreaks.com
Sure, I could make a function look_ahead. Or I could pass the look_ahead value everywhere. Or I could have a DB setting look_ahead. But why would I make it any more complicated than I have to? Globals work very well when used as application preferences that rarely change but could.
Also, $company_address is a global. We're moving to a new building. That's a single variable I need to change. It'll change across every single report/printout/auto-generated PDF. And $db is always a global in my code. I've been coding for 10 years and not once have I had any issues because of that.
edit: Why the downvotes? I thought the proper etiquette here was to post a reply. It's not like I said something offensive.
The problem is PHP's scoping rules don't lend themselves well to this kind of thing. If you were to use a PHP library, for instance, that used the same practices you are using, say a parser, you might find that depending on the include order $look_ahead now means something different.
This kind of "all I did is include a file and everything broke" error is really annoying to find. Constants are useful for this, because at least it will flag an error/warning. Now that PHP has (is getting? It's been a while) namespaces, you can limit its scope and make it less likely to collide.
For the case of databases, it is fairly common to use PHP's builtin functionality to reuse the database connection without having to rely on a global handle. Again, if someone shares your practices the variable could end up getting stepped on. Throw in autoloading and maybe it only occurs when some confluence of events leads to some specific file being included that usually isn't.
So there's a reason for concerns about globals. Putting everything in a singleton with static variables is not really different, but in my mind mostly just namespaces it so your globals don't get in trouble with someone else's globals. I've found in practice that I've only rarely needed global variables or singleton classes housing global variables. This probably largely depends on your style of coding.
I haven't written in PHP for years now so if I've gotten any details wrong please forgive me. Hopefully though you can see some ways that frequent use of globals can lead to hard to find bugs. A lot of programming techniques (OOP, closures, for example) are designed to help pass around state in ways that don't lead to these kinds of problems.
1. As another responder mentioned, some of your examples sound more like named constants than global variables.
2. Not using global variables is just an example of a more general rule: variables should generally have as small a scope as possible. The wider the scope, the harder it is for someone reading the code to understand how the variable is used, and the riskier it becomes to change that code.
3. 20K lines and 100 files is a small project, in the sense that one person can be familiar with all of it. While limiting scopes is still beneficial for readability on that scale, it really starts to pay off when there are multiple people working on a project and no one person knows the whole thing.
I see parallels with modular design generally here: although it is helpful even on quite small projects, you can get away without it up to a certain point. Beyond that point, you really start to suffer from having different parts of your system tied together in ad-hoc, uncontrolled ways.
The problem is passing state around in global variables. Any code can (and readily does) modify the state which is the problem.
You're right that look_ahead/company_address would work as a define("CONSTANT") better but honestly, I hate not putting a $ sign in front of a value (constant or variable). I am more prone to missing a $ than redefining a global. I don't remember ever having the problem of redefining a global unintentionally. I can give 20 examples of when I missed a $ symbol and PHP gave me an error/notice. If PHP constants had a symbol akin to $ (say #), I would definitely use them. But I just feel dirty typing "echo LOOK_AHEAD;" in one place and "echo $look_ahead_partial;" in another because I don't want to get in the habit of putting $ in some places and not putting them in others.
With "echo $look_ahead" there's no indication of that, and in the future another dev might add something that modifies $look_ahead's value and break other parts of the code, making for sneaky bug (which incidentally is why non-constant globals are so disliked ;))
The problem with not using a constant where it's necessary is that you're communicating the wrong thing to someone else or even yourself 6 months down the line. You might want to re-evaluate what you see as "ugly code" especially if it changes how you write the code.
Personally I global the database wrapper, the user session class and config stuff (which yes I could use constants). Although if the code base I'm referring to wasn't 95% me coding I would probably consider using global less.
An example http://pastebin.com/Gf4fzHn2
This way you could lookup the parts of the code that use your global stuff, because they have to get it though the static method call.
GlobslSingleton::set('Something Meaningful',$unique_var);
and
GlobslSingleton::get('Something Meaningful');
I don't think this is complex at all.
This doesn't force you to use $unique_var as a variable name(I know it can be worked around). One of the biggest problem with globals is that they can crash and burn if someone else's code purges it by accident.
Also my pattern allows 'load on demand', I can incorporate it in if I'd want to, your does not.
Also, what happens when one of your new developers comes along and modifies the value of that in some unexpected way?
The last time I ran across a "core" directory, there was all kinds of application-specific code in there, all rolled up in switches and stuff - if you wanted to develop something using the "core", you had to go through about fifty files and add your own cases for your site. It was awful.
But -- just to be a hypocrite -- my number one sign of crappy software is deeply nested if statements. The more you branch the more loose ends you have to juggle up in the air as your solution is formulating.
Just to be productive, allow me to share a tool that has evaded me for many years: assertions. No really!
function GoodFunction
assert(good_state)
do_one_thing_very_well
Reads better, and leads to better design than: function GoodFunction?
if( oh_right )
do_one_thing_very_well_slightly_differently
else
do_one_thing_very_well
In the first the oh_right condition just doesn't fit in and you start thinking about what caused this logical branch in the first place. In the second we feel little hesitation to add an elseif if need be, further increasing complexity.To give credit where its due, "Coders at Work" has actually changed my coding style. I highly recommend the book.
Escaping is not sufficient to prevent SQL Injection Attacks. You must used Parameterized Queries.
Do you have an example or idea of how a SQL injection could occur despite using http://php.net/manual/en/function.mysql-real-escape-string.p... ?
I did this one recently, because I needed to use some PEAR modules that produced notices under E_STRICT. I somehow think all of these have similar exceptions.
If you want all your notices logged into an error file as well as the fatal errors, and the server defines the default error level as E_ERROR, then you'll have to override the default error level.
Horrible article.
(And I'm not a PHP hater, my company writes 90% of our stuff in PHP, and WP works great for blogs and brochure sites. But it's nasty underneath that pretty UI.)
(sorry, I couldn't resist)