web-dev-qa-db-fra.com

Empilement de plusieurs opérateurs ternaires dans PHP

Voici ce que j'ai écrit:

 $Myprovince = (
($province == 6) ? "city-1" :
($province == 7) ? "city-2" :
($province == 8) ? "city-3" :
($province == 30) ? "city-4" : "out of borders"
);

Mais pour chaque champ, j'ai obtenu la valeur city-4. Je souhaite utiliser des opérateurs ternaires au lieu de switch/if parce que je veux expérimenter et voir comment cela se ferait.

Quel est le problème avec ce code?

33
Mac Taylor

D'autres ont déjà suggéré la bonne façon de le faire, mais si vous voulez vraiment utiliser l'opérateur ternaire, vous devez utiliser des parenthèses comme:

$province = 7;
 $Myprovince = (
 ($province == 6) ? "city-1" :
  (($province == 7) ? "city-2" :
   (($province == 8) ? "city-3" :
    (($province == 30) ? "city-4" : "out of borders")))
 );

Lien mis à jour

86
codaddict

L'opérateur ternaire est évalué de gauche à droite. Donc, si vous ne groupez pas correctement les expressions, vous obtiendrez un résultat inattendu.

Le conseil de PHP est  [documents] :

Il est recommandé d'éviter "d'empiler" les expressions ternaires. Le comportement de PHP lors de l'utilisation de plusieurs opérateurs ternaires dans une même instruction n'est pas évident.

Votre code est en fait évalué comme:

(
    (
        (
            $province == 6 ? "city-1" : $province == 7
        ) ? "city-2" : 
        $province == 8
    ) ? "city-3" : $province == 30
) ? "city-4" : "out of borders";

où il devrait être

$province == 6 ? "city-1" : (
    $province == 7 ? "city-2" : (
        $province == 8 ? "city-3" : (
           $province == 30 ? "city-4" : "out of borders"
        )
    )
);

Ce code peut sembler correct, mais quelqu'un le lira et aura besoin de plus de temps que nécessaire pour comprendre ce que fait ce code.


Vous seriez mieux avec quelque chose comme ça:

$map = array( 6 = >'city-1', 
              7 => 'city-2', 
              8 => 'city-3', 
             30 => 'city-4');

$Myprovince = "out of borders";

if(array_key_exists($province, $map)) {
    $Myprovince = $map[$province];
}

Ou comme @ Jonah mentionné dans son commentaire:

$Myprovince = isset($map[$province]) ? $map[$province] : 'out of borders';
33
Felix Kling

N'abusez pas de l'opérateur ternaire pour ce genre de choses. Cela rend le débogage presque impossible à suivre. Pourquoi ne pas faire quelque chose comme

switch($province) {
    case 6: $Myprovince = "city-1"; break;
    case 7: ...
}

ou simplement enchaîné si/alors/sinon

if ($province == 6) {
     $Myprovince = "city-1";
} elseif ($province = ...) {
   ...
}
17
Marc B

Certaines personnes ont suggéré d'utiliser une instruction switch ou une instruction if/else. Mais j'utiliserais un tableau à la place, pour le rendre plus facile à maintenir et à lire:

$provinces = array (
    6 => 'city-1',
    7 => 'city-2',
    8 => 'city-3',
    30 => 'city-4'
);

// then you can call:

$Myprovince = isset($provinces[$province]) ? $provinces[$province] : 'out of borders';

Pourquoi?

Le code sera probablement éventuellement plus facile à gérer. Peut-être voudrez-vous ajouter ces correspondances province-ville à partir de la base de données un jour .. etc. Cela sera difficile à maintenir avec un tas d'instructions de commutateur/cas.

12
arnorhs

Je comprends que c'est une question sur PHP, mais comme il ne s'agit de toute façon que d'un exercice pédagogique, j'ai pensé que vous pourriez être intéressé d'apprendre que Ruby et Javascript se comportent réellement comme vous vous y attendez.

Rubis:

ree-1.8.7-2012.02 :009 > def foo x
ree-1.8.7-2012.02 :010?>   x == 1 ? "city 1" : x == 2 ? "city 2" : "out of borders"
ree-1.8.7-2012.02 :011?>   end
 => nil
ree-1.8.7-2012.02 :012 > foo 1
 => "city 1"
ree-1.8.7-2012.02 :013 > foo 2
 => "city 2"
ree-1.8.7-2012.02 :014 > foo 3
 => "out of borders"

Javascript:

> function f(x) { return x == 1 ? "city 1" : x == 2 ? "city 2" : "out of borders"; }
undefined
> f(1)
"city 1"
> f(2)
"city 2"
> f(3)
"out of borders"
4
Noah

Essayez avec des parenthèses supplémentaires:

$Myprovince = (
($province == 6) ? "city-1" :
(($province == 7) ? "city-2" :
(($province == 8) ? "city-3" :
(($province == 30) ? "city-4" : "out of borders"
))));

Votre code a un problème avec la priorité de l'opérateur ternaire.

Mais je pense que vous devriez vraiment supprimer cet opérateur et essayer d'utiliser un switch à la place.

3
krtek

Je me suis retrouvé dans le même problème aujourd'hui. Les autres donnent déjà des solutions acceptables. Le mien ne met l'accent que sur une seule doublure. Plus lisible à mon avis.

if ($province == 6) $Myprovince = 'city-1';
elseif ($province == 7) $Myprovince = 'city-2';
elseif ($province == 8) $Myprovince = 'city-3';
elseif ($province == 30) $Myprovince = 'city-4';
else $Myprovince = 'out of borders';
0
Fandi Susanto

Utilisez plutôt le commutateur. Les opérateurs ternaires ne devraient vraiment pas être utilisés pour plus que des conditions uniques, car ils deviennent rapidement très difficiles à comprendre.

switch ($province) {
    case 6:
        $Myprovince = 'city-1';
        break;
    case 7:
        $Myprovince = 'city-2';
        break;
    case 8:
        $Myprovince = 'city-3';
        break;
    case 30:
        $Myprovince = 'city-4';
        break;
    default:
        $Myprovince = 'out of borders';
}
0
Jonah