Completed
Push — wip-public-release ( 7c11a5...54ec9d )
by Bogdan
05:19
created

PropertiesHandler::setter()   C

Complexity

Conditions 7
Paths 9

Size

Total Lines 36
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 36
c 0
b 0
f 0
rs 6.7272
cc 7
eloc 19
nc 9
nop 4
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of the pinepain/php-v8-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\NamedPropertyHandlerConfiguration;
28
use V8\NameValue;
29
use V8\PropertyAttribute;
30
use V8\PropertyCallbackInfo;
31
use V8\Value;
32
33
34
class PropertiesHandler implements PropertiesHandlerInterface
35
{
36
    /**
37
     * @var CallbackGuardInterface
38
     */
39
    private $guard;
40
    /**
41
     * @var ExtractorInterface
42
     */
43
    private $extractor;
44
45
    public function __construct(CallbackGuardInterface $guard, ExtractorInterface $extractor)
46
    {
47
        $this->guard = $guard;
48
        $this->extractor = $extractor;
49
    }
50
51
    public function getter(RuntimeObject $bridge, NameValue $name, PropertyCallbackInfo $args): void
52
    {
53
        $js_object = $args->this();
54
55
        $spec      = $bridge->getSpec();
56
        $prop_name = $name->value();
57
58
        if (!$spec->hasProperty($prop_name)) {
59
            return;
60
        }
61
62
        $property_spec = $spec->getProperty($prop_name);
63
64
        if ($property_spec instanceof BindingSpecInterface) {
65
            $prop_name     = $property_spec->getName();
66
            $property_spec = $property_spec->getSpec();
67
        }
68
69
        if ($property_spec instanceof FunctionSpecInterface) {
70
            $js_function = $bridge->getMethod($prop_name);
71
            if (!$js_function) {
72
                $func        = new RuntimeFunction($prop_name, [$bridge->getObject(), $prop_name], $property_spec);
73
                $js_function = $bridge->getWrapper()->wrap($args->getIsolate(), $args->getContext(), $func);
74
75
                $bridge->storeMethod($prop_name, $js_function, $js_object);
0 ignored issues
show
Compatibility introduced by
$js_function of type object<V8\Value> is not a sub-type of object<V8\FunctionObject>. It seems like you assume a child class of the class V8\Value to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
76
                // TODO: is TODO below is still relevant?
77
                // TODO: store function object in object map
78
            }
79
            $ret = $js_function;
80
        } else {
81
            // NOTE: we don't handle static properties at this time
82
83
            if ($getter = $property_spec->getGetterName()) {
84
                $prop_value = $bridge->getObject()->$getter();
85
            } else {
86
                $prop_value = $bridge->getObject()->$prop_name;
87
            }
88
89
            $ret = $bridge->getWrapper()->wrap($args->getIsolate(), $args->getContext(), $prop_value);
90
        }
91
92
        $args->getReturnValue()->set($ret);
93
    }
94
95
    public function setter(RuntimeObject $bridge, NameValue $name, Value $value, PropertyCallbackInfo $args): void
96
    {
97
        $spec      = $bridge->getSpec();
98
        $prop_name = $name->value();
99
100
        if (!$spec->hasProperty($prop_name)) {
101
            return;
102
        }
103
104
        $property_spec = $spec->getProperty($prop_name);
105
106
        if ($property_spec instanceof BindingSpecInterface) {
107
            $prop_name     = $property_spec->getName();
108
            $property_spec = $property_spec->getSpec();
109
        }
110
111
        if ($property_spec instanceof FunctionSpecInterface || $property_spec->isReadonly()) {
112
            return;
113
        }
114
115
        try {
116
            $prop_value = $this->extractor->extract($args->getContext(), $value, $property_spec->getExtractorDefinition());
117
        } catch (ExtractorException $e) {
118
            throw new NativeException("Failed to set property value: {$e->getMessage()}");
119
        }
120
121
        // NOTE: we don't handle static props here
122
123
        if ($setter = $property_spec->getSetterName()) {
124
            $bridge->getObject()->$setter($prop_value);
125
126
            return;
127
        }
128
129
        $bridge->getObject()->$prop_name = $prop_value;
130
    }
131
132
    public function query(RuntimeObject $bridge, NameValue $name, PropertyCallbackInfo $args): void
133
    {
134
        $spec      = $bridge->getSpec();
135
        $prop_name = $name->value();
136
137
        if (!$spec->hasProperty($prop_name)) {
138
            return;
139
        }
140
141
        $property_spec = $spec->getProperty($prop_name);
142
143
        if ($property_spec instanceof BindingSpecInterface) {
144
            $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...
145
            $property_spec = $property_spec->getSpec();
146
        }
147
148
        // Functions should be always read-only
149
        if ($property_spec instanceof FunctionSpecInterface || $property_spec->isReadonly()) {
150
            $args->getReturnValue()->setInteger(PropertyAttribute::READ_ONLY | PropertyAttribute::DONT_DELETE);
151
        }
152
153
        $args->getReturnValue()->setInteger(PropertyAttribute::DONT_DELETE);
154
    }
155
156
    public function deleter(RuntimeObject $bridge, NameValue $name, PropertyCallbackInfo $args): void
157
    {
158
        $spec      = $bridge->getSpec();
159
        $prop_name = $name->value();
160
161
        if (!$spec->hasProperty($prop_name)) {
162
            return;
163
        }
164
165
        $args->getReturnValue()->setBool(false); // We don't allow to delete any property from js
166
    }
167
168
    public function enumerator(RuntimeObject $bridge, PropertyCallbackInfo $args): void
169
    {
170
        $spec = $bridge->getSpec();
171
172
        $names = array_keys($spec->getProperties());
173
174
        $args->getReturnValue()->set($bridge->getWrapper()->wrap($args->getIsolate(), $args->getContext(), $names));
175
    }
176
177
    public function createGetter(RuntimeObject $bridge): callable
178
    {
179
        return $this->guard->guard(function (NameValue $name, PropertyCallbackInfo $args) use ($bridge) {
180
            $this->getter($bridge, $name, $args);
181
        });
182
    }
183
184
    public function createSetter(RuntimeObject $bridge): callable
185
    {
186
        return $this->guard->guard(function (NameValue $name, Value $value, PropertyCallbackInfo $args) use ($bridge) {
187
            $this->setter($bridge, $name, $value, $args);
188
        });
189
    }
190
191
    public function createQuery(RuntimeObject $bridge): callable
192
    {
193
        return $this->guard->guard(function (NameValue $name, PropertyCallbackInfo $args) use ($bridge) {
194
            $this->query($bridge, $name, $args);
195
        });
196
    }
197
198
    public function createDeleter(RuntimeObject $bridge): callable
199
    {
200
        return $this->guard->guard(function (NameValue $name, PropertyCallbackInfo $args) use ($bridge) {
201
            $this->deleter($bridge, $name, $args);
202
        });
203
    }
204
205
    public function createEnumerator(RuntimeObject $bridge): callable
206
    {
207
        return $this->guard->guard(function (PropertyCallbackInfo $args) use ($bridge) {
208
            $this->enumerator($bridge, $args);
209
        });
210
    }
211
212
    public function createConfiguration(RuntimeObject $bridge): NamedPropertyHandlerConfiguration
213
    {
214
        return new NamedPropertyHandlerConfiguration(
215
            $this->createGetter($bridge),
216
            $this->createSetter($bridge),
217
            $this->createQuery($bridge),
218
            $this->createDeleter($bridge),
219
            $this->createEnumerator($bridge)
220
        );
221
    }
222
}
223