fix: anchor Divi name/id patterns at string end, number-advanced stays unknown, doc wording

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 13:16:20 +02:00
co-authored by Claude Opus 5.5
parent 4356e69bb9
commit c24b91d8a5
8 changed files with 13 additions and 8 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ Breakpoints: `desktop`, `tablet`, `phone` (je nach Website auch `tabletWide`, `p
gültigen Pfade dieses Moduls auf dieser Website (live aus Divi) und die Kurzform-Felder mit ihrem Pfad.
`path_kinds` nennt die Feldart eines Pfads (`text`, `richtext`, `code`, `url`, `image`, `color`, `enum`,
`onoff`, `number`, `list`, `icon`), soweit aus den Divi-Metadaten ableitbar. Bei einem zusammengesetzten
Pfad (mehrere benannte Unterfelder unter einem Pfad, z. B. `buttonOne.innerContent` mit `text` und
Pfad (ein oder mehrere benannte Unterfelder unter einem Pfad, z. B. `buttonOne.innerContent` mit `text` und
`linkUrl`) ist der Eintrag stattdessen eine Zuordnung Unterfeld → Feldart (`{"text": "text", "linkUrl":
"url"}`), soweit für die einzelnen Unterfelder ableitbar; fehlende Pfade oder Unterfelder gelten als
unbekannt, statt eine Feldart zu raten.
+1 -1
View File
@@ -20,7 +20,7 @@ use stdClass;
final class Block extends Container
{
/** A valid block name: "namespace/block-name" (WordPress' own block name convention). */
private const NAME_PATTERN = '/^[a-z][a-z0-9_-]*\/[a-z][a-z0-9_-]*$/';
private const NAME_PATTERN = '/^[a-z][a-z0-9_-]*\/[a-z][a-z0-9_-]*$/D';
/**
* @param array<string, mixed> $attrs
@@ -14,7 +14,7 @@ namespace LIT\MCP\Integrations\Divi\BlockTree;
*/
final class NodeIndex
{
public const ID_PATTERN = '/^[srcmgx]\d+(\.[srcmgx]\d+)*$/';
public const ID_PATTERN = '/^[srcmgx]\d+(\.[srcmgx]\d+)*$/D';
/** @var array<string, Block> */
private array $byId = [];
@@ -9,7 +9,7 @@ namespace LIT\MCP\Integrations\Divi\BlockTree;
* delimiters; changed or new blocks get delimiters generated exactly like WordPress'
* get_comment_delimited_block_content() / serialize_block_attributes() do, which also
* yields the escaping Divi expects inside $variable(…)$ payloads: json_encode()'s own
* \" (an escaped quote inside a JSON string) is replaced by ", see ESCAPES below.
* \" (an escaped quote inside a JSON string) is replaced by \u0022, see ESCAPES below.
*/
final class Serializer
{
@@ -69,7 +69,10 @@ final class AttrPathIndex
* type still tells them apart reliably. Only used for composite (subName-bearing)
* candidates; a plain path keeps trusting its field component alone (COMPONENT_KINDS),
* so an already more specific kind there (richtext, code, …) is never downgraded to the
* generic "text" dynamicContent commonly names for both.
* generic "text" dynamicContent commonly names for both. number-advanced (e.g. border
* radius) holds an object of corner values and therefore stays unknown. A path that has
* both a plain field and named sub-fields keeps only the sub-field map (the plain field
* then counts as unknown).
*/
private const DYNAMIC_CONTENT_KINDS = [
'text' => 'text',
@@ -77,7 +80,6 @@ final class AttrPathIndex
'image' => 'image',
'color' => 'color',
'number' => 'number',
'number-advanced' => 'number',
];
private const ATTR_NAME_PATTERN = '/^[A-Za-z0-9]+(\.[A-Za-z0-9]+)+$/';
@@ -84,8 +84,9 @@ final class DiviCatalogAbility implements Ability
'type' => 'object',
'description' => 'Bekannter Attributpfad => Feldart (text, richtext, code, url, '
. 'image, color, enum, onoff, number, list, icon), soweit aus den Divi-Metadaten '
. 'ableitbar. Bei einem zusammengesetzten Pfad (mehrere benannte Unterfelder unter '
. 'einem Pfad, z. B. buttonOne.innerContent mit "text" und "linkUrl") ist der Wert '
. 'ableitbar. Bei einem zusammengesetzten Pfad (ein oder mehrere benannte '
. 'Unterfelder unter einem Pfad, z. B. buttonOne.innerContent mit "text" und '
. '"linkUrl") ist der Wert '
. 'stattdessen eine Zuordnung Unterfeld => Feldart, soweit für die einzelnen '
. 'Unterfelder ableitbar. Fehlende Pfade oder Unterfelder gelten als unbekannt.',
],
@@ -19,6 +19,7 @@ final class BlockTest extends TestCase
return [
'no namespace' => ['button'],
'trailing markup injection' => ['divi/x --><script>'],
'trailing newline' => ["divi/x\n"],
'uppercase' => ['Divi/Button'],
'empty' => [''],
];
@@ -42,6 +42,7 @@ final class NodeIndexTest extends TestCase
);
self::assertSame(1, preg_match(NodeIndex::ID_PATTERN, 's1.g2'));
self::assertSame(0, preg_match(NodeIndex::ID_PATTERN, 's1..g2'));
self::assertSame(0, preg_match(NodeIndex::ID_PATTERN, "s1\n"));
}
public function testDocumentWithoutPlaceholderIsItsOwnRoot(): void