Passed
Push — feature/second-release ( 3d3b5e...855c58 )
by Andrea Marco
14:24
created

MethodAnnotations::forTranslations()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 8
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 18
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
    public function __construct(
35
        private readonly Inspector $inspector,
36
        private readonly bool $force,
37
    ) {}
38
39
    /**
40
     * Retrieve the method annotations.
41
     *
42
     * @return ArrayIterator<string, MethodAnnotation>
43
     */
44
    public function getIterator(): Traversable
45
    {
46
        $annotations = [
47
            ...$this->forCaseNames(),
48
            ...$this->forMetaAttributes(),
49
            ...$this->forTranslations(),
50
            ...$this->force ? [] : $this->existing(),
51
        ];
52
53
        uasort($annotations, function (MethodAnnotation $a, MethodAnnotation $b) {
54
            return [$b->isStatic, $a->name] <=> [$a->isStatic, $b->name];
55
        });
56
57
        return new ArrayIterator($annotations);
58
    }
59
60
    /**
61
     * Retrieve the method annotations for the case names.
62
     *
63
     * @return array<string, MethodAnnotation>
64
     */
65
    public function forCaseNames(): array
66
    {
67
        $annotations = [];
68
        $method = $this->inspector->enumeratesCacheKeys() ? 'forCacheCase' : 'forCase';
69
70
        foreach ($this->inspector->cases() as $case) {
71
            $annotations[$case->name] = MethodAnnotation::$method($case);
72
        }
73
74
        /** @var array<string, MethodAnnotation> */
75
        return $annotations;
76
    }
77
78
    /**
79
     * Retrieve the method annotations for the meta attributes.
80
     *
81
     * @return array<string, MethodAnnotation>
82
     */
83
    public function forMetaAttributes(): array
84
    {
85
        $annotations = [];
86
87
        foreach ($this->inspector->metaAttributeNames() as $name) {
88
            $returnTypes = array_map(function (mixed $case) use ($name) {
89
                $value = $case->resolveMetaAttribute($name);
90
91
                return is_string($value) && namespaceExists($value) ? $value : get_debug_type($value);
92
            }, $this->inspector->cases());
93
94
            $returnType = commonType(...$returnTypes);
95
96
            /** @var class-string $class */
97
            $annotations[$name] = method_exists($class = ltrim($returnType, '?'), '__invoke')
98
                ? MethodAnnotation::forInvokable($name, $class)
99
                : MethodAnnotation::instance($name, $returnType);
100
        }
101
102
        return $annotations;
103
    }
104
105
    /**
106
     * Retrieve the method annotations for the translations.
107
     *
108
     * @return array<string, MethodAnnotation>
109
     */
110
    public function forTranslations(): array
111
    {
112
        $annotations = [];
113
114
        foreach ($this->inspector->cases() as $case) {
115
            $key = Enums::resolveTranslationKey($case);
116
117
            if ($key === $translations = Lang::get($key)) {
118
                continue;
119
            }
120
121
            /** @var array<string, string> $translations */
122
            foreach ($translations as $name => $translation) {
123
                $annotations[$name] ??= MethodAnnotation::forTranslation($name, $translation);
124
            }
125
        }
126
127
        return $annotations;
128
    }
129
130
    /**
131
     * Retrieve the method annotations already annotated on the enum.
132
     *
133
     * @return array<string, MethodAnnotation>
134
     */
135
    public function existing(): array
136
    {
137
        $annotations = [];
138
139
        preg_match_all(self::RE_METHOD, $this->inspector->docBlock(), $matches, PREG_SET_ORDER);
140
141
        foreach ($matches as $match) {
142
            $annotations[$match[2]] = new MethodAnnotation($match[2], $match[1]);
143
        }
144
145
        return $annotations;
146
    }
147
}
148