PropertiesHandler::setter()   C
last analyzed

Complexity

Conditions 7
Paths 13

Size

Total Lines 39
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 0
cts 21
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 21
nc 13
nop 4
crap 56
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of the pinepain/js-sandbox PHP library.
5
 *
6
 * Copyright (c) 2016-2017 Bogdan Padalko <[email protected]>
7
 *
8
 * Licensed under the MIT license: http://opensource.org/licenses/MIT
9
 *
10
 * For the full copyright and license information, please view the
11
 * LICENSE file that was distributed with this source or visit
12
 * http://opensource.org/licenses/MIT
13
 */
14
15
16
namespace Pinepain\JsSandbox\Wrappers\ObjectComponents;
17
18
19
use Pinepain\JsSandbox\Exceptions\NativeException;
20
use Pinepain\JsSandbox\Extractors\ExtractorException;
21
use Pinepain\JsSandbox\Extractors\ExtractorInterface;
22
use Pinepain\JsSandbox\Specs\BindingSpecInterface;
23
use Pinepain\JsSandbox\Specs\FunctionSpecInterface;
24
use Pinepain\JsSandbox\Wrappers\CallbackGuards\CallbackGuardInterface;
25
use Pinepain\JsSandbox\Wrappers\Runtime\RuntimeFunction;
26
use Pinepain\JsSandbox\Wrappers\Runtime\RuntimeObject;
27
use V8\FunctionObject;
28
use V8\NamedPropertyHandlerConfiguration;
29
use V8\NameValue;
30
use V8\PropertyAttribute;
31
use V8\PropertyCallbackInfo;
32
use V8\Value;
33
34
35
class PropertiesHandler implements PropertiesHandlerInterface
36
{
37
    /**
38
     * @var CallbackGuardInterface
39
     */
40
    private $guard;
41
    /**
42
     * @var ExtractorInterface
43
     */
44
    private $extractor;
45
46
    public function __construct(CallbackGuardInterface $guard, ExtractorInterface $extractor)
47
    {
48
        $this->guard     = $guard;
49
        $this->extractor = $extractor;
50
    }
51
52
    public function getter(RuntimeObject $bridge, NameValue $name, PropertyCallbackInfo $args): void
53
    {
54
        $js_object = $args->this();
55
56
        $spec      = $bridge->getSpec();
57
        $prop_name = $name->value();
58
59
        if (!$spec->hasProperty($prop_name)) {
60
            return;
61
        }
62
63
        $property_spec = $spec->getProperty($prop_name);
64
65
        if ($property_spec instanceof BindingSpecInterface) {
66
            $prop_name     = $property_spec->getName();
67
            $property_spec = $property_spec->getSpec();
68
        }
69
70
        if ($property_spec instanceof FunctionSpecInterface) {
71
            $js_function = $bridge->getMethod($prop_name);
72
            if (!$js_function) {
73
                /** @var callable $callback */
74
                $callback = [$bridge->getObject(), $prop_name];
75
                $func     = new RuntimeFunction($prop_name, $callback, $property_spec);
76
                /** @var FunctionObject $js_function */
77
                $js_function = $bridge->getWrapper()->wrap($args->getIsolate(), $args->getContext(), $func);
78
79
                $bridge->storeMethod($prop_name, $js_function, $js_object);
80
                // TODO: is TODO below is still relevant?
81
                // TODO: store function object in object map
82
            }
83
            $ret = $js_function;
84
        } else {
85
            // NOTE: we don't handle static properties at this time
86
87
            if ($getter = $property_spec->getGetterName()) {
88
                $prop_value = $bridge->getObject()->$getter();
89
            } else {
90
                $prop_value = $bridge->getObject()->$prop_name;
91
            }
92
93
            $ret = $bridge->getWrapper()->wrap($args->getIsolate(), $args->getContext(), $prop_value);
94
        }
95
96
        $args->getReturnValue()->set($ret);
97
    }
98
99
    public function setter(RuntimeObject $bridge, NameValue $name, Value $value, PropertyCallbackInfo $args): void
100
    {
101
        $spec      = $bridge->getSpec();
102
        $prop_name = $name->value();
103
104
        if (!$spec->hasProperty($prop_name)) {
105
            return;
106
        }
107
108
        $property_spec = $spec->getProperty($prop_name);
109
110
        if ($property_spec instanceof BindingSpecInterface) {
111
            $prop_name     = $property_spec->getName();
112
            $property_spec = $property_spec->getSpec();
113
        }
114
115
        if ($property_spec instanceof FunctionSpecInterface || $property_spec->isReadonly()) {
116
            return;
117
        }
118
119
        try {
120
            $definition = $property_spec->getExtractorDefinition();
121
            assert(null !== $definition);
122
123
            $prop_value = $this->extractor->extract($args->getContext(), $value, $definition);
124
        } catch (ExtractorException $e) {
125
            throw new NativeException("Failed to set property value: {$e->getMessage()}");
126
        }
127
128
        // NOTE: we don't handle static props here
129
130
        if ($setter = $property_spec->getSetterName()) {
131
            $bridge->getObject()->$setter($prop_value);
132
133
            return;
134
        }
135
136
        $bridge->getObject()->$prop_name = $prop_value;
137
    }
138
139
    public function query(RuntimeObject $bridge, NameValue $name, PropertyCallbackInfo $args): void
140
    {
141
        $spec      = $bridge->getSpec();
142
        $prop_name = $name->value();
143
144
        if (!$spec->hasProperty($prop_name)) {
145
            return;
146
        }
147
148
        $property_spec = $spec->getProperty($prop_name);
149
150
        if ($property_spec instanceof BindingSpecInterface) {
151
            $prop_name     = $property_spec->getName();
0 ignored issues
show
Unused Code introduced by
$prop_name is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
152
            $property_spec = $property_spec->getSpec();
153
        }
154
155
        // Functions should be always read-only
156
        if ($property_spec instanceof FunctionSpecInterface || $property_spec->isReadonly()) {
157
            $args->getReturnValue()->setInteger(PropertyAttribute::READ_ONLY | PropertyAttribute::DONT_DELETE);
158
        }
159
160
        $args->getReturnValue()->setInteger(PropertyAttribute::DONT_DELETE);
161
    }
162
163
    public function deleter(RuntimeObject $bridge, NameValue $name, PropertyCallbackInfo $args): void
164
    {
165
        $spec      = $bridge->getSpec();
166
        $prop_name = $name->value();
167
168
        if (!$spec->hasProperty($prop_name)) {
169
            return;
170
        }
171
172
        $args->getReturnValue()->setBool(false); // We don't allow to delete any property from js
173
    }
174
175
    public function enumerator(RuntimeObject $bridge, PropertyCallbackInfo $args): void
176
    {
177
        $spec = $bridge->getSpec();
178
179
        $names = array_keys($spec->getProperties());
180
181
        $args->getReturnValue()->set($bridge->getWrapper()->wrap($args->getIsolate(), $args->getContext(), $names));
182
    }
183
184
    public function createGetter(RuntimeObject $bridge): callable
185
    {
186
        return $this->guard->guard(function (NameValue $name, PropertyCallbackInfo $args) use ($bridge) {
187
            $this->getter($bridge, $name, $args);
188
        });
189
    }
190
191
    public function createSetter(RuntimeObject $bridge): callable
192
    {
193
        return $this->guard->guard(function (NameValue $name, Value $value, PropertyCallbackInfo $args) use ($bridge) {
194
            $this->setter($bridge, $name, $value, $args);
195
        });
196
    }
197
198
    public function createQuery(RuntimeObject $bridge): callable
199
    {
200
        return $this->guard->guard(function (NameValue $name, PropertyCallbackInfo $args) use ($bridge) {
201
            $this->query($bridge, $name, $args);
202
        });
203
    }
204
205
    public function createDeleter(RuntimeObject $bridge): callable
206
    {
207
        return $this->guard->guard(function (NameValue $name, PropertyCallbackInfo $args) use ($bridge) {
208
            $this->deleter($bridge, $name, $args);
209
        });
210
    }
211
212
    public function createEnumerator(RuntimeObject $bridge): callable
213
    {
214
        return $this->guard->guard(function (PropertyCallbackInfo $args) use ($bridge) {
215
            $this->enumerator($bridge, $args);
216
        });
217
    }
218
219
    public function createConfiguration(RuntimeObject $bridge): NamedPropertyHandlerConfiguration
220
    {
221
        return new NamedPropertyHandlerConfiguration(
222
            $this->createGetter($bridge),
223
            $this->createSetter($bridge),
224
            $this->createQuery($bridge),
225
            $this->createDeleter($bridge),
226
            $this->createEnumerator($bridge)
227
        );
228
    }
229
}
230