Skip to content

SuspiciousBinaryOperation ​

error on by default quick-fix

Group: Probable bugs · PhpStorm name: SuspiciousBinaryOperationInspection

Flags binary operations that are almost certainly not what the author meant: instanceof against a trait, identical operands, == used as a statement, >= typed instead of => in an array, a comparison placed inside a call's parentheses, negated comparisons on nullable values, ?? after a cast or !, concatenation with an array literal, hard-coded booleans in &&/||, and mixed &&/||/assignment precedence.

Example ​

php
<?php
trait Loggable {}
interface Shape {}

function checks($obj, $p, $q, array $tags, ?int $limit) {
    $t1 = $obj instanceof Loggable;
    $t2 = $obj instanceof Shape;
    $s1 = $p->id === ($p->id);
    $s2 = count($tags) > count($tags);
    $p == $q;
    $map = ['enabled' >= true, $p >= 'x'];
    $out = [
        (bool)$p ?? 0,
        (!$q) ?? 1,
        $p ?? $q,
    ];
    $msg = 'tags: ' . ['a'];
    $ok = [
        $p and FALSE,
        $p || true,
        $p && \true,
        $p or null,
    ];
    if (!($limit < 10)) {}
}

class Quota {
    public function fits(string $who): bool {
        if (strlen($who >= 3)) {}
        if ($this->allow($who === 'root')) {}
        return true;
    }
    private function allow(bool $flag) { return $flag; }
}

if ($found = $left != $right) {}
if ($x || $y && $z) {}
if ($row = fetch() || $fallback) {}
if (!  $x >= $limit) {}
if (($x && $y) || $z) {}
$both = $x && $y;
if ((!$x) < $y) {}
php
<?php
trait Loggable {}
interface Shape {}

function checks($obj, $p, $q, array $tags, ?int $limit) {
    $t1 = $obj instanceof Loggable;
    $t2 = $obj instanceof Shape;
    $s1 = $p->id === ($p->id);
    $s2 = count($tags) > count($tags);
    $p == $q;
    $map = ['enabled' >= true, $p >= 'x'];
    $out = [
        (bool)$p ?? 0,
        (!$q) ?? 1,
        $p ?? $q,
    ];
    $msg = 'tags: ' . ['a'];
    $ok = [
        $p and FALSE,
        $p || true,
        $p && \true,
        $p or null,
    ];
    if ($limit >= 10) {}
}

class Quota {
    public function fits(string $who): bool {
        if (strlen($who) >= 3) {}
        if ($this->allow($who === 'root')) {}
        return true;
    }
    private function allow(bool $flag) { return $flag; }
}

if ($found = ($left != $right)) {}
if ($x || ($y && $z)) {}
if ($row = (fetch() || $fallback)) {}
if ((!$x) >= $limit) {}
if (($x && $y) || $z) {}
$both = $x && $y;
if ((!$x) < $y) {}

Reported:

  • line 6: A trait is never an instanceof target; this is always false.
  • line 8: Both operands are the same.
  • line 9: Both operands are the same.
  • line 10: Comparison result is discarded; did you mean '='?
  • line 11: Did you mean '=>' for an array key?
  • line 13: '(bool)$p' is never null, so '??' is useless; add parentheses.
  • line 14: '!$q' is never null, so '??' is useless; add parentheses.
  • line 17: Concatenating an array literal makes no sense.
  • line 19: This constant decides the whole condition.
  • line 20: This constant decides the whole condition.
  • line 21: This constant has no effect in the condition.
  • line 22: This constant has no effect in the condition.
  • line 24: Null or false operands make this negated comparison misleading; use '$limit >= 10'.
  • line 29: This comparison probably belongs outside the call parentheses.
  • line 36: Operator precedence is unclear here; add parentheses.
  • line 37: Operator precedence is unclear here; add parentheses.
  • line 38: Operator precedence is unclear here; add parentheses.
  • line 39: Operator precedence is unclear here; add parentheses.

Options ​

OptionTypeDefaultEffect
VERIFY_CONSTANTS_IN_CONDITIONSbooltrueEnables D9.
VERIFY_UNCLEAR_OPERATIONS_PRIORITIESbooltrueEnables D10.

Configure ​

In custos.json:

json
{
  "rules": {
    "SuspiciousBinaryOperation": {
      "enabled": false,
      "options": {
        "VERIFY_CONSTANTS_IN_CONDITIONS": true,
        "VERIFY_UNCLEAR_OPERATIONS_PRIORITIES": true
      }
    }
  }
}

Suppress ​

Before the statement or declaration (or the first statement of the file), either of:

php
// @custos-ignore SuspiciousBinaryOperation

/**
 * @noinspection SuspiciousBinaryOperationInspection
 */

Released under the MIT License. Rule catalogue modelled on Php Inspections (EA Extended); independent clean-room implementation.