feat: VariableArea schreibt globale Variablen mit Divis Stale-Prüfung und behält beim Wiederherstellen spätere Variablen archiviert

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-19 14:10:02 +02:00
co-authored by Claude Sonnet 5.5
parent 2fd03f3900
commit f1f70114f4
2 changed files with 265 additions and 0 deletions
@@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Integrations\Divi\Design;
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
defined('ABSPATH') || exit;
/**
* The area "variables" (spec §7.2, rulings V1, V7): the global variables in their stored form (VariableBook), saved
* as a whole through GlobalVariablesAdapter::save() together with the inventory read right before, which Divi checks
* for changes in between (global_variables_stale). Divi refuses an inventory that lacks a stored ID, so a restore keeps
* the variables created after the snapshot, archived (VariableBook::restoredFrom()) – also when the pipeline rolls back
* a write that went through. Without the variables_manager right Divi silently saves nothing: verify() compares the
* read-back (same variables, status and non-empty values), differences() names the values Divi sanitized.
*/
final class VariableArea implements DesignArea
{
public function __construct(private GlobalVariablesAdapter $variables)
{
}
public function name(): string
{
return 'variables';
}
public function label(): string
{
return 'Variablen';
}
public function feature(): DiviFeature
{
return DiviFeature::VariableWrite;
}
public function raw(): array
{
return VariableBook::fromDivi($this->variables->read())->data();
}
public function rev(array $raw): string
{
return VariableBook::fromDivi($raw)->rev();
}
/** $mayRemove has no effect: variables are never removed (ruling V1). */
public function write(array $raw, bool $mayRemove): array
{
$this->variables->save(VariableBook::fromDivi($raw)->data(), $this->variables->read());
return $this->raw();
}
public function restore(array $raw): array
{
$live = $this->variables->read();
$this->variables->save(
VariableBook::fromDivi($live)->restoredFrom(VariableBook::fromDivi($raw))->data(),
$live
);
return $this->raw();
}
public function verify(array $expected, array $actual): bool
{
return self::shape($expected) === self::shape($actual);
}
public function differences(array $expected, array $actual): array
{
$warnings = [];
foreach (VariableDiff::between(VariableBook::fromDivi($expected), VariableBook::fromDivi($actual)) as $row) {
if ($row['action'] === 'changed') {
$warnings[] = sprintf('Divi hat den Wert der Variablen „%s“ beim Speichern bereinigt.', $row['name']);
}
}
return $warnings;
}
/**
* @param array<array-key, mixed> $raw
* @return array<string, array{0: string, 1: bool}> "type/id" => [status, has a value], sorted
*/
private static function shape(array $raw): array
{
$shape = [];
foreach (VariableBook::fromDivi($raw)->data() as $type => $entries) {
foreach ($entries as $id => $entry) {
$value = $entry['value'] ?? '';
$shape[$type . '/' . $id] = [
(string) ($entry['status'] ?? ''),
is_scalar($value) && (string) $value !== '',
];
}
}
ksort($shape);
return $shape;
}
}