Passed
Branch master (203e41)
by Nate
01:56
created

ServiceMethodFactory::create()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 50
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 50
ccs 30
cts 30
cp 1
rs 8.6315
c 0
b 0
f 0
cc 6
eloc 31
nc 8
nop 2
crap 6
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 26
    public function __construct(
81
        AnnotationProcessor $annotationProcessor,
82
        CallAdapterProvider $callAdapterProvider,
83
        ConverterProvider $converterProvider,
84
        AnnotationReaderAdapter $annotationReader,
85
        string $baseUrl
86
    ) {
87 26
        $this->annotationProcessor = $annotationProcessor;
88 26
        $this->callAdapterProvider = $callAdapterProvider;
89 26
        $this->converterProvider = $converterProvider;
90 26
        $this->annotationReader = $annotationReader;
91 26
        $this->baseUrl = $baseUrl;
92 26
    }
93
94
    /**
95
     * Creates a [@see DefaultServiceMethod]
96
     *
97
     * @param string $interfaceName
98
     * @param string $methodName
99
     * @return DefaultServiceMethod
100
     * @throws \LogicException
101
     * @throws \ReflectionException
102
     */
103 16
    public function create(string $interfaceName, string $methodName): DefaultServiceMethod
104
    {
105 16
        $serviceMethodBuilder = new DefaultServiceMethodBuilder();
106 16
        $annotations = $this->annotationReader->readMethod($methodName, $interfaceName, true, true);
107
108 15
        $reflectionMethod = new ReflectionMethod($interfaceName, $methodName);
109 15
        $returnType = $reflectionMethod->getReturnType();
110 15
        if ($returnType === null) {
111 1
            throw new LogicException(sprintf(
112 1
                'Retrofit: All service methods must contain a return type. None found for %s::%s()',
113 1
                $reflectionMethod->getDeclaringClass()->getName(),
0 ignored issues
show
introduced by
Consider using $reflectionMethod->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
114 1
                $reflectionMethod->getName()
0 ignored issues
show
Bug introduced by
Consider using $reflectionMethod->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
115
            ));
116
        }
117
118
        /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
119 14
        $returnTypeToken = new TypeToken($returnType->getName());
120
121 14
        $serviceMethodBuilder->setBaseUrl($this->baseUrl);
122 14
        $serviceMethodBuilder->setCallAdapter($this->callAdapterProvider->get($returnTypeToken));
123
124 14
        foreach ($annotations as $annotationArray) {
125 14
            if (!is_array($annotationArray)) {
126 14
                $annotationArray = [$annotationArray];
127
            }
128
129 14
            foreach ($annotationArray as $annotation) {
130
                try {
131 14
                    $this->annotationProcessor->process(
132 14
                        $annotation,
133 14
                        $serviceMethodBuilder,
134 14
                        $this->converterProvider,
135 14
                        $reflectionMethod
136
                    );
137 1
                } catch (LogicException $exception) {
138 1
                    throw new LogicException(
139 1
                        $exception->getMessage() .
140 1
                        sprintf(' for %s::%s()',
141 1
                            $reflectionMethod->getDeclaringClass()->getName(),
0 ignored issues
show
introduced by
Consider using $reflectionMethod->class. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
142 14
                            $reflectionMethod->getName()
0 ignored issues
show
Bug introduced by
Consider using $reflectionMethod->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
143
                        )
144
                    );
145
                }
146
            }
147
        }
148
149 13
        $this->applyConverters($annotations, $serviceMethodBuilder);
150
151 13
        return $serviceMethodBuilder->build();
152
    }
153
154
    /**
155
     * @param AnnotationCollection $annotations
156
     * @param DefaultServiceMethodBuilder $builder
157
     * @throws \LogicException
158
     */
159 13
    private function applyConverters(AnnotationCollection $annotations, DefaultServiceMethodBuilder $builder): void
160
    {
161 13
        $responseBody = $annotations->get(Annot\ResponseBody::class);
162 13 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 11
            $builder->setResponseBodyConverter(
170 11
                $this->converterProvider->getResponseBodyConverter(new TypeToken(StreamInterface::class))
171
            );
172
        }
173
174 13
        $errorBody = $annotations->get(Annot\ErrorBody::class);
175 13 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 12
            $builder->setErrorBodyConverter(
183 12
                $this->converterProvider->getResponseBodyConverter(new TypeToken(StreamInterface::class))
184
            );
185
        }
186 13
    }
187
}
188