Completed
Push — master ( 33e96a...d9c770 )
by cam
06:21
created

mysql.php ➔ calcul_mysql_in()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 9
nop 3
dl 0
loc 12
rs 9.5555
c 0
b 0
f 0
1
<?php
2
3
/* *************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2019                                                *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10
 *  Pour plus de details 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
/**
192
 * Exécute une requête Mysql (obsolète, ne plus utiliser)
193
 *
194
 * @deprecated Utiliser sql_query() ou autres
195
 *
196
 * @param string $query Requête
197
 * @param string $serveur Nom de la connexion
198
 * @param bool $requeter Exécuter la requête, sinon la retourner
199
 * @return Resource        Ressource pour fetch()
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...
200
 **/
201
function spip_query_db($query, $serveur = '', $requeter = true) {
202
	return spip_mysql_query($query, $serveur, $requeter);
203
}
204
205
206
/**
207
 * Exécute une requête MySQL, munie d'une trace à la demande
208
 *
209
 * @param string $query Requête
210
 * @param string $serveur Nom de la connexion
211
 * @param bool $requeter Exécuter la requête, sinon la retourner
212
 * @return array|resource|string|bool
213
 *     - string : Texte de la requête si on ne l'exécute pas
214
 *     - ressource|bool : Si requête exécutée
215
 *     - array : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
216
 */
217
function spip_mysql_query($query, $serveur = '', $requeter = true) {
218
219
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
220
	$prefixe = $connexion['prefixe'];
221
	$link = $connexion['link'];
222
	$db = $connexion['db'];
223
224
	$query = _mysql_traite_query($query, $db, $prefixe);
225
226
	// renvoyer la requete inerte si demandee
227
	if (!$requeter) {
228
		return $query;
229
	}
230
231 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...
232
		include_spip('public/tracer');
233
		$t = trace_query_start();
234
	} else {
235
		$t = 0;
236
	}
237
238
	$connexion['last'] = $query;
239
	$connexion['total_requetes']++;
240
241
	// ajouter un debug utile dans log/mysql-slow.log ?
242
	$debug = '';
243
	if (defined('_DEBUG_SLOW_QUERIES') and _DEBUG_SLOW_QUERIES) {
244
		if (isset($GLOBALS['debug']['aucasou'])) {
245
			list(, $id, , $infos) = $GLOBALS['debug']['aucasou'];
246
			$debug .= "BOUCLE$id @ " . (isset($infos[0]) ? $infos[0] : '') . " | ";
247
		}
248
		$debug .= $_SERVER['REQUEST_URI'] . ' + ' . $GLOBALS['ip'];
249
		$debug = ' /* ' . mysqli_real_escape_string($link, str_replace('*/', '@/', $debug)) . ' */';
250
	}
251
252
	$r = mysqli_query($link, $query . $debug);
253
254
	//Eviter de propager le GoneAway sur les autres requetes d'un même processus PHP
255
	if ($e = spip_mysql_errno($serveur)) {  // Log d'un Gone Away
256
		if ($e == 2006) { //Si Gone Away on relance une connexion vierge
257
			//Fermer la connexion defaillante
258
			mysqli_close($connexion['link']);
259
			unset($GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]);
260
			//Relancer une connexion vierge
261
			spip_connect($serveur);
262
			$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
263
			$link = $connexion['link'];
264
			//On retente au cas où
265
			$r = mysqli_query($link, $query . $debug);
266
		}
267
	}
268
269
	// Log de l'erreur eventuelle
270
	if ($e = spip_mysql_errno($serveur)) {
271
		$e .= spip_mysql_error($query, $serveur);
272
	} // et du fautif
273
	return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
274
}
275
276
/**
277
 * Modifie une structure de table MySQL
278
 *
279
 * @param string $query Requête SQL (sans 'ALTER ')
280
 * @param string $serveur Nom de la connexion
281
 * @param bool $requeter Exécuter la requête, sinon la retourner
282
 * @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...
283
 *     - string : Texte de la requête si on ne l'exécute pas
284
 *     - bool   : Si requête exécutée
285
 *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
286
 */
287
function spip_mysql_alter($query, $serveur = '', $requeter = true) {
288
	// ici on supprime les ` entourant le nom de table pour permettre
289
	// la transposition du prefixe, compte tenu que les plugins ont la mauvaise habitude
290
	// d'utiliser ceux-ci, copie-colle de phpmyadmin
291
	$query = preg_replace(",^TABLE\s*`([^`]*)`,i", "TABLE \\1", $query);
292
293
	return spip_mysql_query("ALTER " . $query, $serveur, $requeter); # i.e. que PG se debrouille
294
}
295
296
297
/**
298
 * Optimise une table MySQL
299
 *
300
 * @param string $table Nom de la table
301
 * @param string $serveur Nom de la connexion
302
 * @param bool $requeter inutilisé
303
 * @return bool            Toujours true
304
 */
305
function spip_mysql_optimize($table, $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...
306
	spip_mysql_query("OPTIMIZE TABLE " . $table);
307
308
	return true;
309
}
310
311
312
/**
313
 * Retourne une explication de requête (Explain) MySQL
314
 *
315
 * @param string $query Texte de la requête
316
 * @param string $serveur Nom de la connexion
317
 * @param bool $requeter inutilisé
318
 * @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...
319
 */
320
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...
321
	if (strpos(ltrim($query), 'SELECT') !== 0) {
322
		return array();
323
	}
324
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
325
	$prefixe = $connexion['prefixe'];
326
	$link = $connexion['link'];
327
	$db = $connexion['db'];
328
329
	$query = 'EXPLAIN ' . _mysql_traite_query($query, $db, $prefixe);
330
	$r = mysqli_query($link, $query);
331
332
	return spip_mysql_fetch($r, null, $serveur);
333
}
334
335
336
/**
337
 * Exécute une requête de sélection avec MySQL
338
 *
339
 * Instance de sql_select (voir ses specs).
340
 *
341
 * @see sql_select()
342
 * @note
343
 *     Les `\n` et `\t` sont utiles au debusqueur.
344
 *
345
 * @param string|array $select Champs sélectionnés
346
 * @param string|array $from Tables sélectionnées
347
 * @param string|array $where Contraintes
348
 * @param string|array $groupby Regroupements
349
 * @param string|array $orderby Tris
350
 * @param string $limit Limites de résultats
351
 * @param string|array $having Contraintes posts sélections
352
 * @param string $serveur Nom de la connexion
353
 * @param bool $requeter Exécuter la requête, sinon la retourner
354
 * @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...
355
 *     - string : Texte de la requête si on ne l'exécute pas
356
 *     - ressource si requête exécutée, ressource pour fetch()
357
 *     - false si la requête exécutée a ratée
358
 *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
359
 */
360
function spip_mysql_select(
361
	$select,
362
	$from,
363
	$where = '',
364
	$groupby = '',
365
	$orderby = '',
366
	$limit = '',
367
	$having = '',
368
	$serveur = '',
369
	$requeter = true
370
) {
371
372
373
	$from = (!is_array($from) ? $from : spip_mysql_select_as($from));
374
	$query =
375
		calculer_mysql_expression('SELECT', $select, ', ')
376
		. calculer_mysql_expression('FROM', $from, ', ')
377
		. calculer_mysql_expression('WHERE', $where)
378
		. calculer_mysql_expression('GROUP BY', $groupby, ',')
379
		. calculer_mysql_expression('HAVING', $having)
380
		. ($orderby ? ("\nORDER BY " . spip_mysql_order($orderby)) : '')
381
		. ($limit ? "\nLIMIT $limit" : '');
382
383
	// renvoyer la requete inerte si demandee
384
	if ($requeter === false) {
385
		return $query;
386
	}
387
	$r = spip_mysql_query($query, $serveur, $requeter);
388
389
	return $r ? $r : $query;
390
}
391
392
393
/**
394
 * Prépare une clause order by
395
 *
396
 * Regroupe en texte les éléments si un tableau est donné
397
 *
398
 * @note
399
 *   0+x avec un champ x commencant par des chiffres est converti par MySQL
400
 *   en le nombre qui commence x. Pas portable malheureusement, on laisse pour le moment.
401
 *
402
 * @param string|array $orderby Texte du orderby à préparer
403
 * @return string Texte du orderby préparé
404
 */
405
function spip_mysql_order($orderby) {
406
	return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
407
}
408
409
410
/**
411
 * Prépare une clause WHERE pour MySQL
412
 *
413
 * Retourne une chaîne avec les bonnes parenthèses pour la
414
 * contrainte indiquée, au format donnée par le compilateur
415
 *
416
 * @param array|string $v
417
 *     Description des contraintes
418
 *     - string : Texte du where
419
 *     - sinon tableau : A et B peuvent être de type string ou array,
420
 *       OP et C sont de type string :
421
 *       - array(A) : A est le texte du where
422
 *       - array(OP, A) : contrainte OP( A )
423
 *       - array(OP, A, B) : contrainte (A OP B)
424
 *       - array(OP, A, B, C) : contrainte (A OP (B) : C)
425
 * @return string
426
 *     Contrainte pour clause WHERE
427
 */
428 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...
429
	if (!is_array($v)) {
430
		return $v;
431
	}
432
433
	$op = array_shift($v);
434
	if (!($n = count($v))) {
435
		return $op;
436
	} else {
437
		$arg = calculer_mysql_where(array_shift($v));
438
		if ($n == 1) {
439
			return "$op($arg)";
440
		} else {
441
			$arg2 = calculer_mysql_where(array_shift($v));
442
			if ($n == 2) {
443
				return "($arg $op $arg2)";
444
			} else {
445
				return "($arg $op ($arg2) : $v[0])";
446
			}
447
		}
448
	}
449
}
450
451
/**
452
 * Calcule un expression pour une requête, en cumulant chaque élément
453
 * avec l'opérateur de liaison ($join) indiqué
454
 *
455
 * Renvoie grosso modo "$expression join($join, $v)"
456
 *
457
 * @param string $expression Mot clé de l'expression, tel que "WHERE" ou "ORDER BY"
458
 * @param array|string $v Données de l'expression
459
 * @param string $join Si les données sont un tableau, elles seront groupées par cette jointure
460
 * @return string            Texte de l'expression, une partie donc, du texte la requête.
461
 */
462 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...
463
	if (empty($v)) {
464
		return '';
465
	}
466
467
	$exp = "\n$expression ";
468
469
	if (!is_array($v)) {
470
		return $exp . $v;
471
	} else {
472
		if (strtoupper($join) === 'AND') {
473
			return $exp . join("\n\t$join ", array_map('calculer_mysql_where', $v));
474
		} else {
475
			return $exp . join($join, $v);
476
		}
477
	}
478
}
479
480
481
/**
482
 * Renvoie des `nom AS alias`
483
 *
484
 * @param array $args
485
 * @return string Sélection de colonnes pour une clause SELECT
486
 */
487 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...
488
	$res = '';
489
	foreach ($args as $k => $v) {
490
		if (substr($k, -1) == '@') {
491
			// c'est une jointure qui se refere au from precedent
492
			// pas de virgule
493
			$res .= '  ' . $v;
494
		} else {
495
			if (!is_numeric($k)) {
496
				$p = strpos($v, " ");
497
				if ($p) {
498
					$v = substr($v, 0, $p) . " AS `$k`" . substr($v, $p);
499
				} else {
500
					$v .= " AS `$k`";
501
				}
502
			}
503
			$res .= ', ' . $v;
504
		}
505
	}
506
507
	return substr($res, 2);
508
}
509
510
511
/**
512
 * Changer les noms des tables ($table_prefix)
513
 *
514
 * TODO: Quand tous les appels SQL seront abstraits on pourra l'améliorer
515
 */
516
define('_SQL_PREFIXE_TABLE_MYSQL', '/([,\s])spip_/S');
517
518
519
/**
520
 * Prépare le texte d'une requête avant son exécution
521
 *
522
 * Change les préfixes de tables SPIP par ceux véritables
523
 *
524
 * @param string $query Requête à préparer
525
 * @param string $db Nom de la base de donnée
526
 * @param string $prefixe Préfixe de tables à appliquer
527
 * @return string           Requête préparée
528
 */
529
function _mysql_traite_query($query, $db = '', $prefixe = '') {
530
531
	if ($GLOBALS['mysql_rappel_nom_base'] and $db) {
532
		$pref = '`' . $db . '`.';
533
	} else {
534
		$pref = '';
535
	}
536
537
	if ($prefixe) {
538
		$pref .= $prefixe . "_";
539
	}
540
541
	if (!preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/i', $query, $regs)) {
542
		$suite = '';
543
	} else {
544
		$suite = strstr($query, $regs[0]);
545
		$query = substr($query, 0, -strlen($suite));
546
		// propager le prefixe en cas de requete imbriquee
547
		// il faut alors echapper les chaine avant de le faire, pour ne pas risquer de
548
		// modifier une requete qui est en fait juste du texte dans un champ
549
		if (stripos($suite, "SELECT") !== false) {
550
			list($suite, $textes) = query_echappe_textes($suite);
551
			if (preg_match('/^(.*?)([(]\s*SELECT\b.*)$/si', $suite, $r)) {
552
				$suite = $r[1] . _mysql_traite_query($r[2], $db, $prefixe);
553
			}
554
			$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...
555
		}
556
	}
557
	$r = preg_replace(_SQL_PREFIXE_TABLE_MYSQL, '\1' . $pref, $query) . $suite;
558
559
	// en option, remplacer les emoji (que mysql ne sait pas gérer) en &#128169;
560
	if (defined('_MYSQL_NOPLANES') and _MYSQL_NOPLANES and lire_meta('charset_sql_connexion') == 'utf8') {
0 ignored issues
show
Deprecated Code introduced by
The function lire_meta() has been deprecated with message: Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`

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...
561
		include_spip('inc/charsets');
562
		$r = utf8_noplanes($r);
563
	}
564
565
	#spip_log("_mysql_traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG);
566
	return $r;
567
}
568
569
/**
570
 * Sélectionne une base de données
571
 *
572
 * @param string $db
573
 *     Nom de la base à utiliser
574
 * @param string $serveur
575
 *     Nom du connecteur
576
 * @param bool $requeter
577
 *     Inutilisé
578
 *
579
 * @return bool
580
 *     - True cas de succès.
581
 *     - False en cas d'erreur.
582
 **/
583
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...
584
	$link = _mysql_link($serveur);
585
	$ok = mysqli_select_db($link, $db);
586
	if (!$ok) {
587
		spip_log('Echec mysqli_selectdb. Erreur : ' . mysqli_error($link), 'mysql.' . _LOG_CRITIQUE);
588
	}
589
590
	return $ok;
591
}
592
593
594
/**
595
 * Retourne les bases de données accessibles
596
 *
597
 * Retourne un tableau du nom de toutes les bases de données
598
 * accessibles avec les permissions de l'utilisateur SQL
599
 * de cette connexion.
600
 *
601
 * Attention on n'a pas toujours les droits !
602
 *
603
 * @param string $serveur
604
 *     Nom du connecteur
605
 * @param bool $requeter
606
 *     Inutilisé
607
 * @return array
608
 *     Liste de noms de bases de données
609
 **/
610
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...
611
	$dbs = array();
612
	if ($res = spip_mysql_query("SHOW DATABASES", $serveur)) {
613
		while ($row = mysqli_fetch_assoc($res)) {
614
			$dbs[] = $row['Database'];
615
		}
616
	}
617
618
	return $dbs;
619
}
620
621
622
/**
623
 * Crée une table SQL
624
 *
625
 * Crée une table SQL nommee `$nom` à partir des 2 tableaux `$champs` et `$cles`
626
 *
627
 * @note Le nom des caches doit être inferieur à 64 caractères
628
 *
629
 * @param string $nom Nom de la table SQL
630
 * @param array $champs Couples (champ => description SQL)
631
 * @param array $cles Couples (type de clé => champ(s) de la clé)
632
 * @param bool $autoinc True pour ajouter un auto-incrément sur la Primary Key
633
 * @param bool $temporary True pour créer une table temporaire
634
 * @param string $serveur Nom de la connexion
635
 * @param bool $requeter inutilisé
636
 * @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...
637
 *     - null si champs ou cles n'est pas un tableau
638
 *     - true si la requête réussie, false sinon.
639
 */
640
function spip_mysql_create(
641
	$nom,
642
	$champs,
643
	$cles,
644
	$autoinc = false,
645
	$temporary = false,
646
	$serveur = '',
647
	$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...
648
) {
649
650
	$query = '';
651
	$keys = '';
652
	$s = '';
653
	$p = '';
654
655
	// certains plugins declarent les tables  (permet leur inclusion dans le dump)
656
	// sans les renseigner (laisse le compilo recuperer la description)
657
	if (!is_array($champs) || !is_array($cles)) {
658
		return;
659
	}
660
661
	$res = spip_mysql_query("SELECT version() as v", $serveur);
662
	if (($row = mysqli_fetch_array($res)) && (version_compare($row['v'], '5.0', '>='))) {
663
		spip_mysql_query("SET sql_mode=''", $serveur);
664
	}
665
666
	foreach ($cles as $k => $v) {
667
		$keys .= "$s\n\t\t$k ($v)";
668
		if ($k == "PRIMARY KEY") {
669
			$p = $v;
670
		}
671
		$s = ",";
672
	}
673
	$s = '';
674
675
	$character_set = "";
676 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...
677
		$character_set .= " CHARACTER SET " . $GLOBALS['meta']['charset_sql_base'];
678
	}
679 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...
680
		$character_set .= " COLLATE " . $GLOBALS['meta']['charset_collation_sql_base'];
681
	}
682
683
	foreach ($champs as $k => $v) {
684
		$v = _mysql_remplacements_definitions_table($v);
685 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...
686
			if (preg_match(',(char|text),i', $defs[1])
687
				and !preg_match(',(binary|CHARACTER|COLLATE),i', $v)
688
			) {
689
				$v = $defs[1] . $character_set . ' ' . substr($v, strlen($defs[1]));
690
			}
691
		}
692
693
		$query .= "$s\n\t\t$k $v"
694
			. (($autoinc && ($p == $k) && preg_match(',\b(big|small|medium)?int\b,i', $v))
695
				? " auto_increment"
696
				: ''
697
			);
698
		$s = ",";
699
	}
700
	$temporary = $temporary ? 'TEMPORARY' : '';
701
	$q = "CREATE $temporary TABLE IF NOT EXISTS $nom ($query" . ($keys ? ",$keys" : '') . ")"
702
		. " ENGINE=MyISAM"
703
		. ($character_set ? " DEFAULT $character_set" : "")
704
		. "\n";
705
706
	return spip_mysql_query($q, $serveur);
707
}
708
709
710
/**
711
 * Adapte pour Mysql la déclaration SQL d'une colonne d'une table
712
 *
713
 * @param string|array $query
714
 *     Définition SQL d'un champ de table ou liste de déclarations
715
 * @return string|array
716
 *     Définition SQL adaptée pour MySQL d'un champ de table
717
 */
718
function _mysql_remplacements_definitions_table($query) {
719
	// quelques remplacements
720
	$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...
721
	$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...
722
723
	$remplace = array(
724
		'/VARCHAR(\s*[^\s\(])/is' => 'VARCHAR(255)\\1',
725
		'/^TIMESTAMP($| NULL DEFAULT NULL)/is' => 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
726
	);
727
728
	if (is_string($query)) {
729
		$query = preg_replace(array_keys($remplace), $remplace, $query);
730
	} elseif (is_array($query)) {
731
		$keys = array_keys($remplace);
732
		foreach ($query as $k => $q) {
733
			$query[$k] = preg_replace($keys, $remplace, $q);
734
		}
735
	}
736
737
	return $query;
738
}
739
740
741
/**
742
 * Crée une base de données MySQL
743
 *
744
 * @param string $nom Nom de la base
745
 * @param string $serveur Nom de la connexion
746
 * @param bool $requeter Exécuter la requête, sinon la retourner
747
 * @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...
748
 **/
749
function spip_mysql_create_base($nom, $serveur = '', $requeter = true) {
750
	return spip_mysql_query("CREATE DATABASE `$nom`", $serveur, $requeter);
751
}
752
753
754
/**
755
 * Crée une vue SQL nommée `$nom`
756
 *
757
 * @param string $nom
758
 *    Nom de la vue à creer
759
 * @param string $query_select
760
 *     Texte de la requête de sélection servant de base à la vue
761
 * @param string $serveur
762
 *     Nom du connecteur
763
 * @param bool $requeter
764
 *     Effectuer la requete, sinon la retourner
765
 * @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...
766
 *     - true si la vue est créée
767
 *     - false si erreur ou si la vue existe déja
768
 *     - string texte de la requête si $requeter vaut false
769
 */
770 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...
771
	if (!$query_select) {
772
		return false;
773
	}
774
	// vue deja presente
775
	if (sql_showtable($nom, false, $serveur)) {
776
		spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)", _LOG_ERREUR);
777
778
		return false;
779
	}
780
781
	$query = "CREATE VIEW $nom AS " . $query_select;
782
783
	return spip_mysql_query($query, $serveur, $requeter);
784
}
785
786
787
/**
788
 * Supprime une table SQL
789
 *
790
 * @param string $table Nom de la table SQL
791
 * @param string $exist True pour ajouter un test d'existence avant de supprimer
792
 * @param string $serveur Nom de la connexion
793
 * @param bool $requeter Exécuter la requête, sinon la retourner
794
 * @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...
795
 *     - string Texte de la requête si demandé
796
 *     - true si la requête a réussie, false sinon
797
 */
798
function spip_mysql_drop_table($table, $exist = '', $serveur = '', $requeter = true) {
799
	if ($exist) {
800
		$exist = " IF EXISTS";
801
	}
802
803
	return spip_mysql_query("DROP TABLE$exist $table", $serveur, $requeter);
804
}
805
806
/**
807
 * Supprime une vue SQL
808
 *
809
 * @param string $view Nom de la vue SQL
810
 * @param string $exist True pour ajouter un test d'existence avant de supprimer
811
 * @param string $serveur Nom de la connexion
812
 * @param bool $requeter Exécuter la requête, sinon la retourner
813
 * @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...
814
 *     - string Texte de la requête si demandé
815
 *     - true si la requête a réussie, false sinon
816
 */
817
function spip_mysql_drop_view($view, $exist = '', $serveur = '', $requeter = true) {
818
	if ($exist) {
819
		$exist = " IF EXISTS";
820
	}
821
822
	return spip_mysql_query("DROP VIEW$exist $view", $serveur, $requeter);
823
}
824
825
/**
826
 * Retourne une ressource de la liste des tables de la base de données
827
 *
828
 * @param string $match
829
 *     Filtre sur tables à récupérer
830
 * @param string $serveur
831
 *     Connecteur de la base
832
 * @param bool $requeter
833
 *     true pour éxecuter la requête
834
 *     false pour retourner le texte de la requête.
835
 * @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...
836
 *     Ressource à utiliser avec sql_fetch()
837
 **/
838
function spip_mysql_showbase($match, $serveur = '', $requeter = true) {
839
	return spip_mysql_query("SHOW TABLES LIKE " . _q($match), $serveur, $requeter);
840
}
841
842
/**
843
 * Répare une table SQL
844
 *
845
 * Utilise `REPAIR TABLE ...` de MySQL
846
 *
847
 * @param string $table Nom de la table SQL
848
 * @param string $serveur Nom de la connexion
849
 * @param bool $requeter Exécuter la requête, sinon la retourner
850
 * @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...
851
 *     - string Texte de la requête si demandée,
852
 *     - true si la requête a réussie, false sinon
853
 */
854
function spip_mysql_repair($table, $serveur = '', $requeter = true) {
855
	return spip_mysql_query("REPAIR TABLE `$table`", $serveur, $requeter);
856
}
857
858
859
define('_MYSQL_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
860
/**
861
 * Obtient la description d'une table ou vue MySQL
862
 *
863
 * Récupère la définition d'une table ou d'une vue avec colonnes, indexes, etc.
864
 * au même format que la définition des tables SPIP, c'est à dire
865
 * un tableau avec les clés
866
 *
867
 * - `field` (tableau colonne => description SQL) et
868
 * - `key` (tableau type de clé => colonnes)
869
 *
870
 * @param string $nom_table Nom de la table SQL
871
 * @param string $serveur Nom de la connexion
872
 * @param bool $requeter Exécuter la requête, sinon la retourner
873
 * @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...
874
 *     - chaîne vide si pas de description obtenue
875
 *     - string Texte de la requête si demandé
876
 *     - array description de la table sinon
877
 */
878
function spip_mysql_showtable($nom_table, $serveur = '', $requeter = true) {
879
	$s = spip_mysql_query("SHOW CREATE TABLE `$nom_table`", $serveur, $requeter);
880
	if (!$s) {
881
		return '';
882
	}
883
	if (!$requeter) {
884
		return $s;
885
	}
886
887
	list(, $a) = mysqli_fetch_array($s, MYSQLI_NUM);
888
	if (preg_match(_MYSQL_RE_SHOW_TABLE, $a, $r)) {
889
		$desc = $r[1];
890
		// extraction d'une KEY éventuelle en prenant garde de ne pas
891
		// relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
892 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...
893
			$namedkeys = $r[2];
894
			$desc = $r[1];
895
		} else {
896
			$namedkeys = "";
897
		}
898
899
		$fields = array();
900
		foreach (preg_split("/,\s*`/", $desc) as $v) {
901
			preg_match("/^\s*`?([^`]*)`\s*(.*)/", $v, $r);
902
			$fields[strtolower($r[1])] = $r[2];
903
		}
904
		$keys = array();
905
906 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...
907
			if (preg_match("/^\s*([^(]*)\(([^(]*(\(\d+\))?)$/", $v, $r)) {
908
				$k = str_replace("`", '', trim($r[1]));
909
				$t = strtolower(str_replace("`", '', $r[2]));
910
				if ($k && !isset($keys[$k])) {
911
					$keys[$k] = $t;
912
				} else {
913
					$keys[] = $t;
914
				}
915
			}
916
		}
917
		spip_mysql_free($s);
918
919
		return array('field' => $fields, 'key' => $keys);
920
	}
921
922
	$res = spip_mysql_query("SHOW COLUMNS FROM `$nom_table`", $serveur);
923
	if ($res) {
924
		$nfields = array();
925
		$nkeys = array();
926
		while ($val = spip_mysql_fetch($res)) {
927
			$nfields[$val["Field"]] = $val['Type'];
928
			if ($val['Null'] == 'NO') {
929
				$nfields[$val["Field"]] .= ' NOT NULL';
930
			}
931
			if ($val['Default'] === '0' || $val['Default']) {
932
				if (preg_match('/[A-Z_]/', $val['Default'])) {
933
					$nfields[$val["Field"]] .= ' DEFAULT ' . $val['Default'];
934
				} else {
935
					$nfields[$val["Field"]] .= " DEFAULT '" . $val['Default'] . "'";
936
				}
937
			}
938
			if ($val['Extra']) {
939
				$nfields[$val["Field"]] .= ' ' . $val['Extra'];
940
			}
941
			if ($val['Key'] == 'PRI') {
942
				$nkeys['PRIMARY KEY'] = $val["Field"];
943
			} else {
944
				if ($val['Key'] == 'MUL') {
945
					$nkeys['KEY ' . $val["Field"]] = $val["Field"];
946
				} else {
947
					if ($val['Key'] == 'UNI') {
948
						$nkeys['UNIQUE KEY ' . $val["Field"]] = $val["Field"];
949
					}
950
				}
951
			}
952
		}
953
		spip_mysql_free($res);
954
955
		return array('field' => $nfields, 'key' => $nkeys);
956
	}
957
958
	return "";
959
}
960
961
962
/**
963
 * Rècupère une ligne de résultat
964
 *
965
 * Récupère la ligne suivante d'une ressource de résultat
966
 *
967
 * @param Ressource $r Ressource de résultat (issu de sql_select)
968
 * @param string $t Structure de résultat attendu (défaut MYSQLI_ASSOC)
969
 * @param string $serveur Nom de la connexion
970
 * @param bool $requeter Inutilisé
971
 * @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...
972
 */
973
function spip_mysql_fetch($r, $t = '', $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...
974
	if (!$t) {
975
		$t = MYSQLI_ASSOC;
976
	}
977
	if ($r) {
978
		return mysqli_fetch_array($r, $t);
979
	}
980
}
981
982
/**
983
 * Place le pointeur de résultat sur la position indiquée
984
 *
985
 * @param Ressource $r Ressource de résultat
986
 * @param int $row_number Position. Déplacer le pointeur à cette ligne
987
 * @param string $serveur Nom de la connexion
988
 * @param bool $requeter Inutilisé
989
 * @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...
990
 **/
991
function spip_mysql_seek($r, $row_number, $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...
992
	if ($r and mysqli_num_rows($r)) {
993
		return mysqli_data_seek($r, $row_number);
994
	}
995
}
996
997
998
/**
999
 * Retourne le nombre de lignes d'une sélection
1000
 *
1001
 * @param array|string $from Tables à consulter (From)
1002
 * @param array|string $where Conditions a remplir (Where)
1003
 * @param array|string $groupby Critère de regroupement (Group by)
1004
 * @param array $having Tableau des des post-conditions à remplir (Having)
1005
 * @param string $serveur Nom de la connexion
1006
 * @param bool $requeter Exécuter la requête, sinon la retourner
1007
 * @return int|string
1008
 *     - String Texte de la requête si demandé
1009
 *     - int Nombre de lignes (0 si la requête n'a pas réussie)
1010
 **/
1011
function spip_mysql_countsel(
1012
	$from = array(),
1013
	$where = array(),
1014
	$groupby = '',
1015
	$having = array(),
1016
	$serveur = '',
1017
	$requeter = true
1018
) {
1019
	$c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
1020
1021
	$r = spip_mysql_select("COUNT($c)", $from, $where, '', '', '', $having, $serveur, $requeter);
1022
	if (!$requeter) {
1023
		return $r;
1024
	}
1025
	if (!$r instanceof mysqli_result) {
1026
		return 0;
1027
	}
1028
	list($c) = mysqli_fetch_array($r, MYSQLI_NUM);
1029
	mysqli_free_result($r);
1030
1031
	return $c;
1032
}
1033
1034
1035
/**
1036
 * Retourne la dernière erreur generée
1037
 *
1038
 * @note
1039
 *   Bien spécifier le serveur auquel on s'adresse,
1040
 *   mais à l'install la globale n'est pas encore complètement définie.
1041
 *
1042
 * @uses sql_error_backtrace()
1043
 *
1044
 * @param string $query
1045
 *     Requête qui était exécutée
1046
 * @param string $serveur
1047
 *     Nom de la connexion
1048
 * @param bool $requeter
1049
 *     Inutilisé
1050
 * @return string
1051
 *     Erreur eventuelle
1052
 **/
1053
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...
1054
	$link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
1055
	$s = mysqli_error($link);
1056
	if ($s) {
1057
		$trace = debug_backtrace();
1058
		if ($trace[0]['function'] != "spip_mysql_error") {
1059
			spip_log("$s - $query - " . sql_error_backtrace(), 'mysql.' . _LOG_ERREUR);
1060
		}
1061
	}
1062
1063
	return $s;
1064
}
1065
1066
1067
/**
1068
 * Retourne le numero de la dernière erreur SQL
1069
 *
1070
 * @param string $serveur
1071
 *     Nom de la connexion
1072
 * @param bool $requeter
1073
 *     Inutilisé
1074
 * @return int
1075
 *     0, pas d'erreur. Autre, numéro de l'erreur.
1076
 **/
1077
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...
1078
	$link = $GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
1079
	$s = mysqli_errno($link);
1080
	// 2006 MySQL server has gone away
1081
	// 2013 Lost connection to MySQL server during query
1082
	if (in_array($s, array(2006, 2013))) {
1083
		define('spip_interdire_cache', true);
1084
	}
1085
	if ($s) {
1086
		spip_log("Erreur mysql $s", _LOG_ERREUR);
1087
	}
1088
1089
	return $s;
1090
}
1091
1092
1093
/**
1094
 * Retourne le nombre de lignes d’une ressource de sélection obtenue
1095
 * avec `sql_select()`
1096
 *
1097
 * @param Ressource $r Ressource de résultat
1098
 * @param string $serveur Nom de la connexion
1099
 * @param bool $requeter Inutilisé
1100
 * @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...
1101
 */
1102
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...
1103
	if ($r) {
1104
		return mysqli_num_rows($r);
1105
	}
1106
}
1107
1108
1109
/**
1110
 * Libère une ressource de résultat
1111
 *
1112
 * Indique à MySQL de libérer de sa mémoire la ressoucre de résultat indiquée
1113
 * car on n'a plus besoin de l'utiliser.
1114
 *
1115
 * @param Ressource $r Ressource de résultat
1116
 * @param string $serveur Nom de la connexion
1117
 * @param bool $requeter Inutilisé
1118
 * @return bool              True si réussi
1119
 */
1120
function spip_mysql_free($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...
1121
	return (($r instanceof mysqli_result) ? mysqli_free_result($r) : false);
1122
}
1123
1124
1125
/**
1126
 * Insère une ligne dans une table
1127
 *
1128
 * @param string $table
1129
 *     Nom de la table SQL
1130
 * @param string $champs
1131
 *     Liste des colonnes impactées,
1132
 * @param string $valeurs
1133
 *     Liste des valeurs,
1134
 * @param array $desc
1135
 *     Tableau de description des colonnes de la table SQL utilisée
1136
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1137
 * @param string $serveur
1138
 *     Nom du connecteur
1139
 * @param bool $requeter
1140
 *     Exécuter la requête, sinon la retourner
1141
 * @return bool|string|int|array
1142
 *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
1143
 *     - Texte de la requête si demandé,
1144
 *     - False en cas d'erreur,
1145
 *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
1146
 **/
1147
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...
1148
1149
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
1150
	$link = $connexion['link'];
1151
	$table = prefixer_table_spip($table, $connexion['prefixe']);
1152
1153
	$query = "INSERT INTO $table $champs VALUES $valeurs";
1154
	if (!$requeter) {
1155
		return $query;
1156
	}
1157
1158 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...
1159
		include_spip('public/tracer');
1160
		$t = trace_query_start();
1161
		$e = '';
1162
	} else {
1163
		$t = 0;
1164
	}
1165
1166
	$connexion['last'] = $query;
1167
	#spip_log($query, 'mysql.'._LOG_DEBUG);
1168
	$r = false;
1169
	if (mysqli_query($link, $query)) {
1170
		$r = mysqli_insert_id($link);
1171
	} else {
1172
		// Log de l'erreur eventuelle
1173
		if ($e = spip_mysql_errno($serveur)) {
1174
			$e .= spip_mysql_error($query, $serveur);
1175
		} // et du fautif
1176
	}
1177
1178
	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...
1179
1180
	// return $r ? $r : (($r===0) ? -1 : 0); pb avec le multi-base.
1181
}
1182
1183
/**
1184
 * Insère une ligne dans une table, en protégeant chaque valeur
1185
 *
1186
 * @param string $table
1187
 *     Nom de la table SQL
1188
 * @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...
1189
 *    Couples (colonne => valeur)
1190
 * @param array $desc
1191
 *     Tableau de description des colonnes de la table SQL utilisée
1192
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1193
 * @param string $serveur
1194
 *     Nom du connecteur
1195
 * @param bool $requeter
1196
 *     Exécuter la requête, sinon la retourner
1197
 * @return bool|string|int|array
1198
 *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
1199
 *     - Texte de la requête si demandé,
1200
 *     - False en cas d'erreur,
1201
 *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
1202
 **/
1203
function spip_mysql_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true) {
1204
1205
	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...
1206
		$desc = description_table($table, $serveur);
1207
	}
1208
	if (!$desc) {
1209
		$couples = array();
1210
	}
1211
	$fields = isset($desc['field']) ? $desc['field'] : array();
1212
1213
	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...
1214
		$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
1215
	}
1216
1217
	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 1206 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...
1218
		$serveur, $requeter);
1219
}
1220
1221
1222
/**
1223
 * Insère plusieurs lignes d'un coup dans une table
1224
 *
1225
 * @param string $table
1226
 *     Nom de la table SQL
1227
 * @param array $tab_couples
1228
 *     Tableau de tableaux associatifs (colonne => valeur)
1229
 * @param array $desc
1230
 *     Tableau de description des colonnes de la table SQL utilisée
1231
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1232
 * @param string $serveur
1233
 *     Nom du connecteur
1234
 * @param bool $requeter
1235
 *     Exécuter la requête, sinon la retourner
1236
 * @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...
1237
 *     - int|true identifiant du dernier élément inséré (si possible), ou true, si réussite
1238
 *     - Texte de la requête si demandé,
1239
 *     - False en cas d'erreur.
1240
 **/
1241
function spip_mysql_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true) {
1242
1243
	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...
1244
		$desc = description_table($table, $serveur);
1245
	}
1246
	if (!$desc) {
1247
		$tab_couples = array();
1248
	}
1249
	$fields = isset($desc['field']) ? $desc['field'] : array();
1250
1251
	$cles = "(" . join(',', array_keys(reset($tab_couples))) . ')';
1252
	$valeurs = array();
1253
	$r = false;
1254
1255
	// Quoter et Inserer par groupes de 100 max pour eviter un debordement de pile
1256
	foreach ($tab_couples as $couples) {
1257
		foreach ($couples as $champ => $val) {
1258
			$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
1259
		}
1260
		$valeurs[] = '(' . join(',', $couples) . ')';
1261 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...
1262
			$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 1244 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...
1263
			$valeurs = array();
1264
		}
1265
	}
1266 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...
1267
		$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 1244 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...
1268
	}
1269
1270
	return $r; // dans le cas d'une table auto_increment, le dernier insert_id
1271
}
1272
1273
/**
1274
 * Met à jour des enregistrements d'une table SQL
1275
 *
1276
 * @param string $table
1277
 *     Nom de la table
1278
 * @param array $champs
1279
 *     Couples (colonne => valeur)
1280
 * @param string|array $where
1281
 *     Conditions a remplir (Where)
1282
 * @param array $desc
1283
 *     Tableau de description des colonnes de la table SQL utilisée
1284
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1285
 * @param string $serveur
1286
 *     Nom de la connexion
1287
 * @param bool $requeter
1288
 *     Exécuter la requête, sinon la retourner
1289
 * @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...
1290
 *     - string : texte de la requête si demandé
1291
 *     - true si la requête a réussie, false sinon
1292
 *     - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
1293
 */
1294
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...
1295
	$set = array();
1296
	foreach ($champs as $champ => $val) {
1297
		$set[] = $champ . "=$val";
1298
	}
1299 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...
1300
		return spip_mysql_query(
1301
			calculer_mysql_expression('UPDATE', $table, ',')
1302
			. calculer_mysql_expression('SET', $set, ',')
1303
			. calculer_mysql_expression('WHERE', $where),
1304
			$serveur, $requeter);
1305
	}
1306
}
1307
1308
/**
1309
 * Met à jour des enregistrements d'une table SQL et protège chaque valeur
1310
 *
1311
 * Protège chaque valeur transmise avec sql_quote(), adapté au type
1312
 * de champ attendu par la table SQL
1313
 *
1314
 * @note
1315
 *   Les valeurs sont des constantes à mettre entre apostrophes
1316
 *   sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
1317
 *
1318
 * @param string $table
1319
 *     Nom de la table
1320
 * @param array $champs
1321
 *     Couples (colonne => valeur)
1322
 * @param string|array $where
1323
 *     Conditions a remplir (Where)
1324
 * @param array $desc
1325
 *     Tableau de description des colonnes de la table SQL utilisée
1326
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1327
 * @param string $serveur
1328
 *     Nom de la connexion
1329
 * @param bool $requeter
1330
 *     Exécuter la requête, sinon la retourner
1331
 * @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...
1332
 *     - string : texte de la requête si demandé
1333
 *     - true si la requête a réussie, false sinon
1334
 *     - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
1335
 */
1336
function spip_mysql_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true) {
1337
1338
	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...
1339
		return;
1340
	}
1341
	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...
1342
		$desc = description_table($table, $serveur);
1343
	}
1344
	if (!$desc) {
1345
		$champs = array();
1346
	} else {
1347
		$fields = $desc['field'];
1348
	}
1349
	$set = array();
1350
	foreach ($champs as $champ => $val) {
1351
		$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...
1352
	}
1353
1354
	return spip_mysql_query(
1355
		calculer_mysql_expression('UPDATE', $table, ',')
1356
		. calculer_mysql_expression('SET', $set, ',')
1357
		. calculer_mysql_expression('WHERE', $where),
1358
		$serveur, $requeter);
1359
}
1360
1361
/**
1362
 * Supprime des enregistrements d'une table
1363
 *
1364
 * @param string $table Nom de la table SQL
1365
 * @param string|array $where Conditions à vérifier
1366
 * @param string $serveur Nom du connecteur
1367
 * @param bool $requeter Exécuter la requête, sinon la retourner
1368
 * @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...
1369
 *     - int : nombre de suppressions réalisées,
1370
 *     - Texte de la requête si demandé,
1371
 *     - False en cas d'erreur.
1372
 **/
1373
function spip_mysql_delete($table, $where = '', $serveur = '', $requeter = true) {
1374
	$res = spip_mysql_query(
1375
		calculer_mysql_expression('DELETE FROM', $table, ',')
1376
		. calculer_mysql_expression('WHERE', $where),
1377
		$serveur, $requeter);
1378
	if (!$requeter) {
1379
		return $res;
1380
	}
1381
	if ($res) {
1382
		$link = _mysql_link($serveur);
1383
1384
		return mysqli_affected_rows($link);
1385
	} else {
1386
		return false;
1387
	}
1388
}
1389
1390
1391
/**
1392
 * Insère où met à jour une entrée d’une table SQL
1393
 *
1394
 * La clé ou les cles primaires doivent être présentes dans les données insérés.
1395
 * La fonction effectue une protection automatique des données.
1396
 *
1397
 * Préférez updateq ou insertq.
1398
 *
1399
 * @param string $table
1400
 *     Nom de la table SQL
1401
 * @param array $couples
1402
 *     Couples colonne / valeur à modifier,
1403
 * @param array $desc
1404
 *     Tableau de description des colonnes de la table SQL utilisée
1405
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1406
 * @param string $serveur
1407
 *     Nom du connecteur
1408
 * @param bool $requeter
1409
 *     Exécuter la requête, sinon la retourner
1410
 * @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...
1411
 *     - true si réussite
1412
 *     - Texte de la requête si demandé,
1413
 *     - False en cas d'erreur.
1414
 **/
1415
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...
1416
	return spip_mysql_query("REPLACE $table (" . join(',', array_keys($couples)) . ') VALUES (' . join(',',
1417
			array_map('_q', $couples)) . ')', $serveur, $requeter);
1418
}
1419
1420
1421
/**
1422
 * Insère où met à jour des entrées d’une table SQL
1423
 *
1424
 * La clé ou les cles primaires doivent être présentes dans les données insérés.
1425
 * La fonction effectue une protection automatique des données.
1426
 *
1427
 * Préférez insertq_multi et sql_updateq
1428
 *
1429
 * @param string $table
1430
 *     Nom de la table SQL
1431
 * @param array $tab_couples
1432
 *     Tableau de tableau (colonne / valeur à modifier),
1433
 * @param array $desc
1434
 *     Tableau de description des colonnes de la table SQL utilisée
1435
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1436
 * @param string $serveur
1437
 *     Nom du connecteur
1438
 * @param bool $requeter
1439
 *     Exécuter la requête, sinon la retourner
1440
 * @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...
1441
 *     - true si réussite
1442
 *     - Texte de la requête si demandé,
1443
 *     - False en cas d'erreur.
1444
 **/
1445
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...
1446
	$cles = "(" . join(',', array_keys($tab_couples[0])) . ')';
1447
	$valeurs = array();
1448
	foreach ($tab_couples as $couples) {
1449
		$valeurs[] = '(' . join(',', array_map('_q', $couples)) . ')';
1450
	}
1451
	$valeurs = implode(', ', $valeurs);
1452
1453
	return spip_mysql_query("REPLACE $table $cles VALUES $valeurs", $serveur, $requeter);
1454
}
1455
1456
1457
/**
1458
 * Retourne l'instruction SQL pour obtenir le texte d'un champ contenant
1459
 * une balise `<multi>` dans la langue indiquée
1460
 *
1461
 * Cette sélection est mise dans l'alias `multi` (instruction AS multi).
1462
 *
1463
 * @param string $objet Colonne ayant le texte
1464
 * @param string $lang Langue à extraire
1465
 * @return string       Texte de sélection pour la requête
1466
 */
1467
function spip_mysql_multi($objet, $lang) {
1468
	$lengthlang = strlen("[$lang]");
1469
	$posmulti = "INSTR(" . $objet . ", '<multi>')";
1470
	$posfinmulti = "INSTR(" . $objet . ", '</multi>')";
1471
	$debutchaine = "LEFT(" . $objet . ", $posmulti-1)";
1472
	$finchaine = "RIGHT(" . $objet . ", CHAR_LENGTH(" . $objet . ") -(7+$posfinmulti))";
1473
	$chainemulti = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7, $posfinmulti -(7+$posmulti)))";
1474
	$poslang = "INSTR($chainemulti,'[" . $lang . "]')";
1475
	$poslang = "IF($poslang=0,INSTR($chainemulti,']')+1,$poslang+$lengthlang)";
1476
	$chainelang = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7+$poslang-1,$posfinmulti -($posmulti+7+$poslang-1) ))";
1477
	$posfinlang = "INSTR(" . $chainelang . ", '[')";
1478
	$chainelang = "IF($posfinlang>0,LEFT($chainelang,$posfinlang-1),$chainelang)";
1479
	//$chainelang = "LEFT($chainelang,$posfinlang-1)";
1480
	$retour = "(TRIM(IF($posmulti = 0 , " .
1481
		"     TRIM(" . $objet . "), " .
1482
		"     CONCAT( " .
1483
		"          $debutchaine, " .
1484
		"          IF( " .
1485
		"               $poslang = 0, " .
1486
		"                     $chainemulti, " .
1487
		"               $chainelang" .
1488
		"          ), " .
1489
		"          $finchaine" .
1490
		"     ) " .
1491
		"))) AS multi";
1492
1493
	return $retour;
1494
}
1495
1496
/**
1497
 * Prépare une chaîne hexadécimale
1498
 *
1499
 * Par exemple : FF ==> 0xFF en MySQL
1500
 *
1501
 * @param string $v
1502
 *     Chaine hexadecimale
1503
 * @return string
1504
 *     Valeur hexadécimale pour MySQL
1505
 **/
1506
function spip_mysql_hex($v) {
1507
	return "0x" . $v;
1508
}
1509
1510
/**
1511
 * Échapper une valeur selon son type ou au mieux
1512
 * comme le fait `_q()` mais pour MySQL avec ses spécificités
1513
 *
1514
 * @param string|array|number $v
1515
 *     Texte, nombre ou tableau à échapper
1516
 * @param string $type
1517
 *     Description du type attendu
1518
 *    (par exemple description SQL de la colonne recevant la donnée)
1519
 * @return string|number
1520
 *    Donnée prête à être utilisée par le gestionnaire SQL
1521
 */
1522
function spip_mysql_quote($v, $type = '') {
1523
	if (!is_array($v)) {
1524
		return spip_mysql_cite($v, $type);
1525
	}
1526
1527
	// si c'est un tableau, le parcourir en propageant le type
1528
	foreach ($v as $k => $r) {
1529
		$v[$k] = spip_mysql_quote($r, $type);
1530
	}
1531
	return implode(',', $v);
1532
}
1533
1534
/**
1535
 * Tester si une date est proche de la valeur d'un champ
1536
 *
1537
 * @param string $champ
1538
 *     Nom du champ a tester
1539
 * @param int $interval
1540
 *     Valeur de l'intervalle : -1, 4, ...
1541
 * @param string $unite
1542
 *     Utité utilisée (DAY, MONTH, YEAR, ...)
1543
 * @return string
1544
 *     Expression SQL
1545
 **/
1546 View Code Duplication
function spip_mysql_date_proche($champ, $interval, $unite) {
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...
1547
	return '('
1548
	. $champ
1549
	. (($interval <= 0) ? '>' : '<')
1550
	. (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
1551
	. '('
1552
	. sql_quote(date('Y-m-d H:i:s'))
1553
	. ', INTERVAL '
1554
	. (($interval > 0) ? $interval : (0 - $interval))
1555
	. ' '
1556
	. $unite
1557
	. '))';
1558
}
1559
1560
1561
/**
1562
 * Retourne une expression IN pour le gestionnaire de base de données
1563
 *
1564
 * IN (...) est limité à 255 éléments, d'où cette fonction assistante
1565
 *
1566
 * @param string $val
1567
 *     Colonne SQL sur laquelle appliquer le test
1568
 * @param string $valeurs
1569
 *     Liste des valeurs possibles (séparés par des virgules)
1570
 * @param string $not
1571
 *     - '' sélectionne les éléments correspondant aux valeurs
1572
 *     - 'NOT' inverse en sélectionnant les éléments ne correspondant pas aux valeurs
1573
 * @param string $serveur
1574
 *     Nom du connecteur
1575
 * @param bool $requeter
1576
 *     Inutilisé
1577
 * @return string
1578
 *     Expression de requête SQL
1579
 **/
1580
function spip_mysql_in($val, $valeurs, $not = '', $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...
1581
	return "($val $not IN ($valeurs))";
1582
}
1583
1584
1585
/**
1586
 * Renvoie les bons echappements (mais pas sur les fonctions comme NOW())
1587
 *
1588
 * @param string|number $v Texte ou nombre à échapper
1589
 * @param string $type Type de donnée attendue, description SQL de la colonne de destination
1590
 * @return string|number     Texte ou nombre échappé
1591
 */
1592
function spip_mysql_cite($v, $type) {
1593
	if (!$type) {
1594
		if (is_numeric($v)) {
1595
			return strval($v);
1596
		}
1597
		return "'" . addslashes($v) . "'";
1598
	}
1599
1600
	if (is_null($v)
1601
		and stripos($type, "NOT NULL") === false
1602
	) {
1603
		return 'NULL';
1604
	} // null php se traduit en NULL SQL
1605
	if (sql_test_date($type) and preg_match('/^\w+\(/', $v)) {
1606
		return $v;
1607
	}
1608
	if (sql_test_int($type)) {
1609
		if (is_numeric($v) or (ctype_xdigit(substr($v, 2))
1610
				and $v[0] == '0' and $v[1] == 'x')
1611
		) {
1612
			return $v;
1613
		} // si pas numerique, forcer le intval
1614
		else {
1615
			return intval($v);
1616
		}
1617
	}
1618
1619
	return ("'" . addslashes($v) . "'");
1620
}
1621
1622
1623
// Ces deux fonctions n'ont pas d'equivalent exact PostGres
1624
// et ne sont la que pour compatibilite avec les extensions de SPIP < 1.9.3
1625
1626
/**
1627
 * Poser un verrou SQL local
1628
 *
1629
 * Changer de nom toutes les heures en cas de blocage MySQL (ca arrive)
1630
 *
1631
 * @deprecated Pas d'équivalence actuellement en dehors de MySQL
1632
 * @see spip_release_lock()
1633
 *
1634
 * @param string $nom
1635
 *     Inutilisé. Le nom est calculé en fonction de la connexion principale
1636
 * @param int $timeout
1637
 * @return string|bool
1638
 *     - Nom du verrou si réussite,
1639
 *     - false sinon
1640
 */
1641
function spip_get_lock($nom, $timeout = 0) {
1642
1643
	define('_LOCK_TIME', intval(time() / 3600 - 316982));
1644
1645
	$connexion = &$GLOBALS['connexions'][0];
1646
	$bd = $connexion['db'];
1647
	$prefixe = $connexion['prefixe'];
1648
	$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
1649
1650
	$connexion['last'] = $q = "SELECT GET_LOCK(" . _q($nom) . ", $timeout) AS n";
1651
1652
	$q = @sql_fetch(mysqli_query(_mysql_link(), $q));
1653
	if (!$q) {
1654
		spip_log("pas de lock sql pour $nom", _LOG_ERREUR);
1655
	}
1656
1657
	return $q['n'];
1658
}
1659
1660
1661
/**
1662
 * Relâcher un verrou SQL local
1663
 *
1664
 * @deprecated Pas d'équivalence actuellement en dehors de MySQL
1665
 * @see spip_get_lock()
1666
 *
1667
 * @param string $nom
1668
 *     Inutilisé. Le nom est calculé en fonction de la connexion principale
1669
 * @return string|bool
0 ignored issues
show
Documentation introduced by
Should the return type not be string|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...
1670
 *     True si réussite, false sinon.
1671
 */
1672
function spip_release_lock($nom) {
1673
1674
	$connexion = &$GLOBALS['connexions'][0];
1675
	$bd = $connexion['db'];
1676
	$prefixe = $connexion['prefixe'];
1677
	$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
1678
1679
	$connexion['last'] = $q = "SELECT RELEASE_LOCK(" . _q($nom) . ")";
1680
	mysqli_query(_mysql_link(), $q);
1681
}
1682
1683
1684
/**
1685
 * Teste si on a les fonctions MySQLi (pour l'install)
1686
 *
1687
 * @return bool
1688
 *     True si on a les fonctions, false sinon
1689
 */
1690
function spip_versions_mysql() {
1691
	return function_exists('mysqli_query');
1692
}
1693
1694
1695
/**
1696
 * Tester si mysql ne veut pas du nom de la base dans les requêtes
1697
 *
1698
 * @param string $server_db
1699
 * @return string
1700
 *     - chaîne vide si nom de la base utile
1701
 *     - chaîne : code compilé pour le faire désactiver par SPIP sinon
1702
 */
1703
function test_rappel_nom_base_mysql($server_db) {
1704
	$GLOBALS['mysql_rappel_nom_base'] = true;
1705
	sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1706
	$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: Pour compatibilité. Utiliser `sql_query()` ou l'API `sql_*`.

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...
1707
1708
	if ($ok) {
1709
		sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1710
1711
		return '';
1712
	} else {
1713
		$GLOBALS['mysql_rappel_nom_base'] = false;
1714
1715
		return "\$GLOBALS['mysql_rappel_nom_base'] = false; " .
1716
		"/* echec de test_rappel_nom_base_mysql a l'installation. */\n";
1717
	}
1718
}
1719
1720
/**
1721
 * Teste si on peut changer les modes de MySQL
1722
 *
1723
 * @link http://dev.mysql.com/doc/refman/5.0/fr/server-sql-mode.html
1724
 *
1725
 * @param string $server_db Nom de la connexion
1726
 * @return string
1727
 *     - chaîne vide si on ne peut pas appliquer de mode
1728
 *     - chaîne : code compilé pour l'indiquer le résultat du test à SPIP
1729
 */
1730
function test_sql_mode_mysql($server_db) {
1731
	$res = sql_select("version() as v", '', '', '', '', '', '', $server_db);
1732
	$row = sql_fetch($res, $server_db);
1733
	if (version_compare($row['v'], '5.0.0', '>=')) {
1734
		defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE', true);
1735
1736
		return "defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE',true);\n";
1737
	}
1738
1739
	return '';
1740
}
1741