discovered) { return; } $basePath = __DIR__; foreach (self::CATEGORIES as $category) { $categoryPath = $basePath.'/'.$category; if (! is_dir($categoryPath)) { continue; } foreach (scandir($categoryPath) as $provider) { if ($provider === '.' || $provider === '..') { continue; } $providerPath = $categoryPath.'/'.$provider; if (! is_dir($providerPath)) { continue; } $identifier = Str::lower($provider); $this->providers[$identifier] = [ 'category' => $category, 'name' => $provider, 'namespace' => "Plug\\{$category}\\{$provider}", 'path' => $providerPath, ]; } } $this->discovered = true; } /** * Register a provider programmatically. * * Used by plug packages to self-register without filesystem scanning. */ public function register(string $identifier, string $category, string $name, string $namespace): void { $this->providers[$identifier] = [ 'category' => $category, 'name' => $name, 'namespace' => $namespace, 'path' => null, ]; } /** * Get all registered provider identifiers. * * @return string[] */ public function identifiers(): array { $this->discover(); return array_keys($this->providers); } /** * Check if a provider exists. */ public function has(string $identifier): bool { $this->discover(); return isset($this->providers[$identifier]); } /** * Get provider metadata. */ public function get(string $identifier): ?array { $this->discover(); return $this->providers[$identifier] ?? null; } /** * Check if provider supports an operation. */ public function supports(string $identifier, string $operation): bool { $meta = $this->get($identifier); if (! $meta) { return false; } $className = $meta['namespace'].'\\'.ucfirst($operation); return class_exists($className); } /** * Get an operation class for a provider. * * @return class-string|null */ public function operation(string $identifier, string $operation): ?string { if (! $this->supports($identifier, $operation)) { return null; } $meta = $this->get($identifier); return $meta['namespace'].'\\'.ucfirst($operation); } /** * Get all providers. */ public function all(): Collection { $this->discover(); return collect($this->providers); } /** * Get provider identifiers by category. */ public function byCategory(string $category): Collection { $this->discover(); return collect($this->providers) ->filter(fn ($meta) => $meta['category'] === $category) ->keys(); } /** * Get providers that support a specific operation. */ public function withCapability(string $operation): Collection { $this->discover(); return collect($this->providers)->filter( fn ($meta, $id) => $this->supports($id, $operation) ); } /** * Get available categories. * * @return string[] */ public function categories(): array { return self::CATEGORIES; } /** * Get the display name for a provider. */ public function displayName(string $identifier): ?string { $authClass = $this->operation($identifier, 'auth'); if ($authClass && method_exists($authClass, 'name')) { return $authClass::name(); } $meta = $this->get($identifier); return $meta['name'] ?? null; } }