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