Composer Co-Author
http://getcomposer.org
phpBB Development Lead
http://www.phpbb.com
CTO of Forumatic phpBB Hosting
https://www.forumatic.com
Contractor at Imagine Easy
http://www.imagineeasy.com
01 | class CitationController |
02 | { |
03 | public function searchAction( $term ) |
04 | { |
05 | return [ |
06 | 'projects' => |
07 | $this ->user->getProjects(), |
08 | 'recommendations' => |
09 | $this ->recommender->suggestFor( $this ->project), |
10 | 'results' => |
11 | $this ->citationRepository->search( $term ), |
12 | ]; |
13 | } |
14 | } |
1 | class CitationRepository |
2 | { |
3 | function search( $term ) |
4 | { |
5 | ... |
6 | // comes down to: mysqli_query |
7 | } |
8 | } |
01 | class CitationController |
02 | { |
03 | public function searchAction( $term ) |
04 | { |
05 | $this ->render([ |
06 | 'projects' => |
07 | $this ->user->getProjects(), |
08 | 'recommendations' => |
09 | $this ->recommender->suggestFor( $this ->project), |
10 | 'results' => |
11 | $this ->citationRepository->search( $term ), |
12 | ]); |
13 | } |
14 | } |
1 | class CitationRepository |
2 | { |
3 | function search( $term ) |
4 | { |
5 | ... |
6 | // comes down to: mysqli_query |
7 | } |
8 | } |
01 | class CitationController |
02 | { |
03 | public function searchAction( $term ) |
04 | { |
05 | return [ |
06 | 'projects' => |
07 | $this ->projectService->getAll( $user ->getId()), |
08 | 'recommendations' => |
09 | $this ->recommender->suggestFor( $this ->project), |
10 | 'results' => |
11 | $this ->citationService->search( $term ), |
12 | ]; |
13 | } |
14 | } |
1 | class CitationService |
2 | { |
3 | public function search( $term ) |
4 | { |
5 | ... |
6 | return $httpClient ->get( '/search?term=' . $term )->json(); |
7 | } |
8 | } |
1 | $app ->get( '/search' , function ( $app , $request ) { |
2 | return $app [ 'citations.repository' ]->search( |
3 | $request ->get( 'term' ) |
4 | ); |
5 | }); |
01 | $app ->get( '/search' , function ( $app , $request ) { |
02 | $term = $request ->get( 'term' ); |
03 |
04 | $results = $app [ 'fooapi.client' ] |
05 | ->get( '/find?keywords=' . $term )->json(); |
06 | $results += $app [ 'barapi.client' ] |
07 | ->get( '/query?search=' . $term )->json(); |
08 |
09 | usort( $results , function ( $a , $b ) {...}); |
10 |
11 | return $results ; |
12 | }); |
1 | <?php |
2 | |
3 | sleep(2); |
4 | |
5 | echo "Hello World!\n" ; |
01 | $ch1 = curl_init(); |
02 | |
03 | curl_setopt( $ch1 , CURLOPT_URL, "http://localhost/~naderman/slow.php" ); |
04 | curl_setopt( $ch1 , CURLOPT_HEADER, 0); |
05 | curl_setopt( $ch1 , CURLOPT_RETURNTRANSFER, true); |
06 | |
07 | $mh = curl_multi_init(); |
08 | curl_multi_add_handle( $mh , $ch1 ); |
09 | |
10 | $active = null; |
11 | do { |
12 | $mrc = curl_multi_exec( $mh , $active ); |
13 | } while ( $active && |
14 | ( $mrc == CURLM_CALL_MULTI_PERFORM || $mrc == CURLM_OK)); |
15 |
16 | // Simulated independent workload |
17 | sleep(1); |
18 | |
19 | curl_multi_remove_handle( $mh , $ch1 ); |
20 | curl_multi_close( $mh ); |
01 | $ch1 = curl_init(); |
02 | |
03 | curl_setopt( $ch1 , CURLOPT_URL, "http://localhost/~naderman/slow.php" ); |
04 | curl_setopt( $ch1 , CURLOPT_HEADER, 0); |
05 | curl_setopt( $ch1 , CURLOPT_RETURNTRANSFER, true); |
06 | |
07 | $mh = curl_multi_init(); |
08 | curl_multi_add_handle( $mh , $ch1 ); |
09 | |
10 | $active = null; |
11 | do { |
12 | $mrc = curl_multi_exec( $mh , $active ); |
13 | } while ( $active && |
14 | ( $mrc == CURLM_CALL_MULTI_PERFORM || $mrc == CURLM_OK)); |
15 |
16 | // Simulated independent workload |
17 | sleep(1); |
18 | |
19 | curl_multi_remove_handle( $mh , $ch1 ); |
20 | curl_multi_close( $mh ); |
01 | $ch1 = curl_init(); |
02 | |
03 | curl_setopt( $ch1 , CURLOPT_URL, "http://localhost/~naderman/slow.php" ); |
04 | curl_setopt( $ch1 , CURLOPT_HEADER, 0); |
05 | curl_setopt( $ch1 , CURLOPT_RETURNTRANSFER, true); |
06 | |
07 | $mh = curl_multi_init(); |
08 | curl_multi_add_handle( $mh , $ch1 ); |
09 | |
10 | $active = null; |
11 | do { |
12 | $mrc = curl_multi_exec( $mh , $active ); |
13 | } while ( $mrc == CURLM_CALL_MULTI_PERFORM); |
14 | |
15 | while ( $active && $mrc == CURLM_OK) { |
16 | if (curl_multi_select( $mh ) != -1) { |
17 | do { |
18 | $mrc = curl_multi_exec( $mh , $active ); |
19 | } while ( $mrc == CURLM_CALL_MULTI_PERFORM); |
20 | } |
21 | } |
22 | |
23 | // Simulated independent workload |
24 | sleep(1); |
25 | |
26 | curl_multi_remove_handle( $mh , $ch1 ); |
27 | curl_multi_close( $mh ); |
01 | $ch1 = curl_init(); |
02 | |
03 | curl_setopt( $ch1 , CURLOPT_URL, "http://localhost/~naderman/slow.php" ); |
04 | curl_setopt( $ch1 , CURLOPT_HEADER, 0); |
05 | curl_setopt( $ch1 , CURLOPT_RETURNTRANSFER, true); |
06 | |
07 | $mh = curl_multi_init(); |
08 | curl_multi_add_handle( $mh , $ch1 ); |
09 | |
10 | $active = null; |
11 | do { |
12 | $mrc = curl_multi_exec( $mh , $active ); |
13 | } while ( $mrc == CURLM_CALL_MULTI_PERFORM); |
14 | |
15 | // Simulated independent workload |
16 | sleep(1); |
17 | |
18 | while ( $active && $mrc == CURLM_OK) { |
19 | if (curl_multi_select( $mh ) != -1) { |
20 | do { |
21 | $mrc = curl_multi_exec( $mh , $active ); |
22 | } while ( $mrc == CURLM_CALL_MULTI_PERFORM); |
23 | } |
24 | } |
25 | |
26 | curl_multi_remove_handle( $mh , $ch1 ); |
27 | curl_multi_close( $mh ); |
01 | naderman@Montsoreau:~/tmp$ time php -f client-parallel.php |
02 | |
03 | real 0m2.029s |
04 | user 0m0.012s |
05 | sys 0m0.012s |
06 | naderman@Montsoreau:~/tmp$ time php -f client-linear.php |
07 | |
08 | real 0m3.029s |
09 | user 0m0.012s |
10 | sys 0m0.012s |
Explicit dereferencing
1 | $response = $client ->get( |
2 | 'http://httpbin.org' , |
3 | [ 'future' => true]); |
4 |
5 | // perform other work here |
6 |
7 | $response ->wait(); |
8 | echo $response ->getStatusCode(); // 200 |
Implicit dereferencing
1 | $response = $client ->get( |
2 | 'http://httpbin.org' , |
3 | [ 'future' => true]); |
4 |
5 | // perform other work here |
6 |
7 | echo $response ->getStatusCode(); // 200 |
https://github.com/reactphp/promise
01 | function getAwesomeResultPromise() |
02 | { |
03 | $deferred = new React\Promise\Deferred(); |
04 | |
05 | computeAwesomeResultAsynchronously( |
06 | function ( $error , $result ) use ( $deferred ) { |
07 | if ( $error ) { |
08 | $deferred ->reject( $error ); |
09 | } else { |
10 | $deferred ->resolve( $result ); |
11 | } |
12 | }); |
13 | return $deferred ->promise(); |
14 | } |
15 |
16 | getAwesomeResultPromise() |
17 | ->then( |
18 | function ( $value ) { |
19 | // Deferred resolved, do something with $value |
20 | }, |
21 | function ( $reason ) { |
22 | // Deferred rejected, do something with $reason |
23 | }, |
24 | function ( $update ) { |
25 | // Progress triggered, do sth with $update |
26 | } |
27 | ); |
01 | $response = $client ->get( |
02 | 'http://httpbin.org' , |
03 | [ 'future' => true]); |
04 |
05 | // Use the response asynchronously |
06 | $response ->then( function ( $response ) { |
07 | echo $response ->getStatusCode(); // 200 |
08 | }); |
09 |
10 | // define other work here |
http://reactphp.org http://github.com/reactphp
01 | function range() |
02 | { |
03 | for ( $i = 0; $i <= 100; $i ++) { |
04 | yield $i => $i *2; |
05 | } |
06 | } |
07 |
08 | foreach (range() as $i => $v ) { |
09 | echo $v ; |
10 | } |
11 | // 0 2 4 6 8 10 ... 200 |
1 | function filterBadUrls( $urlBlacklist , $items ) |
2 | { |
3 | foreach ( $items as $key => $item ) { |
4 | if (!isset( $urlBlacklist [ $item [ 'url' ]])) { |
5 | yield $key => $item ; |
6 | } |
7 | } |
8 | } |
1 | function statsdCount() |
2 | { |
3 | foreach ( $items as $item ) { |
4 | \StatsD::increment( 'elements-processed' ); |
5 | yield $item ; |
6 | } |
7 | } |
01 | function bulkify() |
02 | { |
03 | $bulk = array (); |
04 | foreach ( $items as $item ) { |
05 | $bulk [] = $item ; |
06 |
07 | if ( count ( $bulk ) == 100) { |
08 | yield $bulk ; |
09 | $bulk = array (); |
10 | } |
11 | } |
12 |
13 | if (! empty ( $bulk )) { |
14 | yield $bulk ; |
15 | } |
16 | } |
17 |
18 | foreach (bulkify( $results ) as $documents ) { |
19 | $elasticaType ->addDocuments( $documents ); |
20 | } |
Iteration primitives implemented using generators
Accept all iterables: array, traversable, iterator, aggregate
1 | Iterator map(callable $function , iterable $iterable ) |
2 | Iterator filter(callable $predicate , iterable $iterable ) |
3 | Iterator flatten(iterable $iterable ) |
4 | Iterator slice(iterable $iterable , int $start , int $length ) |
5 | ... |
1 | $nums = iter\range(1, 10); |
2 | $numsTimesTen = iter\map(iter\fn\operator( '*' , 10), $nums ); |
3 | // 10 20 30 40 50 60 70 80 90 100 |
01 | public function downloadFile( $filename , $callback ) |
02 | { |
03 | $contents = get the file asynchronously... |
04 | $callback ( $contents ); |
05 | } |
06 |
07 | public function processDownloadResult( $filename , $contents ) |
08 | { |
09 | echo "The file $filename contained a lot of stuff:\n" ; |
10 | echo $contents ; |
11 | } |
12 |
13 | public function handleDownload( $filename ) |
14 | { |
15 | $this ->downloadFile( $filename , |
16 | [ $this , 'processDownloadResult' ]); |
17 | // how to get $filename in? |
18 | } |
https://github.com/reactphp/partial
"Dependency Injection for functions"
1 | public function handleDownload( $filename ) |
2 | { |
3 | $this ->downloadFile( $filename , |
4 | Partial\bind( |
5 | [ $this , 'processDownloadResult' ], |
6 | $filename |
7 | ) |
8 | ); |
9 | } |
01 | $c [ 'drop' ] = function ( $c ) { |
02 | return igorw\pipeline( |
03 | Partial\bind([Process\Report:: class , 'statsd' ], |
04 | 'import-skip' ), |
05 | Partial\bind([Process\Report:: class , 'debugPrintItem' ], |
06 | $c [ 'logger' ]) |
07 | ); |
08 | }; |
09 | $c [ 'process' ] = $c ->protect( function ( $job ) use ( $c ) { |
10 | return igorw\pipeline( |
11 | $c [ 'crossref.oai' ], |
12 | $c [ 'crossref.mapper' ], |
13 | Partial\bind([Process\Report:: class , 'statsd' ], |
14 | 'import-read' ), |
15 | Partial\bind([Process\Report:: class , 'write' ], |
16 | 'Read ' , $c [ 'logger' ]), |
17 | Partial\bind([Process\Filter:: class , 'requiredFields' ], |
18 | $c [ 'drop' ]), |
19 | Partial\bind([Process\Transform:: class , 'bulkify' ], |
20 | $c [ 'config' ][ 'elasticsearch' ][ 'bulksize' ]), |
21 | Partial\bind([Process\ElasticSearch:: class , 'write' ], |
22 | $c [ 'elasticsearch.type' ]), |
23 | [Process\Transform:: class , 'unbulkify' ], |
24 | Partial\bind([Process\Doctrine:: class , 'updateJob' ], |
25 | $job , $c [ 'doctrine.orm.entity_manager' ]), |
26 | Partial\bind([Process\Report:: class , 'statsd' ], |
27 | 'import-write' ), |
28 | Partial\bind([Process\Report:: class , 'write' ], |
29 | 'Written' , $c [ 'logger' ]) |
30 | ); |
31 | }); |
1 | $input = new \DatePeriod( |
2 | $from , new \DateInterval( 'P1D' ), $until |
3 | ); |
4 | $pipeline = $container [ 'process' ]( $job ); |
5 |
6 | // consume all input through the pipeline |
7 | foreach ( $pipeline ( $input ) as $result ) {} |
01 | $app [ 'search' ] = $app ->protect( function ( $term ) use ( $app ) { |
02 | $responses = $results = []; |
03 |
04 | foreach ( $app [ 'search.backends' ] as $backend ) { |
05 | $response = $backend ->search( $term ); |
06 | $response ->then( function ( $response ) use ( $backend ) { |
07 | foreach ( $response ->json() as $value ) { |
08 | yield $backend ->citationFromJson( $value ); |
09 | } |
10 | }) |
11 | ->then( function ( $citations ) use (& $results ) { |
12 | foreach ( $citations as $citation ) { |
13 | $results [] = json_encode( |
14 | $citation ->getStandardData() |
15 | ); |
16 | } |
17 | }); |
18 | $responses [] = $response ; |
19 | } |
20 | foreach ( $responses as $response ) { |
21 | $response ->wait(); |
22 | } |
23 | return $results ; |
24 | }); |
25 |
26 | $app ->get( '/search' , function ( |
27 | Application $app , Request $request ) { |
28 | $term = $request ->get( 'term' ); |
29 | return json_encode( $app [ 'search' ]( $term )); |
30 | }); |
1 | CitationController::searchAction( $term ) |
2 | $searchResults = |
3 | $this ->citationService->search( $term ) |
4 | ... |
5 | 'results' => |
6 | $searchResults ->deref(), |
01 | class CitationService |
02 | { |
03 | public function search( $term ) |
04 | { |
05 | ... |
06 | return new CitationListFuture( |
07 | $httpClient ->get( '/search?term=' . $term )); |
08 | } |
09 | } |
10 |
11 | class CitationListFuture |
12 | { |
13 | public function __construct(FutureResponse $response ) { |
14 | $this ->response = $response ; |
15 | } |
16 |
17 | public function deref() |
18 | { |
19 | $response ->wait(); |
20 | return $response ->json(); |
21 | } |
22 | } |