Completed
Push — master ( c4957a...984033 )
by cam
05:10 queued 11s
created

utils.php ➔ spip_getimagesize()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

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

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

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

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

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

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

Loading history...
317
 *
318
 *     - int indique le niveau de log, tel que `_LOG_DEBUG`
319
 *     - string indique le type de log
320
 *     - `string.int` indique les 2 éléments.
321
 *     Cette dernière notation est controversée mais le 3ème
322
 *     paramètre est planté pour cause de compatibilité ascendante.
323
 */
324
function spip_log($message = null, $name = null) {
325
	static $pre = array();
326
	static $log;
327
	preg_match('/^([a-z_]*)\.?(\d)?$/iS', (string)$name, $regs);
328
	if (!isset($regs[1]) or !$logname = $regs[1]) {
329
		$logname = null;
330
	}
331
	if (!isset($regs[2]) or !$niveau = $regs[2]) {
332
		$niveau = _LOG_INFO;
333
	}
334
335
	if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE : _LOG_INFO_IMPORTANTE)) {
336
		if (!$pre) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pre of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

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

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

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

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

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

Loading history...
427
 *     - array $c complété si un $c est transmis,
428
 *     - false sinon
429
 **/
430
function set_request($var, $val = null, $c = false) {
431
	if (is_array($c)) {
432
		unset($c[$var]);
433
		if ($val !== null) {
434
			$c[$var] = $val;
435
		}
436
437
		return $c;
438
	}
439
440
	unset($_GET[$var]);
441
	unset($_POST[$var]);
442
	if ($val !== null) {
443
		$_GET[$var] = $val;
444
	}
445
446
	return false; # n'affecte pas $c
447
}
448
449
450
/**
451
 * Tester si une URL est absolue
452
 * 
453
 * On est sur le web, on exclut certains protocoles, 
454
 * notamment 'file://', 'php://' et d'autres…
455
456
 * @param string $url
457
 * @return bool
458
 */
459
function tester_url_absolue($url) {
460
	$url = trim($url);
461
	if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
462
		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...
463
			isset($m[1])
464
			and $p = strtolower(rtrim($m[1], ':'))
465
			and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
466
		  ) {
467
			return false;
468
		}
469
		return true;
470
	}
471
	return false;
472
}
473
474
/**
475
 * Prend une URL et lui ajoute/retire un paramètre
476
 *
477
 * @filtre
478
 * @link http://www.spip.net/4255
479
 * @example
480
 *     ```
481
 *     [(#SELF|parametre_url{suite,18})] (ajout)
482
 *     [(#SELF|parametre_url{suite,''})] (supprime)
483
 *     [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
484
 *     ```
485
 *
486
 * @param string $url URL
487
 * @param string $c Nom du paramètre
488
 * @param string|array|null $v Valeur du paramètre
489
 * @param string $sep Séparateur entre les paramètres
490
 * @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...
491
 */
492
function parametre_url($url, $c, $v = null, $sep = '&amp;') {
493
	// requete erronnee : plusieurs variable dans $c et aucun $v
494
	if (strpos($c, "|") !== false and is_null($v)) {
495
		return null;
496
	}
497
498
	// lever l'#ancre
499
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
500
		$url = $r[1];
501
		$ancre = $r[2];
502
	} else {
503
		$ancre = '';
504
	}
505
506
	// eclater
507
	$url = preg_split(',[?]|&amp;|&,', $url);
508
509
	// recuperer la base
510
	$a = array_shift($url);
511
	if (!$a) {
512
		$a = './';
513
	}
514
515
	$regexp = ',^(' . str_replace('[]', '\[\]', $c) . '[[]?[]]?)(=.*)?$,';
516
	$ajouts = array_flip(explode('|', $c));
517
	$u = is_array($v) ? $v : rawurlencode($v);
518
	$testv = (is_array($v) ? count($v) : strlen($v));
519
	$v_read = null;
520
	// lire les variables et agir
521
	foreach ($url as $n => $val) {
522
		if (preg_match($regexp, urldecode($val), $r)) {
523
			$r = array_pad($r, 3, null);
524
			if ($v === null) {
525
				// c'est un tableau, on memorise les valeurs
526
				if (substr($r[1], -2) == "[]") {
527
					if (!$v_read) {
528
						$v_read = array();
529
					}
530
					$v_read[] = $r[2] ? substr($r[2], 1) : '';
531
				} // c'est un scalaire, on retourne direct
532
				else {
533
					return $r[2] ? substr($r[2], 1) : '';
534
				}
535
			} // suppression
536
			elseif (!$testv) {
537
				unset($url[$n]);
538
			}
539
			// Ajout. Pour une variable, remplacer au meme endroit,
540
			// pour un tableau ce sera fait dans la prochaine boucle
541
			elseif (substr($r[1], -2) != '[]') {
542
				$url[$n] = $r[1] . '=' . $u;
543
				unset($ajouts[$r[1]]);
544
			}
545
			// Pour les tableaux on laisse tomber les valeurs de
546
			// départ, on remplira à l'étape suivante
547
			else {
548
				unset($url[$n]);
549
			}
550
		}
551
	}
552
553
	// traiter les parametres pas encore trouves
554
	if ($v === null
555
		and $args = func_get_args()
556
		and count($args) == 2
557
	) {
558
		return $v_read; // rien trouve ou un tableau
559
	} elseif ($testv) {
560
		foreach ($ajouts as $k => $n) {
561
			if (!is_array($v)) {
562
				$url[] = $k . '=' . $u;
563
			} else {
564
				$id = (substr($k, -2) == '[]') ? $k : ($k . "[]");
565
				foreach ($v as $w) {
566
					$url[] = $id . '=' . (is_array($w) ? 'Array' : $w);
567
				}
568
			}
569
		}
570
	}
571
572
	// eliminer les vides
573
	$url = array_filter($url);
574
575
	// recomposer l'adresse
576
	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...
577
		$a .= '?' . join($sep, $url);
578
	}
579
580
	return $a . $ancre;
581
}
582
583
/**
584
 * Ajoute (ou retire) une ancre sur une URL
585
 *
586
 * L’ancre est nettoyée : on translitère, vire les non alphanum du début,
587
 * et on remplace ceux à l'interieur ou au bout par `-`
588
 *
589
 * @example
590
 *     - `$url = ancre_url($url, 'navigation'); // => mettra l’ancre #navigation
591
 *     - `$url = ancre_url($url, ''); // => enlèvera une éventuelle ancre
592
 * @uses translitteration()
593
 * @param string $url
594
 * @param string $ancre
595
 * @return string
596
 */
597
function ancre_url($url, $ancre) {
598
	// lever l'#ancre
599
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
600
		$url = $r[1];
601
	}
602
	if (preg_match('/[^-_a-zA-Z0-9]+/S', $ancre)) {
603
		if (!function_exists('translitteration')) {
604
			include_spip('inc/charsets');
605
		}
606
		$ancre = preg_replace(
607
			array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'),
608
			array('', '-'),
609
			translitteration($ancre)
610
		);
611
	}
612
	return $url . (strlen($ancre) ? '#' . $ancre : '');
613
}
614
615
/**
616
 * Pour le nom du cache, les `types_urls` et `self`
617
 *
618
 * @param string|null $reset
619
 * @return string
620
 */
621
function nettoyer_uri($reset = null) {
622
	static $done = false;
623
	static $propre = '';
624
	if (!is_null($reset)) {
625
		return $propre = $reset;
626
	}
627
	if ($done) {
628
		return $propre;
629
	}
630
	$done = true;
631
	return $propre = nettoyer_uri_var($GLOBALS['REQUEST_URI']);
632
}
633
634
/**
635
 * Nettoie une request_uri des paramètres var_xxx
636
 * 
637
 * Attention, la regexp doit suivre _CONTEXTE_IGNORE_VARIABLES défini au début de public/assembler.php
638
 * 
639
 * @param $request_uri
640
 * @return string
641
 */
642
function nettoyer_uri_var($request_uri) {
643
	$uri1 = $request_uri;
644
	do {
645
		$uri = $uri1;
646
		$uri1 = preg_replace(',([?&])(var_[^=&]*|PHPSESSID|fbclid|utm_[^=&]*)=[^&]*(&|$),i',
647
			'\1', $uri);
648
	} while ($uri <> $uri1);
649
	return preg_replace(',[?&]$,', '', $uri1);
650
}
651
652
653
/**
654
 * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
655
 *
656
 * @param string $amp
657
 *    Style des esperluettes
658
 * @param bool $root
659
 * @return string
660
 *    URL vers soi-même
661
 **/
662
function self($amp = '&amp;', $root = false) {
663
	$url = nettoyer_uri();
664
	if (!$root
665
		and (
666
			// si pas de profondeur on peut tronquer
667
			$GLOBALS['profondeur_url'] < (_DIR_RESTREINT ? 1 : 2)
668
			// sinon c'est OK si _SET_HTML_BASE a ete force a false
669
			or (defined('_SET_HTML_BASE') and !_SET_HTML_BASE))
670
	) {
671
		$url = preg_replace(',^[^?]*/,', '', $url);
672
	}
673
	// ajouter le cas echeant les variables _POST['id_...']
674
	foreach ($_POST as $v => $c) {
675
		if (substr($v, 0, 3) == 'id_') {
676
			$url = parametre_url($url, $v, $c, '&');
677
		}
678
	}
679
680
	// supprimer les variables sans interet
681
	if (test_espace_prive()) {
682
		$url = preg_replace(',([?&])('
683
			. 'lang|show_docs|'
684
			. 'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
685
		$url = preg_replace(',([?&])[&]+,', '\1', $url);
686
		$url = preg_replace(',[&]$,', '\1', $url);
687
	}
688
689
	// eviter les hacks
690
	include_spip('inc/filtres_mini');
691
	$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...
692
	
693
	$url = str_replace(array("'", '"', '<', '[', ']', ':'), array('%27', '%22', '%3C', '%5B', '%5D', '%3A'), $url);
694
695
	// &amp; ?
696
	if ($amp != '&amp;') {
697
		$url = str_replace('&amp;', $amp, $url);
698
	}
699
700
	// Si ca demarre par ? ou vide, donner './'
701
	$url = preg_replace(',^([?].*)?$,', './\1', $url);
702
703
	return $url;
704
}
705
706
707
/**
708
 * Indique si on est dans l'espace prive
709
 *
710
 * @return bool
711
 *     true si c'est le cas, false sinon.
712
 */
713
function test_espace_prive() {
714
	return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
715
}
716
717
/**
718
 * Vérifie la présence d'un plugin actif, identifié par son préfixe
719
 *
720
 * @param string $plugin
721
 * @return bool
722
 */
723
function test_plugin_actif($plugin) {
724
	return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ? true : false;
725
}
726
727
/**
728
 * Traduction des textes de SPIP
729
 *
730
 * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
731
 *
732
 * @api
733
 * @uses inc_traduire_dist()
734
 * @uses _L()
735
 * @example
736
 *     ```
737
 *     _T('bouton_enregistrer')
738
 *     _T('medias:image_tourner_droite')
739
 *     _T('medias:erreurs', array('nb'=>3))
740
 *     _T("email_sujet", array('spip_lang'=>$lang_usager))
741
 *     ```
742
 *
743
 * @param string $texte
744
 *     Clé de traduction
745
 * @param array $args
746
 *     Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
747
 * @param array $options
748
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
749
 *     - bool force : forcer un retour meme si la chaine n'a pas de traduction
750
 *     - bool sanitize : nettoyer le html suspect dans les arguments
751
 * @return string
752
 *     Texte
753
 */
754
function _T($texte, $args = array(), $options = array()) {
755
	static $traduire = false;
756
	$o = array('class' => '', 'force' => true, 'sanitize' => true);
757
	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...
758
		// support de l'ancien argument $class
759
		if (is_string($options)) {
760
			$options = array('class' => $options);
761
		}
762
		$o = array_merge($o, $options);
763
	}
764
765
	if (!$traduire) {
766
		$traduire = charger_fonction('traduire', 'inc');
767
		include_spip('inc/lang');
768
	}
769
770
	// On peut passer explicitement la langue dans le tableau
771
	// On utilise le même nom de variable que la globale
772 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...
773
		$lang = $args['spip_lang'];
774
		// On l'enleve pour ne pas le passer au remplacement
775
		unset($args['spip_lang']);
776
	} // Sinon on prend la langue du contexte
777
	else {
778
		$lang = $GLOBALS['spip_lang'];
779
	}
780
	$text = $traduire($texte, $lang);
781
782
	if (!strlen($text)) {
783
		if (!$o['force']) {
784
			return '';
785
		}
786
787
		$text = $texte;
788
789
		// pour les chaines non traduites, assurer un service minimum
790
		if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
791
			$text = str_replace('_', ' ',
792
				(($n = strpos($text, ':')) === false ? $texte :
793
					substr($texte, $n + 1)));
794
		}
795
		$o['class'] = null;
796
797
	}
798
799
	return _L($text, $args, $o);
800
801
}
802
803
804
/**
805
 * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
806
 *
807
 * Cette fonction est également appelée dans le code source de SPIP quand une
808
 * chaîne n'est pas encore dans les fichiers de langue.
809
 *
810
 * @see _T()
811
 * @example
812
 *     ```
813
 *     _L('Texte avec @nb@ ...', array('nb'=>3)
814
 *     ```
815
 *
816
 * @param string $text
817
 *     Texte
818
 * @param array $args
819
 *     Couples (variable => valeur) à transformer dans le texte
820
 * @param array $options
821
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
822
 *     - bool sanitize : nettoyer le html suspect dans les arguments
823
 * @return string
824
 *     Texte
825
 */
826
function _L($text, $args = array(), $options = array()) {
827
	$f = $text;
828
	$defaut_options = array(
829
		'class' => null,
830
		'sanitize' => true,
831
	);
832
	// support de l'ancien argument $class
833
	if ($options and is_string($options)) {
834
		$options = array('class' => $options);
835
	}
836
	if (is_array($options)) {
837
		$options += $defaut_options;
838
	} else {
839
		$options = $defaut_options;
840
	}
841
842
	if (is_array($args) and count($args)) {
843
		if (!function_exists('interdire_scripts')) {
844
			include_spip('inc/texte');
845
		}
846
		if (!function_exists('echapper_html_suspect')) {
847
			include_spip('inc/texte_mini');
848
		}
849
		foreach ($args as $name => $value) {
850
			if (strpos($text, "@$name@") !== false) {
851
				if ($options['sanitize']) {
852
					$value = echapper_html_suspect($value);
853
					$value = interdire_scripts($value, -1);
854
				}
855
				if (!empty($options['class'])) {
856
					$value = "<span class='".$options['class']."'>$value</span>";
857
				}
858
				$text = str_replace("@$name@", $value, $text);
859
				unset($args[$name]);
860
			}
861
		}
862
		// Si des variables n'ont pas ete inserees, le signaler
863
		// (chaines de langues pas a jour)
864
		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...
865
			spip_log("$f:  variables inutilisees " . join(', ', array_keys($args)), _LOG_DEBUG);
866
		}
867
	}
868
869
	if (($GLOBALS['test_i18n'] or (_request('var_mode') == 'traduction')) and is_null($options['class'])) {
870
		return "<span class=debug-traduction-erreur>$text</span>";
871
	} else {
872
		return $text;
873
	}
874
}
875
876
877
/**
878
 * Retourne un joli chemin de répertoire
879
 *
880
 * Pour afficher `ecrire/action/` au lieu de `action/` dans les messages
881
 * ou `tmp/` au lieu de `../tmp/`
882
 *
883
 * @param stirng $rep Chemin d’un répertoire
884
 * @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...
885
 */
886
function joli_repertoire($rep) {
887
	$a = substr($rep, 0, 1);
888
	if ($a <> '.' and $a <> '/') {
889
		$rep = (_DIR_RESTREINT ? '' : _DIR_RESTREINT_ABS) . $rep;
890
	}
891
	$rep = preg_replace(',(^\.\.\/),', '', $rep);
892
893
	return $rep;
894
}
895
896
897
/**
898
 * Débute ou arrête un chronomètre et retourne sa valeur
899
 *
900
 * On exécute 2 fois la fonction, la première fois pour démarrer le chrono,
901
 * la seconde fois pour l’arrêter et récupérer la valeur
902
 *
903
 * @example
904
 *     ```
905
 *     spip_timer('papoter');
906
 *     // actions
907
 *     $duree = spip_timer('papoter');
908
 *     ```
909
 *
910
 * @param string $t
911
 *     Nom du chronomètre
912
 * @param bool $raw
913
 *     - false : retour en texte humainement lisible
914
 *     - true : retour en millisecondes
915
 * @return float|int|string|void
916
 */
917
function spip_timer($t = 'rien', $raw = false) {
918
	static $time;
919
	$a = time();
920
	$b = microtime();
921
	// microtime peut contenir les microsecondes et le temps
922
	$b = explode(' ', $b);
923
	if (count($b) == 2) {
924
		$a = end($b);
925
	} // plus precis !
926
	$b = reset($b);
927
	if (!isset($time[$t])) {
928
		$time[$t] = $a + $b;
929
	} else {
930
		$p = ($a + $b - $time[$t]) * 1000;
931
		unset($time[$t]);
932
#			echo "'$p'";exit;
933
		if ($raw) {
934
			return $p;
935
		}
936
		if ($p < 1000) {
937
			$s = '';
938
		} else {
939
			$s = sprintf("%d ", $x = floor($p / 1000));
940
			$p -= ($x * 1000);
941
		}
942
943
		return $s . sprintf($s ? "%07.3f ms" : "%.3f ms", $p);
944
	}
945
}
946
947
948
// Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
949
// sinon renvoie True et le date sauf si ca n'est pas souhaite
950
// http://code.spip.net/@spip_touch
951
function spip_touch($fichier, $duree = 0, $touch = true) {
952
	if ($duree) {
953
		clearstatcache();
954
		if ((@$f = filemtime($fichier)) and ($f >= time() - $duree)) {
955
			return false;
956
		}
957
	}
958
	if ($touch !== false) {
959
		if (!@touch($fichier)) {
960
			spip_unlink($fichier);
961
			@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...
962
		};
963
		@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...
964
	}
965
966
	return true;
967
}
968
969
970
/**
971
 * Action qui déclenche une tache de fond
972
 *
973
 * @see  queue_affichage_cron()
974
 * @see  action_super_cron_dist()
975
 * @uses cron()
976
 **/
977
function action_cron() {
978
	include_spip('inc/headers');
979
	http_status(204); // No Content
980
	header("Connection: close");
981
	define('_DIRECT_CRON_FORCE', true);
982
	cron();
983
}
984
985
/**
986
 * Exécution des tâches de fond
987
 *
988
 * @uses inc_genie_dist()
989
 *
990
 * @param array $taches
991
 *     Tâches forcées
992
 * @param array $taches_old
993
 *     Tâches forcées, pour compat avec ancienne syntaxe
994
 * @return bool
995
 *     True si la tache a pu être effectuée
996
 */
997
function cron($taches = array(), $taches_old = array()) {
998
	// si pas en mode cron force, laisser tomber.
999
	if (!defined('_DIRECT_CRON_FORCE')) {
1000
		return false;
1001
	}
1002
	if (!is_array($taches)) {
1003
		$taches = $taches_old;
1004
	} // compat anciens appels
1005
	// si taches a inserer en base et base inaccessible, laisser tomber
1006
	// sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
1007
	// queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
1008
	// et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
1009
	if ($taches and count($taches) and !spip_connect()) {
1010
		return false;
1011
	}
1012
	spip_log("cron !", 'jq' . _LOG_DEBUG);
1013
	if ($genie = charger_fonction('genie', 'inc', true)) {
1014
		return $genie($taches);
1015
	}
1016
1017
	return false;
1018
}
1019
1020
/**
1021
 * Ajout d'une tache dans la file d'attente
1022
 *
1023
 * @param string $function
1024
 *     Le nom de la fonction PHP qui doit être appelée.
1025
 * @param string $description
1026
 *     Une description humainement compréhensible de ce que fait la tâche
1027
 *     (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
1028
 * @param array $arguments
1029
 *     Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
1030
 * @param string $file
1031
 *     Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
1032
 *     exemple : `'inc/mail'` : il ne faut pas indiquer .php
1033
 *     Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
1034
 * @param bool $no_duplicate
1035
 *     Facultatif, `false` par défaut
1036
 *
1037
 *     - 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.
1038
 *     - 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
1039
 * @param int $time
1040
 *     Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
1041
 *     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).
1042
 * @param int $priority
1043
 *     Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
1044
 *     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.
1045
 * @return int
1046
 *     Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
1047
 */
1048
function job_queue_add(
1049
	$function,
1050
	$description,
1051
	$arguments = array(),
1052
	$file = '',
1053
	$no_duplicate = false,
1054
	$time = 0,
1055
	$priority = 0
1056
) {
1057
	include_spip('inc/queue');
1058
1059
	return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
1060
}
1061
1062
/**
1063
 * Supprimer une tache de la file d'attente
1064
 *
1065
 * @param int $id_job
1066
 *  id of jonb to delete
1067
 * @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...
1068
 */
1069
function job_queue_remove($id_job) {
1070
	include_spip('inc/queue');
1071
1072
	return queue_remove_job($id_job);
1073
}
1074
1075
/**
1076
 * Associer une tache a un/des objets de SPIP
1077
 *
1078
 * @param int $id_job
1079
 *     id of job to link
1080
 * @param array $objets
1081
 *     can be a simple array('objet'=>'article', 'id_objet'=>23)
1082
 *     or an array of simple array to link multiples objet in one time
1083
 */
1084
function job_queue_link($id_job, $objets) {
1085
	include_spip('inc/queue');
1086
1087
	return queue_link_job($id_job, $objets);
1088
}
1089
1090
1091
/**
1092
 * Renvoyer le temps de repos restant jusqu'au prochain job
1093
 *
1094
 * @staticvar int $queue_next_job_time
1095
 * @see queue_set_next_job_time()
1096
 * @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...
1097
 *    Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
1098
 *
1099
 *    - si `true`, force la relecture depuis le fichier
1100
 *    - si int, affecte la static directement avec la valeur
1101
 * @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...
1102
 *
1103
 *  - `0` si un job est à traiter
1104
 *  - `null` si la queue n'est pas encore initialisée
1105
 */
1106
function queue_sleep_time_to_next_job($force = null) {
1107
	static $queue_next_job_time = -1;
1108
	if ($force === true) {
1109
		$queue_next_job_time = -1;
1110
	} elseif ($force) {
1111
		$queue_next_job_time = $force;
1112
	}
1113
1114
	if ($queue_next_job_time == -1) {
1115
		if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
1116
			define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP . "job_queue_next.txt");
1117
		}
1118
		// utiliser un cache memoire si dispo
1119
		if (function_exists("cache_get") and defined('_MEMOIZE_MEMORY') and _MEMOIZE_MEMORY) {
1120
			$queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
1121
		} else {
1122
			$queue_next_job_time = null;
1123
			if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu)) {
1124
				$queue_next_job_time = intval($contenu);
1125
			}
1126
		}
1127
	}
1128
1129
	if (is_null($queue_next_job_time)) {
1130
		return null;
1131
	}
1132
	if (!$_SERVER['REQUEST_TIME']) {
1133
		$_SERVER['REQUEST_TIME'] = time();
1134
	}
1135
1136
	return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
1137
}
1138
1139
1140
/**
1141
 * Transformation XML des `&` en `&amp;`
1142
 * 
1143
 * @pipeline post_typo
1144
 * @param string $u
1145
 * @return string
1146
 */
1147
function quote_amp($u) {
1148
	return preg_replace(
1149
		"/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i",
1150
		"&amp;", $u);
1151
}
1152
1153
1154
/**
1155
 * Produit une balise `<script>` valide
1156
 *
1157
 * @example
1158
 *     ```
1159
 *     echo http_script('alert("ok");');
1160
 *     echo http_script('','js/jquery.js');
1161
 *     ```
1162
 *
1163
 * @param string $script
1164
 *     Code source du script
1165
 * @param string $src
1166
 *     Permet de faire appel à un fichier javascript distant
1167
 * @param string $noscript
1168
 *     Contenu de la balise  `<noscript>`
1169
 * @return string
1170
 *     Balise HTML `<script>` et son contenu
1171
 **/
1172
function http_script($script, $src = '', $noscript = '') {
1173
	static $done = array();
1174
1175
	if ($src && !isset($done[$src])) {
1176
		$done[$src] = true;
1177
		$src = find_in_path($src, _JAVASCRIPT);
1178
		$src = " src='$src'";
1179
	} else {
1180
		$src = '';
1181
	}
1182
	if ($script) {
1183
		$script = ("/*<![CDATA[*/\n" .
1184
			preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
1185
			"/*]]>*/");
1186
	}
1187
	if ($noscript) {
1188
		$noscript = "<noscript>\n\t$noscript\n</noscript>\n";
1189
	}
1190
1191
	return ($src or $script or $noscript)
1192
		? "<script type='text/javascript'$src>$script</script>$noscript"
1193
		: '';
1194
}
1195
1196
1197
/**
1198
 * Sécurise du texte à écrire dans du PHP ou du Javascript.
1199
 *
1200
 * Transforme n'importe quel texte en une chaîne utilisable
1201
 * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
1202
 * simples (`'` uniquement ; pas `"`)
1203
 *
1204
 * Utile particulièrement en filtre dans un squelettes
1205
 * pour écrire un contenu dans une variable JS ou PHP.
1206
 *
1207
 * Échappe les apostrophes (') du contenu transmis.
1208
 *
1209
 * @link http://www.spip.net/4281
1210
 * @example
1211
 *     PHP dans un squelette
1212
 *     ```
1213
 *     $x = '[(#TEXTE|texte_script)]';
1214
 *     ```
1215
 *
1216
 *     JS dans un squelette (transmettre une chaîne de langue)
1217
 *     ```
1218
 *     $x = '<:afficher_calendrier|texte_script:>';
1219
 *     ```
1220
 *
1221
 * @filtre
1222
 * @param string $texte
1223
 *     Texte à échapper
1224
 * @return string
1225
 *     Texte échappé
1226
 **/
1227
function texte_script($texte) {
1228
	return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
1229
}
1230
1231
1232
/**
1233
 * Gestion des chemins (ou path) de recherche de fichiers par SPIP
1234
 *
1235
 * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
1236
 * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
1237
 * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
1238
 *
1239
 * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
1240
 * en tête du path, dans cet ordre (hormis `squelettes` & la globale
1241
 * `$dossier_squelette` si définie qui resteront devant)
1242
 *
1243
 * Retourne dans tous les cas la liste des chemins.
1244
 *
1245
 * @note
1246
 *     Cette fonction est appelée à plusieurs endroits et crée une liste
1247
 *     de chemins finale à peu près de la sorte :
1248
 *
1249
 *     - dossiers squelettes (si globale précisée)
1250
 *     - squelettes/
1251
 *     - plugins (en fonction de leurs dépendances) : ceux qui dépendent
1252
 *       d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
1253
 *     - racine du site
1254
 *     - squelettes-dist/
1255
 *     - prive/
1256
 *     - ecrire/
1257
 *
1258
 * @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...
1259
 *     - Répertoire(s) à empiler au path
1260
 *     - '' provoque un recalcul des chemins.
1261
 * @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...
1262
 *     Liste des chemins, par ordre de priorité.
1263
 **/
1264
function _chemin($dir_path = null) {
1265
	static $path_base = null;
1266
	static $path_full = null;
1267
	if ($path_base == null) {
1268
		// Chemin standard depuis l'espace public
1269
		$path = defined('_SPIP_PATH') ? _SPIP_PATH :
1270
			_DIR_RACINE . ':' .
1271
			_DIR_RACINE . 'squelettes-dist/:' .
1272
			_DIR_RACINE . 'prive/:' .
1273
			_DIR_RESTREINT;
1274
		// Ajouter squelettes/
1275
		if (@is_dir(_DIR_RACINE . 'squelettes')) {
1276
			$path = _DIR_RACINE . 'squelettes/:' . $path;
1277
		}
1278
		foreach (explode(':', $path) as $dir) {
1279
			if (strlen($dir) and substr($dir, -1) != '/') {
1280
				$dir .= "/";
1281
			}
1282
			$path_base[] = $dir;
1283
		}
1284
		$path_full = $path_base;
1285
		// Et le(s) dossier(s) des squelettes nommes
1286 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...
1287
			foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1288
				array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
1289
			}
1290
		}
1291
		$GLOBALS['path_sig'] = md5(serialize($path_full));
1292
	}
1293
	if ($dir_path === null) {
1294
		return $path_full;
1295
	}
1296
1297
	if (strlen($dir_path)) {
1298
		$tete = "";
1299
		if (reset($path_base) == _DIR_RACINE . 'squelettes/') {
1300
			$tete = array_shift($path_base);
1301
		}
1302
		$dirs = array_reverse(explode(':', $dir_path));
1303
		foreach ($dirs as $dir_path) {
1304
			#if ($dir_path{0}!='/')
1305
			#	$dir_path = $dir_path;
1306
			if (substr($dir_path, -1) != '/') {
1307
				$dir_path .= "/";
1308
			}
1309
			if (!in_array($dir_path, $path_base)) {
1310
				array_unshift($path_base, $dir_path);
1311
			}
1312
		}
1313
		if (strlen($tete)) {
1314
			array_unshift($path_base, $tete);
1315
		}
1316
	}
1317
	$path_full = $path_base;
1318
	// Et le(s) dossier(s) des squelettes nommes
1319 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...
1320
		foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1321
			array_unshift($path_full, ((isset($d[0]) and $d[0] == '/') ? '' : _DIR_RACINE) . $d . '/');
1322
		}
1323
	}
1324
1325
	$GLOBALS['path_sig'] = md5(serialize($path_full));
1326
1327
	return $path_full;
1328
}
1329
1330
/**
1331
 * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
1332
 *
1333
 * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
1334
 *
1335
 * @uses _chemin()
1336
 *
1337
 * @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...
1338
 **/
1339
function creer_chemin() {
1340
	$path_a = _chemin();
1341
	static $c = '';
1342
1343
	// on calcule le chemin si le dossier skel a change
1344
	if ($c != $GLOBALS['dossier_squelettes']) {
1345
		// assurer le non plantage lors de la montee de version :
1346
		$c = $GLOBALS['dossier_squelettes'];
1347
		$path_a = _chemin(''); // forcer un recalcul du chemin
1348
	}
1349
1350
	return $path_a;
1351
}
1352
1353
1354
function lister_themes_prives() {
1355
	static $themes = null;
1356
	if (is_null($themes)) {
1357
		// si pas encore definie
1358
		if (!defined('_SPIP_THEME_PRIVE')) {
1359
			define('_SPIP_THEME_PRIVE', 'spip');
1360
		}
1361
		$themes = array(_SPIP_THEME_PRIVE);
1362
		// lors d'une installation neuve, prefs n'est pas definie.
1363 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...
1364
			$prefs = $GLOBALS['visiteur_session']['prefs'];
1365
		} else {
1366
			$prefs = array();
1367
		}
1368
		if (is_string($prefs)) {
1369
			$prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
1370
		}
1371
		if (
1372
			((isset($prefs['theme']) and $theme = $prefs['theme'])
1373
				or (isset($GLOBALS['theme_prive_defaut']) and $theme = $GLOBALS['theme_prive_defaut']))
1374
			and $theme != _SPIP_THEME_PRIVE
1375
		) {
1376
			array_unshift($themes, $theme);
1377
		} // placer le theme choisi en tete
1378
	}
1379
1380
	return $themes;
1381
}
1382
1383
function find_in_theme($file, $subdir = '', $include = false) {
1384
	static $themefiles = array();
1385
	if (isset($themefiles["$subdir$file"])) {
1386
		return $themefiles["$subdir$file"];
1387
	}
1388
	// on peut fournir une icone generique -xx.svg qui fera le job dans toutes les tailles, et qui est prioritaire sur le png
1389
	// si il y a un .svg a la bonne taille (-16.svg) a cote, on l'utilise en remplacement du -16.png
1390
	if (preg_match(',-(\d+)[.](png|gif|svg)$,', $file, $m)
1391
	  and $file_svg_generique = substr($file,0, -strlen($m[0])) . "-xx.svg"
1392
		and $f = find_in_theme("$file_svg_generique")) {
1393
		if ($fsize = substr($f,0,-6) . $m[1] . ".svg" and file_exists($fsize)) {
1394
			return $themefiles["$subdir$file"] = $fsize;
1395
		}
1396
		else {
1397
			return $themefiles["$subdir$file"] = "$f?".$m[1]."px";
1398
		}
1399
	}
1400
1401
	$themes = lister_themes_prives();
1402
	foreach ($themes as $theme) {
1403
		if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
1404
			return $themefiles["$subdir$file"] = $f;
1405
		}
1406
	}
1407
	spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');
1408
1409
	return $themefiles["$subdir$file"] = "";
1410
}
1411
1412
1413
/**
1414
 * Cherche une image dans les dossiers d'images
1415
 *
1416
 * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
1417
 * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
1418
 * de facon temporaire le temps de la migration, et cherche de nouveau.
1419
 *
1420
 * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
1421
 * dans le répertoire défini par la constante `_NOM_IMG_PACK`
1422
 *
1423
 * @see find_in_theme()
1424
 * @see inc_icone_renommer_dist()
1425
 *
1426
 * @param string $icone
1427
 *     Nom de l'icone cherchée
1428
 * @return string
1429
 *     Chemin complet de l'icone depuis la racine si l'icone est trouée,
1430
 *     sinon chaîne vide.
1431
 **/
1432
function chemin_image($icone) {
1433
	static $icone_renommer;
1434
	if ($p = strpos($icone, '?')) {
1435
		$icone = substr($icone,0, $p);
1436
	}
1437
	// gerer le cas d'un double appel en evitant de refaire le travail inutilement
1438
	if (strpos($icone, "/") !== false and file_exists($icone)) {
1439
		return $icone;
1440
	}
1441
1442
	// si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
1443
	if (preg_match(',[.](png|gif|jpg|svg)$,', $icone) and $f = find_in_theme("images/$icone")) {
1444
		return $f;
1445
	}
1446
	// sinon passer par le module de renommage
1447
	if (is_null($icone_renommer)) {
1448
		$icone_renommer = charger_fonction('icone_renommer', 'inc', true);
1449
	}
1450
	if ($icone_renommer) {
1451
		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...
1452
		if (file_exists($icone)) {
1453
			return $icone;
1454
		}
1455
	}
1456
1457
	return find_in_path($icone, _NOM_IMG_PACK);
1458
}
1459
1460
//
1461
// chercher un fichier $file dans le SPIP_PATH
1462
// si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
1463
// si 3e arg vrai, on inclut si ce n'est fait.
1464
$GLOBALS['path_sig'] = '';
1465
$GLOBALS['path_files'] = null;
1466
1467
/**
1468
 * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
1469
 *
1470
 * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
1471
 * suivant l'ordre des chemins connus de SPIP.
1472
 *
1473
 * @api
1474
 * @see  charger_fonction()
1475
 * @uses creer_chemin() Pour la liste des chemins.
1476
 * @example
1477
 *     ```
1478
 *     $f = find_in_path('css/perso.css');
1479
 *     $f = find_in_path('perso.css', 'css');
1480
 *     ```
1481
 *
1482
 * @param string $file
1483
 *     Fichier recherché
1484
 * @param string $dirname
1485
 *     Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
1486
 * @param bool|string $include
1487
 *     - false : ne fait rien de plus
1488
 *     - true : inclut le fichier (include_once)
1489
 *     - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
1490
 * @return string|bool
1491
 *     - string : chemin du fichier trouvé
1492
 *     - false : fichier introuvable
1493
 **/
1494
function find_in_path($file, $dirname = '', $include = false) {
1495
	static $dirs = array();
1496
	static $inc = array(); # cf http://trac.rezo.net/trac/spip/changeset/14743
1497
	static $c = '';
1498
1499
	// on calcule le chemin si le dossier skel a change
1500
	if ($c != $GLOBALS['dossier_squelettes']) {
1501
		// assurer le non plantage lors de la montee de version :
1502
		$c = $GLOBALS['dossier_squelettes'];
1503
		creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
1504
	}
1505
1506
	if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
1507
		if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
1508
			return false;
1509
		}
1510
		if ($include and !isset($inc[$dirname][$file])) {
1511
			include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1512
			$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1513
		}
1514
1515
		return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1516
	}
1517
1518
	$a = strrpos($file, '/');
1519
	if ($a !== false) {
1520
		$dirname .= substr($file, 0, ++$a);
1521
		$file = substr($file, $a);
1522
	}
1523
1524
	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...
1525 View Code Duplication
		if (!isset($dirs[$a = $dir . $dirname])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1526
			$dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a);
1527
		}
1528
		if ($dirs[$a]) {
1529
			if (file_exists(_ROOT_CWD . ($a .= $file))) {
1530
				if ($include and !isset($inc[$dirname][$file])) {
1531
					include_once _ROOT_CWD . $a;
1532
					$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1533
				}
1534 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...
1535
					// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1536
					if (is_null($GLOBALS['path_files'])) {
1537
						return $a;
1538
					}
1539
					define('_SAUVER_CHEMIN', true);
1540
				}
1541
1542
				return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
1543
			}
1544
		}
1545
	}
1546
1547
	if ($include) {
1548
		spip_log("include_spip $dirname$file non trouve");
1549
		if ($include === 'required') {
1550
			echo '<pre>',
1551
			"<strong>Erreur Fatale</strong><br />";
1552
			if (function_exists('debug_print_backtrace')) {
1553
				echo debug_print_backtrace();
1554
			}
1555
			echo '</pre>';
1556
			die("Erreur interne: ne peut inclure $dirname$file");
1557
		}
1558
	}
1559
1560 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...
1561
		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1562
		if (is_null($GLOBALS['path_files'])) {
1563
			return false;
1564
		}
1565
		define('_SAUVER_CHEMIN', true);
1566
	}
1567
1568
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1569
}
1570
1571
function clear_path_cache() {
1572
	$GLOBALS['path_files'] = array();
1573
	spip_unlink(_CACHE_CHEMIN);
1574
}
1575
1576
function load_path_cache() {
1577
	// charger le path des plugins
1578
	if (@is_readable(_CACHE_PLUGINS_PATH)) {
1579
		include_once(_CACHE_PLUGINS_PATH);
1580
	}
1581
	$GLOBALS['path_files'] = array();
1582
	// si le visiteur est admin,
1583
	// on ne recharge pas le cache pour forcer sa mise a jour
1584
	if (
1585
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1586
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1587
		// utiliser le cookie est un pis aller qui marche 'en general'
1588
		// on blinde par un second test au moment de la lecture de la session
1589
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1590
		// et en ignorant ce cache en cas de recalcul explicite
1591
	!_request('var_mode')
1592
	) {
1593
		// on essaye de lire directement sans verrou pour aller plus vite
1594
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
1595
			// mais si semble corrompu on relit avec un verrou
1596
			if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1597
				lire_fichier(_CACHE_CHEMIN, $contenu);
1598
				if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1599
					$GLOBALS['path_files'] = array();
1600
				}
1601
			}
1602
		}
1603
	}
1604
}
1605
1606
function save_path_cache() {
1607
	if (defined('_SAUVER_CHEMIN')
1608
		and _SAUVER_CHEMIN
1609
	) {
1610
		ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
1611
	}
1612
}
1613
1614
1615
/**
1616
 * Trouve tous les fichiers du path correspondants à un pattern
1617
 *
1618
 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1619
 * par un `find_in_path()`
1620
 *
1621
 * @api
1622
 * @uses creer_chemin()
1623
 * @uses preg_files()
1624
 *
1625
 * @param string $dir
1626
 * @param string $pattern
1627
 * @param bool $recurs
1628
 * @return array
1629
 */
1630
function find_all_in_path($dir, $pattern, $recurs = false) {
1631
	$liste_fichiers = array();
1632
	$maxfiles = 10000;
1633
1634
	// cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1635
	// on a pas encore inclus flock.php
1636
	if (!function_exists('preg_files')) {
1637
		include_once _ROOT_RESTREINT . 'inc/flock.php';
1638
	}
1639
1640
	// Parcourir le chemin
1641
	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...
1642
		$f = $d . $dir;
1643
		if (@is_dir($f)) {
1644
			$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...
1645
			foreach ($liste as $chemin) {
1646
				$nom = basename($chemin);
1647
				// ne prendre que les fichiers pas deja trouves
1648
				// car find_in_path prend le premier qu'il trouve,
1649
				// les autres sont donc masques
1650
				if (!isset($liste_fichiers[$nom])) {
1651
					$liste_fichiers[$nom] = $chemin;
1652
				}
1653
			}
1654
		}
1655
	}
1656
1657
	return $liste_fichiers;
1658
}
1659
1660
/**
1661
 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1662
 * et beneficient d'une exception
1663
 *
1664
 * @param string $nom
1665
 * @param bool $strict
1666
 * @return bool
1667
 */
1668
function autoriser_sans_cookie($nom, $strict = false) {
1669
	static $autsanscookie = array('install', 'base_repair');
1670
1671
	if (in_array($nom, $autsanscookie)) {
1672
		if (test_espace_prive()){
1673
			include_spip('base/connect_sql');
1674
			if (!$strict or !spip_connect()){
1675
				return true;
1676
			}
1677
		}
1678
	}
1679
	return false;
1680
}
1681
1682
/**
1683
 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1684
 *
1685
 * @api
1686
 * @param string $id
1687
 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
1688
 * @param string $entite
1689
 *   surnom de la table SQL (donne acces au nom de cle primaire)
1690
 * @param string $args
1691
 *   query_string a placer apres cle=$id&....
1692
 * @param string $ancre
1693
 *   ancre a mettre a la fin de l'URL a produire
1694
 * @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...
1695
 *   produire l'URL publique ou privee (par defaut: selon espace)
1696
 *   si string : serveur de base de donnee (nom du connect)
1697
 * @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...
1698
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
1699
 * @return string|array
1700
 *   url codee ou fonction de decodage
1701
 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1702
 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1703
 */
1704
function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1705
	if ($public === null) {
1706
		$public = !test_espace_prive();
1707
	}
1708
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1709
1710
	if (!$public) {
1711
		if (!$entite) {
1712
			return '';
1713
		}
1714
		if (!function_exists('generer_url_ecrire_objet')) {
1715
			include_spip('inc/urls');
1716
		}
1717
		$res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1718
	} else {
1719
		if ($type === null) {
1720
			$type = (isset($GLOBALS['type_urls']))
1721
				? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1722
				: ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1723
					? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1724
		}
1725
1726
		$f = charger_fonction($type, 'urls', true);
1727
		// se rabattre sur les urls page si les urls perso non dispo
1728
		if (!$f) {
1729
			$f = charger_fonction('page', 'urls', true);
1730
		}
1731
1732
		// si $entite='', on veut la fonction de passage URL ==> id
1733
		// sinon on veut effectuer le passage id ==> URL
1734
		if (!$entite) {
1735
			return $f;
1736
		}
1737
1738
		// mais d'abord il faut tester le cas des urls sur une
1739
		// base distante
1740
		if (is_string($public)
1741
			and $g = charger_fonction('connect', 'urls', true)
1742
		) {
1743
			$f = $g;
1744
		}
1745
1746
		$res = $f(intval($id), $entite, $args, $ancre, $public);
1747
1748
	}
1749
	if ($res) {
1750
		return $res;
1751
	}
1752
	// Sinon c'est un raccourci ou compat SPIP < 2
1753
	if (!function_exists($f = 'generer_url_' . $entite)) {
1754
		if (!function_exists($f .= '_dist')) {
1755
			$f = '';
1756
		}
1757
	}
1758
	if ($f) {
1759
		$url = $f($id, $args, $ancre);
1760
		if (strlen($args)) {
1761
			$url .= strstr($url, '?')
1762
				? '&amp;' . $args
1763
				: '?' . $args;
1764
		}
1765
1766
		return $url;
1767
	}
1768
	// On a ete gentil mais la ....
1769
	spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1770
1771
	return '';
1772
}
1773
1774
function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1775
	$exec = objet_info($entite, 'url_edit');
1776
	$url = generer_url_ecrire($exec, $args);
1777
	if (intval($id)) {
1778
		$url = parametre_url($url, id_table_objet($entite), $id);
1779
	} else {
1780
		$url = parametre_url($url, 'new', 'oui');
1781
	}
1782
	if ($ancre) {
1783
		$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...
1784
	}
1785
1786
	return $url;
1787
}
1788
1789
// http://code.spip.net/@urls_connect_dist
1790
function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1791
	include_spip('base/connect_sql');
1792
	$id_type = id_table_objet($entite, $public);
1793
1794
	return _DIR_RACINE . get_spip_script('./')
1795
	. "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1796
	. (!$args ? '' : "&$args")
1797
	. (!$ancre ? '' : "#$ancre");
1798
}
1799
1800
1801
/**
1802
 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1803
 *
1804
 * @param string $url
1805
 * @return string
1806
 */
1807
function urlencode_1738($url) {
1808
	if (preg_match(',[^\x00-\x7E],sS', $url)) {
1809
		$uri = '';
1810
		for ($i = 0; $i < strlen($url); $i++) {
1811
			if (ord($a = $url[$i]) > 127) {
1812
				$a = rawurlencode($a);
1813
			}
1814
			$uri .= $a;
1815
		}
1816
		$url = $uri;
1817
	}
1818
1819
	return quote_amp($url);
1820
}
1821
1822
// http://code.spip.net/@generer_url_entite_absolue
1823
function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1824
	if (!$connect) {
1825
		$connect = true;
1826
	}
1827
	$h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1828
	if (!preg_match(',^\w+:,', $h)) {
1829
		include_spip('inc/filtres_mini');
1830
		$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...
1831
	}
1832
1833
	return $h;
1834
}
1835
1836
1837
/**
1838
 * Tester qu'une variable d'environnement est active
1839
 *
1840
 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1841
 * variables d'environnement comme `$_SERVER['HTTPS']` ou `ini_get('display_errors')`
1842
 *
1843
 * @param string|bool $truc
1844
 *     La valeur de la variable d'environnement
1845
 * @return bool
1846
 *     true si la valeur est considérée active ; false sinon.
1847
 **/
1848
function test_valeur_serveur($truc) {
1849
	if (!$truc) {
1850
		return false;
1851
	}
1852
1853
	return (strtolower($truc) !== 'off');
1854
}
1855
1856
//
1857
// Fonctions de fabrication des URL des scripts de Spip
1858
//
1859
/**
1860
 * Calcule l'url de base du site
1861
 *
1862
 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1863
 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1864
 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1865
 *
1866
 * @note
1867
 *     La globale `$profondeur_url` doit être initialisée de manière à
1868
 *     indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1869
 *     racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1870
 *     la racine 0. Sur url/perso/ elle vaut 2
1871
 *
1872
 * @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...
1873
 *    - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1874
 *    - si int : indique que l'on veut l'url pour la profondeur indiquée
1875
 *    - si bool : retourne le tableau static complet
1876
 *    - si array : réinitialise le tableau static complet avec la valeur fournie
1877
 * @return string|array
1878
 */
1879
function url_de_base($profondeur = null) {
1880
1881
	static $url = array();
1882
	if (is_array($profondeur)) {
1883
		return $url = $profondeur;
1884
	}
1885
	if ($profondeur === false) {
1886
		return $url;
1887
	}
1888
1889
	if (is_null($profondeur)) {
1890
		$profondeur = $GLOBALS['profondeur_url'];
1891
	}
1892
1893
	if (isset($url[$profondeur])) {
1894
		return $url[$profondeur];
1895
	}
1896
1897
	$http = 'http';
1898
1899
	if (
1900
		isset($_SERVER["SCRIPT_URI"])
1901
		and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1902
	) {
1903
		$http = 'https';
1904
	} elseif (
1905
		isset($_SERVER['HTTPS'])
1906
		and test_valeur_serveur($_SERVER['HTTPS'])
1907
	) {
1908
		$http = 'https';
1909
	}
1910
1911
	// note : HTTP_HOST contient le :port si necessaire
1912
	$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
1913
	// si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1914
	if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1915
		$host = $GLOBALS['meta']['adresse_site'];
1916
		if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1917
			$http = $scheme;
1918
			$host = str_replace("{$scheme}://", '', $host);
1919
		}
1920
	}
1921
	if (isset($_SERVER['SERVER_PORT'])
1922
		and $port = $_SERVER['SERVER_PORT']
1923
		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...
1924
	) {
1925
		if (!defined('_PORT_HTTP_STANDARD')) {
1926
			define('_PORT_HTTP_STANDARD', '80');
1927
		}
1928
		if (!defined('_PORT_HTTPS_STANDARD')) {
1929
			define('_PORT_HTTPS_STANDARD', '443');
1930
		}
1931
		if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
1932
			$host .= ":$port";
1933
		}
1934
		if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
1935
			$host .= ":$port";
1936
		}
1937
	}
1938
1939 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...
1940
		if (isset($_SERVER['REQUEST_URI'])) {
1941
			$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1942
		} else {
1943
			$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
1944
			if (!empty($_SERVER['QUERY_STRING'])
1945
				and !strpos($_SERVER['REQUEST_URI'], '?')
1946
			) {
1947
				$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1948
			}
1949
		}
1950
	}
1951
1952
	$url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1953
1954
	return $url[$profondeur];
1955
}
1956
1957
/**
1958
 * fonction testable de construction d'une url appelee par url_de_base()
1959
 *
1960
 * @param string $http
1961
 * @param string $host
1962
 * @param string $request
1963
 * @param int $prof
1964
 * @return string
1965
 */
1966
function url_de_($http, $host, $request, $prof = 0) {
1967
	$prof = max($prof, 0);
1968
1969
	$myself = ltrim($request, '/');
1970
	# supprimer la chaine de GET
1971
	list($myself) = explode('?', $myself);
1972
	// vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
1973
	// protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
1974 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...
1975
		$myself = explode('://',$myself);
1976
		array_shift($myself);
1977
		$myself = implode('://',$myself);
1978
		$myself = explode('/',$myself);
1979
		array_shift($myself);
1980
		$myself = implode('/',$myself);
1981
	}
1982
	$url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
1983
1984
	$url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
1985
1986
	return $url;
1987
}
1988
1989
1990
// Pour une redirection, la liste des arguments doit etre separee par "&"
1991
// Pour du code XHTML, ca doit etre &amp;
1992
// Bravo au W3C qui n'a pas ete capable de nous eviter ca
1993
// faute de separer proprement langage et meta-langage
1994
1995
// Attention, X?y=z et "X/?y=z" sont completement differents!
1996
// http://httpd.apache.org/docs/2.0/mod/mod_dir.html
1997
1998
/**
1999
 * Crée une URL vers un script de l'espace privé
2000
 *
2001
 * @example
2002
 *     ```
2003
 *     generer_url_ecrire('admin_plugin')
2004
 *     ```
2005
 *
2006
 * @param string $script
2007
 *     Nom de la page privée (xx dans exec=xx)
2008
 * @param string $args
2009
 *     Arguments à transmettre, tel que `arg1=yy&arg2=zz`
2010
 * @param bool $no_entities
2011
 *     Si false : transforme les `&` en `&amp;`
2012
 * @param bool|string $rel
2013
 *     URL relative ?
2014
 *
2015
 *     - false : l’URL sera complète et contiendra l’URL du site
2016
 *     - true : l’URL sera relavive.
2017
 *     - string : on transmet l'url à la fonction
2018
 * @return string URL
2019
 **/
2020
function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
2021
	if (!$rel) {
2022
		$rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
2023
	} else {
2024
		if (!is_string($rel)) {
2025
			$rel = _DIR_RESTREINT ? _DIR_RESTREINT :
2026
				('./' . _SPIP_ECRIRE_SCRIPT);
2027
		}
2028
	}
2029
2030
	list($script, $ancre) = array_pad(explode('#', $script), 2, null);
2031
	if ($script and ($script <> 'accueil' or $rel)) {
2032
		$args = "?exec=$script" . (!$args ? '' : "&$args");
2033
	} elseif ($args) {
2034
		$args = "?$args";
2035
	}
2036
	if ($ancre) {
2037
		$args .= "#$ancre";
2038
	}
2039
2040
	return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
2041
}
2042
2043
//
2044
// Adresse des scripts publics (a passer dans inc-urls...)
2045
//
2046
2047
2048
/**
2049
 * Retourne le nom du fichier d'exécution de SPIP
2050
 *
2051
 * @see _SPIP_SCRIPT
2052
 * @note
2053
 *   Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2054
 *   dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2055
 *
2056
 * @param string $default
2057
 *     Script par défaut
2058
 * @return string
2059
 *     Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2060
 **/
2061
function get_spip_script($default = '') {
2062
	# cas define('_SPIP_SCRIPT', '');
2063
	if (_SPIP_SCRIPT) {
2064
		return _SPIP_SCRIPT;
2065
	} else {
2066
		return $default;
2067
	}
2068
}
2069
2070
/**
2071
 * Crée une URL vers une page publique de SPIP
2072
 *
2073
 * @example
2074
 *     ```
2075
 *     generer_url_public("rubrique","id_rubrique=$id_rubrique")
2076
 *     ```
2077
 *
2078
 * @param string $script
2079
 *     Nom de la page
2080
 * @param string|array $args
2081
 *     Arguments à transmettre a l'URL,
2082
 *      soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2083
 *      soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2084
 * @param bool $no_entities
2085
 *     Si false : transforme les `&` en `&amp;`
2086
 * @param bool $rel
2087
 *     URL relative ?
2088
 *
2089
 *     - false : l’URL sera complète et contiendra l’URL du site
2090
 *     - true : l’URL sera relavive.
2091
 * @param string $action
2092
 *     - Fichier d'exécution public (spip.php par défaut)
2093
 * @return string URL
2094
 **/
2095
function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2096
	// si le script est une action (spip_pass, spip_inscription),
2097
	// standardiser vers la nouvelle API
2098
2099
	if (!$action) {
2100
		$action = get_spip_script();
2101
	}
2102
	if ($script) {
2103
		$action = parametre_url($action, _SPIP_PAGE, $script, '&');
2104
	}
2105
2106
	if ($args) {
2107
		if (is_array($args)) {
2108
			$r = '';
2109
			foreach ($args as $k => $v) {
2110
				$r .= '&' . $k . '=' . $v;
2111
			}
2112
			$args = substr($r, 1);
2113
		}
2114
		$action .=
2115
			(strpos($action, '?') !== false ? '&' : '?') . $args;
2116
	}
2117
	if (!$no_entities) {
2118
		$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...
2119
	}
2120
2121
	// ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2122
	return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2123
}
2124
2125
// http://code.spip.net/@generer_url_prive
2126
function generer_url_prive($script, $args = "", $no_entities = false) {
2127
2128
	return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2129
}
2130
2131
// Pour les formulaires en methode POST,
2132
// mettre le nom du script a la fois en input-hidden et dans le champ action:
2133
// 1) on peut ainsi memoriser le signet comme si c'etait un GET
2134
// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2135
2136
/**
2137
 * Retourne un formulaire (POST par défaut) vers un script exec
2138
 * de l’interface privée
2139
 *
2140
 * @param string $script
2141
 *     Nom de la page exec
2142
 * @param string $corps
2143
 *     Contenu du formulaire
2144
 * @param string $atts
2145
 *     Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2146
 * @param string $submit
2147
 *     Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2148
 * @return string
2149
 *     Code HTML du formulaire
2150
 **/
2151
function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2152
2153
	$script1 = explode('&', $script);
2154
	$script1 = reset($script1);
2155
2156
	return "<form action='"
2157
	. ($script ? generer_url_ecrire($script) : '')
2158
	. "' "
2159
	. ($atts ? $atts : " method='post'")
2160
	. "><div>\n"
2161
	. "<input type='hidden' name='exec' value='$script1' />"
2162
	. $corps
2163
	. (!$submit ? '' :
2164
		("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2165
	. "</div></form>\n";
2166
}
2167
2168
/**
2169
 * Générer un formulaire pour lancer une action vers $script
2170
 *
2171
 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2172
 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2173
 * qui ne sont pas destines a etre mis en signets
2174
 *
2175
 * @param string $script
2176
 * @param string $corps
2177
 * @param string $atts
2178
 * @param bool $public
2179
 * @return string
2180
 */
2181
function generer_form_action($script, $corps, $atts = '', $public = false) {
2182
	// si l'on est dans l'espace prive, on garde dans l'url
2183
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2184
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2185
	$h = (_DIR_RACINE and !$public)
2186
		? generer_url_ecrire(_request('exec'))
2187
		: generer_url_public();
2188
2189
	return "\n<form action='" .
2190
	$h .
2191
	"'" .
2192
	$atts .
2193
	">\n" .
2194
	"<div>" .
2195
	"\n<input type='hidden' name='action' value='$script' />" .
2196
	$corps .
2197
	"</div></form>";
2198
}
2199
2200
/**
2201
 * Créer une URL
2202
 *
2203
 * @param  string $script
2204
 *     Nom du script à exécuter
2205
 * @param  string $args
2206
 *     Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2207
 * @param bool $no_entities
2208
 *     Si false : transforme les & en &amp;
2209
 * @param boolean $public
2210
 *     URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2211
 *     true : l’URL sera relative.
2212
 * @return string
2213
 *     URL
2214
 */
2215
function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2216
	// si l'on est dans l'espace prive, on garde dans l'url
2217
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2218
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2219
	$url = (_DIR_RACINE and !$public)
2220
		? generer_url_ecrire(_request('exec'))
2221
		: generer_url_public('', '', false, false);
2222
	$url = parametre_url($url, 'action', $script);
2223
	if ($args) {
2224
		$url .= quote_amp('&' . $args);
2225
	}
2226
2227
	if ($no_entities) {
2228
		$url = str_replace('&amp;', '&', $url);
2229
	}
2230
2231
	return $url;
2232
}
2233
2234
2235
/**
2236
 * Fonction d'initialisation groupée pour compatibilité ascendante
2237
 *
2238
 * @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...
2239
 * @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...
2240
 * @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...
2241
 * @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...
2242
 */
2243
function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2244
	spip_initialisation_core($pi, $pa, $ti, $ta);
2245
	spip_initialisation_suite();
2246
}
2247
2248
/**
2249
 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2250
 *
2251
 * Elle définit les répertoires et fichiers non partageables
2252
 * et indique dans $test_dirs ceux devant être accessibles en écriture
2253
 * mais ne touche pas à cette variable si elle est déjà définie
2254
 * afin que mes_options.php puisse en spécifier d'autres.
2255
 *
2256
 * Elle définit ensuite les noms des fichiers et les droits.
2257
 * Puis simule un register_global=on sécurisé.
2258
 *
2259
 * @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...
2260
 * @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...
2261
 * @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...
2262
 * @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...
2263
 */
2264
function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2265
	static $too_late = 0;
2266
	if ($too_late++) {
2267
		return;
2268
	}
2269
2270
	// Declaration des repertoires
2271
2272
	// le nom du repertoire plugins/ activables/desactivables
2273
	if (!defined('_DIR_PLUGINS')) {
2274
		define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2275
	}
2276
2277
	// le nom du repertoire des extensions/ permanentes du core, toujours actives
2278
	if (!defined('_DIR_PLUGINS_DIST')) {
2279
		define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2280
	}
2281
2282
	// le nom du repertoire des librairies
2283
	if (!defined('_DIR_LIB')) {
2284
		define('_DIR_LIB', _DIR_RACINE . "lib/");
2285
	}
2286
2287
	if (!defined('_DIR_IMG')) {
2288
		define('_DIR_IMG', $pa);
2289
	}
2290
	if (!defined('_DIR_LOGOS')) {
2291
		define('_DIR_LOGOS', $pa);
2292
	}
2293
	if (!defined('_DIR_IMG_ICONES')) {
2294
		define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2295
	}
2296
2297
	if (!defined('_DIR_DUMP')) {
2298
		define('_DIR_DUMP', $ti . "dump/");
2299
	}
2300
	if (!defined('_DIR_SESSIONS')) {
2301
		define('_DIR_SESSIONS', $ti . "sessions/");
2302
	}
2303
	if (!defined('_DIR_TRANSFERT')) {
2304
		define('_DIR_TRANSFERT', $ti . "upload/");
2305
	}
2306
	if (!defined('_DIR_CACHE')) {
2307
		define('_DIR_CACHE', $ti . "cache/");
2308
	}
2309
	if (!defined('_DIR_CACHE_XML')) {
2310
		define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2311
	}
2312
	if (!defined('_DIR_SKELS')) {
2313
		define('_DIR_SKELS', _DIR_CACHE . "skel/");
2314
	}
2315
	if (!defined('_DIR_AIDE')) {
2316
		define('_DIR_AIDE', _DIR_CACHE . "aide/");
2317
	}
2318
	if (!defined('_DIR_TMP')) {
2319
		define('_DIR_TMP', $ti);
2320
	}
2321
2322
	if (!defined('_DIR_VAR')) {
2323
		define('_DIR_VAR', $ta);
2324
	}
2325
2326
	if (!defined('_DIR_ETC')) {
2327
		define('_DIR_ETC', $pi);
2328
	}
2329
	if (!defined('_DIR_CONNECT')) {
2330
		define('_DIR_CONNECT', $pi);
2331
	}
2332
	if (!defined('_DIR_CHMOD')) {
2333
		define('_DIR_CHMOD', $pi);
2334
	}
2335
2336
	if (!isset($GLOBALS['test_dirs']))
2337
		// Pas $pi car il est bon de le mettre hors ecriture apres intstall
2338
		// il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2339
	{
2340
		$GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2341
	}
2342
2343
	// Declaration des fichiers
2344
2345
	if (!defined('_CACHE_PLUGINS_PATH')) {
2346
		define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2347
	}
2348
	if (!defined('_CACHE_PLUGINS_OPT')) {
2349
		define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2350
	}
2351
	if (!defined('_CACHE_PLUGINS_FCT')) {
2352
		define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2353
	}
2354
	if (!defined('_CACHE_PIPELINES')) {
2355
		define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2356
	}
2357
	if (!defined('_CACHE_CHEMIN')) {
2358
		define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2359
	}
2360
2361
	# attention .php obligatoire pour ecrire_fichier_securise
2362
	if (!defined('_FILE_META')) {
2363
		define('_FILE_META', $ti . 'meta_cache.php');
2364
	}
2365
	if (!defined('_DIR_LOG')) {
2366
		define('_DIR_LOG', _DIR_TMP . 'log/');
2367
	}
2368
	if (!defined('_FILE_LOG')) {
2369
		define('_FILE_LOG', 'spip');
2370
	}
2371
	if (!defined('_FILE_LOG_SUFFIX')) {
2372
		define('_FILE_LOG_SUFFIX', '.log');
2373
	}
2374
2375
	// Le fichier de connexion a la base de donnees
2376
	// tient compte des anciennes versions (inc_connect...)
2377
	if (!defined('_FILE_CONNECT_INS')) {
2378
		define('_FILE_CONNECT_INS', 'connect');
2379
	}
2380
	if (!defined('_FILE_CONNECT')) {
2381
		define('_FILE_CONNECT',
2382
		(@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2383
			: (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2384
				: false)));
2385
	}
2386
2387
	// Le fichier de reglages des droits
2388
	if (!defined('_FILE_CHMOD_INS')) {
2389
		define('_FILE_CHMOD_INS', 'chmod');
2390
	}
2391
	if (!defined('_FILE_CHMOD')) {
2392
		define('_FILE_CHMOD',
2393
		(@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2394
			: false));
2395
	}
2396
2397
	if (!defined('_FILE_LDAP')) {
2398
		define('_FILE_LDAP', 'ldap.php');
2399
	}
2400
2401
	if (!defined('_FILE_TMP_SUFFIX')) {
2402
		define('_FILE_TMP_SUFFIX', '.tmp.php');
2403
	}
2404
	if (!defined('_FILE_CONNECT_TMP')) {
2405
		define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2406
	}
2407
	if (!defined('_FILE_CHMOD_TMP')) {
2408
		define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2409
	}
2410
2411
	// Definition des droits d'acces en ecriture
2412
	if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2413
		include_once _FILE_CHMOD;
2414
	}
2415
2416
	// Se mefier des fichiers mal remplis!
2417
	if (!defined('_SPIP_CHMOD')) {
2418
		define('_SPIP_CHMOD', 0777);
2419
	}
2420
2421
	if (!defined('_DEFAULT_CHARSET')) {
2422
		/** Le charset par défaut lors de l'installation */
2423
		define('_DEFAULT_CHARSET', 'utf-8');
2424
	}
2425
	if (!defined('_ROOT_PLUGINS')) {
2426
		define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2427
	}
2428
	if (!defined('_ROOT_PLUGINS_DIST')) {
2429
		define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2430
	}
2431
	if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2432
		define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2433
	}
2434
2435
	// La taille des Log
2436
	if (!defined('_MAX_LOG')) {
2437
		define('_MAX_LOG', 100);
2438
	}
2439
2440
	// Sommes-nous dans l'empire du Mal ?
2441
	// (ou sous le signe du Pingouin, ascendant GNU ?)
2442
	if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2443
		if (!defined('_OS_SERVEUR')) {
2444
			define('_OS_SERVEUR', 'windows');
2445
		}
2446
		if (!defined('_SPIP_LOCK_MODE')) {
2447
			define('_SPIP_LOCK_MODE', 1);
2448
		} // utiliser le flock php
2449
	} else {
2450
		if (!defined('_OS_SERVEUR')) {
2451
			define('_OS_SERVEUR', '');
2452
		}
2453
		if (!defined('_SPIP_LOCK_MODE')) {
2454
			define('_SPIP_LOCK_MODE', 1);
2455
		} // utiliser le flock php
2456
		#if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2457
	}
2458
2459
	// Langue par defaut
2460
	if (!defined('_LANGUE_PAR_DEFAUT')) {
2461
		define('_LANGUE_PAR_DEFAUT', 'fr');
2462
	}
2463
2464
	//
2465
	// Module de lecture/ecriture/suppression de fichiers utilisant flock()
2466
	// (non surchargeable en l'etat ; attention si on utilise include_spip()
2467
	// pour le rendre surchargeable, on va provoquer un reecriture
2468
	// systematique du noyau ou une baisse de perfs => a etudier)
2469
	include_once _ROOT_RESTREINT . 'inc/flock.php';
2470
2471
	// charger tout de suite le path et son cache
2472
	load_path_cache();
2473
2474
	// *********** traiter les variables ************
2475
2476
	//
2477
	// Securite
2478
	//
2479
2480
	// Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2481
	if (isset($_REQUEST['GLOBALS'])) {
2482
		die();
2483
	}
2484
	// nettoyer les magic quotes \' et les caracteres nuls %00
2485
	spip_desinfecte($_GET);
2486
	spip_desinfecte($_POST);
2487
	spip_desinfecte($_COOKIE);
2488
	spip_desinfecte($_REQUEST);
2489
2490
	// appliquer le cookie_prefix
2491
	if ($GLOBALS['cookie_prefix'] != 'spip') {
2492
		include_spip('inc/cookie');
2493
		recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2494
	}
2495
2496
	//
2497
	// Capacites php (en fonction de la version)
2498
	//
2499
	$GLOBALS['flag_ob'] = (function_exists("ob_start")
2500
		&& function_exists("ini_get")
2501
		&& !strstr(@ini_get('disable_functions'), 'ob_'));
2502
	$GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2503
	$GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
2504
	$GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2505
		(get_cfg_var('upload_max_filesize') > 0));
2506
2507
2508
	// Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2509 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...
2510
		$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2511
	} else {
2512
		$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
2513
		if (!empty($_SERVER['QUERY_STRING'])
2514
			and !strpos($_SERVER['REQUEST_URI'], '?')
2515
		) {
2516
			$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2517
		}
2518
	}
2519
2520
	// Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2521
	if (!defined('_RENOUVELLE_ALEA')) {
2522
		define('_RENOUVELLE_ALEA', 12 * 3600);
2523
	}
2524
	if (!defined('_DUREE_COOKIE_ADMIN')) {
2525
		define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2526
	}
2527
2528
	// charger les meta si possible et renouveller l'alea au besoin
2529
	// charge aussi effacer_meta et ecrire_meta
2530
	$inc_meta = charger_fonction('meta', 'inc');
2531
	$inc_meta();
2532
2533
	// nombre de repertoires depuis la racine
2534
	// on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2535
	// ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2536
	// le calcul est faux)
2537
	if (!_DIR_RESTREINT) {
2538
		$GLOBALS['profondeur_url'] = 1;
2539
	} else {
2540
		$uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
2541
		$uri_ref = $_SERVER["SCRIPT_NAME"];
2542
		if (!$uri_ref
2543
			// si on est appele avec un autre ti, on est sans doute en mutu
2544
			// si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2545
			// a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2546
			// s'en remettre a l'adresse du site. alea jacta est.
2547
			or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2548
		) {
2549
2550
			if (isset($GLOBALS['meta']['adresse_site'])) {
2551
				$uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2552
				$uri_ref = (isset($uri_ref['path']) ? $uri_ref['path'] : '') . '/';
2553
			} else {
2554
				$uri_ref = "";
2555
			}
2556
		}
2557
		if (!$uri or !$uri_ref) {
2558
			$GLOBALS['profondeur_url'] = 0;
2559
		} else {
2560
			$GLOBALS['profondeur_url'] = max(0,
2561
				substr_count($uri[0], '/')
2562
				- substr_count($uri_ref, '/'));
2563
		}
2564
	}
2565
	// s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2566
	if (_FILE_CONNECT) {
2567
		if (verifier_visiteur() == '0minirezo'
2568
			// si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2569
			and !isset($_COOKIE['spip_admin'])
2570
		) {
2571
			clear_path_cache();
2572
		}
2573
	}
2574
2575
}
2576
2577
/**
2578
 * Complements d'initialisation non critiques pouvant etre realises
2579
 * par les plugins
2580
 *
2581
 */
2582
function spip_initialisation_suite() {
2583
	static $too_late = 0;
2584
	if ($too_late++) {
2585
		return;
2586
	}
2587
2588
	// taille mini des login
2589
	if (!defined('_LOGIN_TROP_COURT')) {
2590
		define('_LOGIN_TROP_COURT', 4);
2591
	}
2592
2593
	// la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2594
	#if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2595
	#if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2596
	#if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2597
2598
	// la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2599
	#if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2600
	#if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2601
	#if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2602
	#if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2603
2604
	if (!defined('_PASS_LONGUEUR_MINI')) {
2605
		define('_PASS_LONGUEUR_MINI', 6);
2606
	}
2607
2608
2609
	// Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2610
	if (!defined('_IMG_QUALITE')) {
2611
		define('_IMG_QUALITE', 85);
2612
	} # valeur par defaut
2613
	if (!defined('_IMG_GD_QUALITE')) {
2614
		define('_IMG_GD_QUALITE', _IMG_QUALITE);
2615
	} # surcharge pour la lib GD
2616
	if (!defined('_IMG_CONVERT_QUALITE')) {
2617
		define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
2618
	} # surcharge pour imagick en ligne de commande
2619
	// Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2620
	if (!defined('_IMG_IMAGICK_QUALITE')) {
2621
		define('_IMG_IMAGICK_QUALITE', 75);
2622
	} # surcharge pour imagick en PHP
2623
2624
	if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2625
		define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2626
	} // poids en octet
2627
2628
	// qq chaines standard
2629
	if (!defined('_ACCESS_FILE_NAME')) {
2630
		define('_ACCESS_FILE_NAME', '.htaccess');
2631
	}
2632
	if (!defined('_AUTH_USER_FILE')) {
2633
		define('_AUTH_USER_FILE', '.htpasswd');
2634
	}
2635
	if (!defined('_SPIP_DUMP')) {
2636
		define('_SPIP_DUMP', 'dump@nom_site@@[email protected]');
2637
	}
2638
	if (!defined('_CACHE_RUBRIQUES')) {
2639
		/** Fichier cache pour le navigateur de rubrique du bandeau */
2640
		define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
2641
	}
2642
	if (!defined('_CACHE_RUBRIQUES_MAX')) {
2643
		/** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2644
		define('_CACHE_RUBRIQUES_MAX', 500);
2645
	}
2646
2647
	if (!defined('_EXTENSION_SQUELETTES')) {
2648
		define('_EXTENSION_SQUELETTES', 'html');
2649
	}
2650
2651
	if (!defined('_DOCTYPE_ECRIRE')) {
2652
		/** Définit le doctype de l’espace privé */
2653
		define('_DOCTYPE_ECRIRE', "<!DOCTYPE html>\n");
2654
	}
2655
	if (!defined('_DOCTYPE_AIDE')) {
2656
		/** Définit le doctype de l’aide en ligne */
2657
		define('_DOCTYPE_AIDE',
2658
		"<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2659
	}
2660
2661
	if (!defined('_SPIP_SCRIPT')) {
2662
		/** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2663
		 * le script de l'espace public, alias index.php */
2664
		define('_SPIP_SCRIPT', 'spip.php');
2665
	}
2666
	if (!defined('_SPIP_PAGE')) {
2667
		/** Argument page, personalisable en cas de conflit avec un autre script */
2668
		define('_SPIP_PAGE', 'page');
2669
	}
2670
2671
	// le script de l'espace prive
2672
	// Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2673
	// les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2674
	// meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
2675
	if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2676
		if (!empty($_SERVER['SERVER_SOFTWARE']) and preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE'])) {
2677
			define('_SPIP_ECRIRE_SCRIPT', 'index.php');
2678
		} else {
2679
			define('_SPIP_ECRIRE_SCRIPT', '');
2680
		}
2681
	}
2682
2683
2684
	if (!defined('_SPIP_AJAX')) {
2685
		define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2686
			? 1
2687
			: (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
2688
	}
2689
2690
	// La requete est-elle en ajax ?
2691
	if (!defined('_AJAX')) {
2692
		define('_AJAX',
2693
			(isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2694
				or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2695
				or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2696
				or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2697
			)
2698
			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
2699
		);
2700
	}
2701
2702
	# nombre de pixels maxi pour calcul de la vignette avec gd
2703
	# au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2704
	# les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2705
	if (!defined('_IMG_GD_MAX_PIXELS')) {
2706
		define('_IMG_GD_MAX_PIXELS',
2707
		(isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2708
			? $GLOBALS['meta']['max_taille_vignettes']
2709
			: 0);
2710
	}
2711
2712
	if (!defined('_MEMORY_LIMIT_MIN')) {
2713
		define('_MEMORY_LIMIT_MIN', 16);
2714
	} // en Mo
2715
	// si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2716
	// on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2717
	// il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2718
	if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
2719
		if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2720
			$unit = strtolower(substr($memory, -1));
2721
			$memory = substr($memory, 0, -1);
2722
			switch ($unit) {
2723
				// Le modifieur 'G' est disponible depuis PHP 5.1.0
2724
				case 'g':
2725
					$memory *= 1024;
2726
				case 'm':
2727
					$memory *= 1024;
2728
				case 'k':
2729
					$memory *= 1024;
2730
			}
2731
			if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
2732
				@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...
2733
				if (trim(ini_get('memory_limit')) != $m) {
2734
					if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2735
						define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2736
					} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2737
				}
2738
			}
2739
		} else {
2740
			if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2741
				define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2742
			}
2743
		} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2744
	}
2745
	// Protocoles a normaliser dans les chaines de langues
2746
	if (!defined('_PROTOCOLES_STD')) {
2747
		define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2748
	}
2749
2750
	init_var_mode();
2751
}
2752
2753
/**
2754
 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2755
 * la validité du cache ou certains affichages spéciaux.
2756
 *
2757
 * Le paramètre d'URL `var_mode` permet de
2758
 * modifier la pérennité du cache, recalculer des urls
2759
 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2760
 * d'afficher un écran de débug ou des traductions non réalisées.
2761
 *
2762
 * En fonction de ces paramètres dans l'URL appelante, on définit
2763
 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2764
 *
2765
 * Le paramètre `var_mode` accepte ces valeurs :
2766
 *
2767
 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2768
 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2769
 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2770
 * - `debug` :  modifie l'affichage activant le mode "debug"
2771
 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2772
 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2773
 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2774
 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2775
 *
2776
 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2777
 *
2778
 * @note
2779
 *     Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2780
 *     le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2781
 * `   var_mode` (calcul ou recalcul).
2782
 */
2783
function init_var_mode() {
2784
	static $done = false;
2785
	if (!$done) {
2786
2787
		if (isset($_GET['var_mode'])) {
2788
			$var_mode = explode(',', $_GET['var_mode']);
2789
			// tout le monde peut calcul/recalcul
2790
			if (!defined('_VAR_MODE')) {
2791
				if (in_array('recalcul', $var_mode)) {
2792
					define('_VAR_MODE', 'recalcul');
2793
				} elseif (in_array('calcul', $var_mode)) {
2794
					define('_VAR_MODE', 'calcul');
2795
				}
2796
			}
2797
			$var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2798
			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...
2799
				include_spip('inc/autoriser');
2800
				// autoriser preview si preview seulement, et sinon autoriser debug
2801
				if (autoriser(
2802
					($_GET['var_mode'] == 'preview')
2803
						? 'previsualiser'
2804
						: 'debug'
2805
				)) {
2806 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...
2807
						// forcer le calcul pour passer dans traduire
2808
						if (!defined('_VAR_MODE')) {
2809
							define('_VAR_MODE', 'calcul');
2810
						}
2811
						// et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2812
						if (!defined('_VAR_NOCACHE')) {
2813
							define('_VAR_NOCACHE', true);
2814
						}
2815
						$var_mode = array_diff($var_mode, array('traduction'));
2816
					}
2817 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...
2818
						// basculer sur les criteres de preview dans les boucles
2819
						if (!defined('_VAR_PREVIEW')) {
2820
							define('_VAR_PREVIEW', true);
2821
						}
2822
						// forcer le calcul
2823
						if (!defined('_VAR_MODE')) {
2824
							define('_VAR_MODE', 'calcul');
2825
						}
2826
						// et ne pas enregistrer de cache
2827
						if (!defined('_VAR_NOCACHE')) {
2828
							define('_VAR_NOCACHE', true);
2829
						}
2830
						$var_mode = array_diff($var_mode, array('preview'));
2831
					}
2832 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...
2833
						// forcer le compilo et ignorer les caches existants
2834
						if (!defined('_VAR_MODE')) {
2835
							define('_VAR_MODE', 'calcul');
2836
						}
2837
						if (!defined('_VAR_INCLURE')) {
2838
							define('_VAR_INCLURE', true);
2839
						}
2840
						// et ne pas enregistrer de cache
2841
						if (!defined('_VAR_NOCACHE')) {
2842
							define('_VAR_NOCACHE', true);
2843
						}
2844
						$var_mode = array_diff($var_mode, array('inclure'));
2845
					}
2846 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...
2847
						// forcer le compilo et ignorer les caches existants
2848
						if (!defined('_VAR_MODE')) {
2849
							define('_VAR_MODE', 'calcul');
2850
						}
2851
						if (!defined('_VAR_URLS')) {
2852
							define('_VAR_URLS', true);
2853
						}
2854
						$var_mode = array_diff($var_mode, array('urls'));
2855
					}
2856 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...
2857
						// forcer le compilo et ignorer les caches existants
2858
						if (!defined('_VAR_MODE')) {
2859
							define('_VAR_MODE', 'calcul');
2860
						}
2861
						// indiquer qu'on doit recalculer les images
2862
						if (!defined('_VAR_IMAGES')) {
2863
							define('_VAR_IMAGES', true);
2864
						}
2865
						$var_mode = array_diff($var_mode, array('images'));
2866
					}
2867 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...
2868
						if (!defined('_VAR_MODE')) {
2869
							define('_VAR_MODE', 'debug');
2870
						}
2871
						// et ne pas enregistrer de cache
2872
						if (!defined('_VAR_NOCACHE')) {
2873
							define('_VAR_NOCACHE', true);
2874
						}
2875
						$var_mode = array_diff($var_mode, array('debug'));
2876
					}
2877
					if (count($var_mode) and !defined('_VAR_MODE')) {
2878
						define('_VAR_MODE', reset($var_mode));
2879
					}
2880
					if (isset($GLOBALS['visiteur_session']['nom'])) {
2881
						spip_log($GLOBALS['visiteur_session']['nom']
2882
							. " " . _VAR_MODE);
2883
					}
2884
				} // pas autorise ?
2885
				else {
2886
					// si on n'est pas connecte on se redirige
2887
					if (!$GLOBALS['visiteur_session']) {
2888
						include_spip('inc/headers');
2889
						redirige_par_entete(generer_url_public('login',
2890
							'url=' . rawurlencode(
2891
								parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2892
							), true));
2893
					}
2894
					// sinon tant pis
2895
				}
2896
			}
2897
		}
2898
		if (!defined('_VAR_MODE')) {
2899
			/**
2900
			 * Indique le mode de calcul ou d'affichage de la page.
2901
			 * @see init_var_mode()
2902
			 */
2903
			define('_VAR_MODE', false);
2904
		}
2905
		$done = true;
2906
	}
2907
}
2908
2909
// Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2910
// supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2911
// la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2912
// http://code.spip.net/@spip_desinfecte
2913
function spip_desinfecte(&$t, $deep = true) {
2914
	static $magic_quotes;
2915
	if (!isset($magic_quotes)) {
2916
		$magic_quotes = @get_magic_quotes_gpc();
2917
	}
2918
2919
	foreach ($t as $key => $val) {
2920
		if (is_string($t[$key])) {
2921
			if ($magic_quotes) {
2922
				$t[$key] = stripslashes($t[$key]);
2923
			}
2924
			$t[$key] = str_replace(chr(0), '-', $t[$key]);
2925
		} // traiter aussi les "texte_plus" de article_edit
2926
		else {
2927
			if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2928
				spip_desinfecte($t[$key], $deep);
2929
			}
2930
		}
2931
	}
2932
}
2933
2934
//  retourne le statut du visiteur s'il s'annonce
2935
2936
// http://code.spip.net/@verifier_visiteur
2937
function verifier_visiteur() {
2938
	// Rq: pour que cette fonction marche depuis mes_options
2939
	// il faut forcer l'init si ce n'est fait
2940
	// mais on risque de perturber des plugins en initialisant trop tot
2941
	// certaines constantes
2942
	@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...
2943
		(_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
2944
		(_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
2945
		(_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
2946
		(_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
2947
	);
2948
2949
	// Demarrer une session NON AUTHENTIFIEE si on donne son nom
2950
	// dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
2951
	// Attention on separe bien session_nom et nom, pour eviter
2952
	// les melanges entre donnees SQL et variables plus aleatoires
2953
	$variables_session = array('session_nom', 'session_email');
2954
	foreach ($variables_session as $var) {
2955
		if (_request($var) !== null) {
2956
			$init = true;
2957
			break;
2958
		}
2959
	}
2960
	if (isset($init)) {
2961
		#@spip_initialisation_suite();
2962
		$session = charger_fonction('session', 'inc');
2963
		$session();
2964
		include_spip('inc/texte');
2965
		foreach ($variables_session as $var) {
2966
			if (($a = _request($var)) !== null) {
2967
				$GLOBALS['visiteur_session'][$var] = safehtml($a);
2968
			}
2969
		}
2970
		if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
2971
			$GLOBALS['visiteur_session']['id_auteur'] = 0;
2972
		}
2973
		$session($GLOBALS['visiteur_session']);
2974
2975
		return 0;
2976
	}
2977
2978
	$h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
2979
	if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
2980
2981
		$session = charger_fonction('session', 'inc');
2982
		if ($session()) {
2983
			return $GLOBALS['visiteur_session']['statut'];
2984
		}
2985
		if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
2986
			include_spip('inc/auth');
2987
			$h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
2988
		}
2989
		if ($h) {
2990
			$GLOBALS['visiteur_session'] = $h;
2991
2992
			return $GLOBALS['visiteur_session']['statut'];
2993
		}
2994
	}
2995
2996
	// au moins son navigateur nous dit la langue preferee de cet inconnu
2997
	include_spip('inc/lang');
2998
	utiliser_langue_visiteur();
2999
3000
	return false;
3001
}
3002
3003
3004
/**
3005
 * Sélectionne la langue donnée en argument et mémorise la courante
3006
 *
3007
 * Restaure l'ancienne langue si appellée sans argument.
3008
 *
3009
 * @note
3010
 *     On pourrait économiser l'empilement en cas de non changemnt
3011
 *     et lui faire retourner `False` pour prevenir l'appelant
3012
 *     Le noyau de Spip sait le faire, mais pour assurer la compatibilité
3013
 *     cette fonction retourne toujours non `False`
3014
 *
3015
 * @uses changer_langue()
3016
 * @param null|string $lang
3017
 *     - string : Langue à appliquer,
3018
 *     - null : Pour restituer la dernière langue mémorisée.
3019
 * @return string
3020
 *     - string Langue utilisée.
3021
 **/
3022
function lang_select($lang = null) {
3023
	static $pile_langues = array();
3024
	if (!function_exists('changer_langue')) {
3025
		include_spip('inc/lang');
3026
	}
3027
	if ($lang === null) {
3028
		$lang = array_pop($pile_langues);
3029
	} else {
3030
		array_push($pile_langues, $GLOBALS['spip_lang']);
3031
	}
3032
	if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
3033
		return $lang;
3034
	}
3035
	changer_langue($lang);
3036
3037
	return $lang;
3038
}
3039
3040
/**
3041
 * Renvoie une chaîne qui identifie la session courante
3042
 *
3043
 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
3044
 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
3045
 * de fichier cache.
3046
 *
3047
 * @pipeline_appel definir_session
3048
 *
3049
 * @param bool $force
3050
 * @return string
3051
 *     Identifiant de la session
3052
 **/
3053
function spip_session($force = false) {
3054
	static $session;
3055
	if ($force or !isset($session)) {
3056
		$s = pipeline('definir_session',
3057
			$GLOBALS['visiteur_session']
3058
				? serialize($GLOBALS['visiteur_session'])
3059
				. '_' . @$_COOKIE['spip_session']
3060
				: ''
3061
		);
3062
		$session = $s ? substr(md5($s), 0, 8) : '';
3063
	}
3064
3065
	#spip_log('session: '.$session);
3066
	return $session;
3067
}
3068
3069
3070
/**
3071
 * Retourne un lien vers une aide
3072
 *
3073
 * Aide, aussi depuis l'espace privé à présent.
3074
 * Surchargeable mais pas d'erreur fatale si indisponible.
3075
 *
3076
 * @param string $aide
3077
 *    Cle d'identification de l'aide desiree
3078
 * @param bool $distante
3079
 *    Generer une url locale (par defaut)
3080
 *    ou une url distante [directement sur spip.net]
3081
 * @return
3082
 *    Lien sur une icone d'aide
3083
 **/
3084
function aider($aide = '', $distante = false) {
3085
	$aider = charger_fonction('aide', 'inc', true);
3086
3087
	return $aider ? $aider($aide, '', array(), $distante) : '';
3088
}
3089
3090
/**
3091
 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3092
 *
3093
 * Si l’utiliseur est un webmestre.
3094
 */
3095
function exec_info_dist() {
3096
3097
	include_spip('inc/autoriser');
3098
	if (autoriser('webmestre')) {
3099
		phpinfo();
3100
	} else {
3101
		include_spip('inc/filtres');
3102
		sinon_interdire_acces();
3103
	}
3104
}
3105
3106
/**
3107
 * Génère une erreur de squelette
3108
 *
3109
 * Génère une erreur de squelette qui sera bien visible par un
3110
 * administrateur authentifié lors d'une visite de la page en erreur
3111
 *
3112
 * @param bool|string|array $message
3113
 *     - Message d'erreur (string|array)
3114
 *     - false pour retourner le texte des messages d'erreurs
3115
 *     - vide pour afficher les messages d'erreurs
3116
 * @param string|array|object $lieu
3117
 *     Lieu d'origine de l'erreur
3118
 * @return null|string
3119
 *     - Rien dans la plupart des cas
3120
 *     - string si $message à false.
3121
 **/
3122
function erreur_squelette($message = '', $lieu = '') {
3123
	$debusquer = charger_fonction('debusquer', 'public');
3124
	if (is_array($lieu)) {
3125
		include_spip('public/compiler');
3126
		$lieu = reconstruire_contexte_compil($lieu);
3127
	}
3128
3129
	return $debusquer($message, $lieu);
3130
}
3131
3132
/**
3133
 * Calcule un squelette avec un contexte et retourne son contenu
3134
 *
3135
 * La fonction de base de SPIP : un squelette + un contexte => une page.
3136
 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3137
 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3138
 * $options permet de selectionner les options suivantes :
3139
 *
3140
 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3141
 * - raw  => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3142
 *          pour chaque $fond fourni.
3143
 *
3144
 * @api
3145
 * @param string /array $fond
3146
 *     - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3147
 *     - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3148
 * @param array $contexte
3149
 *     - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3150
 *     - La langue est transmise automatiquement (sauf option étoile).
3151
 * @param array $options
3152
 *     Options complémentaires :
3153
 *
3154
 *     - trim   : applique un trim sur le résultat (true par défaut)
3155
 *     - raw    : retourne un tableau d'information sur le squelette (false par défaut)
3156
 *     - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3157
 *                équivalent de INCLURE*
3158
 *     - ajax   : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3159
 * @param string $connect
3160
 *     Non du connecteur de bdd a utiliser
3161
 * @return string|array
3162
 *     - Contenu du squelette calculé
3163
 *     - ou tableau d'information sur le squelette.
3164
 */
3165
function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3166
	if (!function_exists('evaluer_fond')) {
3167
		include_spip('public/assembler');
3168
	}
3169
	// assurer la compat avec l'ancienne syntaxe
3170
	// (trim etait le 3eme argument, par defaut a true)
3171
	if (!is_array($options)) {
3172
		$options = array('trim' => $options);
3173
	}
3174
	if (!isset($options['trim'])) {
3175
		$options['trim'] = true;
3176
	}
3177
3178
	if (isset($contexte['connect'])) {
3179
		$connect = $contexte['connect'];
3180
		unset($contexte['connect']);
3181
	}
3182
3183
	$texte = "";
3184
	$pages = array();
3185
	$lang_select = '';
3186 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...
3187
		// Si on a inclus sans fixer le critere de lang, on prend la langue courante
3188
		if (!isset($contexte['lang'])) {
3189
			$contexte['lang'] = $GLOBALS['spip_lang'];
3190
		}
3191
3192
		if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3193
			$lang_select = lang_select($contexte['lang']);
3194
		}
3195
	}
3196
3197
	if (!isset($GLOBALS['_INC_PUBLIC'])) {
3198
		$GLOBALS['_INC_PUBLIC'] = 0;
3199
	}
3200
3201
	$GLOBALS['_INC_PUBLIC']++;
3202
3203
	// fix #4235
3204
	$cache_utilise_session_appelant	= (isset($GLOBALS['cache_utilise_session']) ? $GLOBALS['cache_utilise_session'] : null);
3205
3206
3207
	foreach (is_array($fond) ? $fond : array($fond) as $f) {
3208
		
3209
		unset($GLOBALS['cache_utilise_session']);	// fix #4235
3210
3211
		$page = evaluer_fond($f, $contexte, $connect);
3212
		if ($page === '') {
3213
			$c = isset($options['compil']) ? $options['compil'] : '';
3214
			$a = array('fichier' => $f);
3215
			$erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3216
			erreur_squelette($erreur, $c);
3217
			// eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3218
			$page = array('texte' => '', 'erreur' => $erreur);
3219
		}
3220
3221
		$page = pipeline('recuperer_fond', array(
3222
			'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3223
			'data' => $page
3224
		));
3225
		if (isset($options['ajax']) and $options['ajax']) {
3226
			if (!function_exists('encoder_contexte_ajax')) {
3227
				include_spip('inc/filtres');
3228
			}
3229
			$page['texte'] = encoder_contexte_ajax(
3230
				array_merge(
3231
					$contexte,
3232
					array('fond' => $f),
3233
					($connect ? array('connect' => $connect) : array())
3234
				),
3235
				'',
3236
				$page['texte'],
3237
				$options['ajax']
3238
			);
3239
		}
3240
3241
		if (isset($options['raw']) and $options['raw']) {
3242
			$pages[] = $page;
3243
		} else {
3244
			$texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
3245
		}
3246
		
3247
		// contamination de la session appelante, pour les inclusions statiques
3248
		if (isset($page['invalideurs']['session'])){
3249
			$cache_utilise_session_appelant = $page['invalideurs']['session'];
3250
		}
3251
	}
3252
3253
	// restaurer le sessionnement du contexte appelant, 
3254
	// éventuellement contaminé si on vient de récupérer une inclusion statique sessionnée
3255
	if (isset($cache_utilise_session_appelant)) {
3256
		$GLOBALS['cache_utilise_session'] = $cache_utilise_session_appelant;
3257
	}
3258
3259
	$GLOBALS['_INC_PUBLIC']--;
3260
3261
	if ($lang_select) {
3262
		lang_select();
3263
	}
3264
	if (isset($options['raw']) and $options['raw']) {
3265
		return is_array($fond) ? $pages : reset($pages);
3266
	} else {
3267
		return $options['trim'] ? ltrim($texte) : $texte;
3268
	}
3269
}
3270
3271
/**
3272
 * Trouve un squelette dans le repertoire modeles/
3273
 *
3274
 * @param  $nom
3275
 * @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...
3276
 */
3277
function trouve_modele($nom) {
3278
	return trouver_fond($nom, 'modeles/');
3279
}
3280
3281
/**
3282
 * Trouver un squelette dans le chemin
3283
 * on peut specifier un sous-dossier dans $dir
3284
 * si $pathinfo est a true, retourne un tableau avec
3285
 * les composantes du fichier trouve
3286
 * + le chemin complet sans son extension dans fond
3287
 *
3288
 * @param string $nom
3289
 * @param string $dir
3290
 * @param bool $pathinfo
3291
 * @return array|string
3292
 */
3293
function trouver_fond($nom, $dir = '', $pathinfo = false) {
3294
	$f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
3295
	if (!$pathinfo) {
3296
		return $f;
3297
	}
3298
	// renvoyer un tableau detaille si $pathinfo==true
3299
	$p = pathinfo($f);
3300
	if (!isset($p['extension']) or !$p['extension']) {
3301
		$p['extension'] = _EXTENSION_SQUELETTES;
3302
	}
3303
	if (!isset($p['extension']) or !$p['filename']) {
3304
		$p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3305
	}
3306
	$p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
3307
3308
	return $p;
3309
}
3310
3311
/**
3312
 * Teste, pour un nom de page de l'espace privé, s'il est possible
3313
 * de générer son contenu.
3314
 *
3315
 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3316
 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3317
 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3318
 *
3319
 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3320
 *          ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3321
 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3322
 *
3323
 * @param string $nom
3324
 *     Nom de la page
3325
 * @return string
3326
 *     Nom de l'exec, sinon chaîne vide.
3327
 **/
3328
function tester_url_ecrire($nom) {
3329
	static $exec = array();
3330
	if (isset($exec[$nom])) {
3331
		return $exec[$nom];
3332
	}
3333
	// tester si c'est une page en squelette
3334
	if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3335
		return $exec[$nom] = 'fond';
3336
	} // compat skels orthogonaux version precedente
3337
	elseif (trouver_fond($nom, 'prive/exec/')) {
3338
		return $exec[$nom] = 'fond_monobloc';
3339
	} // echafaudage d'un fond !
3340
	elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3341
		return $exec[$nom] = 'fond';
3342
	}
3343
	// attention, il ne faut pas inclure l'exec ici
3344
	// car sinon #URL_ECRIRE provoque des inclusions
3345
	// et des define intrusifs potentiels
3346
	return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
3347
}
3348
3349
3350
/**
3351
 * Teste la présence d’une extension PHP
3352
 *
3353
 * @deprected Utiliser directement la fonction native `extension_loaded($module)`
3354
 * @example
3355
 *     ```
3356
 *     $ok = charger_php_extension('sqlite');
3357
 *     ```
3358
 * @param string $module Nom du module à charger
3359
 * @return bool true si le module est chargé
3360
 **/
3361
function charger_php_extension($module) {
3362
	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...
3363
		return true;
3364
	}
3365
	return false;
3366
}
3367
3368
3369
/**
3370
 * Indique si le code HTML5 est permis sur le site public
3371
 *
3372
 * @return bool
3373
 *     true si et seulement si la configuration autorise le code HTML5 sur le site public
3374
 **/
3375
function html5_permis() {
3376
	return (isset($GLOBALS['meta']['version_html_max'])
3377
		and ('html5' == $GLOBALS['meta']['version_html_max']));
3378
}
3379
3380
/**
3381
 * Lister les formats image acceptes par les lib et fonctions images
3382
 * @param bool $gd
3383
 * @param bool $svg_allowed
3384
 * @return array
3385
 */
3386
function formats_image_acceptables($gd = false, $svg_allowed = true) {
3387
	$config = ($gd ? "gd_formats" : "formats_graphiques");
3388
	$formats = (isset($GLOBALS['meta'][$config]) ? $GLOBALS['meta'][$config] : 'png,gif,jpg');
3389
	$formats = explode(',', $formats);
3390
	$formats = array_filter($formats);
3391
	$formats = array_map('trim', $formats);
3392
3393
	if ($svg_allowed) {
3394
		$formats[] = 'svg';
3395
	}
3396
3397
	return $formats;
3398
}
3399
3400
/**
3401
 * Extension de la fonction getimagesize pour supporter aussi les images SVG
3402
 * @param string $fichier
3403
 * @return array|bool
3404
 */
3405
function spip_getimagesize($fichier) {
3406
	if (!$imagesize = @getimagesize($fichier)) {
3407
3408
		include_spip("inc/svg");
3409
		if ($attrs = svg_lire_attributs($fichier)) {
3410
			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...
3411
			$imagesize = [
3412
				$width,
3413
				$height,
3414
				IMAGETYPE_SVG,
3415
				"width=\"{$width}\" height=\"{$height}\"",
3416
				"mime" => "image/svg+xml"
3417
			];
3418
		}
3419
	}
3420
	return $imagesize;
3421
}
3422
3423
3424
3425
/*
3426
 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3427
 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3428
 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3429
 */
3430
3431
/**
3432
 * lire_meta : fonction dépréciée
3433
 *
3434
 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3435
 * @see lire_config()
3436
 * @param string $nom Clé de meta à lire
3437
 * @return mixed Valeur de la meta.
3438
 **/
3439
function lire_meta($nom) {
3440
	return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
3441
}
3442
3443
3444
/**
3445
 * ecrire_metas : fonction dépréciée
3446
 *
3447
 * @deprecated
3448
 **/
3449
function ecrire_metas() { }
3450
3451
/**
3452
 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3453
 * au prochain passage dans l'espace prive
3454
 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3455
 * les alertes affichees une fois sont effacees
3456
 *
3457
 * @param string $nom
3458
 * @param string $message
3459
 * @param string $statut
3460
 */
3461
function avertir_auteurs($nom, $message, $statut = '') {
3462
	$alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3463
	if (!$alertes
3464
		or !is_array($alertes = unserialize($alertes))
3465
	) {
3466
		$alertes = array();
3467
	}
3468
3469
	if (!isset($alertes[$statut])) {
3470
		$alertes[$statut] = array();
3471
	}
3472
	$alertes[$statut][$nom] = $message;
3473
	ecrire_meta("message_alertes_auteurs", serialize($alertes));
3474
}
3475
3476
if (PHP_VERSION_ID < 50500) {
3477
	if (!function_exists('array_column')) {
3478
		/**
3479
		 * Returns the values from a single column of the input array, identified by
3480
		 * the $columnKey.
3481
		 *
3482
		 * Optionally, you may provide an $indexKey to index the values in the returned
3483
		 * array by the values from the $indexKey column in the input array.
3484
		 *
3485
		 * @link http://php.net/manual/fr/function.array-column.php
3486
		 * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
3487
		 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
3488
		 * @license http://opensource.org/licenses/MIT MIT
3489
		 *
3490
		 * @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...
3491
		 *                     a column of values.
3492
		 * @param mixed $columnKey The column of values to return. This value may be the
3493
		 *                         integer key of the column you wish to retrieve, or it
3494
		 *                         may be the string key name for an associative array.
3495
		 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
3496
		 *                        the returned array. This value may be the integer key
3497
		 *                        of the column, or it may be the string key name.
3498
		 * @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...
3499
		 */
3500
		function array_column($input = null, $columnKey = null, $indexKey = null)
0 ignored issues
show
Unused Code introduced by
The parameter $input is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $columnKey is not used and could be removed.

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

Loading history...
Unused Code introduced by
The parameter $indexKey is not used and could be removed.

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

Loading history...
3501
		{
3502
			// Using func_get_args() in order to check for proper number of
3503
			// parameters and trigger errors exactly as the built-in array_column()
3504
			// does in PHP 5.5.
3505
			$argc = func_num_args();
3506
			$params = func_get_args();
3507
3508
			if ($argc < 2) {
3509
				trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
3510
				return null;
3511
			}
3512
3513
			if (!is_array($params[0])) {
3514
				trigger_error(
3515
					'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
3516
					E_USER_WARNING
3517
				);
3518
				return null;
3519
			}
3520
3521 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...
3522
				&& !is_float($params[1])
3523
				&& !is_string($params[1])
3524
				&& $params[1] !== null
3525
				&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
3526
			) {
3527
				trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
3528
				return false;
3529
			}
3530
3531 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...
3532
				&& !is_int($params[2])
3533
				&& !is_float($params[2])
3534
				&& !is_string($params[2])
3535
				&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
3536
			) {
3537
				trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
3538
				return false;
3539
			}
3540
3541
			$paramsInput = $params[0];
3542
			$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
3543
3544
			$paramsIndexKey = null;
3545
			if (isset($params[2])) {
3546
				if (is_float($params[2]) || is_int($params[2])) {
3547
					$paramsIndexKey = (int) $params[2];
3548
				} else {
3549
					$paramsIndexKey = (string) $params[2];
3550
				}
3551
			}
3552
3553
			$resultArray = array();
3554
3555
			foreach ($paramsInput as $row) {
3556
				$key = $value = null;
3557
				$keySet = $valueSet = false;
3558
3559
				if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
3560
					$keySet = true;
3561
					$key = (string) $row[$paramsIndexKey];
3562
				}
3563
3564
				if ($paramsColumnKey === null) {
3565
					$valueSet = true;
3566
					$value = $row;
3567
				} elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
3568
					$valueSet = true;
3569
					$value = $row[$paramsColumnKey];
3570
				}
3571
3572
				if ($valueSet) {
3573
					if ($keySet) {
3574
						$resultArray[$key] = $value;
3575
					} else {
3576
						$resultArray[] = $value;
3577
					}
3578
				}
3579
3580
			}
3581
3582
			return $resultArray;
3583
		}
3584
3585
	}
3586
}
3587