$selectionPayload * @return array{policy_types: list, categories: list, include_foundations: bool, include_dependencies: bool} */ public function normalize(array $selectionPayload): array { $policyTypes = $this->stringList($selectionPayload['policy_types'] ?? []); sort($policyTypes); $categories = $this->stringList($selectionPayload['categories'] ?? []); sort($categories); return [ 'policy_types' => $policyTypes, 'categories' => $categories, 'include_foundations' => (bool) ($selectionPayload['include_foundations'] ?? false), 'include_dependencies' => (bool) ($selectionPayload['include_dependencies'] ?? false), ]; } /** * @param array $selectionPayload */ public function canonicalJson(array $selectionPayload): string { $normalized = $this->normalize($selectionPayload); $normalized = $this->ksortRecursive($normalized); return (string) json_encode($normalized, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } /** * @param array $selectionPayload */ public function hash(array $selectionPayload): string { return hash('sha256', $this->canonicalJson($selectionPayload)); } /** * @return list */ private function stringList(mixed $value): array { if (! is_array($value)) { return []; } $result = []; foreach ($value as $item) { if (! is_string($item)) { continue; } $item = trim($item); if ($item === '') { continue; } $result[] = $item; } return array_values(array_unique($result)); } private function ksortRecursive(mixed $value): mixed { if (! is_array($value)) { return $value; } $isList = array_is_list($value); if (! $isList) { ksort($value); } foreach ($value as $key => $child) { $value[$key] = $this->ksortRecursive($child); } return $value; } }