{"id":2106,"date":"2017-10-16T14:22:09","date_gmt":"2017-10-16T13:22:09","guid":{"rendered":"https:\/\/www.entropywins.wtf\/blog\/?p=2106"},"modified":"2021-07-29T21:32:47","modified_gmt":"2021-07-29T20:32:47","slug":"introduction-to-iterators-and-generators-in-php","status":"publish","type":"post","link":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/","title":{"rendered":"Introduction to Iterators and Generators in PHP"},"content":{"rendered":"<p>In this post I demonstrate an effective way to create iterators and generators in PHP and provide an example of a scenario in which using them makes sense.<\/p>\n<p><a href=\"http:\/\/php.net\/manual\/en\/language.generators.syntax.php\">Generators<\/a> have been around since PHP 5.5, and <a href=\"https:\/\/secure.php.net\/manual\/en\/class.iterator.php\">iterators<\/a> have been around since the Planck epoch. Even so, a lot of PHP developers do not know how to use them well and cannot recognize situations in which they are helpful. In this blog post I share insights I have gained over the years, that when sharing, always got an interested response from colleague developers. The post goes beyond the basics, provides a real world example, and includes a few tips and tricks. To not leave out those unfamiliar with Iterators the post starts with the &#8220;What are Iterators&#8221; section, which you can safely skip if you can already answer that question.<\/p>\n<h3>What are Iterators<\/h3>\n<p>PHP has an <code>Iterator<\/code> interface that you can implement to represent a collection. You can loop over an instance of an <code>Iterator<\/code> just like you can loop over an array:<\/p>\n<pre class=\"lang:php decode:true\">function doStuff(Iterator $things) {\n    foreach ($things as $thing) { \/* ... *\/ }\n}<\/pre>\n<p>Why would you bother implementing an <code>Iterator<\/code> subclass rather than just using an array? Let&#8217;s look at an example.<\/p>\n<p>Imagine you have a directory with a bunch of text files. One of the files contains an ASCII NyanCat (<code>~=[,,_,,]:3<\/code>). It is the task of our code to find which file the NyanCat is hiding in.<\/p>\n<p>We can get all the files by doing a <code>glob( $path . '*.txt' )<\/code> and we can get the contents for a file with a <code>file_get_contents<\/code>. We could just have a foreach going over the glob result that does the <code>file_get_contents<\/code>. Luckily we realize this would violate separation of concerns and make the &#8220;does this file contain NyanCat&#8221; logic hard to test since it will be bound to the filesystem access code. Hence we create a function that gets the contents of the files, and ones with our logic in it:<\/p>\n<pre class=\"lang:php decode:true\">function getContentsOfTextFiles(): array {\n    \/\/ glob and file_get_contents\n}\n\nfunction findTextWithNyanCat(array $texts) {\n    foreach ($texts as $text) { if ( \/* ... *\/ ) { \/* ... *\/ } }\n}\n\nfunction findNyanCat() {\n    findTextWithNyanCat(getContentsOfTextFiles());\n}<\/pre>\n<p>While this approach is <strong>decoupled<\/strong>, a big drawback is that now we need to fetch the contents of all files and keep all of that in <strong>memory<\/strong> before we even start executing any of our logic. If NyanCat is hiding in the first file, we&#8217;ll have fetched the contents of all others for nothing. We can avoid this by using an <code>Iterator<\/code>, as they can fetch their values on demand: they are <strong>lazy<\/strong>.<\/p>\n<pre class=\"lang:php decode:true\">class TextFileIterator implements Iterator {\n    \/* ... *\/\n    public function current() {\n        \/\/ return file_get_contents\n    }\n    \/* ... *\/\n}\n\nfunction findTextWithNyanCat(Iterator $texts) {\n    foreach ($texts as $text) { if ( \/* ... *\/ ) { \/* ... *\/ } }\n}\n\nfunction findNyanCat() {\n    findTextWithNyanCat(new TextFileIterator());\n}<\/pre>\n<p>Our <code>TextFileIterator<\/code> gives us a nice place to put all the filesystem code, while to the outside just looking like a collection of texts. The function housing our logic, <code>findTextWithNyanCat<\/code>, does not know that the text comes from the filesystem. This means that if you decide to get texts from the database, you could just create a new DatabaseTextBlobIterator and pass it to the logic function without making any changes to the latter. Similarly, when testing the logic function, you can give it an <code>ArrayIterator<\/code>.<\/p>\n<pre class=\"lang:php decode:true\">function testFindTextWithNyanCat() {\n    \/* ... *\/\n    findTextWithNyanCat(new ArrayIterator(['test text', '~=[,,_,,]:3']));\n    \/* ... *\/\n}<\/pre>\n<p>I wrote more about basic <code>Iterator<\/code> functionality in <a href=\"https:\/\/www.entropywins.wtf\/blog\/2015\/08\/19\/lazy-iterators-in-php-and-python\/\">Lazy iterators in PHP and Python<\/a> and <a href=\"https:\/\/www.entropywins.wtf\/blog\/2014\/07\/14\/some-fun-with-iterators\/\">Some fun with iterators<\/a>. I also blogged about a <a href=\"https:\/\/www.entropywins.wtf\/blog\/2015\/11\/08\/wikidata-wikibase-json-dump-reader\/\">library that provides some (Wikidata specific) iterators<\/a> and a<a href=\"https:\/\/www.entropywins.wtf\/blog\/2016\/01\/25\/replicator-a-cli-tool-for-wikidata\/\"> CLI tool build around an Iterator<\/a>. For more on how generators work, see the off-site post <a href=\"https:\/\/www.sitepoint.com\/generators-in-php\/\">Generators in PHP<\/a>.<\/p>\n<h3>PHP&#8217;s collection type hierarchy<\/h3>\n<p>Let&#8217;s start by looking at PHP&#8217;s type hierarchy for collections as of PHP 7.1. These are the core types that I think are most important:<\/p>\n<ul>\n<li>\u00a0iterable\n<ul>\n<li>array<\/li>\n<li>Traversable\n<ul>\n<li>Iterator\n<ul>\n<li>Generator<\/li>\n<\/ul>\n<\/li>\n<li>IteratorAggregate<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>At the very top we have <code>iterable<\/code>, the supertype of both <code>array<\/code> and <a href=\"https:\/\/secure.php.net\/manual\/en\/class.traversable.php\"><code>Traversable<\/code><\/a>. If you are not familiar with this type or are using a version of PHP older than 7.1, don&#8217;t worry, we don&#8217;t need it for the rest of this blog post.<\/p>\n<p><code>Iterator<\/code> is the subtype of <code>Traversable<\/code>, and the same goes for <code><a href=\"https:\/\/secure.php.net\/manual\/en\/class.iteratoraggregate.php\">IteratorAggregate<\/a><\/code>. The standard library <code>iterator_<\/code> functions such as <code>iterator_to_array<\/code> all take a <code>Traversable<\/code>. This is important since it means you can give them an <code>IteratorAggregate<\/code>, even though it is not an <code>Iterator<\/code>. Later on in this post we&#8217;ll get back to what exactly an <code>IteratorAggregate<\/code> is and why it is useful.<\/p>\n<p>Finally we have <code>Generator<\/code>, which is a subtype of <code>Iterator<\/code>. That means all functions that accept an <code>Iterator<\/code> can be given a <code>Generator<\/code>, and, by extension, that you can use generators in combination with the <a href=\"https:\/\/secure.php.net\/manual\/en\/spl.iterators.php\">Iterator classes in the Standard PHP Library<\/a> such as <code>LimitIterator<\/code> and <code>CachingIterator<\/code>.<\/p>\n<h3>IteratorAggregate + Generator = &lt;3<\/h3>\n<p>Generators are a nice and easy way to create iterators. Often you&#8217;ll only loop over them once, and not have any problem. However beware that generators create iterators that are not rewindable, which means that if you loop over them more than once, you&#8217;ll get an exception.<\/p>\n<p>Imagine the scenario where you pass in a generator to a service that accepts an instance of <code>Traversable<\/code>:<\/p>\n<pre class=\"lang:php decode:true\">$aGenerator = function() { \/* ... yield ... *\/ };\n$aService-&gt;doStuff($aGenerator());<\/pre>\n<pre class=\"lang:php decode:true\">public function doStuff(Traversable $things) {\n    foreach ($things as $thing) { \/* ... *\/ }\n}<\/pre>\n<p>The service class in which <code>doStuff<\/code> resides does not know it is getting a <code>Generator<\/code>, it just knows it is getting a <code>Traversable<\/code>. When working on this class, it is entirely reasonable to iterate though <code>$things<\/code> a second time.<\/p>\n<pre class=\"lang:php decode:true\">public function doStuff(Traversable $things) {\n    foreach ($things as $thing) { \/* ... *\/ }\n    foreach ($things as $thing) { \/* ... *\/ } \/\/ Boom if Generator!\n}<\/pre>\n<p>This blows up if the provided <code>$things<\/code> is a <code>Generator<\/code>, because generators are non-rewindable. Note that it does not matter how you iterate through the value. Calling <code>iterator_to_array<\/code> with <code>$things<\/code> has the exact same result as using it in a foreach loop. Most, if not all, generators I have written, do not use resources or state that inherently prevents them from being rewindable. So the double-iteration issue can be unexpected and seemingly silly.<\/p>\n<p>There is a simple and easy way to get around it though. This is where <code>IteratorAggregate<\/code> comes in. Classes implementing <code>IteratorAggregate<\/code> must implement the <code>getIterator()<\/code> method, which returns a <code>Traversable<\/code>. Creating one of these is extremely trivial:<\/p>\n<pre class=\"lang:php decode:true \">class AwesomeWords implements IteratorAggregate {\n    public function getIterator() {\n        yield 'So';\n        yield 'Much';\n        yield 'Such';\n    }\n}<\/pre>\n<p>If you call <code>getIterator<\/code>, you&#8217;ll get a <code>Generator<\/code> instance, just like you&#8217;d expect. However, normally you never call this method. Instead you use the <code>IteratorAggregate<\/code> just as if it was an <code>Iterator<\/code>, by passing it to code that expects a <code>Traversable<\/code>. (This is also why usually you want to accept <code>Traversable<\/code> and not just <code>Iterator<\/code>.) We can now call our service that loops over the <code>$things<\/code> twice without any problem:<\/p>\n<pre class=\"lang:php decode:true\">$aService-&gt;doStuff(new AwesomeWords()); \/\/ no boom!<\/pre>\n<p>By using <code>IteratorAggregate<\/code> we did not just solve the non-rewindable problem, we also found a good way to share our code. Sometimes it makes sense to use the code of a <code>Generator<\/code> in multiple classes, and sometimes it makes sense to have dedicated tests for the <code>Generator<\/code>. In both cases having a dedicated class and file to put it in is very helpful, and a lot nicer than exposing the generator via some public static function.<\/p>\n<p>For cases where it does not make sense to share a <code>Generator<\/code> and you want to keep it entirely private, you might need to deal with the non-rewindable problem. For those cases you can use my <a href=\"https:\/\/github.com\/JeroenDeDauw\/RewindableGenerator\">Rewindable Generator<\/a> library, which allows making your generators rewindable by wrapping their creation function:<\/p>\n<pre class=\"lang:php decode:true \">$aGenerator = function() { \/* ... yield ... *\/ };\n$aService-&gt;doStuff(new RewindableGenerator($aGenerator));<\/pre>\n<p>Edit: You can, and probably should, use PHPs native <a href=\"https:\/\/secure.php.net\/manual\/en\/class.cachingiterator.php\">CachingIterator<\/a> instead.<\/p>\n<h3>A real-world example<\/h3>\n<p>A few months ago I refactored some code part of the <a href=\"https:\/\/www.entropywins.wtf\/blog\/2016\/11\/24\/implementing-the-clean-architecture\/\">Wikimedia\u00a0Deutschland fundraising codebase<\/a>. This code gets the filesystem paths of email templates by looking in a set of specified directories.<\/p>\n<pre class=\"lang:php decode:true crayon-selected\">private function getMailTemplatesOnDisk(array $mailTemplatePaths): array {\n    $mailTemplatesOnDisk = [];\n\n    foreach ($mailTemplatePaths as $path) {\n        $mailFilesInFolder = glob($path . '\/Mail_*');\n        array_walk($mailFilesInFolder, function(&amp;$filename) {\n            $filename = basename($filename); \/\/ this would cause problems w\/ mail templates in sub-folders\n        } );\n        $mailTemplatesOnDisk = array_merge($mailTemplatesOnDisk, $mailFilesInFolder);\n    }\n\n    return $mailTemplatesOnDisk;\n}<\/pre>\n<p>This code made the class bound to the filesystem, which made it hard to test. In fact, this code was not tested. Furthermore, this code irked me, since I like code to be on the functional side. The <code>array_walk<\/code> mutates its by-reference variable and the assignment at the end of the loop mutates the return variable.<\/p>\n<p>This was refactored using the awesome <code>IteratorAggregate<\/code> + <code>Generator<\/code> combo:<\/p>\n<pre class=\"lang:php decode:true\">class MailTemplateFilenameTraversable implements IteratorAggregate {\n    public function __construct(array $mailTemplatePaths) {\n        $this-&gt;mailTemplatePaths = $mailTemplatePaths;\n    }\n\n    public function getIterator() {\n        foreach ($this-&gt;mailTemplatePaths as $path) {\n            foreach (glob($path . '\/Mail_*') as $fileName) {\n                yield basename($fileName);\n            }\n        }\n    }\n}<\/pre>\n<p>Much easier to read\/understand code, no state mutation whatsoever, good separation of concerns, easier testing and reusability of this collection building code elsewhere.<\/p>\n<p>See also: <a href=\"http:\/\/blog.kevingomez.fr\/2016\/01\/20\/use-cases-for-php-generators\/\">Use cases for PHP generators<\/a> (off-site post).<\/p>\n<h3>Tips and Tricks<\/h3>\n<p>Generators can yield key value pairs:<\/p>\n<pre class=\"lang:php decode:true \">yield \"Iterators\" =&gt; \"are useful\";\nyield \"Generators\" =&gt; \"are awesome\";\n\/\/ [ \"Iterators\" =&gt; \"are useful\", \"Generators\" =&gt; \"are awesome\" ]<\/pre>\n<p>You can use <a href=\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/09\/yield-in-phpunit-data-providers\/\">yield in PHPUnit data providers<\/a>.<\/p>\n<p>You can <em>yield from<\/em> an <code>iterable<\/code>.<\/p>\n<pre class=\"lang:php decode:true\">yield from [1, 2, 3];\nyield from new ArrayIterator([4, 5]);\n\/\/ 1, 2, 3, 4, 5 \n<\/pre>\n<pre class=\"lang:php decode:true\">\/\/ Flattens iterable[] into Generator\nforeach ($collections as $collection) {\n    yield from $collection;\n}<\/pre>\n<p>Thanks for Leszek Manicki and Jan Dittrich for reviewing this blog post.<\/p>\n<h3>Don&#8217;t miss the book!<\/h3>\n<script>(function() {\n\twindow.mc4wp = window.mc4wp || {\n\t\tlisteners: [],\n\t\tforms: {\n\t\t\ton: function(evt, cb) {\n\t\t\t\twindow.mc4wp.listeners.push(\n\t\t\t\t\t{\n\t\t\t\t\t\tevent   : evt,\n\t\t\t\t\t\tcallback: cb\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n})();\n<\/script><!-- Mailchimp for WordPress v4.11.1 - https:\/\/wordpress.org\/plugins\/mailchimp-for-wp\/ --><form id=\"mc4wp-form-1\" class=\"mc4wp-form mc4wp-form-2484\" method=\"post\" data-id=\"2484\" data-name=\"Implementing the Clean Architecture\" ><div class=\"mc4wp-form-fields\">Sign up below to receive news on my upcoming Clean Architecture book, including a discount:\r\n<div id=\"mc_embed_signup\">\r\n<div id=\"mc_embed_signup_scroll\">\r\n<div class=\"mc-field-group\"><input id=\"mce-EMAIL\" class=\"required email\" name=\"EMAIL\" type=\"email\" value=\"\" placeholder=\"Your email address\" \/><\/div>\r\n<div id=\"mce-responses\" class=\"clear\">\r\n<div id=\"mce-error-response\" class=\"response\" style=\"display: none;\"><\/div>\r\n<div id=\"mce-success-response\" class=\"response\" style=\"display: none;\"><\/div>\r\n<\/div>\r\n<div style=\"position: absolute; left: -5000px;\" aria-hidden=\"true\"><input tabindex=\"-1\" name=\"b_5e9453f7bede9f5b0220b2c42_9df25d9e9d\" type=\"text\" value=\"\" \/><\/div>\r\n<div class=\"clear\"><input id=\"mc-embedded-subscribe\" class=\"button\" name=\"subscribe\" type=\"submit\" value=\"Subscribe\" \/><\/div>\r\n<\/div>\r\n<\/div><\/div><label style=\"display: none !important;\">Leave this field empty if you're human: <input type=\"text\" name=\"_mc4wp_honeypot\" value=\"\" tabindex=\"-1\" autocomplete=\"off\" \/><\/label><input type=\"hidden\" name=\"_mc4wp_timestamp\" value=\"1776357529\" \/><input type=\"hidden\" name=\"_mc4wp_form_id\" value=\"2484\" \/><input type=\"hidden\" name=\"_mc4wp_form_element_id\" value=\"mc4wp-form-1\" \/><div class=\"mc4wp-response\"><\/div><\/form><!-- \/ Mailchimp for WordPress Plugin -->\n<h3>View the presentation<\/h3>\n<p><a href=\"https:\/\/bit.ly\/purrfect-iterators\"><img loading=\"lazy\" decoding=\"async\" data-attachment-id=\"2622\" data-permalink=\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/d8pl1xwx4aeau5h\/\" data-orig-file=\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg\" data-orig-size=\"1853,826\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Introduction to Iterators and Generators in PHP\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg\" data-large-file=\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg\" class=\"alignnone wp-image-2622\" src=\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg\" alt=\"\" width=\"799\" height=\"356\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post I demonstrate an effective way to create iterators and generators in PHP and provide an example of&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[7],"tags":[408,374,354,195,197,380],"class_list":["post-2106","post","type-post","status-publish","format-standard","hentry","category-programming","tag-collections","tag-generators","tag-iterator","tag-php","tag-planet-wikimedia","tag-rewindable-generator"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Introduction to Iterators and Generators in PHP - Blog of Jeroen De Dauw<\/title>\n<meta name=\"description\" content=\"Effective ways to create iterators and generators in PHP, including an example of a scenario in which using them makes sense.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Introduction to Iterators and Generators in PHP\" \/>\n<meta property=\"og:description\" content=\"Effective ways to create iterators and generators in PHP, including an example of a scenario in which using them makes sense.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/\" \/>\n<meta property=\"og:site_name\" content=\"Blog of Jeroen De Dauw\" \/>\n<meta property=\"article:published_time\" content=\"2017-10-16T13:22:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-07-29T20:32:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/generator.png\" \/>\n\t<meta property=\"og:image:width\" content=\"682\" \/>\n\t<meta property=\"og:image:height\" content=\"417\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Jeroen\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"Introduction to Iterators and Generators in PHP\" \/>\n<meta name=\"twitter:description\" content=\"Effective ways to create iterators and generators in PHP, including an example of a scenario in which using them makes sense.\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/generator.png\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/JeroenDeDauw\" \/>\n<meta name=\"twitter:site\" content=\"@JeroenDeDauw\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jeroen\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/\"},\"author\":{\"name\":\"Jeroen\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"headline\":\"Introduction to Iterators and Generators in PHP\",\"datePublished\":\"2017-10-16T13:22:09+00:00\",\"dateModified\":\"2021-07-29T20:32:47+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/\"},\"wordCount\":1274,\"commentCount\":2,\"publisher\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"image\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg\",\"keywords\":[\"Collections\",\"Generators\",\"Iterator\",\"PHP\",\"Planet Wikimedia\",\"Rewindable Generator\"],\"articleSection\":[\"Programming\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/\",\"url\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/\",\"name\":\"Introduction to Iterators and Generators in PHP - Blog of Jeroen De Dauw\",\"isPartOf\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg\",\"datePublished\":\"2017-10-16T13:22:09+00:00\",\"dateModified\":\"2021-07-29T20:32:47+00:00\",\"description\":\"Effective ways to create iterators and generators in PHP, including an example of a scenario in which using them makes sense.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#primaryimage\",\"url\":\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg\",\"contentUrl\":\"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg\",\"width\":1853,\"height\":826},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.entropywins.wtf\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Introduction to Iterators and Generators in PHP\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#website\",\"url\":\"https:\/\/www.entropywins.wtf\/blog\/\",\"name\":\"Entropy Wins\",\"description\":\"A blog on Software Architecture, Design and Craftsmanship\",\"publisher\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.entropywins.wtf\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7\",\"name\":\"Jeroen\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g\",\"caption\":\"Jeroen\"},\"logo\":{\"@id\":\"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/jeroendedauw\/\",\"https:\/\/x.com\/https:\/\/twitter.com\/JeroenDeDauw\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Introduction to Iterators and Generators in PHP - Blog of Jeroen De Dauw","description":"Effective ways to create iterators and generators in PHP, including an example of a scenario in which using them makes sense.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/","og_locale":"en_US","og_type":"article","og_title":"Introduction to Iterators and Generators in PHP","og_description":"Effective ways to create iterators and generators in PHP, including an example of a scenario in which using them makes sense.","og_url":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/","og_site_name":"Blog of Jeroen De Dauw","article_published_time":"2017-10-16T13:22:09+00:00","article_modified_time":"2021-07-29T20:32:47+00:00","og_image":[{"width":682,"height":417,"url":"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/generator.png","type":"image\/png"}],"author":"Jeroen","twitter_card":"summary_large_image","twitter_title":"Introduction to Iterators and Generators in PHP","twitter_description":"Effective ways to create iterators and generators in PHP, including an example of a scenario in which using them makes sense.","twitter_image":"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/generator.png","twitter_creator":"@https:\/\/twitter.com\/JeroenDeDauw","twitter_site":"@JeroenDeDauw","twitter_misc":{"Written by":"Jeroen","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#article","isPartOf":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/"},"author":{"name":"Jeroen","@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"headline":"Introduction to Iterators and Generators in PHP","datePublished":"2017-10-16T13:22:09+00:00","dateModified":"2021-07-29T20:32:47+00:00","mainEntityOfPage":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/"},"wordCount":1274,"commentCount":2,"publisher":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"image":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#primaryimage"},"thumbnailUrl":"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg","keywords":["Collections","Generators","Iterator","PHP","Planet Wikimedia","Rewindable Generator"],"articleSection":["Programming"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/","url":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/","name":"Introduction to Iterators and Generators in PHP - Blog of Jeroen De Dauw","isPartOf":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#primaryimage"},"image":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#primaryimage"},"thumbnailUrl":"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg","datePublished":"2017-10-16T13:22:09+00:00","dateModified":"2021-07-29T20:32:47+00:00","description":"Effective ways to create iterators and generators in PHP, including an example of a scenario in which using them makes sense.","breadcrumb":{"@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#primaryimage","url":"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg","contentUrl":"https:\/\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2017\/10\/D8Pl1XwX4AEaU5H.jpeg","width":1853,"height":826},{"@type":"BreadcrumbList","@id":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/16\/introduction-to-iterators-and-generators-in-php\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.entropywins.wtf\/blog\/"},{"@type":"ListItem","position":2,"name":"Introduction to Iterators and Generators in PHP"}]},{"@type":"WebSite","@id":"https:\/\/www.entropywins.wtf\/blog\/#website","url":"https:\/\/www.entropywins.wtf\/blog\/","name":"Entropy Wins","description":"A blog on Software Architecture, Design and Craftsmanship","publisher":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.entropywins.wtf\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/4e2ef14f2ca7dc3a0ac137d1692b66b7","name":"Jeroen","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d62e6b5b8e332335cf17854fac850d9c70ba367c4692872613c3110ebd4e009b?s=96&d=mm&r=g","caption":"Jeroen"},"logo":{"@id":"https:\/\/www.entropywins.wtf\/blog\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/in\/jeroendedauw\/","https:\/\/x.com\/https:\/\/twitter.com\/JeroenDeDauw"]}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p74TBF-xY","jetpack-related-posts":[{"id":1460,"url":"https:\/\/www.entropywins.wtf\/blog\/2015\/08\/19\/lazy-iterators-in-php-and-python\/","url_meta":{"origin":2106,"position":0},"title":"Lazy iterators in PHP and Python","author":"Jeroen","date":"2015-08-19","format":false,"excerpt":"I recently watched Clean Code Episode 34: Pattern Apocalypse, which is about several design patterns, including the Iterator. Afterwards I took some example code from this episode and tried writing it first in PHP and then in Python. While there is no rocket science involved in any of it, I\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1484,"url":"https:\/\/www.entropywins.wtf\/blog\/2015\/11\/08\/wikidata-wikibase-json-dump-reader\/","url_meta":{"origin":2106,"position":1},"title":"Wikidata\/Wikibase Json Dump Reader","author":"Jeroen","date":"2015-11-08","format":false,"excerpt":"I've created a small PHP library to read from, and iterate through, Wikidata\/Wikibase JSON dumps. Wikidata is the free knowledge base that anyone can edit, and serves as a central data repository for Wikipedia and associated projects. Wikibase is a set of free open source software that powers Wikidata. You\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1629,"url":"https:\/\/www.entropywins.wtf\/blog\/2016\/02\/22\/missing-in-php7-collections\/","url_meta":{"origin":2106,"position":2},"title":"Missing in PHP7: Collections","author":"Jeroen","date":"2016-02-22","format":false,"excerpt":"This is the fourth post in my Missing in PHP7 series. The previous one is about Value Objects. In this post I'll outline some problems PHP has with regards to collections, the implications of those problems, and a few mitigation strategies. PHP arrays PHPs main collection type is the associative\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/02\/dfp.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/02\/dfp.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/02\/dfp.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2016\/02\/dfp.jpg?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":2092,"url":"https:\/\/www.entropywins.wtf\/blog\/2017\/10\/09\/yield-in-phpunit-data-providers\/","url_meta":{"origin":2106,"position":3},"title":"Yield in PHPUnit data providers","author":"Jeroen","date":"2017-10-09","format":false,"excerpt":"Initially I started creating a general post about PHP Generators, a feature introduced in PHP 5.5. However since I keep failing to come up with good examples for some cool ways to use Generators, I decided to do this mini post focusing on one such cool usage. PHPUnit data providers\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1372,"url":"https:\/\/www.entropywins.wtf\/blog\/2014\/07\/14\/some-fun-with-iterators\/","url_meta":{"origin":2106,"position":4},"title":"Some fun with iterators","author":"Jeroen","date":"2014-07-14","format":false,"excerpt":"Sometimes you need to loop over a big pile of stuff and execute an action for each item. In the Wikibase software, this for instance occurs when we want to rebuild or refresh a part of our secondary persistence, or when we want to create an export of sorts. Historically\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"tweetiterator","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2014\/07\/tweetiterator.jpg?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":2381,"url":"https:\/\/www.entropywins.wtf\/blog\/2018\/10\/28\/php-typed-properties\/","url_meta":{"origin":2106,"position":5},"title":"PHP Typed Properties","author":"Jeroen","date":"2018-10-28","format":false,"excerpt":"Lately there has been a lot of hype around the typed properties that PHP 7.4 will bring. In this post I outline why typed properties are not as big of a game changer as some people seem to think and how they can lead to shitty code. I start by\u2026","rel":"","context":"In &quot;Programming&quot;","block_context":{"text":"Programming","link":"https:\/\/www.entropywins.wtf\/blog\/category\/programming\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/10\/php-1.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/10\/php-1.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/10\/php-1.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/10\/php-1.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/10\/php-1.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/www.entropywins.wtf\/blog\/wp-content\/uploads\/2018\/10\/php-1.png?resize=1400%2C800&ssl=1 4x"},"classes":[]}],"jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/2106","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/comments?post=2106"}],"version-history":[{"count":22,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/2106\/revisions"}],"predecessor-version":[{"id":2827,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/posts\/2106\/revisions\/2827"}],"wp:attachment":[{"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/media?parent=2106"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/categories?post=2106"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.entropywins.wtf\/blog\/wp-json\/wp\/v2\/tags?post=2106"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}