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

ClassMetadataFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 8
dl 0
loc 18
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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