Completed
Push — master ( 92f140...7db589 )
by cam
05:28 queued 21s
created

mysql.php ➔ spip_release_lock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
/* *************************************************************************\
4
 *  SPIP, Système de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright © avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
10
 *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Ce fichier contient les fonctions gérant
15
 * les instructions SQL pour MySQL
16
 *
17
 * Ces instructions utilisent la librairie PHP Mysqli
18
 *
19
 * @package SPIP\Core\SQL\MySQL
20
 */
21
22
if (!defined('_ECRIRE_INC_VERSION')) {
23
	return;
24
}
25
26
if (!defined('_MYSQL_NOPLANES')) {
27
	define('_MYSQL_NOPLANES', true);
28
}
29
30
/**
31
 * Crée la première connexion à un serveur MySQL via MySQLi
32
 *
33
 * @param string $host Chemin du serveur
34
 * @param int $port Port de connexion
35
 * @param string $login Nom d'utilisateur
36
 * @param string $pass Mot de passe
37
 * @param string $db Nom de la base
38
 * @param string $prefixe Préfixe des tables SPIP
39
 * @return array|bool
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use false|array.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
40
 *     - false si la connexion a échoué
41
 *     - tableau décrivant la connexion sinon
42
 */
43
function req_mysql_dist($host, $port, $login, $pass, $db = '', $prefixe = '') {
44
	if (!extension_loaded('mysqli')) {
45
		return false;
46
	}
47
48
	// si port est fourni mais pas host, c'est un socket -> compat avec vieille syntaxe de mysql_connect() et anciens fichiers connect.php
49
	if (
50
		$port and !is_numeric($socket = $port)
51
		and (!$host or $host=='localhost')) {
52
		$link = @mysqli_connect($host, $login, $pass, '', null, $socket);
53
	}
54
	elseif ($port) {
55
		$link = @mysqli_connect($host, $login, $pass, '', $port);
56
	}
57
	else {
58
		$link = @mysqli_connect($host, $login, $pass);
59
	}
60
61
	if (!$link) {
62
		spip_log('Echec mysqli_connect. Erreur : ' . mysqli_connect_error(), 'mysql.' . _LOG_HS);
63
64
		return false;
65
	}
66
	$last = '';
67
	if (!$db) {
68
		$ok = $link;
69
		$db = 'spip';
70
	} else {
71
		$ok = mysqli_select_db($link, $db);
72
		if (defined('_MYSQL_SET_SQL_MODE')
73
			or defined('_MYSQL_SQL_MODE_TEXT_NOT_NULL') // compatibilite
74
		) {
75
			mysqli_query($link, $last = "set sql_mode=''");
76
		}
77
	}
78
79
	spip_log("Connexion MySQLi vers $host, base $db, prefixe $prefixe " . ($ok ? "operationnelle" : 'impossible'),
80
		_LOG_DEBUG);
81
82
	return !$ok ? false : array(
83
		'db' => $db,
84
		'last' => $last,
85
		'prefixe' => $prefixe ? $prefixe : $db,
86
		'link' => $link,
87
		'total_requetes' => 0,
88
	);
89
}
90
91
92
$GLOBALS['spip_mysql_functions_1'] = array(
93
	'alter' => 'spip_mysql_alter',
94
	'count' => 'spip_mysql_count',
95
	'countsel' => 'spip_mysql_countsel',
96
	'create' => 'spip_mysql_create',
97
	'create_base' => 'spip_mysql_create_base',
98
	'create_view' => 'spip_mysql_create_view',
99
	'date_proche' => 'spip_mysql_date_proche',
100
	'delete' => 'spip_mysql_delete',
101
	'drop_table' => 'spip_mysql_drop_table',
102
	'drop_view' => 'spip_mysql_drop_view',
103
	'errno' => 'spip_mysql_errno',
104
	'error' => 'spip_mysql_error',
105
	'explain' => 'spip_mysql_explain',
106
	'fetch' => 'spip_mysql_fetch',
107
	'seek' => 'spip_mysql_seek',
108
	'free' => 'spip_mysql_free',
109
	'hex' => 'spip_mysql_hex',
110
	'in' => 'spip_mysql_in',
111
	'insert' => 'spip_mysql_insert',
112
	'insertq' => 'spip_mysql_insertq',
113
	'insertq_multi' => 'spip_mysql_insertq_multi',
114
	'listdbs' => 'spip_mysql_listdbs',
115
	'multi' => 'spip_mysql_multi',
116
	'optimize' => 'spip_mysql_optimize',
117
	'query' => 'spip_mysql_query',
118
	'quote' => 'spip_mysql_quote',
119
	'replace' => 'spip_mysql_replace',
120
	'replace_multi' => 'spip_mysql_replace_multi',
121
	'repair' => 'spip_mysql_repair',
122
	'select' => 'spip_mysql_select',
123
	'selectdb' => 'spip_mysql_selectdb',
124
	'set_charset' => 'spip_mysql_set_charset',
125
	'get_charset' => 'spip_mysql_get_charset',
126
	'showbase' => 'spip_mysql_showbase',
127
	'showtable' => 'spip_mysql_showtable',
128
	'update' => 'spip_mysql_update',
129
	'updateq' => 'spip_mysql_updateq',
130
131
	// association de chaque nom http d'un charset aux couples MySQL
132
	'charsets' => array(
133
		'cp1250' => array('charset' => 'cp1250', 'collation' => 'cp1250_general_ci'),
134
		'cp1251' => array('charset' => 'cp1251', 'collation' => 'cp1251_general_ci'),
135
		'cp1256' => array('charset' => 'cp1256', 'collation' => 'cp1256_general_ci'),
136
		'iso-8859-1' => array('charset' => 'latin1', 'collation' => 'latin1_swedish_ci'),
137
//'iso-8859-6'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'),
138
		'iso-8859-9' => array('charset' => 'latin5', 'collation' => 'latin5_turkish_ci'),
139
//'iso-8859-15'=>array('charset'=>'latin1','collation'=>'latin1_swedish_ci'),
140
		'utf-8' => array('charset' => 'utf8', 'collation' => 'utf8_general_ci')
141
	)
142
);
143
144
145
/**
146
 * Retrouver un link d'une connexion MySQL via MySQLi
147
 *
148
 * @param string $serveur Nom du serveur
149
 * @return Object Information de connexion pour mysqli
150
 */
151
function _mysql_link($serveur = '') {
152
	$link = &$GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
153
154
	return $link;
155
}
156
157
158
/**
159
 * Définit un charset pour la connexion avec Mysql
160
 *
161
 * @param string $charset Charset à appliquer
162
 * @param string $serveur Nom de la connexion
163
 * @param bool $requeter inutilisé
164
 * @return resource       Ressource de résultats pour fetch()
165
 */
166
function spip_mysql_set_charset($charset, $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
167
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
168
	spip_log("changement de charset sql : " . "SET NAMES " . _q($charset), _LOG_DEBUG);
169
170
	return mysqli_query($connexion['link'], $connexion['last'] = "SET NAMES " . _q($charset));
171
}
172
173
174
/**
175
 * Teste si le charset indiqué est disponible sur le serveur SQL
176
 *
177
 * @param array|string $charset Nom du charset à tester.
178
 * @param string $serveur Nom de la connexion
179
 * @param bool $requeter inutilisé
180
 * @return array                Description du charset (son nom est dans 'charset')
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
181
 */
182
function spip_mysql_get_charset($charset = array(), $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
183
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
184
	$connexion['last'] = $c = "SHOW CHARACTER SET"
185
		. (!$charset ? '' : (" LIKE " . _q($charset['charset'])));
186
187
	return spip_mysql_fetch(mysqli_query($connexion['link'], $c), null, $serveur);
188
}
189
190
/**
191
 * Exécute une requête MySQL, munie d'une trace à la demande
192
 *
193
 * @param string $query Requête
194
 * @param string $serveur Nom de la connexion
195
 * @param bool $requeter Exécuter la requête, sinon la retourner
196
 * @return array|resource|string|bool
197
 *     - string : Texte de la requête si on ne l'exécute pas
198
 *     - ressource|bool : Si requête exécutée
199
 *     - array : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
200
 */
201
function spip_mysql_query($query, $serveur = '', $requeter = true) {
202
203
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
204
	$prefixe = $connexion['prefixe'];
205
	$link = $connexion['link'];
206
	$db = $connexion['db'];
207
208
	$query = _mysql_traite_query($query, $db, $prefixe);
209
210
	// renvoyer la requete inerte si demandee
211
	if (!$requeter) {
212
		return $query;
213
	}
214
215 View Code Duplication
	if (isset($_GET['var_profile'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
216
		include_spip('public/tracer');
217
		$t = trace_query_start();
218
	} else {
219
		$t = 0;
220
	}
221
222
	$connexion['last'] = $query;
223
	$connexion['total_requetes']++;
224
225
	// ajouter un debug utile dans log/mysql-slow.log ?
226
	$debug = '';
227
	if (defined('_DEBUG_SLOW_QUERIES') and _DEBUG_SLOW_QUERIES) {
228
		if (isset($GLOBALS['debug']['aucasou'])) {
229
			list(, $id, , $infos) = $GLOBALS['debug']['aucasou'];
230
			$debug .= "BOUCLE$id @ " . (isset($infos[0]) ? $infos[0] : '') . " | ";
231
		}
232
		if (isset($_SERVER['REQUEST_URI'])) {
233
			$debug .= $_SERVER['REQUEST_URI'];
234
		}
235
		if (!empty($GLOBALS['ip'])) {
236
			$debug .= ' + ' . $GLOBALS['ip'];
237
		}
238
		$debug = ' /* ' . mysqli_real_escape_string($link, str_replace('*/', '@/', $debug)) . ' */';
239
	}
240
241
	$r = mysqli_query($link, $query . $debug);
242
243
	//Eviter de propager le GoneAway sur les autres requetes d'un même processus PHP
244
	if ($e = spip_mysql_errno($serveur)) {  // Log d'un Gone Away
245
		if ($e == 2006) { //Si Gone Away on relance une connexion vierge
246
			//Fermer la connexion defaillante
247
			mysqli_close($connexion['link']);
248
			unset($GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]);
249
			//Relancer une connexion vierge
250
			spip_connect($serveur);
251
			$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
252
			$link = $connexion['link'];
253
			//On retente au cas où
254
			$r = mysqli_query($link, $query . $debug);
255
		}
256
	}
257
258
	// Log de l'erreur eventuelle
259
	if ($e = spip_mysql_errno($serveur)) {
260
		$e .= spip_mysql_error($query, $serveur);
261
	} // et du fautif
262
	return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
263
}
264
265
/**
266
 * Modifie une structure de table MySQL
267
 *
268
 * @param string $query Requête SQL (sans 'ALTER ')
269
 * @param string $serveur Nom de la connexion
270
 * @param bool $requeter Exécuter la requête, sinon la retourner
271
 * @return array|bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
272
 *     - string : Texte de la requête si on ne l'exécute pas
273
 *     - bool   : Si requête exécutée
274
 *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
275
 */
276
function spip_mysql_alter($query, $serveur = '', $requeter = true) {
277
	// ici on supprime les ` entourant le nom de table pour permettre
278
	// la transposition du prefixe, compte tenu que les plugins ont la mauvaise habitude
279
	// d'utiliser ceux-ci, copie-colle de phpmyadmin
280
	$query = preg_replace(",^TABLE\s*`([^`]*)`,i", "TABLE \\1", $query);
281
282
	return spip_mysql_query("ALTER " . $query, $serveur, $requeter); # i.e. que PG se debrouille
283
}
284
285
286
/**
287
 * Optimise une table MySQL
288
 *
289
 * @param string $table Nom de la table
290
 * @param string $serveur Nom de la connexion
291
 * @param bool $requeter inutilisé
292
 * @return bool            Toujours true
293
 */
294
function spip_mysql_optimize($table, $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
295
	spip_mysql_query("OPTIMIZE TABLE " . $table);
296
297
	return true;
298
}
299
300
301
/**
302
 * Retourne une explication de requête (Explain) MySQL
303
 *
304
 * @param string $query Texte de la requête
305
 * @param string $serveur Nom de la connexion
306
 * @param bool $requeter inutilisé
307
 * @return array           Tableau de l'explication
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
308
 */
309
function spip_mysql_explain($query, $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
310
	if (strpos(ltrim($query), 'SELECT') !== 0) {
311
		return array();
312
	}
313
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
314
	$prefixe = $connexion['prefixe'];
315
	$link = $connexion['link'];
316
	$db = $connexion['db'];
317
318
	$query = 'EXPLAIN ' . _mysql_traite_query($query, $db, $prefixe);
319
	$r = mysqli_query($link, $query);
320
321
	return spip_mysql_fetch($r, null, $serveur);
322
}
323
324
325
/**
326
 * Exécute une requête de sélection avec MySQL
327
 *
328
 * Instance de sql_select (voir ses specs).
329
 *
330
 * @see sql_select()
331
 * @note
332
 *     Les `\n` et `\t` sont utiles au debusqueur.
333
 *
334
 * @param string|array $select Champs sélectionnés
335
 * @param string|array $from Tables sélectionnées
336
 * @param string|array $where Contraintes
337
 * @param string|array $groupby Regroupements
338
 * @param string|array $orderby Tris
339
 * @param string $limit Limites de résultats
340
 * @param string|array $having Contraintes posts sélections
341
 * @param string $serveur Nom de la connexion
342
 * @param bool $requeter Exécuter la requête, sinon la retourner
343
 * @return array|bool|resource|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string|array|boolean.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
344
 *     - string : Texte de la requête si on ne l'exécute pas
345
 *     - ressource si requête exécutée, ressource pour fetch()
346
 *     - false si la requête exécutée a ratée
347
 *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
348
 */
349
function spip_mysql_select(
350
	$select,
351
	$from,
352
	$where = '',
353
	$groupby = '',
354
	$orderby = '',
355
	$limit = '',
356
	$having = '',
357
	$serveur = '',
358
	$requeter = true
359
) {
360
361
362
	$from = (!is_array($from) ? $from : spip_mysql_select_as($from));
363
	$query =
364
		calculer_mysql_expression('SELECT', $select, ', ')
365
		. calculer_mysql_expression('FROM', $from, ', ')
366
		. calculer_mysql_expression('WHERE', $where)
367
		. calculer_mysql_expression('GROUP BY', $groupby, ',')
368
		. calculer_mysql_expression('HAVING', $having)
369
		. ($orderby ? ("\nORDER BY " . spip_mysql_order($orderby)) : '')
370
		. ($limit ? "\nLIMIT $limit" : '');
371
372
	// renvoyer la requete inerte si demandee
373
	if ($requeter === false) {
374
		return $query;
375
	}
376
	$r = spip_mysql_query($query, $serveur, $requeter);
377
378
	return $r ? $r : $query;
379
}
380
381
382
/**
383
 * Prépare une clause order by
384
 *
385
 * Regroupe en texte les éléments si un tableau est donné
386
 *
387
 * @note
388
 *   0+x avec un champ x commencant par des chiffres est converti par MySQL
389
 *   en le nombre qui commence x. Pas portable malheureusement, on laisse pour le moment.
390
 *
391
 * @param string|array $orderby Texte du orderby à préparer
392
 * @return string Texte du orderby préparé
393
 */
394
function spip_mysql_order($orderby) {
395
	return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
396
}
397
398
399
/**
400
 * Prépare une clause WHERE pour MySQL
401
 *
402
 * Retourne une chaîne avec les bonnes parenthèses pour la
403
 * contrainte indiquée, au format donnée par le compilateur
404
 *
405
 * @param array|string $v
406
 *     Description des contraintes
407
 *     - string : Texte du where
408
 *     - sinon tableau : A et B peuvent être de type string ou array,
409
 *       OP et C sont de type string :
410
 *       - array(A) : A est le texte du where
411
 *       - array(OP, A) : contrainte OP( A )
412
 *       - array(OP, A, B) : contrainte (A OP B)
413
 *       - array(OP, A, B, C) : contrainte (A OP (B) : C)
414
 * @return string
415
 *     Contrainte pour clause WHERE
416
 */
417 View Code Duplication
function calculer_mysql_where($v) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
418
	if (!is_array($v)) {
419
		return $v;
420
	}
421
422
	$op = array_shift($v);
423
	if (!($n = count($v))) {
424
		return $op;
425
	} else {
426
		$arg = calculer_mysql_where(array_shift($v));
427
		if ($n == 1) {
428
			return "$op($arg)";
429
		} else {
430
			$arg2 = calculer_mysql_where(array_shift($v));
431
			if ($n == 2) {
432
				return "($arg $op $arg2)";
433
			} else {
434
				return "($arg $op ($arg2) : $v[0])";
435
			}
436
		}
437
	}
438
}
439
440
/**
441
 * Calcule un expression pour une requête, en cumulant chaque élément
442
 * avec l'opérateur de liaison ($join) indiqué
443
 *
444
 * Renvoie grosso modo "$expression join($join, $v)"
445
 *
446
 * @param string $expression Mot clé de l'expression, tel que "WHERE" ou "ORDER BY"
447
 * @param array|string $v Données de l'expression
448
 * @param string $join Si les données sont un tableau, elles seront groupées par cette jointure
449
 * @return string            Texte de l'expression, une partie donc, du texte la requête.
450
 */
451 View Code Duplication
function calculer_mysql_expression($expression, $v, $join = 'AND') {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
452
	if (empty($v)) {
453
		return '';
454
	}
455
456
	$exp = "\n$expression ";
457
458
	if (!is_array($v)) {
459
		return $exp . $v;
460
	} else {
461
		if (strtoupper($join) === 'AND') {
462
			return $exp . join("\n\t$join ", array_map('calculer_mysql_where', $v));
463
		} else {
464
			return $exp . join($join, $v);
465
		}
466
	}
467
}
468
469
470
/**
471
 * Renvoie des `nom AS alias`
472
 *
473
 * @param array $args
474
 * @return string Sélection de colonnes pour une clause SELECT
475
 */
476 View Code Duplication
function spip_mysql_select_as($args) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
477
	$res = '';
478
	foreach ($args as $k => $v) {
479
		if (substr($k, -1) == '@') {
480
			// c'est une jointure qui se refere au from precedent
481
			// pas de virgule
482
			$res .= '  ' . $v;
483
		} else {
484
			if (!is_numeric($k)) {
485
				$p = strpos($v, " ");
486
				if ($p) {
487
					$v = substr($v, 0, $p) . " AS `$k`" . substr($v, $p);
488
				} else {
489
					$v .= " AS `$k`";
490
				}
491
			}
492
			$res .= ', ' . $v;
493
		}
494
	}
495
496
	return substr($res, 2);
497
}
498
499
500
/**
501
 * Changer les noms des tables ($table_prefix)
502
 *
503
 * TODO: Quand tous les appels SQL seront abstraits on pourra l'améliorer
504
 */
505
define('_SQL_PREFIXE_TABLE_MYSQL', '/([,\s])spip_/S');
506
507
508
/**
509
 * Prépare le texte d'une requête avant son exécution
510
 *
511
 * Change les préfixes de tables SPIP par ceux véritables
512
 *
513
 * @param string $query Requête à préparer
514
 * @param string $db Nom de la base de donnée
515
 * @param string $prefixe Préfixe de tables à appliquer
516
 * @return string           Requête préparée
517
 */
518
function _mysql_traite_query($query, $db = '', $prefixe = '') {
519
520
	if ($GLOBALS['mysql_rappel_nom_base'] and $db) {
521
		$pref = '`' . $db . '`.';
522
	} else {
523
		$pref = '';
524
	}
525
526
	if ($prefixe) {
527
		$pref .= $prefixe . "_";
528
	}
529
530
	if (!preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/i', $query, $regs)) {
531
		$suite = '';
532
	} else {
533
		$suite = strstr($query, $regs[0]);
534
		$query = substr($query, 0, -strlen($suite));
535
		// propager le prefixe en cas de requete imbriquee
536
		// il faut alors echapper les chaine avant de le faire, pour ne pas risquer de
537
		// modifier une requete qui est en fait juste du texte dans un champ
538
		if (stripos($suite, "SELECT") !== false) {
539
			list($suite, $textes) = query_echappe_textes($suite);
540
			if (preg_match('/^(.*?)([(]\s*SELECT\b.*)$/si', $suite, $r)) {
541
				$suite = $r[1] . _mysql_traite_query($r[2], $db, $prefixe);
542
			}
543
			$suite = query_reinjecte_textes($suite, $textes);
0 ignored issues
show
Security Bug introduced by
It seems like $textes can also be of type false; however, query_reinjecte_textes() does only seem to accept array, did you maybe forget to handle an error condition?
Loading history...
544
		}
545
	}
546
	$r = preg_replace(_SQL_PREFIXE_TABLE_MYSQL, '\1' . $pref, $query) . $suite;
547
548
	// en option, remplacer les emoji (que mysql ne sait pas gérer) en &#128169;
549
	// remplacer les emoji (que mysql ne sait pas gérer) en &#128169;
550 View Code Duplication
	if (defined('_MYSQL_NOPLANES')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
551
		and _MYSQL_NOPLANES
552
		and !empty($GLOBALS['meta']['charset_sql_connexion'])
553
		and $GLOBALS['meta']['charset_sql_connexion'] == 'utf8') {
554
		include_spip('inc/charsets');
555
		$r = utf8_noplanes($r);
556
	}
557
558
	#spip_log("_mysql_traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG);
559
	return $r;
560
}
561
562
/**
563
 * Sélectionne une base de données
564
 *
565
 * @param string $db
566
 *     Nom de la base à utiliser
567
 * @param string $serveur
568
 *     Nom du connecteur
569
 * @param bool $requeter
570
 *     Inutilisé
571
 *
572
 * @return bool
573
 *     - True cas de succès.
574
 *     - False en cas d'erreur.
575
 **/
576
function spip_mysql_selectdb($db, $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
577
	$link = _mysql_link($serveur);
578
	$ok = mysqli_select_db($link, $db);
579
	if (!$ok) {
580
		spip_log('Echec mysqli_selectdb. Erreur : ' . mysqli_error($link), 'mysql.' . _LOG_CRITIQUE);
581
	}
582
583
	return $ok;
584
}
585
586
587
/**
588
 * Retourne les bases de données accessibles
589
 *
590
 * Retourne un tableau du nom de toutes les bases de données
591
 * accessibles avec les permissions de l'utilisateur SQL
592
 * de cette connexion.
593
 *
594
 * Attention on n'a pas toujours les droits !
595
 *
596
 * @param string $serveur
597
 *     Nom du connecteur
598
 * @param bool $requeter
599
 *     Inutilisé
600
 * @return array
601
 *     Liste de noms de bases de données
602
 **/
603
function spip_mysql_listdbs($serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
604
	$dbs = array();
605
	if ($res = spip_mysql_query("SHOW DATABASES", $serveur)) {
606
		while ($row = mysqli_fetch_assoc($res)) {
607
			$dbs[] = $row['Database'];
608
		}
609
	}
610
611
	return $dbs;
612
}
613
614
615
/**
616
 * Crée une table SQL
617
 *
618
 * Crée une table SQL nommee `$nom` à partir des 2 tableaux `$champs` et `$cles`
619
 *
620
 * @note Le nom des caches doit être inferieur à 64 caractères
621
 *
622
 * @param string $nom Nom de la table SQL
623
 * @param array $champs Couples (champ => description SQL)
624
 * @param array $cles Couples (type de clé => champ(s) de la clé)
625
 * @param bool $autoinc True pour ajouter un auto-incrément sur la Primary Key
626
 * @param bool $temporary True pour créer une table temporaire
627
 * @param string $serveur Nom de la connexion
628
 * @param bool $requeter inutilisé
629
 * @return array|null|resource|string
0 ignored issues
show
Documentation introduced by
Should the return type not be null|array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
630
 *     - null si champs ou cles n'est pas un tableau
631
 *     - true si la requête réussie, false sinon.
632
 */
633
function spip_mysql_create(
634
	$nom,
635
	$champs,
636
	$cles,
637
	$autoinc = false,
638
	$temporary = false,
639
	$serveur = '',
640
	$requeter = true
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
641
) {
642
643
	$query = '';
644
	$keys = '';
645
	$s = '';
646
	$p = '';
647
648
	// certains plugins declarent les tables  (permet leur inclusion dans le dump)
649
	// sans les renseigner (laisse le compilo recuperer la description)
650
	if (!is_array($champs) || !is_array($cles)) {
651
		return;
652
	}
653
654
	$res = spip_mysql_query("SELECT version() as v", $serveur);
655
	if (($row = mysqli_fetch_array($res)) && (version_compare($row['v'], '5.0', '>='))) {
656
		spip_mysql_query("SET sql_mode=''", $serveur);
657
	}
658
659
	foreach ($cles as $k => $v) {
660
		$keys .= "$s\n\t\t$k ($v)";
661
		if ($k == "PRIMARY KEY") {
662
			$p = $v;
663
		}
664
		$s = ",";
665
	}
666
	$s = '';
667
668
	$character_set = "";
669 View Code Duplication
	if (@$GLOBALS['meta']['charset_sql_base']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
670
		$character_set .= " CHARACTER SET " . $GLOBALS['meta']['charset_sql_base'];
671
	}
672 View Code Duplication
	if (@$GLOBALS['meta']['charset_collation_sql_base']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
673
		$character_set .= " COLLATE " . $GLOBALS['meta']['charset_collation_sql_base'];
674
	}
675
676
	foreach ($champs as $k => $v) {
677
		$v = _mysql_remplacements_definitions_table($v);
678 View Code Duplication
		if (preg_match(',([a-z]*\s*(\(\s*[0-9]*\s*\))?(\s*binary)?),i', $v, $defs)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
679
			if (preg_match(',(char|text),i', $defs[1])
680
				and !preg_match(',(binary|CHARACTER|COLLATE),i', $v)
681
			) {
682
				$v = $defs[1] . $character_set . ' ' . substr($v, strlen($defs[1]));
683
			}
684
		}
685
686
		$query .= "$s\n\t\t$k $v"
687
			. (($autoinc && ($p == $k) && preg_match(',\b(big|small|medium)?int\b,i', $v))
688
				? " auto_increment"
689
				: ''
690
			);
691
		$s = ",";
692
	}
693
	$temporary = $temporary ? 'TEMPORARY' : '';
694
	$q = "CREATE $temporary TABLE IF NOT EXISTS $nom ($query" . ($keys ? ",$keys" : '') . ")"
695
		. " ENGINE=MyISAM"
696
		. ($character_set ? " DEFAULT $character_set" : "")
697
		. "\n";
698
699
	return spip_mysql_query($q, $serveur);
700
}
701
702
703
/**
704
 * Adapte pour Mysql la déclaration SQL d'une colonne d'une table
705
 *
706
 * @param string|array $query
707
 *     Définition SQL d'un champ de table ou liste de déclarations
708
 * @return string|array
709
 *     Définition SQL adaptée pour MySQL d'un champ de table
710
 */
711
function _mysql_remplacements_definitions_table($query) {
712
	// quelques remplacements
713
	$num = "(\s*\([0-9]*\))?";
0 ignored issues
show
Unused Code introduced by
$num is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
714
	$enum = "(\s*\([^\)]*\))?";
0 ignored issues
show
Unused Code introduced by
$enum is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
715
716
	$remplace = array(
717
		'/VARCHAR(\s*[^\s\(])/is' => 'VARCHAR(255)\\1',
718
		'/^TIMESTAMP($| NULL DEFAULT NULL)/is' => 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
719
	);
720
721
	if (is_string($query)) {
722
		$query = preg_replace(array_keys($remplace), $remplace, $query);
723
	} elseif (is_array($query)) {
724
		$keys = array_keys($remplace);
725
		foreach ($query as $k => $q) {
726
			$query[$k] = preg_replace($keys, $remplace, $q);
727
		}
728
	}
729
730
	return $query;
731
}
732
733
734
/**
735
 * Crée une base de données MySQL
736
 *
737
 * @param string $nom Nom de la base
738
 * @param string $serveur Nom de la connexion
739
 * @param bool $requeter Exécuter la requête, sinon la retourner
740
 * @return bool true si la base est créee.
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
741
 **/
742
function spip_mysql_create_base($nom, $serveur = '', $requeter = true) {
743
	return spip_mysql_query("CREATE DATABASE `$nom`", $serveur, $requeter);
744
}
745
746
747
/**
748
 * Crée une vue SQL nommée `$nom`
749
 *
750
 * @param string $nom
751
 *    Nom de la vue à creer
752
 * @param string $query_select
753
 *     Texte de la requête de sélection servant de base à la vue
754
 * @param string $serveur
755
 *     Nom du connecteur
756
 * @param bool $requeter
757
 *     Effectuer la requete, sinon la retourner
758
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
759
 *     - true si la vue est créée
760
 *     - false si erreur ou si la vue existe déja
761
 *     - string texte de la requête si $requeter vaut false
762
 */
763 View Code Duplication
function spip_mysql_create_view($nom, $query_select, $serveur = '', $requeter = true) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
764
	if (!$query_select) {
765
		return false;
766
	}
767
	// vue deja presente
768
	if (sql_showtable($nom, false, $serveur)) {
769
		spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)", _LOG_ERREUR);
770
771
		return false;
772
	}
773
774
	$query = "CREATE VIEW $nom AS " . $query_select;
775
776
	return spip_mysql_query($query, $serveur, $requeter);
777
}
778
779
780
/**
781
 * Supprime une table SQL
782
 *
783
 * @param string $table Nom de la table SQL
784
 * @param string $exist True pour ajouter un test d'existence avant de supprimer
785
 * @param string $serveur Nom de la connexion
786
 * @param bool $requeter Exécuter la requête, sinon la retourner
787
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
788
 *     - string Texte de la requête si demandé
789
 *     - true si la requête a réussie, false sinon
790
 */
791
function spip_mysql_drop_table($table, $exist = '', $serveur = '', $requeter = true) {
792
	if ($exist) {
793
		$exist = " IF EXISTS";
794
	}
795
796
	return spip_mysql_query("DROP TABLE$exist $table", $serveur, $requeter);
797
}
798
799
/**
800
 * Supprime une vue SQL
801
 *
802
 * @param string $view Nom de la vue SQL
803
 * @param string $exist True pour ajouter un test d'existence avant de supprimer
804
 * @param string $serveur Nom de la connexion
805
 * @param bool $requeter Exécuter la requête, sinon la retourner
806
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
807
 *     - string Texte de la requête si demandé
808
 *     - true si la requête a réussie, false sinon
809
 */
810
function spip_mysql_drop_view($view, $exist = '', $serveur = '', $requeter = true) {
811
	if ($exist) {
812
		$exist = " IF EXISTS";
813
	}
814
815
	return spip_mysql_query("DROP VIEW$exist $view", $serveur, $requeter);
816
}
817
818
/**
819
 * Retourne une ressource de la liste des tables de la base de données
820
 *
821
 * @param string $match
822
 *     Filtre sur tables à récupérer
823
 * @param string $serveur
824
 *     Connecteur de la base
825
 * @param bool $requeter
826
 *     true pour éxecuter la requête
827
 *     false pour retourner le texte de la requête.
828
 * @return ressource
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
829
 *     Ressource à utiliser avec sql_fetch()
830
 **/
831
function spip_mysql_showbase($match, $serveur = '', $requeter = true) {
832
	return spip_mysql_query("SHOW TABLES LIKE " . _q($match), $serveur, $requeter);
833
}
834
835
/**
836
 * Répare une table SQL
837
 *
838
 * Utilise `REPAIR TABLE ...` de MySQL
839
 *
840
 * @param string $table Nom de la table SQL
841
 * @param string $serveur Nom de la connexion
842
 * @param bool $requeter Exécuter la requête, sinon la retourner
843
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
844
 *     - string Texte de la requête si demandée,
845
 *     - true si la requête a réussie, false sinon
846
 */
847
function spip_mysql_repair($table, $serveur = '', $requeter = true) {
848
	return spip_mysql_query("REPAIR TABLE `$table`", $serveur, $requeter);
849
}
850
851
852
define('_MYSQL_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
853
/**
854
 * Obtient la description d'une table ou vue MySQL
855
 *
856
 * Récupère la définition d'une table ou d'une vue avec colonnes, indexes, etc.
857
 * au même format que la définition des tables SPIP, c'est à dire
858
 * un tableau avec les clés
859
 *
860
 * - `field` (tableau colonne => description SQL) et
861
 * - `key` (tableau type de clé => colonnes)
862
 *
863
 * @param string $nom_table Nom de la table SQL
864
 * @param string $serveur Nom de la connexion
865
 * @param bool $requeter Exécuter la requête, sinon la retourner
866
 * @return array|string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|array|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
867
 *     - chaîne vide si pas de description obtenue
868
 *     - string Texte de la requête si demandé
869
 *     - array description de la table sinon
870
 */
871
function spip_mysql_showtable($nom_table, $serveur = '', $requeter = true) {
872
	$s = spip_mysql_query("SHOW CREATE TABLE `$nom_table`", $serveur, $requeter);
873
	if (!$s) {
874
		return '';
875
	}
876
	if (!$requeter) {
877
		return $s;
878
	}
879
880
	list(, $a) = mysqli_fetch_array($s, MYSQLI_NUM);
881
	if (preg_match(_MYSQL_RE_SHOW_TABLE, $a, $r)) {
882
		$desc = $r[1];
883
		// extraction d'une KEY éventuelle en prenant garde de ne pas
884
		// relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
885 View Code Duplication
		if (preg_match("/^(.*?),([^,]*\sKEY[ (].*)$/s", $desc, $r)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
886
			$namedkeys = $r[2];
887
			$desc = $r[1];
888
		} else {
889
			$namedkeys = "";
890
		}
891
892
		$fields = array();
893
		foreach (preg_split("/,\s*`/", $desc) as $v) {
894
			preg_match("/^\s*`?([^`]*)`\s*(.*)/", $v, $r);
895
			$fields[strtolower($r[1])] = $r[2];
896
		}
897
		$keys = array();
898
899 View Code Duplication
		foreach (preg_split('/\)\s*(,|$)/', $namedkeys) as $v) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
900
			if (preg_match("/^\s*([^(]*)\(([^(]*(\(\d+\))?)$/", $v, $r)) {
901
				$k = str_replace("`", '', trim($r[1]));
902
				$t = strtolower(str_replace("`", '', $r[2]));
903
				if ($k && !isset($keys[$k])) {
904
					$keys[$k] = $t;
905
				} else {
906
					$keys[] = $t;
907
				}
908
			}
909
		}
910
		spip_mysql_free($s);
911
912
		return array('field' => $fields, 'key' => $keys);
913
	}
914
915
	$res = spip_mysql_query("SHOW COLUMNS FROM `$nom_table`", $serveur);
916
	if ($res) {
917
		$nfields = array();
918
		$nkeys = array();
919
		while ($val = spip_mysql_fetch($res)) {
920
			$nfields[$val["Field"]] = $val['Type'];
921
			if ($val['Null'] == 'NO') {
922
				$nfields[$val["Field"]] .= ' NOT NULL';
923
			}
924
			if ($val['Default'] === '0' || $val['Default']) {
925
				if (preg_match('/[A-Z_]/', $val['Default'])) {
926
					$nfields[$val["Field"]] .= ' DEFAULT ' . $val['Default'];
927
				} else {
928
					$nfields[$val["Field"]] .= " DEFAULT '" . $val['Default'] . "'";
929
				}
930
			}
931
			if ($val['Extra']) {
932
				$nfields[$val["Field"]] .= ' ' . $val['Extra'];
933
			}
934
			if ($val['Key'] == 'PRI') {
935
				$nkeys['PRIMARY KEY'] = $val["Field"];
936
			} else {
937
				if ($val['Key'] == 'MUL') {
938
					$nkeys['KEY ' . $val["Field"]] = $val["Field"];
939
				} else {
940
					if ($val['Key'] == 'UNI') {
941
						$nkeys['UNIQUE KEY ' . $val["Field"]] = $val["Field"];
942
					}
943
				}
944
			}
945
		}
946
		spip_mysql_free($res);
947
948
		return array('field' => $nfields, 'key' => $nkeys);
949
	}
950
951
	return "";
952
}
953
954
955
/**
956
 * Rècupère une ligne de résultat
957
 *
958
 * Récupère la ligne suivante d'une ressource de résultat
959
 *
960
 * @param Ressource $r Ressource de résultat (issu de sql_select)
961
 * @param string $t Structure de résultat attendu (défaut MYSQLI_ASSOC)
962
 * @param string $serveur Nom de la connexion
963
 * @param bool $requeter Inutilisé
964
 * @return array           Ligne de résultat
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
965
 */
966
function spip_mysql_fetch($r, $t = '', $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
967
	if (!$t) {
968
		$t = MYSQLI_ASSOC;
969
	}
970
	if ($r) {
971
		return mysqli_fetch_array($r, $t);
972
	}
973
}
974
975
/**
976
 * Place le pointeur de résultat sur la position indiquée
977
 *
978
 * @param Ressource $r Ressource de résultat
979
 * @param int $row_number Position. Déplacer le pointeur à cette ligne
980
 * @param string $serveur Nom de la connexion
981
 * @param bool $requeter Inutilisé
982
 * @return bool True si déplacement réussi, false sinon.
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
983
 **/
984
function spip_mysql_seek($r, $row_number, $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
985
	if ($r and mysqli_num_rows($r)) {
986
		return mysqli_data_seek($r, $row_number);
987
	}
988
}
989
990
991
/**
992
 * Retourne le nombre de lignes d'une sélection
993
 *
994
 * @param array|string $from Tables à consulter (From)
995
 * @param array|string $where Conditions a remplir (Where)
996
 * @param array|string $groupby Critère de regroupement (Group by)
997
 * @param array $having Tableau des des post-conditions à remplir (Having)
998
 * @param string $serveur Nom de la connexion
999
 * @param bool $requeter Exécuter la requête, sinon la retourner
1000
 * @return int|string
1001
 *     - String Texte de la requête si demandé
1002
 *     - int Nombre de lignes (0 si la requête n'a pas réussie)
1003
 **/
1004
function spip_mysql_countsel(
1005
	$from = array(),
1006
	$where = array(),
1007
	$groupby = '',
1008
	$having = array(),
1009
	$serveur = '',
1010
	$requeter = true
1011
) {
1012
	$c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
1013
1014
	$r = spip_mysql_select("COUNT($c)", $from, $where, '', '', '', $having, $serveur, $requeter);
1015
	if (!$requeter) {
1016
		return $r;
1017
	}
1018
	if (!$r instanceof mysqli_result) {
1019
		return 0;
1020
	}
1021
	list($c) = mysqli_fetch_array($r, MYSQLI_NUM);
1022
	mysqli_free_result($r);
1023
1024
	return $c;
1025
}
1026
1027
1028
/**
1029
 * Retourne la dernière erreur generée
1030
 *
1031
 * @note
1032
 *   Bien spécifier le serveur auquel on s'adresse,
1033
 *   mais à l'install la globale n'est pas encore complètement définie.
1034
 *
1035
 * @uses sql_error_backtrace()
1036
 *
1037
 * @param string $query
1038
 *     Requête qui était exécutée
1039
 * @param string $serveur
1040
 *     Nom de la connexion
1041
 * @param bool $requeter
1042
 *     Inutilisé
1043
 * @return string
1044
 *     Erreur eventuelle
1045
 **/
1046
function spip_mysql_error($query = '', $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1047
	$link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
1048
	$s = mysqli_error($link);
1049
	if ($s) {
1050
		$trace = debug_backtrace();
1051
		if ($trace[0]['function'] != "spip_mysql_error") {
1052
			spip_log("$s - $query - " . sql_error_backtrace(), 'mysql.' . _LOG_ERREUR);
1053
		}
1054
	}
1055
1056
	return $s;
1057
}
1058
1059
1060
/**
1061
 * Retourne le numero de la dernière erreur SQL
1062
 *
1063
 * @param string $serveur
1064
 *     Nom de la connexion
1065
 * @param bool $requeter
1066
 *     Inutilisé
1067
 * @return int
1068
 *     0, pas d'erreur. Autre, numéro de l'erreur.
1069
 **/
1070
function spip_mysql_errno($serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1071
	$link = $GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
1072
	$s = mysqli_errno($link);
1073
	// 2006 MySQL server has gone away
1074
	// 2013 Lost connection to MySQL server during query
1075
	if (in_array($s, array(2006, 2013))) {
1076
		define('spip_interdire_cache', true);
1077
	}
1078
	if ($s) {
1079
		spip_log("Erreur mysql $s", _LOG_ERREUR);
1080
	}
1081
1082
	return $s;
1083
}
1084
1085
1086
/**
1087
 * Retourne le nombre de lignes d’une ressource de sélection obtenue
1088
 * avec `sql_select()`
1089
 *
1090
 * @param Ressource $r Ressource de résultat
1091
 * @param string $serveur Nom de la connexion
1092
 * @param bool $requeter Inutilisé
1093
 * @return int               Nombre de lignes
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1094
 */
1095
function spip_mysql_count($r, $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1096
	if ($r) {
1097
		return mysqli_num_rows($r);
1098
	}
1099
}
1100
1101
1102
/**
1103
 * Libère une ressource de résultat
1104
 *
1105
 * Indique à MySQL de libérer de sa mémoire la ressoucre de résultat indiquée
1106
 * car on n'a plus besoin de l'utiliser.
1107
 *
1108
 * @param Ressource $r Ressource de résultat
1109
 * @param string $serveur Nom de la connexion
1110
 * @param bool $requeter Inutilisé
1111
 * @return bool              True si réussi
1112
 */
1113
function spip_mysql_free($r, $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1114
	return (($r instanceof mysqli_result) ? mysqli_free_result($r) : false);
1115
}
1116
1117
1118
/**
1119
 * Insère une ligne dans une table
1120
 *
1121
 * @param string $table
1122
 *     Nom de la table SQL
1123
 * @param string $champs
1124
 *     Liste des colonnes impactées,
1125
 * @param string $valeurs
1126
 *     Liste des valeurs,
1127
 * @param array $desc
1128
 *     Tableau de description des colonnes de la table SQL utilisée
1129
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1130
 * @param string $serveur
1131
 *     Nom du connecteur
1132
 * @param bool $requeter
1133
 *     Exécuter la requête, sinon la retourner
1134
 * @return bool|string|int|array
1135
 *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
1136
 *     - Texte de la requête si demandé,
1137
 *     - False en cas d'erreur,
1138
 *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
1139
 **/
1140
function spip_mysql_insert($table, $champs, $valeurs, $desc = array(), $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $desc is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1141
1142
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
1143
	$link = $connexion['link'];
1144
	$table = prefixer_table_spip($table, $connexion['prefixe']);
1145
1146
	// remplacer les emoji (que mysql ne sait pas gérer) en &#128169;
1147 View Code Duplication
	if (defined('_MYSQL_NOPLANES')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1148
		and _MYSQL_NOPLANES
1149
		and !empty($GLOBALS['meta']['charset_sql_connexion'])
1150
		and $GLOBALS['meta']['charset_sql_connexion'] == 'utf8') {
1151
		include_spip('inc/charsets');
1152
		$valeurs = utf8_noplanes($valeurs);
1153
	}
1154
1155
	$query = "INSERT INTO $table $champs VALUES $valeurs";
1156
	if (!$requeter) {
1157
		return $query;
1158
	}
1159
1160 View Code Duplication
	if (isset($_GET['var_profile'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1161
		include_spip('public/tracer');
1162
		$t = trace_query_start();
1163
		$e = '';
1164
	} else {
1165
		$t = 0;
1166
	}
1167
1168
	$connexion['last'] = $query;
1169
	#spip_log($query, 'mysql.'._LOG_DEBUG);
1170
	$r = false;
1171
	if (mysqli_query($link, $query)) {
1172
		$r = mysqli_insert_id($link);
1173
	} else {
1174
		// Log de l'erreur eventuelle
1175
		if ($e = spip_mysql_errno($serveur)) {
1176
			$e .= spip_mysql_error($query, $serveur);
1177
		} // et du fautif
1178
	}
1179
1180
	return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
0 ignored issues
show
Bug introduced by
The variable $e does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1181
1182
	// return $r ? $r : (($r===0) ? -1 : 0); pb avec le multi-base.
1183
}
1184
1185
/**
1186
 * Insère une ligne dans une table, en protégeant chaque valeur
1187
 *
1188
 * @param string $table
1189
 *     Nom de la table SQL
1190
 * @param string $couples
0 ignored issues
show
Documentation introduced by
Should the type for parameter $couples not be string|array? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1191
 *    Couples (colonne => valeur)
1192
 * @param array $desc
1193
 *     Tableau de description des colonnes de la table SQL utilisée
1194
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1195
 * @param string $serveur
1196
 *     Nom du connecteur
1197
 * @param bool $requeter
1198
 *     Exécuter la requête, sinon la retourner
1199
 * @return bool|string|int|array
1200
 *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
1201
 *     - Texte de la requête si demandé,
1202
 *     - False en cas d'erreur,
1203
 *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
1204
 **/
1205
function spip_mysql_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true) {
1206
1207
	if (!$desc) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $desc of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1208
		$desc = description_table($table, $serveur);
1209
	}
1210
	if (!$desc) {
1211
		$couples = array();
1212
	}
1213
	$fields = isset($desc['field']) ? $desc['field'] : array();
1214
1215
	foreach ($couples as $champ => $val) {
0 ignored issues
show
Bug introduced by
The expression $couples of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1216
		$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
1217
	}
1218
1219
	return spip_mysql_insert($table, "(" . join(',', array_keys($couples)) . ")", "(" . join(',', $couples) . ")", $desc,
0 ignored issues
show
Bug introduced by
It seems like $desc defined by description_table($table, $serveur) on line 1208 can also be of type boolean; however, spip_mysql_insert() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1220
		$serveur, $requeter);
1221
}
1222
1223
1224
/**
1225
 * Insère plusieurs lignes d'un coup dans une table
1226
 *
1227
 * @param string $table
1228
 *     Nom de la table SQL
1229
 * @param array $tab_couples
1230
 *     Tableau de tableaux associatifs (colonne => valeur)
1231
 * @param array $desc
1232
 *     Tableau de description des colonnes de la table SQL utilisée
1233
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1234
 * @param string $serveur
1235
 *     Nom du connecteur
1236
 * @param bool $requeter
1237
 *     Exécuter la requête, sinon la retourner
1238
 * @return int|bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string|integer|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1239
 *     - int|true identifiant du dernier élément inséré (si possible), ou true, si réussite
1240
 *     - Texte de la requête si demandé,
1241
 *     - False en cas d'erreur.
1242
 **/
1243
function spip_mysql_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true) {
1244
1245
	if (!$desc) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $desc of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1246
		$desc = description_table($table, $serveur);
1247
	}
1248
	if (!$desc) {
1249
		$tab_couples = array();
1250
	}
1251
	$fields = isset($desc['field']) ? $desc['field'] : array();
1252
1253
	$cles = "(" . join(',', array_keys(reset($tab_couples))) . ')';
1254
	$valeurs = array();
1255
	$r = false;
1256
1257
	// Quoter et Inserer par groupes de 100 max pour eviter un debordement de pile
1258
	foreach ($tab_couples as $couples) {
1259
		foreach ($couples as $champ => $val) {
1260
			$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
1261
		}
1262
		$valeurs[] = '(' . join(',', $couples) . ')';
1263 View Code Duplication
		if (count($valeurs) >= 100) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1264
			$r = spip_mysql_insert($table, $cles, join(', ', $valeurs), $desc, $serveur, $requeter);
0 ignored issues
show
Bug introduced by
It seems like $desc defined by description_table($table, $serveur) on line 1246 can also be of type boolean; however, spip_mysql_insert() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1265
			$valeurs = array();
1266
		}
1267
	}
1268 View Code Duplication
	if (count($valeurs)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1269
		$r = spip_mysql_insert($table, $cles, join(', ', $valeurs), $desc, $serveur, $requeter);
0 ignored issues
show
Bug introduced by
It seems like $desc defined by description_table($table, $serveur) on line 1246 can also be of type boolean; however, spip_mysql_insert() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1270
	}
1271
1272
	return $r; // dans le cas d'une table auto_increment, le dernier insert_id
1273
}
1274
1275
/**
1276
 * Met à jour des enregistrements d'une table SQL
1277
 *
1278
 * @param string $table
1279
 *     Nom de la table
1280
 * @param array $champs
1281
 *     Couples (colonne => valeur)
1282
 * @param string|array $where
1283
 *     Conditions a remplir (Where)
1284
 * @param array $desc
1285
 *     Tableau de description des colonnes de la table SQL utilisée
1286
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1287
 * @param string $serveur
1288
 *     Nom de la connexion
1289
 * @param bool $requeter
1290
 *     Exécuter la requête, sinon la retourner
1291
 * @return array|bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1292
 *     - string : texte de la requête si demandé
1293
 *     - true si la requête a réussie, false sinon
1294
 *     - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
1295
 */
1296
function spip_mysql_update($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $desc is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1297
	$set = array();
1298
	foreach ($champs as $champ => $val) {
1299
		$set[] = $champ . "=$val";
1300
	}
1301 View Code Duplication
	if (!empty($set)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1302
		return spip_mysql_query(
1303
			calculer_mysql_expression('UPDATE', $table, ',')
1304
			. calculer_mysql_expression('SET', $set, ',')
1305
			. calculer_mysql_expression('WHERE', $where),
1306
			$serveur, $requeter);
1307
	}
1308
}
1309
1310
/**
1311
 * Met à jour des enregistrements d'une table SQL et protège chaque valeur
1312
 *
1313
 * Protège chaque valeur transmise avec sql_quote(), adapté au type
1314
 * de champ attendu par la table SQL
1315
 *
1316
 * @note
1317
 *   Les valeurs sont des constantes à mettre entre apostrophes
1318
 *   sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
1319
 *
1320
 * @param string $table
1321
 *     Nom de la table
1322
 * @param array $champs
1323
 *     Couples (colonne => valeur)
1324
 * @param string|array $where
1325
 *     Conditions a remplir (Where)
1326
 * @param array $desc
1327
 *     Tableau de description des colonnes de la table SQL utilisée
1328
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1329
 * @param string $serveur
1330
 *     Nom de la connexion
1331
 * @param bool $requeter
1332
 *     Exécuter la requête, sinon la retourner
1333
 * @return array|bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be null|array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1334
 *     - string : texte de la requête si demandé
1335
 *     - true si la requête a réussie, false sinon
1336
 *     - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
1337
 */
1338
function spip_mysql_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true) {
1339
1340
	if (!$champs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $champs of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1341
		return;
1342
	}
1343
	if (!$desc) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $desc of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1344
		$desc = description_table($table, $serveur);
1345
	}
1346
	if (!$desc) {
1347
		$champs = array();
1348
	} else {
1349
		$fields = $desc['field'];
1350
	}
1351
	$set = array();
1352
	foreach ($champs as $champ => $val) {
1353
		$set[] = $champ . '=' . spip_mysql_cite($val, @$fields[$champ]);
0 ignored issues
show
Bug introduced by
The variable $fields does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1354
	}
1355
1356
	return spip_mysql_query(
1357
		calculer_mysql_expression('UPDATE', $table, ',')
1358
		. calculer_mysql_expression('SET', $set, ',')
1359
		. calculer_mysql_expression('WHERE', $where),
1360
		$serveur, $requeter);
1361
}
1362
1363
/**
1364
 * Supprime des enregistrements d'une table
1365
 *
1366
 * @param string $table Nom de la table SQL
1367
 * @param string|array $where Conditions à vérifier
1368
 * @param string $serveur Nom du connecteur
1369
 * @param bool $requeter Exécuter la requête, sinon la retourner
1370
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean|integer? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1371
 *     - int : nombre de suppressions réalisées,
1372
 *     - Texte de la requête si demandé,
1373
 *     - False en cas d'erreur.
1374
 **/
1375
function spip_mysql_delete($table, $where = '', $serveur = '', $requeter = true) {
1376
	$res = spip_mysql_query(
1377
		calculer_mysql_expression('DELETE FROM', $table, ',')
1378
		. calculer_mysql_expression('WHERE', $where),
1379
		$serveur, $requeter);
1380
	if (!$requeter) {
1381
		return $res;
1382
	}
1383
	if ($res) {
1384
		$link = _mysql_link($serveur);
1385
1386
		return mysqli_affected_rows($link);
1387
	} else {
1388
		return false;
1389
	}
1390
}
1391
1392
1393
/**
1394
 * Insère où met à jour une entrée d’une table SQL
1395
 *
1396
 * La clé ou les cles primaires doivent être présentes dans les données insérés.
1397
 * La fonction effectue une protection automatique des données.
1398
 *
1399
 * Préférez updateq ou insertq.
1400
 *
1401
 * @param string $table
1402
 *     Nom de la table SQL
1403
 * @param array $couples
1404
 *     Couples colonne / valeur à modifier,
1405
 * @param array $desc
1406
 *     Tableau de description des colonnes de la table SQL utilisée
1407
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1408
 * @param string $serveur
1409
 *     Nom du connecteur
1410
 * @param bool $requeter
1411
 *     Exécuter la requête, sinon la retourner
1412
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1413
 *     - true si réussite
1414
 *     - Texte de la requête si demandé,
1415
 *     - False en cas d'erreur.
1416
 **/
1417
function spip_mysql_replace($table, $couples, $desc = array(), $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $desc is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1418
	return spip_mysql_query("REPLACE $table (" . join(',', array_keys($couples)) . ') VALUES (' . join(',',
1419
			array_map('_q', $couples)) . ')', $serveur, $requeter);
1420
}
1421
1422
1423
/**
1424
 * Insère où met à jour des entrées d’une table SQL
1425
 *
1426
 * La clé ou les cles primaires doivent être présentes dans les données insérés.
1427
 * La fonction effectue une protection automatique des données.
1428
 *
1429
 * Préférez insertq_multi et sql_updateq
1430
 *
1431
 * @param string $table
1432
 *     Nom de la table SQL
1433
 * @param array $tab_couples
1434
 *     Tableau de tableau (colonne / valeur à modifier),
1435
 * @param array $desc
1436
 *     Tableau de description des colonnes de la table SQL utilisée
1437
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1438
 * @param string $serveur
1439
 *     Nom du connecteur
1440
 * @param bool $requeter
1441
 *     Exécuter la requête, sinon la retourner
1442
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|resource|string|boolean? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1443
 *     - true si réussite
1444
 *     - Texte de la requête si demandé,
1445
 *     - False en cas d'erreur.
1446
 **/
1447
function spip_mysql_replace_multi($table, $tab_couples, $desc = array(), $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $desc is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1448
	$cles = "(" . join(',', array_keys($tab_couples[0])) . ')';
1449
	$valeurs = array();
1450
	foreach ($tab_couples as $couples) {
1451
		$valeurs[] = '(' . join(',', array_map('_q', $couples)) . ')';
1452
	}
1453
	$valeurs = implode(', ', $valeurs);
1454
1455
	return spip_mysql_query("REPLACE $table $cles VALUES $valeurs", $serveur, $requeter);
1456
}
1457
1458
1459
/**
1460
 * Retourne l'instruction SQL pour obtenir le texte d'un champ contenant
1461
 * une balise `<multi>` dans la langue indiquée
1462
 *
1463
 * Cette sélection est mise dans l'alias `multi` (instruction AS multi).
1464
 *
1465
 * @param string $objet Colonne ayant le texte
1466
 * @param string $lang Langue à extraire
1467
 * @return string       Texte de sélection pour la requête
1468
 */
1469
function spip_mysql_multi($objet, $lang) {
1470
	$lengthlang = strlen("[$lang]");
1471
	$posmulti = "INSTR(" . $objet . ", '<multi>')";
1472
	$posfinmulti = "INSTR(" . $objet . ", '</multi>')";
1473
	$debutchaine = "LEFT(" . $objet . ", $posmulti-1)";
1474
	$finchaine = "RIGHT(" . $objet . ", CHAR_LENGTH(" . $objet . ") -(7+$posfinmulti))";
1475
	$chainemulti = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7, $posfinmulti -(7+$posmulti)))";
1476
	$poslang = "INSTR($chainemulti,'[" . $lang . "]')";
1477
	$poslang = "IF($poslang=0,INSTR($chainemulti,']')+1,$poslang+$lengthlang)";
1478
	$chainelang = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7+$poslang-1,$posfinmulti -($posmulti+7+$poslang-1) ))";
1479
	$posfinlang = "INSTR(" . $chainelang . ", '[')";
1480
	$chainelang = "IF($posfinlang>0,LEFT($chainelang,$posfinlang-1),$chainelang)";
1481
	//$chainelang = "LEFT($chainelang,$posfinlang-1)";
1482
	$retour = "(TRIM(IF($posmulti = 0 , " .
1483
		"     TRIM(" . $objet . "), " .
1484
		"     CONCAT( " .
1485
		"          $debutchaine, " .
1486
		"          IF( " .
1487
		"               $poslang = 0, " .
1488
		"                     $chainemulti, " .
1489
		"               $chainelang" .
1490
		"          ), " .
1491
		"          $finchaine" .
1492
		"     ) " .
1493
		"))) AS multi";
1494
1495
	return $retour;
1496
}
1497
1498
/**
1499
 * Prépare une chaîne hexadécimale
1500
 *
1501
 * Par exemple : FF ==> 0xFF en MySQL
1502
 *
1503
 * @param string $v
1504
 *     Chaine hexadecimale
1505
 * @return string
1506
 *     Valeur hexadécimale pour MySQL
1507
 **/
1508
function spip_mysql_hex($v) {
1509
	return "0x" . $v;
1510
}
1511
1512
/**
1513
 * Échapper une valeur selon son type ou au mieux
1514
 * comme le fait `_q()` mais pour MySQL avec ses spécificités
1515
 *
1516
 * @param string|array|number $v
1517
 *     Texte, nombre ou tableau à échapper
1518
 * @param string $type
1519
 *     Description du type attendu
1520
 *    (par exemple description SQL de la colonne recevant la donnée)
1521
 * @return string|number
1522
 *    Donnée prête à être utilisée par le gestionnaire SQL
1523
 */
1524 View Code Duplication
function spip_mysql_quote($v, $type = '') {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1525
	if (!is_array($v)) {
1526
		return spip_mysql_cite($v, $type);
1527
	}
1528
1529
	// si c'est un tableau, le parcourir en propageant le type
1530
	foreach ($v as $k => $r) {
1531
		$v[$k] = spip_mysql_quote($r, $type);
1532
	}
1533
	return implode(',', $v);
1534
}
1535
1536
/**
1537
 * Tester si une date est proche de la valeur d'un champ
1538
 *
1539
 * @param string $champ
1540
 *     Nom du champ a tester
1541
 * @param int $interval
1542
 *     Valeur de l'intervalle : -1, 4, ...
1543
 * @param string $unite
1544
 *     Utité utilisée (DAY, MONTH, YEAR, ...)
1545
 * @return string
1546
 *     Expression SQL
1547
 **/
1548
function spip_mysql_date_proche($champ, $interval, $unite) {
1549
	$use_now = ( ($champ === 'maj' or strpos($champ, '.maj')) ? true : false );
1550
	return '('
1551
	. $champ
1552
	. (($interval <= 0) ? '>' : '<')
1553
	. (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
1554
	. '('
1555
	. ($use_now ? 'NOW()' : sql_quote(date('Y-m-d H:i:s')))
1556
	. ', INTERVAL '
1557
	. (($interval > 0) ? $interval : (0 - $interval))
1558
	. ' '
1559
	. $unite
1560
	. '))';
1561
}
1562
1563
1564
/**
1565
 * Retourne une expression IN pour le gestionnaire de base de données
1566
 *
1567
 * IN (...) est limité à 255 éléments, d'où cette fonction assistante
1568
 *
1569
 * @param string $val
1570
 *     Colonne SQL sur laquelle appliquer le test
1571
 * @param string $valeurs
1572
 *     Liste des valeurs possibles (séparés par des virgules)
1573
 * @param string $not
1574
 *     - '' sélectionne les éléments correspondant aux valeurs
1575
 *     - 'NOT' inverse en sélectionnant les éléments ne correspondant pas aux valeurs
1576
 * @param string $serveur
1577
 *     Nom du connecteur
1578
 * @param bool $requeter
1579
 *     Inutilisé
1580
 * @return string
1581
 *     Expression de requête SQL
1582
 **/
1583
function spip_mysql_in($val, $valeurs, $not = '', $serveur = '', $requeter = true) {
0 ignored issues
show
Unused Code introduced by
The parameter $requeter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $serveur is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1584
	return "($val $not IN ($valeurs))";
1585
}
1586
1587
1588
/**
1589
 * Renvoie les bons echappements (mais pas sur les fonctions comme NOW())
1590
 *
1591
 * @param string|number $v Texte ou nombre à échapper
1592
 * @param string $type Type de donnée attendue, description SQL de la colonne de destination
1593
 * @return string|number     Texte ou nombre échappé
1594
 */
1595
function spip_mysql_cite($v, $type) {
1596
	if (!$type) {
1597 View Code Duplication
		if (is_bool($v)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1598
			return strval(intval($v));
1599
		}
1600
		elseif (is_numeric($v)) {
1601
			return strval($v);
1602
		}
1603
		return "'" . addslashes($v) . "'";
1604
	}
1605
1606
	if (is_null($v)
1607
		and stripos($type, "NOT NULL") === false
1608
	) {
1609
		return 'NULL';
1610
	} // null php se traduit en NULL SQL
1611
	if (sql_test_date($type) and preg_match('/^\w+\(/', $v)) {
1612
		return $v;
1613
	}
1614
	if (sql_test_int($type)) {
1615
		if (is_numeric($v) or (ctype_xdigit(substr($v, 2))
1616
				and $v[0] == '0' and $v[1] == 'x')
1617
		) {
1618
			return $v;
1619
		} // si pas numerique, forcer le intval
1620
		else {
1621
			return intval($v);
1622
		}
1623
	}
1624
1625
	return ("'" . addslashes($v) . "'");
1626
}
1627
1628
/**
1629
 * Teste si on a les fonctions MySQLi (pour l'install)
1630
 *
1631
 * @return bool
1632
 *     True si on a les fonctions, false sinon
1633
 */
1634
function spip_versions_mysql() {
1635
	return function_exists('mysqli_query');
1636
}
1637
1638
1639
/**
1640
 * Tester si mysql ne veut pas du nom de la base dans les requêtes
1641
 *
1642
 * @param string $server_db
1643
 * @return string
1644
 *     - chaîne vide si nom de la base utile
1645
 *     - chaîne : code compilé pour le faire désactiver par SPIP sinon
1646
 */
1647
function test_rappel_nom_base_mysql($server_db) {
1648
	$GLOBALS['mysql_rappel_nom_base'] = true;
1649
	sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1650
	$ok = spip_query("INSERT INTO spip_meta (nom,valeur) VALUES ('mysql_rappel_nom_base', 'test')", $server_db);
0 ignored issues
show
Deprecated Code introduced by
The function spip_query() has been deprecated with message: 3.1 Pour compatibilité.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1651
1652
	if ($ok) {
1653
		sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1654
1655
		return '';
1656
	} else {
1657
		$GLOBALS['mysql_rappel_nom_base'] = false;
1658
1659
		return "\$GLOBALS['mysql_rappel_nom_base'] = false; " .
1660
		"/* echec de test_rappel_nom_base_mysql a l'installation. */\n";
1661
	}
1662
}
1663
1664
/**
1665
 * Teste si on peut changer les modes de MySQL
1666
 *
1667
 * @link http://dev.mysql.com/doc/refman/5.0/fr/server-sql-mode.html
1668
 *
1669
 * @param string $server_db Nom de la connexion
1670
 * @return string
1671
 *     - chaîne vide si on ne peut pas appliquer de mode
1672
 *     - chaîne : code compilé pour l'indiquer le résultat du test à SPIP
1673
 */
1674
function test_sql_mode_mysql($server_db) {
1675
	$res = sql_select("version() as v", '', '', '', '', '', '', $server_db);
1676
	$row = sql_fetch($res, $server_db);
1677
	if (version_compare($row['v'], '5.0.0', '>=')) {
1678
		defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE', true);
1679
1680
		return "defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE',true);\n";
1681
	}
1682
1683
	return '';
1684
}
1685