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