Completed
Push — development ( fd35b5...2c05ec )
by Nils
07:23
created

MeekroDB::__construct()   C

Complexity

Conditions 7
Paths 64

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 19
nc 64
nop 6
dl 0
loc 27
rs 6.7272
c 0
b 0
f 0
1
<?php
2
/*
3
    Copyright (C) 2008-2012 Sergey Tsalkov ([email protected])
4
5
    This program is free software: you can redistribute it and/or modify
6
    it under the terms of the GNU Lesser General Public License as published by
7
    the Free Software Foundation, either version 3 of the License, or
8
    (at your option) any later version.
9
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU Lesser General Public License for more details.
14
15
    You should have received a copy of the GNU Lesser General Public License
16
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
20
class DB {
21
    // initial connection
22
    public static $dbName = '';
23
    public static $user = '';
24
    public static $password = '';
25
    public static $host = 'localhost';
26
    public static $port = null;
27
    public static $encoding = 'utf8';
28
29
    // configure workings
30
    public static $param_char = '%';
31
    public static $named_param_seperator = '_';
32
    public static $success_handler = false;
33
    public static $error_handler = true;
34
    public static $throw_exception_on_error = false;
35
    public static $nonsql_error_handler = null;
36
    public static $throw_exception_on_nonsql_error = false;
37
    public static $nested_transactions = false;
38
    public static $usenull = true;
39
40
    // internal
41
    protected static $mdb = null;
42
43
    public static function getMDB() {
44
    $mdb = DB::$mdb;
45
46
    if ($mdb === null) {
47
        $mdb = DB::$mdb = new MeekroDB();
48
    }
49
50
    if ($mdb->param_char !== DB::$param_char) {
51
        $mdb->param_char = DB::$param_char;
52
    }
53
    if ($mdb->named_param_seperator !== DB::$named_param_seperator) {
54
        $mdb->named_param_seperator = DB::$named_param_seperator;
55
    }
56
    if ($mdb->success_handler !== DB::$success_handler) {
57
        $mdb->success_handler = DB::$success_handler;
58
    }
59
    if ($mdb->error_handler !== DB::$error_handler) {
60
        $mdb->error_handler = DB::$error_handler;
61
    }
62
    if ($mdb->throw_exception_on_error !== DB::$throw_exception_on_error) {
63
        $mdb->throw_exception_on_error = DB::$throw_exception_on_error;
64
    }
65
    if ($mdb->nonsql_error_handler !== DB::$nonsql_error_handler) {
66
        $mdb->nonsql_error_handler = DB::$nonsql_error_handler;
67
    }
68
    if ($mdb->throw_exception_on_nonsql_error !== DB::$throw_exception_on_nonsql_error) {
69
        $mdb->throw_exception_on_nonsql_error = DB::$throw_exception_on_nonsql_error;
70
    }
71
    if ($mdb->nested_transactions !== DB::$nested_transactions) {
72
        $mdb->nested_transactions = DB::$nested_transactions;
73
    }
74
    if ($mdb->usenull !== DB::$usenull) {
75
        $mdb->usenull = DB::$usenull;
76
    }
77
78
    return $mdb;
79
    }
80
81
    public static function get() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'get'), $args); }
82
    public static function disconnect() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'disconnect'), $args); }
83
    public static function query() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'query'), $args); }
84
    public static function queryFirstRow() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryFirstRow'), $args); }
85
    public static function queryOneRow() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryOneRow'), $args); }
86
    public static function queryAllLists() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryAllLists'), $args); }
87
    public static function queryFullColumns() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryFullColumns'), $args); }
88
    public static function queryFirstList() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryFirstList'), $args); }
89
    public static function queryOneList() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryOneList'), $args); }
90
    public static function queryFirstColumn() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryFirstColumn'), $args); }
91
    public static function queryOneColumn() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryOneColumn'), $args); }
92
    public static function queryFirstField() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryFirstField'), $args); }
93
    public static function queryOneField() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryOneField'), $args); }
94
    public static function queryRaw() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryRaw'), $args); }
95
    public static function queryRawUnbuf() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'queryRawUnbuf'), $args); }
96
97
    public static function insert() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'insert'), $args); }
98
    public static function insertIgnore() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'insertIgnore'), $args); }
99
    public static function insertUpdate() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'insertUpdate'), $args); }
100
    public static function replace() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'replace'), $args); }
101
    public static function update() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'update'), $args); }
102
    public static function delete() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'delete'), $args); }
103
104
    public static function insertId() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'insertId'), $args); }
105
    public static function count() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'count'), $args); }
106
    public static function affectedRows() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'affectedRows'), $args); }
107
108
    public static function useDB() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'useDB'), $args); }
109
    public static function startTransaction() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'startTransaction'), $args); }
110
    public static function commit() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'commit'), $args); }
111
    public static function rollback() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'rollback'), $args); }
112
    public static function tableList() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'tableList'), $args); }
113
    public static function columnList() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'columnList'), $args); }
114
115
    public static function sqlEval() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'sqlEval'), $args); }
116
    public static function nonSQLError() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'nonSQLError'), $args); }
117
118
    public static function serverVersion() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'serverVersion'), $args); }
119
    public static function transactionDepth() { $args = func_get_args(); return call_user_func_array(array(DB::getMDB(), 'transactionDepth'), $args); }
120
121
122
    public static function debugMode($handler = true) {
123
    DB::$success_handler = $handler;
124
    }
125
126
    public function version() {
127
    return $this->version_info();
0 ignored issues
show
Bug introduced by
The method version_info() does not exist on DB. Did you maybe mean version()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
128
    }
129
130
}
131
132
133
class MeekroDB {
134
    // initial connection
135
    public $dbName = '';
136
    public $user = '';
137
    public $password = '';
138
    public $host = 'localhost';
139
    public $port = null;
140
    public $encoding = 'latin1';
141
142
    // configure workings
143
    public $param_char = '%';
144
    public $named_param_seperator = '_';
145
    public $success_handler = false;
146
    public $error_handler = true;
147
    public $throw_exception_on_error = false;
148
    public $nonsql_error_handler = null;
149
    public $throw_exception_on_nonsql_error = false;
150
    public $nested_transactions = false;
151
    public $usenull = true;
152
153
    // internal
154
    public $internal_mysql = null;
155
    public $server_info = null;
156
    public $insert_id = 0;
157
    public $num_rows = 0;
158
    public $affected_rows = 0;
159
    public $current_db = null;
160
    public $nested_transactions_count = 0;
161
162
163
    public function __construct($host = null, $user = null, $password = null, $dbName = null, $port = null, $encoding = null) {
164
    if ($host === null) {
165
        $host = DB::$host;
166
    }
167
    if ($user === null) {
168
        $user = DB::$user;
169
    }
170
    if ($password === null) {
171
        $password = DB::$password;
172
    }
173
    if ($dbName === null) {
174
        $dbName = DB::$dbName;
175
    }
176
    if ($port === null) {
177
        $port = DB::$port;
178
    }
179
    if ($encoding === null) {
180
        $encoding = DB::$encoding;
181
    }
182
183
    $this->host = $host;
184
    $this->user = $user;
185
    $this->password = $password;
186
    $this->dbName = $dbName;
187
    $this->port = $port;
188
    $this->encoding = $encoding;
189
    }
190
191
    public function get() {
192
    $mysql = $this->internal_mysql;
193
194
    if (!($mysql instanceof MySQLi)) {
195
        if (!$this->port) {
196
            $this->port = ini_get('mysqli.default_port');
197
        }
198
        $this->current_db = $this->dbName;
199
      
200
        $mysql = new mysqli($this->host, $this->user, $this->password, $this->dbName, $this->port);
201
202
        if ($mysql->connect_error) {
203
        $this->nonSQLError('Unable to connect to MySQL server! Error: '.$mysql->connect_error);
204
        }
205
206
        $mysql->set_charset($this->encoding);
207
        $this->internal_mysql = $mysql;
208
        $this->server_info = $mysql->server_info;
209
    }
210
211
    return $mysql;
212
    }
213
214
    public function disconnect() {
215
    $mysqli = $this->internal_mysql;
216
    if ($mysqli instanceof MySQLi) {
217
        if ($thread_id = $mysqli->thread_id) {
218
            $mysqli->kill($thread_id);
219
        }
220
        $mysqli->close();
221
    }
222
    $this->internal_mysql = null;
223
    }
224
225
    public function nonSQLError($message) {
226
    if ($this->throw_exception_on_nonsql_error) {
227
        $e = new MeekroDBException($message);
228
        throw $e;
229
    }
230
231
    $error_handler = is_callable($this->nonsql_error_handler) ? $this->nonsql_error_handler : 'meekrodb_error_handler';
232
233
    call_user_func($error_handler, array(
234
        'type' => 'nonsql',
235
        'error' => $message
236
    ));
237
    }
238
239
    public function debugMode($handler = true) {
240
    $this->success_handler = $handler;
241
    }
242
243
    public function serverVersion() { $this->get(); return $this->server_info; }
244
    public function transactionDepth() { return $this->nested_transactions_count; }
245
    public function insertId() { return $this->insert_id; }
246
    public function affectedRows() { return $this->affected_rows; }
247
    public function count() { $args = func_get_args(); return call_user_func_array(array($this, 'numRows'), $args); }
248
    public function numRows() { return $this->num_rows; }
249
250
    public function useDB() { $args = func_get_args(); return call_user_func_array(array($this, 'setDB'), $args); }
251
    public function setDB($dbName) {
252
    $db = $this->get();
253
    if (!$db->select_db($dbName)) {
254
        $this->nonSQLError("Unable to set database to $dbName");
255
    }
256
    $this->current_db = $dbName;
257
    }
258
259
260
    public function startTransaction() {
261
    if ($this->nested_transactions && $this->serverVersion() < '5.5') {
262
        return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL ".$this->serverVersion());
263
    }
264
265
    if (!$this->nested_transactions || $this->nested_transactions_count == 0) {
266
        $this->query('START TRANSACTION');
267
        $this->nested_transactions_count = 1;
268
    } else {
269
        $this->query("SAVEPOINT LEVEL{$this->nested_transactions_count}");
270
        $this->nested_transactions_count++;
271
    }
272
273
    return $this->nested_transactions_count;
274
    }
275
276 View Code Duplication
    public function commit($all = false) {
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...
277
    if ($this->nested_transactions && $this->serverVersion() < '5.5') {
278
        return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL ".$this->serverVersion());
279
    }
280
281
    if ($this->nested_transactions && $this->nested_transactions_count > 0) {
282
            $this->nested_transactions_count--;
283
    }
284
285
    if (!$this->nested_transactions || $all || $this->nested_transactions_count == 0) {
286
        $this->nested_transactions_count = 0;
287
        $this->query('COMMIT');
288
    } else {
289
        $this->query("RELEASE SAVEPOINT LEVEL{$this->nested_transactions_count}");
290
    }
291
292
    return $this->nested_transactions_count;
293
    }
294
295 View Code Duplication
    public function rollback($all = false) {
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...
296
    if ($this->nested_transactions && $this->serverVersion() < '5.5') {
297
        return $this->nonSQLError("Nested transactions are only available on MySQL 5.5 and greater. You are using MySQL ".$this->serverVersion());
298
    }
299
300
    if ($this->nested_transactions && $this->nested_transactions_count > 0) {
301
            $this->nested_transactions_count--;
302
    }
303
304
    if (!$this->nested_transactions || $all || $this->nested_transactions_count == 0) {
305
        $this->nested_transactions_count = 0;
306
        $this->query('ROLLBACK');
307
    } else {
308
        $this->query("ROLLBACK TO SAVEPOINT LEVEL{$this->nested_transactions_count}");
309
    }
310
311
    return $this->nested_transactions_count;
312
    }
313
314
    protected function formatTableName($table) {
315
    $table = trim($table, '`');
316
317 View Code Duplication
    if (strpos($table, '.')) {
318
        return implode('.', array_map(array($this, 'formatTableName'), explode('.', $table)));
319
    } else {
320
        return '`'.str_replace('`', '``', $table).'`';
321
    }
322
    }
323
324
    public function update() {
325
    $args = func_get_args();
326
    $table = array_shift($args);
327
    $params = array_shift($args);
328
    $where = array_shift($args);
329
330
    $query = "UPDATE %b SET %? WHERE ".$where;
331
332
    array_unshift($args, $params);
333
    array_unshift($args, $table);
334
    array_unshift($args, $query);
335
    return call_user_func_array(array($this, 'query'), $args);
336
    }
337
338
    /**
339
     * @param string $which
340
     */
341
    public function insertOrReplace($which, $table, $datas, $options = array()) {
342
    $datas = unserialize(serialize($datas)); // break references within array
343
    $keys = $values = array();
344
345
    if (isset($datas[0]) && is_array($datas[0])) {
346
        foreach ($datas as $datum) {
347
        ksort($datum);
348
        if (!$keys) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $keys 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...
349
            $keys = array_keys($datum);
350
        }
351
        $values[] = array_values($datum);
352
        }
353
354
    } else {
355
        $keys = array_keys($datas);
356
        $values = array_values($datas);
357
    }
358
359
    if (isset($options['ignore']) && $options['ignore']) {
360
        $which = 'INSERT IGNORE';
361
    }
362
363
    if (isset($options['update']) && is_array($options['update']) && $options['update'] && strtolower($which) == 'insert') {
364
        if (array_values($options['update']) !== $options['update']) {
365
        return $this->query("INSERT INTO %b %lb VALUES %? ON DUPLICATE KEY UPDATE %?", $table, $keys, $values, $options['update']);
366
        } else {
367
        $update_str = array_shift($options['update']);
368
        $query_param = array("INSERT INTO %b %lb VALUES %? ON DUPLICATE KEY UPDATE $update_str", $table, $keys, $values);
369
        $query_param = array_merge($query_param, $options['update']);
370
        return call_user_func_array(array($this, 'query'), $query_param);
371
        }
372
373
    }
374
375
    return $this->query("%l INTO %b %lb VALUES %?", $which, $table, $keys, $values);
376
    }
377
378
    public function insert($table, $data) { return $this->insertOrReplace('INSERT', $table, $data); }
379
    public function insertIgnore($table, $data) { return $this->insertOrReplace('INSERT', $table, $data, array('ignore' => true)); }
380
    public function replace($table, $data) { return $this->insertOrReplace('REPLACE', $table, $data); }
381
382
    public function insertUpdate() {
383
    $args = func_get_args();
384
    $table = array_shift($args);
385
    $data = array_shift($args);
386
387
    if (!isset($args[0])) { // update will have all the data of the insert
388
        if (isset($data[0]) && is_array($data[0])) { //multiple insert rows specified -- failing!
389
        $this->nonSQLError("Badly formatted insertUpdate() query -- you didn't specify the update component!");
390
        }
391
392
        $args[0] = $data;
393
    }
394
395
    if (is_array($args[0])) {
396
        $update = $args[0];
397
    } else {
398
        $update = $args;
399
    }
400
401
    return $this->insertOrReplace('INSERT', $table, $data, array('update' => $update));
402
    }
403
404
    public function delete() {
405
    $args = func_get_args();
406
    $table = $this->formatTableName(array_shift($args));
407
    $where = array_shift($args);
408
    $buildquery = "DELETE FROM $table WHERE $where";
409
    array_unshift($args, $buildquery);
410
    return call_user_func_array(array($this, 'query'), $args);
411
    }
412
413
    public function sqleval() {
414
    $args = func_get_args();
415
    $text = call_user_func_array(array($this, 'parseQueryParams'), $args);
416
    return new MeekroDBEval($text);
417
    }
418
419
    public function columnList($table) {
420
    return $this->queryOneColumn('Field', "SHOW COLUMNS FROM $table");
421
    }
422
423
    public function tableList($db = null) {
424
    if ($db) {
425
        $olddb = $this->current_db;
426
        $this->useDB($db);
427
    }
428
429
    $result = $this->queryFirstColumn('SHOW TABLES');
430
    if (isset($olddb)) {
431
        $this->useDB($olddb);
432
    }
433
    return $result;
434
    }
435
436
    protected function preparseQueryParams() {
437
    $args = func_get_args();
438
    $sql = trim(strval(array_shift($args)));
439
    $args_all = $args;
440
441
    if (count($args_all) == 0) {
442
        return array($sql);
443
    }
444
445
    $param_char_length = strlen($this->param_char);
446
    $named_seperator_length = strlen($this->named_param_seperator);
447
448
    $types = array(
449
        $this->param_char.'ll', // list of literals
450
        $this->param_char.'ls', // list of strings
451
        $this->param_char.'l', // literal
452
        $this->param_char.'li', // list of integers
453
        $this->param_char.'ld', // list of decimals
454
        $this->param_char.'lb', // list of backticks
455
        $this->param_char.'lt', // list of timestamps
456
        $this->param_char.'s', // string
457
        $this->param_char.'i', // integer
458
        $this->param_char.'d', // double / decimal
459
        $this->param_char.'b', // backtick
460
        $this->param_char.'t', // timestamp
461
        $this->param_char.'?', // infer type
462
        $this->param_char.'ss'  // search string (like string, surrounded with %'s)
463
    );
464
465
    // generate list of all MeekroDB variables in our query, and their position
466
    // in the form "offset => variable", sorted by offsets
467
    $posList = array();
468
    foreach ($types as $type) {
469
        $lastPos = 0;
470
        while (($pos = strpos($sql, $type, $lastPos)) !== false) {
471
        $lastPos = $pos + 1;
472
        if (isset($posList[$pos]) && strlen($posList[$pos]) > strlen($type)) {
473
            continue;
474
        }
475
        $posList[$pos] = $type;
476
        }
477
    }
478
479
    ksort($posList);
480
481
    // for each MeekroDB variable, substitute it with array(type: i, value: 53) or whatever
482
    $chunkyQuery = array(); // preparsed query
483
    $pos_adj = 0; // how much we've added or removed from the original sql string
484
    foreach ($posList as $pos => $type) {
485
        $type = substr($type, $param_char_length); // variable, without % in front of it
486
        $length_type = strlen($type) + $param_char_length; // length of variable w/o %
487
488
        $new_pos = $pos + $pos_adj; // position of start of variable
489
        $new_pos_back = $new_pos + $length_type; // position of end of variable
490
        $arg_number_length = 0; // length of any named or numbered parameter addition
0 ignored issues
show
Unused Code introduced by
$arg_number_length 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...
491
492
        // handle numbered parameters
493
        if ($arg_number_length = strspn($sql, '0123456789', $new_pos_back)) {
494
        $arg_number = substr($sql, $new_pos_back, $arg_number_length);
495
        if (!array_key_exists($arg_number, $args_all)) {
496
            $this->nonSQLError("Non existent argument reference (arg $arg_number): $sql");
497
        }
498
499
        $arg = $args_all[$arg_number];
500
501
        // handle named parameters
502
        } else if (substr($sql, $new_pos_back, $named_seperator_length) == $this->named_param_seperator) {
503
        $arg_number_length = strspn($sql, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_',
504
            $new_pos_back + $named_seperator_length) + $named_seperator_length;
505
506
        $arg_number = substr($sql, $new_pos_back + $named_seperator_length, $arg_number_length - $named_seperator_length);
507
        if (count($args_all) != 1 || !is_array($args_all[0])) {
508
            $this->nonSQLError("If you use named parameters, the second argument must be an array of parameters");
509
        }
510
        if (!array_key_exists($arg_number, $args_all[0])) {
511
            $this->nonSQLError("Non existent argument reference (arg $arg_number): $sql");
512
        }
513
514
        $arg = $args_all[0][$arg_number];
515
516
        } else {
517
        $arg_number = 0;
0 ignored issues
show
Unused Code introduced by
$arg_number 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...
518
        $arg = array_shift($args);
519
        }
520
521
        if ($new_pos > 0) {
522
            $chunkyQuery[] = substr($sql, 0, $new_pos);
523
        }
524
525
        if (is_object($arg) && ($arg instanceof WhereClause)) {
526
        list($clause_sql, $clause_args) = $arg->textAndArgs();
527
        array_unshift($clause_args, $clause_sql);
528
        $preparsed_sql = call_user_func_array(array($this, 'preparseQueryParams'), $clause_args);
529
        $chunkyQuery = array_merge($chunkyQuery, $preparsed_sql);
530
        } else {
531
        $chunkyQuery[] = array('type' => $type, 'value' => $arg);
532
        }
533
534
        $sql = substr($sql, $new_pos_back + $arg_number_length);
535
        $pos_adj -= $new_pos_back + $arg_number_length;
536
    }
537
538
    if (strlen($sql) > 0) {
539
        $chunkyQuery[] = $sql;
540
    }
541
542
    return $chunkyQuery;
543
    }
544
545
    protected function escape($str) { return "'".$this->get()->real_escape_string(strval($str))."'"; }
546
547
    protected function sanitize($value) {
548
    if (is_object($value)) {
549
        if ($value instanceof MeekroDBEval) {
550
            return $value->text;
551
        } else if ($value instanceof DateTime) {
552
            return $this->escape($value->format('Y-m-d H:i:s'));
553
        } else {
554
            return '';
555
        }
556
    }
557
558
    if (is_null($value)) {
559
        return $this->usenull ? 'NULL' : "''";
560
    } else if (is_bool($value)) {
561
        return ($value ? 1 : 0);
562
    } else if (is_int($value)) {
563
        return $value;
564
    } else if (is_float($value)) {
565
        return $value;
566
    } else if (is_array($value)) {
567
        // non-assoc array?
568
        if (array_values($value) === $value) {
569 View Code Duplication
        if (is_array($value[0])) {
570
            return implode(', ', array_map(array($this, 'sanitize'), $value));
571
        } else {
572
            return '('.implode(', ', array_map(array($this, 'sanitize'), $value)).')';
573
        }
574
        }
575
576
        $pairs = array();
577
        foreach ($value as $k => $v) {
578
        $pairs[] = $this->formatTableName($k).'='.$this->sanitize($v);
579
        }
580
581
        return implode(', ', $pairs);
582
    } else {
583
        return $this->escape($value);
584
    }
585
    }
586
587
    protected function parseTS($ts) {
588
    if (is_string($ts)) {
589
        return date('Y-m-d H:i:s', strtotime($ts));
590
    } else if (is_object($ts) && ($ts instanceof DateTime)) {
591
        return $ts->format('Y-m-d H:i:s');
592
    }
593
    }
594
595
    protected function intval($var) {
596
    if (PHP_INT_SIZE == 8) {
597
        return intval($var);
598
    }
599
    return floor(doubleval($var));
600
    }
601
602
    protected function parseQueryParams() {
603
    $args = func_get_args();
604
    $chunkyQuery = call_user_func_array(array($this, 'preparseQueryParams'), $args);
605
606
    $query = '';
607
    $array_types = array('ls', 'li', 'ld', 'lb', 'll', 'lt');
608
609
    foreach ($chunkyQuery as $chunk) {
610
        if (is_string($chunk)) {
611
        $query .= $chunk;
612
        continue;
613
        }
614
615
        $type = $chunk['type'];
616
        $arg = $chunk['value'];
617
        $result = '';
618
619
        if ($type != '?') {
620
        $is_array_type = in_array($type, $array_types, true);
621
        if ($is_array_type && !is_array($arg)) {
622
            $this->nonSQLError("Badly formatted SQL query: Expected array, got scalar instead!");
623
        } else if (!$is_array_type && is_array($arg)) {
624
            $this->nonSQLError("Badly formatted SQL query: Expected scalar, got array instead!");
625
        }
626
        }
627
628
        if ($type == 's') {
629
            $result = $this->escape($arg);
630
        } else if ($type == 'i') {
631
            $result = $this->intval($arg);
632
        } else if ($type == 'd') {
633
            $result = doubleval($arg);
634
        } else if ($type == 'b') {
635
            $result = $this->formatTableName($arg);
636
        } else if ($type == 'l') {
637
            $result = $arg;
638
        } else if ($type == 'ss') {
639
            $result = $this->escape("%".str_replace(array('%', '_'), array('\%', '\_'), $arg)."%");
640
        } else if ($type == 't') {
641
            $result = $this->escape($this->parseTS($arg));
642
        } else if ($type == 'ls') {
643
            $result = array_map(array($this, 'escape'), $arg);
644
        } else if ($type == 'li') {
645
            $result = array_map(array($this, 'intval'), $arg);
646
        } else if ($type == 'ld') {
647
            $result = array_map('doubleval', $arg);
648
        } else if ($type == 'lb') {
649
            $result = array_map(array($this, 'formatTableName'), $arg);
650
        } else if ($type == 'll') {
651
            $result = $arg;
652
        } else if ($type == 'lt') {
653
            $result = array_map(array($this, 'escape'), array_map(array($this, 'parseTS'), $arg));
654
        } else if ($type == '?') {
655
            $result = $this->sanitize($arg);
656
        } else {
657
            $this->nonSQLError("Badly formatted SQL query: Invalid MeekroDB param $type");
658
        }
659
660
        if (is_array($result)) {
661
            $result = '('.implode(',', $result).')';
662
        }
663
664
        $query .= $result;
665
    }
666
667
    return $query;
668
    }
669
670
    /**
671
     * @param string $prepend
672
     */
673
    protected function prependCall($function, $args, $prepend) { array_unshift($args, $prepend); return call_user_func_array($function, $args); }
674
    public function query() { $args = func_get_args(); return $this->prependCall(array($this, 'queryHelper'), $args, 'assoc'); }
675
    public function queryAllLists() { $args = func_get_args(); return $this->prependCall(array($this, 'queryHelper'), $args, 'list'); }
676
    public function queryFullColumns() { $args = func_get_args(); return $this->prependCall(array($this, 'queryHelper'), $args, 'full'); }
677
678
    public function queryRaw() { $args = func_get_args(); return $this->prependCall(array($this, 'queryHelper'), $args, 'raw_buf'); }
679
    public function queryRawUnbuf() { $args = func_get_args(); return $this->prependCall(array($this, 'queryHelper'), $args, 'raw_unbuf'); }
680
681
    protected function queryHelper() {
682
    $args = func_get_args();
683
    $type = array_shift($args);
684
    $db = $this->get();
685
686
    $is_buffered = true;
687
    $row_type = 'assoc'; // assoc, list, raw
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
688
    $full_names = false;
689
690
    switch ($type) {
691
        case 'assoc':
692
        break;
693
        case 'list':
694
        $row_type = 'list';
695
        break;
696
        case 'full':
697
        $row_type = 'list';
698
        $full_names = true;
699
        break;
700
        case 'raw_buf':
701
        $row_type = 'raw';
702
        break;
703
        case 'raw_unbuf':
704
        $is_buffered = false;
705
        $row_type = 'raw';
706
        break;
707
        default:
708
        $this->nonSQLError('Error -- invalid argument to queryHelper!');
709
    }
710
711
    $sql = call_user_func_array(array($this, 'parseQueryParams'), $args);
712
713
    if ($this->success_handler) {
714
        $starttime = microtime(true);
715
    }
716
    $result = $db->query($sql, $is_buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT);
717
    if ($this->success_handler) {
718
        $runtime = microtime(true) - $starttime;
0 ignored issues
show
Bug introduced by
The variable $starttime does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
719
    } else {
720
        $runtime = 0;
721
    }
722
723
    // ----- BEGIN ERROR HANDLING
724
    if (!$sql || $db->error) {
725
        if ($this->error_handler) {
726
        $db_error = $db->error;
727
        $db_errno = $db->errno;
728
    $db->query(
729
        "INSERT INTO ".$GLOBALS['pre']."log_system SET
730
      date=".time().",
731
      qui=".$_SESSION['user_id'].",
732
      label='Query: ".addslashes($sql)."<br />Error: ".addslashes($db_error)."<br />@ ".mysqli_real_escape_string($link, filter_var($_SERVER['REQUEST_URI'], FILTER_SANITIZE_STRING))."',
0 ignored issues
show
Bug introduced by
The variable $link does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
733
      type='error'",
734
        MYSQLI_USE_RESULT
735
    );
736
737
        $error_handler = is_callable($this->error_handler) ? $this->error_handler : 'meekrodb_error_handler';
738
739
        call_user_func($error_handler, array(
740
            'type' => 'sql',
741
            'query' => $sql,
742
            'error' => $db_error,
743
            'code' => $db_errno
744
        ));
745
        }
746
747
        if ($this->throw_exception_on_error) {
748
        $e = new MeekroDBException($db_error, $sql, $db_errno);
0 ignored issues
show
Bug introduced by
The variable $db_error does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $db_errno does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
749
        throw $e;
750
        }
751
    } else if ($this->success_handler) {
752
        $runtime = sprintf('%f', $runtime * 1000);
753
        $success_handler = is_callable($this->success_handler) ? $this->success_handler : 'meekrodb_debugmode_handler';
754
755
        call_user_func($success_handler, array(
756
        'query' => $sql,
757
        'runtime' => $runtime,
758
        'affected' => $db->affected_rows
759
        ));
760
    }
761
762
    // ----- END ERROR HANDLING
763
764
    $this->insert_id = $db->insert_id;
765
    $this->affected_rows = $db->affected_rows;
766
767
    // mysqli_result->num_rows won't initially show correct results for unbuffered data
768
    if ($is_buffered && ($result instanceof MySQLi_Result)) {
769
        $this->num_rows = $result->num_rows;
770
    } else {
771
        $this->num_rows = null;
772
    }
773
774
    if ($row_type == 'raw' || !($result instanceof MySQLi_Result)) {
775
        return $result;
776
    }
777
778
    $return = array();
779
780
    if ($full_names) {
781
        $infos = array();
782
        foreach ($result->fetch_fields() as $info) {
783
        if (strlen($info->table)) {
784
            $infos[] = $info->table.'.'.$info->name;
785
        } else {
786
            $infos[] = $info->name;
787
        }
788
        }
789
    }
790
791
    while ($row = ($row_type == 'assoc' ? $result->fetch_assoc() : $result->fetch_row())) {
792
        if ($full_names) {
793
            $row = array_combine($infos, $row);
0 ignored issues
show
Bug introduced by
The variable $infos does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
794
        }
795
        $return[] = $row;
796
    }
797
798
    // free results
799
    $result->free();
800
    while ($db->more_results()) {
801
        $db->next_result();
802
        if ($result = $db->use_result()) {
803
            $result->free();
804
        }
805
    }
806
807
    return $return;
808
    }
809
810
    public function queryOneRow() { $args = func_get_args(); return call_user_func_array(array($this, 'queryFirstRow'), $args); }
811 View Code Duplication
    public function queryFirstRow() {
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...
812
    $args = func_get_args();
813
    $result = call_user_func_array(array($this, 'query'), $args);
814
    if (!$result) {
815
        return null;
816
    }
817
    return reset($result);
818
    }
819
820
    public function queryOneList() { $args = func_get_args(); return call_user_func_array(array($this, 'queryFirstList'), $args); }
821 View Code Duplication
    public function queryFirstList() {
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...
822
    $args = func_get_args();
823
    $result = call_user_func_array(array($this, 'queryAllLists'), $args);
824
    if (!$result) {
825
        return null;
826
    }
827
    return reset($result);
828
    }
829
830
    public function queryFirstColumn() {
831
    $args = func_get_args();
832
    $results = call_user_func_array(array($this, 'queryAllLists'), $args);
833
    $ret = array();
834
835
    if (!count($results) || !count($results[0])) {
836
        return $ret;
837
    }
838
839
    foreach ($results as $row) {
840
        $ret[] = $row[0];
841
    }
842
843
    return $ret;
844
    }
845
846
    public function queryOneColumn() {
847
    $args = func_get_args();
848
    $column = array_shift($args);
849
    $results = call_user_func_array(array($this, 'query'), $args);
850
    $ret = array();
851
852
    if (!count($results) || !count($results[0])) {
853
        return $ret;
854
    }
855 View Code Duplication
    if ($column === null) {
856
        $keys = array_keys($results[0]);
857
        $column = $keys[0];
858
    }
859
860
    foreach ($results as $row) {
861
        $ret[] = $row[$column];
862
    }
863
864
    return $ret;
865
    }
866
867
    public function queryFirstField() {
868
    $args = func_get_args();
869
    $row = call_user_func_array(array($this, 'queryFirstList'), $args);
870
    if ($row == null) {
871
        return null;
872
    }
873
    return $row[0];
874
    }
875
876
    public function queryOneField() {
877
    $args = func_get_args();
878
    $column = array_shift($args);
879
880
    $row = call_user_func_array(array($this, 'queryOneRow'), $args);
881
    if ($row == null) {
882
        return null;
883 View Code Duplication
    } else if ($column === null) {
884
        $keys = array_keys($row);
885
        $column = $keys[0];
886
    }
887
888
    return $row[$column];
889
    }
890
}
891
892
class WhereClause {
893
    public $type = 'and'; //AND or OR
894
    public $negate = false;
895
    public $clauses = array();
896
897
    function __construct($type) {
898
    $type = strtolower($type);
899
    if ($type !== 'or' && $type !== 'and') {
900
        DB::nonSQLError('you must use either WhereClause(and) or WhereClause(or)');
901
    }
902
    $this->type = $type;
903
    }
904
905
    function add() {
906
    $args = func_get_args();
907
    $sql = array_shift($args);
908
909
    if ($sql instanceof WhereClause) {
910
        $this->clauses[] = $sql;
911
    } else {
912
        $this->clauses[] = array('sql' => $sql, 'args' => $args);
913
    }
914
    }
915
916
    function negateLast() {
917
    $i = count($this->clauses) - 1;
918
    if (!isset($this->clauses[$i])) {
919
        return;
920
    }
921
922
    if ($this->clauses[$i] instanceof WhereClause) {
923
        $this->clauses[$i]->negate();
924
    } else {
925
        $this->clauses[$i]['sql'] = 'NOT ('.$this->clauses[$i]['sql'].')';
926
    }
927
    }
928
929
    function negate() {
930
    $this->negate = !$this->negate;
931
    }
932
933
    function addClause($type) {
934
    $r = new WhereClause($type);
935
    $this->add($r);
936
    return $r;
937
    }
938
939
    function count() {
940
    return count($this->clauses);
941
    }
942
943
    function textAndArgs() {
944
    $sql = array();
945
    $args = array();
946
947
    if (count($this->clauses) == 0) {
948
        return array('(1)', $args);
949
    }
950
951
    foreach ($this->clauses as $clause) {
952
        if ($clause instanceof WhereClause) {
953
        list($clause_sql, $clause_args) = $clause->textAndArgs();
954
        } else {
955
        $clause_sql = $clause['sql'];
956
        $clause_args = $clause['args'];
957
        }
958
959
        $sql[] = "($clause_sql)";
960
        $args = array_merge($args, $clause_args);
961
    }
962
963
    if ($this->type == 'and') {
964
        $sql = implode(' AND ', $sql);
965
    } else {
966
        $sql = implode(' OR ', $sql);
967
    }
968
969
    if ($this->negate) {
970
        $sql = '(NOT '.$sql.')';
971
    }
972
    return array($sql, $args);
973
    }
974
975
    // backwards compatability
976
    // we now return full WhereClause object here and evaluate it in preparseQueryParams
977
    function text() { return $this; }
978
}
979
980
class DBTransaction {
981
    private $committed = false;
982
983
    function __construct() {
984
    DB::startTransaction();
985
    }
986
    function __destruct() {
987
    if (!$this->committed) {
988
        DB::rollback();
989
    }
990
    }
991
    function commit() {
992
    DB::commit();
993
    $this->committed = true;
994
    }
995
996
997
}
998
999
class MeekroDBException extends Exception {
1000
    protected $query = '';
1001
1002
    function __construct($message = '', $query = '', $code = 0) {
1003
    parent::__construct($message);
1004
    $this->query = $query;
1005
    $this->code = $code;
1006
    }
1007
1008
    public function getQuery() { return $this->query; }
1009
}
1010
1011
class DBHelper {
1012
    /*
1013
    verticalSlice
1014
    1. For an array of assoc rays, return an array of values for a particular key
1015
    2. if $keyfield is given, same as above but use that hash key as the key in new array
1016
  */
1017
1018
    public static function verticalSlice($array, $field, $keyfield = null) {
1019
    $array = (array) $array;
1020
1021
    $R = array();
1022
    foreach ($array as $obj) {
1023
        if (!array_key_exists($field, $obj)) {
1024
            die("verticalSlice: array doesn't have requested field\n");
1025
        }
1026
1027
        if ($keyfield) {
1028
        if (!array_key_exists($keyfield, $obj)) {
1029
            die("verticalSlice: array doesn't have requested field\n");
1030
        }
1031
        $R[$obj[$keyfield]] = $obj[$field];
1032
        } else {
1033
        $R[] = $obj[$field];
1034
        }
1035
    }
1036
    return $R;
1037
    }
1038
1039
    /*
1040
    reIndex
1041
    For an array of assoc rays, return a new array of assoc rays using a certain field for keys
1042
  */
1043
1044
    public static function reIndex() {
1045
    $fields = func_get_args();
1046
    $array = array_shift($fields);
1047
    $array = (array) $array;
1048
1049
    $R = array();
1050
    foreach ($array as $obj) {
1051
        $target = & $R;
1052
1053
        foreach ($fields as $field) {
1054
        if (!array_key_exists($field, $obj)) {
1055
            die("reIndex: array doesn't have requested field\n");
1056
        }
1057
1058
        $nextkey = $obj[$field];
1059
        $target = & $target[$nextkey];
1060
        }
1061
        $target = $obj;
1062
    }
1063
    return $R;
1064
    }
1065
}
1066
1067
function meekrodb_error_handler($params) {
1068
    echo prepareExchangedData('[{"error" : "'.$params['error'].'"}]', "encode");
1069
1070
    die;
1071
}
1072
1073
function meekrodb_debugmode_handler($params) {
1074
    echo "QUERY: ".$params['query']." [".$params['runtime']." ms]";
1075
    if (php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR'])) {
1076
    echo "\n";
1077
    } else {
1078
    echo "<br>\n";
1079
    }
1080
}
1081
1082
class MeekroDBEval {
1083
    public $text = '';
1084
1085
    function __construct($text) {
1086
    $this->text = $text;
1087
    }
1088
}