The Module System: Extending Winter with Libraries
Winter Boot’s module system gives you a clean, attribute-driven mechanism for bundling infrastructure integrations — Redis, Kafka, Doctrine ORM, service discovery, and more — as self-contained, reusable units. Modules are discovered and loaded before the application starts, so each module can register its own beans, scan additional namespaces, and run any required initialisation without touching your application code. The WinterModule interface keeps the contract minimal: implement two lifecycle hooks and annotate your class with #[Module].
The WinterModule Interface
Section titled “The WinterModule Interface”Every module must implement the WinterModule interface. The framework calls its two lifecycle methods during startup in a predictable order.
use dev\winterframework\core\app\WinterModule;use dev\winterframework\core\context\ApplicationContext;use dev\winterframework\core\context\ApplicationContextData;
class MyCustomModule implements WinterModule {
/** * Called during the module loading phase. * Register beans, validate configuration, check required extensions here. */ public function init(ApplicationContext $ctx, ApplicationContextData $ctxData): void { // initialise resources }
/** * Called after all modules are loaded and the application context is ready. * Start background processes, connect to external services, etc. */ public function begin(ApplicationContext $ctx, ApplicationContextData $ctxData): void { // begin operations }}-
init()voidCalled during the module loading phase. Use this hook to register beans, validate configuration, and verify required PHP extensions are present. -
begin()voidCalled after all modules are loaded and the application context is fully ready. Use this hook to open connections, start background processes, or subscribe to events.
The #[Module] Attribute
Section titled “The #[Module] Attribute”Annotate every module class with #[Module]. This tells the framework how to scan the module’s namespaces and provides metadata used in log output.
use dev\winterframework\stereotype\Module;
#[Module( title: 'My Custom Module', // human-readable name shown in logs initMethod: 'init', // method called on load (default: 'init') destroyMethod: '', // method called on shutdown (optional) namespaces: [], // extra PSR-4 namespaces to scan)]class MyCustomModule implements WinterModule { // ...}Enabling Modules in application.yml
Section titled “Enabling Modules in application.yml”Declare modules in your application.yml under the top-level modules key. Each entry requires a module class name and an enabled flag.
modules: - module: 'dev\winterframework\telemetry\OpenTelemetryModule' enabled: true configFile: 'config/telemetry.yml' # optional path to module-specific config
- module: 'dev\winterframework\data\redis\RedisModule' enabled: true
- module: 'dev\winterframework\kafka\KafkaModule' enabled: false # set to false to temporarily disableFirst-Party Modules
Section titled “First-Party Modules”All official modules live in the suvera/winter-modules monorepo. Install the entire collection with a single Composer command:
composer require suvera/winter-modulesSee the Modules overview for a full catalog with guidance on which module to pick for which use case.
Building a Custom Module
Section titled “Building a Custom Module”Package any infrastructure concern — a third-party SDK, a custom cache layer, an internal service client — as a reusable, injectable module by following these steps.
-
Create the module class
src/Module/AcmeMyModule.php <?phpdeclare(strict_types=1);namespace Acme\MyModule;use dev\winterframework\core\app\WinterModule;use dev\winterframework\core\context\ApplicationContext;use dev\winterframework\core\context\ApplicationContextData;use dev\winterframework\stereotype\Module;#[Module(title: 'Acme My Module')]class AcmeMyModule implements WinterModule {public function init(ApplicationContext $ctx, ApplicationContextData $ctxData): void {// Validate required config, register beans}public function begin(ApplicationContext $ctx, ApplicationContextData $ctxData): void {// Start connections or background processes}} -
Register beans inside init()
Use
ApplicationContextData::getBeanProvider()to register beans that consumers can inject with#[Autowired]:public function init(ApplicationContext $ctx, ApplicationContextData $ctxData): void {$client = new AcmeClient(/* config */);$ctxData->getBeanProvider()->registerInternalBean($client,AcmeClient::class,true // mark as primary bean);} -
Read module configuration
The framework passes the raw
moduleDefYAML entry to your#[Module]attribute. Use theModuleTraithelper to retrieve it:use dev\winterframework\util\ModuleTrait;#[Module(title: 'Acme My Module')]class AcmeMyModule implements WinterModule {use ModuleTrait;public function begin(ApplicationContext $ctx, ApplicationContextData $ctxData): void {$moduleDef = $ctx->getModule(static::class);$config = $this->retrieveConfiguration($ctx, $ctxData, $moduleDef);$host = $config['acme']['host'] ?? 'localhost';}} -
Enable the module in application.yml
application.yml modules:- module: 'Acme\MyModule\AcmeMyModule'enabled: trueconfigFile: 'config/acme.yml' -
Install via Composer
Terminal window composer require acme/my-winter-module