Files
WP-LIT-MCP/src/Integrations/Divi/Design/GlobalVariablesAdapter.php
T
Jan-Niclas LoosenandClaude Opus 5.5 b511ef6348 refactor(compat): Prüfbausteine nach src/Compatibility, Matrix und ManifestCheck generisch
Requirement, Call, ConstantRequirement, Mirror, Problem, FeatureStatus, CompatibilityMatrix, Manifest, ManifestCheck sowie Fingerprints und LineDiff liegen jetzt im divi-freien Kern. Neu: GlobalRequirement, CompatibilityMatrix::fromProblems(), ManifestCheck-Option signatures, DiviManifestCheck::create() und DiviAreas. Divi-Verhalten unverändert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 12:03:30 +01:00

115 lines
4.5 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\Design;
use ET\Builder\Packages\GlobalData\GlobalData;
use LIT\MCP\Compatibility\Call;
use LIT\MCP\Integrations\Divi\Release\DiviAdapter;
use LIT\MCP\Integrations\Divi\Release\DiviAreas;
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
use LIT\MCP\Integrations\Divi\Release\DiviLocked;
use LIT\MCP\Compatibility\Manifest;
use LIT\MCP\Integrations\Divi\Release\ReleaseGate;
use RuntimeException;
use WP_Error;
defined('ABSPATH') || exit;
/**
* Adapter "Designsystem" (spec §3) for the global variables (formats §2, plan 4c V7): reads them through
* GlobalData::get_global_variables() back into the stored form and writes them through set_global_variables() with
* the inventory the change was planned on (Divi's own stale check). Without edit_theme_options or Divi's role right
* "Variables Manager" Divi silently saves nothing – DesignRights::variables() asks canManage() first. Not final so
* the variable area can be tested with a mock.
*/
class GlobalVariablesAdapter implements DiviAdapter
{
public static function manifest(): Manifest
{
return new Manifest(
DiviAreas::DESIGN,
calls: [
new Call(GlobalData::class . '::get_global_variables', 0),
new Call(GlobalData::class . '::set_global_variables', 2),
new Call('et_pb_is_allowed', 1),
],
features: [DiviFeature::VariableWrite]
);
}
/**
* The stored form: type => ID => entry as arrays, without allowedActions, without the customizer fonts and the
* group "colors" (both are not stored there), empty groups left out.
*
* @return array<string, array<string, array<string, mixed>>>
*/
public function read(): array
{
$data = [];
foreach (GlobalData::get_global_variables() as $type => $items) {
if ($type === 'colors' || (! is_array($items) && ! is_object($items))) {
continue;
}
foreach ((array) $items as $id => $item) {
// Divi's sanitizing keeps gvid- IDs only; the customizer fonts (--et_global_*_font) are not stored.
if (! str_starts_with((string) $id, 'gvid-') || (! is_array($item) && ! is_object($item))) {
continue;
}
$entry = (array) $item;
unset($entry['allowedActions']);
$data[(string) $type][(string) $id] = $entry;
}
}
return $data;
}
/**
* @param array<string, array<string, array<string, mixed>>> $data The whole new inventory (stored form).
* @param array<string, array<string, array<string, mixed>>> $expected The inventory the change was planned on.
* @throws DiviLocked When variable-write is not released – before anything is written.
* @throws RuntimeException When Divi refuses the inventory (stale, missing IDs, invalid entries).
*/
public function save(array $data, array $expected): void
{
ReleaseGate::assertFeature(DiviFeature::VariableWrite);
$refusal = GlobalData::set_global_variables($data, $expected);
if ($refusal instanceof WP_Error) {
throw new RuntimeException('global variables refused: ' . $refusal->get_error_code() . ' '
. $refusal->get_error_message());
}
}
/** Divi's role right "Variables Manager"; fail closed without Divi. */
public function canManage(): bool
{
return function_exists('et_pb_is_allowed') && (bool) et_pb_is_allowed('variables_manager');
}
/**
* Audit L5: GlobalData keeps a static copy of the variables that can be older than another writer's finished
* save (the option cache is dropped by DesignLock). Called under the lock, before reading.
*/
public function refresh(): void
{
self::forgetCached('variables');
}
/** Resets one entry of GlobalData's static runtime cache (private, so by reflection; gone = nothing to reset). */
private static function forgetCached(string $key): void
{
try {
$cache = new \ReflectionProperty(GlobalData::class, '_cached_data');
$data = $cache->getValue();
if (is_array($data) && array_key_exists($key, $data)) {
$data[$key] = null;
$cache->setValue(null, $data);
}
} catch (\ReflectionException) {
// Divi without that property: its reads are not cached this way.
}
}
}