Skip to content

PluginManager

Process-global registry + lifecycle for PluginInterfaces, mirroring the static, worker-lifetime pattern of MiddlewareCatalog and Events: plugins are registered once and their contributions persist for the life of the process.

Lifecycle: - PluginManager::add() — programmatic registration (before bootstrap). - PluginManager::bootFromConfig() — called by Quiote::bootstrap() after settings load and before contexts are created (the one seam between those steps): reads the plugins config key, instantiates + adds them, then calls PluginInterface::register() on every plugin in deterministic order, de-duped by class. Idempotent. - PluginManager::configureContainer() — applies deferred DI-service contributions to a context’s container (register-if-absent). - PluginManager::configureHttpClients() — applies named-HTTP-client contributions to a container’s HttpClientFactory. - PluginManager::moduleDirectories() / PluginManager::contributedCommands() — read by the attribute route scanner / console application.

final class PluginManager

SourcePlugin/PluginManager.php
MethodDescription
[`add(PluginInterfacestring $plugin): void`](#add)
addCommand(string $fqcn): voidRecords a console command class a plugin contributes to the CLI application.
addContainerService(string $id, mixed $concrete, ?string $scope, list<string> $aliases): void
addHttpClientConfig(string $name, callable $configurator): voidRecords a configurator for a named HTTP client, keyed by $name.
addModuleDirectory(string $dir): voidRecords a directory a plugin contributes as a module search root.
addRequestEndClear(string $label, \Closure(): void $clear): voidContribute a clear that runs when a request on any context ends.
addStateReset(string $label, \Closure(): void $reset): voidContribute a callback that clears a plugin-owned static registry, run as part of PluginManager::reset().
bootFromConfig(): voidBoot phase: pull plugins from the plugins config key, then invoke register() on every plugin once, in order.
configureContainer(Container $container): voidApply deferred DI-service contributions to a container, register-if-absent so app/core bindings (and the first contributing plugin) win.
configureHttpClients(HttpClientFactory $factory): voidApply named-HTTP-client contributions to a factory (does not overwrite an already-configured name).
configureLifecycle(ContextLifecycle $lifecycle): voidAppend plugin-contributed clears to a context’s lifecycle, after the framework’s own — the identity clears must not be displaced by a plugin.
contributedCommands(): list<string>
isBooted(): boolReports whether plugin registration has already run in this process.
moduleDirectories(): list<string>
registeredPlugins(): array<class-string, PluginInterface>
reset(): voidTest isolation: clears every plugin + contribution and the booted flag.

public static function add(PluginInterface|string $plugin): void

Register a plugin (instance or class-string).

De-duped by class; declared order preserved.

ParameterTypeDescription
$pluginPluginInterface`“string`

public static function addCommand(string $fqcn): void

Records a console command class a plugin contributes to the CLI application.

De-duplicated on the class name, so registering the same command twice adds it once. The class is not loaded or instantiated here; it is handed over when the console application reads PluginManager::contributedCommands().

ParameterTypeDescription
$fqcnstring

public static function addContainerService(string $id, mixed $concrete, ?string $scope, list<string> $aliases): void

ParameterTypeDescription
$idstring
$concretemixed
$scope?``string
$aliaseslist``<``string``>

public static function addHttpClientConfig(string $name, callable $configurator): void

Records a configurator for a named HTTP client, keyed by $name.

Registering the same name twice replaces the earlier configurator rather than stacking. The configurator is only invoked when PluginManager::configureHttpClients() applies it to a factory, and only for a name the factory has not already configured.

ParameterTypeDescription
$namestring
$configuratorcallable

public static function addModuleDirectory(string $dir): void

Records a directory a plugin contributes as a module search root.

De-duplicated on the exact string, so re-registering the same directory is a no-op. The contribution is stored statically and applied later by whoever reads PluginManager::moduleDirectories(), not at the moment of the call.

ParameterTypeDescription
$dirstring

public static function addRequestEndClear(string $label, \Closure(): void $clear): void

Contribute a clear that runs when a request on any context ends.

ParameterTypeDescription
$labelstring
$clear\Closure(): void

public static function addStateReset(string $label, \Closure(): void $reset): void

Contribute a callback that clears a plugin-owned static registry, run as part of PluginManager::reset().

ParameterTypeDescription
$labelstring
$reset\Closure(): void

public static function bootFromConfig(): void

Boot phase: pull plugins from the plugins config key, then invoke register() on every plugin once, in order.

Called from Quiote::bootstrap() after settings load. Idempotent — safe if bootstrap runs more than once.

public static function configureContainer(Container $container): void

Apply deferred DI-service contributions to a container, register-if-absent so app/core bindings (and the first contributing plugin) win.

Safe to call repeatedly for the same container (idempotent).

ParameterTypeDescription
$containerContainer

public static function configureHttpClients(HttpClientFactory $factory): void

Apply named-HTTP-client contributions to a factory (does not overwrite an already-configured name).

ParameterTypeDescription
$factoryHttpClientFactory

public static function configureLifecycle(ContextLifecycle $lifecycle): void

Append plugin-contributed clears to a context’s lifecycle, after the framework’s own — the identity clears must not be displaced by a plugin.

ParameterTypeDescription
$lifecycleContextLifecycle

public static function contributedCommands(): list<string>

Returns list``<``string``>

public static function isBooted(): bool

Reports whether plugin registration has already run in this process.

The flag guards registration against running twice per worker; PluginManager::reset() clears it along with the contributions.

Returns bool

public static function moduleDirectories(): list<string>

Returns list``<``string``>

public static function registeredPlugins(): array<class-string, PluginInterface>

Returns array``<``class-string``, PluginInterface>

public static function reset(): void

Test isolation: clears every plugin + contribution and the booted flag.

Middleware contributions are cleared with the rest. They live in their own registries rather than in this class, and leaving them behind produced a half-registered plugin: the pipeline still advertised the plugin’s middleware while the container had lost the service that middleware’s factory resolves, so the next dispatch died on a missing service rather than simply running without the plugin.