PHP

Return null vs return void

The other day I had a discussion about that, and why important open source frameworks like CakePHP use void in @return doc tags when void and null are code-wise totally identical.
So here my reasoning why it is a good idea to make a distinction:

Help the developer, not the machine

We already discovered that it doesn’t help the machine to make the difference.
So if anything, it would help the developer. So does it? Yes, it does (as with all coding standards, these things are there to make it easier for the developer and avoid human error).
In fact, most modern IDEs would light up the function like a Xmas tree if you tried to use a return void function value.

The following would be marked yellow (warning) right away:

/**
 * @return void
 */
public function doSth() {
}

/**
 * @return void
 */
public function badCode() {
    return $this->doSth(); // This would be marked yellow
}

So it helps the developer to not try to use return values of those methods.
This would not happen without the explicit @return void annotation.

See the following screenshot of what PHPStorm, for example, can now help us with:

return-null-vs-return-void

And also the other way around:

enforce-return

Bottom line:
void as pseudo type is not used to state that we return nothing/null (which is still true from a value point of view), but it is used to state that we should not expect anything (not trying to use it’s return value in any way).

Other reasons maybe

Be explicit

If you just see the following code, you might not know immediately, if someone just forgot the return type statement. If it was there from the start you would automatically know.

/**
 * @param string $param Param
 */
public function doSth($param) {
    // very long code block...
}

Be consistent

Some methods end up to not have any doc block, just because the doc block would have the return void part:

/**
 * @param string $param Param
 */
public function doSth($param) {
}

public function doSthElse() {
}

/**
 * @param string $param Param again
 */
public function doSthAgain($param) {
}

Especially if you don’t use descriptions this is quite inconsistent.

Part of PSR-5 and PHP RFC

It is also part of the upcoming PSR-5 standard.
To point out that is not just fiction 😉
Even though they state it to be optional (omitting that type altogether would also be valid, of course).

As long term goal then, once PHP RFC void_return_type is implemented, we can easily use a script to adjust the code.
Without being explicit this will not work out. So better use the best practice approach now already and save yourself tons of work in the long run.

Automated checking

It will be easier to automate and verify this via code sniffer or other tools when explicitly set, whereas the omitting does not tell if it was on purpose or just forgotten.
Especially since it forces to code more return type aware from the beginning (see the following part on potentially hidden issues around mixed return types).

Implications to consider

This means you should also use it consistently through-out the code base. Here a few things to keep in your mind.

Whenever you return multiple types (mixed), do NOT use void, use null instead.
The reason is simple:
You are returning an object or not (null), but you are using/checking the value of it afterwards, so returning void would be a lie (or trying to check on a void return result would be "pointless").

/**
 * @param Object $object The object.
 * @return Object|null
 */
public function doSthAndGiveMeSth(Object $object) {
    if ($object->isNotValid(()) {
        return null;
    }
    // ...
}

/**
 * @param Object $object The object.
 * @return void
 */
public function doSthWithIt(Object $object) {
    $returnValue = $this->doSth($object);
    // ...
}

This is also true for returning early inside those methods/functions.

Also always explicitly return null at the end of a "non-void" method, if nothing else is being returned.

For returning early in void methods, use the following:

/**
 * @param string $param Param
 * @return void
 */
public function doSth($param) {
    if ($param === null) {
        return;
    }
    // ...
}

An explicit return; statement at the end of those methods is never necessary (= should not be used therefore) and implies void when omitted.

Do not use return statements for constructor __construct() and destructor __destruct(), as both per definition cannot return anything.
This is the only exception made.

Adjusting your IDE

You can adjust IDEs usually to automatically add @return void as default.

For PHPStorm go to Settings -> Editor -> File and Code Templates -> Includes (tab) -> PHP Function Doc Comment.
Replace the template with:

/**
#if (${PARAM_DOC} != "") ${PARAM_DOC}
    #if (${TYPE_HINT} != "void") *
    #end
#end
${THROWS_DOC}
 * @return ${TYPE_HINT}
 */

Other implications

Exceptions

Some frameworks use special methods that can return an object, or nothing, e.g. controller methods as actions.
In that case it can be convenient to not have to return explicit nulls all the time at the end of each action.
But never use null|void. Just omit the return value. The IDE won’t complain then as hard, either. It might show the closing bracket yellow, but that’s fair enough.

Building fluid interfaces

Once you use use a lot of @return void you end up using their methods appropriately – which implies that you don’t use the return value at all (not permitted).
That concludes that you could, basically without breaking the API, start returning the object itself ($this as in @return self). This transforms the class into fluid interfaces which allow method chaining then.
So look for those return types in your classes and think about where those could be part of a fluent interface pattern. Might not always be a good idea, but could very well be in some places.

Further reading

Take a look at php-void-much-ado-about-nothing.

It is also part of PSR-2-R Additions.

Update 2015-11

So the void RFC has been approved. Will be part of PHP 7.1 then 🙂

3.83 avg. rating (78% score) - 6 votes

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.