Flat pair arrays: three pitfalls from the Related articles block
| The flat pair array | |
| Count entries, not articles | |
| Cap the array you loop over | |
| array_search and index 0 |
The flat pair array
The Related articles block of this site is fed by a flat array: url, title, url, title and so on. The renderer walks it two steps at a time:
<?php
$i = 0;
while ($i < $limit) {
$url = $items[$i];
$name = $items[$i + 1];
$i += 2;
}
Three small mistakes hide in this loop. All three shipped on this very site, so here they are with fixes.
Count entries, not articles
Twelve articles are twenty-four array entries. Capping the loop at 12 shows six articles; capping at 24 shows twelve. And the cap must stay even, otherwise the last iteration reads one past the end of the array:
<?php
// BAD: six articles, not twelve
$limit = 12;
// GOOD: twelve articles, even cap
$limit = min(12 * 2, count($items));
$limit -= $limit % 2;
An odd-length array (a trailing url with no title after a bad edit) would otherwise end in an undefined-index warning or a broken link.
Cap the array you loop over
Do not take the limit from a stale length property when the loop runs over a filtered copy. Removing the current page shrinks the working array by two, so a limit computed before the filter can run past its end:
<?php
// BAD: limit from the unfiltered array
$limit = $this->related_urls_length;
$shown = array_values($filtered);
// GOOD: limit from the array in the loop
$shown = array_values($filtered);
$limit = min(12 * 2, count($shown));
$limit -= $limit % 2;
array_search and index 0
Filtering out the current page usually starts with array_search(). It returns the index, and the first element lives at index 0, which is falsy. A loose comparison silently skips the removal exactly when the current page is first in the list:
<?php
$at = array_search($current, $items);
// BAD: misses index 0, because 0 != false is false
if ($at != false) {
unset($items[$at], $items[$at + 1]);
}
// GOOD: strict comparison, then reindex the pair list
if ($at !== false) {
unset($items[$at], $items[$at + 1]);
$items = array_values($items);
}
The reindex matters too: after unset() the pair loop expects consecutive numeric keys, so array_values() restores them.
Article author: Andrei Olegovich