Generated Power: PHP 5.5 Generators
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Generated Power: PHP 5.5 Generators

on

  • 2,480 views

Slides from presentation on PHP 5.5 Generators given to PHP Brighton group on 16th December 2013, and subsequently to the PHP Cambridge group on 22nd September 2014

Slides from presentation on PHP 5.5 Generators given to PHP Brighton group on 16th December 2013, and subsequently to the PHP Cambridge group on 22nd September 2014

Statistics

Views

Total Views
2,480
Views on SlideShare
2,464
Embed Views
16

Actions

Likes
1
Downloads
10
Comments
0

2 Embeds 16

http://lanyrd.com 11
https://twitter.com 5

Accessibility

Categories

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Generated Power: PHP 5.5 Generators Presentation Transcript

  • 1. Generated Power PHP 5.5 – Generators
  • 2. Who am I? Mark Baker Design and Development Manager InnovEd (Innovative Solutions for Education) Learning Ltd Coordinator and Developer of: Open Source PHPOffice library PHPExcel, PHPWord,PHPPowerPoint, PHPProject, PHPVisio Minor contributor to PHP core Other small open source libraries available on github @Mark_Baker https://github.com/MarkBaker http://uk.linkedin.com/pub/mark-baker/b/572/171
  • 3. PHP 5.5 – Generators • Introduced in PHP 5.5 • Iterable Objects • Can return a series of values, one at a time • Can accept values when sent to the generator • Maintain state between iterations • Similar to enumerators in Ruby
  • 4. PHP 5.5 – Generators • Don’t • Add anything to PHP that couldn’t be done before • Do • • • • Allow you to perform iterative operations without an array to iterate Potentially reduce memory use Potentially faster than iterating over an array Potentially cleaner and shorter code
  • 5. PHP 5.5 – Generators • Automatically created when PHP identifies a function or method containing the “yield” keyword function myGenerator() { yield 1; } $generator = myGenerator(); var_dump($generator); object(Generator)#1 (0) { }
  • 6. PHP 5.5 – Generators • Implemented as an Object final class Generator implements Iterator { void rewind(); bool valid(); mixed current(); mixed key(); void next(); mixed send(mixed $value); } • Can’t be extended
  • 7. PHP 5.5 – Generators function xrange($lower, $upper) { for ($i = $lower; $i <= $upper; ++$i) { yield $i; } } $rangeGenerator = xrange(0,10); while ($rangeGenerator->valid()) { $key = $rangeGenerator->key(); $value = $rangeGenerator->current(); echo $key , ' -> ' , $value, PHP_EOL; $rangeGenerator->next(); }
  • 8. PHP 5.5 – Generators function xrange($lower, $upper) { for ($i = $lower; $i <= $upper; ++$i) { yield $i; } } foreach (xrange(0,10) as $key => $value) { echo $key , ' -> ' , $value, PHP_EOL; }
  • 9. PHP 5.5 – Generators foreach (range(0,65535) as $i => $value) { echo $i , ' -> ' , $value, PHP_EOL; } Time: 0.0183 s Current Memory: 123.44 k Peak Memory: 5500.11 k function xrange($lower, $upper) { for ($i = $lower; $i <= $upper; ++$i) { yield $i; } } foreach (xrange(0, 65535) as $i => $value) { echo $i , ' -> ' , $value, PHP_EOL; } Time: 0.0135 s Current Memory: 124.33 k Peak Memory: 126.84 k Time: 0.0042 s for($i = 0; $i <= 65535; ++$i) { echo $i , ' -> ' , $value, PHP_EOL; } Current Memory: 122.92 k Peak Memory: 124.49 k
  • 10. PHP 5.5 – Generators function fibonacci($count) { $prev = 0; $current = 1; } for ($i = 0; $i < $count; ++$i) { yield $prev; $current = $prev + $current; $prev = $current - $prev; } 0 -> 0 1 -> 1 2 -> 1 3 -> 2 4 -> 3 5 -> 5 6 -> 8 7 -> 13 8 -> 21 foreach (fibonacci(15) as $i => $value) { echo $i , ' -> ' , $value, PHP_EOL; } 9 -> 34 10 -> 55 11 -> 89 12 -> 144 13 -> 233 14 -> 377 15 -> 610
  • 11. PHP 5.5 – Generators foreach (xlColumnRange('A', 'CQ') as $i => $value) { printf('%3d -> %2s', $i, $value); echo (($i > 0) && ($i+1 % 5 == 0)) ? PHP_EOL : "t"; } 0 -> A 1 -> B 2 -> C 3 -> D 4 -> E 5 -> F 6 -> G 7 -> H 8 -> I 9 -> J 10 -> K 11 -> L 12 -> M 13 -> N 14 -> O 15 -> P 16 -> Q 17 -> R 18 -> S 19 -> T 20 -> U 21 -> V 22 -> W 23 -> X 24 -> Y 25 -> function xlColumnRange($lower, $upper) { ++$upper; for ($i = $lower; $i != $upper; ++$i) { yield $i; } } Z 26 -> AA 27 -> AB 28 -> AC 29 -> AD 30 -> AE 31 -> AF 32 -> AG 33 -> AH 34 -> AI 35 -> AJ 36 -> AK 37 -> AL 38 -> AM 39 -> AN 40 -> AO 41 -> AP 42 -> AQ 43 -> AR 44 -> AS 45 -> AT 46 -> AU 47 -> AV 48 -> AW 49 -> AX 50 -> AY 51 -> AZ 52 -> BA 53 -> BB 54 -> BC 55 -> BD 56 -> BE 57 -> BF 58 -> BG 59 -> BH 60 -> BI 61 -> BJ 62 -> BK 63 -> BL 64 -> BM 65 -> BN 66 -> BO 67 -> BP 68 -> BQ 69 -> BR 70 -> BS 71 -> BT 72 -> BU 73 -> BV 74 -> BW 75 -> BX 76 -> BY 77 -> BZ 78 -> CA 79 -> CB 80 -> CC 81 -> CD 82 -> CE 83 -> CF 84 -> CG 85 -> CH 86 -> CI 87 -> CJ 88 -> CK 89 -> CL 90 -> CM 91 -> CN 92 -> CO 93 -> CP 94 -> CQ
  • 12. PHP 5.5 – Generators $isEven = function ($value) { return !($value & 1); }; xFilter for Odd Numbers num is: 1 num is: 3 $isOdd = function ($value) { return $value & 1; }; function xFilter(callable $callback, array $args=array()) { foreach($args as $arg) if (call_user_func($callback, $arg)) yield $arg; } $data = range(1,10); num is: 5 num is: 7 num is: 9 xFilter for Even Numbers num is: 2 num is: 4 num is: 6 num is: 8 echo 'xFilter for Odd Numbers', PHP_EOL; foreach(xFilter($isOdd, $data) as $i) echo('num is: '.$i.PHP_EOL); echo 'xFilter for Even Numbers', PHP_EOL; foreach(xFilter($isEven, $data) as $i) echo('num is: '.$i.PHP_EOL); num is: 10
  • 13. PHP 5.5 – Generators // Endpoint is Brighton $endPoint = new DistanceCalculator( 50.8429, 0.1313 ); function retrieveCityData(DistanceCalculator $endPoint) { $file = new SplFileObject("cities.csv"); $file->setFlags( SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY ); while (!$file->eof()) { $cityData = $file->fgetcsv(); if ($cityData !== NULL) { $city = new StdClass; $city->name = $cityData[0]; $city->latitude = $cityData[1]; $city->longitude = $cityData[2]; $city->distance = $endPoint->calculateDistance($city); yield $city; } } } foreach (retrieveCityData($endPoint) as $city) { echo $city->name, ' is ', sprintf('%.2f', $city->distance), ' miles from Brighton', PHP_EOL; }
  • 14. PHP 5.5 – Generators • Can return both a value and a “pseudo” key • By default • The key is an integer value • Starting with 0 for the first iteration • Incrementing by 1 each iteration • Accessed from foreach() as: foreach(generator() as $key => $value) {} • or using $key = $generatorObject->key();
  • 15. PHP 5.5 – Generators • Default key behaviour can be changed • Syntax is: yield $key => $value; • Unlike array keys: • “Pseudo” keys can be any PHP datatype • “Pseudo” key values can be duplicated
  • 16. PHP 5.5 – Generators function xrange($lower, $upper) { $k = 0; for ($i = $lower; $i <= $upper; ++$i) { yield ++$k => $i; } } foreach (xrange(0,10) as $i => $value) { echo $i , ' -> ' , $value, PHP_EOL; }
  • 17. PHP 5.5 – Generators function duplicateKeys($lower, $upper) { for ($i = $lower; $i <= $upper; ++$i) { yield (($i-1) % 3) + 1 => $i; } } foreach (duplicateKeys(1,15) as $i => $value) { echo $i , ' -> ' , $value, PHP_EOL; } 1 -> 1 2 -> 2 3 -> 3 1 -> 4 2 -> 5 3 -> 6 1 -> 7 2 -> 8 3 -> 9 1 -> 10 2 -> 11 3 -> 12 1 -> 13 2 -> 14 3 -> 15
  • 18. PHP 5.5 – Generators function duplicateKeys($string) { $string = strtolower($string); $length = strlen($string); for ($i = 0; $i < $length; ++$i) { yield strpos($string, $string[$i]) => $string[$i]; } } foreach (duplicateKeys('badass') as $i => $value) { echo $i , ' -> ' , $value, PHP_EOL; } 0 -> b 1 -> a 2 -> d 1 -> a 4 -> s 4 -> s
  • 19. PHP 5.5 – Generators function floatKeys($lower, $upper) { for ($i = $lower; $i <= $upper; ++$i) { yield ($i / 5) => $i; } } foreach (floatKeys(1,16) as $i => $value) { printf( '%0.2f -> %2d' . PHP_EOL, $i, $value ); } 0.20 -> 1 0.40 -> 2 0.60 -> 3 0.80 -> 4 1.00 -> 5 1.20 -> 6 1.40 -> 7 1.60 -> 8 1.80 -> 9 2.00 -> 10 2.20 -> 11 2.40 -> 12 2.60 -> 13 2.80 -> 14 3.00 -> 15 3.20 -> 16
  • 20. PHP 5.5 – Generators // Endpoint is Brighton $endPoint = new DistanceCalculator( 50.8429, 0.1313 ); function retrieveCityData(DistanceCalculator $endPoint) { $file = new SplFileObject("cities.csv"); $file->setFlags( SplFileObject::DROP_NEW_LINE | SplFileObject::SKIP_EMPTY ); while (!$file->eof()) { $cityData = $file->fgetcsv(); if ($cityData !== NULL) { $city = new StdClass; $city->name = $cityData[0]; $city->latitude = $cityData[1]; $city->longitude = $cityData[2]; yield $city => $endPoint->calculateDistance($city); } } } foreach (retrieveCityData($endPoint) as $city => $distance) { echo $city->name, ' is ', sprintf('%.2f', $distance), ' miles from Brighton', PHP_EOL; }
  • 21. PHP 5.5 – Generators • Data can be passed to the generator • Called a “coroutine” when used in this way • Syntax is: $value = yield; • Calling script uses the “send” method: $generatorObject->send($value);
  • 22. PHP 5.5 – Generators $data = array( 'London', 'New York', 'Paris', 'Munich', ); function generatorSend() { while (true) { $cityName = yield; echo $cityName, PHP_EOL; } } $generatorObject = generatorSend(); foreach($data as $value) { $generatorObject->send($value); } London New York Paris Munich
  • 23. PHP 5.5 – Generators $logFileName = __DIR__ . '/error.log'; function logger($logFileName) { $f = fopen($logFileName, 'a'); while ($logentry = yield) { fwrite( $f, (new DateTime())->format('Y-m-d H:i:s ') . $logentry . PHP_EOL ); } } $logger = logger($logFileName); for($i = 0; $i < 12; ++$i) { $logger->send('Message #' . $i ); }
  • 24. PHP 5.5 – Generators • It is possible to combine a Generator to both send and accept data
  • 25. PHP 5.5 – Generators function generatorSend($limit) { for ($i = 1; $i <= $limit; ++$i) { yield $i => pow($i, $i); $continue = yield; if (!$continue) break; } } $generatorObject = generatorSend(100); $carryOnRegardless = true; while ($generatorObject->valid()) { $key = $generatorObject->key(); $value = $generatorObject->current(); if ($key >= 10) $carryOnRegardless = false; $generatorObject->next(); $generatorObject->send($carryOnRegardless); echo $key, ' -> ', $value, PHP_EOL; } 1 -> 1 2 -> 4 3 -> 27 4 -> 256 5 -> 3125 6 -> 46656 7 -> 823543 8 -> 16777216 9 -> 387420489 10 -> 10000000000
  • 26. PHP 5.5 – Generators (Gotcha) function generatorSend($limit) { for ($i = 1; $i <= $limit; ++$i) { yield pow($i, $i); $continue = yield; if (!$continue) break; } } $generatorObject = generatorSend(100); $carryOnRegardless = true; while ($generatorObject->valid()) { $key = $generatorObject->key(); $value = $generatorObject->current(); if ($key >= 10) $carryOnRegardless = false; $generatorObject->next(); $generatorObject->send($carryOnRegardless); echo $key, ' -> ', $value, PHP_EOL; } 0 -> 1 2 -> 4 4 -> 27 6 -> 256 8 -> 3125 10 -> 46656
  • 27. PHP 5.5 – Generators function generatorSend($limit) { for ($i = 1; $i <= $limit; ++$i) { $continue = (yield pow($i, $i)); if (!$continue) break; } } $generatorObject = generatorSend(100); $carryOnRegardless = true; while($generatorObject->valid()) { $key = $generatorObject->key(); $value = $generatorObject->current(); echo $key, ' -> ', $value, PHP_EOL; if ($key >= 10) $carryOnRegardless = false; $generatorObject->send($carryOnRegardless); } 0 1 2 3 4 5 6 7 8 9 -> -> -> -> -> -> -> -> -> -> 1 4 27 256 3125 46656 823543 16777216 387420489 10000000000
  • 28. PHP 5.5 – Generators function bingo($card) { $card = array_flip($card); while (!empty($card)) { $number = yield; echo 'Checking card'; if (isset($card[$number])) { echo ' - Match'; unset($card[$number]); } if (empty($card)) { echo ' *** HOUSE ***'; } else { echo ' - ', count($card), ' number', (count($card) == 1 ? '' : 's'), ' left'; } yield empty($card); } }
  • 29. PHP 5.5 – Generators Matthew has 3,8,11 Mark has 6,7,11 Luke has 3,9,12 John has 3,7,12 $players = array(); foreach($playerNames as $playerName) { shuffle($numbers); $card = array_slice($numbers,0,$cardSize); $player = new StdClass(); $player->name = $playerName; $player->checknumbers = bingo($card); $players[] = $player; } Brian has 1,7,9 Caller Draws 9 Matthew: Checking card - 3 numbers left Mark: Checking card - 3 numbers left Luke: Checking card - Match - 2 numbers left John: Checking card - 3 numbers left Brian: Checking card - Match - 2 numbers left Caller Draws 1 Matthew: Checking card - 3 numbers left Mark: Checking card - 3 numbers left $houseCalled = false; while (!$houseCalled && !empty($numbers)) { $number = array_pop($numbers); echo PHP_EOL, 'Caller Draws ', $number, PHP_EOL; foreach($players as $player) { echo $player->name, ': '; $player->checknumbers->send($number); $houseCalled = $player->checknumbers->current(); if ($houseCalled) { echo PHP_EOL, PHP_EOL, $player->name, ' WINS'; break; } $player->checknumbers->next(); echo PHP_EOL; } } Luke: Checking card - 2 numbers left John: Checking card - 3 numbers left Brian: Checking card - Match - 1 number left Caller Draws 4 Matthew: Checking card - 3 numbers left Mark: Checking card - 3 numbers left Luke: Checking card - 2 numbers left John: Checking card - 3 numbers left Brian: Checking card - 1 number left Caller Draws 7 Matthew: Checking card - 3 numbers left Mark: Checking card - Match - 2 numbers left Luke: Checking card - 2 numbers left John: Checking card - Match - 2 numbers left Brian: Checking card - Match *** HOUSE *** Brian WINS
  • 30. PHP 5.5 – Generators public function search(QuadTreeBoundingBox $boundary) { $results = array(); if ($this->boundingBox->encompasses($boundary) || $this->boundingBox->intersects($boundary)) { // Test each point that falls within the current QuadTree node foreach($this->points as $point) { // Test each point stored in this QuadTree node in turn, // passing back to the caller if it falls within the bounding box if ($boundary->containsPoint($point)) { yield $point; } } // If we have child QuadTree nodes.... if (isset($this->northWest)) { // ... search each child node in turn, merging with any existing results foreach($this->northWest->search($boundary) as $result) yield $result; foreach($this->northEast->search($boundary) as $result) yield $result; foreach($this->southWest->search($boundary) as $result) yield $result; foreach($this->southEast->search($boundary) as $result) yield $result; } } }
  • 31. PHP 5.5 – Generators $files = glob('server*.log'); $domain = 'innovedtest.co.uk'; $pregDomain = '/' . preg_quote($domain) . '/'; function searchLogData($fileName, $searchDomain) { $file = new SplFileObject($fileName); while (!$file->eof()) { $logData = $file->fgets(); if ($logData !== NULL && preg_match($searchDomain, $logData)) { yield $logData; } } } $fileGeneratorArray = array(); foreach($files as $filename) { $fileGeneratorArray[] = searchLogData($filename, $pregDomain); }
  • 32. PHP 5.5 – Generators $output = fopen('php://output', 'w'); while (!empty($fileGeneratorArray)) { foreach($fileGeneratorArray as $key => $fileGenerator) { $result = $fileGenerator->current(); $fileGenerator->next(); if (!$fileGenerator->valid()) { unset($fileGeneratorArray[$key]); } fwrite($output, $result); } }
  • 33. PHP 5.5 – Generators • Additional Reading: • http://blog.ircmaxell.com/2012/07/what-generators-can-do-for-you.html • http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html
  • 34. PHP 5.5 – Generators ? Questions