PHP is weak, crappy, and encourages people to write terrible code. But … if you know what you’re doing, all of those might actually be really really good things.
(by the way, if you haven’t already, you should read Eric Ries thoughts on PHP…)
“Best Practices” are one of the best guides you can get for sailing between the Scylla and Charybdis of “crap code” and “over-engineering”.
So I’d like some, please.
Perfection
Anyone with zero computer science education can write crap code; that’s easy. At the other end of the spectrum we have the people who write Perfect code.
Anyone who writes perfect code is either:
- a liar
- too rich to work for money
- works in an industry that is immune to marketing
…because marketing is always a cheaper and more effective way of making more money, compared to writing “perfect” code.
(disclaimer: in the past, I’ve been the first, and lusted after the third, but never quite managed the second)
The skill that marks out the difference between a competent programmer and a good (or great) one is knowing which of the language’s strengths to play up, and which to play down. Sometimes, the fact that a language allows extremely sloppy code is a very good thing. e.g. most scripting languages – especially any that run on an “intelligent” runtime like the truly Awesome JavaVM – where being grossly inefficient is not only “OK” but in fact “doesn’t happen”, because once it notices what stupid thing you did, the runtime silently recompiles everything into decent C code while you’re not looking.
So … how the heck do you win?
You look for Best Practices, language-specific (and domain-specific, but that’s obviously of more niche value).
I recently did two moderate sized PHP projects. I figured: OK, so PHP is very easy to hack code together knowing very little about the platform (and I’d been using my Perl experience most of the time when writing PHP) – but Perl showed us that Best Practices are damn useful and not too hard to stick to. There must be lots that the PHP community has worked out by now!
(as Quake3 would say, in a bass rumbling voice:) Denied!
Um, no. Apparently not. Google – with *considerable massaging* – threw out a few dozen attempts from different people.
There was some interesting stuff in there. I found some obscure bits of standard and near-standard extensions I hadn’t noticed previously when reading the manuals.
But, to be honest, nearly all of it was a waste of time – it simply wasn’t PHP specific. Telling people that they should “document function headers” is meaningless as a “PHP Best Practice”. It’s not a PHP Best Practice, it’s a general programming practice. You can find it in *any* textbook (and if you don’t know stuff like that, I suggest you go read a few key books like Code Complete, for starters).
There’s also – and this quite upset me – a lot of BAD practice out there being passed on from PHP programmer to PHP programmer and hailed as if it were “a good thing”, when often it’s about as wise and valuable as staring down the barrel of a loaded gun so you can see better while you clean the inside.
What is PHP good for? Why?
I have recently realised that the majority of PHP programmers cannot answer these questions, and that many of the rest aren’t great at answering them either. If you can’t answer them, you can probably never be a great PHP programmer, and it’s debatable exactly how “good” you are. (/me ducks and runs for cover)
I’ve not done enough PHP to know what it does from the inside.
But I’ve done tonnes of code in “languages that are not PHP”, and I have a really really good idea what it does from the outside. And most of the Best Practices I saw for PHP were stolen from other languages (especially C derivatives) and are totally inappropriate for PHP. Because PHP is not C, and PHP programmers will never be decent C programmers, no matter how big their inferiority complex, and no matter how hard they slave to try and write “high quality code”.
Some things PHP is good at: (in no particular order!)
- It’s a Scripting Language (like JavaScript, and ActionScript, and Perl. But not like C, and not like C++, and not like Java)
- It’s intended to be trivial to read and write code; the core language is “easy for a non-programmer to grasp”
- There’s no feature in the language that requires thinking to understand what’s going on, by design (unfortunately, some of the libraries broke this. c.f. the great Register Globals disaster too)
- idiots can code effectively…
- …which means that highly intelligent individuals who were never trained as programmers can ALSO code effectively, with minimal pain and suffering
- …and that changing (modifying, fixing, extending) other people’s PHP code is trivially easy
- There’s no compiler. No cached builds. What you see is what you executed, literally.
- It’s very easy to grasp two of the three most important structures of a web application (the third one is “data flow” – PHP sucks at that one). The two it does great are “program logic” and “program inputs + outputs”; together these sum to “execution structure”
- One page == one source file : you read an output (FORM or A link) to an HREF? Or a page is crashing in the browser? You know exactly which file to find it in
- One source file == one page : all the logic (PHP tags) and the input/output parts of the user-interface (HTML tags) are in one file. So you can’t make assumptions and the resulting mistakes
- It’s a context-free language. This is a by-product of being so tightly integrated with HTTP/HTML. It is one of the things that “real” programmers sneer at PHP for – how can you have a context-free language that isn’t Functional and expect it to be any good? That’s some kind of mutant offspring of Fn and Imp that ought to be strangled at birth. Or … maybe … just maybe … it’s one of PHP’s greatest strengths (although admittedly it is a mutant offspring too)
- When a single source file is executing *there cannot be any other context* (except for the Session, sadly – but this is a strange piece of context-that-is-not-context because of all the rules about what can and cannot be in a session)
- Oh, OK: there cannot be any context from other threads, other users, other simultaneously executing code. This is one of the hardest, nastiest parts of all systems languages, the multi-threading stuff. And PHP completely wipes it out as a problem. Nice.
- All source files – hence all user-interaction points – are (semi-)atomic by definition: either they execute, or they don’t (by default, if they start, they dont stop, not even if a crash-level event happens; more on that later)
- Code is mingled with presentation, but is “together but apart” because it’s guarded (in the computer science meaning of that word, sort-of) by enclosing magic PHP tags
- You can copy/paste move around code without the risk of it becoming part of the presentation code (this problem happens A LOT (and causes some awful security holes and application crashes) in some languages, especially in templating languages, even though it seems like such a simple thing. I kid you not.)
- Any decent IDE (/wave at Eclipse (download PDT to add PHP to it)) can do a lot better at second-guessing what the programmer wants to type next, thanks to the explicit context of being either “in code” or “in presentation”. Saves a lot of time, in very small pieces many times a day.
- It’s a mainstream scripting language: very very fast to write code, code is very very fast to read + understand (it CANNOT DO complex things like STL or Operator Overloading), and you can “make it up as you go along” when writing a program – it doesn’t expect you to plan ANYTHING in advance
- No declarations : you don’t have to plan your variables in advance
- No declarations = faster “flow of mental programming” : you can aribtrarily change your mind “mid sentence” when writing code, and not have to move backwards in the source file to fix a declaration as a result
- No memory management : you don’t have to plan the LIFECYCLES of your variables in advance
- Writing basic imperative code. Fast. (Imperative == “not Functional”; if you don’t know what that means, look it up – Fn programming is awesome)
- Templates for pages.
- If you’re going to use a template method (or, god forbid, “library”) in PHP, make sure that its source code looks *exactly like PHP*. Because PHP is an excellent templating language.
- This begs the question of “why aren’t you just using PHP in the first place, fool?”. The answer to which is usually “I didn’t read Adam’s Best Practices on this website and wrote long waffling PHP source that no-one could understand”, or “I have some truly stupid accomplices who will overwrite the PHP tags whenever they see them, just for fun” (anyone using DreamWeaver has almost certainly done this at some point, without even realising it; in that case, it’s not the human’s fault, it’s the fault of their editing software – DW – but the PHP programmer can’t really tell the difference, it’s the same net effect)
Some things PHP is bad at: (in no particular order!)
- NOTE: just because PHP is “bad” at something does not imply that this is a bad thing about PHP!
- OOP. PHP is poor at OOP. It’s not as bad as Objective C (which is terrible) nor Perl (which is horrifically awful at OOP), but it’s not part of the core language, and it shows.
- Actually, I don’t even know what to say here. Go on a long rant about how it should look like C++? Hmm. Not really helpful. I’ll just leave it at that. If you *really* need to ask why PHP’s OOP is bad, it’s going to take more than a couple of bullet points to explain it to you.
- It’s an “old fashioned” scripting language; it lacks some so-called modern features (many of them are older than the microchip but took a few decades to make it into mainstream programming)
- Closures? Where are my closures? : without this, it’s much harder to write self-adapting code, and much harder to write code “for other programmers to extend and alter without needing to change my source code”
- No OOP scripting : without this, OOP is a pain in the ass, taking a lot more typing, and standing out like a sore thumb inside any PHP file
- No modern Array derefence syntax : (you have to do: “${arr[‘var’]}” instead of: “$arr.var”) this makes getting data out of arrays so hard that people go out of their way not to use arrays in PHP.
- Data flow (the third prong of three mentioned in the Good bits section)
- SQL in PHP is not fun : it’s all manual SQL statements, with practically zero language-level support (check out Perl::DBI for an example of how funktastic language support for SQL can get; I didn’t say “good”, I said “funktastic”. There’s a difference)
- Distribution
- There isn’t any. PEAR doesn’t count – and if it did, well … PEAR has one of the most appalling distribution systems I’ve seen in about 10 years. The fact that I had to hack PEAR’s installer – in 2008 – just to “not crash” let alone to get it to “start installing” says it all, really.
- No package management. No bundling. No metadata. No repository (PEAR or CPAN? take your pick. I find both fine as manual systems, and frequent failures as automated systems).
- (if you still don’t believe me, find yourself a Debian sysadmin and ask them to show you Aptitude, a worn-out decade-old text-based front-end to apt. And marvel at how fantastic it is despite all that!)
- Register Globals fiasco : the maintainers killed some core features of the language in an attempt to fix a security hole, instead of just fixing the security hole
- Sadly, the core libraries now force you to use Arrays for evertying, because the language maintainers panicked over Register Globals and destroyed the whole input-sampling system.
- …which is bad because of the lack of Array-de-ref syntax in the language (see above)
A quick retrospective
If you’re a PHP programmer, I hope you’ve already noticed the implications of some of the good/bad points I hilighted.
There are things that a lot of the PHP community loves with a lack of reasoning that sometimes borders on obsession/fetishism, but which – from the above analysis – are inappropriate for PHP. I’ve tried some of these, and confirmed: whoever thought this was a good idea was either smoking crack or didn’t understand how to build web applications.
Some quick examples:
- Page Caching. You gotta be kidding me. There’s are plenty of reasons that all other languages push this OUT of the language, and OUT of the application, and most of them apply equally to PHP. Use Memcached instead, and learn to use it properly – that is, externally, not internally.
- Separating Code from Data. As a gross generalization, if you do this, you should stop writing PHP, because you just threw away most of the benefits of PHP, and you’d be better off writing J2EE from this point on – you’d get the benefits of J2EE and of the Java language AND of the Java standard libs (which kick the living daylights out of PHP’s weak set of “Extensions”) – and you’d hardly lose anything in the transfer.
- M/V/C architectures. If you do it the way we do it in other languages, then it’s equally stupid as the above point. It *can* be done, conceptually, in ways that fit with PHP – but the majority of PHP systems I’ve poked inside didn’t get that clever, they just tried to do it the traditional ways.
By The Way: I’m probably wrong on some of this. I haven’t spent much time thinking about it. Unfortunately, I’m not sure at this point which points are where I’m Totally Right, Dude, and which ones are where I’m Smoking Something Funky.
Sorry.
Too many words! Argh
I keep being told off (nicely) for writing blog posts that are too long.
In petty revenge, after making you read through the rant-tastic introductory bits (I have my Asbestos suit at the ready…), I’m splitting the rest into a separate post. I almost feel bad about doing this. Almost.
So, on to … PART 2: Best Practice for PHP Programming!
14 replies on “PHP: Looking for some Best Practices”
Hello Adam. I only have two comments. First of all, I enjoyed reading this. I am a php developer and i KNOW how bad php code can get. I have slapped myself on multiple occasions for the things i have done.
But I wanted to add that php does have closures. They are not often used and I personally have not taken advantage of them yet, but they are there. It is something i plan to investigate soon. (hopefully).
I look forward to reading Part Two.
:)
PS, I didnt realize the website was going to directly become a link, so it definitely does not go anywhere.
If you are interested or want to fix the link, i would suggest; http://www.watchhisbeardgrow.com/
It is a recent development of mine and I plan to write more and more…
Couple of minor quibbles/points.
1) Perl is definitely a scripting language and Perl is absolutely not ‘intended to be trivial to read and write code; the core language is “easy for a non-programmer to grasp”’. Which means that the definition of a scripting language needs work. PHP is a scriptling language that is also fairly approachable, but that’s not implied by the fact that it’s a scripting language. I think one important aspect of its approachability is that it falls within the mainline 3GL syntax. . meaning it’s imperative, uses braces for block definition, uses the typical iteration operators, etc, etc. It’s not forcing you to jump into functional syntax or significant whitespace just to do some scripting.
2) “Some things PHP is bad at”
It’s weakly typed, meaning it’s unreliable and requires ginormous hacks like the === operator.
Put this busted syllogism in your interpreter and smoke it:
var_dump(true == “true”); // bool(true);
var_dump(true == 1); // bool(true);
var_dump(1 == “true”); // bool(false);
wtf?
Aside: Your comment post interface requires https and Chrome hates your cert so it’s throwing up a big, red wall of ‘do not visit this site’.
@haddad
It has closures? Yikes. How did that slip past me. I feel very stupid for missing it (I looked, but clearly not hard enough; I wasn’t particularly bothered since I rarely use them myself unless it’s a language thwyb makes heavy use of them habitually).
Thanks.
I’ll go look into that some more
@sidereal
Hmm. Point taken about perl. Although i think that what I said is true in it’s own way – if I’d phrased it better. Certainly Perl was jumped on by non programmers it seemee to me in much the same way that Rexx was, and it was great for whacking out one-liners, and for “writing what you intend to do and it’ll probably “just work” but god help you if it doesn’t and you have to start debugging”.
Perhaps I just meant that perl was a language designed to write code without planning it, which tends to support non programmers since they don’t know how to plan code even if they want to?
Also: sorry about the https, it’s a design flaw in wordpress (admin login is plaintext) they have yet to fix even after many years, and the only workaround is for me to buy a cert and force all users onto https. Only I’m too cheap to buy a cert and this one is in my trust chain so it doesn’t bother me an I keep forgetting about it! When I’m ned feeling rich I’ll pony up :). Until then: I’m sorry.
Also, I had forgotten triple equals.I now automatically design my php so that I never encounter that stufff, mainly because if found nearly all scripting languages have the same or similar problem. I guess it’s a php best practice to avoid it? Thanks!
adam, I think you’re getting much closer with the ‘I don’t have to design the whole architecture before I start writing code’ angle. Technically this is more or less true of any language, but I suppose the difference is that scripting languages don’t make you feel guilty about it by reminding you with boilerplate that you should be doing it another way.
I’m not sure avoiding === is a best practice. I’d say avoiding the things that force you to use it are probably a best practice. For example, it’s common to initialize all variables to null or false and then do a if (!foo) check later to test for whether it’s ever been set. Unfortunately, there are all sorts of non-false non-null things that evaluate to false (like 0), so if your variable is intended to be an integer and you need to check for 0 later, you can’t do if (foo == 0) because null and false both match. You have to do ‘if (foo === 0)’. This is just horrifying, both to write and to debug, has no clean solutions, and is a strong argument against taking advantage of the ‘ease’ that weakly typed languages are supposed to provide. Declaring a datatype and doing some explicit casting is just not that much work.
if( isset( $foo ) ) ?
:)
Doesn’t work for a 0 test. You’d have to do
if ($foo == 0 && isset($foo))
which isn’t much of an improvement
Nod. Where I said I avoid ===, I meant I do things like structure code so that all vars are either isset check at start of execution, or immediately at the time they are set in a way that has a reasonably high chance of failure (e.g. Setting a value by Reading a DB value for a row that may or may not exist).
(one of my practices is to deliberately ignore a lot of error checking that I’d consider mandatory in other langs :) – anything that reduces readability or increases bloat)
I’m not sure about this but it seems to have worked best for me out of the different structures I’ve tried.
…forgot to add: and I switch logic flow if not set, each time, so that at any point I can guarantee all vars are set and don’t need to explicitly check them.
Sounds like a best practice :)
The problem with php being reasonably easy to grasp is that modern HTML certainly is not: Unfortunately, that badly undermines the value of languages like php.
Interesting point. Which aspects of modern HTML are you aiming at there? (I’m guessing: CSS, primarily. Personally I’ve found that both the biggest waste of my time with anything HTML and also the biggest timesaver :))