Completed
Push — work-fleets ( de8005...be60df )
by SuperNova.WS
05:54
created

PropertyHider::getPropertiesStatic()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
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 0
dl 0
loc 3
rs 10
ccs 3
cts 3
cp 1
crap 1
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 1
  /**
53
   * List of property names->$delta that was adjusted since last DB operation - and then need to be processed as Deltas
54 1
   *
55 1
   * @var array
56
   */
57 1
  protected $propertiesAdjusted = array();
58 1
59
  /**
60
   * @param array $properties
61
   */
62
  public function setProperties($properties) {
63
    // TODO - reset internals??
64 1
    $this->_properties = $properties;
65 1
  }
66
67 2
  public function getProperties() {
68 2
    return $this->_properties;
69 1
  }
70
71 1
  /**
72
   * PropertyHider constructor.
73 2
   */
74 2
  public function __construct() {
75 1
  }
76
77 1
  protected function checkPropertyExists($name) {
78 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 1
    }
81
  }
82
83
  protected function checkOverwriteAdjusted($name) {
84 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
      throw new PropertyAccessException('Property [' . get_called_class() . '::' . $name . '] already was adjusted so no SET is possible until dbSave', ERR_ERROR);
86
    }
87
  }
88
89
  /**
90
   * Method checks if action is available for named property
91 1
   *
92
   * @param string $name
93 1
   * @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 1
   *
134 1
   * @return mixed
135
   */
136
  protected function adjustProperty($name, $diff) {
137
    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 1
   *
146 1
   * @return mixed
147
   */
148
  protected function deltaProperty($name, $diff) {
149
    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 3
      $result = $this->{$action . 'Property'}($name, $value);
175 3
    } else {
176
      throw new ExceptionPropertyNotExists('Property [' . get_called_class() . '::' . $name . '] does not have ' . $action . 'ter/property to ' . $action, ERR_ERROR);
177 2
    }
178
179 1
    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
  public function __get($name) {
191
    $this->checkPropertyExists($name);
192
193 3
    $result = $this->actionProperty('get', $name, null);
194 3
195
    return $result;
196
  }
197
198 1
199
  //+
200
  /**
201
   * Unsafe setter - w/o checking if the property was already adjusted
202 1
   *
203
   * @param string $name
204
   * @param mixed  $value
205
   *
206
   * @return mixed|null
207
   * @throws ExceptionPropertyNotExists
208
   */
209
  protected function _setUnsafe($name, $value) {
210
    $result = $this->actionProperty('set', $name, $value);
211
212
    // TODO - should be primed only if value changed
213
//    if($result) {
214
    $this->propertiesChanged[$name] = true;
215
216 1
//    }
217 1
218 1
    return $result;
219
  }
220 1
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 2
  // TODO - сеттер должен параллельно изменять значение db_row - for now...
231 2
  // TODO - Проверка, а действительно ли данные были изменены?? Понадобится определение типов - разные типы сравниваются по-разному
232
  public function __set($name, $value) {
233
    $this->checkOverwriteAdjusted($name);
234
    $this->checkPropertyExists($name);
235
236
    return $this->_setUnsafe($name, $value);
237
  }
238
239
240 1
  /**
241 1
   * @param string $name
242
   * @param int    $diff
243
   *
244
   * @return int
245
   */
246
  protected function adjustPropertyInteger($name, $diff) {
247
    return intval($this->$name) + intval($diff);
248
  }
249
250 1
  /**
251 1
   * @param string $name
252
   * @param float  $diff
253
   *
254
   * @return float
255
   */
256
  protected function adjustPropertyDouble($name, $diff) {
257
    return floatval($this->$name) + floatval($diff);
258
  }
259
260 1
  /**
261 1
   * @param string $name
262 1
   * @param string $diff
263
   *
264 1
   * @return string
265
   */
266
  protected function adjustPropertyString($name, $diff) {
267
    return (string)$this->$name . (string)$diff;
268
  }
269
270
  /**
271
   * @param string $name
272
   * @param array  $diff
273 2
   *
274 2
   * @return array
275
   */
276
  protected function adjustPropertyArray($name, $diff) {
277
    $copy = (array)$this->$name;
278
    HelperArray::merge($copy, (array)$diff, HelperArray::MERGE_PHP);
279
280
    return $copy;
281
  }
282
283 1
  /**
284 1
   * @param string $name
285
   * @param int    $diff
286
   *
287
   * @return int
288
   */
289
  protected function deltaInteger($name, $diff) {
290
    return (int)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, 0) + (int)$diff;
291
  }
292
293 1
  /**
294 1
   * @param string $name
295
   * @param float  $diff
296
   *
297
   * @return float
298
   */
299
  protected function deltaDouble($name, $diff) {
300
    return (float)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, 0.0) + (float)$diff;
301
  }
302
303 1
  /**
304 1
   * @param string $name
305 1
   * @param string $diff
306
   *
307 1
   * @return string
308
   */
309
  protected function deltaString($name, $diff) {
310
    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
  protected function deltaArray($name, $diff) {
320
    $copy = (array)HelperArray::keyExistsOr($this->propertiesAdjusted, $name, array());
321
    HelperArray::merge($copy, $diff, HelperArray::MERGE_PHP);
322 13
323 13
    return $copy;
324
  }
325 13
326 13
  /**
327 13
   * Get adjusted value by callback with generated name
328
   * Support types: "integer", "double", "string", "array"
329 13
   * Throws exception on: "boolean", "object", "resource", "NULL", "unknown type",
330 5
   *
331
   * @param string $name
332
   * @param mixed  $diff
333 8
   * @param string $prefix
334
   *
335
   * @return mixed
336
   * @throws ExceptionTypeUnsupported
337
   */
338
  protected function propertyMethodResult($name, $diff, $prefix = '') {
339
    $type = gettype($this->$name);
340
    // Capitalizing type name
341
    $methodName = explode(' ', $type);
342
    array_walk($methodName, 'DbSqlHelper::UCFirstByRef');
343
    $methodName = $prefix . implode('', $methodName);
344
345
    if (!method_exists($this, $methodName)) {
346
      throw new ExceptionTypeUnsupported('Type "' . $type . '" is unsupported in PropertyHider::propertyMethodResult');
347 2
    }
348 2
349
    return call_user_func(array($this, $methodName), $name, $diff);
350 2
  }
351
352
  /**
353 2
   * 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 2
   * Optionally there can be DIFF-adjuster adjXXXDiff() for complex types
357 2
   *
358 2
   * @param string $name
359
   * @param mixed  $diff
360 2
   *
361
   * @return mixed
362 2
   */
363
  public function __adjust($name, $diff) {
364
    $this->checkPropertyExists($name);
365
366
    $result = $this->actionProperty('adjust', $name, $diff);
367
368
    // Invoking property setter
369
    $this->_setUnsafe($name, $result);
370
371
    // Initializing value of adjustment
372
    if (!array_key_exists($name, $this->propertiesAdjusted)) {
373
      $this->propertiesAdjusted[$name] = null;
374
    }
375
376
    $this->propertiesAdjusted[$name] = $this->actionProperty('delta', $name, $diff);
377
378
    return $this->$name;
379
  }
380
381
}
382