In the preceding set of recipes we demonstrated the use of iterators provided in the PHP 7 SPL. But what if this set doesn't provide you with what is needed for a given project? One solution would be to develop a function that, instead of building an array that is then returned, uses the yield keyword to return values progressively by way of iteration. Such a function is referred to as a generator. In fact, in the background, the PHP engine will automatically convert your function into a special built-in class called Generator.
There are several advantages to this approach. The main benefit is seen when you have a large container to traverse (that is, parsing a massive file). The traditional approach has been to build up an array, and then return that array. The problem with this is that you are effectively doubling the amount of memory required! Also, performance is affected in that results are only achieved once the final array has been returned.
ArrayIterator, FilterIterator, and LimitIterator.filteredResultsGenerator() function. We then calculate the offset based on the page number and limit (that is, number of items per page). Next, we loop through the array, apply the filter, and continue the loop if the offset has not yet been reached, or break if the limit has been reached:function filteredResultsGenerator(array $array, $filter, $limit = 10, $page = 0)
{
$max = count($array);
$offset = $page * $limit;
foreach ($array as $key => $value) {
if (!stripos($value, $filter) !== FALSE) continue;
if (--$offset >= 0) continue;
if (--$limit <= 0) break;
yield $value;
}
}yield keyword. The effect of this keyword is to signal the PHP engine to produce a Generator instance and encapsulate the code.To demonstrate the use of the filteredResultsGenerator() function we'll have you implement a web application that scans a web page and produces a filtered and paginated list of URLs hoovered from HREF attributes.
First you need to add the code for the filteredResultsGenerator() function to the library file used in the previous recipe, then place the functions described previously into an include file, chap_03_developing_functions_iterators_library.php.
Next, define a test script, chap_03_developing_functions_using_generator.php, that includes both the function library as well as the file that defines Application\Web\Hoover, described in Chapter 1, Building a Foundation:
include (__DIR__ . DIRECTORY_SEPARATOR . 'chap_03_developing_functions_iterators_library.php'); include (__DIR__ . '/../Application/Web/Hoover.php');
You will then need to gather input from the user regarding which URL to scan, what string to use as a filter, how many items per page, and the current page number.
$url = trim(strip_tags($_GET['url'] ?? '')); $filter = trim(strip_tags($_GET['filter'] ?? '')); $limit = (int) ($_GET['limit'] ?? 10); $page = (int) ($_GET['page'] ?? 0);
You are then in a position to define variables used in links for previous and next pages in the paginated list. Note that you could also apply a sanity check to make sure the next page doesn't go off the end of the result set. For the sake of brevity, such a sanity check was not applied in this example:
$next = $page + 1;
$prev = $page - 1;
$base = '?url=' . htmlspecialchars($url)
. '&filter=' . htmlspecialchars($filter)
. '&limit=' . $limit
. '&page=';We then need to create an Application\Web\Hoover instance and grab HREF attributes from the target URL:
$vac = new Application\Web\Hoover(); $list = $vac->getAttribute($url, 'href');
Finally, we define HTML output that renders an input form and runs our generator through the htmlList() function described previously:
<form> <table> <tr> <th>URL</th> <td> <input type="text" name="url" value="<?= htmlspecialchars($url) ?>"/> </td> </tr> <tr> <th>Filter</th> <td> <input type="text" name="filter" value="<?= htmlspecialchars($filter) ?>"/></td> </tr> <tr> <th>Limit</th> <td><input type="text" name="limit" value="<?= $limit ?>"/></td> </tr> <tr> <th> </th><td><input type="submit" /></td> </tr> <tr> <td> </td> <td> <a href="<?= $base . $prev ?>"><-- PREV | <a href="<?= $base . $next ?>">NEXT --></td> </tr> </table> </form> <hr> <?= htmlList(filteredResultsGenerator( $list, $filter, $limit, $page)); ?>
Here is an example of the output:
