forked from louislam/redbean-node
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmagic-methods.ts
119 lines (100 loc) · 3.49 KB
/
magic-methods.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
* From: https://gist.github.com/loilo/4d385d64e2b8552dcc12a0f5126b6df8
* @param clazz
*/
import {RedBeanNode} from "./redbean-node";
import defineProperty = Reflect.defineProperty;
export function magicMethods (clazz) {
// A toggle switch for the __isset method
// Needed to control "prop in instance" inside of getters
let issetEnabled = true
const classHandler = Object.create(null)
// Trap for class instantiation
classHandler.construct = (target, args, receiver) => {
// Wrapped class instance
const instance = Reflect.construct(target, args, receiver)
// Instance traps
const instanceHandler = Object.create(null)
// __get()
// Catches "instance.property"
const get = Object.getOwnPropertyDescriptor(clazz.prototype, '__get')
if (get) {
instanceHandler.get = (target, name, receiver) => {
// We need to turn off the __isset() trap for the moment to establish compatibility with PHP behaviour
// PHP's __get() method doesn't care about its own __isset() method, so neither should we
issetEnabled = false
const exists = Reflect.has(target, name)
issetEnabled = true
if (exists) {
return Reflect.get(target, name, receiver)
} else {
// No idea why Bean is being called then(), just skip this
if (name == "then" && args[1] instanceof RedBeanNode) {
return undefined;
}
return get.value.call(target, name)
}
}
}
// __set()
// Catches "instance.property = ..."
const set = Object.getOwnPropertyDescriptor(clazz.prototype, '__set')
if (set) {
instanceHandler.set = (target, name, value, receiver) => {
target.__set.call(target, name, value, receiver)
return true;
}
}
// __defineProperty()
// Catches "instance.property = ..."
const defineProperty = Object.getOwnPropertyDescriptor(clazz.prototype, '__defineProperty')
if (defineProperty) {
instanceHandler.defineProperty = (target, name, desc) => {
if (name in target) {
Reflect.defineProperty(target, name, desc)
} else {
target.__set.call(target, name, desc)
}
return target;
}
}
// __isset()
// Catches "'property' in instance"
const isset = Object.getOwnPropertyDescriptor(clazz.prototype, '__isset')
if (isset) {
instanceHandler.has = (target, name) => {
if (!issetEnabled) return Reflect.has(target, name)
return isset.value.call(target, name)
}
}
// __unset()
// Catches "delete instance.property"
const unset = Object.getOwnPropertyDescriptor(clazz.prototype, '__unset')
if (unset) {
instanceHandler.deleteProperty = (target, name) => {
return unset.value.call(target, name)
}
}
return new Proxy(instance, instanceHandler)
}
// __getStatic()
// Catches "class.property"
if (Object.getOwnPropertyDescriptor(clazz, '__getStatic')) {
classHandler.get = (target, name, receiver) => {
if (name in target) {
return target[name]
} else {
return undefined;
}
}
}
// __setStatic()
// Catches "class.property = ..."
if (Object.getOwnPropertyDescriptor(clazz, '__setStatic')) {
classHandler.set = (target, name, value, receiver) => {
target.__setStatic.call(receiver, name, value)
return true;
}
}
return new Proxy(clazz, classHandler)
}