Passed
Branch work-fleets (125b09)
by Sabine
07:19
created

PropertyHider::deltaProperty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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;
0 ignored issues
show
Unused Code introduced by
$result 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...
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