Completed
Push — master ( 53937e...5698ad )
by Lars
06:10
created

Prepare::prepare()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 23
Code Lines 12

Duplication

Lines 5
Ratio 21.74 %

Code Coverage

Tests 9
CRAP Score 5.3906

Importance

Changes 0
Metric Value
dl 5
loc 23
ccs 9
cts 12
cp 0.75
rs 8.5906
c 0
b 0
f 0
cc 5
eloc 12
nc 4
nop 1
crap 5.3906
1
<?php
2
3
declare(strict_types=1);
4
5
namespace voku\db;
6
7
use voku\db\exceptions\DBGoneAwayException;
8
use voku\db\exceptions\QueryException;
9
10
/**
11
 * Prepare: This class can handle the prepare-statement from the "DB"-class.
12
 *
13
 * @package   voku\db
14
 */
15
final class Prepare extends \mysqli_stmt
16
{
17
18
  /**
19
   * @var string $_sql - the unchanged query string provided to the constructor
20
   */
21
  private $_sql;
22
23
  /**
24
   * @var string $_sql_with_bound_parameters - the query string with bound parameters interpolated
25
   */
26
  private $_sql_with_bound_parameters;
27
28
  /**
29
   * @var bool
30
   */
31
  private $_use_bound_parameters_interpolated = false;
32
33
  /**
34
   * @var array $_boundParams - array of arrays containing values that have been bound to the query as parameters
35
   */
36
  private $_boundParams = array();
37
38
  /**
39
   * @var DB
40
   */
41
  private $_db;
42
43
  /**
44
   * @var Debug
45
   */
46
  private $_debug;
47
48
  /**
49
   * Prepare constructor.
50
   *
51
   * @param DB     $db
52
   * @param string $query
53 9
   */
54
  public function __construct(DB $db, string $query)
55 9
  {
56 9
    $this->_db = $db;
57
    $this->_debug = $db->getDebugger();
58 9
59
    parent::__construct($db->getLink(), $query);
60 9
61 9
    $this->prepare($query);
62
  }
63
64
  /**
65
   * Prepare destructor.
66 9
   */
67
  public function __destruct()
68 9
  {
69 9
    $this->close();
70
  }
71
72
  /**
73
   * Combines the values stored in $this->boundParams into one array suitable for pushing as the input arguments to
74
   * parent::bind_param when used with call_user_func_array
75
   *
76
   * @return array
77 6
   */
78
  private function _buildArguments(): array
79 6
  {
80 6
    $arguments = array();
81
    $arguments[0] = '';
82 6
83 6
    foreach ($this->_boundParams as $param) {
84 6
      $arguments[0] .= $param['type'];
85 6
      $arguments[] =& $param['value'];
86
    }
87 6
88
    return $arguments;
89
  }
90
91
  /**
92
   * Escapes the supplied value.
93
   *
94
   * @param array $param
95
   *
96
   * @return array 0 => "$value" escaped<br />
97
   *               1 => "$valueForSqlWithBoundParameters" for insertion into the interpolated query string
98 6
   */
99
  private function _prepareValue(array &$param): array
100 6
  {
101 6
    $type = $param['type']; // 'i', 'b', 's', 'd'
102
    $value = $param['value'];
103
104 6
    /** @noinspection ReferenceMismatchInspection */
105
    $value = $this->_db->escape($value);
106 6
107 4
    if ($type === 's') {
108 6
      $valueForSqlWithBoundParameters = "'" . $value . "'";
109 2
    } elseif ($type === 'i') {
110 2
      $valueForSqlWithBoundParameters = (int)$value;
111 1
    } elseif ($type === 'd') {
112 1
      $valueForSqlWithBoundParameters = (double)$value;
113
    } else {
114
      $valueForSqlWithBoundParameters = $value;
115
    }
116 6
117
    return array($value, $valueForSqlWithBoundParameters);
118
  }
119
120
  /**
121
   * @return int
122
   */
123
  public function affected_rows(): int
124
  {
125
    return $this->affected_rows;
126
  }
127
128
  /**
129
   * This is a wrapper for "bind_param" what binds variables to a prepared statement as parameters. If you use this
130
   * wrapper, you can debug your query with e.g. "$this->get_sql_with_bound_parameters()".
131
   *
132
   * @param string $types <strong>i<strong> corresponding variable has type integer<br />
133
   *                      <strong>d</strong> corresponding variable has type double<br />
134
   *                      <strong>s</strong> corresponding variable has type string<br />
135
   *                      <strong>b</strong> corresponding variable is a blob and will be sent in packets
136
   *
137
   * INFO: We have to explicitly declare all parameters as references, otherwise it does not seem possible to pass them
138
   * on without losing the reference property.
139
   *
140
   * @param mixed  $v1
141
   * @param mixed  $v2
142
   * @param mixed  $v3
143
   * @param mixed  $v4
144
   * @param mixed  $v5
145
   * @param mixed  $v6
146
   * @param mixed  $v7
147
   * @param mixed  $v8
148
   * @param mixed  $v9
149
   * @param mixed  $v10
150
   * @param mixed  $v11
151
   * @param mixed  $v12
152
   * @param mixed  $v13
153
   * @param mixed  $v14
154
   * @param mixed  $v15
155
   * @param mixed  $v16
156
   * @param mixed  $v17
157
   * @param mixed  $v18
158
   * @param mixed  $v19
159
   * @param mixed  $v20
160
   * @param mixed  $v21
161
   * @param mixed  $v22
162
   * @param mixed  $v23
163
   * @param mixed  $v24
164
   * @param mixed  $v25
165
   * @param mixed  $v26
166
   * @param mixed  $v27
167
   * @param mixed  $v28
168
   * @param mixed  $v29
169
   * @param mixed  $v30
170
   * @param mixed  $v31
171
   * @param mixed  $v32
172
   * @param mixed  $v33
173
   * @param mixed  $v34
174
   * @param mixed  $v35
175
   *
176
   * @return mixed
177 6
   */
178
  public function bind_param_debug(string $types, &$v1 = null, &$v2 = null, &$v3 = null, &$v4 = null, &$v5 = null, &$v6 = null, &$v7 = null, &$v8 = null, &$v9 = null, &$v10 = null, &$v11 = null, &$v12 = null, &$v13 = null, &$v14 = null, &$v15 = null, &$v16 = null, &$v17 = null, &$v18 = null, &$v19 = null, &$v20 = null, &$v21 = null, &$v22 = null, &$v23 = null, &$v24 = null, &$v25 = null, &$v26 = null, &$v27 = null, &$v28 = null, &$v29 = null, &$v30 = null, &$v31 = null, &$v32 = null, &$v33 = null, &$v34 = null, &$v35 = null)
0 ignored issues
show
Unused Code introduced by
The parameter $v1 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...
Unused Code introduced by
The parameter $v2 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...
Unused Code introduced by
The parameter $v3 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...
Unused Code introduced by
The parameter $v4 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...
Unused Code introduced by
The parameter $v5 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...
Unused Code introduced by
The parameter $v6 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...
Unused Code introduced by
The parameter $v7 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...
Unused Code introduced by
The parameter $v8 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...
Unused Code introduced by
The parameter $v9 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...
Unused Code introduced by
The parameter $v10 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...
Unused Code introduced by
The parameter $v11 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...
Unused Code introduced by
The parameter $v12 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...
Unused Code introduced by
The parameter $v13 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...
Unused Code introduced by
The parameter $v14 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...
Unused Code introduced by
The parameter $v15 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...
Unused Code introduced by
The parameter $v16 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...
Unused Code introduced by
The parameter $v17 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...
Unused Code introduced by
The parameter $v18 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...
Unused Code introduced by
The parameter $v19 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...
Unused Code introduced by
The parameter $v20 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...
Unused Code introduced by
The parameter $v21 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...
Unused Code introduced by
The parameter $v22 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...
Unused Code introduced by
The parameter $v23 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...
Unused Code introduced by
The parameter $v24 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...
Unused Code introduced by
The parameter $v25 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...
Unused Code introduced by
The parameter $v26 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...
Unused Code introduced by
The parameter $v27 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...
Unused Code introduced by
The parameter $v28 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...
Unused Code introduced by
The parameter $v29 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...
Unused Code introduced by
The parameter $v30 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...
Unused Code introduced by
The parameter $v31 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...
Unused Code introduced by
The parameter $v32 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...
Unused Code introduced by
The parameter $v33 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...
Unused Code introduced by
The parameter $v34 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...
Unused Code introduced by
The parameter $v35 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...
179 6
  {
180
    $this->_use_bound_parameters_interpolated = true;
181
182 6
    // debug_backtrace returns arguments by reference, see comments at http://php.net/manual/de/function.func-get-args.php
183 6
    $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
184 6
185
    $args =& $trace[0]['args'];
186
    $types = \str_split($types);
187
188 6
    $args_count = \count($args) - 1;
189 6
    $types_count = \count($types);
190
191 6
    if ($args_count !== $types_count) {
192 6
      trigger_error('Number of variables doesn\'t match number of parameters in prepared statement', E_WARNING);
193
194 6
      return false;
195
    }
196
197
    $arg = 1;
198
    foreach ($types as $typeInner) {
199
      $val =& $args[$arg];
200 6
      $this->_boundParams[] = array(
201 6
          'type'  => $typeInner,
202 6
          'value' => &$val,
203 6
      );
204 6
      $arg++;
205 6
    }
206
207 6
    return true;
208 6
  }
209
210 6
  /**
211
   * @inheritdoc
212
   *
213
   * @return bool
214
   */
215
  public function execute_raw(): bool
216
  {
217
    return parent::execute();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (execute() instead of execute_raw()). Are you sure this is correct? If so, you might want to change this to $this->execute().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
218
  }
219
220
  /**
221
   * Executes a prepared Query
222
   *
223
   * @link http://php.net/manual/en/mysqli-stmt.execute.php
224
   *
225
   * @return bool|int|Result   "Result" by "<b>SELECT</b>"-queries<br />
226
   *                           "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
227
   *                           "int" (affected_rows) by "<b>UPDATE / DELETE</b>"-queries<br />
228
   *                           "true" by e.g. "DROP"-queries<br />
229
   *                           "false" on error
230
   */
231
  public function execute()
232
  {
233
    if ($this->_use_bound_parameters_interpolated === true) {
234 9
      $this->interpolateQuery();
235
      \call_user_func_array(array('parent', 'bind_param'), $this->_buildArguments());
236 9
    }
237 6
238 6
    $query_start_time = microtime(true);
239 6
    $result = parent::execute();
240
    $query_duration = microtime(true) - $query_start_time;
241 9
242 9
    if ($result === true) {
243 9
244
      // "INSERT" || "REPLACE"
245 9 View Code Duplication
      if (preg_match('/^\s*"?(INSERT|REPLACE)\s+/i', $this->_sql)) {
246
        $insert_id = (int)$this->insert_id;
247
        $this->_debug->logQuery($this->_sql_with_bound_parameters, $query_duration, $insert_id);
248 7
249 3
        return $insert_id;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $insert_id; (integer) is incompatible with the return type of the parent method mysqli_stmt::execute of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
250 3
      }
251
252 3
      // "UPDATE" || "DELETE"
253 View Code Duplication
      if (preg_match('/^\s*"?(UPDATE|DELETE)\s+/i', $this->_sql)) {
254
        $affected_rows = (int)$this->affected_rows;
255
        $this->_debug->logQuery($this->_sql_with_bound_parameters, $query_duration, $affected_rows);
256 4
257 2
        return $affected_rows;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $affected_rows; (integer) is incompatible with the return type of the parent method mysqli_stmt::execute of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
258 2
      }
259
260 2
      // "SELECT"
261
      if (preg_match('/^\s*"?(SELECT)\s+/i', $this->_sql)) {
262
        $result = $this->get_result();
0 ignored issues
show
Bug introduced by
The method get_result() does not seem to exist on object<voku\db\Prepare>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
263
        $num_rows = (int)$result->num_rows;
264 2
        $this->_debug->logQuery($this->_sql_with_bound_parameters, $query_duration, $num_rows);
265 2
266 2
        return new Result($this->_sql_with_bound_parameters, $result);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \voku\db\Resu...d_parameters, $result); (voku\db\Result) is incompatible with the return type of the parent method mysqli_stmt::execute of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
267 2
      }
268
269 2
      // log the ? query
270
      $this->_debug->logQuery($this->_sql_with_bound_parameters, $query_duration, 0);
271
272
      return true;
273
    }
274
275
    // log the error query
276
    $this->_debug->logQuery($this->_sql_with_bound_parameters, $query_duration, 0, true);
277
278
    return $this->queryErrorHandling($this->error, $this->_sql_with_bound_parameters);
279 2
  }
280
281 2
  /**
282
   * Prepare an SQL statement for execution
283
   *
284
   * @link  http://php.net/manual/en/mysqli-stmt.prepare.php
285
   *
286
   * @param string $query <p>
287
   *                      The query, as a string. It must consist of a single SQL statement.
288
   *                      </p>
289
   *                      <p>
290
   *                      You can include one or more parameter markers in the SQL statement by
291
   *                      embedding question mark (?) characters at the
292
   *                      appropriate positions.
293
   *                      </p>
294
   *                      <p>
295
   *                      You should not add a terminating semicolon or \g
296
   *                      to the statement.
297
   *                      </p>
298
   *                      <p>
299
   *                      The markers are legal only in certain places in SQL statements.
300
   *                      For example, they are allowed in the VALUES() list of an INSERT statement
301
   *                      (to specify column values for a row), or in a comparison with a column in
302
   *                      a WHERE clause to specify a comparison value.
303
   *                      </p>
304
   *                      <p>
305
   *                      However, they are not allowed for identifiers (such as table or column names),
306
   *                      in the select list that names the columns to be returned by a SELECT statement),
307
   *                      or to specify both operands of a binary operator such as the =
308
   *                      equal sign. The latter restriction is necessary because it would be impossible
309
   *                      to determine the parameter type. In general, parameters are legal only in Data
310
   *                      Manipulation Language (DML) statements, and not in Data Definition Language
311
   *                      (DDL) statements.
312
   *                      </p>
313
   *
314
   * @return bool <p>false on error</p>
315
   * @since 5.0
316
   */
317
  public function prepare(string $query): bool
318
  {
319
    $this->_sql = $query;
320 9
    $this->_sql_with_bound_parameters = $query;
321
322 9
    if (!$this->_db->isReady()) {
323 9
      return false;
324
    }
325 9
326 View Code Duplication
    if (!$query || $query === '') {
327
      $this->_debug->displayError('Can not prepare an empty query.', false);
328
329 9
      return false;
330
    }
331
332
    $bool = parent::prepare($query);
333
334
    if ($bool === false) {
335 9
      $this->_debug->displayError('Can not prepare query: ' . $query . ' | ' . $this->error, false);
336
    }
337 9
338 2
    return $bool;
339 2
  }
340
341 9
  /**
342
   * Ger the bound parameters from sql-query as array, if you use the "$this->bind_param_debug()" method.
343
   *
344
   * @return array
345
   */
346
  public function get_bound_params(): array
347
  {
348
    return $this->_boundParams;
349
  }
350
351
  /**
352
   * @return string
353
   */
354
  public function get_sql(): string
355
  {
356
    return $this->_sql;
357
  }
358
359
  /**
360
   * Get the sql-query with bound parameters, if you use the "$this->bind_param_debug()" method.
361
   *
362
   * @return string
363
   */
364
  public function get_sql_with_bound_parameters(): string
365
  {
366
    return $this->_sql_with_bound_parameters;
367 4
  }
368
369 4
  /**
370
   * @return int
371
   */
372
  public function insert_id(): int
373
  {
374
    return $this->insert_id;
375
  }
376
377
  /**
378
   * Copies $this->_sql then replaces bound markers with associated values ($this->_sql is not modified
379
   * but the resulting query string is assigned to $this->sql_bound_parameters)
380
   *
381
   * @return string $testQuery - interpolated db query string
382
   */
383
  private function interpolateQuery(): string
384
  {
385
    $testQuery = $this->_sql;
386 6
    if ($this->_boundParams) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_boundParams of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
387
      foreach ($this->_boundParams as &$param) {
388 6
        $values = $this->_prepareValue($param);
389 6
390 6
        // set new values
391 6
        $param['value'] = $values[0];
392
        // we need to replace the question mark "?" here
393
        $values[1] = str_replace('?', '###simple_mysqli__prepare_question_mark###', $values[1]);
394 6
        // build the query (only for debugging)
395
        $testQuery = preg_replace("/\?/", $values[1], $testQuery, 1);
396 6
      }
397
      unset($param);
398 6
      $testQuery = str_replace('###simple_mysqli__prepare_question_mark###', '?', $testQuery);
399 6
    }
400 6
    $this->_sql_with_bound_parameters = $testQuery;
401 6
402 6
    return $testQuery;
403 6
  }
404
405 6
  /**
406
   * Error-handling for the sql-query.
407
   *
408
   * @param string $errorMsg
409
   * @param string $sql
410
   *
411
   * @throws QueryException
412
   * @throws DBGoneAwayException
413
   *
414
   * @return bool
415
   */
416
  private function queryErrorHandling(string $errorMsg, string $sql): bool
417
  {
418
    if ($errorMsg === 'DB server has gone away' || $errorMsg === 'MySQL server has gone away') {
419 2
      static $RECONNECT_COUNTER;
420
421 2
      // exit if we have more then 3 "DB server has gone away"-errors
422
      if ($RECONNECT_COUNTER > 3) {
423
        $this->_debug->mailToAdmin('DB-Fatal-Error', $errorMsg . ":\n<br />" . $sql, 5);
424
        throw new DBGoneAwayException($errorMsg);
425
      }
426
427
      $this->_debug->mailToAdmin('DB-Error', $errorMsg . ":\n<br />" . $sql);
428
429
      // reconnect
430
      $RECONNECT_COUNTER++;
431
      $this->_db->reconnect(true);
432
433
      // re-run the current query
434
      return $this->execute();
435
    }
436
437
    $this->_debug->mailToAdmin('SQL-Error', $errorMsg . ":\n<br />" . $sql);
438
439
    // this query returned an error, we must display it (only for dev) !!!
440 2
    $this->_debug->displayError($errorMsg . ' | ' . $sql);
441
442
    return false;
443 2
  }
444
445
}
446