Completed
Push — work-fleets ( 8cb23c...5a7baa )
by SuperNova.WS
05:37
created

PropertyHider::adjustPropertyArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 9.4285
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
   * Getting value
14
   */
15
  const ACTION_GET = 'get';
16
  /**
17
   * Setting value
18
   */
19
  const ACTION_SET = 'set';
20
  /**
21
   * Adjusting value
22
   */
23
  const ACTION_ADJUST = 'adjust';
24
  /**
25
   * Calculating value
26
   */
27
  const ACTION_DELTA = 'delta';
28
29
  /**
30
   * Property list
31
   *
32
   * @var array[]
33
   */
34
  protected static $_properties = array();
35
36
  /**
37
   * List of property names that was changed since last DB operation
38
   *
39
   * @var boolean[]
40
   */
41
  protected $propertiesChanged = array();
42
  /**
43
   * List of property names->$delta that was adjusted since last DB operation - and then need to be processed as Deltas
44
   *
45
   * @var array
46
   */
47
  protected $propertiesAdjusted = array();
48
49
  /**
50
   * @param array $properties
51
   */
52 1
  public static function setProperties($properties) {
53
    // TODO - reset internals??
54 1
    static::$_properties = $properties;
55 1
  }
56
57 1
  public static function getProperties() {
58 1
    return static::$_properties;
59
  }
60
61
  /**
62
   * PropertyHider constructor.
63
   */
64 1
  public function __construct() {
65 1
  }
66
67 2
  protected function checkPropertyExists($name) {
68 2
    if (!array_key_exists($name, static::$_properties)) {
69 1
      throw new ExceptionPropertyNotExists('Property [' . get_called_class() . '::' . $name . '] not exists', ERR_ERROR);
70
    }
71 1
  }
72
73 2
  protected function checkOverwriteAdjusted($name) {
74 2
    if (array_key_exists($name, $this->propertiesAdjusted)) {
75 1
      throw new PropertyAccessException('Property [' . get_called_class() . '::' . $name . '] already was adjusted so no SET is possible until dbSave', ERR_ERROR);
76
    }
77 1
  }
78
79 1
  private function getPhysicalPropertyName($name) {
80 1
    return '_' . $name;
81
  }
82
83
  /**
84
   * Method checks if action is available for named property
85
   *
86
   * @param string $name
87
   * @param string $action
88
   *
89
   * @return bool
90
   */
91 1
  protected function isPropertyActionAvailable($name, $action = '') {
0 ignored issues
show
Unused Code introduced by
The parameter $action is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
92
    // By default all action available for existing properties
93 1
    return property_exists($this, $this->getPhysicalPropertyName($name));
94
  }
95
96
  /**
97
   * Internal method that make real changes to property value
98
   * May be override in child class
99
   *
100
   * @param string $name
101
   * @param mixed  $value
102
   *
103
   * @return mixed
104
   */
105
  protected function setProperty($name, $value) {
106
    // TODO: Change property only if value differs ????
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
107
//      if($this->{$this->getPhysicalPropertyName($name)} !== $value) {
108
//      }
109
    return $this->{$this->getPhysicalPropertyName($name)} = $value;
110
  }
111
112
  /**
113
   * Internal method that make really reads property value
114
   * May be override in child class
115
   *
116
   * @param string $name
117
   * @param mixed  $value - ignored. Used for compatibility
118
   *
119
   * @return mixed
120
   */
121
  protected function getProperty($name, $value = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
122
    return $this->{$this->getPhysicalPropertyName($name)};
123
  }
124
125
  /**
126
   * Directly adjusts value without Adjuster
127
   *
128
   * @param string $name
129
   * @param mixed  $diff
130
   *
131
   * @return mixed
132
   */
133 1
  protected function adjustProperty($name, $diff) {
134 1
    return $this->propertyMethodResult($name, $diff, 'adjustProperty');
135
  }
136
137
  /**
138
   * Directly adjusts value Delta for properties without Adjuster
139
   *
140
   * @param string $name
141
   * @param mixed  $diff
142
   *
143
   * @return mixed
144
   */
145 1
  protected function deltaProperty($name, $diff) {
146 1
    return $this->propertyMethodResult($name, $diff, 'delta');
147
  }
148
149
  protected function actionProperty($action, $name, $value) {
150
    $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...
151
    // Now deciding - will we call a protected setter or will we work with protected property
152
    if (method_exists($this, $methodName = $action . ucfirst($name))) {
153
      // If method exists - just calling it
154
      // TODO - should return TRUE if value changed or FALSE otherwise
155
      $result = call_user_func_array(array($this, $methodName), array($value));
156
    } elseif ($this->isPropertyActionAvailable($name, $action)) {
157
      // No setter exists - works directly with protected property
158
      $result = $this->{$action . 'Property'}($name, $value);
159
    } else {
160
      throw new ExceptionPropertyNotExists('Property [' . get_called_class() . '::' . $name . '] does not have ' . $action . 'ter/property to ' . $action, ERR_ERROR);
161
    }
162
163
    return $result;
164
  }
165
166
  /**
167
   * Getter with support of protected methods
168
   *
169
   * @param $name
170
   *
171
   * @return mixed
172
   * @throws ExceptionPropertyNotExists
173
   */
174 3
  public function __get($name) {
175 3
    $this->checkPropertyExists($name);
176
177 2
    $result = $this->actionProperty('get', $name, null);
178
179 1
    return $result;
180
  }
181
182
183
  //+
184
  /**
185
   * Unsafe setter - w/o checking if the property was already adjusted
186
   *
187
   * @param string $name
188
   * @param mixed  $value
189
   *
190
   * @return mixed|null
191
   * @throws ExceptionPropertyNotExists
192
   */
193 3
  protected function _setUnsafe($name, $value) {
194 3
    $result = $this->actionProperty('set', $name, $value);
195
196
    // TODO - should be primed only if value changed
197
//    if($result) {
198 1
    $this->propertiesChanged[$name] = true;
199
200
//    }
201
202 1
    return $result;
203
  }
204
205
  /**
206
   * Setter wrapper with support of protected properties/methods
207
   *
208
   * @param string $name
209
   * @param mixed  $value
210
   *
211
   * @return mixed|null
212
   * @throws ExceptionPropertyNotExists
213
   */
214
  // TODO - сеттер должен параллельно изменять значение db_row - for now...
215
  // TODO - Проверка, а действительно ли данные были изменены?? Понадобится определение типов - разные типы сравниваются по-разному
216 1
  public function __set($name, $value) {
217 1
    $this->checkOverwriteAdjusted($name);
218 1
    $this->checkPropertyExists($name);
219
220 1
    return $this->_setUnsafe($name, $value);
221
  }
222
223
224
  /**
225
   * @param string $name
226
   * @param int    $diff
227
   *
228
   * @return int
229
   */
230 2
  protected function adjustPropertyInteger($name, $diff) {
231 2
    return intval($this->$name) + intval($diff);
232
  }
233
234
  /**
235
   * @param string $name
236
   * @param float  $diff
237
   *
238
   * @return float
239
   */
240 1
  protected function adjustPropertyDouble($name, $diff) {
241 1
    return floatval($this->$name) + floatval($diff);
242
  }
243
244
  /**
245
   * @param string $name
246
   * @param string $diff
247
   *
248
   * @return string
249
   */
250 1
  protected function adjustPropertyString($name, $diff) {
251 1
    return (string)$this->$name . (string)$diff;
252
  }
253
254
  /**
255
   * @param string $name
256
   * @param array  $diff
257
   *
258
   * @return array
259
   */
260 1
  protected function adjustPropertyArray($name, $diff) {
261 1
    $copy = (array)$this->$name;
262 1
    HelperArray::merge($copy, (array)$diff, HelperArray::MERGE_PHP);
263
264 1
    return $copy;
265
  }
266
267
  /**
268
   * @param string $name
269
   * @param int    $diff
270
   *
271
   * @return int
272
   */
273 2
  protected function deltaInteger($name, $diff) {
274 2
    return (int)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, 0) + (int)$diff;
275
  }
276
277
  /**
278
   * @param string $name
279
   * @param float  $diff
280
   *
281
   * @return float
282
   */
283 1
  protected function deltaDouble($name, $diff) {
284 1
    return (float)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, 0.0) + (float)$diff;
285
  }
286
287
  /**
288
   * @param string $name
289
   * @param string $diff
290
   *
291
   * @return string
292
   */
293 1
  protected function deltaString($name, $diff) {
294 1
    return (string)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, '') . (string)$diff;
295
  }
296
297
  /**
298
   * @param string $name
299
   * @param array  $diff
300
   *
301
   * @return array
302
   */
303 1
  protected function deltaArray($name, $diff) {
304 1
    $copy = (array)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, array());
305 1
    HelperArray::merge($copy, $diff, HelperArray::MERGE_PHP);
306
307 1
    return $copy;
308
  }
309
310
  /**
311
   * Get adjusted value by callback with generated name
312
   * Support types: "integer", "double", "string", "array"
313
   * Throws exception on: "boolean", "object", "resource", "NULL", "unknown type",
314
   *
315
   * @param string $name
316
   * @param mixed  $diff
317
   * @param string $prefix
318
   *
319
   * @return mixed
320
   * @throws ExceptionTypeUnsupported
321
   */
322 9
  protected function propertyMethodResult($name, $diff, $prefix = '') {
323
    // TODO - property type checks
324
    // Capitalizing type name
325 9
    $type = explode(' ', gettype($this->$name));
326 9
    array_walk($type, 'DbSqlHelper::UCFirstByRef');
327 9
    $type = implode('', $type);
328
329 9
    if (!method_exists($this, $methodName = $prefix . $type)) {
330 1
      throw new ExceptionTypeUnsupported();
331
    }
332
333 8
    return call_user_func(array($this, $methodName), $name, $diff);
334
  }
335
336
  /**
337
   * Adjust property value with $diff
338
   * Adjusted values put into DB with UPDATE query
339
   * Adjuster callback adjXXX() should return new value which will be propagated via __set()
340
   * Optionally there can be DIFF-adjuster adjXXXDiff() for complex types
341
   *
342
   * @param string $name
343
   * @param mixed  $diff
344
   *
345
   * @return mixed
346
   */
347 2
  public function __adjust($name, $diff) {
348 2
    $this->checkPropertyExists($name);
349
350 2
    $result = $this->actionProperty('adjust', $name, $diff);
351
352
    // Invoking property setter
353 2
    $this->_setUnsafe($name, $result);
354
355
    // Initializing value of adjustment
356 2
    if (!array_key_exists($name, $this->propertiesAdjusted)) {
357 2
      $this->propertiesAdjusted[$name] = null;
358 2
    }
359
360 2
    $this->propertiesAdjusted[$name] = $this->actionProperty('delta', $name, $diff);
361
362 2
    return $this->$name;
363
  }
364
365
}
366