adachsoft / plugin-discovery
Discovers plugin declarations from installed Composer packages.
Requires
- php: ^8.3
- composer-runtime-api: ^2.0
- adachsoft/collection: ^3.0
Requires (Dev)
- adachsoft/php-code-style: ^0.7.1
- friendsofphp/php-cs-fixer: ^3.95
- phpstan/phpstan: ^2.3
- phpunit/phpunit: ^13.4
- rector/rector: ^2.8
- symplify/phpstan-rules: ^14.9
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-11-06 13:33:01 UTC
README
adachsoft/plugin-discovery is a domain-independent PHP library for discovering plugins declared by installed Composer packages. A caller supplies the discovery key, the plugin interface, and the IDs to disable.
The library reads vendor/composer/installed.json. It does not scan source files, instantiate plugin classes, or persist a cache; decoded package metadata is memoized only for the lifetime of a discovery instance.
Requirements
- PHP 8.3 or later
- Composer 2
Install the library with Composer:
composer require adachsoft/plugin-discovery
Examples
Declare plugins in a package
A plugin package declares its plugins in composer.json under the discovery key selected by the application:
{
"extra": {
"adachsoft/ai-image-contract": {
"plugins": {
"xai": "AdachSoft\\AiImageXai\\XaiImagePlugin"
}
}
}
}
Each plugin ID must contain lowercase ASCII letters or digits, optionally separated by ., _, or -, and must be no longer than 64 characters. Each class must exist, implement the interface requested by the caller, be instantiable, and have no required constructor arguments.
Discover plugins
use AdachSoft\PluginDiscovery\Collection\PluginIdVoCollection;
use AdachSoft\PluginDiscovery\PluginDiscoveryFactory;
use AdachSoft\AiImageContract\ImageGeneratorPluginInterface;
$discovery = (new PluginDiscoveryFactory())->create();
$plugins = $discovery->discover(
'adachsoft/ai-image-contract',
ImageGeneratorPluginInterface::class,
PluginIdVoCollection::fromStrings(['minimax']),
);
foreach ($plugins as $plugin) {
// $plugin->id->value, $plugin->className, and $plugin->packageName are available.
}
PluginDiscoveryFactory::create() uses the first registered Composer autoloader to determine the vendor directory. Pass a vendor directory explicitly when the application needs to select a different Composer installation:
$discovery = (new PluginDiscoveryFactory())->create('/path/to/vendor');
Discovery validates declarations but does not instantiate plugin classes. The consumer is responsible for creating plugin instances.
Exceptions
All library exceptions implement PluginDiscoveryExceptionInterface.
| Exception | When it is thrown |
|---|---|
InvalidPluginIdException | A plugin ID supplied to PluginIdVo or PluginIdVoCollection::fromStrings() is invalid. |
InvalidDiscoveryQueryException | The discovery key is empty or the requested interface does not exist. |
ComposerMetadataNotFoundException | The Composer vendor directory cannot be determined or installed.json is missing or unreadable. |
InvalidComposerMetadataException | installed.json contains invalid JSON or does not use the supported Composer 2 format. |
InvalidPluginDeclarationException | A package's plugin map, ID, or class declaration is invalid. |
DuplicatePluginIdException | More than one enabled package declares the same plugin ID. |
Invalid plugin IDs found in package metadata are reported as InvalidPluginDeclarationException with the original InvalidPluginIdException as the previous exception. Disabled plugin IDs are skipped before class validation, so a broken plugin can be disabled without uninstalling its package. Disabled IDs that are not installed are ignored.
Limitations
- Only packages listed in Composer 2's
installed.jsonare searched. The root application package is not listed there, so its own plugin declarations are not discovered. - The functional test installs fixture packages with Composer and therefore requires the
composerexecutable to be available inPATH.