2004-10-20 00:47:16 +02:00
< ? php
2012-09-02 22:48:52 +02:00
/* Copyright ( C ) 2001 Fabien Seisen < seisen @ linuxfr . org >
* Copyright ( C ) 2002 - 2005 Rodolphe Quiedeville < rodolphe @ quiedeville . org >
* Copyright ( C ) 2004 - 2011 Laurent Destailleur < eldy @ users . sourceforge . net >
* Copyright ( C ) 2006 Andre Cianfarani < acianfa @ free . fr >
2018-10-27 14:43:12 +02:00
* Copyright ( C ) 2005 - 2012 Regis Houssin < regis . houssin @ inodbox . com >
2015-12-17 21:10:14 +01:00
* Copyright ( C ) 2015 Raphaël Doursenaud < rdoursenaud @ gpcsolutions . fr >
2025-02-03 14:28:54 +01:00
* Copyright ( C ) 2024 - 2025 MDW < mdeweerd @ users . noreply . github . com >
2024-07-31 15:41:00 +02:00
* Copyright ( C ) 2024 Charlene Benke < charlene @ patas - monkey . com >
2024-11-04 23:53:20 +01:00
* Copyright ( C ) 2024 Frédéric France < frederic . france @ free . fr >
2002-04-30 12:44:42 +02:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
2013-01-16 15:36:08 +01:00
* the Free Software Foundation ; either version 3 of the License , or
2002-04-30 12:44:42 +02:00
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2019-09-23 21:55:30 +02:00
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2002-04-30 12:44:42 +02:00
*/
2005-03-26 13:42:09 +01:00
/**
2011-10-24 18:40:28 +02:00
* \file htdocs / core / db / mysqli . class . php
2015-05-12 17:56:01 +02:00
* \brief Class file to manage Dolibarr database access for a MySQL database
2008-09-06 01:08:07 +02:00
*/
2009-07-22 15:35:09 +02:00
2020-04-10 10:59:32 +02:00
require_once DOL_DOCUMENT_ROOT . '/core/db/DoliDB.class.php' ;
2004-07-16 00:17:39 +02:00
2005-02-15 23:13:41 +01:00
/**
2015-05-12 17:56:01 +02:00
* Class to manage Dolibarr database access for a MySQL database using the MySQLi extension
2008-09-06 01:08:07 +02:00
*/
2013-09-10 12:29:55 +02:00
class DoliDBMysqli extends DoliDB
2004-11-07 14:26:30 +01:00
{
2015-09-17 20:38:48 +02:00
/** @var mysqli Database object */
public $db ;
2020-10-31 14:32:18 +01:00
//! Database type
public $type = 'mysqli' ;
2023-01-03 18:54:11 +01:00
2020-10-31 14:32:18 +01:00
//! Database label
const LABEL = 'MySQL or MariaDB' ;
//! Version min database
const VERSIONMIN = '5.0.3' ;
2023-01-03 18:54:11 +01:00
2019-11-14 09:13:15 +01:00
/** @var bool|mysqli_result Resultset of last query */
2012-02-15 13:55:00 +01:00
private $_results ;
2011-06-19 20:09:41 +02:00
2020-10-31 14:32:18 +01:00
/**
2011-09-03 01:09:39 +02:00
* Constructor .
2024-01-13 19:48:20 +01:00
* This create an opened connection to a database server and eventually to a database
2011-09-03 01:09:39 +02:00
*
2023-12-23 18:47:38 +01:00
* @ param string $type Type of database ( mysql , pgsql ... ) . Not used .
2011-09-03 01:09:39 +02:00
* @ param string $host Address of database server
2023-08-01 17:06:22 +02:00
* @ param string $user Name of database user
* @ param string $pass Password of database user
* @ param string $name Name of database
2011-09-03 01:09:39 +02:00
* @ param int $port Port of database server
2020-10-31 14:32:18 +01:00
*/
public function __construct ( $type , $host , $user , $pass , $name = '' , $port = 0 )
{
global $conf , $langs ;
// Note that having "static" property for "$forcecharset" and "$forcecollate" will make error here in strict mode, so they are not static
2021-02-23 22:03:23 +01:00
if ( ! empty ( $conf -> db -> character_set )) {
$this -> forcecharset = $conf -> db -> character_set ;
}
if ( ! empty ( $conf -> db -> dolibarr_main_db_collation )) {
$this -> forcecollate = $conf -> db -> dolibarr_main_db_collation ;
}
2020-10-31 14:32:18 +01:00
$this -> database_user = $user ;
$this -> database_host = $host ;
$this -> database_port = $port ;
$this -> transaction_opened = 0 ;
//print "Name DB: $host,$user,$pass,$name<br>";
2021-02-23 22:03:23 +01:00
if ( ! class_exists ( 'mysqli' )) {
2020-10-31 14:32:18 +01:00
$this -> connected = false ;
$this -> ok = false ;
$this -> error = " Mysqli PHP functions for using Mysqli driver are not available in this version of PHP. Try to use another driver. " ;
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 );
}
2021-02-23 22:03:23 +01:00
if ( ! $host ) {
2020-10-31 14:32:18 +01:00
$this -> connected = false ;
$this -> ok = false ;
$this -> error = $langs -> trans ( " ErrorWrongHostParameter " );
dol_syslog ( get_class ( $this ) . " ::DoliDBMysqli : Connect error, wrong host parameters " , LOG_ERR );
}
2011-06-19 20:09:41 +02:00
2015-09-17 20:38:48 +02:00
// Try server connection
2024-01-13 19:48:20 +01:00
// We do not try to connect to database, only to server. Connect to database is done later in constructor
2015-09-17 20:38:48 +02:00
$this -> db = $this -> connect ( $host , $user , $pass , '' , $port );
2022-07-04 03:06:56 +02:00
if ( $this -> db && empty ( $this -> db -> connect_errno )) {
$this -> connected = true ;
$this -> ok = true ;
} else {
2015-09-17 20:38:48 +02:00
$this -> connected = false ;
$this -> ok = false ;
2022-07-04 03:06:56 +02:00
$this -> error = empty ( $this -> db ) ? 'Failed to connect' : $this -> db -> connect_error ;
2020-04-10 10:59:32 +02:00
dol_syslog ( get_class ( $this ) . " ::DoliDBMysqli Connect error: " . $this -> error , LOG_ERR );
2015-09-17 20:38:48 +02:00
}
2024-09-30 23:11:31 +02:00
$disableforcecharset = 0 ; // Set to 1 to test without charset forcing
2015-09-17 20:38:48 +02:00
// If server connection is ok, we try to connect to the database
2021-02-23 22:03:23 +01:00
if ( $this -> connected && $name ) {
if ( $this -> select_db ( $name )) {
2020-10-31 14:32:18 +01:00
$this -> database_selected = true ;
$this -> database_name = $name ;
$this -> ok = true ;
// If client is old latin, we force utf8
2024-04-12 01:06:52 +02:00
$clientmustbe = empty ( $conf -> db -> character_set ) ? 'utf8' : ( string ) $conf -> db -> character_set ;
2021-02-23 22:03:23 +01:00
if ( preg_match ( '/latin1/' , $clientmustbe )) {
$clientmustbe = 'utf8' ;
}
2017-11-27 21:53:22 +01:00
2023-10-05 12:58:44 +02:00
if ( empty ( $disableforcecharset ) && $this -> db -> character_set_name () != $clientmustbe ) {
try {
2024-09-12 02:21:14 +02:00
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 );
// To get current charset: USE databasename; SHOW VARIABLES LIKE 'character_set_database'
// or: USE databasename; SELECT schema_name, default_character_set_name FROM information_schema.SCHEMATA;
// To get current collation: USE databasename; SHOW VARIABLES LIKE 'collation_database'
// or: USE databasename; SELECT schema_name, default_character_set_name FROM information_schema.SCHEMATA;
// To upgrade database default, you can do: ALTER DATABASE databasename CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
$this -> db -> set_charset ( $clientmustbe ); // This set charset, but with a bad collation (colllation is forced later)
2023-10-05 12:58:44 +02:00
} catch ( Exception $e ) {
2024-03-18 17:27:06 +01:00
print 'Failed to force character_set_client to ' . $clientmustbe . " (according to setup) to match the one of the server database.<br> \n " ;
2023-10-05 12:58:44 +02:00
print $e -> getMessage ();
print " <br> \n " ;
if ( $clientmustbe != 'utf8' ) {
2024-03-18 17:27:06 +01:00
print 'Edit conf/conf.php file to set a charset "utf8"' ;
if ( $clientmustbe != 'utf8mb4' ) {
print ' or "utf8mb4"' ;
}
print ' instead of "' . $clientmustbe . '".' . " \n " ;
2023-10-05 12:58:44 +02:00
}
exit ;
}
2017-11-27 21:53:22 +01:00
2023-09-14 15:31:05 +02:00
$collation = ( empty ( $conf ) ? 'utf8_unicode_ci' : $conf -> db -> dolibarr_main_db_collation );
2021-02-23 22:03:23 +01:00
if ( preg_match ( '/latin1/' , $collation )) {
$collation = 'utf8_unicode_ci' ;
}
2017-11-27 21:53:22 +01:00
2021-02-23 22:03:23 +01:00
if ( ! preg_match ( '/general/' , $collation )) {
$this -> db -> query ( " SET collation_connection = " . $collation );
}
2015-12-17 21:10:14 +01:00
}
2020-10-31 14:32:18 +01:00
} else {
$this -> database_selected = false ;
$this -> database_name = '' ;
$this -> ok = false ;
$this -> error = $this -> error ();
dol_syslog ( get_class ( $this ) . " ::DoliDBMysqli : Select_db error " . $this -> error , LOG_ERR );
}
} else {
2023-10-05 12:58:44 +02:00
// No selection of database done. We may only be connected or not (ok or ko) to the server.
2020-10-31 14:32:18 +01:00
$this -> database_selected = false ;
2021-02-23 22:03:23 +01:00
if ( $this -> connected ) {
2020-10-31 14:32:18 +01:00
// If client is old latin, we force utf8
2024-04-12 01:06:52 +02:00
$clientmustbe = empty ( $conf -> db -> character_set ) ? 'utf8' : ( string ) $conf -> db -> character_set ;
2021-02-23 22:03:23 +01:00
if ( preg_match ( '/latin1/' , $clientmustbe )) {
$clientmustbe = 'utf8' ;
}
if ( preg_match ( '/utf8mb4/' , $clientmustbe )) {
$clientmustbe = 'utf8' ;
}
2020-10-31 14:32:18 +01:00
2024-09-30 23:11:31 +02:00
if ( empty ( $disableforcecharset ) && $this -> db -> character_set_name () != $clientmustbe ) {
2020-10-31 14:32:18 +01:00
$this -> db -> set_charset ( $clientmustbe ); // This set utf8_unicode_ci
$collation = $conf -> db -> dolibarr_main_db_collation ;
2021-02-23 22:03:23 +01:00
if ( preg_match ( '/latin1/' , $collation )) {
$collation = 'utf8_unicode_ci' ;
}
if ( preg_match ( '/utf8mb4/' , $collation )) {
$collation = 'utf8_unicode_ci' ;
}
if ( ! preg_match ( '/general/' , $collation )) {
$this -> db -> query ( " SET collation_connection = " . $collation );
}
2015-12-17 21:10:14 +01:00
}
2017-11-27 21:53:22 +01:00
}
2020-10-31 14:32:18 +01:00
}
}
2022-04-06 17:32:56 +02:00
/**
* Return SQL string to force an index
*
* @ param string $nameofindex Name of index
2024-11-12 11:33:31 +01:00
* @ param int $mode 0 = Use , 1 = Force
2022-04-06 17:32:56 +02:00
* @ return string SQL string
*/
2024-11-12 11:33:31 +01:00
public function hintindex ( $nameofindex , $mode = 1 )
2022-04-06 17:32:56 +02:00
{
2024-11-12 11:33:31 +01:00
return " " . ( $mode == 1 ? 'FORCE' : 'USE' ) . " INDEX( " . preg_replace ( '/[^a-z0-9_]/' , '' , $nameofindex ) . " ) " ;
2022-04-06 17:32:56 +02:00
}
2020-10-31 14:32:18 +01:00
/**
* Convert a SQL request in Mysql syntax to native syntax
*
* @ param string $line SQL request line to convert
* @ param string $type Type of SQL order ( 'ddl' for insert , update , select , delete or 'dml' for create , alter ... )
* @ return string SQL request line converted
*/
2023-08-04 04:18:59 +02:00
public function convertSQLFromMysql ( $line , $type = 'ddl' )
2020-10-31 14:32:18 +01:00
{
return $line ;
}
2022-04-06 17:32:56 +02:00
2020-10-31 14:32:18 +01:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2011-09-03 01:09:39 +02:00
/**
2018-09-04 21:53:53 +02:00
* Select a database
2011-09-03 01:09:39 +02:00
*
2018-09-04 21:53:53 +02:00
* @ param string $database Name of database
* @ return boolean true if OK , false if KO
2011-09-03 01:09:39 +02:00
*/
2020-10-31 14:32:18 +01:00
public function select_db ( $database )
{
// phpcs:enable
dol_syslog ( get_class ( $this ) . " ::select_db database= " . $database , LOG_DEBUG );
2022-07-04 03:06:56 +02:00
$result = false ;
try {
$result = $this -> db -> select_db ( $database );
} catch ( Exception $e ) {
2022-07-06 15:48:17 +02:00
// Nothing done on error
2022-07-04 03:06:56 +02:00
}
return $result ;
2020-10-31 14:32:18 +01:00
}
2011-06-19 20:09:41 +02:00
2015-09-17 20:38:48 +02:00
/**
* Connect to server
2011-09-03 01:09:39 +02:00
*
2024-03-08 12:10:09 +01:00
* @ param string $host Database server host
* @ param string $login Login
* @ param string $passwd Password
* @ param string $name Name of database ( not used for mysql , used for pgsql )
* @ param integer $port Port of database server
* @ return mysqli | mysqliDoli | false Database access object
2019-03-23 12:36:05 +01:00
* @ see close ()
2015-09-17 20:38:48 +02:00
*/
2020-10-31 14:32:18 +01:00
public function connect ( $host , $login , $passwd , $name , $port = 0 )
{
2020-04-10 10:59:32 +02:00
dol_syslog ( get_class ( $this ) . " ::connect host= $host , port= $port , login= $login , passwd=--hidden--, name= $name " , LOG_DEBUG );
2011-06-19 20:09:41 +02:00
2022-01-28 11:11:36 +01:00
//mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
2022-07-04 03:06:56 +02:00
$tmp = false ;
try {
2023-05-14 02:05:35 +02:00
if ( ! class_exists ( 'mysqli' )) {
2024-01-20 09:22:38 +01:00
dol_print_error ( null , 'Driver mysqli for PHP not available' );
2024-10-20 00:03:46 +02:00
return false ;
2023-05-14 02:05:35 +02:00
}
2023-08-01 16:58:32 +02:00
if ( strpos ( $host , 'ssl://' ) === 0 ) {
2023-08-01 17:06:22 +02:00
$tmp = new mysqliDoli ( $host , $login , $passwd , $name , $port );
2023-08-01 16:58:32 +02:00
} else {
$tmp = new mysqli ( $host , $login , $passwd , $name , $port );
}
2022-07-04 03:06:56 +02:00
} catch ( Exception $e ) {
dol_syslog ( get_class ( $this ) . " ::connect failed " , LOG_DEBUG );
}
return $tmp ;
2020-10-31 14:32:18 +01:00
}
2011-06-19 20:09:41 +02:00
2020-10-31 14:32:18 +01:00
/**
2011-09-03 01:09:39 +02:00
* Return version of database server
*
* @ return string Version string
2020-10-31 14:32:18 +01:00
*/
public function getVersion ()
{
return $this -> db -> server_info ;
}
/**
* Return version of database client driver
*
* @ return string Version string
*/
public function getDriverInfo ()
{
return $this -> db -> client_info ;
}
/**
2024-01-13 19:48:20 +01:00
* Close database connection
2020-10-31 14:32:18 +01:00
*
2024-01-13 19:48:20 +01:00
* @ return bool True if disconnect successful , false otherwise
2020-10-31 14:32:18 +01:00
* @ see connect ()
*/
public function close ()
{
2021-02-23 22:03:23 +01:00
if ( $this -> db ) {
if ( $this -> transaction_opened > 0 ) {
dol_syslog ( get_class ( $this ) . " ::close Closing a connection with an opened transaction depth= " . $this -> transaction_opened , LOG_ERR );
}
2020-10-31 14:32:18 +01:00
$this -> connected = false ;
return $this -> db -> close ();
}
return false ;
}
2022-01-28 11:11:36 +01:00
2020-10-31 14:32:18 +01:00
/**
* Execute a SQL request and return the resultset
*
* @ param string $query SQL query string
* @ 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 ) .
* 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 .
* @ param string $type Type of SQL order ( 'ddl' for insert , update , select , delete or 'dml' for create , alter ... )
2022-03-19 13:06:30 +01:00
* @ 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 )
2024-04-28 17:10:51 +02:00
* @ return false | mysqli_result Resultset of answer
2020-10-31 14:32:18 +01:00
*/
2021-08-16 22:01:58 +02:00
public function query ( $query , $usesavepoint = 0 , $type = 'auto' , $result_mode = 0 )
2020-10-31 14:32:18 +01:00
{
2024-04-28 17:10:51 +02:00
global $dolibarr_main_db_readonly ;
2020-10-31 14:32:18 +01:00
$query = trim ( $query );
2021-02-23 22:03:23 +01:00
if ( ! in_array ( $query , array ( 'BEGIN' , 'COMMIT' , 'ROLLBACK' ))) {
2020-10-31 14:32:18 +01:00
$SYSLOG_SQL_LIMIT = 10000 ; // limit log to 10kb per line to limit DOS attacks
dol_syslog ( 'sql=' . substr ( $query , 0 , $SYSLOG_SQL_LIMIT ), LOG_DEBUG );
}
2021-02-23 22:03:23 +01:00
if ( empty ( $query )) {
return false ; // Return false = error if empty request
}
2020-10-31 14:32:18 +01:00
2021-07-09 19:34:17 +02:00
if ( ! empty ( $dolibarr_main_db_readonly )) {
2021-07-10 09:42:20 +02:00
if ( preg_match ( '/^(INSERT|UPDATE|REPLACE|DELETE|CREATE|ALTER|TRUNCATE|DROP)/i' , $query )) {
2021-07-09 19:34:17 +02:00
$this -> lasterror = 'Application in read-only mode' ;
$this -> lasterrno = 'APPREADONLY' ;
$this -> lastquery = $query ;
return false ;
}
}
2022-01-28 11:11:36 +01:00
try {
2024-03-11 12:58:51 +01:00
$ret = $this -> db -> query ( $query , $result_mode );
2022-01-28 11:11:36 +01:00
} catch ( Exception $e ) {
dol_syslog ( get_class ( $this ) . " ::query Exception in query instead of returning an error: " . $e -> getMessage (), LOG_ERR );
$ret = false ;
2020-10-31 14:32:18 +01:00
}
2021-02-23 22:03:23 +01:00
if ( ! preg_match ( " /^COMMIT/i " , $query ) && ! preg_match ( " /^ROLLBACK/i " , $query )) {
2020-10-31 14:32:18 +01:00
// Si requete utilisateur, on la sauvegarde ainsi que son resultset
2021-02-23 22:03:23 +01:00
if ( ! $ret ) {
2020-10-31 14:32:18 +01:00
$this -> lastqueryerror = $query ;
$this -> lasterror = $this -> error ();
$this -> lasterrno = $this -> errno ();
2014-06-13 01:34:39 +02:00
2023-07-26 01:42:22 +02:00
if ( getDolGlobalInt ( 'SYSLOG_LEVEL' ) < LOG_DEBUG ) {
2021-02-23 22:03:23 +01:00
dol_syslog ( get_class ( $this ) . " ::query SQL Error query: " . $query , LOG_ERR ); // Log of request was not yet done previously
}
2024-03-02 19:57:59 +01:00
dol_syslog ( get_class ( $this ) . " ::query SQL Error message: " . $this -> lasterrno . " " . $this -> lasterror . self :: getCallerInfoString (), LOG_ERR );
2020-10-31 14:32:18 +01:00
//var_dump(debug_print_backtrace());
}
$this -> lastquery = $query ;
$this -> _results = $ret ;
}
return $ret ;
}
2024-03-02 19:57:59 +01:00
/**
* Get caller info
*
* @ return string
*/
final protected static function getCallerInfoString ()
{
$backtrace = debug_backtrace ();
$msg = " " ;
if ( count ( $backtrace ) >= 1 ) {
$trace = $backtrace [ 1 ];
if ( isset ( $trace [ 'file' ], $trace [ 'line' ])) {
$msg = " From { $trace [ 'file' ] } : { $trace [ 'line' ] } . " ;
}
}
return $msg ;
}
2020-10-31 14:32:18 +01:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2021-08-16 22:01:58 +02:00
* Returns the current line ( as an object ) for the resultset cursor
2020-10-31 14:32:18 +01:00
*
* @ param mysqli_result $resultset Curseur de la requete voulue
* @ return object | null Object result line or null if KO or end of cursor
*/
public function fetch_object ( $resultset )
{
// phpcs:enable
2024-01-13 19:48:20 +01:00
// If the resultset was not provided, we get the last one for this connection
2021-02-23 22:03:23 +01:00
if ( ! is_object ( $resultset )) {
$resultset = $this -> _results ;
}
2015-09-17 20:38:48 +02:00
return $resultset -> fetch_object ();
2020-10-31 14:32:18 +01:00
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2024-10-29 22:44:47 +01:00
* Return data as an array
2020-10-31 14:32:18 +01:00
*
* @ param mysqli_result $resultset Resultset of request
2024-10-29 22:44:47 +01:00
* @ return array < int | string , mixed >| null | false Array or null if KO or end of cursor
2020-10-31 14:32:18 +01:00
*/
public function fetch_array ( $resultset )
{
// phpcs:enable
2024-01-13 19:48:20 +01:00
// If resultset not provided, we take the last used by connection
2021-02-23 22:03:23 +01:00
if ( ! is_object ( $resultset )) {
$resultset = $this -> _results ;
}
2020-10-31 14:32:18 +01:00
return $resultset -> fetch_array ();
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2024-10-29 22:44:47 +01:00
* Return data as an array
2020-10-31 14:32:18 +01:00
*
* @ param mysqli_result $resultset Resultset of request
2024-10-29 22:44:47 +01:00
* @ return array < int , mixed >| null | int < 0 , 0 > Array or null if KO or end of cursor or 0 if resultset is bool
2020-10-31 14:32:18 +01:00
*/
public function fetch_row ( $resultset )
{
// phpcs:enable
2024-01-13 19:48:20 +01:00
// If resultset not provided, we take the last used by connection
2021-02-23 22:03:23 +01:00
if ( ! is_bool ( $resultset )) {
if ( ! is_object ( $resultset )) {
$resultset = $this -> _results ;
}
2020-10-31 14:32:18 +01:00
return $resultset -> fetch_row ();
} else {
2024-01-13 19:48:20 +01:00
// si le curseur est un boolean on retourne la valeur 0
2020-10-31 14:32:18 +01:00
return 0 ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return number of lines for result of a SELECT
*
* @ param mysqli_result $resultset Resulset of requests
* @ return int Nb of lines
* @ see affected_rows ()
*/
public function num_rows ( $resultset )
{
// phpcs:enable
2024-01-13 19:48:20 +01:00
// If resultset not provided, we take the last used by connection
2021-02-23 22:03:23 +01:00
if ( ! is_object ( $resultset )) {
$resultset = $this -> _results ;
}
2022-09-09 12:26:17 +02:00
return isset ( $resultset -> num_rows ) ? $resultset -> num_rows : 0 ;
2020-10-31 14:32:18 +01:00
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return the number of lines in the result of a request INSERT , DELETE or UPDATE
*
* @ param mysqli_result $resultset Curseur de la requete voulue
* @ return int Number of lines
* @ see num_rows ()
*/
public function affected_rows ( $resultset )
{
// phpcs:enable
2024-01-13 19:48:20 +01:00
// If resultset not provided, we take the last used by connection
2021-02-23 22:03:23 +01:00
if ( ! is_object ( $resultset )) {
$resultset = $this -> _results ;
}
2020-10-31 14:32:18 +01:00
// mysql necessite un link de base pour cette fonction contrairement
// a pqsql qui prend un resultset
return $this -> db -> affected_rows ;
}
/**
2024-01-13 19:48:20 +01:00
* Libere le dernier resultset utilise sur cette connection
2020-10-31 14:32:18 +01:00
*
* @ param mysqli_result $resultset Curseur de la requete voulue
* @ return void
*/
public function free ( $resultset = null )
{
2024-01-13 19:48:20 +01:00
// If resultset not provided, we take the last used by connection
2021-02-23 22:03:23 +01:00
if ( ! is_object ( $resultset )) {
$resultset = $this -> _results ;
}
2020-10-31 14:32:18 +01:00
// Si resultset en est un, on libere la memoire
2021-02-23 22:03:23 +01:00
if ( is_object ( $resultset )) {
$resultset -> free_result ();
}
2020-10-31 14:32:18 +01:00
}
/**
* Escape a string to insert data
*
* @ param string $stringtoencode String to escape
* @ return string String escaped
*/
public function escape ( $stringtoencode )
{
2022-07-16 15:20:45 +02:00
return $this -> db -> real_escape_string (( string ) $stringtoencode );
2020-10-31 14:32:18 +01:00
}
2022-09-14 16:01:45 +02:00
/**
* Escape a string to insert data into a like
*
* @ param string $stringtoencode String to escape
* @ return string String escaped
*/
public function escapeforlike ( $stringtoencode )
{
2024-03-29 17:09:36 +01:00
// We must first replace the \ char into \\, then we can replace _ and % into \_ and \%
2023-04-15 01:24:50 +02:00
return str_replace ( array ( '\\' , '_' , '%' ), array ( '\\\\' , '\_' , '\%' ), ( string ) $stringtoencode );
2022-09-14 16:01:45 +02:00
}
2020-10-31 14:32:18 +01:00
/**
* Return generic error code of last operation .
*
2024-01-13 19:48:20 +01:00
* @ return string Error code ( Examples : DB_ERROR_TABLE_ALREADY_EXISTS , DB_ERROR_RECORD_ALREADY_EXISTS ... )
2020-10-31 14:32:18 +01:00
*/
public function errno ()
{
if ( ! $this -> connected ) {
2024-01-13 19:48:20 +01:00
// Si il y a eu echec de connection, $this->db n'est pas valide.
2020-10-31 14:32:18 +01:00
return 'DB_ERROR_FAILED_TO_CONNECT' ;
} else {
// Constants to convert a MySql error code to a generic Dolibarr error code
$errorcode_map = array (
2023-10-05 12:58:44 +02:00
1004 => 'DB_ERROR_CANNOT_CREATE' ,
1005 => 'DB_ERROR_CANNOT_CREATE' ,
1006 => 'DB_ERROR_CANNOT_CREATE' ,
1007 => 'DB_ERROR_ALREADY_EXISTS' ,
1008 => 'DB_ERROR_CANNOT_DROP' ,
1022 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS' ,
1025 => 'DB_ERROR_NO_FOREIGN_KEY_TO_DROP' ,
1044 => 'DB_ERROR_ACCESSDENIED' ,
1046 => 'DB_ERROR_NODBSELECTED' ,
1048 => 'DB_ERROR_CONSTRAINT' ,
1050 => 'DB_ERROR_TABLE_ALREADY_EXISTS' ,
1051 => 'DB_ERROR_NOSUCHTABLE' ,
1054 => 'DB_ERROR_NOSUCHFIELD' ,
1060 => 'DB_ERROR_COLUMN_ALREADY_EXISTS' ,
1061 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS' ,
1062 => 'DB_ERROR_RECORD_ALREADY_EXISTS' ,
1064 => 'DB_ERROR_SYNTAX' ,
1068 => 'DB_ERROR_PRIMARY_KEY_ALREADY_EXISTS' ,
1075 => 'DB_ERROR_CANT_DROP_PRIMARY_KEY' ,
1091 => 'DB_ERROR_NOSUCHFIELD' ,
1100 => 'DB_ERROR_NOT_LOCKED' ,
1136 => 'DB_ERROR_VALUE_COUNT_ON_ROW' ,
1146 => 'DB_ERROR_NOSUCHTABLE' ,
1215 => 'DB_ERROR_CANNOT_ADD_FOREIGN_KEY_CONSTRAINT' ,
1216 => 'DB_ERROR_NO_PARENT' ,
1217 => 'DB_ERROR_CHILD_EXISTS' ,
1396 => 'DB_ERROR_USER_ALREADY_EXISTS' , // When creating a user that already existing
1451 => 'DB_ERROR_CHILD_EXISTS' ,
1826 => 'DB_ERROR_KEY_NAME_ALREADY_EXISTS'
2020-10-31 14:32:18 +01:00
);
if ( isset ( $errorcode_map [ $this -> db -> errno ])) {
return $errorcode_map [ $this -> db -> errno ];
}
$errno = $this -> db -> errno ;
return ( $errno ? 'DB_ERROR_' . $errno : '0' );
}
}
/**
2012-01-28 17:22:02 +01:00
* Return description of last error
*
* @ return string Error text
2020-10-31 14:32:18 +01:00
*/
public function error ()
{
if ( ! $this -> connected ) {
2024-01-13 19:48:20 +01:00
// Si il y a eu echec de connection, $this->db n'est pas valide pour mysqli_error.
2020-10-31 14:32:18 +01:00
return 'Not connected. Check setup parameters in conf/conf.php file and your mysql client and server versions' ;
} else {
return $this -> db -> error ;
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2011-12-19 23:32:24 +01:00
* Get last ID after an insert INSERT
*
* @ param string $tab Table name concerned by insert . Ne sert pas sous MySql mais requis pour compatibilite avec Postgresql
* @ param string $fieldid Field name
2015-05-12 17:56:01 +02:00
* @ return int | string Id of row
2020-10-31 14:32:18 +01:00
*/
public function last_insert_id ( $tab , $fieldid = 'rowid' )
{
// phpcs:enable
return $this -> db -> insert_id ;
}
/**
2021-09-02 13:25:00 +02:00
* Encrypt sensitive data in database
* Warning : This function includes the escape and add the SQL simple quotes on strings .
2020-10-31 14:32:18 +01:00
*
2021-09-02 13:25:00 +02:00
* @ param string $fieldorvalue Field name or value to encrypt
* @ param int $withQuotes Return string including the SQL simple quotes . This param must always be 1 ( Value 0 is bugged and deprecated ) .
* @ return string XXX ( field ) or XXX ( 'value' ) or field or 'value'
2020-10-31 14:32:18 +01:00
*/
2021-09-02 13:25:00 +02:00
public function encrypt ( $fieldorvalue , $withQuotes = 1 )
2020-10-31 14:32:18 +01:00
{
global $conf ;
// Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
$cryptType = ( ! empty ( $conf -> db -> dolibarr_main_db_encryption ) ? $conf -> db -> dolibarr_main_db_encryption : 0 );
//Encryption key
$cryptKey = ( ! empty ( $conf -> db -> dolibarr_main_db_cryptkey ) ? $conf -> db -> dolibarr_main_db_cryptkey : '' );
2021-09-02 13:25:00 +02:00
$escapedstringwithquotes = ( $withQuotes ? " ' " : " " ) . $this -> escape ( $fieldorvalue ) . ( $withQuotes ? " ' " : " " );
2020-10-31 14:32:18 +01:00
2021-02-23 22:03:23 +01:00
if ( $cryptType && ! empty ( $cryptKey )) {
if ( $cryptType == 2 ) {
2021-09-02 13:25:00 +02:00
$escapedstringwithquotes = " AES_ENCRYPT( " . $escapedstringwithquotes . " , ' " . $this -> escape ( $cryptKey ) . " ') " ;
2021-02-23 22:03:23 +01:00
} elseif ( $cryptType == 1 ) {
2021-09-02 13:25:00 +02:00
$escapedstringwithquotes = " DES_ENCRYPT( " . $escapedstringwithquotes . " , ' " . $this -> escape ( $cryptKey ) . " ') " ;
2020-10-31 14:32:18 +01:00
}
}
2021-09-02 13:25:00 +02:00
return $escapedstringwithquotes ;
2020-10-31 14:32:18 +01:00
}
/**
* Decrypt sensitive data in database
*
* @ param string $value Value to decrypt
* @ return string Decrypted value if used
*/
public function decrypt ( $value )
{
global $conf ;
// Type of encryption (2: AES (recommended), 1: DES , 0: no encryption)
$cryptType = ( ! empty ( $conf -> db -> dolibarr_main_db_encryption ) ? $conf -> db -> dolibarr_main_db_encryption : 0 );
//Encryption key
$cryptKey = ( ! empty ( $conf -> db -> dolibarr_main_db_cryptkey ) ? $conf -> db -> dolibarr_main_db_cryptkey : '' );
$return = $value ;
2021-02-23 22:03:23 +01:00
if ( $cryptType && ! empty ( $cryptKey )) {
if ( $cryptType == 2 ) {
2020-10-31 14:32:18 +01:00
$return = 'AES_DECRYPT(' . $value . ',\'' . $cryptKey . '\')' ;
2021-02-23 22:03:23 +01:00
} elseif ( $cryptType == 1 ) {
2020-10-31 14:32:18 +01:00
$return = 'DES_DECRYPT(' . $value . ',\'' . $cryptKey . '\')' ;
}
}
return $return ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2024-01-13 19:48:20 +01:00
* Return connection ID
2012-01-28 16:04:48 +01:00
*
2024-01-13 19:48:20 +01:00
* @ return string Id connection
2020-10-31 14:32:18 +01:00
*/
public function DDLGetConnectId ()
{
// phpcs:enable
$resql = $this -> query ( 'SELECT CONNECTION_ID()' );
2021-02-23 22:03:23 +01:00
if ( $resql ) {
2020-10-31 14:32:18 +01:00
$row = $this -> fetch_row ( $resql );
return $row [ 0 ];
2021-02-23 22:03:23 +01:00
} else {
return '?' ;
}
2020-10-31 14:32:18 +01:00
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Create a new database
2012-01-28 17:22:02 +01:00
* Do not use function xxx_create_db ( xxx = mysql , ... ) as they are deprecated
* We force to create database with charset this -> forcecharset and collate this -> forcecollate
*
* @ param string $database Database name to create
* @ param string $charset Charset used to store data
* @ param string $collation Charset used to sort data
* @ param string $owner Username of database owner
2024-04-28 17:10:51 +02:00
* @ return null | mysqli_result Resource defined if OK , null if KO
2020-10-31 14:32:18 +01:00
*/
public function DDLCreateDb ( $database , $charset = '' , $collation = '' , $owner = '' )
{
// phpcs:enable
2021-02-23 22:03:23 +01:00
if ( empty ( $charset )) {
$charset = $this -> forcecharset ;
}
if ( empty ( $collation )) {
$collation = $this -> forcecollate ;
}
2020-10-31 14:32:18 +01:00
// ALTER DATABASE dolibarr_db DEFAULT CHARACTER SET latin DEFAULT COLLATE latin1_swedish_ci
2012-07-22 21:50:08 +02:00
$sql = " CREATE DATABASE ` " . $this -> escape ( $database ) . " ` " ;
2020-04-10 10:59:32 +02:00
$sql .= " DEFAULT CHARACTER SET ` " . $this -> escape ( $charset ) . " ` DEFAULT COLLATE ` " . $this -> escape ( $collation ) . " ` " ;
2011-06-19 20:09:41 +02:00
2020-10-31 14:32:18 +01:00
dol_syslog ( $sql , LOG_DEBUG );
$ret = $this -> query ( $sql );
2021-02-23 22:03:23 +01:00
if ( ! $ret ) {
2020-10-31 14:32:18 +01:00
// We try again for compatibility with Mysql < 4.1.1
$sql = " CREATE DATABASE ` " . $this -> escape ( $database ) . " ` " ;
dol_syslog ( $sql , LOG_DEBUG );
$ret = $this -> query ( $sql );
}
2024-04-28 17:10:51 +02:00
2020-10-31 14:32:18 +01:00
return $ret ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2012-01-06 14:51:09 +01:00
* List tables into a database
*
* @ param string $database Name of database
2024-01-13 19:48:20 +01:00
* @ param string $table Name of table filter ( 'xxx%' )
2024-10-29 22:44:47 +01:00
* @ return string [] List of tables in an array
2020-10-31 14:32:18 +01:00
*/
public function DDLListTables ( $database , $table = '' )
{
// phpcs:enable
$listtables = array ();
$like = '' ;
2021-02-23 22:03:23 +01:00
if ( $table ) {
2021-11-11 17:08:35 +01:00
$tmptable = preg_replace ( '/[^a-z0-9\.\-\_%]/i' , '' , $table );
$like = " LIKE ' " . $this -> escape ( $tmptable ) . " ' " ;
2021-02-23 22:03:23 +01:00
}
2021-11-11 17:08:35 +01:00
$tmpdatabase = preg_replace ( '/[^a-z0-9\.\-\_]/i' , '' , $database );
2023-01-24 10:54:48 +01:00
$sql = " SHOW TABLES FROM ` " . $tmpdatabase . " ` " . $like . " ; " ;
2020-10-31 14:32:18 +01:00
//print $sql;
$result = $this -> query ( $sql );
2021-02-23 22:03:23 +01:00
if ( $result ) {
while ( $row = $this -> fetch_row ( $result )) {
2020-10-31 14:32:18 +01:00
$listtables [] = $row [ 0 ];
}
}
return $listtables ;
}
2023-04-14 10:06:10 +02:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* List tables into a database
*
* @ param string $database Name of database
2024-01-13 19:48:20 +01:00
* @ param string $table Name of table filter ( 'xxx%' )
2024-10-29 22:44:47 +01:00
* @ return array < array { 0 : string , 1 : string } > List of tables in an array
2023-04-14 10:06:10 +02:00
*/
public function DDLListTablesFull ( $database , $table = '' )
{
// phpcs:enable
$listtables = array ();
$like = '' ;
if ( $table ) {
$tmptable = preg_replace ( '/[^a-z0-9\.\-\_%]/i' , '' , $table );
$like = " LIKE ' " . $this -> escape ( $tmptable ) . " ' " ;
}
$tmpdatabase = preg_replace ( '/[^a-z0-9\.\-\_]/i' , '' , $database );
$sql = " SHOW FULL TABLES FROM ` " . $tmpdatabase . " ` " . $like . " ; " ;
$result = $this -> query ( $sql );
if ( $result ) {
while ( $row = $this -> fetch_row ( $result )) {
$listtables [] = $row ;
}
}
return $listtables ;
}
2020-10-31 14:32:18 +01:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2024-10-29 22:44:47 +01:00
* List information of columns in a table .
2012-01-06 14:51:09 +01:00
*
* @ param string $table Name of table
2024-10-29 22:44:47 +01:00
* @ return array < array < mixed >> Table with information of columns in the table
2020-10-31 14:32:18 +01:00
*/
public function DDLInfoTable ( $table )
{
// phpcs:enable
$infotables = array ();
2021-11-11 17:08:35 +01:00
$tmptable = preg_replace ( '/[^a-z0-9\.\-\_]/i' , '' , $table );
$sql = " SHOW FULL COLUMNS FROM " . $tmptable . " ; " ;
2020-10-31 14:32:18 +01:00
dol_syslog ( $sql , LOG_DEBUG );
$result = $this -> query ( $sql );
2021-02-23 22:03:23 +01:00
if ( $result ) {
while ( $row = $this -> fetch_row ( $result )) {
2020-10-31 14:32:18 +01:00
$infotables [] = $row ;
}
}
return $infotables ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2011-09-12 19:08:02 +02:00
* Create a table into database
*
2017-11-21 11:50:57 +01:00
* @ param string $table Name of table
2024-12-05 00:06:24 +01:00
* @ param array < string , array { type : string , label : string , enabled : int < 0 , 2 >| string , position : int , notnull ? : int , visible : int <- 2 , 5 >| string , alwayseditable ? : int < 0 , 1 > , noteditable ? : int < 0 , 1 > , default ? : string , index ? : int , foreignkey ? : string , searchall ? : int < 0 , 1 > , isameasure ? : int < 0 , 1 > , css ? : string , csslist ? : string , help ? : string , showoncombobox ? : int < 0 , 2 > , disabled ? : int < 0 , 1 > , arrayofkeyval ? : array < int , string > , autofocusoncreate ? : int < 0 , 1 > , comment ? : string , copytoclipboard ? : int < 1 , 2 > , validate ? : int < 0 , 1 > } > $fields Tableau associatif [ nom champ ][ tableau des descriptions ]
2011-09-12 19:08:02 +02:00
* @ param string $primary_key Nom du champ qui sera la clef primaire
* @ param string $type Type de la table
2024-10-29 22:44:47 +01:00
* @ param ? array < string , mixed > $unique_keys Tableau associatifs Nom de champs qui seront clef unique => valeur
* @ param string [] $fulltext_keys Tableau des Nom de champs qui seront indexes en fulltext
* @ param array < string , mixed > $keys Tableau des champs cles noms => valeur
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 if KO , >= 0 if OK
2020-10-31 14:32:18 +01:00
*/
public function DDLCreateTable ( $table , $fields , $primary_key , $type , $unique_keys = null , $fulltext_keys = null , $keys = null )
{
// phpcs:enable
2024-03-21 12:10:09 +01:00
// @TODO: $fulltext_keys parameter is unused
if ( empty ( $type )) {
$type = 'InnoDB' ;
}
2020-10-31 14:32:18 +01:00
2022-07-06 03:16:08 +02:00
$pk = '' ;
2024-03-21 13:19:31 +01:00
$sqlk = array ();
$sqluq = array ();
2022-07-06 03:16:08 +02:00
2024-03-21 12:10:09 +01:00
// Keys found into the array $fields: type,value,attribute,null,default,extra
// ex. : $fields['rowid'] = array(
// 'type'=>'int' or 'integer',
// 'value'=>'11',
// 'null'=>'not null',
// 'extra'=> 'auto_increment'
// );
$sql = " CREATE TABLE " . $this -> sanitize ( $table ) . " ( " ;
2020-10-31 14:32:18 +01:00
$i = 0 ;
$sqlfields = array ();
2021-02-23 22:03:23 +01:00
foreach ( $fields as $field_name => $field_desc ) {
2024-03-21 12:10:09 +01:00
$sqlfields [ $i ] = $this -> sanitize ( $field_name ) . " " ;
$sqlfields [ $i ] .= $this -> sanitize ( $field_desc [ 'type' ]);
2024-03-21 13:55:23 +01:00
if ( isset ( $field_desc [ 'value' ]) && $field_desc [ 'value' ] !== '' ) {
2024-03-21 12:10:09 +01:00
$sqlfields [ $i ] .= " ( " . $this -> sanitize ( $field_desc [ 'value' ]) . " ) " ;
2013-05-21 14:31:39 +02:00
}
2024-03-21 13:55:23 +01:00
if ( isset ( $field_desc [ 'attribute' ]) && $field_desc [ 'attribute' ] !== '' ) {
2024-07-31 15:41:00 +02:00
$sqlfields [ $i ] .= " " . $this -> sanitize ( $field_desc [ 'attribute' ], 0 , 0 , 1 ); // Allow space to accept attributes like "ON UPDATE CURRENT_TIMESTAMP"
2013-05-21 14:31:39 +02:00
}
2024-03-21 13:55:23 +01:00
if ( isset ( $field_desc [ 'default' ]) && $field_desc [ 'default' ] !== '' ) {
2024-03-21 12:10:09 +01:00
if ( in_array ( $field_desc [ 'type' ], array ( 'tinyint' , 'smallint' , 'int' , 'double' ))) {
$sqlfields [ $i ] .= " DEFAULT " . (( float ) $field_desc [ 'default' ]);
} elseif ( $field_desc [ 'default' ] == 'null' || $field_desc [ 'default' ] == 'CURRENT_TIMESTAMP' ) {
$sqlfields [ $i ] .= " DEFAULT " . $this -> sanitize ( $field_desc [ 'default' ]);
2020-05-21 15:05:19 +02:00
} else {
2024-03-21 12:10:09 +01:00
$sqlfields [ $i ] .= " DEFAULT ' " . $this -> escape ( $field_desc [ 'default' ]) . " ' " ;
2013-05-21 14:31:39 +02:00
}
}
2024-03-21 13:55:23 +01:00
if ( isset ( $field_desc [ 'null' ]) && $field_desc [ 'null' ] !== '' ) {
2024-03-21 12:10:09 +01:00
$sqlfields [ $i ] .= " " . $this -> sanitize ( $field_desc [ 'null' ], 0 , 0 , 1 );
2013-05-21 14:31:39 +02:00
}
2024-03-21 13:55:23 +01:00
if ( isset ( $field_desc [ 'extra' ]) && $field_desc [ 'extra' ] !== '' ) {
2024-03-21 12:45:58 +01:00
$sqlfields [ $i ] .= " " . $this -> sanitize ( $field_desc [ 'extra' ], 0 , 0 , 1 );
2013-05-21 14:31:39 +02:00
}
2024-03-21 15:57:45 +01:00
if ( ! empty ( $primary_key ) && $primary_key == $field_name ) {
$sqlfields [ $i ] .= " AUTO_INCREMENT PRIMARY KEY " ; // mysql instruction that will be converted by driver late
}
2020-10-31 14:32:18 +01:00
$i ++ ;
}
if ( is_array ( $unique_keys )) {
$i = 0 ;
2021-02-23 22:03:23 +01:00
foreach ( $unique_keys as $key => $value ) {
2024-03-21 12:10:09 +01:00
$sqluq [ $i ] = " UNIQUE KEY ' " . $this -> sanitize ( $key ) . " ' (' " . $this -> escape ( $value ) . " ') " ;
2020-10-31 14:32:18 +01:00
$i ++ ;
}
}
2021-02-23 22:03:23 +01:00
if ( is_array ( $keys )) {
2020-10-31 14:32:18 +01:00
$i = 0 ;
2021-02-23 22:03:23 +01:00
foreach ( $keys as $key => $value ) {
2024-03-21 12:10:09 +01:00
$sqlk [ $i ] = " KEY " . $this -> sanitize ( $key ) . " ( " . $value . " ) " ;
2020-10-31 14:32:18 +01:00
$i ++ ;
}
}
2024-03-21 14:13:17 +01:00
$sql .= implode ( ', ' , $sqlfields );
2024-10-29 22:44:47 +01:00
if ( ! is_array ( $unique_keys ) && $unique_keys != " " ) {
2021-02-23 22:03:23 +01:00
$sql .= " , " . implode ( ',' , $sqluq );
}
if ( is_array ( $keys )) {
$sql .= " , " . implode ( ',' , $sqlk );
}
2024-03-21 12:10:09 +01:00
$sql .= " ) " ;
$sql .= " engine= " . $this -> sanitize ( $type );
2020-10-31 14:32:18 +01:00
2021-02-23 22:03:23 +01:00
if ( ! $this -> query ( $sql )) {
return - 1 ;
} else {
return 1 ;
}
2020-10-31 14:32:18 +01:00
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Drop a table into database
*
* @ param string $table Name of table
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 if KO , >= 0 if OK
2020-10-31 14:32:18 +01:00
*/
public function DDLDropTable ( $table )
{
// phpcs:enable
2021-11-11 17:08:35 +01:00
$tmptable = preg_replace ( '/[^a-z0-9\.\-\_]/i' , '' , $table );
2024-03-21 12:21:38 +01:00
$sql = " DROP TABLE " . $this -> sanitize ( $tmptable );
2017-11-21 11:50:57 +01:00
2021-02-23 22:03:23 +01:00
if ( ! $this -> query ( $sql )) {
return - 1 ;
} else {
return 1 ;
}
2020-10-31 14:32:18 +01:00
}
2017-11-21 11:50:57 +01:00
2020-10-31 14:32:18 +01:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2012-01-06 14:51:09 +01:00
* Return a pointer of line with description of a table or field
*
* @ param string $table Name of table
* @ param string $field Optionnel : Name of field if we want description of field
2015-05-12 17:56:01 +02:00
* @ return bool | mysqli_result Resultset x ( x -> Field , x -> Type , ... )
2020-10-31 14:32:18 +01:00
*/
public function DDLDescTable ( $table , $field = " " )
{
// phpcs:enable
2024-03-21 12:45:58 +01:00
$sql = " DESC " . $this -> sanitize ( $table ) . " " . $this -> sanitize ( $field );
2020-10-31 14:32:18 +01:00
dol_syslog ( get_class ( $this ) . " ::DDLDescTable " . $sql , LOG_DEBUG );
$this -> _results = $this -> query ( $sql );
return $this -> _results ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2012-01-06 14:51:09 +01:00
* Create a new field into table
*
* @ param string $table Name of table
* @ param string $field_name Name of field to add
2025-02-03 14:28:54 +01:00
* @ param array { type : string , label ? : string , enabled ? : int < 0 , 2 >| string , position ? : int , notnull ? : int , visible ? : int , noteditable ? : int , default ? : string , extra ? : string , null ? : 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 array of description of the field to insert [ parameter name ][ parameter value ]
2024-01-13 19:48:20 +01:00
* @ param string $field_position Optional e . g .: " after some_field "
2023-12-01 19:51:32 +01:00
* @ return int Return integer < 0 if KO , > 0 if OK
2020-10-31 14:32:18 +01:00
*/
public function DDLAddField ( $table , $field_name , $field_desc , $field_position = " " )
{
// phpcs:enable
// cles recherchees dans le tableau des descriptions (field_desc) : type,value,attribute,null,default,extra
// ex. : $field_desc = array('type'=>'int','value'=>'11','null'=>'not null','extra'=> 'auto_increment');
2024-03-21 12:45:58 +01:00
$sql = " ALTER TABLE " . $this -> sanitize ( $table ) . " ADD " . $this -> sanitize ( $field_name ) . " " ;
$sql .= $this -> sanitize ( $field_desc [ 'type' ]);
if ( isset ( $field_desc [ 'value' ]) && preg_match ( " /^[^ \ s]/i " , $field_desc [ 'value' ])) {
if ( ! in_array ( $field_desc [ 'type' ], array ( 'tinyint' , 'smallint' , 'int' , 'date' , 'datetime' )) && $field_desc [ 'value' ]) {
$sql .= " ( " . $this -> sanitize ( $field_desc [ 'value' ]) . " ) " ;
2020-10-31 14:32:18 +01:00
}
}
2021-02-23 22:03:23 +01:00
if ( isset ( $field_desc [ 'attribute' ]) && preg_match ( " /^[^ \ s]/i " , $field_desc [ 'attribute' ])) {
2024-03-21 12:45:58 +01:00
$sql .= " " . $this -> sanitize ( $field_desc [ 'attribute' ]);
2020-10-31 14:32:18 +01:00
}
2021-02-23 22:03:23 +01:00
if ( isset ( $field_desc [ 'null' ]) && preg_match ( " /^[^ \ s]/i " , $field_desc [ 'null' ])) {
2025-01-03 15:55:05 +01:00
if ( $field_desc [ 'null' ] == 'NOT NULL' ) {
$sql .= " " . $this -> sanitize ( $field_desc [ 'null' ], 0 , 0 , 1 );
} else {
$sql .= " " . $this -> sanitize ( $field_desc [ 'null' ]);
}
2020-10-31 14:32:18 +01:00
}
2021-02-23 22:03:23 +01:00
if ( isset ( $field_desc [ 'default' ]) && preg_match ( " /^[^ \ s]/i " , $field_desc [ 'default' ])) {
2024-03-21 12:45:58 +01:00
if ( in_array ( $field_desc [ 'type' ], array ( 'tinyint' , 'smallint' , 'int' , 'double' ))) {
$sql .= " DEFAULT " . (( float ) $field_desc [ 'default' ]);
} elseif ( $field_desc [ 'default' ] == 'null' || $field_desc [ 'default' ] == 'CURRENT_TIMESTAMP' ) {
$sql .= " DEFAULT " . $this -> sanitize ( $field_desc [ 'default' ]);
2021-02-23 22:03:23 +01:00
} else {
2024-03-21 12:45:58 +01:00
$sql .= " DEFAULT ' " . $this -> escape ( $field_desc [ 'default' ]) . " ' " ;
2021-02-23 22:03:23 +01:00
}
2020-10-31 14:32:18 +01:00
}
2021-02-23 22:03:23 +01:00
if ( isset ( $field_desc [ 'extra' ]) && preg_match ( " /^[^ \ s]/i " , $field_desc [ 'extra' ])) {
2024-03-21 13:02:41 +01:00
$sql .= " " . $this -> sanitize ( $field_desc [ 'extra' ], 0 , 0 , 1 );
2020-10-31 14:32:18 +01:00
}
2024-03-21 13:02:41 +01:00
$sql .= " " . $this -> sanitize ( $field_position , 0 , 0 , 1 );
2020-10-31 14:32:18 +01:00
dol_syslog ( get_class ( $this ) . " ::DDLAddField " . $sql , LOG_DEBUG );
if ( $this -> query ( $sql )) {
return 1 ;
}
return - 1 ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2012-01-06 14:51:09 +01:00
* Update format of a field into a table
*
* @ param string $table Name of table
* @ param string $field_name Name of field to modify
2024-09-29 21:52:31 +02:00
* @ 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 , value ? : string , null ? : string } $field_desc Array with description of field format
2023-12-01 19:51:32 +01:00
* @ return int Return integer < 0 if KO , > 0 if OK
2020-10-31 14:32:18 +01:00
*/
public function DDLUpdateField ( $table , $field_name , $field_desc )
{
// phpcs:enable
2024-03-21 12:45:58 +01:00
$sql = " ALTER TABLE " . $this -> sanitize ( $table );
$sql .= " MODIFY COLUMN " . $this -> sanitize ( $field_name ) . " " . $this -> sanitize ( $field_desc [ 'type' ]);
2024-09-29 21:52:31 +02:00
if ( in_array ( $field_desc [ 'type' ], array ( 'double' , 'tinyint' , 'int' , 'varchar' )) && array_key_exists ( 'value' , $field_desc ) && $field_desc [ 'value' ]) {
2024-03-21 12:45:58 +01:00
$sql .= " ( " . $this -> sanitize ( $field_desc [ 'value' ]) . " ) " ;
2020-10-31 14:32:18 +01:00
}
2024-09-29 21:52:31 +02:00
if ( isset ( $field_desc [ 'null' ]) && ( $field_desc [ 'null' ] == 'not null' || $field_desc [ 'null' ] == 'NOT NULL' )) {
2020-10-31 14:32:18 +01:00
// 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
2021-02-23 22:03:23 +01:00
if ( $field_desc [ 'type' ] == 'varchar' || $field_desc [ 'type' ] == 'text' ) {
2024-03-21 12:45:58 +01:00
$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 " ;
2020-10-31 14:32:18 +01:00
$this -> query ( $sqlbis );
2024-03-21 12:45:58 +01:00
} elseif ( in_array ( $field_desc [ 'type' ], array ( 'tinyint' , 'smallint' , 'int' , 'double' ))) {
$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 " ;
2020-10-31 14:32:18 +01:00
$this -> query ( $sqlbis );
}
$sql .= " NOT NULL " ;
}
2023-03-22 17:13:27 +01:00
if ( isset ( $field_desc [ 'default' ]) && $field_desc [ 'default' ] != '' ) {
2024-03-21 11:14:39 +01:00
if ( in_array ( $field_desc [ 'type' ], array ( 'tinyint' , 'smallint' , 'int' , 'double' ))) {
$sql .= " DEFAULT " . (( float ) $field_desc [ 'default' ]);
2021-02-23 22:03:23 +01:00
} elseif ( $field_desc [ 'type' ] != 'text' ) {
$sql .= " DEFAULT ' " . $this -> escape ( $field_desc [ 'default' ]) . " ' " ; // Default not supported on text fields
}
2020-10-31 14:32:18 +01:00
}
2012-11-11 15:10:01 +01:00
2020-10-31 14:32:18 +01:00
dol_syslog ( get_class ( $this ) . " ::DDLUpdateField " . $sql , LOG_DEBUG );
2021-02-23 22:03:23 +01:00
if ( ! $this -> query ( $sql )) {
return - 1 ;
} else {
return 1 ;
}
2020-10-31 14:32:18 +01:00
}
2011-06-19 20:09:41 +02:00
2020-10-31 14:32:18 +01:00
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2012-01-06 14:51:09 +01:00
* Drop a field from table
*
* @ param string $table Name of table
* @ param string $field_name Name of field to drop
2023-12-01 19:51:32 +01:00
* @ return int Return integer < 0 if KO , > 0 if OK
2020-10-31 14:32:18 +01:00
*/
public function DDLDropField ( $table , $field_name )
{
// phpcs:enable
2021-11-11 18:20:31 +01:00
$tmp_field_name = preg_replace ( '/[^a-z0-9\.\-\_]/i' , '' , $field_name );
2024-03-21 12:45:58 +01:00
$sql = " ALTER TABLE " . $this -> sanitize ( $table ) . " DROP COLUMN ` " . $this -> sanitize ( $tmp_field_name ) . " ` " ;
2020-10-31 14:32:18 +01:00
if ( $this -> query ( $sql )) {
return 1 ;
}
$this -> error = $this -> lasterror ();
return - 1 ;
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
2012-01-06 14:51:09 +01:00
* Create a user and privileges to connect to database ( even if database does not exists yet )
*
2016-12-23 00:23:57 +01:00
* @ param string $dolibarr_main_db_host Ip server or '%'
2024-01-13 19:48:20 +01:00
* @ param string $dolibarr_main_db_user Nom new user
* @ param string $dolibarr_main_db_pass Password for the new user
2012-01-06 14:51:09 +01:00
* @ param string $dolibarr_main_db_name Database name where user must be granted
2023-12-06 15:46:39 +01:00
* @ return int Return integer < 0 if KO , >= 0 if OK
2020-10-31 14:32:18 +01:00
*/
public function DDLCreateUser ( $dolibarr_main_db_host , $dolibarr_main_db_user , $dolibarr_main_db_pass , $dolibarr_main_db_name )
{
// phpcs:enable
2022-03-31 16:04:28 +02:00
$sql = " CREATE USER ' " . $this -> escape ( $dolibarr_main_db_user ) . " ' IDENTIFIED BY ' " . $this -> escape ( $dolibarr_main_db_pass ) . " ' " ;
2020-10-31 14:32:18 +01:00
dol_syslog ( get_class ( $this ) . " ::DDLCreateUser " , LOG_DEBUG ); // No sql to avoid password in log
$resql = $this -> query ( $sql );
2021-02-23 22:03:23 +01:00
if ( ! $resql ) {
if ( $this -> lasterrno != 'DB_ERROR_USER_ALREADY_EXISTS' ) {
2020-10-31 14:32:18 +01:00
return - 1 ;
} else {
// If user already exists, we continue to set permissions
dol_syslog ( get_class ( $this ) . " ::DDLCreateUser sql= " . $sql , LOG_WARNING );
}
}
// Redo with localhost forced (sometimes user is created on %)
2021-12-28 16:26:21 +01:00
$sql = " CREATE USER ' " . $this -> escape ( $dolibarr_main_db_user ) . " '@'localhost' IDENTIFIED BY ' " . $this -> escape ( $dolibarr_main_db_pass ) . " ' " ;
2020-10-31 14:32:18 +01:00
$resql = $this -> query ( $sql );
2021-12-28 16:26:21 +01:00
$sql = " GRANT ALL PRIVILEGES ON " . $this -> escape ( $dolibarr_main_db_name ) . " .* TO ' " . $this -> escape ( $dolibarr_main_db_user ) . " '@' " . $this -> escape ( $dolibarr_main_db_host ) . " ' " ;
2020-10-31 14:32:18 +01:00
dol_syslog ( get_class ( $this ) . " ::DDLCreateUser " , LOG_DEBUG ); // No sql to avoid password in log
$resql = $this -> query ( $sql );
2021-02-23 22:03:23 +01:00
if ( ! $resql ) {
2021-12-28 16:26:21 +01:00
$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 ) . " ' " ;
2020-10-31 14:32:18 +01:00
return - 1 ;
}
$sql = " FLUSH Privileges " ;
dol_syslog ( get_class ( $this ) . " ::DDLCreateUser " , LOG_DEBUG );
$resql = $this -> query ( $sql );
2021-02-23 22:03:23 +01:00
if ( ! $resql ) {
2020-10-31 14:32:18 +01:00
return - 1 ;
}
return 1 ;
}
/**
* Return charset used to store data in current database
* 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 " ;)
*
* @ return string Charset
* @ see getDefaultCollationDatabase ()
*/
public function getDefaultCharacterSetDatabase ()
{
2023-10-05 12:58:44 +02:00
$resql = $this -> query ( " SHOW VARIABLES LIKE 'character_set_database' " );
2021-02-23 22:03:23 +01:00
if ( ! $resql ) {
2020-10-31 14:32:18 +01:00
// version Mysql < 4.1.1
return $this -> forcecharset ;
}
$liste = $this -> fetch_array ( $resql );
$tmpval = $liste [ 'Value' ];
return $tmpval ;
}
/**
* Return list of available charset that can be used to store data in database
*
2024-10-29 22:44:47 +01:00
* @ return ? array < int , array { charset : string , description : string } > List of Charset
2020-10-31 14:32:18 +01:00
*/
public function getListOfCharacterSet ()
{
$resql = $this -> query ( 'SHOW CHARSET' );
$liste = array ();
2021-02-23 22:03:23 +01:00
if ( $resql ) {
2020-10-31 14:32:18 +01:00
$i = 0 ;
2021-02-23 22:03:23 +01:00
while ( $obj = $this -> fetch_object ( $resql )) {
2020-10-31 14:32:18 +01:00
$liste [ $i ][ 'charset' ] = $obj -> Charset ;
$liste [ $i ][ 'description' ] = $obj -> Description ;
$i ++ ;
}
$this -> free ( $resql );
} else {
// version Mysql < 4.1.1
return null ;
}
return $liste ;
}
/**
* Return collation used in current database
*
* @ return string Collation value
* @ see getDefaultCharacterSetDatabase ()
*/
public function getDefaultCollationDatabase ()
{
2023-10-05 12:58:44 +02:00
$resql = $this -> query ( " SHOW VARIABLES LIKE 'collation_database' " );
2021-02-23 22:03:23 +01:00
if ( ! $resql ) {
2020-10-31 14:32:18 +01:00
// version Mysql < 4.1.1
return $this -> forcecollate ;
}
$liste = $this -> fetch_array ( $resql );
$tmpval = $liste [ 'Value' ];
return $tmpval ;
}
/**
* Return list of available collation that can be used for database
*
2024-10-29 22:44:47 +01:00
* @ return ? array < int , array { collation : string } > List of Collations
2020-10-31 14:32:18 +01:00
*/
public function getListOfCollation ()
{
$resql = $this -> query ( 'SHOW COLLATION' );
$liste = array ();
2021-02-23 22:03:23 +01:00
if ( $resql ) {
2020-10-31 14:32:18 +01:00
$i = 0 ;
2021-02-23 22:03:23 +01:00
while ( $obj = $this -> fetch_object ( $resql )) {
2020-10-31 14:32:18 +01:00
$liste [ $i ][ 'collation' ] = $obj -> Collation ;
$i ++ ;
}
$this -> free ( $resql );
} else {
// version Mysql < 4.1.1
return null ;
}
return $liste ;
}
/**
2011-08-04 15:58:14 +02:00
* Return full path of dump program
2012-01-06 14:51:09 +01:00
*
2011-08-04 15:58:14 +02:00
* @ return string Full path of dump program
2020-10-31 14:32:18 +01:00
*/
public function getPathOfDump ()
{
$fullpathofdump = '/pathtomysqldump/mysqldump' ;
2023-10-05 12:58:44 +02:00
$resql = $this -> query ( " SHOW VARIABLES LIKE 'basedir' " );
2021-02-23 22:03:23 +01:00
if ( $resql ) {
2020-10-31 14:32:18 +01:00
$liste = $this -> fetch_array ( $resql );
$basedir = $liste [ 'Value' ];
$fullpathofdump = $basedir . ( preg_match ( '/\/$/' , $basedir ) ? '' : '/' ) . 'bin/mysqldump' ;
}
return $fullpathofdump ;
}
/**
* Return full path of restore program
*
* @ return string Full path of restore program
*/
public function getPathOfRestore ()
{
$fullpathofimport = '/pathtomysql/mysql' ;
2023-10-05 12:58:44 +02:00
$resql = $this -> query ( " SHOW VARIABLES LIKE 'basedir' " );
2021-02-23 22:03:23 +01:00
if ( $resql ) {
2020-10-31 14:32:18 +01:00
$liste = $this -> fetch_array ( $resql );
$basedir = $liste [ 'Value' ];
$fullpathofimport = $basedir . ( preg_match ( '/\/$/' , $basedir ) ? '' : '/' ) . 'bin/mysql' ;
}
return $fullpathofimport ;
}
/**
* Return value of server parameters
*
2024-10-29 22:44:47 +01:00
* @ param string $filter Filter list on a particular value
* @ return array < string , string > Array of key - values ( key => value )
2020-10-31 14:32:18 +01:00
*/
public function getServerParametersValues ( $filter = '' )
{
$result = array ();
$sql = 'SHOW VARIABLES' ;
2021-02-23 22:03:23 +01:00
if ( $filter ) {
$sql .= " LIKE ' " . $this -> escape ( $filter ) . " ' " ;
}
2020-10-31 14:32:18 +01:00
$resql = $this -> query ( $sql );
2021-02-23 22:03:23 +01:00
if ( $resql ) {
while ( $obj = $this -> fetch_object ( $resql )) {
$result [ $obj -> Variable_name ] = $obj -> Value ;
}
2020-10-31 14:32:18 +01:00
}
return $result ;
}
/**
* Return value of server status ( current indicators on memory , cache ... )
*
2024-10-29 22:44:47 +01:00
* @ param string $filter Filter list on a particular value
* @ return array < string , string > Array of key - values ( key => value )
2020-10-31 14:32:18 +01:00
*/
public function getServerStatusValues ( $filter = '' )
{
$result = array ();
$sql = 'SHOW STATUS' ;
2021-02-23 22:03:23 +01:00
if ( $filter ) {
$sql .= " LIKE ' " . $this -> escape ( $filter ) . " ' " ;
}
2020-10-31 14:32:18 +01:00
$resql = $this -> query ( $sql );
2021-02-23 22:03:23 +01:00
if ( $resql ) {
while ( $obj = $this -> fetch_object ( $resql )) {
$result [ $obj -> Variable_name ] = $obj -> Value ;
}
2020-10-31 14:32:18 +01:00
}
return $result ;
}
2002-04-30 12:44:42 +02:00
}
2023-08-01 16:58:32 +02:00
2024-10-18 23:48:24 +02:00
2024-10-20 00:03:46 +02:00
if ( class_exists ( 'myslqi' )) {
2023-08-01 17:06:22 +02:00
/**
2024-10-20 00:03:46 +02:00
* Class to make SSL connection
2023-08-01 17:06:22 +02:00
*/
2024-10-20 00:03:46 +02:00
class mysqliDoli extends mysqli
2023-08-01 17:06:22 +02:00
{
2024-10-20 00:03:46 +02:00
/**
* Constructor .
* This create an opened connection to a database server and eventually to a database
*
* @ param string $host Address of database server
* @ param string $user Name of database user
* @ param string $pass Password of database user
* @ param string $name Name of database
* @ param int $port Port of database server
* @ param string $socket Socket
*/
public function __construct ( $host , $user , $pass , $name , $port = 0 , $socket = " " )
{
$flags = 0 ;
if ( PHP_VERSION_ID >= 80100 ) {
parent :: __construct ();
} else {
// @phan-suppress-next-line PhanDeprecatedFunctionInternal
parent :: init ();
}
if ( strpos ( $host , 'ssl://' ) === 0 ) {
$host = substr ( $host , 6 );
parent :: options ( MYSQLI_OPT_SSL_VERIFY_SERVER_CERT , false );
// Suppress false positive @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal
parent :: ssl_set ( null , null , " " , null , null );
$flags = MYSQLI_CLIENT_SSL ;
}
parent :: real_connect ( $host , $user , $pass , $name , $port , $socket , $flags );
2023-08-01 16:58:32 +02:00
}
}
}