|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* Class PropertyHider |
|
5
|
|
|
* |
|
6
|
|
|
* Hides properties from visibility |
|
7
|
|
|
* Implements new set of methods adjXXX |
|
8
|
|
|
* - adjXXX method returns value of property adjusted by $diff |
|
9
|
|
|
*/ |
|
10
|
|
|
class PropertyHider extends stdClass { |
|
11
|
|
|
|
|
12
|
|
|
/** |
|
13
|
|
|
* Property list |
|
14
|
|
|
* |
|
15
|
|
|
* @var array[] |
|
16
|
|
|
*/ |
|
17
|
|
|
protected static $_properties = array(); |
|
18
|
|
|
|
|
19
|
|
|
/** |
|
20
|
|
|
* List of property names that was changed since last DB operation |
|
21
|
|
|
* |
|
22
|
|
|
* @var boolean[] |
|
23
|
|
|
*/ |
|
24
|
|
|
protected $propertiesChanged = array(); |
|
25
|
|
|
/** |
|
26
|
|
|
* List of property names->$delta that was adjusted since last DB operation - and then need to be processed as Deltas |
|
27
|
|
|
* |
|
28
|
|
|
* @var array |
|
29
|
|
|
*/ |
|
30
|
|
|
protected $propertiesAdjusted = array(); |
|
31
|
|
|
|
|
32
|
|
|
//+ |
|
33
|
|
|
/** |
|
34
|
|
|
* @param array $properties |
|
35
|
|
|
*/ |
|
36
|
1 |
|
public static function setProperties($properties) { |
|
37
|
|
|
// TODO - reset internals?? |
|
38
|
1 |
|
static::$_properties = $properties; |
|
39
|
1 |
|
} |
|
40
|
|
|
|
|
41
|
|
|
//+ |
|
42
|
1 |
|
public static function getProperties() { |
|
43
|
1 |
|
return static::$_properties; |
|
44
|
|
|
} |
|
45
|
|
|
|
|
46
|
|
|
//+ |
|
47
|
|
|
/** |
|
48
|
|
|
* PropertyHider constructor. |
|
49
|
|
|
*/ |
|
50
|
1 |
|
public function __construct() { |
|
51
|
1 |
|
} |
|
52
|
|
|
|
|
53
|
|
|
//+ |
|
54
|
2 |
|
protected function checkPropertyExists($name) { |
|
55
|
2 |
View Code Duplication |
if (!array_key_exists($name, static::$_properties)) { |
|
|
|
|
|
|
56
|
1 |
|
throw new ExceptionPropertyNotExists('Property [' . get_called_class() . '::' . $name . '] not exists', ERR_ERROR); |
|
57
|
|
|
} |
|
58
|
1 |
|
} |
|
59
|
|
|
|
|
60
|
|
|
//+ |
|
61
|
2 |
|
protected function checkOverwriteAdjusted($name) { |
|
62
|
2 |
View Code Duplication |
if (array_key_exists($name, $this->propertiesAdjusted)) { |
|
|
|
|
|
|
63
|
1 |
|
throw new PropertyAccessException('Property [' . get_called_class() . '::' . $name . '] already was adjusted so no SET is possible until dbSave', ERR_ERROR); |
|
64
|
|
|
} |
|
65
|
1 |
|
} |
|
66
|
|
|
|
|
67
|
|
|
//+ |
|
68
|
1 |
|
private function getPhysicalPropertyName($name) { |
|
69
|
1 |
|
return '_' . $name; |
|
70
|
|
|
} |
|
71
|
|
|
|
|
72
|
|
|
//+ |
|
73
|
1 |
|
protected function isPropertyDeclared($name) { |
|
74
|
1 |
|
return property_exists($this, $this->getPhysicalPropertyName($name)); |
|
75
|
|
|
} |
|
76
|
|
|
|
|
77
|
|
|
//+ |
|
78
|
|
|
/** |
|
79
|
|
|
* Getter with support of protected methods |
|
80
|
|
|
* |
|
81
|
|
|
* @param $name |
|
82
|
|
|
* |
|
83
|
|
|
* @return mixed |
|
84
|
|
|
* @throws ExceptionPropertyNotExists |
|
85
|
|
|
*/ |
|
86
|
3 |
View Code Duplication |
public function __get($name) { |
|
|
|
|
|
|
87
|
3 |
|
$this->checkPropertyExists($name); |
|
88
|
|
|
|
|
89
|
2 |
|
$result = null; |
|
|
|
|
|
|
90
|
|
|
// Now deciding - will we call a protected setter or will we work with protected property |
|
91
|
2 |
|
if (method_exists($this, $methodName = 'get' . ucfirst($name))) { |
|
92
|
|
|
// If method exists - just calling it |
|
93
|
1 |
|
$result = call_user_func_array(array($this, $methodName), array()); |
|
94
|
2 |
|
} elseif ($this->isPropertyDeclared($name)) { |
|
95
|
|
|
// No getter exists - works directly with protected property |
|
96
|
1 |
|
$result = $this->{$this->getPhysicalPropertyName($name)}; |
|
97
|
1 |
|
} else { |
|
98
|
1 |
|
throw new ExceptionPropertyNotExists('Property [' . get_called_class() . '::' . $name . '] does not have getter/property to get', ERR_ERROR); |
|
99
|
|
|
} |
|
100
|
|
|
|
|
101
|
1 |
|
return $result; |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
//+ |
|
105
|
|
|
/** |
|
106
|
|
|
* Unsafe setter - w/o checking if the property was already adjusted |
|
107
|
|
|
* |
|
108
|
|
|
* @param string $name |
|
109
|
|
|
* @param mixed $value |
|
110
|
|
|
* |
|
111
|
|
|
* @return mixed|null |
|
112
|
|
|
* @throws ExceptionPropertyNotExists |
|
113
|
|
|
*/ |
|
114
|
3 |
View Code Duplication |
protected function _setUnsafe($name, $value) { |
|
|
|
|
|
|
115
|
3 |
|
$result = null; |
|
116
|
|
|
// Now deciding - will we call a protected setter or will we work with protected property |
|
117
|
3 |
|
if (method_exists($this, $methodName = 'set' . ucfirst($name))) { |
|
118
|
|
|
// If method exists - just calling it |
|
119
|
|
|
// TODO - should return TRUE if value changed or FALSE otherwise |
|
120
|
1 |
|
$result = call_user_func_array(array($this, $methodName), array($value)); |
|
121
|
3 |
|
} elseif ($this->isPropertyDeclared($name)) { |
|
122
|
|
|
// No setter exists - works directly with protected property |
|
123
|
|
|
// if($result = $this->$propertyName !== $value) { |
|
124
|
1 |
|
$this->{$this->getPhysicalPropertyName($name)} = $value; |
|
125
|
|
|
// } |
|
126
|
1 |
|
} else { |
|
127
|
2 |
|
throw new ExceptionPropertyNotExists('Property [' . get_called_class() . '::' . $name . '] does not have setter/property to set', ERR_ERROR); |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
// TODO - should be primed only if value changed |
|
131
|
|
|
// if($result) { |
|
132
|
1 |
|
$this->propertiesChanged[$name] = true; |
|
133
|
|
|
|
|
134
|
|
|
// } |
|
135
|
|
|
|
|
136
|
1 |
|
return $result; |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* Setter wrapper with support of protected properties/methods |
|
141
|
|
|
* |
|
142
|
|
|
* @param string $name |
|
143
|
|
|
* @param mixed $value |
|
144
|
|
|
* |
|
145
|
|
|
* @return mixed|null |
|
146
|
|
|
* @throws ExceptionPropertyNotExists |
|
147
|
|
|
*/ |
|
148
|
|
|
// TODO - сеттер должен параллельно изменять значение db_row - for now... |
|
149
|
|
|
// TODO - Проверка, а действительно ли данные были изменены?? Понадобится определение типов - разные типы сравниваются по-разному |
|
150
|
1 |
|
public function __set($name, $value) { |
|
151
|
1 |
|
$this->checkPropertyExists($name); |
|
152
|
1 |
|
$this->checkOverwriteAdjusted($name); |
|
153
|
|
|
|
|
154
|
1 |
|
return $this->_setUnsafe($name, $value); |
|
155
|
|
|
} |
|
156
|
|
|
|
|
157
|
|
|
|
|
158
|
|
|
/** |
|
159
|
|
|
* @param string $name |
|
160
|
|
|
* @param int $diff |
|
161
|
|
|
* |
|
162
|
|
|
* @return int |
|
163
|
|
|
*/ |
|
164
|
2 |
|
protected function _adjustValueInteger($name, $diff) { |
|
165
|
2 |
|
return intval($this->$name) + intval($diff); |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
/** |
|
169
|
|
|
* @param string $name |
|
170
|
|
|
* @param float $diff |
|
171
|
|
|
* |
|
172
|
|
|
* @return float |
|
173
|
|
|
*/ |
|
174
|
1 |
|
protected function _adjustValueDouble($name, $diff) { |
|
175
|
1 |
|
return floatval($this->$name) + floatval($diff); |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
/** |
|
179
|
|
|
* @param string $name |
|
180
|
|
|
* @param string $diff |
|
181
|
|
|
* |
|
182
|
|
|
* @return string |
|
183
|
|
|
*/ |
|
184
|
1 |
|
protected function _adjustValueString($name, $diff) { |
|
185
|
1 |
|
return (string)$this->$name . (string)$diff; |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
/** |
|
189
|
|
|
* @param string $name |
|
190
|
|
|
* @param array $diff |
|
191
|
|
|
* |
|
192
|
|
|
* @return array |
|
193
|
|
|
*/ |
|
194
|
1 |
|
protected function _adjustValueArray($name, $diff) { |
|
195
|
1 |
|
$copy = (array)$this->$name; |
|
196
|
1 |
|
HelperArray::merge($copy, (array)$diff, HelperArray::MERGE_PHP); |
|
197
|
1 |
|
return $copy; |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
/** |
|
201
|
|
|
* @param string $name |
|
202
|
|
|
* @param int $diff |
|
203
|
|
|
* |
|
204
|
|
|
* @return int |
|
205
|
|
|
*/ |
|
206
|
2 |
|
protected function _adjustValueIntegerDiff($name, $diff) { |
|
207
|
2 |
|
return (int)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, 0) + (int)$diff; |
|
208
|
|
|
} |
|
209
|
|
|
|
|
210
|
|
|
/** |
|
211
|
|
|
* @param string $name |
|
212
|
|
|
* @param float $diff |
|
213
|
|
|
* |
|
214
|
|
|
* @return float |
|
215
|
|
|
*/ |
|
216
|
1 |
|
protected function _adjustValueDoubleDiff($name, $diff) { |
|
217
|
1 |
|
return (float)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, 0.0) + (float)$diff; |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
/** |
|
221
|
|
|
* @param string $name |
|
222
|
|
|
* @param string $diff |
|
223
|
|
|
* |
|
224
|
|
|
* @return string |
|
225
|
|
|
*/ |
|
226
|
1 |
|
protected function _adjustValueStringDiff($name, $diff) { |
|
227
|
1 |
|
return (string)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, '') . (string)$diff; |
|
228
|
|
|
} |
|
229
|
|
|
|
|
230
|
|
|
/** |
|
231
|
|
|
* @param string $name |
|
232
|
|
|
* @param array $diff |
|
233
|
|
|
* |
|
234
|
|
|
* @return array |
|
235
|
|
|
*/ |
|
236
|
1 |
|
protected function _adjustValueArrayDiff($name, $diff) { |
|
237
|
1 |
|
$copy = (array)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, array()); |
|
238
|
1 |
|
HelperArray::merge($copy, $diff, HelperArray::MERGE_PHP); |
|
239
|
1 |
|
return $copy; |
|
240
|
|
|
} |
|
241
|
|
|
|
|
242
|
|
|
/** |
|
243
|
|
|
* Get adjusted value by callback with generated name |
|
244
|
|
|
* Support types: "integer", "double", "string", "array" |
|
245
|
|
|
* Throws exception on: "boolean", "object", "resource", "NULL", "unknown type", |
|
246
|
|
|
* |
|
247
|
|
|
* @param string $name |
|
248
|
|
|
* @param mixed $diff |
|
249
|
|
|
* @param string $suffix |
|
250
|
|
|
* |
|
251
|
|
|
* @return mixed |
|
252
|
|
|
* @throws ExceptionTypeUnsupported |
|
253
|
|
|
*/ |
|
254
|
9 |
|
protected function propertyMethodResult($name, $diff, $suffix = '') { |
|
255
|
|
|
// TODO - property type checks |
|
256
|
|
|
// Capitalizing type name |
|
257
|
9 |
|
$type = explode(' ', gettype($this->$name)); |
|
258
|
9 |
|
array_walk($type, 'DbSqlHelper::UCFirstByRef'); |
|
259
|
9 |
|
$type = implode('', $type); |
|
260
|
|
|
|
|
261
|
9 |
|
if (!method_exists($this, $methodName = '_adjustValue' . $type . $suffix)) { |
|
262
|
1 |
|
throw new ExceptionTypeUnsupported(); |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
8 |
|
return call_user_func(array($this, $methodName), $name, $diff); |
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
/** |
|
269
|
|
|
* Directly adjusts value without Adjuster |
|
270
|
|
|
* |
|
271
|
|
|
* @param string $name |
|
272
|
|
|
* @param mixed $diff |
|
273
|
|
|
* |
|
274
|
|
|
* @return mixed |
|
275
|
|
|
*/ |
|
276
|
1 |
|
protected function _adjustValue($name, $diff) { |
|
277
|
1 |
|
return $this->propertyMethodResult($name, $diff); |
|
278
|
|
|
} |
|
279
|
|
|
|
|
280
|
|
|
/** |
|
281
|
|
|
* Directly adjusts value DIFF without Adjuster |
|
282
|
|
|
* |
|
283
|
|
|
* @param string $name |
|
284
|
|
|
* @param mixed $diff |
|
285
|
|
|
* |
|
286
|
|
|
* @return mixed |
|
287
|
|
|
*/ |
|
288
|
1 |
|
protected function _adjustValueDiff($name, $diff) { |
|
289
|
1 |
|
return $this->propertyMethodResult($name, $diff, 'Diff'); |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
/** |
|
293
|
|
|
* Adjust property value with $diff |
|
294
|
|
|
* Adjusted values put into DB with UPDATE query |
|
295
|
|
|
* Adjuster callback adjXXX() should return new value which will be propagated via __set() |
|
296
|
|
|
* Optionally there can be DIFF-adjuster adjXXXDiff() for complex types |
|
297
|
|
|
* |
|
298
|
|
|
* @param string $name |
|
299
|
|
|
* @param mixed $diff |
|
300
|
|
|
* |
|
301
|
|
|
* @return mixed |
|
302
|
|
|
*/ |
|
303
|
2 |
|
public function __adjust($name, $diff) { |
|
304
|
2 |
|
$this->checkPropertyExists($name); |
|
305
|
|
|
|
|
306
|
|
|
// Now deciding - will we call a protected setter or will we work with protected property |
|
307
|
2 |
|
if (method_exists($this, $methodName = 'adj' . ucfirst($name))) { |
|
308
|
|
|
// If method exists - just calling it |
|
309
|
|
|
// Method returns new adjusted value |
|
310
|
2 |
|
$newValue = call_user_func_array(array($this, $methodName), array($diff)); |
|
311
|
2 |
|
} else { |
|
312
|
|
|
// No adjuster exists - works directly with protected property |
|
313
|
|
|
// TODO - property type checks |
|
314
|
1 |
|
$newValue = $this->_adjustValue($name, $diff); |
|
315
|
|
|
} |
|
316
|
|
|
|
|
317
|
|
|
// Invoking property setter |
|
318
|
2 |
|
$this->_setUnsafe($name, $newValue); |
|
319
|
|
|
|
|
320
|
|
|
// Initializing value of adjustment |
|
321
|
2 |
|
if (!array_key_exists($name, $this->propertiesAdjusted)) { |
|
322
|
2 |
|
$this->propertiesAdjusted[$name] = null; |
|
323
|
2 |
|
} |
|
324
|
|
|
|
|
325
|
|
|
// Adding diff to adjustment accumulator |
|
326
|
2 |
|
if (method_exists($this, $methodName = 'adj' . ucfirst($name) . 'Diff')) { |
|
327
|
1 |
|
call_user_func_array(array($this, $methodName), array($diff)); |
|
328
|
1 |
|
} else { |
|
329
|
|
|
// TODO - property type checks |
|
330
|
1 |
|
$this->propertiesAdjusted[$name] = $this->_adjustValueDiff($name, $diff); |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
2 |
|
return $this->$name; |
|
334
|
|
|
} |
|
335
|
|
|
|
|
336
|
|
|
} |
|
337
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.