S.Pols
S.Pols

Reputation: 3434

Correct way to handle PHP 7 return types

I'm busy creating an application and i want to use the PHP 7 return types. Now I read on php.net that it was a design decision that it is not allowed to return null when a return type is defined.

What is the correct way too handle this?

One option is a try...catch block:

public function getMyObject() : MyObject
{ 
     return null;
}

try
{
    getMyObject();
}
catch(Exception $e)
{
    //Catch the exception
}

I don't have a good feeling about this, because my code will be a huge try...catch block since I need to write a try...catch block for every method that returns an object.

The Null Object Pattern is a good solution for this, but I don't like the idea to create a NullObject for each object in my application. Is there correct way to do this?

Upvotes: 28

Views: 17375

Answers (6)

Jared
Jared

Reputation: 113

As of PHP7.1, you can use nullable return types:

public function getMyObject(): ?MyObject {
  return null;
}

http://php.net/manual/en/migration71.new-features.php

Upvotes: 2

Marcin Nabiałek
Marcin Nabiałek

Reputation: 111829

I was going to ask for the same. I don't see much point for this.

If I have the following sample code to find User by name:

<?php

class User
{
    protected $name;

    /**
     * User constructor.
     *
     * @param $name
     */
    public function __construct(string $name)
    {
        $this->name = $name;
    }

    /**
     * @return mixed
     */
    public function getName() : string
    {
        return $this->name;
    }
}


function findUserByName(string $name, array $users) : User {
    foreach ($users as $user) {
        if ($user->getName() == $name) {
            return $user;
        }
    }

    return null;
}


$users = [
    new User('John'), new User('Daniel')
];

$user = findUserByName('John', $users);

echo $user->getName()."\n";

$user2 = findUserByName('Micheal', $users);

I would like to tell Php I can get either User or null. Now I need to wrap it always in try catch block and I don't see much sense to declare return type in this case. On the other hand If my findUserByName function would be complex I could return accidentally for example user name instead of full user object so I would like to have return type defined.

I think much more reasonable would be defining multiple return types for functions. Sometimes you create function that return one object or array of objects, and it would be nice to define, this function should return for example User or array of users: User[].

In OP case (and also mine) we could then declare return type as User, null and it would solve the problem.

Upvotes: 0

nikita2206
nikita2206

Reputation: 1188

You can use Option data type.

It's a type that has Something or Nothing as a wrapped value.

You can find more elaborate description here and one of the open implementations in PHP here

PS please don't use exceptions, out parameters or additional methods for that kind of logic, it's a wrong tool for the job.

Upvotes: 6

Steve
Steve

Reputation: 20469

This is not possable currently, though as mentioned there are RFC's that might address the issue for future versions.

As of now, your options are:

1.Drop the return type from your method signature:

public function getMyObject()
{ 
     return null;
}

2.Throw and catch an exception (as per @NiettheDarkAbsol's answer):

public function getMyObject() : MyObject
{
    throw new MyCustomException("Cannot get my object");
}

3.Refactor to two methods:

private $myObject;
//TODO think of a better method name
public function canGetMyObject() : bool
{
    $this->myObject = new myObject();
    return true;
}

public function getMyObject() : MyObject
{
    if(!$this->myObject){
        throw new MyCustomException("Cannot get my object");
    }
    return $this->myObject;
}

Calling code:

if($cls->canGetMyObject()){
    $myObject  = $cls->getMyObject();
}

4.Use a bool return type and an out parameter:

public function tryGetMyObject(&$out) : bool
{
    $out = new myObject();
    return true;
}

Calling code:

$myObject = null;

if($cls->tryGetMyObject($myObject)){      
    $myObject->someMethod(); //$myObject is an instance of MyObject class
}

The 3rd and 4th options are only really worth considering in cases where a null return is expected and frequant, such that the overhead of exceptions is a factor. Probably you will find this doesnt actually apply all that often and exceptions are the way forward

Upvotes: 3

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324620

If your code expects to return an instance of a class, in this case MyObject, but it does not, then that is indeed an exception and should be handled as such.

However, you should throw this exception yourself.

public function getMyObject() : MyObject
{
    throw new MyCustomException("Cannot get my object");
}

Then you can catch that specific exception and proceed accordingly.

Upvotes: 29

Fabian Schmengler
Fabian Schmengler

Reputation: 24551

I don't have a good feeling about this, because my code will be a huge try/catch block since I need to write a try / catch block for every method that returns an object.

The alternative (with your code) would be to always check the returned value for null to not get errors from using null as an object.

Using exceptions is more expressive and makes bugs visible early, which is a good thing. So either make sure that your method always returns an object or throw an exception if it cannot fulfil its contract. Otherwise the return type has no real value.

Upvotes: 7

Related Questions