Passed
Pull Request — master (#1375)
by Rene
02:58
created

DefaultAccessorStrategy::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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\Expression\CompilableExpressionEvaluatorInterface;
11
use JMS\Serializer\Expression\Expression;
12
use JMS\Serializer\Expression\ExpressionEvaluatorInterface;
13
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
14
use JMS\Serializer\Metadata\PropertyMetadata;
15
use JMS\Serializer\Metadata\StaticPropertyMetadata;
16
use JMS\Serializer\SerializationContext;
17
18
/**
19
 * @author Asmir Mustafic <[email protected]>
20
 */
21
final class DefaultAccessorStrategy implements AccessorStrategyInterface
22
{
23
    /**
24
     * @var callable[]
25
     */
26
    private $readAccessors = [];
27
28
    /**
29
     * @var callable[]
30 333
     */
31
    private $writeAccessors = [];
32 333
33 333
    /**
34
     * @var \ReflectionProperty[]
35 172
     */
36
    private $propertyReflectionCache = [];
37 172
38 15
    /**
39
     * @var ExpressionEvaluatorInterface
40
     */
41 168
    private $evaluator;
42 6
43 2
    public function __construct(?ExpressionEvaluatorInterface $evaluator = null)
44
    {
45 4
        $this->evaluator = $evaluator;
46
    }
47
48 164
    /**
49 158
     * {@inheritdoc}
50 158
     */
51
    public function getValue(object $object, PropertyMetadata $metadata, SerializationContext $context)
52
    {
53 4
        if ($metadata instanceof StaticPropertyMetadata) {
54 4
            return $metadata->getValue();
55 4
        }
56 4
57 4
        if ($metadata instanceof ExpressionPropertyMetadata) {
58
            if (null === $this->evaluator) {
59
                throw new ExpressionLanguageRequiredException(sprintf('The property %s on %s requires the expression accessor strategy to be enabled.', $metadata->name, $metadata->class));
60 4
            }
61 4
62
            $variables = ['object' => $object, 'context' => $context, 'property_metadata' => $metadata];
63
64 156
            if (($metadata->expression instanceof Expression) && ($this->evaluator instanceof CompilableExpressionEvaluatorInterface)) {
65 156
                return $this->evaluator->evaluateParsed($metadata->expression, $variables);
66
            }
67
68
            return $this->evaluator->evaluate($metadata->expression, $variables);
69 158
        }
70
71
        if (null !== $metadata->getter) {
72 24
            return $object->{$metadata->getter}();
73
        }
74
75 64
        if ($metadata->forceReflectionAccess || ($context->getShouldSerializeUnitializedAsNull() && $metadata->hasType)) {
76
            $ref = $this->propertyReflectionCache[$metadata->class][$metadata->name] ?? null;
77 64
            if (null === $ref) {
78
                $ref = new \ReflectionProperty($metadata->class, $metadata->name);
79
                $ref->setAccessible(true);
80
                $this->propertyReflectionCache[$metadata->class][$metadata->name] = $ref;
81 64
            }
82 63
83 63
            if (PHP_VERSION_ID >= 70400 && !$ref->isInitialized($object)) {
84
                return null;
85 4
            }
86 4
87
            return $ref->getValue($object);
88
        }
89
90
        $accessor = $this->readAccessors[$metadata->class] ?? null;
91
        if (null === $accessor) {
92 4
            $accessor = \Closure::bind(static function ($o, $name) {
93 4
                return $o->$name;
94
            }, null, $metadata->class);
95
            $this->readAccessors[$metadata->class] = $accessor;
96 61
        }
97 61
98
        return $accessor($object, $metadata->name);
99
    }
100
101 63
    /**
102 63
     * {@inheritdoc}
103
     */
104
    public function setValue(object $object, $value, PropertyMetadata $metadata, DeserializationContext $context): void
105 3
    {
106 3
        if (true === $metadata->readOnly) {
107
            throw new LogicException(sprintf('%s on %s is read only.', $metadata->name, $metadata->class));
108
        }
109
110
        if (null !== $metadata->setter) {
111
            $object->{$metadata->setter}($value);
112
113
            return;
114
        }
115
116
        if ($metadata->forceReflectionAccess) {
117
            $ref = $this->propertyReflectionCache[$metadata->class][$metadata->name] ?? null;
118
            if (null === $ref) {
119
                $ref = new \ReflectionProperty($metadata->class, $metadata->name);
120
                $ref->setAccessible(true);
121
                $this->propertyReflectionCache[$metadata->class][$metadata->name] = $ref;
122
            }
123
124
            $ref->setValue($object, $value);
125
126
            return;
127
        }
128
129
        $accessor = $this->writeAccessors[$metadata->class] ?? null;
130
        if (null === $accessor) {
131
            $accessor = \Closure::bind(static function ($o, $name, $value): void {
132
                $o->$name = $value;
133
            }, null, $metadata->class);
134
            $this->writeAccessors[$metadata->class] = $accessor;
135
        }
136
137
        $accessor($object, $metadata->name, $value);
138
    }
139
}
140