1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Bavix\Geo\Value; |
4
|
|
|
|
5
|
|
|
abstract class Valable implements \JsonSerializable |
6
|
|
|
{ |
7
|
|
|
|
8
|
|
|
const READ_ONLY = 1; |
9
|
|
|
const WRITE = 2; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* @var string |
13
|
|
|
*/ |
14
|
|
|
protected $objectId; |
15
|
|
|
|
16
|
|
|
/** |
17
|
|
|
* @var array |
18
|
|
|
*/ |
19
|
|
|
protected $properties; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @var array |
23
|
|
|
*/ |
24
|
|
|
protected $extends; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var array |
28
|
|
|
*/ |
29
|
|
|
private $data = []; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Object constructor. |
33
|
|
|
* |
34
|
|
|
* @param array|null $data |
35
|
|
|
*/ |
36
|
|
|
public function __construct(array $data = null) |
37
|
|
|
{ |
38
|
|
|
$this->objectId = \spl_object_id($this); |
39
|
|
|
|
40
|
|
|
if ($this->extends) { |
|
|
|
|
41
|
|
|
$this->properties = \array_merge_recursive( |
42
|
|
|
$this->properties, |
43
|
|
|
$this->extends |
44
|
|
|
); |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
foreach ((array)$data as $key => $value) { |
48
|
|
|
$this->$key = $value; |
49
|
|
|
} |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @return string |
54
|
|
|
*/ |
55
|
|
|
public function objectId(): string |
56
|
|
|
{ |
57
|
|
|
return $this->objectId; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @param array|null $data |
62
|
|
|
* |
63
|
|
|
* @return static |
64
|
|
|
*/ |
65
|
|
|
public static function make(array $data = null): self |
66
|
|
|
{ |
67
|
|
|
return new static($data); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @param string $name |
72
|
|
|
* @param array $arguments |
73
|
|
|
* |
74
|
|
|
* @return static |
75
|
|
|
*/ |
76
|
|
|
public static function __callStatic(string $name, array $arguments): self |
77
|
|
|
{ |
78
|
|
|
$name = \preg_replace('~([A-Z]{1})~', '_$1', $name, 1); |
79
|
|
|
list($method, $property) = \explode('_', $name, 2); |
80
|
|
|
|
81
|
|
|
if ($method !== 'from') { |
82
|
|
|
throw new \InvalidArgumentException('Method ' . $method . ' not found'); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
return static::make([ |
86
|
|
|
\lcfirst($property) => $arguments[0] ?? 0 |
87
|
|
|
]); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @return static |
92
|
|
|
*/ |
93
|
|
|
public function proxy(): self |
94
|
|
|
{ |
95
|
|
|
static $proxies = []; |
96
|
|
|
|
97
|
|
|
if (empty($proxies[$this->objectId()])) { |
98
|
|
|
$proxies[$this->objectId()] = $self = clone $this; |
99
|
|
|
foreach ((array)$this->properties as $key => $value) { |
100
|
|
|
$self->properties[$key]['type'] = self::READ_ONLY; |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
return $proxies[$this->objectId()]; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @param string $name |
109
|
|
|
*/ |
110
|
|
|
protected function propertyValidate(string $name) |
111
|
|
|
{ |
112
|
|
|
if (empty($this->properties[$name])) { |
113
|
|
|
throw new \InvalidArgumentException('Property not exists'); |
114
|
|
|
} |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* @param string $name |
119
|
|
|
* @param $value |
120
|
|
|
*/ |
121
|
|
|
protected function modifies(string $name, $value) |
122
|
|
|
{ |
123
|
|
|
$this->data[$name] = $value; |
124
|
|
|
|
125
|
|
|
if (isset($this->properties[$name]['modify'])) { |
126
|
|
|
$this->modify($name, $this->properties[$name]['modify']); |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @param string $name |
132
|
|
|
* @param array $props |
133
|
|
|
*/ |
134
|
|
|
protected function modify(string $name, array $props) |
135
|
|
|
{ |
136
|
|
|
foreach ($props as $prop => $callback) { |
137
|
|
|
if (!function_exists($callback) && class_exists($callback)) { |
138
|
|
|
$this->dataSet($prop, $this->classModify($callback, $name, $prop)); |
139
|
|
|
continue; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
$this->dataSet($prop, $this->$callback($this->data[$name], $name, $prop)); |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* @param string $prop |
148
|
|
|
* @param $mixed |
149
|
|
|
*/ |
150
|
|
|
protected function dataSet(string $prop, $mixed) |
151
|
|
|
{ |
152
|
|
|
if ($mixed !== null) { |
153
|
|
|
$this->data[$prop] = $mixed; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* @param string $class |
159
|
|
|
* @param string $name |
160
|
|
|
* @param string $prop |
161
|
|
|
* |
162
|
|
|
* @return mixed |
163
|
|
|
*/ |
164
|
|
|
protected function classModify(string $class, string $name, string $prop) |
165
|
|
|
{ |
166
|
|
|
$object = new $class(); |
167
|
|
|
|
168
|
|
|
if (!($object instanceof ValueInterface)) { |
169
|
|
|
throw new \InvalidArgumentException('Interface ' . ValueInterface::class . ' not found'); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
return $object::modify($this, $name, $prop); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @param string $name |
177
|
|
|
* |
178
|
|
|
* @return mixed |
179
|
|
|
*/ |
180
|
|
|
public function __get(string $name) |
181
|
|
|
{ |
182
|
|
|
$this->propertyValidate($name); |
183
|
|
|
|
184
|
|
|
if ($this->__isset($name)) { |
185
|
|
|
return $this->data[$name]; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
return null; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* @param string $name |
193
|
|
|
* @param $value |
194
|
|
|
*/ |
195
|
|
|
public function __set(string $name, $value) |
196
|
|
|
{ |
197
|
|
|
$this->propertyValidate($name); |
198
|
|
|
|
199
|
|
|
if (empty($this->properties[$name]['type'])) { |
200
|
|
|
throw new \InvalidArgumentException('Type not found'); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
if ($this->properties[$name]['type'] !== self::WRITE) { |
204
|
|
|
throw new \InvalidArgumentException('Magic __set(\'' . $name . '\') is not installed'); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
$this->modifies($name, $value); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* @param string $name |
212
|
|
|
* |
213
|
|
|
* @return bool |
214
|
|
|
*/ |
215
|
|
|
public function __isset(string $name): bool |
216
|
|
|
{ |
217
|
|
|
return |
218
|
|
|
\array_key_exists($name, $this->data) || |
219
|
|
|
isset($this->properties[$name]); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* @return array |
224
|
|
|
*/ |
225
|
|
|
public function jsonSerialize(): array |
226
|
|
|
{ |
227
|
|
|
return $this->data; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
} |
231
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.