web-dev-qa-db-fra.com

Manière correcte de gérer PHP 7 types de retour

Je suis en train de créer une application et je souhaite utiliser les types de retour PHP 7. Maintenant, j'ai lu sur php.net que c'était une décision de conception qu'il n'était pas permis de retourner null lorsqu'un type de retour est défini.

Quelle est la bonne façon de gérer cela?

Une option est un essai ... catch block:

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

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

Je n'ai pas une très bonne impression à ce sujet, car mon code sera un gros essai ... catch block puisque je dois écrire un essai ... catch block pour chaque méthode renvoyant un objet.

Le Null Object Pattern est une bonne solution pour cela, mais je n'aime pas l'idée de créer une NullObject pour chaque objet de mon application. Y a-t-il une bonne façon de faire cela? 

27
S.Pols

Je n'ai pas une très bonne impression à ce sujet, car mon code sera un énorme bloc try/catch car je dois écrire un bloc try/catch pour chaque méthode renvoyant un objet.

L’alternative (avec votre code) serait de toujours vérifier la valeur renvoyée pour null afin de ne pas obtenir d’erreurs lors de l’utilisation de null comme objet.

L'utilisation d'exceptions est plus expressive et permet de détecter rapidement les bogues, ce qui est une bonne chose. Donc, soit assurez-vous que votre méthode retourne toujours un objet, soit émettez une exception si elle ne peut pas remplir son contrat. Sinon, le type de retour n'a pas de valeur réelle.

6
Fabian Schmengler

Si votre code s'attend à renvoyer une instance d'une classe, dans ce cas, MyObject, mais ce n'est pas le cas, il s'agit bien d'une exception et doit être traitée comme telle.

Cependant, vous devriez lancer cette exception vous-même.

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

Ensuite, vous pouvez intercepter cette exception spécifique et procéder en conséquence.

27
Niet the Dark Absol

Vous pouvez utiliser le type de données Option.

C'est un type qui a Something ou Nothing comme valeur encapsulée.

Vous pouvez trouver une description plus élaborée ici et une des implémentations ouvertes dans PHP ici

PS, veuillez ne pas utiliser d’exceptions, de paramètres externes ou de méthodes supplémentaires pour ce type de logique, c’est un mauvais outil.

6
nikita2206

Ce n'est pas possible actuellement, bien que, comme mentionné, il existe des RFC qui pourraient résoudre le problème pour les versions futures.

A partir de maintenant, vos options sont:

1.Drop le type de retour de votre signature de méthode:

public function getMyObject()
{ 
     return null;
}

2.Effectuer une exception (d'après la réponse de @ NiettheDarkAbsol):

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

3.Refactor à deux méthodes:

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;
}

Code d'appel:

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

4.Utilisez un type de retour bool et un paramètre out:

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

Code d'appel:

$myObject = null;

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

Les 3ème et 4ème options ne valent vraiment la peine d'être considérées que dans les cas où un retour nul est attendu et fréquent, de sorte que le surcoût des exceptions constitue un facteur .. vers l'avant

3
Steve

Depuis PHP7.1, vous pouvez utiliser les types de retour nullables:

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

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

1
Jared

J'allais demander la même chose. Je ne vois pas grand chose à cela.

Si j'ai le code exemple suivant pour trouver l'utilisateur par son nom:

<?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);

Je voudrais dire à Php que je peux obtenir l'utilisateur ou null. Maintenant, j'ai besoin de l'envelopper toujours dans le bloc catch et je ne vois pas l'intérêt de déclarer le type de retour dans ce cas. D'un autre côté, si ma fonction findUserByName est complexe, je pourrais renvoyer accidentellement un nom d'utilisateur, par exemple, au lieu d'un objet utilisateur complet. J'aimerais donc que le type de retour soit défini.

Je pense qu’il serait beaucoup plus raisonnable de définir plusieurs types de retour pour des fonctions. Parfois, vous créez une fonction qui retourne un objet ou un tableau d'objets, et il serait agréable de définir cette fonction. Cette fonction devrait par exemple renvoyer User ou un tableau d'utilisateurs: User[]

Dans le cas OP (et aussi le mien), nous pourrions alors déclarer le type de retour sous la forme User, null et le problème serait résolu.

0
Marcin Nabiałek