Skip to content

SlowArrayOperationsInLoop ​

error on by default quick-fix

Group: Performance · PhpStorm name: SlowArrayOperationsInLoopInspection

Two loop anti-patterns:

  • Accumulating merges: $acc = array_merge($acc, $chunk) inside a loop copies the whole accumulator on every iteration (quadratic cost). Collect the chunks and merge once after the loop.
  • Length in a for condition: $i < count($items) re-evaluates the length on every iteration. Compute it once in the initialiser.

Example ​

php
<?php
function collect(array $batches, $repo)
{
    $all = [];
    $map = [];
    foreach ($batches as $batch) {
        $all = array_merge($all, $batch);
        $map = \array_replace($batch, $map);
        try {
            $repo->rows = array_merge_recursive($repo->rows, $batch);
        } finally {
        }
    }
    while ($batch = array_shift($batches)) {
        $map[$batch['id']] = array_merge($map[$batch['id']], $batch);
        if ($batch) {
            $all = array_merge($all, $batch);
        }
        $other = array_merge($all, $batch);
    }
    foreach ($batches as $batch) {
        $all = array_merge($all, $batch);
        break;
    }
    $all = array_merge($all, $map);

    for ($n = 0; $n < count($batches); $n++) {}
    for ($n = 0, $m = 1; strlen($repo->name) >= $n; $n++) {}
    for ($repo->at = 0; $repo->at <= mb_strlen($repo->name); $repo->at++) {}
    for (; count($batches) > $n; $n++) {}
    for ($n = 0, $total = count($batches); $n < $total; $n++) {}
    for ($n = 0; $n < count($batches) - 1; $n++) {}
    for ($n = 0; ; $n++) {}
    return [$all, $map];
}
php
<?php
function collect(array $batches, $repo)
{
    $all = [];
    $map = [];
    foreach ($batches as $batch) {
        $all = array_merge($all, $batch);
        $map = \array_replace($batch, $map);
        try {
            $repo->rows = array_merge_recursive($repo->rows, $batch);
        } finally {
        }
    }
    while ($batch = array_shift($batches)) {
        $map[$batch['id']] = array_merge($map[$batch['id']], $batch);
        if ($batch) {
            $all = array_merge($all, $batch);
        }
        $other = array_merge($all, $batch);
    }
    foreach ($batches as $batch) {
        $all = array_merge($all, $batch);
        break;
    }
    $all = array_merge($all, $map);

    for ($n = 0, $nMax = count($batches); $n < $nMax; $n++) {}
    for ($n = 0, $m = 1, $nMax = strlen($repo->name); $nMax >= $n; $n++) {}
    for ($repo->at = 0, $loopsMax = mb_strlen($repo->name); $repo->at <= $loopsMax; $repo->at++) {}
    for ($nMax = count($batches); $nMax > $n; $n++) {}
    for ($n = 0, $total = count($batches); $n < $total; $n++) {}
    for ($n = 0; $n < count($batches) - 1; $n++) {}
    for ($n = 0; ; $n++) {}
    return [$all, $map];
}

Reported:

  • line 7: 'array_merge(...)' inside a loop re-copies the accumulator each time; merge once after the loop.
  • line 8: 'array_replace(...)' inside a loop re-copies the accumulator each time; merge once after the loop.
  • line 10: 'array_merge_recursive(...)' inside a loop re-copies the accumulator each time; merge once after the loop.
  • line 27: 'count(...)' is re-evaluated on every iteration; compute it once before the loop.
  • line 28: 'strlen(...)' is re-evaluated on every iteration; compute it once before the loop.
  • line 29: 'mb_strlen(...)' is re-evaluated on every iteration; compute it once before the loop.
  • line 30: 'count(...)' is re-evaluated on every iteration; compute it once before the loop.

Configure ​

In custos.json:

json
{
  "rules": {
    "SlowArrayOperationsInLoop": {
      "enabled": false
    }
  }
}

Suppress ​

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

php
// @custos-ignore SlowArrayOperationsInLoop

/**
 * @noinspection SlowArrayOperationsInLoopInspection
 */

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