Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
90.48% covered (success)
90.48%
76 / 84
82.61% covered (success)
82.61%
19 / 23
CRAP
0.00% covered (danger)
0.00%
0 / 1
EnumGenerator
90.48% covered (success)
90.48%
76 / 84
82.61% covered (success)
82.61%
19 / 23
56.52
0.00% covered (danger)
0.00%
0 / 1
 __construct
77.78% covered (success)
77.78%
7 / 9
0.00% covered (danger)
0.00%
0 / 1
5.27
 setBackingType
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getBackingType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasBackingType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addInterface
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addInterfaces
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getInterfaces
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasInterfaces
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 hasInterface
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 removeInterface
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 addCases
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 addCase
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getCase
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 hasCase
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 hasCases
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCases
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 removeCase
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 addMethod
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 render
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
15
 formatCases
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 formatConstants
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 formatMethods
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 __toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Code\Generator;
16
17/**
18 * Enum generator class
19 *
20 * @category   Pop
21 * @package    Pop\Code
22 * @author     Nick Sagona, III <dev@noladev.com>
23 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
24 * @license    https://www.popphp.org/license     New BSD License
25 * @version    6.0.0
26 */
27class EnumGenerator extends AbstractClassGenerator
28{
29
30    use Traits\UseTrait;
31
32    /**
33     * Backing type (null for a pure enum, otherwise 'int' or 'string')
34     * @var ?string
35     */
36    protected ?string $backingType = null;
37
38    /**
39     * Interfaces that are implemented
40     * @var array
41     */
42    protected array $interfaces = [];
43
44    /**
45     * Array of enum case generator objects
46     * @var array
47     */
48    protected array $cases = [];
49
50    /**
51     * Constructor
52     *
53     * Instantiate the enum generator object
54     *
55     * @param  string  $name
56     * @param  ?string $backingType
57     * @param  mixed   $interface
58     */
59    public function __construct(string $name, ?string $backingType = null, mixed $interface = null)
60    {
61        $this->setName($name);
62
63        if ($backingType !== null) {
64            $this->setBackingType($backingType);
65        }
66
67        if ($interface !== null) {
68            if (is_array($interface)) {
69                $this->addInterfaces($interface);
70            } else if (str_contains($interface, ',')) {
71                $this->addInterfaces(array_map('trim', explode(',', $interface)));
72            } else {
73                $this->addInterface($interface);
74            }
75        }
76    }
77
78    /**
79     * Set the backing type
80     *
81     * @param  ?string $backingType
82     * @return EnumGenerator
83     */
84    public function setBackingType(?string $backingType = null): EnumGenerator
85    {
86        $this->backingType = $backingType;
87        return $this;
88    }
89
90    /**
91     * Get the backing type
92     *
93     * @return string|null
94     */
95    public function getBackingType(): string|null
96    {
97        return $this->backingType;
98    }
99
100    /**
101     * Has backing type
102     *
103     * @return bool
104     */
105    public function hasBackingType(): bool
106    {
107        return ($this->backingType !== null);
108    }
109
110    /**
111     * Add an interface
112     *
113     * @param  string $interface
114     * @return EnumGenerator
115     */
116    public function addInterface(string $interface): EnumGenerator
117    {
118        if (!in_array($interface, $this->interfaces)) {
119            $this->interfaces[] = $interface;
120        }
121
122        return $this;
123    }
124
125    /**
126     * Add interfaces
127     *
128     * @param  array $interfaces
129     * @return EnumGenerator
130     */
131    public function addInterfaces(array $interfaces): EnumGenerator
132    {
133        foreach ($interfaces as $interface) {
134            $this->addInterface($interface);
135        }
136
137        return $this;
138    }
139
140    /**
141     * Get the interfaces
142     *
143     * @return array
144     */
145    public function getInterfaces(): array
146    {
147        return $this->interfaces;
148    }
149
150    /**
151     * Has interfaces
152     *
153     * @return bool
154     */
155    public function hasInterfaces(): bool
156    {
157        return (!empty($this->interfaces));
158    }
159
160    /**
161     * Has interface
162     *
163     * @param  string $interface
164     * @return bool
165     */
166    public function hasInterface(string $interface): bool
167    {
168        return (in_array($interface, $this->interfaces));
169    }
170
171    /**
172     * Remove interface
173     *
174     * @param  string $interface
175     * @return EnumGenerator
176     */
177    public function removeInterface(string $interface): EnumGenerator
178    {
179        if (in_array($interface, $this->interfaces)) {
180            $key = array_search($interface, $this->interfaces);
181            unset($this->interfaces[$key]);
182        }
183
184        return $this;
185    }
186
187    /**
188     * Add cases
189     *
190     * @param  array $cases
191     * @return EnumGenerator
192     */
193    public function addCases(array $cases): EnumGenerator
194    {
195        foreach ($cases as $case) {
196            $this->addCase($case);
197        }
198        return $this;
199    }
200
201    /**
202     * Add a case
203     *
204     * @param  EnumCaseGenerator $case
205     * @return EnumGenerator
206     */
207    public function addCase(EnumCaseGenerator $case): EnumGenerator
208    {
209        $this->cases[$case->getName()] = $case;
210        return $this;
211    }
212
213    /**
214     * Get a case
215     *
216     * @param  mixed $case
217     * @return EnumCaseGenerator|null
218     */
219    public function getCase(mixed $case): EnumCaseGenerator|null
220    {
221        $c = ($case instanceof EnumCaseGenerator) ? $case->getName() : $case;
222        return (isset($this->cases[$c])) ? $this->cases[$c] : null;
223    }
224
225    /**
226     * Has a case
227     *
228     * @param  mixed $case
229     * @return bool
230     */
231    public function hasCase(mixed $case): bool
232    {
233        $c = ($case instanceof EnumCaseGenerator) ? $case->getName() : $case;
234        return (isset($this->cases[$c]));
235    }
236
237    /**
238     * Has cases
239     *
240     * @return bool
241     */
242    public function hasCases(): bool
243    {
244        return (!empty($this->cases));
245    }
246
247    /**
248     * Get all cases
249     *
250     * @return array
251     */
252    public function getCases(): array
253    {
254        return $this->cases;
255    }
256
257    /**
258     * Remove a case
259     *
260     * @param  mixed $case
261     * @return EnumGenerator
262     */
263    public function removeCase(mixed $case): EnumGenerator
264    {
265        $c = ($case instanceof EnumCaseGenerator) ? $case->getName() : $case;
266        if (isset($this->cases[$c])) {
267            unset($this->cases[$c]);
268        }
269        return $this;
270    }
271
272    /**
273     * Add a method (overridden to reject a constructor, which enums cannot declare)
274     *
275     * @param  MethodGenerator $method
276     * @throws Exception
277     * @return EnumGenerator
278     */
279    public function addMethod(MethodGenerator $method): EnumGenerator
280    {
281        if (strtolower($method->getName()) === '__construct') {
282            throw new Exception('Error: Enums cannot declare a constructor.');
283        }
284        parent::addMethod($method);
285        return $this;
286    }
287
288    /**
289     * Render enum
290     *
291     * @throws Exception
292     * @return string
293     */
294    public function render(): string
295    {
296        foreach ($this->cases as $case) {
297            if ($this->hasBackingType() && !$case->hasValue()) {
298                throw new Exception("Error: Case '" . $case->getName() . "' of a backed enum must have a value.");
299            }
300            if (!$this->hasBackingType() && $case->hasValue()) {
301                throw new Exception("Error: Case '" . $case->getName() . "' of a non-backed enum must not have a value.");
302            }
303        }
304
305        $this->output  = ($this->namespace !== null) ? $this->namespace->render() . PHP_EOL : null;
306        $this->output .= ($this->docblock !== null) ? $this->docblock->render() : null;
307        $this->output .= $this->formatAttributes(false);
308        $this->output .= 'enum ' . $this->name;
309
310        if ($this->backingType !== null) {
311            $this->output .= ': ' . $this->backingType;
312        }
313        if (!empty($this->interfaces)) {
314            $this->output .= ' implements ' . implode(', ', $this->interfaces);
315        }
316
317        $this->output .= PHP_EOL . '{' . PHP_EOL;
318
319        if ($this->hasUses()) {
320            $this->output .= PHP_EOL;
321            foreach ($this->uses as $ns => $as) {
322                // Unlike a namespace-level `use Foo\Bar as Baz;` import, a trait-use inside a
323                // class-like body has no whole-trait aliasing syntax -- `use SomeTrait as Alias;`
324                // here is not valid PHP (only per-method conflict resolution, `use A, B { A::m as
325                // n; }`, exists, and this simple $ns => $as map has no way to represent that). Any
326                // alias is therefore intentionally ignored when rendering a trait-use.
327                $this->output .= $this->printIndent() . 'use ' . $ns . ';' . PHP_EOL;
328            }
329        }
330
331        if ($this->hasCases()) {
332            $this->output .= $this->formatCases();
333        }
334        if ($this->hasConstants()) {
335            $this->output .= $this->formatConstants();
336        }
337        if ($this->hasMethods()) {
338            $this->output .= $this->formatMethods();
339        }
340
341        $this->output .= PHP_EOL . '}' . PHP_EOL;
342
343        return $this->output;
344    }
345
346    /**
347     * Format the cases
348     *
349     * @return string
350     */
351    protected function formatCases(): string
352    {
353        $cases = null;
354
355        foreach ($this->cases as $case) {
356            $cases .= $case->render() . PHP_EOL;
357        }
358
359        return $cases;
360    }
361
362    /**
363     * Format the constants
364     *
365     * @return string
366     */
367    protected function formatConstants(): string
368    {
369        $constants = null;
370
371        foreach ($this->constants as $constant) {
372            $constants .= $constant->render() . PHP_EOL;
373        }
374
375        return $constants;
376    }
377
378    /**
379     * Format the methods
380     *
381     * @return string
382     */
383    protected function formatMethods(): string
384    {
385        $methods = null;
386
387        foreach ($this->methods as $method) {
388            $methods .= $method->render() . PHP_EOL;
389        }
390
391        return $methods;
392    }
393
394    /**
395     * Print enum
396     *
397     * @return string
398     */
399    public function __toString(): string
400    {
401        return $this->render();
402    }
403
404}