Completed
Push — master ( 1a3b2f...b50fb4 )
by cam
09:59
created

install.php ➔ install_select_serveur()   B

Complexity

Conditions 9
Paths 8

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 8
nop 0
dl 0
loc 29
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2018                                                *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Gestion de l'installation de SPIP
15
 *
16
 * @package SPIP\Core\Installation
17
 **/
18
19
if (!defined('_ECRIRE_INC_VERSION')) {
20
	return;
21
}
22
23
24
/**
25
 * Écrit un fichier PHP nécessitant SPIP
26
 *
27
 * Écrit le texte transmis dans un fichier PHP. Cette fonction
28
 * ajoute les entêtes PHP et le test de sécurité vérifiant que SPIP
29
 * est chargé.
30
 *
31
 * @example
32
 *     ```
33
 *     install_fichier_connexion(_FILE_CONNECT_TMP, $contenu);
34
 *     ```
35
 *
36
 * @todo
37
 *     Renommer cette fonction qui peut servir à d'autres utilisations ?
38
 *
39
 * @param string $nom
40
 *     Chemin du fichier à créer
41
 * @param string $texte
42
 *     Code source du fichier (sans l'ouverture/fermeture PHP)
43
 * @return void
44
 **/
45
function install_fichier_connexion($nom, $texte) {
46
	$texte = "<" . "?php\n"
47
		. "if (!defined(\"_ECRIRE_INC_VERSION\")) return;\n"
48
		. $texte
49
		. "?" . ">";
50
51
	ecrire_fichier($nom, $texte);
52
}
53
54
55
/**
56
 * Retourne le code source d'un fichier de connexion à une base de données
57
 *
58
 * Le code est un appel à la fonction spip_connect_db()
59
 *
60
 * @see spip_connect_db()
61
 *
62
 * @internal
63
 *     Attention etape_ldap4 suppose qu'il n'y aura qu'un seul appel de fonction
64
 *     dans le fichier produit.
65
 *
66
 * @param string $adr Adresse de la base de données {@example 'localhost'}
67
 * @param string $port Numéro de port
68
 * @param string $login Login de connexion
69
 * @param string $pass Mot de passe de connexion
70
 * @param string $base Nom de la base de données
71
 * @param string $type Moteur SQL {@example 'sqlite3', 'mysql'}
72
 * @param string $pref Préfixe des tables {@example 'spip'}
73
 * @param string $ldap Type d'authentification (cas si 'ldap')
74
 * @param string $charset Charset de la connexion SQL
75
 * @return string
76
 *     Texte du fichier de connexion
77
 *
78
 **/
79
function install_connexion($adr, $port, $login, $pass, $base, $type, $pref, $ldap = '', $charset = '') {
80
	$adr = addcslashes($adr, "'\\");
81
	$port = addcslashes($port, "'\\");
82
	$login = addcslashes($login, "'\\");
83
	$pass = addcslashes($pass, "'\\");
84
	$base = addcslashes($base, "'\\");
85
	$type = addcslashes($type, "'\\");
86
	$pref = addcslashes($pref, "'\\");
87
	$ldap = addcslashes($ldap, "'\\");
88
	$charset = addcslashes($charset, "'\\");
89
90
	return "\$GLOBALS['spip_connect_version'] = 0.8;\n"
91
	. "spip_connect_db("
92
	. "'$adr','$port','$login','$pass','$base'"
93
	. ",'$type', '$pref','$ldap','$charset');\n";
94
}
95
96
97
/**
98
 * Analyse un fichier de connexion à une base de données
99
 *
100
 * Le fichier contient normalement le résultat de la fonction install_connexion().
101
 * L'analyse tient également compte des syntaxes des versions précédentes.
102
 *
103
 * @param string $file
104
 *     Chemin du fichier de connexion à analyser
105
 * @return array
106
 *     Tableau des informations sur la connexion
107
 **/
108
function analyse_fichier_connection($file) {
109
	$s = @join('', file($file));
110
	if (preg_match("#mysql_connect\([\"'](.*)[\"'],[\"'](.*)[\"'],[\"'](.*)[\"']\)#", $s, $regs)) {
111
		array_shift($regs);
112
113
		return $regs;
114
	} else {
115
		$ar = '\s*\'([^\']*)\'';
116
		$r = '\s*,' . $ar;
117
		$r = "#spip_connect_db[(]$ar$r$r$r$r(?:$r(?:$r(?:$r(?:$r)?)?)?)?#";
118
		if (preg_match($r, $s, $regs)) {
119
			$regs[2] = $regs[1] . (!$regs[2] ? '' : ":" . $regs[2] . ";");
120
			array_shift($regs);
121
			array_shift($regs);
122
123
			return $regs;
124
		}
125
	}
126
	spip_log("$file n'est pas un fichier de connexion");
127
128
	return array();
129
}
130
131
/**
132
 * Liste les connecteurs aux bases SQL disponibles
133
 *
134
 * Dans le code SPIP ces connecteurs sont souvent appelés $connect ou $serveur
135
 *
136
 * @example
137
 *     $bases = bases_referencees(_FILE_CONNECT_TMP);
138
 *
139
 * @param string $exclu
140
 *     Exclure un connecteur particulier (nom du fichier)
141
 * @return array
142
 *     Liste des noms de connecteurs
143
 **/
144
function bases_referencees($exclu = '') {
145
	$tables = array();
146
	foreach (preg_files(_DIR_CONNECT, '.php$') as $f) {
147
		if ($f != $exclu and analyse_fichier_connection($f)) {
148
			$tables[] = basename($f, '.php');
149
		}
150
	}
151
152
	return $tables;
153
}
154
155
156
function install_mode_appel($server_db, $tout = true) {
157
	return ($server_db != 'mysql') ? ''
158
		: (($tout ? test_rappel_nom_base_mysql($server_db) : '')
159
			. test_sql_mode_mysql($server_db));
160
}
161
162
//
163
// Verifier que l'hebergement est compatible SPIP ... ou l'inverse :-)
164
// (sert a l'etape 1 de l'installation)
165
// http://code.spip.net/@tester_compatibilite_hebergement
166
function tester_compatibilite_hebergement() {
167
	$err = array();
168
169
	$p = phpversion();
170
	if (version_compare($p, _PHP_MIN, '<')) {
171
		$err[] = _T('install_php_version', array('version' => $p, 'minimum' => _PHP_MIN));
172
	}
173
174
	// Si on n'a pas la bonne version de PHP, c'est la fin
175
	if ($err) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $err 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...
176
		die("<div class='error'>"
0 ignored issues
show
Coding Style Compatibility introduced by
The function tester_compatibilite_hebergement() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
177
			. "<h3>" . _T('avis_attention') . '</h3><p>' . _T('install_echec_annonce') . "</p><ul class='spip'>"
178
			. "<li><strong>{$err[0]}</strong></li>\n</ul></div>");
179
	}
180
181
	// Il faut une base de donnees tout de meme ...
182
	$serveurs = install_select_serveur();
183
	if (!$serveurs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $serveurs 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...
184
		$err[] = _T('install_extension_php_obligatoire')
185
			. " <a href='http://www.php.net/mysql'>MYSQL</a>"
186
			. "| <a href='http://www.php.net/pgsql'>PostgreSQL</a>"
187
			. "| <a href='http://www.php.net/sqlite'>SQLite</a>";
188
	}
189
190
	// et il faut preg
191
	if (!function_exists('preg_match_all')) {
192
		$err[] = _T('install_extension_php_obligatoire')
193
			. " <a href='http://se.php.net/pcre'>PCRE</a>";
194
	}
195
196
	// et surtout pas ce mbstring.overload
197
	if ($a = @ini_get('mbstring.func_overload')) {
198
		$err[] = _T('install_extension_mbstring')
199
			. "mbstring.func_overload=$a - <a href='http://www.php.net/mb_string'>mb_string</a>.<br /><small>";
200
	}
201
202
	if ($err) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $err 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...
203
		echo "<div class='error'>"
204
			. "<h3>" . _T('avis_attention') . '</h3><p>' . _T('install_echec_annonce') . "</p><ul class='spip'>";
205
		foreach ($err as $e) {
206
			echo "<li><strong>$e</strong></li>\n";
207
		}
208
209
		# a priori ici on pourrait die(), mais il faut laisser la possibilite
210
		# de forcer malgre tout (pour tester, ou si bug de detection)
211
		echo "</ul></div>\n";
212
	}
213
}
214
215
216
/** 
217
 * Faciliter la recherche du login d'installation en fonction de certains hébergeurs connus
218
 * 
219
 * @note superflu ??
220
 */
221
function login_hebergeur() {
222
	$base_hebergeur = 'localhost'; # par defaut
223
224
	// Free
225
	if (preg_match(',(.*)\.free\.fr$,', $_SERVER['SERVER_NAME'], $regs)) {
226
		$base_hebergeur = 'sql.free.fr';
227
		$login_hebergeur = $regs[1];
228
	} else {
229
		$login_hebergeur = '';
230
	}
231
232
	return array($base_hebergeur, $login_hebergeur);
233
}
234
235
236
// http://code.spip.net/@info_etape
237
function info_etape($titre, $complement = '') {
238
	return "<h2>" . $titre . "</h2>\n" .
239
	($complement ? "" . $complement . "\n" : '');
240
}
241
242
/**
243
 * Retourne le code HTML d'un bouton `suivant>>` pour les phases d'installation
244
 *
245
 * @param string $code Texte du bouton
246
 * @return string Code HTML du bouton
247
 **/
248
function bouton_suivant($code = '') {
249
	if ($code == '') {
250
		$code = _T('bouton_suivant');
251
	}
252
	static $suivant = 0;
253
	$id = 'suivant' . (($suivant > 0) ? strval($suivant) : '');
254
	$suivant += 1;
255
256
	return "\n<p class='boutons suivant'><input id='" . $id . "' type='submit'\nvalue=\"" .
257
	$code .
258
	" >>\" /></p>\n";
259
}
260
261
// http://code.spip.net/@info_progression_etape
262
function info_progression_etape($en_cours, $phase, $dir, $erreur = false) {
263
	//$en_cours = _request('etape')?_request('etape'):"";
264
	$liste = find_all_in_path($dir, $phase . '(([0-9])+|fin)[.]php$');
265
	$debut = 1;
266
	$etat = "ok";
0 ignored issues
show
Unused Code introduced by
$etat 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...
267
	$last = count($liste);
268
//	$texte_etat = array('ok'=>'OK','encours'=>_T('en_cours'),'todo'=>_T('todo'));
269
270
	$intitule_etat["etape_"][1] = typo(_T('info_connexion_base_donnee'));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$intitule_etat was never initialized. Although not strictly required by PHP, it is generally a good practice to add $intitule_etat = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
271
	$intitule_etat["etape_"][2] = typo(_T('menu_aide_installation_choix_base'));
272
	$intitule_etat["etape_"][3] = typo(_T('info_informations_personnelles'));
273
	$intitule_etat["etape_"][4] = typo(_T('info_derniere_etape'));
274
275
	$intitule_etat["etape_ldap"][1] = typo(_T('titre_connexion_ldap'));
276
	$intitule_etat["etape_ldap"][2] = typo(_T('titre_connexion_ldap'));
277
	$intitule_etat["etape_ldap"][3] = typo(_T('info_chemin_acces_1'));
278
	$intitule_etat["etape_ldap"][4] = typo(_T('info_reglage_ldap'));
279
	$intitule_etat["etape_ldap"][5] = typo(_T('info_ldap_ok'));
280
281
//	$aff_etapes = "<span id='etapes'>";
282
283
	$aff_etapes = "<ul id='infos_etapes' class='infos_$phase$en_cours'>";
284
285
	foreach ($liste as $etape => $fichier) {
286
		if ($debut < $last) {
287
			if ($debut == $en_cours && $erreur) {
288
				$class = "on erreur";
289
			} else {
290
				if ($debut == $en_cours) {
291
					$class = "on";
292
				} else {
293
					if ($debut > $en_cours) {
294
						$class = "prochains";
295
					} else {
296
						$class = "valides";
297
					}
298
				}
299
			}
300
301
			$aff_etapes .= "<li class='$class'><div class='fond'>";
302
			$aff_etapes .= ($debut == $en_cours) ? "<strong>" : '';
303
			$aff_etapes .= "<em>" . _T('etape') . " </em><span class='numero_etape'>$debut</span><em>&nbsp;: </em>";
304
			$aff_etapes .= $intitule_etat["$phase"][$debut];
305
			$aff_etapes .= ($debut == $en_cours) ? "</strong>" : '';
306
			$aff_etapes .= "</div></li>";
307
		}
308
		$debut++;
309
	}
310
	$aff_etapes .= "</ul>";
311
	$aff_etapes .= "<br class='nettoyeur' />\n";
312
313
	return $aff_etapes;
314
}
315
316
317
// http://code.spip.net/@fieldset
318
function fieldset($legend, $champs = array(), $apres = '', $avant = '') {
319
	return "<fieldset>\n" .
320
	$avant .
321
	($legend ? "<legend>" . $legend . "</legend>\n" : '') .
322
	fieldset_champs($champs) .
323
	$apres .
324
	"</fieldset>\n";
325
}
326
327
function fieldset_champs($champs = array()) {
328
	$fieldset = '';
329
	foreach ($champs as $nom => $contenu) {
330
		$type = isset($contenu['hidden']) ? 'hidden' : (preg_match(',^pass,', $nom) ? 'password' : 'text');
331
		$class = isset($contenu['hidden']) ? '' : "class='formo' size='40' ";
332
		if (isset($contenu['alternatives'])) {
333
			$fieldset .= $contenu['label'] . "\n";
334
			foreach ($contenu['alternatives'] as $valeur => $label) {
335
				$fieldset .= "<input type='radio' name='" . $nom .
336
					"' id='$nom-$valeur' value='$valeur'"
337
					. (($valeur == $contenu['valeur']) ? "\nchecked='checked'" : '')
338
					. "/>\n";
339
				$fieldset .= "<label for='$nom-$valeur'>" . $label . "</label>\n";
340
			}
341
			$fieldset .= "<br />\n";
342
		} else {
343
			$fieldset .= "<label for='" . $nom . "'>" . $contenu['label'] . "</label>\n";
344
			$fieldset .= "<input " . $class . "type='" . $type . "' id='" . $nom . "' name='" . $nom . "'\nvalue='" . $contenu['valeur'] . "'"
345
				. (preg_match(',^(pass|login),', $nom) ? " autocomplete='off'" : '')
346
				. ((isset($contenu['required']) and $contenu['required']) ? " required='required'" : "")
347
				. " />\n";
348
		}
349
	}
350
351
	return $fieldset;
352
}
353
354
function install_select_serveur() {
355
	$options = array();
356
	$dir = _DIR_RESTREINT . 'req/';
357
	$d = opendir($dir);
358
	if (!$d) {
359
		return array();
360
	}
361
	while (($f = readdir($d)) !== false) {
362
		if ((preg_match('/^(.*)[.]php$/', $f, $s))
363
			and is_readable($f = $dir . $f)
364
		) {
365
			require_once($f);
366
			$s = $s[1];
367
			$v = 'spip_versions_' . $s;
368
			if (function_exists($v) and $v()) {
369
				$titre = _T("install_select_type_$s");
370
				// proposer mysql par defaut si dispo
371
				$checked = ($s == 'mysql' ? " checked='checked'" : "");
372
				$options[$s] = "<li><input type='radio' id='$s' value='$s' name='server_db'$checked>"
373
					. "<label for='$s'>" . ($titre ? $titre : $s) . "</label></li>";
374
			} else {
375
				spip_log("$s: portage indisponible");
376
			}
377
		}
378
	}
379
	sort($options);
380
381
	return $options;
382
}
383
384
// http://code.spip.net/@install_connexion_form
385
function install_connexion_form($db, $login, $pass, $predef, $hidden, $etape, $jquery = true) {
386
	$server_db = (is_string($predef[0])) ? $predef[0] : '';
387
388
	return generer_form_ecrire('install', (
389
		"\n<input type='hidden' name='etape' value='$etape' />"
390
		. $hidden
391
		. (_request('echec') ?
392
			("<p><b>" . _T('avis_connexion_echec_1') .
393
				"</b></p><p>" . _T('avis_connexion_echec_2') . "</p><p style='font-size: small;'>" . _T('avis_connexion_echec_3') . "</p>")
394
			: "")
395
396
		. ($jquery ? http_script('', 'jquery.js') : '')
397
		. http_script('
398
		jQuery(function($) {
399
			$("input[type=hidden][name=server_db]").each(function(){
400
				if ($(this).attr("value").match("sqlite*")){
401
					$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").hide();
402
				}
403
			});
404
			if ($("input[name=server_db][checked]").attr("value").match("sqlite*"))
405
				$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").hide();
406
			else
407
				$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").show();
408
			$("input[name=server_db]").each(function(){
409
				$(this).on("change",function(){
410
					if ($(this).prop("checked") && $(this).attr("value").match("sqlite*")) {
411
						$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").hide();
412
					}
413
					if ($(this).prop("checked") && !$(this).attr("value").match("sqlite*")) {
414
						$("#install_adresse_base_hebergeur,#install_login_base_hebergeur,#install_pass_base_hebergeur").show();
415
					}
416
				});
417
			});
418
		});')
419
420
		. ($server_db
421
			? '<input type="hidden" name="server_db" value="' . $server_db . '" />'
422
			. (($predef[0])
423
				? ('<h3>' . _T('install_serveur_hebergeur') . '</h3>')
424
				: '')
425
			: ('<fieldset><legend>'
426
				. _T('install_select_type_db')
427
				. "</legend>"
428
				. '<p class="explication">'
429
				. _T('install_types_db_connus')
430
				// Passer l'avertissement SQLIte en  commentaire, on pourra facilement le supprimer par la suite sans changer les traductions.
431
				// . "<br /><small>(". _T('install_types_db_connus_avertissement') .')</small>'
432
				. '</p>'
433
				. "\n<div class='p'>\n<ul>\n"
434
				. join("\n", install_select_serveur())
435
				. "\n</ul>\n</div></fieldset>")
436
		)
437
		. '<div id="install_adresse_base_hebergeur">'
438
		. '<p>' . _T('texte_connexion_mysql') . '</p>'
439
		. ($predef[1]
440
			? '<h3>' . _T('install_adresse_base_hebergeur') . '</h3>'
441
			: fieldset(_T('entree_base_donnee_1'),
442
				array(
443
					'adresse_db' => array(
444
						'label' => $db[1],
445
						'valeur' => $db[0]
446
					),
447
				)
448
			)
449
		)
450
		. '</div>'
451
452
		. '<div id="install_login_base_hebergeur">'
453
		. ($predef[2]
454
			? '<h3>' . _T('install_login_base_hebergeur') . '</h3>'
455
			: fieldset(_T('entree_login_connexion_1'),
456
				array(
457
					'login_db' => array(
458
						'label' => $login[1],
459
						'valeur' => $login[0]
460
					),
461
				)
462
			)
463
		)
464
		. '</div>'
465
466
		. '<div id="install_pass_base_hebergeur">'
467
		. ($predef[3]
468
			? '<h3>' . _T('install_pass_base_hebergeur') . '</h3>'
469
			: fieldset(_T('entree_mot_passe_1'),
470
				array(
471
					'pass_db' => array(
472
						'label' => $pass[1],
473
						'valeur' => $pass[0]
474
					),
475
				)
476
			)
477
		)
478
		. '</div>'
479
480
		. bouton_suivant()));
481
482
}
483
484
// 4 valeurs qu'on reconduit d'un script a l'autre
485
// sauf s'ils sont predefinis.
486
487
// http://code.spip.net/@predef_ou_cache
488
function predef_ou_cache($adresse_db, $login_db, $pass_db, $server_db) {
489
	return ((defined('_INSTALL_HOST_DB'))
490
		? ''
491
		: "\n<input type='hidden' name='adresse_db'  value=\"" . spip_htmlspecialchars($adresse_db) . "\" />"
492
	)
493
	. ((defined('_INSTALL_USER_DB'))
494
		? ''
495
		: "\n<input type='hidden' name='login_db' value=\"" . spip_htmlspecialchars($login_db) . "\" />"
496
	)
497
	. ((defined('_INSTALL_PASS_DB'))
498
		? ''
499
		: "\n<input type='hidden' name='pass_db' value=\"" . spip_htmlspecialchars($pass_db) . "\" />"
500
	)
501
502
	. ((defined('_INSTALL_SERVER_DB'))
503
		? ''
504
		: "\n<input type='hidden' name='server_db' value=\"" . spip_htmlspecialchars($server_db) . "\" />"
505
	);
506
}
507
508
// presentation des bases existantes
509
510
// http://code.spip.net/@install_etape_liste_bases
511
function install_etape_liste_bases($server_db, $login_db, $disabled = array()) {
512
	$bases = $checked = array();
513
	$noms = sql_listdbs($server_db);
514
	if (!$noms) {
515
		return '';
516
	}
517
518
	foreach ($noms as $nom) {
0 ignored issues
show
Bug introduced by
The expression $noms of type array|boolean 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...
519
		$id = spip_htmlspecialchars($nom);
520
		$dis = in_array($nom, $disabled) ? " disabled='disabled'" : '';
521
		$base = " name=\"choix_db\" value=\""
522
			. $nom
523
			. '"'
524
			. $dis
525
			. " type='radio' id='$id'";
526
		$label = "<label for='$id'>"
527
			. ($dis ? "<i>$nom</i>" : $nom)
528
			. "</label>";
529
530
		if (!$checked and !$dis and
531
			(($nom == $login_db) or
532
				($GLOBALS['table_prefix'] == $nom))
533
		) {
534
			$checked = "<input$base checked='checked' />\n$label";
535
		} else {
536
			$bases[] = "<input$base />\n$label";
537
		}
538
	}
539
540
	if (!$bases && !$checked) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $bases 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...
541
		return false;
542
	}
543
544
	if ($checked) {
545
		array_unshift($bases, $checked);
546
		$checked = true;
547
	}
548
549
	return array($checked, $bases);
550
}
551
552
function install_propager($hidden) {
553
	$res = '';
554
	foreach ($hidden as $k) {
555
		$v = spip_htmlentities(_request($k));
556
		$res .= "<input type='hidden' name='$k' value='$v' />";
557
	}
558
559
	return $res;
560
}
561