Completed
Push — work-fleets ( b0e897...355465 )
by SuperNova.WS
13:11 queued 07:54
created

DbSqlPrepare::comment()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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