Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

2019 11-bgphp

110 views

Published on

As PHP programmers we are used to waiting for network I/O, in general we may not even consider any other option. But why wait? Why not jump on board the Async bullet-train and experience life in the fast lane and give Go and NodeJS a run for the money. This talk will aim to make the audience aware of the benefits, opportunities, and pitfalls of asynchronous programming in PHP, and guide them through the native functionality, frameworks and PHP extensions though which it can be facilitated.

Published in: Technology
  • Be the first to comment

  • Be the first to like this

2019 11-bgphp

  1. 1. Exploring Async PHP @dantleech Bulgaria PHP 9th November 2019
  2. 2. MySQL
  3. 3. SLOW
  4. 4. HTTP Requests
  5. 5. SLOW
  6. 6. REDIS
  7. 7. SLOW
  8. 8. Elastic
  9. 9. SLOW
  10. 10. TL;DR;
  11. 11. SLOW Network I/O
  12. 12. Array HTTP GET (local) HTTP GET (network) HTP GET (internet) 3 1084 9221 94348
  13. 13. WHO I AM • Daniel Leech • PHP since 2007 • Phpactor: Refactoring and intellisense for VIM • Phpbench: Code Benchmarking • Fink: Link Checker • Works at Inviqa in Berlin
  14. 14. Product Import
  15. 15. News Website
  16. 16. Tasks with satisfied dependencies can be parallelized
  17. 17. What can we make with an Async PHP?
  18. 18. Concurrent I/O • HTTP Requests • Database Queries • Child Processes • File Access
  19. 19. Servers and CLI Applications • Web Socket Server • Chat Server • Web Crawler • Task Runner • HTTP Server
  20. 20. HTTP Server =
  21. 21. STREAMS
  22. 22. Streams • Abstraction layer for resources: file://, http://, tcp://, etc • Can be read from and written to incrementally • Can be non blocking
  23. 23. 1: File get contents <?php echo file_get_contents('http://localhost:8080?sleep=1');
  24. 24. 2: Stream socket client <?php $stream = stream_socket_client('tcp://localhost:8080'); fwrite($stream, "GET /?sleep=1 HTTP/1.0rnrn"); echo stream_get_contents($stream);
  25. 25. 3: Stream set blocking <?php $stream = stream_socket_client('tcp://localhost:8080'); stream_set_blocking($stream, false); fwrite($stream, "GET /?sleep=1 HTTP/1.0rnrn"); echo stream_get_contents($read[0]); die(‘Hello World’);
  26. 26. 3: Stream set blocking <?php $stream = stream_socket_client('tcp://localhost:8080'); stream_set_blocking($stream, false); fwrite($stream, "GET /?sleep=1 HTTP/1.0rnrn"); $write = $except = null; $read = [ $stream ]; stream_select($read, $write, $except, null); echo stream_get_contents($read[0]);
  27. 27. 4: Multiple Requests<?php $stream1 = create_non_blocking_get('tcp://localhost:8080', ‘/?sleep=1’); $stream2 = create_non_blocking_get('tcp://localhost:8080', ‘/?sleep=1’); $await = [ $stream1, $stream2 ]; $write = $except = null; while ($await) { $read = $await; stream_select($read, $write, $except, null); foreach ($read as $stream) { echo stream_get_contents($stream) . PHP_EOL; unset($await[array_search($stream, $await)]); fclose($stream); } }
  28. 28. Amphp
  29. 29. EVENT LOOP
  30. 30. EVENT LOOP
  31. 31. EVENT LOOP
  32. 32. <?php $stream = create_non_blocking_get('tcp://localhost:8080', ‘/?sleep=1’); Loop::onReadable($stream, function ($watcherId, $stream) { echo stream_get_contents($stream); }); Loop::run(); EVENT LOOP
  33. 33. PROMISES
  34. 34. No Promise <?php http_request("http://example/.com/foobar", function ($response) { echo $response->getStatusCode(); });
  35. 35. Promise <?php http_request("http://example/.com/foobar")->onResolve(function ($failure, $response) { echo $response->getStatusCode(); });
  36. 36. Multiple Promise <?php $promise1 = http_request("http://example/.com/foobar"); $promise2 = http_request("http://example/.com/barfoo"); $results = wait ( all ($promise1, $promise2)); echo $promise1->getStatusCode(); echo $promise2->getStatusCode();
  37. 37. You can yield a promise ...
  38. 38. COROUTINES
  39. 39. CO- ROUTINE CO- ROUTINE CO- ROUTINE CO- ROUTINE Event Loop (scheduler)
  40. 40. Coroutine Example <?php call(function () { $response = yield http_request("http://example/.com/foobar"); echo $reponse->getStatusCode(); $body = yield $response->getBody(); echo $body; }); call(function () { $response = yield http_request("http://example/.com/barfoo"); });
  41. 41. LET’S BUILD A CI SYSTEM
  42. 42. <?php class Runner { public function __construct(Workspace $workspace, array $scripts) { /** ... */ } public function run(): int { /** ... */ } private function runRepos(): Promise { /** ... */ } private function runRepo($repoUrl, $scripts, int $color): Promise { /** ... */ } private function runCommand(string $command, $cwd, int $color): Promise { /** ... */ } private function display(ProcessInputStream $processInputStream, $color) { /** ... */ } }
  43. 43. $runner = new Runner( new Workspace(__DIR__ . '/../Workspace'), [ 'git@github.com:dantleech/fink' => [ 'composer install --prefer-dist', 'vendor/bin/phpunit', 'vendor/bin/phpstan analyse --level=7', ], 'git@github.com:phpactor/path-finder' => [ 'composer install', './vendor/bin/phpunit', 'vendor/bin/phpstan analyse --level=7', ] ] ); $aggregateExitCode = $runner->run(); sprintf('Aggregate exit code: %s', $aggregateExitCode) . PHP_EOL; exit($aggregateExitCode);
  44. 44. class Runner { /** * @var array */ private $scripts; /** * @var Workspace */ private $workspace; public function __construct(Workspace $workspace, array $scripts) { $this->scripts = $scripts; $this->workspace = $workspace; } // ...
  45. 45. class Runner { // ... public function run(): int { $this->workspace->reset(); $exitCodes = wait ( $this->runRepos()); return array_sum($exitCodes); }
  46. 46. class Runner { // ... private function runRepos(): Promise { $exitCodePromises = []; foreach ($this->scripts as $repoUrl => $scripts) { $exitCodePromises[] = $this->runRepo($repoUrl, $scripts); } return all($exitCodePromises); }
  47. 47. class Runner { // ... private function runRepo($repoUrl, $scripts): Promise { return call(function () use ($repoUrl, $scripts) { $repoPath = $this->workspace->path(basename($repoUrl)); $exitCode = yield $this->runCommand(sprintf( 'git clone %s %s', $repoUrl, $repoPath, ), $this->workspace->path('/')); foreach ($scripts as $script) { $exitCode += yield $this->runCommand($script, $repoPath); } return $exitCode; }); }
  48. 48. class Runner { // ... private function runCommand(string $command, string $cwd, int $color): Promise { return call(function () use ($command, $cwd) { $process = new Process($command, $cwd); $pid = yield $process->start(); $this->display($process->getStdout()); $this->display($process->getStderr()); $exitCode = yield $process->join(); return $exitCode; }); }
  49. 49. class Runner { // ... private function display(ProcessInputStream $processInputStream): void { asyncCall(function () use ($processInputStream) { while (null !== $chunk = yield $processInputStream->read()) { echo $chunk; }; }); }
  50. 50. Other Frameworks
  51. 51. Amphp • Born 2013 • Redis Client • MySQL Client • Promises • Coroutines • Event Loop
  52. 52. React PHP • Born 2012 • Redis Client • MySQL Client • Promises • Event Emitter • Event Loop
  53. 53. Swoole • Born 2012 • Redis Client • MySQL Client • Promises • Coroutines • Event Loop
  54. 54. PHP Extension CLI Only
  55. 55. Can out perform Go and NodeJS* … at some things
  56. 56. Think Differently
  57. 57. CO- ROUTINE CO- ROUTINE CO- ROUTINE CO- ROUTINE CO- ROUTINE CO- ROUTINE
  58. 58. @dantleech Talk to me about Phpbench and Phpactor Works for in Berlin Examples for this talk: https://github.com/dantleech/2019-exploring-async

×