Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
126 lines
4.4 KiB
PHP
126 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace LIT\MCP\Integrations\Divi\Design;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* What a preset change does (spec §4 step 4, "diff"): per preset added, changed (with the changed top-level keys),
|
|
* removed, and per module/group a default that is set or cleared. Keys the plugin derives or Divi maintains
|
|
* (updated, styleAttrs, renderAttrs, version) are no change of their own. Sorted by kind, scope, id, action.
|
|
* Pure PHP.
|
|
*/
|
|
final class PresetDiff
|
|
{
|
|
private const DERIVED = ['updated', 'styleAttrs', 'renderAttrs', 'version'];
|
|
|
|
/**
|
|
* @return list<array{kind: string, scope: string, id: string, name: string, action: string,
|
|
* fields?: list<string>}>
|
|
*/
|
|
public static function between(PresetBook $before, PresetBook $after): array
|
|
{
|
|
$old = $before->data();
|
|
$new = $after->data();
|
|
$rows = [];
|
|
foreach ([PresetScope::MODULE, PresetScope::GROUP] as $kind) {
|
|
$names = array_unique(array_merge(array_keys($old[$kind] ?? []), array_keys($new[$kind] ?? [])));
|
|
sort($names);
|
|
foreach ($names as $name) {
|
|
$was = $old[$kind][$name] ?? ['default' => '', 'items' => []];
|
|
$is = $new[$kind][$name] ?? ['default' => '', 'items' => []];
|
|
array_push($rows, ...self::items($kind, (string) $name, $was['items'], $is['items']));
|
|
array_push($rows, ...self::defaults($kind, (string) $name, $was, $is));
|
|
}
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, array<string, mixed>> $was
|
|
* @param array<string, array<string, mixed>> $is
|
|
* @return list<array<string, mixed>>
|
|
*/
|
|
private static function items(string $kind, string $scope, array $was, array $is): array
|
|
{
|
|
$ids = array_unique(array_merge(array_keys($was), array_keys($is)));
|
|
sort($ids);
|
|
$rows = [];
|
|
foreach ($ids as $id) {
|
|
$key = (string) $id;
|
|
$before = $was[$key] ?? null;
|
|
$after = $is[$key] ?? null;
|
|
if ($before === null || $after === null) {
|
|
$rows[] = self::row($kind, $scope, $key, $after ?? $before, $before === null ? 'added' : 'removed');
|
|
continue;
|
|
}
|
|
$fields = self::changedFields($before, $after);
|
|
if ($fields !== []) {
|
|
$rows[] = self::row($kind, $scope, $key, $after, 'changed') + ['fields' => $fields];
|
|
}
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
/**
|
|
* @param array{default: string, items: array<string, array<string, mixed>>} $was
|
|
* @param array{default: string, items: array<string, array<string, mixed>>} $is
|
|
* @return list<array<string, mixed>>
|
|
*/
|
|
private static function defaults(string $kind, string $scope, array $was, array $is): array
|
|
{
|
|
if ($was['default'] === $is['default']) {
|
|
return [];
|
|
}
|
|
if ($is['default'] === '') {
|
|
$gone = $was['items'][$was['default']] ?? null;
|
|
|
|
return [self::row($kind, $scope, $was['default'], $gone, 'default_cleared')];
|
|
}
|
|
|
|
$item = $is['items'][$is['default']] ?? null;
|
|
|
|
return [self::row($kind, $scope, $is['default'], $item, 'default_set')];
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $before
|
|
* @param array<string, mixed> $after
|
|
* @return list<string>
|
|
*/
|
|
private static function changedFields(array $before, array $after): array
|
|
{
|
|
$keys = array_diff(array_unique(array_merge(array_keys($before), array_keys($after))), self::DERIVED);
|
|
$fields = [];
|
|
foreach ($keys as $key) {
|
|
$was = $before[$key] ?? null;
|
|
$is = $after[$key] ?? null;
|
|
if ($was != $is) { // phpcs:ignore Universal.Operators.StrictComparisons
|
|
$fields[] = (string) $key;
|
|
}
|
|
}
|
|
sort($fields);
|
|
|
|
return $fields;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed>|null $item
|
|
* @return array{kind: string, scope: string, id: string, name: string, action: string}
|
|
*/
|
|
private static function row(string $kind, string $scope, string $id, ?array $item, string $action): array
|
|
{
|
|
return [
|
|
'kind' => $kind,
|
|
'scope' => $scope,
|
|
'id' => $id,
|
|
'name' => is_string($item['name'] ?? null) ? $item['name'] : $id,
|
|
'action' => $action,
|
|
];
|
|
}
|
|
}
|