Completed
Push — master ( 033c1a...a9d2a1 )
by Nate
03:23
created

TypeAdapterProvider::addTypeAdapter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
crap 1
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 InvalidArgumentException;
10
use Tebru\Gson\Annotation\JsonAdapter;
11
use Tebru\Gson\Internal\TypeAdapter\CustomWrappedTypeAdapter;
12
use Tebru\Gson\JsonDeserializer;
13
use Tebru\Gson\JsonSerializer;
14
use Tebru\Gson\TypeAdapter;
15
use Tebru\Gson\TypeAdapterFactory;
16
use Tebru\PhpType\TypeToken;
17
18
/**
19
 * Class TypeAdapterProvider
20
 *
21
 * @author Nate Brunette <[email protected]>
22
 */
23
final class TypeAdapterProvider
24
{
25
    /**
26
     * A cache of created type adapters
27
     *
28
     * @var array
29
     */
30
    private $typeAdapters = [];
31
32
    /**
33
     * All registered [@see TypeAdapter] factories
34
     *
35
     * @var TypeAdapterFactory[]
36
     */
37
    private $typeAdapterFactories;
38
39
    /**
40
     * @var ConstructorConstructor
41
     */
42
    private $constructorConstructor;
43
44
    /**
45
     * Constructor
46
     *
47
     * @param array $typeAdapterFactories
48
     * @param ConstructorConstructor $constructorConstructor
49
     */
50 12
    public function __construct(array $typeAdapterFactories, ConstructorConstructor $constructorConstructor)
51
    {
52 12
        $this->typeAdapterFactories = $typeAdapterFactories;
53 12
        $this->constructorConstructor = $constructorConstructor;
54 12
    }
55
56
    /**
57
     * Creates a key based on the type, and optionally the class that should be skipped.
58
     * Returns the [@see TypeAdapter] if it has already been created, otherwise loops
59
     * over all of the factories and finds a type adapter that supports the type.
60
     *
61
     * @param TypeToken $type
62
     * @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...
63
     * @return TypeAdapter
64
     * @throws \InvalidArgumentException if the type cannot be handled by a type adapter
65
     */
66 5
    public function getAdapter(TypeToken $type, TypeAdapterFactory $skip = null): TypeAdapter
67
    {
68 5
        $key = (string)$type;
69 5
        if (null === $skip && isset($this->typeAdapters[$key])) {
70 1
            return $this->typeAdapters[$key];
71
        }
72
73 5
        foreach ($this->typeAdapterFactories as $typeAdapterFactory) {
74 4
            if ($typeAdapterFactory === $skip) {
75 1
                continue;
76
            }
77
78 3
            if (!$typeAdapterFactory->supports($type)) {
79 3
                continue;
80
            }
81
82 3
            $adapter = $typeAdapterFactory->create($type, $this);
83
84
            // do not save skipped adapters
85 3
            if (null === $skip) {
86 3
                $this->typeAdapters[$key] = $adapter;
87
            }
88
89 3
            return $adapter;
90
        }
91
92 2
        throw new InvalidArgumentException(sprintf(
93 2
            'The type "%s" could not be handled by any of the registered type adapters',
94 2
            (string)$type
95
        ));
96
    }
97
98
    /**
99
     * Get a type adapter from a [@see JsonAdapter] annotation
100
     *
101
     * The class may be a TypeAdapter, TypeAdapterFactory, JsonSerializer, or JsonDeserializer
102
     *
103
     * @param TypeToken $type
104
     * @param JsonAdapter $jsonAdapterAnnotation
105
     * @return TypeAdapter
106
     * @throws \InvalidArgumentException
107
     * @throws \Tebru\PhpType\Exception\MalformedTypeException If the type cannot be parsed
108
     */
109 6
    public function getAdapterFromAnnotation(TypeToken $type, JsonAdapter $jsonAdapterAnnotation): TypeAdapter
110
    {
111 6
        $object = $this->constructorConstructor->get(new TypeToken($jsonAdapterAnnotation->getClass()))->construct();
112
113 6
        if ($object instanceof TypeAdapter) {
114 1
            return $object;
115
        }
116
117 5
        if ($object instanceof TypeAdapterFactory) {
118 1
            return $object->create($type, $this);
119
        }
120
121 4
        if ($object instanceof JsonSerializer && $object instanceof JsonDeserializer) {
122 1
            return new CustomWrappedTypeAdapter($type, $this, $object, $object);
123
        }
124
125 3
        if ($object instanceof JsonSerializer) {
126 1
            return new CustomWrappedTypeAdapter($type, $this, $object);
127
        }
128
129 2
        if ($object instanceof JsonDeserializer) {
130 1
            return new CustomWrappedTypeAdapter($type, $this, null, $object);
131
        }
132
133 1
        throw new InvalidArgumentException(sprintf(
134 1
            'The type adapter must be an instance of TypeAdapter, TypeAdapterFactory, JsonSerializer, or JsonDeserializer, but "%s" was found',
135
            get_class($object)
136
        ));
137
    }
138
}
139