chillerlan/php-settings-container
Lightweight PHP settings container to keep configuration logic out of your app (not a DI container). Provides a SettingsContainerInterface with “property hook”-style access for PHP < 8.4, plus sane defaults for organizing and retrieving settings objects.
Installation:
composer require chillerlan/php-settings-container
Requires PHP 8.4+.
Basic Container:
use Chillerlan\SettingsContainer\SettingsContainerAbstract;
class AppSettings extends SettingsContainerAbstract {
protected string $app_name;
protected string $debug_mode = 'false';
}
First Use Case:
$settings = new AppSettings(['app_name' => 'MyApp']);
echo $settings->app_name; // "MyApp"
echo $settings->debug_mode; // "false" (default)
SettingsContainerAbstract: Core class for extending.SettingsContainerInterface: Defines the contract (e.g., toArray(), toJSON()).Pattern: Use the container to encapsulate app/config settings as immutable objects.
class DatabaseConfig extends SettingsContainerAbstract {
protected string $host;
protected int $port;
protected string $username;
protected string $password;
}
// Initialize once (e.g., in a service provider)
$databaseConfig = new DatabaseConfig([
'host' => env('DB_HOST'),
'port' => env('DB_PORT'),
'username' => env('DB_USER'),
'password' => env('DB_PASS'),
]);
Workflow:
DatabaseConfig class).Pattern: Combine traits from libraries into a single container.
use Chillerlan\SettingsContainer\SettingsContainerAbstract;
use LibraryOne\OptionsTrait;
use LibraryTwo\MoreOptionsTrait;
class CompositeSettings extends SettingsContainerAbstract {
use OptionsTrait, MoreOptionsTrait;
protected string $custom_field;
}
// Initialize with merged config
$settings = new CompositeSettings([
'foo' => 'bar', // From OptionsTrait
'bar' => 'baz', // From MoreOptionsTrait
'custom_field' => 'value',
]);
Integration Tips:
set_*/get_* methods for property logic (e.g., hashing, validation).construct() in the parent class to trigger trait initialization.Pattern: Persist settings to JSON or serialize for caching.
// Save to JSON
$json = $settings->toJSON(JSON_PRETTY_PRINT);
file_put_contents('config.json', $json);
// Load from JSON
$loadedSettings = new AppSettings();
$loadedSettings->fromJSON($json);
// Serialize for caching (e.g., Redis)
$serialized = serialize($settings);
$unserialized = unserialize($serialized);
Use Case: Store settings in a database or cache (e.g., Laravel's cache driver).
Pattern: Use PHP 8.4 property hooks for runtime validation.
class ValidatedSettings extends SettingsContainerAbstract {
protected string $email {
set => $this->validateEmail($value);
}
private function validateEmail(string $email): void {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException("Invalid email: $email");
}
}
}
Alternative (Pre-8.4): Use set_* methods in traits.
trait EmailValidationTrait {
protected function set_email(string $value): void {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException("Invalid email: $value");
}
$this->email = $value;
}
}
Pattern: Load settings from .env dynamically.
class EnvSettings extends SettingsContainerAbstract {
protected string $env;
protected string $debug;
public function __construct() {
$this->env = env('APP_ENV', 'production');
$this->debug = env('APP_DEBUG', 'false');
}
}
Workflow:
fromIterable() if needed.Property Hooks vs. Magic Methods (PHP 8.4+):
set => ...), the set_* method is ignored.hasSetHook()/hasGetHook() to debug conflicts.Non-Existent Properties:
null.[ThrowOnInvalidProperty(true)]:
#[ThrowOnInvalidProperty(true)]
class StrictSettings extends SettingsContainerAbstract { ... }
Now $settings->nonexistent throws InvalidPropertyException.Trait Initialization Order:
construct() after properties are set.construct() to control flow.Serialization Quirks:
serialize()/unserialize() bypass magic methods/hooks.toArray()/fromIterable() for consistent behavior.Check Property Access:
__get()/__set() to log access:
public function __get(string $name) {
error_log("Getting property: $name");
return parent::__get($name);
}
Validate Traits:
set_foo for $foo).get_class_methods() to list available methods:
print_r(get_class_methods($settings));
JSON/Array Conversion:
toArray() now runs through magic getters (fixed in v3.2.0).toArray() output.Custom Serialization:
__serialize()/__unserialize() for non-standard formats.Dynamic Property Handling:
SettingsContainerAbstract to add dynamic properties:
public function __get(string $name) {
if (str_starts_with($name, 'dynamic_')) {
return $this->generateDynamicValue($name);
}
return parent::__get($name);
}
Attribute-Based Configuration:
#[Attribute]
class Encrypted {}
class SecureSettings extends SettingsContainerAbstract {
#[Encrypted] protected string $api_key;
}
construct() to process attributes.Integration with Laravel:
$app->singleton(AppSettings::class, fn() => new AppSettings());
'settings' => [
'app_name' => app(AppSettings::class)->app_name,
],
How can I help you explore Laravel packages today?