Repository: symfony/symfony
File: src/Symfony/Component/HttpFoundation/EventStreamResponse.php (PHP)

Code before:
 *
 * Example usage:
 *
 *     return new EventStreamResponse(function () {
 *         while (true) {
 *             yield new ServerEvent(time(), type: 'ping');
 *
 *             if (connection_aborted()) {
 *                 break;
 *             }
 *
 *             sleep(1);
 *         }
 *     });
 */
class EventStreamResponse extends StreamedResponse
{
    /**
     * @param int $retry The event reconnection time in milliseconds
     */
    public function __construct(?callable $callback = null, int $status = 200, array $headers = [], private int $retry = 0)
    {
        $headers += [
            'Content-Type' => 'text/event-stream',
            'Cache-Control' => 'no-cache',
            'Connection' => 'keep-alive',
        ];

        parent::__construct($callback, $status, $headers);
    }

    public function setCallback(callable $callback): static
    {
        if ($this->callback) {
            return parent::setCallback($callback);
        }

        $this->callback = function () use ($callback) {
            if (is_iterable($events = $callback($this))) {
                foreach ($events as $event) {
                    $this->sendEvent($event);
                }
            }
        };

        return $this;
    }

    /**
     * @param bool $flush Whether output buffers should be flushed
     *

Review comment (performance):
We should also set these legacy cache headers and the `X-Accel-Buffering` NGINX header by default to prevent issues with many reverse proxies (like NGINX Ingress for Kubernetes, which is very popular): https://github.com/dunglas/mercure/blob/main/subscribe.go#L218

Code after:
 * cases: https://symfony.com/doc/current/mercure.html
 *
 * @see ServerEvent
 *
 * @author Yonel Ceruto <open@yceruto.dev>
 *
 * Example usage:
 *
 *     return new EventStreamResponse(function () {
 *         yield new ServerEvent(time());
 *
 *         sleep(1);
 *
 *         yield new ServerEvent(time());
 *     });
 */
class EventStreamResponse extends StreamedResponse
{
    /**
     * @param int|null $retry The number of milliseconds the client should wait
     *                        before reconnecting in case of network failure
     */
    public function __construct(?callable $callback = null, int $status = 200, array $headers = [], private ?int $retry = null)
    {
        $headers += [
            'Connection' => 'keep-alive',
            'Content-Type' => 'text/event-stream',
            'Cache-Control' => 'private, no-cache, no-store, must-revalidate, max-age=0',
            'X-Accel-Buffering' => 'no',
            'Pragma' => 'no-cache',
            'Expire' => '0',
        ];

        parent::__construct($callback, $status, $headers);
    }

    public function setCallback(callable $callback): static
    {
        if ($this->callback) {
            return parent::setCallback($callback);
        }

        $this->callback = function () use ($callback) {
            if (is_iterable($events = $callback($this))) {
                foreach ($events as $event) {
                    $this->sendEvent($event);

                    if (connection_aborted()) {
                        break;
                    }
                }