AnnotationProcessor::process()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 24
ccs 13
cts 13
cp 1
rs 9.536
c 0
b 0
f 0
cc 3
nc 3
nop 4
crap 3
1
<?php
2
/*
3
 * Copyright (c) Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
declare(strict_types=1);
8
9
namespace Tebru\Retrofit\Internal;
10
11
use LogicException;
12
use ReflectionMethod;
13
use Tebru\AnnotationReader\AbstractAnnotation;
14
use Tebru\PhpType\TypeToken;
15
use Tebru\Retrofit\AnnotationHandler;
16
use Tebru\Retrofit\Converter;
17
use Tebru\Retrofit\Internal\Converter\ConverterProvider;
18
use Tebru\Retrofit\Annotation\ParameterAwareAnnotation;
19
use Tebru\Retrofit\RequestBodyConverter;
20
use Tebru\Retrofit\ServiceMethodBuilder;
21
use Tebru\Retrofit\StringConverter;
22
23
/**
24
 * Class AnnotationProcessor
25
 *
26
 * Given an array of handlers, process an [@see AbstractAnnotation]
27
 *
28
 * @author Nate Brunette <[email protected]>
29
 */
30
final class AnnotationProcessor
31
{
32
    /**
33
     * An array of annotation handlers
34
     *
35
     * @var AnnotationHandler[]
36
     */
37
    private $handlers;
38
39
    /**
40
     * Constructor
41
     *
42
     * @param AnnotationHandler[] $handlers
43
     */
44 37
    public function __construct(array $handlers)
45
    {
46 37
        $this->handlers = $handlers;
47 37
    }
48
49
    /**
50
     * Accepts an annotation and delegates to an [@see AnnotationHandler]
51
     *
52
     * @param AbstractAnnotation $annotation
53
     * @param ServiceMethodBuilder $serviceMethodBuilder
54
     * @param ConverterProvider $converterProvider
55
     * @param ReflectionMethod $reflectionMethod
56
     * @throws \LogicException
57
     */
58 25
    public function process(
59
        AbstractAnnotation $annotation,
60
        ServiceMethodBuilder $serviceMethodBuilder,
61
        ConverterProvider $converterProvider,
62
        ReflectionMethod $reflectionMethod
63
    ): void {
64 25
        $name = $annotation->getName();
65
66 25
        if (!isset($this->handlers[$name])) {
67 3
            return;
68
        }
69
70 24
        $handler = $this->handlers[$name];
71 24
        $converter = null;
72 24
        $index = null;
73
74 24
        if ($annotation instanceof ParameterAwareAnnotation) {
75 14
            $index = $this->findMethodParameterIndex($reflectionMethod, $annotation->getVariableName());
76 13
            $type = $this->getParameterType($reflectionMethod, $index);
77 12
            $converter = $this->getConverter($annotation, $converterProvider, $type);
78
        }
79
80 21
        $handler->handle($annotation, $serviceMethodBuilder, $converter, $index);
81 21
    }
82
83
    /**
84
     * Find the position of the method parameter
85
     *
86
     * @param ReflectionMethod $reflectionMethod
87
     * @param string $name
88
     * @return int
89
     * @throws \LogicException
90
     */
91 14
    private function findMethodParameterIndex(ReflectionMethod $reflectionMethod, string $name): int
92
    {
93 14
        $reflectionParameters = $reflectionMethod->getParameters();
94 14
        foreach ($reflectionParameters as $index => $reflectionParameter) {
95 14
            if ($reflectionParameter->name === $name) {
96 14
                return $index;
97
            }
98
        }
99
100 1
        throw new LogicException(sprintf(
101
            'Retrofit: Could not find parameter named %s in %s::%s. Please double check that annotations are properly ' .
102 1
            'referencing method parameters.',
103 1
            $name,
104 1
            $reflectionMethod->getDeclaringClass()->name,
105 1
            $reflectionMethod->name
106
        ));
107
    }
108
109
    /**
110
     * Get the parameter type
111
     *
112
     * @param ReflectionMethod $reflectionMethod
113
     * @param int $index
114
     * @return TypeToken
115
     * @throws \LogicException
116
     */
117 13
    private function getParameterType(ReflectionMethod $reflectionMethod, int $index): TypeToken
118
    {
119 13
        $reflectionParameter = $reflectionMethod->getParameters()[$index];
120 13
        $reflectionType = $reflectionParameter->getType();
121
122 13
        if ($reflectionType === null) {
123 1
            throw new LogicException(sprintf(
124 1
                'Retrofit: Parameter type was not found for method %s::%s',
125 1
                $reflectionMethod->getDeclaringClass()->name,
126 1
                $reflectionMethod->name
127
            ));
128
        }
129
130
        /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
131 12
        return new TypeToken($reflectionType->getName());
132
    }
133
134
    /**
135
     * Get the converter from annotation converter class
136
     *
137
     * @param ParameterAwareAnnotation $annotation
138
     * @param ConverterProvider $converterProvider
139
     * @param TypeToken $type
140
     * @return Converter
141
     * @throws \LogicException
142
     */
143 12
    private function getConverter(
144
        ParameterAwareAnnotation $annotation,
145
        ConverterProvider $converterProvider,
146
        TypeToken $type
147
    ): Converter {
148 12
        switch ($annotation->converterType()) {
149 12
            case RequestBodyConverter::class:
150 4
                return $converterProvider->getRequestBodyConverter($type);
151 8
            case StringConverter::class:
152 7
                return $converterProvider->getStringConverter($type);
153
        }
154
155 1
        throw new LogicException(sprintf(
156 1
            'Retrofit: Unable to handle converter of type %s. Please use RequestBodyConverter or StringConverter',
157 1
            $annotation->converterType()
158
        ));
159
    }
160
}
161