VOOZH about

URL: https://deepwiki.com/MahoCommerce/maho-composer-plugin/2-plugin-architecture

⇱ Plugin Architecture | MahoCommerce/maho-composer-plugin | DeepWiki


Loading...
Menu

Plugin Architecture

The Maho Composer Plugin implements a three-plugin architecture that integrates with Composer's Plugin API v2.1+ and Runtime API v2+. The three plugins—AutoloadPlugin, ModmanPlugin, and FileCopyPlugin—each implement both Composer\Plugin\PluginInterface and Composer\EventDispatcher\EventSubscriberInterface to hook into Composer's event-driven lifecycle.

Each plugin subscribes to specific Composer events and executes in a coordinated sequence:

  • ModmanPlugin processes PackageEvents::POST_PACKAGE_* events for per-package symlink deployment
  • AutoloadPlugin hooks ScriptEvents::PRE_AUTOLOAD_DUMP to modify autoload configuration
  • FileCopyPlugin handles ScriptEvents::POST_*_CMD events for asset distribution

This architecture transforms a standard Composer project into a functional Maho installation by managing code autoloading, module deployment, and web asset distribution.

Sources: composer.json6-9 src/AutoloadPlugin.php1-46 src/ModmanPlugin.php1-20 src/FileCopyPlugin.php1-43

Plugin Registration and Discovery

Composer discovers and activates plugins by reading the extra.class array from composer.json21-27 which declares three fully-qualified class names implementing PluginInterface and EventSubscriberInterface.

Plugin Registration Diagram


Sources: composer.json21-27 src/FileCopyPlugin.php1-13 src/AutoloadPlugin.php1-13 src/ModmanPlugin.php1-20

Registered Plugin Classes

Plugin ClassSubscribed EventsPrimary Responsibility
Maho\ComposerPlugin\AutoloadPluginScriptEvents::PRE_AUTOLOAD_DUMPGenerates PSR-0 autoload mappings and include paths for Maho's code pool structure
Maho\ComposerPlugin\ModmanPluginPackageEvents::POST_PACKAGE_INSTALL
PackageEvents::POST_PACKAGE_UPDATE
PackageEvents::POST_PACKAGE_UNINSTALL
Creates symlinks in vendor/mahocommerce/maho-modman-symlinks/ based on modman file mappings
Maho\ComposerPlugin\FileCopyPluginScriptEvents::POST_INSTALL_CMD
ScriptEvents::POST_UPDATE_CMD
ScriptEvents::POST_CREATE_PROJECT_CMD
Copies static assets (public/, skin/, js/) and the maho CLI executable to the project root

Sources: composer.json21-27 src/AutoloadPlugin.php65-70 src/ModmanPlugin.php41-48 src/FileCopyPlugin.php36-43

Architecture Components

The plugin system consists of three layers: the Composer API dependency layer, the plugin implementation layer containing the three plugins, and a runtime support layer providing shared functionality.

Component Architecture Diagram


Sources: composer.json6-9 src/FileCopyPlugin.php5-10 src/AutoloadPlugin.php5-12 src/ModmanPlugin.php5-14

Composer API Dependencies

The plugin system requires two Composer APIs declared in composer.json6-9:

DependencyVersionProvidesUsed By
composer-plugin-api^2.1Composer\Plugin\PluginInterface
Composer\EventDispatcher\EventSubscriberInterface
Composer\IO\IOInterface
Composer\Composer
All plugins
composer-runtime-api^2Composer\InstalledVersionsAutoloadRuntime package discovery

Plugin Interface Implementation

All three plugins implement PluginInterface, which requires:

  • activate(Composer $composer, IOInterface $io): void - Called when the plugin is loaded
  • deactivate(Composer $composer, IOInterface $io): void - Called when the plugin is deactivated
  • uninstall(Composer $composer, IOInterface $io): void - Called when the plugin is uninstalled

During activation, plugins store the injected dependencies:

Sources: composer.json6-9 src/AutoloadPlugin.php51-63 src/ModmanPlugin.php26-39 src/FileCopyPlugin.php21-61

Plugin Execution Flow

The plugins execute in a sequence determined by their subscribed Composer events. ModmanPlugin acts first during individual package installations, AutoloadPlugin intervenes before autoload generation, and FileCopyPlugin executes last after all packages are installed.

Plugin Execution Sequence


Sources: src/FileCopyPlugin.php36-108 src/AutoloadPlugin.php65-113 src/ModmanPlugin.php41-168

Execution Order and Dependencies

PhasePluginEventRationale
1ModmanPluginPackageEvents::POST_PACKAGE_INSTALLCreates symlinks immediately after each package install, ensuring symlinked code exists before autoload scanning
2AutoloadPluginScriptEvents::PRE_AUTOLOAD_DUMPModifies root package autoload configuration before Composer generates vendor/autoload.php
3FileCopyPluginScriptEvents::POST_INSTALL_CMD
ScriptEvents::POST_UPDATE_CMD
Copies web assets after all packages are installed and the autoloader is available

Idempotency Mechanisms

  • ModmanPlugin: Calls undeploy() before deploy() src/ModmanPlugin.php54-63 to remove old symlinks
  • AutoloadPlugin: Modifies the root package object each time, allowing repeated execution
  • FileCopyPlugin: Uses static $hasRun flag src/FileCopyPlugin.php14-51 to prevent duplicate execution within a single Composer command

Sources: src/ModmanPlugin.php41-71 src/AutoloadPlugin.php65-113 src/FileCopyPlugin.php14-51

Inter-Plugin Coordination

The three plugins coordinate implicitly through shared filesystem conventions and Composer's event ordering. There are no direct method calls between plugins; coordination occurs through well-defined filesystem locations and package type conventions.

Inter-Plugin Coordination Diagram


Sources: src/ModmanPlugin.php73-168 src/AutoloadPlugin.php72-113 src/FileCopyPlugin.php45-108

Shared Dependencies and Conventions

DependencyUsage PatternCode References
Composer\Repository\LocalRepositoryAll plugins call getPackages() to discover installed packagessrc/FileCopyPlugin.php83-84
Package type filteringEach plugin independently checks for maho-source, maho-module, magento-modulesrc/FileCopyPlugin.php85-87 src/ModmanPlugin.php73-76
Composer\Util\FilesystemUsed by ModmanPlugin and implicitly by FileCopyPlugin for file operationssrc/ModmanPlugin.php24-30
IOInterfaceAll plugins use for user feedback via write() and writeError()src/FileCopyPlugin.php53-135

Coordination Mechanisms

The plugins coordinate through four implicit mechanisms:

  1. Package type conventions: All plugins recognize the same package types. FileCopyPlugin filters for magento-module and maho-module src/FileCopyPlugin.php85-87 while ModmanPlugin checks with isMahoModule() src/ModmanPlugin.php73-76

  2. Filesystem conventions: ModmanPlugin creates symlinks at a known location src/ModmanPlugin.php78-81 which FileCopyPlugin checks when resolving package paths src/FileCopyPlugin.php92-94

  3. Event ordering: Composer's event system ensures ModmanPlugin executes before FileCopyPlugin, so symlinks exist when assets are copied.

  4. Idempotency: Each plugin can execute multiple times safely. FileCopyPlugin uses self::$hasRun src/FileCopyPlugin.php14-51 to prevent duplicate execution within a single Composer command.

This loose coupling allows independent plugin evolution while maintaining system coherence through well-defined contracts.

Sources: src/FileCopyPlugin.php14-94 src/ModmanPlugin.php73-81 src/AutoloadPlugin.php72-113

Package Type Processing Strategy

Each plugin implements independent package type filtering to process only relevant Maho/Magento ecosystem packages. This selective processing ensures plugins ignore standard Composer dependencies like PSR-4 libraries.

Package Type Processing Matrix

Package TypeAutoloadPluginModmanPluginFileCopyPluginCode References
maho-source✓ Generates code pool paths✗ Ignores✓ Copies public/ dir + maho executablesrc/FileCopyPlugin.php76-80
maho-module✓ Generates code pool paths✓ Parses modman/extra.map✓ Copies public/, skin/, js/src/FileCopyPlugin.php85-107 src/ModmanPlugin.php73-76
magento-module✓ Generates code pool paths✓ Parses modman/extra.map✓ Copies public/, skin/, js/src/FileCopyPlugin.php85-107 src/ModmanPlugin.php73-76
composer-plugin✗ Ignores✗ Ignores✗ IgnoresN/A
library✗ Ignores✗ Ignores✗ IgnoresN/A

Implementation Details

  • FileCopyPlugin: Special handling for mahocommerce/maho package src/FileCopyPlugin.php76-80 then filters for magento-module and maho-module via array_filter() src/FileCopyPlugin.php85-87
  • ModmanPlugin: Uses isMahoModule() method checking in_array($package->getType(), ['maho-module', 'magento-module'], true) src/ModmanPlugin.php73-76
  • AutoloadPlugin: Delegates filtering to AutoloadRuntime::getInstalledPackages() which accepts maho-source, maho-module, and magento-module package types

The maho-source type is reserved for the main Maho platform package and receives special treatment in FileCopyPlugin, which copies the entire public/ directory and the CLI executable src/FileCopyPlugin.php76-80

Sources: src/FileCopyPlugin.php76-107 src/ModmanPlugin.php73-168

Plugin Activation Lifecycle

Each plugin implements PluginInterface and EventSubscriberInterface, following Composer's standard plugin activation protocol. Activation occurs before any events fire, allowing plugins to prepare their internal state.

Plugin Activation Flow


Sources: composer.json22-26 src/FileCopyPlugin.php21-43 src/AutoloadPlugin.php51-70 src/ModmanPlugin.php26-48

Activation Sequence Details

StepActionCode Location
1. DiscoveryComposer reads extra.class array containing plugin class namescomposer.json22-26
2. InstantiationComposer instantiates each plugin class via constructorImplicit
3. ActivationComposer calls activate(Composer $composer, IOInterface $io)src/FileCopyPlugin.php21-24 src/AutoloadPlugin.php51-55 src/ModmanPlugin.php26-31
4. SubscriptionComposer calls getSubscribedEvents() returning event-to-method arraysrc/FileCopyPlugin.php36-43 src/AutoloadPlugin.php65-70 src/ModmanPlugin.php41-48
5. RegistrationComposer's EventDispatcher registers returned event handlersInternal to Composer
6. ExecutionEvent handlers fire during composer install, composer update, etc.src/FileCopyPlugin.php45-108 src/AutoloadPlugin.php72-113 src/ModmanPlugin.php50-71

Plugin State Storage

Sources: src/FileCopyPlugin.php16-73 src/AutoloadPlugin.php48-70 src/ModmanPlugin.php22-48

Error Handling and Resilience

The plugin architecture follows a fail-fast principle where errors in one plugin do not prevent other plugins from executing. Each plugin independently handles its errors and reports them through the IOInterface.

Error Isolation

  • Each plugin's event handler operates independently
  • Exceptions in ModmanPlugin do not affect AutoloadPlugin or FileCopyPlugin
  • Composer's event dispatcher catches and reports plugin exceptions
  • Users receive clear error messages indicating which plugin failed and why

Retry and Idempotency

All three plugins are designed to be idempotent:

  • ModmanPlugin: Re-running creates the same symlink structure
  • AutoloadPlugin: Re-running generates the same autoload configuration
  • FileCopyPlugin: Re-running copies the same files (respecting preserve-files rules)

This idempotency allows users to safely re-run composer install or composer update to recover from partial failures.

Sources: composer.json21-27