chillerlan / php-settings-container
A container class for immutable settings objects. Not a DI container.
                                    Fund package maintenance!
                                                                            
                                                                                                                                        Ko Fi
                                                                                    
                                                                            
                                                                                                                                        www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4
                                                                                    
                                                                
Installs: 19 915 115
Dependents: 24
Suggesters: 0
Security: 0
Stars: 29
Watchers: 2
Forks: 5
Open Issues: 0
pkg:composer/chillerlan/php-settings-container
Requires
- php: ^8.1
- ext-json: *
Requires (Dev)
- phpmd/phpmd: ^2.15
- phpstan/phpstan: ^1.11
- phpstan/phpstan-deprecation-rules: ^1.2
- phpunit/phpunit: ^10.5
- squizlabs/php_codesniffer: ^3.10
README
A container class for settings objects - decouple configuration logic from your application! Not a DI container.
- SettingsContainerInterfaceprovides fancy property hooks for PHP < 8.4.
Documentation
Installation
requires composer
composer.json (note: replace dev-main with a version constraint, e.g. ^3.0 - see releases for valid versions)
{
	"require": {
		"php": "^8.1",
		"chillerlan/php-settings-container": "dev-main"
	}
}
Profit!
Usage
The SettingsContainerInterface (wrapped inSettingsContainerAbstract) provides plug-in functionality for immutable object properties and adds some fancy, like loading/saving JSON, arrays etc.
It takes an iterable as the only constructor argument and calls a method with the trait's name on invocation (MyTrait::MyTrait()) for each used trait.
A PHPStan ruleset to exclude errors generated by accessing magic properties on SettingsContainerInterface can be found in rules-magic-access.neon.
Simple usage
class MyContainer extends SettingsContainerAbstract{ protected string $foo; protected string $bar; }
// use it just like a \stdClass (except the properties are fixed) $container = new MyContainer; $container->foo = 'what'; $container->bar = 'foo'; // which is equivalent to $container = new MyContainer(['bar' => 'foo', 'foo' => 'what']); // ...or try $container->fromJSON('{"foo": "what", "bar": "foo"}'); // fetch all properties as array $container->toArray(); // -> ['foo' => 'what', 'bar' => 'foo'] // or JSON $container->toJSON(); // -> {"foo": "what", "bar": "foo"} // JSON via JsonSerializable $json = json_encode($container); // -> {"foo": "what", "bar": "foo"} //non-existing properties will be ignored: $container->nope = 'what'; var_dump($container->nope); // -> null
Advanced usage
// from library 1 trait SomeOptions{ protected string $foo; protected string $what; // this method will be called in SettingsContainerAbstract::construct() // after the properties have been set protected function SomeOptions():void{ // just some constructor stuff... $this->foo = strtoupper($this->foo); } /* * special prefixed magic setters & getters */ // this method will be called from __set() when property $what is set protected function set_what(string $value):void{ $this->what = md5($value); } // this method is called on __get() for the property $what protected function get_what():string{ return 'hash: '.$this->what; } } // from library 2 trait MoreOptions{ protected string $bar = 'whatever'; // provide default values }
$commonOptions = [ // SomeOptions 'foo' => 'whatever', // MoreOptions 'bar' => 'nothing', ]; // now plug the several library options together to a single object $container = new class ($commonOptions) extends SettingsContainerAbstract{ use SomeOptions, MoreOptions; }; var_dump($container->foo); // -> WHATEVER (constructor ran strtoupper on the value) var_dump($container->bar); // -> nothing $container->what = 'some value'; var_dump($container->what); // -> hash: 5946210c9e93ae37891dfe96c3e39614 (custom getter added "hash: ")
API
SettingsContainerAbstract
| method | return | info | 
|---|---|---|
| __construct(iterable $properties = null) | - | calls construct()internally after the properties have been set | 
| (protected) construct() | void | calls a method with trait name as replacement constructor for each used trait | 
| __get(string $property) | mixed | calls $this->{'get_'.$property}()if such a method exists | 
| __set(string $property, $value) | void | calls $this->{'set_'.$property}($value)if such a method exists | 
| __isset(string $property) | bool | |
| __unset(string $property) | void | |
| __toString() | string | a JSON string | 
| toArray() | array | |
| fromIterable(iterable $properties) | SettingsContainerInterface | |
| toJSON(int $jsonOptions = null) | string | accepts JSON options constants | 
| fromJSON(string $json) | SettingsContainerInterface | |
| jsonSerialize() | mixed | implements the JsonSerializableinterface | 
| serialize() | string | implements the Serializableinterface | 
| unserialize(string $data) | void | implements the Serializableinterface | 
| __serialize() | array | implements the Serializableinterface | 
| __unserialize(array $data) | void | implements the Serializableinterface | 
Disclaimer
This might be either an absolutely brilliant or completely stupid idea - you decide. (in hindsight it was a great idea I guess - property hooks made their way into PHP 8.4) Also, this is not a dependency injection container. Stop using DI containers FFS.