Application Lifecycle and Startup Hooks in Winter Boot
Every Winter Boot application begins with a single annotated class and a call to a runner. From that moment the framework scans your code, builds a complete bean graph, calls lifecycle hooks, and hands control to the runtime — whether that is a Swoole HTTP server, plain PHP-FPM, a CLI tool, or a migration runner. Knowing exactly what happens at each stage helps you write correct startup code and understand why the order of #[PostConstruct] and #[OnApplicationReady] matters.
The container follows a fixed, deterministic sequence every time your application boots.
Bootstrap the runner
Instantiate a runner class (e.g. WinterWebSwooleApplication) and call run(MyApplication::class). The runner reads the #[WinterBootApplication] attribute to learn which config directories and namespaces to use.
Load configuration
The runner locates and parses every application.yml (and profile-specific variants) in the configured configDirectory paths. The merged result is available through ApplicationContext::getProperty*() before any bean is created.
Scan namespaces
The runner walks the PSR-4 namespaces listed in scanNamespaces, skipping any prefixes in scanExcludeNamespaces. Every class carrying a known stereotype attribute is recorded as a ClassResource in the registry. If autoload is true, classes that have not been loaded by Composer yet are loaded on demand during this scan.
Build the application context
The WinterApplicationContext is constructed from the scanned resources. If eager is true, every bean is instantiated immediately; otherwise beans are instantiated lazily on first access.
Wire dependencies
For each instantiated bean the container resolves all #[Autowired] properties and #[Value] properties, injecting the correct objects and scalar values.
Call #[PostConstruct] methods
After a bean is fully constructed and its dependencies are injected, any public non-abstract method annotated with #[PostConstruct] is called. Use this hook for validation, connection setup, or derived-field initialisation that depends on injected values.
Load modules
Modules listed in application.yml under the modules key are initialised. Each module can contribute its own beans and namespaces to the context.
Fire #[OnApplicationReady]
Every bean whose class is annotated with #[OnApplicationReady] has its onApplicationReady() method called. This is the correct place for work that must happen after the entire context is ready — seeding data, starting background threads, registering routes, or opening connections.
Start serving
The runner hands off to the underlying runtime — starting the Swoole HTTP server, dispatching a single PHP-FPM request, executing CLI logic, or running migrations.
Choose the runner class that matches how your application is deployed.
WinterWebSwooleApplication
High-performance HTTP server powered by OpenSwoole. Starts a persistent
multi-worker server that handles concurrent requests in a single PHP process.
The right choice for production APIs and microservices.
Traditional PHP-FPM / Apache web runner. Bootstraps the full context once
per request under a conventional web server. Use when Swoole is unavailable
or when integrating with an existing PHP hosting environment.
Command-line runner. Boots the full DI context, fires
#[OnApplicationReady], then returns control so your CLI logic can execute.
Ideal for console commands, one-off scripts, and integration test harnesses.
Database migration runner. Accepts --configDir, --sqlPath, and
--migrationType CLI flags. Intentionally skips module loading and
#[OnApplicationReady] events for a lean, fast migration run.
A single codebase can expose several starter classes, each bootstrapping a different slice of the application. This is a first-class pattern in Winter Boot — ideal for separating test, web, and worker entry points.
#[PostConstruct] is a method-level attribute. The container calls the annotated method immediately after the bean is instantiated and all #[Autowired] and #[Value] injections have been applied.
The framework enforces these rules at startup:
The method must be public.
The method must not be abstract or a constructor.
The method must not declare a return type (it should return void).
DatabaseConnectionPool.php
use dev\winterframework\stereotype\{Service, Autowired, PostConstruct};
#[Service]
classDatabaseConnectionPool {
#[Autowired]
privateApplicationContext$appCtx;
#[Value('${db.poolSize}')]
privateint$poolSize;
privatearray$connections= [];
#[PostConstruct]
publicfunctioninit():void {
// $this->poolSize and $this->appCtx are already injected here
#[OnApplicationReady] is a class-level attribute. The framework calls onApplicationReady() on every annotated bean after the entire application context has been built, modules have been loaded, and the container is fully operational.
The framework enforces these requirements at startup:
The class must also carry #[Component], #[Service], or #[Configuration].
The class must implement the ApplicationReadyEvent interface (which declares onApplicationReady(): void).
AppStartupTasks.php
use dev\winterframework\stereotype\{Component, OnApplicationReady};
use dev\winterframework\stereotype\Autowired;
use dev\winterframework\core\app\ApplicationReadyEvent;
Winter Boot automatically registers bean destroy methods to run on process exit. When a #[Bean] factory method declares a destroyMethod, the framework registers it with PHP’s shutdown mechanism. On process exit, all registered destroy methods are called in registration order.
ResourceConfig.php
use dev\winterframework\stereotype\{Configuration, Bean};
WinterModule is the base contract for optional framework modules that extend Winter Boot with additional capabilities. Modules are declared in your application.yml and are initialised after the main application context has been built.
Each module can register its own beans, scan additional namespaces, and integrate with the application context. Winter Boot’s built-in features — such as transaction management, caching, async tasks, and scheduling — are all delivered as modules.
application.yml — enabling modules
modules:
- dev\winterframework\pdox\PdoModule
- dev\winterframework\cache\CacheModule
- dev\winterframework\task\TaskModule
To create a custom module, implement the WinterModule interface and list your module class under modules in application.yml. The framework instantiates each module during the Load modules phase of startup and calls its initialisation methods in declaration order.
CustomAuditModule.php
use dev\winterframework\core\app\WinterModule;
use dev\winterframework\core\context\ApplicationContext;