Completed
Push — work-fleets ( 692eef...9e987d )
by SuperNova.WS
05:46
created

classCache::__set()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 11
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 8
c 2
b 0
f 0
nc 3
nop 2
dl 11
loc 11
rs 9.4285
ccs 0
cts 10
cp 0
crap 12
1
<?php
2
/**
3
 *
4
 * @package supernova
5
 * @version $Id$
6
 * @copyright (c) 2009-2010 Gorlum for http://supernova.ws
7
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
8
 *
9
 */
10
11
/**
12
 * @ignore
13
 * Defining some constants
14
 */
15
define('CACHER_NOT_INIT', -1);
16
define('CACHER_NO_CACHE', 0);
17
define('CACHER_XCACHE', 1);
18
19
define('CACHER_LOCK_WAIT', 5); // maximum cacher wait for table unlock in seconds. Can be float
20
21
// max timeout cacher can sleep in waiting for unlockDefault = 10000 ms = 0.01s
22
// really it will sleep mt_rand(100, classCache::CACHER_LOCK_SLEEP)
23
define('CACHER_LOCK_SLEEP', 10000);
24
25
/**
26
 *
27
 * Basic cacher class that handles different cache engines
28
 * It's pretty smart to handle one cache instance for all application instances (if there is PHP-cacher installed)
29
 * Currently supported only XCache and no-cache (array)
30
 * With no-cache some advanced features would be unaccessible
31
 * Cacher works not only with single values. It's also support multidimensional arrays
32
 * Currently support is a bit limited - for example there is no "walk" function. However basic array abilities supported
33
 * You should NEVER operate with arrays inside of cacher and should ALWAYS use wrap-up functions
34
 *
35
 *
36
 * @property bool _INITIALIZED
37
 * @property array lng_stat_usage - Array for locale strings usage statistics
38
 * @property array tables
39
 *
40
 * @package supernova
41
 */
42
class classCache {
43
  // CACHER_NOT_INIT - not initialized
44
  // CACHER_NO_CACHE - no cache - array() used
45
  // CACHER_XCACHE   - xCache
46
  protected static $mode = CACHER_NOT_INIT;
47
  protected static $data;
48
  protected $prefix;
49
50
  protected static $cacheObject;
51
52
  /**
53
   * @return int
54
   */
55
  public function getMode() {
56
    return static::$mode;
57
  }
58
59
  public function __construct($prefIn = 'CACHE_', $init_mode = false) {
60
    if (!($init_mode === false || $init_mode === CACHER_NO_CACHE || ($init_mode === CACHER_XCACHE && extension_loaded('xcache')))) {
61
      throw new UnexpectedValueException('Wrong work mode or current mode does not supported on your server');
62
    }
63
64
    $this->prefix = $prefIn;
65
    if (extension_loaded('xcache') && ($init_mode === CACHER_XCACHE || $init_mode === false)) {
66
      if (self::$mode === CACHER_NOT_INIT) {
67
        self::$mode = CACHER_XCACHE;
68
      }
69
    } else {
70
      if (self::$mode === CACHER_NOT_INIT) {
71
        self::$mode = CACHER_NO_CACHE;
72
        if (!self::$data) {
73
          self::$data = array();
74
        }
75
      }
76
    }
77
  }
78
79
  public static function getInstance($prefIn = 'CACHE_', $table_name = '') {
80
    if (!isset(self::$cacheObject)) {
81
      $className = get_class();
82
      self::$cacheObject = new $className($prefIn);
83
    }
84
85
    return self::$cacheObject;
86
  }
87
88
  public final function __clone() {
89
    // You NEVER need to copy cacher object or siblings
90
    throw new BadMethodCallException('Clone is not allowed');
91
  }
92
93
  // -------------------------------------------------------------------------
94
  // Here comes low-level functions - those that directly works with cacher engines
95
  // -------------------------------------------------------------------------
96 View Code Duplication
  public function __set($name, $value) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
97
    switch (self::$mode) {
98
      case CACHER_NO_CACHE:
99
        self::$data[$this->prefix . $name] = $value;
100
      break;
101
102
      case CACHER_XCACHE:
103
        xcache_set($this->prefix . $name, $value);
104
      break;
105
    }
106
  }
107
108
  public function __get($name) {
109
    switch (self::$mode) {
110
      case CACHER_NO_CACHE:
111
        return self::$data[$this->prefix . $name];
112
      break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
113
114
      case CACHER_XCACHE:
115
        return xcache_get($this->prefix . $name);
116
      break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
117
    }
118
119
    return null;
120
  }
121
122
  public function __isset($name) {
123
    switch (self::$mode) {
124
      case CACHER_NO_CACHE:
125
        return isset(self::$data[$this->prefix . $name]);
126
      break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
127
128
      case CACHER_XCACHE:
129
        return xcache_isset($this->prefix . $name) && ($this->__get($name) !== null);
130
      break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
131
    }
132
133
    return false;
134
  }
135
136 View Code Duplication
  public function __unset($name) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
137
    switch (self::$mode) {
138
      case CACHER_NO_CACHE:
139
        unset(self::$data[$this->prefix . $name]);
140
      break;
141
142
      case CACHER_XCACHE:
143
        xcache_unset($this->prefix . $name);
144
      break;
145
    }
146
  }
147
148
  public function unset_by_prefix($prefix_unset = '') {
149
    static $array_clear;
150
    !$array_clear ? $array_clear = function (&$v, $k, $p) {
151
      strpos($k, $p) === 0 ? $v = null : false;
152
    } : false;
153
154
    switch (self::$mode) {
155
      case CACHER_NO_CACHE:
156
//        array_walk(self::$data, create_function('&$v,$k,$p', 'if(strpos($k, $p) === 0)$v = NULL;'), $this->prefix.$prefix_unset);
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% 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...
157
        array_walk(self::$data, $array_clear, $this->prefix . $prefix_unset);
158
159
        return true;
160
      break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
161
162
      case CACHER_XCACHE:
163
        if (!function_exists('xcache_unset_by_prefix')) {
164
          return false;
165
        }
166
167
        return xcache_unset_by_prefix($this->prefix . $prefix_unset);
168
      break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
169
    }
170
171
    return true;
172
  }
173
  // -------------------------------------------------------------------------
174
  // End of low-level functions
175
  // -------------------------------------------------------------------------
176
177
  protected function make_element_name($args, $diff = 0) {
178
    $num_args = count($args);
179
180
    if ($num_args < 1) {
181
      return false;
182
    }
183
184
    $name = '';
185
    $aName = array();
186
    for ($i = 0; $i <= $num_args - 1 - $diff; $i++) {
187
      $name .= "[{$args[$i]}]";
188
      array_unshift($aName, $name);
189
    }
190
191
    return $aName;
192
  }
193
194
  public function array_set() {
195
    $args = func_get_args();
196
    $name = $this->make_element_name($args, 1);
197
198
    if (!$name) {
199
      return null;
200
    }
201
202
    if ($this->$name[0] === null) {
203
      for ($i = count($name) - 1; $i > 0; $i--) {
204
        $cName = "{$name[$i]}_COUNT";
205
        $cName1 = "{$name[$i-1]}_COUNT";
206
        if ($this->$cName1 == null || $i == 1) {
207
          $this->$cName++;
208
        }
209
      }
210
    }
211
212
    $this->$name[0] = $args[count($args) - 1];
213
214
    return true;
215
  }
216
217
  public function array_get() {
218
    $name = $this->make_element_name(func_get_args());
219
    if (!$name) {
220
      return null;
221
    }
222
223
    return $this->$name[0];
224
  }
225
226
  public function array_count() {
227
    $name = $this->make_element_name(func_get_args());
228
    if (!$name) {
229
      return 0;
230
    }
231
    $cName = "{$name[0]}_COUNT";
232
    $retVal = $this->$cName;
233
    if (!$retVal) {
234
      $retVal = null;
235
    }
236
237
    return $retVal;
238
  }
239
240
  public function array_unset() {
241
    $name = $this->make_element_name(func_get_args());
242
243
    if (!$name) {
244
      return false;
245
    }
246
    $this->unset_by_prefix($name[0]);
247
248
    for ($i = 1; $i < count($name); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
249
      $cName = "{$name[$i]}_COUNT";
250
      $cName1 = "{$name[$i-1]}_COUNT";
251
252
      if ($i == 1 || $this->$cName1 === null) {
253
        $this->$cName--;
254
        if ($this->$cName <= 0) {
255
          unset($this->$cName);
256
        }
257
      }
258
    }
259
260
    return true;
261
  }
262
263
  public function dumpData() {
264
    switch (self::$mode) {
265
      case CACHER_NO_CACHE:
266
        return dump(self::$data, $this->prefix);
267
      break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
268
269
      default:
270
        return false;
271
      break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
272
    }
273
  }
274
275
  public function reset() {
276
    $this->unset_by_prefix();
277
278
    $this->_INITIALIZED = false;
279
  }
280
281
  public function init($reInit = false) {
0 ignored issues
show
Unused Code introduced by
The parameter $reInit 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...
282
    $this->_INITIALIZED = true;
283
  }
284
285
  public function isInitialized() {
286
    return $this->_INITIALIZED;
287
  }
288
}
289
290
291
292
293
294
295
296
297
298
299
///*
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% 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...
300
//New test metaclass for handling DB table caching
301
//*/
302
//
303
//class class_db_cache extends classCache
304
//{
305
//  protected $tables = array(
306
//    'users' => array('name' => 'users', 'id' => 'id', 'index' => 'users_INDEX', 'count' => 'users_COUNT', 'lock' => 'users_LOCK', 'callback' => 'cb_users', 'ttl' => 0),
307
//    'config' => array('name' => 'config', 'id' => 'config_name', 'index' => 'config_INDEX', 'count' => 'config_COUNT', 'lock' => 'config_LOCK', 'callback' => 'cb_config', 'ttl' => 0),
308
//  );
309
//
310
//  public function __construct($gamePrefix = 'sn_')
311
//  {
312
//    parent::__construct("{$gamePrefix}dbcache_");
313
//  }
314
//
315
//  public function cache_add($table_name = 'table', $id_field = 'id', $ttl = 0, $force_load = false)
316
//  {
317
//    $table['name']     = $table_name;
318
//    $table['id']       = $id_field;
319
//    $table['index']    = "{$table_name}_INDEX";
320
//    $table['count']    = "{$table_name}_COUNT";
321
//    $table['lock']     = "{$table_name}_LOCK";
322
//    $table['callback'] = "cb_{$table_name}";
323
//    $table['ttl']      = $ttl;
324
//
325
//    $this->tables[$table_name] = $table;
326
//    // here we can load table data from DB - fields and indexes
327
//    // $force_reload would show should we need to reload table data from DB
328
//  }
329
//
330
//  // multilock
331
//  protected function table_lock($table_name, $wait_if_locked = false, $lock = NULL)
332
//  {
333
//    $lock_field_name = $this->tables['$table_name']['lock'];
334
//
335
//    $lock_wait_start = microtime(true);
336
//    while($wait_if_locked && !$this->$lock_field_name && (microtime(true) - $lock_wait_start <= CACHER_LOCK_WAIT))
337
//    {
338
//      usleep(mt_rand(100, classCache::CACHER_LOCK_SLEEP));
339
//    }
340
//
341
//    $result = (!$this->$lock_field_name) XOR ($lock === NULL);
342
//    if($result && $lock)
343
//    {
344
//      $this->$lock_field_name = $lock;
345
//    }
346
//
347
//    return $result;
348
//  }
349
//
350
//
351
//  /*
352
//    Magic start here. __call magic method will transform name of call to table name and handle all caching & db-related stuff
353
//    If there is such row in cache it will be returned. Otherwise it will read from DB, cached and returned
354
//    I.e. class_db_cache->table_name() call mean that all request will be done with records (cached or DB) in `table_name` table
355
//    __call interpets last argument as optional boolean parameter $force.
356
//    In read operations $force === true will tell cacher not to use cached data but load records from DB
357
//    In write operations $force === true will tell cacher immidiatly store new data to DB not relating on internal mechanics
358
//
359
//    __call have several forms
360
//
361
//    Form 1:
362
//      __call($id, [$force]) - "SELECT * FROM {table} WHERE id_field = $id"
363
//
364
//    Form 2: (not implemented yet)
365
//      __call('get', $condition, [$force]) - "SELECT * FROM {table} WHERE $condition"
366
//
367
//    Form 3: (not implemented yet)
368
//      __call('set', $data, [$condition], [$force]) - "UPDATE {table} SET $row = $data WHERE $condition"
369
//
370
//    Form 4: (not implemented yet)
371
//      __call('add', $data, [$condition], [$force]) - "UPDATE {table} SET $row = $row + $data WHERE $condition"
372
//  */
373
//
374
//  public function __call($name, $arguments)
375
//  {
376
//    $main_argument = $arguments[0];
377
//
378
//    switch($main_argument)
379
//    {
380
//      case 'get':
381
//        // it might be SELECT
382
//      break;
383
//
384
//      case 'set':
385
//        // it might be UPDATE
386
//      break;
387
//
388
//      default:
389
//        // it might be SELECT * FROM {table} WHERE id = $main_argument;
390
//        return $this->get_item($name, $main_argument, $arguments[1]);
391
//      break;
392
//    }
393
//  }
394
//
395
//  public function get_item($table_name, $id, $force_reload = false)
396
//  {
397
//    $internal_name = "{$table_name}_{$id}";
398
//
399
//    if(isset($this->$internal_name) && !$force_reload)
400
//    {
401
//      // pdump("{$id} - returning stored data");
402
//
403
//      return $this->$internal_name;
404
//    }
405
//    else
406
//    {
407
//      // pdump("{$id} - asking DB");
408
//
409
//      return $this->db_loadItem($table_name, $id);
410
//    }
411
//  }
412
//
413
//  public function db_loadItem($table_name, $id)
414
//  {
415
//    // If no table_name or no index - there is no such element
416
//    if(!$id && !$table_name)
417
//    {
418
//      return NULL;
419
//    }
420
//
421
//    $result = $this->db_loadItems($table_name, '*', "`{$this->tables[$table_name]['id']}` = '{$id}'", 1);
422
//    if($result)
423
//    {
424
//      return $result[$id];
425
//    }
426
//    else
427
//    {
428
//      $this->del_item($table_name, $id);
429
//      return NULL;
430
//    }
431
//  }
432
//
433
//  public function db_loadItems($table_name, $fields = '*', $condition = '', $limits = '')
434
//  {
435
//    if(!$fields)
436
//    {
437
//      $fields = '*';
438
//    }
439
//
440
//    if($condition)
441
//    {
442
//      $condition = " WHERE {$condition}";
443
//    }
444
//
445
//    if($limits)
446
//    {
447
//      $limits = " LIMIT {$limits}";
448
//    }
449
//
450
//    $query = doquery("SELECT {$fields} FROM `{{{$table_name}}}`{$condition}{$limits};");
451
//
452
//    $table = $this->tables[$table_name];
453
//
454
//    $index = $this->$table['index'];
455
//    $count = $this->$table['count'];
456
//
457
//    $result = NULL;
458
//
459
//    while ( $row = db_fetch($query) )
460
//    {
461
//      /*
462
//      foreach($row as $index => &$value)
463
//      {
464
//        if(is_numeric($value))
465
//        {
466
//          $value = floatval($value);
467
//
468
//          //if((double)intval($value) === $value)
469
//          //{
470
//          //  $value = intval($value);
471
//          //}
472
//        }
473
//      }
474
//      */
475
//
476
//      $item_id = $row[$table['id']];
477
//      $item_name = "{$table['name']}_{$item_id}";
478
//      if(!isset($this->$item_name))
479
//      {
480
//        $count++;
481
//      }
482
//
483
//      // Loading element to cache
484
//      $this->$item_name = $row;
485
//      // Also loading element to returning set
486
//      $result[$item_id] = $row;
487
//
488
//      // Internal work
489
//      // Indexing element for fast search
490
//      $index[$item_id] = true;
491
//    }
492
//
493
//    if($result)
494
//    {
495
//      $this->$table['index'] = $index;
496
//      $this->$table['count'] = $count;
497
//    }
498
//
499
//    return $result;
500
//  }
501
//
502
//  public function db_loadAll($table_name)
503
//  {
504
//    $this->unset_by_prefix("{$table_name}_");
505
//    $this->db_loadItems($table_name);
506
//  }
507
//
508
//  public function del_item($table_name, $id, $force_db_remove = false)
509
//  {
510
//    $internal_name = "{$table_name}_{$id}";
511
//
512
//    if(isset($this->$internal_name))
513
//    {
514
//      $table = $this->tables[$table_name];
515
//
516
//      $index = $this->$table['index'];
517
//      unset($index[$id]);
518
//      $this->$table['index'] = $index;
519
//
520
//      $this->$table['count']--;
521
//    }
522
//
523
//    if($force_db_remove)
524
//    {
525
//      doDelete("DELETE FROM {{{$table_name}}} WHERE `{$table['id']}` = '{$id}';");
526
//    }
527
//  }
528
//
529
///*
530
//  public function db_saveAll()
531
//  {
532
//    $toSave = array();
533
//    foreach($defaults as $field => $value)
534
//    {
535
//      $toSave[$field] = NULL;
536
//    }
537
//
538
//    $this->db_saveItem($toSave);
539
//  }
540
//
541
//  public function db_saveItem($index, $value = NULL)
542
//  {
543
//    if($index)
544
//    {
545
//      if(!is_array($index))
546
//      {
547
//        $index = array($index => $value);
548
//      }
549
//
550
//      foreach($index as $item_name => &$value)
551
//      {
552
//        if($value !== NULL)
553
//        {
554
//          $this->$item_name = $value;
555
//        }
556
//        else
557
//        {
558
//          $value = $this->$item_name;
559
//        }
560
//
561
//        $qry .= " ('{$item_name}', '{$value}'),";
562
//      }
563
//
564
//      $qry = substr($qry, 0, -1);
565
//      $qry = "REPLACE INTO `{{{$this->table_name}}}` (`{$this->sql_index_field}`, `{$this->sql_value_field}`) VALUES {$qry};";
566
//      doquery($qry);
567
//    };
568
//  }
569
//*/
570
//}
571