Configuration in Winter Boot: Properties, Beans & Sources
Winter Boot is configured in two ways: settings in application.yml, and beans you build in code with #[Configuration] classes. Both plug into the dependency-injection container, so any property or bean can be injected anywhere in your application. This page covers the YAML file, the #[Value] attribute, extra property sources (environment variables, INI files, your own), the || fallback syntax, and profiles.
application.yml Structure
Section titled “application.yml Structure”Properties live in application.yml, inside the directory set by the configDirectory option of #[WinterBootApplication] (or the -c <dir> command-line flag, which overrides it). Winter Boot reads this file at startup and makes every key available for injection. The framework’s own keys are shown below; your own keys can go anywhere else in the file.
server: port: 8080 # Port the built-in HTTP server binds to address: 127.0.0.1 # Network interface to listen on context-path: / # URL prefix for all routes
winter: application: name: My Microservice # Human-readable service name id: my-microservice # Machine-readable identifier version: 1.0.0-DEV # Semantic version string
datasource: - name: default url: "sqlite:/opt/databases/mydb.sq3" username: password: validationQuery: SELECT 'ok' driverClass: dev\winterframework\pdbc\pdo\PdoDataSource connection: persistent: true errorMode: ERRMODE_EXCEPTION columnsCase: CASE_NATURAL idleTimeout: 300 autoCommit: true
# Custom application properties — any structure you like:myApp: value1: This is a string property value2: 99 value3: true value4: 10.89Core Framework Keys
Section titled “Core Framework Keys”-
server.portinteger(default:8080) Port the built-in HTTP server binds to. -
server.addressstring(default:127.0.0.1) Network interface the server listens on. Use0.0.0.0to bind all interfaces. -
server.context-pathstring(default:/) URL prefix prepended to all mapped routes. -
winter.application.namestringHuman-readable service name, shown in the startup banner. -
winter.application.idstringMachine-readable service identifier, used for example in service-discovery registrations. -
winter.application.versionstringSemantic version string for the running service. -
datasourcearrayList of datasource definitions. Each entry requires at leastname,url, anddriverClass. See Database.
For every key the framework and its modules read, see the application.yml reference.
#[WinterBootApplication] Attribute Options
Section titled “#[WinterBootApplication] Attribute Options”The entry-point attribute controls how Winter Boot starts your application. configDirectory and scanNamespaces are required; the rest are optional.
<?php
use dev\winterframework\stereotype\WinterBootApplication;use dev\winterframework\core\app\WinterWebSwooleApplication;
#[WinterBootApplication( // Directories to search for application.yml and other config files configDirectory: [__DIR__ . '/config'],
// Namespace-to-directory mappings for component scanning: // each entry is [NamespacePrefix, BaseDirectory] scanNamespaces: [ ['com\\example\\myapp', __DIR__ . '/src'] ],
// If true, the scanner require()s scanned files that Composer cannot autoload autoload: false,
// Namespaces the scanner should skip entirely scanExcludeNamespaces: ['com\\example\\myapp\\generated'],
// Optional: load application-{profile}.yml instead of application.yml profile: null,
// Set to true to instantiate all beans eagerly at startup // (predictable boot but slower start time) eager: false)]class Application{ public static function main(): void { (new WinterWebSwooleApplication())->run(Application::class); }}Attribute Parameter Reference
Section titled “Attribute Parameter Reference”| Parameter | Type | Default | Description |
|---|---|---|---|
configDirectory |
array |
— (required) | Directories searched for application.yml and other config files. The -c <dir> CLI flag overrides it. |
scanNamespaces |
array |
— (required) | Pairs of [NamespacePrefix, BaseDirectory] scanned for beans. |
autoload |
bool |
false |
When true, the scanner requires scanned files whose classes Composer cannot autoload. Leave false if your namespace is in composer.json. |
scanExcludeNamespaces |
array |
[] |
Namespaces the scanner skips entirely. |
profile |
string|null |
null |
Loads application-{profile}.yml instead of application.yml. |
eager |
bool |
false |
When true, all beans are created at startup rather than on first use. Startup is slower but errors surface earlier. |
Injecting Properties with #[Value]
Section titled “Injecting Properties with #[Value]”The #[Value] attribute injects a configuration value into a bean property. Reference YAML keys with dot notation inside ${ }.
<?php
declare(strict_types=1);
namespace com\example\myapp;
use dev\winterframework\stereotype\Service;use dev\winterframework\stereotype\Value;
#[Service]class AppConfig{ #[Value('${server.port}')] private int $serverPort;
#[Value('${winter.application.name}')] private string $appName;
#[Value('${myApp.value1}')] private string $customMessage;
#[Value('${myApp.value2}')] private int $numericSetting;
public function getServerPort(): int { return $this->serverPort; } public function getAppName(): string { return $this->appName; }}Type conversion
Section titled “Type conversion”Values from environment variables and INI files are strings, so #[Value] converts them to the property’s type by meaning (since 2.1.6). The same rules apply to defaultValue.
| Property type | Accepted values |
|---|---|
bool |
true, false, yes, no, on, off, 1, 0 (any case, trimmed); an empty string is false |
int |
whole numbers such as 8080, -3, 007 |
float |
any number, such as 0.25 |
string |
any value; numbers and booleans become their text |
nullable (?bool, ?int, …) |
also null in any case, which sets null |
A value that doesn’t fit, such as FEATURE_ENABLED=maybe for a bool or PORT=80a for an int, stops the application at startup. The error names the key, the class and property, and where to fix it, but never the value itself, because configuration can hold secrets:
Invalid value for config property #[Value] "${feature.enabled}" on com\example\myapp\AppConfig::$enabled (bool):expected a boolean (true/false, yes/no, on/off, 1/0); fix "feature.enabled" in application.yml, or in the$env/$ini/property source it points toWhen the key is missing, the attribute’s defaultValue is used, otherwise the property’s own PHP default. A required property without either stops startup.
Programmatic Bean Configuration
Section titled “Programmatic Bean Configuration”For beans that need construction logic — typically third-party clients that are not Winter Boot beans themselves — use a #[Configuration] class. Each #[Bean] method is called once and its return value is registered as a managed bean. Method parameters that are typed as other beans are injected automatically.
<?php
declare(strict_types=1);
namespace com\example\myapp;
use dev\winterframework\stereotype\Bean;use dev\winterframework\stereotype\Configuration;use dev\winterframework\stereotype\Value;use GuzzleHttp\Client;
#[Configuration]class HttpClientConfig{ #[Value('${payments.baseUrl}')] private string $paymentsUrl;
/** * Registers a Guzzle client as a bean, available for #[Autowired] injection. */ #[Bean] public function paymentsClient(): Client { return new Client(['base_uri' => $this->paymentsUrl, 'timeout' => 5]); }
/** * The Client bean defined above is injected as a parameter. */ #[Bean] public function paymentsGateway(Client $client): PaymentsGateway { return new PaymentsGateway($client); }}Additional Property Sources
Section titled “Additional Property Sources”Beyond application.yml, Winter Boot supports pluggable PropertySource implementations. Register them in application.yml under the propertySources key and reference their values with $sourceName.key notation.
The built-in EnvPropertySource reads from $_ENV. Register it and reference variables with the $env. prefix:
propertySources: - name: env provider: dev\winterframework\io\EnvPropertySource
datasource: - name: default username: "$env.DB_USERNAME" password: "$env.DB_PASSWORD"Use IniPropertySource to read secrets from an .ini file — useful for mounted Kubernetes secrets or local development overrides:
propertySources: - name: ini provider: dev\winterframework\io\IniPropertySource filePath: /var/run/secrets/db.ini
datasource: - name: default username: "$ini.dbUsername" password: "$ini.dbPassword"A matching db.ini file:
dbUsername = appuserdbPassword = s3cr3tImplement the PropertySource interface to pull values from any external system — HashiCorp Vault, AWS Secrets Manager, Consul, or your own API:
propertySources: - name: vault provider: com\example\myapp\VaultPropertySource url: https://vault.example.com:8200/ token: "$env.VAULT_TOKEN"
datasource: - name: default password: "$vault.db_password"Fallback Chains with ||
Section titled “Fallback Chains with ||”A property value can be a ||-separated chain. Winter Boot tries each term from left to right and uses the first present value. This lets you layer sources — environment variable first, INI file second, hard-coded default last — without any PHP code.
propertySources: - name: env provider: dev\winterframework\io\EnvPropertySource - name: ini provider: dev\winterframework\io\IniPropertySource filePath: /etc/myapp/secrets.ini
datasource: - name: default # Try ENV first, then INI file, fall back to literal default username: "$env.DB_USERNAME || $ini.dbUsername || appuser" password: "$ini.dbPassword || $vault.db_password || secret"
# Booleans and numbers keep their type when used as literals sslEnabled: "$env.DB_SSL || false" poolSize: "$env.DB_POOL_SIZE || $ini.poolSize || 10"
# A chain can end with null to make the property optional optionalNote: "$env.DB_NOTE || null"
# Quoted string literal as the final default welcome: "$env.DB_GREETING || 'hello world'"Fallback Chain Rules
Section titled “Fallback Chain Rules”| Value | Behavior |
|---|---|
| Any non-empty, non-null value | Present — stops the chain and uses this value. |
false, 0, '0' |
Present — stops the chain immediately. |
null or '' |
Falls through to the next term in the chain. |
| Unknown source name | Treated as a missing key — falls through. |
Single $source.key (no ||) |
A missing key throws a PropertyException. |
Plain value with no $source.key |
Never treated as a chain — "a || b" stays the literal string a || b. |
Profiles
Section titled “Profiles”Use the profile parameter of #[WinterBootApplication] to load environment-specific configuration. With a profile set, Winter Boot reads application-{profile}.yml instead of application.yml — the files are not merged, so the profile file must contain the complete configuration. The logger follows the same pattern: it reads logger-{profile}.yml and falls back to logger.yml.
<?php
declare(strict_types=1);
use dev\winterframework\stereotype\WinterBootApplication;use dev\winterframework\core\app\WinterWebSwooleApplication;
require_once __DIR__ . '/vendor/autoload.php';
#[WinterBootApplication( configDirectory: [__DIR__ . '/config'], scanNamespaces: [['com\\example\\myapp', __DIR__ . '/src']], profile: 'prod' // loads config/application-prod.yml)]class Application{ public static function main(): void { (new WinterWebSwooleApplication())->run(Application::class); }}
Application::main();server: port: 80 address: 0.0.0.0
winter: application: name: My Service id: my-service version: 1.0.0Complete Example
Section titled “Complete Example”The example below shows a full application.yml combining a custom property source, fallback chains, and all standard Winter Boot keys:
server: port: "$env.SERVER_PORT || 8080" address: "$env.SERVER_ADDRESS || 0.0.0.0"
winter: application: name: "$env.APP_NAME || My Service" id: my-service version: 1.0.0
propertySources: - name: env provider: dev\winterframework\io\EnvPropertySource - name: ini provider: dev\winterframework\io\IniPropertySource filePath: /run/secrets/app.ini
datasource: - name: default url: "$env.DB_URL || $ini.dbUrl" username: "$env.DB_USER || $ini.dbUser || appuser" password: "$env.DB_PASS || $ini.dbPass" validationQuery: SELECT 1 driverClass: dev\winterframework\pdbc\pdo\PdoDataSource connection: persistent: true autoCommit: true idleTimeout: 300