app.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <?php
  2. use Psr\Http\Message\ResponseInterface;
  3. use Psr\Http\Message\ServerRequestInterface as Request;
  4. use React\EventLoop\Loop;
  5. use React\MySQL\ConnectionInterface as DbConnection;
  6. use React\MySQL\Factory as DbFactory;
  7. use React\Http\Message\Response;
  8. use React\MySQL\QueryResult;
  9. use React\Promise\PromiseInterface;
  10. use function React\Promise\all;
  11. use function React\Promise\resolve;
  12. /** @return Closure(Request):ResponseInterface */
  13. function requestHandler(): Closure
  14. {
  15. $connection = establishDbConnection('benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world?idle=0.5');
  16. $world = static function (int $id) use ($connection): PromiseInterface {
  17. return $connection->query('SELECT id,randomNumber FROM World WHERE id=?', [$id]);
  18. };
  19. $fortune = static function () use ($connection): PromiseInterface {
  20. return $connection->query('SELECT id,message FROM Fortune');
  21. };
  22. $update = static function (int $id, int $randomNumber) use ($connection): PromiseInterface {
  23. return $connection->query('UPDATE World SET randomNumber=? WHERE id=?', [$randomNumber, $id]);
  24. };
  25. return static function (Request $request) use ($world, $fortune, $update): ResponseInterface | PromiseInterface {
  26. return resolve((match($request->getUri()->getPath()) {
  27. '/plaintext' => Response::plaintext('Hello, World!'),
  28. '/json' => Response::json(['message' => 'Hello, World!']),
  29. '/db' => db($world),
  30. '/fortunes' => fortune($fortune),
  31. '/query' => query(queryCount($request), $world),
  32. '/update' => updateraw(queryCount($request), $world, $update),
  33. // '/info' => info(),
  34. default => new Response(404, [], 'Error 404'),
  35. }))->catch(
  36. static fn (Throwable $error): PromiseInterface => resolve(Response::plaintext($error->getMessage())->withStatus(500)),
  37. );
  38. };
  39. }
  40. function establishDbConnection(
  41. #[SensitiveParameter]
  42. string $uri,
  43. ): DbConnection {
  44. $connection = (new DbFactory())->createLazyConnection($uri);
  45. $interrupt = $connection->quit(...);
  46. $connection->on('close', static function () use (&$interrupt) {
  47. Loop::removeSignal(SIGINT, $interrupt);
  48. Loop::removeSignal(SIGTERM, $interrupt);
  49. });
  50. Loop::addSignal(SIGINT, $interrupt);
  51. Loop::addSignal(SIGTERM, $interrupt);
  52. return $connection;
  53. }
  54. /** @param Closure(int):PromiseInterface $world */
  55. function db(Closure $world): PromiseInterface
  56. {
  57. $id = mt_rand(1, 10000);
  58. return $world($id)->then(
  59. static fn (QueryResult $result): ResponseInterface => Response::json($result->resultRows[0]),
  60. );
  61. }
  62. function queryCount(Request $request): int
  63. {
  64. $count = (int) ($request->getQueryParams()['q'] ?? 1);
  65. if ($count > 1) {
  66. return min($count, 500);
  67. }
  68. return 1;
  69. }
  70. /** @param Closure(int):PromiseInterface $world */
  71. function query(int $queryCount, Closure $world): PromiseInterface
  72. {
  73. $processQueries = static function (int $count) use ($world): iterable {
  74. while ($count--) {
  75. $id = mt_rand(1, 10000);
  76. yield $world($id)->then(static fn (QueryResult $result): array => $result->resultRows[0]);
  77. }
  78. };
  79. return all($processQueries($queryCount))
  80. ->then(static fn (array $result): ResponseInterface => Response::json($result));
  81. }
  82. /**
  83. * @param Closure(int):PromiseInterface $world
  84. * @param Closure(int, int):PromiseInterface $update
  85. */
  86. function updateraw(int $queryCount, Closure $world, Closure $update): PromiseInterface
  87. {
  88. $processQueries = static function (int $count) use ($world, $update): iterable {
  89. while ($count--) {
  90. $id = mt_rand(1, 10000);
  91. yield $world($id)->then(
  92. static function (QueryResult $result) use ($update): PromiseInterface {
  93. $updated = $result->resultRows[0];
  94. $updated['randomNumber'] = mt_rand(1, 10000);
  95. return $update($updated['id'], $updated['randomNumber'])
  96. ->then(static fn (): array => $updated);
  97. }
  98. );
  99. }
  100. };
  101. return all($processQueries($queryCount))
  102. ->then(static fn (array $result): ResponseInterface => Response::json($result));
  103. }
  104. function fortune(Closure $fortune): PromiseInterface
  105. {
  106. $formatResult = static function (array $rows): string {
  107. $rows[] = ['id' => 0, 'message' => 'Additional fortune added at request time.'];
  108. usort($rows, static fn (array $one, array $other) => $one['message'] <=> $other['message']);
  109. $html = '';
  110. foreach ($rows as $row) {
  111. $message = htmlspecialchars($row['message'], ENT_QUOTES, 'UTF-8');
  112. $html .= "<tr><td>{$row['id']}</td><td>{$message}</td></tr>";
  113. }
  114. return "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>$html</table></body></html>";
  115. };
  116. return $fortune()->then(
  117. static fn (QueryResult $result): ResponseInterface => Response::html($formatResult($result->resultRows)),
  118. );
  119. }
  120. /* function info()
  121. {
  122. ob_start();
  123. phpinfo();
  124. return new Response(200, ['Content-Type' => 'text/plain'], ob_get_clean());
  125. }
  126. */