ClassMetadataFactory   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 189
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 88
dl 0
loc 189
ccs 82
cts 82
cp 1
rs 10
c 0
b 0
f 0
wmc 10

2 Methods

Rating   Name   Duplication   Size   Complexity  
B create() 0 109 9
A __construct() 0 18 1
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\Gson\Internal\Data;
10
11
use Psr\SimpleCache\CacheInterface;
12
use ReflectionClass;
13
use ReflectionProperty;
14
use Tebru\AnnotationReader\AnnotationReaderAdapter;
15
use Tebru\Gson\Annotation\VirtualProperty;
16
use Tebru\Gson\ClassMetadata;
17
use Tebru\Gson\Internal\AccessorMethodProvider;
18
use Tebru\Gson\Internal\AccessorStrategy\GetByMethod;
19
use Tebru\Gson\Internal\AccessorStrategy\SetByNull;
20
use Tebru\Gson\Internal\AccessorStrategyFactory;
21
use Tebru\Gson\Internal\DefaultClassMetadata;
22
use Tebru\Gson\Internal\Excluder;
23
use Tebru\Gson\Internal\Naming\PropertyNamer;
24
use Tebru\Gson\Internal\TypeTokenFactory;
25
use Tebru\PhpType\TypeToken;
26
27
/**
28
 * Class PropertyCollectionFactory
29
 *
30
 * Aggregates information about class properties to be used during
31
 * future parsing.
32
 *
33
 * @author Nate Brunette <[email protected]>
34
 */
35
final class ClassMetadataFactory
36
{
37
    /**
38
     * @var ReflectionPropertySetFactory
39
     */
40
    private $reflectionPropertySetFactory;
41
42
    /**
43
     * @var AnnotationReaderAdapter
44
     */
45
    private $annotationReader;
46
47
    /**
48
     * @var PropertyNamer
49
     */
50
    private $propertyNamer;
51
52
    /**
53
     * @var AccessorMethodProvider
54
     */
55
    private $accessorMethodProvider;
56
57
    /**
58
     * @var AccessorStrategyFactory
59
     */
60
    private $accessorStrategyFactory;
61
62
    /**
63
     * @var TypeTokenFactory
64
     */
65
    private $phpTypeFactory;
66
67
    /**
68
     * @var Excluder
69
     */
70
    private $excluder;
71
72
    /**
73
     * @var CacheInterface
74
     */
75
    private $cache;
76
77
    /**
78
     * Constructor
79
     *
80
     * @param ReflectionPropertySetFactory $reflectionPropertySetFactory
81
     * @param AnnotationReaderAdapter $annotationReader
82
     * @param PropertyNamer $propertyNamer
83
     * @param AccessorMethodProvider $accessorMethodProvider
84
     * @param AccessorStrategyFactory $accessorStrategyFactory
85
     * @param TypeTokenFactory $phpTypeFactory
86
     * @param Excluder $excluder
87
     * @param CacheInterface $cache
88
     */
89 10
    public function __construct(
90
        ReflectionPropertySetFactory $reflectionPropertySetFactory,
91
        AnnotationReaderAdapter $annotationReader,
92
        PropertyNamer $propertyNamer,
93
        AccessorMethodProvider $accessorMethodProvider,
94
        AccessorStrategyFactory $accessorStrategyFactory,
95
        TypeTokenFactory $phpTypeFactory,
96
        Excluder $excluder,
97
        CacheInterface $cache
98
    ) {
99 10
        $this->reflectionPropertySetFactory = $reflectionPropertySetFactory;
100 10
        $this->annotationReader = $annotationReader;
101 10
        $this->propertyNamer = $propertyNamer;
102 10
        $this->accessorMethodProvider = $accessorMethodProvider;
103 10
        $this->accessorStrategyFactory = $accessorStrategyFactory;
104 10
        $this->phpTypeFactory = $phpTypeFactory;
105 10
        $this->excluder = $excluder;
106 10
        $this->cache = $cache;
107 10
    }
108
109
    /**
110
     * Create a new [@see ClassMetadata] with properties of the provided type
111
     *
112
     * @param TypeToken $phpType
113
     * @return DefaultClassMetadata
114
     */
115 5
    public function create(TypeToken $phpType): DefaultClassMetadata
116
    {
117 5
        $class = $phpType->rawType;
118 5
        $key = 'gson.classmetadata.'.str_replace('\\', '', $class);
119
120 5
        $data = $this->cache->get($key);
121 5
        if ($data !== null) {
122 1
            return $data;
123
        }
124
125 5
        $properties = new PropertyCollection();
126 5
        $classMetadata = new DefaultClassMetadata($class, $this->annotationReader->readClass($class, true), $properties);
127
128 5
        $reflectionClass = new ReflectionClass($class);
129 5
        $reflectionProperties = $this->reflectionPropertySetFactory->create($reflectionClass);
130
131
        /** @var ReflectionProperty $reflectionProperty */
132 5
        foreach ($reflectionProperties as $reflectionProperty) {
133 4
            $annotations = $this->annotationReader->readProperty(
134 4
                $reflectionProperty->getName(),
135 4
                $reflectionProperty->getDeclaringClass()->getName(),
136 4
                false,
137 4
                true
138
            );
139
140 4
            $serializedName = $this->propertyNamer->serializedName($reflectionProperty->getName(), $annotations);
141 4
            $getterMethod = $this->accessorMethodProvider->getterMethod($reflectionClass, $reflectionProperty, $annotations);
142 4
            $setterMethod = $this->accessorMethodProvider->setterMethod($reflectionClass, $reflectionProperty, $annotations);
143 4
            $getterStrategy = $this->accessorStrategyFactory->getterStrategy($reflectionProperty, $getterMethod);
144 4
            $setterStrategy = $this->accessorStrategyFactory->setterStrategy($reflectionProperty, $setterMethod);
145 4
            $type = $this->phpTypeFactory->create($annotations, $getterMethod, $setterMethod, $reflectionProperty);
146
147 4
            $property = new Property(
148 4
                $reflectionProperty->getName(),
149 4
                $serializedName,
150 4
                $type,
151 4
                $getterStrategy,
152 4
                $setterStrategy,
153 4
                $annotations,
154 4
                $reflectionProperty->getModifiers(),
155 4
                false,
156 4
                $classMetadata
157
            );
158
159 4
            $skipSerialize = $this->excluder->excludePropertySerialize($property);
160 4
            $skipDeserialize = $this->excluder->excludePropertyDeserialize($property);
161
162
            // if we're skipping serialization and deserialization, we don't need
163
            // to add the property to the collection
164 4
            if ($skipSerialize && $skipDeserialize) {
165 2
                continue;
166
            }
167
168 4
            $property->setSkipSerialize($skipSerialize);
169 4
            $property->setSkipDeserialize($skipDeserialize);
170
171 4
            $properties->add($property);
172
        }
173
174
        // add virtual properties
175 5
        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
176 4
            $annotations = $this->annotationReader->readMethod(
177 4
                $reflectionMethod->getName(),
178 4
                $reflectionMethod->getDeclaringClass()->getName(),
179 4
                false,
180 4
                true
181
            );
182 4
            if (null === $annotations->get(VirtualProperty::class)) {
183 4
                continue;
184
            }
185
186 2
            $serializedName = $this->propertyNamer->serializedName($reflectionMethod->getName(), $annotations);
187 2
            $type = $this->phpTypeFactory->create($annotations, $reflectionMethod);
188 2
            $getterStrategy = new GetByMethod($reflectionMethod->getName());
189 2
            $setterStrategy = new SetByNull();
190
191 2
            $property = new Property(
192 2
                $reflectionMethod->getName(),
193 2
                $serializedName,
194 2
                $type,
195 2
                $getterStrategy,
196 2
                $setterStrategy,
197 2
                $annotations,
198 2
                $reflectionMethod->getModifiers(),
199 2
                true,
200 2
                $classMetadata
201
            );
202
203 2
            $skipSerialize = $this->excluder->excludePropertySerialize($property);
204 2
            $skipDeserialize = $this->excluder->excludePropertyDeserialize($property);
205
206
            // if we're skipping serialization and deserialization, we don't need
207
            // to add the property to the collection
208 2
            if ($skipSerialize && $skipDeserialize) {
209 2
                continue;
210
            }
211
212 2
            $property->setSkipSerialize($skipSerialize);
213 2
            $property->setSkipDeserialize($skipDeserialize);
214
215 2
            $properties->add($property);
216
        }
217
218 5
        $classMetadata->setSkipSerialize($this->excluder->excludeClassSerialize($classMetadata));
219 5
        $classMetadata->setSkipDeserialize($this->excluder->excludeClassDeserialize($classMetadata));
220
221 5
        $this->cache->set($key, $classMetadata);
222
223 5
        return $classMetadata;
224
    }
225
}
226