Configuration binding, and the options pattern

Reading configuration by key from a dictionary means every consumer knows the key names and nothing validates them; binding to a class means the shape is declared once.

public sealed class StripeOptions
{
    [Required] public string ApiKey { get; set; }
    [Range(1, 30)] public int TimeoutSeconds { get; set; } = 3;
}

services.AddOptions<StripeOptions>()
    .Bind(Configuration.GetSection("Stripe"))
    .ValidateDataAnnotations();

// injected as IOptions<StripeOptions>

Validation at startup is the property that matters: a missing key is a failure to boot rather than a null appearing during a request three hours later. IOptionsSnapshot re-reads per request and IOptionsMonitor notifies on change, which is the reloading behaviour PHP has no equivalent of at all. The pattern transfers directly — a typed configuration object validated at boot is worth building in any language.