refactor: split VariableRef into ControlChars and DynamicContentRules (H2)

- ControlChars::strip()/stripDeep() (was VariableRef::withoutControls/Deep) with
  the S32/S32a/S35 docblock; callers AttrWriter, ShortformWriter, TextReplacer and
  the setting cleaner use it.
- DynamicContentRules (design system + dynamic content map): fieldsFor() (was
  contentFields: meta prefix -> map -> loop_ -> design names -> unknown),
  cleanSetting() (was setting(): raw strip -> raw marker -> field type ->
  final-string check -> meta key check, same order), one private method per
  field family (textSetting, selectPostSetting, scalarSetting), isMetaKey(),
  checkMap(), and the constants DEFAULT_DYNAMIC_CONTENT, META_SETTINGS,
  DESIGN_SETTINGS, META_KEY.
- VariableRef keeps the syntax: const END = ')$' replaces the six literals,
  payloadOf() is shared by typeOf() and canonical(); unknownName() is public for
  the rules. Error messages unchanged.
Tests unchanged apart from a class name in DynamicContentMapTest.

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 18:05:38 +02:00
co-authored by Claude Opus 5.5
parent e420145cbe
commit 874a92c6be
9 changed files with 403 additions and 316 deletions
@@ -6,24 +6,24 @@ namespace LIT\MCP\Integrations\Divi\Pages;
use Closure;
use LIT\MCP\Integrations\Divi\Compose\Placeholders;
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
use LIT\MCP\Integrations\Divi\Compose\DynamicContentRules;
use LIT\MCP\Log;
use Throwable;
/**
* VariableRef's dynamic content allow map (ruling S22) from Divi's live registry
* DynamicContentRules' dynamic content allow map (ruling S22) from Divi's live registry
* DynamicContentOptions::get_options($postId, 'edit') – the options the Visual Builder offers
* for this entry (filter divi_module_dynamic_content_options; the list depends on the post:
* post_categories/post_tags only for types with those taxonomies). Shape:
* option name => [setting key => field type], e.g. post_date => [before => richtext, …,
* date_format => select]. VariableRef routes each setting by that type and keeps its fixed
* date_format => select]. DynamicContentRules routes each setting by that type and keeps its fixed
* rules whatever the map says (enable_html refused, meta keys S29/S30, no markers S28).
*
* - loop_* options never enter the map: Divi offers them only inside loop-enabled modules,
* which the tools do not track – VariableRef refuses them ("nur innerhalb von Divi-Schleifen").
* - The map REPLACES VariableRef's default, so every Placeholders::POST option is added when
* the registry lacks it, with the settings the placeholders write (VariableRef's default).
* - Registry unavailable (no Divi, empty, error): null – VariableRef's default map applies.
* which the tools do not track – DynamicContentRules refuses them ("nur innerhalb von Divi-Schleifen").
* - The map REPLACES DynamicContentRules' default, so every Placeholders::POST option is added when
* the registry lacks it, with the settings the placeholders write (DynamicContentRules' default).
* - Registry unavailable (no Divi, empty, error): null – DynamicContentRules' default map applies.
*
* Not final so abilities can be tested with a fake registry.
*/
@@ -38,7 +38,7 @@ class DynamicContentMap
{
}
/** @return array<string, array<string, string>>|null Null = use VariableRef's default map. */
/** @return array<string, array<string, string>>|null Null = use DynamicContentRules' default map. */
public function forPost(int $postId): ?array
{
try {
@@ -74,7 +74,7 @@ class DynamicContentMap
}
foreach (Placeholders::POST as $option) {
$map[$option] = ($map[$option] ?? []) + (VariableRef::DEFAULT_DYNAMIC_CONTENT[$option] ?? []);
$map[$option] = ($map[$option] ?? []) + (DynamicContentRules::DEFAULT_DYNAMIC_CONTENT[$option] ?? []);
}
return $map;
@@ -85,7 +85,7 @@ class DynamicContentMap
* outputs them as HTML (DynamicContentElements::get_wrapper_element, wp_kses by the author's
* unfiltered_html), and sanitize_text_field() would trim their meaningful spaces ("Von ").
* html() is kses for editors, the S28 marker checks hold either way. No readable type: '' –
* VariableRef's strictest branch (a scalar without markup).
* DynamicContentRules' strictest branch (a scalar without markup).
*/
private static function type(string $key, mixed $field): string
{