Skip to content

DisconnectedForeachInstruction ​

info off by default

Group: Control flow · PhpStorm name: DisconnectedForeachInstructionInspection

A statement inside a foreach body that uses no loop variable and nothing the loop body changes produces the same effect on every iteration; it most likely belongs before or after the loop. Optionally (off by default) the rule also points at objects instantiated on every iteration that could be built once and cloned.

Example ​

php
<?php
/* @var array $orders */
$type = \ArrayObject::class;
foreach ($orders as $key => $order) {
    $box = new $type();
    $node = (new \DOMDocument())->createElement('row');

    syslog(LOG_INFO, $banner);
    while ($pending > 0) {
        usleep($pending);
    }
    if ($debugMode) {
        dump($settings);
    }
    switch ($mode) {
        default:
    }
    try {
        $warmup();
    } catch (\RuntimeException $problem) {
        report($problem);
    }

    echo '<br>';                        // E4: no variables
    if ($stop) { return; }              // E3
    $total += $order;                   // E2 + connected
    $copy = clone $template;            // clone: never reported
    $sink[] = 'seen';                   // accumulate
    $logger->push($order);              // D8a: $logger becomes modified
    $logger->flush();                   // connected through $logger
    preg_match('/x/', $order, $hits);   // D8b: $hits modified
    print_r($hits);                     // connected
    $meta->touch(compact('key'));       // D11: depends on $key
}

foreach ($groups as $group) {
    foreach ($group as $member) {
        echo $group;                    // D4: outer loop variable
    }
}

foreach ($rows as $row) {
    ?><td><?= $title ?></td><?php       // E5: inline HTML, loop skipped
}

Reported:

  • line 5: Create the object once before the loop and clone it here.
  • line 6: Create the object once before the loop and clone it here.
  • line 8: Statement does not depend on the loop; move it out.
  • line 9: Statement does not depend on the loop; move it out.
  • line 12: Statement does not depend on the loop; move it out.
  • line 15: Statement does not depend on the loop; move it out.
  • line 18: Statement does not depend on the loop; move it out.

Options ​

OptionTypeDefaultEffect
SUGGEST_USING_CLONEboolfalseEnables D13 (object creation on every iteration). The upstream test suite runs with it enabled.

Configure ​

In custos.json:

json
{
  "rules": {
    "DisconnectedForeachInstruction": {
      "enabled": true,
      "options": {
        "SUGGEST_USING_CLONE": false
      }
    }
  }
}

Suppress ​

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

php
// @custos-ignore DisconnectedForeachInstruction

/**
 * @noinspection DisconnectedForeachInstructionInspection
 */

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