DoliDBMysqli::__construct()   F
last analyzed

Complexity

Conditions 29
Paths 6432

Size

Total Lines 124
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 29
eloc 79
nc 6432
nop 6
dl 0
loc 124
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* Copyright (C) 2001       Fabien Seisen           <[email protected]>
4
 * Copyright (C) 2002-2005	Rodolphe Quiedeville	<[email protected]>
5
 * Copyright (C) 2004-2011	Laurent Destailleur		<[email protected]>
6
 * Copyright (C) 2006		Andre Cianfarani		<[email protected]>
7
 * Copyright (C) 2005-2012	Regis Houssin			<[email protected]>
8
 * Copyright (C) 2015       Raphaël Doursenaud      <[email protected]>
9
 * Copyright (C) 2024		MDW							<[email protected]>
10
 * Copyright (C) 2024       Rafael San José             <[email protected]>
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 3 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24
 */
25
26
use Dolibarr\Lib\ViewMain;
27
28
/**
29
 *  \file       htdocs/core/db/mysqli.class.php
30
 *  \brief      Class file to manage Dolibarr database access for a MySQL database
31
 */
32
33
require_once constant('DOL_DOCUMENT_ROOT') . '/core/db/DoliDB.class.php';
34
35
/**
36
 *  Class to manage Dolibarr database access for a MySQL database using the MySQLi extension
37
 */
38
class DoliDBMysqli extends DoliDB
39
{
40
    /** @var mysqli Database object */
41
    public $db;
42
    //! Database type
43
    public $type = 'mysqli';
44
45
    //! Database label
46
    const LABEL = 'MySQL or MariaDB';
47
    //! Version min database
48
    const VERSIONMIN = '5.0.3';
49
50
    /** @var bool|mysqli_result Resultset of last query */
51
    private $_results;
52
53
    /**
54
     *  Constructor.
55
     *  This create an opened connection to a database server and eventually to a database
56
     *
57
     * @param string $type Type of database (mysql, pgsql...). Not used.
58
     * @param string $host Address of database server
59
     * @param string $user Name of database user
60
     * @param string $pass Password of database user
61
     * @param string $name Name of database
62
     * @param int $port Port of database server
63
     */
64
    public function __construct($type, $host, $user, $pass, $name = '', $port = 0)
65
    {
66
        global $conf, $langs;
67
68
        // Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
69
        if (!empty($conf->db->character_set)) {
70
            $this->forcecharset = $conf->db->character_set;
71
        }
72
        if (!empty($conf->db->dolibarr_main_db_collation)) {
73
            $this->forcecollate = $conf->db->dolibarr_main_db_collation;
74
        }
75
76
        $this->database_user = $user;
77
        $this->database_host = $host;
78
        $this->database_port = $port;
79
80
        $this->transaction_opened = 0;
81
82
        //print "Name DB: $host,$user,$pass,$name<br>";
83
84
        if (!class_exists('mysqli')) {
85
            $this->connected = false;
86
            $this->ok = false;
87
            $this->error = "Mysqli PHP functions for using Mysqli driver are not available in this version of PHP. Try to use another driver.";
88
            dol_syslog(get_only_class($this) . "::DoliDBMysqli : Mysqli PHP functions for using Mysqli driver are not available in this version of PHP. Try to use another driver.", LOG_ERR);
89
        }
90
91
        if (!$host) {
92
            $this->connected = false;
93
            $this->ok = false;
94
            $this->error = $langs->trans("ErrorWrongHostParameter");
95
            dol_syslog(get_only_class($this) . "::DoliDBMysqli : Connect error, wrong host parameters", LOG_ERR);
96
        }
97
98
        // Try server connection
99
        // We do not try to connect to database, only to server. Connect to database is done later in constructor
100
        $this->db = $this->connect($host, $user, $pass, '', $port);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->connect($host, $user, $pass, '', $port) can also be of type false. However, the property $db is declared as type mysqli. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
101
102
        if ($this->db && empty($this->db->connect_errno)) {
103
            $this->connected = true;
104
            $this->ok = true;
105
        } else {
106
            $this->connected = false;
107
            $this->ok = false;
108
            $this->error = empty($this->db) ? 'Failed to connect' : $this->db->connect_error;
109
            dol_syslog(get_only_class($this) . "::DoliDBMysqli Connect error: " . $this->error, LOG_ERR);
110
        }
111
112
        // If server connection is ok, we try to connect to the database
113
        if ($this->connected && $name) {
114
            if ($this->select_db($name)) {
115
                $this->database_selected = true;
116
                $this->database_name = $name;
117
                $this->ok = true;
118
119
                // If client is old latin, we force utf8
120
                $clientmustbe = empty($conf->db->character_set) ? 'utf8' : (string)$conf->db->character_set;
121
                if (preg_match('/latin1/', $clientmustbe)) {
122
                    $clientmustbe = 'utf8';
123
                }
124
125
                $disableforcecharset = 0;   // Set to 1 to test without charset forcing
126
                if (empty($disableforcecharset) && $this->db->character_set_name() != $clientmustbe) {
127
                    try {
128
                        //print "You should set the \$dolibarr_main_db_character_set and \$dolibarr_main_db_collation for the PHP to the one of the database ".$this->db->character_set_name();
129
                        dol_syslog(get_only_class($this) . "::DoliDBMysqli You should set the \$dolibarr_main_db_character_set and \$dolibarr_main_db_collation for the PHP to the one of the database " . $this->db->character_set_name(), LOG_WARNING);
130
                        $this->db->set_charset($clientmustbe); // This set charset, but with a bad collation
131
                    } catch (Exception $e) {
132
                        print 'Failed to force character_set_client to ' . $clientmustbe . " (according to setup) to match the one of the server database.<br>\n";
133
                        print $e->getMessage();
134
                        print "<br>\n";
135
                        if ($clientmustbe != 'utf8') {
136
                            print 'Edit conf/conf.php file to set a charset "utf8"';
137
                            if ($clientmustbe != 'utf8mb4') {
138
                                print ' or "utf8mb4"';
139
                            }
140
                            print ' instead of "' . $clientmustbe . '".' . "\n";
141
                        }
142
                        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
143
                    }
144
145
                    $collation = (empty($conf) ? 'utf8_unicode_ci' : $conf->db->dolibarr_main_db_collation);
146
                    if (preg_match('/latin1/', $collation)) {
147
                        $collation = 'utf8_unicode_ci';
148
                    }
149
150
                    if (!preg_match('/general/', $collation)) {
151
                        $this->db->query("SET collation_connection = " . $collation);
152
                    }
153
                }
154
            } else {
155
                $this->database_selected = false;
156
                $this->database_name = '';
157
                $this->ok = false;
158
                $this->error = $this->error();
159
                dol_syslog(get_only_class($this) . "::DoliDBMysqli : Select_db error " . $this->error, LOG_ERR);
160
            }
161
        } else {
162
            // No selection of database done. We may only be connected or not (ok or ko) to the server.
163
            $this->database_selected = false;
164
165
            if ($this->connected) {
166
                // If client is old latin, we force utf8
167
                $clientmustbe = empty($conf->db->character_set) ? 'utf8' : (string)$conf->db->character_set;
168
                if (preg_match('/latin1/', $clientmustbe)) {
169
                    $clientmustbe = 'utf8';
170
                }
171
                if (preg_match('/utf8mb4/', $clientmustbe)) {
172
                    $clientmustbe = 'utf8';
173
                }
174
175
                if ($this->db->character_set_name() != $clientmustbe) {
176
                    $this->db->set_charset($clientmustbe); // This set utf8_unicode_ci
177
178
                    $collation = $conf->db->dolibarr_main_db_collation;
179
                    if (preg_match('/latin1/', $collation)) {
180
                        $collation = 'utf8_unicode_ci';
181
                    }
182
                    if (preg_match('/utf8mb4/', $collation)) {
183
                        $collation = 'utf8_unicode_ci';
184
                    }
185
186
                    if (!preg_match('/general/', $collation)) {
187
                        $this->db->query("SET collation_connection = " . $collation);
188
                    }
189
                }
190
            }
191
        }
192
    }
193
194
195
    /**
196
     * Return SQL string to force an index
197
     *
198
     * @param string $nameofindex Name of index
199
     * @return  string                  SQL string
200
     */
201
    public function hintindex($nameofindex)
202
    {
203
        return " FORCE INDEX(" . preg_replace('/[^a-z0-9_]/', '', $nameofindex) . ")";
204
    }
205
206
207
    /**
208
     *  Convert a SQL request in Mysql syntax to native syntax
209
     *
210
     * @param string $line SQL request line to convert
211
     * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
212
     * @return    string           SQL request line converted
213
     */
214
    public function convertSQLFromMysql($line, $type = 'ddl')
215
    {
216
        return $line;
217
    }
218
219
220
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
221
222
    /**
223
     *  Select a database
224
     *
225
     * @param string $database Name of database
226
     * @return     boolean             true if OK, false if KO
227
     */
228
    public function select_db($database)
229
    {
230
        // phpcs:enable
231
        dol_syslog(get_only_class($this) . "::select_db database=" . $database, LOG_DEBUG);
232
        $result = false;
233
        try {
234
            $result = $this->db->select_db($database);
235
        } catch (Exception $e) {
236
            // Nothing done on error
237
        }
238
        return $result;
239
    }
240
241
242
    /**
243
     * Connect to server
244
     *
245
     * @param string $host Database server host
246
     * @param string $login Login
247
     * @param string $passwd Password
248
     * @param string $name Name of database (not used for mysql, used for pgsql)
249
     * @param integer $port Port of database server
250
     * @return  mysqli|mysqliDoli|false         Database access object
251
     * @see close()
252
     */
253
    public function connect($host, $login, $passwd, $name, $port = 0)
254
    {
255
        dol_syslog(get_only_class($this) . "::connect host=$host, port=$port, login=$login, passwd=--hidden--, name=$name", LOG_DEBUG);
256
257
        //mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
258
259
        $tmp = false;
260
        try {
261
            if (!class_exists('mysqli')) {
262
                dol_print_error(null, 'Driver mysqli for PHP not available');
263
            }
264
            if (strpos($host, 'ssl://') === 0) {
265
                $tmp = new mysqliDoli($host, $login, $passwd, $name, $port);
266
            } else {
267
                $tmp = new mysqli($host, $login, $passwd, $name, $port);
268
            }
269
        } catch (Exception $e) {
270
            dol_syslog(get_only_class($this) . "::connect failed", LOG_DEBUG);
271
        }
272
        return $tmp;
273
    }
274
275
    /**
276
     *  Return version of database server
277
     *
278
     * @return         string      Version string
279
     */
280
    public function getVersion()
281
    {
282
        return $this->db->server_info;
283
    }
284
285
    /**
286
     *  Return version of database client driver
287
     *
288
     * @return         string      Version string
289
     */
290
    public function getDriverInfo()
291
    {
292
        return $this->db->client_info;
293
    }
294
295
296
    /**
297
     *  Close database connection
298
     *
299
     * @return     bool     True if disconnect successful, false otherwise
300
     * @see        connect()
301
     */
302
    public function close()
303
    {
304
        if ($this->db) {
305
            if ($this->transaction_opened > 0) {
306
                dol_syslog(get_only_class($this) . "::close Closing a connection with an opened transaction depth=" . $this->transaction_opened, LOG_ERR);
307
            }
308
            $this->connected = false;
309
            return $this->db->close();
310
        }
311
        return false;
312
    }
313
314
315
    /**
316
     *  Execute a SQL request and return the resultset
317
     *
318
     * @param string $query SQL query string
319
     * @param int $usesavepoint 0=Default mode, 1=Run a savepoint before and a rollback to savepoint if error (this allow to have some request with errors inside global transactions).
320
     *                                  Note that with Mysql, this parameter is not used as Myssql can already commit a transaction even if one request is in error, without using savepoints.
321
     * @param string $type Type of SQL order ('ddl' for insert, update, select, delete or 'dml' for create, alter...)
322
     * @param int $result_mode Result mode (Using 1=MYSQLI_USE_RESULT instead of 0=MYSQLI_STORE_RESULT will not buffer the result and save memory)
323
     * @return false|mysqli_result     Resultset of answer
324
     */
325
    public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0)
326
    {
327
        global $dolibarr_main_db_readonly;
328
329
        $query = trim($query);
330
331
        if (!in_array($query, array('BEGIN', 'COMMIT', 'ROLLBACK'))) {
332
            $SYSLOG_SQL_LIMIT = 10000; // limit log to 10kb per line to limit DOS attacks
333
            dol_syslog('sql=' . substr($query, 0, $SYSLOG_SQL_LIMIT), LOG_DEBUG);
334
        }
335
        if (empty($query)) {
336
            return false; // Return false = error if empty request
337
        }
338
339
        if (!empty($dolibarr_main_db_readonly)) {
340
            if (preg_match('/^(INSERT|UPDATE|REPLACE|DELETE|CREATE|ALTER|TRUNCATE|DROP)/i', $query)) {
341
                $this->lasterror = 'Application in read-only mode';
342
                $this->lasterrno = 'APPREADONLY';
343
                $this->lastquery = $query;
344
                return false;
345
            }
346
        }
347
348
        try {
349
            $ret = $this->db->query($query, $result_mode);
350
        } catch (Exception $e) {
351
            dol_syslog(get_only_class($this) . "::query Exception in query instead of returning an error: " . $e->getMessage(), LOG_ERR);
352
            $ret = false;
353
        }
354
355
        if (!preg_match("/^COMMIT/i", $query) && !preg_match("/^ROLLBACK/i", $query)) {
356
            // Si requete utilisateur, on la sauvegarde ainsi que son resultset
357
            if (!$ret) {
358
                $this->lastqueryerror = $query;
359
                $this->lasterror = $this->error();
360
                $this->lasterrno = $this->errno();
361
362
                if (getDolGlobalInt('SYSLOG_LEVEL') < LOG_DEBUG) {
363
                    dol_syslog(get_only_class($this) . "::query SQL Error query: " . $query, LOG_ERR); // Log of request was not yet done previously
364
                }
365
                dol_syslog(get_only_class($this) . "::query SQL Error message: " . $this->lasterrno . " " . $this->lasterror . self::getCallerInfoString(), LOG_ERR);
366
                //var_dump(debug_print_backtrace());
367
            }
368
            $this->lastquery = $query;
369
            $this->_results = $ret;
370
        }
371
372
        return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type boolean which is incompatible with the documented return type false|mysqli_result.
Loading history...
373
    }
374
375
    /**
376
     * Get caller info
377
     *
378
     * @return string
379
     */
380
    final protected static function getCallerInfoString()
381
    {
382
        $backtrace = debug_backtrace();
383
        $msg = "";
384
        if (count($backtrace) >= 1) {
385
            $trace = $backtrace[1];
386
            if (isset($trace['file'], $trace['line'])) {
387
                $msg = " From {$trace['file']}:{$trace['line']}.";
388
            }
389
        }
390
        return $msg;
391
    }
392
393
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
394
395
    /**
396
     *  Returns the current line (as an object) for the resultset cursor
397
     *
398
     * @param mysqli_result $resultset Curseur de la requete voulue
399
     * @return object|null                 Object result line or null if KO or end of cursor
400
     */
401
    public function fetch_object($resultset)
402
    {
403
        // phpcs:enable
404
        // If the resultset was not provided, we get the last one for this connection
405
        if (!is_object($resultset)) {
406
            $resultset = $this->_results;
407
        }
408
        return $resultset->fetch_object();
409
    }
410
411
412
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
413
414
    /**
415
     *  Return datas as an array
416
     *
417
     * @param mysqli_result $resultset Resultset of request
418
     * @return array|null                  Array or null if KO or end of cursor
419
     */
420
    public function fetch_array($resultset)
421
    {
422
        // phpcs:enable
423
        // If resultset not provided, we take the last used by connection
424
        if (!is_object($resultset)) {
425
            $resultset = $this->_results;
426
        }
427
        return $resultset->fetch_array();
428
    }
429
430
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
431
432
    /**
433
     *  Return datas as an array
434
     *
435
     * @param mysqli_result $resultset Resultset of request
436
     * @return array|null|int              Array or null if KO or end of cursor or 0 if resultset is bool
437
     */
438
    public function fetch_row($resultset)
439
    {
440
        // phpcs:enable
441
        // If resultset not provided, we take the last used by connection
442
        if (!is_bool($resultset)) {
443
            if (!is_object($resultset)) {
444
                $resultset = $this->_results;
445
            }
446
            return $resultset->fetch_row();
447
        } else {
448
            // si le curseur est un boolean on retourne la valeur 0
449
            return 0;
450
        }
451
    }
452
453
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
454
455
    /**
456
     *  Return number of lines for result of a SELECT
457
     *
458
     * @param mysqli_result $resultset Resulset of requests
459
     * @return int             Nb of lines
460
     * @see    affected_rows()
461
     */
462
    public function num_rows($resultset)
463
    {
464
        // phpcs:enable
465
        // If resultset not provided, we take the last used by connection
466
        if (!is_object($resultset)) {
467
            $resultset = $this->_results;
468
        }
469
        return isset($resultset->num_rows) ? $resultset->num_rows : 0;
470
    }
471
472
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
473
474
    /**
475
     *  Return the number of lines in the result of a request INSERT, DELETE or UPDATE
476
     *
477
     * @param mysqli_result $resultset Curseur de la requete voulue
478
     * @return int                         Number of lines
479
     * @see    num_rows()
480
     */
481
    public function affected_rows($resultset)
482
    {
483
        // phpcs:enable
484
        // If resultset not provided, we take the last used by connection
485
        if (!is_object($resultset)) {
486
            $resultset = $this->_results;
487
        }
488
        // mysql necessite un link de base pour cette fonction contrairement
489
        // a pqsql qui prend un resultset
490
        return $this->db->affected_rows;
491
    }
492
493
    /**
494
     *  Libere le dernier resultset utilise sur cette connection
495
     *
496
     * @param mysqli_result $resultset Curseur de la requete voulue
497
     * @return void
498
     */
499
    public function free($resultset = null)
500
    {
501
        // If resultset not provided, we take the last used by connection
502
        if (!is_object($resultset)) {
503
            $resultset = $this->_results;
504
        }
505
        // Si resultset en est un, on libere la memoire
506
        if (is_object($resultset)) {
507
            $resultset->free_result();
508
        }
509
    }
510
511
    /**
512
     *  Escape a string to insert data
513
     *
514
     * @param string $stringtoencode String to escape
515
     * @return string                      String escaped
516
     */
517
    public function escape($stringtoencode)
518
    {
519
        return $this->db->real_escape_string((string)$stringtoencode);
520
    }
521
522
    /**
523
     *  Escape a string to insert data into a like
524
     *
525
     * @param string $stringtoencode String to escape
526
     * @return string                      String escaped
527
     */
528
    public function escapeforlike($stringtoencode)
529
    {
530
        // We must first replace the \ char into \\, then we can replace _ and % into \_ and \%
531
        return str_replace(array('\\', '_', '%'), array('\\\\', '\_', '\%'), (string)$stringtoencode);
532
    }
533
534
    /**
535
     *  Return generic error code of last operation.
536
     *
537
     * @return string      Error code (Examples: DB_ERROR_TABLE_ALREADY_EXISTS, DB_ERROR_RECORD_ALREADY_EXISTS...)
538
     */
539
    public function errno()
540
    {
541
        if (!$this->connected) {
542
            // Si il y a eu echec de connection, $this->db n'est pas valide.
543
            return 'DB_ERROR_FAILED_TO_CONNECT';
544
        } else {
545
            // Constants to convert a MySql error code to a generic Dolibarr error code
546
            $errorcode_map = array(
547
                1004 => 'DB_ERROR_CANNOT_CREATE',
548
                1005 => 'DB_ERROR_CANNOT_CREATE',
549
                1006 => 'DB_ERROR_CANNOT_CREATE',
550
                1007 => 'DB_ERROR_ALREADY_EXISTS',
551
                1008 => 'DB_ERROR_CANNOT_DROP',
552
                1022 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
553
                1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
554
                1044 => 'DB_ERROR_ACCESSDENIED',
555
                1046 => 'DB_ERROR_NODBSELECTED',
556
                1048 => 'DB_ERROR_CONSTRAINT',
557
                1050 => 'DB_ERROR_TABLE_ALREADY_EXISTS',
558
                1051 => 'DB_ERROR_NOSUCHTABLE',
559
                1054 => 'DB_ERROR_NOSUCHFIELD',
560
                1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS',
561
                1061 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
562
                1062 => 'DB_ERROR_RECORD_ALREADY_EXISTS',
563
                1064 => 'DB_ERROR_SYNTAX',
564
                1068 => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
565
                1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY',
566
                1091 => 'DB_ERROR_NOSUCHFIELD',
567
                1100 => 'DB_ERROR_NOT_LOCKED',
568
                1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW',
569
                1146 => 'DB_ERROR_NOSUCHTABLE',
570
                1215 => 'DB_ERROR_CANNOT_ADD_FOREIGN_KEY_CONSTRAINT',
571
                1216 => 'DB_ERROR_NO_PARENT',
572
                1217 => 'DB_ERROR_CHILD_EXISTS',
573
                1396 => 'DB_ERROR_USER_ALREADY_EXISTS', // When creating a user that already existing
574
                1451 => 'DB_ERROR_CHILD_EXISTS',
575
                1826 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS'
576
            );
577
578
            if (isset($errorcode_map[$this->db->errno])) {
579
                return $errorcode_map[$this->db->errno];
580
            }
581
            $errno = $this->db->errno;
582
            return ($errno ? 'DB_ERROR_' . $errno : '0');
583
        }
584
    }
585
586
    /**
587
     *  Return description of last error
588
     *
589
     * @return string      Error text
590
     */
591
    public function error()
592
    {
593
        if (!$this->connected) {
594
            // Si il y a eu echec de connection, $this->db n'est pas valide pour mysqli_error.
595
            return 'Not connected. Check setup parameters in conf/conf.php file and your mysql client and server versions';
596
        } else {
597
            return $this->db->error;
598
        }
599
    }
600
601
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
602
603
    /**
604
     * Get last ID after an insert INSERT
605
     *
606
     * @param string $tab Table name concerned by insert. Ne sert pas sous MySql mais requis pour compatibilite avec Postgresql
607
     * @param string $fieldid Field name
608
     * @return  int|string          Id of row
609
     */
610
    public function last_insert_id($tab, $fieldid = 'rowid')
611
    {
612
        // phpcs:enable
613
        return $this->db->insert_id;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->db->insert_id also could return the type string which is incompatible with the return type mandated by Database::last_insert_id() of integer.
Loading history...
614
    }
615
616
    /**
617
     * Encrypt sensitive data in database
618
     * Warning: This function includes the escape and add the SQL simple quotes on strings.
619
     *
620
     * @param string $fieldorvalue Field name or value to encrypt
621
     * @param int $withQuotes Return string including the SQL simple quotes. This param must always be 1 (Value 0 is bugged and deprecated).
622
     * @return  string                  XXX(field) or XXX('value') or field or 'value'
623
     */
624
    public function encrypt($fieldorvalue, $withQuotes = 1)
625
    {
626
        global $conf;
627
628
        // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
629
        $cryptType = (!empty($conf->db->dolibarr_main_db_encryption) ? $conf->db->dolibarr_main_db_encryption : 0);
630
631
        //Encryption key
632
        $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : '');
633
634
        $escapedstringwithquotes = ($withQuotes ? "'" : "") . $this->escape($fieldorvalue) . ($withQuotes ? "'" : "");
635
636
        if ($cryptType && !empty($cryptKey)) {
637
            if ($cryptType == 2) {
638
                $escapedstringwithquotes = "AES_ENCRYPT(" . $escapedstringwithquotes . ", '" . $this->escape($cryptKey) . "')";
639
            } elseif ($cryptType == 1) {
640
                $escapedstringwithquotes = "DES_ENCRYPT(" . $escapedstringwithquotes . ", '" . $this->escape($cryptKey) . "')";
641
            }
642
        }
643
644
        return $escapedstringwithquotes;
645
    }
646
647
    /**
648
     *  Decrypt sensitive data in database
649
     *
650
     * @param string $value Value to decrypt
651
     * @return string                  Decrypted value if used
652
     */
653
    public function decrypt($value)
654
    {
655
        global $conf;
656
657
        // Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
658
        $cryptType = (!empty($conf->db->dolibarr_main_db_encryption) ? $conf->db->dolibarr_main_db_encryption : 0);
659
660
        //Encryption key
661
        $cryptKey = (!empty($conf->db->dolibarr_main_db_cryptkey) ? $conf->db->dolibarr_main_db_cryptkey : '');
662
663
        $return = $value;
664
665
        if ($cryptType && !empty($cryptKey)) {
666
            if ($cryptType == 2) {
667
                $return = 'AES_DECRYPT(' . $value . ',\'' . $cryptKey . '\')';
668
            } elseif ($cryptType == 1) {
669
                $return = 'DES_DECRYPT(' . $value . ',\'' . $cryptKey . '\')';
670
            }
671
        }
672
673
        return $return;
674
    }
675
676
677
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
678
679
    /**
680
     * Return connection ID
681
     *
682
     * @return          string      Id connection
683
     */
684
    public function DDLGetConnectId()
685
    {
686
        // phpcs:enable
687
        $resql = $this->query('SELECT CONNECTION_ID()');
688
        if ($resql) {
689
            $row = $this->fetch_row($resql);
690
            return $row[0];
691
        } else {
692
            return '?';
693
        }
694
    }
695
696
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
697
698
    /**
699
     *  Create a new database
700
     *  Do not use function xxx_create_db (xxx=mysql, ...) as they are deprecated
701
     *  We force to create database with charset this->forcecharset and collate this->forcecollate
702
     *
703
     * @param string $database Database name to create
704
     * @param string $charset Charset used to store data
705
     * @param string $collation Charset used to sort data
706
     * @param string $owner Username of database owner
707
     * @return null|mysqli_result      Resource defined if OK, null if KO
708
     */
709
    public function DDLCreateDb($database, $charset = '', $collation = '', $owner = '')
710
    {
711
        // phpcs:enable
712
        if (empty($charset)) {
713
            $charset = $this->forcecharset;
714
        }
715
        if (empty($collation)) {
716
            $collation = $this->forcecollate;
717
        }
718
719
        // ALTER DATABASE dolibarr_db DEFAULT CHARACTER SET latin DEFAULT COLLATE latin1_swedish_ci
720
        $sql = "CREATE DATABASE `" . $this->escape($database) . "`";
721
        $sql .= " DEFAULT CHARACTER SET `" . $this->escape($charset) . "` DEFAULT COLLATE `" . $this->escape($collation) . "`";
722
723
        dol_syslog($sql, LOG_DEBUG);
724
        $ret = $this->query($sql);
725
        if (!$ret) {
726
            // We try again for compatibility with Mysql < 4.1.1
727
            $sql = "CREATE DATABASE `" . $this->escape($database) . "`";
728
            dol_syslog($sql, LOG_DEBUG);
729
            $ret = $this->query($sql);
730
        }
731
732
        return $ret;
733
    }
734
735
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
736
737
    /**
738
     *  List tables into a database
739
     *
740
     * @param string $database Name of database
741
     * @param string $table Name of table filter ('xxx%')
742
     * @return array                   List of tables in an array
743
     */
744
    public function DDLListTables($database, $table = '')
745
    {
746
        // phpcs:enable
747
        $listtables = array();
748
749
        $like = '';
750
        if ($table) {
751
            $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
752
753
            $like = "LIKE '" . $this->escape($tmptable) . "'";
754
        }
755
        $tmpdatabase = preg_replace('/[^a-z0-9\.\-\_]/i', '', $database);
756
757
        $sql = "SHOW TABLES FROM `" . $tmpdatabase . "` " . $like . ";";
758
        //print $sql;
759
        $result = $this->query($sql);
760
        if ($result) {
761
            while ($row = $this->fetch_row($result)) {
762
                $listtables[] = $row[0];
763
            }
764
        }
765
        return $listtables;
766
    }
767
768
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
769
770
    /**
771
     *  List tables into a database
772
     *
773
     * @param string $database Name of database
774
     * @param string $table Name of table filter ('xxx%')
775
     * @return array                   List of tables in an array
776
     */
777
    public function DDLListTablesFull($database, $table = '')
778
    {
779
        // phpcs:enable
780
        $listtables = array();
781
782
        $like = '';
783
        if ($table) {
784
            $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
785
786
            $like = "LIKE '" . $this->escape($tmptable) . "'";
787
        }
788
        $tmpdatabase = preg_replace('/[^a-z0-9\.\-\_]/i', '', $database);
789
790
        $sql = "SHOW FULL TABLES FROM `" . $tmpdatabase . "` " . $like . ";";
791
792
        $result = $this->query($sql);
793
        if ($result) {
794
            while ($row = $this->fetch_row($result)) {
795
                $listtables[] = $row;
796
            }
797
        }
798
        return $listtables;
799
    }
800
801
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
802
803
    /**
804
     *  List information of columns into a table.
805
     *
806
     * @param string $table Name of table
807
     * @return array               Tableau des information des champs de la table
808
     */
809
    public function DDLInfoTable($table)
810
    {
811
        // phpcs:enable
812
        $infotables = array();
813
814
        $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table);
815
816
        $sql = "SHOW FULL COLUMNS FROM " . $tmptable . ";";
817
818
        dol_syslog($sql, LOG_DEBUG);
819
        $result = $this->query($sql);
820
        if ($result) {
821
            while ($row = $this->fetch_row($result)) {
822
                $infotables[] = $row;
823
            }
824
        }
825
        return $infotables;
826
    }
827
828
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
829
830
    /**
831
     *  Create a table into database
832
     *
833
     * @param string $table Name of table
834
     * @param array<string,array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull?:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string}> $fields Tableau associatif [nom champ][tableau des descriptions]
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<string,array{type:...ring>,comment?:string}> at position 16 could not be parsed: Expected '}' at position 16, but found 'int'.
Loading history...
835
     * @param string $primary_key Nom du champ qui sera la clef primaire
836
     * @param string $type Type de la table
837
     * @param array $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
838
     * @param array $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
839
     * @param array $keys Tableau des champs cles noms => valeur
840
     * @return     int                     Return integer <0 if KO, >=0 if OK
841
     */
842
    public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null)
843
    {
844
        // phpcs:enable
845
        // @TODO: $fulltext_keys parameter is unused
846
847
        if (empty($type)) {
848
            $type = 'InnoDB';
849
        }
850
851
        $pk = '';
852
        $sqlk = array();
853
        $sqluq = array();
854
855
        // Keys found into the array $fields: type,value,attribute,null,default,extra
856
        // ex. : $fields['rowid'] = array(
857
        //          'type'=>'int' or 'integer',
858
        //          'value'=>'11',
859
        //          'null'=>'not null',
860
        //          'extra'=> 'auto_increment'
861
        //      );
862
        $sql = "CREATE TABLE " . $this->sanitize($table) . "(";
863
        $i = 0;
864
        $sqlfields = array();
865
        foreach ($fields as $field_name => $field_desc) {
866
            $sqlfields[$i] = $this->sanitize($field_name) . " ";
867
            $sqlfields[$i] .= $this->sanitize($field_desc['type']);
868
            if (isset($field_desc['value']) && $field_desc['value'] !== '') {
869
                $sqlfields[$i] .= "(" . $this->sanitize($field_desc['value']) . ")";
870
            }
871
            if (isset($field_desc['attribute']) && $field_desc['attribute'] !== '') {
872
                $sqlfields[$i] .= " " . $this->sanitize($field_desc['attribute']);
873
            }
874
            if (isset($field_desc['default']) && $field_desc['default'] !== '') {
875
                if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
876
                    $sqlfields[$i] .= " DEFAULT " . ((float)$field_desc['default']);
877
                } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') {
878
                    $sqlfields[$i] .= " DEFAULT " . $this->sanitize($field_desc['default']);
879
                } else {
880
                    $sqlfields[$i] .= " DEFAULT '" . $this->escape($field_desc['default']) . "'";
881
                }
882
            }
883
            if (isset($field_desc['null']) && $field_desc['null'] !== '') {
884
                $sqlfields[$i] .= " " . $this->sanitize($field_desc['null'], 0, 0, 1);
885
            }
886
            if (isset($field_desc['extra']) && $field_desc['extra'] !== '') {
887
                $sqlfields[$i] .= " " . $this->sanitize($field_desc['extra'], 0, 0, 1);
888
            }
889
            if (!empty($primary_key) && $primary_key == $field_name) {
890
                $sqlfields[$i] .= " AUTO_INCREMENT PRIMARY KEY";    // mysql instruction that will be converted by driver late
891
            }
892
            $i++;
893
        }
894
895
        if (is_array($unique_keys)) {
896
            $i = 0;
897
            foreach ($unique_keys as $key => $value) {
898
                $sqluq[$i] = "UNIQUE KEY '" . $this->sanitize($key) . "' ('" . $this->escape($value) . "')";
899
                $i++;
900
            }
901
        }
902
        if (is_array($keys)) {
903
            $i = 0;
904
            foreach ($keys as $key => $value) {
905
                $sqlk[$i] = "KEY " . $this->sanitize($key) . " (" . $value . ")";
906
                $i++;
907
            }
908
        }
909
        $sql .= implode(', ', $sqlfields);
910
        if ($unique_keys != "") {
911
            $sql .= "," . implode(',', $sqluq);
912
        }
913
        if (is_array($keys)) {
914
            $sql .= "," . implode(',', $sqlk);
915
        }
916
        $sql .= ")";
917
        $sql .= " engine=" . $this->sanitize($type);
918
919
        if (!$this->query($sql)) {
920
            return -1;
921
        } else {
922
            return 1;
923
        }
924
    }
925
926
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
927
928
    /**
929
     *  Drop a table into database
930
     *
931
     * @param string $table Name of table
932
     * @return     int                     Return integer <0 if KO, >=0 if OK
933
     */
934
    public function DDLDropTable($table)
935
    {
936
        // phpcs:enable
937
        $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table);
938
939
        $sql = "DROP TABLE " . $this->sanitize($tmptable);
940
941
        if (!$this->query($sql)) {
942
            return -1;
943
        } else {
944
            return 1;
945
        }
946
    }
947
948
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
949
950
    /**
951
     *  Return a pointer of line with description of a table or field
952
     *
953
     * @param string $table Name of table
954
     * @param string $field Optionnel : Name of field if we want description of field
955
     * @return bool|mysqli_result  Resultset x (x->Field, x->Type, ...)
956
     */
957
    public function DDLDescTable($table, $field = "")
958
    {
959
        // phpcs:enable
960
        $sql = "DESC " . $this->sanitize($table) . " " . $this->sanitize($field);
961
962
        dol_syslog(get_only_class($this) . "::DDLDescTable " . $sql, LOG_DEBUG);
963
        $this->_results = $this->query($sql);
964
        return $this->_results;
965
    }
966
967
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
968
969
    /**
970
     *  Create a new field into table
971
     *
972
     * @param string $table Name of table
973
     * @param string $field_name Name of field to add
974
     * @param array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull?:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string} $field_desc Associative table with description of field to insert [parameter name][parameter value]
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{type:string,label:...tring>,comment?:string} at position 12 could not be parsed: Expected '}' at position 12, but found 'int'.
Loading history...
975
     * @param string $field_position Optional e.g.: "after some_field"
976
     * @return int                         Return integer <0 if KO, >0 if OK
977
     */
978
    public function DDLAddField($table, $field_name, $field_desc, $field_position = "")
979
    {
980
        // phpcs:enable
981
        // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra
982
        // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
983
        $sql = "ALTER TABLE " . $this->sanitize($table) . " ADD " . $this->sanitize($field_name) . " ";
984
        $sql .= $this->sanitize($field_desc['type']);
985
        if (isset($field_desc['value']) && preg_match("/^[^\s]/i", $field_desc['value'])) {
986
            if (!in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'date', 'datetime')) && $field_desc['value']) {
987
                $sql .= "(" . $this->sanitize($field_desc['value']) . ")";
988
            }
989
        }
990
        if (isset($field_desc['attribute']) && preg_match("/^[^\s]/i", $field_desc['attribute'])) {
991
            $sql .= " " . $this->sanitize($field_desc['attribute']);
992
        }
993
        if (isset($field_desc['null']) && preg_match("/^[^\s]/i", $field_desc['null'])) {
994
            $sql .= " " . $field_desc['null'];
995
        }
996
        if (isset($field_desc['default']) && preg_match("/^[^\s]/i", $field_desc['default'])) {
997
            if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
998
                $sql .= " DEFAULT " . ((float)$field_desc['default']);
999
            } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') {
1000
                $sql .= " DEFAULT " . $this->sanitize($field_desc['default']);
1001
            } else {
1002
                $sql .= " DEFAULT '" . $this->escape($field_desc['default']) . "'";
1003
            }
1004
        }
1005
        if (isset($field_desc['extra']) && preg_match("/^[^\s]/i", $field_desc['extra'])) {
1006
            $sql .= " " . $this->sanitize($field_desc['extra'], 0, 0, 1);
1007
        }
1008
        $sql .= " " . $this->sanitize($field_position, 0, 0, 1);
1009
1010
        dol_syslog(get_only_class($this) . "::DDLAddField " . $sql, LOG_DEBUG);
1011
        if ($this->query($sql)) {
1012
            return 1;
1013
        }
1014
        return -1;
1015
    }
1016
1017
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1018
1019
    /**
1020
     *  Update format of a field into a table
1021
     *
1022
     * @param string $table Name of table
1023
     * @param string $field_name Name of field to modify
1024
     * @param array{type:string,label:string,enabled:int<0,2>|string,position:int,notnull?:int,visible:int,noteditable?:int,default?:string,index?:int,foreignkey?:string,searchall?:int,isameasure?:int,css?:string,csslist?:string,help?:string,showoncombobox?:int,disabled?:int,arrayofkeyval?:array<int,string>,comment?:string} $field_desc Array with description of field format
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{type:string,label:...tring>,comment?:string} at position 12 could not be parsed: Expected '}' at position 12, but found 'int'.
Loading history...
1025
     * @return int                         Return integer <0 if KO, >0 if OK
1026
     */
1027
    public function DDLUpdateField($table, $field_name, $field_desc)
1028
    {
1029
        // phpcs:enable
1030
        $sql = "ALTER TABLE " . $this->sanitize($table);
1031
        $sql .= " MODIFY COLUMN " . $this->sanitize($field_name) . " " . $this->sanitize($field_desc['type']);
1032
        if (in_array($field_desc['type'], array('double', 'tinyint', 'int', 'varchar')) && $field_desc['value']) {
1033
            $sql .= "(" . $this->sanitize($field_desc['value']) . ")";
1034
        }
1035
        if (isset($field_desc['value']) && ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL')) {
1036
            // We will try to change format of column to NOT NULL. To be sure the ALTER works, we try to update fields that are NULL
1037
            if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') {
1038
                $sqlbis = "UPDATE " . $this->sanitize($table) . " SET " . $this->sanitize($field_name) . " = '" . $this->escape(isset($field_desc['default']) ? $field_desc['default'] : '') . "' WHERE " . $this->sanitize($field_name) . " IS NULL";
1039
                $this->query($sqlbis);
1040
            } elseif (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1041
                $sqlbis = "UPDATE " . $this->sanitize($table) . " SET " . $this->sanitize($field_name) . " = " . ((float)$this->escape(isset($field_desc['default']) ? $field_desc['default'] : 0)) . " WHERE " . $this->sanitize($field_name) . " IS NULL";
1042
                $this->query($sqlbis);
1043
            }
1044
1045
            $sql .= " NOT NULL";
1046
        }
1047
1048
        if (isset($field_desc['default']) && $field_desc['default'] != '') {
1049
            if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1050
                $sql .= " DEFAULT " . ((float)$field_desc['default']);
1051
            } elseif ($field_desc['type'] != 'text') {
1052
                $sql .= " DEFAULT '" . $this->escape($field_desc['default']) . "'"; // Default not supported on text fields
1053
            }
1054
        }
1055
1056
        dol_syslog(get_only_class($this) . "::DDLUpdateField " . $sql, LOG_DEBUG);
1057
        if (!$this->query($sql)) {
1058
            return -1;
1059
        } else {
1060
            return 1;
1061
        }
1062
    }
1063
1064
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1065
1066
    /**
1067
     *  Drop a field from table
1068
     *
1069
     * @param string $table Name of table
1070
     * @param string $field_name Name of field to drop
1071
     * @return int                     Return integer <0 if KO, >0 if OK
1072
     */
1073
    public function DDLDropField($table, $field_name)
1074
    {
1075
        // phpcs:enable
1076
        $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name);
1077
1078
        $sql = "ALTER TABLE " . $this->sanitize($table) . " DROP COLUMN `" . $this->sanitize($tmp_field_name) . "`";
1079
        if ($this->query($sql)) {
1080
            return 1;
1081
        }
1082
        $this->error = $this->lasterror();
1083
        return -1;
1084
    }
1085
1086
1087
    // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1088
1089
    /**
1090
     *  Create a user and privileges to connect to database (even if database does not exists yet)
1091
     *
1092
     * @param string $dolibarr_main_db_host Ip server or '%'
1093
     * @param string $dolibarr_main_db_user Nom new user
1094
     * @param string $dolibarr_main_db_pass Password for the new user
1095
     * @param string $dolibarr_main_db_name Database name where user must be granted
1096
     * @return int                                 Return integer <0 if KO, >=0 if OK
1097
     */
1098
    public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name)
1099
    {
1100
        // phpcs:enable
1101
        $sql = "CREATE USER '" . $this->escape($dolibarr_main_db_user) . "' IDENTIFIED BY '" . $this->escape($dolibarr_main_db_pass) . "'";
1102
        dol_syslog(get_only_class($this) . "::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log
1103
        $resql = $this->query($sql);
1104
        if (!$resql) {
1105
            if ($this->lasterrno != 'DB_ERROR_USER_ALREADY_EXISTS') {
1106
                return -1;
1107
            } else {
1108
                // If user already exists, we continue to set permissions
1109
                dol_syslog(get_only_class($this) . "::DDLCreateUser sql=" . $sql, LOG_WARNING);
1110
            }
1111
        }
1112
1113
        // Redo with localhost forced (sometimes user is created on %)
1114
        $sql = "CREATE USER '" . $this->escape($dolibarr_main_db_user) . "'@'localhost' IDENTIFIED BY '" . $this->escape($dolibarr_main_db_pass) . "'";
1115
        $resql = $this->query($sql);
1116
1117
        $sql = "GRANT ALL PRIVILEGES ON " . $this->escape($dolibarr_main_db_name) . ".* TO '" . $this->escape($dolibarr_main_db_user) . "'@'" . $this->escape($dolibarr_main_db_host) . "'";
1118
        dol_syslog(get_only_class($this) . "::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log
1119
        $resql = $this->query($sql);
1120
        if (!$resql) {
1121
            $this->error = "Connected user not allowed to GRANT ALL PRIVILEGES ON " . $this->escape($dolibarr_main_db_name) . ".* TO '" . $this->escape($dolibarr_main_db_user) . "'@'" . $this->escape($dolibarr_main_db_host) . "'";
1122
            return -1;
1123
        }
1124
1125
        $sql = "FLUSH Privileges";
1126
1127
        dol_syslog(get_only_class($this) . "::DDLCreateUser", LOG_DEBUG);
1128
        $resql = $this->query($sql);
1129
        if (!$resql) {
1130
            return -1;
1131
        }
1132
1133
        return 1;
1134
    }
1135
1136
    /**
1137
     *  Return charset used to store data in current database
1138
     *  Note: if we are connected to databasename, it is same result than using SELECT default_character_set_name FROM information_schema.SCHEMATA WHERE schema_name = "databasename";)
1139
     *
1140
     * @return     string      Charset
1141
     * @see getDefaultCollationDatabase()
1142
     */
1143
    public function getDefaultCharacterSetDatabase()
1144
    {
1145
        $resql = $this->query("SHOW VARIABLES LIKE 'character_set_database'");
1146
        if (!$resql) {
1147
            // version Mysql < 4.1.1
1148
            return $this->forcecharset;
1149
        }
1150
        $liste = $this->fetch_array($resql);
1151
        $tmpval = $liste['Value'];
1152
1153
        return $tmpval;
1154
    }
1155
1156
    /**
1157
     *  Return list of available charset that can be used to store data in database
1158
     *
1159
     * @return     array|null      List of Charset
1160
     */
1161
    public function getListOfCharacterSet()
1162
    {
1163
        $resql = $this->query('SHOW CHARSET');
1164
        $liste = array();
1165
        if ($resql) {
1166
            $i = 0;
1167
            while ($obj = $this->fetch_object($resql)) {
1168
                $liste[$i]['charset'] = $obj->Charset;
1169
                $liste[$i]['description'] = $obj->Description;
1170
                $i++;
1171
            }
1172
            $this->free($resql);
1173
        } else {
1174
            // version Mysql < 4.1.1
1175
            return null;
1176
        }
1177
        return $liste;
1178
    }
1179
1180
    /**
1181
     *  Return collation used in current database
1182
     *
1183
     * @return     string      Collation value
1184
     * @see getDefaultCharacterSetDatabase()
1185
     */
1186
    public function getDefaultCollationDatabase()
1187
    {
1188
        $resql = $this->query("SHOW VARIABLES LIKE 'collation_database'");
1189
        if (!$resql) {
1190
            // version Mysql < 4.1.1
1191
            return $this->forcecollate;
1192
        }
1193
        $liste = $this->fetch_array($resql);
1194
        $tmpval = $liste['Value'];
1195
1196
        return $tmpval;
1197
    }
1198
1199
    /**
1200
     *  Return list of available collation that can be used for database
1201
     *
1202
     * @return     array|null      Liste of Collation
1203
     */
1204
    public function getListOfCollation()
1205
    {
1206
        $resql = $this->query('SHOW COLLATION');
1207
        $liste = array();
1208
        if ($resql) {
1209
            $i = 0;
1210
            while ($obj = $this->fetch_object($resql)) {
1211
                $liste[$i]['collation'] = $obj->Collation;
1212
                $i++;
1213
            }
1214
            $this->free($resql);
1215
        } else {
1216
            // version Mysql < 4.1.1
1217
            return null;
1218
        }
1219
        return $liste;
1220
    }
1221
1222
    /**
1223
     *  Return full path of dump program
1224
     *
1225
     * @return     string      Full path of dump program
1226
     */
1227
    public function getPathOfDump()
1228
    {
1229
        $fullpathofdump = '/pathtomysqldump/mysqldump';
1230
1231
        $resql = $this->query("SHOW VARIABLES LIKE 'basedir'");
1232
        if ($resql) {
1233
            $liste = $this->fetch_array($resql);
1234
            $basedir = $liste['Value'];
1235
            $fullpathofdump = $basedir . (preg_match('/\/$/', $basedir) ? '' : '/') . 'bin/mysqldump';
1236
        }
1237
        return $fullpathofdump;
1238
    }
1239
1240
    /**
1241
     *  Return full path of restore program
1242
     *
1243
     * @return     string      Full path of restore program
1244
     */
1245
    public function getPathOfRestore()
1246
    {
1247
        $fullpathofimport = '/pathtomysql/mysql';
1248
1249
        $resql = $this->query("SHOW VARIABLES LIKE 'basedir'");
1250
        if ($resql) {
1251
            $liste = $this->fetch_array($resql);
1252
            $basedir = $liste['Value'];
1253
            $fullpathofimport = $basedir . (preg_match('/\/$/', $basedir) ? '' : '/') . 'bin/mysql';
1254
        }
1255
        return $fullpathofimport;
1256
    }
1257
1258
    /**
1259
     * Return value of server parameters
1260
     *
1261
     * @param string $filter Filter list on a particular value
1262
     * @return  array               Array of key-values (key=>value)
1263
     */
1264
    public function getServerParametersValues($filter = '')
1265
    {
1266
        $result = array();
1267
1268
        $sql = 'SHOW VARIABLES';
1269
        if ($filter) {
1270
            $sql .= " LIKE '" . $this->escape($filter) . "'";
1271
        }
1272
        $resql = $this->query($sql);
1273
        if ($resql) {
1274
            while ($obj = $this->fetch_object($resql)) {
1275
                $result[$obj->Variable_name] = $obj->Value;
1276
            }
1277
        }
1278
1279
        return $result;
1280
    }
1281
1282
    /**
1283
     * Return value of server status (current indicators on memory, cache...)
1284
     *
1285
     * @param string $filter Filter list on a particular value
1286
     * @return  array               Array of key-values (key=>value)
1287
     */
1288
    public function getServerStatusValues($filter = '')
1289
    {
1290
        $result = array();
1291
1292
        $sql = 'SHOW STATUS';
1293
        if ($filter) {
1294
            $sql .= " LIKE '" . $this->escape($filter) . "'";
1295
        }
1296
        $resql = $this->query($sql);
1297
        if ($resql) {
1298
            while ($obj = $this->fetch_object($resql)) {
1299
                $result[$obj->Variable_name] = $obj->Value;
1300
            }
1301
        }
1302
1303
        return $result;
1304
    }
1305
}
1306
1307
/**
1308
 * Class to make SSL connection
1309
 */
1310
class mysqliDoli extends mysqli
1311
{
1312
    /**
1313
     *  Constructor.
1314
     *  This create an opened connection to a database server and eventually to a database
1315
     *
1316
     * @param string $host Address of database server
1317
     * @param string $user Name of database user
1318
     * @param string $pass Password of database user
1319
     * @param string $name Name of database
1320
     * @param int $port Port of database server
1321
     * @param string $socket Socket
1322
     */
1323
    public function __construct($host, $user, $pass, $name, $port = 0, $socket = "")
1324
    {
1325
        $flags = 0;
1326
        if (PHP_VERSION_ID >= 80100) {
1327
            parent::__construct();
1328
        } else {
1329
            // @phan-suppress-next-line PhanDeprecatedFunctionInternal
1330
            parent::init();
1331
        }
1332
        if (strpos($host, 'ssl://') === 0) {
1333
            $host = substr($host, 6);
1334
            parent::options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, false);
1335
            // Suppress false positive @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal
1336
            parent::ssl_set(null, null, "", null, null);
1337
            $flags = MYSQLI_CLIENT_SSL;
1338
        }
1339
        parent::real_connect($host, $user, $pass, $name, $port, $socket, $flags);
1340
    }
1341
}
1342