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>
This commit is contained in:
Jan-Niclas Loosen
2026-11-06 12:03:30 +01:00
co-authored by Claude Opus 5.5
parent 9beb55e228
commit b511ef6348
92 changed files with 1185 additions and 636 deletions
+98
View File
@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Compatibility;
use Closure;
defined('ABSPATH') || exit;
/**
* One failed manifest check and the capabilities it concerns (Manifest::featuresOf()). The kind decides how the
* resolver weighs it (E1): call, mirror, constant and global lock their features for every version, released
* or not; signature and reference-missing only matter for a version without release (automatic release). A problem
* without features concerns every feature (fail closed).
*/
final class Problem
{
public const CALL = 'call';
public const SIGNATURE = 'signature';
public const MIRROR = 'mirror';
public const CONSTANT = 'constant';
public const GLOBAL = 'global';
public const REFERENCE_MISSING = 'reference-missing';
public const RESOLVER = 'resolver';
private const KINDS = [
self::CALL,
self::SIGNATURE,
self::MIRROR,
self::CONSTANT,
self::GLOBAL,
self::REFERENCE_MISSING,
self::RESOLVER,
];
private const HARD = [self::CALL, self::MIRROR, self::CONSTANT, self::GLOBAL, self::RESOLVER];
/**
* @param string $label German, e.g. "Seiten: et_x()" (the labels of ManifestCheck).
* @param list<Capability> $features
* @param string $kind One of the constants above.
*/
public function __construct(
public readonly string $label,
public readonly array $features,
public readonly string $kind
) {
}
/** Locks its features whatever the release (the third-party code no longer fits or a mirror differs). */
public function isHard(): bool
{
return in_array($this->kind, self::HARD, true);
}
public function concerns(Capability $feature): bool
{
return $this->features === [] || in_array($feature, $this->features, true);
}
/** @return array{label: string, kind: string, features: list<string>} For the transient. */
public function toArray(): array
{
return [
'label' => $this->label,
'kind' => $this->kind,
'features' => array_map(static fn (Capability $feature): string => $feature->id(), $this->features),
];
}
/**
* The problem of toArray(), or null for anything else (an unknown kind or feature included).
*
* @param Closure(string): ?Capability $capability Finds a capability by its ID, null for an unknown one.
*/
public static function fromArray(mixed $row, Closure $capability): ?self
{
if (
! is_array($row)
|| ! is_string($row['label'] ?? null)
|| ! in_array($row['kind'] ?? null, self::KINDS, true)
|| ! is_array($row['features'] ?? null)
) {
return null;
}
$features = [];
foreach ($row['features'] as $value) {
$feature = is_string($value) ? $capability($value) : null;
if ($feature === null) {
return null;
}
$features[] = $feature;
}
return new self($row['label'], $features, $row['kind']);
}
}