{"id":17191662,"url":"https://github.com/fab2s/enumerate","last_synced_at":"2026-02-22T06:32:32.229Z","repository":{"id":248587692,"uuid":"829157140","full_name":"fab2s/Enumerate","owner":"fab2s","description":"Enumerate, a nice boost to your enums","archived":false,"fork":false,"pushed_at":"2025-03-16T14:48:52.000Z","size":27,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-11T15:20:22.188Z","etag":null,"topics":[],"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/fab2s.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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,"zenodo":null}},"created_at":"2024-07-15T21:55:59.000Z","updated_at":"2025-03-16T14:48:56.000Z","dependencies_parsed_at":"2025-03-16T16:00:55.041Z","dependency_job_id":null,"html_url":"https://github.com/fab2s/Enumerate","commit_stats":null,"previous_names":["fab2s/enumerated","fab2s/enumerable","fab2s/enumerate"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/fab2s/Enumerate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fab2s%2FEnumerate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fab2s%2FEnumerate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fab2s%2FEnumerate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fab2s%2FEnumerate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fab2s","download_url":"https://codeload.github.com/fab2s/Enumerate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fab2s%2FEnumerate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29706562,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-22T05:59:28.568Z","status":"ssl_error","status_checked_at":"2026-02-22T05:58:46.208Z","response_time":110,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2024-10-15T01:27:01.335Z","updated_at":"2026-02-22T06:32:32.199Z","avatar_url":"https://github.com/fab2s.png","language":"PHP","readme":"# Enumerate\n[![QA](https://github.com/fab2s/Enumerate/actions/workflows/qa.yml/badge.svg)](https://github.com/fab2s/Enumerate/actions/workflows/qa.yml) [![CI](https://github.com/fab2s/Enumerate/actions/workflows/ci.yml/badge.svg)](https://github.com/fab2s/Enumerate/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/fab2s/Enumerate/graph/badge.svg?token=M4PZ6Z6MqU)](https://codecov.io/gh/fab2s/Enumerate) [![Packagist Version](https://img.shields.io/packagist/v/fab2s/enumerate)](https://packagist.org/packages/fab2s/Enumerate)\n [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com) [![License](http://poser.pugx.org/fab2s/Enumerate/license)](https://packagist.org/packages/fab2s/Enumerate)\n\n`Enumerate` gives a nice boost to your [native enums](https://www.php.net/manual/en/language.types.enumerations.php).\n\n## Why ?\n\n`PHP Enums` are a wonderful addition to PHP, but they miss few things to make them fully practical in real world. If you dig, you will find that most of the current limitations seems to deal more with ideology than pragmatism.\n\nAs a software craftsmanship in spirit, I think that a feature like that should carry more complexity internally to bring more simplicity externally. \n\nFor example, I just don't understand why `enums` are not, and worst, cannot be `stringable`. It's ok if you think that an `Unitenum` should not be `stringable`, or if you would be hurt to think that it would be treason to even imagine to `(string)` an `IntBackedEnum`, but these are all `SHOULDs` that just limit the way we can use them in real life situations when they become a `MUST`.\n\nIn practice, even `string` casting an `IntBackedEnum` could make sens in `HTTP` context where string is the only type, or when writing in a database. Yes these would be shortcuts, but why should we be forced to follow every detour ? \n\nOf course, I would prefer an interface that would allow us to `scalar` cast objects and end up with an `int` or `string` for enums, but this is a dream :-)\n\nUnfortunately, we currently cannot have the freedom to embed such behaviors in our enums.\n\nI am all in for strict types, but I find it cumbersome to have to write code just to be able to read an HTTP request or write in a database while it could all have been implemented once and for all in a way that would work perfectly fine.\n\nAnother thing that I don't understand is why can't we extend enums. There are so many practical cases where we would love to stay `DRY` in the way we can for example describe `roles` or even `types` in our applications. That in itself is an example where the engine could handle some added complexity, that is to inspect inheritance to maintain consistency and disallow case values to be updated, in order to provide with dryness and more _freedom_ to be creative.\n\nAnyway, `enums` are great, and this package aims at making them just a little more usable.\n\nIt provides with a unified way to instantiate and use them.\n\nThis package is implemented as a static helper class `Enumerate`, that can be used separately to manipulate any enum in a standardised way (instantiation, json serialization ...), a trait, `EnumerateTrait`, that can be used in your enum to make them easier to deal with and an interface, `EnumerateInterface` that extends `JsonSerializable` and can be useful to `instanceof` your `enums`.\n\n## Installation\n\n`Enumerate` can be installed using composer:\n\n```shell\ncomposer require \"fab2s/enumerate\"\n```\n\n## Usage\n\nTo boost **any** PHP enum, use the `EnumerateTrait` trait in your enums:\n\n```php\nuse fab2s\\Enumerate\\EnumerateTrait;\n\nenum MyEnum // :string or : int or nothing\n{\n    use EnumerateTrait;\n    // ...\n```\n\n`Enumerate` implements `jsonSerialize()` through `EnumerateInterface`, but, and this is another questionable matter with `PHP Traits`, you will have to declare that your `enum` implements the `JsonSerializable` or `EnumerateInterface` interface as traits currently cannot:\n\n```php\nuse fab2s\\Enumerate\\EnumerateTrait;\nuse fab2s\\Enumerate\\EnumerateInterface;\n\nenum MyEnum /* :string or : int or nothing */ implements EnumerateInterface // or JsonSerializable\n{\n    use EnumerateTrait;\n    // ...\n```\n\n## So what ?\n\nFrom there your enums benefits from most `Enumerate` helper methods, to the exception to the type resolution methods.\n\n### `BackedEnum::tryFrom`\n\nCurrent state is that both `IntBackedEnum` and `StringBackedEnum` are `BackedEnum`, but they don't agree on the types we are allowed to try.\n\nThis result in a situation where trying is actually only allowed on a single type (`string` or `int`) where the whole concept of trying seems a lot broader in itself. I mean, why shouldn't we be able to try `null` or even an enum `instance` ? \n\nThere is nothing wrong with _trying_ as long as the result is consistent. In practice this will often result in having to implement more checks before we can even try anything.\n\n`Enumerate` solves this by adding the `tryFromAny` method:\n\n```php\n// in EnumerateTrait /  EnumerateInterface\n    /**\n     * @throws ReflectionException\n     */\n    public static function tryFromAny(int|string|UnitEnum|null $value, bool $strict = true): ?static\n\n// in Enumerate\n    /**\n     * @param UnitEnum|class-string\u003cUnitEnum|BackedEnum\u003e $enum\n     *\n     * @throws ReflectionException\n     */\n    public static function tryFromAny(UnitEnum|string $enum, int|string|UnitEnum|null $value, bool $strict = true): UnitEnum|BackedEnum|null\n```\n\nSo now you can try for more types in a way that is just more practical without breaking the consistency of the answer.\n\nTrying a `null` will be `null`, trying an `instance` will give the `instance` itself when it makes sens, trying an `int` on a `StringBackedEnum` will be `null` and so will a string on an `IntBackedEnum`. Doing this does not break anything, it just handles internally the complexity you would have to otherwise handle externally.\n\nNothing fancy, just usability.\n\n`Enumerate` can go a little further if you decide to drop _strictness_ (as in, you are _free_ to do it or not):\n```php\n// will always be null\n$result = MyEnum::tryFromAny(AnotherEnumWithOverlappingCases::someCase); \n// same as \n$result = Enumerate::tryFromAny(MyEnum::class, AnotherEnumWithOverlappingCases::someCase); \n\n// can return MyEnum::someCase if the case exist in MyEnum\n// matched by value or case name for Unitenum's\n$result = MyEnum::tryFromAny(AnotherEnumWithOverlappingCases::someCase, false);\n// same as, works with enum FQN and instances\n$result = Enumerate::tryFromAny(MyEnum::anyCase, AnotherEnumWithOverlappingCases::someCase, false);\n\n```\n\n### `BackedEnum::from`\n\nLikewise, `Enumerate` provides with `fromAny` that do the same as the native `from` method but throws an `InvalidArgumentException` instead of returning `null` when no instance can be created from input.\n\nLike with `tryFromAny`, you can reduce _strictness_:\n```php\n// throws an InvalidArgumentException if someCase is not present in MyEnum\n// either by value for BackedEnum or case name for Unitenum\n$result = MyEnum::fromAny('someCase');\n// same as\n$result = Enumerate::fromAny(MyEnum::class, 'someCase');\n\n\n// can return MyEnum::someCase if the case exist in MyEnum\n$result = MyEnum::fromAny(AnotherEnumWithOverlappingCases::someCase, false);\n// same as\n$result = Enumerate::fromAny(MyEnum::class, AnotherEnumWithOverlappingCases::someCase, false);\n\n```\n\n### _Merely_ `Stringable`\n\n`Enumerate` adds a `toValue` method being the closest you can get to `stringable` so far. \nTypes are respected, to `toValue` will return:\n    - the int value for `IntBackedEnum` \n    - the string value for `StringBackedEnum`\n    - the string case name for `Unitenum`\n\nAnd all these value are valid input to create an instance using `tryFromAny` / `fromAny`.\n\n```php\n// either someCase name for UnitEnum or someCase value for BackedEnum\n$result = MyEnum::someCase-\u003etoValue();\n// same as\n$result = Enumerate::toValue(MyEnum::someCase);\n\n```\n\n### `UnitEnum`\n\n`Enumerate` treats `UnitEnum` as any `Enum`, using a match by _case name_ logic instead of the default and builtin _case value_ matching.\n\nThis is done internally using the `fromName` / `tryFromName` methods in a way that completely unifies enum types.\n\nDoing so is of course a bit slower than value matching as we have to iterate through cases, but it could come handy in case where `UnitEnum` are present in existing code.\n\nNo need to say that doing this makes it possible to store and transfer `UnitEnum` with ease.\n\n```php\nuse fab2s\\Enumerate\\Enumerate;\nuse fab2s\\Enumerate\\EnumerateTrait;\nuse fab2s\\Enumerate\\EnumerateInterface;\n\nenum SomeUnitEnum implements EnumerateInterface\n{\n    use EnumerateTrait;\n\n    case ONE;\n    case TWO;\n    case three;\n}\n\nSomeUnitEnum::tryFromName('ONE'); // SomeUnitEnum::ONE\n// same as\nEnumerate::tryFromName(SomeUnitEnum::class, 'ONE'); // SomeUnitEnum::ONE\n\n// the toValue method is the nearest we can get to stringable\nSomeUnitEnum::ONE-\u003etovalue(); // \"ONE\"\n// same as\nSomeUnitEnum::ONE-\u003ejsonSerialize(); // \"ONE\"\n// same as, except it will take nulls in\nEnumerate::toValue(SomeUnitEnum::ONE); // \"ONE\"\n// same as \njson_encode(SomeUnitEnum::ONE); // \"ONE\"\n\n\nSomeUnitEnum::fromAny('TWO'); // SomeUnitEnum::TWO\nSomeUnitEnum::tryFromAny('TWO'); // SomeUnitEnum::TWO\nSomeUnitEnum::tryFromAny(UnitEnum::TWO); // SomeUnitEnum::TWO\n```\n\n### `BackedEnum`\n\n`IntBackedEnum` and `StringBackedEnum` works the same.\n\n````php\nuse fab2s\\Enumerate\\EnumerateTrait;\n\nenum SomeIntBackedEnum: int implements JsonSerializable\n{\n    use EnumerateTrait;\n\n    case ONE   = 1;\n    case TWO   = 2;\n    case three = 3;\n}\n\nSomeIntBackedEnum::tryFromAny(1); // SomeIntBackedEnum::ONE\nSomeIntBackedEnum::tryFromAny('1'); // null\nSomeIntBackedEnum::tryFromAny('ONE'); // null\nSomeIntBackedEnum::tryFromName('ONE'); // SomeIntBackedEnum::ONE\n````\n\n### Comparing enums\n\n`Enumerate` comes with two methods to assert if some input matches a case: `equals` and `compares`.\n\nThe `equals` methods is strict and will only return true if at least one of the argument can be turned into an enum case while `compares` will do the same even if the only match comes from a compatible `enum` instance (read another `enum` that overlaps one of the current `enum` case).\n\nAgain, `UnitEnum` are matched by `case name` which seams perfectly reasonable.\n\n```php\n// true when someCaseValue is the value of someCase for BackedEnum\n// false for UnitEnum, would be true with someCase\nMyEnum::someCase-\u003eequals('someCaseValue'); \n// same as \nEnumerate::equals(MyEnum::someCase, 'someCaseValue')\n// always true\nMyEnum::someCase-\u003eequals(MyEnum::someCase, null, 'whatever' /*, ...*/); \n// same as \nEnumerate::equals(MyEnum::someCase, MyEnum::someCase, null, 'whatever' /*, ...*/)\n\n// true if we have an equality by value for BackedEnum\n// or by name for UnitEnum\nMyEnum:::someCase-\u003ecompares(AnotherEnumWithOverlappingCases::someCase); \n// same as \nEnumerate::compares(MyEnum:::someCase, AnotherEnumWithOverlappingCases::someCase); \n```\n\n## Requirements\n\n`Enumerate` is tested against php 8.1, 8.2, 8.3 and 8.4\n\n## Contributing\n\nContributions are welcome, do not hesitate to open issues and submit pull requests.\n\n## License\n\n`Enumerate` is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffab2s%2Fenumerate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffab2s%2Fenumerate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffab2s%2Fenumerate/lists"}