Some PHP Iterator Fun

I must have had too much free time. Over at Stackoverflow some kind of PHP question pops up from time to time about how to distribute a sequence of data in form of a table or in columns.

You already know the answer, it’s modulus, don’t you? I thought maybe it’s possible to solve this with an Iterator as well. The secret is iterator stacking.

Let’s take the example that you have to fetch X rows from a database and you would like to output each of them into a column or cell of a table. For illustration, I take a simplified example from the PHP manual:

$query = "SELECT Name, CountryCode FROM City ORDER by ID DESC";

...

/* fetch associative array */
while ($row = mysqli_fetch_assoc($result)) {
    printf ("%s (%s)\n", $row["Name"], $row["CountryCode"]);
}

This is a loop and it has iteration. So first of all I was looking how to turn that into an Iterator. The idea was fairly simple: per each iteration, the function is executed and the return value is then the current value of the iteration. So why not create an iterator that accepts a function as a parameter and takes care about the details? I named it FetchIterator, example with a Closure, any callback does work:

$fetchFunction = function() use ($result) {
    return mysqli_fetch_assoc($result);
}
$it = new FetchIterator($fetchFunction);
foreach($it as $row) {
    printf ("%s (%s)\n", $row["Name"], $row["CountryCode"]);
}

That’s not much different than the original loop but the function call is encapsulated inside an iterator. It can be easily replaced with something else like an `ArrayIterator` or just something that behaves similar. For demonstration purposes I created a callback function that returns a value shifted from an array when called (Demo):

array_fetch_item(range('A', 'F'));

$it = new FetchIterator('array_fetch_item');

foreach ($it as $index => $row) {
    printf("%s: %s\n", $index, $row);
}

Output:

0: A
1: B
...
5: F

Nothing spectacular. To convert this now into something with columns, this needs to be properly divided.

Let’s say this little list of characters should be distributed over multiple columns. This could be done by checking the index, do the modulo operation and create a new row each time it’s necessary. Finally at the end there can be missing columns to complete the table, and this needs to be taken care of as well. But wouldn’t it be nicer to just stack two iterators into each other? One for the rows, and the inner one for the columns? Like so:

foreach ($it as $row) {
    # one row at a time
    foreach ($row as $column)
        # one column at a time
    }
}

This can be done with an Iterator because it moves on each time, regardless in which indent. Because the FetchIterator extends from the NoRewindIterator which ships with PHP. And the NoRewindIterator just does not rewind. Normally, foreach rewinds at the beginning, but not with a NoRewindIterator. That’s nice.

But the inner foreach must be limited to the number of columns and in case it can not provide enough data for the four columns it still must continue to iterate and give a padding value, for example a blank string.

So the idea of a PadIterator was born to do exactly this: Limit the number of iterations it will go forward and transparently offer the padding value if needed:

array_fetch_item(range('A', 'F'));

$it = new FetchIterator('array_fetch_item');

$columns = 4;

foreach ($it as $index => $row) {
    foreach (new PadIterator($it, $columns, ' ') as $col) {
        printf('| %s ', $col);
    }
    print("|\n");
}

Output:

| A | B | C | D |
| E | F |   |   |

So this actually works. But if you want to make the output more nice, it becomes a bit akward. The following example uses some more ascii:

array_fetch_item(range(' ', '~'));

$it = new FetchIterator('array_fetch_item');

$columns = 20;

echo str_repeat('+---', $columns), "+\n";
foreach ($it as $firstCell)
{
    foreach (new PadIterator($it, $columns, ' ') as $cell) {
        printf('| %s ', $cell);
    }
    echo "|\n", str_repeat('+---', $columns), "+\n";
}

echo "\n";

Demo and output:

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|   | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / | 0 | 1 | 2 | 3 |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? | @ | A | B | C | D | E | F | G |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z | [ |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| \ | ] | ^ | _ | ` | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| p | q | r | s | t | u | v | w | x | y | z | { | | | } | ~ |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

As this already looks like a table, the TableIterator was not far away. Let’s see some HTML example with an actual table. The TableIterator is a decorating iterator for the main iterator and provides the PadIterator as it’s value so it’s a bit more expressive. Additionally the number of columns can be passed to it’s constructor and in case there are no results, the display of the table can be made optional with a little if:


array_fetch_item(range('A', 'G'));

$it = new FetchIterator('array_fetch_item');

$table = new TableIterator($it, 5);

if ($table->valid()) : ?>
<table border="1">
    <?php foreach ($table as $row => $columns) : ?>
    <tr class="<?php echo $row % 2 ? 'odd' : 'even'; ?>">
        <?php foreach ($columns as $index => $column) : ?>
        <td><?php echo $index, ': ', $column; ?></td>
        <?php endforeach; ?>
    </tr>
    <?php endforeach; ?>
</table>
<?php endif;

Again the Demo and output:

0: A 1: B 2: C 3: D 4: E
5: F 6: G 7: 8: 9:
<table border="1">
        <tr class="even">
                <td>0: A </td>
                <td>1: B </td>
                <td>2: C </td>
                <td>3: D </td>
                <td>4: E </td>
            </tr>
        <tr class="odd">
                <td>5: F </td>
                <td>6: G </td>
                <td>7:  </td>
                <td>8:  </td>
                <td>9:  </td>
            </tr>
    </table>

I’ve put the whole code as well as a gist on github, in case codepad goes down, it’s the last example only which contains all three iterator classes: FetchIterator, PadIterator and TableIterator. This was some fun to play and probably this works well with the many templating engines around that do accept iterators.

Instead of an awful HTML table you can also distribute blog posts inside divs, create <ul><li> lists and such. If you need to limit the number of items, you can use a LimitIterator as well. In the PHP manual, the SPL section has a whole bunch of ready made iterators to use right away.


Some assorted Iterator related questions on Stackoverflow:

Image: Mendeleev’s Periodic Table

About these ads
This entry was posted in Developing, Hakre's Tips, PHP Development, Pressed and tagged , , , , , , , . Bookmark the permalink.

One Response to Some PHP Iterator Fun

  1. Pingback: Iterating over Multiple Iterators at Once | hakre on wordpress

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s