Completed
Push — master ( d58a44...9f5c8b )
by cam
04:31
created

mysql.php ➔ spip_mysql_query()   F

Complexity

Conditions 16
Paths 834

Size

Total Lines 63

Duplication

Lines 6
Ratio 9.52 %

Importance

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

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

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

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

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

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

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

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

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

Loading history...
183
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
184
	$connexion['last'] = $c = "SHOW CHARACTER SET"
185
		. (!$charset ? '' : (" LIKE " . _q($charset['charset'])));
186
187
	return spip_mysql_fetch(mysqli_query($connexion['link'], $c), null, $serveur);
188
}
189
190
191
/**
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
		if (isset($_SERVER['REQUEST_URI'])) {
249
			$debug .= $_SERVER['REQUEST_URI'];
250
		}
251
		if (!empty($GLOBALS['ip'])) {
252
			$debug .= ' + ' . $GLOBALS['ip'];
253
		}
254
		$debug = ' /* ' . mysqli_real_escape_string($link, str_replace('*/', '@/', $debug)) . ' */';
255
	}
256
257
	$r = mysqli_query($link, $query . $debug);
258
259
	//Eviter de propager le GoneAway sur les autres requetes d'un même processus PHP
260
	if ($e = spip_mysql_errno($serveur)) {  // Log d'un Gone Away
261
		if ($e == 2006) { //Si Gone Away on relance une connexion vierge
262
			//Fermer la connexion defaillante
263
			mysqli_close($connexion['link']);
264
			unset($GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]);
265
			//Relancer une connexion vierge
266
			spip_connect($serveur);
267
			$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
268
			$link = $connexion['link'];
269
			//On retente au cas où
270
			$r = mysqli_query($link, $query . $debug);
271
		}
272
	}
273
274
	// Log de l'erreur eventuelle
275
	if ($e = spip_mysql_errno($serveur)) {
276
		$e .= spip_mysql_error($query, $serveur);
277
	} // et du fautif
278
	return $t ? trace_query_end($query, $t, $r, $e, $serveur) : $r;
279
}
280
281
/**
282
 * Modifie une structure de table MySQL
283
 *
284
 * @param string $query Requête SQL (sans 'ALTER ')
285
 * @param string $serveur Nom de la connexion
286
 * @param bool $requeter Exécuter la requête, sinon la retourner
287
 * @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...
288
 *     - string : Texte de la requête si on ne l'exécute pas
289
 *     - bool   : Si requête exécutée
290
 *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
291
 */
292
function spip_mysql_alter($query, $serveur = '', $requeter = true) {
293
	// ici on supprime les ` entourant le nom de table pour permettre
294
	// la transposition du prefixe, compte tenu que les plugins ont la mauvaise habitude
295
	// d'utiliser ceux-ci, copie-colle de phpmyadmin
296
	$query = preg_replace(",^TABLE\s*`([^`]*)`,i", "TABLE \\1", $query);
297
298
	return spip_mysql_query("ALTER " . $query, $serveur, $requeter); # i.e. que PG se debrouille
299
}
300
301
302
/**
303
 * Optimise une table MySQL
304
 *
305
 * @param string $table Nom de la table
306
 * @param string $serveur Nom de la connexion
307
 * @param bool $requeter inutilisé
308
 * @return bool            Toujours true
309
 */
310
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...
311
	spip_mysql_query("OPTIMIZE TABLE " . $table);
312
313
	return true;
314
}
315
316
317
/**
318
 * Retourne une explication de requête (Explain) MySQL
319
 *
320
 * @param string $query Texte de la requête
321
 * @param string $serveur Nom de la connexion
322
 * @param bool $requeter inutilisé
323
 * @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...
324
 */
325
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...
326
	if (strpos(ltrim($query), 'SELECT') !== 0) {
327
		return array();
328
	}
329
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
330
	$prefixe = $connexion['prefixe'];
331
	$link = $connexion['link'];
332
	$db = $connexion['db'];
333
334
	$query = 'EXPLAIN ' . _mysql_traite_query($query, $db, $prefixe);
335
	$r = mysqli_query($link, $query);
336
337
	return spip_mysql_fetch($r, null, $serveur);
338
}
339
340
341
/**
342
 * Exécute une requête de sélection avec MySQL
343
 *
344
 * Instance de sql_select (voir ses specs).
345
 *
346
 * @see sql_select()
347
 * @note
348
 *     Les `\n` et `\t` sont utiles au debusqueur.
349
 *
350
 * @param string|array $select Champs sélectionnés
351
 * @param string|array $from Tables sélectionnées
352
 * @param string|array $where Contraintes
353
 * @param string|array $groupby Regroupements
354
 * @param string|array $orderby Tris
355
 * @param string $limit Limites de résultats
356
 * @param string|array $having Contraintes posts sélections
357
 * @param string $serveur Nom de la connexion
358
 * @param bool $requeter Exécuter la requête, sinon la retourner
359
 * @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...
360
 *     - string : Texte de la requête si on ne l'exécute pas
361
 *     - ressource si requête exécutée, ressource pour fetch()
362
 *     - false si la requête exécutée a ratée
363
 *     - array  : Tableau décrivant requête et temps d'exécution si var_profile actif pour tracer.
364
 */
365
function spip_mysql_select(
366
	$select,
367
	$from,
368
	$where = '',
369
	$groupby = '',
370
	$orderby = '',
371
	$limit = '',
372
	$having = '',
373
	$serveur = '',
374
	$requeter = true
375
) {
376
377
378
	$from = (!is_array($from) ? $from : spip_mysql_select_as($from));
379
	$query =
380
		calculer_mysql_expression('SELECT', $select, ', ')
381
		. calculer_mysql_expression('FROM', $from, ', ')
382
		. calculer_mysql_expression('WHERE', $where)
383
		. calculer_mysql_expression('GROUP BY', $groupby, ',')
384
		. calculer_mysql_expression('HAVING', $having)
385
		. ($orderby ? ("\nORDER BY " . spip_mysql_order($orderby)) : '')
386
		. ($limit ? "\nLIMIT $limit" : '');
387
388
	// renvoyer la requete inerte si demandee
389
	if ($requeter === false) {
390
		return $query;
391
	}
392
	$r = spip_mysql_query($query, $serveur, $requeter);
393
394
	return $r ? $r : $query;
395
}
396
397
398
/**
399
 * Prépare une clause order by
400
 *
401
 * Regroupe en texte les éléments si un tableau est donné
402
 *
403
 * @note
404
 *   0+x avec un champ x commencant par des chiffres est converti par MySQL
405
 *   en le nombre qui commence x. Pas portable malheureusement, on laisse pour le moment.
406
 *
407
 * @param string|array $orderby Texte du orderby à préparer
408
 * @return string Texte du orderby préparé
409
 */
410
function spip_mysql_order($orderby) {
411
	return (is_array($orderby)) ? join(", ", $orderby) : $orderby;
412
}
413
414
415
/**
416
 * Prépare une clause WHERE pour MySQL
417
 *
418
 * Retourne une chaîne avec les bonnes parenthèses pour la
419
 * contrainte indiquée, au format donnée par le compilateur
420
 *
421
 * @param array|string $v
422
 *     Description des contraintes
423
 *     - string : Texte du where
424
 *     - sinon tableau : A et B peuvent être de type string ou array,
425
 *       OP et C sont de type string :
426
 *       - array(A) : A est le texte du where
427
 *       - array(OP, A) : contrainte OP( A )
428
 *       - array(OP, A, B) : contrainte (A OP B)
429
 *       - array(OP, A, B, C) : contrainte (A OP (B) : C)
430
 * @return string
431
 *     Contrainte pour clause WHERE
432
 */
433 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...
434
	if (!is_array($v)) {
435
		return $v;
436
	}
437
438
	$op = array_shift($v);
439
	if (!($n = count($v))) {
440
		return $op;
441
	} else {
442
		$arg = calculer_mysql_where(array_shift($v));
443
		if ($n == 1) {
444
			return "$op($arg)";
445
		} else {
446
			$arg2 = calculer_mysql_where(array_shift($v));
447
			if ($n == 2) {
448
				return "($arg $op $arg2)";
449
			} else {
450
				return "($arg $op ($arg2) : $v[0])";
451
			}
452
		}
453
	}
454
}
455
456
/**
457
 * Calcule un expression pour une requête, en cumulant chaque élément
458
 * avec l'opérateur de liaison ($join) indiqué
459
 *
460
 * Renvoie grosso modo "$expression join($join, $v)"
461
 *
462
 * @param string $expression Mot clé de l'expression, tel que "WHERE" ou "ORDER BY"
463
 * @param array|string $v Données de l'expression
464
 * @param string $join Si les données sont un tableau, elles seront groupées par cette jointure
465
 * @return string            Texte de l'expression, une partie donc, du texte la requête.
466
 */
467 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...
468
	if (empty($v)) {
469
		return '';
470
	}
471
472
	$exp = "\n$expression ";
473
474
	if (!is_array($v)) {
475
		return $exp . $v;
476
	} else {
477
		if (strtoupper($join) === 'AND') {
478
			return $exp . join("\n\t$join ", array_map('calculer_mysql_where', $v));
479
		} else {
480
			return $exp . join($join, $v);
481
		}
482
	}
483
}
484
485
486
/**
487
 * Renvoie des `nom AS alias`
488
 *
489
 * @param array $args
490
 * @return string Sélection de colonnes pour une clause SELECT
491
 */
492 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...
493
	$res = '';
494
	foreach ($args as $k => $v) {
495
		if (substr($k, -1) == '@') {
496
			// c'est une jointure qui se refere au from precedent
497
			// pas de virgule
498
			$res .= '  ' . $v;
499
		} else {
500
			if (!is_numeric($k)) {
501
				$p = strpos($v, " ");
502
				if ($p) {
503
					$v = substr($v, 0, $p) . " AS `$k`" . substr($v, $p);
504
				} else {
505
					$v .= " AS `$k`";
506
				}
507
			}
508
			$res .= ', ' . $v;
509
		}
510
	}
511
512
	return substr($res, 2);
513
}
514
515
516
/**
517
 * Changer les noms des tables ($table_prefix)
518
 *
519
 * TODO: Quand tous les appels SQL seront abstraits on pourra l'améliorer
520
 */
521
define('_SQL_PREFIXE_TABLE_MYSQL', '/([,\s])spip_/S');
522
523
524
/**
525
 * Prépare le texte d'une requête avant son exécution
526
 *
527
 * Change les préfixes de tables SPIP par ceux véritables
528
 *
529
 * @param string $query Requête à préparer
530
 * @param string $db Nom de la base de donnée
531
 * @param string $prefixe Préfixe de tables à appliquer
532
 * @return string           Requête préparée
533
 */
534
function _mysql_traite_query($query, $db = '', $prefixe = '') {
535
536
	if ($GLOBALS['mysql_rappel_nom_base'] and $db) {
537
		$pref = '`' . $db . '`.';
538
	} else {
539
		$pref = '';
540
	}
541
542
	if ($prefixe) {
543
		$pref .= $prefixe . "_";
544
	}
545
546
	if (!preg_match('/\s(SET|VALUES|WHERE|DATABASE)\s/i', $query, $regs)) {
547
		$suite = '';
548
	} else {
549
		$suite = strstr($query, $regs[0]);
550
		$query = substr($query, 0, -strlen($suite));
551
		// propager le prefixe en cas de requete imbriquee
552
		// il faut alors echapper les chaine avant de le faire, pour ne pas risquer de
553
		// modifier une requete qui est en fait juste du texte dans un champ
554
		if (stripos($suite, "SELECT") !== false) {
555
			list($suite, $textes) = query_echappe_textes($suite);
556
			if (preg_match('/^(.*?)([(]\s*SELECT\b.*)$/si', $suite, $r)) {
557
				$suite = $r[1] . _mysql_traite_query($r[2], $db, $prefixe);
558
			}
559
			$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...
560
		}
561
	}
562
	$r = preg_replace(_SQL_PREFIXE_TABLE_MYSQL, '\1' . $pref, $query) . $suite;
563
564
	// en option, remplacer les emoji (que mysql ne sait pas gérer) en &#128169;
565
	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...
566
		include_spip('inc/charsets');
567
		$r = utf8_noplanes($r);
568
	}
569
570
	#spip_log("_mysql_traite_query: " . substr($r,0, 50) . ".... $db, $prefixe", _LOG_DEBUG);
571
	return $r;
572
}
573
574
/**
575
 * Sélectionne une base de données
576
 *
577
 * @param string $db
578
 *     Nom de la base à utiliser
579
 * @param string $serveur
580
 *     Nom du connecteur
581
 * @param bool $requeter
582
 *     Inutilisé
583
 *
584
 * @return bool
585
 *     - True cas de succès.
586
 *     - False en cas d'erreur.
587
 **/
588
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...
589
	$link = _mysql_link($serveur);
590
	$ok = mysqli_select_db($link, $db);
591
	if (!$ok) {
592
		spip_log('Echec mysqli_selectdb. Erreur : ' . mysqli_error($link), 'mysql.' . _LOG_CRITIQUE);
593
	}
594
595
	return $ok;
596
}
597
598
599
/**
600
 * Retourne les bases de données accessibles
601
 *
602
 * Retourne un tableau du nom de toutes les bases de données
603
 * accessibles avec les permissions de l'utilisateur SQL
604
 * de cette connexion.
605
 *
606
 * Attention on n'a pas toujours les droits !
607
 *
608
 * @param string $serveur
609
 *     Nom du connecteur
610
 * @param bool $requeter
611
 *     Inutilisé
612
 * @return array
613
 *     Liste de noms de bases de données
614
 **/
615
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...
616
	$dbs = array();
617
	if ($res = spip_mysql_query("SHOW DATABASES", $serveur)) {
618
		while ($row = mysqli_fetch_assoc($res)) {
619
			$dbs[] = $row['Database'];
620
		}
621
	}
622
623
	return $dbs;
624
}
625
626
627
/**
628
 * Crée une table SQL
629
 *
630
 * Crée une table SQL nommee `$nom` à partir des 2 tableaux `$champs` et `$cles`
631
 *
632
 * @note Le nom des caches doit être inferieur à 64 caractères
633
 *
634
 * @param string $nom Nom de la table SQL
635
 * @param array $champs Couples (champ => description SQL)
636
 * @param array $cles Couples (type de clé => champ(s) de la clé)
637
 * @param bool $autoinc True pour ajouter un auto-incrément sur la Primary Key
638
 * @param bool $temporary True pour créer une table temporaire
639
 * @param string $serveur Nom de la connexion
640
 * @param bool $requeter inutilisé
641
 * @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...
642
 *     - null si champs ou cles n'est pas un tableau
643
 *     - true si la requête réussie, false sinon.
644
 */
645
function spip_mysql_create(
646
	$nom,
647
	$champs,
648
	$cles,
649
	$autoinc = false,
650
	$temporary = false,
651
	$serveur = '',
652
	$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...
653
) {
654
655
	$query = '';
656
	$keys = '';
657
	$s = '';
658
	$p = '';
659
660
	// certains plugins declarent les tables  (permet leur inclusion dans le dump)
661
	// sans les renseigner (laisse le compilo recuperer la description)
662
	if (!is_array($champs) || !is_array($cles)) {
663
		return;
664
	}
665
666
	$res = spip_mysql_query("SELECT version() as v", $serveur);
667
	if (($row = mysqli_fetch_array($res)) && (version_compare($row['v'], '5.0', '>='))) {
668
		spip_mysql_query("SET sql_mode=''", $serveur);
669
	}
670
671
	foreach ($cles as $k => $v) {
672
		$keys .= "$s\n\t\t$k ($v)";
673
		if ($k == "PRIMARY KEY") {
674
			$p = $v;
675
		}
676
		$s = ",";
677
	}
678
	$s = '';
679
680
	$character_set = "";
681 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...
682
		$character_set .= " CHARACTER SET " . $GLOBALS['meta']['charset_sql_base'];
683
	}
684 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...
685
		$character_set .= " COLLATE " . $GLOBALS['meta']['charset_collation_sql_base'];
686
	}
687
688
	foreach ($champs as $k => $v) {
689
		$v = _mysql_remplacements_definitions_table($v);
690 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...
691
			if (preg_match(',(char|text),i', $defs[1])
692
				and !preg_match(',(binary|CHARACTER|COLLATE),i', $v)
693
			) {
694
				$v = $defs[1] . $character_set . ' ' . substr($v, strlen($defs[1]));
695
			}
696
		}
697
698
		$query .= "$s\n\t\t$k $v"
699
			. (($autoinc && ($p == $k) && preg_match(',\b(big|small|medium)?int\b,i', $v))
700
				? " auto_increment"
701
				: ''
702
			);
703
		$s = ",";
704
	}
705
	$temporary = $temporary ? 'TEMPORARY' : '';
706
	$q = "CREATE $temporary TABLE IF NOT EXISTS $nom ($query" . ($keys ? ",$keys" : '') . ")"
707
		. " ENGINE=MyISAM"
708
		. ($character_set ? " DEFAULT $character_set" : "")
709
		. "\n";
710
711
	return spip_mysql_query($q, $serveur);
712
}
713
714
715
/**
716
 * Adapte pour Mysql la déclaration SQL d'une colonne d'une table
717
 *
718
 * @param string|array $query
719
 *     Définition SQL d'un champ de table ou liste de déclarations
720
 * @return string|array
721
 *     Définition SQL adaptée pour MySQL d'un champ de table
722
 */
723
function _mysql_remplacements_definitions_table($query) {
724
	// quelques remplacements
725
	$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...
726
	$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...
727
728
	$remplace = array(
729
		'/VARCHAR(\s*[^\s\(])/is' => 'VARCHAR(255)\\1',
730
		'/^TIMESTAMP($| NULL DEFAULT NULL)/is' => 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
731
	);
732
733
	if (is_string($query)) {
734
		$query = preg_replace(array_keys($remplace), $remplace, $query);
735
	} elseif (is_array($query)) {
736
		$keys = array_keys($remplace);
737
		foreach ($query as $k => $q) {
738
			$query[$k] = preg_replace($keys, $remplace, $q);
739
		}
740
	}
741
742
	return $query;
743
}
744
745
746
/**
747
 * Crée une base de données MySQL
748
 *
749
 * @param string $nom Nom de la base
750
 * @param string $serveur Nom de la connexion
751
 * @param bool $requeter Exécuter la requête, sinon la retourner
752
 * @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...
753
 **/
754
function spip_mysql_create_base($nom, $serveur = '', $requeter = true) {
755
	return spip_mysql_query("CREATE DATABASE `$nom`", $serveur, $requeter);
756
}
757
758
759
/**
760
 * Crée une vue SQL nommée `$nom`
761
 *
762
 * @param string $nom
763
 *    Nom de la vue à creer
764
 * @param string $query_select
765
 *     Texte de la requête de sélection servant de base à la vue
766
 * @param string $serveur
767
 *     Nom du connecteur
768
 * @param bool $requeter
769
 *     Effectuer la requete, sinon la retourner
770
 * @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...
771
 *     - true si la vue est créée
772
 *     - false si erreur ou si la vue existe déja
773
 *     - string texte de la requête si $requeter vaut false
774
 */
775 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...
776
	if (!$query_select) {
777
		return false;
778
	}
779
	// vue deja presente
780
	if (sql_showtable($nom, false, $serveur)) {
781
		spip_log("Echec creation d'une vue sql ($nom) car celle-ci existe deja (serveur:$serveur)", _LOG_ERREUR);
782
783
		return false;
784
	}
785
786
	$query = "CREATE VIEW $nom AS " . $query_select;
787
788
	return spip_mysql_query($query, $serveur, $requeter);
789
}
790
791
792
/**
793
 * Supprime une table SQL
794
 *
795
 * @param string $table Nom de la table SQL
796
 * @param string $exist True pour ajouter un test d'existence avant de supprimer
797
 * @param string $serveur Nom de la connexion
798
 * @param bool $requeter Exécuter la requête, sinon la retourner
799
 * @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...
800
 *     - string Texte de la requête si demandé
801
 *     - true si la requête a réussie, false sinon
802
 */
803
function spip_mysql_drop_table($table, $exist = '', $serveur = '', $requeter = true) {
804
	if ($exist) {
805
		$exist = " IF EXISTS";
806
	}
807
808
	return spip_mysql_query("DROP TABLE$exist $table", $serveur, $requeter);
809
}
810
811
/**
812
 * Supprime une vue SQL
813
 *
814
 * @param string $view Nom de la vue SQL
815
 * @param string $exist True pour ajouter un test d'existence avant de supprimer
816
 * @param string $serveur Nom de la connexion
817
 * @param bool $requeter Exécuter la requête, sinon la retourner
818
 * @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...
819
 *     - string Texte de la requête si demandé
820
 *     - true si la requête a réussie, false sinon
821
 */
822
function spip_mysql_drop_view($view, $exist = '', $serveur = '', $requeter = true) {
823
	if ($exist) {
824
		$exist = " IF EXISTS";
825
	}
826
827
	return spip_mysql_query("DROP VIEW$exist $view", $serveur, $requeter);
828
}
829
830
/**
831
 * Retourne une ressource de la liste des tables de la base de données
832
 *
833
 * @param string $match
834
 *     Filtre sur tables à récupérer
835
 * @param string $serveur
836
 *     Connecteur de la base
837
 * @param bool $requeter
838
 *     true pour éxecuter la requête
839
 *     false pour retourner le texte de la requête.
840
 * @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...
841
 *     Ressource à utiliser avec sql_fetch()
842
 **/
843
function spip_mysql_showbase($match, $serveur = '', $requeter = true) {
844
	return spip_mysql_query("SHOW TABLES LIKE " . _q($match), $serveur, $requeter);
845
}
846
847
/**
848
 * Répare une table SQL
849
 *
850
 * Utilise `REPAIR TABLE ...` de MySQL
851
 *
852
 * @param string $table Nom de la table SQL
853
 * @param string $serveur Nom de la connexion
854
 * @param bool $requeter Exécuter la requête, sinon la retourner
855
 * @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...
856
 *     - string Texte de la requête si demandée,
857
 *     - true si la requête a réussie, false sinon
858
 */
859
function spip_mysql_repair($table, $serveur = '', $requeter = true) {
860
	return spip_mysql_query("REPAIR TABLE `$table`", $serveur, $requeter);
861
}
862
863
864
define('_MYSQL_RE_SHOW_TABLE', '/^[^(),]*\(((?:[^()]*\((?:[^()]*\([^()]*\))?[^()]*\)[^()]*)*[^()]*)\)[^()]*$/');
865
/**
866
 * Obtient la description d'une table ou vue MySQL
867
 *
868
 * Récupère la définition d'une table ou d'une vue avec colonnes, indexes, etc.
869
 * au même format que la définition des tables SPIP, c'est à dire
870
 * un tableau avec les clés
871
 *
872
 * - `field` (tableau colonne => description SQL) et
873
 * - `key` (tableau type de clé => colonnes)
874
 *
875
 * @param string $nom_table Nom de la table SQL
876
 * @param string $serveur Nom de la connexion
877
 * @param bool $requeter Exécuter la requête, sinon la retourner
878
 * @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...
879
 *     - chaîne vide si pas de description obtenue
880
 *     - string Texte de la requête si demandé
881
 *     - array description de la table sinon
882
 */
883
function spip_mysql_showtable($nom_table, $serveur = '', $requeter = true) {
884
	$s = spip_mysql_query("SHOW CREATE TABLE `$nom_table`", $serveur, $requeter);
885
	if (!$s) {
886
		return '';
887
	}
888
	if (!$requeter) {
889
		return $s;
890
	}
891
892
	list(, $a) = mysqli_fetch_array($s, MYSQLI_NUM);
893
	if (preg_match(_MYSQL_RE_SHOW_TABLE, $a, $r)) {
894
		$desc = $r[1];
895
		// extraction d'une KEY éventuelle en prenant garde de ne pas
896
		// relever un champ dont le nom contient KEY (ex. ID_WHISKEY)
897 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...
898
			$namedkeys = $r[2];
899
			$desc = $r[1];
900
		} else {
901
			$namedkeys = "";
902
		}
903
904
		$fields = array();
905
		foreach (preg_split("/,\s*`/", $desc) as $v) {
906
			preg_match("/^\s*`?([^`]*)`\s*(.*)/", $v, $r);
907
			$fields[strtolower($r[1])] = $r[2];
908
		}
909
		$keys = array();
910
911 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...
912
			if (preg_match("/^\s*([^(]*)\(([^(]*(\(\d+\))?)$/", $v, $r)) {
913
				$k = str_replace("`", '', trim($r[1]));
914
				$t = strtolower(str_replace("`", '', $r[2]));
915
				if ($k && !isset($keys[$k])) {
916
					$keys[$k] = $t;
917
				} else {
918
					$keys[] = $t;
919
				}
920
			}
921
		}
922
		spip_mysql_free($s);
923
924
		return array('field' => $fields, 'key' => $keys);
925
	}
926
927
	$res = spip_mysql_query("SHOW COLUMNS FROM `$nom_table`", $serveur);
928
	if ($res) {
929
		$nfields = array();
930
		$nkeys = array();
931
		while ($val = spip_mysql_fetch($res)) {
932
			$nfields[$val["Field"]] = $val['Type'];
933
			if ($val['Null'] == 'NO') {
934
				$nfields[$val["Field"]] .= ' NOT NULL';
935
			}
936
			if ($val['Default'] === '0' || $val['Default']) {
937
				if (preg_match('/[A-Z_]/', $val['Default'])) {
938
					$nfields[$val["Field"]] .= ' DEFAULT ' . $val['Default'];
939
				} else {
940
					$nfields[$val["Field"]] .= " DEFAULT '" . $val['Default'] . "'";
941
				}
942
			}
943
			if ($val['Extra']) {
944
				$nfields[$val["Field"]] .= ' ' . $val['Extra'];
945
			}
946
			if ($val['Key'] == 'PRI') {
947
				$nkeys['PRIMARY KEY'] = $val["Field"];
948
			} else {
949
				if ($val['Key'] == 'MUL') {
950
					$nkeys['KEY ' . $val["Field"]] = $val["Field"];
951
				} else {
952
					if ($val['Key'] == 'UNI') {
953
						$nkeys['UNIQUE KEY ' . $val["Field"]] = $val["Field"];
954
					}
955
				}
956
			}
957
		}
958
		spip_mysql_free($res);
959
960
		return array('field' => $nfields, 'key' => $nkeys);
961
	}
962
963
	return "";
964
}
965
966
967
/**
968
 * Rècupère une ligne de résultat
969
 *
970
 * Récupère la ligne suivante d'une ressource de résultat
971
 *
972
 * @param Ressource $r Ressource de résultat (issu de sql_select)
973
 * @param string $t Structure de résultat attendu (défaut MYSQLI_ASSOC)
974
 * @param string $serveur Nom de la connexion
975
 * @param bool $requeter Inutilisé
976
 * @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...
977
 */
978
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...
979
	if (!$t) {
980
		$t = MYSQLI_ASSOC;
981
	}
982
	if ($r) {
983
		return mysqli_fetch_array($r, $t);
984
	}
985
}
986
987
/**
988
 * Place le pointeur de résultat sur la position indiquée
989
 *
990
 * @param Ressource $r Ressource de résultat
991
 * @param int $row_number Position. Déplacer le pointeur à cette ligne
992
 * @param string $serveur Nom de la connexion
993
 * @param bool $requeter Inutilisé
994
 * @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...
995
 **/
996
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...
997
	if ($r and mysqli_num_rows($r)) {
998
		return mysqli_data_seek($r, $row_number);
999
	}
1000
}
1001
1002
1003
/**
1004
 * Retourne le nombre de lignes d'une sélection
1005
 *
1006
 * @param array|string $from Tables à consulter (From)
1007
 * @param array|string $where Conditions a remplir (Where)
1008
 * @param array|string $groupby Critère de regroupement (Group by)
1009
 * @param array $having Tableau des des post-conditions à remplir (Having)
1010
 * @param string $serveur Nom de la connexion
1011
 * @param bool $requeter Exécuter la requête, sinon la retourner
1012
 * @return int|string
1013
 *     - String Texte de la requête si demandé
1014
 *     - int Nombre de lignes (0 si la requête n'a pas réussie)
1015
 **/
1016
function spip_mysql_countsel(
1017
	$from = array(),
1018
	$where = array(),
1019
	$groupby = '',
1020
	$having = array(),
1021
	$serveur = '',
1022
	$requeter = true
1023
) {
1024
	$c = !$groupby ? '*' : ('DISTINCT ' . (is_string($groupby) ? $groupby : join(',', $groupby)));
1025
1026
	$r = spip_mysql_select("COUNT($c)", $from, $where, '', '', '', $having, $serveur, $requeter);
1027
	if (!$requeter) {
1028
		return $r;
1029
	}
1030
	if (!$r instanceof mysqli_result) {
1031
		return 0;
1032
	}
1033
	list($c) = mysqli_fetch_array($r, MYSQLI_NUM);
1034
	mysqli_free_result($r);
1035
1036
	return $c;
1037
}
1038
1039
1040
/**
1041
 * Retourne la dernière erreur generée
1042
 *
1043
 * @note
1044
 *   Bien spécifier le serveur auquel on s'adresse,
1045
 *   mais à l'install la globale n'est pas encore complètement définie.
1046
 *
1047
 * @uses sql_error_backtrace()
1048
 *
1049
 * @param string $query
1050
 *     Requête qui était exécutée
1051
 * @param string $serveur
1052
 *     Nom de la connexion
1053
 * @param bool $requeter
1054
 *     Inutilisé
1055
 * @return string
1056
 *     Erreur eventuelle
1057
 **/
1058
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...
1059
	$link = $GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0]['link'];
1060
	$s = mysqli_error($link);
1061
	if ($s) {
1062
		$trace = debug_backtrace();
1063
		if ($trace[0]['function'] != "spip_mysql_error") {
1064
			spip_log("$s - $query - " . sql_error_backtrace(), 'mysql.' . _LOG_ERREUR);
1065
		}
1066
	}
1067
1068
	return $s;
1069
}
1070
1071
1072
/**
1073
 * Retourne le numero de la dernière erreur SQL
1074
 *
1075
 * @param string $serveur
1076
 *     Nom de la connexion
1077
 * @param bool $requeter
1078
 *     Inutilisé
1079
 * @return int
1080
 *     0, pas d'erreur. Autre, numéro de l'erreur.
1081
 **/
1082
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...
1083
	$link = $GLOBALS['connexions'][$serveur ? $serveur : 0]['link'];
1084
	$s = mysqli_errno($link);
1085
	// 2006 MySQL server has gone away
1086
	// 2013 Lost connection to MySQL server during query
1087
	if (in_array($s, array(2006, 2013))) {
1088
		define('spip_interdire_cache', true);
1089
	}
1090
	if ($s) {
1091
		spip_log("Erreur mysql $s", _LOG_ERREUR);
1092
	}
1093
1094
	return $s;
1095
}
1096
1097
1098
/**
1099
 * Retourne le nombre de lignes d’une ressource de sélection obtenue
1100
 * avec `sql_select()`
1101
 *
1102
 * @param Ressource $r Ressource de résultat
1103
 * @param string $serveur Nom de la connexion
1104
 * @param bool $requeter Inutilisé
1105
 * @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...
1106
 */
1107
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...
1108
	if ($r) {
1109
		return mysqli_num_rows($r);
1110
	}
1111
}
1112
1113
1114
/**
1115
 * Libère une ressource de résultat
1116
 *
1117
 * Indique à MySQL de libérer de sa mémoire la ressoucre de résultat indiquée
1118
 * car on n'a plus besoin de l'utiliser.
1119
 *
1120
 * @param Ressource $r Ressource de résultat
1121
 * @param string $serveur Nom de la connexion
1122
 * @param bool $requeter Inutilisé
1123
 * @return bool              True si réussi
1124
 */
1125
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...
1126
	return (($r instanceof mysqli_result) ? mysqli_free_result($r) : false);
1127
}
1128
1129
1130
/**
1131
 * Insère une ligne dans une table
1132
 *
1133
 * @param string $table
1134
 *     Nom de la table SQL
1135
 * @param string $champs
1136
 *     Liste des colonnes impactées,
1137
 * @param string $valeurs
1138
 *     Liste des valeurs,
1139
 * @param array $desc
1140
 *     Tableau de description des colonnes de la table SQL utilisée
1141
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1142
 * @param string $serveur
1143
 *     Nom du connecteur
1144
 * @param bool $requeter
1145
 *     Exécuter la requête, sinon la retourner
1146
 * @return bool|string|int|array
1147
 *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
1148
 *     - Texte de la requête si demandé,
1149
 *     - False en cas d'erreur,
1150
 *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
1151
 **/
1152
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...
1153
1154
	$connexion = &$GLOBALS['connexions'][$serveur ? strtolower($serveur) : 0];
1155
	$link = $connexion['link'];
1156
	$table = prefixer_table_spip($table, $connexion['prefixe']);
1157
1158
	$query = "INSERT INTO $table $champs VALUES $valeurs";
1159
	if (!$requeter) {
1160
		return $query;
1161
	}
1162
1163 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...
1164
		include_spip('public/tracer');
1165
		$t = trace_query_start();
1166
		$e = '';
1167
	} else {
1168
		$t = 0;
1169
	}
1170
1171
	$connexion['last'] = $query;
1172
	#spip_log($query, 'mysql.'._LOG_DEBUG);
1173
	$r = false;
1174
	if (mysqli_query($link, $query)) {
1175
		$r = mysqli_insert_id($link);
1176
	} else {
1177
		// Log de l'erreur eventuelle
1178
		if ($e = spip_mysql_errno($serveur)) {
1179
			$e .= spip_mysql_error($query, $serveur);
1180
		} // et du fautif
1181
	}
1182
1183
	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...
1184
1185
	// return $r ? $r : (($r===0) ? -1 : 0); pb avec le multi-base.
1186
}
1187
1188
/**
1189
 * Insère une ligne dans une table, en protégeant chaque valeur
1190
 *
1191
 * @param string $table
1192
 *     Nom de la table SQL
1193
 * @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...
1194
 *    Couples (colonne => valeur)
1195
 * @param array $desc
1196
 *     Tableau de description des colonnes de la table SQL utilisée
1197
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1198
 * @param string $serveur
1199
 *     Nom du connecteur
1200
 * @param bool $requeter
1201
 *     Exécuter la requête, sinon la retourner
1202
 * @return bool|string|int|array
1203
 *     - int|true identifiant de l'élément inséré (si possible), ou true, si réussite
1204
 *     - Texte de la requête si demandé,
1205
 *     - False en cas d'erreur,
1206
 *     - Tableau de description de la requête et du temps d'exécution, si var_profile activé
1207
 **/
1208
function spip_mysql_insertq($table, $couples = array(), $desc = array(), $serveur = '', $requeter = true) {
1209
1210
	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...
1211
		$desc = description_table($table, $serveur);
1212
	}
1213
	if (!$desc) {
1214
		$couples = array();
1215
	}
1216
	$fields = isset($desc['field']) ? $desc['field'] : array();
1217
1218
	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...
1219
		$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
1220
	}
1221
1222
	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 1211 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...
1223
		$serveur, $requeter);
1224
}
1225
1226
1227
/**
1228
 * Insère plusieurs lignes d'un coup dans une table
1229
 *
1230
 * @param string $table
1231
 *     Nom de la table SQL
1232
 * @param array $tab_couples
1233
 *     Tableau de tableaux associatifs (colonne => valeur)
1234
 * @param array $desc
1235
 *     Tableau de description des colonnes de la table SQL utilisée
1236
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1237
 * @param string $serveur
1238
 *     Nom du connecteur
1239
 * @param bool $requeter
1240
 *     Exécuter la requête, sinon la retourner
1241
 * @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...
1242
 *     - int|true identifiant du dernier élément inséré (si possible), ou true, si réussite
1243
 *     - Texte de la requête si demandé,
1244
 *     - False en cas d'erreur.
1245
 **/
1246
function spip_mysql_insertq_multi($table, $tab_couples = array(), $desc = array(), $serveur = '', $requeter = true) {
1247
1248
	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...
1249
		$desc = description_table($table, $serveur);
1250
	}
1251
	if (!$desc) {
1252
		$tab_couples = array();
1253
	}
1254
	$fields = isset($desc['field']) ? $desc['field'] : array();
1255
1256
	$cles = "(" . join(',', array_keys(reset($tab_couples))) . ')';
1257
	$valeurs = array();
1258
	$r = false;
1259
1260
	// Quoter et Inserer par groupes de 100 max pour eviter un debordement de pile
1261
	foreach ($tab_couples as $couples) {
1262
		foreach ($couples as $champ => $val) {
1263
			$couples[$champ] = spip_mysql_cite($val, $fields[$champ]);
1264
		}
1265
		$valeurs[] = '(' . join(',', $couples) . ')';
1266 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...
1267
			$r = spip_mysql_insert($table, $cles, join(', ', $valeurs), $desc, $serveur, $requeter);
0 ignored issues
show
Bug introduced by
It seems like $desc defined by description_table($table, $serveur) on line 1249 can also be of type boolean; however, spip_mysql_insert() does only seem to accept array, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
1268
			$valeurs = array();
1269
		}
1270
	}
1271 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...
1272
		$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 1249 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...
1273
	}
1274
1275
	return $r; // dans le cas d'une table auto_increment, le dernier insert_id
1276
}
1277
1278
/**
1279
 * Met à jour des enregistrements d'une table SQL
1280
 *
1281
 * @param string $table
1282
 *     Nom de la table
1283
 * @param array $champs
1284
 *     Couples (colonne => valeur)
1285
 * @param string|array $where
1286
 *     Conditions a remplir (Where)
1287
 * @param array $desc
1288
 *     Tableau de description des colonnes de la table SQL utilisée
1289
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1290
 * @param string $serveur
1291
 *     Nom de la connexion
1292
 * @param bool $requeter
1293
 *     Exécuter la requête, sinon la retourner
1294
 * @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...
1295
 *     - string : texte de la requête si demandé
1296
 *     - true si la requête a réussie, false sinon
1297
 *     - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
1298
 */
1299
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...
1300
	$set = array();
1301
	foreach ($champs as $champ => $val) {
1302
		$set[] = $champ . "=$val";
1303
	}
1304 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...
1305
		return spip_mysql_query(
1306
			calculer_mysql_expression('UPDATE', $table, ',')
1307
			. calculer_mysql_expression('SET', $set, ',')
1308
			. calculer_mysql_expression('WHERE', $where),
1309
			$serveur, $requeter);
1310
	}
1311
}
1312
1313
/**
1314
 * Met à jour des enregistrements d'une table SQL et protège chaque valeur
1315
 *
1316
 * Protège chaque valeur transmise avec sql_quote(), adapté au type
1317
 * de champ attendu par la table SQL
1318
 *
1319
 * @note
1320
 *   Les valeurs sont des constantes à mettre entre apostrophes
1321
 *   sauf les expressions de date lorsqu'il s'agit de fonctions SQL (NOW etc)
1322
 *
1323
 * @param string $table
1324
 *     Nom de la table
1325
 * @param array $champs
1326
 *     Couples (colonne => valeur)
1327
 * @param string|array $where
1328
 *     Conditions a remplir (Where)
1329
 * @param array $desc
1330
 *     Tableau de description des colonnes de la table SQL utilisée
1331
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1332
 * @param string $serveur
1333
 *     Nom de la connexion
1334
 * @param bool $requeter
1335
 *     Exécuter la requête, sinon la retourner
1336
 * @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...
1337
 *     - string : texte de la requête si demandé
1338
 *     - true si la requête a réussie, false sinon
1339
 *     - array Tableau décrivant la requête et son temps d'exécution si var_profile est actif
1340
 */
1341
function spip_mysql_updateq($table, $champs, $where = '', $desc = array(), $serveur = '', $requeter = true) {
1342
1343
	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...
1344
		return;
1345
	}
1346
	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...
1347
		$desc = description_table($table, $serveur);
1348
	}
1349
	if (!$desc) {
1350
		$champs = array();
1351
	} else {
1352
		$fields = $desc['field'];
1353
	}
1354
	$set = array();
1355
	foreach ($champs as $champ => $val) {
1356
		$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...
1357
	}
1358
1359
	return spip_mysql_query(
1360
		calculer_mysql_expression('UPDATE', $table, ',')
1361
		. calculer_mysql_expression('SET', $set, ',')
1362
		. calculer_mysql_expression('WHERE', $where),
1363
		$serveur, $requeter);
1364
}
1365
1366
/**
1367
 * Supprime des enregistrements d'une table
1368
 *
1369
 * @param string $table Nom de la table SQL
1370
 * @param string|array $where Conditions à vérifier
1371
 * @param string $serveur Nom du connecteur
1372
 * @param bool $requeter Exécuter la requête, sinon la retourner
1373
 * @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...
1374
 *     - int : nombre de suppressions réalisées,
1375
 *     - Texte de la requête si demandé,
1376
 *     - False en cas d'erreur.
1377
 **/
1378
function spip_mysql_delete($table, $where = '', $serveur = '', $requeter = true) {
1379
	$res = spip_mysql_query(
1380
		calculer_mysql_expression('DELETE FROM', $table, ',')
1381
		. calculer_mysql_expression('WHERE', $where),
1382
		$serveur, $requeter);
1383
	if (!$requeter) {
1384
		return $res;
1385
	}
1386
	if ($res) {
1387
		$link = _mysql_link($serveur);
1388
1389
		return mysqli_affected_rows($link);
1390
	} else {
1391
		return false;
1392
	}
1393
}
1394
1395
1396
/**
1397
 * Insère où met à jour une entrée d’une table SQL
1398
 *
1399
 * La clé ou les cles primaires doivent être présentes dans les données insérés.
1400
 * La fonction effectue une protection automatique des données.
1401
 *
1402
 * Préférez updateq ou insertq.
1403
 *
1404
 * @param string $table
1405
 *     Nom de la table SQL
1406
 * @param array $couples
1407
 *     Couples colonne / valeur à modifier,
1408
 * @param array $desc
1409
 *     Tableau de description des colonnes de la table SQL utilisée
1410
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1411
 * @param string $serveur
1412
 *     Nom du connecteur
1413
 * @param bool $requeter
1414
 *     Exécuter la requête, sinon la retourner
1415
 * @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...
1416
 *     - true si réussite
1417
 *     - Texte de la requête si demandé,
1418
 *     - False en cas d'erreur.
1419
 **/
1420
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...
1421
	return spip_mysql_query("REPLACE $table (" . join(',', array_keys($couples)) . ') VALUES (' . join(',',
1422
			array_map('_q', $couples)) . ')', $serveur, $requeter);
1423
}
1424
1425
1426
/**
1427
 * Insère où met à jour des entrées d’une table SQL
1428
 *
1429
 * La clé ou les cles primaires doivent être présentes dans les données insérés.
1430
 * La fonction effectue une protection automatique des données.
1431
 *
1432
 * Préférez insertq_multi et sql_updateq
1433
 *
1434
 * @param string $table
1435
 *     Nom de la table SQL
1436
 * @param array $tab_couples
1437
 *     Tableau de tableau (colonne / valeur à modifier),
1438
 * @param array $desc
1439
 *     Tableau de description des colonnes de la table SQL utilisée
1440
 *     (il sera calculé si nécessaire s'il n'est pas transmis).
1441
 * @param string $serveur
1442
 *     Nom du connecteur
1443
 * @param bool $requeter
1444
 *     Exécuter la requête, sinon la retourner
1445
 * @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...
1446
 *     - true si réussite
1447
 *     - Texte de la requête si demandé,
1448
 *     - False en cas d'erreur.
1449
 **/
1450
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...
1451
	$cles = "(" . join(',', array_keys($tab_couples[0])) . ')';
1452
	$valeurs = array();
1453
	foreach ($tab_couples as $couples) {
1454
		$valeurs[] = '(' . join(',', array_map('_q', $couples)) . ')';
1455
	}
1456
	$valeurs = implode(', ', $valeurs);
1457
1458
	return spip_mysql_query("REPLACE $table $cles VALUES $valeurs", $serveur, $requeter);
1459
}
1460
1461
1462
/**
1463
 * Retourne l'instruction SQL pour obtenir le texte d'un champ contenant
1464
 * une balise `<multi>` dans la langue indiquée
1465
 *
1466
 * Cette sélection est mise dans l'alias `multi` (instruction AS multi).
1467
 *
1468
 * @param string $objet Colonne ayant le texte
1469
 * @param string $lang Langue à extraire
1470
 * @return string       Texte de sélection pour la requête
1471
 */
1472
function spip_mysql_multi($objet, $lang) {
1473
	$lengthlang = strlen("[$lang]");
1474
	$posmulti = "INSTR(" . $objet . ", '<multi>')";
1475
	$posfinmulti = "INSTR(" . $objet . ", '</multi>')";
1476
	$debutchaine = "LEFT(" . $objet . ", $posmulti-1)";
1477
	$finchaine = "RIGHT(" . $objet . ", CHAR_LENGTH(" . $objet . ") -(7+$posfinmulti))";
1478
	$chainemulti = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7, $posfinmulti -(7+$posmulti)))";
1479
	$poslang = "INSTR($chainemulti,'[" . $lang . "]')";
1480
	$poslang = "IF($poslang=0,INSTR($chainemulti,']')+1,$poslang+$lengthlang)";
1481
	$chainelang = "TRIM(SUBSTRING(" . $objet . ", $posmulti+7+$poslang-1,$posfinmulti -($posmulti+7+$poslang-1) ))";
1482
	$posfinlang = "INSTR(" . $chainelang . ", '[')";
1483
	$chainelang = "IF($posfinlang>0,LEFT($chainelang,$posfinlang-1),$chainelang)";
1484
	//$chainelang = "LEFT($chainelang,$posfinlang-1)";
1485
	$retour = "(TRIM(IF($posmulti = 0 , " .
1486
		"     TRIM(" . $objet . "), " .
1487
		"     CONCAT( " .
1488
		"          $debutchaine, " .
1489
		"          IF( " .
1490
		"               $poslang = 0, " .
1491
		"                     $chainemulti, " .
1492
		"               $chainelang" .
1493
		"          ), " .
1494
		"          $finchaine" .
1495
		"     ) " .
1496
		"))) AS multi";
1497
1498
	return $retour;
1499
}
1500
1501
/**
1502
 * Prépare une chaîne hexadécimale
1503
 *
1504
 * Par exemple : FF ==> 0xFF en MySQL
1505
 *
1506
 * @param string $v
1507
 *     Chaine hexadecimale
1508
 * @return string
1509
 *     Valeur hexadécimale pour MySQL
1510
 **/
1511
function spip_mysql_hex($v) {
1512
	return "0x" . $v;
1513
}
1514
1515
/**
1516
 * Échapper une valeur selon son type ou au mieux
1517
 * comme le fait `_q()` mais pour MySQL avec ses spécificités
1518
 *
1519
 * @param string|array|number $v
1520
 *     Texte, nombre ou tableau à échapper
1521
 * @param string $type
1522
 *     Description du type attendu
1523
 *    (par exemple description SQL de la colonne recevant la donnée)
1524
 * @return string|number
1525
 *    Donnée prête à être utilisée par le gestionnaire SQL
1526
 */
1527 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...
1528
	if (!is_array($v)) {
1529
		return spip_mysql_cite($v, $type);
1530
	}
1531
1532
	// si c'est un tableau, le parcourir en propageant le type
1533
	foreach ($v as $k => $r) {
1534
		$v[$k] = spip_mysql_quote($r, $type);
1535
	}
1536
	return implode(',', $v);
1537
}
1538
1539
/**
1540
 * Tester si une date est proche de la valeur d'un champ
1541
 *
1542
 * @param string $champ
1543
 *     Nom du champ a tester
1544
 * @param int $interval
1545
 *     Valeur de l'intervalle : -1, 4, ...
1546
 * @param string $unite
1547
 *     Utité utilisée (DAY, MONTH, YEAR, ...)
1548
 * @return string
1549
 *     Expression SQL
1550
 **/
1551
function spip_mysql_date_proche($champ, $interval, $unite) {
1552
	$use_now = ( ($champ === 'maj' or strpos($champ, '.maj')) ? true : false );
1553
	return '('
1554
	. $champ
1555
	. (($interval <= 0) ? '>' : '<')
1556
	. (($interval <= 0) ? 'DATE_SUB' : 'DATE_ADD')
1557
	. '('
1558
	. ($use_now ? 'NOW()' : sql_quote(date('Y-m-d H:i:s')))
1559
	. ', INTERVAL '
1560
	. (($interval > 0) ? $interval : (0 - $interval))
1561
	. ' '
1562
	. $unite
1563
	. '))';
1564
}
1565
1566
1567
/**
1568
 * Retourne une expression IN pour le gestionnaire de base de données
1569
 *
1570
 * IN (...) est limité à 255 éléments, d'où cette fonction assistante
1571
 *
1572
 * @param string $val
1573
 *     Colonne SQL sur laquelle appliquer le test
1574
 * @param string $valeurs
1575
 *     Liste des valeurs possibles (séparés par des virgules)
1576
 * @param string $not
1577
 *     - '' sélectionne les éléments correspondant aux valeurs
1578
 *     - 'NOT' inverse en sélectionnant les éléments ne correspondant pas aux valeurs
1579
 * @param string $serveur
1580
 *     Nom du connecteur
1581
 * @param bool $requeter
1582
 *     Inutilisé
1583
 * @return string
1584
 *     Expression de requête SQL
1585
 **/
1586
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...
1587
	return "($val $not IN ($valeurs))";
1588
}
1589
1590
1591
/**
1592
 * Renvoie les bons echappements (mais pas sur les fonctions comme NOW())
1593
 *
1594
 * @param string|number $v Texte ou nombre à échapper
1595
 * @param string $type Type de donnée attendue, description SQL de la colonne de destination
1596
 * @return string|number     Texte ou nombre échappé
1597
 */
1598
function spip_mysql_cite($v, $type) {
1599
	if (!$type) {
1600
		if (is_numeric($v)) {
1601
			return strval($v);
1602
		}
1603
		return "'" . addslashes($v) . "'";
1604
	}
1605
1606
	if (is_null($v)
1607
		and stripos($type, "NOT NULL") === false
1608
	) {
1609
		return 'NULL';
1610
	} // null php se traduit en NULL SQL
1611
	if (sql_test_date($type) and preg_match('/^\w+\(/', $v)) {
1612
		return $v;
1613
	}
1614
	if (sql_test_int($type)) {
1615
		if (is_numeric($v) or (ctype_xdigit(substr($v, 2))
1616
				and $v[0] == '0' and $v[1] == 'x')
1617
		) {
1618
			return $v;
1619
		} // si pas numerique, forcer le intval
1620
		else {
1621
			return intval($v);
1622
		}
1623
	}
1624
1625
	return ("'" . addslashes($v) . "'");
1626
}
1627
1628
1629
// Ces deux fonctions n'ont pas d'equivalent exact PostGres
1630
// et ne sont la que pour compatibilite avec les extensions de SPIP < 1.9.3
1631
1632
/**
1633
 * Poser un verrou SQL local
1634
 *
1635
 * Changer de nom toutes les heures en cas de blocage MySQL (ca arrive)
1636
 *
1637
 * @deprecated Pas d'équivalence actuellement en dehors de MySQL
1638
 * @see spip_release_lock()
1639
 *
1640
 * @param string $nom
1641
 *     Inutilisé. Le nom est calculé en fonction de la connexion principale
1642
 * @param int $timeout
1643
 * @return string|bool
1644
 *     - Nom du verrou si réussite,
1645
 *     - false sinon
1646
 */
1647
function spip_get_lock($nom, $timeout = 0) {
1648
1649
	define('_LOCK_TIME', intval(time() / 3600 - 316982));
1650
1651
	$connexion = &$GLOBALS['connexions'][0];
1652
	$bd = $connexion['db'];
1653
	$prefixe = $connexion['prefixe'];
1654
	$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
1655
1656
	$connexion['last'] = $q = "SELECT GET_LOCK(" . _q($nom) . ", $timeout) AS n";
1657
1658
	$q = @sql_fetch(mysqli_query(_mysql_link(), $q));
1659
	if (!$q) {
1660
		spip_log("pas de lock sql pour $nom", _LOG_ERREUR);
1661
	}
1662
1663
	return $q['n'];
1664
}
1665
1666
1667
/**
1668
 * Relâcher un verrou SQL local
1669
 *
1670
 * @deprecated Pas d'équivalence actuellement en dehors de MySQL
1671
 * @see spip_get_lock()
1672
 *
1673
 * @param string $nom
1674
 *     Inutilisé. Le nom est calculé en fonction de la connexion principale
1675
 * @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...
1676
 *     True si réussite, false sinon.
1677
 */
1678
function spip_release_lock($nom) {
1679
1680
	$connexion = &$GLOBALS['connexions'][0];
1681
	$bd = $connexion['db'];
1682
	$prefixe = $connexion['prefixe'];
1683
	$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
1684
1685
	$connexion['last'] = $q = "SELECT RELEASE_LOCK(" . _q($nom) . ")";
1686
	mysqli_query(_mysql_link(), $q);
1687
}
1688
1689
1690
/**
1691
 * Teste si on a les fonctions MySQLi (pour l'install)
1692
 *
1693
 * @return bool
1694
 *     True si on a les fonctions, false sinon
1695
 */
1696
function spip_versions_mysql() {
1697
	return function_exists('mysqli_query');
1698
}
1699
1700
1701
/**
1702
 * Tester si mysql ne veut pas du nom de la base dans les requêtes
1703
 *
1704
 * @param string $server_db
1705
 * @return string
1706
 *     - chaîne vide si nom de la base utile
1707
 *     - chaîne : code compilé pour le faire désactiver par SPIP sinon
1708
 */
1709
function test_rappel_nom_base_mysql($server_db) {
1710
	$GLOBALS['mysql_rappel_nom_base'] = true;
1711
	sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1712
	$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...
1713
1714
	if ($ok) {
1715
		sql_delete('spip_meta', "nom='mysql_rappel_nom_base'", $server_db);
1716
1717
		return '';
1718
	} else {
1719
		$GLOBALS['mysql_rappel_nom_base'] = false;
1720
1721
		return "\$GLOBALS['mysql_rappel_nom_base'] = false; " .
1722
		"/* echec de test_rappel_nom_base_mysql a l'installation. */\n";
1723
	}
1724
}
1725
1726
/**
1727
 * Teste si on peut changer les modes de MySQL
1728
 *
1729
 * @link http://dev.mysql.com/doc/refman/5.0/fr/server-sql-mode.html
1730
 *
1731
 * @param string $server_db Nom de la connexion
1732
 * @return string
1733
 *     - chaîne vide si on ne peut pas appliquer de mode
1734
 *     - chaîne : code compilé pour l'indiquer le résultat du test à SPIP
1735
 */
1736
function test_sql_mode_mysql($server_db) {
1737
	$res = sql_select("version() as v", '', '', '', '', '', '', $server_db);
1738
	$row = sql_fetch($res, $server_db);
1739
	if (version_compare($row['v'], '5.0.0', '>=')) {
1740
		defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE', true);
1741
1742
		return "defined('_MYSQL_SET_SQL_MODE') || define('_MYSQL_SET_SQL_MODE',true);\n";
1743
	}
1744
1745
	return '';
1746
}
1747