66

I did not find any standard for this case:

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

}

or

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

}

Imagine the var-names are longer and 80 letters are exceeded. How should I handle this? It could look like:

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

    }
Joe
  • 15,205
  • 8
  • 49
  • 56
user3631654
  • 1,735
  • 6
  • 22
  • 38
  • 4
    You could also shorten this to `$a == $g` – Daan May 20 '14 at 11:46
  • 1
    I don't believe there is any PSR defining what standard should be used in this case; though other coding standards such as Zend have their own rules that generally suggest splitting across several lines with indenting, and bracketing is a matter of code logic and readability – Mark Baker May 20 '14 at 11:47
  • This looks like an exceptional case to me, treat it as such. – Halcyon May 20 '14 at 11:48
  • Ok, so what's the recommended practise for this case? I am using PSR-2 and want to give other programmers the best code to understand it quickly – user3631654 May 20 '14 at 11:49
  • 6
    @Daan it's only an example, I don't have any case where I will compare like this – user3631654 May 20 '14 at 12:10
  • Refer here https://pear.php.net/manual/en/rfc.cs-enhancements.splitlongstatements.php – Pratik Nov 21 '17 at 07:51
  • 2
    Best thing to do is refactor your expression to well named variables: `$is_foo = ($condition1 || $condition2); $is_bar = ($condition3 && $condtion4); if ($is_foo && $is_bar) { // .... }` – Pratik Nov 21 '17 at 07:53
  • 6
    @Daan Nope, your condition evaluates to true even if `$c` is not equal to `$d`. Also, you're completely missing the point. – George Dimitriadis Jan 17 '18 at 10:44

10 Answers10

68

Personally, I prefer

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

For each line, you start with the double ampersand, indicating that the following statement is separate from the others. If you put the ampersand on the end of the line, it can become less obvious when the lines vary a lot in length.

For example;

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

In this case you have to scan the code more to know that each line is connected by a double ampersand.

Maurice
  • 4,829
  • 7
  • 41
  • 50
  • 8
    I actually prefer your answer over the accepted one. It looks OK visually - only OK, because the first line stands out from the rest -, while also works with PSR2. The argument for the double ampersand is convincing too, I might just switch to this style. :) – ZeeCoder Oct 20 '15 at 08:11
  • 8
    Furthermore the accepted answer is not psr-2 valid (if I can trust "squizlabs/php_codesniffer"). – Armin Jan 19 '16 at 15:27
  • 1
    This approach also makes it easier to comment out a particular condition as it's on the same line as its operator(s) – James Smith Sep 17 '18 at 11:02
67

There is no recommendation / convention for this case, and as Halcyon already mentioned this is a quite exceptional case.

However, there is a recommendation for a function call with a long list of parameters:

Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line.

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

So if I had to create an if-statement similar to your's, I'd do this:

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

As you can see, this is almost the same as the solution you proposed yourself, but I prefer adding the && operators after the conditions.

Nic Wortel
  • 11,155
  • 6
  • 60
  • 79
  • 8
    I do not understand how the makers of this standard do not have a solution. that makes me eye cancer – user3631654 May 20 '14 at 13:52
  • 7
    It is absolutly not a quite exeptional case! If you hold on splitting into several lines at 80 characters this comes very very often! – user3292653 May 26 '14 at 06:48
  • 2
    If I had to do it, I'd put first-level operator in front, becuase sometimes you may end up with nested conditionals you you may want to keep them on the same line. – gskema Jun 10 '15 at 12:45
  • 7
    phpcs 2.5.0 with psr2 standard says: `Expected 0 spaces after opening bracket; newline found`. So this seems not to be valid psr2. The variant of Maurice below works for code sniffer. – Armin Jan 19 '16 at 15:26
  • Comparison operator should be first in the line and there should be no whitespace after the opening parenthesis. https://pear.php.net/manual/en/rfc.cs-enhancements.splitlongstatements.php – Pratik Nov 21 '17 at 07:51
  • 1
    I have to downvote this accepted answer in favour of Ernesto Allely's answer, which is not based on preference and links to the PSR-12 page. His answer should have more visibility over this one and should instead be the accepted answer. – user2700551 Jun 24 '22 at 10:28
  • I use the same strat I use for Vue. With auto lint. If you line is larger than 120 chars, then break it down like an array. That's how I think of it. As the PSR standards are really non-specific about it. – dustbuster Mar 27 '23 at 14:24
  • try comment second one line and you have to edit code.. In this code you don't have to – keizah7 Aug 07 '23 at 10:15
48

Worth mention that the new standard PSR-12, that replaces PSR-2, clarifies this question.

Expressions in parentheses MAY be split across multiple lines, where each subsequent line is indented at least once. When doing so, the first condition MUST be on the next line. The closing parenthesis and opening brace MUST be placed together on their own line with one space between them. Boolean operators between conditions MUST always be at the beginning or at the end of the line, not a mix of both.

<?php

if (
    $expr1
    && $expr2
) {
    // if body
} elseif (
    $expr3
    && $expr4
) {
    // elseif body
}

Source: https://www.php-fig.org/psr/psr-12/#51-if-elseif-else

Ernesto Allely
  • 851
  • 10
  • 16
  • 16
    This should be the accepted answer because it is not based on a personal point of view, but quoted from and confirmed with an official source link. – Billal Begueradj Feb 12 '20 at 13:02
13

Edit

One year later, I would strongly recommend to rewrite your code to have a shorter if statement. Through variables or function calls.

Original

I came into this situation, so I decided to go with the following format:

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

But, I use phpcbf, which transformed (following the PSR2 standard) the previous code into:

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

I wanted to know more: how does it know that this is the behaviour expected by the standard if it is not written anywhere? Well, the answer is simple: the case is taken into account by the standard, by the following sentence:

There MUST NOT be a space after the opening parenthesis

This explains why the second snippet is the one, and the only one, which follows the PSR-2 standard, as declared by php-fig.

tleb
  • 4,395
  • 3
  • 25
  • 33
9

I prefer putting logical operators in long if statements at the beginning of the line, mainly for readability and better behavior in version control.

Note that as also mentioned in the other answers it's usually a code smell to have long if statements. However sometimes you have to do it, or the code is already there and you can't rewrite it, so if it's already a bad thing then it helps not to make even more of a mess.

Also these things apply to if statements with just a single "and" where the different elements are so long you still need to split it to multiple lines (long variable or class names for instance).

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

Readability: As all logical operators are vertically grouped you can instantly see which operator is on each line. As your eye scans the code it can just move straight vertically and it only needs to move horizontally when there is an actual extra logical level.

If the operators are at the end of the line your eye needs to move back and forth randomly between lines of uneven lenght.

Better behavior in version control: When an extra clause is added at the bottom of the if statement then this translates to 1 line added and 0 removed in version control.

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

If you put logical operators at the end then that will be 2 lines added and 1 removed. That in turn obscures useful information: your commit message for the last change will be shown for both lines when you Git annotate, so you would have to go to the previous version to see the commit message for the line you added the operator to.

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
inwerpsel
  • 2,677
  • 1
  • 14
  • 21
5

There is a recommendation nowadays... in PSR-12.

Expressions in parentheses MAY be split across multiple lines, where each subsequent line is indented at least once. When doing so, the first condition MUST be on the next line. The closing parenthesis and opening brace MUST be placed together on their own line with one space between them. Boolean operators between conditions MUST always be at the beginning or at the end of the line, not a mix of both.

<?php

if (
    $expr1
    && $expr2
) {
    // if body
} elseif (
    $expr3
    && $expr4
) {
    // elseif body
}
2

My favourite approach is to remove sub-expressions from the IF statement, as follows:

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

This approach will make it also easier to debug.

The second case you expose is equivalent to the first one due to the associative property of the logic operators. Therefore, $a && $b && $c is the same as ($a && $b) && $c, which is the same as $a && ($b && $c)

Nicolas
  • 1,320
  • 2
  • 16
  • 28
  • 6
    This code is *not* identical to the original code, since it evaluates `$c, $d, $g` even if `$a != $b`, thus can only be used if those do not have side effects. – Jaap Eldering Aug 05 '18 at 21:49
  • There WILL be side effects in performances. Whether it's acceptable or not... well, I prefer not take the habit. – Chicna Oct 24 '18 at 09:53
  • I guess you can always fall back to assembly code to make things faster... but that's not what this is about @Chicna – Nicolas Oct 24 '18 at 09:57
1

I would suggest you try to think about the operation in different terms. For example:

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

You will perhaps find that you can express the whole algorithm as more of an operation on a set, use an array instead of individual variables and use logical operations on the set like shown above. That can lead to drastically different and more readable code.

Another example of 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)));
        }
    }

}
deceze
  • 510,633
  • 85
  • 743
  • 889
  • 2
    the question is about formatting long conditions not about this particular one. So your answer althoug interesting is quite useless. – Eloar Jan 23 '17 at 13:22
  • 1
    The advice applies equally to *any* code, and is merely demonstrated on this concrete example. Let me put it this way: *try to avoid long conditions in the first place.* – deceze Jan 23 '17 at 13:24
  • 1
    @deceze Serious programmers do avoid long conditions in the first place. But if there's has to be a long condition your answer does not add any value to the question. If OP asks about styling long conditions he's indeed familiar with general best practices. Anyone unfamiliar would just drop the condition without its styling in mind. – jakub_jo Feb 16 '17 at 16:51
1

I prefer it at the beginning as well:

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

I prefer to do it in this style:

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

But again, keep your if's as simple as possible.

Maybe it's a better idea to separate a big condition in multiple if's.

For your question, I would create a function which takes an array and returns true if all && are met. Then, in my main code you'd have like

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

if (allTrue($arr))
    // do something
Lemures
  • 474
  • 5
  • 11