Just came across a Youtube talk "Iterators in PHP" by Jake Smith published in 2014 that steps through the many different Iterators and some practical examples.
I have reproduced many of the examples Jake used below, running them with PHP 8.1.5 and published the code at https://github.com/toggenation/iterator-training
I also found a short and clearly explained video about Iterators by Dr Adam Nielson which was helpful too. Watch this one first as it's a bit of a primer.
An Important Point About the RecursiveIterators. They require RecursiveIteratorIterator!
You can't just use one of the Recursive*Iterators without wrapping it in RecursiveIteratorIterator if you do use it on its own it will not recurse down the tree
- RecursiveArrayIterator
- RecursiveCachingIterator
- RecursiveCallbackFilterIterator
- RecursiveDirectoryIterator
- RecursiveFilterIterator
- RecursiveIteratorIterator <== Wrap there rest of these with this.
- RecursiveRegexIterator
- RecursiveTreeIterator
<?php
$rdi = new RecursiveDirectoryIterator('/var/www/wms/webroot', FilesystemIterator::SKIP_DOTS);
$rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST);
// now ready to walk the entire tree below the specified root
foreach ($rii as $file) {
/**
* @var SplFileInfo $file
*/
echo $file->getPathname(), "\n";
}
/*
/var/www/wms/webroot/img/x-lg.svg
/var/www/wms/webroot/img/TOGGEN-LOGO.svg
/var/www/wms/webroot/img/toggen-logo-122x27.png
/var/www/wms/webroot/img/down-arrow.svg
/var/www/wms/webroot/img/100pbc-bottling-company.png
/var/www/wms/webroot/img/test-error-icon.png
/var/www/wms/webroot/img/cake.logo.svg
/var/www/wms/webroot/img/TOGGEN-GOAT.svg
/var/www/wms/webroot/img/test-fail-icon.png
/var/www/wms/webroot/img
/var/www/wms/webroot/phpinfo.php
/var/www/wms/webroot/.htaccess
*/
CachingIterator
<?php
// CachingIterator aka LookAhead Iterator
$animals = ['Cat', 'Dog', 'Shark', 'Elephant', 'Tiger', 'Lion', 'Bear', 'Donkey', 'Sheep'];
$collection = new CachingIterator(new ArrayIterator($animals));
var_dump($collection->current()); // null
var_dump($collection->getInnerIterator()->current()); // Cat
foreach ($collection as $animal) {
echo "Current: {$animal}";
if ($collection->hasNext()) {
echo ' - Next:' . $collection->getInnerIterator()->current();
}
echo PHP_EOL;
}
/**
* output
*
* Current: Cat - Next:Dog
* Current: Dog - Next:Shark
* Current: Shark - Next:Elephant
* Current: Elephant - Next:Tiger
* Current: Tiger - Next:Lion
* Current: Lion - Next:Bear
* Current: Bear - Next:Donkey
* Current: Donkey - Next:Sheep
* Current: Sheep
*/
CallBackIterator
<?php
$dirIt = new GlobIterator('/var/www/wms/tmp/*.csv');
$recursiveFiles = new CallbackFilterIterator(
$dirIt,
function ($current, $key, $it) {
return preg_match('|test627|i', $current);
}
);
foreach ($recursiveFiles as $name) {
echo $name . PHP_EOL;
}
echo iterator_count($recursiveFiles) . PHP_EOL;
/**
* output
* /var/www/wms/tmp/TEST627b509d5ac743.01130673.csv
* /var/www/wms/tmp/TEST627b52ac03dad4.63971256.csv
* /var/www/wms/tmp/TEST627b53a94454a9.12415465.csv
* /var/www/wms/tmp/TEST627b54cdee2a88.20247776.csv
* /var/www/wms/tmp/TEST627b55a2530101.43769565.csv
* /var/www/wms/tmp/TEST627b5643c63625.69582969.csv
* 6
*/
DirectoryIterator
<?php
$it = new DirectoryIterator('/var/www/wms/src');
foreach ($it as $file) {
if ($file->isDot()) {
continue;
}
if ($file->isFile()) {
echo "FILE:::: {$file->getFilename()}" . PHP_EOL;
}
if ($file->isDir()) {
echo "DIR:::: {$file->getFilename()}" . PHP_EOL;
}
echo $file->getFilename() . PHP_EOL;
}
/**
* output
*
* DIR:::: Command
* Command
* DIR:::: Log
* Log
* DIR:::: Routing
* Routing
* DIR:::: Job
* Job
* DIR:::: Lib
* Lib
* DIR:::: Model
* Model
* DIR:::: React
* React
* DIR:::: View
* View
* DIR:::: Middleware
* Middleware
* FILE:::: Application.php
* Application.php
* DIR:::: Listener
* Listener
* DIR:::: Error
* Error
* DIR:::: Policy
* Policy
* DIR:::: Console
* Console
* DIR:::: Form
* Form
* DIR:::: Controller
* Controller
* DIR:::: Mailer
* Mailer
*/
FileSystemIterator
<?php
// filesystem iterator has skip dots on by default
$it = new FilesystemIterator('/var/www/wms/src');
foreach ($it as $file) {
// not required
// if ($file->isDot()) {
// continue;
// }
// if ($file->isFile()) {
// echo "{$file->getFilename()}" . PHP_EOL;
// }
// if ($file->isDir()) {
// echo "DIR:::: {$file->getFilename()}" . PHP_EOL;
// }
echo $file->getFilename() . PHP_EOL;
}
// Output
/**
Command
Log
Routing
Job
Lib
Model
React
View
Middleware
Application.php
Listener
Error
Policy
Console
Form
Controller
Mailer
*/
LargeImageFilter and NoCVSFilter Custom Filter and RecursiveFilterIterator
<?php
class RecursiveNoVCSIterator extends RecursiveFilterIterator
{
public function accept(): bool
{
$vcsFolders = ['.git', '.github', '.svn', '.vscode', '.cache'];
/**
* @var SplFileInfo $file File Info
*/
$file = $this->current();
if ($file->isDir() && in_array($file->getFilename(), $vcsFolders)) {
return false;
};
return true;
}
}
$dirs = new RecursiveDirectoryIterator('/var/www/wms', FilesystemIterator::SKIP_DOTS);
$filter = new RecursiveNoVCSIterator($dirs);
$recurseIt = new RecursiveIteratorIterator($filter, RecursiveIteratorIterator::SELF_FIRST);
// foreach ($recurseIt as $path => $info) {
// /**
// * @var SplFileInfo $info Info
// */
// echo $info->getPathname() . PHP_EOL;
// }
class LargeImageFilter extends FilterIterator
{
private array $safeImageTypes = ['png'];
private int $fileSize;
public function __construct(Iterator $it, $imageTypes, $fileSize = 50000)
{
parent::__construct($it);
if (!empty($imageTypes)) {
$this->safeImageTypes = $imageTypes;
}
$this->fileSize = $fileSize;
}
public function accept(): bool
{
$file = $this->current();
if (in_array($file->getExtension(), $this->safeImageTypes) && $file->getSize() > $this->fileSize) {
return true;
}
return false;
}
}
$dirs = new DirectoryIterator('/var/www/wms/webroot/files/templates');
$filter = new LargeImageFilter($dirs, [
'png',
'jpeg'
], 30000);
foreach ($filter as $file) {
echo $file . ': ' . $file->getSize() . PHP_EOL;
}
echo iterator_count($filter) . PHP_EOL;
Generators
<?php
// genorator
function abc()
{
yield 1;
yield 2;
yield 3;
}
echo "Generator\n";
foreach (abc() as $num) {
echo $num . PHP_EOL;
}
// stop a generator
function rabc()
{
yield 1;
return; // don't use a return value
yield 2;
yield 3;
}
echo "Stopped Generator\n";
foreach (rabc() as $num) {
echo $num . PHP_EOL;
}
// Output
/*
Generator
1
2
3
Stopped Generator
1
*/
GlobIterator
<?php
echo "*.php\n";
$it = new GlobIterator('/var/www/wms/webroot/*.php');;
foreach ($it as $path => $file) {
echo $path . ' - ' . $file->getFilename() . PHP_EOL;
}
echo "*.txt\n";
$it = new GlobIterator('/var/www/wms/webroot/*.txt');;
foreach ($it as $path => $file) {
echo $path . ' - ' . $file->getFilename() . PHP_EOL;
}
// Output
/*
*.php
/var/www/wms/webroot/index.php - index.php
/var/www/wms/webroot/info.php - info.php
/var/www/wms/webroot/phpinfo.php - phpinfo.php
*.txt
/var/www/wms/webroot/htaccess.txt - htaccess.txt
*/
InfiniteIterator
<?php
$data = [1, 2, 3];
// as it's name implies it rewinds the pointer to the start
// and keeps going forever
$it = new InfiniteIterator(new ArrayIterator($data));
$ctr = 0;
foreach ($it as $i) {
$ctr++;
// do this or it will go on forever
if ($ctr === 10000) die;
echo "{$i} - {$ctr}" . PHP_EOL;
}
// Output
/*
// ... forever...
2 - 9995
3 - 9996
1 - 9997
2 - 9998
3 - 9999
*/
LimitIterator
<?php
require '../../vendor/autoload.php';
use Faker\Factory;
$page = 5;
$perPage = 10;
$resultOffset = ($page * $perPage) - $perPage;
$faker = Faker\Factory::create();
for ($i = 0; $i < 125; $i++) {
$words[]['name'] = $faker->word() . ' ' . $i;
}
$it = new ArrayIterator($words);
try {
foreach (new LimitIterator($it, $resultOffset, $perPage) as $result) {
echo "{$result['name']}\n";
}
} catch (OutOfBoundsException $e) {
echo "No Records Found" . PHP_EOL;
} catch (Exception $e) {
echo $e->getMessage();
}
// Output
/*
ullam 40
harum 41
deleniti 42
est 43
molestiae 44
aut 45
enim 46
molestias 47
placeat 48
molestiae 49
*/
Extending RecursiveIteratorIterator to create a HTML Menu
<?php
use Enqueue\Router\Recipient;
$menu = [
'Home',
'Register',
'About' => [
'The Team',
"Our Story",
],
'Contact' => [
'Locations',
'Support',
]
];
class HookRecursiveIteratorIterator extends RecursiveIteratorIterator
{
public function beginChildren(): void
{
// echo __METHOD__ . PHP_EOL;
echo '<ul data-where="beginChildren">', PHP_EOL;
}
public function endChildren(): void
{
// echo __METHOD__ . PHP_EOL;
echo '</ul></li>', "\n";
}
}
$it = new HookRecursiveIteratorIterator(
new RecursiveArrayIterator($menu),
RecursiveIteratorIterator::SELF_FIRST
);
ob_start();
echo '<ul>', "\n";
foreach ($it as $k => $v) {
/**
* @var RecursiveIterator $it
*/
if ($it->hasChildren()) {
echo "<li>{$k}\n";
continue;
}
echo "<li>{$v}</li>\n";
}
echo "</ul>\n";
$html = ob_get_clean();
$config = [
'indent' => true,
'output-xhtml' => false,
'wrap' => 200,
'show-body-only' => true,
'clean' => true,
];
// Tidy
$tidy = new tidy;
$tidy->parseString($html, $config, 'utf8');
$tidy->cleanRepair();
// Output
echo $tidy . PHP_EOL;
// Output
/*
<ul>
<li>Home
</li>
<li>Register
</li>
<li>About
<ul data-where="beginChildren">
<li>The Team
</li>
<li>Our Story
</li>
</ul>
</li>
<li>Contact
<ul data-where="beginChildren">
<li>Locations
</li>
<li>Support
</li>
</ul>
</li>
</ul>
*/
ParentIterator
<?php
$rdi = new RecursiveDirectoryIterator('/var/www/wms/src');
// only iterates nodes with children
// ie. directories are parents
// in this case it iterates directories
$dirsOnly = new ParentIterator($rdi);
$iter = new RecursiveIteratorIterator(
$dirsOnly,
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iter as $dir) {
echo $dir . PHP_EOL;
}
// Output
// Note CHILD_FIRST shows the deepest folders in the tree first
/*
/var/www/wms/src/React/shipment-app
/var/www/wms/src/React/put-away/public
/var/www/wms/src/React/put-away/src
/var/www/wms/src/React/put-away
/var/www/wms/src/React
/var/www/wms/src/View/Helper
/var/www/wms/src/View/Cell
/var/www/wms/src/View
/var/www/wms/src/Middleware/UnauthorizedHandler
/var/www/wms/src/Middleware
/var/www/wms/src/Listener
/var/www/wms/src/Error
/var/www/wms/src/Policy
/var/www/wms/src/Console
/var/www/wms/src/Form
/var/www/wms/src/Controller/Component
/var/www/wms/src/Controller
/var/www/wms/src/Mailer
*/
Using RecursiveDirectoryIterator to mass delete files and/or Folders
I have modified this so it not only deletes files but folders also. Caution you can destroy your file system if you specifiy the wrong Directory.
<?php
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('/var/www/wms/logs', FilesystemIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
// run out of memory trying to convert the iterator
// to an array
// var_dump(iterator_to_array($it));
// echo print_r(iterator_to_array($it), true);
// array_map('unlink', iterator_to_array($it));
// this allows it to run without running out of memory
foreach ($it as $k => $v) {
if ($it->hasChildren()) {
echo "Has Children " . $k . "\n";
rmdir($k);
continue;
}
echo "Deleting $k";
unlink($k);
}
RecursiveTreeIterator
<?php
$it = new RecursiveDirectoryIterator('/var/www/wms/webroot', FilesystemIterator::SKIP_DOTS);
foreach (new RecursiveTreeIterator($it) as $path => $file) {
echo $file . "\n";
}
$menu = [
'Home',
'Register',
'About' => [
'The Team',
"Our Story",
],
'Contact' => [
'Locations',
'Support',
]
];
$data = new RecursiveArrayIterator($menu);
$it = new RecursiveTreeIterator($data);
$it->setPrefixPart(
RecursiveTreeIterator::PREFIX_LEFT,
'//'
);
$it->setPrefixPart(
RecursiveTreeIterator::PREFIX_RIGHT,
'||'
);
foreach ($it as $nav) {
echo $nav . PHP_EOL;
}
// Default output
/*
| | |-/var/www/wms/webroot/files/templates/200x150-Product-Sample-Label.png
| | |-/var/www/wms/webroot/files/templates/5-CartonLabel.txt
| | |-/var/www/wms/webroot/files/templates/Screen Shot 2022-02-11 at 11.18.47 am.png
| | |-/var/www/wms/webroot/files/templates/69-NoBarcode3DigitQtySSCCPalletLabel.txt
| | |-/var/www/wms/webroot/files/templates/200x150-Product-Sample-Label.glabels
| | \-/var/www/wms/webroot/files/templates/100x50custom-1.glabels
| |-/var/www/wms/webroot/files/edi
| | \-/var/www/wms/webroot/files/edi/send
| | |-/var/www/wms/webroot/files/edi/send/outbound
| | \-/var/www/wms/webroot/files/ed* i/send/inbound
| |-/var/www/wms/webroot/files/daily_reports
| | |-/var/www/wms/webroot/files/daily_reports/2022-04-_Daily_Shift_Report.pdf
| | |-/var/www/wms/webroot/files/daily_reports/2022-04-14_Daily_Shift_Report.pdf
| | |-/var/www/wms/webroot/files/daily_reports/2022-05-05_Daily_Shift_Report.pdf
| | |-/var/www/wms/webroot/files/daily_reports/20_Daily_Shift_Report.pdf
*/
// With prefixes modified
/*
/|-||Home
//|-||Register
//|-||Array
//| |-||The Team
//| \-||Our Story
//\-||Array
// |-||Locations
// \-||Support
*/
RegexIterator
<?php
$a = new ArrayIterator(['test1', 'test2', 'test3', 'test4', 'aaa']);
$it = new RegexIterator(
$a,
'/^(test)(\d)+/',
RegexIterator::REPLACE
);
$it->replacement = '$2:$1';
foreach ($it as $el) {
echo $el . PHP_EOL;
}
// Output
/*
1:test
2:test
3:test
4:test
*/
0 Comments