Completed
Push — master ( 7eb3f0...6768bf )
by cam
04:10
created

mysql.php ➔ spip_mysql_query()   D

Complexity

Conditions 14
Paths 258

Size

Total Lines 58

Duplication

Lines 6
Ratio 10.34 %

Importance

Changes 0
Metric Value
cc 14
nc 258
nop 3
dl 6
loc 58
rs 4.7083
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 $query
714
 *     Définition SQL d'un champ de table
715
 * @return string
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
	);
726
727
	$query = preg_replace(array_keys($remplace), $remplace, $query);
728
729
	return $query;
730
}
731
732
733
/**
734
 * Crée une base de données MySQL
735
 *
736
 * @param string $nom Nom de la base
737
 * @param string $serveur Nom de la connexion
738
 * @param bool $requeter Exécuter la requête, sinon la retourner
739
 * @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...
740
 **/
741
function spip_mysql_create_base($nom, $serveur = '', $requeter = true) {
742
	return spip_mysql_query("CREATE DATABASE `$nom`", $serveur, $requeter);
743
}
744
745
746
/**
747
 * Crée une vue SQL nommée `$nom`
748
 *
749
 * @param string $nom
750
 *    Nom de la vue à creer
751
 * @param string $query_select
752
 *     Texte de la requête de sélection servant de base à la vue
753
 * @param string $serveur
754
 *     Nom du connecteur
755
 * @param bool $requeter
756
 *     Effectuer la requete, sinon la retourner
757
 * @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...
758
 *     - true si la vue est créée
759
 *     - false si erreur ou si la vue existe déja
760
 *     - string texte de la requête si $requeter vaut false
761
 */
762 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...
763
	if (!$query_select) {
764
		return false;
765
	}
766
	// vue deja presente
767
	if (sql_showtable($nom, false, $serveur)) {
768
		spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)", _LOG_ERREUR);
769
770
		return false;
771
	}
772
773
	$query = "CREATE VIEW $nom AS " . $query_select;
774
775
	return spip_mysql_query($query, $serveur, $requeter);
776
}
777
778
779
/**
780
 * Supprime une table SQL
781
 *
782
 * @param string $table Nom de la table SQL
783
 * @param string $exist True pour ajouter un test d'existence avant de supprimer
784
 * @param string $serveur Nom de la connexion
785
 * @param bool $requeter Exécuter la requête, sinon la retourner
786
 * @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...
787
 *     - string Texte de la requête si demandé
788
 *     - true si la requête a réussie, false sinon
789
 */
790
function spip_mysql_drop_table($table, $exist = '', $serveur = '', $requeter = true) {
791
	if ($exist) {
792
		$exist = " IF EXISTS";
793
	}
794
795
	return spip_mysql_query("DROP TABLE$exist $table", $serveur, $requeter);
796
}
797
798
/**
799
 * Supprime une vue SQL
800
 *
801
 * @param string $view Nom de la vue SQL
802
 * @param string $exist True pour ajouter un test d'existence avant de supprimer
803
 * @param string $serveur Nom de la connexion
804
 * @param bool $requeter Exécuter la requête, sinon la retourner
805
 * @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...
806
 *     - string Texte de la requête si demandé
807
 *     - true si la requête a réussie, false sinon
808
 */
809
function spip_mysql_drop_view($view, $exist = '', $serveur = '', $requeter = true) {
810
	if ($exist) {
811
		$exist = " IF EXISTS";
812
	}
813
814
	return spip_mysql_query("DROP VIEW$exist $view", $serveur, $requeter);
815
}
816
817
/**
818
 * Retourne une ressource de la liste des tables de la base de données
819
 *
820
 * @param string $match
821
 *     Filtre sur tables à récupérer
822
 * @param string $serveur
823
 *     Connecteur de la base
824
 * @param bool $requeter
825
 *     true pour éxecuter la requête
826
 *     false pour retourner le texte de la requête.
827
 * @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...
828
 *     Ressource à utiliser avec sql_fetch()
829
 **/
830
function spip_mysql_showbase($match, $serveur = '', $requeter = true) {
831
	return spip_mysql_query("SHOW TABLES LIKE " . _q($match), $serveur, $requeter);
832
}
833
834
/**
835
 * Répare une table SQL
836
 *
837
 * Utilise `REPAIR TABLE ...` de MySQL
838
 *
839
 * @param string $table Nom de la table SQL
840
 * @param string $serveur Nom de la connexion
841
 * @param bool $requeter Exécuter la requête, sinon la retourner
842
 * @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...
843
 *     - string Texte de la requête si demandée,
844
 *     - true si la requête a réussie, false sinon
845
 */
846
function spip_mysql_repair($table, $serveur = '', $requeter = true) {
847
	return spip_mysql_query("REPAIR TABLE `$table`", $serveur, $requeter);
848
}
849
850
851
define('_MYSQL_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
852
/**
853
 * Obtient la description d'une table ou vue MySQL
854
 *
855
 * Récupère la définition d'une table ou d'une vue avec colonnes, indexes, etc.
856
 * au même format que la définition des tables SPIP, c'est à dire
857
 * un tableau avec les clés
858
 *
859
 * - `field` (tableau colonne => description SQL) et
860
 * - `key` (tableau type de clé => colonnes)
861
 *
862
 * @param string $nom_table Nom de la table SQL
863
 * @param string $serveur Nom de la connexion
864
 * @param bool $requeter Exécuter la requête, sinon la retourner
865
 * @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...
866
 *     - chaîne vide si pas de description obtenue
867
 *     - string Texte de la requête si demandé
868
 *     - array description de la table sinon
869
 */
870
function spip_mysql_showtable($nom_table, $serveur = '', $requeter = true) {
871
	$s = spip_mysql_query("SHOW CREATE TABLE `$nom_table`", $serveur, $requeter);
872
	if (!$s) {
873
		return '';
874
	}
875
	if (!$requeter) {
876
		return $s;
877
	}
878
879
	list(, $a) = mysqli_fetch_array($s, MYSQLI_NUM);
880
	if (preg_match(_MYSQL_RE_SHOW_TABLE, $a, $r)) {
881
		$desc = $r[1];
882
		// extraction d'une KEY éventuelle en prenant garde de ne pas
883
		// relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
884 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...
885
			$namedkeys = $r[2];
886
			$desc = $r[1];
887
		} else {
888
			$namedkeys = "";
889
		}
890
891
		$fields = array();
892
		foreach (preg_split("/,\s*`/", $desc) as $v) {
893
			preg_match("/^\s*`?([^`]*)`\s*(.*)/", $v, $r);
894
			$fields[strtolower($r[1])] = $r[2];
895
		}
896
		$keys = array();
897
898 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...
899
			if (preg_match("/^\s*([^(]*)\(([^(]*(\(\d+\))?)$/", $v, $r)) {
900
				$k = str_replace("`", '', trim($r[1]));
901
				$t = strtolower(str_replace("`", '', $r[2]));
902
				if ($k && !isset($keys[$k])) {
903
					$keys[$k] = $t;
904
				} else {
905
					$keys[] = $t;
906
				}
907
			}
908
		}
909
		spip_mysql_free($s);
910
911
		return array('field' => $fields, 'key' => $keys);
912
	}
913
914
	$res = spip_mysql_query("SHOW COLUMNS FROM `$nom_table`", $serveur);
915
	if ($res) {
916
		$nfields = array();
917
		$nkeys = array();
918
		while ($val = spip_mysql_fetch($res)) {
919
			$nfields[$val["Field"]] = $val['Type'];
920
			if ($val['Null'] == 'NO') {
921
				$nfields[$val["Field"]] .= ' NOT NULL';
922
			}
923
			if ($val['Default'] === '0' || $val['Default']) {
924
				if (preg_match('/[A-Z_]/', $val['Default'])) {
925
					$nfields[$val["Field"]] .= ' DEFAULT ' . $val['Default'];
926
				} else {
927
					$nfields[$val["Field"]] .= " DEFAULT '" . $val['Default'] . "'";
928
				}
929
			}
930
			if ($val['Extra']) {
931
				$nfields[$val["Field"]] .= ' ' . $val['Extra'];
932
			}
933
			if ($val['Key'] == 'PRI') {
934
				$nkeys['PRIMARY KEY'] = $val["Field"];
935
			} else {
936
				if ($val['Key'] == 'MUL') {
937
					$nkeys['KEY ' . $val["Field"]] = $val["Field"];
938
				} else {
939
					if ($val['Key'] == 'UNI') {
940
						$nkeys['UNIQUE KEY ' . $val["Field"]] = $val["Field"];
941
					}
942
				}
943
			}
944
		}
945
		spip_mysql_free($res);
946
947
		return array('field' => $nfields, 'key' => $nkeys);
948
	}
949
950
	return "";
951
}
952
953
954
/**
955
 * Rècupère une ligne de résultat
956
 *
957
 * Récupère la ligne suivante d'une ressource de résultat
958
 *
959
 * @param Ressource $r Ressource de résultat (issu de sql_select)
960
 * @param string $t Structure de résultat attendu (défaut MYSQLI_ASSOC)
961
 * @param string $serveur Nom de la connexion
962
 * @param bool $requeter Inutilisé
963
 * @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...
964
 */
965
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...
966
	if (!$t) {
967
		$t = MYSQLI_ASSOC;
968
	}
969
	if ($r) {
970
		return mysqli_fetch_array($r, $t);
971
	}
972
}
973
974
/**
975
 * Place le pointeur de résultat sur la position indiquée
976
 *
977
 * @param Ressource $r Ressource de résultat
978
 * @param int $row_number Position. Déplacer le pointeur à cette ligne
979
 * @param string $serveur Nom de la connexion
980
 * @param bool $requeter Inutilisé
981
 * @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...
982
 **/
983
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...
984
	if ($r and mysqli_num_rows($r)) {
985
		return mysqli_data_seek($r, $row_number);
986
	}
987
}
988
989
990
/**
991
 * Retourne le nombre de lignes d'une sélection
992
 *
993
 * @param array|string $from Tables à consulter (From)
994
 * @param array|string $where Conditions a remplir (Where)
995
 * @param array|string $groupby Critère de regroupement (Group by)
996
 * @param array $having Tableau des des post-conditions à remplir (Having)
997
 * @param string $serveur Nom de la connexion
998
 * @param bool $requeter Exécuter la requête, sinon la retourner
999
 * @return int|string
1000
 *     - String Texte de la requête si demandé
1001
 *     - int Nombre de lignes (0 si la requête n'a pas réussie)
1002
 **/
1003
function spip_mysql_countsel(
1004
	$from = array(),
1005
	$where = array(),
1006
	$groupby = '',
1007
	$having = array(),
1008
	$serveur = '',
1009
	$requeter = true
1010
) {
1011
	$c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
1012
1013
	$r = spip_mysql_select("COUNT($c)", $from, $where, '', '', '', $having, $serveur, $requeter);
1014
	if (!$requeter) {
1015
		return $r;
1016
	}
1017
	if (!$r instanceof mysqli_result) {
1018
		return 0;
1019
	}
1020
	list($c) = mysqli_fetch_array($r, MYSQLI_NUM);
1021
	mysqli_free_result($r);
1022
1023
	return $c;
1024
}
1025
1026
1027
/**
1028
 * Retourne la dernière erreur generée
1029
 *
1030
 * @note
1031
 *   Bien spécifier le serveur auquel on s'adresse,
1032
 *   mais à l'install la globale n'est pas encore complètement définie.
1033
 *
1034
 * @uses sql_error_backtrace()
1035
 *
1036
 * @param string $query
1037
 *     Requête qui était exécutée
1038
 * @param string $serveur
1039
 *     Nom de la connexion
1040
 * @param bool $requeter
1041
 *     Inutilisé
1042
 * @return string
1043
 *     Erreur eventuelle
1044
 **/
1045
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...
1046
	$link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
1047
	$s = mysqli_error($link);
1048
	if ($s) {
1049
		$trace = debug_backtrace();
1050
		if ($trace[0]['function'] != "spip_mysql_error") {
1051
			spip_log("$s - $query - " . sql_error_backtrace(), 'mysql.' . _LOG_ERREUR);
1052
		}
1053
	}
1054
1055
	return $s;
1056
}
1057
1058
1059
/**
1060
 * Retourne le numero de la dernière erreur SQL
1061
 *
1062
 * @param string $serveur
1063
 *     Nom de la connexion
1064
 * @param bool $requeter
1065
 *     Inutilisé
1066
 * @return int
1067
 *     0, pas d'erreur. Autre, numéro de l'erreur.
1068
 **/
1069
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...
1070
	$link = $GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
1071
	$s = mysqli_errno($link);
1072
	// 2006 MySQL server has gone away
1073
	// 2013 Lost connection to MySQL server during query
1074
	if (in_array($s, array(2006, 2013))) {
1075
		define('spip_interdire_cache', true);
1076
	}
1077
	if ($s) {
1078
		spip_log("Erreur mysql $s", _LOG_ERREUR);
1079
	}
1080
1081
	return $s;
1082
}
1083
1084
1085
/**
1086
 * Retourne le nombre de lignes d’une ressource de sélection obtenue
1087
 * avec `sql_select()`
1088
 *
1089
 * @param Ressource $r Ressource de résultat
1090
 * @param string $serveur Nom de la connexion
1091
 * @param bool $requeter Inutilisé
1092
 * @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...
1093
 */
1094
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...
1095
	if ($r) {
1096
		return mysqli_num_rows($r);
1097
	}
1098
}
1099
1100
1101
/**
1102
 * Libère une ressource de résultat
1103
 *
1104
 * Indique à MySQL de libérer de sa mémoire la ressoucre de résultat indiquée
1105
 * car on n'a plus besoin de l'utiliser.
1106
 *
1107
 * @param Ressource $r Ressource de résultat
1108
 * @param string $serveur Nom de la connexion
1109
 * @param bool $requeter Inutilisé
1110
 * @return bool              True si réussi
1111
 */
1112
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...
1113
	return (($r instanceof mysqli_result) ? mysqli_free_result($r) : false);
1114
}
1115
1116
1117
/**
1118
 * Insère une ligne dans une table
1119
 *
1120
 * @param string $table
1121
 *     Nom de la table SQL
1122
 * @param string $champs
1123
 *     Liste des colonnes impactées,
1124
 * @param string $valeurs
1125
 *     Liste des valeurs,
1126
 * @param array $desc
1127
 *     Tableau de description des colonnes de la table SQL utilisée
1128
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1129
 * @param string $serveur
1130
 *     Nom du connecteur
1131
 * @param bool $requeter
1132
 *     Exécuter la requête, sinon la retourner
1133
 * @return bool|string|int|array
1134
 *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
1135
 *     - Texte de la requête si demandé,
1136
 *     - False en cas d'erreur,
1137
 *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
1138
 **/
1139
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...
1140
1141
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
1142
	$link = $connexion['link'];
1143
	$table = prefixer_table_spip($table, $connexion['prefixe']);
1144
1145
	$query = "INSERT INTO $table $champs VALUES $valeurs";
1146
	if (!$requeter) {
1147
		return $query;
1148
	}
1149
1150 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...
1151
		include_spip('public/tracer');
1152
		$t = trace_query_start();
1153
		$e = '';
1154
	} else {
1155
		$t = 0;
1156
	}
1157
1158
	$connexion['last'] = $query;
1159
	#spip_log($query, 'mysql.'._LOG_DEBUG);
1160
	$r = false;
1161
	if (mysqli_query($link, $query)) {
1162
		$r = mysqli_insert_id($link);
1163
	} else {
1164
		// Log de l'erreur eventuelle
1165
		if ($e = spip_mysql_errno($serveur)) {
1166
			$e .= spip_mysql_error($query, $serveur);
1167
		} // et du fautif
1168
	}
1169
1170
	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...
1171
1172
	// return $r ? $r : (($r===0) ? -1 : 0); pb avec le multi-base.
1173
}
1174
1175
/**
1176
 * Insère une ligne dans une table, en protégeant chaque valeur
1177
 *
1178
 * @param string $table
1179
 *     Nom de la table SQL
1180
 * @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...
1181
 *    Couples (colonne => valeur)
1182
 * @param array $desc
1183
 *     Tableau de description des colonnes de la table SQL utilisée
1184
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1185
 * @param string $serveur
1186
 *     Nom du connecteur
1187
 * @param bool $requeter
1188
 *     Exécuter la requête, sinon la retourner
1189
 * @return bool|string|int|array
1190
 *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
1191
 *     - Texte de la requête si demandé,
1192
 *     - False en cas d'erreur,
1193
 *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
1194
 **/
1195
function spip_mysql_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true) {
1196
1197
	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...
1198
		$desc = description_table($table, $serveur);
1199
	}
1200
	if (!$desc) {
1201
		$couples = array();
1202
	}
1203
	$fields = isset($desc['field']) ? $desc['field'] : array();
1204
1205
	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...
1206
		$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
1207
	}
1208
1209
	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 1198 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...
1210
		$serveur, $requeter);
1211
}
1212
1213
1214
/**
1215
 * Insère plusieurs lignes d'un coup dans une table
1216
 *
1217
 * @param string $table
1218
 *     Nom de la table SQL
1219
 * @param array $tab_couples
1220
 *     Tableau de tableaux associatifs (colonne => valeur)
1221
 * @param array $desc
1222
 *     Tableau de description des colonnes de la table SQL utilisée
1223
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1224
 * @param string $serveur
1225
 *     Nom du connecteur
1226
 * @param bool $requeter
1227
 *     Exécuter la requête, sinon la retourner
1228
 * @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...
1229
 *     - int|true identifiant du dernier élément inséré (si possible), ou true, si réussite
1230
 *     - Texte de la requête si demandé,
1231
 *     - False en cas d'erreur.
1232
 **/
1233
function spip_mysql_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true) {
1234
1235
	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...
1236
		$desc = description_table($table, $serveur);
1237
	}
1238
	if (!$desc) {
1239
		$tab_couples = array();
1240
	}
1241
	$fields = isset($desc['field']) ? $desc['field'] : array();
1242
1243
	$cles = "(" . join(',', array_keys(reset($tab_couples))) . ')';
1244
	$valeurs = array();
1245
	$r = false;
1246
1247
	// Quoter et Inserer par groupes de 100 max pour eviter un debordement de pile
1248
	foreach ($tab_couples as $couples) {
1249
		foreach ($couples as $champ => $val) {
1250
			$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
1251
		}
1252
		$valeurs[] = '(' . join(',', $couples) . ')';
1253 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...
1254
			$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 1236 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...
1255
			$valeurs = array();
1256
		}
1257
	}
1258 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...
1259
		$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 1236 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...
1260
	}
1261
1262
	return $r; // dans le cas d'une table auto_increment, le dernier insert_id
1263
}
1264
1265
/**
1266
 * Met à jour des enregistrements d'une table SQL
1267
 *
1268
 * @param string $table
1269
 *     Nom de la table
1270
 * @param array $champs
1271
 *     Couples (colonne => valeur)
1272
 * @param string|array $where
1273
 *     Conditions a remplir (Where)
1274
 * @param array $desc
1275
 *     Tableau de description des colonnes de la table SQL utilisée
1276
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1277
 * @param string $serveur
1278
 *     Nom de la connexion
1279
 * @param bool $requeter
1280
 *     Exécuter la requête, sinon la retourner
1281
 * @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...
1282
 *     - string : texte de la requête si demandé
1283
 *     - true si la requête a réussie, false sinon
1284
 *     - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
1285
 */
1286
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...
1287
	$set = array();
1288
	foreach ($champs as $champ => $val) {
1289
		$set[] = $champ . "=$val";
1290
	}
1291 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...
1292
		return spip_mysql_query(
1293
			calculer_mysql_expression('UPDATE', $table, ',')
1294
			. calculer_mysql_expression('SET', $set, ',')
1295
			. calculer_mysql_expression('WHERE', $where),
1296
			$serveur, $requeter);
1297
	}
1298
}
1299
1300
/**
1301
 * Met à jour des enregistrements d'une table SQL et protège chaque valeur
1302
 *
1303
 * Protège chaque valeur transmise avec sql_quote(), adapté au type
1304
 * de champ attendu par la table SQL
1305
 *
1306
 * @note
1307
 *   Les valeurs sont des constantes à mettre entre apostrophes
1308
 *   sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
1309
 *
1310
 * @param string $table
1311
 *     Nom de la table
1312
 * @param array $champs
1313
 *     Couples (colonne => valeur)
1314
 * @param string|array $where
1315
 *     Conditions a remplir (Where)
1316
 * @param array $desc
1317
 *     Tableau de description des colonnes de la table SQL utilisée
1318
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1319
 * @param string $serveur
1320
 *     Nom de la connexion
1321
 * @param bool $requeter
1322
 *     Exécuter la requête, sinon la retourner
1323
 * @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...
1324
 *     - string : texte de la requête si demandé
1325
 *     - true si la requête a réussie, false sinon
1326
 *     - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
1327
 */
1328
function spip_mysql_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true) {
1329
1330
	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...
1331
		return;
1332
	}
1333
	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...
1334
		$desc = description_table($table, $serveur);
1335
	}
1336
	if (!$desc) {
1337
		$champs = array();
1338
	} else {
1339
		$fields = $desc['field'];
1340
	}
1341
	$set = array();
1342
	foreach ($champs as $champ => $val) {
1343
		$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...
1344
	}
1345
1346
	return spip_mysql_query(
1347
		calculer_mysql_expression('UPDATE', $table, ',')
1348
		. calculer_mysql_expression('SET', $set, ',')
1349
		. calculer_mysql_expression('WHERE', $where),
1350
		$serveur, $requeter);
1351
}
1352
1353
/**
1354
 * Supprime des enregistrements d'une table
1355
 *
1356
 * @param string $table Nom de la table SQL
1357
 * @param string|array $where Conditions à vérifier
1358
 * @param string $serveur Nom du connecteur
1359
 * @param bool $requeter Exécuter la requête, sinon la retourner
1360
 * @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...
1361
 *     - int : nombre de suppressions réalisées,
1362
 *     - Texte de la requête si demandé,
1363
 *     - False en cas d'erreur.
1364
 **/
1365
function spip_mysql_delete($table, $where = '', $serveur = '', $requeter = true) {
1366
	$res = spip_mysql_query(
1367
		calculer_mysql_expression('DELETE FROM', $table, ',')
1368
		. calculer_mysql_expression('WHERE', $where),
1369
		$serveur, $requeter);
1370
	if (!$requeter) {
1371
		return $res;
1372
	}
1373
	if ($res) {
1374
		$link = _mysql_link($serveur);
1375
1376
		return mysqli_affected_rows($link);
1377
	} else {
1378
		return false;
1379
	}
1380
}
1381
1382
1383
/**
1384
 * Insère où met à jour une entrée d’une table SQL
1385
 *
1386
 * La clé ou les cles primaires doivent être présentes dans les données insérés.
1387
 * La fonction effectue une protection automatique des données.
1388
 *
1389
 * Préférez updateq ou insertq.
1390
 *
1391
 * @param string $table
1392
 *     Nom de la table SQL
1393
 * @param array $couples
1394
 *     Couples colonne / valeur à modifier,
1395
 * @param array $desc
1396
 *     Tableau de description des colonnes de la table SQL utilisée
1397
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1398
 * @param string $serveur
1399
 *     Nom du connecteur
1400
 * @param bool $requeter
1401
 *     Exécuter la requête, sinon la retourner
1402
 * @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...
1403
 *     - true si réussite
1404
 *     - Texte de la requête si demandé,
1405
 *     - False en cas d'erreur.
1406
 **/
1407
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...
1408
	return spip_mysql_query("REPLACE $table (" . join(',', array_keys($couples)) . ') VALUES (' . join(',',
1409
			array_map('_q', $couples)) . ')', $serveur, $requeter);
1410
}
1411
1412
1413
/**
1414
 * Insère où met à jour des entrées d’une table SQL
1415
 *
1416
 * La clé ou les cles primaires doivent être présentes dans les données insérés.
1417
 * La fonction effectue une protection automatique des données.
1418
 *
1419
 * Préférez insertq_multi et sql_updateq
1420
 *
1421
 * @param string $table
1422
 *     Nom de la table SQL
1423
 * @param array $tab_couples
1424
 *     Tableau de tableau (colonne / valeur à modifier),
1425
 * @param array $desc
1426
 *     Tableau de description des colonnes de la table SQL utilisée
1427
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1428
 * @param string $serveur
1429
 *     Nom du connecteur
1430
 * @param bool $requeter
1431
 *     Exécuter la requête, sinon la retourner
1432
 * @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...
1433
 *     - true si réussite
1434
 *     - Texte de la requête si demandé,
1435
 *     - False en cas d'erreur.
1436
 **/
1437
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...
1438
	$cles = "(" . join(',', array_keys($tab_couples[0])) . ')';
1439
	$valeurs = array();
1440
	foreach ($tab_couples as $couples) {
1441
		$valeurs[] = '(' . join(',', array_map('_q', $couples)) . ')';
1442
	}
1443
	$valeurs = implode(', ', $valeurs);
1444
1445
	return spip_mysql_query("REPLACE $table $cles VALUES $valeurs", $serveur, $requeter);
1446
}
1447
1448
1449
/**
1450
 * Retourne l'instruction SQL pour obtenir le texte d'un champ contenant
1451
 * une balise `<multi>` dans la langue indiquée
1452
 *
1453
 * Cette sélection est mise dans l'alias `multi` (instruction AS multi).
1454
 *
1455
 * @param string $objet Colonne ayant le texte
1456
 * @param string $lang Langue à extraire
1457
 * @return string       Texte de sélection pour la requête
1458
 */
1459
function spip_mysql_multi($objet, $lang) {
1460
	$lengthlang = strlen("[$lang]");
1461
	$posmulti = "INSTR(" . $objet . ", '<multi>')";
1462
	$posfinmulti = "INSTR(" . $objet . ", '</multi>')";
1463
	$debutchaine = "LEFT(" . $objet . ", $posmulti-1)";
1464
	$finchaine = "RIGHT(" . $objet . ", CHAR_LENGTH(" . $objet . ") -(7+$posfinmulti))";
1465
	$chainemulti = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7, $posfinmulti -(7+$posmulti)))";
1466
	$poslang = "INSTR($chainemulti,'[" . $lang . "]')";
1467
	$poslang = "IF($poslang=0,INSTR($chainemulti,']')+1,$poslang+$lengthlang)";
1468
	$chainelang = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7+$poslang-1,$posfinmulti -($posmulti+7+$poslang-1) ))";
1469
	$posfinlang = "INSTR(" . $chainelang . ", '[')";
1470
	$chainelang = "IF($posfinlang>0,LEFT($chainelang,$posfinlang-1),$chainelang)";
1471
	//$chainelang = "LEFT($chainelang,$posfinlang-1)";
1472
	$retour = "(TRIM(IF($posmulti = 0 , " .
1473
		"     TRIM(" . $objet . "), " .
1474
		"     CONCAT( " .
1475
		"          $debutchaine, " .
1476
		"          IF( " .
1477
		"               $poslang = 0, " .
1478
		"                     $chainemulti, " .
1479
		"               $chainelang" .
1480
		"          ), " .
1481
		"          $finchaine" .
1482
		"     ) " .
1483
		"))) AS multi";
1484
1485
	return $retour;
1486
}
1487
1488
/**
1489
 * Prépare une chaîne hexadécimale
1490
 *
1491
 * Par exemple : FF ==> 0xFF en MySQL
1492
 *
1493
 * @param string $v
1494
 *     Chaine hexadecimale
1495
 * @return string
1496
 *     Valeur hexadécimale pour MySQL
1497
 **/
1498
function spip_mysql_hex($v) {
1499
	return "0x" . $v;
1500
}
1501
1502
/**
1503
 * Échapper une valeur selon son type ou au mieux
1504
 * comme le fait `_q()` mais pour MySQL avec ses spécificités
1505
 *
1506
 * @param string|array|number $v
1507
 *     Texte, nombre ou tableau à échapper
1508
 * @param string $type
1509
 *     Description du type attendu
1510
 *    (par exemple description SQL de la colonne recevant la donnée)
1511
 * @return string|number
0 ignored issues
show
Documentation introduced by
Should the return type not be string|integer|double|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...
1512
 *    Donnée prête à être utilisée par le gestionnaire SQL
1513
 */
1514 View Code Duplication
function spip_mysql_quote($v, $type = '') {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

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

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

Loading history...
1515
	if ($type) {
1516
		if (!is_array($v)) {
1517
			return spip_mysql_cite($v, $type);
1518
		}
1519
		// si c'est un tableau, le parcourir en propageant le type
1520
		foreach ($v as $k => $r) {
1521
			$v[$k] = spip_mysql_quote($r, $type);
1522
		}
1523
1524
		return $v;
1525
	}
1526
	// si on ne connait pas le type, s'en remettre a _q :
1527
	// on ne fera pas mieux
1528
	else {
1529
		return _q($v);
1530
	}
1531
}
1532
1533
/**
1534
 * Tester si une date est proche de la valeur d'un champ
1535
 *
1536
 * @param string $champ
1537
 *     Nom du champ a tester
1538
 * @param int $interval
1539
 *     Valeur de l'intervalle : -1, 4, ...
1540
 * @param string $unite
1541
 *     Utité utilisée (DAY, MONTH, YEAR, ...)
1542
 * @return string
1543
 *     Expression SQL
1544
 **/
1545 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...
1546
	return '('
1547
	. $champ
1548
	. (($interval <= 0) ? '>' : '<')
1549
	. (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
1550
	. '('
1551
	. sql_quote(date('Y-m-d H:i:s'))
1552
	. ', INTERVAL '
1553
	. (($interval > 0) ? $interval : (0 - $interval))
1554
	. ' '
1555
	. $unite
1556
	. '))';
1557
}
1558
1559
1560
/**
1561
 * Retourne une expression IN pour le gestionnaire de base de données
1562
 *
1563
 * IN (...) est limité à 255 éléments, d'où cette fonction assistante
1564
 *
1565
 * @param string $val
1566
 *     Colonne SQL sur laquelle appliquer le test
1567
 * @param string|array $valeurs
1568
 *     Liste des valeurs possibles (séparés par des virgules si string)
1569
 * @param string $not
1570
 *     - '' sélectionne les éléments correspondant aux valeurs
1571
 *     - 'NOT' inverse en sélectionnant les éléments ne correspondant pas aux valeurs
1572
 * @param string $serveur
1573
 *     Nom du connecteur
1574
 * @param bool $requeter
1575
 *     Inutilisé
1576
 * @return string
1577
 *     Expression de requête SQL
1578
 **/
1579 View Code Duplication
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...
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...
1580
	$n = $i = 0;
1581
	$in_sql = "";
1582
	while ($n = strpos($valeurs, ',', $n + 1)) {
1583
		if ((++$i) >= 255) {
1584
			$in_sql .= "($val $not IN (" .
1585
				substr($valeurs, 0, $n) .
1586
				"))\n" .
1587
				($not ? "AND\t" : "OR\t");
1588
			$valeurs = substr($valeurs, $n + 1);
1589
			$i = $n = 0;
1590
		}
1591
	}
1592
	$in_sql .= "($val $not IN ($valeurs))";
1593
1594
	return "($in_sql)";
1595
}
1596
1597
1598
/**
1599
 * Retourne une expression IN pour le gestionnaire de base de données
1600
 *
1601
 * Pour compatibilité. Ne plus utiliser.
1602
 *
1603
 * @deprecated Utiliser sql_in()
1604
 *
1605
 * @param string $val Nom de la colonne
1606
 * @param string|array $valeurs Valeurs
1607
 * @param string $not NOT pour inverser
1608
 * @return string               Expression de requête SQL
1609
 */
1610
function calcul_mysql_in($val, $valeurs, $not = '') {
1611
	if (is_array($valeurs)) {
1612
		$valeurs = join(',', array_map('_q', $valeurs));
1613
	} elseif ($valeurs[0] === ',') {
1614
		$valeurs = substr($valeurs, 1);
1615
	}
1616
	if (!strlen(trim($valeurs))) {
1617
		return ($not ? "0=0" : '0=1');
1618
	}
1619
1620
	return spip_mysql_in($val, $valeurs, $not);
1621
}
1622
1623
1624
/**
1625
 * Renvoie les bons echappements (mais pas sur les fonctions comme NOW())
1626
 *
1627
 * @param string|number $v Texte ou nombre à échapper
1628
 * @param string $type Type de donnée attendue, description SQL de la colonne de destination
1629
 * @return string|number     Texte ou nombre échappé
1630
 */
1631
function spip_mysql_cite($v, $type) {
1632
	if (is_null($v)
1633
		and stripos($type, "NOT NULL") === false
1634
	) {
1635
		return 'NULL';
1636
	} // null php se traduit en NULL SQL
1637
	if (sql_test_date($type) and preg_match('/^\w+\(/', $v)) {
1638
		return $v;
1639
	}
1640
	if (sql_test_int($type)) {
1641
		if (is_numeric($v) or (ctype_xdigit(substr($v, 2))
1642
				and $v[0] == '0' and $v[1] == 'x')
1643
		) {
1644
			return $v;
1645
		} // si pas numerique, forcer le intval
1646
		else {
1647
			return intval($v);
1648
		}
1649
	}
1650
1651
	return ("'" . addslashes($v) . "'");
1652
}
1653
1654
1655
// Ces deux fonctions n'ont pas d'equivalent exact PostGres
1656
// et ne sont la que pour compatibilite avec les extensions de SPIP < 1.9.3
1657
1658
/**
1659
 * Poser un verrou SQL local
1660
 *
1661
 * Changer de nom toutes les heures en cas de blocage MySQL (ca arrive)
1662
 *
1663
 * @deprecated Pas d'équivalence actuellement en dehors de MySQL
1664
 * @see spip_release_lock()
1665
 *
1666
 * @param string $nom
1667
 *     Inutilisé. Le nom est calculé en fonction de la connexion principale
1668
 * @param int $timeout
1669
 * @return string|bool
1670
 *     - Nom du verrou si réussite,
1671
 *     - false sinon
1672
 */
1673
function spip_get_lock($nom, $timeout = 0) {
1674
1675
	define('_LOCK_TIME', intval(time() / 3600 - 316982));
1676
1677
	$connexion = &$GLOBALS['connexions'][0];
1678
	$bd = $connexion['db'];
1679
	$prefixe = $connexion['prefixe'];
1680
	$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
1681
1682
	$connexion['last'] = $q = "SELECT GET_LOCK(" . _q($nom) . ", $timeout) AS n";
1683
1684
	$q = @sql_fetch(mysqli_query(_mysql_link(), $q));
1685
	if (!$q) {
1686
		spip_log("pas de lock sql pour $nom", _LOG_ERREUR);
1687
	}
1688
1689
	return $q['n'];
1690
}
1691
1692
1693
/**
1694
 * Relâcher un verrou SQL local
1695
 *
1696
 * @deprecated Pas d'équivalence actuellement en dehors de MySQL
1697
 * @see spip_get_lock()
1698
 *
1699
 * @param string $nom
1700
 *     Inutilisé. Le nom est calculé en fonction de la connexion principale
1701
 * @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...
1702
 *     True si réussite, false sinon.
1703
 */
1704
function spip_release_lock($nom) {
1705
1706
	$connexion = &$GLOBALS['connexions'][0];
1707
	$bd = $connexion['db'];
1708
	$prefixe = $connexion['prefixe'];
1709
	$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
1710
1711
	$connexion['last'] = $q = "SELECT RELEASE_LOCK(" . _q($nom) . ")";
1712
	mysqli_query(_mysql_link(), $q);
1713
}
1714
1715
1716
/**
1717
 * Teste si on a les fonctions MySQLi (pour l'install)
1718
 *
1719
 * @return bool
1720
 *     True si on a les fonctions, false sinon
1721
 */
1722
function spip_versions_mysql() {
1723
	return function_exists('mysqli_query');
1724
}
1725
1726
1727
/**
1728
 * Tester si mysql ne veut pas du nom de la base dans les requêtes
1729
 *
1730
 * @param string $server_db
1731
 * @return string
1732
 *     - chaîne vide si nom de la base utile
1733
 *     - chaîne : code compilé pour le faire désactiver par SPIP sinon
1734
 */
1735
function test_rappel_nom_base_mysql($server_db) {
1736
	$GLOBALS['mysql_rappel_nom_base'] = true;
1737
	sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1738
	$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...
1739
1740
	if ($ok) {
1741
		sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1742
1743
		return '';
1744
	} else {
1745
		$GLOBALS['mysql_rappel_nom_base'] = false;
1746
1747
		return "\$GLOBALS['mysql_rappel_nom_base'] = false; " .
1748
		"/* echec de test_rappel_nom_base_mysql a l'installation. */\n";
1749
	}
1750
}
1751
1752
/**
1753
 * Teste si on peut changer les modes de MySQL
1754
 *
1755
 * @link http://dev.mysql.com/doc/refman/5.0/fr/server-sql-mode.html
1756
 *
1757
 * @param string $server_db Nom de la connexion
1758
 * @return string
1759
 *     - chaîne vide si on ne peut pas appliquer de mode
1760
 *     - chaîne : code compilé pour l'indiquer le résultat du test à SPIP
1761
 */
1762
function test_sql_mode_mysql($server_db) {
1763
	$res = sql_select("version() as v", '', '', '', '', '', '', $server_db);
1764
	$row = sql_fetch($res, $server_db);
1765
	if (version_compare($row['v'], '5.0.0', '>=')) {
1766
		defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE', true);
1767
1768
		return "defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE',true);\n";
1769
	}
1770
1771
	return '';
1772
}
1773