Completed
Push — master ( 3886f0...1f9c9a )
by Nate
02:55
created

PropertyCollectionFactory::create()   D

Complexity

Conditions 9
Paths 13

Size

Total Lines 101
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 54
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 101
ccs 54
cts 54
cp 1
rs 4.8196
c 0
b 0
f 0
cc 9
eloc 64
nc 13
nop 1
crap 9

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
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