1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Class PropertyHider - Hides properties from visibility |
5
|
|
|
* |
6
|
|
|
* - Property access - via property names |
7
|
|
|
* - Allowed property name is an index in static $_properties() array |
8
|
|
|
* - Property XXX can have 4 accessories: |
9
|
|
|
* - getXXX - getter |
10
|
|
|
* - setXXX - setter. Setting property is logging in $propertiesChanged array |
11
|
|
|
* - adjustXXX - adjusts value. Delta from original value is holding in $propertiesAdjusted array |
12
|
|
|
* - deltaXXX - calculates delta from original value |
13
|
|
|
* - Accessories can have any visibility |
14
|
|
|
* - Public accessories should take care setting elements of $propertiesChanged/$propertiesAdjusted arrays if needed |
15
|
|
|
* |
16
|
|
|
* |
17
|
|
|
* Implements new set of methods adjXXX |
18
|
|
|
* - adjXXX method returns value of property adjusted by $diff |
19
|
|
|
*/ |
20
|
|
|
abstract class PropertyHider extends stdClass { |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Getting value |
24
|
|
|
*/ |
25
|
|
|
const ACTION_GET = 'get'; |
26
|
|
|
/** |
27
|
|
|
* Setting value |
28
|
|
|
*/ |
29
|
|
|
const ACTION_SET = 'set'; |
30
|
|
|
/** |
31
|
|
|
* Adjusting value |
32
|
|
|
*/ |
33
|
|
|
const ACTION_ADJUST = 'adjust'; |
34
|
|
|
/** |
35
|
|
|
* Calculating value |
36
|
|
|
*/ |
37
|
|
|
const ACTION_DELTA = 'delta'; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Property list |
41
|
|
|
* |
42
|
|
|
* @var array[] |
43
|
|
|
*/ |
44
|
|
|
protected $_properties = array(); |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* List of property names that was changed since last DB operation |
48
|
|
|
* |
49
|
|
|
* @var boolean[] |
50
|
|
|
*/ |
51
|
|
|
protected $propertiesChanged = array(); |
52
|
|
|
/** |
53
|
|
|
* List of property names->$delta that was adjusted since last DB operation - and then need to be processed as Deltas |
54
|
|
|
* |
55
|
|
|
* @var array |
56
|
|
|
*/ |
57
|
|
|
protected $propertiesAdjusted = array(); |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @param array $properties |
61
|
|
|
*/ |
62
|
1 |
|
public function setProperties($properties) { |
63
|
|
|
// TODO - reset internals?? |
64
|
1 |
|
$this->_properties = $properties; |
65
|
1 |
|
} |
66
|
|
|
|
67
|
1 |
|
public function getProperties() { |
68
|
1 |
|
return $this->_properties; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* PropertyHider constructor. |
73
|
|
|
*/ |
74
|
1 |
|
public function __construct() { |
75
|
1 |
|
} |
76
|
|
|
|
77
|
2 |
|
protected function checkPropertyExists($name) { |
78
|
2 |
View Code Duplication |
if (!array_key_exists($name, $this->_properties)) { |
|
|
|
|
79
|
1 |
|
throw new ExceptionPropertyNotExists('Property [' . get_called_class() . '::' . $name . '] not exists', ERR_ERROR); |
80
|
|
|
} |
81
|
1 |
|
} |
82
|
|
|
|
83
|
2 |
|
protected function checkOverwriteAdjusted($name) { |
84
|
2 |
View Code Duplication |
if (array_key_exists($name, $this->propertiesAdjusted)) { |
|
|
|
|
85
|
1 |
|
throw new PropertyAccessException('Property [' . get_called_class() . '::' . $name . '] already was adjusted so no SET is possible until dbSave', ERR_ERROR); |
86
|
|
|
} |
87
|
1 |
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Method checks if action is available for named property |
91
|
|
|
* |
92
|
|
|
* @param string $name |
93
|
|
|
* @param string $action |
94
|
|
|
* |
95
|
|
|
* @return bool |
96
|
|
|
*/ |
97
|
|
|
abstract protected function isPropertyActionAvailable($name, $action = ''); |
98
|
|
|
/** |
99
|
|
|
* Internal method that make real changes to property value |
100
|
|
|
* May be override in child class |
101
|
|
|
* |
102
|
|
|
* @param string $name |
103
|
|
|
* @param mixed $value |
104
|
|
|
* |
105
|
|
|
* @return mixed |
106
|
|
|
*/ |
107
|
|
|
abstract protected function setProperty($name, $value); |
108
|
|
|
/** |
109
|
|
|
* Internal method that make really reads property value |
110
|
|
|
* May be override in child class |
111
|
|
|
* |
112
|
|
|
* @param string $name |
113
|
|
|
* @param mixed $value - ignored. Used for compatibility |
114
|
|
|
* |
115
|
|
|
* @return mixed |
116
|
|
|
*/ |
117
|
|
|
abstract protected function getProperty($name, $value = null); |
118
|
|
|
/** |
119
|
|
|
* Magic method that checks if named property is set |
120
|
|
|
* May be override in child class |
121
|
|
|
* |
122
|
|
|
* @param $name |
123
|
|
|
* |
124
|
|
|
* @return bool |
125
|
|
|
*/ |
126
|
|
|
abstract public function __isset($name); |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Directly adjusts value without Adjuster |
130
|
|
|
* |
131
|
|
|
* @param string $name |
132
|
|
|
* @param mixed $diff |
133
|
|
|
* |
134
|
|
|
* @return mixed |
135
|
|
|
*/ |
136
|
1 |
|
protected function adjustProperty($name, $diff) { |
137
|
1 |
|
return $this->propertyMethodResult($name, $diff, 'adjustProperty'); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Directly adjusts value Delta for properties without Adjuster |
142
|
|
|
* |
143
|
|
|
* @param string $name |
144
|
|
|
* @param mixed $diff |
145
|
|
|
* |
146
|
|
|
* @return mixed |
147
|
|
|
*/ |
148
|
1 |
|
protected function deltaProperty($name, $diff) { |
149
|
1 |
|
return $this->propertyMethodResult($name, $diff, 'delta'); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* Performs '$action' on property with $name - possible with $value |
154
|
|
|
* |
155
|
|
|
* Universal method for call getter, setter, adjuster or delta calculator |
156
|
|
|
* |
157
|
|
|
* @param string $action |
158
|
|
|
* @param string $name |
159
|
|
|
* @param mixed $value |
160
|
|
|
* |
161
|
|
|
* @return mixed|null |
162
|
|
|
* @throws ExceptionPropertyNotExists |
163
|
|
|
*/ |
164
|
|
|
protected function actionProperty($action, $name, $value) { |
165
|
|
|
$result = null; |
|
|
|
|
166
|
|
|
// Now deciding - will we call a protected setter or will we work with protected property |
167
|
|
|
// Todo - on init recalc all method_exists |
168
|
|
|
if (method_exists($this, $methodName = $action . ucfirst($name))) { |
169
|
|
|
// If method exists - just calling it |
170
|
|
|
// TODO - should return TRUE if value changed or FALSE otherwise |
171
|
|
|
$result = call_user_func_array(array($this, $methodName), array($value)); |
172
|
|
|
} elseif ($this->isPropertyActionAvailable($name, $action)) { |
173
|
|
|
// No setter exists - works directly with protected property |
174
|
|
|
$result = $this->{$action . 'Property'}($name, $value); |
175
|
|
|
} else { |
176
|
|
|
throw new ExceptionPropertyNotExists('Property [' . get_called_class() . '::' . $name . '] does not have ' . $action . 'ter/property to ' . $action, ERR_ERROR); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
return $result; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Getter with support of protected methods |
184
|
|
|
* |
185
|
|
|
* @param $name |
186
|
|
|
* |
187
|
|
|
* @return mixed |
188
|
|
|
* @throws ExceptionPropertyNotExists |
189
|
|
|
*/ |
190
|
3 |
|
public function __get($name) { |
191
|
3 |
|
$this->checkPropertyExists($name); |
192
|
|
|
|
193
|
2 |
|
$result = $this->actionProperty('get', $name, null); |
194
|
|
|
|
195
|
1 |
|
return $result; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
|
199
|
|
|
//+ |
200
|
|
|
/** |
201
|
|
|
* Unsafe setter - w/o checking if the property was already adjusted |
202
|
|
|
* |
203
|
|
|
* @param string $name |
204
|
|
|
* @param mixed $value |
205
|
|
|
* |
206
|
|
|
* @return mixed|null |
207
|
|
|
* @throws ExceptionPropertyNotExists |
208
|
|
|
*/ |
209
|
3 |
|
protected function _setUnsafe($name, $value) { |
210
|
3 |
|
$result = $this->actionProperty('set', $name, $value); |
211
|
|
|
|
212
|
|
|
// TODO - should be primed only if value changed |
213
|
|
|
// if($result) { |
214
|
1 |
|
$this->propertiesChanged[$name] = true; |
215
|
|
|
|
216
|
|
|
// } |
217
|
|
|
|
218
|
1 |
|
return $result; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Setter wrapper with support of protected properties/methods |
223
|
|
|
* |
224
|
|
|
* @param string $name |
225
|
|
|
* @param mixed $value |
226
|
|
|
* |
227
|
|
|
* @return mixed|null |
228
|
|
|
* @throws ExceptionPropertyNotExists |
229
|
|
|
*/ |
230
|
|
|
// TODO - сеттер должен параллельно изменять значение db_row - for now... |
231
|
|
|
// TODO - Проверка, а действительно ли данные были изменены?? Понадобится определение типов - разные типы сравниваются по-разному |
232
|
1 |
|
public function __set($name, $value) { |
233
|
1 |
|
$this->checkOverwriteAdjusted($name); |
234
|
1 |
|
$this->checkPropertyExists($name); |
235
|
|
|
|
236
|
1 |
|
return $this->_setUnsafe($name, $value); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* @param string $name |
242
|
|
|
* @param int $diff |
243
|
|
|
* |
244
|
|
|
* @return int |
245
|
|
|
*/ |
246
|
2 |
|
protected function adjustPropertyInteger($name, $diff) { |
247
|
2 |
|
return intval($this->$name) + intval($diff); |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* @param string $name |
252
|
|
|
* @param float $diff |
253
|
|
|
* |
254
|
|
|
* @return float |
255
|
|
|
*/ |
256
|
1 |
|
protected function adjustPropertyDouble($name, $diff) { |
257
|
1 |
|
return floatval($this->$name) + floatval($diff); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
/** |
261
|
|
|
* @param string $name |
262
|
|
|
* @param string $diff |
263
|
|
|
* |
264
|
|
|
* @return string |
265
|
|
|
*/ |
266
|
1 |
|
protected function adjustPropertyString($name, $diff) { |
267
|
1 |
|
return (string)$this->$name . (string)$diff; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* @param string $name |
272
|
|
|
* @param array $diff |
273
|
|
|
* |
274
|
|
|
* @return array |
275
|
|
|
*/ |
276
|
1 |
|
protected function adjustPropertyArray($name, $diff) { |
277
|
1 |
|
$copy = (array)$this->$name; |
278
|
1 |
|
HelperArray::merge($copy, (array)$diff, HelperArray::MERGE_PHP); |
279
|
|
|
|
280
|
1 |
|
return $copy; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* @param string $name |
285
|
|
|
* @param int $diff |
286
|
|
|
* |
287
|
|
|
* @return int |
288
|
|
|
*/ |
289
|
2 |
|
protected function deltaInteger($name, $diff) { |
290
|
2 |
|
return (int)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, 0) + (int)$diff; |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
/** |
294
|
|
|
* @param string $name |
295
|
|
|
* @param float $diff |
296
|
|
|
* |
297
|
|
|
* @return float |
298
|
|
|
*/ |
299
|
1 |
|
protected function deltaDouble($name, $diff) { |
300
|
1 |
|
return (float)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, 0.0) + (float)$diff; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* @param string $name |
305
|
|
|
* @param string $diff |
306
|
|
|
* |
307
|
|
|
* @return string |
308
|
|
|
*/ |
309
|
1 |
|
protected function deltaString($name, $diff) { |
310
|
1 |
|
return (string)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, '') . (string)$diff; |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* @param string $name |
315
|
|
|
* @param array $diff |
316
|
|
|
* |
317
|
|
|
* @return array |
318
|
|
|
*/ |
319
|
1 |
|
protected function deltaArray($name, $diff) { |
320
|
1 |
|
$copy = (array)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, array()); |
321
|
1 |
|
HelperArray::merge($copy, $diff, HelperArray::MERGE_PHP); |
322
|
|
|
|
323
|
1 |
|
return $copy; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Get adjusted value by callback with generated name |
328
|
|
|
* Support types: "integer", "double", "string", "array" |
329
|
|
|
* Throws exception on: "boolean", "object", "resource", "NULL", "unknown type", |
330
|
|
|
* |
331
|
|
|
* @param string $name |
332
|
|
|
* @param mixed $diff |
333
|
|
|
* @param string $prefix |
334
|
|
|
* |
335
|
|
|
* @return mixed |
336
|
|
|
* @throws ExceptionTypeUnsupported |
337
|
|
|
*/ |
338
|
13 |
|
protected function propertyMethodResult($name, $diff, $prefix = '') { |
339
|
13 |
|
$type = gettype($this->$name); |
340
|
|
|
// Capitalizing type name |
341
|
13 |
|
$methodName = explode(' ', $type); |
342
|
13 |
|
array_walk($methodName, 'DbSqlHelper::UCFirstByRef'); |
343
|
13 |
|
$methodName = $prefix . implode('', $methodName); |
344
|
|
|
|
345
|
13 |
|
if (!method_exists($this, $methodName)) { |
346
|
5 |
|
throw new ExceptionTypeUnsupported('Type "' . $type . '" is unsupported in PropertyHider::propertyMethodResult'); |
347
|
|
|
} |
348
|
|
|
|
349
|
8 |
|
return call_user_func(array($this, $methodName), $name, $diff); |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Adjust property value with $diff |
354
|
|
|
* Adjusted values put into DB with UPDATE query |
355
|
|
|
* Adjuster callback adjXXX() should return new value which will be propagated via __set() |
356
|
|
|
* Optionally there can be DIFF-adjuster adjXXXDiff() for complex types |
357
|
|
|
* |
358
|
|
|
* @param string $name |
359
|
|
|
* @param mixed $diff |
360
|
|
|
* |
361
|
|
|
* @return mixed |
362
|
|
|
*/ |
363
|
2 |
|
public function __adjust($name, $diff) { |
364
|
2 |
|
$this->checkPropertyExists($name); |
365
|
|
|
|
366
|
2 |
|
$result = $this->actionProperty('adjust', $name, $diff); |
367
|
|
|
|
368
|
|
|
// Invoking property setter |
369
|
2 |
|
$this->_setUnsafe($name, $result); |
370
|
|
|
|
371
|
|
|
// Initializing value of adjustment |
372
|
2 |
|
if (!array_key_exists($name, $this->propertiesAdjusted)) { |
373
|
2 |
|
$this->propertiesAdjusted[$name] = null; |
374
|
2 |
|
} |
375
|
|
|
|
376
|
2 |
|
$this->propertiesAdjusted[$name] = $this->actionProperty('delta', $name, $diff); |
377
|
|
|
|
378
|
2 |
|
return $this->$name; |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
} |
382
|
|
|
|
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.