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: Add the package via Composer:
composer require chillerlan/php-settings-container
Requires PHP 8.4+.
Basic Container Class: Create a class extending SettingsContainerAbstract:
use Chillerlan\SettingsContainer\SettingsContainerAbstract;
class AppSettings extends SettingsContainerAbstract {
protected string $appName;
protected string $environment;
protected int $debugMode = 0;
}
First Use Case: Initialize with settings (array, JSON, or empty):
$settings = new AppSettings([
'appName' => 'MyApp',
'environment' => 'production',
'debugMode' => 1
]);
// Access values
echo $settings->appName; // "MyApp"
get_/set_ prefixed methods for custom logic (e.g., get_debugMode()).toJSON()/fromJSON() or toArray().Workflow:
class DatabaseConfig extends SettingsContainerAbstract {
protected string $host;
protected int $port;
protected string $username;
protected string $password;
protected function DatabaseConfig(): void {
$this->password = password_hash($this->password, PASSWORD_DEFAULT);
}
}
Integration with Laravel:
.env in bootstrap/app.php:
$databaseConfig = new DatabaseConfig([
'host' => env('DB_HOST'),
'port' => env('DB_PORT'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
]);
Pattern: Combine traits from libraries/modules into one container.
MailSettings and CacheSettings traits:
class AppConfig extends SettingsContainerAbstract {
use MailSettings, CacheSettings;
protected function MailSettings(): void {
$this->mailFrom = strtolower($this->mailFrom);
}
}
Pattern: Use set_ methods for validation or defaults.
protected function set_appName(string $value): void {
if (empty($value)) {
throw new \InvalidArgumentException("App name cannot be empty");
}
$this->appName = $value;
}
Pattern: Bind container to Laravel’s service container:
// In a service provider
$this->app->singleton('config', function ($app) {
return new AppSettings([
'appName' => config('app.name'),
'debugMode' => config('app.debug'),
]);
});
Usage in Controllers:
public function __construct(private AppSettings $settings) {}
Pattern: Load different configs per environment.
debugMode in AppServiceProvider:
$settings = app('config');
$settings->fromIterable([
'debugMode' => app()->environment('local') ? 1 : 0,
]);
Gotcha: Property hooks (PHP 8.4+) take precedence over magic get_/set_ methods.
protected string $foo { set => ... }), the magic method is ignored.hasSetHook()/hasGetHook() to debug conflicts.Tip: Use hooks for simple transformations (e.g., auto-trimming strings):
protected string $name { set => trim($value); }
null.
[ThrowOnInvalidProperty(true)]:
#[ThrowOnInvalidProperty(true)]
class AppSettings extends SettingsContainerAbstract { ... }
$settings->nonexistent throws InvalidPropertyException.serialize()/unserialize() bypass magic methods/property hooks.
toArray()/fromIterable() for consistent behavior:
$serialized = serialize($settings->toArray());
$restored = new AppSettings();
$restored->fromIterable(unserialize($serialized));
TraitA and TraitB are used, TraitB::TraitB() runs first.construct():
protected function construct(): void {
$this->TraitA();
$this->TraitB();
}
toArray() now respects magic getters (since v3.2.0).
$settings->toArray(); // Calls get_debugMode(), not direct property access
get_object_vars() to inspect raw properties (bypasses magic methods):
print_r(get_object_vars($settings)); // Shows unprocessed values
$this->app->singleton('config', fn() => new AppSettings(config('app.settings')));
abstract class BaseSettings extends SettingsContainerAbstract {
protected function construct(): void {
// Shared logic (e.g., logging, validation)
}
}
Extend this for all app-specific containers.$mockSettings = $this->createMock(AppSettings::class);
$mockSettings->method('toArray')->willReturn(['debugMode' => 1]);
fromIterable() to reset test state:
$settings->fromIterable([]); // Reset to defaults
How can I help you explore Laravel packages today?