Completed
Push — master ( ac0b2b...25e509 )
by Nate
02:57
created

PropertyCollectionFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 11
cts 11
cp 1
rs 9.3142
c 0
b 0
f 0
cc 1
eloc 19
nc 1
nop 9
crap 1

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
namespace Tebru\Gson\Internal\Data;
8
9
use Doctrine\Common\Cache\Cache;
10
use ReflectionClass;
11
use ReflectionProperty;
12
use Tebru\Gson\Annotation\JsonAdapter;
13
use Tebru\Gson\Annotation\VirtualProperty;
14
use Tebru\Gson\Internal\AccessorMethodProvider;
15
use Tebru\Gson\Internal\AccessorStrategy\GetByMethod;
16
use Tebru\Gson\Internal\AccessorStrategy\SetByNull;
17
use Tebru\Gson\Internal\AccessorStrategyFactory;
18
use Tebru\Gson\Internal\Excluder;
19
use Tebru\Gson\Internal\MetadataFactory;
20
use Tebru\Gson\Internal\Naming\PropertyNamer;
21
use Tebru\Gson\PhpType;
22
use Tebru\Gson\Internal\PhpTypeFactory;
23
use Tebru\Gson\Internal\TypeAdapterProvider;
24
use Tebru\Gson\PropertyMetadata;
25
26
/**
27
 * Class PropertyCollectionFactory
28
 *
29
 * Aggregates information about class properties to be used during
30
 * future parsing.
31
 *
32
 * @author Nate Brunette <[email protected]>
33
 */
34
final class PropertyCollectionFactory
35
{
36
    /**
37
     * @var ReflectionPropertySetFactory
38
     */
39
    private $reflectionPropertySetFactory;
40
41
    /**
42
     * @var AnnotationCollectionFactory
43
     */
44
    private $annotationCollectionFactory;
45
46
    /**
47
     * @var MetadataFactory
48
     */
49
    private $metadataFactory;
50
51
    /**
52
     * @var PropertyNamer
53
     */
54
    private $propertyNamer;
55
56
    /**
57
     * @var AccessorMethodProvider
58
     */
59
    private $accessorMethodProvider;
60
61
    /**
62
     * @var AccessorStrategyFactory
63
     */
64
    private $accessorStrategyFactory;
65
66
    /**
67
     * @var PhpTypeFactory
68
     */
69
    private $phpTypeFactory;
70
71
    /**
72
     * @var Excluder
73
     */
74
    private $excluder;
75
76
    /**
77
     * @var Cache
78
     */
79
    private $cache;
80
81
    /**
82
     * Constructor
83
     *
84
     * @param ReflectionPropertySetFactory $reflectionPropertySetFactory
85
     * @param AnnotationCollectionFactory $annotationCollectionFactory
86
     * @param MetadataFactory $metadataFactory
87
     * @param PropertyNamer $propertyNamer
88
     * @param AccessorMethodProvider $accessorMethodProvider
89
     * @param AccessorStrategyFactory $accessorStrategyFactory
90
     * @param PhpTypeFactory $phpTypeFactory
91
     * @param Excluder $excluder
92
     * @param Cache $cache
93
     */
94 4
    public function __construct(
95
        ReflectionPropertySetFactory $reflectionPropertySetFactory,
96
        AnnotationCollectionFactory $annotationCollectionFactory,
97
        MetadataFactory $metadataFactory,
98
        PropertyNamer $propertyNamer,
99
        AccessorMethodProvider $accessorMethodProvider,
100
        AccessorStrategyFactory $accessorStrategyFactory,
101
        PhpTypeFactory $phpTypeFactory,
102
        Excluder $excluder,
103
        Cache $cache
104
    ) {
105 4
        $this->reflectionPropertySetFactory = $reflectionPropertySetFactory;
106 4
        $this->annotationCollectionFactory = $annotationCollectionFactory;
107 4
        $this->metadataFactory = $metadataFactory;
108 4
        $this->propertyNamer = $propertyNamer;
109 4
        $this->accessorMethodProvider = $accessorMethodProvider;
110 4
        $this->accessorStrategyFactory = $accessorStrategyFactory;
111 4
        $this->phpTypeFactory = $phpTypeFactory;
112 4
        $this->excluder = $excluder;
113 4
        $this->cache = $cache;
114 4
    }
115
116
    /**
117
     * Create a [@see PropertyCollection] based on the properties of the provided type
118
     *
119
     * @param PhpType $phpType
120
     * @param TypeAdapterProvider $typeAdapterProvider
121
     * @return PropertyCollection
122
     * @throws \RuntimeException If the value is not valid
123
     * @throws \Tebru\Gson\Exception\MalformedTypeException If the type cannot be parsed
124
     * @throws \InvalidArgumentException if the type cannot be handled by a type adapter
125
     * @throws \InvalidArgumentException If the type does not exist
126
     */
127 4
    public function create(PhpType $phpType, TypeAdapterProvider $typeAdapterProvider): PropertyCollection
128
    {
129 4
        $class = $phpType->getClass();
130
131 4
        $data = $this->cache->fetch($class);
132 4
        if (false !== $data) {
133 1
            return $data;
134
        }
135
136 4
        $reflectionClass = new ReflectionClass($class);
137 4
        $reflectionProperties = $this->reflectionPropertySetFactory->create($reflectionClass);
138 4
        $properties = new PropertyCollection();
139
140
        /** @var ReflectionProperty $reflectionProperty */
141 4
        foreach ($reflectionProperties as $reflectionProperty) {
142 4
            $annotations = $this->annotationCollectionFactory->createPropertyAnnotations(
143 4
                $reflectionProperty->getDeclaringClass()->getName(),
144 4
                $reflectionProperty->getName()
145
            );
146
147 4
            $serializedName = $this->propertyNamer->serializedName($reflectionProperty->getName(), $annotations, AnnotationSet::TYPE_PROPERTY);
148 4
            $getterMethod = $this->accessorMethodProvider->getterMethod($reflectionClass, $reflectionProperty, $annotations);
149 4
            $setterMethod = $this->accessorMethodProvider->setterMethod($reflectionClass, $reflectionProperty, $annotations);
150 4
            $getterStrategy = $this->accessorStrategyFactory->getterStrategy($reflectionProperty, $getterMethod);
151 4
            $setterStrategy = $this->accessorStrategyFactory->setterStrategy($reflectionProperty, $setterMethod);
152 4
            $type = $this->phpTypeFactory->create($annotations, AnnotationSet::TYPE_PROPERTY, $getterMethod, $setterMethod);
153
154 4
            $property = new Property(
155 4
                $reflectionProperty->getName(),
156
                $serializedName,
157
                $type,
158
                $getterStrategy,
159
                $setterStrategy,
160
                $annotations,
161 4
                $reflectionProperty->getModifiers(),
162 4
                false
163
            );
164
165 4
            $classMetadata = $this->metadataFactory->createClassMetadata($reflectionProperty->getDeclaringClass()->getName());
166 4
            $propertyMetadata = $this->metadataFactory->createPropertyMetadata($property, $classMetadata);
167
168 4
            $skipSerialize = $this->excludeProperty($propertyMetadata, true);
169 4
            $skipDeserialize = $this->excludeProperty($propertyMetadata, false);
170
171
            // if we're skipping serialization and deserialization, we don't need
172
            // to add the property to the collection
173 4
            if ($skipSerialize && $skipDeserialize) {
174 1
                continue;
175
            }
176
177
            /** @var JsonAdapter $jsonAdapterAnnotation */
178 3
            $jsonAdapterAnnotation = $annotations->getAnnotation(JsonAdapter::class, AnnotationSet::TYPE_PROPERTY);
179 3
            $adapter = null !== $jsonAdapterAnnotation
180 1
                ? $typeAdapterProvider->getAdapterFromAnnotation($type, $jsonAdapterAnnotation)
181 3
                : $typeAdapterProvider->getAdapter($type);
182
183 3
            $property->setTypeAdapter($adapter);
184 3
            $property->setSkipSerialize($skipSerialize);
185 3
            $property->setSkipDeserialize($skipDeserialize);
186
187 3
            $properties->add($property);
188
        }
189
190
        // add virtual properties
191 4
        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
192 4
            $annotations = $this->annotationCollectionFactory->createMethodAnnotations($reflectionMethod->getDeclaringClass()->getName(), $reflectionMethod->getName());
193 4
            if (null === $annotations->getAnnotation(VirtualProperty::class, AnnotationSet::TYPE_METHOD)) {
194 2
                continue;
195
            }
196
197 4
            $serializedName = $this->propertyNamer->serializedName($reflectionMethod->getName(), $annotations, AnnotationSet::TYPE_METHOD);
198 4
            $type = $this->phpTypeFactory->create($annotations, AnnotationSet::TYPE_METHOD, $reflectionMethod);
199 4
            $getterStrategy = new GetByMethod($reflectionMethod->getName());
200 4
            $setterStrategy = new SetByNull();
201
202 4
            $property = new Property(
203 4
                $reflectionMethod->getName(),
204
                $serializedName,
205
                $type,
206
                $getterStrategy,
207
                $setterStrategy,
208
                $annotations,
209 4
                $reflectionMethod->getModifiers(),
210 4
                true
211
            );
212
213 4
            $classMetadata = $this->metadataFactory->createClassMetadata($reflectionMethod->getDeclaringClass()->getName());
214 4
            $propertyMetadata = $this->metadataFactory->createPropertyMetadata($property, $classMetadata);
215
216 4
            $skipSerialize = $this->excludeProperty($propertyMetadata, true);
217 4
            $skipDeserialize = $this->excludeProperty($propertyMetadata, false);
218
219
            // if we're skipping serialization and deserialization, we don't need
220
            // to add the property to the collection
221 4
            if ($skipSerialize && $skipDeserialize) {
222 1
                continue;
223
            }
224
225
            /** @var JsonAdapter $jsonAdapterAnnotation */
226 3
            $jsonAdapterAnnotation = $annotations->getAnnotation(JsonAdapter::class, AnnotationSet::TYPE_METHOD);
227 3
            $adapter = null !== $jsonAdapterAnnotation
228 1
                ? $typeAdapterProvider->getAdapterFromAnnotation($type, $jsonAdapterAnnotation)
229 3
                : $typeAdapterProvider->getAdapter($type);
230
231 3
            $property->setTypeAdapter($adapter);
232 3
            $property->setSkipSerialize($skipSerialize);
233 3
            $property->setSkipDeserialize($skipDeserialize);
234
235 3
            $properties->add($property);
236
        }
237
238 4
        $this->cache->save($class, $properties);
239
240 4
        return $properties;
241
    }
242
243
    /**
244
     * Returns true if we should skip this property
245
     *
246
     * Asks the excluder if we should skip the property or class
247
     *
248
     * @param PropertyMetadata $propertyMetadata
249
     * @param bool $serialize
250
     * @return bool
251
     * @throws \InvalidArgumentException If the type does not exist
252
     */
253 4
    private function excludeProperty(PropertyMetadata $propertyMetadata, bool $serialize): bool
254
    {
255 4
        $excludeClass = $this->excluder->excludeClass($propertyMetadata->getDeclaringClassMetadata(), $serialize);
256 4
        $excludeProperty = $this->excluder->excludeProperty($propertyMetadata, $serialize);
257
258 4
        return $excludeClass || $excludeProperty;
259
    }
260
}
261