diff --git a/src/Turbo/src/Helper/TurboStream.php b/src/Turbo/src/Helper/TurboStream.php index 4ac16a27e2..8d183835b2 100644 --- a/src/Turbo/src/Helper/TurboStream.php +++ b/src/Turbo/src/Helper/TurboStream.php @@ -37,7 +37,7 @@ public static function prepend(string $target, string $html): string */ public static function replace(string $target, string $html, bool $morph = false): string { - return self::custom('replace', $target, $html, $morph ? ['method="morph"'] : []); + return self::custom('replace', $target, $html, $morph ? ['method' => 'morph'] : []); } /** @@ -45,7 +45,7 @@ public static function replace(string $target, string $html, bool $morph = false */ public static function update(string $target, string $html, bool $morph = false): string { - return self::custom('update', $target, $html, $morph ? ['method="morph"'] : []); + return self::custom('update', $target, $html, $morph ? ['method' => 'morph'] : []); } /** @@ -87,17 +87,33 @@ public static function refresh(?string $requestId = null): string } /** - * @param array $attr + * Custom action and attributes. + * + * @param array $attr */ public static function custom(string $action, string $target, string $html, array $attr = []): string { - // Join array elements with a space and prepend a leading space - $atrrString = empty($attr) ? '' : ' '.implode(' ', $attr); + if (\array_key_exists('action', $attr) || \array_key_exists('targets', $attr)) { + throw new \InvalidArgumentException('The "action" and "targets" attributes are reserved and cannot be used.'); + } + + $attr['action'] = $action; + $attr['targets'] = $target; + $attr = array_merge(['action' => $action, 'targets' => $target], $attr); + + $attrString = ''; + foreach ($attr as $key => $value) { + if (null === $value) { + $attrString .= \sprintf(' %s', $key); + } else { + $attrString .= \sprintf(' %s="%s"', $key, \is_string($value) ? htmlspecialchars($value) : $value); + } + } return \sprintf(<< + - EOHTML, $action, htmlspecialchars($target), $atrrString, $html); + EOHTML, $attrString, $html); } } diff --git a/src/Turbo/tests/Helper/TurboStreamTest.php b/src/Turbo/tests/Helper/TurboStreamTest.php index 8683f9fb2d..66cd25b396 100644 --- a/src/Turbo/tests/Helper/TurboStreamTest.php +++ b/src/Turbo/tests/Helper/TurboStreamTest.php @@ -11,6 +11,8 @@ namespace Symfony\UX\Turbo\Tests\Helper; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use Symfony\UX\Turbo\Helper\TurboStream; @@ -84,7 +86,24 @@ public function testCustom(): void EOHTML, - TurboStream::custom('customAction', 'some["selector"]', '
content
', ['someAttr="someValue"', 'boolAttr']) + TurboStream::custom('customAction', 'some["selector"]', '
content
', ['someAttr' => 'someValue', 'boolAttr' => null]) ); } + + /** + * @dataProvider customThrowsExceptionDataProvider + * + * @param array $attr + */ + public function testCustomThrowsException(string $action, string $target, string $html, array $attr): void + { + $this->expectException(\InvalidArgumentException::class); + TurboStream::custom($action, $target, $html, $attr); + } + + public static function customThrowsExceptionDataProvider(): \Generator + { + yield ['customAction', 'some["selector"]', '
content
', ['action' => 'someAction']]; + yield ['customAction', 'some["selector"]', '
content
', ['targets' => 'someTargets']]; + } }