ServiceMethodFactory::applyConverters()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 28

Duplication

Lines 22
Ratio 78.57 %

Code Coverage

Tests 14
CRAP Score 3

Importance

Changes 0
Metric Value
dl 22
loc 28
ccs 14
cts 14
cp 1
rs 9.472
c 0
b 0
f 0
cc 3
nc 4
nop 2
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\ServiceMethod;
10
11
use LogicException;
12
use Psr\Http\Message\StreamInterface;
13
use ReflectionMethod;
14
use Tebru\AnnotationReader\AbstractAnnotation;
15
use Tebru\AnnotationReader\AnnotationCollection;
16
use Tebru\AnnotationReader\AnnotationReaderAdapter;
17
use Tebru\PhpType\TypeToken;
18
use Tebru\Retrofit\Annotation as Annot;
19
use Tebru\Retrofit\CallAdapter;
20
use Tebru\Retrofit\Converter;
21
use Tebru\Retrofit\Internal\AnnotationProcessor;
22
use Tebru\Retrofit\Internal\CallAdapter\CallAdapterProvider;
23
use Tebru\Retrofit\Internal\Converter\ConverterProvider;
24
use Tebru\Retrofit\ServiceMethodBuilder;
25
26
/**
27
 * Class ServiceMethodFactory
28
 *
29
 * Creates and sets up values for [@see ServiceMethodBuilder], then delegates final setup
30
 * to annotation handles.
31
 *
32
 * @author Nate Brunette <[email protected]>
33
 */
34
final class ServiceMethodFactory
35
{
36
    /**
37
     * Handles an [@see AbstractAnnotation]
38
     *
39
     * @var AnnotationProcessor
40
     */
41
    private $annotationProcessor;
42
43
    /**
44
     * Fetches a [@see CallAdapter]
45
     *
46
     * @var CallAdapterProvider
47
     */
48
    private $callAdapterProvider;
49
50
    /**
51
     * Fetches a [@see Converter]
52
     *
53
     * @var ConverterProvider
54
     */
55
    private $converterProvider;
56
57
    /**
58
     * Reads annotations from service interface
59
     *
60
     * @var AnnotationReaderAdapter
61
     */
62
    private $annotationReader;
63
64
    /**
65
     * The request base url
66
     *
67
     * @var string
68
     */
69
    private $baseUrl;
70
71
    /**
72
     * Constructor
73
     *
74
     * @param AnnotationProcessor $annotationProcessor
75
     * @param CallAdapterProvider $callAdapterProvider
76
     * @param ConverterProvider $converterProvider
77
     * @param AnnotationReaderAdapter $annotationReader
78
     * @param string $baseUrl
79
     */
80 30
    public function __construct(
81
        AnnotationProcessor $annotationProcessor,
82
        CallAdapterProvider $callAdapterProvider,
83
        ConverterProvider $converterProvider,
84
        AnnotationReaderAdapter $annotationReader,
85
        string $baseUrl
86
    ) {
87 30
        $this->annotationProcessor = $annotationProcessor;
88 30
        $this->callAdapterProvider = $callAdapterProvider;
89 30
        $this->converterProvider = $converterProvider;
90 30
        $this->annotationReader = $annotationReader;
91 30
        $this->baseUrl = $baseUrl;
92 30
    }
93
94
    /**
95
     * Creates a [@see DefaultServiceMethod]
96
     *
97
     * @param string $interfaceName
98
     * @param string $methodName
99
     * @return DefaultServiceMethod
100
     * @throws \LogicException
101
     */
102 20
    public function create(string $interfaceName, string $methodName): DefaultServiceMethod
103
    {
104 20
        $serviceMethodBuilder = new DefaultServiceMethodBuilder();
105 20
        $annotations = $this->annotationReader->readMethod($methodName, $interfaceName, true, true);
106
107 19
        $reflectionMethod = new ReflectionMethod($interfaceName, $methodName);
108 19
        $returnType = $reflectionMethod->getReturnType();
109 19
        if ($returnType === null) {
110 1
            throw new LogicException(sprintf(
111 1
                'Retrofit: All service methods must contain a return type. None found for %s::%s()',
112 1
                $reflectionMethod->getDeclaringClass()->name,
113 1
                $reflectionMethod->name
114
            ));
115
        }
116
117
        /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
118 18
        $returnTypeToken = new TypeToken($returnType->getName());
119
120 18
        $serviceMethodBuilder->setBaseUrl($this->baseUrl);
121 18
        $serviceMethodBuilder->setCallAdapter($this->callAdapterProvider->get($returnTypeToken));
122
123 18
        foreach ($annotations as $annotationArray) {
124 18
            if (!\is_array($annotationArray)) {
125 18
                $annotationArray = [$annotationArray];
126
            }
127
128 18
            foreach ($annotationArray as $annotation) {
129
                try {
130 18
                    $this->annotationProcessor->process(
131 18
                        $annotation,
132 18
                        $serviceMethodBuilder,
133 18
                        $this->converterProvider,
134 18
                        $reflectionMethod
135
                    );
136 1
                } catch (LogicException $exception) {
137 1
                    throw new LogicException(
138 1
                        $exception->getMessage() .
139 1
                        sprintf(
140 1
                            ' for %s::%s()',
141 1
                            $reflectionMethod->getDeclaringClass()->name,
142 18
                            $reflectionMethod->name
143
                        )
144
                    );
145
                }
146
            }
147
        }
148
149 17
        $this->applyConverters($annotations, $serviceMethodBuilder);
150
151 17
        return $serviceMethodBuilder->build();
152
    }
153
154
    /**
155
     * @param AnnotationCollection $annotations
156
     * @param DefaultServiceMethodBuilder $builder
157
     * @throws \LogicException
158
     */
159 17
    private function applyConverters(AnnotationCollection $annotations, DefaultServiceMethodBuilder $builder): void
160
    {
161 17
        $responseBody = $annotations->get(Annot\ResponseBody::class);
162 17 View Code Duplication
        if ($responseBody !== null) {
163
            /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
164 2
            $builder->setResponseBodyConverter(
165 2
                $this->converterProvider->getResponseBodyConverter(new TypeToken($responseBody->getValue()))
166
            );
167
        } else {
168
            /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
169 15
            $builder->setResponseBodyConverter(
170 15
                $this->converterProvider->getResponseBodyConverter(new TypeToken(StreamInterface::class))
171
            );
172
        }
173
174 17
        $errorBody = $annotations->get(Annot\ErrorBody::class);
175 17 View Code Duplication
        if ($errorBody !== null) {
176
            /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
177 1
            $builder->setErrorBodyConverter(
178 1
                $this->converterProvider->getResponseBodyConverter(new TypeToken($errorBody->getValue()))
179
            );
180
        } else {
181
            /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
182 16
            $builder->setErrorBodyConverter(
183 16
                $this->converterProvider->getResponseBodyConverter(new TypeToken(StreamInterface::class))
184
            );
185
        }
186 17
    }
187
}
188