Completed
Push — work-fleets ( 99cd5d...15d822 )
by SuperNova.WS
05:44
created

DbSqlPrepare::execute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 5
ccs 0
cts 3
cp 0
crap 2
rs 9.4285
1
<?php
2
3
/**
4
 * Class DbSqlPrepare
5
 */
6
// TODO - Вааще всё переделать.
7
// Получение стейтмента по строке - это должен быть статик
8
// Тогда же должны ребиндится параметры
9
class DbSqlPrepare {
10
  const COMMENT_PLACEHOLDER = ':__COMMENT__';
11
  const PARAM_PREG = '#(\:.+?\b)#im';
12
13
  /**
14
   * SQL text
15
   *
16
   * @var string $query
17
   */
18
  public $query = '';
19
  /**
20
   * Array of used params :param => value
21
   * Each param would bind with bind_param() function
22
   * Used for DML part of query
23
   *
24
   * @var array $values
25
   */
26
  public $values = array();
27
  /**
28
   * Array of used placeholders |param => value
29
   * Each placeholder would be placed into query with conversion via (string)
30
   * Used for dynamic queries creation for DDL part
31
   *
32
   * @var array $placeholders
33
   */
34
  // TODO - make use of it
35
  public $placeholders = array();
36
37
  /**
38
   * Comment for query
39
   * Should be quoted with SQL comment quote
40
   * Will be placed after query itself. Used mainly for debug purposes (should be disabled on production servers)
41
   *
42
   * @var string
43
   */
44
  // TODO - disable comments in SQL via game config
45
  public $comment = '';
46
47
  // Prepared values for query execution
48
  public $queryPrepared = '';
49
  public $paramsPrepared = array();
50
  public $valuesPrepared = array();
51
  public $valueTypesPrepared = '';
52
  /**
53
   * @var mysqli_stmt $statement
54
   */
55
  public $statement;
56
  /**
57
   * Flag that params already bound
58
   * Setting values performed via updating $values property
59
   *
60
   * @var bool $isParamsBound
61
   */
62
  protected $isParamsBound = false;
63
64
//  /**
65
//   * @var ReflectionMethod
66
//   */
67
//  public static $bindParamMethod;
68
69
  /**
70
   * @var mysqli_stmt[]
71
   */
72
  protected static $statements = array();
73
74
  /**
75
   * DbSqlPrepare constructor.
76
   *
77
   * @param string $statement
78
   * @param array  $values
79
   */
80 1
  public function __construct($statement, $values = array()) {
81
//    if(empty(static::$bindParamMethod)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
61% 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...
82
//      $ref = new ReflectionClass('mysqli_stmt');
83
//      static::$bindParamMethod = $ref->getMethod("bind_param");
84
//    }
85
86 1
    $this->query = trim($statement);
87 1
    $this->values = $values;
88 1
  }
89
90
  /**
91
   * @param string $statement
92
   * @param array  $values
93
   *
94
   * @return static
95
   *
96
   */
97 1
  public static function build($statement, $values = array()) {
98 1
    return new static($statement, $values);
99
  }
100
101
102
  public function setQuery($query) {
103
    $this->query = $query;
104
105
    return $this;
106
  }
107
108
  public function commentRemove() {
109
    if (!empty($this->values[static::COMMENT_PLACEHOLDER])) {
110
      unset($this->values[static::COMMENT_PLACEHOLDER]);
111
      $this->query = str_replace(' ' . static::COMMENT_PLACEHOLDER, '', $this->query);
112
    }
113
  }
114
115
  public function commentAdd($comment) {
116
    if (empty($this->values[static::COMMENT_PLACEHOLDER])) {
117
      $this->query .= ' ' . static::COMMENT_PLACEHOLDER;
118
    }
119
    $this->values[static::COMMENT_PLACEHOLDER] = $comment;
120
  }
121
122
  /**
123
   * @param string $comment
124
   */
125
  public function comment($comment) {
126
    if (empty($comment)) {
127
      $this->commentRemove();
128
    } else {
129
      $this->commentAdd($comment);
130
    }
131
132
    return $this;
133
  }
134
135
136
  // TODO - method to re-set and re-bind values
137
138
139 1
  public function __toString() {
140
//    $result = str_replace(array_keys($this->tables), $this->tables, $this->query);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
141 1
    $result = str_replace(array_keys($this->values), $this->values, $this->query);
142
143 1
    return $result;
144
  }
145
146
  public function compileMySqlI() {
147
    $this->queryPrepared = $this->query;
148
    $this->paramsPrepared = array();
149
    $this->valuesPrepared = array();
150
    $this->valueTypesPrepared = '';
151
152
    if ($variableCount = preg_match_all(self::PARAM_PREG, $this->query, $matches, PREG_PATTERN_ORDER)) {
0 ignored issues
show
Unused Code introduced by
$variableCount 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...
153
      $this->paramsPrepared = $matches[0];
154
      if (in_array(static::COMMENT_PLACEHOLDER, $this->paramsPrepared)) {
155
        // Removing comment placeholder from statement
156
        $this->queryPrepared = str_replace(static::COMMENT_PLACEHOLDER, $this->comment, $this->queryPrepared);
157
        // Removing comment value from values list
158
        $this->paramsPrepared = array_filter($this->paramsPrepared, function ($value) { return $value != DbSqlPrepare::COMMENT_PLACEHOLDER; });
159
        // TODO - Add comment value directly to statement
160
      }
161
162
      // Replacing actual param names with '?' - for mysqli_prepare
163
      $this->queryPrepared = preg_replace(self::PARAM_PREG, '?', $this->queryPrepared);
164
165
      // Now filling found params with it values
166
      // We can't use param names as keys 'cause same param can be met twice
167
      // So one key would overwrite another and total number of valuesUsed will be less then actual values used
168
      // TODO - move out of this proc to separate method to allow rebind of params
169
      foreach ($this->paramsPrepared as $key => &$value) {
170
        if (!key_exists($value, $this->values)) {
171
          // Throw exception if not key found in statement values list
172
          throw new Exception('DbSqlPrepare::compileMySqlI() - values array has no match for statement params');
173
        }
174
175
        // Reference need for call mysqli::bind_param later in bindParam() method
176
        $this->valuesPrepared[$key] = &$this->values[$value];
177
178
        // TODO - move out of this proc to separate method and own loop to allow rebind of params
179
        // i corresponding variable has type integer
180
        // d corresponding variable has type double
181
        // s corresponding variable has type string
182
        // b corresponding variable is a blob and will be sent in packets
183
        if (is_int($this->values[$value])) {
184
          $this->valueTypesPrepared .= 'i';
185
        } elseif (is_double($this->values[$value])) {
186
          $this->valueTypesPrepared .= 'd';
187
        } else {
188
          $this->valueTypesPrepared .= 's';
189
        }
190
      }
191
    }
192
193
    return $this;
194
  }
195
196
197
  /**
198
   * @internal param mysqli_stmt $mysqli_stmt
199
   */
200
  protected function bindParams() {
201
    if (count($this->valuesPrepared)) {
202
      $params = array_merge(array(&$this->valueTypesPrepared), $this->valuesPrepared);
203
      // static::$bindParamMethod->invokeArgs($this->statement, $params);
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% 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...
204
      call_user_func_array(array($this->statement, 'bind_param'), $params);
205
    }
206
  }
207
208
  /**
209
   * @param db_mysql $db
210
   *
211
   * @return DbSqlPrepare
212
   * @throws Exception
213
   */
214
  public function statementGet($db) {
215
    // TODO - к этому моменту плейсхолдеры под DDL уже должны быть заполнены соответствующими значениями
216
    // Надо вынести собственно prepared statement в отдельный объект, что бы здесь остались только манипуляции с полями
217
    $md5 = md5($this->queryPrepared);
218
219
    if (empty(static::$statements[$md5])) {
220
      if (!(static::$statements[$md5] = $db->db_prepare($this->queryPrepared))) {
221
        throw new Exception('DbSqlPrepare::statementGet() - can not prepare statement');
222
      }
223
      $this->statement = static::$statements[$md5];
0 ignored issues
show
Documentation Bug introduced by
It seems like static::$statements[$md5] can also be of type boolean. However, the property $statement is declared as type object<mysqli_stmt>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
224
      $this->bindParams();
225
    } else {
226
      // TODO - вот тут фигня. На самом деле нельзя под один и тот же DbSqlPrepare исползовать разные mysqli_stmt
227
      // С другой стороны - это позволяет реюзать параметры. Так что еще вопрос - фигня ли это....
228
      $this->statement = static::$statements[$md5];
229
    }
230
231
    return $this;
232
  }
233
234
  /**
235
   * @return $this
236
   */
237
  public function execute() {
238
    $this->statement->execute();
239
240
    return $this;
241
  }
242
243
  public function getResult() {
244
    return $this->statement->get_result();
245
  }
246
247
}
248