Skip to content

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.

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.

config/application.yml
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.89
  • server.port integer (default: 8080) Port the built-in HTTP server binds to.

  • server.address string (default: 127.0.0.1) Network interface the server listens on. Use 0.0.0.0 to bind all interfaces.

  • server.context-path string (default: /) URL prefix prepended to all mapped routes.

  • winter.application.name string Human-readable service name, shown in the startup banner.

  • winter.application.id string Machine-readable service identifier, used for example in service-discovery registrations.

  • winter.application.version string Semantic version string for the running service.

  • datasource array List of datasource definitions. Each entry requires at least name, url, and driverClass. 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.

Application.php
<?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);
}
}
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.

The #[Value] attribute injects a configuration value into a bean property. Reference YAML keys with dot notation inside ${ }.

src/AppConfig.php
<?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; }
}

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 to

When the key is missing, the attribute’s defaultValue is used, otherwise the property’s own PHP default. A required property without either stops startup.

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.

src/HttpClientConfig.php
<?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);
}
}

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:

config/application.yml
propertySources:
- name: env
provider: dev\winterframework\io\EnvPropertySource
datasource:
- name: default
username: "$env.DB_USERNAME"
password: "$env.DB_PASSWORD"

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.

config/application.yml
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'"
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.

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.

Application.php
<?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();
config/application-prod.yml
server:
port: 80
address: 0.0.0.0
winter:
application:
name: My Service
id: my-service
version: 1.0.0

The example below shows a full application.yml combining a custom property source, fallback chains, and all standard Winter Boot keys:

config/application.yml
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