Completed
Push — master ( 2ac042...4e5e3b )
by Nate
02:40
created

AnnotationProvider::getBodyParts()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 8

Duplication

Lines 16
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 16
loc 16
rs 9.4285
cc 3
eloc 8
nc 3
nop 0
1
<?php
2
/*
3
 * Copyright (c) 2015 Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
namespace Tebru\Retrofit\Generation\Provider;
8
9
use LogicException;
10
use OutOfBoundsException;
11
use ReflectionClass;
12
use Tebru\Dynamo\Collection\AnnotationCollection;
13
use Tebru\Dynamo\Model\MethodModel;
14
use Tebru\Dynamo\Model\ParameterModel;
15
use Tebru\Retrofit\Annotation\BaseUrl;
16
use Tebru\Retrofit\Annotation\Body;
17
use Tebru\Retrofit\Annotation\FormUrlEncoded;
18
use Tebru\Retrofit\Annotation\Header;
19
use Tebru\Retrofit\Annotation\Headers;
20
use Tebru\Retrofit\Annotation\HttpRequest;
21
use Tebru\Retrofit\Annotation\JsonBody;
22
use Tebru\Retrofit\Annotation\Multipart;
23
use Tebru\Retrofit\Annotation\Part;
24
use Tebru\Retrofit\Annotation\Query;
25
use Tebru\Retrofit\Annotation\QueryMap;
26
use Tebru\Retrofit\Annotation\ResponseType;
27
use Tebru\Retrofit\Annotation\Returns;
28
use Tebru\Retrofit\Annotation\Serializer\DeserializationContext;
29
use Tebru\Retrofit\Annotation\Serializer\SerializationContext;
30
use Tebru\Retrofit\Exception\RetrofitException;
31
32
/**
33
 * Class AnnotationProvider
34
 *
35
 * @author Nate Brunette <[email protected]>
36
 */
37
class AnnotationProvider
38
{
39
    /**
40
     * @var AnnotationCollection
41
     */
42
    private $annotations;
43
44
    /**
45
     * @var MethodModel
46
     */
47
    private $methodModel;
48
49
    /**
50
     * @var string
51
     */
52
    private $multipartBoundary;
53
54
    /**
55
     * Constructor
56
     *
57
     * @param AnnotationCollection $annotations
58
     * @param MethodModel $methodModel
59
     */
60
    public function __construct(AnnotationCollection $annotations, MethodModel $methodModel)
61
    {
62
        $this->annotations = $annotations;
63
        $this->methodModel = $methodModel;
64
    }
65
66
    /**
67
     * Get base url
68
     *
69
     * @return null|string
70
     */
71
    public function getBaseUrl()
72
    {
73
        if (!$this->annotations->exists(BaseUrl::NAME)) {
74
            return null;
75
        }
76
77
        /** @var BaseUrl $baseUrlAnnotation */
78
        $baseUrlAnnotation = $this->annotations->get(BaseUrl::NAME);
79
80
        return $baseUrlAnnotation->getVariable();
81
    }
82
83
    /**
84
     * Get request method
85
     *
86
     * @return string
87
     * @throws LogicException
88
     */
89
    public function getRequestMethod()
90
    {
91
        return $this->getRequestAnnotation()->getType();
92
    }
93
94
    /**
95
     * Get request uri
96
     *
97
     * @return string
98
     * @throws LogicException
99
     */
100
    public function getRequestUri()
101
    {
102
        return $this->getRequestAnnotation()->getPath();
103
    }
104
105
    /**
106
     * Get request queries
107
     *
108
     * @return null|array
109
     * @throws LogicException
110
     */
111
    public function getQueries()
112
    {
113
        $queries = $this->getRequestAnnotation()->getQueries();
114
115
        if ($this->annotations->exists(Query::NAME)) {
116
            /** @var Query[] $queryAnnotations */
117
            $queryAnnotations = $this->annotations->get(Query::NAME);
118
119
            /** @var Query $queryAnnotation */
120
            foreach ($queryAnnotations as $queryAnnotation) {
121
                $queries[$queryAnnotation->getRequestKey()] = $queryAnnotation->getVariable();
122
            }
123
124
        }
125
126
        return 0 === count($queries) ? null : $queries;
127
    }
128
129
    /**
130
     * Get query map
131
     *
132
     * @return null|string
133
     */
134
    public function getQueryMap()
135
    {
136
        if (!$this->annotations->exists(QueryMap::NAME)) {
137
            return null;
138
        }
139
140
        /** @var QueryMap $queryMapAnnotation */
141
        $queryMapAnnotation = $this->annotations->get(QueryMap::NAME);
142
143
        return $queryMapAnnotation->getVariable();
144
    }
145
146
    /**
147
     * Get header variables
148
     *
149
     * @return array|null
150
     */
151 View Code Duplication
    public function getHeaders()
152
    {
153
        if (!$this->annotations->exists(Header::NAME)) {
154
            return null;
155
        }
156
157
        /** @var Header[] $headerAnnotations */
158
        $headerAnnotations = $this->annotations->get(Header::NAME);
159
160
        $headers = [];
161
        foreach ($headerAnnotations as $headerAnnotation) {
162
            $headers[$headerAnnotation->getRequestKey()] = $headerAnnotation->getVariable();
163
        }
164
165
        return $headers;
166
    }
167
168
    /**
169
     * Get headers defined by "@Headers"
170
     *
171
     * @return null|array
172
     */
173
    public function getStaticHeaders()
174
    {
175
        if (!$this->annotations->exists(Headers::NAME)) {
176
            return null;
177
        }
178
179
        /** @var Headers $headersAnnotation */
180
        $headersAnnotation = $this->annotations->get(Headers::NAME);
181
182
        $headers = [];
183
        foreach ($headersAnnotation->getHeaders() as $key => $value) {
184
            $headers[$key] = $value;
185
        }
186
187
        return $headers;
188
    }
189
190
    /**
191
     * If the request is json encoded
192
     *
193
     * @return bool
194
     */
195
    public function isJsonEncoded()
196
    {
197
        if ($this->annotations->exists(JsonBody::NAME)) {
198
            return true;
199
        }
200
201
        return false;
202
    }
203
204
    /**
205
     * If the request is form encoded
206
     *
207
     * @return bool
208
     */
209
    public function isFormUrlEncoded()
210
    {
211
        if ($this->annotations->exists(FormUrlEncoded::NAME)) {
212
            return true;
213
        }
214
215
        return !$this->isMultipart() && !$this->isJsonEncoded();
216
    }
217
218
    /**
219
     * If the request is multipart encoded
220
     *
221
     * @return bool
222
     */
223
    public function isMultipart()
224
    {
225
        if ($this->annotations->exists(Multipart::NAME)) {
226
            return true;
227
        }
228
229
        return false;
230
    }
231
232
    /**
233
     * Get the multipart boundary
234
     *
235
     * @return string
236
     */
237
    public function getMultipartBoundary()
238
    {
239
        /** @var Multipart $multipartAnnotation */
240
        $multipartAnnotation = $this->annotations->get(Multipart::NAME);
241
        $this->multipartBoundary = $multipartAnnotation->getBoundary();
242
243
        if (null === $this->multipartBoundary) {
244
            $this->multipartBoundary = uniqid('', false);
245
        }
246
247
        return $this->multipartBoundary;
248
    }
249
250
    /**
251
     * If there is a request body
252
     *
253
     * @return bool
254
     */
255
    public function hasBody()
256
    {
257
        return $this->annotations->exists(Body::NAME) || $this->annotations->exists(Part::NAME);
258
    }
259
260
    /**
261
     * If there is a body annotation
262
     *
263
     * @return bool
264
     */
265
    public function hasBodyAnnotation()
266
    {
267
        return $this->annotations->exists(Body::NAME);
268
    }
269
270
    /**
271
     * Get body variable
272
     *
273
     * @return null|string
274
     * @throws LogicException
275
     */
276
    public function getBody()
277
    {
278
        if (!$this->annotations->exists(Body::NAME)) {
279
            return null;
280
        }
281
282
        return $this->getBodyAnnotation()->getVariable();
283
    }
284
285
    /**
286
     * Get body parts
287
     *
288
     * @return array|null
289
     */
290 View Code Duplication
    public function getBodyParts()
291
    {
292
        if (!$this->annotations->exists(Part::NAME)) {
293
            return null;
294
        }
295
296
        /** @var Part[] $partAnnotations */
297
        $partAnnotations = $this->annotations->get(Part::NAME);
298
299
        $parts = [];
300
        foreach ($partAnnotations as $partAnnotation) {
301
            $parts[$partAnnotation->getRequestKey()] = $partAnnotation->getVariable();
302
        }
303
304
        return $parts;
305
    }
306
307
    /**
308
     * If the body parameter is an object
309
     *
310
     * @return bool
311
     * @throws LogicException
312
     */
313 View Code Duplication
    public function isBodyObject()
314
    {
315
        if (!$this->annotations->exists(Body::NAME)) {
316
            return false;
317
        }
318
319
        return $this->methodModel->getParameter($this->getBodyAnnotation()->getVariableName())->isObject();
320
    }
321
322
    /**
323
     * If the body parameter is an array
324
     *
325
     * @return bool
326
     * @throws LogicException
327
     */
328 View Code Duplication
    public function isBodyArray()
329
    {
330
        if (!$this->annotations->exists(Body::NAME)) {
331
            return false;
332
        }
333
334
        return $this->methodModel->getParameter($this->getBodyAnnotation()->getVariableName())->isArray();
335
    }
336
337
    /**
338
     * If the body parameter is optional
339
     *
340
     * @return bool
341
     * @throws LogicException
342
     */
343 View Code Duplication
    public function isBodyOptional()
344
    {
345
        if (!$this->annotations->exists(Body::NAME)) {
346
            return false;
347
        }
348
349
        return $this->methodModel->getParameter($this->getBodyAnnotation()->getVariableName())->isOptional();
350
    }
351
352
    /**
353
     * If the body parameter implements \JsonSerializable
354
     *
355
     * @return bool
356
     * @throws LogicException
357
     */
358
    public function isBodyJsonSerializable()
359
    {
360
        if (!$this->isBodyObject()) {
361
            return false;
362
        }
363
364
        $typehint = $this->methodModel->getParameter($this->getBodyAnnotation()->getVariableName())->getTypeHint();
365
366
        $reflectionClass = new ReflectionClass($typehint);
367
        $interfaces = $reflectionClass->getInterfaceNames();
368
369
        return in_array('JsonSerializable', $interfaces, true);
370
    }
371
372
    /**
373
     * Get JMS Serialization context
374
     *
375
     * @return array|null
376
     */
377 View Code Duplication
    public function getSerializationContext()
378
    {
379
        if (!$this->annotations->exists(SerializationContext::NAME)) {
380
            return null;
381
        }
382
383
        /** @var SerializationContext $contextAnnotation */
384
        $contextAnnotation = $this->annotations->get(SerializationContext::NAME);
385
386
        return [
387
            'groups' => $contextAnnotation->getGroups(),
388
            'version' => $contextAnnotation->getVersion(),
389
            'serializeNull' => $contextAnnotation->getSerializeNull(),
390
            'enableMaxDepthChecks' => $contextAnnotation->getEnableMaxDepthChecks(),
391
            'attributes' => $contextAnnotation->getAttributes(),
392
        ];
393
    }
394
395
    /**
396
     * Get JMS Deserialization context
397
     *
398
     * @return array|null
399
     */
400 View Code Duplication
    public function getDeserializationContext()
401
    {
402
        if (!$this->annotations->exists(DeserializationContext::NAME)) {
403
            return null;
404
        }
405
406
        /** @var DeserializationContext $contextAnnotation */
407
        $contextAnnotation = $this->annotations->get(DeserializationContext::NAME);
408
409
        return [
410
            'groups' => $contextAnnotation->getGroups(),
411
            'version' => $contextAnnotation->getVersion(),
412
            'serializeNull' => $contextAnnotation->getSerializeNull(),
413
            'enableMaxDepthChecks' => $contextAnnotation->getEnableMaxDepthChecks(),
414
            'attributes' => $contextAnnotation->getAttributes(),
415
            'depth' => $contextAnnotation->getDepth(),
416
        ];
417
    }
418
419
    /**
420
     * Get the expected return
421
     *
422
     * @return null|string
423
     */
424
    public function getReturnType()
425
    {
426
        if (!$this->annotations->exists(Returns::NAME)) {
427
            return null;
428
        }
429
430
        /** @var Returns $returnAnnotation */
431
        $returnAnnotation = $this->annotations->get(Returns::NAME);
432
433
        return $returnAnnotation->getReturn();
434
    }
435
436
    /**
437
     * Get the return type for a response return
438
     *
439
     * @return null|string
440
     */
441
    public function getResponseType()
442
    {
443
        if (!$this->annotations->exists(ResponseType::NAME)) {
444
            return null;
445
        }
446
447
        /** @var ResponseType $returnAnnotation */
448
        $returnAnnotation = $this->annotations->get(ResponseType::NAME);
449
450
        return $returnAnnotation->getType();
451
    }
452
453
    /**
454
     * Get the callback parameter variable
455
     *
456
     * @return null|string
457
     * @throws RetrofitException
458
     */
459 View Code Duplication
    public function getCallback()
460
    {
461
        $callback = $this->getCallbackParameter();
462
463
        if (null === $callback) {
464
            return null;
465
        }
466
467
        return '$' . $callback->getName();
468
    }
469
470
    /**
471
     * Returns if the callback is optional
472
     *
473
     * @return bool
474
     * @throws LogicException
475
     * @throws RetrofitException
476
     */
477 View Code Duplication
    public function isCallbackOptional()
478
    {
479
        $callback = $this->getCallbackParameter();
480
481
        if (null === $callback) {
482
            throw new LogicException('Callback does not exist');
483
        }
484
485
        return $callback->isOptional();
486
    }
487
488
    /**
489
     * Get the request annotation
490
     *
491
     * @return HttpRequest
492
     * @throws LogicException
493
     */
494
    private function getRequestAnnotation()
495
    {
496
        try {
497
            $requestAnnotation = $this->annotations->get(HttpRequest::NAME);
498
        } catch (OutOfBoundsException $exception) {
499
            throw new LogicException('Request annotation not found (e.g. @GET, @POST)');
500
        }
501
502
        return $requestAnnotation;
503
    }
504
505
    /***
506
     * Get the body annotation
507
     *
508
     * @return Body
509
     */
510
    private function getBodyAnnotation()
511
    {
512
        return $this->annotations->get(Body::NAME);
513
    }
514
515
    /**
516
     * Get the callback method parameter
517
     *
518
     * @return null|ParameterModel
519
     * @throws RetrofitException
520
     */
521
    private function getCallbackParameter()
522
    {
523
        $parameters = array_reverse($this->methodModel->getParameters());
524
        $callback = null;
525
526
        /** @var ParameterModel $parameter */
527
        foreach ($parameters as $parameter) {
528
            if ('\Tebru\Retrofit\Http\Callback' === $parameter->getTypeHint()) {
529
                $callback = $parameter;
530
            }
531
        }
532
533
        if (null === $callback) {
534
            return null;
535
        }
536
537
        $reflectionClass = new ReflectionClass($this->methodModel->getClassModel()->getInterface());
538
539
        if (!in_array('Tebru\Retrofit\Http\AsyncAware', $reflectionClass->getInterfaceNames(), true)) {
540
            throw new RetrofitException('Interfaces using async methods must implement the "AsyncAware" class');
541
        }
542
543
        return $callback;
544
    }
545
}
546