feat: Signatur-Fingerabdruck der Divi-Aufrufe (Reflection); fingerprints.json enthält sig:-Einträge, divi-check --update-reference schreibt sie; Referenz für Divi 5.15.1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-18 22:01:56 +02:00
co-authored by Claude Sonnet 5.5
parent 392fe7c569
commit 710444e88d
8 changed files with 525 additions and 20 deletions
@@ -9,15 +9,22 @@ use RuntimeException;
defined('ABSPATH') || exit;
/**
* The expected fingerprints of the mirrors (fingerprints.json, shipped with the plugin) and their reference sources
* (tests/Fixtures/divi/reference/<id>.txt, development only – not in the plugin package). Written by
* wp lit-mcp divi-check --update-reference.
* The expected fingerprints (fingerprints.json, shipped with the plugin): the source fingerprint of every mirror under
* its ID, and the signature fingerprint of every Divi call under "sig:<symbol>" (SignatureFingerprint) – plus the
* reference sources of the mirrors (tests/Fixtures/divi/reference/<id>.txt, development only – not in the plugin
* package). Written by wp lit-mcp divi-check --update-reference.
*/
final class DiviReference
{
public const FILE = __DIR__ . '/fingerprints.json';
/** @return array<string, string> Mirror ID => sha256; [] when the file is missing or broken. */
/** Key prefix of the signature fingerprints; no mirror ID starts with it. */
public const SIGNATURE_PREFIX = 'sig:';
/**
* @return array<string, string> Mirror ID or "sig:<symbol>" => sha256, sorted by key; [] when the file is
* missing or broken.
*/
public static function expected(?string $file = null): array
{
$file ??= self::FILE;
@@ -40,7 +47,7 @@ final class DiviReference
}
/**
* @param array<string, string> $hashes Mirror ID => sha256.
* @param array<string, string> $hashes Mirror ID or "sig:<symbol>" => sha256; written sorted by key.
* @throws RuntimeException
*/
public static function writeExpected(array $hashes, ?string $file = null): void
@@ -54,6 +61,34 @@ final class DiviReference
}
}
/** The key of $call's signature fingerprint: "sig:" and the symbol without a leading backslash. */
public static function signatureKey(Call $call): string
{
return self::SIGNATURE_PREFIX . ltrim($call->symbol, '\\');
}
/**
* Signature fingerprints of the calls in the running PHP process (Divi loaded).
*
* @param list<Call>|null $calls null = DiviAdapters::calls().
* @return array<string, string> "sig:<symbol>" => sha256, sorted by key.
* @throws RuntimeException When a symbol is not loaded.
*/
public static function currentSignatures(?array $calls = null): array
{
$signatures = [];
foreach ($calls ?? DiviAdapters::calls() as $call) {
$hash = SignatureFingerprint::of($call);
if ($hash === null) {
throw new RuntimeException(sprintf('signature of %s: symbol is not loaded', $call->label()));
}
$signatures[self::signatureKey($call)] = $hash;
}
ksort($signatures);
return $signatures;
}
/** tests/Fixtures/divi/reference of a development checkout; null in an installed plugin. */
public static function sourceDir(): ?string
{