dolibarr 23.0.3
mysqli.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2001 Fabien Seisen <seisen@linuxfr.org>
3 * Copyright (C) 2002-2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
4 * Copyright (C) 2004-2011 Laurent Destailleur <eldy@users.sourceforge.net>
5 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
6 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
7 * Copyright (C) 2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
8 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
9 * Copyright (C) 2024 Charlene Benke <charlene@patas-monkey.com>
10 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
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
31require_once DOL_DOCUMENT_ROOT.'/core/db/DoliDB.class.php';
32
36class DoliDBMysqli extends DoliDB
37{
39 public $db;
41 public $type = 'mysqli';
42
44 const LABEL = 'MySQL or MariaDB';
46 const VERSIONMIN = '5.0.3';
47
49 private $_results;
50
62 public function __construct($type, $host, $user, $pass, $name = '', $port = 0)
63 {
64 global $conf, $langs;
65
66 // Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
67 if (!empty($conf->db->character_set)) {
68 $this->forcecharset = $conf->db->character_set;
69 }
70 if (!empty($conf->db->dolibarr_main_db_collation)) {
71 $this->forcecollate = $conf->db->dolibarr_main_db_collation;
72 }
73
74 $this->database_user = $user;
75 $this->database_host = $host;
76 $this->database_port = $port;
77
78 $this->transaction_opened = 0;
79
80 //print "Name DB: $host,$user,$pass,$name<br>";
81
82 if (!class_exists('mysqli')) {
83 $this->connected = false;
84 $this->ok = false;
85 $this->error = "Mysqli PHP functions for using Mysqli driver are not available in this version of PHP. Try to use another driver.";
86 dol_syslog(get_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);
87 }
88
89 if (!$host) {
90 $this->connected = false;
91 $this->ok = false;
92 $this->error = $langs->trans("ErrorWrongHostParameter");
93 dol_syslog(get_class($this)."::DoliDBMysqli : Connect error, wrong host parameters", LOG_ERR);
94 }
95
96 // Try server connection
97 // We do not try to connect to database, only to server. Connect to database is done later in constructor
98 $this->db = $this->connect($host, $user, $pass, '', $port);
99
100 if ($this->db && empty($this->db->connect_errno)) {
101 $this->connected = true;
102 $this->ok = true;
103 } else {
104 $this->connected = false;
105 $this->ok = false;
106 $this->error = empty($this->db) ? 'Failed to connect' : $this->db->connect_error;
107 dol_syslog(get_class($this)."::DoliDBMysqli Connect error: ".$this->error, LOG_ERR);
108 }
109
110 $disableforcecharset = 0; // Set to 1 to test without charset forcing
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 if (empty($disableforcecharset) && $this->db->character_set_name() != $clientmustbe) {
126 try {
127 dol_syslog(get_class($this)."::DoliDBMysqli You should set the \$dolibarr_main_db_character_set and \$dolibarr_main_db_collation for the PHP to the same as the database default, so to ".$this->db->character_set_name(). " or upgrade database default to ".$clientmustbe.".", LOG_WARNING);
128 // To get current charset: USE databasename; SHOW VARIABLES LIKE 'character_set_database'
129 // or: USE databasename; SELECT schema_name, default_character_set_name FROM information_schema.SCHEMATA;
130 // To get current collation: USE databasename; SHOW VARIABLES LIKE 'collation_database'
131 // or: USE databasename; SELECT schema_name, default_character_set_name FROM information_schema.SCHEMATA;
132 // To upgrade database default, you can do: ALTER DATABASE databasename CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
133
134 $this->db->set_charset($clientmustbe); // This set charset, but with a bad collation (colllation is forced later)
135 } catch (Exception $e) {
136 print 'Failed to force character_set_client to '.$clientmustbe." (according to setup) to match the one of the server database.<br>\n";
137 print $e->getMessage();
138 print "<br>\n";
139 if ($clientmustbe != 'utf8') {
140 print 'Edit conf/conf.php file to set a charset "utf8"';
141 if ($clientmustbe != 'utf8mb4') {
142 print ' or "utf8mb4"';
143 }
144 print ' instead of "'.$clientmustbe.'".'."\n";
145 }
146 exit;
147 }
148
149 $collation = (empty($conf) ? 'utf8_unicode_ci' : $conf->db->dolibarr_main_db_collation);
150 if (preg_match('/latin1/', $collation)) {
151 $collation = 'utf8_unicode_ci';
152 }
153
154 if (!preg_match('/general/', $collation)) {
155 $this->db->query("SET collation_connection = ".$collation);
156 }
157 }
158 } else {
159 $this->database_selected = false;
160 $this->database_name = '';
161 $this->ok = false;
162 $this->error = $this->error();
163 dol_syslog(get_class($this)."::DoliDBMysqli : Select_db error ".$this->error, LOG_ERR);
164 }
165 } else {
166 // No selection of database done. We may only be connected or not (ok or ko) to the server.
167 $this->database_selected = false;
168
169 if ($this->connected) {
170 // If client is old latin, we force utf8
171 $clientmustbe = empty($conf->db->character_set) ? 'utf8' : (string) $conf->db->character_set;
172 if (preg_match('/latin1/', $clientmustbe)) {
173 $clientmustbe = 'utf8';
174 }
175 if (preg_match('/utf8mb4/', $clientmustbe)) {
176 $clientmustbe = 'utf8';
177 }
178
179 if (empty($disableforcecharset) && $this->db->character_set_name() != $clientmustbe) {
180 $this->db->set_charset($clientmustbe); // This set utf8_unicode_ci
181
182 $collation = $conf->db->dolibarr_main_db_collation;
183 if (preg_match('/latin1/', $collation)) {
184 $collation = 'utf8_unicode_ci';
185 }
186 if (preg_match('/utf8mb4/', $collation)) {
187 $collation = 'utf8_unicode_ci';
188 }
189
190 if (!preg_match('/general/', $collation)) {
191 $this->db->query("SET collation_connection = ".$collation);
192 }
193 }
194 }
195 }
196 }
197
198
206 public function hintindex($nameofindex, $mode = 1)
207 {
208 return " ".($mode == 1 ? 'FORCE' : 'USE')." INDEX(".preg_replace('/[^a-z0-9_]/', '', $nameofindex).")";
209 }
210
211
219 public function convertSQLFromMysql($line, $type = 'ddl')
220 {
221 return $line;
222 }
223
224
225 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
232 public function select_db($database)
233 {
234 // phpcs:enable
235 dol_syslog(get_class($this)."::select_db database=".$database, LOG_DEBUG);
236 $result = false;
237 try {
238 $result = $this->db->select_db($database);
239 } catch (Exception $e) {
240 // Nothing done on error
241 }
242 return $result;
243 }
244
245
257 public function connect($host, $login, $passwd, $name, $port = 0)
258 {
259 dol_syslog(get_class($this)."::connect host=$host, port=$port, login=$login, passwd=--hidden--, name=$name", LOG_DEBUG);
260
261 //mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
262
263 $tmp = false;
264 try {
265 if (!class_exists('mysqli')) {
266 dol_print_error(null, 'Driver mysqli for PHP not available');
267 return false;
268 }
269 if (strpos($host, 'ssl://') === 0) {
270 $tmp = new mysqliDoli($host, $login, $passwd, $name, $port);
271 } else {
272 $tmp = new mysqli($host, $login, $passwd, $name, $port);
273 }
274 } catch (Exception $e) {
275 dol_syslog(get_class($this)."::connect failed", LOG_DEBUG);
276 }
277 return $tmp;
278 }
279
285 public function getVersion()
286 {
287 return $this->db->server_info;
288 }
289
295 public function getDriverInfo()
296 {
297 return $this->db->client_info;
298 }
299
300
307 public function close()
308 {
309 if ($this->db) {
310 if ($this->transaction_opened > 0) {
311 dol_syslog(get_class($this)."::close Closing a connection with an opened transaction depth=".$this->transaction_opened, LOG_ERR);
312 }
313 $this->connected = false;
314 return $this->db->close();
315 }
316 return false;
317 }
318
319
320
331 public function query($query, $usesavepoint = 0, $type = 'auto', $result_mode = 0)
332 {
333 global $dolibarr_main_db_readonly;
334
335 $query = trim($query);
336
337 if (!in_array($query, array('BEGIN', 'COMMIT', 'ROLLBACK'))) {
338 $SYSLOG_SQL_LIMIT = 10000; // limit log to 10kb per line to limit DOS attacks
339 dol_syslog('sql='.substr($query, 0, $SYSLOG_SQL_LIMIT), LOG_DEBUG);
340 }
341 if (empty($query)) {
342 return false; // Return false = error if empty request
343 }
344
345 if (!empty($dolibarr_main_db_readonly)) {
346 if (preg_match('/^(INSERT|UPDATE|REPLACE|DELETE|CREATE|ALTER|TRUNCATE|DROP)/i', $query)) {
347 $this->lasterror = 'Application in read-only mode';
348 $this->lasterrno = 'APPREADONLY';
349 $this->lastquery = $query;
350 return false;
351 }
352 }
353
354 try {
355 $ret = $this->db->query($query, $result_mode);
356 } catch (Exception $e) {
357 dol_syslog(get_class($this)."::query Exception in query instead of returning an error: ".$e->getMessage(), LOG_ERR);
358 $ret = false;
359 }
360
361 if (!preg_match("/^COMMIT/i", $query) && !preg_match("/^ROLLBACK/i", $query)) {
362 // Si requete utilisateur, on la sauvegarde ainsi que son resultset
363 if (!$ret) {
364 $this->lastqueryerror = $query;
365 $this->lasterror = $this->error();
366 $this->lasterrno = $this->errno();
367
368 if (getDolGlobalInt('SYSLOG_LEVEL') < LOG_DEBUG) {
369 dol_syslog(get_class($this)."::query SQL Error query: ".$query, LOG_ERR); // Log of request was not yet done previously
370 }
371 dol_syslog(get_class($this)."::query SQL Error message: ".$this->lasterrno." ".$this->lasterror.self::getCallerInfoString(), LOG_ERR);
372 //var_dump(debug_print_backtrace());
373 }
374 $this->lastquery = $query;
375 $this->_results = $ret;
376 }
377
378 return $ret;
379 }
380
386 final protected static function getCallerInfoString()
387 {
388 $backtrace = debug_backtrace();
389 $msg = "";
390 if (count($backtrace) >= 1) {
391 $trace = $backtrace[1];
392 if (isset($trace['file'], $trace['line'])) {
393 $msg = " From {$trace['file']}:{$trace['line']}.";
394 }
395 }
396 return $msg;
397 }
398
399 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
406 public function fetch_object($resultset)
407 {
408 // phpcs:enable
409 // If the resultset was not provided, we get the last one for this connection
410 if (!is_object($resultset)) {
411 $resultset = $this->_results;
412 }
413 return $resultset->fetch_object();
414 }
415
416
417 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
424 public function fetch_array($resultset)
425 {
426 // phpcs:enable
427 // If resultset not provided, we take the last used by connection
428 if (!is_object($resultset)) {
429 $resultset = $this->_results;
430 }
431 return $resultset->fetch_array();
432 }
433
434 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
441 public function fetch_row($resultset)
442 {
443 // phpcs:enable
444 // If resultset not provided, we take the last used by connection
445 if (!is_bool($resultset)) {
446 if (!is_object($resultset)) {
447 $resultset = $this->_results;
448 }
449 return $resultset->fetch_row();
450 } else {
451 // si le curseur est un boolean on retourne la valeur 0
452 return 0;
453 }
454 }
455
456 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
464 public function num_rows($resultset)
465 {
466 // phpcs:enable
467 // If resultset not provided, we take the last used by connection
468 if (!is_object($resultset)) {
469 $resultset = $this->_results;
470 }
471 return isset($resultset->num_rows) ? $resultset->num_rows : 0;
472 }
473
474 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
482 public function affected_rows($resultset)
483 {
484 // phpcs:enable
485 // If resultset not provided, we take the last used by connection
486 if (!is_object($resultset)) {
487 $resultset = $this->_results;
488 }
489 // mysql necessite un link de base pour cette fonction contrairement
490 // a pqsql qui prend un resultset
491 return $this->db->affected_rows;
492 }
493
500 public function free($resultset = null)
501 {
502 // If resultset not provided, we take the last used by connection
503 if (!is_object($resultset)) {
504 $resultset = $this->_results;
505 }
506 // Si resultset en est un, on libere la memoire
507 if (is_object($resultset)) {
508 $resultset->free_result();
509 }
510 }
511
518 public function escape($stringtoencode)
519 {
520 return $this->db->real_escape_string((string) $stringtoencode);
521 }
522
529 public function escapeforlike($stringtoencode)
530 {
531 // We must first replace the \ char into \\, then we can replace _ and % into \_ and \%
532 return str_replace(array('\\', '_', '%'), array('\\\\', '\_', '\%'), (string) $stringtoencode);
533 }
534
540 public function errno()
541 {
542 if (!$this->connected) {
543 // Si il y a eu echec de connection, $this->db n'est pas valide.
544 return 'DB_ERROR_FAILED_TO_CONNECT';
545 } else {
546 // Constants to convert a MySql error code to a generic Dolibarr error code
547 $errorcode_map = array(
548 1004 => 'DB_ERROR_CANNOT_CREATE',
549 1005 => 'DB_ERROR_CANNOT_CREATE',
550 1006 => 'DB_ERROR_CANNOT_CREATE',
551 1007 => 'DB_ERROR_ALREADY_EXISTS',
552 1008 => 'DB_ERROR_CANNOT_DROP',
553 1022 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
554 1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP',
555 1044 => 'DB_ERROR_ACCESSDENIED',
556 1046 => 'DB_ERROR_NODBSELECTED',
557 1048 => 'DB_ERROR_CONSTRAINT',
558 1050 => 'DB_ERROR_TABLE_ALREADY_EXISTS',
559 1051 => 'DB_ERROR_NOSUCHTABLE',
560 1054 => 'DB_ERROR_NOSUCHFIELD',
561 1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS',
562 1061 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS',
563 1062 => 'DB_ERROR_RECORD_ALREADY_EXISTS',
564 1064 => 'DB_ERROR_SYNTAX',
565 1068 => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS',
566 1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY',
567 1091 => 'DB_ERROR_NOSUCHFIELD',
568 1100 => 'DB_ERROR_NOT_LOCKED',
569 1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW',
570 1146 => 'DB_ERROR_NOSUCHTABLE',
571 1215 => 'DB_ERROR_CANNOT_ADD_FOREIGN_KEY_CONSTRAINT',
572 1216 => 'DB_ERROR_NO_PARENT',
573 1217 => 'DB_ERROR_CHILD_EXISTS',
574 1396 => 'DB_ERROR_USER_ALREADY_EXISTS', // When creating a user that already existing
575 1451 => 'DB_ERROR_CHILD_EXISTS',
576 1826 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS'
577 );
578
579 if (isset($errorcode_map[$this->db->errno])) {
580 return $errorcode_map[$this->db->errno];
581 }
582 $errno = $this->db->errno;
583 return ($errno ? 'DB_ERROR_'.$errno : '0');
584 }
585 }
586
592 public function error()
593 {
594 if (!$this->connected) {
595 // Si il y a eu echec de connection, $this->db n'est pas valide pour mysqli_error.
596 return 'Not connected. Check setup parameters in conf/conf.php file and your mysql client and server versions';
597 } else {
598 return $this->db->error;
599 }
600 }
601
602 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
610 public function last_insert_id($tab, $fieldid = 'rowid')
611 {
612 // phpcs:enable
613 return $this->db->insert_id;
614 }
615
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
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
683 public function DDLGetConnectId()
684 {
685 // phpcs:enable
686 $resql = $this->query('SELECT CONNECTION_ID()');
687 if ($resql) {
688 $row = $this->fetch_row($resql);
689 return $row[0];
690 } else {
691 return '?';
692 }
693 }
694
695 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
707 public function DDLCreateDb($database, $charset = '', $collation = '', $owner = '')
708 {
709 // phpcs:enable
710 if (empty($charset)) {
711 $charset = $this->forcecharset;
712 }
713 if (empty($collation)) {
714 $collation = $this->forcecollate;
715 }
716
717 // ALTER DATABASE dolibarr_db DEFAULT CHARACTER SET latin DEFAULT COLLATE latin1_swedish_ci
718 $sql = "CREATE DATABASE `".$this->escape($database)."`";
719 $sql .= " DEFAULT CHARACTER SET `".$this->escape($charset)."` DEFAULT COLLATE `".$this->escape($collation)."`";
720
721 dol_syslog($sql, LOG_DEBUG);
722 $ret = $this->query($sql);
723 if (!$ret) {
724 // We try again for compatibility with Mysql < 4.1.1
725 $sql = "CREATE DATABASE `".$this->escape($database)."`";
726 dol_syslog($sql, LOG_DEBUG);
727 $ret = $this->query($sql);
728 }
729
730 return $ret;
731 }
732
733 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
741 public function DDLListTables($database, $table = '')
742 {
743 // phpcs:enable
744 $listtables = array();
745
746 $like = '';
747 if ($table) {
748 $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
749
750 $like = "LIKE '".$this->escape($tmptable)."'";
751 }
752 $tmpdatabase = preg_replace('/[^a-z0-9\.\-\_]/i', '', $database);
753
754 $sql = "SHOW TABLES FROM `".$tmpdatabase."` ".$like.";";
755 //print $sql;
756 $result = $this->query($sql);
757 if ($result) {
758 while ($row = $this->fetch_row($result)) {
759 $listtables[] = $row[0];
760 }
761 }
762 return $listtables;
763 }
764
765 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
773 public function DDLListTablesFull($database, $table = '')
774 {
775 // phpcs:enable
776 $listtables = array();
777
778 $like = '';
779 if ($table) {
780 $tmptable = preg_replace('/[^a-z0-9\.\-\_%]/i', '', $table);
781
782 $like = "LIKE '".$this->escape($tmptable)."'";
783 }
784 $tmpdatabase = preg_replace('/[^a-z0-9\.\-\_]/i', '', $database);
785
786 $sql = "SHOW FULL TABLES FROM `".$tmpdatabase."` ".$like.";";
787
788 $result = $this->query($sql);
789 if ($result) {
790 while ($row = $this->fetch_row($result)) {
791 $listtables[] = $row;
792 }
793 }
794 return $listtables;
795 }
796
797 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
804 public function DDLInfoTable($table)
805 {
806 // phpcs:enable
807 $infotables = array();
808
809 $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table);
810
811 $sql = "SHOW FULL COLUMNS FROM ".$tmptable.";";
812
813 dol_syslog($sql, LOG_DEBUG);
814 $result = $this->query($sql);
815 if ($result) {
816 while ($row = $this->fetch_row($result)) {
817 $infotables[] = $row;
818 }
819 }
820 return $infotables;
821 }
822
823 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
836 public function DDLCreateTable($table, $fields, $primary_key, $type, $unique_keys = null, $fulltext_keys = null, $keys = null)
837 {
838 // phpcs:enable
839 // @TODO: $fulltext_keys parameter is unused
840
841 if (empty($type)) {
842 $type = 'InnoDB';
843 }
844
845 $pk = '';
846 $sqlk = array();
847 $sqluq = array();
848
849 // Keys found into the array $fields: type,value,attribute,null,default,extra
850 // ex. : $fields['rowid'] = array(
851 // 'type'=>'int' or 'integer',
852 // 'value'=>'11',
853 // 'null'=>'not null',
854 // 'extra'=> 'auto_increment'
855 // );
856 $sql = "CREATE TABLE ".$this->sanitize($table)."(";
857 $i = 0;
858 $sqlfields = array();
859 foreach ($fields as $field_name => $field_desc) {
860 $sqlfields[$i] = $this->sanitize($field_name)." ";
861 $sqlfields[$i] .= $this->sanitize($field_desc['type']);
862 if (isset($field_desc['value']) && $field_desc['value'] !== '') {
863 $sqlfields[$i] .= "(".$this->sanitize($field_desc['value']).")";
864 }
865 if (isset($field_desc['attribute']) && $field_desc['attribute'] !== '') {
866 $sqlfields[$i] .= " ".$this->sanitize($field_desc['attribute'], 0, 0, 1); // Allow space to accept attributes like "ON UPDATE CURRENT_TIMESTAMP"
867 }
868 if (isset($field_desc['default']) && $field_desc['default'] !== '') {
869 if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
870 $sqlfields[$i] .= " DEFAULT ".((float) $field_desc['default']);
871 } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') {
872 $sqlfields[$i] .= " DEFAULT ".$this->sanitize($field_desc['default']);
873 } else {
874 $sqlfields[$i] .= " DEFAULT '".$this->escape($field_desc['default'])."'";
875 }
876 }
877 if (isset($field_desc['null']) && $field_desc['null'] !== '') {
878 $sqlfields[$i] .= " ".$this->sanitize($field_desc['null'], 0, 0, 1);
879 }
880 if (isset($field_desc['extra']) && $field_desc['extra'] !== '') {
881 $sqlfields[$i] .= " ".$this->sanitize($field_desc['extra'], 0, 0, 1);
882 }
883 if (!empty($primary_key) && $primary_key == $field_name) {
884 $sqlfields[$i] .= " AUTO_INCREMENT PRIMARY KEY"; // mysql instruction that will be converted by driver late
885 }
886 $i++;
887 }
888
889 if (is_array($unique_keys)) {
890 $i = 0;
891 foreach ($unique_keys as $key => $value) {
892 $sqluq[$i] = "UNIQUE KEY '".$this->sanitize($key)."' ('".$this->escape($value)."')";
893 $i++;
894 }
895 }
896 if (is_array($keys)) {
897 $i = 0;
898 foreach ($keys as $key => $value) {
899 $sqlk[$i] = "KEY ".$this->sanitize($key)." (".$value.")";
900 $i++;
901 }
902 }
903 $sql .= implode(', ', $sqlfields);
904 if (!is_array($unique_keys) && $unique_keys != "") {
905 $sql .= ",".implode(',', $sqluq);
906 }
907 if (is_array($keys)) {
908 $sql .= ",".implode(',', $sqlk);
909 }
910 $sql .= ")";
911 $sql .= " engine=".$this->sanitize($type);
912
913 if (!$this->query($sql)) {
914 return -1;
915 } else {
916 return 1;
917 }
918 }
919
920 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
927 public function DDLDropTable($table)
928 {
929 // phpcs:enable
930 $tmptable = preg_replace('/[^a-z0-9\.\-\_]/i', '', $table);
931
932 $sql = "DROP TABLE ".$this->sanitize($tmptable);
933
934 if (!$this->query($sql)) {
935 return -1;
936 } else {
937 return 1;
938 }
939 }
940
941 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
949 public function DDLDescTable($table, $field = "")
950 {
951 // phpcs:enable
952 $sql = "DESC ".$this->sanitize($table)." ".$this->sanitize($field);
953
954 dol_syslog(get_class($this)."::DDLDescTable ".$sql, LOG_DEBUG);
955 $this->_results = $this->query($sql);
956 return $this->_results;
957 }
958
959 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
969 public function DDLAddField($table, $field_name, $field_desc, $field_position = "")
970 {
971 // phpcs:enable
972 // cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra
973 // ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
974 $sql = "ALTER TABLE ".$this->sanitize($table)." ADD ".$this->sanitize($field_name)." ";
975
976 if ($field_desc['type'] !== 'datetimegmt') {
977 $sql .= $this->sanitize($field_desc['type']);
978 } else {
979 $sql .= 'datetime';
980 }
981
982 if (isset($field_desc['value']) && preg_match("/^[^\s]/i", $field_desc['value'])) {
983 if (!in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'date', 'datetime', 'datetimegmt')) && $field_desc['value']) {
984 $sql .= "(".$this->sanitize($field_desc['value']).")";
985 }
986 }
987 if (isset($field_desc['attribute']) && preg_match("/^[^\s]/i", $field_desc['attribute'])) {
988 $sql .= " ".$this->sanitize($field_desc['attribute']);
989 }
990 if (isset($field_desc['null']) && preg_match("/^[^\s]/i", $field_desc['null'])) {
991 if ($field_desc['null'] == 'NOT NULL') {
992 $sql .= " ".$this->sanitize($field_desc['null'], 0, 0, 1);
993 } else {
994 $sql .= " ".$this->sanitize($field_desc['null']);
995 }
996 }
997 if (isset($field_desc['default']) && preg_match("/^[^\s]/i", $field_desc['default'])) {
998 if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
999 $sql .= " DEFAULT ".((float) $field_desc['default']);
1000 } elseif ($field_desc['default'] == 'null' || $field_desc['default'] == 'CURRENT_TIMESTAMP') {
1001 $sql .= " DEFAULT ".$this->sanitize($field_desc['default']);
1002 } else {
1003 $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'";
1004 }
1005 }
1006 if (isset($field_desc['extra']) && preg_match("/^[^\s]/i", $field_desc['extra'])) {
1007 $sql .= " ".$this->sanitize($field_desc['extra'], 0, 0, 1);
1008 }
1009 $sql .= " ".$this->sanitize($field_position, 0, 0, 1);
1010
1011 dol_syslog(get_class($this)."::DDLAddField ".$sql, LOG_DEBUG);
1012 if ($this->query($sql)) {
1013 return 1;
1014 }
1015 return -1;
1016 }
1017
1018 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
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)." ";
1032
1033 if ($field_desc['type'] !== 'datetimegmt') {
1034 $sql .= $this->sanitize($field_desc['type']);
1035 } else {
1036 $sql .= 'datetime';
1037 }
1038
1039 if (in_array($field_desc['type'], array('double', 'tinyint', 'int', 'varchar')) && array_key_exists('value', $field_desc) && $field_desc['value']) {
1040 $sql .= "(".$this->sanitize($field_desc['value']).")";
1041 }
1042 if (isset($field_desc['null']) && ($field_desc['null'] == 'not null' || $field_desc['null'] == 'NOT NULL')) {
1043 // 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
1044 if ($field_desc['type'] == 'varchar' || $field_desc['type'] == 'text') {
1045 $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";
1046 $this->query($sqlbis);
1047 } elseif (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1048 $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";
1049 $this->query($sqlbis);
1050 }
1051
1052 $sql .= " NOT NULL";
1053 }
1054
1055 if (isset($field_desc['default']) && $field_desc['default'] != '') {
1056 if (in_array($field_desc['type'], array('tinyint', 'smallint', 'int', 'double'))) {
1057 $sql .= " DEFAULT ".((float) $field_desc['default']);
1058 } elseif ($field_desc['type'] != 'text') {
1059 $sql .= " DEFAULT '".$this->escape($field_desc['default'])."'"; // Default not supported on text fields
1060 }
1061 }
1062
1063 dol_syslog(get_class($this)."::DDLUpdateField ".$sql, LOG_DEBUG);
1064 if (!$this->query($sql)) {
1065 return -1;
1066 } else {
1067 return 1;
1068 }
1069 }
1070
1071 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1079 public function DDLDropField($table, $field_name)
1080 {
1081 // phpcs:enable
1082 $tmp_field_name = preg_replace('/[^a-z0-9\.\-\_]/i', '', $field_name);
1083
1084 $sql = "ALTER TABLE ".$this->sanitize($table)." DROP COLUMN `".$this->sanitize($tmp_field_name)."`";
1085 if ($this->query($sql)) {
1086 return 1;
1087 }
1088 $this->error = $this->lasterror();
1089 return -1;
1090 }
1091
1092
1093 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1103 public function DDLCreateUser($dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_pass, $dolibarr_main_db_name)
1104 {
1105 // phpcs:enable
1106 $sql = "CREATE USER '".$this->escape($dolibarr_main_db_user)."' IDENTIFIED BY '".$this->escape($dolibarr_main_db_pass)."'";
1107 dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log
1108 $resql = $this->query($sql);
1109 if (!$resql) {
1110 if ($this->lasterrno != 'DB_ERROR_USER_ALREADY_EXISTS') {
1111 return -1;
1112 } else {
1113 // If user already exists, we continue to set permissions
1114 dol_syslog(get_class($this)."::DDLCreateUser sql=".$sql, LOG_WARNING);
1115 }
1116 }
1117
1118 // Redo with localhost forced (sometimes user is created on %)
1119 $sql = "CREATE USER '".$this->escape($dolibarr_main_db_user)."'@'localhost' IDENTIFIED BY '".$this->escape($dolibarr_main_db_pass)."'";
1120 $resql = $this->query($sql);
1121
1122 $sql = "GRANT ALL PRIVILEGES ON ".$this->escape($dolibarr_main_db_name).".* TO '".$this->escape($dolibarr_main_db_user)."'@'".$this->escape($dolibarr_main_db_host)."'";
1123 dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG); // No sql to avoid password in log
1124 $resql = $this->query($sql);
1125 if (!$resql) {
1126 $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)."'";
1127 return -1;
1128 }
1129
1130 $sql = "FLUSH Privileges";
1131
1132 dol_syslog(get_class($this)."::DDLCreateUser", LOG_DEBUG);
1133 $resql = $this->query($sql);
1134 if (!$resql) {
1135 return -1;
1136 }
1137
1138 return 1;
1139 }
1140
1148 public function getDefaultCharacterSetDatabase()
1149 {
1150 $resql = $this->query("SHOW VARIABLES LIKE 'character_set_database'");
1151 if (!$resql) {
1152 // version Mysql < 4.1.1
1153 return $this->forcecharset;
1154 }
1155 $liste = $this->fetch_array($resql);
1156 $tmpval = $liste['Value'];
1157
1158 return $tmpval;
1159 }
1160
1166 public function getListOfCharacterSet()
1167 {
1168 $resql = $this->query('SHOW CHARSET');
1169 $liste = array();
1170 if ($resql) {
1171 $i = 0;
1172 while ($obj = $this->fetch_object($resql)) {
1173 $liste[$i]['charset'] = $obj->Charset;
1174 $liste[$i]['description'] = $obj->Description;
1175 $i++;
1176 }
1177 $this->free($resql);
1178 } else {
1179 // version Mysql < 4.1.1
1180 return null;
1181 }
1182 return $liste;
1183 }
1184
1191 public function getDefaultCollationDatabase()
1192 {
1193 $resql = $this->query("SHOW VARIABLES LIKE 'collation_database'");
1194 if (!$resql) {
1195 // version Mysql < 4.1.1
1196 return $this->forcecollate;
1197 }
1198 $liste = $this->fetch_array($resql);
1199 $tmpval = $liste['Value'];
1200
1201 return $tmpval;
1202 }
1203
1209 public function getListOfCollation()
1210 {
1211 $resql = $this->query('SHOW COLLATION');
1212 $liste = array();
1213 if ($resql) {
1214 $i = 0;
1215 while ($obj = $this->fetch_object($resql)) {
1216 $liste[$i]['collation'] = $obj->Collation;
1217 $i++;
1218 }
1219 $this->free($resql);
1220 } else {
1221 // version Mysql < 4.1.1
1222 return null;
1223 }
1224 return $liste;
1225 }
1226
1232 public function getPathOfDump()
1233 {
1234 $fullpathofdump = '/pathtomysqldump/mysqldump';
1235
1236 $resql = $this->query("SHOW VARIABLES LIKE 'basedir'");
1237 if ($resql) {
1238 $liste = $this->fetch_array($resql);
1239 $basedir = $liste['Value'];
1240 $fullpathofdump = $basedir.(preg_match('/\/$/', $basedir) ? '' : '/').'bin/mysqldump';
1241 }
1242 return $fullpathofdump;
1243 }
1244
1250 public function getPathOfRestore()
1251 {
1252 $fullpathofimport = '/pathtomysql/mysql';
1253
1254 $resql = $this->query("SHOW VARIABLES LIKE 'basedir'");
1255 if ($resql) {
1256 $liste = $this->fetch_array($resql);
1257 $basedir = $liste['Value'];
1258 $fullpathofimport = $basedir.(preg_match('/\/$/', $basedir) ? '' : '/').'bin/mysql';
1259 }
1260 return $fullpathofimport;
1261 }
1262
1269 public function getServerParametersValues($filter = '')
1270 {
1271 $result = array();
1272
1273 $sql = 'SHOW VARIABLES';
1274 if ($filter) {
1275 $sql .= " LIKE '".$this->escape($filter)."'";
1276 }
1277 $resql = $this->query($sql);
1278 if ($resql) {
1279 while ($obj = $this->fetch_object($resql)) {
1280 $result[$obj->Variable_name] = $obj->Value;
1281 }
1282 }
1283
1284 return $result;
1285 }
1286
1293 public function getServerStatusValues($filter = '')
1294 {
1295 $result = array();
1296
1297 $sql = 'SHOW STATUS';
1298 if ($filter) {
1299 $sql .= " LIKE '".$this->escape($filter)."'";
1300 }
1301 $resql = $this->query($sql);
1302 if ($resql) {
1303 while ($obj = $this->fetch_object($resql)) {
1304 $result[$obj->Variable_name] = $obj->Value;
1305 }
1306 }
1307
1308 return $result;
1309 }
1310
1317 public function getNextAutoIncrementId($table)
1318 {
1319 // Request to get last status of table
1320 $sql = "SHOW TABLE STATUS LIKE '".$this->escape($table)."'";
1321 $result = $this->query($sql);
1322
1323 if ($result) {
1324 $obj = $this->fetch_object($result);
1325 if ($obj && isset($obj->Auto_increment)) {
1326 return (int) $obj->Auto_increment;
1327 }
1328 }
1329
1330 return -1;
1331 }
1332
1339 public function prepare($sql)
1340 {
1341 if (!$this->connected) {
1342 $this->lasterror = 'Not connected to database';
1343 return false;
1344 }
1345 $stmt = $this->db->prepare($sql);
1346 if ($stmt === false) {
1347 $this->lasterror = $this->db->error;
1348 $this->lastqueryerror = $sql;
1349 return false;
1350 }
1351
1352 return $stmt;
1353 }
1354}
1355
1356if (class_exists('mysqli')) {
1360 class mysqliDoli extends mysqli
1361 {
1373 public function __construct($host, $user, $pass, $name, $port = 0, $socket = "")
1374 {
1375 $flags = 0;
1376 if (PHP_VERSION_ID >= 80100) {
1377 parent::__construct();
1378 } else {
1379 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
1380 parent::init();
1381 }
1382 if (strpos($host, 'ssl://') === 0) {
1383 $host = substr($host, 6);
1384 parent::options(MYSQLI_OPT_SSL_VERIFY_SERVER_CERT, false);
1385 // Suppress false positive @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal
1386 parent::ssl_set(null, null, "", null, null);
1387 $flags = MYSQLI_CLIENT_SSL;
1388 }
1389 parent::real_connect($host, $user, $pass, $name, $port, $socket, $flags);
1390 }
1391 }
1392}
Class to manage Dolibarr database access.
lastqueryerror()
Return last query in error.
lasterror()
Return last error label.
lasterrno()
Return last error code.
lastquery()
Return last request executed with query()
Class to manage Dolibarr database access for a MySQL database using the MySQLi extension.
fetch_array($resultset)
Return data as an array.
__construct($type, $host, $user, $pass, $name='', $port=0)
Constructor.
free($resultset=null)
Libere le dernier resultset utilise sur cette connection.
escapeforlike($stringtoencode)
Escape a string to insert data into a like.
num_rows($resultset)
Return number of lines for result of a SELECT.
hintindex($nameofindex, $mode=1)
Return SQL string to force an index.
const VERSIONMIN
Version min database.
$type
Database type.
error()
Return description of last error.
escape($stringtoencode)
Escape a string to insert data.
getVersion()
Return version of database server.
fetch_object($resultset)
Returns the current line (as an object) for the resultset cursor.
encrypt($fieldorvalue, $withQuotes=1)
Encrypt sensitive data in database Warning: This function includes the escape and add the SQL simple ...
convertSQLFromMysql($line, $type='ddl')
Convert a SQL request in Mysql syntax to native syntax.
affected_rows($resultset)
Return the number of lines in the result of a request INSERT, DELETE or UPDATE.
select_db($database)
Select a database.
decrypt($value)
Decrypt sensitive data in database.
fetch_row($resultset)
Return data as an array.
last_insert_id($tab, $fieldid='rowid')
Get last ID after an insert INSERT.
const LABEL
Database label.
query($query, $usesavepoint=0, $type='auto', $result_mode=0)
Execute a SQL request and return the resultset.
connect($host, $login, $passwd, $name, $port=0)
Connect to server.
errno()
Return generic error code of last operation.
static getCallerInfoString()
Get caller info.
getDriverInfo()
Return version of database client driver.
close()
Close database connection.
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
if(getDolGlobalString( 'TAKEPOS_SHOW_CUSTOMER')) print $langs trans('Date')." left Label right Qty right Price right TotalHT right TotalTTC right right right right right right right right right centpercent right TotalHT right n right VAT right n right TotalVAT right n No sujeto a RE IRPF right TotalLT1 right n right TotalLT2 right n right TotalTTC right n takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency takeposcustomercurrency right TotalTTC takeposcustomercurrency right takeposcustomercurrency n right PaymentTypeShortLIQ right SELECT p pos_change as p datep as date
Definition receipt.php:464
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:125