Completed
Push — master ( 8b905c...fce320 )
by cam
04:38
created

utils.php ➔ spip_version_compare()   B

Complexity

Conditions 10
Paths 44

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
nc 44
nop 3
dl 0
loc 30
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

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