Completed
Push — master ( f0405e...7791a8 )
by cam
05:00
created

utils.php ➔ include_fichiers_fonctions()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 9
nop 0
dl 0
loc 22
rs 9.2568
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
/**
176
 * Raccourci pour inclure mes_fonctions.php et tous les fichiers _fonctions.php des plugin
177
 * quand on a besoin dans le PHP de filtres/fonctions qui y sont definis
178
 */
179
function include_fichiers_fonctions() {
180
	static $done = false;
181
	if (!$done) {
182
		include_spip('inc/lang');
183
184
		// NB: mes_fonctions peut initialiser $dossier_squelettes (old-style)
185
		// donc il faut l'inclure "en globals"
186
		if ($f = find_in_path('mes_fonctions.php')) {
187
			global $dossier_squelettes;
188
			include_once(_ROOT_CWD . $f);
189
		}
190
191
		if (@is_readable(_CACHE_PLUGINS_FCT)) {
192
			// chargement optimise precompile
193
			include_once(_CACHE_PLUGINS_FCT);
194
		}
195
		if (test_espace_prive()) {
196
			include_spip('inc/filtres_ecrire');
197
		}
198
		$done = true;
199
	}
200
}
201
202
/**
203
 * Exécute une fonction (appellée par un pipeline) avec la donnée transmise.
204
 *
205
 * Un pipeline est lie a une action et une valeur
206
 * chaque element du pipeline est autorise a modifier la valeur
207
 * le pipeline execute les elements disponibles pour cette action,
208
 * les uns apres les autres, et retourne la valeur finale
209
 *
210
 * Cf. compose_filtres dans references.php, qui est la
211
 * version compilee de cette fonctionnalite
212
 * appel unitaire d'une fonction du pipeline
213
 * utilisee dans le script pipeline precompile
214
 *
215
 * on passe $val par reference pour limiter les allocations memoire
216
 *
217
 * @param string $fonc
218
 *     Nom de la fonction appelée par le pipeline
219
 * @param string|array $val
220
 *     Les paramètres du pipeline, son environnement
221
 * @return string|array $val
222
 *     Les paramètres du pipeline modifiés
223
 **/
224
function minipipe($fonc, &$val) {
225
	// fonction
226
	if (function_exists($fonc)) {
227
		$val = call_user_func($fonc, $val);
228
	} // Class::Methode
229
	else {
230
		if (preg_match("/^(\w*)::(\w*)$/S", $fonc, $regs)
231
			and $methode = array($regs[1], $regs[2])
232
			and is_callable($methode)
233
		) {
234
			$val = call_user_func($methode, $val);
235
		} else {
236
			spip_log("Erreur - '$fonc' non definie !");
237
		}
238
	}
239
240
	return $val;
241
}
242
243
/**
244
 * Appel d’un pipeline
245
 *
246
 * Exécute le pipeline souhaité, éventuellement avec des données initiales.
247
 * Chaque plugin qui a demandé à voir ce pipeline vera sa fonction spécifique appelée.
248
 * Les fonctions (des plugins) appelées peuvent modifier à leur guise le contenu.
249
 *
250
 * Deux types de retours. Si `$val` est un tableau de 2 éléments, avec une clé `data`
251
 * on retourne uniquement ce contenu (`$val['data']`) sinon on retourne tout `$val`.
252
 *
253
 *
254
 * @example
255
 *     Appel du pipeline `pre_insertion`
256
 *     ```
257
 *     $champs = pipeline('pre_insertion', array(
258
 *         'args' => array('table' => 'spip_articles'),
259
 *         'data' => $champs
260
 *     ));
261
 *     ```
262
 *
263
 * @param string $action
264
 *     Nom du pipeline
265
 * @param null|string|array $val
266
 *     Données à l’entrée du pipeline
267
 * @return mixed|null
268
 *     Résultat
269
 */
270
function pipeline($action, $val = null) {
271
	static $charger;
272
273
	// chargement initial des fonctions mises en cache, ou generation du cache
274
	if (!$charger) {
275
		if (!($ok = @is_readable($charger = _CACHE_PIPELINES))) {
276
			include_spip('inc/plugin');
277
			// generer les fichiers php precompiles
278
			// de chargement des plugins et des pipelines
279
			actualise_plugins_actifs();
280
			if (!($ok = @is_readable($charger))) {
281
				spip_log("fichier $charger pas cree");
282
			}
283
		}
284
285
		if ($ok) {
286
			include_once $charger;
287
		}
288
	}
289
290
	// appliquer notre fonction si elle existe
291
	$fonc = 'execute_pipeline_' . strtolower($action);
292
	if (function_exists($fonc)) {
293
		$val = $fonc($val);
294
	} // plantage ?
295
	else {
296
		spip_log("fonction $fonc absente : pipeline desactive", _LOG_ERREUR);
297
	}
298
299
	// si le flux est une table avec 2 cle args&data
300
	// on ne ressort du pipe que les donnees dans 'data'
301
	// array_key_exists pour php 4.1.0
302
	if (is_array($val)
303
		and count($val) == 2
304
		and (array_key_exists('data', $val))
305
	) {
306
		$val = $val['data'];
307
	}
308
309
	return $val;
310
}
311
312
/**
313
 * Enregistrement des événements
314
 *
315
 * Signature : `spip_log(message[,niveau|type|type.niveau])`
316
 *
317
 * Le niveau de log par défaut est la valeur de la constante `_LOG_INFO`
318
 *
319
 * Les différents niveaux possibles sont :
320
 *
321
 * - `_LOG_HS` : écrira 'HS' au début de la ligne logguée
322
 * - `_LOG_ALERTE_ROUGE` : 'ALERTE'
323
 * - `_LOG_CRITIQUE` :  'CRITIQUE'
324
 * - `_LOG_ERREUR` : 'ERREUR'
325
 * - `_LOG_AVERTISSEMENT` : 'WARNING'
326
 * - `_LOG_INFO_IMPORTANTE` : '!INFO'
327
 * - `_LOG_INFO` : 'info'
328
 * - `_LOG_DEBUG` : 'debug'
329
 *
330
 * @example
331
 *   ```
332
 *   spip_log($message)
333
 *   spip_log($message, 'recherche')
334
 *   spip_log($message, _LOG_DEBUG)
335
 *   spip_log($message, 'recherche.'._LOG_DEBUG)
336
 *   ```
337
 *
338
 * @api
339
 * @link https://programmer.spip.net/spip_log
340
 * @uses inc_log_dist()
341
 *
342
 * @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...
343
 *     Message à loger
344
 * @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...
345
 *
346
 *     - int indique le niveau de log, tel que `_LOG_DEBUG`
347
 *     - string indique le type de log
348
 *     - `string.int` indique les 2 éléments.
349
 *     Cette dernière notation est controversée mais le 3ème
350
 *     paramètre est planté pour cause de compatibilité ascendante.
351
 */
352
function spip_log($message = null, $name = null) {
353
	static $pre = array();
354
	static $log;
355
	preg_match('/^([a-z_]*)\.?(\d)?$/iS', (string)$name, $regs);
356
	if (!isset($regs[1]) or !$logname = $regs[1]) {
357
		$logname = null;
358
	}
359
	if (!isset($regs[2]) or !$niveau = $regs[2]) {
360
		$niveau = _LOG_INFO;
361
	}
362
363
	if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE : _LOG_INFO_IMPORTANTE)) {
364
		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...
365
			$pre = array(
366
				_LOG_HS => 'HS:',
367
				_LOG_ALERTE_ROUGE => 'ALERTE:',
368
				_LOG_CRITIQUE => 'CRITIQUE:',
369
				_LOG_ERREUR => 'ERREUR:',
370
				_LOG_AVERTISSEMENT => 'WARNING:',
371
				_LOG_INFO_IMPORTANTE => '!INFO:',
372
				_LOG_INFO => 'info:',
373
				_LOG_DEBUG => 'debug:'
374
			);
375
			$log = charger_fonction('log', 'inc');
376
		}
377
		if (!is_string($message)) {
378
			$message = print_r($message, true);
379
		}
380
		$log($pre[$niveau] . ' ' . $message, $logname);
381
	}
382
}
383
384
/**
385
 * Enregistrement des journaux
386
 *
387
 * @uses inc_journal_dist()
388
 * @param string $phrase Texte du journal
389
 * @param array $opt Tableau d'options
390
 **/
391
function journal($phrase, $opt = array()) {
392
	$journal = charger_fonction('journal', 'inc');
393
	$journal($phrase, $opt);
394
}
395
396
397
/**
398
 * Renvoie le `$_GET` ou le `$_POST` émis par l'utilisateur
399
 * ou pioché dans un tableau transmis
400
 *
401
 * @api
402
 * @param string $var
403
 *     Clé souhaitée
404
 * @param bool|array $c
405
 *     Tableau transmis (sinon cherche dans GET ou POST)
406
 * @return mixed|null
407
 *     - null si la clé n'a pas été trouvée
408
 *     - la valeur de la clé sinon.
409
 **/
410
function _request($var, $c = false) {
411
412
	if (is_array($c)) {
413
		return isset($c[$var]) ? $c[$var] : null;
414
	}
415
416
	if (isset($_GET[$var])) {
417
		$a = $_GET[$var];
418
	} elseif (isset($_POST[$var])) {
419
		$a = $_POST[$var];
420
	} else {
421
		return null;
422
	}
423
424
	// Si on est en ajax et en POST tout a ete encode
425
	// via encodeURIComponent, il faut donc repasser
426
	// dans le charset local...
427
	if (defined('_AJAX')
428
		and _AJAX
429
		and isset($GLOBALS['meta']['charset'])
430
		and $GLOBALS['meta']['charset'] != 'utf-8'
431
		and is_string($a)
432
		// check rapide mais pas fiable
433
		and preg_match(',[\x80-\xFF],', $a)
434
		// check fiable
435
		and include_spip('inc/charsets')
436
		and is_utf8($a)
437
	) {
438
		return importer_charset($a, 'utf-8');
439
	}
440
441
	return $a;
442
}
443
444
445
/**
446
 * Affecte une valeur à une clé (pour usage avec `_request()`)
447
 *
448
 * @see _request() Pour obtenir la valeur
449
 * @note Attention au cas ou l'on fait `set_request('truc', NULL);`
450
 *
451
 * @param string $var Nom de la clé
452
 * @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...
453
 * @param bool|array $c Tableau de données (sinon utilise `$_GET` et `$_POST`)
454
 * @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...
455
 *     - array $c complété si un $c est transmis,
456
 *     - false sinon
457
 **/
458
function set_request($var, $val = null, $c = false) {
459
	if (is_array($c)) {
460
		unset($c[$var]);
461
		if ($val !== null) {
462
			$c[$var] = $val;
463
		}
464
465
		return $c;
466
	}
467
468
	unset($_GET[$var]);
469
	unset($_POST[$var]);
470
	if ($val !== null) {
471
		$_GET[$var] = $val;
472
	}
473
474
	return false; # n'affecte pas $c
475
}
476
477
/**
478
 * Sanitizer une valeur *SI* elle provient du GET ou POST
479
 * Utile dans les squelettes pour les valeurs qu'on attrape dans le env,
480
 * dont on veut permettre à un squelette de confiance appelant de fournir une valeur complexe
481
 * mais qui doit etre nettoyee si elle provient de l'URL
482
 *
483
 * On peut sanitizer
484
 * - une valeur simple : `$where = spip_sanitize_from_request($value, 'where')`
485
 * - un tableau en partie : `$env = spip_sanitize_from_request($env, ['key1','key2'])`
486
 * - un tableau complet : `$env = spip_sanitize_from_request($env, '*')`
487
 *
488
 * @param string|array $value
489
 * @param string|array $key
490
 * @param string $sanitize_function
491
 * @return array|mixed|string
492
 */
493
function spip_sanitize_from_request($value, $key, $sanitize_function='entites_html') {
494
	if (is_array($value)) {
495
		if ($key=='*') {
496
			$key = array_keys($value);
497
		}
498
		if (!is_array($key)) {
499
			$key = [$key];
500
		}
501
		foreach ($key as $k) {
502
			if (!empty($value[$k])) {
503
				$value[$k] = spip_sanitize_from_request($value[$k], $k, $sanitize_function);
504
			}
505
		}
506
		return $value;
507
	}
508
	// si la valeur vient des GET ou POST on la sanitize
509
	if (!empty($value) and $value == _request($key)) {
0 ignored issues
show
Bug introduced by
It seems like $key defined by parameter $key on line 493 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...
510
		$value = $sanitize_function($value);
511
	}
512
	return $value;
513
}
514
515
/**
516
 * Tester si une URL est absolue
517
 * 
518
 * On est sur le web, on exclut certains protocoles, 
519
 * notamment 'file://', 'php://' et d'autres…
520
521
 * @param string $url
522
 * @return bool
523
 */
524
function tester_url_absolue($url) {
525
	$url = trim($url);
526
	if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
527
		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...
528
			isset($m[1])
529
			and $p = strtolower(rtrim($m[1], ':'))
530
			and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
531
		  ) {
532
			return false;
533
		}
534
		return true;
535
	}
536
	return false;
537
}
538
539
/**
540
 * Prend une URL et lui ajoute/retire un paramètre
541
 *
542
 * @filtre
543
 * @link https://www.spip.net/4255
544
 * @example
545
 *     ```
546
 *     [(#SELF|parametre_url{suite,18})] (ajout)
547
 *     [(#SELF|parametre_url{suite,''})] (supprime)
548
 *     [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
549
 *     ```
550
 *
551
 * @param string $url URL
552
 * @param string $c Nom du paramètre
553
 * @param string|array|null $v Valeur du paramètre
554
 * @param string $sep Séparateur entre les paramètres
555
 * @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...
556
 */
557
function parametre_url($url, $c, $v = null, $sep = '&amp;') {
558
	// requete erronnee : plusieurs variable dans $c et aucun $v
559
	if (strpos($c, "|") !== false and is_null($v)) {
560
		return null;
561
	}
562
563
	// lever l'#ancre
564
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
565
		$url = $r[1];
566
		$ancre = $r[2];
567
	} else {
568
		$ancre = '';
569
	}
570
571
	// eclater
572
	$url = preg_split(',[?]|&amp;|&,', $url);
573
574
	// recuperer la base
575
	$a = array_shift($url);
576
	if (!$a) {
577
		$a = './';
578
	}
579
580
	$regexp = ',^(' . str_replace('[]', '\[\]', $c) . '[[]?[]]?)(=.*)?$,';
581
	$ajouts = array_flip(explode('|', $c));
582
	$u = is_array($v) ? $v : rawurlencode($v);
583
	$testv = (is_array($v) ? count($v) : strlen($v));
584
	$v_read = null;
585
	// lire les variables et agir
586
	foreach ($url as $n => $val) {
587
		if (preg_match($regexp, urldecode($val), $r)) {
588
			$r = array_pad($r, 3, null);
589
			if ($v === null) {
590
				// c'est un tableau, on memorise les valeurs
591
				if (substr($r[1], -2) == "[]") {
592
					if (!$v_read) {
593
						$v_read = array();
594
					}
595
					$v_read[] = $r[2] ? substr($r[2], 1) : '';
596
				} // c'est un scalaire, on retourne direct
597
				else {
598
					return $r[2] ? substr($r[2], 1) : '';
599
				}
600
			} // suppression
601
			elseif (!$testv) {
602
				unset($url[$n]);
603
			}
604
			// Ajout. Pour une variable, remplacer au meme endroit,
605
			// pour un tableau ce sera fait dans la prochaine boucle
606
			elseif (substr($r[1], -2) != '[]') {
607
				$url[$n] = $r[1] . '=' . $u;
608
				unset($ajouts[$r[1]]);
609
			}
610
			// Pour les tableaux on laisse tomber les valeurs de
611
			// départ, on remplira à l'étape suivante
612
			else {
613
				unset($url[$n]);
614
			}
615
		}
616
	}
617
618
	// traiter les parametres pas encore trouves
619
	if ($v === null
620
		and $args = func_get_args()
621
		and count($args) == 2
622
	) {
623
		return $v_read; // rien trouve ou un tableau
624
	} elseif ($testv) {
625
		foreach ($ajouts as $k => $n) {
626
			if (!is_array($v)) {
627
				$url[] = $k . '=' . $u;
628
			} else {
629
				$id = (substr($k, -2) == '[]') ? $k : ($k . "[]");
630
				foreach ($v as $w) {
631
					$url[] = $id . '=' . (is_array($w) ? 'Array' : $w);
632
				}
633
			}
634
		}
635
	}
636
637
	// eliminer les vides
638
	$url = array_filter($url);
639
640
	// recomposer l'adresse
641
	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...
642
		$a .= '?' . join($sep, $url);
643
	}
644
645
	return $a . $ancre;
646
}
647
648
/**
649
 * Ajoute (ou retire) une ancre sur une URL
650
 *
651
 * L’ancre est nettoyée : on translitère, vire les non alphanum du début,
652
 * et on remplace ceux à l'interieur ou au bout par `-`
653
 *
654
 * @example
655
 *     - `$url = ancre_url($url, 'navigation'); // => mettra l’ancre #navigation
656
 *     - `$url = ancre_url($url, ''); // => enlèvera une éventuelle ancre
657
 * @uses translitteration()
658
 * @param string $url
659
 * @param string $ancre
660
 * @return string
661
 */
662
function ancre_url($url, $ancre) {
663
	// lever l'#ancre
664
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
665
		$url = $r[1];
666
	}
667
	if (preg_match('/[^-_a-zA-Z0-9]+/S', $ancre)) {
668
		if (!function_exists('translitteration')) {
669
			include_spip('inc/charsets');
670
		}
671
		$ancre = preg_replace(
672
			array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'),
673
			array('', '-'),
674
			translitteration($ancre)
675
		);
676
	}
677
	return $url . (strlen($ancre) ? '#' . $ancre : '');
678
}
679
680
/**
681
 * Pour le nom du cache, les `types_urls` et `self`
682
 *
683
 * @param string|null $reset
684
 * @return string
685
 */
686
function nettoyer_uri($reset = null) {
687
	static $done = false;
688
	static $propre = '';
689
	if (!is_null($reset)) {
690
		return $propre = $reset;
691
	}
692
	if ($done) {
693
		return $propre;
694
	}
695
	$done = true;
696
	return $propre = nettoyer_uri_var($GLOBALS['REQUEST_URI']);
697
}
698
699
/**
700
 * Nettoie une request_uri des paramètres var_xxx
701
 * 
702
 * Attention, la regexp doit suivre _CONTEXTE_IGNORE_VARIABLES défini au début de public/assembler.php
703
 * 
704
 * @param $request_uri
705
 * @return string
706
 */
707
function nettoyer_uri_var($request_uri) {
708
	$uri1 = $request_uri;
709
	do {
710
		$uri = $uri1;
711
		$uri1 = preg_replace(',([?&])(var_[^=&]*|PHPSESSID|fbclid|utm_[^=&]*)=[^&]*(&|$),i',
712
			'\1', $uri);
713
	} while ($uri <> $uri1);
714
	return preg_replace(',[?&]$,', '', $uri1);
715
}
716
717
718
/**
719
 * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
720
 *
721
 * @param string $amp
722
 *    Style des esperluettes
723
 * @param bool $root
724
 * @return string
725
 *    URL vers soi-même
726
 **/
727
function self($amp = '&amp;', $root = false) {
728
	$url = nettoyer_uri();
729
	if (!$root
730
		and (
731
			// si pas de profondeur on peut tronquer
732
			$GLOBALS['profondeur_url'] < (_DIR_RESTREINT ? 1 : 2)
733
			// sinon c'est OK si _SET_HTML_BASE a ete force a false
734
			or (defined('_SET_HTML_BASE') and !_SET_HTML_BASE))
735
	) {
736
		$url = preg_replace(',^[^?]*/,', '', $url);
737
	}
738
	// ajouter le cas echeant les variables _POST['id_...']
739
	foreach ($_POST as $v => $c) {
740
		if (substr($v, 0, 3) == 'id_') {
741
			$url = parametre_url($url, $v, $c, '&');
742
		}
743
	}
744
745
	// supprimer les variables sans interet
746
	if (test_espace_prive()) {
747
		$url = preg_replace(',([?&])('
748
			. 'lang|show_docs|'
749
			. 'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
750
		$url = preg_replace(',([?&])[&]+,', '\1', $url);
751
		$url = preg_replace(',[&]$,', '\1', $url);
752
	}
753
754
	// eviter les hacks
755
	include_spip('inc/filtres_mini');
756
	$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...
757
	
758
	$url = str_replace(array("'", '"', '<', '[', ']', ':'), array('%27', '%22', '%3C', '%5B', '%5D', '%3A'), $url);
759
760
	// &amp; ?
761
	if ($amp != '&amp;') {
762
		$url = str_replace('&amp;', $amp, $url);
763
	}
764
765
	// Si ca demarre par ? ou vide, donner './'
766
	$url = preg_replace(',^([?].*)?$,', './\1', $url);
767
768
	return $url;
769
}
770
771
772
/**
773
 * Indique si on est dans l'espace prive
774
 *
775
 * @return bool
776
 *     true si c'est le cas, false sinon.
777
 */
778
function test_espace_prive() {
779
	return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
780
}
781
782
/**
783
 * Vérifie la présence d'un plugin actif, identifié par son préfixe
784
 *
785
 * @param string $plugin
786
 * @return bool
787
 */
788
function test_plugin_actif($plugin) {
789
	return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ? true : false;
790
}
791
792
/**
793
 * Traduction des textes de SPIP
794
 *
795
 * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
796
 *
797
 * @api
798
 * @uses inc_traduire_dist()
799
 * @uses _L()
800
 * @example
801
 *     ```
802
 *     _T('bouton_enregistrer')
803
 *     _T('medias:image_tourner_droite')
804
 *     _T('medias:erreurs', array('nb'=>3))
805
 *     _T("email_sujet", array('spip_lang'=>$lang_usager))
806
 *     ```
807
 *
808
 * @param string $texte
809
 *     Clé de traduction
810
 * @param array $args
811
 *     Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
812
 * @param array $options
813
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
814
 *     - bool force : forcer un retour meme si la chaine n'a pas de traduction
815
 *     - bool sanitize : nettoyer le html suspect dans les arguments
816
 * @return string
817
 *     Texte
818
 */
819
function _T($texte, $args = array(), $options = array()) {
820
	static $traduire = false;
821
	$o = array('class' => '', 'force' => true, 'sanitize' => true);
822
	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...
823
		// support de l'ancien argument $class
824
		if (is_string($options)) {
825
			$options = array('class' => $options);
826
		}
827
		$o = array_merge($o, $options);
828
	}
829
830
	if (!$traduire) {
831
		$traduire = charger_fonction('traduire', 'inc');
832
		include_spip('inc/lang');
833
	}
834
835
	// On peut passer explicitement la langue dans le tableau
836
	// On utilise le même nom de variable que la globale
837 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...
838
		$lang = $args['spip_lang'];
839
		// On l'enleve pour ne pas le passer au remplacement
840
		unset($args['spip_lang']);
841
	} // Sinon on prend la langue du contexte
842
	else {
843
		$lang = $GLOBALS['spip_lang'];
844
	}
845
	$text = $traduire($texte, $lang);
846
847
	if (!strlen($text)) {
848
		if (!$o['force']) {
849
			return '';
850
		}
851
852
		$text = $texte;
853
854
		// pour les chaines non traduites, assurer un service minimum
855
		if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
856
			$text = str_replace('_', ' ',
857
				(($n = strpos($text, ':')) === false ? $texte :
858
					substr($texte, $n + 1)));
859
		}
860
		$o['class'] = null;
861
862
	}
863
864
	return _L($text, $args, $o);
865
866
}
867
868
869
/**
870
 * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
871
 *
872
 * Cette fonction est également appelée dans le code source de SPIP quand une
873
 * chaîne n'est pas encore dans les fichiers de langue.
874
 *
875
 * @see _T()
876
 * @example
877
 *     ```
878
 *     _L('Texte avec @nb@ ...', array('nb'=>3)
879
 *     ```
880
 *
881
 * @param string $text
882
 *     Texte
883
 * @param array $args
884
 *     Couples (variable => valeur) à transformer dans le texte
885
 * @param array $options
886
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
887
 *     - bool sanitize : nettoyer le html suspect dans les arguments
888
 * @return string
889
 *     Texte
890
 */
891
function _L($text, $args = array(), $options = array()) {
892
	$f = $text;
893
	$defaut_options = array(
894
		'class' => null,
895
		'sanitize' => true,
896
	);
897
	// support de l'ancien argument $class
898
	if ($options and is_string($options)) {
899
		$options = array('class' => $options);
900
	}
901
	if (is_array($options)) {
902
		$options += $defaut_options;
903
	} else {
904
		$options = $defaut_options;
905
	}
906
907
	if (is_array($args) and count($args)) {
908
		if (!function_exists('interdire_scripts')) {
909
			include_spip('inc/texte');
910
		}
911
		if (!function_exists('echapper_html_suspect')) {
912
			include_spip('inc/texte_mini');
913
		}
914
		foreach ($args as $name => $value) {
915
			if (strpos($text, "@$name@") !== false) {
916
				if ($options['sanitize']) {
917
					$value = echapper_html_suspect($value);
918
					$value = interdire_scripts($value, -1);
919
				}
920
				if (!empty($options['class'])) {
921
					$value = "<span class='".$options['class']."'>$value</span>";
922
				}
923
				$text = str_replace("@$name@", $value, $text);
924
				unset($args[$name]);
925
			}
926
		}
927
		// Si des variables n'ont pas ete inserees, le signaler
928
		// (chaines de langues pas a jour)
929
		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...
930
			spip_log("$f:  variables inutilisees " . join(', ', array_keys($args)), _LOG_DEBUG);
931
		}
932
	}
933
934
	if (($GLOBALS['test_i18n'] or (_request('var_mode') == 'traduction')) and is_null($options['class'])) {
935
		return "<span class=debug-traduction-erreur>$text</span>";
936
	} else {
937
		return $text;
938
	}
939
}
940
941
942
/**
943
 * Retourne un joli chemin de répertoire
944
 *
945
 * Pour afficher `ecrire/action/` au lieu de `action/` dans les messages
946
 * ou `tmp/` au lieu de `../tmp/`
947
 *
948
 * @param stirng $rep Chemin d’un répertoire
949
 * @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...
950
 */
951
function joli_repertoire($rep) {
952
	$a = substr($rep, 0, 1);
953
	if ($a <> '.' and $a <> '/') {
954
		$rep = (_DIR_RESTREINT ? '' : _DIR_RESTREINT_ABS) . $rep;
955
	}
956
	$rep = preg_replace(',(^\.\.\/),', '', $rep);
957
958
	return $rep;
959
}
960
961
962
/**
963
 * Débute ou arrête un chronomètre et retourne sa valeur
964
 *
965
 * On exécute 2 fois la fonction, la première fois pour démarrer le chrono,
966
 * la seconde fois pour l’arrêter et récupérer la valeur
967
 *
968
 * @example
969
 *     ```
970
 *     spip_timer('papoter');
971
 *     // actions
972
 *     $duree = spip_timer('papoter');
973
 *     ```
974
 *
975
 * @param string $t
976
 *     Nom du chronomètre
977
 * @param bool $raw
978
 *     - false : retour en texte humainement lisible
979
 *     - true : retour en millisecondes
980
 * @return float|int|string|void
981
 */
982
function spip_timer($t = 'rien', $raw = false) {
983
	static $time;
984
	$a = time();
985
	$b = microtime();
986
	// microtime peut contenir les microsecondes et le temps
987
	$b = explode(' ', $b);
988
	if (count($b) == 2) {
989
		$a = end($b);
990
	} // plus precis !
991
	$b = reset($b);
992
	if (!isset($time[$t])) {
993
		$time[$t] = $a + $b;
994
	} else {
995
		$p = ($a + $b - $time[$t]) * 1000;
996
		unset($time[$t]);
997
#			echo "'$p'";exit;
998
		if ($raw) {
999
			return $p;
1000
		}
1001
		if ($p < 1000) {
1002
			$s = '';
1003
		} else {
1004
			$s = sprintf("%d ", $x = floor($p / 1000));
1005
			$p -= ($x * 1000);
1006
		}
1007
1008
		return $s . sprintf($s ? "%07.3f ms" : "%.3f ms", $p);
1009
	}
1010
}
1011
1012
1013
// Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
1014
// sinon renvoie True et le date sauf si ca n'est pas souhaite
1015
// https://code.spip.net/@spip_touch
1016
function spip_touch($fichier, $duree = 0, $touch = true) {
1017
	if ($duree) {
1018
		clearstatcache();
1019
		if ((@$f = filemtime($fichier)) and ($f >= time() - $duree)) {
1020
			return false;
1021
		}
1022
	}
1023
	if ($touch !== false) {
1024
		if (!@touch($fichier)) {
1025
			spip_unlink($fichier);
1026
			@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...
1027
		};
1028
		@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...
1029
	}
1030
1031
	return true;
1032
}
1033
1034
1035
/**
1036
 * Action qui déclenche une tache de fond
1037
 *
1038
 * @see  queue_affichage_cron()
1039
 * @see  action_super_cron_dist()
1040
 * @uses cron()
1041
 **/
1042
function action_cron() {
1043
	include_spip('inc/headers');
1044
	http_status(204); // No Content
1045
	header("Connection: close");
1046
	define('_DIRECT_CRON_FORCE', true);
1047
	cron();
1048
}
1049
1050
/**
1051
 * Exécution des tâches de fond
1052
 *
1053
 * @uses inc_genie_dist()
1054
 *
1055
 * @param array $taches
1056
 *     Tâches forcées
1057
 * @param array $taches_old
1058
 *     Tâches forcées, pour compat avec ancienne syntaxe
1059
 * @return bool
1060
 *     True si la tache a pu être effectuée
1061
 */
1062
function cron($taches = array(), $taches_old = array()) {
1063
	// si pas en mode cron force, laisser tomber.
1064
	if (!defined('_DIRECT_CRON_FORCE')) {
1065
		return false;
1066
	}
1067
	if (!is_array($taches)) {
1068
		$taches = $taches_old;
1069
	} // compat anciens appels
1070
	// si taches a inserer en base et base inaccessible, laisser tomber
1071
	// sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
1072
	// queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
1073
	// et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
1074
	if ($taches and count($taches) and !spip_connect()) {
1075
		return false;
1076
	}
1077
	spip_log("cron !", 'jq' . _LOG_DEBUG);
1078
	if ($genie = charger_fonction('genie', 'inc', true)) {
1079
		return $genie($taches);
1080
	}
1081
1082
	return false;
1083
}
1084
1085
/**
1086
 * Ajout d'une tache dans la file d'attente
1087
 *
1088
 * @param string $function
1089
 *     Le nom de la fonction PHP qui doit être appelée.
1090
 * @param string $description
1091
 *     Une description humainement compréhensible de ce que fait la tâche
1092
 *     (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
1093
 * @param array $arguments
1094
 *     Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
1095
 * @param string $file
1096
 *     Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
1097
 *     exemple : `'inc/mail'` : il ne faut pas indiquer .php
1098
 *     Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
1099
 * @param bool $no_duplicate
1100
 *     Facultatif, `false` par défaut
1101
 *
1102
 *     - 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.
1103
 *     - 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
1104
 * @param int $time
1105
 *     Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
1106
 *     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).
1107
 * @param int $priority
1108
 *     Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
1109
 *     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.
1110
 * @return int
1111
 *     Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
1112
 */
1113
function job_queue_add(
1114
	$function,
1115
	$description,
1116
	$arguments = array(),
1117
	$file = '',
1118
	$no_duplicate = false,
1119
	$time = 0,
1120
	$priority = 0
1121
) {
1122
	include_spip('inc/queue');
1123
1124
	return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
1125
}
1126
1127
/**
1128
 * Supprimer une tache de la file d'attente
1129
 *
1130
 * @param int $id_job
1131
 *  id of jonb to delete
1132
 * @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...
1133
 */
1134
function job_queue_remove($id_job) {
1135
	include_spip('inc/queue');
1136
1137
	return queue_remove_job($id_job);
1138
}
1139
1140
/**
1141
 * Associer une tache a un/des objets de SPIP
1142
 *
1143
 * @param int $id_job
1144
 *     id of job to link
1145
 * @param array $objets
1146
 *     can be a simple array('objet'=>'article', 'id_objet'=>23)
1147
 *     or an array of simple array to link multiples objet in one time
1148
 */
1149
function job_queue_link($id_job, $objets) {
1150
	include_spip('inc/queue');
1151
1152
	return queue_link_job($id_job, $objets);
1153
}
1154
1155
1156
/**
1157
 * Renvoyer le temps de repos restant jusqu'au prochain job
1158
 *
1159
 * @staticvar int $queue_next_job_time
1160
 * @see queue_set_next_job_time()
1161
 * @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...
1162
 *    Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
1163
 *
1164
 *    - si `true`, force la relecture depuis le fichier
1165
 *    - si int, affecte la static directement avec la valeur
1166
 * @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...
1167
 *
1168
 *  - `0` si un job est à traiter
1169
 *  - `null` si la queue n'est pas encore initialisée
1170
 */
1171
function queue_sleep_time_to_next_job($force = null) {
1172
	static $queue_next_job_time = -1;
1173
	if ($force === true) {
1174
		$queue_next_job_time = -1;
1175
	} elseif ($force) {
1176
		$queue_next_job_time = $force;
1177
	}
1178
1179
	if ($queue_next_job_time == -1) {
1180
		if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
1181
			define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP . "job_queue_next.txt");
1182
		}
1183
		// utiliser un cache memoire si dispo
1184
		if (function_exists("cache_get") and defined('_MEMOIZE_MEMORY') and _MEMOIZE_MEMORY) {
1185
			$queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
1186
		} else {
1187
			$queue_next_job_time = null;
1188
			if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu)) {
1189
				$queue_next_job_time = intval($contenu);
1190
			}
1191
		}
1192
	}
1193
1194
	if (is_null($queue_next_job_time)) {
1195
		return null;
1196
	}
1197
	if (!$_SERVER['REQUEST_TIME']) {
1198
		$_SERVER['REQUEST_TIME'] = time();
1199
	}
1200
1201
	return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
1202
}
1203
1204
1205
/**
1206
 * Transformation XML des `&` en `&amp;`
1207
 * 
1208
 * @pipeline post_typo
1209
 * @param string $u
1210
 * @return string
1211
 */
1212
function quote_amp($u) {
1213
	return preg_replace(
1214
		"/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i",
1215
		"&amp;", $u);
1216
}
1217
1218
1219
/**
1220
 * Produit une balise `<script>` valide
1221
 *
1222
 * @example
1223
 *     ```
1224
 *     echo http_script('alert("ok");');
1225
 *     echo http_script('','js/jquery.js');
1226
 *     ```
1227
 *
1228
 * @param string $script
1229
 *     Code source du script
1230
 * @param string $src
1231
 *     Permet de faire appel à un fichier javascript distant
1232
 * @param string $noscript
1233
 *     Contenu de la balise  `<noscript>`
1234
 * @return string
1235
 *     Balise HTML `<script>` et son contenu
1236
 **/
1237
function http_script($script, $src = '', $noscript = '') {
1238
	static $done = array();
1239
1240
	if ($src && !isset($done[$src])) {
1241
		$done[$src] = true;
1242
		$src = find_in_path($src, _JAVASCRIPT);
1243
		$src = " src='$src'";
1244
	} else {
1245
		$src = '';
1246
	}
1247
	if ($script) {
1248
		$script = ("/*<![CDATA[*/\n" .
1249
			preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
1250
			"/*]]>*/");
1251
	}
1252
	if ($noscript) {
1253
		$noscript = "<noscript>\n\t$noscript\n</noscript>\n";
1254
	}
1255
1256
	return ($src or $script or $noscript)
1257
		? "<script type='text/javascript'$src>$script</script>$noscript"
1258
		: '';
1259
}
1260
1261
1262
/**
1263
 * Sécurise du texte à écrire dans du PHP ou du Javascript.
1264
 *
1265
 * Transforme n'importe quel texte en une chaîne utilisable
1266
 * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
1267
 * simples (`'` uniquement ; pas `"`)
1268
 *
1269
 * Utile particulièrement en filtre dans un squelettes
1270
 * pour écrire un contenu dans une variable JS ou PHP.
1271
 *
1272
 * Échappe les apostrophes (') du contenu transmis.
1273
 *
1274
 * @link https://www.spip.net/4281
1275
 * @example
1276
 *     PHP dans un squelette
1277
 *     ```
1278
 *     $x = '[(#TEXTE|texte_script)]';
1279
 *     ```
1280
 *
1281
 *     JS dans un squelette (transmettre une chaîne de langue)
1282
 *     ```
1283
 *     $x = '<:afficher_calendrier|texte_script:>';
1284
 *     ```
1285
 *
1286
 * @filtre
1287
 * @param string $texte
1288
 *     Texte à échapper
1289
 * @return string
1290
 *     Texte échappé
1291
 **/
1292
function texte_script($texte) {
1293
	return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
1294
}
1295
1296
1297
/**
1298
 * Gestion des chemins (ou path) de recherche de fichiers par SPIP
1299
 *
1300
 * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
1301
 * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
1302
 * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
1303
 *
1304
 * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
1305
 * en tête du path, dans cet ordre (hormis `squelettes` & la globale
1306
 * `$dossier_squelette` si définie qui resteront devant)
1307
 *
1308
 * Retourne dans tous les cas la liste des chemins.
1309
 *
1310
 * @note
1311
 *     Cette fonction est appelée à plusieurs endroits et crée une liste
1312
 *     de chemins finale à peu près de la sorte :
1313
 *
1314
 *     - dossiers squelettes (si globale précisée)
1315
 *     - squelettes/
1316
 *     - plugins (en fonction de leurs dépendances) : ceux qui dépendent
1317
 *       d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
1318
 *     - racine du site
1319
 *     - squelettes-dist/
1320
 *     - prive/
1321
 *     - ecrire/
1322
 *
1323
 * @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...
1324
 *     - Répertoire(s) à empiler au path
1325
 *     - '' provoque un recalcul des chemins.
1326
 * @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...
1327
 *     Liste des chemins, par ordre de priorité.
1328
 **/
1329
function _chemin($dir_path = null) {
1330
	static $path_base = null;
1331
	static $path_full = null;
1332
	if ($path_base == null) {
1333
		// Chemin standard depuis l'espace public
1334
		$path = defined('_SPIP_PATH') ? _SPIP_PATH :
1335
			_DIR_RACINE . ':' .
1336
			_DIR_RACINE . 'squelettes-dist/:' .
1337
			_DIR_RACINE . 'prive/:' .
1338
			_DIR_RESTREINT;
1339
		// Ajouter squelettes/
1340
		if (@is_dir(_DIR_RACINE . 'squelettes')) {
1341
			$path = _DIR_RACINE . 'squelettes/:' . $path;
1342
		}
1343
		foreach (explode(':', $path) as $dir) {
1344
			if (strlen($dir) and substr($dir, -1) != '/') {
1345
				$dir .= "/";
1346
			}
1347
			$path_base[] = $dir;
1348
		}
1349
		$path_full = $path_base;
1350
		// Et le(s) dossier(s) des squelettes nommes
1351 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...
1352
			foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1353
				array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
1354
			}
1355
		}
1356
		$GLOBALS['path_sig'] = md5(serialize($path_full));
1357
	}
1358
	if ($dir_path === null) {
1359
		return $path_full;
1360
	}
1361
1362
	if (strlen($dir_path)) {
1363
		$tete = "";
1364
		if (reset($path_base) == _DIR_RACINE . 'squelettes/') {
1365
			$tete = array_shift($path_base);
1366
		}
1367
		$dirs = array_reverse(explode(':', $dir_path));
1368
		foreach ($dirs as $dir_path) {
1369
			#if ($dir_path{0}!='/')
1370
			#	$dir_path = $dir_path;
1371
			if (substr($dir_path, -1) != '/') {
1372
				$dir_path .= "/";
1373
			}
1374
			if (!in_array($dir_path, $path_base)) {
1375
				array_unshift($path_base, $dir_path);
1376
			}
1377
		}
1378
		if (strlen($tete)) {
1379
			array_unshift($path_base, $tete);
1380
		}
1381
	}
1382
	$path_full = $path_base;
1383
	// Et le(s) dossier(s) des squelettes nommes
1384 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...
1385
		foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1386
			array_unshift($path_full, ((isset($d[0]) and $d[0] == '/') ? '' : _DIR_RACINE) . $d . '/');
1387
		}
1388
	}
1389
1390
	$GLOBALS['path_sig'] = md5(serialize($path_full));
1391
1392
	return $path_full;
1393
}
1394
1395
/**
1396
 * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
1397
 *
1398
 * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
1399
 *
1400
 * @uses _chemin()
1401
 *
1402
 * @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...
1403
 **/
1404
function creer_chemin() {
1405
	$path_a = _chemin();
1406
	static $c = '';
1407
1408
	// on calcule le chemin si le dossier skel a change
1409
	if ($c != $GLOBALS['dossier_squelettes']) {
1410
		// assurer le non plantage lors de la montee de version :
1411
		$c = $GLOBALS['dossier_squelettes'];
1412
		$path_a = _chemin(''); // forcer un recalcul du chemin
1413
	}
1414
1415
	return $path_a;
1416
}
1417
1418
1419
function lister_themes_prives() {
1420
	static $themes = null;
1421
	if (is_null($themes)) {
1422
		// si pas encore definie
1423
		if (!defined('_SPIP_THEME_PRIVE')) {
1424
			define('_SPIP_THEME_PRIVE', 'spip');
1425
		}
1426
		$themes = array(_SPIP_THEME_PRIVE);
1427
		// lors d'une installation neuve, prefs n'est pas definie.
1428 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...
1429
			$prefs = $GLOBALS['visiteur_session']['prefs'];
1430
		} else {
1431
			$prefs = array();
1432
		}
1433
		if (is_string($prefs)) {
1434
			$prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
1435
		}
1436
		if (
1437
			((isset($prefs['theme']) and $theme = $prefs['theme'])
1438
				or (isset($GLOBALS['theme_prive_defaut']) and $theme = $GLOBALS['theme_prive_defaut']))
1439
			and $theme != _SPIP_THEME_PRIVE
1440
		) {
1441
			array_unshift($themes, $theme);
1442
		} // placer le theme choisi en tete
1443
	}
1444
1445
	return $themes;
1446
}
1447
1448
function find_in_theme($file, $subdir = '', $include = false) {
1449
	static $themefiles = array();
1450
	if (isset($themefiles["$subdir$file"])) {
1451
		return $themefiles["$subdir$file"];
1452
	}
1453
	// on peut fournir une icone generique -xx.svg qui fera le job dans toutes les tailles, et qui est prioritaire sur le png
1454
	// si il y a un .svg a la bonne taille (-16.svg) a cote, on l'utilise en remplacement du -16.png
1455
	if (preg_match(',-(\d+)[.](png|gif|svg)$,', $file, $m)
1456
	  and $file_svg_generique = substr($file,0, -strlen($m[0])) . "-xx.svg"
1457
		and $f = find_in_theme("$file_svg_generique")) {
1458
		if ($fsize = substr($f,0,-6) . $m[1] . ".svg" and file_exists($fsize)) {
1459
			return $themefiles["$subdir$file"] = $fsize;
1460
		}
1461
		else {
1462
			return $themefiles["$subdir$file"] = "$f?".$m[1]."px";
1463
		}
1464
	}
1465
1466
	$themes = lister_themes_prives();
1467
	foreach ($themes as $theme) {
1468
		if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
1469
			return $themefiles["$subdir$file"] = $f;
1470
		}
1471
	}
1472
	spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');
1473
1474
	return $themefiles["$subdir$file"] = "";
1475
}
1476
1477
1478
/**
1479
 * Cherche une image dans les dossiers d'images
1480
 *
1481
 * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
1482
 * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
1483
 * de facon temporaire le temps de la migration, et cherche de nouveau.
1484
 *
1485
 * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
1486
 * dans le répertoire défini par la constante `_NOM_IMG_PACK`
1487
 *
1488
 * @see find_in_theme()
1489
 * @see inc_icone_renommer_dist()
1490
 *
1491
 * @param string $icone
1492
 *     Nom de l'icone cherchée
1493
 * @return string
1494
 *     Chemin complet de l'icone depuis la racine si l'icone est trouée,
1495
 *     sinon chaîne vide.
1496
 **/
1497
function chemin_image($icone) {
1498
	static $icone_renommer;
1499
	if ($p = strpos($icone, '?')) {
1500
		$icone = substr($icone,0, $p);
1501
	}
1502
	// gerer le cas d'un double appel en evitant de refaire le travail inutilement
1503
	if (strpos($icone, "/") !== false and file_exists($icone)) {
1504
		return $icone;
1505
	}
1506
1507
	// si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
1508
	if (preg_match(',[.](png|gif|jpg|webp|svg)$,', $icone) and $f = find_in_theme("images/$icone")) {
1509
		return $f;
1510
	}
1511
	// sinon passer par le module de renommage
1512
	if (is_null($icone_renommer)) {
1513
		$icone_renommer = charger_fonction('icone_renommer', 'inc', true);
1514
	}
1515
	if ($icone_renommer) {
1516
		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...
1517
		if (file_exists($icone)) {
1518
			return $icone;
1519
		}
1520
	}
1521
1522
	return find_in_path($icone, _NOM_IMG_PACK);
1523
}
1524
1525
//
1526
// chercher un fichier $file dans le SPIP_PATH
1527
// si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
1528
// si 3e arg vrai, on inclut si ce n'est fait.
1529
$GLOBALS['path_sig'] = '';
1530
$GLOBALS['path_files'] = null;
1531
1532
/**
1533
 * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
1534
 *
1535
 * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
1536
 * suivant l'ordre des chemins connus de SPIP.
1537
 *
1538
 * @api
1539
 * @see  charger_fonction()
1540
 * @uses creer_chemin() Pour la liste des chemins.
1541
 * @example
1542
 *     ```
1543
 *     $f = find_in_path('css/perso.css');
1544
 *     $f = find_in_path('perso.css', 'css');
1545
 *     ```
1546
 *
1547
 * @param string $file
1548
 *     Fichier recherché
1549
 * @param string $dirname
1550
 *     Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
1551
 * @param bool|string $include
1552
 *     - false : ne fait rien de plus
1553
 *     - true : inclut le fichier (include_once)
1554
 *     - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
1555
 * @return string|bool
1556
 *     - string : chemin du fichier trouvé
1557
 *     - false : fichier introuvable
1558
 **/
1559
function find_in_path($file, $dirname = '', $include = false) {
1560
	static $dirs = array();
1561
	static $inc = array(); # cf https://git.spip.net/spip/spip/commit/42e4e028e38c839121efaee84308d08aee307eec
1562
	static $c = '';
1563
1564
	if (!$file and !strlen($file)) {
1565
		return false;
1566
	}
1567
1568
	// on calcule le chemin si le dossier skel a change
1569
	if ($c != $GLOBALS['dossier_squelettes']) {
1570
		// assurer le non plantage lors de la montee de version :
1571
		$c = $GLOBALS['dossier_squelettes'];
1572
		creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
1573
	}
1574
1575
	if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
1576
		if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
1577
			return false;
1578
		}
1579
		if ($include and !isset($inc[$dirname][$file])) {
1580
			include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1581
			$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1582
		}
1583
1584
		return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1585
	}
1586
1587
	$a = strrpos($file, '/');
1588
	if ($a !== false) {
1589
		$dirname .= substr($file, 0, ++$a);
1590
		$file = substr($file, $a);
1591
	}
1592
1593
	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...
1594 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...
1595
			$dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a);
1596
		}
1597
		if ($dirs[$a]) {
1598
			if (file_exists(_ROOT_CWD . ($a .= $file))) {
1599
				if ($include and !isset($inc[$dirname][$file])) {
1600
					include_once _ROOT_CWD . $a;
1601
					$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1602
				}
1603 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...
1604
					// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1605
					if (is_null($GLOBALS['path_files'])) {
1606
						return $a;
1607
					}
1608
					define('_SAUVER_CHEMIN', true);
1609
				}
1610
1611
				return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
1612
			}
1613
		}
1614
	}
1615
1616
	if ($include) {
1617
		spip_log("include_spip $dirname$file non trouve");
1618
		if ($include === 'required') {
1619
			echo '<pre>',
1620
			"<strong>Erreur Fatale</strong><br />";
1621
			if (function_exists('debug_print_backtrace')) {
1622
				echo debug_print_backtrace();
1623
			}
1624
			echo '</pre>';
1625
			die("Erreur interne: ne peut inclure $dirname$file");
1626
		}
1627
	}
1628
1629 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...
1630
		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1631
		if (is_null($GLOBALS['path_files'])) {
1632
			return false;
1633
		}
1634
		define('_SAUVER_CHEMIN', true);
1635
	}
1636
1637
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1638
}
1639
1640
function clear_path_cache() {
1641
	$GLOBALS['path_files'] = array();
1642
	spip_unlink(_CACHE_CHEMIN);
1643
}
1644
1645
function load_path_cache() {
1646
	// charger le path des plugins
1647
	if (@is_readable(_CACHE_PLUGINS_PATH)) {
1648
		include_once(_CACHE_PLUGINS_PATH);
1649
	}
1650
	$GLOBALS['path_files'] = array();
1651
	// si le visiteur est admin,
1652
	// on ne recharge pas le cache pour forcer sa mise a jour
1653
	if (
1654
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1655
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1656
		// utiliser le cookie est un pis aller qui marche 'en general'
1657
		// on blinde par un second test au moment de la lecture de la session
1658
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1659
		// et en ignorant ce cache en cas de recalcul explicite
1660
	!_request('var_mode')
1661
	) {
1662
		// on essaye de lire directement sans verrou pour aller plus vite
1663
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
1664
			// mais si semble corrompu on relit avec un verrou
1665
			if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1666
				lire_fichier(_CACHE_CHEMIN, $contenu);
1667
				if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1668
					$GLOBALS['path_files'] = array();
1669
				}
1670
			}
1671
		}
1672
	}
1673
}
1674
1675
function save_path_cache() {
1676
	if (defined('_SAUVER_CHEMIN')
1677
		and _SAUVER_CHEMIN
1678
	) {
1679
		ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
1680
	}
1681
}
1682
1683
1684
/**
1685
 * Trouve tous les fichiers du path correspondants à un pattern
1686
 *
1687
 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1688
 * par un `find_in_path()`
1689
 *
1690
 * @api
1691
 * @uses creer_chemin()
1692
 * @uses preg_files()
1693
 *
1694
 * @param string $dir
1695
 * @param string $pattern
1696
 * @param bool $recurs
1697
 * @return array
1698
 */
1699
function find_all_in_path($dir, $pattern, $recurs = false) {
1700
	$liste_fichiers = array();
1701
	$maxfiles = 10000;
1702
1703
	// cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1704
	// on a pas encore inclus flock.php
1705
	if (!function_exists('preg_files')) {
1706
		include_once _ROOT_RESTREINT . 'inc/flock.php';
1707
	}
1708
1709
	// Parcourir le chemin
1710
	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...
1711
		$f = $d . $dir;
1712
		if (@is_dir($f)) {
1713
			$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...
1714
			foreach ($liste as $chemin) {
1715
				$nom = basename($chemin);
1716
				// ne prendre que les fichiers pas deja trouves
1717
				// car find_in_path prend le premier qu'il trouve,
1718
				// les autres sont donc masques
1719
				if (!isset($liste_fichiers[$nom])) {
1720
					$liste_fichiers[$nom] = $chemin;
1721
				}
1722
			}
1723
		}
1724
	}
1725
1726
	return $liste_fichiers;
1727
}
1728
1729
/**
1730
 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1731
 * et beneficient d'une exception
1732
 *
1733
 * @param string $nom
1734
 * @param bool $strict
1735
 * @return bool
1736
 */
1737
function autoriser_sans_cookie($nom, $strict = false) {
1738
	static $autsanscookie = array('install', 'base_repair');
1739
1740
	if (in_array($nom, $autsanscookie)) {
1741
		if (test_espace_prive()){
1742
			include_spip('base/connect_sql');
1743
			if (!$strict or !spip_connect()){
1744
				return true;
1745
			}
1746
		}
1747
	}
1748
	return false;
1749
}
1750
1751
/**
1752
 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1753
 *
1754
 * @api
1755
 * @param string $id
1756
 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
1757
 * @param string $entite
1758
 *   surnom de la table SQL (donne acces au nom de cle primaire)
1759
 * @param string $args
1760
 *   query_string a placer apres cle=$id&....
1761
 * @param string $ancre
1762
 *   ancre a mettre a la fin de l'URL a produire
1763
 * @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...
1764
 *   produire l'URL publique ou privee (par defaut: selon espace)
1765
 *   si string : serveur de base de donnee (nom du connect)
1766
 * @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...
1767
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
1768
 * @return string|array
1769
 *   url codee ou fonction de decodage
1770
 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1771
 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1772
 */
1773
function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1774
	if ($public === null) {
1775
		$public = !test_espace_prive();
1776
	}
1777
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1778
1779
	if (!$public) {
1780
		if (!$entite) {
1781
			return '';
1782
		}
1783
		if (!function_exists('generer_url_ecrire_objet')) {
1784
			include_spip('inc/urls');
1785
		}
1786
		$res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1787
	} else {
1788
		if ($type === null) {
1789
			$type = (isset($GLOBALS['type_urls']))
1790
				? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1791
				: ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1792
					? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1793
		}
1794
1795
		$f = charger_fonction($type, 'urls', true);
1796
		// se rabattre sur les urls page si les urls perso non dispo
1797
		if (!$f) {
1798
			$f = charger_fonction('page', 'urls', true);
1799
		}
1800
1801
		// si $entite='', on veut la fonction de passage URL ==> id
1802
		// sinon on veut effectuer le passage id ==> URL
1803
		if (!$entite) {
1804
			return $f;
1805
		}
1806
1807
		// mais d'abord il faut tester le cas des urls sur une
1808
		// base distante
1809
		if (is_string($public)
1810
			and $g = charger_fonction('connect', 'urls', true)
1811
		) {
1812
			$f = $g;
1813
		}
1814
1815
		$res = $f(intval($id), $entite, $args, $ancre, $public);
1816
1817
	}
1818
	if ($res) {
1819
		return $res;
1820
	}
1821
	// Sinon c'est un raccourci ou compat SPIP < 2
1822
	if (!function_exists($f = 'generer_url_' . $entite)) {
1823
		if (!function_exists($f .= '_dist')) {
1824
			$f = '';
1825
		}
1826
	}
1827
	if ($f) {
1828
		$url = $f($id, $args, $ancre);
1829
		if (strlen($args)) {
1830
			$url .= strstr($url, '?')
1831
				? '&amp;' . $args
1832
				: '?' . $args;
1833
		}
1834
1835
		return $url;
1836
	}
1837
	// On a ete gentil mais la ....
1838
	spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1839
1840
	return '';
1841
}
1842
1843
function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1844
	$exec = objet_info($entite, 'url_edit');
1845
	$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 1844 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...
1846
	if (intval($id)) {
1847
		$url = parametre_url($url, id_table_objet($entite), $id);
1848
	} else {
1849
		$url = parametre_url($url, 'new', 'oui');
1850
	}
1851
	if ($ancre) {
1852
		$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...
1853
	}
1854
1855
	return $url;
1856
}
1857
1858
// https://code.spip.net/@urls_connect_dist
1859
function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1860
	include_spip('base/connect_sql');
1861
	$id_type = id_table_objet($entite, $public);
1862
1863
	return _DIR_RACINE . get_spip_script('./')
1864
	. "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1865
	. (!$args ? '' : "&$args")
1866
	. (!$ancre ? '' : "#$ancre");
1867
}
1868
1869
1870
/**
1871
 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1872
 *
1873
 * @param string $url
1874
 * @return string
1875
 */
1876
function urlencode_1738($url) {
1877
	if (preg_match(',[^\x00-\x7E],sS', $url)) {
1878
		$uri = '';
1879
		for ($i = 0; $i < strlen($url); $i++) {
1880
			if (ord($a = $url[$i]) > 127) {
1881
				$a = rawurlencode($a);
1882
			}
1883
			$uri .= $a;
1884
		}
1885
		$url = $uri;
1886
	}
1887
1888
	return quote_amp($url);
1889
}
1890
1891
// https://code.spip.net/@generer_url_entite_absolue
1892
function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1893
	if (!$connect) {
1894
		$connect = true;
1895
	}
1896
	$h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1897
	if (!preg_match(',^\w+:,', $h)) {
1898
		include_spip('inc/filtres_mini');
1899
		$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...
1900
	}
1901
1902
	return $h;
1903
}
1904
1905
1906
/**
1907
 * Tester qu'une variable d'environnement est active
1908
 *
1909
 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1910
 * variables d'environnement comme `$_SERVER['HTTPS']` ou `ini_get('display_errors')`
1911
 *
1912
 * @param string|bool $truc
1913
 *     La valeur de la variable d'environnement
1914
 * @return bool
1915
 *     true si la valeur est considérée active ; false sinon.
1916
 **/
1917
function test_valeur_serveur($truc) {
1918
	if (!$truc) {
1919
		return false;
1920
	}
1921
1922
	return (strtolower($truc) !== 'off');
1923
}
1924
1925
//
1926
// Fonctions de fabrication des URL des scripts de Spip
1927
//
1928
/**
1929
 * Calcule l'url de base du site
1930
 *
1931
 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1932
 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1933
 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1934
 *
1935
 * @note
1936
 *     La globale `$profondeur_url` doit être initialisée de manière à
1937
 *     indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1938
 *     racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1939
 *     la racine 0. Sur url/perso/ elle vaut 2
1940
 *
1941
 * @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...
1942
 *    - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1943
 *    - si int : indique que l'on veut l'url pour la profondeur indiquée
1944
 *    - si bool : retourne le tableau static complet
1945
 *    - si array : réinitialise le tableau static complet avec la valeur fournie
1946
 * @return string|array
1947
 */
1948
function url_de_base($profondeur = null) {
1949
1950
	static $url = array();
1951
	if (is_array($profondeur)) {
1952
		return $url = $profondeur;
1953
	}
1954
	if ($profondeur === false) {
1955
		return $url;
1956
	}
1957
1958
	if (is_null($profondeur)) {
1959
		$profondeur = $GLOBALS['profondeur_url'];
1960
	}
1961
1962
	if (isset($url[$profondeur])) {
1963
		return $url[$profondeur];
1964
	}
1965
1966
	$http = 'http';
1967
1968
	if (
1969
		isset($_SERVER["SCRIPT_URI"])
1970
		and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1971
	) {
1972
		$http = 'https';
1973
	} elseif (
1974
		isset($_SERVER['HTTPS'])
1975
		and test_valeur_serveur($_SERVER['HTTPS'])
1976
	) {
1977
		$http = 'https';
1978
	}
1979
1980
	// note : HTTP_HOST contient le :port si necessaire
1981
	$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
1982
	// si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1983
	if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1984
		$host = $GLOBALS['meta']['adresse_site'];
1985
		if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1986
			$http = $scheme;
1987
			$host = str_replace("{$scheme}://", '', $host);
1988
		}
1989
	}
1990
	if (isset($_SERVER['SERVER_PORT'])
1991
		and $port = $_SERVER['SERVER_PORT']
1992
		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...
1993
	) {
1994
		if (!defined('_PORT_HTTP_STANDARD')) {
1995
			define('_PORT_HTTP_STANDARD', '80');
1996
		}
1997
		if (!defined('_PORT_HTTPS_STANDARD')) {
1998
			define('_PORT_HTTPS_STANDARD', '443');
1999
		}
2000
		if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
2001
			$host .= ":$port";
2002
		}
2003
		if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
2004
			$host .= ":$port";
2005
		}
2006
	}
2007
2008 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...
2009
		if (isset($_SERVER['REQUEST_URI'])) {
2010
			$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2011
		} else {
2012
			$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
2013
			if (!empty($_SERVER['QUERY_STRING'])
2014
				and !strpos($_SERVER['REQUEST_URI'], '?')
2015
			) {
2016
				$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2017
			}
2018
		}
2019
	}
2020
2021
	$url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
2022
2023
	return $url[$profondeur];
2024
}
2025
2026
/**
2027
 * fonction testable de construction d'une url appelee par url_de_base()
2028
 *
2029
 * @param string $http
2030
 * @param string $host
2031
 * @param string $request
2032
 * @param int $prof
2033
 * @return string
2034
 */
2035
function url_de_($http, $host, $request, $prof = 0) {
2036
	$prof = max($prof, 0);
2037
2038
	$myself = ltrim($request, '/');
2039
	# supprimer la chaine de GET
2040
	list($myself) = explode('?', $myself);
2041
	// vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
2042
	// protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
2043 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...
2044
		$myself = explode('://',$myself);
2045
		array_shift($myself);
2046
		$myself = implode('://',$myself);
2047
		$myself = explode('/',$myself);
2048
		array_shift($myself);
2049
		$myself = implode('/',$myself);
2050
	}
2051
	$url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
2052
2053
	$url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
2054
2055
	return $url;
2056
}
2057
2058
2059
// Pour une redirection, la liste des arguments doit etre separee par "&"
2060
// Pour du code XHTML, ca doit etre &amp;
2061
// Bravo au W3C qui n'a pas ete capable de nous eviter ca
2062
// faute de separer proprement langage et meta-langage
2063
2064
// Attention, X?y=z et "X/?y=z" sont completement differents!
2065
// http://httpd.apache.org/docs/2.0/mod/mod_dir.html
2066
2067
/**
2068
 * Crée une URL vers un script de l'espace privé
2069
 *
2070
 * @example
2071
 *     ```
2072
 *     generer_url_ecrire('admin_plugin')
2073
 *     ```
2074
 *
2075
 * @param string $script
2076
 *     Nom de la page privée (xx dans exec=xx)
2077
 * @param string $args
2078
 *     Arguments à transmettre, tel que `arg1=yy&arg2=zz`
2079
 * @param bool $no_entities
2080
 *     Si false : transforme les `&` en `&amp;`
2081
 * @param bool|string $rel
2082
 *     URL relative ?
2083
 *
2084
 *     - false : l’URL sera complète et contiendra l’URL du site
2085
 *     - true : l’URL sera relavive.
2086
 *     - string : on transmet l'url à la fonction
2087
 * @return string URL
2088
 **/
2089
function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
2090
	if (!$rel) {
2091
		$rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
2092
	} else {
2093
		if (!is_string($rel)) {
2094
			$rel = _DIR_RESTREINT ? _DIR_RESTREINT :
2095
				('./' . _SPIP_ECRIRE_SCRIPT);
2096
		}
2097
	}
2098
2099
	list($script, $ancre) = array_pad(explode('#', $script), 2, null);
2100
	if ($script and ($script <> 'accueil' or $rel)) {
2101
		$args = "?exec=$script" . (!$args ? '' : "&$args");
2102
	} elseif ($args) {
2103
		$args = "?$args";
2104
	}
2105
	if ($ancre) {
2106
		$args .= "#$ancre";
2107
	}
2108
2109
	return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
2110
}
2111
2112
//
2113
// Adresse des scripts publics (a passer dans inc-urls...)
2114
//
2115
2116
2117
/**
2118
 * Retourne le nom du fichier d'exécution de SPIP
2119
 *
2120
 * @see _SPIP_SCRIPT
2121
 * @note
2122
 *   Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2123
 *   dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2124
 *
2125
 * @param string $default
2126
 *     Script par défaut
2127
 * @return string
2128
 *     Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2129
 **/
2130
function get_spip_script($default = '') {
2131
	# cas define('_SPIP_SCRIPT', '');
2132
	if (_SPIP_SCRIPT) {
2133
		return _SPIP_SCRIPT;
2134
	} else {
2135
		return $default;
2136
	}
2137
}
2138
2139
/**
2140
 * Crée une URL vers une page publique de SPIP
2141
 *
2142
 * @example
2143
 *     ```
2144
 *     generer_url_public("rubrique","id_rubrique=$id_rubrique")
2145
 *     ```
2146
 *
2147
 * @param string $script
2148
 *     Nom de la page
2149
 * @param string|array $args
2150
 *     Arguments à transmettre a l'URL,
2151
 *      soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2152
 *      soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2153
 * @param bool $no_entities
2154
 *     Si false : transforme les `&` en `&amp;`
2155
 * @param bool $rel
2156
 *     URL relative ?
2157
 *
2158
 *     - false : l’URL sera complète et contiendra l’URL du site
2159
 *     - true : l’URL sera relavive.
2160
 * @param string $action
2161
 *     - Fichier d'exécution public (spip.php par défaut)
2162
 * @return string URL
2163
 **/
2164
function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2165
	// si le script est une action (spip_pass, spip_inscription),
2166
	// standardiser vers la nouvelle API
2167
2168
	if (!$action) {
2169
		$action = get_spip_script();
2170
	}
2171
	if ($script) {
2172
		$action = parametre_url($action, _SPIP_PAGE, $script, '&');
2173
	}
2174
2175
	if ($args) {
2176
		if (is_array($args)) {
2177
			$r = '';
2178
			foreach ($args as $k => $v) {
2179
				$r .= '&' . $k . '=' . $v;
2180
			}
2181
			$args = substr($r, 1);
2182
		}
2183
		$action .=
2184
			(strpos($action, '?') !== false ? '&' : '?') . $args;
2185
	}
2186
	if (!$no_entities) {
2187
		$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...
2188
	}
2189
2190
	// ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2191
	return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2192
}
2193
2194
// https://code.spip.net/@generer_url_prive
2195
function generer_url_prive($script, $args = "", $no_entities = false) {
2196
2197
	return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2198
}
2199
2200
// Pour les formulaires en methode POST,
2201
// mettre le nom du script a la fois en input-hidden et dans le champ action:
2202
// 1) on peut ainsi memoriser le signet comme si c'etait un GET
2203
// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2204
2205
/**
2206
 * Retourne un formulaire (POST par défaut) vers un script exec
2207
 * de l’interface privée
2208
 *
2209
 * @param string $script
2210
 *     Nom de la page exec
2211
 * @param string $corps
2212
 *     Contenu du formulaire
2213
 * @param string $atts
2214
 *     Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2215
 * @param string $submit
2216
 *     Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2217
 * @return string
2218
 *     Code HTML du formulaire
2219
 **/
2220
function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2221
2222
	$script1 = explode('&', $script);
2223
	$script1 = reset($script1);
2224
2225
	return "<form action='"
2226
	. ($script ? generer_url_ecrire($script) : '')
2227
	. "' "
2228
	. ($atts ? $atts : " method='post'")
2229
	. "><div>\n"
2230
	. "<input type='hidden' name='exec' value='$script1' />"
2231
	. $corps
2232
	. (!$submit ? '' :
2233
		("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2234
	. "</div></form>\n";
2235
}
2236
2237
/**
2238
 * Générer un formulaire pour lancer une action vers $script
2239
 *
2240
 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2241
 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2242
 * qui ne sont pas destines a etre mis en signets
2243
 *
2244
 * @param string $script
2245
 * @param string $corps
2246
 * @param string $atts
2247
 * @param bool $public
2248
 * @return string
2249
 */
2250
function generer_form_action($script, $corps, $atts = '', $public = false) {
2251
	// si l'on est dans l'espace prive, on garde dans l'url
2252
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2253
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2254
	$h = (_DIR_RACINE and !$public)
2255
		? generer_url_ecrire(_request('exec'))
2256
		: generer_url_public();
2257
2258
	return "\n<form action='" .
2259
	$h .
2260
	"'" .
2261
	$atts .
2262
	">\n" .
2263
	"<div>" .
2264
	"\n<input type='hidden' name='action' value='$script' />" .
2265
	$corps .
2266
	"</div></form>";
2267
}
2268
2269
/**
2270
 * Créer une URL
2271
 *
2272
 * @param  string $script
2273
 *     Nom du script à exécuter
2274
 * @param  string $args
2275
 *     Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2276
 * @param bool $no_entities
2277
 *     Si false : transforme les & en &amp;
2278
 * @param boolean $public
2279
 *     URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2280
 *     true : l’URL sera relative.
2281
 * @return string
2282
 *     URL
2283
 */
2284
function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2285
	// si l'on est dans l'espace prive, on garde dans l'url
2286
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2287
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2288
	$url = (_DIR_RACINE and !$public)
2289
		? generer_url_ecrire(_request('exec'))
2290
		: generer_url_public('', '', false, false);
2291
	$url = parametre_url($url, 'action', $script);
2292
	if ($args) {
2293
		$url .= quote_amp('&' . $args);
2294
	}
2295
2296
	if ($no_entities) {
2297
		$url = str_replace('&amp;', '&', $url);
2298
	}
2299
2300
	return $url;
2301
}
2302
2303
2304
/**
2305
 * Fonction d'initialisation groupée pour compatibilité ascendante
2306
 *
2307
 * @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...
2308
 * @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...
2309
 * @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...
2310
 * @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...
2311
 */
2312
function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2313
	spip_initialisation_core($pi, $pa, $ti, $ta);
2314
	spip_initialisation_suite();
2315
}
2316
2317
/**
2318
 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2319
 *
2320
 * Elle définit les répertoires et fichiers non partageables
2321
 * et indique dans $test_dirs ceux devant être accessibles en écriture
2322
 * mais ne touche pas à cette variable si elle est déjà définie
2323
 * afin que mes_options.php puisse en spécifier d'autres.
2324
 *
2325
 * Elle définit ensuite les noms des fichiers et les droits.
2326
 * Puis simule un register_global=on sécurisé.
2327
 *
2328
 * @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...
2329
 * @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...
2330
 * @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...
2331
 * @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...
2332
 */
2333
function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2334
	static $too_late = 0;
2335
	if ($too_late++) {
2336
		return;
2337
	}
2338
2339
	// Declaration des repertoires
2340
2341
	// le nom du repertoire plugins/ activables/desactivables
2342
	if (!defined('_DIR_PLUGINS')) {
2343
		define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2344
	}
2345
2346
	// le nom du repertoire des extensions/ permanentes du core, toujours actives
2347
	if (!defined('_DIR_PLUGINS_DIST')) {
2348
		define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2349
	}
2350
2351
	// le nom du repertoire des librairies
2352
	if (!defined('_DIR_LIB')) {
2353
		define('_DIR_LIB', _DIR_RACINE . "lib/");
2354
	}
2355
2356
	if (!defined('_DIR_IMG')) {
2357
		define('_DIR_IMG', $pa);
2358
	}
2359
	if (!defined('_DIR_LOGOS')) {
2360
		define('_DIR_LOGOS', $pa);
2361
	}
2362
	if (!defined('_DIR_IMG_ICONES')) {
2363
		define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2364
	}
2365
2366
	if (!defined('_DIR_DUMP')) {
2367
		define('_DIR_DUMP', $ti . "dump/");
2368
	}
2369
	if (!defined('_DIR_SESSIONS')) {
2370
		define('_DIR_SESSIONS', $ti . "sessions/");
2371
	}
2372
	if (!defined('_DIR_TRANSFERT')) {
2373
		define('_DIR_TRANSFERT', $ti . "upload/");
2374
	}
2375
	if (!defined('_DIR_CACHE')) {
2376
		define('_DIR_CACHE', $ti . "cache/");
2377
	}
2378
	if (!defined('_DIR_CACHE_XML')) {
2379
		define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2380
	}
2381
	if (!defined('_DIR_SKELS')) {
2382
		define('_DIR_SKELS', _DIR_CACHE . "skel/");
2383
	}
2384
	if (!defined('_DIR_AIDE')) {
2385
		define('_DIR_AIDE', _DIR_CACHE . "aide/");
2386
	}
2387
	if (!defined('_DIR_TMP')) {
2388
		define('_DIR_TMP', $ti);
2389
	}
2390
2391
	if (!defined('_DIR_VAR')) {
2392
		define('_DIR_VAR', $ta);
2393
	}
2394
2395
	if (!defined('_DIR_ETC')) {
2396
		define('_DIR_ETC', $pi);
2397
	}
2398
	if (!defined('_DIR_CONNECT')) {
2399
		define('_DIR_CONNECT', $pi);
2400
	}
2401
	if (!defined('_DIR_CHMOD')) {
2402
		define('_DIR_CHMOD', $pi);
2403
	}
2404
2405
	if (!isset($GLOBALS['test_dirs']))
2406
		// Pas $pi car il est bon de le mettre hors ecriture apres intstall
2407
		// il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2408
	{
2409
		$GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2410
	}
2411
2412
	// Declaration des fichiers
2413
2414
	if (!defined('_CACHE_PLUGINS_PATH')) {
2415
		define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2416
	}
2417
	if (!defined('_CACHE_PLUGINS_OPT')) {
2418
		define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2419
	}
2420
	if (!defined('_CACHE_PLUGINS_FCT')) {
2421
		define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2422
	}
2423
	if (!defined('_CACHE_PIPELINES')) {
2424
		define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2425
	}
2426
	if (!defined('_CACHE_CHEMIN')) {
2427
		define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2428
	}
2429
2430
	# attention .php obligatoire pour ecrire_fichier_securise
2431
	if (!defined('_FILE_META')) {
2432
		define('_FILE_META', $ti . 'meta_cache.php');
2433
	}
2434
	if (!defined('_DIR_LOG')) {
2435
		define('_DIR_LOG', _DIR_TMP . 'log/');
2436
	}
2437
	if (!defined('_FILE_LOG')) {
2438
		define('_FILE_LOG', 'spip');
2439
	}
2440
	if (!defined('_FILE_LOG_SUFFIX')) {
2441
		define('_FILE_LOG_SUFFIX', '.log');
2442
	}
2443
2444
	// Le fichier de connexion a la base de donnees
2445
	// tient compte des anciennes versions (inc_connect...)
2446
	if (!defined('_FILE_CONNECT_INS')) {
2447
		define('_FILE_CONNECT_INS', 'connect');
2448
	}
2449
	if (!defined('_FILE_CONNECT')) {
2450
		define('_FILE_CONNECT',
2451
		(@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2452
			: (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2453
				: false)));
2454
	}
2455
2456
	// Le fichier de reglages des droits
2457
	if (!defined('_FILE_CHMOD_INS')) {
2458
		define('_FILE_CHMOD_INS', 'chmod');
2459
	}
2460
	if (!defined('_FILE_CHMOD')) {
2461
		define('_FILE_CHMOD',
2462
		(@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2463
			: false));
2464
	}
2465
2466
	if (!defined('_FILE_LDAP')) {
2467
		define('_FILE_LDAP', 'ldap.php');
2468
	}
2469
2470
	if (!defined('_FILE_TMP_SUFFIX')) {
2471
		define('_FILE_TMP_SUFFIX', '.tmp.php');
2472
	}
2473
	if (!defined('_FILE_CONNECT_TMP')) {
2474
		define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2475
	}
2476
	if (!defined('_FILE_CHMOD_TMP')) {
2477
		define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2478
	}
2479
2480
	// Definition des droits d'acces en ecriture
2481
	if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2482
		include_once _FILE_CHMOD;
2483
	}
2484
2485
	// Se mefier des fichiers mal remplis!
2486
	if (!defined('_SPIP_CHMOD')) {
2487
		define('_SPIP_CHMOD', 0777);
2488
	}
2489
2490
	if (!defined('_DEFAULT_CHARSET')) {
2491
		/** Le charset par défaut lors de l'installation */
2492
		define('_DEFAULT_CHARSET', 'utf-8');
2493
	}
2494
	if (!defined('_ROOT_PLUGINS')) {
2495
		define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2496
	}
2497
	if (!defined('_ROOT_PLUGINS_DIST')) {
2498
		define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2499
	}
2500
	if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2501
		define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2502
	}
2503
2504
	// La taille des Log
2505
	if (!defined('_MAX_LOG')) {
2506
		define('_MAX_LOG', 100);
2507
	}
2508
2509
	// Sommes-nous dans l'empire du Mal ?
2510
	// (ou sous le signe du Pingouin, ascendant GNU ?)
2511
	if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2512
		if (!defined('_OS_SERVEUR')) {
2513
			define('_OS_SERVEUR', 'windows');
2514
		}
2515
		if (!defined('_SPIP_LOCK_MODE')) {
2516
			define('_SPIP_LOCK_MODE', 1);
2517
		} // utiliser le flock php
2518
	} else {
2519
		if (!defined('_OS_SERVEUR')) {
2520
			define('_OS_SERVEUR', '');
2521
		}
2522
		if (!defined('_SPIP_LOCK_MODE')) {
2523
			define('_SPIP_LOCK_MODE', 1);
2524
		} // utiliser le flock php
2525
		#if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2526
	}
2527
2528
	// Langue par defaut
2529
	if (!defined('_LANGUE_PAR_DEFAUT')) {
2530
		define('_LANGUE_PAR_DEFAUT', 'fr');
2531
	}
2532
2533
	//
2534
	// Module de lecture/ecriture/suppression de fichiers utilisant flock()
2535
	// (non surchargeable en l'etat ; attention si on utilise include_spip()
2536
	// pour le rendre surchargeable, on va provoquer un reecriture
2537
	// systematique du noyau ou une baisse de perfs => a etudier)
2538
	include_once _ROOT_RESTREINT . 'inc/flock.php';
2539
2540
	// charger tout de suite le path et son cache
2541
	load_path_cache();
2542
2543
	// *********** traiter les variables ************
2544
2545
	//
2546
	// Securite
2547
	//
2548
2549
	// Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2550
	if (isset($_REQUEST['GLOBALS'])) {
2551
		die();
2552
	}
2553
	// nettoyer les magic quotes \' et les caracteres nuls %00
2554
	spip_desinfecte($_GET);
2555
	spip_desinfecte($_POST);
2556
	spip_desinfecte($_COOKIE);
2557
	spip_desinfecte($_REQUEST);
2558
2559
	// appliquer le cookie_prefix
2560
	if ($GLOBALS['cookie_prefix'] != 'spip') {
2561
		include_spip('inc/cookie');
2562
		recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2563
	}
2564
2565
	//
2566
	// Capacites php (en fonction de la version)
2567
	//
2568
	$GLOBALS['flag_ob'] = (function_exists("ob_start")
2569
		&& function_exists("ini_get")
2570
		&& !strstr(@ini_get('disable_functions'), 'ob_'));
2571
	$GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2572
	$GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
2573
	$GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2574
		(get_cfg_var('upload_max_filesize') > 0));
2575
2576
2577
	// Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2578 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...
2579
		$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2580
	} else {
2581
		$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
2582
		if (!empty($_SERVER['QUERY_STRING'])
2583
			and !strpos($_SERVER['REQUEST_URI'], '?')
2584
		) {
2585
			$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2586
		}
2587
	}
2588
2589
	// Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2590
	if (!defined('_RENOUVELLE_ALEA')) {
2591
		define('_RENOUVELLE_ALEA', 12 * 3600);
2592
	}
2593
	if (!defined('_DUREE_COOKIE_ADMIN')) {
2594
		define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2595
	}
2596
2597
	// charger les meta si possible et renouveller l'alea au besoin
2598
	// charge aussi effacer_meta et ecrire_meta
2599
	$inc_meta = charger_fonction('meta', 'inc');
2600
	$inc_meta();
2601
2602
	// nombre de repertoires depuis la racine
2603
	// on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2604
	// ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2605
	// le calcul est faux)
2606
	if (!_DIR_RESTREINT) {
2607
		$GLOBALS['profondeur_url'] = 1;
2608
	} else {
2609
		$uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
2610
		$uri_ref = $_SERVER["SCRIPT_NAME"];
2611
		if (!$uri_ref
2612
			// si on est appele avec un autre ti, on est sans doute en mutu
2613
			// si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2614
			// a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2615
			// s'en remettre a l'adresse du site. alea jacta est.
2616
			or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2617
		) {
2618
2619
			if (isset($GLOBALS['meta']['adresse_site'])) {
2620
				$uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2621
				$uri_ref = (isset($uri_ref['path']) ? $uri_ref['path'] : '') . '/';
2622
			} else {
2623
				$uri_ref = "";
2624
			}
2625
		}
2626
		if (!$uri or !$uri_ref) {
2627
			$GLOBALS['profondeur_url'] = 0;
2628
		} else {
2629
			$GLOBALS['profondeur_url'] = max(0,
2630
				substr_count($uri[0], '/')
2631
				- substr_count($uri_ref, '/'));
2632
		}
2633
	}
2634
	// s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2635
	if (_FILE_CONNECT) {
2636
		if (verifier_visiteur() == '0minirezo'
2637
			// si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2638
			and !isset($_COOKIE['spip_admin'])
2639
		) {
2640
			clear_path_cache();
2641
		}
2642
	}
2643
2644
}
2645
2646
/**
2647
 * Complements d'initialisation non critiques pouvant etre realises
2648
 * par les plugins
2649
 *
2650
 */
2651
function spip_initialisation_suite() {
2652
	static $too_late = 0;
2653
	if ($too_late++) {
2654
		return;
2655
	}
2656
2657
	// taille mini des login
2658
	if (!defined('_LOGIN_TROP_COURT')) {
2659
		define('_LOGIN_TROP_COURT', 4);
2660
	}
2661
2662
	// la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2663
	#if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2664
	#if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2665
	#if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2666
2667
	// la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2668
	#if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2669
	#if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2670
	#if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2671
	#if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2672
2673
	if (!defined('_PASS_LONGUEUR_MINI')) {
2674
		define('_PASS_LONGUEUR_MINI', 6);
2675
	}
2676
2677
2678
	// Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2679
	if (!defined('_IMG_QUALITE')) {
2680
		define('_IMG_QUALITE', 85);
2681
	} # valeur par defaut
2682
	if (!defined('_IMG_GD_QUALITE')) {
2683
		define('_IMG_GD_QUALITE', _IMG_QUALITE);
2684
	} # surcharge pour la lib GD
2685
	if (!defined('_IMG_CONVERT_QUALITE')) {
2686
		define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
2687
	} # surcharge pour imagick en ligne de commande
2688
	// Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2689
	if (!defined('_IMG_IMAGICK_QUALITE')) {
2690
		define('_IMG_IMAGICK_QUALITE', 75);
2691
	} # surcharge pour imagick en PHP
2692
2693
	if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2694
		define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2695
	} // poids en octet
2696
2697
	// qq chaines standard
2698
	if (!defined('_ACCESS_FILE_NAME')) {
2699
		define('_ACCESS_FILE_NAME', '.htaccess');
2700
	}
2701
	if (!defined('_AUTH_USER_FILE')) {
2702
		define('_AUTH_USER_FILE', '.htpasswd');
2703
	}
2704
	if (!defined('_SPIP_DUMP')) {
2705
		define('_SPIP_DUMP', 'dump@nom_site@@[email protected]');
2706
	}
2707
	if (!defined('_CACHE_RUBRIQUES')) {
2708
		/** Fichier cache pour le navigateur de rubrique du bandeau */
2709
		define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
2710
	}
2711
	if (!defined('_CACHE_RUBRIQUES_MAX')) {
2712
		/** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2713
		define('_CACHE_RUBRIQUES_MAX', 500);
2714
	}
2715
2716
	if (!defined('_EXTENSION_SQUELETTES')) {
2717
		define('_EXTENSION_SQUELETTES', 'html');
2718
	}
2719
2720
	if (!defined('_DOCTYPE_ECRIRE')) {
2721
		/** Définit le doctype de l’espace privé */
2722
		define('_DOCTYPE_ECRIRE', "<!DOCTYPE html>\n");
2723
	}
2724
	if (!defined('_DOCTYPE_AIDE')) {
2725
		/** Définit le doctype de l’aide en ligne */
2726
		define('_DOCTYPE_AIDE',
2727
		"<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2728
	}
2729
2730
	if (!defined('_SPIP_SCRIPT')) {
2731
		/** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2732
		 * le script de l'espace public, alias index.php */
2733
		define('_SPIP_SCRIPT', 'spip.php');
2734
	}
2735
	if (!defined('_SPIP_PAGE')) {
2736
		/** Argument page, personalisable en cas de conflit avec un autre script */
2737
		define('_SPIP_PAGE', 'page');
2738
	}
2739
2740
	// le script de l'espace prive
2741
	// Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2742
	// les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2743
	// meme pb sur thttpd cf. https://forum.spip.net/fr_184153.html
2744
	if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2745
		if (!empty($_SERVER['SERVER_SOFTWARE']) and preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE'])) {
2746
			define('_SPIP_ECRIRE_SCRIPT', 'index.php');
2747
		} else {
2748
			define('_SPIP_ECRIRE_SCRIPT', '');
2749
		}
2750
	}
2751
2752
2753
	if (!defined('_SPIP_AJAX')) {
2754
		define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2755
			? 1
2756
			: (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
2757
	}
2758
2759
	// La requete est-elle en ajax ?
2760
	if (!defined('_AJAX')) {
2761
		define('_AJAX',
2762
			(isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2763
				or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2764
				or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2765
				or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2766
			)
2767
			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
2768
		);
2769
	}
2770
2771
	# nombre de pixels maxi pour calcul de la vignette avec gd
2772
	# au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2773
	# les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2774
	if (!defined('_IMG_GD_MAX_PIXELS')) {
2775
		define('_IMG_GD_MAX_PIXELS',
2776
		(isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2777
			? $GLOBALS['meta']['max_taille_vignettes']
2778
			: 0);
2779
	}
2780
2781
	if (!defined('_MEMORY_LIMIT_MIN')) {
2782
		define('_MEMORY_LIMIT_MIN', 16);
2783
	} // en Mo
2784
	// si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2785
	// on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2786
	// il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2787
	if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
2788
		if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2789
			$unit = strtolower(substr($memory, -1));
2790
			$memory = substr($memory, 0, -1);
2791
			switch ($unit) {
2792
				// Le modifieur 'G' est disponible depuis PHP 5.1.0
2793
				case 'g':
2794
					$memory *= 1024;
2795
				case 'm':
2796
					$memory *= 1024;
2797
				case 'k':
2798
					$memory *= 1024;
2799
			}
2800
			if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
2801
				@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...
2802
				if (trim(ini_get('memory_limit')) != $m) {
2803
					if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2804
						define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2805
					} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2806
				}
2807
			}
2808
		} else {
2809
			if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2810
				define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2811
			}
2812
		} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2813
	}
2814
	// Protocoles a normaliser dans les chaines de langues
2815
	if (!defined('_PROTOCOLES_STD')) {
2816
		define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2817
	}
2818
2819
	init_var_mode();
2820
}
2821
2822
/**
2823
 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2824
 * la validité du cache ou certains affichages spéciaux.
2825
 *
2826
 * Le paramètre d'URL `var_mode` permet de
2827
 * modifier la pérennité du cache, recalculer des urls
2828
 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2829
 * d'afficher un écran de débug ou des traductions non réalisées.
2830
 *
2831
 * En fonction de ces paramètres dans l'URL appelante, on définit
2832
 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2833
 *
2834
 * Le paramètre `var_mode` accepte ces valeurs :
2835
 *
2836
 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2837
 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2838
 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2839
 * - `debug` :  modifie l'affichage activant le mode "debug"
2840
 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2841
 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2842
 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2843
 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2844
 *
2845
 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2846
 *
2847
 * @note
2848
 *     Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2849
 *     le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2850
 * `   var_mode` (calcul ou recalcul).
2851
 */
2852
function init_var_mode() {
2853
	static $done = false;
2854
	if (!$done) {
2855
2856
		if (isset($_GET['var_mode'])) {
2857
			$var_mode = explode(',', $_GET['var_mode']);
2858
			// tout le monde peut calcul/recalcul
2859
			if (!defined('_VAR_MODE')) {
2860
				if (in_array('recalcul', $var_mode)) {
2861
					define('_VAR_MODE', 'recalcul');
2862
				} elseif (in_array('calcul', $var_mode)) {
2863
					define('_VAR_MODE', 'calcul');
2864
				}
2865
			}
2866
			$var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2867
			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...
2868
				include_spip('inc/autoriser');
2869
				// autoriser preview si preview seulement, et sinon autoriser debug
2870
				if (autoriser(
2871
					($_GET['var_mode'] == 'preview')
2872
						? 'previsualiser'
2873
						: 'debug'
2874
				)) {
2875 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...
2876
						// forcer le calcul pour passer dans traduire
2877
						if (!defined('_VAR_MODE')) {
2878
							define('_VAR_MODE', 'calcul');
2879
						}
2880
						// et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2881
						if (!defined('_VAR_NOCACHE')) {
2882
							define('_VAR_NOCACHE', true);
2883
						}
2884
						$var_mode = array_diff($var_mode, array('traduction'));
2885
					}
2886 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...
2887
						// basculer sur les criteres de preview dans les boucles
2888
						if (!defined('_VAR_PREVIEW')) {
2889
							define('_VAR_PREVIEW', true);
2890
						}
2891
						// forcer le calcul
2892
						if (!defined('_VAR_MODE')) {
2893
							define('_VAR_MODE', 'calcul');
2894
						}
2895
						// et ne pas enregistrer de cache
2896
						if (!defined('_VAR_NOCACHE')) {
2897
							define('_VAR_NOCACHE', true);
2898
						}
2899
						$var_mode = array_diff($var_mode, array('preview'));
2900
					}
2901 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...
2902
						// forcer le compilo et ignorer les caches existants
2903
						if (!defined('_VAR_MODE')) {
2904
							define('_VAR_MODE', 'calcul');
2905
						}
2906
						if (!defined('_VAR_INCLURE')) {
2907
							define('_VAR_INCLURE', true);
2908
						}
2909
						// et ne pas enregistrer de cache
2910
						if (!defined('_VAR_NOCACHE')) {
2911
							define('_VAR_NOCACHE', true);
2912
						}
2913
						$var_mode = array_diff($var_mode, array('inclure'));
2914
					}
2915 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...
2916
						// forcer le compilo et ignorer les caches existants
2917
						if (!defined('_VAR_MODE')) {
2918
							define('_VAR_MODE', 'calcul');
2919
						}
2920
						if (!defined('_VAR_URLS')) {
2921
							define('_VAR_URLS', true);
2922
						}
2923
						$var_mode = array_diff($var_mode, array('urls'));
2924
					}
2925 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...
2926
						// forcer le compilo et ignorer les caches existants
2927
						if (!defined('_VAR_MODE')) {
2928
							define('_VAR_MODE', 'calcul');
2929
						}
2930
						// indiquer qu'on doit recalculer les images
2931
						if (!defined('_VAR_IMAGES')) {
2932
							define('_VAR_IMAGES', true);
2933
						}
2934
						$var_mode = array_diff($var_mode, array('images'));
2935
					}
2936 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...
2937
						if (!defined('_VAR_MODE')) {
2938
							define('_VAR_MODE', 'debug');
2939
						}
2940
						// et ne pas enregistrer de cache
2941
						if (!defined('_VAR_NOCACHE')) {
2942
							define('_VAR_NOCACHE', true);
2943
						}
2944
						$var_mode = array_diff($var_mode, array('debug'));
2945
					}
2946
					if (count($var_mode) and !defined('_VAR_MODE')) {
2947
						define('_VAR_MODE', reset($var_mode));
2948
					}
2949
					if (isset($GLOBALS['visiteur_session']['nom'])) {
2950
						spip_log($GLOBALS['visiteur_session']['nom']
2951
							. " " . _VAR_MODE);
2952
					}
2953
				} // pas autorise ?
2954
				else {
2955
					// si on n'est pas connecte on se redirige
2956
					if (!$GLOBALS['visiteur_session']) {
2957
						include_spip('inc/headers');
2958
						redirige_par_entete(generer_url_public('login',
2959
							'url=' . rawurlencode(
2960
								parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2961
							), true));
2962
					}
2963
					// sinon tant pis
2964
				}
2965
			}
2966
		}
2967
		if (!defined('_VAR_MODE')) {
2968
			/**
2969
			 * Indique le mode de calcul ou d'affichage de la page.
2970
			 * @see init_var_mode()
2971
			 */
2972
			define('_VAR_MODE', false);
2973
		}
2974
		$done = true;
2975
	}
2976
}
2977
2978
// Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2979
// supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2980
// la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2981
// https://code.spip.net/@spip_desinfecte
2982
function spip_desinfecte(&$t, $deep = true) {
2983
	foreach ($t as $key => $val) {
2984
		if (is_string($t[$key])) {
2985
			$t[$key] = str_replace(chr(0), '-', $t[$key]);
2986
		} // traiter aussi les "texte_plus" de article_edit
2987
		else {
2988
			if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2989
				spip_desinfecte($t[$key], $deep);
2990
			}
2991
		}
2992
	}
2993
}
2994
2995
//  retourne le statut du visiteur s'il s'annonce
2996
2997
// https://code.spip.net/@verifier_visiteur
2998
function verifier_visiteur() {
2999
	// Rq: pour que cette fonction marche depuis mes_options
3000
	// il faut forcer l'init si ce n'est fait
3001
	// mais on risque de perturber des plugins en initialisant trop tot
3002
	// certaines constantes
3003
	@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...
3004
		(_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
3005
		(_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
3006
		(_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
3007
		(_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
3008
	);
3009
3010
	// Demarrer une session NON AUTHENTIFIEE si on donne son nom
3011
	// dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
3012
	// Attention on separe bien session_nom et nom, pour eviter
3013
	// les melanges entre donnees SQL et variables plus aleatoires
3014
	$variables_session = array('session_nom', 'session_email');
3015
	foreach ($variables_session as $var) {
3016
		if (_request($var) !== null) {
3017
			$init = true;
3018
			break;
3019
		}
3020
	}
3021
	if (isset($init)) {
3022
		#@spip_initialisation_suite();
3023
		$session = charger_fonction('session', 'inc');
3024
		$session();
3025
		include_spip('inc/texte');
3026
		foreach ($variables_session as $var) {
3027
			if (($a = _request($var)) !== null) {
3028
				$GLOBALS['visiteur_session'][$var] = safehtml($a);
3029
			}
3030
		}
3031
		if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
3032
			$GLOBALS['visiteur_session']['id_auteur'] = 0;
3033
		}
3034
		$session($GLOBALS['visiteur_session']);
3035
3036
		return 0;
3037
	}
3038
3039
	$h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
3040
	if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
3041
3042
		$session = charger_fonction('session', 'inc');
3043
		if ($session()) {
3044
			return $GLOBALS['visiteur_session']['statut'];
3045
		}
3046
		if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
3047
			include_spip('inc/auth');
3048
			$h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
3049
		}
3050
		if ($h) {
3051
			$GLOBALS['visiteur_session'] = $h;
3052
3053
			return $GLOBALS['visiteur_session']['statut'];
3054
		}
3055
	}
3056
3057
	// au moins son navigateur nous dit la langue preferee de cet inconnu
3058
	include_spip('inc/lang');
3059
	utiliser_langue_visiteur();
3060
3061
	return false;
3062
}
3063
3064
3065
/**
3066
 * Sélectionne la langue donnée en argument et mémorise la courante
3067
 *
3068
 * Restaure l'ancienne langue si appellée sans argument.
3069
 *
3070
 * @note
3071
 *     On pourrait économiser l'empilement en cas de non changemnt
3072
 *     et lui faire retourner `False` pour prevenir l'appelant
3073
 *     Le noyau de Spip sait le faire, mais pour assurer la compatibilité
3074
 *     cette fonction retourne toujours non `False`
3075
 *
3076
 * @uses changer_langue()
3077
 * @param null|string $lang
3078
 *     - string : Langue à appliquer,
3079
 *     - null : Pour restituer la dernière langue mémorisée.
3080
 * @return string
3081
 *     - string Langue utilisée.
3082
 **/
3083
function lang_select($lang = null) {
3084
	static $pile_langues = array();
3085
	if (!function_exists('changer_langue')) {
3086
		include_spip('inc/lang');
3087
	}
3088
	if ($lang === null) {
3089
		$lang = array_pop($pile_langues);
3090
	} else {
3091
		array_push($pile_langues, $GLOBALS['spip_lang']);
3092
	}
3093
	if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
3094
		return $lang;
3095
	}
3096
	changer_langue($lang);
3097
3098
	return $lang;
3099
}
3100
3101
/**
3102
 * Renvoie une chaîne qui identifie la session courante
3103
 *
3104
 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
3105
 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
3106
 * de fichier cache.
3107
 *
3108
 * @pipeline_appel definir_session
3109
 *
3110
 * @param bool $force
3111
 * @return string
3112
 *     Identifiant de la session
3113
 **/
3114
function spip_session($force = false) {
3115
	static $session;
3116
	if ($force or !isset($session)) {
3117
		$s = pipeline('definir_session',
3118
			$GLOBALS['visiteur_session']
3119
				? serialize($GLOBALS['visiteur_session'])
3120
				. '_' . @$_COOKIE['spip_session']
3121
				: ''
3122
		);
3123
		$session = $s ? substr(md5($s), 0, 8) : '';
3124
	}
3125
3126
	#spip_log('session: '.$session);
3127
	return $session;
3128
}
3129
3130
3131
/**
3132
 * Retourne un lien vers une aide
3133
 *
3134
 * Aide, aussi depuis l'espace privé à présent.
3135
 * Surchargeable mais pas d'erreur fatale si indisponible.
3136
 *
3137
 * @param string $aide
3138
 *    Cle d'identification de l'aide desiree
3139
 * @param bool $distante
3140
 *    Generer une url locale (par defaut)
3141
 *    ou une url distante [directement sur spip.net]
3142
 * @return
3143
 *    Lien sur une icone d'aide
3144
 **/
3145
function aider($aide = '', $distante = false) {
3146
	$aider = charger_fonction('aide', 'inc', true);
3147
3148
	return $aider ? $aider($aide, '', array(), $distante) : '';
3149
}
3150
3151
/**
3152
 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3153
 *
3154
 * Si l’utiliseur est un webmestre.
3155
 */
3156
function exec_info_dist() {
3157
3158
	include_spip('inc/autoriser');
3159
	if (autoriser('phpinfos')) {
3160
		$cookies_masques = ['spip_session', 'PHPSESSID'];
3161
		$cookies_backup = [];
3162
		foreach ($cookies_masques as $k) {
3163
			if (!empty($_COOKIE[$k])) {
3164
				$cookies_backup[$k] = $_COOKIE[$k];
3165
				$_COOKIE[$k] = '******************************';
3166
			}
3167
		}
3168
		phpinfo();
3169
		foreach ($cookies_backup as $k => $v) {
3170
			$_COOKIE[$k] = $v;
3171
		}
3172
	} else {
3173
		include_spip('inc/filtres');
3174
		sinon_interdire_acces();
3175
	}
3176
}
3177
3178
/**
3179
 * Génère une erreur de squelette
3180
 *
3181
 * Génère une erreur de squelette qui sera bien visible par un
3182
 * administrateur authentifié lors d'une visite de la page en erreur
3183
 *
3184
 * @param bool|string|array $message
3185
 *     - Message d'erreur (string|array)
3186
 *     - false pour retourner le texte des messages d'erreurs
3187
 *     - vide pour afficher les messages d'erreurs
3188
 * @param string|array|object $lieu
3189
 *     Lieu d'origine de l'erreur
3190
 * @return null|string
3191
 *     - Rien dans la plupart des cas
3192
 *     - string si $message à false.
3193
 **/
3194
function erreur_squelette($message = '', $lieu = '') {
3195
	$debusquer = charger_fonction('debusquer', 'public');
3196
	if (is_array($lieu)) {
3197
		include_spip('public/compiler');
3198
		$lieu = reconstruire_contexte_compil($lieu);
3199
	}
3200
3201
	return $debusquer($message, $lieu);
3202
}
3203
3204
/**
3205
 * Calcule un squelette avec un contexte et retourne son contenu
3206
 *
3207
 * La fonction de base de SPIP : un squelette + un contexte => une page.
3208
 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3209
 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3210
 * $options permet de selectionner les options suivantes :
3211
 *
3212
 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3213
 * - raw  => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3214
 *          pour chaque $fond fourni.
3215
 *
3216
 * @api
3217
 * @param string /array $fond
3218
 *     - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3219
 *     - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3220
 * @param array $contexte
3221
 *     - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3222
 *     - La langue est transmise automatiquement (sauf option étoile).
3223
 * @param array $options
3224
 *     Options complémentaires :
3225
 *
3226
 *     - trim   : applique un trim sur le résultat (true par défaut)
3227
 *     - raw    : retourne un tableau d'information sur le squelette (false par défaut)
3228
 *     - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3229
 *                équivalent de INCLURE*
3230
 *     - ajax   : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3231
 * @param string $connect
3232
 *     Non du connecteur de bdd a utiliser
3233
 * @return string|array
3234
 *     - Contenu du squelette calculé
3235
 *     - ou tableau d'information sur le squelette.
3236
 */
3237
function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3238
	if (!function_exists('evaluer_fond')) {
3239
		include_spip('public/assembler');
3240
	}
3241
	// assurer la compat avec l'ancienne syntaxe
3242
	// (trim etait le 3eme argument, par defaut a true)
3243
	if (!is_array($options)) {
3244
		$options = array('trim' => $options);
3245
	}
3246
	if (!isset($options['trim'])) {
3247
		$options['trim'] = true;
3248
	}
3249
3250
	if (isset($contexte['connect'])) {
3251
		$connect = $contexte['connect'];
3252
		unset($contexte['connect']);
3253
	}
3254
3255
	$texte = "";
3256
	$pages = array();
3257
	$lang_select = '';
3258 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...
3259
		// Si on a inclus sans fixer le critere de lang, on prend la langue courante
3260
		if (!isset($contexte['lang'])) {
3261
			$contexte['lang'] = $GLOBALS['spip_lang'];
3262
		}
3263
3264
		if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3265
			$lang_select = lang_select($contexte['lang']);
3266
		}
3267
	}
3268
3269
	if (!isset($GLOBALS['_INC_PUBLIC'])) {
3270
		$GLOBALS['_INC_PUBLIC'] = 0;
3271
	}
3272
3273
	$GLOBALS['_INC_PUBLIC']++;
3274
3275
	// fix #4235
3276
	$cache_utilise_session_appelant	= (isset($GLOBALS['cache_utilise_session']) ? $GLOBALS['cache_utilise_session'] : null);
3277
3278
3279
	foreach (is_array($fond) ? $fond : array($fond) as $f) {
3280
		
3281
		unset($GLOBALS['cache_utilise_session']);	// fix #4235
3282
3283
		$page = evaluer_fond($f, $contexte, $connect);
3284
		if ($page === '') {
3285
			$c = isset($options['compil']) ? $options['compil'] : '';
3286
			$a = array('fichier' => $f);
3287
			$erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3288
			erreur_squelette($erreur, $c);
3289
			// eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3290
			$page = array('texte' => '', 'erreur' => $erreur);
3291
		}
3292
3293
		$page = pipeline('recuperer_fond', array(
3294
			'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3295
			'data' => $page
3296
		));
3297
		if (isset($options['ajax']) and $options['ajax']) {
3298
			if (!function_exists('encoder_contexte_ajax')) {
3299
				include_spip('inc/filtres');
3300
			}
3301
			$page['texte'] = encoder_contexte_ajax(
3302
				array_merge(
3303
					$contexte,
3304
					array('fond' => $f),
3305
					($connect ? array('connect' => $connect) : array())
3306
				),
3307
				'',
3308
				$page['texte'],
3309
				$options['ajax']
3310
			);
3311
		}
3312
3313
		if (isset($options['raw']) and $options['raw']) {
3314
			$pages[] = $page;
3315
		} else {
3316
			$texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
3317
		}
3318
		
3319
		// contamination de la session appelante, pour les inclusions statiques
3320
		if (isset($page['invalideurs']['session'])){
3321
			$cache_utilise_session_appelant = $page['invalideurs']['session'];
3322
		}
3323
	}
3324
3325
	// restaurer le sessionnement du contexte appelant, 
3326
	// éventuellement contaminé si on vient de récupérer une inclusion statique sessionnée
3327
	if (isset($cache_utilise_session_appelant)) {
3328
		$GLOBALS['cache_utilise_session'] = $cache_utilise_session_appelant;
3329
	}
3330
3331
	$GLOBALS['_INC_PUBLIC']--;
3332
3333
	if ($lang_select) {
3334
		lang_select();
3335
	}
3336
	if (isset($options['raw']) and $options['raw']) {
3337
		return is_array($fond) ? $pages : reset($pages);
3338
	} else {
3339
		return $options['trim'] ? ltrim($texte) : $texte;
3340
	}
3341
}
3342
3343
/**
3344
 * Trouve un squelette dans le repertoire modeles/
3345
 *
3346
 * @param  $nom
3347
 * @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...
3348
 */
3349
function trouve_modele($nom) {
3350
	return trouver_fond($nom, 'modeles/');
3351
}
3352
3353
/**
3354
 * Trouver un squelette dans le chemin
3355
 * on peut specifier un sous-dossier dans $dir
3356
 * si $pathinfo est a true, retourne un tableau avec
3357
 * les composantes du fichier trouve
3358
 * + le chemin complet sans son extension dans fond
3359
 *
3360
 * @param string $nom
3361
 * @param string $dir
3362
 * @param bool $pathinfo
3363
 * @return array|string
3364
 */
3365
function trouver_fond($nom, $dir = '', $pathinfo = false) {
3366
	$f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
3367
	if (!$pathinfo) {
3368
		return $f;
3369
	}
3370
	// renvoyer un tableau detaille si $pathinfo==true
3371
	$p = pathinfo($f);
3372
	if (!isset($p['extension']) or !$p['extension']) {
3373
		$p['extension'] = _EXTENSION_SQUELETTES;
3374
	}
3375
	if (!isset($p['extension']) or !$p['filename']) {
3376
		$p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3377
	}
3378
	$p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
3379
3380
	return $p;
3381
}
3382
3383
/**
3384
 * Teste, pour un nom de page de l'espace privé, s'il est possible
3385
 * de générer son contenu.
3386
 *
3387
 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3388
 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3389
 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3390
 *
3391
 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3392
 *          ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3393
 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3394
 *
3395
 * @param string $nom
3396
 *     Nom de la page
3397
 * @return string
3398
 *     Nom de l'exec, sinon chaîne vide.
3399
 **/
3400
function tester_url_ecrire($nom) {
3401
	static $exec = array();
3402
	if (isset($exec[$nom])) {
3403
		return $exec[$nom];
3404
	}
3405
	// tester si c'est une page en squelette
3406
	if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3407
		return $exec[$nom] = 'fond';
3408
	} // compat skels orthogonaux version precedente
3409
	elseif (trouver_fond($nom, 'prive/exec/')) {
3410
		return $exec[$nom] = 'fond_monobloc';
3411
	} // echafaudage d'un fond !
3412
	elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3413
		return $exec[$nom] = 'fond';
3414
	}
3415
	// attention, il ne faut pas inclure l'exec ici
3416
	// car sinon #URL_ECRIRE provoque des inclusions
3417
	// et des define intrusifs potentiels
3418
	return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
3419
}
3420
3421
3422
/**
3423
 * Teste la présence d’une extension PHP
3424
 *
3425
 * @deprected Utiliser directement la fonction native `extension_loaded($module)`
3426
 * @example
3427
 *     ```
3428
 *     $ok = charger_php_extension('sqlite');
3429
 *     ```
3430
 * @param string $module Nom du module à charger
3431
 * @return bool true si le module est chargé
3432
 **/
3433
function charger_php_extension($module) {
3434
	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...
3435
		return true;
3436
	}
3437
	return false;
3438
}
3439
3440
3441
/**
3442
 * Indique si le code HTML5 est permis sur le site public
3443
 *
3444
 * @return bool
3445
 *     true si et seulement si la configuration autorise le code HTML5 sur le site public
3446
 **/
3447
function html5_permis() {
3448
	return (isset($GLOBALS['meta']['version_html_max'])
3449
		and ('html5' == $GLOBALS['meta']['version_html_max']));
3450
}
3451
3452
/**
3453
 * Lister les formats image acceptes par les lib et fonctions images
3454
 * @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...
3455
 * @param bool $svg_allowed
3456
 * @return array
3457
 */
3458
function formats_image_acceptables($gd = null, $svg_allowed = true) {
3459
	$formats = null;
3460
	if (!is_null($gd)) {
3461
		$config = ($gd ? "gd_formats" : "formats_graphiques");
3462
		if (isset($GLOBALS['meta'][$config])) {
3463
			$formats = $GLOBALS['meta'][$config];
3464
			$formats = explode(',', $formats);
3465
			$formats = array_filter($formats);
3466
			$formats = array_map('trim', $formats);
3467
		}
3468
	}
3469
	if (is_null($formats)) {
3470
		include_spip('inc/filtres_images_lib_mini');
3471
		$formats = _image_extensions_acceptees_en_entree();
3472
	}
3473
3474
	if ($svg_allowed) {
3475
		if (!in_array('svg', $formats)) {
3476
			$formats[] = 'svg';
3477
		}
3478
	}
3479
	else {
3480
		$formats = array_diff($formats, ['svg']);
3481
	}
3482
	return $formats;
3483
}
3484
3485
/**
3486
 * Extension de la fonction getimagesize pour supporter aussi les images SVG
3487
 * @param string $fichier
3488
 * @return array|bool
3489
 */
3490
function spip_getimagesize($fichier) {
3491
	if (!$imagesize = @getimagesize($fichier)) {
3492
3493
		include_spip("inc/svg");
3494
		if ($attrs = svg_lire_attributs($fichier)) {
3495
			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...
3496
			$imagesize = [
3497
				$width,
3498
				$height,
3499
				IMAGETYPE_SVG,
3500
				"width=\"{$width}\" height=\"{$height}\"",
3501
				"mime" => "image/svg+xml"
3502
			];
3503
		}
3504
	}
3505
	return $imagesize;
3506
}
3507
3508
3509
3510
/*
3511
 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3512
 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3513
 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3514
 */
3515
3516
/**
3517
 * lire_meta : fonction dépréciée
3518
 *
3519
 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3520
 * @see lire_config()
3521
 * @param string $nom Clé de meta à lire
3522
 * @return mixed Valeur de la meta.
3523
 **/
3524
function lire_meta($nom) {
3525
	return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
3526
}
3527
3528
3529
/**
3530
 * ecrire_metas : fonction dépréciée
3531
 *
3532
 * @deprecated
3533
 **/
3534
function ecrire_metas() { }
3535
3536
/**
3537
 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3538
 * au prochain passage dans l'espace prive
3539
 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3540
 * les alertes affichees une fois sont effacees
3541
 *
3542
 * @param string $nom
3543
 * @param string $message
3544
 * @param string $statut
3545
 */
3546
function avertir_auteurs($nom, $message, $statut = '') {
3547
	$alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3548
	if (!$alertes
3549
		or !is_array($alertes = unserialize($alertes))
3550
	) {
3551
		$alertes = array();
3552
	}
3553
3554
	if (!isset($alertes[$statut])) {
3555
		$alertes[$statut] = array();
3556
	}
3557
	$alertes[$statut][$nom] = $message;
3558
	ecrire_meta("message_alertes_auteurs", serialize($alertes));
3559
}
3560
3561
if (PHP_VERSION_ID < 50500) {
3562
	if (!function_exists('array_column')) {
3563
		/**
3564
		 * Returns the values from a single column of the input array, identified by
3565
		 * the $columnKey.
3566
		 *
3567
		 * Optionally, you may provide an $indexKey to index the values in the returned
3568
		 * array by the values from the $indexKey column in the input array.
3569
		 *
3570
		 * @link http://php.net/manual/fr/function.array-column.php
3571
		 * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
3572
		 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
3573
		 * @license http://opensource.org/licenses/MIT MIT
3574
		 *
3575
		 * @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...
3576
		 *                     a column of values.
3577
		 * @param mixed $columnKey The column of values to return. This value may be the
3578
		 *                         integer key of the column you wish to retrieve, or it
3579
		 *                         may be the string key name for an associative array.
3580
		 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
3581
		 *                        the returned array. This value may be the integer key
3582
		 *                        of the column, or it may be the string key name.
3583
		 * @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...
3584
		 */
3585
		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...
3586
		{
3587
			// Using func_get_args() in order to check for proper number of
3588
			// parameters and trigger errors exactly as the built-in array_column()
3589
			// does in PHP 5.5.
3590
			$argc = func_num_args();
3591
			$params = func_get_args();
3592
3593
			if ($argc < 2) {
3594
				trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
3595
				return null;
3596
			}
3597
3598
			if (!is_array($params[0])) {
3599
				trigger_error(
3600
					'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
3601
					E_USER_WARNING
3602
				);
3603
				return null;
3604
			}
3605
3606 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...
3607
				&& !is_float($params[1])
3608
				&& !is_string($params[1])
3609
				&& $params[1] !== null
3610
				&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
3611
			) {
3612
				trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
3613
				return false;
3614
			}
3615
3616 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...
3617
				&& !is_int($params[2])
3618
				&& !is_float($params[2])
3619
				&& !is_string($params[2])
3620
				&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
3621
			) {
3622
				trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
3623
				return false;
3624
			}
3625
3626
			$paramsInput = $params[0];
3627
			$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
3628
3629
			$paramsIndexKey = null;
3630
			if (isset($params[2])) {
3631
				if (is_float($params[2]) || is_int($params[2])) {
3632
					$paramsIndexKey = (int) $params[2];
3633
				} else {
3634
					$paramsIndexKey = (string) $params[2];
3635
				}
3636
			}
3637
3638
			$resultArray = array();
3639
3640
			foreach ($paramsInput as $row) {
3641
				$key = $value = null;
3642
				$keySet = $valueSet = false;
3643
3644
				if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
3645
					$keySet = true;
3646
					$key = (string) $row[$paramsIndexKey];
3647
				}
3648
3649
				if ($paramsColumnKey === null) {
3650
					$valueSet = true;
3651
					$value = $row;
3652
				} elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
3653
					$valueSet = true;
3654
					$value = $row[$paramsColumnKey];
3655
				}
3656
3657
				if ($valueSet) {
3658
					if ($keySet) {
3659
						$resultArray[$key] = $value;
3660
					} else {
3661
						$resultArray[] = $value;
3662
					}
3663
				}
3664
3665
			}
3666
3667
			return $resultArray;
3668
		}
3669
3670
	}
3671
}
3672
3673
/**
3674
 * Nettoie une chaine pour servir comme classes CSS.
3675
 *
3676
 * @note
3677
 *     les classes CSS acceptent théoriquement tous les caractères sauf NUL.
3678
 *     Ici, on limite (enlève) les caractères autres qu’alphanumérique, espace, - + _ @
3679
 *
3680
 * @param string|string[] $classes
3681
 * @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...
3682
 */
3683
function spip_sanitize_classname($classes) {
3684
	if (is_array($classes)) {
3685
		return array_map('spip_sanitize_classname', $classes);
3686
	}
3687
	return preg_replace("/[^ 0-9a-z_\-+@]/i", "", $classes);
3688
}
3689