web-dev-qa-db-fra.com

PSR-2 standard pour de longues conditions si

Je n'ai trouvé aucune norme pour ce cas:

if ($a == $b && $b == $c && $c == $d && $g == $d) {

}

ou

if (($a == $b && $b == $c) && ($c == $d && $g == $d)) {

}

Imaginez que les noms de variables sont plus longs et que 80 lettres sont dépassées. Comment dois-je gérer cela? Cela pourrait ressembler à:

if (
       $a == $b
    && $b == $c
    && $c == $d
    && $g == $d
) {

    }
39
user3631654

Il n'y a pas de recommandation/convention pour ce cas, et comme Halcyon l'a déjà mentionné, il s'agit d'un cas assez exceptionnel.

Cependant, il existe une recommandation pour un appel de fonction avec une longue liste de paramètres:

Les listes d'arguments PEUVENT être divisées sur plusieurs lignes, où chaque la ligne suivante est indentée une fois. Ce faisant, le premier élément du fichier La liste DOIT être sur la ligne suivante et il NE DOIT y avoir qu'un seul argument par ligne.

<?php
$foo->bar(
    $longArgument,
    $longerArgument,
    $muchLongerArgument
);

Donc si je devais créer une instruction if similaire à la votre, je ferais ceci:

if (
    $a == $b &&
    $b == $c &&
    $c == $d &&
    $g == $d
) {
    // do something
}

Comme vous pouvez le constater, cette solution est presque identique à celle que vous avez proposée, mais je préfère ajouter les opérateurs && après les conditions.

48
Nic Wortel

Personnellement, je préfère

if ($a == $b
    && $b == $c
    && $c == $d
    && $g == $d
) {
    // code here...
}

Pour chaque ligne, vous commencez par la double perluète, indiquant que l'instruction suivante est distincte des autres. Si vous placez l'esperluette au bout de la ligne, cela peut devenir moins évident lorsque la longueur des lignes varie beaucoup.

Par exemple;

if ($a == $b && 
    $b == $c && 
    $thisisamuchlongerstatementbecauseofthisvar == $d && 
    $g == $d
) {
    // code here...
}

Dans ce cas, vous devez scanner davantage le code pour savoir que chaque ligne est connectée par une double esperluette.

40
Maurice

Modifier

Un an plus tard, je vous recommande fortement de réécrire votre code pour avoir une instruction if plus courte. Via des variables ou des appels de fonction.

Original

Je suis arrivé dans cette situation, alors j'ai décidé de choisir le format suivant:

if (
    $a == $b &&
    $b == $c &&
    $c == $d &&
    $g == $d) {
}

Mais j'utilise phpcbf, qui a transformé (en suivant la norme PSR2) le code précédent en:

if ($a == $b &&
    $b == $c &&
    $c == $d &&
    $g == $d) {
}

Je voulais en savoir plus: comment savoir qu'il s'agit du comportement attendu par le standard s'il n'est écrit nulle part? Eh bien, la réponse est simple: le cas est pris en compte par la norme, par la phrase suivante:

Il NE DOIT PAS y avoir d'espace après la parenthèse d'ouverture

Ceci explique pourquoi le deuxième extrait est celui et le seul qui respecte le standard PSR-2, tel que déclaré par php-fig .

11
tleb

Mon approche préférée consiste à supprimer les sous-expressions de l'instruction IF, comme suit: 

$c1 = $a == $b;
$c2 = $b == $c;
$c3 = $c == $d;
$c4 = $g == $d;
if ($c1 && $c2 && $c3 && $c4) {
}

Cette approche facilitera également le débogage.

Le second cas que vous exposez est équivalent au premier en raison de la propriété associative des opérateurs logiques . Par conséquent, $a && $b && $c est identique à ($a && $b) && $c, ce qui est identique à $a && ($b && $c)

3
Nicolas

Je préfère mettre les opérateurs logiques dans des instructions if longues au début de la ligne, principalement pour des raisons de lisibilité et d’amélioration du comportement en contrôle de version.

Notez que, comme mentionné également dans les autres réponses, il s’agit généralement d’une odeur de code qui contient des instructions longues si. Cependant, parfois, vous devez le faire, ou le code est déjà là et vous ne pouvez pas le réécrire, donc si c'est déjà une mauvaise chose, il est préférable de ne pas gâcher davantage.

En outre, ces éléments s'appliquent aux instructions si un seul "et" où les différents éléments sont si longs que vous devez toujours le scinder en plusieurs lignes (noms de variable ou de classe longs, par exemple).

if (
    $something->getValue() === 'some_value'
    || (
        $something instanceof SomeClass
        && $something->has($someNumber)
        && $someNumber > 42
    )
) {
    // do something
}

Lisibilité : Comme tous les opérateurs logiques sont groupés verticalement, vous pouvez voir instantanément quel opérateur se trouve sur chaque ligne. Lorsque votre œil scrute le code, il peut simplement se déplacer verticalement et n'a besoin que de se déplacer horizontalement lorsqu'il existe un niveau logique supplémentaire.

Si les opérateurs se trouvent au bout de la ligne, votre œil doit se déplacer de manière aléatoire entre des lignes de longueur inégale.

Meilleur comportement dans le contrôle de version : Lorsqu'une clause supplémentaire est ajoutée au bas de l'instruction if, cela se traduit par 1 ligne ajoutée et 0 supprimée dans le contrôle de version.

diff --git a/3.php b/3.php
index 367c57c..2a40c3a 100644
--- a/3.php
+++ b/3.php
@@ -6,6 +6,7 @@ 
    if (
         $something instanceof SomeClass
         && $something->has($someNumber)
         && $someNumber > 42
+        && $anotherCase
    ) {
     // do something

Si vous placez des opérateurs logiques à la fin, 2 lignes seront ajoutées et 1 supprimée. Cela masque à son tour des informations utiles: votre message de validation pour la dernière modification sera affiché pour les deux lignes lorsque vous annoterez Git. Vous devrez donc passer à la version précédente pour voir le message de validation de la ligne pour laquelle vous avez ajouté l'opérateur.

diff --git a/4.php b/4.php
index f654780..2b9e0c5 100644
--- a/4.php
+++ b/4.php
@@ -5,7 +5,8 @@ 
    if (
        $something instanceof SomeClass &&
        $something->has($someNumber) &&
-       $someNumber > 42
+       $someNumber > 42 &&
+       $anotherCase
     ) {
     // do something
1
inwerpsel

Je le préfère aussi au début:

if (   self::LOG_ALL
    || (    self::DEBUG__EXECUTION_TIME__IS_ENABLED
        && (self::DEBUG__EXECUTION_TIME__THRESHOLD_SECONDS < $trxDurinationSeconds)
       )
) {
    doSomething();
}
1
tol

Je vous suggérerais d'essayer de penser à l'opération de manière différente. Par exemple:

if (count(array_unique([$a, $b, $c, $d, $g])) == 1)

Vous constaterez peut-être que vous pouvez exprimer l'ensemble de l'algorithme sous la forme d'une opération sur un ensemble, utiliser un tableau au lieu de variables individuelles et utiliser des opérations logiques sur l'ensemble comme indiqué ci-dessus. Cela peut conduire à un code radicalement différent et plus lisible.

Un autre exemple de refactoring:

namespace My;

UnexpectedValueException::assertAllEqual($a, $b, $c, $d, $g);


class UnexpectedValueException extends \UnexpectedValueException {

    public static function assertAllEqual(/* $value, ... */) {
        $args = func_get_args();
        if (count(array_unique($args)) > 1) {
            throw new static(sprintf('[%s] are not all equal', join(', ', $args)));
        }
    }

}
1
deceze

Je préfère le faire dans ce style:

if (condition1
|| (condition2_1 
    && condition2_2
    && condition2_3)
&& (c3 && c4) {
    // do something
}

Mais encore une fois, gardez votre si aussi simple que possible.

Peut-être c'est une meilleure idée de séparer une grosse condition en plusieurs si.

Pour votre question, je créerais une fonction qui prend un tableau et renvoie true si tous les && sont satisfaits. Ensuite, dans mon code principal, vous auriez aimé

$arr = [$a => $b, $b => $c, $c => $d];
// or you can create array of arrays [[$a, $b], [$b, $c] ...]

if (allTrue($arr))
    // do something
0
Lemures