*/ private array $decisionCache = []; public function __construct( private readonly AuthorizationService $authorizationService ) { } public function allow(int $actorUserId, string $ability, array $context = []): bool { $ability = trim($ability); if ($actorUserId <= 0 || $ability === '') { return false; } $normalizedContext = $this->normalizeContext($context); $cacheKey = $actorUserId . '|' . $ability . '|' . sha1(json_encode($normalizedContext, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: ''); if (array_key_exists($cacheKey, $this->decisionCache)) { return $this->decisionCache[$cacheKey]; } $decision = $this->authorizationService->authorize($ability, [ 'actor_user_id' => $actorUserId, ...$normalizedContext, ]); return $this->decisionCache[$cacheKey] = $decision->isAllowed(); } /** * @param array}> $map * @param array $baseContext * @return array */ public function resolveMap(int $actorUserId, array $map, array $baseContext = []): array { $resolved = []; $normalizedBaseContext = $this->normalizeContext($baseContext); foreach ($map as $key => $definition) { if (is_string($definition)) { $resolved[$key] = $this->allow($actorUserId, $definition, $normalizedBaseContext); continue; } if (!is_array($definition)) { $resolved[$key] = false; continue; } $ability = $definition['ability']; $context = []; if (array_key_exists('context', $definition) && is_array($definition['context'])) { $context = $this->normalizeContext($definition['context']); } $resolved[$key] = $this->allow($actorUserId, $ability, [ ...$normalizedBaseContext, ...$context, ]); } return $resolved; } /** * @param array}> $map * @param array $baseContext * @return array */ public function pageCapabilities(int $actorUserId, array $map, array $baseContext = []): array { return $this->resolveMap($actorUserId, $map, $baseContext); } /** * @return array */ public function layoutCapabilities(int $actorUserId): array { return $this->resolveMap($actorUserId, UiCapabilityMap::LAYOUT); } /** * @param array $context * @return array */ private function normalizeContext(array $context): array { $normalized = $this->normalizeValue($context); return is_array($normalized) ? $normalized : []; } private function normalizeValue(mixed $value): mixed { if (!is_array($value)) { return $value; } $isList = array_is_list($value); if ($isList) { foreach ($value as $idx => $entry) { $value[$idx] = $this->normalizeValue($entry); } return $value; } ksort($value); foreach ($value as $key => $entry) { $value[$key] = $this->normalizeValue($entry); } return $value; } }