PHP
Article

Defensive Programming in PHP

By Jeff Smith

Finagle’s Law of Dynamic Negatives:
Anything that can go wrong, will – at the worst possible moment.

Lightbulb with shield inside it

What Does “Defensive Programming” Mean?

Defensive programming, simply put, is programming with the intent to anticipate likely failure points. The goal is to circumvent those likely problems before they occur. You see the problem, right? There’s something inherently difficult with the advice “expect the unexpected” and it’s made many times worse when one alters it to “expect the unexpected and try to prevent it”.

Let’s look at some practical examples.

Conditional Statements

This is one of the easiest places to program defensively, and one where it’s easiest to be complacent. In many cases while programming in PHP, you simply won’t need an “else” case.

Hypothetically, you are working on a function and need a conditional. You only have three possible states for your particular variable at this point – and you have those covered in a block like this:

if($var == a){ }
else if($var == b){ }
else if($var == c){ }

There are no other possibilities, you say, and you move on with your code. Let me stop you there, though. I know that you know there are no other possibilities. And I believe you. But sometimes (unexpected) things happen. We forget about something. We look over mistakes. We end up reusing a bit of code outside of its originally intended scope. And all of a sudden we have leaking and sometimes silent error states because there’s no catch here. Make use of else blocks. Make use of default in switches. Use them to return or log an error so that you’re aware of what’s happened, should it ever happen. It may be an extra couple of lines of code, but it’s worth it when something happens that you did not expect.

Never Trust User Input

Have you heard this statement before? Most programmers have. It is a bit of a vague, generalized thing to say, granted. But it’s true. You should never trust user input. This does not mean that you assume that all users are crazed hackers out to destroy your application with a single set of well crafted commands. There is no need for paranoia. But, you should assume that users don’t know your code. They don’t know what parameters you need filled or how long the input can be. They don’t know what file types they can upload (even if the app tells them) or what size. And occasionally, they are a bot or a hacker and they are going to be trying to run scripts in their inputs. Sometimes even from inputs behind a login wall. How do you know when you can trust things like authentication or captchas to provide a safe barrier before users arrive at input forms?

The answer: Never.

If you never trust user input, then you never have a breach because you trusted user input. See? So always validate your input, and always ensure that you’re using appropriate techniques when handling data, especially when storing it in the database or retrieving it for display. For that matter – don’t trust input at all, even when from somewhere besides your users – input validation is always your friend. Check out Survive the Deep End: PHP Security and look into using a validation library.

Assumptions About Your Code

Don’t assume anything. If the previous two topics teach us anything, it’s that we should not make any assumptions. As programmers, especially when focusing on a single project for too long, we begin to assume a lot. We begin to assume that the user knows some of the things we know. Not necessarily technical details, but functional details about the program. We assume that the user will know how big files can be because… we already know that fact. Or that they’ll know that in order for the mailing script to… but no, they have no idea about any of that. This can sometimes matter more for front-end work, but obviously you still deal with user behavior and user input in the back-end as well, so it’s worth thinking about.

Another staggering assumption that many programmers make is the assumption of the obvious nature of our functions, classes, or whatever other bit of code we’re working on at any given time. A defensive programmer will try to think carefully about not only normal documentation and describing what a bit of functionality does – but they will also document any assumptions they are making about input, parameters, use cases, or any number of other similar things. Because we are all humans, and we sometimes forget things later. We also will all most likely end up with someone maintaining, extending, or replacing our code someday. If nothing else, recall that programming is happening in a world full of technological changes. If your application is still around in several years, it may need to be updated to a newer version of PHP and lose some of its functionality, or any number of components that it interacts with may change and necessitate changes in your own code. It is very difficult to predict these things, so good comments and documentation are very important.

Tunnel Vision

Another thing that can cause us to both forget good commenting practices as well as standards is tunnel vision. Tunnel vision happens a lot to programmers. You know the feeling. You’re solving a problem, you’re in the groove. You’re feeling isolated in your own little world, with your music (or lack thereof) and you’re just coding and all of a sudden two hours have passed since you last checked the clock and you realize that you’ve written countless lines of code without any comments. It happens to all of us at one time or another, but the important thing is to, at some point, catch that and add some where appropriate.

Consistency in Syntax and Naming

Consistency is a bit of a grey area – that delves a bit more into coding standards and the like, but it is relevant to defensive programming. In PHP, there are standards that can be followed to streamline your code for others who might view it, or for your own future use. But often, no one is actually making you code to standard. However, whether you’re coding to some set of standards or not, you should at very least be internally consistent – because it will make you less error prone. This applies especially to small syntax errors that take an aggravating amount of time to return to and fix after being caught. If you always use the same spacing, the same formats and syntax, naming conventions, etc., than you are less likely to make a mistake resulting in your misreading of your own code. You are also more likely to be able to quickly scan and find things that you need to find.

Conclusion

Beyond user behaviors and actions, don’t assume anything in general about your program. Assumptions are one of the biggest enemies of a defensive programmer. Don’t assume you won’t need that default case or else statement. Don’t avoid creating appropriate user error messages, alerts, logs, and whatever else you need just because you assume they won’t be needed. Your assumptions are often right – but we do not care about that. What we care about are the times that they are wrong. Always plan as though you may need to return to your code in a few hours, weeks, months or even years, or that someone else will – and document it accordingly. Don’t assume it will never need to be updated, extended, or maintained. That’s naive at best, and negligent in more cases than not. Sometimes just keeping the idea of defensive programming in the back of your mind can help you to estimate, plan, and program more effectively and more safely.

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

  • gskema

    I was hoping this article would be more explicit. The cases of def. programming I had in mind were always casting/converting input variables into valid ones, although an error/exception should be issued there. That’s what I image (over)defensive programming to be. Sometimes I ask myself if I’m force-converting the input too much and I should throw some errors or maybe “let go of the crutches” and assume that my script is going to do fine without this much casting.

    • http://jeffreyleesmith.com/ Jeff Smith

      Definitely a fair point. I was going for more of a high level overview of the topic than giving explicit examples. You do raise a good question – what is too much? When does proper defensive programming become overkill and a waste of the developer’s time? There’s definitely a line somewhere, I’d imagine.

    • http://www.octane.uk.net/ Wayne Smallman

      Also, this article is applicable to almost any programming language.

      You could also take that one code sample ($ sign notwithstanding) and drop it in anything from PHP, to the various flavours of C, and out to JavaScript.

      Applicability aside, it’s still a relevant advice, nonetheless.

      • http://jeffreyleesmith.com/ Jeff Smith

        Yeah, it came close to being completely language agnostic. Chose to go ahead and drop it into PHP anyway. But most of the advice could apply all over. Feel free to share with non PHP friends or colleagues ;)

  • http://cneude-createur-web.com Matthieu

    Good article, thank you for that.

    I am not sure about the else statement. I don’t like nested conditionals (it can be very difficult to read) and it’s easy to become totally paranoid each time we write an if.

    There is always a way not to use the else or elseif as:

    if($var == ‘a’){}
    if($var == ‘b’){}
    if($var == ‘c’){}

    throw new Exception(“var has another value !!!”); // or whatever you handle the error

    I would like as well see some examples. The theory is good though,

    • http://jeffreyleesmith.com/ Jeff Smith

      Good thoughts!
      Remember though that there is definitely a point to a carefully ordered else if compared to just a sequence of if statements. The comparison is only made until one of the else if succeed as opposed to a series of if which are all made. In many cases with a small number of comparisons there may be no sizable difference in logic, but there can be.

      • http://cneude-createur-web.com Matthieu

        Good point: the logic is different but by experience it’s often possible to avoid a bunch of elseif. You ‘only’ need to be careful when you do so.

        It was an example. My point was: please try to avoid nested elseif / else statements.

        • http://jeffreyleesmith.com/ Jeff Smith

          Yeah, unnecessarily nested conditionals are definitely a frequently seen issue and a problem in more than one way, agreed!

  • liam

    Some parts of defensive programming can help to make code a lot more readable…

    The most common problem I encounter while reviewing code is that people don’t try to fail but instead just check for the error and try to build up as much conditions as they can…

    It can become a horrific mess if someone does not try to bail out early or is just “collecting further conditions” (worst seen example was like… 14 nested conditions with if / else AND usage of reference operator… *cry*). By using exceptions or return values it’s much more readable and especially less error prone… (noone. noone can understand easily nested conditions)

    Another nice example are loops… some people tend to write very complex loops with continue / break spread over hundred lines of code instead of focusing on a simple pattern like…

    foreach( $foo as $faa ) {
    // error checking
    // all is valid, do your magic
    // result of magic is unexpected -> exception / continue etc.
    // next!
    }

    these are cases where one might not think of defensive programming, but I think it is… and especially it is very useful (and can be even seen as a style guideline…)…

    I’m thankful when someone respects these “guidelines”, as sometimes this can really drive you insane…

    • http://jeffreyleesmith.com/ Jeff Smith

      Absolutely! And although some people see separate sort of dogmas almost there, I definitely think that a fail loud and early mentality can be a part of your defensive programming strategy.

      Thanks for your thoughts and additions!

  • Nilesh Pawar

    Liked it!

    • http://jeffreyleesmith.com/ Jeff Smith

      Thanks!

  • Naomi Kyoto
    • gskema

      Good stuff. “Defensive programming is a form of defensive design intended to ensure the continuing function of a piece of software in spite of unforeseeable usage of said software.”. “You should provide feedback when parameters are incorrect not hide the fact that something is wrong.”

  • Yoni L.

    The title is quite attractive but i agree with gskema, i didn’t see any try/catch for example or what if your $var is null no isset test? when code is compiled, elseif return to an if in an else statement so…
    Thanks for sharing this interesting subject but to my mind you must go deeper for it to be consistent perhaps part 2 ;)
    good job anyway

    • http://jeffreyleesmith.com/ Jeff Smith

      Thanks for the input! I’ve gotten a lot of feedback from various mediums indicating that a part 2 would be welcome, to take things a little more in depth, and several suggestions for content. So, it’s on my writing list and I’m compiling examples ;)

      • Yoni L.

        I will be really happy to read that :)

  • Jose Manuel Mujica Puentes

    Great article Jeff , i think you could take us to read a 2nd part with more techniques and examples. Thanks.

    • http://jeffreyleesmith.com/ Jeff Smith

      Thanks! Planning on it, if all goes well.

  • https://www.raptor-editor.com/ Petah

    This is what assertions are for, is it not?

  • http://www.slatius.nl Arno Slatius

    I’m missing the most basic defensive measure that one can take when writing conditional statements when comparing to constants; reverse the notation, Always write them like this:
    if (‘a’ == $var) {}

    That way you’ll get a compilation error should you happen to forget one ‘=’, which happens more often that one would like, right?

    I know, it takes some time to get used to it but it’s much safer and you’ll never be questioning yourself again why that code keeps running when you’re absolutely sure the condition isn’t true.

  • Carles

    Hi good post. I missed some comments about unit/integration/acceptance tests. What do you think about them? Wouldn’t they help us trust our code more?

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in PHP, once a week, for free.