From 410e773d287e34ea6c222de277cde411afba20d4 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Tue, 20 Oct 2026 00:41:33 +0200 Subject: [PATCH] fix(4e-review clean-code): eine sanitize_key-Nachbildung (LoopScope::sanitizeKey) statt dreier; Docblock zur strengeren LoopContext::enabled() Co-Authored-By: Claude Sonnet 5.5 --- src/Integrations/Divi/Compose/LoopContext.php | 13 ++++++++----- src/Integrations/Divi/Compose/LoopScope.php | 6 ++++++ src/Integrations/Divi/Compose/ValueCleaner.php | 11 +++++------ .../Integrations/Divi/Compose/LoopScopeTest.php | 10 ++++++++++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/Integrations/Divi/Compose/LoopContext.php b/src/Integrations/Divi/Compose/LoopContext.php index c31f8ff..c013883 100644 --- a/src/Integrations/Divi/Compose/LoopContext.php +++ b/src/Integrations/Divi/Compose/LoopContext.php @@ -26,7 +26,13 @@ final class LoopContext { private const PATH = 'module.advanced.loop'; - /** @param array $attrs Attributes of a block */ + /** + * Whether the block runs a loop for the tools: exactly the raw string "on", Divi's render gate. Deliberately + * stricter than the write guard (ValueCleaner::guardLoop reads enable like sanitize_key): a loop the render + * gate does not run gives its content no loop context, which fails closed. + * + * @param array $attrs Attributes of a block + */ public static function enabled(array $attrs): bool { return (self::settings($attrs)['enable'] ?? null) === 'on'; @@ -45,10 +51,7 @@ final class LoopContext $queryType = self::settings($attrs)['queryType'] ?? 'post_types'; - // Divi's sanitize_key() (LoopUtils.php:729-731) makes a non-string "" and lowercases and strips the rest. - return LoopScope::ofQueryType( - is_string($queryType) ? preg_replace('/[^a-z0-9_\-]/', '', strtolower($queryType)) ?? '' : '' - ); + return LoopScope::ofQueryType(LoopScope::sanitizeKey($queryType)); } /** diff --git a/src/Integrations/Divi/Compose/LoopScope.php b/src/Integrations/Divi/Compose/LoopScope.php index 7e08473..2db44bb 100644 --- a/src/Integrations/Divi/Compose/LoopScope.php +++ b/src/Integrations/Divi/Compose/LoopScope.php @@ -158,6 +158,12 @@ final class LoopScope return ! str_contains($option, '_meta_key_') || array_unique($this->kinds()) === [$kind]; } + /** Divi's sanitize_key(): lowercase, only a-z, 0-9, "_" and "-"; "" for anything that is no scalar. */ + public static function sanitizeKey(mixed $value): string + { + return is_scalar($value) ? (string) preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)) : ''; + } + /** Kind of a loop_* option by its name; null for other names and ACF repeater options. */ public static function kindOfOption(string $option): ?string { diff --git a/src/Integrations/Divi/Compose/ValueCleaner.php b/src/Integrations/Divi/Compose/ValueCleaner.php index 675daa3..e67e3ad 100644 --- a/src/Integrations/Divi/Compose/ValueCleaner.php +++ b/src/Integrations/Divi/Compose/ValueCleaner.php @@ -181,15 +181,14 @@ final class ValueCleaner return; } // Divi reads enable and queryType through sanitize_key() (LoopUtils.php:725-731): stored values count so. - $key = static fn (mixed $value): string => is_scalar($value) - ? (string) preg_replace('/[^a-z0-9_\-]/', '', strtolower((string) $value)) - : ''; - if ($key($desktopValue['enable'] ?? null) !== 'on') { + if (LoopScope::sanitizeKey($desktopValue['enable'] ?? null) !== 'on') { return; } // isset() like Divi: null falls back to post_types. - $queryType = isset($desktopValue['queryType']) ? $key($desktopValue['queryType']) : 'post_types'; + $queryType = isset($desktopValue['queryType']) + ? LoopScope::sanitizeKey($desktopValue['queryType']) + : 'post_types'; if (($queryType === 'user_roles' || $queryType === 'users') && ! $this->sanitizer->canListUsers()) { throw new InvalidArgumentException( 'Eine Benutzer-Schleife listet Konten und E-Mail-Adressen und braucht das Recht list_users; ' @@ -214,7 +213,7 @@ final class ValueCleaner foreach ($raw as $entry) { $value = is_string($subTypes) ? $entry : (is_array($entry) ? ($entry['value'] ?? null) : null); if (is_string($value) && trim($value) !== '') { - $types[] = preg_replace('/[^a-z0-9_\-]/', '', strtolower($value)); + $types[] = LoopScope::sanitizeKey($value); } } diff --git a/tests/Unit/Integrations/Divi/Compose/LoopScopeTest.php b/tests/Unit/Integrations/Divi/Compose/LoopScopeTest.php index 6e50868..847c673 100644 --- a/tests/Unit/Integrations/Divi/Compose/LoopScopeTest.php +++ b/tests/Unit/Integrations/Divi/Compose/LoopScopeTest.php @@ -105,4 +105,14 @@ final class LoopScopeTest extends TestCase self::assertFalse(LoopScope::ofQueryType('repeater_team')->fits('loop_post_title')); self::assertTrue(LoopScope::ofQueryType('current_page')->fits('loop_post_title')); } + + /** The one reading of Divi's sanitize_key() (LoopUtils.php:725-731) the loop checks share. */ + public function testSanitizeKeyReadsLikeDivi(): void + { + self::assertSame('userbb_roles', LoopScope::sanitizeKey(' User_Roles ')); + self::assertSame('on', LoopScope::sanitizeKey('o n')); + self::assertSame('1', LoopScope::sanitizeKey(true)); + self::assertSame('', LoopScope::sanitizeKey(null)); + self::assertSame('', LoopScope::sanitizeKey(['on'])); + } }