Transcript
Page 1: Generated Power: PHP 5.5 Generators

Generated Power

PHP 5.5 – Generators

Page 2: Generated Power: PHP 5.5 Generators

Who am I?

Mark BakerDesign and Development ManagerInnovEd (Innovative Solutions for Education) Learning Ltd

Coordinator and Developer of:Open Source PHPOffice library

PHPExcel, PHPWord,PHPPowerPoint, PHPProject, PHPVisioMinor contributor to PHP coreOther small open source libraries available on github

@Mark_Baker

https://github.com/MarkBaker

http://uk.linkedin.com/pub/mark-baker/b/572/171

Page 3: Generated Power: PHP 5.5 Generators

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

Page 4: Generated Power: PHP 5.5 Generators

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

Page 5: Generated Power: PHP 5.5 Generators

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) { }

Page 6: Generated Power: PHP 5.5 Generators

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

Page 7: Generated Power: PHP 5.5 Generators

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(); }

Page 8: Generated Power: PHP 5.5 Generators

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; }

Page 9: Generated Power: PHP 5.5 Generators

PHP 5.5 – Generators

foreach (range(0,65535) as $i => $value) {    echo $i , ' -> ' , $value, PHP_EOL;}

function xrange($lower, $upper) {    for ($i = $lower; $i <= $upper; ++$i) {        yield $i;    }}

foreach (xrange(0, 65535) as $i => $value) {    echo $i , ' -> ' , $value, PHP_EOL;}

for($i = 0; $i <= 65535; ++$i) {    echo $i , ' -> ' , $value, PHP_EOL;}

Time: 0.0183 s

Current Memory: 123.44 k

Peak Memory: 5500.11 k

Time: 0.0135 s

Current Memory: 124.33 k

Peak Memory: 126.84 k

Time: 0.0042 s

Current Memory: 122.92 k

Peak Memory: 124.49 k

Page 10: Generated Power: PHP 5.5 Generators

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;    }}

foreach (fibonacci(15) as $i => $value) {    echo $i , ' -> ' , $value, PHP_EOL;}

0 -> 0

1 -> 1

2 -> 1

3 -> 2

4 -> 3

5 -> 5

6 -> 8

7 -> 13

8 -> 21

9 -> 34

10 -> 55

11 -> 89

12 -> 144

13 -> 233

14 -> 377

15 -> 610

Page 11: Generated Power: PHP 5.5 Generators

PHP 5.5 – Generators

function xlColumnRange($lower, $upper) {

    ++$upper;

    for ($i = $lower; $i != $upper; ++$i) {

        yield $i;

    }

}

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 -> 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

Page 12: Generated Power: PHP 5.5 Generators

PHP 5.5 – Generators

$isEven = function ($value) {

    return !($value & 1);

};

$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);

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);

xFilter for Odd Numbers

num is: 1

num is: 3

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

num is: 10

Page 13: Generated Power: PHP 5.5 Generators

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;

}

Page 14: Generated Power: PHP 5.5 Generators

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();

Page 15: Generated Power: PHP 5.5 Generators

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

Page 16: Generated Power: PHP 5.5 Generators

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;}

Page 17: Generated Power: PHP 5.5 Generators

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

Page 18: Generated Power: PHP 5.5 Generators

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

Page 19: Generated Power: PHP 5.5 Generators

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

Page 20: Generated Power: PHP 5.5 Generators

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;

}

Page 21: Generated Power: PHP 5.5 Generators

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);

Page 22: Generated Power: PHP 5.5 Generators

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);

}

LondonNew YorkParisMunich

Page 23: Generated Power: PHP 5.5 Generators

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 );

}

Page 24: Generated Power: PHP 5.5 Generators

PHP 5.5 – Generators

• It is possible to combine a Generator to both send and accept data

Page 25: Generated Power: PHP 5.5 Generators

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

Page 26: Generated Power: PHP 5.5 Generators

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

Page 27: Generated Power: PHP 5.5 Generators

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

1 -> 4

2 -> 27

3 -> 256

4 -> 3125

5 -> 46656

6 -> 823543

7 -> 16777216

8 -> 387420489

9 -> 10000000000

Page 28: Generated Power: PHP 5.5 Generators

PHP 5.5 – Generatorsfunction 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);

    }

}

Page 29: Generated Power: PHP 5.5 Generators

PHP 5.5 – Generators$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;

}

$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;

    }

}

Matthew has 3,8,11

Mark has 6,7,11

Luke has 3,9,12

John has 3,7,12

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

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

Page 30: Generated Power: PHP 5.5 Generators

PHP 5.5 – Generatorspublic 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;

        }

    }

}

Page 31: Generated Power: PHP 5.5 Generators

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);

}

Page 32: Generated Power: PHP 5.5 Generators

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);

    }

}

Page 33: Generated Power: PHP 5.5 Generators

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

Page 34: Generated Power: PHP 5.5 Generators

PHP 5.5 – Generators

?Questions


Top Related