$backupItemId, 'tenant_external_id' => trim($tenantExternalId), 'policy_external_id' => trim($policyExternalId), ]); } /** * @param array> $assignments * @param array $groupMapping * @param array $foundationMapping */ public static function forRestore( int $restoreRunId, int $tenantId, string $policyType, string $policyId, array $assignments, array $groupMapping, array $foundationMapping = [], ): string { return self::hash('assignments.restore', [ 'restore_run_id' => $restoreRunId, 'tenant_id' => $tenantId, 'policy_type' => trim($policyType), 'policy_id' => trim($policyId), 'assignments' => self::normalizeAssignments($assignments), 'group_mapping' => $groupMapping, 'foundation_mapping' => $foundationMapping, ]); } public static function executionIdentityKey( string $jobType, int $tenantId, string $fingerprint, ?int $operationRunId = null, ): string { if (is_int($operationRunId) && $operationRunId > 0) { return 'operation_run:'.$operationRunId; } return 'tenant:'.$tenantId .'|job_type:'.trim($jobType) .'|fingerprint:'.trim($fingerprint); } /** * @param array $identityInputs */ private static function hash(string $jobType, array $identityInputs): string { $payload = [ 'job_type' => trim($jobType), 'identity' => self::normalize($identityInputs), ]; $json = json_encode($payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); return hash('sha256', (string) $json); } /** * @param array> $assignments * @return array> */ private static function normalizeAssignments(array $assignments): array { $normalized = []; foreach ($assignments as $assignment) { if (! is_array($assignment)) { continue; } $target = is_array($assignment['target'] ?? null) ? $assignment['target'] : []; $normalized[] = [ 'id' => is_scalar($assignment['id'] ?? null) ? (string) $assignment['id'] : '', 'target_type' => is_scalar($target['@odata.type'] ?? null) ? (string) $target['@odata.type'] : '', 'group_id' => is_scalar($target['groupId'] ?? null) ? (string) $target['groupId'] : '', 'assignment_filter_id' => is_scalar($assignment['deviceAndAppManagementAssignmentFilterId'] ?? null) ? (string) $assignment['deviceAndAppManagementAssignmentFilterId'] : (is_scalar($target['deviceAndAppManagementAssignmentFilterId'] ?? null) ? (string) $target['deviceAndAppManagementAssignmentFilterId'] : ''), 'assignment_filter_type' => is_scalar($assignment['deviceAndAppManagementAssignmentFilterType'] ?? null) ? (string) $assignment['deviceAndAppManagementAssignmentFilterType'] : (is_scalar($target['deviceAndAppManagementAssignmentFilterType'] ?? null) ? (string) $target['deviceAndAppManagementAssignmentFilterType'] : ''), ]; } usort($normalized, static function (array $left, array $right): int { $leftJson = json_encode($left, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $rightJson = json_encode($right, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); return strcmp((string) $leftJson, (string) $rightJson); }); return $normalized; } private static function normalize(mixed $value): mixed { if (! is_array($value)) { return $value; } if (array_is_list($value)) { $items = array_map(static fn (mixed $item): mixed => self::normalize($item), $value); usort($items, static function (mixed $left, mixed $right): int { $leftJson = json_encode($left, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $rightJson = json_encode($right, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); return strcmp((string) $leftJson, (string) $rightJson); }); return array_values($items); } ksort($value); foreach ($value as $key => $item) { $value[$key] = self::normalize($item); } return $value; } }