Passed
Push — feature/second-release ( 176745...89fc3a )
by Andrea Marco
02:27
created

MethodAnnotations::getIterator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 14
ccs 11
cts 11
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cerbero\LaravelEnum\Services;
6
7
use ArrayIterator;
8
use Cerbero\LaravelEnum\Data\MethodAnnotation;
9
use Cerbero\LaravelEnum\Enums;
10
use Illuminate\Support\Facades\Lang;
11
use IteratorAggregate;
12
use Traversable;
13
14
use function Cerbero\LaravelEnum\commonType;
15
use function Cerbero\LaravelEnum\namespaceExists;
16
17
/**
18
 * The method annotations collector.
19
 *
20
 * @implements IteratorAggregate<string, MethodAnnotation>
21
 */
22
final class MethodAnnotations implements IteratorAggregate
23
{
24
    /**
25
     * The regular expression to extract method annotations already annotated on the enum.
26
     */
27
    public const RE_METHOD = '~@method\s+((?:static)?\s*[^\s]+\s+([^\(]+).*)~';
28
29
    /**
30
     * Instantiate the class.
31
     *
32
     * @param Inspector<\UnitEnum> $inspector
33
     */
34 1
    public function __construct(
35
        private readonly Inspector $inspector,
36
        private readonly bool $force,
37 1
    ) {}
38
39
    /**
40
     * Retrieve the method annotations.
41
     *
42
     * @return ArrayIterator<string, MethodAnnotation>
43
     */
44 1
    public function getIterator(): Traversable
45
    {
46 1
        $annotations = [
47 1
            ...$this->forCaseNames(),
48 1
            ...$this->forMetaAttributes(),
49 1
            ...$this->forTranslations(),
50 1
            ...$this->force ? [] : $this->existing(),
51 1
        ];
52
53 1
        uasort($annotations, function (MethodAnnotation $a, MethodAnnotation $b) {
54 1
            return [$b->isStatic, $a->name] <=> [$a->isStatic, $b->name];
55 1
        });
56
57 1
        return new ArrayIterator($annotations);
58
    }
59
60
    /**
61
     * Retrieve the method annotations for the case names.
62
     *
63
     * @return array<string, MethodAnnotation>
64
     */
65 1
    public function forCaseNames(): array
66
    {
67 1
        $annotations = [];
68 1
        $method = $this->inspector->enumeratesCacheKeys() ? 'forCacheCase' : 'forCase';
69
70 1
        foreach ($this->inspector->cases() as $case) {
71 1
            $annotations[$case->name] = MethodAnnotation::$method($case);
72
        }
73
74
        /** @var array<string, MethodAnnotation> */
75 1
        return $annotations;
76
    }
77
78
    /**
79
     * Retrieve the method annotations for the meta attributes.
80
     *
81
     * @return array<string, MethodAnnotation>
82
     */
83 1
    public function forMetaAttributes(): array
84
    {
85 1
        $annotations = [];
86
87 1
        foreach ($this->inspector->metaAttributeNames() as $name) {
88 1
            $returnTypes = array_map(function (mixed $case) use ($name) {
89 1
                $value = $case->resolveMetaAttribute($name);
90
91 1
                return is_string($value) && namespaceExists($value) ? $value : get_debug_type($value);
92 1
            }, $this->inspector->cases());
93
94 1
            $returnType = commonType(...$returnTypes);
95
96
            /** @var class-string $class */
97 1
            $annotations[$name] = method_exists($class = ltrim($returnType, '?'), '__invoke')
98 1
                ? MethodAnnotation::forInvokable($name, $class)
99 1
                : MethodAnnotation::instance($name, $returnType);
100
        }
101
102 1
        return $annotations;
103
    }
104
105
    /**
106
     * Retrieve the method annotations for the translations.
107
     *
108
     * @return array<string, MethodAnnotation>
109
     */
110 1
    public function forTranslations(): array
111
    {
112 1
        $annotations = [];
113
114 1
        foreach ($this->inspector->cases() as $case) {
115 1
            $key = Enums::resolveTranslationKey($case);
116
117 1
            if ($key === $translations = Lang::get($key)) {
118 1
                continue;
119
            }
120
121
            /** @var array<string, string> $translations */
122 1
            foreach ($translations as $name => $translation) {
123 1
                $annotations[$name] ??= MethodAnnotation::forTranslation($name, $translation);
124
            }
125
        }
126
127 1
        return $annotations;
128
    }
129
130
    /**
131
     * Retrieve the method annotations already annotated on the enum.
132
     *
133
     * @return array<string, MethodAnnotation>
134
     */
135 1
    public function existing(): array
136
    {
137 1
        $annotations = [];
138
139 1
        preg_match_all(self::RE_METHOD, $this->inspector->docBlock(), $matches, PREG_SET_ORDER);
140
141 1
        foreach ($matches as $match) {
142 1
            $annotations[$match[2]] = new MethodAnnotation($match[2], $match[1]);
143
        }
144
145 1
        return $annotations;
146
    }
147
}
148