Completed
Push — split-exclusion-strategies ( e073a8 )
by Nate
03:36
created

ClassMetadataFactory::create()   B

Complexity

Conditions 9
Paths 13

Size

Total Lines 109
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 71
nc 13
nop 1
dl 0
loc 109
rs 7.0771
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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