{"id":13669269,"url":"https://github.com/ICanBoogie/Accessor","last_synced_at":"2025-04-27T01:32:56.546Z","repository":{"id":26635916,"uuid":"30091734","full_name":"ICanBoogie/Accessor","owner":"ICanBoogie","description":"Implements getters/setters, read-only/write-only properties, volatile defaults, type control…","archived":false,"fork":false,"pushed_at":"2024-12-01T18:40:30.000Z","size":101,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-20T07:37:44.557Z","etag":null,"topics":["accessor","getters","lazy-properties","setters","virtual-properties"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ICanBoogie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2015-01-30T21:33:58.000Z","updated_at":"2023-02-15T08:34:15.000Z","dependencies_parsed_at":"2023-10-04T05:47:24.909Z","dependency_job_id":null,"html_url":"https://github.com/ICanBoogie/Accessor","commit_stats":{"total_commits":26,"total_committers":1,"mean_commits":26.0,"dds":0.0,"last_synced_commit":"745e4da28f02530cd711b984e6701fba94cc8340"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICanBoogie%2FAccessor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICanBoogie%2FAccessor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICanBoogie%2FAccessor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ICanBoogie%2FAccessor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ICanBoogie","download_url":"https://codeload.github.com/ICanBoogie/Accessor/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251057977,"owners_count":21529645,"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":["accessor","getters","lazy-properties","setters","virtual-properties"],"created_at":"2024-08-02T08:01:08.131Z","updated_at":"2025-04-27T01:32:54.567Z","avatar_url":"https://github.com/ICanBoogie.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"# Accessor\n\n[![Release](https://img.shields.io/packagist/v/icanboogie/accessor.svg)](https://packagist.org/packages/icanboogie/accessor)\n[![Code Quality](https://img.shields.io/scrutinizer/g/ICanBoogie/Accessor.svg)](https://scrutinizer-ci.com/g/ICanBoogie/Accessor)\n[![Code Coverage](https://img.shields.io/coveralls/ICanBoogie/Accessor.svg)](https://coveralls.io/r/ICanBoogie/Accessor)\n[![Downloads](https://img.shields.io/packagist/dt/icanboogie/accessor.svg)](https://packagist.org/packages/icanboogie/accessor)\n\nThe **icanboogie/accessor** package allows classes to implement [ICanBoogie][]'s accessor\ndesign pattern. Using a combination of getters, setters, properties, and property visibilities,\nyou can create read-only properties, write-only properties, virtual properties;\nand implement defaults, type control, guarding, and lazy loading.\n\n\n\n#### Installation\n\n```bash\ncomposer require icanboogie/inflector\n```\n\n\n\n### Preamble\n\nBecause the package is a citizen of [ICanBoogie][]'s realm, which elected [snake case][] a long\ntime ago for its readability, the following examples use the same casing, but [CamelCase][] is\nequally supported as we'll learn by the end of this document. Actually, because all getters and\nsetters are formatted using the `accessor_format` trait method it is very easy to bind the\nformatting to one's requirements simply by overriding that method.\n\n\n\n\n\n## Getters and setters\n\nA getter is a method that gets the value of a specific property. A setter is a method that sets\nthe value of a specific property. You can define getters and setters on classes using\nthe [AccessorTrait][] trait, and optionally inform of its feature by implementing the\n[HasAccessor][] interface.\n\n__Something to remember__: Getters and setters are only invoked when their corresponding property\nis not accessible. This is most notably important to remember when using lazy loading,\nwhich creates the associated property when it is invoked.\n\n__Another thing to remember__: You don't _need_ to use getter/setter for everything and their cats,\nPHP is no Java, and it's okay to have public properties.\n\n\n\n\n\n## Read-only properties\n\nRead-only properties are created by defining only a getter. A [PropertyNotWritable][] exception\nis thrown in attempt to set a read-only property.\n\nThe following example demonstrates how a `property` read-only property can be implemented:\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\n/**\n * @property-read mixed $property\n */\nclass ReadOnlyProperty\n{\n    use AccessorTrait;\n\n    protected function get_property()\n    {\n        return 'value';\n    }\n}\n\n$a = new ReadOnlyProperty;\necho $a-\u003eproperty;     // value\n$a-\u003eproperty = null;   // throws ICanBoogie\\PropertyNotWritable\n```\n\nAn existing property can be made read-only by setting its visibility to `protected` or `private`:\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\n/**\n * @property-read mixed $property\n */\nclass ReadOnlyProperty\n{\n    use AccessorTrait;\n\n    private $property = \"value\";\n\n    protected function get_property()\n    {\n        return $this-\u003eproperty;\n    }\n}\n\n$a = new ReadOnlyProperty;\necho $a-\u003eproperty;     // value\n$a-\u003eproperty = null;   // throws ICanBoogie\\PropertyNotWritable\n```\n\n### Protecting a _construct_ property\n\nRead-only properties are often used to provide read access to a property that was provided\nduring _construct_, which should stay unchanged during the life time of an instance.\n\nThe following example demonstrates how a `connection` property passed during _construct_\ncan only be read afterwards. The visibility of the property is set to _private_\nso that even an extending class cannot modify the property.\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\nclass Connection\n{\n    // …\n}\n\n/**\n * @property-read Connection $connection\n */\nclass Model\n{\n    use AccessorTrait;\n\n    /**\n     * @var Connection\n     */\n    private $connection;\n\n    protected function get_connection(): Connection\n    {\n        return $this-\u003econnection;\n    }\n\n    protected $options;\n\n    public function __construct(Connection $connection, array $options)\n    {\n        $this-\u003econnection = $connection;\n        $this-\u003eoptions = $options;\n    }\n}\n\n$connection = new Connection(…);\n$model = new Model($connection, …);\n\n$connection === $model-\u003econnection;   // true\n$model-\u003econnection = null;            // throws ICanBoogie\\PropertyNotWritable\n```\n\n\n\n\n\n## Write-only properties\n\nWrite-only properties are created by defining only a setter. A [PropertyNotReadable][] exception\nis thrown in attempt to get a write-only property.\n\nThe following example demonstrates how a `property` write-only property can be implemented:\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\n/**\n * @property-write mixed $property\n */\nclass WriteOnlyProperty\n{\n    use AccessorTrait;\n\n    protected function set_property($value)\n    {\n        // …\n    }\n}\n\n$a = new WriteOnlyProperty;\n$a-\u003eproperty = 'value';\necho $a-\u003eproperty;   // throws ICanBoogie\\PropertyNotReadable\n```\n\nAn existing property can be made write-only by setting its visibility to `protected` or `private`:\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\n/**\n * @property-write mixed $property\n */\nclass WriteOnlyProperty\n{\n    use AccessorTrait;\n\n    private $property = 'value';\n\n    protected function set_property($value)\n    {\n        $this-\u003eproperty = $value;\n    }\n}\n\n$a = new WriteOnlyProperty;\n$a-\u003eproperty = 'value';\necho $a-\u003eproperty;   // throws ICanBoogie\\PropertyNotReadable\n```\n\n\n\n\n\n## Virtual properties\n\nA virtual property is created by defining a getter and a setter but no corresponding property.\nVirtual properties are usually providing an interface to another property or data structure.\n\nThe following example demonstrates how a `minutes` virtual property can be implemented\nas an interface to a `seconds` property.\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\n/**\n * @property int $minutes\n */\nclass Time\n{\n    use AccessorTrait;\n\n    public $seconds;\n\n    protected function set_minutes(int $minutes)\n    {\n        $this-\u003eseconds = $minutes * 60;\n    }\n\n    protected function get_minutes(): int\n    {\n        return $this-\u003eseconds / 60;\n    }\n}\n\n$time = new Time;\n$time-\u003eseconds = 120;\necho $time-\u003eminutes;   // 2\n\n$time-\u003eminutes = 4;\necho $time-\u003eseconds;   // 240\n```\n\n\n\n\n\n## Providing a default value until a property is set\n\nBecause getters are invoked when their corresponding property is inaccessible,\nand because an unset property is of course inaccessible, it is possible to define getters\nproviding default values until a value is actually set.\n\nThe following example demonstrates how a default value can be provided while a property\nis inaccessible (unset an that case). During construct, if the `slug` property is empty\nit is unset, making it inaccessible. Thus, until the property is actually set,\nwhen the `slug` property is read its getter is invoked and returns a default value created from\nthe `title` property.\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\nclass Article\n{\n    use AccessorTrait;\n\n    public $title;\n    public $slug;\n\n    public function __construct(string $title, string $slug = null)\n    {\n        $this-\u003etitle = $title;\n\n        if ($slug)\n        {\n            $this-\u003eslug = $slug;\n        }\n        else\n        {\n            unset($this-\u003eslug);\n        }\n    }\n\n    protected function get_slug(): string\n    {\n        return \\ICanBoogie\\normalize($this-\u003etitle);\n    }\n}\n\n$article = new Article(\"This is my article\");\necho $article-\u003eslug;   // this-is-my-article\n$article-\u003eslug = \"my-article\";\necho $article-\u003eslug;   // my-article\nunset($article-\u003eslug);\necho $article-\u003eslug;   // this-is-my-article\n```\n\n\n\n\n\n## Façade properties (and type control)\n\nSometimes you want to be able to manage the type of a property, what can be stored,\nwhat can be retrieved, the most transparently possible. This can be achieved\nwith _façade properties_.\n\nFaçade properties are implemented by defining a private property along with its getter and setter.\n\nThe following example demonstrates how a `created_at` property is implemented.\nIt can be set to a mixed value, but is always read as a `DateTime` instance.\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\nuse ICanBoogie\\DateTime;\n\n/**\n * @property DateTime $created_at\n */\nclass Article\n{\n    use AccessorTrait;\n\n    private $created_at;\n\n    protected function set_created_at($datetime)\n    {\n        $this-\u003ecreated_at = $datetime;\n    }\n\n    protected function get_created_at(): DateTime\n    {\n        $datetime = $this-\u003ecreated_at;\n\n        if ($datetime instanceof DateTime)\n        {\n            return $datetime;\n        }\n\n        return $this-\u003ecreated_at = ($datetime === null) ? DateTime::none() : new DateTime($datetime, 'utc');\n    }\n}\n```\n\n\n\n\n\n### Façade properties are exported on serialization\n\nAlthough façade properties are defined using private properties, they are exported when\nthe instance is serialized, just like they would if they were public or protected.\n\n```php\n\u003c?php\n\n$article = new Article;\n$article-\u003ecreated_at = 'now';\n\n$test = unserialize(serialize($article));\necho get_class($test-\u003ecreated_at);           // ICanBoogie/DateTime\n$article-\u003ecreated_at == $test-\u003ecreated_at;   // true\n```\n\n\n\n\n\n## Lazy loading\n\nLazy loading creates the associated property when it is invoked, making subsequent accesses using\nthe property rather than the getter.\n\nIn the following example, the `lazy_get_pseudo_uniqid()` getter returns a unique value,\nbut because the `pseudo_uniqid` property is created with the `public` visibility after\nthe getter was called, any subsequent access to the property returns the same value:\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\n/**\n * @property string $pseudo_uniqid\n */\nclass PseudoUniqID\n{\n    use AccessorTrait;\n\n    protected function lazy_get_pseudo_uniqid(): string\n    {\n        return uniqid();\n    }\n}\n\n$a = new PseudoUniqID;\n\necho $a-\u003epseudo_uniqid; // 5089497a540f8\necho $a-\u003epseudo_uniqid; // 5089497a540f8\n```\n\nOf course, unsetting the created property resets the process.\n\n```php\n\u003c?php\n\nunset($a-\u003epseudo_uniqid);\n\necho $a-\u003epseudo_uniqid; // 508949b5aaa00\necho $a-\u003epseudo_uniqid; // 508949b5aaa00\n```\n\n\n\n\n\n### Setting a lazy property\n\nLazy properties are implemented similarly to read-only properties, by defining a method\nto get a value, but unlike read-only properties lazy properties can be written too:\n\n```php\n\u003c?php\n\n$a = new PseudoUniqID;\n\necho $a-\u003epseudo_uniqid;   // a009b3a984a50\n$a-\u003epseudo_uniqid = 123456;\necho $a-\u003epseudo_uniqid;   // 123456\n\nunset($a-\u003epseudo_uniqid);\necho $a-\u003epseudo_uniqid;   // 57e5ada092180\n```\n\nYou need to remember that lazy properties actually _create_ a property,\nthus the getter won't be invoked if the property is already accessible.\n\n\n\n\n\n## Overloading getters and setters\n\nBecause getters and setters are classic methods, they can be overloaded. That is,\nthe setter or getter of a parent class can be overloaded by an extending class.\n\nThe following example demonstrates how an `Awesome` class extending an `Plain` class can turn\na _plain_ getter into an awesome getter:\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorTrait;\n\n/**\n * @property-read string $property\n */\nclass Plain\n{\n    use AccessorTrait;\n\n    protected function get_property()\n    {\n        return \"value\";\n    }\n}\n\nclass Awesome extends Plain\n{\n    protected function get_property()\n    {\n        return \"awesome \" . parent::get_property();\n    }\n}\n\n$plain = new Plain;\necho $plain-\u003eproperty;     // value\n\n$awesome = new Awesome;\necho $awesome-\u003eproperty;   // awesome value\n```\n\n\n\n\n\n## CamelCase support\n\n[CamelCase][] getters and setters are equally supported. Instead of using the [AccessorTrait][],\nuse the [AccessorCamelTrait][]:\n\n```php\n\u003c?php\n\nuse ICanBoogie\\Accessor\\AccessorCamelTrait;\n\n/**\n * @property-read $camelProperty\n */\nclass CamelExample\n{\n    use AccessorCamelTrait;\n\n    private $camelProperty;\n\n    protected function getCamelProperty()\n    {\n        return $this-\u003ecamelProperty;\n    }\n\n    public function __construct($value)\n    {\n        $this-\u003ecamelProperty = $value;\n    }\n}\n\n$a = new CamelExample(\"value\");\necho $a-\u003ecamelProperty;   // value\n```\n\n\n\n\n\n----------\n\n\n\n## Continuous Integration\n\nThe project is continuously tested by [GitHub actions](https://github.com/ICanBoogie/Accessor/actions).\n\n[![Tests](https://github.com/ICanBoogie/Accessor/workflows/test/badge.svg)](https://github.com/ICanBoogie/Accessor/actions?query=workflow%3Atest)\n[![Static Analysis](https://github.com/ICanBoogie/Accessor/workflows/static-analysis/badge.svg)](https://github.com/ICanBoogie/Accessor/actions?query=workflow%3Astatic-analysis)\n[![Code Style](https://github.com/ICanBoogie/Accessor/workflows/code-style/badge.svg)](https://github.com/ICanBoogie/Accessor/actions?query=workflow%3Acode-style)\n\n\n\n## Code of Conduct\n\nThis project adheres to a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in\nthis project and its community, you are expected to uphold this code.\n\n\n\n## Contributing\n\nPlease see [CONTRIBUTING](CONTRIBUTING.md) for details.\n\n\n\n## License\n\n**icanboogie/accessor** is released under the [BSD-3-Clause](LICENSE).\n\n\n\n\n[documentation]:       https://icanboogie.org/api/accessor/master/\n[AccessorCamelTrait]:  https://icanboogie.org/api/accessor/master/class-ICanBoogie.Accessor.AccessorCamelTrait.html\n[AccessorTrait]:       https://icanboogie.org/api/accessor/master/class-ICanBoogie.Accessor.AccessorTrait.html\n[FormatAsCamel]:       https://icanboogie.org/api/accessor/master/class-ICanBoogie.Accessor.FormatAsCamel.html\n[HasAccessor]:         https://icanboogie.org/api/accessor/master/class-ICanBoogie.Accessor.HasAccessor.html\n[PropertyNotWritable]: https://icanboogie.org/api/common/1.2/class-ICanBoogie.PropertyNotWritable.html\n[PropertyNotReadable]: https://icanboogie.org/api/common/1.2/class-ICanBoogie.PropertyNotReadable.html\n[ICanBoogie]:          https://icanboogie.org\n[CamelCase]:           http://en.wikipedia.org/wiki/CamelCase\n[Snake case]:          http://en.wikipedia.org/wiki/Snake_case\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FICanBoogie%2FAccessor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FICanBoogie%2FAccessor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FICanBoogie%2FAccessor/lists"}