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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <?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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | <?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?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