Completed
Push — trunk ( c0c172...91a948 )
by SuperNova.WS
11:04
created

classPersistent::__unset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
/**
3
 * Created by Gorlum 29.10.2016 10:16
4
 */
5
6
/**
7
 *
8
 * Persistent is extension of class cacher and can save itself to DB
9
 * It's most usefull to hold basic structures as configuration, variables etc
10
 * Persistent pretty smart to handle one-level tables structures a-la "variable_name"+"variable_value"
11
 * Look supernova.sql to learn more
12
 * Also this class can holds default values for variables
13
 *
14
 * @package supernova
15
 *
16
 */
17
class classPersistent extends classCache {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
18
  protected $table_name;
19
  protected $sql_index_field;
20
  protected $sql_value_field;
21
22
  protected $defaults = array();
23
24
  /**
25
   * @var bool $force
26
   */
27
  protected $force = false;
28
29
  public function __construct($gamePrefix = 'sn_', $table_name = 'table') {
30
    parent::__construct("{$gamePrefix}{$table_name}_");
31
    $this->table_name = $table_name;
32
33
    $this->sql_index_field = "{$table_name}_name";
34
    $this->sql_value_field = "{$table_name}_value";
35
36
    if(!$this->_DB_LOADED) {
37
      $this->db_loadAll();
38
    }
39
  }
40
41
  public static function getInstance($gamePrefix = 'sn_', $table_name = '') {
42
    if (!isset(self::$cacheObject)) {
43
      $className = get_class();
44
      self::$cacheObject = new $className($gamePrefix, $table_name);
45
    }
46
    return self::$cacheObject;
47
  }
48
49
  /**
50
   * @param string $index
51
   *
52
   * @return string|null
53
   */
54
  public function db_loadItem($index) {
55
    $result = null;
56
    if($index) {
57
      $index_safe = db_escape($index);
58
      $queryResult = doquery("SELECT `{$this->sql_value_field}` FROM `{{{$this->table_name}}}` WHERE `{$this->sql_index_field}` = '{$index_safe}' FOR UPDATE", true);
59
      if(is_array($queryResult) && !empty($queryResult)) {
60
        $this->$index = $result = $queryResult[$this->sql_value_field];
61
      }
62
    }
63
64
    return $result;
65
  }
66
67
  public function db_loadAll() {
68
    $this->loadDefaults();
69
70
    $query = doquery("SELECT * FROM {{{$this->table_name}}} FOR UPDATE;");
71
    while($row = db_fetch($query)) {
72
      $this->$row[$this->sql_index_field] = $row[$this->sql_value_field];
73
    }
74
75
    $this->_DB_LOADED = true;
0 ignored issues
show
Bug Best Practice introduced by
The property _DB_LOADED does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
76
  }
77
78
  public function loadDefaults() {
79
    foreach($this->defaults as $defName => $defValue) {
80
      $this->$defName = $defValue;
81
    }
82
  }
83
84
  public function db_saveAll() {
85
    $this->db_saveItem(array_combine(array_keys($this->defaults), array_fill(0, count($this->defaults), null)));
86
  }
87
88
  public function db_saveItem($item_list, $value = NULL) {
89
    if(empty($item_list)) {
90
      return;
91
    }
92
93
    !is_array($item_list) ? $item_list = array($item_list => $value) : false;
94
95
    // Сначала записываем данные в базу - что бы поймать все блокировки
96
    $qry = array();
97
    foreach($item_list as $item_name => $item_value) {
98
      if($item_name) {
99
        $item_value = db_escape($item_value === NULL ? $this->$item_name : $item_value);
100
        $item_name = db_escape($item_name);
101
        $qry[] = "('{$item_name}', '{$item_value}')";
102
      }
103
    }
104
    doquery("REPLACE INTO `{{" . $this->table_name . "}}` (`{$this->sql_index_field}`, `{$this->sql_value_field}`) VALUES " . implode(',', $qry) . ";");
105
106
    // И только после взятия блокировок - меняем значения в кэше
107
    foreach($item_list as $item_name => $item_value) {
108
      if($item_name && $item_value !== null) {
109
        $this->__set($item_name, $item_value);
110
      }
111
    }
112
  }
113
114
  /**
115
   * Instructs cache to pass next operation to DB - whether it read or write
116
   *
117
   * This allows more transparency when accessing variables. So
118
   *    $this->db_loadItem('variable_name')
119
   * converts to
120
   *    $this->pass()->variable_name
121
   * Latest makes IDE aware of operation with variables and makes navigation and code refactoring (i.e. variable renaming) much easier
122
   * Same work with saving items directly to DB:
123
   *    $this->db_saveItem('variable_name', $value)
124
   * becomes
125
   *    $this->pass()->variable_name = $value;
126
   *
127
   * @return $this
128
   */
129
  public function pass() {
130
    $this->force = true;
131
132
    return $this;
133
  }
134
135
  public function __get($name) {
136
    if($this->force) {
137
      $this->force = false;
138
      $this->db_loadItem($name);
139
    }
140
141
    return parent::__get($name);
142
  }
143
144
  public function __set($name, $value) {
145
    if($this->force) {
146
      $this->force = false;
147
      $this->db_saveItem($name, $value);
148
    }
149
150
    parent::__set($name, $value);
151
  }
152
153
  public function __unset($name) {
154
    doquery("DELETE FROM `{{config}}` WHERE `config_name` = " .SN::$db->db_escape($name));
155
156
    parent::__unset($name);
157
  }
158
159
}
160