Completed
Push — master ( dc53d4...3886f0 )
by Nate
02:49
created

TypeAdapterProvider::getAdapterFromAnnotation()   C

Complexity

Conditions 7
Paths 6

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 15
cts 15
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 16
nc 6
nop 2
crap 7
1
<?php
2
/*
3
 * Copyright (c) Nate Brunette.
4
 * Distributed under the MIT License (http://opensource.org/licenses/MIT)
5
 */
6
7
namespace Tebru\Gson\Internal;
8
9
use Doctrine\Common\Cache\CacheProvider;
10
use InvalidArgumentException;
11
use Tebru\Gson\Annotation\JsonAdapter;
12
use Tebru\Gson\Internal\TypeAdapter\CustomWrappedTypeAdapter;
13
use Tebru\Gson\JsonDeserializer;
14
use Tebru\Gson\JsonSerializer;
15
use Tebru\Gson\PhpType;
16
use Tebru\Gson\TypeAdapter;
17
use Tebru\Gson\TypeAdapterFactory;
18
19
/**
20
 * Class TypeAdapterProvider
21
 *
22
 * @author Nate Brunette <[email protected]>
23
 */
24
final class TypeAdapterProvider
25
{
26
    /**
27
     * A cache of mapped factories
28
     *
29
     * @var CacheProvider
30
     */
31
    private $typeAdapterCache;
32
33
    /**
34
     * All registered [@see TypeAdapter]s
35
     *
36
     * @var TypeAdapterFactory[]
37
     */
38
    private $typeAdapterFactories = [];
39
40
    /**
41
     * Constructor
42
     *
43
     * @param array $typeAdapterFactories
44
     * @param CacheProvider $cache
45
     */
46 12
    public function __construct(array $typeAdapterFactories, CacheProvider $cache)
47
    {
48 12
        $this->typeAdapterFactories = $typeAdapterFactories;
49 12
        $this->typeAdapterCache = $cache;
50 12
    }
51
52
    /**
53
     * Add type adapter directly into cache
54
     *
55
     * @param string $type
56
     * @param TypeAdapter $typeAdapter
57
     */
58 1
    public function addTypeAdapter(string $type, TypeAdapter $typeAdapter): void
59
    {
60 1
        $this->typeAdapterCache->save($type, $typeAdapter);
61 1
    }
62
63
    /**
64
     * Creates a key based on the type, and optionally the class that should be skipped.
65
     * Returns the [@see TypeAdapter] if it has already been created, otherwise loops
66
     * over all of the factories and finds a type adapter that supports the type.
67
     *
68
     * @param PhpType $type
69
     * @param TypeAdapterFactory $skip
0 ignored issues
show
Documentation introduced by
Should the type for parameter $skip not be null|TypeAdapterFactory?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
70
     * @return TypeAdapter
71
     * @throws \InvalidArgumentException if the type cannot be handled by a type adapter
72
     */
73 6
    public function getAdapter(PhpType $type, TypeAdapterFactory $skip = null): TypeAdapter
74
    {
75 6
        $key = $type->getUniqueKey();
76 6
        $typeAdapter = $this->typeAdapterCache->fetch($key);
77 6
        if (null === $skip && false !== $typeAdapter) {
78 2
            return $typeAdapter;
79
        }
80
81 5
        foreach ($this->typeAdapterFactories as $typeAdapterFactory) {
82 5
            if ($typeAdapterFactory === $skip) {
83 1
                continue;
84
            }
85
86 4
            if (!$typeAdapterFactory->supports($type)) {
87 1
                continue;
88
            }
89
90 3
            $adapter = $typeAdapterFactory->create($type, $this);
91
92
            // do not save skipped adapters
93 3
            if (null === $skip) {
94 3
                $this->typeAdapterCache->save($key, $adapter);
95
            }
96
97 3
            return $adapter;
98
        }
99
100 3
        throw new InvalidArgumentException(sprintf(
101 3
            'The type "%s" could not be handled by any of the registered type adapters',
102 3
            (string) $type
103
        ));
104
    }
105
106
    /**
107
     * Get a type adapter from a [@see JsonAdapter] annotation
108
     *
109
     * The class may be a TypeAdapter, TypeAdapterFactory, JsonSerializer, or JsonDeserializer
110
     *
111
     * @param PhpType $phpType
112
     * @param JsonAdapter $jsonAdapterAnnotation
113
     * @return TypeAdapter
114
     * @throws \InvalidArgumentException if an invalid adapter is found
115
     */
116 6
    public function getAdapterFromAnnotation(PhpType $phpType, JsonAdapter $jsonAdapterAnnotation): TypeAdapter
117
    {
118 6
        $class = $jsonAdapterAnnotation->getClass();
119 6
        $object = new $class();
120
121 6
        if ($object instanceof TypeAdapter) {
122 1
            return $object;
123
        }
124
125 5
        if ($object instanceof TypeAdapterFactory) {
126 1
            return $object->create($phpType, $this);
127
        }
128
129 4
        if ($object instanceof JsonSerializer && $object instanceof JsonDeserializer) {
130 1
            return new CustomWrappedTypeAdapter($phpType, $this, $object, $object);
131
        }
132
133 3
        if ($object instanceof JsonSerializer) {
134 1
            return new CustomWrappedTypeAdapter($phpType, $this, $object);
135
        }
136
137 2
        if ($object instanceof JsonDeserializer) {
138 1
            return new CustomWrappedTypeAdapter($phpType, $this, null, $object);
139
        }
140
141 1
        throw new InvalidArgumentException(sprintf(
142 1
            'The type adapter must be an instance of TypeAdapter, TypeAdapterFactory, JsonSerializer, or JsonDeserializer, but "%s" was found',
143
            get_class($object)
144
        ));
145
    }
146
}
147