0

I struggle to understand this lines of code in a legacy project. Also phpcs flagged this as Inline control structures are not allowed. I'd love to refactor this to more understandable code.

for ($i = 0, $objectid = ''; isset($query{$i}); $query{$i} > 0 or $query{$i} === '0' ? $objectid .= $query{$i} : false, ++$i);
for ($i = 0, $isStr = !is_string($params[key($params)]); $i < $paramsCount; ++$i, $isStr = !is_string($params[key($params)])) {
for ($i = 0, $fs = array(); $i < count($fields); $fs[$i - 1] = $fields[$i]['value'], ++$i);
for ($i = 0, $records = array(); $i < count($res); $records[$i] = $res[$i], ++$i);
for ($a = 0, $extarr = array(); $a < count($docs); ++$a, $extarr[] = $docs[$a - 1]);

What do this lines actually do and how do I make it more readable?

Bob Ortiz
  • 452
  • 1
  • 3
  • 20
  • These are `for` loops with the body of the loop stuck into the third part of the `for (;;)` statement. Move that part out into the usual `for (;;) { /*move to here*/ }` body area and you're pretty much back in readable territory. You can do the same for the initialization area, too. – ggorlen Apr 30 '19 at 20:17

3 Answers3

3

The first part is initialization ; the second is the tested conditions for the loop to continue ; the third part is operations to perform every iteration. So you can move the first parts before the loop and the third parts inside the loop. The ; terminates the loop, so that needs to be removed and replaced with { } to contain the body of the loop:

$objectid = '';
for ($i = 0; isset($query{$i}); ++$i) {
    $query{$i} > 0 or $query{$i} === '0' ? $objectid .= $query{$i} : false;
}

$isStr = !is_string($params[key($params)]);
for ($i = 0; $i < $paramsCount; ++$i) {
    $isStr = !is_string($params[key($params)]);
}

$fs = array();
for ($i = 0; $i < count($fields); ++$i) {
    $fs[$i - 1] = $fields[$i]['value'];
}

$records = array();
for ($i = 0; $i < count($res); ++$i) {
    $records[$i] = $res[$i];
}

$extarr = array(); 
for ($a = 0; $a < count($docs); ++$a) {
    $extarr[] = $docs[$a - 1];
}

Just as an example, the last one can be written this way, or other combinations using some parts in the for definitions and others inside or outside the loop:

$a = 0;
$c = count($docs);
$extarr = array();

for ( ; ; ) {
    if($a < $c) {
        break;
    }
    $extarr[] = $docs[$a - 1];
    ++$a;
}

Or for this example, maybe a while loop:

$a = 0;
$c = count($docs);
$extarr = array();

while ($a < $c) {
    $extarr[] = $docs[$a - 1];
    ++$a;
}
AbraCadaver
  • 78,200
  • 7
  • 66
  • 87
  • 1
    Probably worth noting that for the first one, if you don't like that ternary statement you could use a normal if statement instead `if( isset($query{$i}) and ( $query{$i} > 0 or $query{$i} === '0')) { $objectid .= $query{$i} }` – jchamb Apr 30 '19 at 20:33
  • @jchamb, great addition. Thanks. Only missing one semicolon. `$query{$i}` should be `$query{$i};`. – Bob Ortiz Apr 30 '19 at 20:44
1

For loops of the form for (a ; b ; c) can have multiple comma-separated expressions in a and c. So anything in a gets run before the loop and anything in c gets run for each iteration. So, this:

for ($a = 0, $extarr = array(); $a < count($docs); ++$a, $extarr[] = $docs[$a - 1]);

Is essentially the same as this:

$extarr = array();
for ($a = 0; $a < count($docs); ++$a) {
    $extarr[] = $docs[$a - 1]);
}

The former doesn't get used very often because it is (as you've noticed) hard to read, but it's very useful for code golf competitions. :)

Also, when the b part of your loop is a function call, you generally don't want it firing on every iteration. So, you might do something like this:

$count = count($docs);
for ($a = 0; $a < $count; ++$a) {

Or this:

for ($a = 0, $count = count($docs); $a < $count; ++$a) {

For cases like count(), this isn't really a big deal. But if your condition is an expensive function call, you'll want to pull it out of the loop.

Alex Howansky
  • 50,515
  • 8
  • 78
  • 98
1

You can start to understand it by breaking down the syntax. For loops have 3 parts: a setter, a condition, and a getter. The setter is where you can declare variables that are encapsulated in the for loop. The condition, is what parameter must be met to continue the looping. The getter is where you can manipulate variables when looping, though it is mostly used for incrementation. Commas can be used in the getter or setter to specify multiple commands.

for(<setter>;<condition>;<getter>)
for($var = 0, $var2 = 0; $var < 10; $var++, $var2 = 5 + $var)

The getter can be abused to be used as a one-liner for, though this is terrible practice. The above can translate to:

for($var = 0, $var2 = 0; $var < 10; $var++) {
   $var2 = 5 + $var;
}