moduleRegistry->getPermissions() as $permissionDefinition) { $total++; $key = trim((string) $permissionDefinition['key']); if ($key === '') { throw new \RuntimeException('Module permission definition has empty key.'); } $desired = [ 'key' => $key, 'description' => trim((string) $permissionDefinition['description']), 'active' => (int) $permissionDefinition['active'], 'is_system' => (int) $permissionDefinition['is_system'], ]; $existing = $this->permissionRepository->findByKey($key); if (!is_array($existing)) { $createdId = $this->permissionRepository->create($desired); if ($createdId === null) { throw new \RuntimeException("Failed to create module permission '{$key}'."); } $created++; continue; } $existingId = (int) ($existing['id'] ?? 0); if ($existingId <= 0) { throw new \RuntimeException("Permission '{$key}' exists without a valid id."); } if ($this->isPermissionEqual($existing, $desired)) { $unchanged++; continue; } $wasUpdated = $this->permissionRepository->update($existingId, $desired); if (!$wasUpdated) { throw new \RuntimeException("Failed to update module permission '{$key}' (id {$existingId})."); } $updated++; } $deactivated = $this->deactivateOrphaned(); return [ 'created' => $created, 'updated' => $updated, 'unchanged' => $unchanged, 'deactivated' => $deactivated, 'total' => $total, ]; } /** * Deactivate system permissions that no active module claims. * * Only touches is_system=1 rows (module-owned). Admin-created permissions * (is_system=0) are never modified. Setting active=0 is reversible — if the * module is re-enabled, the next sync will set active=1 again. */ private function deactivateOrphaned(): int { $activeKeys = $this->moduleRegistry->getPermissionKeys(); $systemPermissions = $this->permissionRepository->listSystem(); $deactivated = 0; foreach ($systemPermissions as $permission) { $key = (string) $permission['key']; $id = (int) $permission['id']; $isActive = (int) $permission['active']; if ($key === '' || $id <= 0) { continue; } // Already inactive — nothing to do if ($isActive === 0) { continue; } // Still claimed by an active module — keep it if (in_array($key, $activeKeys, true)) { continue; } // Orphaned: system permission not claimed by any active module → deactivate $this->permissionRepository->update($id, [ 'key' => $key, 'description' => (string) $permission['description'], 'active' => 0, 'is_system' => 1, ]); $deactivated++; } return $deactivated; } /** * @param array $existing * @param array $desired */ private function isPermissionEqual(array $existing, array $desired): bool { $existingDescription = trim((string) ($existing['description'] ?? '')); $existingActive = (int) ($existing['active'] ?? 0); $existingSystem = (int) ($existing['is_system'] ?? 0); return $existingDescription === (string) ($desired['description'] ?? '') && $existingActive === (int) ($desired['active'] ?? 0) && $existingSystem === (int) ($desired['is_system'] ?? 0); } }