Completed
Push — master ( 2ca868...d39161 )
by cam
08:10 queued 03:16
created

utils.php ➔ formats_image_acceptables()   B

Complexity

Conditions 7
Paths 30

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 30
nop 2
dl 0
loc 26
rs 8.5706
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Système de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright © avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
10
 *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Utilitaires indispensables autour du serveur Http.
15
 *
16
 * @package SPIP\Core\Utilitaires
17
 **/
18
19
if (!defined('_ECRIRE_INC_VERSION')) {
20
	return;
21
}
22
23
24
/**
25
 * Cherche une fonction surchargeable et en retourne le nom exact,
26
 * après avoir chargé le fichier la contenant si nécessaire.
27
 *
28
 * Charge un fichier (suivant les chemins connus) et retourne si elle existe
29
 * le nom de la fonction homonyme `$dir_$nom`, ou suffixé `$dir_$nom_dist`
30
 *
31
 * Peut être appelé plusieurs fois, donc optimisé.
32
 *
33
 * @api
34
 * @uses include_spip() Pour charger le fichier
35
 * @example
36
 *     ```
37
 *     $envoyer_mail = charger_fonction('envoyer_mail', 'inc');
38
 *     $envoyer_mail($email, $sujet, $texte);
39
 *     ```
40
 *
41
 * @param string $nom
42
 *     Nom de la fonction (et du fichier)
43
 * @param string $dossier
44
 *     Nom du dossier conteneur
45
 * @param bool $continue
46
 *     true pour ne pas râler si la fonction n'est pas trouvée
47
 * @return string
48
 *     Nom de la fonction, ou false.
49
 */
50
function charger_fonction($nom, $dossier = 'exec', $continue = false) {
51
	static $echecs = array();
52
53
	if (strlen($dossier) and substr($dossier, -1) != '/') {
54
		$dossier .= '/';
55
	}
56
	$f = str_replace('/', '_', $dossier) . $nom;
57
58
	if (function_exists($f)) {
59
		return $f;
60
	}
61
	if (function_exists($g = $f . '_dist')) {
62
		return $g;
63
	}
64
65
	if (isset($echecs[$f])) {
66
		return $echecs[$f];
67
	}
68
	// Sinon charger le fichier de declaration si plausible
69
70
	if (!preg_match(',^\w+$,', $f)) {
71
		if ($continue) {
72
			return false;
73
		} //appel interne, on passe
74
		include_spip('inc/minipres');
75
		echo minipres();
76
		exit;
77
	}
78
79
	// passer en minuscules (cf les balises de formulaires)
80
	// et inclure le fichier
81
	if (!$inc = include_spip($dossier . ($d = strtolower($nom)))
82
		// si le fichier truc/machin/nom.php n'existe pas,
83
		// la fonction peut etre definie dans truc/machin.php qui regroupe plusieurs petites fonctions
84
		and strlen(dirname($dossier)) and dirname($dossier) != '.'
85
	) {
86
		include_spip(substr($dossier, 0, -1));
87
	}
88
	if (function_exists($f)) {
89
		return $f;
90
	}
91
	if (function_exists($g)) {
92
		return $g;
93
	}
94
95
	if ($continue) {
96
		return $echecs[$f] = false;
97
	}
98
99
	// Echec : message d'erreur
100
	spip_log("fonction $nom ($f ou $g) indisponible" .
101
		($inc ? "" : " (fichier $d absent de $dossier)"));
102
103
	include_spip('inc/minipres');
104
	echo minipres(_T('forum_titre_erreur'),
105
		_T('fichier_introuvable', array('fichier' => '<b>' . spip_htmlentities($d) . '</b>')),
106
		array('all_inline'=>true,'status'=>404));
107
	exit;
108
}
109
110
/**
111
 * Inclusion unique avec verification d'existence du fichier + log en crash sinon
112
 *
113
 * @param string $file
114
 * @return bool
115
 */
116
function include_once_check($file) {
117
	if (file_exists($file)) {
118
		include_once $file;
119
120
		return true;
121
	}
122
	$crash = (isset($GLOBALS['meta']['message_crash_plugins']) ? unserialize($GLOBALS['meta']['message_crash_plugins']) : '');
123
	$crash = ($crash ? $crash : array());
124
	$crash[$file] = true;
125
	ecrire_meta('message_crash_plugins', serialize($crash));
126
127
	return false;
128
}
129
130
131
/**
132
 * Inclut un fichier PHP (en le cherchant dans les chemins)
133
 *
134
 * @api
135
 * @uses find_in_path()
136
 * @example
137
 *     ```
138
 *     include_spip('inc/texte');
139
 *     ```
140
 *
141
 * @param string $f
142
 *     Nom du fichier (sans l'extension)
143
 * @param bool $include
144
 *     - true pour inclure le fichier,
145
 *     - false ne fait que le chercher
146
 * @return string|bool
147
 *     - false : fichier introuvable
148
 *     - string : chemin du fichier trouvé
149
 **/
150
function include_spip($f, $include = true) {
151
	return find_in_path($f . '.php', '', $include);
152
}
153
154
/**
155
 * Requiert un fichier PHP (en le cherchant dans les chemins)
156
 *
157
 * @uses find_in_path()
158
 * @see  include_spip()
159
 * @example
160
 *     ```
161
 *     require_spip('inc/texte');
162
 *     ```
163
 *
164
 * @param string $f
165
 *     Nom du fichier (sans l'extension)
166
 * @return string|bool
167
 *     - false : fichier introuvable
168
 *     - string : chemin du fichier trouvé
169
 **/
170
function require_spip($f) {
171
	return find_in_path($f . '.php', '', 'required');
172
}
173
174
/**
175
 * Exécute une fonction (appellée par un pipeline) avec la donnée transmise.
176
 *
177
 * Un pipeline est lie a une action et une valeur
178
 * chaque element du pipeline est autorise a modifier la valeur
179
 * le pipeline execute les elements disponibles pour cette action,
180
 * les uns apres les autres, et retourne la valeur finale
181
 *
182
 * Cf. compose_filtres dans references.php, qui est la
183
 * version compilee de cette fonctionnalite
184
 * appel unitaire d'une fonction du pipeline
185
 * utilisee dans le script pipeline precompile
186
 *
187
 * on passe $val par reference pour limiter les allocations memoire
188
 *
189
 * @param string $fonc
190
 *     Nom de la fonction appelée par le pipeline
191
 * @param string|array $val
192
 *     Les paramètres du pipeline, son environnement
193
 * @return string|array $val
194
 *     Les paramètres du pipeline modifiés
195
 **/
196
function minipipe($fonc, &$val) {
197
	// fonction
198
	if (function_exists($fonc)) {
199
		$val = call_user_func($fonc, $val);
200
	} // Class::Methode
201
	else {
202
		if (preg_match("/^(\w*)::(\w*)$/S", $fonc, $regs)
203
			and $methode = array($regs[1], $regs[2])
204
			and is_callable($methode)
205
		) {
206
			$val = call_user_func($methode, $val);
207
		} else {
208
			spip_log("Erreur - '$fonc' non definie !");
209
		}
210
	}
211
212
	return $val;
213
}
214
215
/**
216
 * Appel d’un pipeline
217
 *
218
 * Exécute le pipeline souhaité, éventuellement avec des données initiales.
219
 * Chaque plugin qui a demandé à voir ce pipeline vera sa fonction spécifique appelée.
220
 * Les fonctions (des plugins) appelées peuvent modifier à leur guise le contenu.
221
 *
222
 * Deux types de retours. Si `$val` est un tableau de 2 éléments, avec une clé `data`
223
 * on retourne uniquement ce contenu (`$val['data']`) sinon on retourne tout `$val`.
224
 *
225
 *
226
 * @example
227
 *     Appel du pipeline `pre_insertion`
228
 *     ```
229
 *     $champs = pipeline('pre_insertion', array(
230
 *         'args' => array('table' => 'spip_articles'),
231
 *         'data' => $champs
232
 *     ));
233
 *     ```
234
 *
235
 * @param string $action
236
 *     Nom du pipeline
237
 * @param null|string|array $val
238
 *     Données à l’entrée du pipeline
239
 * @return mixed|null
240
 *     Résultat
241
 */
242
function pipeline($action, $val = null) {
243
	static $charger;
244
245
	// chargement initial des fonctions mises en cache, ou generation du cache
246
	if (!$charger) {
247
		if (!($ok = @is_readable($charger = _CACHE_PIPELINES))) {
248
			include_spip('inc/plugin');
249
			// generer les fichiers php precompiles
250
			// de chargement des plugins et des pipelines
251
			actualise_plugins_actifs();
252
			if (!($ok = @is_readable($charger))) {
253
				spip_log("fichier $charger pas cree");
254
			}
255
		}
256
257
		if ($ok) {
258
			include_once $charger;
259
		}
260
	}
261
262
	// appliquer notre fonction si elle existe
263
	$fonc = 'execute_pipeline_' . strtolower($action);
264
	if (function_exists($fonc)) {
265
		$val = $fonc($val);
266
	} // plantage ?
267
	else {
268
		spip_log("fonction $fonc absente : pipeline desactive", _LOG_ERREUR);
269
	}
270
271
	// si le flux est une table avec 2 cle args&data
272
	// on ne ressort du pipe que les donnees dans 'data'
273
	// array_key_exists pour php 4.1.0
274
	if (is_array($val)
275
		and count($val) == 2
276
		and (array_key_exists('data', $val))
277
	) {
278
		$val = $val['data'];
279
	}
280
281
	return $val;
282
}
283
284
/**
285
 * Enregistrement des événements
286
 *
287
 * Signature : `spip_log(message[,niveau|type|type.niveau])`
288
 *
289
 * Le niveau de log par défaut est la valeur de la constante `_LOG_INFO`
290
 *
291
 * Les différents niveaux possibles sont :
292
 *
293
 * - `_LOG_HS` : écrira 'HS' au début de la ligne logguée
294
 * - `_LOG_ALERTE_ROUGE` : 'ALERTE'
295
 * - `_LOG_CRITIQUE` :  'CRITIQUE'
296
 * - `_LOG_ERREUR` : 'ERREUR'
297
 * - `_LOG_AVERTISSEMENT` : 'WARNING'
298
 * - `_LOG_INFO_IMPORTANTE` : '!INFO'
299
 * - `_LOG_INFO` : 'info'
300
 * - `_LOG_DEBUG` : 'debug'
301
 *
302
 * @example
303
 *   ```
304
 *   spip_log($message)
305
 *   spip_log($message, 'recherche')
306
 *   spip_log($message, _LOG_DEBUG)
307
 *   spip_log($message, 'recherche.'._LOG_DEBUG)
308
 *   ```
309
 *
310
 * @api
311
 * @link https://programmer.spip.net/spip_log
312
 * @uses inc_log_dist()
313
 *
314
 * @param string $message
0 ignored issues
show
Documentation introduced by
Should the type for parameter $message not be string|null?

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.

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

Loading history...
315
 *     Message à loger
316
 * @param string|int $name
0 ignored issues
show
Documentation introduced by
Should the type for parameter $name not be string|integer|null?

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.

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

Loading history...
317
 *
318
 *     - int indique le niveau de log, tel que `_LOG_DEBUG`
319
 *     - string indique le type de log
320
 *     - `string.int` indique les 2 éléments.
321
 *     Cette dernière notation est controversée mais le 3ème
322
 *     paramètre est planté pour cause de compatibilité ascendante.
323
 */
324
function spip_log($message = null, $name = null) {
325
	static $pre = array();
326
	static $log;
327
	preg_match('/^([a-z_]*)\.?(\d)?$/iS', (string)$name, $regs);
328
	if (!isset($regs[1]) or !$logname = $regs[1]) {
329
		$logname = null;
330
	}
331
	if (!isset($regs[2]) or !$niveau = $regs[2]) {
332
		$niveau = _LOG_INFO;
333
	}
334
335
	if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE : _LOG_INFO_IMPORTANTE)) {
336
		if (!$pre) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pre 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...
337
			$pre = array(
338
				_LOG_HS => 'HS:',
339
				_LOG_ALERTE_ROUGE => 'ALERTE:',
340
				_LOG_CRITIQUE => 'CRITIQUE:',
341
				_LOG_ERREUR => 'ERREUR:',
342
				_LOG_AVERTISSEMENT => 'WARNING:',
343
				_LOG_INFO_IMPORTANTE => '!INFO:',
344
				_LOG_INFO => 'info:',
345
				_LOG_DEBUG => 'debug:'
346
			);
347
			$log = charger_fonction('log', 'inc');
348
		}
349
		if (!is_string($message)) {
350
			$message = print_r($message, true);
351
		}
352
		$log($pre[$niveau] . ' ' . $message, $logname);
353
	}
354
}
355
356
/**
357
 * Enregistrement des journaux
358
 *
359
 * @uses inc_journal_dist()
360
 * @param string $phrase Texte du journal
361
 * @param array $opt Tableau d'options
362
 **/
363
function journal($phrase, $opt = array()) {
364
	$journal = charger_fonction('journal', 'inc');
365
	$journal($phrase, $opt);
366
}
367
368
369
/**
370
 * Renvoie le `$_GET` ou le `$_POST` émis par l'utilisateur
371
 * ou pioché dans un tableau transmis
372
 *
373
 * @api
374
 * @param string $var
375
 *     Clé souhaitée
376
 * @param bool|array $c
377
 *     Tableau transmis (sinon cherche dans GET ou POST)
378
 * @return mixed|null
379
 *     - null si la clé n'a pas été trouvée
380
 *     - la valeur de la clé sinon.
381
 **/
382
function _request($var, $c = false) {
383
384
	if (is_array($c)) {
385
		return isset($c[$var]) ? $c[$var] : null;
386
	}
387
388
	if (isset($_GET[$var])) {
389
		$a = $_GET[$var];
390
	} elseif (isset($_POST[$var])) {
391
		$a = $_POST[$var];
392
	} else {
393
		return null;
394
	}
395
396
	// Si on est en ajax et en POST tout a ete encode
397
	// via encodeURIComponent, il faut donc repasser
398
	// dans le charset local...
399
	if (defined('_AJAX')
400
		and _AJAX
401
		and isset($GLOBALS['meta']['charset'])
402
		and $GLOBALS['meta']['charset'] != 'utf-8'
403
		and is_string($a)
404
		// check rapide mais pas fiable
405
		and preg_match(',[\x80-\xFF],', $a)
406
		// check fiable
407
		and include_spip('inc/charsets')
408
		and is_utf8($a)
409
	) {
410
		return importer_charset($a, 'utf-8');
411
	}
412
413
	return $a;
414
}
415
416
417
/**
418
 * Affecte une valeur à une clé (pour usage avec `_request()`)
419
 *
420
 * @see _request() Pour obtenir la valeur
421
 * @note Attention au cas ou l'on fait `set_request('truc', NULL);`
422
 *
423
 * @param string $var Nom de la clé
424
 * @param string $val Valeur à affecter
0 ignored issues
show
Documentation introduced by
Should the type for parameter $val not be string|null?

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.

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

Loading history...
425
 * @param bool|array $c Tableau de données (sinon utilise `$_GET` et `$_POST`)
426
 * @return array|bool
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array|false.

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...
427
 *     - array $c complété si un $c est transmis,
428
 *     - false sinon
429
 **/
430
function set_request($var, $val = null, $c = false) {
431
	if (is_array($c)) {
432
		unset($c[$var]);
433
		if ($val !== null) {
434
			$c[$var] = $val;
435
		}
436
437
		return $c;
438
	}
439
440
	unset($_GET[$var]);
441
	unset($_POST[$var]);
442
	if ($val !== null) {
443
		$_GET[$var] = $val;
444
	}
445
446
	return false; # n'affecte pas $c
447
}
448
449
/**
450
 * Sanitizer une valeur *SI* elle provient du GET ou POST
451
 * Utile dans les squelettes pour les valeurs qu'on attrape dans le env,
452
 * dont on veut permettre à un squelette de confiance appelant de fournir une valeur complexe
453
 * mais qui doit etre nettoyee si elle provient de l'URL
454
 *
455
 * On peut sanitizer
456
 * - une valeur simple : `$where = spip_sanitize_from_request($value, 'where')`
457
 * - un tableau en partie : `$env = spip_sanitize_from_request($env, ['key1','key2'])`
458
 * - un tableau complet : `$env = spip_sanitize_from_request($env, '*')`
459
 *
460
 * @param string|array $value
461
 * @param string|array $key
462
 * @param string $sanitize_function
463
 * @return array|mixed|string
464
 */
465
function spip_sanitize_from_request($value, $key, $sanitize_function='entites_html') {
466
	if (is_array($value)) {
467
		if ($key=='*') {
468
			$key = array_keys($value);
469
		}
470
		if (!is_array($key)) {
471
			$key = [$key];
472
		}
473
		foreach ($key as $k) {
474
			if (!empty($value[$k])) {
475
				$value[$k] = spip_sanitize_from_request($value[$k], $k, $sanitize_function);
476
			}
477
		}
478
		return $value;
479
	}
480
	// si la valeur vient des GET ou POST on la sanitize
481
	if (!empty($value) and $value == _request($key)) {
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 465 can also be of type array; however, _request() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
482
		$value = $sanitize_function($value);
483
	}
484
	return $value;
485
}
486
487
/**
488
 * Tester si une URL est absolue
489
 * 
490
 * On est sur le web, on exclut certains protocoles, 
491
 * notamment 'file://', 'php://' et d'autres…
492
493
 * @param string $url
494
 * @return bool
495
 */
496
function tester_url_absolue($url) {
497
	$url = trim($url);
498
	if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
499
		if (
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !(isset($m[1]) an...g', 'expect', 'zip')));.
Loading history...
500
			isset($m[1])
501
			and $p = strtolower(rtrim($m[1], ':'))
502
			and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
503
		  ) {
504
			return false;
505
		}
506
		return true;
507
	}
508
	return false;
509
}
510
511
/**
512
 * Prend une URL et lui ajoute/retire un paramètre
513
 *
514
 * @filtre
515
 * @link https://www.spip.net/4255
516
 * @example
517
 *     ```
518
 *     [(#SELF|parametre_url{suite,18})] (ajout)
519
 *     [(#SELF|parametre_url{suite,''})] (supprime)
520
 *     [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
521
 *     ```
522
 *
523
 * @param string $url URL
524
 * @param string $c Nom du paramètre
525
 * @param string|array|null $v Valeur du paramètre
526
 * @param string $sep Séparateur entre les paramètres
527
 * @return string URL
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string|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.

Loading history...
528
 */
529
function parametre_url($url, $c, $v = null, $sep = '&amp;') {
530
	// requete erronnee : plusieurs variable dans $c et aucun $v
531
	if (strpos($c, "|") !== false and is_null($v)) {
532
		return null;
533
	}
534
535
	// lever l'#ancre
536
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
537
		$url = $r[1];
538
		$ancre = $r[2];
539
	} else {
540
		$ancre = '';
541
	}
542
543
	// eclater
544
	$url = preg_split(',[?]|&amp;|&,', $url);
545
546
	// recuperer la base
547
	$a = array_shift($url);
548
	if (!$a) {
549
		$a = './';
550
	}
551
552
	$regexp = ',^(' . str_replace('[]', '\[\]', $c) . '[[]?[]]?)(=.*)?$,';
553
	$ajouts = array_flip(explode('|', $c));
554
	$u = is_array($v) ? $v : rawurlencode($v);
555
	$testv = (is_array($v) ? count($v) : strlen($v));
556
	$v_read = null;
557
	// lire les variables et agir
558
	foreach ($url as $n => $val) {
559
		if (preg_match($regexp, urldecode($val), $r)) {
560
			$r = array_pad($r, 3, null);
561
			if ($v === null) {
562
				// c'est un tableau, on memorise les valeurs
563
				if (substr($r[1], -2) == "[]") {
564
					if (!$v_read) {
565
						$v_read = array();
566
					}
567
					$v_read[] = $r[2] ? substr($r[2], 1) : '';
568
				} // c'est un scalaire, on retourne direct
569
				else {
570
					return $r[2] ? substr($r[2], 1) : '';
571
				}
572
			} // suppression
573
			elseif (!$testv) {
574
				unset($url[$n]);
575
			}
576
			// Ajout. Pour une variable, remplacer au meme endroit,
577
			// pour un tableau ce sera fait dans la prochaine boucle
578
			elseif (substr($r[1], -2) != '[]') {
579
				$url[$n] = $r[1] . '=' . $u;
580
				unset($ajouts[$r[1]]);
581
			}
582
			// Pour les tableaux on laisse tomber les valeurs de
583
			// départ, on remplira à l'étape suivante
584
			else {
585
				unset($url[$n]);
586
			}
587
		}
588
	}
589
590
	// traiter les parametres pas encore trouves
591
	if ($v === null
592
		and $args = func_get_args()
593
		and count($args) == 2
594
	) {
595
		return $v_read; // rien trouve ou un tableau
596
	} elseif ($testv) {
597
		foreach ($ajouts as $k => $n) {
598
			if (!is_array($v)) {
599
				$url[] = $k . '=' . $u;
600
			} else {
601
				$id = (substr($k, -2) == '[]') ? $k : ($k . "[]");
602
				foreach ($v as $w) {
603
					$url[] = $id . '=' . (is_array($w) ? 'Array' : $w);
604
				}
605
			}
606
		}
607
	}
608
609
	// eliminer les vides
610
	$url = array_filter($url);
611
612
	// recomposer l'adresse
613
	if ($url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url 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...
614
		$a .= '?' . join($sep, $url);
615
	}
616
617
	return $a . $ancre;
618
}
619
620
/**
621
 * Ajoute (ou retire) une ancre sur une URL
622
 *
623
 * L’ancre est nettoyée : on translitère, vire les non alphanum du début,
624
 * et on remplace ceux à l'interieur ou au bout par `-`
625
 *
626
 * @example
627
 *     - `$url = ancre_url($url, 'navigation'); // => mettra l’ancre #navigation
628
 *     - `$url = ancre_url($url, ''); // => enlèvera une éventuelle ancre
629
 * @uses translitteration()
630
 * @param string $url
631
 * @param string $ancre
632
 * @return string
633
 */
634
function ancre_url($url, $ancre) {
635
	// lever l'#ancre
636
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
637
		$url = $r[1];
638
	}
639
	if (preg_match('/[^-_a-zA-Z0-9]+/S', $ancre)) {
640
		if (!function_exists('translitteration')) {
641
			include_spip('inc/charsets');
642
		}
643
		$ancre = preg_replace(
644
			array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'),
645
			array('', '-'),
646
			translitteration($ancre)
647
		);
648
	}
649
	return $url . (strlen($ancre) ? '#' . $ancre : '');
650
}
651
652
/**
653
 * Pour le nom du cache, les `types_urls` et `self`
654
 *
655
 * @param string|null $reset
656
 * @return string
657
 */
658
function nettoyer_uri($reset = null) {
659
	static $done = false;
660
	static $propre = '';
661
	if (!is_null($reset)) {
662
		return $propre = $reset;
663
	}
664
	if ($done) {
665
		return $propre;
666
	}
667
	$done = true;
668
	return $propre = nettoyer_uri_var($GLOBALS['REQUEST_URI']);
669
}
670
671
/**
672
 * Nettoie une request_uri des paramètres var_xxx
673
 * 
674
 * Attention, la regexp doit suivre _CONTEXTE_IGNORE_VARIABLES défini au début de public/assembler.php
675
 * 
676
 * @param $request_uri
677
 * @return string
678
 */
679
function nettoyer_uri_var($request_uri) {
680
	$uri1 = $request_uri;
681
	do {
682
		$uri = $uri1;
683
		$uri1 = preg_replace(',([?&])(var_[^=&]*|PHPSESSID|fbclid|utm_[^=&]*)=[^&]*(&|$),i',
684
			'\1', $uri);
685
	} while ($uri <> $uri1);
686
	return preg_replace(',[?&]$,', '', $uri1);
687
}
688
689
690
/**
691
 * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
692
 *
693
 * @param string $amp
694
 *    Style des esperluettes
695
 * @param bool $root
696
 * @return string
697
 *    URL vers soi-même
698
 **/
699
function self($amp = '&amp;', $root = false) {
700
	$url = nettoyer_uri();
701
	if (!$root
702
		and (
703
			// si pas de profondeur on peut tronquer
704
			$GLOBALS['profondeur_url'] < (_DIR_RESTREINT ? 1 : 2)
705
			// sinon c'est OK si _SET_HTML_BASE a ete force a false
706
			or (defined('_SET_HTML_BASE') and !_SET_HTML_BASE))
707
	) {
708
		$url = preg_replace(',^[^?]*/,', '', $url);
709
	}
710
	// ajouter le cas echeant les variables _POST['id_...']
711
	foreach ($_POST as $v => $c) {
712
		if (substr($v, 0, 3) == 'id_') {
713
			$url = parametre_url($url, $v, $c, '&');
714
		}
715
	}
716
717
	// supprimer les variables sans interet
718
	if (test_espace_prive()) {
719
		$url = preg_replace(',([?&])('
720
			. 'lang|show_docs|'
721
			. 'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
722
		$url = preg_replace(',([?&])[&]+,', '\1', $url);
723
		$url = preg_replace(',[&]$,', '\1', $url);
724
	}
725
726
	// eviter les hacks
727
	include_spip('inc/filtres_mini');
728
	$url = spip_htmlspecialchars($url);
0 ignored issues
show
Bug introduced by
It seems like $url can also be of type array<integer,string> or null; however, spip_htmlspecialchars() does only seem to accept string, 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...
729
	
730
	$url = str_replace(array("'", '"', '<', '[', ']', ':'), array('%27', '%22', '%3C', '%5B', '%5D', '%3A'), $url);
731
732
	// &amp; ?
733
	if ($amp != '&amp;') {
734
		$url = str_replace('&amp;', $amp, $url);
735
	}
736
737
	// Si ca demarre par ? ou vide, donner './'
738
	$url = preg_replace(',^([?].*)?$,', './\1', $url);
739
740
	return $url;
741
}
742
743
744
/**
745
 * Indique si on est dans l'espace prive
746
 *
747
 * @return bool
748
 *     true si c'est le cas, false sinon.
749
 */
750
function test_espace_prive() {
751
	return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
752
}
753
754
/**
755
 * Vérifie la présence d'un plugin actif, identifié par son préfixe
756
 *
757
 * @param string $plugin
758
 * @return bool
759
 */
760
function test_plugin_actif($plugin) {
761
	return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ? true : false;
762
}
763
764
/**
765
 * Traduction des textes de SPIP
766
 *
767
 * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
768
 *
769
 * @api
770
 * @uses inc_traduire_dist()
771
 * @uses _L()
772
 * @example
773
 *     ```
774
 *     _T('bouton_enregistrer')
775
 *     _T('medias:image_tourner_droite')
776
 *     _T('medias:erreurs', array('nb'=>3))
777
 *     _T("email_sujet", array('spip_lang'=>$lang_usager))
778
 *     ```
779
 *
780
 * @param string $texte
781
 *     Clé de traduction
782
 * @param array $args
783
 *     Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
784
 * @param array $options
785
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
786
 *     - bool force : forcer un retour meme si la chaine n'a pas de traduction
787
 *     - bool sanitize : nettoyer le html suspect dans les arguments
788
 * @return string
789
 *     Texte
790
 */
791
function _T($texte, $args = array(), $options = array()) {
792
	static $traduire = false;
793
	$o = array('class' => '', 'force' => true, 'sanitize' => true);
794
	if ($options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options 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...
795
		// support de l'ancien argument $class
796
		if (is_string($options)) {
797
			$options = array('class' => $options);
798
		}
799
		$o = array_merge($o, $options);
800
	}
801
802
	if (!$traduire) {
803
		$traduire = charger_fonction('traduire', 'inc');
804
		include_spip('inc/lang');
805
	}
806
807
	// On peut passer explicitement la langue dans le tableau
808
	// On utilise le même nom de variable que la globale
809 View Code Duplication
	if (isset($args['spip_lang'])) {
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...
810
		$lang = $args['spip_lang'];
811
		// On l'enleve pour ne pas le passer au remplacement
812
		unset($args['spip_lang']);
813
	} // Sinon on prend la langue du contexte
814
	else {
815
		$lang = $GLOBALS['spip_lang'];
816
	}
817
	$text = $traduire($texte, $lang);
818
819
	if (!strlen($text)) {
820
		if (!$o['force']) {
821
			return '';
822
		}
823
824
		$text = $texte;
825
826
		// pour les chaines non traduites, assurer un service minimum
827
		if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
828
			$text = str_replace('_', ' ',
829
				(($n = strpos($text, ':')) === false ? $texte :
830
					substr($texte, $n + 1)));
831
		}
832
		$o['class'] = null;
833
834
	}
835
836
	return _L($text, $args, $o);
837
838
}
839
840
841
/**
842
 * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
843
 *
844
 * Cette fonction est également appelée dans le code source de SPIP quand une
845
 * chaîne n'est pas encore dans les fichiers de langue.
846
 *
847
 * @see _T()
848
 * @example
849
 *     ```
850
 *     _L('Texte avec @nb@ ...', array('nb'=>3)
851
 *     ```
852
 *
853
 * @param string $text
854
 *     Texte
855
 * @param array $args
856
 *     Couples (variable => valeur) à transformer dans le texte
857
 * @param array $options
858
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
859
 *     - bool sanitize : nettoyer le html suspect dans les arguments
860
 * @return string
861
 *     Texte
862
 */
863
function _L($text, $args = array(), $options = array()) {
864
	$f = $text;
865
	$defaut_options = array(
866
		'class' => null,
867
		'sanitize' => true,
868
	);
869
	// support de l'ancien argument $class
870
	if ($options and is_string($options)) {
871
		$options = array('class' => $options);
872
	}
873
	if (is_array($options)) {
874
		$options += $defaut_options;
875
	} else {
876
		$options = $defaut_options;
877
	}
878
879
	if (is_array($args) and count($args)) {
880
		if (!function_exists('interdire_scripts')) {
881
			include_spip('inc/texte');
882
		}
883
		if (!function_exists('echapper_html_suspect')) {
884
			include_spip('inc/texte_mini');
885
		}
886
		foreach ($args as $name => $value) {
887
			if (strpos($text, "@$name@") !== false) {
888
				if ($options['sanitize']) {
889
					$value = echapper_html_suspect($value);
890
					$value = interdire_scripts($value, -1);
891
				}
892
				if (!empty($options['class'])) {
893
					$value = "<span class='".$options['class']."'>$value</span>";
894
				}
895
				$text = str_replace("@$name@", $value, $text);
896
				unset($args[$name]);
897
			}
898
		}
899
		// Si des variables n'ont pas ete inserees, le signaler
900
		// (chaines de langues pas a jour)
901
		if ($args) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $args 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...
902
			spip_log("$f:  variables inutilisees " . join(', ', array_keys($args)), _LOG_DEBUG);
903
		}
904
	}
905
906
	if (($GLOBALS['test_i18n'] or (_request('var_mode') == 'traduction')) and is_null($options['class'])) {
907
		return "<span class=debug-traduction-erreur>$text</span>";
908
	} else {
909
		return $text;
910
	}
911
}
912
913
914
/**
915
 * Retourne un joli chemin de répertoire
916
 *
917
 * Pour afficher `ecrire/action/` au lieu de `action/` dans les messages
918
 * ou `tmp/` au lieu de `../tmp/`
919
 *
920
 * @param stirng $rep Chemin d’un répertoire
921
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|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.

Loading history...
922
 */
923
function joli_repertoire($rep) {
924
	$a = substr($rep, 0, 1);
925
	if ($a <> '.' and $a <> '/') {
926
		$rep = (_DIR_RESTREINT ? '' : _DIR_RESTREINT_ABS) . $rep;
927
	}
928
	$rep = preg_replace(',(^\.\.\/),', '', $rep);
929
930
	return $rep;
931
}
932
933
934
/**
935
 * Débute ou arrête un chronomètre et retourne sa valeur
936
 *
937
 * On exécute 2 fois la fonction, la première fois pour démarrer le chrono,
938
 * la seconde fois pour l’arrêter et récupérer la valeur
939
 *
940
 * @example
941
 *     ```
942
 *     spip_timer('papoter');
943
 *     // actions
944
 *     $duree = spip_timer('papoter');
945
 *     ```
946
 *
947
 * @param string $t
948
 *     Nom du chronomètre
949
 * @param bool $raw
950
 *     - false : retour en texte humainement lisible
951
 *     - true : retour en millisecondes
952
 * @return float|int|string|void
953
 */
954
function spip_timer($t = 'rien', $raw = false) {
955
	static $time;
956
	$a = time();
957
	$b = microtime();
958
	// microtime peut contenir les microsecondes et le temps
959
	$b = explode(' ', $b);
960
	if (count($b) == 2) {
961
		$a = end($b);
962
	} // plus precis !
963
	$b = reset($b);
964
	if (!isset($time[$t])) {
965
		$time[$t] = $a + $b;
966
	} else {
967
		$p = ($a + $b - $time[$t]) * 1000;
968
		unset($time[$t]);
969
#			echo "'$p'";exit;
970
		if ($raw) {
971
			return $p;
972
		}
973
		if ($p < 1000) {
974
			$s = '';
975
		} else {
976
			$s = sprintf("%d ", $x = floor($p / 1000));
977
			$p -= ($x * 1000);
978
		}
979
980
		return $s . sprintf($s ? "%07.3f ms" : "%.3f ms", $p);
981
	}
982
}
983
984
985
// Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
986
// sinon renvoie True et le date sauf si ca n'est pas souhaite
987
// https://code.spip.net/@spip_touch
988
function spip_touch($fichier, $duree = 0, $touch = true) {
989
	if ($duree) {
990
		clearstatcache();
991
		if ((@$f = filemtime($fichier)) and ($f >= time() - $duree)) {
992
			return false;
993
		}
994
	}
995
	if ($touch !== false) {
996
		if (!@touch($fichier)) {
997
			spip_unlink($fichier);
998
			@touch($fichier);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
999
		};
1000
		@chmod($fichier, _SPIP_CHMOD & ~0111);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1001
	}
1002
1003
	return true;
1004
}
1005
1006
1007
/**
1008
 * Action qui déclenche une tache de fond
1009
 *
1010
 * @see  queue_affichage_cron()
1011
 * @see  action_super_cron_dist()
1012
 * @uses cron()
1013
 **/
1014
function action_cron() {
1015
	include_spip('inc/headers');
1016
	http_status(204); // No Content
1017
	header("Connection: close");
1018
	define('_DIRECT_CRON_FORCE', true);
1019
	cron();
1020
}
1021
1022
/**
1023
 * Exécution des tâches de fond
1024
 *
1025
 * @uses inc_genie_dist()
1026
 *
1027
 * @param array $taches
1028
 *     Tâches forcées
1029
 * @param array $taches_old
1030
 *     Tâches forcées, pour compat avec ancienne syntaxe
1031
 * @return bool
1032
 *     True si la tache a pu être effectuée
1033
 */
1034
function cron($taches = array(), $taches_old = array()) {
1035
	// si pas en mode cron force, laisser tomber.
1036
	if (!defined('_DIRECT_CRON_FORCE')) {
1037
		return false;
1038
	}
1039
	if (!is_array($taches)) {
1040
		$taches = $taches_old;
1041
	} // compat anciens appels
1042
	// si taches a inserer en base et base inaccessible, laisser tomber
1043
	// sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
1044
	// queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
1045
	// et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
1046
	if ($taches and count($taches) and !spip_connect()) {
1047
		return false;
1048
	}
1049
	spip_log("cron !", 'jq' . _LOG_DEBUG);
1050
	if ($genie = charger_fonction('genie', 'inc', true)) {
1051
		return $genie($taches);
1052
	}
1053
1054
	return false;
1055
}
1056
1057
/**
1058
 * Ajout d'une tache dans la file d'attente
1059
 *
1060
 * @param string $function
1061
 *     Le nom de la fonction PHP qui doit être appelée.
1062
 * @param string $description
1063
 *     Une description humainement compréhensible de ce que fait la tâche
1064
 *     (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
1065
 * @param array $arguments
1066
 *     Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
1067
 * @param string $file
1068
 *     Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
1069
 *     exemple : `'inc/mail'` : il ne faut pas indiquer .php
1070
 *     Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
1071
 * @param bool $no_duplicate
1072
 *     Facultatif, `false` par défaut
1073
 *
1074
 *     - si `true` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction et les mêmes arguments.
1075
 *     - si `function_only` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction indépendamment de ses arguments
1076
 * @param int $time
1077
 *     Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
1078
 *     Si `0` ou une date passée, la tâche sera exécutée aussitôt que possible (en général en fin hit, en asynchrone).
1079
 * @param int $priority
1080
 *     Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
1081
 *     Les tâches sont exécutées par ordre de priorité décroissante, une fois leur date d’exécution passée. La priorité est surtout utilisée quand une tâche cron indique qu’elle n’a pas fini et doit être relancée : dans ce cas SPIP réduit sa priorité pour être sûr que celle tâche ne monopolise pas la file d’attente.
1082
 * @return int
1083
 *     Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
1084
 */
1085
function job_queue_add(
1086
	$function,
1087
	$description,
1088
	$arguments = array(),
1089
	$file = '',
1090
	$no_duplicate = false,
1091
	$time = 0,
1092
	$priority = 0
1093
) {
1094
	include_spip('inc/queue');
1095
1096
	return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
1097
}
1098
1099
/**
1100
 * Supprimer une tache de la file d'attente
1101
 *
1102
 * @param int $id_job
1103
 *  id of jonb to delete
1104
 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|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.

Loading history...
1105
 */
1106
function job_queue_remove($id_job) {
1107
	include_spip('inc/queue');
1108
1109
	return queue_remove_job($id_job);
1110
}
1111
1112
/**
1113
 * Associer une tache a un/des objets de SPIP
1114
 *
1115
 * @param int $id_job
1116
 *     id of job to link
1117
 * @param array $objets
1118
 *     can be a simple array('objet'=>'article', 'id_objet'=>23)
1119
 *     or an array of simple array to link multiples objet in one time
1120
 */
1121
function job_queue_link($id_job, $objets) {
1122
	include_spip('inc/queue');
1123
1124
	return queue_link_job($id_job, $objets);
1125
}
1126
1127
1128
/**
1129
 * Renvoyer le temps de repos restant jusqu'au prochain job
1130
 *
1131
 * @staticvar int $queue_next_job_time
1132
 * @see queue_set_next_job_time()
1133
 * @param int|bool $force
0 ignored issues
show
Documentation introduced by
Should the type for parameter $force not be integer|boolean|null?

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.

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

Loading history...
1134
 *    Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
1135
 *
1136
 *    - si `true`, force la relecture depuis le fichier
1137
 *    - si int, affecte la static directement avec la valeur
1138
 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be null|integer|double?

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...
1139
 *
1140
 *  - `0` si un job est à traiter
1141
 *  - `null` si la queue n'est pas encore initialisée
1142
 */
1143
function queue_sleep_time_to_next_job($force = null) {
1144
	static $queue_next_job_time = -1;
1145
	if ($force === true) {
1146
		$queue_next_job_time = -1;
1147
	} elseif ($force) {
1148
		$queue_next_job_time = $force;
1149
	}
1150
1151
	if ($queue_next_job_time == -1) {
1152
		if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
1153
			define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP . "job_queue_next.txt");
1154
		}
1155
		// utiliser un cache memoire si dispo
1156
		if (function_exists("cache_get") and defined('_MEMOIZE_MEMORY') and _MEMOIZE_MEMORY) {
1157
			$queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
1158
		} else {
1159
			$queue_next_job_time = null;
1160
			if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu)) {
1161
				$queue_next_job_time = intval($contenu);
1162
			}
1163
		}
1164
	}
1165
1166
	if (is_null($queue_next_job_time)) {
1167
		return null;
1168
	}
1169
	if (!$_SERVER['REQUEST_TIME']) {
1170
		$_SERVER['REQUEST_TIME'] = time();
1171
	}
1172
1173
	return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
1174
}
1175
1176
1177
/**
1178
 * Transformation XML des `&` en `&amp;`
1179
 * 
1180
 * @pipeline post_typo
1181
 * @param string $u
1182
 * @return string
1183
 */
1184
function quote_amp($u) {
1185
	return preg_replace(
1186
		"/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i",
1187
		"&amp;", $u);
1188
}
1189
1190
1191
/**
1192
 * Produit une balise `<script>` valide
1193
 *
1194
 * @example
1195
 *     ```
1196
 *     echo http_script('alert("ok");');
1197
 *     echo http_script('','js/jquery.js');
1198
 *     ```
1199
 *
1200
 * @param string $script
1201
 *     Code source du script
1202
 * @param string $src
1203
 *     Permet de faire appel à un fichier javascript distant
1204
 * @param string $noscript
1205
 *     Contenu de la balise  `<noscript>`
1206
 * @return string
1207
 *     Balise HTML `<script>` et son contenu
1208
 **/
1209
function http_script($script, $src = '', $noscript = '') {
1210
	static $done = array();
1211
1212
	if ($src && !isset($done[$src])) {
1213
		$done[$src] = true;
1214
		$src = find_in_path($src, _JAVASCRIPT);
1215
		$src = " src='$src'";
1216
	} else {
1217
		$src = '';
1218
	}
1219
	if ($script) {
1220
		$script = ("/*<![CDATA[*/\n" .
1221
			preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
1222
			"/*]]>*/");
1223
	}
1224
	if ($noscript) {
1225
		$noscript = "<noscript>\n\t$noscript\n</noscript>\n";
1226
	}
1227
1228
	return ($src or $script or $noscript)
1229
		? "<script type='text/javascript'$src>$script</script>$noscript"
1230
		: '';
1231
}
1232
1233
1234
/**
1235
 * Sécurise du texte à écrire dans du PHP ou du Javascript.
1236
 *
1237
 * Transforme n'importe quel texte en une chaîne utilisable
1238
 * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
1239
 * simples (`'` uniquement ; pas `"`)
1240
 *
1241
 * Utile particulièrement en filtre dans un squelettes
1242
 * pour écrire un contenu dans une variable JS ou PHP.
1243
 *
1244
 * Échappe les apostrophes (') du contenu transmis.
1245
 *
1246
 * @link https://www.spip.net/4281
1247
 * @example
1248
 *     PHP dans un squelette
1249
 *     ```
1250
 *     $x = '[(#TEXTE|texte_script)]';
1251
 *     ```
1252
 *
1253
 *     JS dans un squelette (transmettre une chaîne de langue)
1254
 *     ```
1255
 *     $x = '<:afficher_calendrier|texte_script:>';
1256
 *     ```
1257
 *
1258
 * @filtre
1259
 * @param string $texte
1260
 *     Texte à échapper
1261
 * @return string
1262
 *     Texte échappé
1263
 **/
1264
function texte_script($texte) {
1265
	return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
1266
}
1267
1268
1269
/**
1270
 * Gestion des chemins (ou path) de recherche de fichiers par SPIP
1271
 *
1272
 * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
1273
 * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
1274
 * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
1275
 *
1276
 * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
1277
 * en tête du path, dans cet ordre (hormis `squelettes` & la globale
1278
 * `$dossier_squelette` si définie qui resteront devant)
1279
 *
1280
 * Retourne dans tous les cas la liste des chemins.
1281
 *
1282
 * @note
1283
 *     Cette fonction est appelée à plusieurs endroits et crée une liste
1284
 *     de chemins finale à peu près de la sorte :
1285
 *
1286
 *     - dossiers squelettes (si globale précisée)
1287
 *     - squelettes/
1288
 *     - plugins (en fonction de leurs dépendances) : ceux qui dépendent
1289
 *       d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
1290
 *     - racine du site
1291
 *     - squelettes-dist/
1292
 *     - prive/
1293
 *     - ecrire/
1294
 *
1295
 * @param string $dir_path
0 ignored issues
show
Documentation introduced by
Should the type for parameter $dir_path not be string|null?

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.

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

Loading history...
1296
 *     - Répertoire(s) à empiler au path
1297
 *     - '' provoque un recalcul des chemins.
1298
 * @return array
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...
1299
 *     Liste des chemins, par ordre de priorité.
1300
 **/
1301
function _chemin($dir_path = null) {
1302
	static $path_base = null;
1303
	static $path_full = null;
1304
	if ($path_base == null) {
1305
		// Chemin standard depuis l'espace public
1306
		$path = defined('_SPIP_PATH') ? _SPIP_PATH :
1307
			_DIR_RACINE . ':' .
1308
			_DIR_RACINE . 'squelettes-dist/:' .
1309
			_DIR_RACINE . 'prive/:' .
1310
			_DIR_RESTREINT;
1311
		// Ajouter squelettes/
1312
		if (@is_dir(_DIR_RACINE . 'squelettes')) {
1313
			$path = _DIR_RACINE . 'squelettes/:' . $path;
1314
		}
1315
		foreach (explode(':', $path) as $dir) {
1316
			if (strlen($dir) and substr($dir, -1) != '/') {
1317
				$dir .= "/";
1318
			}
1319
			$path_base[] = $dir;
1320
		}
1321
		$path_full = $path_base;
1322
		// Et le(s) dossier(s) des squelettes nommes
1323 View Code Duplication
		if (strlen($GLOBALS['dossier_squelettes'])) {
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...
1324
			foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1325
				array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
1326
			}
1327
		}
1328
		$GLOBALS['path_sig'] = md5(serialize($path_full));
1329
	}
1330
	if ($dir_path === null) {
1331
		return $path_full;
1332
	}
1333
1334
	if (strlen($dir_path)) {
1335
		$tete = "";
1336
		if (reset($path_base) == _DIR_RACINE . 'squelettes/') {
1337
			$tete = array_shift($path_base);
1338
		}
1339
		$dirs = array_reverse(explode(':', $dir_path));
1340
		foreach ($dirs as $dir_path) {
1341
			#if ($dir_path{0}!='/')
1342
			#	$dir_path = $dir_path;
1343
			if (substr($dir_path, -1) != '/') {
1344
				$dir_path .= "/";
1345
			}
1346
			if (!in_array($dir_path, $path_base)) {
1347
				array_unshift($path_base, $dir_path);
1348
			}
1349
		}
1350
		if (strlen($tete)) {
1351
			array_unshift($path_base, $tete);
1352
		}
1353
	}
1354
	$path_full = $path_base;
1355
	// Et le(s) dossier(s) des squelettes nommes
1356 View Code Duplication
	if (strlen($GLOBALS['dossier_squelettes'])) {
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...
1357
		foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1358
			array_unshift($path_full, ((isset($d[0]) and $d[0] == '/') ? '' : _DIR_RACINE) . $d . '/');
1359
		}
1360
	}
1361
1362
	$GLOBALS['path_sig'] = md5(serialize($path_full));
1363
1364
	return $path_full;
1365
}
1366
1367
/**
1368
 * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
1369
 *
1370
 * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
1371
 *
1372
 * @uses _chemin()
1373
 *
1374
 * @return array Liste de chemins
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...
1375
 **/
1376
function creer_chemin() {
1377
	$path_a = _chemin();
1378
	static $c = '';
1379
1380
	// on calcule le chemin si le dossier skel a change
1381
	if ($c != $GLOBALS['dossier_squelettes']) {
1382
		// assurer le non plantage lors de la montee de version :
1383
		$c = $GLOBALS['dossier_squelettes'];
1384
		$path_a = _chemin(''); // forcer un recalcul du chemin
1385
	}
1386
1387
	return $path_a;
1388
}
1389
1390
1391
function lister_themes_prives() {
1392
	static $themes = null;
1393
	if (is_null($themes)) {
1394
		// si pas encore definie
1395
		if (!defined('_SPIP_THEME_PRIVE')) {
1396
			define('_SPIP_THEME_PRIVE', 'spip');
1397
		}
1398
		$themes = array(_SPIP_THEME_PRIVE);
1399
		// lors d'une installation neuve, prefs n'est pas definie.
1400 View Code Duplication
		if (isset($GLOBALS['visiteur_session']['prefs'])) {
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...
1401
			$prefs = $GLOBALS['visiteur_session']['prefs'];
1402
		} else {
1403
			$prefs = array();
1404
		}
1405
		if (is_string($prefs)) {
1406
			$prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
1407
		}
1408
		if (
1409
			((isset($prefs['theme']) and $theme = $prefs['theme'])
1410
				or (isset($GLOBALS['theme_prive_defaut']) and $theme = $GLOBALS['theme_prive_defaut']))
1411
			and $theme != _SPIP_THEME_PRIVE
1412
		) {
1413
			array_unshift($themes, $theme);
1414
		} // placer le theme choisi en tete
1415
	}
1416
1417
	return $themes;
1418
}
1419
1420
function find_in_theme($file, $subdir = '', $include = false) {
1421
	static $themefiles = array();
1422
	if (isset($themefiles["$subdir$file"])) {
1423
		return $themefiles["$subdir$file"];
1424
	}
1425
	// on peut fournir une icone generique -xx.svg qui fera le job dans toutes les tailles, et qui est prioritaire sur le png
1426
	// si il y a un .svg a la bonne taille (-16.svg) a cote, on l'utilise en remplacement du -16.png
1427
	if (preg_match(',-(\d+)[.](png|gif|svg)$,', $file, $m)
1428
	  and $file_svg_generique = substr($file,0, -strlen($m[0])) . "-xx.svg"
1429
		and $f = find_in_theme("$file_svg_generique")) {
1430
		if ($fsize = substr($f,0,-6) . $m[1] . ".svg" and file_exists($fsize)) {
1431
			return $themefiles["$subdir$file"] = $fsize;
1432
		}
1433
		else {
1434
			return $themefiles["$subdir$file"] = "$f?".$m[1]."px";
1435
		}
1436
	}
1437
1438
	$themes = lister_themes_prives();
1439
	foreach ($themes as $theme) {
1440
		if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
1441
			return $themefiles["$subdir$file"] = $f;
1442
		}
1443
	}
1444
	spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');
1445
1446
	return $themefiles["$subdir$file"] = "";
1447
}
1448
1449
1450
/**
1451
 * Cherche une image dans les dossiers d'images
1452
 *
1453
 * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
1454
 * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
1455
 * de facon temporaire le temps de la migration, et cherche de nouveau.
1456
 *
1457
 * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
1458
 * dans le répertoire défini par la constante `_NOM_IMG_PACK`
1459
 *
1460
 * @see find_in_theme()
1461
 * @see inc_icone_renommer_dist()
1462
 *
1463
 * @param string $icone
1464
 *     Nom de l'icone cherchée
1465
 * @return string
1466
 *     Chemin complet de l'icone depuis la racine si l'icone est trouée,
1467
 *     sinon chaîne vide.
1468
 **/
1469
function chemin_image($icone) {
1470
	static $icone_renommer;
1471
	if ($p = strpos($icone, '?')) {
1472
		$icone = substr($icone,0, $p);
1473
	}
1474
	// gerer le cas d'un double appel en evitant de refaire le travail inutilement
1475
	if (strpos($icone, "/") !== false and file_exists($icone)) {
1476
		return $icone;
1477
	}
1478
1479
	// si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
1480
	if (preg_match(',[.](png|gif|jpg|webp|svg)$,', $icone) and $f = find_in_theme("images/$icone")) {
1481
		return $f;
1482
	}
1483
	// sinon passer par le module de renommage
1484
	if (is_null($icone_renommer)) {
1485
		$icone_renommer = charger_fonction('icone_renommer', 'inc', true);
1486
	}
1487
	if ($icone_renommer) {
1488
		list($icone, $fonction) = $icone_renommer($icone, "");
0 ignored issues
show
Unused Code introduced by
The assignment to $fonction is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1489
		if (file_exists($icone)) {
1490
			return $icone;
1491
		}
1492
	}
1493
1494
	return find_in_path($icone, _NOM_IMG_PACK);
1495
}
1496
1497
//
1498
// chercher un fichier $file dans le SPIP_PATH
1499
// si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
1500
// si 3e arg vrai, on inclut si ce n'est fait.
1501
$GLOBALS['path_sig'] = '';
1502
$GLOBALS['path_files'] = null;
1503
1504
/**
1505
 * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
1506
 *
1507
 * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
1508
 * suivant l'ordre des chemins connus de SPIP.
1509
 *
1510
 * @api
1511
 * @see  charger_fonction()
1512
 * @uses creer_chemin() Pour la liste des chemins.
1513
 * @example
1514
 *     ```
1515
 *     $f = find_in_path('css/perso.css');
1516
 *     $f = find_in_path('perso.css', 'css');
1517
 *     ```
1518
 *
1519
 * @param string $file
1520
 *     Fichier recherché
1521
 * @param string $dirname
1522
 *     Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
1523
 * @param bool|string $include
1524
 *     - false : ne fait rien de plus
1525
 *     - true : inclut le fichier (include_once)
1526
 *     - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
1527
 * @return string|bool
1528
 *     - string : chemin du fichier trouvé
1529
 *     - false : fichier introuvable
1530
 **/
1531
function find_in_path($file, $dirname = '', $include = false) {
1532
	static $dirs = array();
1533
	static $inc = array(); # cf https://git.spip.net/spip/spip/commit/42e4e028e38c839121efaee84308d08aee307eec
1534
	static $c = '';
1535
1536
	if (!$file and !strlen($file)) {
1537
		return false;
1538
	}
1539
1540
	// on calcule le chemin si le dossier skel a change
1541
	if ($c != $GLOBALS['dossier_squelettes']) {
1542
		// assurer le non plantage lors de la montee de version :
1543
		$c = $GLOBALS['dossier_squelettes'];
1544
		creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
1545
	}
1546
1547
	if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
1548
		if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
1549
			return false;
1550
		}
1551
		if ($include and !isset($inc[$dirname][$file])) {
1552
			include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1553
			$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1554
		}
1555
1556
		return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1557
	}
1558
1559
	$a = strrpos($file, '/');
1560
	if ($a !== false) {
1561
		$dirname .= substr($file, 0, ++$a);
1562
		$file = substr($file, $a);
1563
	}
1564
1565
	foreach (creer_chemin() as $dir) {
0 ignored issues
show
Bug introduced by
The expression creer_chemin() of type array|null 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...
1566 View Code Duplication
		if (!isset($dirs[$a = $dir . $dirname])) {
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...
1567
			$dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a);
1568
		}
1569
		if ($dirs[$a]) {
1570
			if (file_exists(_ROOT_CWD . ($a .= $file))) {
1571
				if ($include and !isset($inc[$dirname][$file])) {
1572
					include_once _ROOT_CWD . $a;
1573
					$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1574
				}
1575 View Code Duplication
				if (!defined('_SAUVER_CHEMIN')) {
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...
1576
					// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1577
					if (is_null($GLOBALS['path_files'])) {
1578
						return $a;
1579
					}
1580
					define('_SAUVER_CHEMIN', true);
1581
				}
1582
1583
				return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
1584
			}
1585
		}
1586
	}
1587
1588
	if ($include) {
1589
		spip_log("include_spip $dirname$file non trouve");
1590
		if ($include === 'required') {
1591
			echo '<pre>',
1592
			"<strong>Erreur Fatale</strong><br />";
1593
			if (function_exists('debug_print_backtrace')) {
1594
				echo debug_print_backtrace();
1595
			}
1596
			echo '</pre>';
1597
			die("Erreur interne: ne peut inclure $dirname$file");
1598
		}
1599
	}
1600
1601 View Code Duplication
	if (!defined('_SAUVER_CHEMIN')) {
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...
1602
		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1603
		if (is_null($GLOBALS['path_files'])) {
1604
			return false;
1605
		}
1606
		define('_SAUVER_CHEMIN', true);
1607
	}
1608
1609
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1610
}
1611
1612
function clear_path_cache() {
1613
	$GLOBALS['path_files'] = array();
1614
	spip_unlink(_CACHE_CHEMIN);
1615
}
1616
1617
function load_path_cache() {
1618
	// charger le path des plugins
1619
	if (@is_readable(_CACHE_PLUGINS_PATH)) {
1620
		include_once(_CACHE_PLUGINS_PATH);
1621
	}
1622
	$GLOBALS['path_files'] = array();
1623
	// si le visiteur est admin,
1624
	// on ne recharge pas le cache pour forcer sa mise a jour
1625
	if (
1626
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1627
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1628
		// utiliser le cookie est un pis aller qui marche 'en general'
1629
		// on blinde par un second test au moment de la lecture de la session
1630
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1631
		// et en ignorant ce cache en cas de recalcul explicite
1632
	!_request('var_mode')
1633
	) {
1634
		// on essaye de lire directement sans verrou pour aller plus vite
1635
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
1636
			// mais si semble corrompu on relit avec un verrou
1637
			if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1638
				lire_fichier(_CACHE_CHEMIN, $contenu);
1639
				if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1640
					$GLOBALS['path_files'] = array();
1641
				}
1642
			}
1643
		}
1644
	}
1645
}
1646
1647
function save_path_cache() {
1648
	if (defined('_SAUVER_CHEMIN')
1649
		and _SAUVER_CHEMIN
1650
	) {
1651
		ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
1652
	}
1653
}
1654
1655
1656
/**
1657
 * Trouve tous les fichiers du path correspondants à un pattern
1658
 *
1659
 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1660
 * par un `find_in_path()`
1661
 *
1662
 * @api
1663
 * @uses creer_chemin()
1664
 * @uses preg_files()
1665
 *
1666
 * @param string $dir
1667
 * @param string $pattern
1668
 * @param bool $recurs
1669
 * @return array
1670
 */
1671
function find_all_in_path($dir, $pattern, $recurs = false) {
1672
	$liste_fichiers = array();
1673
	$maxfiles = 10000;
1674
1675
	// cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1676
	// on a pas encore inclus flock.php
1677
	if (!function_exists('preg_files')) {
1678
		include_once _ROOT_RESTREINT . 'inc/flock.php';
1679
	}
1680
1681
	// Parcourir le chemin
1682
	foreach (creer_chemin() as $d) {
0 ignored issues
show
Bug introduced by
The expression creer_chemin() of type array|null 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...
1683
		$f = $d . $dir;
1684
		if (@is_dir($f)) {
1685
			$liste = preg_files($f, $pattern, $maxfiles - count($liste_fichiers), $recurs === true ? array() : $recurs);
0 ignored issues
show
Bug introduced by
It seems like $recurs === true ? array() : $recurs can also be of type boolean; however, preg_files() 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...
1686
			foreach ($liste as $chemin) {
1687
				$nom = basename($chemin);
1688
				// ne prendre que les fichiers pas deja trouves
1689
				// car find_in_path prend le premier qu'il trouve,
1690
				// les autres sont donc masques
1691
				if (!isset($liste_fichiers[$nom])) {
1692
					$liste_fichiers[$nom] = $chemin;
1693
				}
1694
			}
1695
		}
1696
	}
1697
1698
	return $liste_fichiers;
1699
}
1700
1701
/**
1702
 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1703
 * et beneficient d'une exception
1704
 *
1705
 * @param string $nom
1706
 * @param bool $strict
1707
 * @return bool
1708
 */
1709
function autoriser_sans_cookie($nom, $strict = false) {
1710
	static $autsanscookie = array('install', 'base_repair');
1711
1712
	if (in_array($nom, $autsanscookie)) {
1713
		if (test_espace_prive()){
1714
			include_spip('base/connect_sql');
1715
			if (!$strict or !spip_connect()){
1716
				return true;
1717
			}
1718
		}
1719
	}
1720
	return false;
1721
}
1722
1723
/**
1724
 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1725
 *
1726
 * @api
1727
 * @param string $id
1728
 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
1729
 * @param string $entite
1730
 *   surnom de la table SQL (donne acces au nom de cle primaire)
1731
 * @param string $args
1732
 *   query_string a placer apres cle=$id&....
1733
 * @param string $ancre
1734
 *   ancre a mettre a la fin de l'URL a produire
1735
 * @param bool|string $public
0 ignored issues
show
Documentation introduced by
Should the type for parameter $public not be boolean|string|null?

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.

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

Loading history...
1736
 *   produire l'URL publique ou privee (par defaut: selon espace)
1737
 *   si string : serveur de base de donnee (nom du connect)
1738
 * @param string $type
0 ignored issues
show
Documentation introduced by
Should the type for parameter $type not be string|null?

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.

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

Loading history...
1739
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
1740
 * @return string|array
1741
 *   url codee ou fonction de decodage
1742
 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1743
 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1744
 */
1745
function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1746
	if ($public === null) {
1747
		$public = !test_espace_prive();
1748
	}
1749
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1750
1751
	if (!$public) {
1752
		if (!$entite) {
1753
			return '';
1754
		}
1755
		if (!function_exists('generer_url_ecrire_objet')) {
1756
			include_spip('inc/urls');
1757
		}
1758
		$res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1759
	} else {
1760
		if ($type === null) {
1761
			$type = (isset($GLOBALS['type_urls']))
1762
				? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1763
				: ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1764
					? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1765
		}
1766
1767
		$f = charger_fonction($type, 'urls', true);
1768
		// se rabattre sur les urls page si les urls perso non dispo
1769
		if (!$f) {
1770
			$f = charger_fonction('page', 'urls', true);
1771
		}
1772
1773
		// si $entite='', on veut la fonction de passage URL ==> id
1774
		// sinon on veut effectuer le passage id ==> URL
1775
		if (!$entite) {
1776
			return $f;
1777
		}
1778
1779
		// mais d'abord il faut tester le cas des urls sur une
1780
		// base distante
1781
		if (is_string($public)
1782
			and $g = charger_fonction('connect', 'urls', true)
1783
		) {
1784
			$f = $g;
1785
		}
1786
1787
		$res = $f(intval($id), $entite, $args, $ancre, $public);
1788
1789
	}
1790
	if ($res) {
1791
		return $res;
1792
	}
1793
	// Sinon c'est un raccourci ou compat SPIP < 2
1794
	if (!function_exists($f = 'generer_url_' . $entite)) {
1795
		if (!function_exists($f .= '_dist')) {
1796
			$f = '';
1797
		}
1798
	}
1799
	if ($f) {
1800
		$url = $f($id, $args, $ancre);
1801
		if (strlen($args)) {
1802
			$url .= strstr($url, '?')
1803
				? '&amp;' . $args
1804
				: '?' . $args;
1805
		}
1806
1807
		return $url;
1808
	}
1809
	// On a ete gentil mais la ....
1810
	spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1811
1812
	return '';
1813
}
1814
1815
function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1816
	$exec = objet_info($entite, 'url_edit');
1817
	$url = generer_url_ecrire($exec, $args);
0 ignored issues
show
Bug introduced by
It seems like $exec defined by objet_info($entite, 'url_edit') on line 1816 can also be of type array; however, generer_url_ecrire() does only seem to accept string, 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...
1818
	if (intval($id)) {
1819
		$url = parametre_url($url, id_table_objet($entite), $id);
1820
	} else {
1821
		$url = parametre_url($url, 'new', 'oui');
1822
	}
1823
	if ($ancre) {
1824
		$url = ancre_url($url, $ancre);
0 ignored issues
show
Bug introduced by
It seems like $url can also be of type array<integer,string> or null; however, ancre_url() does only seem to accept string, 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...
1825
	}
1826
1827
	return $url;
1828
}
1829
1830
// https://code.spip.net/@urls_connect_dist
1831
function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1832
	include_spip('base/connect_sql');
1833
	$id_type = id_table_objet($entite, $public);
1834
1835
	return _DIR_RACINE . get_spip_script('./')
1836
	. "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1837
	. (!$args ? '' : "&$args")
1838
	. (!$ancre ? '' : "#$ancre");
1839
}
1840
1841
1842
/**
1843
 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1844
 *
1845
 * @param string $url
1846
 * @return string
1847
 */
1848
function urlencode_1738($url) {
1849
	if (preg_match(',[^\x00-\x7E],sS', $url)) {
1850
		$uri = '';
1851
		for ($i = 0; $i < strlen($url); $i++) {
1852
			if (ord($a = $url[$i]) > 127) {
1853
				$a = rawurlencode($a);
1854
			}
1855
			$uri .= $a;
1856
		}
1857
		$url = $uri;
1858
	}
1859
1860
	return quote_amp($url);
1861
}
1862
1863
// https://code.spip.net/@generer_url_entite_absolue
1864
function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1865
	if (!$connect) {
1866
		$connect = true;
1867
	}
1868
	$h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1869
	if (!preg_match(',^\w+:,', $h)) {
1870
		include_spip('inc/filtres_mini');
1871
		$h = url_absolue($h);
0 ignored issues
show
Bug introduced by
It seems like $h can also be of type array; however, url_absolue() does only seem to accept string, 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...
1872
	}
1873
1874
	return $h;
1875
}
1876
1877
1878
/**
1879
 * Tester qu'une variable d'environnement est active
1880
 *
1881
 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1882
 * variables d'environnement comme `$_SERVER['HTTPS']` ou `ini_get('display_errors')`
1883
 *
1884
 * @param string|bool $truc
1885
 *     La valeur de la variable d'environnement
1886
 * @return bool
1887
 *     true si la valeur est considérée active ; false sinon.
1888
 **/
1889
function test_valeur_serveur($truc) {
1890
	if (!$truc) {
1891
		return false;
1892
	}
1893
1894
	return (strtolower($truc) !== 'off');
1895
}
1896
1897
//
1898
// Fonctions de fabrication des URL des scripts de Spip
1899
//
1900
/**
1901
 * Calcule l'url de base du site
1902
 *
1903
 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1904
 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1905
 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1906
 *
1907
 * @note
1908
 *     La globale `$profondeur_url` doit être initialisée de manière à
1909
 *     indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1910
 *     racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1911
 *     la racine 0. Sur url/perso/ elle vaut 2
1912
 *
1913
 * @param int|boo|array $profondeur
0 ignored issues
show
Documentation introduced by
Should the type for parameter $profondeur not be integer|boo|array|null? 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...
1914
 *    - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1915
 *    - si int : indique que l'on veut l'url pour la profondeur indiquée
1916
 *    - si bool : retourne le tableau static complet
1917
 *    - si array : réinitialise le tableau static complet avec la valeur fournie
1918
 * @return string|array
1919
 */
1920
function url_de_base($profondeur = null) {
1921
1922
	static $url = array();
1923
	if (is_array($profondeur)) {
1924
		return $url = $profondeur;
1925
	}
1926
	if ($profondeur === false) {
1927
		return $url;
1928
	}
1929
1930
	if (is_null($profondeur)) {
1931
		$profondeur = $GLOBALS['profondeur_url'];
1932
	}
1933
1934
	if (isset($url[$profondeur])) {
1935
		return $url[$profondeur];
1936
	}
1937
1938
	$http = 'http';
1939
1940
	if (
1941
		isset($_SERVER["SCRIPT_URI"])
1942
		and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1943
	) {
1944
		$http = 'https';
1945
	} elseif (
1946
		isset($_SERVER['HTTPS'])
1947
		and test_valeur_serveur($_SERVER['HTTPS'])
1948
	) {
1949
		$http = 'https';
1950
	}
1951
1952
	// note : HTTP_HOST contient le :port si necessaire
1953
	$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
1954
	// si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1955
	if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1956
		$host = $GLOBALS['meta']['adresse_site'];
1957
		if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1958
			$http = $scheme;
1959
			$host = str_replace("{$scheme}://", '', $host);
1960
		}
1961
	}
1962
	if (isset($_SERVER['SERVER_PORT'])
1963
		and $port = $_SERVER['SERVER_PORT']
1964
		and strpos($host, ":") == false
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($host, ':') of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
1965
	) {
1966
		if (!defined('_PORT_HTTP_STANDARD')) {
1967
			define('_PORT_HTTP_STANDARD', '80');
1968
		}
1969
		if (!defined('_PORT_HTTPS_STANDARD')) {
1970
			define('_PORT_HTTPS_STANDARD', '443');
1971
		}
1972
		if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
1973
			$host .= ":$port";
1974
		}
1975
		if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
1976
			$host .= ":$port";
1977
		}
1978
	}
1979
1980 View Code Duplication
	if (!$GLOBALS['REQUEST_URI']) {
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...
1981
		if (isset($_SERVER['REQUEST_URI'])) {
1982
			$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1983
		} else {
1984
			$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
1985
			if (!empty($_SERVER['QUERY_STRING'])
1986
				and !strpos($_SERVER['REQUEST_URI'], '?')
1987
			) {
1988
				$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1989
			}
1990
		}
1991
	}
1992
1993
	$url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1994
1995
	return $url[$profondeur];
1996
}
1997
1998
/**
1999
 * fonction testable de construction d'une url appelee par url_de_base()
2000
 *
2001
 * @param string $http
2002
 * @param string $host
2003
 * @param string $request
2004
 * @param int $prof
2005
 * @return string
2006
 */
2007
function url_de_($http, $host, $request, $prof = 0) {
2008
	$prof = max($prof, 0);
2009
2010
	$myself = ltrim($request, '/');
2011
	# supprimer la chaine de GET
2012
	list($myself) = explode('?', $myself);
2013
	// vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
2014
	// protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
2015 View Code Duplication
	if (strpos($myself,'://') !== false) {
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...
2016
		$myself = explode('://',$myself);
2017
		array_shift($myself);
2018
		$myself = implode('://',$myself);
2019
		$myself = explode('/',$myself);
2020
		array_shift($myself);
2021
		$myself = implode('/',$myself);
2022
	}
2023
	$url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
2024
2025
	$url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
2026
2027
	return $url;
2028
}
2029
2030
2031
// Pour une redirection, la liste des arguments doit etre separee par "&"
2032
// Pour du code XHTML, ca doit etre &amp;
2033
// Bravo au W3C qui n'a pas ete capable de nous eviter ca
2034
// faute de separer proprement langage et meta-langage
2035
2036
// Attention, X?y=z et "X/?y=z" sont completement differents!
2037
// http://httpd.apache.org/docs/2.0/mod/mod_dir.html
2038
2039
/**
2040
 * Crée une URL vers un script de l'espace privé
2041
 *
2042
 * @example
2043
 *     ```
2044
 *     generer_url_ecrire('admin_plugin')
2045
 *     ```
2046
 *
2047
 * @param string $script
2048
 *     Nom de la page privée (xx dans exec=xx)
2049
 * @param string $args
2050
 *     Arguments à transmettre, tel que `arg1=yy&arg2=zz`
2051
 * @param bool $no_entities
2052
 *     Si false : transforme les `&` en `&amp;`
2053
 * @param bool|string $rel
2054
 *     URL relative ?
2055
 *
2056
 *     - false : l’URL sera complète et contiendra l’URL du site
2057
 *     - true : l’URL sera relavive.
2058
 *     - string : on transmet l'url à la fonction
2059
 * @return string URL
2060
 **/
2061
function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
2062
	if (!$rel) {
2063
		$rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
2064
	} else {
2065
		if (!is_string($rel)) {
2066
			$rel = _DIR_RESTREINT ? _DIR_RESTREINT :
2067
				('./' . _SPIP_ECRIRE_SCRIPT);
2068
		}
2069
	}
2070
2071
	list($script, $ancre) = array_pad(explode('#', $script), 2, null);
2072
	if ($script and ($script <> 'accueil' or $rel)) {
2073
		$args = "?exec=$script" . (!$args ? '' : "&$args");
2074
	} elseif ($args) {
2075
		$args = "?$args";
2076
	}
2077
	if ($ancre) {
2078
		$args .= "#$ancre";
2079
	}
2080
2081
	return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
2082
}
2083
2084
//
2085
// Adresse des scripts publics (a passer dans inc-urls...)
2086
//
2087
2088
2089
/**
2090
 * Retourne le nom du fichier d'exécution de SPIP
2091
 *
2092
 * @see _SPIP_SCRIPT
2093
 * @note
2094
 *   Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2095
 *   dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2096
 *
2097
 * @param string $default
2098
 *     Script par défaut
2099
 * @return string
2100
 *     Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2101
 **/
2102
function get_spip_script($default = '') {
2103
	# cas define('_SPIP_SCRIPT', '');
2104
	if (_SPIP_SCRIPT) {
2105
		return _SPIP_SCRIPT;
2106
	} else {
2107
		return $default;
2108
	}
2109
}
2110
2111
/**
2112
 * Crée une URL vers une page publique de SPIP
2113
 *
2114
 * @example
2115
 *     ```
2116
 *     generer_url_public("rubrique","id_rubrique=$id_rubrique")
2117
 *     ```
2118
 *
2119
 * @param string $script
2120
 *     Nom de la page
2121
 * @param string|array $args
2122
 *     Arguments à transmettre a l'URL,
2123
 *      soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2124
 *      soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2125
 * @param bool $no_entities
2126
 *     Si false : transforme les `&` en `&amp;`
2127
 * @param bool $rel
2128
 *     URL relative ?
2129
 *
2130
 *     - false : l’URL sera complète et contiendra l’URL du site
2131
 *     - true : l’URL sera relavive.
2132
 * @param string $action
2133
 *     - Fichier d'exécution public (spip.php par défaut)
2134
 * @return string URL
2135
 **/
2136
function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2137
	// si le script est une action (spip_pass, spip_inscription),
2138
	// standardiser vers la nouvelle API
2139
2140
	if (!$action) {
2141
		$action = get_spip_script();
2142
	}
2143
	if ($script) {
2144
		$action = parametre_url($action, _SPIP_PAGE, $script, '&');
2145
	}
2146
2147
	if ($args) {
2148
		if (is_array($args)) {
2149
			$r = '';
2150
			foreach ($args as $k => $v) {
2151
				$r .= '&' . $k . '=' . $v;
2152
			}
2153
			$args = substr($r, 1);
2154
		}
2155
		$action .=
2156
			(strpos($action, '?') !== false ? '&' : '?') . $args;
2157
	}
2158
	if (!$no_entities) {
2159
		$action = quote_amp($action);
0 ignored issues
show
Bug introduced by
It seems like $action can also be of type array<integer,string> or null; however, quote_amp() does only seem to accept string, 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...
2160
	}
2161
2162
	// ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2163
	return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2164
}
2165
2166
// https://code.spip.net/@generer_url_prive
2167
function generer_url_prive($script, $args = "", $no_entities = false) {
2168
2169
	return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2170
}
2171
2172
// Pour les formulaires en methode POST,
2173
// mettre le nom du script a la fois en input-hidden et dans le champ action:
2174
// 1) on peut ainsi memoriser le signet comme si c'etait un GET
2175
// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2176
2177
/**
2178
 * Retourne un formulaire (POST par défaut) vers un script exec
2179
 * de l’interface privée
2180
 *
2181
 * @param string $script
2182
 *     Nom de la page exec
2183
 * @param string $corps
2184
 *     Contenu du formulaire
2185
 * @param string $atts
2186
 *     Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2187
 * @param string $submit
2188
 *     Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2189
 * @return string
2190
 *     Code HTML du formulaire
2191
 **/
2192
function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2193
2194
	$script1 = explode('&', $script);
2195
	$script1 = reset($script1);
2196
2197
	return "<form action='"
2198
	. ($script ? generer_url_ecrire($script) : '')
2199
	. "' "
2200
	. ($atts ? $atts : " method='post'")
2201
	. "><div>\n"
2202
	. "<input type='hidden' name='exec' value='$script1' />"
2203
	. $corps
2204
	. (!$submit ? '' :
2205
		("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2206
	. "</div></form>\n";
2207
}
2208
2209
/**
2210
 * Générer un formulaire pour lancer une action vers $script
2211
 *
2212
 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2213
 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2214
 * qui ne sont pas destines a etre mis en signets
2215
 *
2216
 * @param string $script
2217
 * @param string $corps
2218
 * @param string $atts
2219
 * @param bool $public
2220
 * @return string
2221
 */
2222
function generer_form_action($script, $corps, $atts = '', $public = false) {
2223
	// si l'on est dans l'espace prive, on garde dans l'url
2224
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2225
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2226
	$h = (_DIR_RACINE and !$public)
2227
		? generer_url_ecrire(_request('exec'))
2228
		: generer_url_public();
2229
2230
	return "\n<form action='" .
2231
	$h .
2232
	"'" .
2233
	$atts .
2234
	">\n" .
2235
	"<div>" .
2236
	"\n<input type='hidden' name='action' value='$script' />" .
2237
	$corps .
2238
	"</div></form>";
2239
}
2240
2241
/**
2242
 * Créer une URL
2243
 *
2244
 * @param  string $script
2245
 *     Nom du script à exécuter
2246
 * @param  string $args
2247
 *     Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2248
 * @param bool $no_entities
2249
 *     Si false : transforme les & en &amp;
2250
 * @param boolean $public
2251
 *     URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2252
 *     true : l’URL sera relative.
2253
 * @return string
2254
 *     URL
2255
 */
2256
function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2257
	// si l'on est dans l'espace prive, on garde dans l'url
2258
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2259
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2260
	$url = (_DIR_RACINE and !$public)
2261
		? generer_url_ecrire(_request('exec'))
2262
		: generer_url_public('', '', false, false);
2263
	$url = parametre_url($url, 'action', $script);
2264
	if ($args) {
2265
		$url .= quote_amp('&' . $args);
2266
	}
2267
2268
	if ($no_entities) {
2269
		$url = str_replace('&amp;', '&', $url);
2270
	}
2271
2272
	return $url;
2273
}
2274
2275
2276
/**
2277
 * Fonction d'initialisation groupée pour compatibilité ascendante
2278
 *
2279
 * @param string $pi Répertoire permanent inaccessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pi not be string|null?

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.

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

Loading history...
2280
 * @param string $pa Répertoire permanent accessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pa not be string|null?

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.

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

Loading history...
2281
 * @param string $ti Répertoire temporaire inaccessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $ti not be string|null?

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.

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

Loading history...
2282
 * @param string $ta Répertoire temporaire accessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $ta not be string|null?

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.

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

Loading history...
2283
 */
2284
function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2285
	spip_initialisation_core($pi, $pa, $ti, $ta);
2286
	spip_initialisation_suite();
2287
}
2288
2289
/**
2290
 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2291
 *
2292
 * Elle définit les répertoires et fichiers non partageables
2293
 * et indique dans $test_dirs ceux devant être accessibles en écriture
2294
 * mais ne touche pas à cette variable si elle est déjà définie
2295
 * afin que mes_options.php puisse en spécifier d'autres.
2296
 *
2297
 * Elle définit ensuite les noms des fichiers et les droits.
2298
 * Puis simule un register_global=on sécurisé.
2299
 *
2300
 * @param string $pi Répertoire permanent inaccessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pi not be string|null?

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.

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

Loading history...
2301
 * @param string $pa Répertoire permanent accessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pa not be string|null?

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.

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

Loading history...
2302
 * @param string $ti Répertoire temporaire inaccessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $ti not be string|null?

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.

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

Loading history...
2303
 * @param string $ta Répertoire temporaire accessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $ta not be string|null?

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.

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

Loading history...
2304
 */
2305
function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2306
	static $too_late = 0;
2307
	if ($too_late++) {
2308
		return;
2309
	}
2310
2311
	// Declaration des repertoires
2312
2313
	// le nom du repertoire plugins/ activables/desactivables
2314
	if (!defined('_DIR_PLUGINS')) {
2315
		define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2316
	}
2317
2318
	// le nom du repertoire des extensions/ permanentes du core, toujours actives
2319
	if (!defined('_DIR_PLUGINS_DIST')) {
2320
		define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2321
	}
2322
2323
	// le nom du repertoire des librairies
2324
	if (!defined('_DIR_LIB')) {
2325
		define('_DIR_LIB', _DIR_RACINE . "lib/");
2326
	}
2327
2328
	if (!defined('_DIR_IMG')) {
2329
		define('_DIR_IMG', $pa);
2330
	}
2331
	if (!defined('_DIR_LOGOS')) {
2332
		define('_DIR_LOGOS', $pa);
2333
	}
2334
	if (!defined('_DIR_IMG_ICONES')) {
2335
		define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2336
	}
2337
2338
	if (!defined('_DIR_DUMP')) {
2339
		define('_DIR_DUMP', $ti . "dump/");
2340
	}
2341
	if (!defined('_DIR_SESSIONS')) {
2342
		define('_DIR_SESSIONS', $ti . "sessions/");
2343
	}
2344
	if (!defined('_DIR_TRANSFERT')) {
2345
		define('_DIR_TRANSFERT', $ti . "upload/");
2346
	}
2347
	if (!defined('_DIR_CACHE')) {
2348
		define('_DIR_CACHE', $ti . "cache/");
2349
	}
2350
	if (!defined('_DIR_CACHE_XML')) {
2351
		define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2352
	}
2353
	if (!defined('_DIR_SKELS')) {
2354
		define('_DIR_SKELS', _DIR_CACHE . "skel/");
2355
	}
2356
	if (!defined('_DIR_AIDE')) {
2357
		define('_DIR_AIDE', _DIR_CACHE . "aide/");
2358
	}
2359
	if (!defined('_DIR_TMP')) {
2360
		define('_DIR_TMP', $ti);
2361
	}
2362
2363
	if (!defined('_DIR_VAR')) {
2364
		define('_DIR_VAR', $ta);
2365
	}
2366
2367
	if (!defined('_DIR_ETC')) {
2368
		define('_DIR_ETC', $pi);
2369
	}
2370
	if (!defined('_DIR_CONNECT')) {
2371
		define('_DIR_CONNECT', $pi);
2372
	}
2373
	if (!defined('_DIR_CHMOD')) {
2374
		define('_DIR_CHMOD', $pi);
2375
	}
2376
2377
	if (!isset($GLOBALS['test_dirs']))
2378
		// Pas $pi car il est bon de le mettre hors ecriture apres intstall
2379
		// il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2380
	{
2381
		$GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2382
	}
2383
2384
	// Declaration des fichiers
2385
2386
	if (!defined('_CACHE_PLUGINS_PATH')) {
2387
		define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2388
	}
2389
	if (!defined('_CACHE_PLUGINS_OPT')) {
2390
		define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2391
	}
2392
	if (!defined('_CACHE_PLUGINS_FCT')) {
2393
		define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2394
	}
2395
	if (!defined('_CACHE_PIPELINES')) {
2396
		define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2397
	}
2398
	if (!defined('_CACHE_CHEMIN')) {
2399
		define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2400
	}
2401
2402
	# attention .php obligatoire pour ecrire_fichier_securise
2403
	if (!defined('_FILE_META')) {
2404
		define('_FILE_META', $ti . 'meta_cache.php');
2405
	}
2406
	if (!defined('_DIR_LOG')) {
2407
		define('_DIR_LOG', _DIR_TMP . 'log/');
2408
	}
2409
	if (!defined('_FILE_LOG')) {
2410
		define('_FILE_LOG', 'spip');
2411
	}
2412
	if (!defined('_FILE_LOG_SUFFIX')) {
2413
		define('_FILE_LOG_SUFFIX', '.log');
2414
	}
2415
2416
	// Le fichier de connexion a la base de donnees
2417
	// tient compte des anciennes versions (inc_connect...)
2418
	if (!defined('_FILE_CONNECT_INS')) {
2419
		define('_FILE_CONNECT_INS', 'connect');
2420
	}
2421
	if (!defined('_FILE_CONNECT')) {
2422
		define('_FILE_CONNECT',
2423
		(@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2424
			: (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2425
				: false)));
2426
	}
2427
2428
	// Le fichier de reglages des droits
2429
	if (!defined('_FILE_CHMOD_INS')) {
2430
		define('_FILE_CHMOD_INS', 'chmod');
2431
	}
2432
	if (!defined('_FILE_CHMOD')) {
2433
		define('_FILE_CHMOD',
2434
		(@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2435
			: false));
2436
	}
2437
2438
	if (!defined('_FILE_LDAP')) {
2439
		define('_FILE_LDAP', 'ldap.php');
2440
	}
2441
2442
	if (!defined('_FILE_TMP_SUFFIX')) {
2443
		define('_FILE_TMP_SUFFIX', '.tmp.php');
2444
	}
2445
	if (!defined('_FILE_CONNECT_TMP')) {
2446
		define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2447
	}
2448
	if (!defined('_FILE_CHMOD_TMP')) {
2449
		define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2450
	}
2451
2452
	// Definition des droits d'acces en ecriture
2453
	if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2454
		include_once _FILE_CHMOD;
2455
	}
2456
2457
	// Se mefier des fichiers mal remplis!
2458
	if (!defined('_SPIP_CHMOD')) {
2459
		define('_SPIP_CHMOD', 0777);
2460
	}
2461
2462
	if (!defined('_DEFAULT_CHARSET')) {
2463
		/** Le charset par défaut lors de l'installation */
2464
		define('_DEFAULT_CHARSET', 'utf-8');
2465
	}
2466
	if (!defined('_ROOT_PLUGINS')) {
2467
		define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2468
	}
2469
	if (!defined('_ROOT_PLUGINS_DIST')) {
2470
		define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2471
	}
2472
	if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2473
		define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2474
	}
2475
2476
	// La taille des Log
2477
	if (!defined('_MAX_LOG')) {
2478
		define('_MAX_LOG', 100);
2479
	}
2480
2481
	// Sommes-nous dans l'empire du Mal ?
2482
	// (ou sous le signe du Pingouin, ascendant GNU ?)
2483
	if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2484
		if (!defined('_OS_SERVEUR')) {
2485
			define('_OS_SERVEUR', 'windows');
2486
		}
2487
		if (!defined('_SPIP_LOCK_MODE')) {
2488
			define('_SPIP_LOCK_MODE', 1);
2489
		} // utiliser le flock php
2490
	} else {
2491
		if (!defined('_OS_SERVEUR')) {
2492
			define('_OS_SERVEUR', '');
2493
		}
2494
		if (!defined('_SPIP_LOCK_MODE')) {
2495
			define('_SPIP_LOCK_MODE', 1);
2496
		} // utiliser le flock php
2497
		#if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2498
	}
2499
2500
	// Langue par defaut
2501
	if (!defined('_LANGUE_PAR_DEFAUT')) {
2502
		define('_LANGUE_PAR_DEFAUT', 'fr');
2503
	}
2504
2505
	//
2506
	// Module de lecture/ecriture/suppression de fichiers utilisant flock()
2507
	// (non surchargeable en l'etat ; attention si on utilise include_spip()
2508
	// pour le rendre surchargeable, on va provoquer un reecriture
2509
	// systematique du noyau ou une baisse de perfs => a etudier)
2510
	include_once _ROOT_RESTREINT . 'inc/flock.php';
2511
2512
	// charger tout de suite le path et son cache
2513
	load_path_cache();
2514
2515
	// *********** traiter les variables ************
2516
2517
	//
2518
	// Securite
2519
	//
2520
2521
	// Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2522
	if (isset($_REQUEST['GLOBALS'])) {
2523
		die();
2524
	}
2525
	// nettoyer les magic quotes \' et les caracteres nuls %00
2526
	spip_desinfecte($_GET);
2527
	spip_desinfecte($_POST);
2528
	spip_desinfecte($_COOKIE);
2529
	spip_desinfecte($_REQUEST);
2530
2531
	// appliquer le cookie_prefix
2532
	if ($GLOBALS['cookie_prefix'] != 'spip') {
2533
		include_spip('inc/cookie');
2534
		recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2535
	}
2536
2537
	//
2538
	// Capacites php (en fonction de la version)
2539
	//
2540
	$GLOBALS['flag_ob'] = (function_exists("ob_start")
2541
		&& function_exists("ini_get")
2542
		&& !strstr(@ini_get('disable_functions'), 'ob_'));
2543
	$GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2544
	$GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
2545
	$GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2546
		(get_cfg_var('upload_max_filesize') > 0));
2547
2548
2549
	// Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2550 View Code Duplication
	if (isset($_SERVER['REQUEST_URI'])) {
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...
2551
		$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2552
	} else {
2553
		$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
2554
		if (!empty($_SERVER['QUERY_STRING'])
2555
			and !strpos($_SERVER['REQUEST_URI'], '?')
2556
		) {
2557
			$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2558
		}
2559
	}
2560
2561
	// Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2562
	if (!defined('_RENOUVELLE_ALEA')) {
2563
		define('_RENOUVELLE_ALEA', 12 * 3600);
2564
	}
2565
	if (!defined('_DUREE_COOKIE_ADMIN')) {
2566
		define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2567
	}
2568
2569
	// charger les meta si possible et renouveller l'alea au besoin
2570
	// charge aussi effacer_meta et ecrire_meta
2571
	$inc_meta = charger_fonction('meta', 'inc');
2572
	$inc_meta();
2573
2574
	// nombre de repertoires depuis la racine
2575
	// on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2576
	// ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2577
	// le calcul est faux)
2578
	if (!_DIR_RESTREINT) {
2579
		$GLOBALS['profondeur_url'] = 1;
2580
	} else {
2581
		$uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
2582
		$uri_ref = $_SERVER["SCRIPT_NAME"];
2583
		if (!$uri_ref
2584
			// si on est appele avec un autre ti, on est sans doute en mutu
2585
			// si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2586
			// a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2587
			// s'en remettre a l'adresse du site. alea jacta est.
2588
			or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2589
		) {
2590
2591
			if (isset($GLOBALS['meta']['adresse_site'])) {
2592
				$uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2593
				$uri_ref = (isset($uri_ref['path']) ? $uri_ref['path'] : '') . '/';
2594
			} else {
2595
				$uri_ref = "";
2596
			}
2597
		}
2598
		if (!$uri or !$uri_ref) {
2599
			$GLOBALS['profondeur_url'] = 0;
2600
		} else {
2601
			$GLOBALS['profondeur_url'] = max(0,
2602
				substr_count($uri[0], '/')
2603
				- substr_count($uri_ref, '/'));
2604
		}
2605
	}
2606
	// s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2607
	if (_FILE_CONNECT) {
2608
		if (verifier_visiteur() == '0minirezo'
2609
			// si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2610
			and !isset($_COOKIE['spip_admin'])
2611
		) {
2612
			clear_path_cache();
2613
		}
2614
	}
2615
2616
}
2617
2618
/**
2619
 * Complements d'initialisation non critiques pouvant etre realises
2620
 * par les plugins
2621
 *
2622
 */
2623
function spip_initialisation_suite() {
2624
	static $too_late = 0;
2625
	if ($too_late++) {
2626
		return;
2627
	}
2628
2629
	// taille mini des login
2630
	if (!defined('_LOGIN_TROP_COURT')) {
2631
		define('_LOGIN_TROP_COURT', 4);
2632
	}
2633
2634
	// la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2635
	#if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2636
	#if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2637
	#if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2638
2639
	// la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2640
	#if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2641
	#if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2642
	#if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2643
	#if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2644
2645
	if (!defined('_PASS_LONGUEUR_MINI')) {
2646
		define('_PASS_LONGUEUR_MINI', 6);
2647
	}
2648
2649
2650
	// Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2651
	if (!defined('_IMG_QUALITE')) {
2652
		define('_IMG_QUALITE', 85);
2653
	} # valeur par defaut
2654
	if (!defined('_IMG_GD_QUALITE')) {
2655
		define('_IMG_GD_QUALITE', _IMG_QUALITE);
2656
	} # surcharge pour la lib GD
2657
	if (!defined('_IMG_CONVERT_QUALITE')) {
2658
		define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
2659
	} # surcharge pour imagick en ligne de commande
2660
	// Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2661
	if (!defined('_IMG_IMAGICK_QUALITE')) {
2662
		define('_IMG_IMAGICK_QUALITE', 75);
2663
	} # surcharge pour imagick en PHP
2664
2665
	if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2666
		define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2667
	} // poids en octet
2668
2669
	// qq chaines standard
2670
	if (!defined('_ACCESS_FILE_NAME')) {
2671
		define('_ACCESS_FILE_NAME', '.htaccess');
2672
	}
2673
	if (!defined('_AUTH_USER_FILE')) {
2674
		define('_AUTH_USER_FILE', '.htpasswd');
2675
	}
2676
	if (!defined('_SPIP_DUMP')) {
2677
		define('_SPIP_DUMP', 'dump@nom_site@@[email protected]');
2678
	}
2679
	if (!defined('_CACHE_RUBRIQUES')) {
2680
		/** Fichier cache pour le navigateur de rubrique du bandeau */
2681
		define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
2682
	}
2683
	if (!defined('_CACHE_RUBRIQUES_MAX')) {
2684
		/** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2685
		define('_CACHE_RUBRIQUES_MAX', 500);
2686
	}
2687
2688
	if (!defined('_EXTENSION_SQUELETTES')) {
2689
		define('_EXTENSION_SQUELETTES', 'html');
2690
	}
2691
2692
	if (!defined('_DOCTYPE_ECRIRE')) {
2693
		/** Définit le doctype de l’espace privé */
2694
		define('_DOCTYPE_ECRIRE', "<!DOCTYPE html>\n");
2695
	}
2696
	if (!defined('_DOCTYPE_AIDE')) {
2697
		/** Définit le doctype de l’aide en ligne */
2698
		define('_DOCTYPE_AIDE',
2699
		"<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2700
	}
2701
2702
	if (!defined('_SPIP_SCRIPT')) {
2703
		/** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2704
		 * le script de l'espace public, alias index.php */
2705
		define('_SPIP_SCRIPT', 'spip.php');
2706
	}
2707
	if (!defined('_SPIP_PAGE')) {
2708
		/** Argument page, personalisable en cas de conflit avec un autre script */
2709
		define('_SPIP_PAGE', 'page');
2710
	}
2711
2712
	// le script de l'espace prive
2713
	// Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2714
	// les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2715
	// meme pb sur thttpd cf. https://forum.spip.net/fr_184153.html
2716
	if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2717
		if (!empty($_SERVER['SERVER_SOFTWARE']) and preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE'])) {
2718
			define('_SPIP_ECRIRE_SCRIPT', 'index.php');
2719
		} else {
2720
			define('_SPIP_ECRIRE_SCRIPT', '');
2721
		}
2722
	}
2723
2724
2725
	if (!defined('_SPIP_AJAX')) {
2726
		define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2727
			? 1
2728
			: (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
2729
	}
2730
2731
	// La requete est-elle en ajax ?
2732
	if (!defined('_AJAX')) {
2733
		define('_AJAX',
2734
			(isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2735
				or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2736
				or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2737
				or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2738
			)
2739
			and empty($_REQUEST['var_noajax']) # horrible exception, car c'est pas parce que la requete est ajax jquery qu'il faut tuer tous les formulaires ajax qu'elle contient
2740
		);
2741
	}
2742
2743
	# nombre de pixels maxi pour calcul de la vignette avec gd
2744
	# au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2745
	# les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2746
	if (!defined('_IMG_GD_MAX_PIXELS')) {
2747
		define('_IMG_GD_MAX_PIXELS',
2748
		(isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2749
			? $GLOBALS['meta']['max_taille_vignettes']
2750
			: 0);
2751
	}
2752
2753
	if (!defined('_MEMORY_LIMIT_MIN')) {
2754
		define('_MEMORY_LIMIT_MIN', 16);
2755
	} // en Mo
2756
	// si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2757
	// on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2758
	// il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2759
	if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
2760
		if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2761
			$unit = strtolower(substr($memory, -1));
2762
			$memory = substr($memory, 0, -1);
2763
			switch ($unit) {
2764
				// Le modifieur 'G' est disponible depuis PHP 5.1.0
2765
				case 'g':
2766
					$memory *= 1024;
2767
				case 'm':
2768
					$memory *= 1024;
2769
				case 'k':
2770
					$memory *= 1024;
2771
			}
2772
			if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
2773
				@ini_set('memory_limit', $m = _MEMORY_LIMIT_MIN . 'M');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2774
				if (trim(ini_get('memory_limit')) != $m) {
2775
					if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2776
						define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2777
					} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2778
				}
2779
			}
2780
		} else {
2781
			if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2782
				define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2783
			}
2784
		} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2785
	}
2786
	// Protocoles a normaliser dans les chaines de langues
2787
	if (!defined('_PROTOCOLES_STD')) {
2788
		define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2789
	}
2790
2791
	init_var_mode();
2792
}
2793
2794
/**
2795
 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2796
 * la validité du cache ou certains affichages spéciaux.
2797
 *
2798
 * Le paramètre d'URL `var_mode` permet de
2799
 * modifier la pérennité du cache, recalculer des urls
2800
 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2801
 * d'afficher un écran de débug ou des traductions non réalisées.
2802
 *
2803
 * En fonction de ces paramètres dans l'URL appelante, on définit
2804
 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2805
 *
2806
 * Le paramètre `var_mode` accepte ces valeurs :
2807
 *
2808
 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2809
 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2810
 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2811
 * - `debug` :  modifie l'affichage activant le mode "debug"
2812
 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2813
 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2814
 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2815
 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2816
 *
2817
 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2818
 *
2819
 * @note
2820
 *     Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2821
 *     le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2822
 * `   var_mode` (calcul ou recalcul).
2823
 */
2824
function init_var_mode() {
2825
	static $done = false;
2826
	if (!$done) {
2827
2828
		if (isset($_GET['var_mode'])) {
2829
			$var_mode = explode(',', $_GET['var_mode']);
2830
			// tout le monde peut calcul/recalcul
2831
			if (!defined('_VAR_MODE')) {
2832
				if (in_array('recalcul', $var_mode)) {
2833
					define('_VAR_MODE', 'recalcul');
2834
				} elseif (in_array('calcul', $var_mode)) {
2835
					define('_VAR_MODE', 'calcul');
2836
				}
2837
			}
2838
			$var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2839
			if ($var_mode) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $var_mode 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...
2840
				include_spip('inc/autoriser');
2841
				// autoriser preview si preview seulement, et sinon autoriser debug
2842
				if (autoriser(
2843
					($_GET['var_mode'] == 'preview')
2844
						? 'previsualiser'
2845
						: 'debug'
2846
				)) {
2847 View Code Duplication
					if (in_array('traduction', $var_mode)) {
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...
2848
						// forcer le calcul pour passer dans traduire
2849
						if (!defined('_VAR_MODE')) {
2850
							define('_VAR_MODE', 'calcul');
2851
						}
2852
						// et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2853
						if (!defined('_VAR_NOCACHE')) {
2854
							define('_VAR_NOCACHE', true);
2855
						}
2856
						$var_mode = array_diff($var_mode, array('traduction'));
2857
					}
2858 View Code Duplication
					if (in_array('preview', $var_mode)) {
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...
2859
						// basculer sur les criteres de preview dans les boucles
2860
						if (!defined('_VAR_PREVIEW')) {
2861
							define('_VAR_PREVIEW', true);
2862
						}
2863
						// forcer le calcul
2864
						if (!defined('_VAR_MODE')) {
2865
							define('_VAR_MODE', 'calcul');
2866
						}
2867
						// et ne pas enregistrer de cache
2868
						if (!defined('_VAR_NOCACHE')) {
2869
							define('_VAR_NOCACHE', true);
2870
						}
2871
						$var_mode = array_diff($var_mode, array('preview'));
2872
					}
2873 View Code Duplication
					if (in_array('inclure', $var_mode)) {
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...
2874
						// forcer le compilo et ignorer les caches existants
2875
						if (!defined('_VAR_MODE')) {
2876
							define('_VAR_MODE', 'calcul');
2877
						}
2878
						if (!defined('_VAR_INCLURE')) {
2879
							define('_VAR_INCLURE', true);
2880
						}
2881
						// et ne pas enregistrer de cache
2882
						if (!defined('_VAR_NOCACHE')) {
2883
							define('_VAR_NOCACHE', true);
2884
						}
2885
						$var_mode = array_diff($var_mode, array('inclure'));
2886
					}
2887 View Code Duplication
					if (in_array('urls', $var_mode)) {
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...
2888
						// forcer le compilo et ignorer les caches existants
2889
						if (!defined('_VAR_MODE')) {
2890
							define('_VAR_MODE', 'calcul');
2891
						}
2892
						if (!defined('_VAR_URLS')) {
2893
							define('_VAR_URLS', true);
2894
						}
2895
						$var_mode = array_diff($var_mode, array('urls'));
2896
					}
2897 View Code Duplication
					if (in_array('images', $var_mode)) {
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...
2898
						// forcer le compilo et ignorer les caches existants
2899
						if (!defined('_VAR_MODE')) {
2900
							define('_VAR_MODE', 'calcul');
2901
						}
2902
						// indiquer qu'on doit recalculer les images
2903
						if (!defined('_VAR_IMAGES')) {
2904
							define('_VAR_IMAGES', true);
2905
						}
2906
						$var_mode = array_diff($var_mode, array('images'));
2907
					}
2908 View Code Duplication
					if (in_array('debug', $var_mode)) {
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...
2909
						if (!defined('_VAR_MODE')) {
2910
							define('_VAR_MODE', 'debug');
2911
						}
2912
						// et ne pas enregistrer de cache
2913
						if (!defined('_VAR_NOCACHE')) {
2914
							define('_VAR_NOCACHE', true);
2915
						}
2916
						$var_mode = array_diff($var_mode, array('debug'));
2917
					}
2918
					if (count($var_mode) and !defined('_VAR_MODE')) {
2919
						define('_VAR_MODE', reset($var_mode));
2920
					}
2921
					if (isset($GLOBALS['visiteur_session']['nom'])) {
2922
						spip_log($GLOBALS['visiteur_session']['nom']
2923
							. " " . _VAR_MODE);
2924
					}
2925
				} // pas autorise ?
2926
				else {
2927
					// si on n'est pas connecte on se redirige
2928
					if (!$GLOBALS['visiteur_session']) {
2929
						include_spip('inc/headers');
2930
						redirige_par_entete(generer_url_public('login',
2931
							'url=' . rawurlencode(
2932
								parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2933
							), true));
2934
					}
2935
					// sinon tant pis
2936
				}
2937
			}
2938
		}
2939
		if (!defined('_VAR_MODE')) {
2940
			/**
2941
			 * Indique le mode de calcul ou d'affichage de la page.
2942
			 * @see init_var_mode()
2943
			 */
2944
			define('_VAR_MODE', false);
2945
		}
2946
		$done = true;
2947
	}
2948
}
2949
2950
// Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2951
// supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2952
// la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2953
// https://code.spip.net/@spip_desinfecte
2954
function spip_desinfecte(&$t, $deep = true) {
2955
	foreach ($t as $key => $val) {
2956
		if (is_string($t[$key])) {
2957
			$t[$key] = str_replace(chr(0), '-', $t[$key]);
2958
		} // traiter aussi les "texte_plus" de article_edit
2959
		else {
2960
			if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2961
				spip_desinfecte($t[$key], $deep);
2962
			}
2963
		}
2964
	}
2965
}
2966
2967
//  retourne le statut du visiteur s'il s'annonce
2968
2969
// https://code.spip.net/@verifier_visiteur
2970
function verifier_visiteur() {
2971
	// Rq: pour que cette fonction marche depuis mes_options
2972
	// il faut forcer l'init si ce n'est fait
2973
	// mais on risque de perturber des plugins en initialisant trop tot
2974
	// certaines constantes
2975
	@spip_initialisation_core(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
2976
		(_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
2977
		(_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
2978
		(_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
2979
		(_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
2980
	);
2981
2982
	// Demarrer une session NON AUTHENTIFIEE si on donne son nom
2983
	// dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
2984
	// Attention on separe bien session_nom et nom, pour eviter
2985
	// les melanges entre donnees SQL et variables plus aleatoires
2986
	$variables_session = array('session_nom', 'session_email');
2987
	foreach ($variables_session as $var) {
2988
		if (_request($var) !== null) {
2989
			$init = true;
2990
			break;
2991
		}
2992
	}
2993
	if (isset($init)) {
2994
		#@spip_initialisation_suite();
2995
		$session = charger_fonction('session', 'inc');
2996
		$session();
2997
		include_spip('inc/texte');
2998
		foreach ($variables_session as $var) {
2999
			if (($a = _request($var)) !== null) {
3000
				$GLOBALS['visiteur_session'][$var] = safehtml($a);
3001
			}
3002
		}
3003
		if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
3004
			$GLOBALS['visiteur_session']['id_auteur'] = 0;
3005
		}
3006
		$session($GLOBALS['visiteur_session']);
3007
3008
		return 0;
3009
	}
3010
3011
	$h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
3012
	if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
3013
3014
		$session = charger_fonction('session', 'inc');
3015
		if ($session()) {
3016
			return $GLOBALS['visiteur_session']['statut'];
3017
		}
3018
		if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
3019
			include_spip('inc/auth');
3020
			$h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
3021
		}
3022
		if ($h) {
3023
			$GLOBALS['visiteur_session'] = $h;
3024
3025
			return $GLOBALS['visiteur_session']['statut'];
3026
		}
3027
	}
3028
3029
	// au moins son navigateur nous dit la langue preferee de cet inconnu
3030
	include_spip('inc/lang');
3031
	utiliser_langue_visiteur();
3032
3033
	return false;
3034
}
3035
3036
3037
/**
3038
 * Sélectionne la langue donnée en argument et mémorise la courante
3039
 *
3040
 * Restaure l'ancienne langue si appellée sans argument.
3041
 *
3042
 * @note
3043
 *     On pourrait économiser l'empilement en cas de non changemnt
3044
 *     et lui faire retourner `False` pour prevenir l'appelant
3045
 *     Le noyau de Spip sait le faire, mais pour assurer la compatibilité
3046
 *     cette fonction retourne toujours non `False`
3047
 *
3048
 * @uses changer_langue()
3049
 * @param null|string $lang
3050
 *     - string : Langue à appliquer,
3051
 *     - null : Pour restituer la dernière langue mémorisée.
3052
 * @return string
3053
 *     - string Langue utilisée.
3054
 **/
3055
function lang_select($lang = null) {
3056
	static $pile_langues = array();
3057
	if (!function_exists('changer_langue')) {
3058
		include_spip('inc/lang');
3059
	}
3060
	if ($lang === null) {
3061
		$lang = array_pop($pile_langues);
3062
	} else {
3063
		array_push($pile_langues, $GLOBALS['spip_lang']);
3064
	}
3065
	if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
3066
		return $lang;
3067
	}
3068
	changer_langue($lang);
3069
3070
	return $lang;
3071
}
3072
3073
/**
3074
 * Renvoie une chaîne qui identifie la session courante
3075
 *
3076
 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
3077
 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
3078
 * de fichier cache.
3079
 *
3080
 * @pipeline_appel definir_session
3081
 *
3082
 * @param bool $force
3083
 * @return string
3084
 *     Identifiant de la session
3085
 **/
3086
function spip_session($force = false) {
3087
	static $session;
3088
	if ($force or !isset($session)) {
3089
		$s = pipeline('definir_session',
3090
			$GLOBALS['visiteur_session']
3091
				? serialize($GLOBALS['visiteur_session'])
3092
				. '_' . @$_COOKIE['spip_session']
3093
				: ''
3094
		);
3095
		$session = $s ? substr(md5($s), 0, 8) : '';
3096
	}
3097
3098
	#spip_log('session: '.$session);
3099
	return $session;
3100
}
3101
3102
3103
/**
3104
 * Retourne un lien vers une aide
3105
 *
3106
 * Aide, aussi depuis l'espace privé à présent.
3107
 * Surchargeable mais pas d'erreur fatale si indisponible.
3108
 *
3109
 * @param string $aide
3110
 *    Cle d'identification de l'aide desiree
3111
 * @param bool $distante
3112
 *    Generer une url locale (par defaut)
3113
 *    ou une url distante [directement sur spip.net]
3114
 * @return
3115
 *    Lien sur une icone d'aide
3116
 **/
3117
function aider($aide = '', $distante = false) {
3118
	$aider = charger_fonction('aide', 'inc', true);
3119
3120
	return $aider ? $aider($aide, '', array(), $distante) : '';
3121
}
3122
3123
/**
3124
 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3125
 *
3126
 * Si l’utiliseur est un webmestre.
3127
 */
3128
function exec_info_dist() {
3129
3130
	include_spip('inc/autoriser');
3131
	if (autoriser('phpinfos')) {
3132
		$cookies_masques = ['spip_session', 'PHPSESSID'];
3133
		$cookies_backup = [];
3134
		foreach ($cookies_masques as $k) {
3135
			if (!empty($_COOKIE[$k])) {
3136
				$cookies_backup[$k] = $_COOKIE[$k];
3137
				$_COOKIE[$k] = '******************************';
3138
			}
3139
		}
3140
		phpinfo();
3141
		foreach ($cookies_backup as $k => $v) {
3142
			$_COOKIE[$k] = $v;
3143
		}
3144
	} else {
3145
		include_spip('inc/filtres');
3146
		sinon_interdire_acces();
3147
	}
3148
}
3149
3150
/**
3151
 * Génère une erreur de squelette
3152
 *
3153
 * Génère une erreur de squelette qui sera bien visible par un
3154
 * administrateur authentifié lors d'une visite de la page en erreur
3155
 *
3156
 * @param bool|string|array $message
3157
 *     - Message d'erreur (string|array)
3158
 *     - false pour retourner le texte des messages d'erreurs
3159
 *     - vide pour afficher les messages d'erreurs
3160
 * @param string|array|object $lieu
3161
 *     Lieu d'origine de l'erreur
3162
 * @return null|string
3163
 *     - Rien dans la plupart des cas
3164
 *     - string si $message à false.
3165
 **/
3166
function erreur_squelette($message = '', $lieu = '') {
3167
	$debusquer = charger_fonction('debusquer', 'public');
3168
	if (is_array($lieu)) {
3169
		include_spip('public/compiler');
3170
		$lieu = reconstruire_contexte_compil($lieu);
3171
	}
3172
3173
	return $debusquer($message, $lieu);
3174
}
3175
3176
/**
3177
 * Calcule un squelette avec un contexte et retourne son contenu
3178
 *
3179
 * La fonction de base de SPIP : un squelette + un contexte => une page.
3180
 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3181
 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3182
 * $options permet de selectionner les options suivantes :
3183
 *
3184
 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3185
 * - raw  => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3186
 *          pour chaque $fond fourni.
3187
 *
3188
 * @api
3189
 * @param string /array $fond
3190
 *     - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3191
 *     - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3192
 * @param array $contexte
3193
 *     - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3194
 *     - La langue est transmise automatiquement (sauf option étoile).
3195
 * @param array $options
3196
 *     Options complémentaires :
3197
 *
3198
 *     - trim   : applique un trim sur le résultat (true par défaut)
3199
 *     - raw    : retourne un tableau d'information sur le squelette (false par défaut)
3200
 *     - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3201
 *                équivalent de INCLURE*
3202
 *     - ajax   : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3203
 * @param string $connect
3204
 *     Non du connecteur de bdd a utiliser
3205
 * @return string|array
3206
 *     - Contenu du squelette calculé
3207
 *     - ou tableau d'information sur le squelette.
3208
 */
3209
function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3210
	if (!function_exists('evaluer_fond')) {
3211
		include_spip('public/assembler');
3212
	}
3213
	// assurer la compat avec l'ancienne syntaxe
3214
	// (trim etait le 3eme argument, par defaut a true)
3215
	if (!is_array($options)) {
3216
		$options = array('trim' => $options);
3217
	}
3218
	if (!isset($options['trim'])) {
3219
		$options['trim'] = true;
3220
	}
3221
3222
	if (isset($contexte['connect'])) {
3223
		$connect = $contexte['connect'];
3224
		unset($contexte['connect']);
3225
	}
3226
3227
	$texte = "";
3228
	$pages = array();
3229
	$lang_select = '';
3230 View Code Duplication
	if (!isset($options['etoile']) or !$options['etoile']) {
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...
3231
		// Si on a inclus sans fixer le critere de lang, on prend la langue courante
3232
		if (!isset($contexte['lang'])) {
3233
			$contexte['lang'] = $GLOBALS['spip_lang'];
3234
		}
3235
3236
		if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3237
			$lang_select = lang_select($contexte['lang']);
3238
		}
3239
	}
3240
3241
	if (!isset($GLOBALS['_INC_PUBLIC'])) {
3242
		$GLOBALS['_INC_PUBLIC'] = 0;
3243
	}
3244
3245
	$GLOBALS['_INC_PUBLIC']++;
3246
3247
	// fix #4235
3248
	$cache_utilise_session_appelant	= (isset($GLOBALS['cache_utilise_session']) ? $GLOBALS['cache_utilise_session'] : null);
3249
3250
3251
	foreach (is_array($fond) ? $fond : array($fond) as $f) {
3252
		
3253
		unset($GLOBALS['cache_utilise_session']);	// fix #4235
3254
3255
		$page = evaluer_fond($f, $contexte, $connect);
3256
		if ($page === '') {
3257
			$c = isset($options['compil']) ? $options['compil'] : '';
3258
			$a = array('fichier' => $f);
3259
			$erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3260
			erreur_squelette($erreur, $c);
3261
			// eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3262
			$page = array('texte' => '', 'erreur' => $erreur);
3263
		}
3264
3265
		$page = pipeline('recuperer_fond', array(
3266
			'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3267
			'data' => $page
3268
		));
3269
		if (isset($options['ajax']) and $options['ajax']) {
3270
			if (!function_exists('encoder_contexte_ajax')) {
3271
				include_spip('inc/filtres');
3272
			}
3273
			$page['texte'] = encoder_contexte_ajax(
3274
				array_merge(
3275
					$contexte,
3276
					array('fond' => $f),
3277
					($connect ? array('connect' => $connect) : array())
3278
				),
3279
				'',
3280
				$page['texte'],
3281
				$options['ajax']
3282
			);
3283
		}
3284
3285
		if (isset($options['raw']) and $options['raw']) {
3286
			$pages[] = $page;
3287
		} else {
3288
			$texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
3289
		}
3290
		
3291
		// contamination de la session appelante, pour les inclusions statiques
3292
		if (isset($page['invalideurs']['session'])){
3293
			$cache_utilise_session_appelant = $page['invalideurs']['session'];
3294
		}
3295
	}
3296
3297
	// restaurer le sessionnement du contexte appelant, 
3298
	// éventuellement contaminé si on vient de récupérer une inclusion statique sessionnée
3299
	if (isset($cache_utilise_session_appelant)) {
3300
		$GLOBALS['cache_utilise_session'] = $cache_utilise_session_appelant;
3301
	}
3302
3303
	$GLOBALS['_INC_PUBLIC']--;
3304
3305
	if ($lang_select) {
3306
		lang_select();
3307
	}
3308
	if (isset($options['raw']) and $options['raw']) {
3309
		return is_array($fond) ? $pages : reset($pages);
3310
	} else {
3311
		return $options['trim'] ? ltrim($texte) : $texte;
3312
	}
3313
}
3314
3315
/**
3316
 * Trouve un squelette dans le repertoire modeles/
3317
 *
3318
 * @param  $nom
3319
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string? 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...
3320
 */
3321
function trouve_modele($nom) {
3322
	return trouver_fond($nom, 'modeles/');
3323
}
3324
3325
/**
3326
 * Trouver un squelette dans le chemin
3327
 * on peut specifier un sous-dossier dans $dir
3328
 * si $pathinfo est a true, retourne un tableau avec
3329
 * les composantes du fichier trouve
3330
 * + le chemin complet sans son extension dans fond
3331
 *
3332
 * @param string $nom
3333
 * @param string $dir
3334
 * @param bool $pathinfo
3335
 * @return array|string
3336
 */
3337
function trouver_fond($nom, $dir = '', $pathinfo = false) {
3338
	$f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
3339
	if (!$pathinfo) {
3340
		return $f;
3341
	}
3342
	// renvoyer un tableau detaille si $pathinfo==true
3343
	$p = pathinfo($f);
3344
	if (!isset($p['extension']) or !$p['extension']) {
3345
		$p['extension'] = _EXTENSION_SQUELETTES;
3346
	}
3347
	if (!isset($p['extension']) or !$p['filename']) {
3348
		$p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3349
	}
3350
	$p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
3351
3352
	return $p;
3353
}
3354
3355
/**
3356
 * Teste, pour un nom de page de l'espace privé, s'il est possible
3357
 * de générer son contenu.
3358
 *
3359
 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3360
 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3361
 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3362
 *
3363
 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3364
 *          ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3365
 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3366
 *
3367
 * @param string $nom
3368
 *     Nom de la page
3369
 * @return string
3370
 *     Nom de l'exec, sinon chaîne vide.
3371
 **/
3372
function tester_url_ecrire($nom) {
3373
	static $exec = array();
3374
	if (isset($exec[$nom])) {
3375
		return $exec[$nom];
3376
	}
3377
	// tester si c'est une page en squelette
3378
	if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3379
		return $exec[$nom] = 'fond';
3380
	} // compat skels orthogonaux version precedente
3381
	elseif (trouver_fond($nom, 'prive/exec/')) {
3382
		return $exec[$nom] = 'fond_monobloc';
3383
	} // echafaudage d'un fond !
3384
	elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3385
		return $exec[$nom] = 'fond';
3386
	}
3387
	// attention, il ne faut pas inclure l'exec ici
3388
	// car sinon #URL_ECRIRE provoque des inclusions
3389
	// et des define intrusifs potentiels
3390
	return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
3391
}
3392
3393
3394
/**
3395
 * Teste la présence d’une extension PHP
3396
 *
3397
 * @deprected Utiliser directement la fonction native `extension_loaded($module)`
3398
 * @example
3399
 *     ```
3400
 *     $ok = charger_php_extension('sqlite');
3401
 *     ```
3402
 * @param string $module Nom du module à charger
3403
 * @return bool true si le module est chargé
3404
 **/
3405
function charger_php_extension($module) {
3406
	if (extension_loaded($module)) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return extension_loaded($module);.
Loading history...
3407
		return true;
3408
	}
3409
	return false;
3410
}
3411
3412
3413
/**
3414
 * Indique si le code HTML5 est permis sur le site public
3415
 *
3416
 * @return bool
3417
 *     true si et seulement si la configuration autorise le code HTML5 sur le site public
3418
 **/
3419
function html5_permis() {
3420
	return (isset($GLOBALS['meta']['version_html_max'])
3421
		and ('html5' == $GLOBALS['meta']['version_html_max']));
3422
}
3423
3424
/**
3425
 * Lister les formats image acceptes par les lib et fonctions images
3426
 * @param bool $gd
0 ignored issues
show
Documentation introduced by
Should the type for parameter $gd not be boolean|null?

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.

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

Loading history...
3427
 * @param bool $svg_allowed
3428
 * @return array
3429
 */
3430
function formats_image_acceptables($gd = null, $svg_allowed = true) {
3431
	$formats = null;
3432
	if (!is_null($gd)) {
3433
		$config = ($gd ? "gd_formats" : "formats_graphiques");
3434
		if (isset($GLOBALS['meta'][$config])) {
3435
			$formats = $GLOBALS['meta'][$config];
3436
			$formats = explode(',', $formats);
3437
			$formats = array_filter($formats);
3438
			$formats = array_map('trim', $formats);
3439
		}
3440
	}
3441
	if (is_null($formats)) {
3442
		include_spip('inc/filtres_images_lib_mini');
3443
		$formats = _image_extensions_acceptees_en_entree();
3444
	}
3445
3446
	if ($svg_allowed) {
3447
		if (!in_array('svg', $formats)) {
3448
			$formats[] = 'svg';
3449
		}
3450
	}
3451
	else {
3452
		$formats = array_diff($formats, ['svg']);
3453
	}
3454
	return $formats;
3455
}
3456
3457
/**
3458
 * Extension de la fonction getimagesize pour supporter aussi les images SVG
3459
 * @param string $fichier
3460
 * @return array|bool
3461
 */
3462
function spip_getimagesize($fichier) {
3463
	if (!$imagesize = @getimagesize($fichier)) {
3464
3465
		include_spip("inc/svg");
3466
		if ($attrs = svg_lire_attributs($fichier)) {
3467
			list($width, $height, $viewbox) = svg_getimagesize_from_attr($attrs);
0 ignored issues
show
Unused Code introduced by
The assignment to $viewbox is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
3468
			$imagesize = [
3469
				$width,
3470
				$height,
3471
				IMAGETYPE_SVG,
3472
				"width=\"{$width}\" height=\"{$height}\"",
3473
				"mime" => "image/svg+xml"
3474
			];
3475
		}
3476
	}
3477
	return $imagesize;
3478
}
3479
3480
3481
3482
/*
3483
 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3484
 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3485
 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3486
 */
3487
3488
/**
3489
 * lire_meta : fonction dépréciée
3490
 *
3491
 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3492
 * @see lire_config()
3493
 * @param string $nom Clé de meta à lire
3494
 * @return mixed Valeur de la meta.
3495
 **/
3496
function lire_meta($nom) {
3497
	return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
3498
}
3499
3500
3501
/**
3502
 * ecrire_metas : fonction dépréciée
3503
 *
3504
 * @deprecated
3505
 **/
3506
function ecrire_metas() { }
3507
3508
/**
3509
 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3510
 * au prochain passage dans l'espace prive
3511
 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3512
 * les alertes affichees une fois sont effacees
3513
 *
3514
 * @param string $nom
3515
 * @param string $message
3516
 * @param string $statut
3517
 */
3518
function avertir_auteurs($nom, $message, $statut = '') {
3519
	$alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3520
	if (!$alertes
3521
		or !is_array($alertes = unserialize($alertes))
3522
	) {
3523
		$alertes = array();
3524
	}
3525
3526
	if (!isset($alertes[$statut])) {
3527
		$alertes[$statut] = array();
3528
	}
3529
	$alertes[$statut][$nom] = $message;
3530
	ecrire_meta("message_alertes_auteurs", serialize($alertes));
3531
}
3532
3533
if (PHP_VERSION_ID < 50500) {
3534
	if (!function_exists('array_column')) {
3535
		/**
3536
		 * Returns the values from a single column of the input array, identified by
3537
		 * the $columnKey.
3538
		 *
3539
		 * Optionally, you may provide an $indexKey to index the values in the returned
3540
		 * array by the values from the $indexKey column in the input array.
3541
		 *
3542
		 * @link http://php.net/manual/fr/function.array-column.php
3543
		 * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
3544
		 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
3545
		 * @license http://opensource.org/licenses/MIT MIT
3546
		 *
3547
		 * @param array $input A multi-dimensional array (record set) from which to pull
0 ignored issues
show
Documentation introduced by
Should the type for parameter $input not be array|null? 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...
3548
		 *                     a column of values.
3549
		 * @param mixed $columnKey The column of values to return. This value may be the
3550
		 *                         integer key of the column you wish to retrieve, or it
3551
		 *                         may be the string key name for an associative array.
3552
		 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
3553
		 *                        the returned array. This value may be the integer key
3554
		 *                        of the column, or it may be the string key name.
3555
		 * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be null|false|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...
3556
		 */
3557
		function array_column($input = null, $columnKey = null, $indexKey = null)
0 ignored issues
show
Unused Code introduced by
The parameter $input 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 $columnKey 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 $indexKey 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...
3558
		{
3559
			// Using func_get_args() in order to check for proper number of
3560
			// parameters and trigger errors exactly as the built-in array_column()
3561
			// does in PHP 5.5.
3562
			$argc = func_num_args();
3563
			$params = func_get_args();
3564
3565
			if ($argc < 2) {
3566
				trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
3567
				return null;
3568
			}
3569
3570
			if (!is_array($params[0])) {
3571
				trigger_error(
3572
					'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
3573
					E_USER_WARNING
3574
				);
3575
				return null;
3576
			}
3577
3578 View Code Duplication
			if (!is_int($params[1])
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...
3579
				&& !is_float($params[1])
3580
				&& !is_string($params[1])
3581
				&& $params[1] !== null
3582
				&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
3583
			) {
3584
				trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
3585
				return false;
3586
			}
3587
3588 View Code Duplication
			if (isset($params[2])
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...
3589
				&& !is_int($params[2])
3590
				&& !is_float($params[2])
3591
				&& !is_string($params[2])
3592
				&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
3593
			) {
3594
				trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
3595
				return false;
3596
			}
3597
3598
			$paramsInput = $params[0];
3599
			$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
3600
3601
			$paramsIndexKey = null;
3602
			if (isset($params[2])) {
3603
				if (is_float($params[2]) || is_int($params[2])) {
3604
					$paramsIndexKey = (int) $params[2];
3605
				} else {
3606
					$paramsIndexKey = (string) $params[2];
3607
				}
3608
			}
3609
3610
			$resultArray = array();
3611
3612
			foreach ($paramsInput as $row) {
3613
				$key = $value = null;
3614
				$keySet = $valueSet = false;
3615
3616
				if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
3617
					$keySet = true;
3618
					$key = (string) $row[$paramsIndexKey];
3619
				}
3620
3621
				if ($paramsColumnKey === null) {
3622
					$valueSet = true;
3623
					$value = $row;
3624
				} elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
3625
					$valueSet = true;
3626
					$value = $row[$paramsColumnKey];
3627
				}
3628
3629
				if ($valueSet) {
3630
					if ($keySet) {
3631
						$resultArray[$key] = $value;
3632
					} else {
3633
						$resultArray[] = $value;
3634
					}
3635
				}
3636
3637
			}
3638
3639
			return $resultArray;
3640
		}
3641
3642
	}
3643
}
3644
3645
/**
3646
 * Nettoie une chaine pour servir comme classes CSS.
3647
 *
3648
 * @note
3649
 *     les classes CSS acceptent théoriquement tous les caractères sauf NUL.
3650
 *     Ici, on limite (enlève) les caractères autres qu’alphanumérique, espace, - + _ @
3651
 *
3652
 * @param string|string[] $classes
3653
 * @return string|string[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string? 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...
3654
 */
3655
function spip_sanitize_classname($classes) {
3656
	if (is_array($classes)) {
3657
		return array_map('spip_sanitize_classname', $classes);
3658
	}
3659
	return preg_replace("/[^ 0-9a-z_\-+@]/i", "", $classes);
3660
}
3661