This post is wrong.
As bloggers, sometimes we make mistakes. This isn’t my best work. However, the comments hold many great suggestions that are worth keeping around for posterity. This post is superceeded by Really, Always Return Something.
This post has been updated since it was first published.
A few weeks ago, there was a discussion on Twitter about whether or not a method should always return a value, or whether or not null was a valid value to return. The answer to this question is a resounding no, a null value should never be returned.
You should always return something when writing a function or a method. Here’s why.
A return value can be used to tell if the function behaved properly.
When you’re writing code, you should always do a sanity check to verify that the functions you’ve executed have been successful. For example, you check that a file you opened exists, or that a resource performed correctly before using it. But if you receive a null response, how do you test for this?
The answer is you can’t. You can’t check whether or not the response was valid because the response doesn’t give any indication. Even if a failure results in a null response (which many native PHP functions do), you can’t know for sure if you received a null response due to a failure of the function or some API change that broke the function between releases.
Having an expected return value eliminates this possibility and makes sanity checking in your code possible.
A null return value can’t easily be tested.
Just as your code can’t execute a proper sanity check, a null value prevents proper unit testing. Imagine that you’re testing a setter and you receive a null response; how then, do you know that the setter worked properly?
You can invoke the getter to check the object, but that creates an unnecessary dependency and doesn’t truly test the setter. With the null response you have no way of knowing that the function executed properly, and that some other developer didn’t insert a random return statement into the code somewhere.
With a return value, even a true or false value (for success or failure), you can have a greater level of confidence that the function is performing as expected. Changes in that return value will break the test, resulting in a failure, without having to invoke another function and compromise the atomic nature of the test.
Returning a null value breaks fluent interfaces.
Many developers like to use getters and setters in a fluent way. For example:
<?php $obj->set($key, $val)->set($key2, $val2)->set($key3, $val3); ?>
While fluent interfaces can be a code smell in some cases, using them in setters in this way is perfectly valid and made possible by the return of the $this variable by the setter.
If a developer returns a null value instead, fluent interfaces become impossible. This may be by design, but it can also be a source of frustration for developers who want to chain objects together (for example, in an ORM).
Note that I don’t actually recommend using fluent interfaces. Fluent interfaces create a mess out of your code in most instances. They produce unreadable code that is difficult to test. Fluent interfaces make assumptions about the return values you’re expecting from the method you’re executing. Fluent interfaces are a form of code smell that I generally avoid.
So, what should you return?
If you’re not returning null, what should you return?
Raise an exception
If you ask a method for some data that it doesn’t have, or cannot get for you, raise an exception. An exception is a perfectly valid response when you are asking a method for something that’s either impossible, or reasonable but not available. The exception should be handled by the requestor of the information that isn’t available.
Django does this for many methods. If you request something from the database that’s impossible an exception is raised. It doesn’t “fail silently” with a None response; instead it tells you that what you asked for couldn’t be provided, and expects you to deal with it.
Return an empty set
If you are requesting a data set, you should always return some sort of empty data set rather than null. For example, an empty array is a valid response of there is no data available. If you are returning a data set object, return an empty one (or a special one that implements the correct interfaces). Not only does this make your code more explicit, but it doesn’t break functions or routines that expect a particular type (e.g. foreach() expects an array or other iterable, and will emit a warning on NULL, which you then have to test for).
Return NULL anyway
If you’re pulling a column from a data set that was retrieved from a database, null may well be the only value that’s appropriate. In these cases, knowing that null is an appropriate value makes your code more likely to perform the correct evaluations to determine if that value was appropriate. Every rule has its exceptions, and this is certainly one case where NULL is an acceptable return value.
Frustrated with your company’s development practices?
You don't have to be!
No matter what the issues are, they can be fixed. You can begin to shed light on these issues with my handy checklist.
Plus, I'll help you with strategies to approach the issues at the organization level and "punch above your weight."