Passed
Push — main ( c1e16b...1fa233 )
by Breno
01:45
created

ExtractorUsingAttribute::attributesAvailable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace OniBus\Handler\ClassMethod\Extractor;
5
6
use OniBus\Handler\ClassMethod\ClassMethod;
7
use OniBus\Handler\ClassMethod\ClassMethodExtractor;
8
use ReflectionAttribute;
9
use ReflectionClass;
10
use ReflectionException;
11
use ReflectionFunctionAbstract;
12
use ReflectionMethod;
13
use OniBus\Attributes\Handler;
14
use RuntimeException;
15
use function Sodium\version_string;
16
17
class ExtractorUsingAttribute implements ClassMethodExtractor
18
{
19
    /**
20
     * @var string
21
     */
22
    protected $attribute;
23
24
    /**
25
     * @var array
26
     */
27
    protected $handlersFQCN;
28
29
    public function __construct(string $attribute, array $handlersFQCN = [])
30
    {
31
        $this->assertAttributesAvailable();
32
        $this->attribute = $attribute;
33
        $this->handlersFQCN = $handlersFQCN;
34
    }
35
36
    /**
37
     * @inheritDoc
38
     */
39
    public function extractClassMethods(): array
40
    {
41
        $mapped = [];
42
        foreach ($this->handlersFQCN as $class) {
43
            $mapped = array_merge($mapped, $this->getHandlers($class));
44
        }
45
46
        return $mapped;
47
    }
48
49
    /**
50
     * @param  string $class
51
     * @return ClassMethod[]
52
     * @throws ReflectionException
53
     */
54
    protected function getHandlers(string $class): array
55
    {
56
        $handlers = [];
57
        if (!class_exists($class)) {
58
            return $handlers;
59
        }
60
61
        $reflectionClass = new ReflectionClass($class);
62
        foreach ($reflectionClass->getMethods() as $method) {
63
            $attribute = $this->extractHandlerAttribute($method);
64
            if (empty($attribute)) {
65
                continue;
66
            }
67
68
            $handlers[] = new ClassMethod($this->extractMessage($method, $attribute), $class, $method->getName());
69
        }
70
71
        return $handlers;
72
    }
73
74
    protected function extractHandlerAttribute(ReflectionFunctionAbstract $method): ?Handler
75
    {
76
        $attribute = $method->getAttributes($this->attribute, ReflectionAttribute::IS_INSTANCEOF)[0] ?? null;
77
        if ($attribute instanceof ReflectionAttribute) {
78
            $attr =  $attribute->newInstance();
79
            assert($attr instanceof Handler);
80
            return $attr;
81
        }
82
83
        return null;
84
    }
85
86
    protected function extractMessage(ReflectionMethod $method, Handler $attribute): string
87
    {
88
        $hasParameters = (bool) $method->getNumberOfParameters();
89
        if (empty($attribute->getMessage()) && !$hasParameters) {
90
            throw new RuntimeException(
91
                sprintf(
92
                    "Invalid Handler: No 'Message' has been set in %s::%s",
93
                    $method->getDeclaringClass()->getName(),
94
                    $method->getName()
95
                )
96
            );
97
        }
98
99
        return $attribute->getMessage() ?? (string) $method->getParameters()[0]->getType();
100
    }
101
102
    public function attributesAvailable(): bool
103
    {
104
        return PHP_VERSION_ID >= 80000;
105
    }
106
107
    public function assertAttributesAvailable():void
108
    {
109
        if (!$this->attributesAvailable()) {
110
            throw new RuntimeException(
111
                sprintf("Attributes are not available for your PHP version. Use PHP version >= 8.0")
112
            );
113
        }
114
    }
115
}
116