#[PostConstruct] and #[OnApplicationReady] matters.
Startup sequence
The container follows a fixed, deterministic sequence every time your application boots.1
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.2
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.3
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.4
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.5
Wire dependencies
For each instantiated bean the container resolves all
#[Autowired] properties and #[Value] properties, injecting the correct objects and scalar values.6
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.7
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.8
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.9
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.
#[WinterBootApplication]
The entry-point class must be annotated with #[WinterBootApplication]. This attribute is the single source of truth for all startup configuration.
MyApplication.php
Option reference
array
required
List of directory paths to search for
application.yml and logger.yml.array
required
PSR-4
[prefix, directory] pairs to scan for stereotype attributes.bool
default:"false"
Load classes from disk if not already in memory during scanning.
array
default:"[]"
Namespace prefixes to skip entirely during scanning.
string | null
default:"null"
Active environment profile. Loads
application-{profile}.yml overrides on
top of the base configuration.bool
default:"false"
Instantiate all beans at startup instead of lazily on first access. Results
in a slower startup but a faster first request.
Application runner types
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.
WinterWebApplication
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.
WinterCliApplication
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.WinterMigrationApplication
Database migration runner. Accepts
--configDir, --sqlPath, and
--migrationType CLI flags. Intentionally skips module loading and
#[OnApplicationReady] events for a lean, fast migration run.Multiple starters
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.Multiple starters
#[PostConstruct]
#[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
#[OnApplicationReady]
#[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
ApplicationReadyEventinterface (which declaresonApplicationReady(): void).
AppStartupTasks.php
#[OnApplicationReady] fires once per application boot, not once per
request. In the Swoole runner it fires in the master process before the HTTP
server starts accepting connections.Shutdown hooks
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.
Destroy methods must be
public and accept no required parameters. The
framework discovers them via reflection at startup and validates both
constraints before the application begins serving.WinterModule
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
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
application.yml — loading a custom module