]*?>.*?@si', '', $value)); } $value = trim((string) preg_replace('/[\r\n\t ]+/', ' ', $value)); $found = false; while (preg_match('/%[a-f0-9]{2}/i', $value, $match) === 1) { $value = str_replace($match[0], '', $value); $found = true; } return $found ? trim((string) preg_replace('/ +/', ' ', $value)) : $value; } /** * Like WpValueSanitizer (ruling S4): raw for users with unfiltered_html; otherwise like * wp_kses_post: NUL bytes, #is', '', str_replace(chr(0), '', $value)); return (string) preg_replace( '#]*>#i', '', $value ); } public function code(string $value): string { return $value; } /** * Like esc_url_raw: spaces become %20, characters outside its allow list (tags, quotes, * braces, NUL) go, %0d/%0a are removed repeatedly, javascript: gives ''. */ public function url(string $value): string { $value = str_replace(' ', '%20', trim($value)); $value = (string) preg_replace('|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\x80-\xff]|i', '', $value); do { $before = $value; $value = str_replace(['%0d', '%0a', '%0D', '%0A'], '', $value); } while ($value !== $before); return str_starts_with(strtolower($value), 'javascript:') ? '' : $value; } public function allowsUnfilteredHtml(): bool { return $this->unfiltered; } }; } public static function media(): MediaResolver { return new class implements MediaResolver { public function image(int $id): ?array { return match ($id) { 33 => ['url' => 'https://kunde.test/wp-content/uploads/team.jpg', 'alt' => 'Unser Team'], 12 => ['url' => 'https://kunde.test/wp-content/uploads/halle.jpg', 'alt' => ''], // Alt texts carrying a reference, whole or NUL-split (ruling S33b: dropped). 44 => ['url' => 'https://kunde.test/wp-content/uploads/a.jpg', 'alt' => ComposeFixture::REFERENCE_ALT], 45 => ['url' => 'https://kunde.test/wp-content/uploads/b.jpg', 'alt' => '$variable' . chr(0) . substr(ComposeFixture::REFERENCE_ALT, strlen('$variable'))], default => null, }; } }; } public static function placeholders(): Placeholders { return new Placeholders(DesignSystemTest::sample()); } public static function attrWriter(bool $unfiltered = false): AttrWriter { return new AttrWriter(ModuleCatalogTest::sampleCatalog(), self::placeholders(), self::sanitizer($unfiltered)); } /** * @param array>>|null $modules Short form per * type instead of ShortformMap (to reach paths the curated map never names). */ public static function shortform(bool $unfiltered = false, ?array $modules = null): ShortformWriter { return new ShortformWriter(self::placeholders(), self::media(), self::attrWriter($unfiltered), $modules); } public static function rules(): StructureRules { return new StructureRules(ModuleCatalogTest::sampleCatalog()); } /** * Builder version 5.2.1. $design replaces the sample design system (one Placeholders instance * shared by AttrWriter and ShortformWriter, like the live wiring – ruling S31). */ public static function composer(bool $unfiltered = false, ?DesignSystem $design = null): Composer { $design ??= DesignSystemTest::sample(); $placeholders = new Placeholders($design); $attrWriter = new AttrWriter(ModuleCatalogTest::sampleCatalog(), $placeholders, self::sanitizer($unfiltered)); return new Composer( ModuleCatalogTest::sampleCatalog(), self::rules(), new ShortformWriter($placeholders, self::media(), $attrWriter), $attrWriter, $design, '5.2.1' ); } }