app.php 5.0 KB

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