feat: Divi-Fähigkeiten (DiviFeature, Requirement, ConstantRequirement) an Call, Mirror und Manifest; alle acht Adapter getaggt; Tests für Vollständigkeit und die gepinnte Zuordnung
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
e6e0f914ba
commit
392fe7c569
@@ -10,11 +10,17 @@ defined('ABSPATH') || exit;
|
||||
* One Divi function or static method an adapter calls (spec §4.1), with the number of arguments the plugin passes.
|
||||
* $args null: only its existence matters (a function Divi itself needs, e.g. et_() inside store_template).
|
||||
*/
|
||||
final class Call
|
||||
final class Call implements Requirement
|
||||
{
|
||||
/** @param string $symbol "function_name" or "Fully\Qualified\Class::method". */
|
||||
public function __construct(public readonly string $symbol, public readonly ?int $args = null)
|
||||
{
|
||||
/**
|
||||
* @param string $symbol "function_name" or "Fully\Qualified\Class::method".
|
||||
* @param list<DiviFeature> $features [] = the default of the manifest.
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $symbol,
|
||||
public readonly ?int $args = null,
|
||||
private readonly array $features = []
|
||||
) {
|
||||
}
|
||||
|
||||
public function className(): ?string
|
||||
@@ -27,6 +33,11 @@ final class Call
|
||||
return str_contains($this->symbol, '::') ? explode('::', $this->symbol, 2)[1] : $this->symbol;
|
||||
}
|
||||
|
||||
public function features(): array
|
||||
{
|
||||
return $this->features;
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
$class = $this->className();
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Release;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/** A Divi constant an adapter reads (spec §4.1); it must be defined. */
|
||||
final class ConstantRequirement implements Requirement
|
||||
{
|
||||
/** @param list<DiviFeature> $features [] = the default of the manifest. */
|
||||
public function __construct(public readonly string $name, private readonly array $features = [])
|
||||
{
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'Konstante ' . $this->name;
|
||||
}
|
||||
|
||||
public function features(): array
|
||||
{
|
||||
return $this->features;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Release;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
// The installed PHPCompatibility sniffs predate enums (PHP 8.1) and read their methods as plain functions.
|
||||
// phpcs:disable PHPCompatibility.Variables.ForbiddenThisUseContexts.OutsideObjectContext
|
||||
|
||||
/**
|
||||
* What the plugin can do with Divi, as far as a Divi update may break it separately: every manifest entry (call,
|
||||
* mirror, constant) belongs to one or more features, and the release gate decides per feature.
|
||||
*/
|
||||
enum DiviFeature: string
|
||||
{
|
||||
case PageRead = 'page-read';
|
||||
case PageWrite = 'page-write';
|
||||
case Catalog = 'catalog';
|
||||
case DynamicSources = 'dynamic-sources';
|
||||
case DesignRead = 'design-read';
|
||||
case PresetWrite = 'preset-write';
|
||||
case LibraryRead = 'library-read';
|
||||
case LibraryWrite = 'library-write';
|
||||
case Roles = 'roles';
|
||||
case ThemeBuilderRead = 'tb-read';
|
||||
case ThemeBuilderWrite = 'tb-write';
|
||||
|
||||
/** Whether the feature changes data (the four *Write cases). */
|
||||
public function isWrite(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
self::PageWrite, self::PresetWrite, self::LibraryWrite, self::ThemeBuilderWrite => true,
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
|
||||
/** Name for messages, the status tool and the admin tab (German). */
|
||||
public function label(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::PageRead => 'Seiten lesen',
|
||||
self::PageWrite => 'Seiten schreiben',
|
||||
self::Catalog => 'Modulkatalog',
|
||||
self::DynamicSources => 'Dynamische Inhalte',
|
||||
self::DesignRead => 'Designsystem lesen',
|
||||
self::PresetWrite => 'Presets schreiben',
|
||||
self::LibraryRead => 'Bibliothek lesen',
|
||||
self::LibraryWrite => 'Bibliothek schreiben',
|
||||
self::Roles => 'Divi-Rollen',
|
||||
self::ThemeBuilderRead => 'Theme Builder lesen',
|
||||
self::ThemeBuilderWrite => 'Theme Builder schreiben',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -18,27 +18,56 @@ final class Manifest
|
||||
public const DESIGN = 'Designsystem';
|
||||
public const CATALOG = 'Katalog';
|
||||
|
||||
/** @var list<ConstantRequirement> */
|
||||
public readonly array $constants;
|
||||
|
||||
/**
|
||||
* @param string $area One of the constants above (the five areas of spec §3).
|
||||
* @param list<Call> $calls Divi functions and static methods the adapter calls.
|
||||
* @param list<string> $constants Divi constants it reads.
|
||||
* @param list<string> $postTypes Divi post types it needs registered.
|
||||
* @param list<string> $taxonomies Divi taxonomies it needs registered.
|
||||
* @param list<string> $blockTypes Divi block types it needs (registered or in Divi's module metadata).
|
||||
* @param list<string> $keys Other Divi names it uses as strings: meta keys, options, internal post
|
||||
* types that need no registration check, query arguments.
|
||||
* @param list<Mirror> $mirrors Divi functions whose storage logic it repeats.
|
||||
* @param string $area One of the constants above (the five areas of spec §3).
|
||||
* @param list<Call> $calls Divi functions and static methods the adapter calls.
|
||||
* @param list<string|ConstantRequirement> $constants Divi constants it reads; a plain name stands for a
|
||||
* ConstantRequirement without features of its own.
|
||||
* @param list<string> $postTypes Divi post types it needs registered.
|
||||
* @param list<string> $taxonomies Divi taxonomies it needs registered.
|
||||
* @param list<string> $blockTypes Divi block types it needs (registered or in Divi's
|
||||
* module metadata).
|
||||
* @param list<string> $keys Other Divi names it uses as strings: meta keys, options,
|
||||
* internal post types that need no registration check,
|
||||
* query arguments.
|
||||
* @param list<Mirror> $mirrors Divi functions whose storage logic it repeats.
|
||||
* @param list<DiviFeature> $features The features of every call, mirror and constant that names
|
||||
* none itself (featuresOf()).
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $area,
|
||||
public readonly array $calls = [],
|
||||
public readonly array $constants = [],
|
||||
array $constants = [],
|
||||
public readonly array $postTypes = [],
|
||||
public readonly array $taxonomies = [],
|
||||
public readonly array $blockTypes = [],
|
||||
public readonly array $keys = [],
|
||||
public readonly array $mirrors = []
|
||||
public readonly array $mirrors = [],
|
||||
public readonly array $features = []
|
||||
) {
|
||||
$this->constants = array_map(
|
||||
static fn (string|ConstantRequirement $constant): ConstantRequirement => is_string($constant)
|
||||
? new ConstantRequirement($constant)
|
||||
: $constant,
|
||||
array_values($constants)
|
||||
);
|
||||
}
|
||||
|
||||
/** @return list<Requirement> Calls, constants and mirrors – everything the release gate checks as code. */
|
||||
public function requirements(): array
|
||||
{
|
||||
return [...$this->calls, ...$this->constants, ...$this->mirrors];
|
||||
}
|
||||
|
||||
/** @return list<DiviFeature> The features $requirement names itself, else this manifest's default. */
|
||||
public function featuresOf(Requirement $requirement): array
|
||||
{
|
||||
$own = $requirement->features();
|
||||
|
||||
return $own !== [] ? $own : $this->features;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,11 +80,12 @@ final class Manifest
|
||||
foreach ($this->calls as $call) {
|
||||
$names[] = $call->className() ?? $call->member();
|
||||
}
|
||||
$constants = array_map(static fn (ConstantRequirement $constant): string => $constant->name, $this->constants);
|
||||
$mirrored = array_map(static fn (Mirror $mirror): string => $mirror->function, $this->mirrors);
|
||||
|
||||
return array_values(array_unique(array_merge(
|
||||
$names,
|
||||
$this->constants,
|
||||
$constants,
|
||||
$this->postTypes,
|
||||
$this->taxonomies,
|
||||
$this->keys,
|
||||
|
||||
@@ -57,7 +57,8 @@ class ManifestCheck
|
||||
$checks[$manifest->area . ': ' . $call->label()] = static fn (): bool => self::accepts($call);
|
||||
}
|
||||
foreach ($manifest->constants as $constant) {
|
||||
$checks[$manifest->area . ': Konstante ' . $constant] = static fn (): bool => defined($constant);
|
||||
$checks[$manifest->area . ': ' . $constant->label()] =
|
||||
static fn (): bool => defined($constant->name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,25 +10,33 @@ defined('ABSPATH') || exit;
|
||||
* A Divi function whose storage logic an adapter repeats instead of calling it (spec §4.1 "mirrors"). Its
|
||||
* fingerprint (SourceFingerprint) must match the one in fingerprints.json, else Divi changed what we copy.
|
||||
*/
|
||||
final class Mirror
|
||||
final class Mirror implements Requirement
|
||||
{
|
||||
/**
|
||||
* @param string $id Key in fingerprints.json and name of tests/Fixtures/divi/reference/<id>.txt.
|
||||
* @param string $file Divi file, relative to the Divi theme directory.
|
||||
* @param string $function Function or method name.
|
||||
* @param string|null $class Class of a method – the short name is enough (the file fixes where it
|
||||
* lives, and a short name is no Divi name for DiviBoundaryTest); null for a function.
|
||||
* @param string $what What the plugin repeats (German, for reports).
|
||||
* @param string $id Key in fingerprints.json and name of tests/Fixtures/divi/reference/<id>.txt.
|
||||
* @param string $file Divi file, relative to the Divi theme directory.
|
||||
* @param string $function Function or method name.
|
||||
* @param string|null $class Class of a method – the short name is enough (the file fixes where it
|
||||
* lives, and a short name is no Divi name for DiviBoundaryTest); null for a
|
||||
* function.
|
||||
* @param string $what What the plugin repeats (German, for reports).
|
||||
* @param list<DiviFeature> $features [] = the default of the manifest.
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $id,
|
||||
public readonly string $file,
|
||||
public readonly string $function,
|
||||
public readonly ?string $class,
|
||||
public readonly string $what
|
||||
public readonly string $what,
|
||||
private readonly array $features = []
|
||||
) {
|
||||
}
|
||||
|
||||
public function features(): array
|
||||
{
|
||||
return $this->features;
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
$class = $this->class === null ? '' : SourceFingerprint::shortName($this->class) . '::';
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Release;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/** One thing an adapter needs from Divi (a call, a mirrored function or a constant) and the features it serves. */
|
||||
interface Requirement
|
||||
{
|
||||
/** Name for reports, e.g. "GlobalPreset::get_data()" (without the manifest area). */
|
||||
public function label(): string;
|
||||
|
||||
/**
|
||||
* The features this requirement serves itself; [] = the default of the manifest that lists it
|
||||
* (Manifest::featuresOf() resolves that).
|
||||
*
|
||||
* @return list<DiviFeature>
|
||||
*/
|
||||
public function features(): array;
|
||||
}
|
||||
Reference in New Issue
Block a user