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>
99 lines
3.1 KiB
PHP
99 lines
3.1 KiB
PHP
<?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']);
|
|
}
|
|
}
|