Slide 1

Slide 1 text

AUGUST 22, 2018 #MEETUP @PHPUGDD

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

github.com/hollodotme @hollodotme EXECUTIVE PRODUCER holger woltersdorf

Slide 4

Slide 4 text

github.com/hollodotme @[email protected] EXECUTIVE PRODUCER holger woltersdorf

Slide 5

Slide 5 text

CHAPTER 1: GENERATORS @hollodotme

Slide 6

Slide 6 text

FITZ'PLANATION The heart of a generator function is the yield keyword. 
 In its simplest form, a yield statement looks much like a return statement, except that instead of stopping execution of the function and returning, yield instead provides a value to the code looping over the generator and pauses execution of the generator function. php.net @hollodotme

Slide 7

Slide 7 text

FITZ'PLANATION BOY, WHAT THE HELL ARE YOU TALKING ABOUT? @hollodotme

Slide 8

Slide 8 text

JEMMA'LOC function generate() { yield 1; } $generator = generate(); echo 'Type: ', gettype($generator), PHP_EOL; echo 'Class: ', get_class($generator), PHP_EOL; echo 'Is ', (is_callable($generator) ? 'callable' : 'not callable'), PHP_EOL; $generatorClone = clone $generator; @hollodotme

Slide 9

Slide 9 text

JEMMA'LOC function generate() { yield 1; } $generator = generate(); echo 'Type: ', gettype($generator), PHP_EOL; echo 'Class: ', get_class($generator), PHP_EOL; echo 'Is ', (is_callable($generator) ? 'callable' : 'not callable'), PHP_EOL; $generatorClone = clone $generator; — Type: object Class: Generator Is not callable Fatal error: Uncaught Error: Trying to clone an uncloneable object of class Generator @hollodotme

Slide 10

Slide 10 text

JEMMA'LOC function range( int $start, int $limit ) : array { $elements = []; for ( $i = $start; $i !<= $limit; $i!++ ) { $elements[] = $i; } return $elements; } # vs. function xrange( int $start, int $limit ) : Generator { for ( $i = $start; $i !<= $limit; $i!++ ) { yield $i; } } @hollodotme

Slide 11

Slide 11 text

JEMMA'LOC foreach ( range( 1, 10000 ) as $i ) { print $i . '.'; } # 2 MiB foreach ( range( 1, 100000 ) as $i ) { print $i . '.'; } # 6 MiB foreach ( range( 1, 1000000 ) as $i ) { print $i . '.'; } # 34 MiB @hollodotme

Slide 12

Slide 12 text

JEMMA'LOC foreach ( xrange( 1, 10000 ) as $i ) { print $i . '.'; } # 2 MiB foreach ( xrange( 1, 100000 ) as $i ) { print $i . '.'; } # 2 MiB foreach ( xrange( 1, 1000000 ) as $i ) { print $i . '.'; } # 2 MiB @hollodotme

Slide 13

Slide 13 text

JEMMA'LOC # OPCODES OF RANGE() ~$ php -d vld.active=1 -d vld.execute=0 -f range.func.php number of ops: 22 compiled vars: !0 = $start, !1 = $limit, !2 = $step, !3 = $elements, !4 = $i # OPCODES OF XRANGE() ~$ php -d vld.active=1 -d vld.execute=0 -f xrange.func.php number of ops: 16 compiled vars: !0 = $start, !1 = $limit, !2 = $step, !3 = $i @hollodotme

Slide 14

Slide 14 text

JEMMA'LOC function generate() { print 'generate - start' . PHP_EOL; for ( $i = 1; $i !<= 5; $i!++ ) { print 'generate - yielding!!...' . PHP_EOL; yield $i; print 'generate - continued' . PHP_EOL; } print 'generate - end' . PHP_EOL; } $generator = generate(); print 'Generator created' . PHP_EOL; while ( $generator!->valid() ) { print 'Loop gets current value' . PHP_EOL; print $generator!->current() . PHP_EOL; $generator!->next(); } @hollodotme

Slide 15

Slide 15 text

JEMMA'LOC function generate() { print 'generate - start' . PHP_EOL; for ( $i = 1; $i !<= 5; $i!++ ) { print 'generate - yielding!!...' . PHP_EOL; yield $i; print 'generate - continued' . PHP_EOL; } print 'generate - end' . PHP_EOL; } $generator = generate(); print 'Generator created' . PHP_EOL; while ( $generator!->valid() ) { print 'Loop gets current value' . PHP_EOL; print $generator!->current() . PHP_EOL; $generator!->next(); } Generator created generate - start generate - yielding!!... Loop gets current value 1 generate - continued generate - yielding!!... Loop gets current value 2 generate - continued generate - yielding!!... Loop gets current value 3 generate - continued generate - yielding!!... Loop gets current value 4 generate - continued generate - yielding!!... Loop gets current value 5 generate - continued generate - end @hollodotme

Slide 16

Slide 16 text

JEMMA'LOC function generate() { print 'generate - start' . PHP_EOL; for ( $i = 1; $i !<= 5; $i!++ ) { print 'generate - yielding!!...' . PHP_EOL; yield $i; print 'generate - continued' . PHP_EOL; } print 'generate - end' . PHP_EOL; } $generator = generate(); print 'Generator created' . PHP_EOL; while ( $generator!->valid() ) { print 'Loop gets current value' . PHP_EOL; print $generator!->current() . PHP_EOL; $generator!->next(); } Generator created generate - start generate - yielding!!... Loop gets current value 1 generate - continued generate - yielding!!... Loop gets current value 2 generate - continued generate - yielding!!... Loop gets current value 3 generate - continued generate - yielding!!... Loop gets current value 4 generate - continued generate - yielding!!... Loop gets current value 5 generate - continued generate - end @hollodotme

Slide 17

Slide 17 text

JEMMA'LOC function generate() { print 'generate - start' . PHP_EOL; for ( $i = 1; $i !<= 5; $i!++ ) { print 'generate - yielding!!...' . PHP_EOL; yield $i; print 'generate - continued' . PHP_EOL; } print 'generate - end' . PHP_EOL; } $generator = generate(); print 'Generator created' . PHP_EOL; while ( $generator!->valid() ) { print 'Loop gets current value' . PHP_EOL; print $generator!->current() . PHP_EOL; $generator!->next(); } Generator created generate - start generate - yielding!!... Loop gets current value 1 generate - continued generate - yielding!!... Loop gets current value 2 generate - continued generate - yielding!!... Loop gets current value 3 generate - continued generate - yielding!!... Loop gets current value 4 generate - continued generate - yielding!!... Loop gets current value 5 generate - continued generate - end @hollodotme

Slide 18

Slide 18 text

JEMMA'LOC function generate() { print 'generate - start' . PHP_EOL; for ( $i = 1; $i !<= 5; $i!++ ) { print 'generate - yielding!!...' . PHP_EOL; yield $i; print 'generate - continued' . PHP_EOL; } print 'generate - end' . PHP_EOL; } $generator = generate(); print 'Generator created' . PHP_EOL; while ( $generator!->valid() ) { print 'Loop gets current value' . PHP_EOL; print $generator!->current() . PHP_EOL; $generator!->next(); } Generator created generate - start generate - yielding!!... Loop gets current value 1 generate - continued generate - yielding!!... Loop gets current value 2 generate - continued generate - yielding!!... Loop gets current value 3 generate - continued generate - yielding!!... Loop gets current value 4 generate - continued generate - yielding!!... Loop gets current value 5 generate - continued generate - end @hollodotme

Slide 19

Slide 19 text

JEMMA'LOC function generate() { print 'generate - start' . PHP_EOL; for ( $i = 1; $i !<= 5; $i!++ ) { print 'generate - yielding!!...' . PHP_EOL; yield $i; print 'generate - continued' . PHP_EOL; } print 'generate - end' . PHP_EOL; } $generator = generate(); print 'Generator created' . PHP_EOL; while ( $generator!->valid() ) { print 'Loop gets current value' . PHP_EOL; print $generator!->current() . PHP_EOL; $generator!->next(); } Generator created generate - start generate - yielding!!... Loop gets current value 1 generate - continued generate - yielding!!... Loop gets current value 2 generate - continued generate - yielding!!... Loop gets current value 3 generate - continued generate - yielding!!... Loop gets current value 4 generate - continued generate - yielding!!... Loop gets current value 5 generate - continued generate - end @hollodotme

Slide 20

Slide 20 text

AND… GENERATORS IMPLEMENT IT… @hollodotme

Slide 21

Slide 21 text

AND… GENERATORS IMPLEMENT IT… ITE… @hollodotme

Slide 22

Slide 22 text

AND… GENERATORS IMPLEMENT IT… ITE… ITERATOR! @hollodotme

Slide 23

Slide 23 text

FITZ'PLANATION The primary advantage of generators is their simplicity. Much less boilerplate code has to be written compared to implementing an Iterator class, and the code is generally much more readable. php.net @hollodotme

Slide 24

Slide 24 text

JEMMA'LOC $generator = xrange( 1, 100 ); while ( $generator!->valid() ) { echo $generator!->current() . '.'; $generator!->next(); } # The Iterator interface interface Iterator extends Traversable { public function current(); public function next(); public function key(); public function valid(); public function rewind(); } @hollodotme

Slide 25

Slide 25 text

OH… GOOD TO SEE SOMETHING FAMILIAR
 -
 SOMETHING I CAN COUNT ON! @hollodotme

Slide 26

Slide 26 text

BUT YOU CAN’T, PHIL. @hollodotme

Slide 27

Slide 27 text

IT’S NOT COUNTABLE! @hollodotme

Slide 28

Slide 28 text

JEMMA'LOC $generator = xrange( 1, 100 ); count($generator); — PHP Warning: count(): Parameter must be an array or an object that implements Countable @hollodotme

Slide 29

Slide 29 text

FITZ'PLANATION A generator is a highly dynamic construct with an iterator API, but an unknown number of elements. It’s not a data structure like an array or a common object. hw @hollodotme

Slide 30

Slide 30 text

JEMMA'LOC $generator = xrange( 1, 100 ); $count = count( iterator_to_array( $generator ) ); echo 'Count: ', $count, PHP_EOL; $count = 0; foreach ( $generator as $item ) { $count!++; } echo 'Count: ', $count, PHP_EOL; @hollodotme

Slide 31

Slide 31 text

JEMMA'LOC $generator = xrange( 1, 100 ); $count = count( iterator_to_array( $generator ) ); echo 'Count: ', $count, PHP_EOL; $count = 0; foreach ( $generator as $item ) { $count!++; } echo 'Count: ', $count, PHP_EOL; — Count: 100 Exception: Cannot traverse an already closed generator @hollodotme

Slide 32

Slide 32 text

JEMMA'LOC $generator = xrange( 1, 100 ); $count = count( iterator_to_array( $generator ) ); echo 'Count: ', $count, PHP_EOL; $generator!->rewind(); $count = 0; foreach ( $generator as $item ) { $count!++; } echo 'Count: ', $count, PHP_EOL; @hollodotme

Slide 33

Slide 33 text

JEMMA'LOC $generator = xrange( 1, 100 ); $count = count( iterator_to_array( $generator ) ); echo 'Count: ', $count, PHP_EOL; $generator!->rewind(); $count = 0; foreach ( $generator as $item ) { $count!++; } echo 'Count: ', $count, PHP_EOL; — Count: 100 Exception: Cannot rewind a generator that was already run @hollodotme

Slide 34

Slide 34 text

What the F***?! @hollodotme

Slide 35

Slide 35 text

FITZ'PLANATION […] generators are forward-only iterators, and cannot be rewound once iteration has started. This also means that the same generator can't be iterated over multiple times: the generator will need to be rebuilt by calling the generator function again. php.net @hollodotme

Slide 36

Slide 36 text

YOU KNOW, IT'S LIKE ... @hollodotme

Slide 37

Slide 37 text

GONE. @hollodotme

Slide 38

Slide 38 text

JEMMA'LOC $generator = xrange( 1, 100 ); $count = count( iterator_to_array( $generator ) ); echo 'Count: ', $count, PHP_EOL; # Create new generator $generator = xrange( 1, 100 ); $count = 0; foreach ( $generator as $item ) { $count!++; } echo 'Count: ', $count, PHP_EOL; — Count: 100 Count: 100 @hollodotme

Slide 39

Slide 39 text

YIELD FROM … @hollodotme

Slide 40

Slide 40 text

JEMMA'LOC function countToTen() : Generator { yield 1; for ( $i = 2; $i < 4; $i!++ ) { yield $i; } yield from [4, 5]; yield from json_decode( '[6,7]' ); yield from eightNine(); yield 10; } function eightNine() : Generator { yield '8 ' !=> 9; } foreach ( countToTen() as $key !=> $number ) print (is_string($key) ? $key : '') . $number . ' '; @hollodotme

Slide 41

Slide 41 text

JEMMA'LOC function countToTen() : Generator { yield 1; for ( $i = 2; $i < 4; $i!++ ) { yield $i; } yield from [4, 5]; yield from json_decode( '[6,7]' ); yield from eightNine(); yield 10; } function eightNine() : Generator { yield '8 ' !=> 9; } foreach ( countToTen() as $key !=> $number ) print (is_string($key) ? $key : '') . $number . ' '; — 1 2 3 4 5 6 7 8 9 10 @hollodotme

Slide 42

Slide 42 text

YIELD ASS=>OC @hollodotme

Slide 43

Slide 43 text

JEMMA'LOC function gen() { $key = new stdClass(); $key!->property = 'I am an object!'; yield $key !=> '& I am a simple string!'; } $generator = gen(); echo $generator!->key()!->property, ' ', $generator!->current(); @hollodotme

Slide 44

Slide 44 text

JEMMA'LOC function gen() { $key = new stdClass(); $key!->property = 'I am an object!'; yield $key !=> '& I am a simple string!'; } $generator = gen(); echo $generator!->key()!->property, ' ', $generator!->current(); — I am an object! & I am a simple string! @hollodotme

Slide 45

Slide 45 text

JEMMA'LOC $key = new stdClass(); $key!->property = 'I am an object!'; $array = [$key !=> '& I am a simple string.']; echo key($array)!->property, ' ', current($array); @hollodotme

Slide 46

Slide 46 text

JEMMA'LOC $key = new stdClass(); $key!->property = 'I am an object!'; $array = [$key !=> '& I am a simple string.']; echo key($array)!->property, ' ', current($array); — PHP Warning: Illegal offset type in line 4 PHP Notice: Trying to get property 'property' of non-object in line 6 NO OUTPUT!!! @hollodotme

Slide 47

Slide 47 text

YIELD & RETURN @hollodotme

Slide 48

Slide 48 text

JEMMA'LOC function genFibonacci( int $loops ) : Generator { $fibonacci = ''; for ( $i = 0; $i < $loops; $i!++ ) { yield $i; $fibonacci !.= round( pow( (sqrt( 5 ) + 1) / 2, $i ) / sqrt( 5 ) ); } return $fibonacci; } $generator = genFibonacci( 10 ); while ( $generator!->valid() ) { print $generator!->current(); $generator!->next(); } print PHP_EOL . 'Fibonacci: ' . $generator!->getReturn() . PHP_EOL; @hollodotme

Slide 49

Slide 49 text

JEMMA'LOC function genFibonacci( int $loops ) : Generator { $fibonacci = ''; for ( $i = 0; $i < $loops; $i!++ ) { yield $i; $fibonacci !.= round( pow( (sqrt( 5 ) + 1) / 2, $i ) / sqrt( 5 ) ); } return $fibonacci; } $generator = genFibonacci( 10 ); while ( $generator!->valid() ) { print $generator!->current(); $generator!->next(); } print PHP_EOL . 'Fibonacci: ' . $generator!->getReturn() . PHP_EOL; @hollodotme

Slide 50

Slide 50 text

JEMMA'LOC function genFibonacci( int $loops ) : Generator { $fibonacci = ''; for ( $i = 0; $i < $loops; $i!++ ) { yield $i; $fibonacci !.= round( pow( (sqrt( 5 ) + 1) / 2, $i ) / sqrt( 5 ) ); } return $fibonacci; } $generator = genFibonacci( 10 ); while ( $generator!->valid() ) { print $generator!->current(); $generator!->next(); } print PHP_EOL . 'Fibonacci: ' . $generator!->getReturn() . PHP_EOL; — 0123456789 Fibonacci: 0112358132134 @hollodotme

Slide 51

Slide 51 text

PSA: PHP >=7.0 ONLY @hollodotme

Slide 52

Slide 52 text

UPGRADE YOUR STUFF! @hollodotme

Slide 53

Slide 53 text

SERIOUSLY @hollodotme

Slide 54

Slide 54 text

RECAP • A yield in a function or method always creates a Generator object • yield can provide a value to the caller of the Generator and pauses execution of its local context • Generator implements the Iterator interface, but can be consumed only once and forward-only • Generator can return a value when its execution stops @hollodotme

Slide 55

Slide 55 text

THIS IS A NICE CAR! @hollodotme

Slide 56

Slide 56 text

Mack, 
 CAN WE MAKE IT FLY? @hollodotme

Slide 57

Slide 57 text

CHAPTER 2: COROUTINES @hollodotme

Slide 58

Slide 58 text

FITZ'PLANATION Coroutines and concurrency are largely orthogonal. Coroutines are a general control structure whereby flow control is cooperatively passed between two different routines without returning. stackoverflow.com @hollodotme

Slide 59

Slide 59 text

JEMMA'LOC function queue( PDO $pdo ) : Generator { $pdo!->query( 'CREATE TABLE queue (message TEXT)' ); $statement = $pdo!->prepare( 'INSERT INTO queue (message) VALUES (:message)' ); while ( true ) { $message = yield; $statement!->execute( ['message' !=> $message] ); print 'Sent: ' . $message . PHP_EOL; } } $queue = queue( new PDO( 'sqlite!::memory:' ) ); $queue!->send( 'Foo' ); $queue!->send( 'Bar' ); @hollodotme

Slide 60

Slide 60 text

JEMMA'LOC function queue( PDO $pdo ) : Generator { $pdo!->query( 'CREATE TABLE queue (message TEXT)' ); $statement = $pdo!->prepare( 'INSERT INTO queue (message) VALUES (:message)' ); while ( true ) { $message = yield; $statement!->execute( ['message' !=> $message] ); print 'Sent: ' . $message . PHP_EOL; } } $queue = queue( new PDO( 'sqlite!::memory:' ) ); $queue!->send( 'Foo' ); $queue!->send( 'Bar' ); @hollodotme

Slide 61

Slide 61 text

JEMMA'LOC function queue( PDO $pdo ) : Generator { $pdo!->query( 'CREATE TABLE queue (message TEXT)' ); $statement = $pdo!->prepare( 'INSERT INTO queue (message) VALUES (:message)' ); while ( true ) { $message = yield; $statement!->execute( ['message' !=> $message] ); print 'Sent: ' . $message . PHP_EOL; } } $queue = queue( new PDO( 'sqlite!::memory:' ) ); $queue!->send( 'Foo' ); $queue!->send( 'Bar' ); — Sent: Foo Sent: Bar @hollodotme

Slide 62

Slide 62 text

JEMMA'LOC function queue( PDO $pdo ) : Generator { $pdo!->query( 'CREATE TABLE queue (message TEXT)' ); $statement = $pdo!->prepare( 'INSERT INTO queue (message) VALUES (:message)' ); $counter = 0; while ( true ) { $message = yield $counter!++; $statement!->execute( ['message' !=> $message] ); print 'Sent: ' . $message; } } $queue = queue( new PDO( 'sqlite!::memory:' ) ); $queue!->send( 'Foo' ); print ' (' . $queue!->current() . ')' . PHP_EOL; $queue!->send( 'Bar' ); print ' (' . $queue!->current() . ')' . PHP_EOL; @hollodotme

Slide 63

Slide 63 text

JEMMA'LOC function queue( PDO $pdo ) : Generator { $pdo!->query( 'CREATE TABLE queue (message TEXT)' ); $statement = $pdo!->prepare( 'INSERT INTO queue (message) VALUES (:message)' ); $counter = 0; while ( true ) { $message = yield $counter!++; $statement!->execute( ['message' !=> $message] ); print 'Sent: ' . $message; } } $queue = queue( new PDO( 'sqlite!::memory:' ) ); $queue!->send( 'Foo' ); print ' (' . $queue!->current() . ')' . PHP_EOL; $queue!->send( 'Bar' ); print ' (' . $queue!->current() . ')' . PHP_EOL; @hollodotme

Slide 64

Slide 64 text

JEMMA'LOC Sent: Foo (1) Sent: Bar (2) @hollodotme

Slide 65

Slide 65 text

@hollodotme

Slide 66

Slide 66 text

WHAT HAPPENED TO ZERO? @hollodotme

Slide 67

Slide 67 text

JEMMA'LOC Sent: Foo (1) Sent: Bar (2) — $queue = queue( new PDO( 'sqlite!::memory:' ) ); print 'Hidden first value: ' . $queue!->current() . PHP_EOL; $queue!->send( 'Foo' ); print ' (' . $queue!->current() . ')' . PHP_EOL; $queue!->send( 'Bar' ); print ' (' . $queue!->current() . ')' . PHP_EOL; @hollodotme

Slide 68

Slide 68 text

JEMMA'LOC Sent: Foo (1) Sent: Bar (2) — $queue = queue( new PDO( 'sqlite!::memory:' ) ); print 'Hidden first value: ' . $queue!->current() . PHP_EOL; $queue!->send( 'Foo' ); print ' (' . $queue!->current() . ')' . PHP_EOL; $queue!->send( 'Bar' ); print ' (' . $queue!->current() . ')' . PHP_EOL; — Hidden first value: 0 Sent: Foo (1) Sent: Bar (2) @hollodotme

Slide 69

Slide 69 text

JEMMA'LOC function gen() { yield 'foo'; yield 'bar'; } $gen = gen(); print $gen!->send( '' ) . PHP_EOL; @hollodotme

Slide 70

Slide 70 text

JEMMA'LOC function gen() { yield 'foo'; yield 'bar'; } $gen = gen(); print $gen!->send( '' ) . PHP_EOL; — bar @hollodotme

Slide 71

Slide 71 text

JEMMA'LOC function gen() { yield 'foo'; yield 'bar'; } $gen = gen(); $gen!->rewind(); print $gen!->send( '' ) . PHP_EOL; @hollodotme

Slide 72

Slide 72 text

JEMMA'LOC function gen() { yield 'foo'; yield 'bar'; } $gen = gen(); $gen!->rewind(); print $gen!->send( '' ) . PHP_EOL; — bar @hollodotme

Slide 73

Slide 73 text

JEMMA'LOC function gen() { yield 'foo'; yield 'bar'; } $gen = gen(); $gen!->rewind(); # can be omitted print $gen!->current() . PHP_EOL; print $gen!->send( '' ) . PHP_EOL; @hollodotme

Slide 74

Slide 74 text

JEMMA'LOC function gen() { yield 'foo'; yield 'bar'; } $gen = gen(); $gen!->rewind(); # can be omitted print $gen!->current() . PHP_EOL; print $gen!->send( '' ) . PHP_EOL; — foo bar @hollodotme

Slide 75

Slide 75 text

JEMMA'LOC function gen() { yield 'foo'; yield 'bar'; } $gen = gen(); $gen!->rewind(); # can be omitted print $gen!->current() . PHP_EOL; print $gen!->send( '' ) . PHP_EOL; — foo bar COPY THAT. @hollodotme

Slide 76

Slide 76 text

JEMMA'LOC interface Traversable { # Marker interface, cannot be implemented directly } interface Iterator extends Traversable { public function rewind() : void; public function key(); public function current(); public function next() : void; public function valid() : bool; } interface Generator extends Iterator { public function getReturn(); public function send( $value ) : void; public function throw( Throwable $throwable ) : void; public function !__wakeup() : void; } @hollodotme

Slide 77

Slide 77 text

JEMMA'LOC interface Traversable { # Marker interface, cannot be implemented directly } interface Iterator extends Traversable { public function rewind() : void; public function key(); public function current(); public function next() : void; public function valid() : bool; } interface Generator extends Iterator { public function getReturn(); public function send( $value ) : void; public function throw( Throwable $throwable ) : void; public function !__wakeup() : void; } @hollodotme

Slide 78

Slide 78 text

JEMMA'LOC function gen() { yield 1; } $generator = gen(); $generator!->!__wakeup(); @hollodotme

Slide 79

Slide 79 text

JEMMA'LOC function gen() { yield 1; } $generator = gen(); $generator!->!__wakeup(); — PHP Fatal error: Uncaught Exception: Unserialization of 'Generator' is not allowed @hollodotme

Slide 80

Slide 80 text

JEMMA'LOC function gen() { yield 1; } $generator = gen(); $generator!->!__wakeup(); — PHP Fatal error: Uncaught Exception: Unserialization of 'Generator' is not allowed @hollodotme DON’T!

Slide 81

Slide 81 text

JEMMA'LOC function gen() : Generator { while ( true ) { try { echo "\n", yield; } catch ( LogicException $e ) { echo 'Exception: ' . $e!->getMessage(); } } } $generator = gen(); $generator!->send( 'Hello ' ); $generator!->throw( new LogicException( 'Raised inside the generator.' ) ); $generator!->send( 'World' ); $generator!->send( 'of tomorrow' ); @hollodotme

Slide 82

Slide 82 text

JEMMA'LOC function gen() : Generator { while ( true ) { try { echo "\n", yield; } catch ( LogicException $e ) { echo 'Exception: ' . $e!->getMessage(); } } } $generator = gen(); $generator!->send( 'Hello ' ); $generator!->throw( new LogicException( 'Raised inside the generator.' ) ); $generator!->send( 'World' ); $generator!->send( 'of tomorrow' ); — Hello Exception: Raised inside the generator. World of tomorrow @hollodotme

Slide 83

Slide 83 text

JEMMA'LOC function gen() : Generator { while ( true ) { try { echo "\n", yield; } catch ( RuntimeException $e ) { echo $e!->getMessage(); break; } } } $generator = gen(); $generator!->send( 'Hello ' ); $generator!->send( 'World' ); $generator!->throw( new RuntimeException( 'Stopping coroutine.' ) ); $generator!->send( 'of tomorrow' ); @hollodotme

Slide 84

Slide 84 text

JEMMA'LOC function gen() : Generator { while ( true ) { try { echo "\n", yield; } catch ( RuntimeException $e ) { echo $e!->getMessage(); break; } } } $generator = gen(); $generator!->send( 'Hello ' ); $generator!->send( 'World' ); $generator!->throw( new RuntimeException( 'Stopping coroutine.' ) ); $generator!->send( 'of tomorrow' ); — Hello World Stopping coroutine. @hollodotme

Slide 85

Slide 85 text

RECAP • Through yield a value can be sent to the generator to continue its execution • Through yield an exception can be raised in a generator • yield provides first and receives second • You can start a generator by calling any of its Iterator methods • rewind ignores the first provided value @hollodotme

Slide 86

Slide 86 text

TAHITI? @hollodotme

Slide 87

Slide 87 text

CHAPTER 3: COOPERATIVE MULTITASKING @hollodotme

Slide 88

Slide 88 text

FITZ'PLANATION Co-operative multitasking, also k n o w n a s n o n - p r e e m p t i v e multitasking, is a style of computer multitasking in which the operating system never initiates a context switch from a running process to another process. Instead, processes voluntarily yield control periodically or when idle or logically blocked in order to enable multiple applications to be run concurrently. wikipedia.org @hollodotme

Slide 89

Slide 89 text

THE FRAMEWORK (Task Scheduler) AGENTS OF YIELD (Coroutines) SystemCalls (Hooks into the Coroutines) Ammunition (Limits)

Slide 90

Slide 90 text

final class TheFramework { private $backup, $agents = []; public function !__construct() { $this!->backup = new SplQueue(); } public function newAgent( string $agentName, Generator $coroutine ) : void { $agent = new Agent( $agentName, $coroutine ); $this!->agents[ $agentName ] = $agent; $this!->sendToBattle( $agent ); } public function sendToBattle( Agent $task ) : void { $this!->backup!->enqueue( $task ); } public function fight() : void { while ( !$this!->backup!->isEmpty() ) { $agent = $this!->backup!->dequeue(); $retval = $agent!->fight(); if ( $retval instanceof SystemCall ) { $retval( $agent, $this ); continue; } if ( $agent!->outOfMunitions() ) { unset( $this!->agents[ $agent!->getName() ] ); continue; } $this!->sendToBattle( $agent ); } } } @hollodotme

Slide 91

Slide 91 text

final class Agent { private $name, $coroutine, $sendValue; private $beforeFirstYield = true; public function !__construct( string $name, Generator $coroutine ) { $this!->name = $name; $this!->coroutine = $coroutine; } public function getName() : string { return $this!->name; } public function setSendValue( $sendValue ) : void { $this!->sendValue = $sendValue; } public function fight() { if ( $this!->beforeFirstYield ) { $this!->beforeFirstYield = false; return $this!->coroutine!->current(); } $retval = $this!->coroutine!->send( $this!->sendValue ); $this!->sendValue = null; return $retval; } public function outOfMunitions() : bool { return !$this!->coroutine!->valid(); } } @hollodotme

Slide 92

Slide 92 text

final class SystemCall { private $callback; public function !__construct( callable $callback ) { $this!->callback = $callback; } public function !__invoke( Agent $agent, TheFramework $battleGround ) { $callback = $this!->callback; return $callback( $agent, $battleGround ); } } @hollodotme

Slide 93

Slide 93 text

# System call corouting function getAgentName() { return new SystemCall( function ( Agent $agent, TheFramework $battleGround ) { $agent!->setSendValue( $agent!->getName() ); $battleGround!->sendToBattle( $agent ); } ); } # Agent of yield function agent( $shots ) { $agentName = yield getAgentName(); for ( $i = 1; $i !<= $shots; !++$i ) { print "Agent {$agentName} fires shot {$i}." . PHP_EOL; yield; } print "Agent {$agentName} is out of munitions." . PHP_EOL; } @hollodotme

Slide 94

Slide 94 text

THE FINAL BATTLE @hollodotme

Slide 95

Slide 95 text

$battleGround = new TheFramework(); $battleGround!->newAgent( 'Coulson', agent( 12 ) ); $battleGround!->newAgent( 'Mack', agent( 5 ) ); $battleGround!->fight(); print 'Game Over' . PHP_EOL; @hollodotme

Slide 96

Slide 96 text

$battleGround = new TheFramework(); $battleGround!->newAgent( 'Coulson', agent( 12 ) ); $battleGround!->newAgent( 'Mack', agent( 5 ) ); $battleGround!->fight(); print 'Game Over' . PHP_EOL; @hollodotme Agent Coulson fires shot 1. Agent Mack fires shot 1. Agent Coulson fires shot 2. Agent Mack fires shot 2. Agent Coulson fires shot 3. Agent Mack fires shot 3. Agent Coulson fires shot 4. Agent Mack fires shot 4. Agent Coulson fires shot 5. Agent Mack fires shot 5. Agent Coulson fires shot 6. Agent Mack is out of munitions. Agent Coulson fires shot 7. Agent Coulson fires shot 8. Agent Coulson fires shot 9. Agent Coulson fires shot 10. Agent Coulson fires shot 11. Agent Coulson fires shot 12. Agent Coulson is out of munitions. Game Over

Slide 97

Slide 97 text

FITZ'PLANATION When I first heard about all this 
 I f o u n d t h i s co n ce p t to t a l l y awesome and that’s what motivated me to implement it in PHP. At the same time I find coroutines really scary. There is a thin line between awesome code and a total mess and I think coroutines sit exactly on that line. It’s hard for me to say whether writing async code in the way outlined is really beneficial. Nikita Popov @hollodotme THE REAL FITZ

Slide 98

Slide 98 text

THE END @hollodotme

Slide 99

Slide 99 text

github.com/hollodotme @hollodotme THANK YOU! holger woltersdorf

Slide 100

Slide 100 text

RESOURCES • PHP Generator Documentation: https://secure.php.net/manual/en/language.generators.php • VLD extension for PHP: https://derickrethans.nl/projects.html#vld • «Introduction to generators» by Niklas Keller: https://blog.kelunik.com/2017/09/14/an-introduction-to-generators-in-php.html • PHP 5.5 migration guide: https://secure.php.net/manual/en/migration55.new-features.php • «Cooperative multitasking using coroutines (in PHP!)» by Nikita Popov
 https://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html • Repo with executable code of this talk:
 https://github.com/hollodotme/talk-preps/tree/master/agents-of-yield • Definition of Coroutines on Stack Overflow:
 https://stackoverflow.com/questions/553704/what-is-a-coroutine • Definition of Cooperative Multitasking on wikipedia.org: 
 https://en.wikipedia.org/wiki/Cooperative_multitasking @hollodotme