|null */ private ?array $catalog = null; public function __construct(private Container $container) {} /** * The full catalog keyed by permission key (e.g. 'tickets.create' => Permission). * * @return array */ public function all(): array { if ($this->catalog !== null) { return $this->catalog; } $this->catalog = []; foreach ($this->providerClasses() as $class) { $provider = $this->container->make($class); if (! $provider instanceof ProvidesPermissions) { continue; } foreach ($provider->permissions() as $permission) { $this->catalog[$permission->key] = $permission; } } return $this->catalog; } public function get(string $key): ?Permission { return $this->all()[$key] ?? null; } /** Drop the in-memory and cross-request provider caches (call on plugin enable/disable). */ public function flush(): void { $this->catalog = null; Cache::store('installation')->forget(self::PROVIDER_CACHE_KEY); } /** * The discovered provider class names. Cached on the installation store outside debug * mode, exactly like EventDispatcher's 'domainEvents'. * * @return array */ private function providerClasses(): array { if ((bool) config('debug') === false) { return Cache::store('installation')->rememberForever(self::PROVIDER_CACHE_KEY, fn () => $this->scanProviderClasses()); } return $this->scanProviderClasses(); } /** * Glob the conventional provider locations and resolve each to a FQCN. * * @return array */ private function scanProviderClasses(): array { $patterns = [ APP_ROOT.'/app/Domain/*/Permissions/*Permissions.php', APP_ROOT.'/app/Plugins/*/Permissions/*Permissions.php', ]; $classes = []; foreach ($patterns as $pattern) { foreach ((array) glob($pattern) as $file) { $class = $this->classFromPath((string) $file); if ($class !== null) { $classes[] = $class; } } } return $classes; } /** * Map an app file path to its FQCN under the Leantime namespace * (app/Domain/Tickets/Permissions/TicketsPermissions.php -> * Leantime\Domain\Tickets\Permissions\TicketsPermissions). */ private function classFromPath(string $file): ?string { $relative = str_replace(APP_ROOT.'/app/', '', $file); $relative = substr($relative, 0, -strlen('.php')); $class = 'Leantime\\'.str_replace('/', '\\', $relative); return class_exists($class) ? $class : null; } }