plugin-installer

The Composer installer that registers Craft CMS's craft-plugin package type and builds the plugin manifest Craft reads at runtime.

Tool
Composer
v1.6.0
28stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
26/100Needs Attention
Development Activity16
Maintenance0
Community16
Maturity60
Momentum12

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
53/100Fair
Architecture78
Code Quality35
Innovation55
Learning Curve45

plugin-installer is the Composer plugin that powers Craft CMS’s plugin ecosystem. It registers a custom craft-plugin package type with Composer, so any package declaring that type in its composer.json is treated as a Craft plugin instead of a generic library. During install, update, and uninstall, it reads each plugin’s extra block (class name, base path, handle, developer info, and other metadata) and writes it into a generated vendor/craftcms/plugins.php manifest that Craft’s core loads at runtime to discover and register installed plugins.

Because it hooks directly into Composer’s InstallationManager, the installer keeps that manifest in sync automatically: invalid plugins are rejected and rolled back, removed plugins are cleaned out of the manifest, and root-package plugins (a plugin under active development at the root of a Composer project) are registered the same way as vendor-installed ones.

What You Get

  • Custom craft-plugin Composer package type recognized automatically once this installer is present
  • Generated vendor/craftcms/plugins.php manifest listing every installed plugin’s class, base path, handle, and metadata
  • Automatic PSR-4 alias resolution so Craft can map a plugin’s namespace to its installed path
  • Safe rollback on invalid plugins - a package missing a required class, base path, or handle is uninstalled rather than left in a broken state
  • Support for root-package plugins under active development, not just vendor-installed ones

Common Use Cases

  • Craft CMS core uses the generated manifest at boot to discover and instantiate installed plugins without filesystem scanning
  • Plugin developers declare type: “craft-plugin” and an extra.handle in composer.json to make their package installable as a Craft plugin
  • Composer automatically re-runs the installer on composer update / composer remove to keep the plugin manifest current
  • CI pipelines building Craft projects rely on this installer running silently as part of composer install, with no separate build step required

Under The Hood

Architecture The package is a tiny, single-purpose Composer plugin composed of three classes: Plugin.php (implements Composer’s PluginInterface, registering an Installer instance with Composer’s InstallationManager on activate() and removing it on deactivate()), Installer.php (extends Composer’s LibraryInstaller and overrides install/update/uninstall to wrap the parent library-install behavior with plugin-manifest bookkeeping via addPlugin/removePlugin/registerPlugin/unregisterPlugin/loadPlugins/savePlugins), and InvalidPluginException.php (a typed exception carrying the offending package and an error string). There is no independent data layer or service boundary - the data flow is entirely Composer’s own package lifecycle events driving reads of a package’s extra metadata and PSR-4 autoload map, normalized into a generated PHP manifest that Craft’s core reads at boot. Because Installer::addPlugin is the sole write path to that manifest, any change to its shape would ripple through every consumer at once.

Tech Stack Written in PHP with a very low floor (composer.json requires PHP >=5.4) and depends only on composer-plugin-api (supporting both Composer 1.x and 2.x) plus composer/composer as a dev-only dependency. There is no framework, database, or build tooling beyond Composer itself - the package is published straight to Packagist as a composer-plugin type with a PSR-4 autoloaded craft\composer\ namespace, and its only external integration is with Composer’s InstallationManager and React\Promise\PromiseInterface to support Composer 2’s asynchronous install pipeline.

Code Quality There are no test files anywhere in the repository, and no linter or static-analysis configuration is present; the .github directory holds only a CODEOWNERS file with no CI workflow evident. Error handling is explicit and typed, though: InvalidPluginException carries the offending package plus a human-readable reason, and Installer::install()/update() catch it to roll back the partially-completed Composer operation before re-throwing. Naming is consistent and follows Composer plugin conventions throughout, and PHPDoc comments cover most methods, but there is limited use of scalar or return type declarations, reflecting the PHP 5.4 compatibility target.

What Makes It Unique The package’s genuinely useful trick is registering a brand-new Composer package type and hooking into Composer’s own install/update/uninstall lifecycle to auto-generate a single manifest file that Craft’s core reads at boot - letting any third-party plugin become discoverable purely by declaring the right type and handle in its own composer.json, with no manual registration step for the site owner. That is a clever application of Composer’s plugin API, though the broader pattern (a framework using a custom package type plus a generated manifest for auto-discovery) is well precedented elsewhere in the PHP ecosystem.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search