TenantAtlas/apps/platform/app/Services/TenantConfiguration/GenericPayloadNormalizer.php
Ahmed Darrazi 736e61c73e
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m37s
feat: add generic content-backed coverage capture
2026-06-25 21:55:27 +02:00

73 lines
1.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\TenantConfiguration;
final class GenericPayloadNormalizer
{
/**
* @param array<string, mixed> $payload
* @param list<string> $volatileFields
* @return array<string, mixed>
*/
public function normalize(array $payload, array $volatileFields = []): array
{
$volatileLookup = array_fill_keys(array_map('strval', $volatileFields), true);
return $this->normalizeValue($payload, $volatileLookup);
}
/**
* @param array<string, mixed> $normalizedPayload
*/
public function payloadHash(array $normalizedPayload): string
{
return hash('sha256', $this->canonicalJson($normalizedPayload));
}
/**
* @param array<string, mixed> $payload
*/
public function canonicalJson(array $payload): string
{
return json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
}
/**
* @param array<string, bool> $volatileLookup
* @return mixed
*/
private function normalizeValue(mixed $value, array $volatileLookup): mixed
{
if (! is_array($value)) {
return $value;
}
if ($this->isList($value)) {
return array_map(fn (mixed $item): mixed => $this->normalizeValue($item, $volatileLookup), $value);
}
$normalized = [];
foreach ($value as $key => $nestedValue) {
$key = (string) $key;
if (isset($volatileLookup[$key])) {
continue;
}
$normalized[$key] = $this->normalizeValue($nestedValue, $volatileLookup);
}
ksort($normalized);
return $normalized;
}
private function isList(array $value): bool
{
return array_is_list($value);
}
}