EventStreamResponse.php 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpFoundation;
  11. /**
  12. * Represents a streaming HTTP response for sending server events
  13. * as part of the Server-Sent Events (SSE) streaming technique.
  14. *
  15. * To broadcast events to multiple users at once, for long-running
  16. * connections and for high-traffic websites, prefer using the Mercure
  17. * Symfony Component, which relies on Software designed for these use
  18. * cases: https://symfony.com/doc/current/mercure.html
  19. *
  20. * @see ServerEvent
  21. *
  22. * @author Yonel Ceruto <open@yceruto.dev>
  23. *
  24. * Example usage:
  25. *
  26. * return new EventStreamResponse(function () {
  27. * yield new ServerEvent(time());
  28. *
  29. * sleep(1);
  30. *
  31. * yield new ServerEvent(time());
  32. * });
  33. */
  34. class EventStreamResponse extends StreamedResponse
  35. {
  36. /**
  37. * @param int|null $retry The number of milliseconds the client should wait
  38. * before reconnecting in case of network failure
  39. */
  40. public function __construct(?callable $callback = null, int $status = 200, array $headers = [], private ?int $retry = null)
  41. {
  42. $headers += [
  43. 'Connection' => 'keep-alive',
  44. 'Content-Type' => 'text/event-stream',
  45. 'Cache-Control' => 'private, no-cache, no-store, must-revalidate, max-age=0',
  46. 'X-Accel-Buffering' => 'no',
  47. 'Pragma' => 'no-cache',
  48. 'Expires' => '0',
  49. ];
  50. parent::__construct($callback, $status, $headers);
  51. }
  52. public function setCallback(callable $callback): static
  53. {
  54. if ($this->callback) {
  55. return parent::setCallback($callback);
  56. }
  57. $this->callback = function () use ($callback) {
  58. if (is_iterable($events = $callback($this))) {
  59. foreach ($events as $event) {
  60. $this->sendEvent($event);
  61. if (connection_aborted()) {
  62. break;
  63. }
  64. }
  65. }
  66. };
  67. return $this;
  68. }
  69. /**
  70. * Sends a server event to the client.
  71. *
  72. * @return $this
  73. */
  74. public function sendEvent(ServerEvent $event): static
  75. {
  76. if ($this->retry > 0 && !$event->getRetry()) {
  77. $event->setRetry($this->retry);
  78. }
  79. foreach ($event as $part) {
  80. echo $part;
  81. if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
  82. static::closeOutputBuffers(0, true);
  83. flush();
  84. }
  85. }
  86. return $this;
  87. }
  88. public function getRetry(): ?int
  89. {
  90. return $this->retry;
  91. }
  92. public function setRetry(int $retry): void
  93. {
  94. $this->retry = $retry;
  95. }
  96. }