diff --git a/CHANGELOG.md b/CHANGELOG.md index 66af5d35d..948f85a30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - Chg #271: Remove deprecated methods `withDefaultExtension()` and `getDefaultExtension()` from `ViewInterface` (@vjik) - Chg #271: Rename configuration parameter `defaultExtension` to `fallbackExtension` (@vjik) - Chg #272: Add variadic parameter `$default` to `ViewInterface::getParameter()` (@vjik) +- Bug #273: Fix empty string and "0" keys in `WebView` methods: `registerCss()`, `registerStyleTag()`, + `registerCssFile()`, `registerJs()`, `registerScriptTag()` and `registerJsFile()` (@vjik) +- Enh #273: Use more specific psalm types in results of `WebView` methods: `getLinkTags()`, `getCss()`, `getCssFiles()`, + `getJs()` and `getJsFiles()` (@vjik) ## 10.0.0 June 28, 2024 diff --git a/psalm.xml b/psalm.xml index b48c894ef..d091d59c2 100644 --- a/psalm.xml +++ b/psalm.xml @@ -15,6 +15,5 @@ - diff --git a/psalm83.xml b/psalm83.xml index ee80eb36d..4fc7490e9 100644 --- a/psalm83.xml +++ b/psalm83.xml @@ -15,7 +15,6 @@ - diff --git a/src/State/WebViewState.php b/src/State/WebViewState.php index e262d9a48..b5509aa96 100644 --- a/src/State/WebViewState.php +++ b/src/State/WebViewState.php @@ -41,7 +41,7 @@ final class WebViewState /** * @var array The registered link tags. - * @psalm-var array + * @psalm-var array> * * @see registerLink() * @see registerLinkTag() @@ -50,7 +50,7 @@ final class WebViewState /** * @var array The registered CSS code blocks. - * @psalm-var array + * @psalm-var array> * * {@see registerCss()} */ @@ -58,7 +58,7 @@ final class WebViewState /** * @var array The registered CSS files. - * @psalm-var array + * @psalm-var array> * * {@see registerCssFile()} */ @@ -66,7 +66,7 @@ final class WebViewState /** * @var array The registered JS code blocks - * @psalm-var array + * @psalm-var array> * * {@see registerJs()} */ @@ -74,7 +74,7 @@ final class WebViewState /** * @var array The registered JS files. - * @psalm-var array + * @psalm-var array> * * {@see registerJsFile()} */ @@ -98,7 +98,7 @@ public function getMetaTags(): array /** * @return array The registered link tags. - * @psalm-return array + * @psalm-return array> */ public function getLinkTags(): array { @@ -107,7 +107,7 @@ public function getLinkTags(): array /** * @return array The registered CSS code blocks. - * @psalm-return array + * @psalm-return array> */ public function getCss(): array { @@ -116,7 +116,7 @@ public function getCss(): array /** * @return array The registered CSS files. - * @psalm-return array + * @psalm-return array> */ public function getCssFiles(): array { @@ -125,7 +125,7 @@ public function getCssFiles(): array /** * @return array The registered JS code blocks - * @psalm-return array + * @psalm-return array> */ public function getJs(): array { @@ -134,7 +134,7 @@ public function getJs(): array /** * @return array The registered JS files. - * @psalm-return array + * @psalm-return array> */ public function getJsFiles(): array { @@ -224,9 +224,9 @@ public function registerLinkTag(Link $link, int $position = WebView::POSITION_HE * Registers a CSS code block. * * @param string $css The content of the CSS code block to be registered. - * @param string|null $key The key that identifies the CSS code block. If null, it will use $css as the key. - * If two CSS code blocks are registered with the same key, the latter will overwrite the former. * @param array $attributes The HTML attributes for the {@see Style} tag. + * @param string|null $key The key that identifies the CSS code block. If `null`, it will use `$css` as the key. + * If two CSS code blocks are registered with the same key, the latter will overwrite the former. */ public function registerCss( string $css, @@ -234,8 +234,7 @@ public function registerCss( array $attributes = [], ?string $key = null ): void { - $key = $key ?: md5($css); - $this->css[$position][$key] = $attributes === [] ? $css : Html::style($css, $attributes); + $this->css[$position][$key ?? md5($css)] = $attributes === [] ? $css : Html::style($css, $attributes); } /** @@ -266,8 +265,7 @@ public function registerCssFromFile( */ public function registerStyleTag(Style $style, int $position = WebView::POSITION_HEAD, ?string $key = null): void { - $key = $key ?: md5($style->render()); - $this->css[$position][$key] = $style; + $this->css[$position][$key ?? md5($style->render())] = $style; } /** @@ -280,20 +278,20 @@ public function registerStyleTag(Style $style, int $position = WebView::POSITION * @param string $url The CSS file to be registered. * @param array $options the HTML attributes for the link tag. Please refer to {@see \Yiisoft\Html\Html::cssFile()} * for the supported options. - * @param string|null $key The key that identifies the CSS script file. If null, it will use $url as the key. + * @param string|null $key The key that identifies the CSS script file. If `null`, it will use `$url` as the key. * If two CSS files are registered with the same key, the latter will overwrite the former. */ public function registerCssFile( string $url, int $position = WebView::POSITION_HEAD, array $options = [], - string $key = null + ?string $key = null ): void { if (!$this->isValidCssPosition($position)) { throw new InvalidArgumentException('Invalid position of CSS file.'); } - $this->cssFiles[$position][$key ?: $url] = Html::cssFile($url, $options)->render(); + $this->cssFiles[$position][$key ?? $url] = Html::cssFile($url, $options)->render(); } /** @@ -337,13 +335,12 @@ public function addCssStrings(array $cssStrings): void * - {@see WebView::POSITION_END}: at the end of the body section. This is the default value. * - {@see WebView::POSITION_LOAD}: executed when HTML page is completely loaded. * - {@see WebView::POSITION_READY}: executed when HTML document composition is ready. - * @param string|null $key The key that identifies the JS code block. If null, it will use $js as the key. + * @param string|null $key The key that identifies the JS code block. If `null`, it will use `$js` as the key. * If two JS code blocks are registered with the same key, the latter will overwrite the former. */ public function registerJs(string $js, int $position = WebView::POSITION_END, ?string $key = null): void { - $key = $key ?: md5($js); - $this->js[$position][$key] = $js; + $this->js[$position][$key ?? md5($js)] = $js; } /** @@ -353,8 +350,7 @@ public function registerJs(string $js, int $position = WebView::POSITION_END, ?s */ public function registerScriptTag(Script $script, int $position = WebView::POSITION_END, ?string $key = null): void { - $key = $key ?: md5($script->render()); - $this->js[$position][$key] = $script; + $this->js[$position][$key ?? md5($script->render())] = $script; } /** @@ -389,7 +385,7 @@ public function registerJsFile( throw new InvalidArgumentException('Invalid position of JS file.'); } - $this->jsFiles[$position][$key ?: $url] = Html::javaScriptFile($url, $options)->render(); + $this->jsFiles[$position][$key ?? $url] = Html::javaScriptFile($url, $options)->render(); } /** diff --git a/tests/WebViewTest.php b/tests/WebViewTest.php index 9735afdd6..bc33027f5 100644 --- a/tests/WebViewTest.php +++ b/tests/WebViewTest.php @@ -118,6 +118,27 @@ public function testRegisterJsFileWithPosition(string $expected, int $position): $this->assertStringContainsString($expected, $html); } + public function testRegisterJsFileWithKeyEdgeCase(): void + { + $webView = TestHelper::createWebView() + ->registerJsFile('main.js') + ->registerJsFile('main.js', key: '') + ->registerJsFile('main.js', key: '0') + ->registerJsFile('main.js', key: 'four'); + + $html = $webView->render('positions.php'); + + $this->assertStringContainsString( + << + + + [/ENDBODY] + HTML, + $html + ); + } + public function testRegisterStyleTag(): void { $webView = TestHelper::createWebView(); @@ -136,6 +157,31 @@ public function testRegisterStyleTag(): void $this->assertSame($expected, $html); } + public function testRegisterStyleTagEdgeCase(): void + { + $style = Html::style('H1 { color: red; }'); + $webView = TestHelper::createWebView() + ->registerStyleTag($style) + ->registerStyleTag($style, key: '') + ->registerStyleTag($style, key: '0') + ->registerStyleTag($style, key: 'test'); + + $html = $webView->render('positions.php'); + + $expected = <<render()} + {$style->render()} + {$style->render()} + {$style->render()}[/HEAD] + [BEGINBODY][/BEGINBODY] + [ENDBODY][/ENDBODY] + [ENDPAGE][/ENDPAGE] + CODE; + + $this->assertSame($expected, $html); + } + public static function dataRegisterCssFile(): array { return [ @@ -159,6 +205,25 @@ public function testRegisterCssFile(string $url): void $this->assertStringContainsString('[HEAD][/HEAD]', $html); } + public function testRegisterCssFileEdgeCase(): void + { + $webView = TestHelper::createWebView() + ->registerCssFile('main.css') + ->registerCssFile('main.css', options: ['data-x' => '1'], key: '') + ->registerCssFile('main.css', options: ['data-x' => '2'], key: '0'); + + $html = $webView->render('positions.php'); + + $this->assertStringContainsString( + << + + [/HEAD] + HTML, + $html + ); + } + public function testRegisterCssFileWithInvalidPosition(): void { $webView = TestHelper::createWebView(); @@ -329,6 +394,26 @@ public function testRegisterCss(string $expected, ?int $position): void $this->assertStringContainsString($expected, $html); } + public function testRegisterCssWithKeyEdgeCase(): void + { + $webView = TestHelper::createWebView() + ->registerCss('.red{color:red;}') + ->registerCss('.red{color:red;}', key: '') + ->registerCss('.red{color:red;}', key: '0') + ->registerCss('.red{color:red;}', key: 'red'); + + $html = $webView->render('positions.php'); + + $this->assertStringContainsString( + <<assertSame($expected, $html); } + public function testRegisterScriptTagWithKeyEdgeCase(): void + { + $script = Html::script('alert(1);'); + $webView = TestHelper::createWebView() + ->registerScriptTag($script) + ->registerScriptTag($script, key: '') + ->registerScriptTag($script, key: '0') + ->registerScriptTag($script, key: 'four'); + + $html = $webView->render('positions.php'); + + $expected = <<render()} + {$script->render()} + {$script->render()} + {$script->render()}[/ENDBODY] + [ENDPAGE][/ENDPAGE] + HTML; + + $this->assertSame($expected, $html); + } + public function testRegisterJsAndRegisterScriptTag(): void { $webView = TestHelper::createWebView(); @@ -525,6 +635,27 @@ public function testRegisterJsAndRegisterScriptTagWithAjax(): void $this->assertSame($expected, $html); } + public function testRegisterJsWithKeyEdgeCase(): void + { + $webView = TestHelper::createWebView() + ->registerJs('alert(1);') + ->registerJs('alert(1);', key: '') + ->registerJs('alert(1);', key: '0') + ->registerJs('alert(1);', key: 'four'); + + $html = $webView->render('positions.php'); + + $this->assertStringContainsString( + <<alert(1); + alert(1); + alert(1); + alert(1); + JS, + $html + ); + } + public function testAddCssFiles(): void { $webView = TestHelper::createWebView();