Completed
Push — work-fleets ( 7aaf68...e155c0 )
by SuperNova.WS
06:30
created

PropertyHider::isContainerEmpty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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