feat: ThemeOptionsArea – Bereich theme_options ersetzt die Whitelist, prüft das Zurücklesen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
05ff2d90f3
commit
1bc8ce65b7
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Design;
|
||||
|
||||
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* The area "theme_options" (spec §8.1, rulings W2, W3, W6): the 20 whitelisted Divi theme options as et_divi stores
|
||||
* them (ThemeOptionBook); a key missing from the inventory is unset, i.e. at Divi's default. Every write replaces the
|
||||
* whole whitelist through ThemeOptionsAdapter::write() – keys of the inventory are set, whitelist keys missing from it
|
||||
* are deleted – so a change, a restore and a rollback all leave exactly the given inventory. verify() insists on the
|
||||
* same keys with the same values and only tolerates a number stored as another type (15, "15", 15.0);
|
||||
* differences() names those.
|
||||
*/
|
||||
final class ThemeOptionsArea implements DesignArea
|
||||
{
|
||||
/** Ruling W7: theme options have no page reference; every plan warns about their reach instead. */
|
||||
public const SITE_WIDE = 'Theme-Optionen wirken auf die gesamte Website.';
|
||||
|
||||
public function __construct(private ThemeOptionsAdapter $options)
|
||||
{
|
||||
}
|
||||
|
||||
public function name(): string
|
||||
{
|
||||
return 'theme_options';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Theme-Optionen';
|
||||
}
|
||||
|
||||
public function feature(): DiviFeature
|
||||
{
|
||||
return DiviFeature::ThemeOptionWrite;
|
||||
}
|
||||
|
||||
public function raw(): array
|
||||
{
|
||||
return ThemeOptionBook::fromDivi($this->options->read())->data();
|
||||
}
|
||||
|
||||
public function rev(array $raw): string
|
||||
{
|
||||
return ThemeOptionBook::fromDivi($raw)->rev();
|
||||
}
|
||||
|
||||
/** $mayRemove has no effect: a key missing from the planned inventory is a reset to Divi's default (W3). */
|
||||
public function write(array $raw, bool $mayRemove): array
|
||||
{
|
||||
$planned = ThemeOptionBook::fromDivi($raw)->data();
|
||||
$saved = $this->options->write(
|
||||
$planned,
|
||||
array_values(array_diff(ThemeOptionSpec::KEYS, array_keys($planned)))
|
||||
);
|
||||
|
||||
return ThemeOptionBook::fromDivi($saved)->data();
|
||||
}
|
||||
|
||||
public function restore(array $raw): array
|
||||
{
|
||||
return $this->write($raw, true);
|
||||
}
|
||||
|
||||
public function verify(array $expected, array $actual): bool
|
||||
{
|
||||
$expected = ThemeOptionBook::fromDivi($expected)->data();
|
||||
$actual = ThemeOptionBook::fromDivi($actual)->data();
|
||||
if (array_keys($expected) !== array_keys($actual)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($expected as $key => $value) {
|
||||
if (! self::same($value, $actual[$key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function differences(array $expected, array $actual): array
|
||||
{
|
||||
$actual = ThemeOptionBook::fromDivi($actual)->data();
|
||||
$warnings = [];
|
||||
foreach (ThemeOptionBook::fromDivi($expected)->data() as $key => $value) {
|
||||
if (array_key_exists($key, $actual) && $actual[$key] !== $value) {
|
||||
$warnings[] = sprintf(
|
||||
'Divi hat die Theme-Option „%s“ als %s statt %s gespeichert.',
|
||||
ThemeOptionSpec::definition($key)['label'],
|
||||
self::shown($actual[$key]),
|
||||
self::shown($value)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $warnings;
|
||||
}
|
||||
|
||||
/** Whether Divi keeps $b as $a: equal, or the same number of another type (absint, floatval, a digit string). */
|
||||
public static function same(mixed $a, mixed $b): bool
|
||||
{
|
||||
return $a === $b || (is_numeric($a) && is_numeric($b) && (float) $a === (float) $b);
|
||||
}
|
||||
|
||||
private static function shown(int|float|string|false $value): string
|
||||
{
|
||||
return match (true) {
|
||||
is_string($value) => '„' . $value . '“',
|
||||
$value === false => 'false',
|
||||
default => (string) $value,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user