From 77741e2047def4b6a55a610b95c89847fae21cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Mon, 25 Mar 2024 22:36:45 +0100 Subject: [PATCH] Remove SSE abort handling (#2188) --- demos/basic/header.php | 2 +- demos/basic/menu.php | 2 +- src/CardDeck.php | 2 +- src/Grid.php | 4 +- src/JsSse.php | 87 ++++++++++++++++-------------------------- tests/DemosTest.php | 9 +++-- 6 files changed, 44 insertions(+), 62 deletions(-) diff --git a/demos/basic/header.php b/demos/basic/header.php index 04bef42aea..9a2318190c 100644 --- a/demos/basic/header.php +++ b/demos/basic/header.php @@ -19,7 +19,7 @@ Header::addTo($seg, ['H3 Header', 'size' => 3]); Header::addTo($seg, ['H4 Header', 'size' => 4]); Header::addTo($seg, ['H5 Header', 'size' => 5, 'class.dividing' => true]); -View::addTo($seg, ['element' => 'P'])->set('This is a following paragraph of text'); +View::addTo($seg, ['element' => 'p'])->set('This is a following paragraph of text'); Header::addTo($seg, ['H1', 'size' => 1, 'subHeader' => 'H1 subheader']); Header::addTo($seg, ['H5', 'size' => 5, 'subHeader' => 'H5 subheader']); diff --git a/demos/basic/menu.php b/demos/basic/menu.php index 770a43fa9d..6fc2caf3c7 100644 --- a/demos/basic/menu.php +++ b/demos/basic/menu.php @@ -53,7 +53,7 @@ $menu = Menu::addTo($app, ['vertical']); $i = $menu->addItem(); Header::addTo($i, ['size' => 4])->set('Promotions'); -View::addTo($i, ['element' => 'P'])->set('Check out our promotions'); +View::addTo($i, ['element' => 'p'])->set('Check out our promotions'); // menu without any item should not show Menu::addTo($app); diff --git a/src/CardDeck.php b/src/CardDeck.php index 8270ada283..e0c74dcef8 100644 --- a/src/CardDeck.php +++ b/src/CardDeck.php @@ -117,7 +117,7 @@ protected function init(): void protected function addMenuBarSearch(): void { - $view = View::addTo($this->menu->addMenuRight()->addItem()->setElement('div')); + $view = View::addTo($this->menu->addMenuRight()->addItem()); $this->search = $view->add(Factory::factory($this->search, ['context' => $this->container])); $this->search->reload = $this->container; diff --git a/src/Grid.php b/src/Grid.php index 4e1e5f8f31..2f329702cf 100644 --- a/src/Grid.php +++ b/src/Grid.php @@ -122,7 +122,7 @@ protected function init(): void $menuElementNameCountsBackup = \Closure::bind(fn () => $this->_elementNameCounts, $this->menu, AbstractView::class)(); try { $menuRight = $this->menu->addMenuRight(); // @phpstan-ignore-line - $menuItemView = View::addTo($menuRight->addItem()->setElement('div')); + $menuItemView = View::addTo($menuRight->addItem()); $quickSearch = JsSearch::addTo($menuItemView); $this->stickyGet($quickSearch->name . '_q'); $this->menu->removeElement($menuRight->shortName); @@ -316,7 +316,7 @@ public function addQuickSearch($fields = [], $hasAutoQuery = false): void throw new Exception('Unable to add QuickSearch without Menu'); } - $view = View::addTo($this->menu->addMenuRight()->addItem()->setElement('div')); + $view = View::addTo($this->menu->addMenuRight()->addItem()); $this->quickSearch = JsSearch::addTo($view, ['reload' => $this->container, 'autoQuery' => $hasAutoQuery]); $q = $this->stickyGet($this->quickSearch->name . '_q') ?? ''; diff --git a/src/JsSse.php b/src/JsSse.php index 5b1b429346..b7f601380b 100644 --- a/src/JsSse.php +++ b/src/JsSse.php @@ -13,21 +13,15 @@ class JsSse extends JsCallback { use HookTrait; - /** Executed when user aborted, or disconnect browser, when using this SSE. */ - public const HOOK_ABORTED = self::class . '@connectionAborted'; - /** @var bool Allows us to fall-back to standard functionality of JsCallback if browser does not support SSE. */ public $browserSupport = false; - /** @var bool Show Loader when doing sse. */ + /** @var bool Show Loader when doing SSE. */ public $showLoader = false; /** @var bool add window.beforeunload listener for closing js EventSource. Off by default. */ public $closeBeforeUnload = false; - /** @var bool Keep execution alive or not if connection is close by user. False mean that execution will stop on user aborted. */ - public $keepAlive = false; - /** @var \Closure|null custom function for outputting (instead of echo) */ public $echoFunction; @@ -75,6 +69,25 @@ public function set($fx = null, $args = null) }); } + protected function initSse(): void + { + $this->getApp()->setResponseHeader('content-type', 'text/event-stream'); + + // disable buffering for nginx, see https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers + $this->getApp()->setResponseHeader('x-accel-buffering', 'no'); + + // disable compression + @ini_set('zlib.output_compression', '0'); + if (function_exists('apache_setenv')) { + @apache_setenv('no-gzip', '1'); + } + + // prevent buffering + while (ob_get_level() > 0) { + ob_end_flush(); + } + } + /** * Sending an SSE action. */ @@ -119,11 +132,13 @@ public function sendEvent(string $id, string $data, ?string $eventName = null): } /** - * Send Data in buffer to client. + * Create SSE data string. */ - public function flush(): void + private function wrapData(string $string): string { - flush(); + return implode('', array_map(static function (string $v): string { + return 'data: ' . $v . "\n"; + }, preg_split('~\r?\n|\r~', $string))); } private function output(string $content): void @@ -141,17 +156,16 @@ private function output(string $content): void }, null, $app)(); } - public function sendBlock(string $id, string $data, ?string $eventName = null): void + /** + * Send Data in buffer to client. + */ + public function flush(): void { - if (connection_aborted()) { - $this->hook(self::HOOK_ABORTED); - - // stop execution when aborted if not keepAlive - if (!$this->keepAlive) { - $this->getApp()->callExit(); - } - } + flush(); + } + public function sendBlock(string $id, string $data, ?string $eventName = null): void + { $this->output('id: ' . $id . "\n"); if ($eventName !== null) { $this->output('event: ' . $eventName . "\n"); @@ -159,39 +173,4 @@ public function sendBlock(string $id, string $data, ?string $eventName = null): $this->output($this->wrapData($data) . "\n"); $this->flush(); } - - /** - * Create SSE data string. - */ - private function wrapData(string $string): string - { - return implode('', array_map(static function (string $v): string { - return 'data: ' . $v . "\n"; - }, preg_split('~\r?\n|\r~', $string))); - } - - /** - * It will ignore user abort by default. - */ - protected function initSse(): void - { - @set_time_limit(0); // disable time limit - ignore_user_abort(true); - - $this->getApp()->setResponseHeader('content-type', 'text/event-stream'); - - // disable buffering for nginx, see https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers - $this->getApp()->setResponseHeader('x-accel-buffering', 'no'); - - // disable compression - @ini_set('zlib.output_compression', '0'); - if (function_exists('apache_setenv')) { - @apache_setenv('no-gzip', '1'); - } - - // prevent buffering - while (ob_get_level() > 0) { - ob_end_flush(); - } - } } diff --git a/tests/DemosTest.php b/tests/DemosTest.php index cb321ed731..918a87cca7 100644 --- a/tests/DemosTest.php +++ b/tests/DemosTest.php @@ -41,8 +41,8 @@ class DemosTest extends TestCase |(?"([^"\\\]*|\\\["\\\bfnrt/]|\\\u[0-9a-f]{4})*") |(?\[(?:(?&json)(?:,(?&json))*|\s*)\]) |(?\{(?:(?\s*(?&string)\s*:(?&json))(?:,(?&pair))*|\s*)\}) - )\s*)$~six'; - protected static string $regexSseLine = '~^(id|event|data).*$~s'; + )\s*)$~sx'; + protected static string $regexSseLine = '~^(id|event|data): .*$~'; #[\Override] public static function setUpBeforeClass(): void @@ -448,7 +448,10 @@ public function testDemoSseResponse(string $path): void $response = $this->getResponseFromRequest($path); self::assertSame(200, $response->getStatusCode()); - $outputLines = preg_split('~\r?\n|\r~', $response->getBody()->getContents(), -1, \PREG_SPLIT_NO_EMPTY); + $outputLines = array_filter( + explode("\n", $response->getBody()->getContents()), + static fn ($v) => $v !== '' + ); // check SSE Syntax self::assertGreaterThan(0, count($outputLines));