feat: Divi composer – short/full form to blocks with structure rules, presets and labels

Labels, background_color and preset ids go through the choke point (S13);
ambiguous preset names fail with their ids (S13a); fullwidth + rows is an
error. Carry-ins: control characters stripped before the marker check (S32),
kind override may only tighten, reference alt texts dropped, color check via
VariableRef::typeOf (S33).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
2026-09-26 14:15:33 +02:00
co-authored by Claude Opus 5.5
parent f86cd5b34a
commit 4b72c2bb42
11 changed files with 1314 additions and 17 deletions
@@ -161,6 +161,15 @@ final class VariableRefTest extends TestCase
'allein im Wert',
],
'octet-split marker in date format' => [self::content('post_date', '{"custom_date_format":"$variable%41(y"}'), 'allein im Wert'],
// S32: text() keeps control characters (like sanitize_text_field); VariableRef strips them.
'NUL-split marker in date format' => [
self::content('post_date', '{"custom_date_format":"$variable' . self::escape('0000') . '(y"}'),
'allein im Wert',
],
'DEL-split marker in date format' => [
self::content('post_date', '{"custom_date_format":"$variable' . self::escape('007f') . '(y"}'),
'allein im Wert',
],
'nested marker in before' => [self::content('post_title', '{"before":"$variable(","after":""}'), 'allein im Wert'],
'broken json' => ['$variable(kaputt)$', 'JSON'],
'trailing text' => [self::content('post_title', '{}') . ' ', 'allein im Wert'],
@@ -395,6 +404,19 @@ final class VariableRefTest extends TestCase
self::ref($map); // @phpstan-ignore-line
}
/** Ruling S33c: the type of a whole reference, read from its payload (not from a string prefix). */
public function testTypeOfAReference(): void
{
self::assertSame('color', VariableRef::typeOf('$variable({"type":"color","value":{"name":"gcid-a1b2","settings":{}}})$'));
self::assertSame('content', VariableRef::typeOf(self::content('post_title', '{}')));
self::assertSame('color', VariableRef::typeOf('$variable({"value":{"name":"gcid-a1b2"},"type":"color"})$'));
self::assertNull(VariableRef::typeOf('#1a1a1a'));
self::assertNull(VariableRef::typeOf('$variable(kaputt)$'));
self::assertNull(VariableRef::typeOf('$variable({"type":"evil","value":{"name":"x"}})$'));
self::assertNull(VariableRef::typeOf('#fff $variable({"type":"color","value":{"name":"gcid-a1b2","settings":{}}})$'));
self::assertNull(VariableRef::typeOf('$variable({"type":"color","value":{"name":"gcid-a1b2","settings":{}}})$ '));
}
public function testDetectsReferences(): void
{
self::assertTrue(VariableRef::contains('a $variable( b'));