Issues (3083)

htdocs/class/database/mysqldatabase.php (2 issues)

1
<?php
2
/**
3
 * MySQL access using MySQLi extension
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       (c) 2000-2019 XOOPS Project (https://xoops.org)
13
 * @license             GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             class
15
 * @subpackage          database
16
 * @since               1.0.0
17
 * @author              Kazumi Ono <[email protected]>
18
 * @author              Rodney Fulk <[email protected]>
19
 */
20
defined('XOOPS_ROOT_PATH') || die('Restricted access');
21
22
include_once XOOPS_ROOT_PATH . '/class/database/database.php';
23
24
/**
25
 * connection to a mysql database using MySQLi extension
26
 *
27
 * @abstract
28
 * @author              Kazumi Ono <[email protected]>
29
 * @copyright       (c) 2000-2019 XOOPS Project (https://xoops.org)
30
 * @package             class
31
 * @subpackage          database
32
 */
33
abstract class XoopsMySQLDatabase extends XoopsDatabase
34
{
35
    /**
36
     * Database connection
37
     *
38
     * @var XoopsDatabase|mysqli
39
     */
40
    public $conn;
41
42
    /**
43
     * connect to the database
44
     *
45
     * @param bool $selectdb select the database now?
46
     * @return bool successful?
47
     */
48
    public function connect($selectdb = true)
49
    {
50
        if (!extension_loaded('mysqli')) {
51
            trigger_error('notrace:mysqli extension not loaded', E_USER_ERROR);
52
53
            return false;
54
        }
55
56
        $this->allowWebChanges = ($_SERVER['REQUEST_METHOD'] !== 'GET');
57
58
        if ($selectdb) {
59
            $dbname = constant('XOOPS_DB_NAME');
60
        } else {
61
            $dbname = '';
62
        }
63
        mysqli_report(MYSQLI_REPORT_OFF);
64
        if (XOOPS_DB_PCONNECT == 1) {
0 ignored issues
show
The condition XOOPS_DB_PCONNECT == 1 is always false.
Loading history...
65
            $this->conn = new mysqli('p:' . XOOPS_DB_HOST, XOOPS_DB_USER, XOOPS_DB_PASS, $dbname);
66
        } else {
67
            $this->conn = new mysqli(XOOPS_DB_HOST, XOOPS_DB_USER, XOOPS_DB_PASS, $dbname);
68
        }
69
70
        // errno is 0 if connect was successful
71
        if (0 !== $this->conn->connect_errno) {
72
            return false;
73
        }
74
75
        if (defined('XOOPS_DB_CHARSET') && ('' !== XOOPS_DB_CHARSET)) {
0 ignored issues
show
The condition '' !== XOOPS_DB_CHARSET is always false.
Loading history...
76
            // $this->queryF("SET NAMES '" . XOOPS_DB_CHARSET . "'");
77
            $this->conn->set_charset(XOOPS_DB_CHARSET);
78
        }
79
        $this->queryF('SET SQL_BIG_SELECTS = 1');
80
81
        return true;
82
    }
83
84
    /**
85
     * generate an ID for a new row
86
     *
87
     * This is for compatibility only. Will always return 0, because MySQL supports
88
     * autoincrement for primary keys.
89
     *
90
     * @param string $sequence name of the sequence from which to get the next ID
91
     * @return int always 0, because mysql has support for autoincrement
92
     */
93
    public function genId($sequence)
94
    {
95
        return 0; // will use auto_increment
96
    }
97
98
    /**
99
     * Get a result row as an enumerated array
100
     *
101
     * @param mysqli_result $result
102
     *
103
     * @return array|false false on end of data
104
     */
105
    public function fetchRow($result)
106
    {
107
        $row = @mysqli_fetch_row($result);
108
        return (null === $row) ? false : $row;
109
    }
110
111
    /**
112
     * Fetch a result row as an associative array
113
     *
114
     * @param mysqli_result $result
115
     *
116
     * @return array|false false on end of data
117
     */
118
    public function fetchArray($result)
119
    {
120
        $row = @mysqli_fetch_assoc($result);
121
        return (null === $row) ? false : $row;
122
123
    }
124
125
    /**
126
     * Fetch a result row as an associative array
127
     *
128
     * @param mysqli_result $result
129
     *
130
     * @return array|false false on end of data
131
     */
132
    public function fetchBoth($result)
133
    {
134
        $row = @mysqli_fetch_array($result, MYSQLI_BOTH);
135
        return (null === $row) ? false : $row;
136
    }
137
138
    /**
139
     * XoopsMySQLDatabase::fetchObject()
140
     *
141
     * @param mysqli_result $result
142
     * @return stdClass|false false on end of data
143
     */
144
    public function fetchObject($result)
145
    {
146
        $row = @mysqli_fetch_object($result);
147
        return (null === $row) ? false : $row;
148
    }
149
150
    /**
151
     * Get the ID generated from the previous INSERT operation
152
     *
153
     * @return int|string
154
     */
155
    public function getInsertId()
156
    {
157
        return mysqli_insert_id($this->conn);
158
    }
159
160
    /**
161
     * Get number of rows in result
162
     *
163
     * @param mysqli_result $result
164
     *
165
     * @return int
166
     */
167
    public function getRowsNum($result)
168
    {
169
        return (int)@mysqli_num_rows($result);
170
    }
171
172
    /**
173
     * Get number of affected rows
174
     *
175
     * @return int
176
     */
177
    public function getAffectedRows()
178
    {
179
        return (int)mysqli_affected_rows($this->conn);
180
    }
181
182
    /**
183
     * Close MySQL connection
184
     *
185
     * @return void
186
     */
187
    public function close()
188
    {
189
        mysqli_close($this->conn);
190
    }
191
192
    /**
193
     * will free all memory associated with the result identifier result.
194
     *
195
     * @param mysqli_result $result result
196
     *
197
     * @return void
198
     */
199
    public function freeRecordSet($result)
200
    {
201
        mysqli_free_result($result);
202
    }
203
204
    /**
205
     * Returns the text of the error message from previous MySQL operation
206
     *
207
     * @return string Returns the error text from the last MySQL function, or '' (the empty string) if no error occurred.
208
     */
209
    public function error()
210
    {
211
        return @mysqli_error($this->conn);
212
    }
213
214
    /**
215
     * Returns the numerical value of the error message from previous MySQL operation
216
     *
217
     * @return int Returns the error number from the last MySQL function, or 0 (zero) if no error occurred.
218
     */
219
    public function errno()
220
    {
221
        return @mysqli_errno($this->conn);
222
    }
223
224
    /**
225
     * Returns escaped string text with single quotes around it to be safely stored in database
226
     *
227
     * @param string $str unescaped string text
228
     * @return string escaped string text with single quotes around
229
     */
230
    public function quoteString($str)
231
    {
232
        return $this->quote($str);
233
    }
234
235
    /**
236
     * Quotes a string for use in a query.
237
     *
238
     * @param string $string string to quote/escape for use in query
239
     *
240
     * @return string
241
     */
242
    public function quote($string)
243
    {
244
        $quoted = $this->escape($string);
245
        return "'{$quoted}'";
246
    }
247
248
    /**
249
     * Escapes a string for use in a query. Does not add surrounding quotes.
250
     *
251
     * @param string $string string to escape
252
     *
253
     * @return string
254
     */
255
    public function escape($string)
256
    {
257
        return mysqli_real_escape_string($this->conn, (string) $string);
258
    }
259
260
    /**
261
     * perform a query on the database
262
     *
263
     * @param string $sql   a valid MySQL query
264
     * @param int    $limit number of records to return
265
     * @param int    $start offset of first record to return
266
     * @return mysqli_result|bool query result or FALSE if successful
267
     *                      or TRUE if successful and no result
268
     */
269
    public function queryF($sql, $limit = 0, $start = 0)
270
    {
271
        if (!empty($limit)) {
272
            if (empty($start)) {
273
                $start = 0;
274
            }
275
            $sql = $sql . ' LIMIT ' . (int)$start . ', ' . (int)$limit;
276
        }
277
        $this->logger->startTime('query_time');
278
        $result = mysqli_query($this->conn, $sql);
279
        $this->logger->stopTime('query_time');
280
        $query_time = $this->logger->dumpTime('query_time', true);
281
        if ($result) {
282
            $this->logger->addQuery($sql, null, null, $query_time);
283
284
            return $result;
285
        } else {
286
            $this->logger->addQuery($sql, $this->error(), $this->errno(), $query_time);
287
288
            return false;
289
        }
290
    }
291
292
    /**
293
     * perform a query
294
     *
295
     * This method is empty and does nothing! It should therefore only be
296
     * used if nothing is exactly what you want done! ;-)
297
     *
298
     * @param string $sql   a valid MySQL query
299
     * @param int    $limit number of records to return
300
     * @param int    $start offset of first record to return
301
     *
302
     * @return mysqli_result|bool query result or FALSE if successful
303
     *                      or TRUE if successful and no result
304
     */
305
    abstract public function query($sql, $limit = 0, $start = 0);
306
307
    /**
308
     * perform queries from SQL dump file in a batch
309
     *
310
     * @param string $file file path to an SQL dump file
311
     * @return bool FALSE if failed reading SQL file or TRUE if the file has been read and queries executed
312
     */
313
    public function queryFromFile($file)
314
    {
315
        if (false !== ($fp = fopen($file, 'r'))) {
316
            include_once XOOPS_ROOT_PATH . '/class/database/sqlutility.php';
317
            $sql_queries = trim(fread($fp, filesize($file)));
318
            SqlUtility::splitMySqlFile($pieces, $sql_queries);
319
            foreach ($pieces as $query) {
320
                // [0] contains the prefixed query
321
                // [4] contains unprefixed table name
322
                $prefixed_query = SqlUtility::prefixQuery(trim($query), $this->prefix());
323
                if ($prefixed_query != false) {
324
                    $this->query($prefixed_query[0]);
325
                }
326
            }
327
328
            return true;
329
        }
330
331
        return false;
332
    }
333
334
    /**
335
     * Get field name
336
     *
337
     * @param mysqli_result $result query result
338
     * @param int           $offset numerical field index
339
     *
340
     * @return string
341
     */
342
    public function getFieldName($result, $offset)
343
    {
344
        return $result->fetch_field_direct($offset)->name;
345
    }
346
347
    /**
348
     * Get field type
349
     *
350
     * @param mysqli_result $result query result
351
     * @param int           $offset numerical field index
352
     *
353
     * @return string
354
     */
355
    public function getFieldType($result, $offset)
356
    {
357
        $typecode = $result->fetch_field_direct($offset)->type;
358
        switch ($typecode) {
359
            case MYSQLI_TYPE_DECIMAL:
360
            case MYSQLI_TYPE_NEWDECIMAL:
361
                $type = 'decimal';
362
                break;
363
            case MYSQLI_TYPE_BIT:
364
                $type = 'bit';
365
                break;
366
            case MYSQLI_TYPE_TINY:
367
            case MYSQLI_TYPE_CHAR:
368
                $type = 'tinyint';
369
                break;
370
            case MYSQLI_TYPE_SHORT:
371
                $type = 'smallint';
372
                break;
373
            case MYSQLI_TYPE_LONG:
374
                $type = 'int';
375
                break;
376
            case MYSQLI_TYPE_FLOAT:
377
                $type = 'float';
378
                break;
379
            case MYSQLI_TYPE_DOUBLE:
380
                $type = 'double';
381
                break;
382
            case MYSQLI_TYPE_NULL:
383
                $type = 'NULL';
384
                break;
385
            case MYSQLI_TYPE_TIMESTAMP:
386
                $type = 'timestamp';
387
                break;
388
            case MYSQLI_TYPE_LONGLONG:
389
                $type = 'bigint';
390
                break;
391
            case MYSQLI_TYPE_INT24:
392
                $type = 'mediumint';
393
                break;
394
            case MYSQLI_TYPE_NEWDATE:
395
            case MYSQLI_TYPE_DATE:
396
                $type = 'date';
397
                break;
398
            case MYSQLI_TYPE_TIME:
399
                $type = 'time';
400
                break;
401
            case MYSQLI_TYPE_DATETIME:
402
                $type = 'datetime';
403
                break;
404
            case MYSQLI_TYPE_YEAR:
405
                $type = 'year';
406
                break;
407
            case MYSQLI_TYPE_INTERVAL:
408
                $type = 'interval';
409
                break;
410
            case MYSQLI_TYPE_ENUM:
411
                $type = 'enum';
412
                break;
413
            case MYSQLI_TYPE_SET:
414
                $type = 'set';
415
                break;
416
            case MYSQLI_TYPE_TINY_BLOB:
417
                $type = 'tinyblob';
418
                break;
419
            case MYSQLI_TYPE_MEDIUM_BLOB:
420
                $type = 'mediumblob';
421
                break;
422
            case MYSQLI_TYPE_LONG_BLOB:
423
                $type = 'longblob';
424
                break;
425
            case MYSQLI_TYPE_BLOB:
426
                $type = 'blob';
427
                break;
428
            case MYSQLI_TYPE_VAR_STRING:
429
                $type = 'varchar';
430
                break;
431
            case MYSQLI_TYPE_STRING:
432
                $type = 'char';
433
                break;
434
            case MYSQLI_TYPE_GEOMETRY:
435
                $type = 'geometry';
436
                break;
437
            default:
438
                $type = 'unknown';
439
                break;
440
        }
441
442
        return $type;
443
    }
444
445
    /**
446
     * Get number of fields in result
447
     *
448
     * @param mysqli_result $result query result
449
     *
450
     * @return int
451
     */
452
    public function getFieldsNum($result)
453
    {
454
        return mysqli_num_fields($result);
455
    }
456
457
    /**
458
     * getServerVersion get version of the mysql server
459
     *
460
     * @return string
461
     */
462
    public function getServerVersion()
463
    {
464
        return mysqli_get_server_info($this->conn);
465
    }
466
467
    /**
468
     * Test the passed result to determine if it is a valid result set
469
     *
470
     * @param mixed $result value to test
471
     *
472
     * @return bool true if $result is a database result set, otherwise false
473
     */
474
    public function isResultSet($result)
475
    {
476
        return is_a($result, 'mysqli_result');
477
    }
478
}
479
480
/**
481
 * Safe Connection to a MySQL database.
482
 *
483
 * @author              Kazumi Ono <[email protected]>
484
 * @copyright       (c) 2000-2019 XOOPS Project (https://xoops.org)
485
 * @package             kernel
486
 * @subpackage          database
487
 */
488
class XoopsMySQLDatabaseSafe extends XoopsMySQLDatabase
489
{
490
    /**
491
     * perform a query on the database
492
     *
493
     * @param string $sql   a valid MySQL query
494
     * @param int    $limit number of records to return
495
     * @param int    $start offset of first record to return
496
     * @return mysqli_result|bool query result or FALSE if successful
497
     *                      or TRUE if successful and no result
498
     */
499
    public function query($sql, $limit = 0, $start = 0)
500
    {
501
        return $this->queryF($sql, $limit, $start);
502
    }
503
}
504
505
/**
506
 * Read-Only connection to a MySQL database.
507
 *
508
 * This class allows only SELECT queries to be performed through its
509
 * {@link query()} method for security reasons.
510
 *
511
 * @author              Kazumi Ono <[email protected]>
512
 * @copyright       (c) 2000-2019 XOOPS Project (https://xoops.org)
513
 * @package             class
514
 * @subpackage          database
515
 */
516
class XoopsMySQLDatabaseProxy extends XoopsMySQLDatabase
517
{
518
    /**
519
     * perform a query on the database
520
     *
521
     * this method allows only SELECT queries for safety.
522
     *
523
     * @param string $sql   a valid MySQL query
524
     * @param int    $limit number of records to return
525
     * @param int    $start offset of first record to return
526
     *
527
     * @return mysqli_result|bool query result or FALSE if successful
528
     *                      or TRUE if successful and no result
529
     */
530
    public function query($sql, $limit = 0, $start = 0)
531
    {
532
        $sql = ltrim($sql);
533
        if (!$this->allowWebChanges && strtolower(substr($sql, 0, 6)) !== 'select') {
534
            trigger_error('Database updates are not allowed during processing of a GET request', E_USER_WARNING);
535
536
            return false;
537
        }
538
539
        return $this->queryF($sql, $limit, $start);
540
    }
541
}
542