ExtractorUsingAttribute::attributesAvailable()   A
last analyzed

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