{"id":18321786,"url":"https://github.com/chillerlan/php-settings-container","last_synced_at":"2026-04-01T20:18:35.786Z","repository":{"id":32922869,"uuid":"146363289","full_name":"chillerlan/php-settings-container","owner":"chillerlan","description":"A container class for immutable settings objects. Not a DI container.","archived":false,"fork":false,"pushed_at":"2024-07-17T01:34:52.000Z","size":467,"stargazers_count":29,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-08T23:44:59.138Z","etag":null,"topics":["configuration","configuration-management","php","php7","php8","settings","settings-management","settings-storage"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chillerlan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"ko_fi":"codemasher","custom":"https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4"}},"created_at":"2018-08-27T22:55:29.000Z","updated_at":"2025-04-09T18:21:19.000Z","dependencies_parsed_at":"2024-01-05T22:31:19.504Z","dependency_job_id":"6d4db0ae-e49a-4f32-8b85-a91481580257","html_url":"https://github.com/chillerlan/php-settings-container","commit_stats":{"total_commits":95,"total_committers":2,"mean_commits":47.5,"dds":"0.19999999999999996","last_synced_commit":"10d97f8c219e89adeba275b02680981bea2674e4"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chillerlan%2Fphp-settings-container","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chillerlan%2Fphp-settings-container/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chillerlan%2Fphp-settings-container/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chillerlan%2Fphp-settings-container/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chillerlan","download_url":"https://codeload.github.com/chillerlan/php-settings-container/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253166473,"owners_count":21864467,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["configuration","configuration-management","php","php7","php8","settings","settings-management","settings-storage"],"created_at":"2024-11-05T18:21:32.238Z","updated_at":"2026-04-01T20:18:35.777Z","avatar_url":"https://github.com/chillerlan.png","language":"PHP","funding_links":["https://ko-fi.com/codemasher","https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4"],"categories":[],"sub_categories":[],"readme":"# chillerlan/php-settings-container\n\nA container class for settings objects - decouple configuration logic from your application! Not a DI container.\n- [`SettingsContainerInterface`](https://github.com/chillerlan/php-settings-container/blob/main/src/SettingsContainerInterface.php) provides fancy [property hooks](https://wiki.php.net/rfc/property-hooks) for PHP \u003c 8.4.\n\n[![PHP Version Support][php-badge]][php]\n[![version][packagist-badge]][packagist]\n[![license][license-badge]][license]\n[![Continuous Integration][gh-action-badge]][gh-action]\n[![Coverage][coverage-badge]][coverage]\n[![Codacy][codacy-badge]][codacy]\n[![Packagist downloads][downloads-badge]][downloads]\n\n[php-badge]: https://img.shields.io/packagist/php-v/chillerlan/php-settings-container?logo=php\u0026color=8892BF\n[php]: https://www.php.net/supported-versions.php\n[packagist-badge]: https://img.shields.io/packagist/v/chillerlan/php-settings-container.svg?logo=packagist\n[packagist]: https://packagist.org/packages/chillerlan/php-settings-container\n[license-badge]: https://img.shields.io/github/license/chillerlan/php-settings-container.svg\n[license]: https://github.com/chillerlan/php-settings-container/blob/main/LICENSE\n[coverage-badge]: https://img.shields.io/codecov/c/github/chillerlan/php-settings-container.svg?logo=codecov\n[coverage]: https://codecov.io/github/chillerlan/php-settings-container\n[codacy-badge]: https://img.shields.io/codacy/grade/bd2467799e2943d2853ce3ebad5af490/main?logo=codacy\n[codacy]: https://app.codacy.com/gh/chillerlan/php-settings-container/dashboard?branch=main\n[downloads-badge]: https://img.shields.io/packagist/dt/chillerlan/php-settings-container.svg?logo=packagist\n[downloads]: https://packagist.org/packages/chillerlan/php-settings-container/stats\n[gh-action-badge]: https://img.shields.io/github/actions/workflow/status/chillerlan/php-settings-container/ci.yml?branch=main\u0026logo=github\n[gh-action]: https://github.com/chillerlan/php-settings-container/actions/workflows/ci.yml?query=branch%3Amain\n\n## Documentation\n\n### Installation\n**requires [composer](https://getcomposer.org)**\n\n*composer.json* (note: replace `dev-main` with a [version constraint](https://getcomposer.org/doc/articles/versions.md#writing-version-constraints), e.g. `^3.0` - see [releases](https://github.com/chillerlan/php-settings-container/releases) for valid versions)\n```json\n{\n\t\"require\": {\n\t\t\"php\": \"^8.4\",\n\t\t\"chillerlan/php-settings-container\": \"dev-main\"\n\t}\n}\n```\n\nProfit!\n\n## Usage\n\nThe `SettingsContainerInterface` (wrapped in`SettingsContainerAbstract`) provides plug-in functionality for immutable object properties and adds some fancy, like loading/saving JSON, arrays etc.\nIt 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.\n\nA PHPStan ruleset to exclude errors generated by accessing magic properties on `SettingsContainerInterface` can be found in `rules-magic-access.neon`.\n\n\n### Simple usage\n```php\nclass MyContainer extends SettingsContainerAbstract{\n\tprotected string $foo;\n\tprotected string $bar;\n}\n```\n\n```php\n// use it just like a \\stdClass (except the properties are fixed)\n$container = new MyContainer;\n$container-\u003efoo = 'what';\n$container-\u003ebar = 'foo';\n\n// which is equivalent to\n$container = new MyContainer(['bar' =\u003e 'foo', 'foo' =\u003e 'what']);\n// ...or try\n$container-\u003efromJSON('{\"foo\": \"what\", \"bar\": \"foo\"}');\n\n\n// fetch all properties as array\n$container-\u003etoArray(); // -\u003e ['foo' =\u003e 'what', 'bar' =\u003e 'foo']\n// or JSON\n$container-\u003etoJSON(); // -\u003e {\"foo\": \"what\", \"bar\": \"foo\"}\n// JSON via JsonSerializable\n$json = json_encode($container); // -\u003e {\"foo\": \"what\", \"bar\": \"foo\"}\n```\n\nBy default, non-existing properties will be ignored and return `null`:\n\n```php\n$container-\u003enope = 'what';\n\nvar_dump($container-\u003enope); // -\u003e null\n```\n\nYou can change this behaviour by adding the attribute `ThrowOnInvalidProperty` to your container class:\n\n```php\n#[ThrowOnInvalidProperty(true)]\nclass MyContainer extends SettingsContainerAbstract{\n\t// ...\n}\n\n$container-\u003enope = 'what'; // -\u003e throws: attempt to write invalid property: \"$nope\"\n```\n\n\n### Advanced usage\n\nSuppose the following trait from library 1:\n\n```php\ntrait SomeOptions{\n\tprotected string $foo;\n\tprotected string $what;\n\n\t// this method will be called in SettingsContainerAbstract::construct()\n\t// after the properties have been set\n\tprotected function SomeOptions():void{\n\t\t// just some constructor stuff...\n\t\t$this-\u003efoo = strtoupper($this-\u003efoo);\n\t}\n\n\t/*\n\t * special prefixed magic setters \u0026 getters (\"set_\"/\"get_\" + property name)\n\t */\n\n\t// this method will be called from __set() when property $what is set\n\tprotected function set_what(string $value):void{\n\t\t$this-\u003ewhat = md5($value);\n\t}\n\n\t// this method is called on __get() for the property $what\n\tprotected function get_what():string{\n\t\treturn 'hash: '.$this-\u003ewhat;\n\t}\n}\n```\n\nAnd another trait from library 2:\n\n```php\ntrait MoreOptions{\n\tprotected string $bar = 'whatever'; // provide default values\n}\n```\n\nWe can now plug the several library options together to a single class/object:\n\n```php\n$commonOptions = [\n\t// SomeOptions\n\t'foo' =\u003e 'whatever',\n\t// MoreOptions\n\t'bar' =\u003e 'nothing',\n];\n\n$container = new class ($commonOptions) extends SettingsContainerAbstract{\n\tuse SomeOptions, MoreOptions;\n};\n\nvar_dump($container-\u003efoo); // -\u003e WHATEVER (constructor ran strtoupper on the value)\nvar_dump($container-\u003ebar); // -\u003e nothing\n\n$container-\u003ewhat = 'some value';\nvar_dump($container-\u003ewhat); // -\u003e hash: 5946210c9e93ae37891dfe96c3e39614 (custom getter added \"hash: \")\n```\n\n### A note on property hooks (PHP 8.4+)\n\nProperty hooks are called whenever a property is accessed (except from within the hook itself of course), which means that the custom get/set methods this library allows would conflict when a custom method is defined for a property that also has a hook defined.\nTo prevent double method calls, the internal methods `hasSetHook()` and `hasGetHook()` have been introduced, and are called whenever the magic get/set methods are called: when both, a custom method and a property hook exist, only the property hook will be called.\n\u003cbr/\u003ePublic properties will never call the magic get/set, however, their hooks *will* be called. (un)serializing a `SettingsContainerInterface` instance will bypass magic get/set and existing property hooks, while JSON de/encode as will call magic get/set or existing hooks explicitly via the `toArray()` and `fromIterable()` methods.\n\n```php\nclass PropertyHooksContainer extends SettingsContainerAbstract{\n\n\tprotected string $someValue{\n\t\tset =\u003e doStuff($value);\n\t}\n\n\t// this method will be ignored in magic calls as a \"set\" hook on the property exists\n\tprotected function set_someValue(string $value):void{\n\t\t$this-\u003esomeValue = doOtherStuff($value);\n\t}\n\n\t// this custom method will be called as the property has no \"get\" hook\n\tprotected function get_someValue():string{\n\t\treturn doWhatever($this-\u003esomeValue);\n\t}\n\n\t// this property will never trigger the magic get/set and associated methods\n\tpublic string $otherValue{\n\t\tset =\u003e doStuff($value);\n\t\tget =\u003e $this-\u003eotherValue;\n\t}\n\n}\n```\n\n\n### API\n\n#### [`SettingsContainerAbstract`](https://github.com/chillerlan/php-settings-container/blob/main/src/SettingsContainerAbstract.php)\n\n| method                                     | return                       | info                                                                                                                |\n|--------------------------------------------|------------------------------|---------------------------------------------------------------------------------------------------------------------|\n| `__construct(iterable $properties = null)` | -                            | calls `construct()` internally after the properties have been set                                                   |\n| `__get(string $property)`                  | mixed                        | calls `$this-\u003e{'get_'.$property}()` if such a method exists                                                         |\n| `__set(string $property, $value)`          | void                         | calls `$this-\u003e{'set_'.$property}($value)` if such a method exists                                                   |\n| `__isset(string $property)`                | bool                         |                                                                                                                     |\n| `__unset(string $property)`                | void                         |                                                                                                                     |\n| `__toString()`                             | string                       | a JSON string                                                                                                       |\n| `toArray()`                                | array                        |                                                                                                                     |\n| `fromIterable(iterable $properties)`       | `SettingsContainerInterface` |                                                                                                                     |\n| `toJSON(int $jsonOptions = null)`          | string                       | accepts [JSON options constants](http://php.net/manual/json.constants.php)                                          |\n| `fromJSON(string $json)`                   | `SettingsContainerInterface` |                                                                                                                     |\n| `jsonSerialize()`                          | mixed                        | implements the [`JsonSerializable`](https://www.php.net/manual/en/jsonserializable.jsonserialize.php) interface     |\n| `serialize()`                              | string                       | implements the [`Serializable`](https://www.php.net/manual/en/serializable.serialize.php) interface                 |\n| `unserialize(string $data)`                | void                         | implements the [`Serializable`](https://www.php.net/manual/en/serializable.unserialize.php) interface               |\n| `__serialize()`                            | array                        | implements the [`Serializable`](https://www.php.net/manual/en/language.oop5.magic.php#object.serialize) interface   |\n| `__unserialize(array $data)`               | void                         | implements the [`Serializable`](https://www.php.net/manual/en/language.oop5.magic.php#object.unserialize) interface |\n\n\n#### Internal (protected) methods\n\n| method                               | return | info                                                                          |\n|--------------------------------------|--------|-------------------------------------------------------------------------------|\n| `construct()`                        | void   | calls a method with trait name as replacement constructor for each used trait |\n| `isPrivate(string $property)`        | bool   | private properties are excluded from magic calls                              |\n| `hasSetHook(string $property)`       | bool   |                                                                               |\n| `hasGetHook(string $property)`       | bool   |                                                                               |\n\n\n## Disclaimer\nThis 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).\n\u003cbr/\u003eAlso, this is not a dependency injection container. Stop using DI containers FFS.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchillerlan%2Fphp-settings-container","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchillerlan%2Fphp-settings-container","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchillerlan%2Fphp-settings-container/lists"}