Passed
Pull Request — master (#964)
by Asmir
14:09
created

DefaultAccessorStrategy   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 123
Duplicated Lines 0 %

Test Coverage

Coverage 91.49%

Importance

Changes 0
Metric Value
dl 0
loc 123
rs 10
c 0
b 0
f 0
ccs 43
cts 47
cp 0.9149
wmc 21

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getValues() 0 17 4
C getValue() 0 35 7
B setValue() 0 31 6
A setValues() 0 10 3
A __construct() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Accessor;
6
7
use JMS\Serializer\DeserializationContext;
8
use JMS\Serializer\Exception\LogicException;
9
use JMS\Serializer\Expression\ExpressionEvaluatorInterface;
10
use JMS\Serializer\Metadata\ClassMetadata;
11
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
12
use JMS\Serializer\Metadata\PropertyMetadata;
13
use JMS\Serializer\Metadata\StaticPropertyMetadata;
14
use JMS\Serializer\SerializationContext;
15
16
/**
17
 * @author Asmir Mustafic <[email protected]>
18
 */
19
final class DefaultAccessorStrategy implements AccessorStrategyInterface
20
{
21
    private $readAccessors = [];
22
    private $writeAccessors = [];
23
    private $propertyReflectionCache = [];
24
    /**
25
     * @var ExpressionEvaluatorInterface
26
     */
27
    private $evaluator;
28 331
29
    public function __construct(
30 331
        ExpressionEvaluatorInterface $evaluator = null
31 331
    ) {
32
        $this->evaluator = $evaluator;
33 170
    }
34
35 170
    public function getValues(object $data, ClassMetadata $metadata, array $properties, SerializationContext $context): array
36 15
    {
37
        $shouldSerializeNull = $context->shouldSerializeNull();
38
39 166
        $values = [];
40 4
        foreach ($properties as $propertyMetadata) {
41 2
42
            $v = $this->getValue($data, $propertyMetadata, $context);
43
44 2
            if (null === $v && $shouldSerializeNull !== true) {
45
                continue;
46
            }
47 164
48 158
            $values[] = $v;
49 158
        }
50 4
51
        return $values;
52 4
    }
53 4
54 4
    /**
55 4
     * @param object $object
56 4
     * @param mixed[] $values
57
     * @param PropertyMetadata[] $properties
58
     * @param DeserializationContext $context
59 4
     * @return void
60 4
     */
61
    public function setValues(object $object, array $values, ClassMetadata $metadata, array $properties, DeserializationContext $context): void
62 156
    {
63 156
        $values = [];
64 156
        foreach ($properties as $i => $propertyMetadata) {
65
66
            if (!array_key_exists($i, $values)) {
67
                continue;
68 158
            }
69
70
            $this->setValue($object, $values[$i], $propertyMetadata, $context);
71 24
        }
72
    }
73
74 64
    private function getValue(object $object, PropertyMetadata $metadata, SerializationContext $context)
75
    {
76 64
        if ($metadata instanceof StaticPropertyMetadata) {
77
            return $metadata->getValue();
78
        }
79
80 64
        if ($metadata instanceof ExpressionPropertyMetadata) {
81 63
            return $this->evaluator->evaluate($metadata->expression, ['object' => $object]);
82 63
        }
83 4
84 4
        if (null === $metadata->getter) {
85 4
            if (!isset($this->readAccessors[$metadata->class])) {
86
                if ($metadata->forceReflectionAccess === true) {
87
                    $this->readAccessors[$metadata->class] = function ($o, $name) use ($metadata) {
88
89
                        $ref = $this->propertyReflectionCache[$metadata->class][$name] ?? null;
90
                        if ($ref === null) {
91 4
                            $ref = new \ReflectionProperty($metadata->class, $name);
92 4
                            $ref->setAccessible(true);
93
                            $this->propertyReflectionCache[$metadata->class][$name] = $ref;
94 61
                        }
95 61
96 61
                        return $ref->getValue($o);
97
                    };
98
                } else {
99
                    $this->readAccessors[$metadata->class] = \Closure::bind(function ($o, $name) {
100 63
                        return $o->$name;
101 63
                    }, null, $metadata->class);
102
                }
103
            }
104 3
105 3
            return $this->readAccessors[$metadata->class]($object, $metadata->name);
106
        }
107
108
        return $object->{$metadata->getter}();
109
    }
110
111
    private function setValue(object $object, $value, PropertyMetadata $metadata, DeserializationContext $context): void
112
    {
113
        if ($metadata->readOnly) {
114
            throw new LogicException(sprintf('%s on %s is read only.', $metadata->name, $metadata->class));
115
        }
116
117
        if (null === $metadata->setter) {
118
            if (!isset($this->writeAccessors[$metadata->class])) {
119
                if ($metadata->forceReflectionAccess === true) {
120
                    $this->writeAccessors[$metadata->class] = function ($o, $name, $value) use ($metadata) {
121
                        $ref = $this->propertyReflectionCache[$metadata->class][$name] ?? null;
122
                        if ($ref === null) {
123
                            $ref = new \ReflectionProperty($metadata->class, $name);
124
                            $ref->setAccessible(true);
125
                            $this->propertyReflectionCache[$metadata->class][$name] = $ref;
126
                        }
127
128
                        $ref->setValue($o, $value);
129
                    };
130
                } else {
131
                    $this->writeAccessors[$metadata->class] = \Closure::bind(function ($o, $name, $value) {
132
                        $o->$name = $value;
133
                    }, null, $metadata->class);
134
                }
135
            }
136
137
            $this->writeAccessors[$metadata->class]($object, $metadata->name, $value);
138
            return;
139
        }
140
141
        $object->{$metadata->setter}($value);
142
    }
143
}
144