Files
WP-LIT-MCP/src/Integrations/Divi/Pages/DiviComposition.php
T

166 lines
6.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\Divi\Pages;
use LIT\MCP\Integrations\Divi\Compose\AttrWriter;
use LIT\MCP\Integrations\Divi\Compose\Composer;
use LIT\MCP\Integrations\Divi\Compose\LinkTargets;
use LIT\MCP\Integrations\Divi\Compose\MediaResolver;
use LIT\MCP\Integrations\Divi\Compose\Placeholders;
use LIT\MCP\Integrations\Divi\Compose\ShortformWriter;
use LIT\MCP\Integrations\Divi\Compose\StoredNodeGuard;
use LIT\MCP\Integrations\Divi\Compose\StructureRules;
use LIT\MCP\Integrations\Divi\Compose\TreeEditor;
use LIT\MCP\Integrations\Divi\Compose\ValueCleaner;
use LIT\MCP\Integrations\Divi\Compose\ValueSanitizer;
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
use LIT\MCP\Integrations\Divi\Compose\Warnings;
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter;
use LIT\MCP\Integrations\Divi\Design\PresetAssignment;
use LIT\MCP\Integrations\Divi\Design\PresetBook;
use LIT\MCP\Integrations\Divi\Schema\DiviModules;
defined('ABSPATH') || exit;
/**
* Wires the pure Compose classes with the live catalog, design system, Divi's dynamic content
* registry and the WordPress sanitizer/media lookup – once per request and entry. Ruling S31:
* ONE VariableRef (carrying the live dynamic content map of the entry, S22), ONE Placeholders
* and ONE ValueCleaner instance are shared by AttrWriter, ShortformWriter and Composer, and the
* same AttrWriter serves Composer and the abilities.
*
* Presets (D7) reach blocks through ONE PresetAssignment per composition: its book is Divi's stored preset
* inventory (DesignSystemWriter::presets()), read lazily and at most once – only when a write names a preset or a
* warning looks at a block that has one – and Divi's option groups come from DesignSystemWriter::groupsOf().
* Without a preset source (null, unit tests) there are no presets.
*
* forPost() binds the composition to the entry being written (DiviWriter::composition() does it
* for the abilities); unbound, VariableRef's default map applies. Not final so abilities can be
* tested with a mock.
*/
class DiviComposition
{
private ?int $postId = null;
private ?string $themeBuilderArea = null;
private ?VariableRef $references = null;
private ?Placeholders $placeholders = null;
private ?ValueCleaner $cleaner = null;
private ?AttrWriter $attrWriter = null;
private ?Composer $composer = null;
private ?PresetAssignment $assignment = null;
public function __construct(
private DiviModules $modules,
private DesignSystemReader $design,
private ValueSanitizer $sanitizer = new WpValueSanitizer(),
private MediaResolver $media = new WpMediaResolver(),
private DynamicContentMap $dynamicContent = new DynamicContentMap(),
private LinkTargets $links = new WpLinkTargets(),
private ?DesignSystemWriter $presetSource = null
) {
}
/** A fresh composition for entry $postId (own VariableRef with its live map; TB area for StructureRules). */
public function forPost(int $postId, ?string $themeBuilderArea = null): static
{
$bound = clone $this;
$bound->postId = $postId;
$bound->themeBuilderArea = $themeBuilderArea;
$bound->references = null;
$bound->placeholders = null;
$bound->cleaner = null;
$bound->attrWriter = null;
$bound->composer = null;
$bound->assignment = null;
return $bound;
}
/**
* The one checker of $variable(…)$ values of this composition, with the entry's live map (S22, S31).
*
* @internal Public for tests; code uses cleaner() / composer().
*/
public function references(): VariableRef
{
return $this->references ??= new VariableRef(
$this->design->read(),
$this->postId === null ? null : $this->dynamicContent->forPost($this->postId)
);
}
/**
* The one Placeholders instance of this composition (ruling S31).
*
* @internal Public for tests; code uses attrWriter() / composer().
*/
public function placeholders(): Placeholders
{
return $this->placeholders ??= new Placeholders($this->design->read(), $this->links);
}
/** The one choke point of this composition (ValueCleaner, rulings S1–S3, S7, S13, S28, S32). */
public function cleaner(): ValueCleaner
{
return $this->cleaner ??= new ValueCleaner($this->modules->catalog(), $this->references(), $this->sanitizer);
}
public function attrWriter(): AttrWriter
{
return $this->attrWriter ??= new AttrWriter($this->modules->catalog(), $this->placeholders(), $this->cleaner());
}
/**
* The one PresetAssignment of this composition (D7, rulings R2, R3): the book comes lazily from Divi's stored
* presets, ids must pass the choke point unchanged.
*
* @internal Public for tests; code uses composer() / warnings().
*/
public function presets(): PresetAssignment
{
return $this->assignment ??= new PresetAssignment(
fn (): PresetBook => PresetBook::fromDivi($this->presetSource?->presets() ?? []),
fn (string $module): array => $this->presetSource?->groupsOf($module) ?? [],
$this->cleaner()->keepsId(...)
);
}
public function composer(): Composer
{
return $this->composer ??= new Composer(
$this->modules->catalog(),
$this->rules(),
new ShortformWriter($this->placeholders(), $this->media, $this->cleaner()),
$this->attrWriter(),
$this->cleaner(),
$this->presets(),
$this->modules->builderVersion()
);
}
/** @internal Public for tests; code uses composer() / editor(). */
public function rules(): StructureRules
{
return new StructureRules($this->modules->catalog(), $this->themeBuilderArea);
}
/** E6: the check for stored nodes that move into this composition's entry (same cleaner and references). */
public function storedGuard(): StoredNodeGuard
{
return new StoredNodeGuard($this->cleaner(), $this->references(), $this->sanitizer);
}
public function editor(): TreeEditor
{
return new TreeEditor($this->rules());
}
public function warnings(): Warnings
{
return new Warnings($this->design->read());
}
}