Completed
Push — performance-improvements-v2 ( 8879cc...332ef3 )
by Nate
02:35
created

PropertyCollectionFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 8
dl 0
loc 18
ccs 9
cts 9
cp 1
crap 1
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\Excluder;
22
use Tebru\Gson\Internal\Naming\PropertyNamer;
23
use Tebru\Gson\Internal\PhpTypeFactory;
24
use Tebru\Gson\PropertyMetadata;
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 PropertyCollectionFactory
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 PhpTypeFactory
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 PhpTypeFactory $phpTypeFactory
86
     * @param Excluder $excluder
87
     * @param CacheInterface $cache
88
     */
89 6
    public function __construct(
90
        ReflectionPropertySetFactory $reflectionPropertySetFactory,
91
        AnnotationReaderAdapter $annotationReader,
92
        PropertyNamer $propertyNamer,
93
        AccessorMethodProvider $accessorMethodProvider,
94
        AccessorStrategyFactory $accessorStrategyFactory,
95
        PhpTypeFactory $phpTypeFactory,
96
        Excluder $excluder,
97
        CacheInterface $cache
98
    ) {
99 6
        $this->reflectionPropertySetFactory = $reflectionPropertySetFactory;
100 6
        $this->annotationReader = $annotationReader;
101 6
        $this->propertyNamer = $propertyNamer;
102 6
        $this->accessorMethodProvider = $accessorMethodProvider;
103 6
        $this->accessorStrategyFactory = $accessorStrategyFactory;
104 6
        $this->phpTypeFactory = $phpTypeFactory;
105 6
        $this->excluder = $excluder;
106 6
        $this->cache = $cache;
107 6
    }
108
109
    /**
110
     * Create a [@see PropertyCollection] based on the properties of the provided type
111
     *
112
     * @param TypeToken $phpType
113
     * @param ClassMetadata $classMetadata
114
     * @return PropertyCollection
115
     */
116 5
    public function create(TypeToken $phpType, ClassMetadata $classMetadata): PropertyCollection
117
    {
118 5
        $class = $phpType->getRawType();
119 5
        $key = 'gson.properties.'.\str_replace('\\', '', $class);
120
121 5
        $data = $this->cache->get($key);
122 5
        if ($data !== null) {
123 1
            return $data;
124
        }
125
126 5
        $reflectionClass = new ReflectionClass($class);
127 5
        $reflectionProperties = $this->reflectionPropertySetFactory->create($reflectionClass);
128 5
        $properties = new PropertyCollection();
129
130
        /** @var ReflectionProperty $reflectionProperty */
131 5
        foreach ($reflectionProperties as $reflectionProperty) {
132 4
            $annotations = $this->annotationReader->readProperty(
133 4
                $reflectionProperty->getName(),
134 4
                $reflectionProperty->getDeclaringClass()->getName(),
135 4
                false,
136 4
                true
137
            );
138
139 4
            $serializedName = $this->propertyNamer->serializedName($reflectionProperty->getName(), $annotations);
140 4
            $getterMethod = $this->accessorMethodProvider->getterMethod($reflectionClass, $reflectionProperty, $annotations);
141 4
            $setterMethod = $this->accessorMethodProvider->setterMethod($reflectionClass, $reflectionProperty, $annotations);
142 4
            $getterStrategy = $this->accessorStrategyFactory->getterStrategy($reflectionProperty, $getterMethod);
143 4
            $setterStrategy = $this->accessorStrategyFactory->setterStrategy($reflectionProperty, $setterMethod);
144 4
            $type = $this->phpTypeFactory->create($annotations, $getterMethod, $setterMethod, $reflectionProperty);
145
146 4
            $property = new Property(
147 4
                $reflectionProperty->getName(),
148 4
                $serializedName,
149 4
                $type,
150 4
                $getterStrategy,
151 4
                $setterStrategy,
152 4
                $annotations,
153 4
                $reflectionProperty->getModifiers(),
154 4
                false,
155 4
                $classMetadata
156
            );
157
158 4
            $skipSerialize = $this->excludeProperty($property, true);
159 4
            $skipDeserialize = $this->excludeProperty($property, false);
160
161
            // if we're skipping serialization and deserialization, we don't need
162
            // to add the property to the collection
163 4
            if ($skipSerialize && $skipDeserialize) {
164 2
                continue;
165
            }
166
167 4
            $property->setSkipSerialize($skipSerialize);
168 4
            $property->setSkipDeserialize($skipDeserialize);
169
170 4
            $properties->add($property);
171
        }
172
173
        // add virtual properties
174 5
        foreach ($reflectionClass->getMethods() as $reflectionMethod) {
175 4
            $annotations = $this->annotationReader->readMethod(
176 4
                $reflectionMethod->getName(),
177 4
                $reflectionMethod->getDeclaringClass()->getName(),
178 4
                false,
179 4
                true
180
            );
181 4
            if (null === $annotations->get(VirtualProperty::class)) {
182 4
                continue;
183
            }
184
185 2
            $serializedName = $this->propertyNamer->serializedName($reflectionMethod->getName(), $annotations);
186 2
            $type = $this->phpTypeFactory->create($annotations, $reflectionMethod);
187 2
            $getterStrategy = new GetByMethod($reflectionMethod->getName());
188 2
            $setterStrategy = new SetByNull();
189
190 2
            $property = new Property(
191 2
                $reflectionMethod->getName(),
192 2
                $serializedName,
193 2
                $type,
194 2
                $getterStrategy,
195 2
                $setterStrategy,
196 2
                $annotations,
197 2
                $reflectionMethod->getModifiers(),
198 2
                true,
199 2
                $classMetadata
200
            );
201
202 2
            $skipSerialize = $this->excludeProperty($property, true);
203 2
            $skipDeserialize = $this->excludeProperty($property, false);
204
205
            // if we're skipping serialization and deserialization, we don't need
206
            // to add the property to the collection
207 2
            if ($skipSerialize && $skipDeserialize) {
208 2
                continue;
209
            }
210
211 2
            $property->setSkipSerialize($skipSerialize);
212 2
            $property->setSkipDeserialize($skipDeserialize);
213
214 2
            $properties->add($property);
215
        }
216
217 5
        $this->cache->set($key, $properties);
218
219 5
        return $properties;
220
    }
221
222
    /**
223
     * Returns true if we should skip this property
224
     *
225
     * Asks the excluder if we should skip the property or class
226
     *
227
     * @param PropertyMetadata $propertyMetadata
228
     * @param bool $serialize
229
     * @return bool
230
     */
231 4
    private function excludeProperty(PropertyMetadata $propertyMetadata, bool $serialize): bool
232
    {
233 4
        $excludeClass = $this->excluder->excludeClass($propertyMetadata->getDeclaringClassMetadata(), $serialize);
234 4
        $excludeProperty = $this->excluder->excludeProperty($propertyMetadata, $serialize);
235
236 4
        return $excludeClass || $excludeProperty;
237
    }
238
}
239