Passed
Push — master ( d642a7...c02600 )
by Asmir
08:01 queued 05:48
created

DefaultAccessorStrategy   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 122
Duplicated Lines 0 %

Test Coverage

Coverage 90.7%

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 56
c 2
b 0
f 1
dl 0
loc 122
ccs 39
cts 43
cp 0.907
rs 10
wmc 19

3 Methods

Rating   Name   Duplication   Size   Complexity  
C getValue() 0 52 12
B setValue() 0 34 6
A __construct() 0 3 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\ExpressionLanguageRequiredException;
9
use JMS\Serializer\Exception\LogicException;
10
use JMS\Serializer\Exception\UninitializedPropertyException;
11
use JMS\Serializer\Expression\CompilableExpressionEvaluatorInterface;
12
use JMS\Serializer\Expression\Expression;
13
use JMS\Serializer\Expression\ExpressionEvaluatorInterface;
14
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
15
use JMS\Serializer\Metadata\PropertyMetadata;
16
use JMS\Serializer\Metadata\StaticPropertyMetadata;
17
use JMS\Serializer\SerializationContext;
18
19
/**
20
 * @author Asmir Mustafic <[email protected]>
21
 */
22
final class DefaultAccessorStrategy implements AccessorStrategyInterface
23
{
24
    /**
25
     * @var callable[]
26
     */
27
    private $readAccessors = [];
28
29
    /**
30 333
     * @var callable[]
31
     */
32 333
    private $writeAccessors = [];
33 333
34
    /**
35 172
     * @var \ReflectionProperty[]
36
     */
37 172
    private $propertyReflectionCache = [];
38 15
39
    /**
40
     * @var ExpressionEvaluatorInterface
41 168
     */
42 6
    private $evaluator;
43 2
44
    public function __construct(?ExpressionEvaluatorInterface $evaluator = null)
45 4
    {
46
        $this->evaluator = $evaluator;
47
    }
48 164
49 158
    /**
50 158
     * {@inheritdoc}
51
     */
52
    public function getValue(object $object, PropertyMetadata $metadata, SerializationContext $context)
53 4
    {
54 4
        if ($metadata instanceof StaticPropertyMetadata) {
55 4
            return $metadata->getValue();
56 4
        }
57 4
58
        if ($metadata instanceof ExpressionPropertyMetadata) {
59
            if (null === $this->evaluator) {
60 4
                throw new ExpressionLanguageRequiredException(sprintf('The property %s on %s requires the expression accessor strategy to be enabled.', $metadata->name, $metadata->class));
61 4
            }
62
63
            $variables = ['object' => $object, 'context' => $context, 'property_metadata' => $metadata];
64 156
65 156
            if (($metadata->expression instanceof Expression) && ($this->evaluator instanceof CompilableExpressionEvaluatorInterface)) {
66
                return $this->evaluator->evaluateParsed($metadata->expression, $variables);
67
            }
68
69 158
            return $this->evaluator->evaluate($metadata->expression, $variables);
70
        }
71
72 24
        if (null !== $metadata->getter) {
73
            return $object->{$metadata->getter}();
74
        }
75 64
76
        if ($metadata->forceReflectionAccess) {
77 64
            $ref = $this->propertyReflectionCache[$metadata->class][$metadata->name] ?? null;
78
            if (null === $ref) {
79
                $ref = new \ReflectionProperty($metadata->class, $metadata->name);
80
                $ref->setAccessible(true);
81 64
                $this->propertyReflectionCache[$metadata->class][$metadata->name] = $ref;
82 63
            }
83 63
84
            return $ref->getValue($object);
85 4
        }
86 4
87
        $accessor = $this->readAccessors[$metadata->class] ?? null;
88
        if (null === $accessor) {
89
            $accessor = \Closure::bind(static function ($o, $name) {
90
                return $o->$name;
91
            }, null, $metadata->class);
92 4
            $this->readAccessors[$metadata->class] = $accessor;
93 4
        }
94
95
        try {
96 61
            return $accessor($object, $metadata->name);
97 61
        } catch (\Error $e) {
98
            // handle uninitialized properties in PHP >= 7.4
99
            if (preg_match('/^Typed property ([\w\\\\@]+)::\$(\w+) must not be accessed before initialization$/', $e->getMessage(), $matches)) {
100
                throw new UninitializedPropertyException(sprintf('Uninitialized property "%s::$%s".', $metadata->class, $metadata->name), 0, $e);
101 63
            }
102 63
103
            throw $e;
104
        }
105 3
    }
106 3
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function setValue(object $object, $value, PropertyMetadata $metadata, DeserializationContext $context): void
111
    {
112
        if (true === $metadata->readOnly) {
113
            throw new LogicException(sprintf('%s on %s is read only.', $metadata->name, $metadata->class));
114
        }
115
116
        if (null !== $metadata->setter) {
117
            $object->{$metadata->setter}($value);
118
119
            return;
120
        }
121
122
        if ($metadata->forceReflectionAccess) {
123
            $ref = $this->propertyReflectionCache[$metadata->class][$metadata->name] ?? null;
124
            if (null === $ref) {
125
                $ref = new \ReflectionProperty($metadata->class, $metadata->name);
126
                $ref->setAccessible(true);
127
                $this->propertyReflectionCache[$metadata->class][$metadata->name] = $ref;
128
            }
129
130
            $ref->setValue($object, $value);
131
132
            return;
133
        }
134
135
        $accessor = $this->writeAccessors[$metadata->class] ?? null;
136
        if (null === $accessor) {
137
            $accessor = \Closure::bind(static function ($o, $name, $value): void {
138
                $o->$name = $value;
139
            }, null, $metadata->class);
140
            $this->writeAccessors[$metadata->class] = $accessor;
141
        }
142
143
        $accessor($object, $metadata->name, $value);
144
    }
145
}
146