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