With the release of php 5.5 we got ourself a cool new language feature: generators. If you’re new to the concept I suggest you read the excellent blog post from Anthony Ferrara about this subject. In this post I’ll show an example of how I use generators in my every day work.
The other day I was working on a import of a large batch of vacancies from a remote system via a SOAP webservice (yes, C#.NET on the other side). The service returned a container containing the current resultset and a boolean value whether there where more results. Consider the following the classical approach without 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  | 
						<?php class RefreshVacanciesCommand extends ContainerAwareCommand {     // ...     protected function execute(InputInterface $input, OutputInterface $output)     {         $service = $this->getContainer()->get('soap_service');         $batchSize = 100;         $startId = 0;         do {             $response = $service->getAllVacancies($batchSize, $startId);             foreach ($response->GetAllVacanciesResult->Vacancies->Vacancy as $vacancy) {                 // Do actual stuff with $vacancy             }             $startId++;             $more = $response->GetAllVacanciesResult->More;         } while ($more);     } }  | 
					
There’s fundamentally not much wrong with this approach. It works. But let me show a more elegant way of solving this issue which also brings more benefits to the table as you’ll see.
The GetAllVacanciesGenerator class
Take a look at this GetAllVacanciesGenerator who’s responsibility it is to do the cumbersome fetching of the results and notice the yield keyword turning it into a Generator:
| 
					 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  | 
						<?php class GetAllVacanciesGenerator {     /**      * @var Service      */     private $service;     public function __construct(Service $service)     {         $this->service = $service;     }     /**      * @param $batchSize      * @param $startId      * @return \Generator      */     public function getAllVacancies($batchSize, $startId)     {         do {             $response = $this->service->getAllVacancies($batchSize, $startId);             foreach ($response->GetAllVacanciesResult->Vacancies->Vacancy as $vacancy) {                 $startId = $vacancy->Id;                 yield $vacancy;             }             $startId++;             $more = $response->GetAllVacanciesResult->More;         } while ($more);     } }  | 
					
The command is much cleaner now:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18  | 
						<?php class RefreshVacanciesCommand extends ContainerAwareCommand {     // ...     protected function execute(InputInterface $input, OutputInterface $output)     {         $service = $this->getContainer()->get('soap_service');         $batchSize = 100;         $startId = 0;         $generator = new GetAllVacanciesGenerator($service);         foreach ($generator->getAllVacancies($batchSize, 0, null) as $vacancy) {             // Do stuff          }     } }  | 
					
As you can see the command is looking pretty again. I love this kind of improvements. It’s also easy to test this seperate generator class:
| 
					 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  | 
						<?php class GetAllVacanciesGeneratorTest extends \PHPUnit_Framework_TestCase {     public function test_it_should_fetch_all_vacancies()     {         $mock = $this->getMockBuilder('Service')             ->disableOriginalConstructor()             ->getMock();         $mock->expects($this->at(0))             ->method('getAllVacancies')             ->with(5, 0, 'test')             ->will($this->returnValue($this->createResponse(true, [1,2,3,4,5])));         $mock->expects($this->at(1))             ->method('getAllVacancies')             ->with(5, 6, 'test')             ->will($this->returnValue($this->createResponse(false, [6,7])));         $mock->expects($this->exactly(2))             ->method('getAllVacancies');         $generator = new GetAllVacanciesGenerator($mock);         foreach ($generator->getAllVacancies(5, 0) as $result) {             $this->assertInstanceOf('Vacancy', $result);         }     }     private function createResponse($more, $vacancyIds = [])     {         // Helper to ease building of fake Soap response         ...     } }  | 
					
This could also be done with implementing a Iterator but I think the generator is much more readable.