├── .github
└── workflows
│ └── main.yml
├── .idea
├── EnumHelper.iml
├── encodings.xml
├── modules.xml
├── php.xml
└── vcs.xml
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── composer.json
└── src
├── CasesIndexedByName.php
├── EnumRestorableFromName.php
└── EnumValidatableCase.php
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: main
2 | on: [ push, pull_request ]
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | continue-on-error: ${{ matrix.php == '8.2' }}
7 | strategy:
8 | matrix:
9 | experimental:
10 | - false
11 | php-version:
12 | - '8.1'
13 | - '8.2'
14 |
15 | name: PHP ${{ matrix.php-version }}
16 |
17 | steps:
18 | - name: Checkout
19 | uses: actions/checkout@v2
20 |
21 | - name: Setup PHP, with composer and extensions
22 | uses: shivammathur/setup-php@v2
23 | with:
24 | php-version: ${{ matrix.php-version }}
25 | coverage: none
26 |
27 | - name: Get composer cache directory
28 | id: composer-cache
29 | run: echo "::set-output name=dir::$(composer config cache-files-dir)"
30 |
31 | - name: Cache composer dependencies
32 | uses: actions/cache@v2
33 | with:
34 | path: ${{ steps.composer-cache.outputs.dir }}
35 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
36 | restore-keys: ${{ runner.os }}-composer-
37 |
38 | - name: Delete composer lock file
39 | id: composer-lock
40 | if: ${{ matrix.php-version == '8.1' }}
41 | run: |
42 | echo "::set-output name=flags::--ignore-platform-reqs"
43 |
44 | - name: Install dependencies
45 | run: composer update --no-progress --prefer-dist --optimize-autoloader ${{ steps.composer-lock.outputs.flags }}
46 |
47 | - name: Setup problem matchers for PHP
48 | run: echo "::add-matcher::${{ runner.tool_cache }}/php.json"
49 |
50 | - name: Setup problem matchers for PHPUnit
51 | run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
52 |
53 | - name: "Run PHPUnit tests (Experimental: ${{ matrix.experimental }})"
54 | env:
55 | FAILURE_ACTION: "${{ matrix.experimental == true }}"
56 | run: vendor/bin/phpunit --verbose || $FAILURE_ACTION
57 |
58 | phpcs:
59 | runs-on: ubuntu-latest
60 | steps:
61 | - name: Checkout
62 | uses: actions/checkout@v2
63 |
64 | - name: Setup PHP, with composer and extensions
65 | uses: shivammathur/setup-php@v2
66 | with:
67 | php-version: 8.1
68 | coverage: none
69 | tools: cs2pr
70 |
71 | - name: Get composer cache directory
72 | id: composer-cache
73 | run: echo "::set-output name=dir::$(composer config cache-files-dir)"
74 |
75 | - name: Cache composer dependencies
76 | uses: actions/cache@v2
77 | with:
78 | path: ${{ steps.composer-cache.outputs.dir }}
79 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
80 | restore-keys: ${{ runner.os }}-composer-
81 |
82 | - name: Install dependencies
83 | run: composer install --no-progress --prefer-dist --optimize-autoloader
84 |
85 | - name: Code style with PHP_CodeSniffer
86 | run: ./vendor/bin/phpcs -q --report=checkstyle src/ tests/src/ | cs2pr
87 |
88 | versions:
89 | runs-on: ubuntu-latest
90 | steps:
91 | - name: Checkout
92 | uses: actions/checkout@v2
93 |
94 | - name: Setup PHP, with composer and extensions
95 | uses: shivammathur/setup-php@v2
96 | with:
97 | php-version: 8.1
98 | coverage: none
99 | tools: cs2pr
100 |
101 | - name: Get composer cache directory
102 | id: composer-cache
103 | run: echo "::set-output name=dir::$(composer config cache-files-dir)"
104 |
105 | - name: Cache composer dependencies
106 | uses: actions/cache@v2
107 | with:
108 | path: ${{ steps.composer-cache.outputs.dir }}
109 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
110 | restore-keys: ${{ runner.os }}-composer-
111 |
112 | - name: Install dependencies
113 | run: composer install --no-progress --prefer-dist --optimize-autoloader
114 |
115 | - name: Code Version Compatibility check with PHP_CodeSniffer
116 | run: ./vendor/bin/phpcs -q --report-width=200 --report=summary,full src/ tests/src/ --standard=PHPCompatibility --runtime-set testVersion 8.1-
117 |
118 | coverage:
119 | runs-on: ubuntu-latest
120 | steps:
121 | - name: Checkout
122 | uses: actions/checkout@v2
123 |
124 | - name: Setup PHP, with composer and extensions
125 | uses: shivammathur/setup-php@v2
126 | with:
127 | php-version: 8.1
128 | coverage: pcov
129 |
130 | - name: Get composer cache directory
131 | id: composer-cache
132 | run: echo "::set-output name=dir::$(composer config cache-files-dir)"
133 |
134 | - name: Cache composer dependencies
135 | uses: actions/cache@v2
136 | with:
137 | path: ${{ steps.composer-cache.outputs.dir }}
138 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
139 | restore-keys: ${{ runner.os }}-composer-
140 |
141 | - name: Install dependencies
142 | run: composer install --no-progress --prefer-dist --optimize-autoloader
143 |
144 | - name: Test Coverage
145 | run: ./vendor/bin/phpunit --verbose --coverage-text
146 |
--------------------------------------------------------------------------------
/.idea/EnumHelper.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/php.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to `EnumHelper` will be documented in this file.
4 |
5 | ## 1.0.2 - 2022-01-20
6 |
7 | - Added `CasesIndexedByName` trait, providing a `casesIndexedByName()` method to return an associative array of cases with the case name as the index, rather than an enumerated array that is returned by `cases()`.
8 |
9 | ## 1.0.1 - 2021-12-08
10 |
11 | - Added `EnumValidatableCase` trait, providing an `isValidCase()` method to validate a string value against the set of case names defined for an enum.
12 |
13 | ## 1.0.0 - 2021-12-07
14 |
15 | - Initial release
16 |
17 | Created `EnumValidatableCase` trait, providing a `fromName()` method that allows a string value which matches an enum case to be used to create an instance of that enum.
18 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | =====================
3 |
4 | Copyright © `2017` `Mark Baker`
5 |
6 | Permission is hereby granted, free of charge, to any person
7 | obtaining a copy of this software and associated documentation
8 | files (the “Software”), to deal in the Software without
9 | restriction, including without limitation the rights to use,
10 | copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the
12 | Software is furnished to do so, subject to the following
13 | conditions:
14 |
15 | The above copyright notice and this permission notice shall be
16 | included in all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A library of helper traits for working with PHP 8.1 enums
2 |
3 | [](https://github.com/MarkBaker/EnumHelper/actions)
4 | [](https://packagist.org/packages/markbaker/enumhelper)
5 | [](https://packagist.org/packages/markbaker/enumhelper)
6 | [](https://packagist.org/packages/markbaker/enumhelper)
7 |
8 | This package provides a series of traits that allows you to:
9 | - RestorableFromName Trait
10 |
11 | Create/restore a PHP 8.1 enum from a name string.
12 | - EnumValidatableCase
13 |
14 | Validate an enum name from a name string.
15 | - CasesIndexedByName
16 |
17 | Similar to the standard static `cases()` method, but returns an associative array, indexed by the case names.
18 |
19 | ## Installation
20 |
21 | You can install the package via composer:
22 |
23 | ```bash
24 | composer require markbaker/enumhelper
25 | ```
26 |
27 | ## Usage
28 |
29 | ### RestorableFromName Trait
30 |
31 | In PHP 8.1, it is possible to create a backed enum from a value using the enum's `from()` or `tryFrom()` methods.
32 |
33 | ```php
34 | enum Suit: string {
35 | case Hearts = 'H';
36 | case Diamonds = 'D';
37 | case Clubs = 'C';
38 | case Spades = 'S';
39 | }
40 |
41 | $suit = Suit::Diamonds;
42 |
43 | $value = $suit->value; // Returns 'D'
44 |
45 | $newSuit = Suit::from($value);
46 | ```
47 |
48 | The `EnumHelper\EnumRestorableFromName` trait provided in this library adds a `fromName()` method to any enum where you want to create an enum from its name, rather than from its value.
49 |
50 | ```php
51 | enum Suit: string {
52 | use EnumHelper\EnumRestorableFromName;
53 |
54 | case Hearts = 'H';
55 | case Diamonds = 'D';
56 | case Clubs = 'C';
57 | case Spades = 'S';
58 | }
59 |
60 | $suit = Suit::Diamonds;
61 |
62 | $suitName = $suit->name; // Returns 'Diamonds'
63 |
64 | $newSuit = Suit::fromName($suitName);
65 | ```
66 |
67 | An invalid name will throw an exception. Note that names are case-sensitive.
68 |
69 | This could be useful if you wanted to store the name in a database for readability (particularly appropriate for unbacked enums); then recreate the enum in the model when you load the database record.
70 |
71 | This works with both backed and unbacked enums.
72 |
73 | ### EnumValidatableCase Trait
74 |
75 | Useful to validate if a name has been defined in the case set for an enum:
76 |
77 | ```php
78 | enum Suit: string {
79 | use EnumHelper\EnumValidatableCase;
80 |
81 | case Hearts = 'H';
82 | case Diamonds = 'D';
83 | case Clubs = 'C';
84 | case Spades = 'S';
85 | }
86 |
87 | $suit = Suit::Diamonds;
88 |
89 | $validCaseName = Suit::Hearts;
90 | $isCaseNameValid = Suit::isValidCase($validCaseName); // Returns boolean true
91 |
92 | $invalidCaseName = 'HeArTs';
93 | $isCaseNameValid = Suit::isValidCase($invalidCaseName); // Returns boolean false
94 | ```
95 |
96 | Note that names are case-sensitive.
97 |
98 | This works with both backed and unbacked enums.
99 |
100 | ### CasesIndexedByName Trait
101 |
102 | While PHP 8.1+ Enums already provide a standard static `cases()` method to return a list of all cases defined for that enum:
103 | ```php
104 | enum Suit: string {
105 | use EnumHelper\EnumValidatableCase;
106 |
107 | case Hearts = 'H';
108 | case Diamonds = 'D';
109 | case Clubs = 'C';
110 | case Spades = 'S';
111 | }
112 |
113 | var_dump(Suit::cases());
114 | ```
115 | which returns an enumerated array of the defined cases.
116 | ```
117 | array(4) {
118 | [0]=>
119 | enum(Suit::Hearts)
120 | [1]=>
121 | enum(Suit::Diamonds)
122 | [2]=>
123 | enum(Suit::Clubs)
124 | [3]=>
125 | enum(Suit::Spades)
126 | }
127 | ```
128 | Using the `CasesIndexedByName` Trait and the related `casesIndexedByName()` method
129 |
130 | ```php
131 | enum Suit: string {
132 | use EnumHelper\CasesIndexedByName;
133 |
134 | case Hearts = 'H';
135 | case Diamonds = 'D';
136 | case Clubs = 'C';
137 | case Spades = 'S';
138 | }
139 |
140 | var_dump(Suit::casesIndexedByName());
141 | ```
142 | which will return an associative array of the defined cases, where the array index is the case name.
143 | ```
144 | array(4) {
145 | ["Hearts"]=>
146 | enum(Suit::Hearts)
147 | ["Diamonds"]=>
148 | enum(Suit::Diamonds)
149 | ["Clubs"]=>
150 | enum(Suit::Clubs)
151 | ["Spades"]=>
152 | enum(Suit::Spades)
153 | }
154 | ```
155 | This can be particularly useful if you filter the `cases()` list to return a subset of cases, and don't like the gaps in numeric sequence in the enumerated array.
156 |
157 | ```php
158 | enum Suit: string {
159 | use EnumHelper\CasesIndexedByName;
160 |
161 | public const RED = 'Red';
162 | public const BLACK = 'Black';
163 |
164 | case Hearts = 'H';
165 | case Diamonds = 'D';
166 | case Clubs = 'C';
167 | case Spades = 'S';
168 |
169 | public function color(): string {
170 | return match($this) {
171 | self::Hearts, self::Diamonds => self::RED,
172 | self::Clubs, self::Spades => self::BLACK,
173 | };
174 | }
175 |
176 | public static function red(): array {
177 | return array_filter(
178 | self::casesIndexedByName(),
179 | fn(self $suit) => $suit->color() === self::RED
180 | );
181 | }
182 |
183 | public static function black(): array {
184 | return array_filter(
185 | self::casesIndexedByName(),
186 | fn(self $suit) => $suit->color() === self::BLACK
187 | );
188 | }
189 | }
190 |
191 | var_dump(Suit::black());
192 | ```
193 | will return
194 | ```
195 | array(2) {
196 | ["Clubs"]=>
197 | enum(Suit::Clubs)
198 | ["Spades"]=>
199 | enum(Suit::Spades)
200 | }
201 | ```
202 |
203 | ## Changelog
204 |
205 | Please see the [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
206 |
207 | ## License
208 |
209 | This library is released under the MIT License (MIT). Please see [License File](LICENSE.md) for more information.
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "markbaker/enumhelper",
3 | "description": "A small library that provides Helper Traits for PHP 8.1 Enums",
4 | "keywords": ["enum", "helper", "trait"],
5 | "homepage": "https://github.com/MarkBaker/EnumHelper",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Mark Baker",
10 | "email": "mark@demon-angel.eu"
11 | }
12 | ],
13 | "require": {
14 | "php": "^8.1"
15 | },
16 | "require-dev": {
17 | "squizlabs/php_codesniffer": "^3.4",
18 | "phpunit/phpunit": "^9.5||^10.0",
19 | "phpcompatibility/php-compatibility": "^9.0",
20 | "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0"
21 | },
22 | "autoload": {
23 | "psr-4": {
24 | "EnumHelper\\": "src/"
25 | }
26 | },
27 | "autoload-dev": {
28 | "psr-4": {
29 | "EnumHelper\\TestData\\": "tests/data",
30 | "EnumHelper\\Tests\\": "tests/src"
31 | }
32 | },
33 | "scripts": {
34 | "style": "phpcs --report-width=200 --standard=PSR2 --report=summary,full src/ tests/src/ -n",
35 | "versions": "phpcs --report-width=200 --standard=PHPCompatibility --report=summary,full src/ --runtime-set testVersion 8.1- -n"
36 | },
37 | "config": {
38 | "sort-packages": true
39 | },
40 | "minimum-stability": "dev",
41 | "prefer-stable": true
42 | }
--------------------------------------------------------------------------------
/src/CasesIndexedByName.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | public static function casesIndexedByName()
11 | {
12 | return array_combine(
13 | array_map(
14 | fn(self $case) => $case->name,
15 | self::cases()
16 | ),
17 | self::cases()
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/EnumRestorableFromName.php:
--------------------------------------------------------------------------------
1 | getCase($name);
12 | return $enumReflector->getValue();
13 | } catch (\ReflectionException $e) {
14 | throw new \Exception(sprintf('Undefined enum name %s::%s', self::class, $name));
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/EnumValidatableCase.php:
--------------------------------------------------------------------------------
1 | getCase($name);
12 | } catch (\ReflectionException $e) {
13 | return false;
14 | }
15 |
16 | return true;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------