Completed
Push — master ( 65d759...defb8f )
by cam
19:03
created

utils.php ➔ generer_url_retour()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2017                                                *
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;
0 ignored issues
show
Coding Style Compatibility introduced by
The function charger_fonction() contains an exit expression.

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

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

Loading history...
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;
0 ignored issues
show
Coding Style Compatibility introduced by
The function charger_fonction() contains an exit expression.

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

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

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

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

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

Loading history...
1539
		}
1540
	}
1541
1542 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...
1543
		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1544
		if (is_null($GLOBALS['path_files'])) {
1545
			return false;
1546
		}
1547
		define('_SAUVER_CHEMIN', true);
1548
	}
1549
1550
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1551
}
1552
1553
function clear_path_cache() {
1554
	$GLOBALS['path_files'] = array();
1555
	spip_unlink(_CACHE_CHEMIN);
1556
}
1557
1558
function load_path_cache() {
1559
	// charger le path des plugins
1560
	if (@is_readable(_CACHE_PLUGINS_PATH)) {
1561
		include_once(_CACHE_PLUGINS_PATH);
1562
	}
1563
	$GLOBALS['path_files'] = array();
1564
	// si le visiteur est admin,
1565
	// on ne recharge pas le cache pour forcer sa mise a jour
1566
	if (
1567
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1568
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1569
		// utiliser le cookie est un pis aller qui marche 'en general'
1570
		// on blinde par un second test au moment de la lecture de la session
1571
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1572
		// et en ignorant ce cache en cas de recalcul explicite
1573
	!_request('var_mode')
1574
	) {
1575
		// on essaye de lire directement sans verrou pour aller plus vite
1576
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
1577
			// mais si semble corrompu on relit avec un verrou
1578
			if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1579
				lire_fichier(_CACHE_CHEMIN, $contenu);
1580
				if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1581
					$GLOBALS['path_files'] = array();
1582
				}
1583
			}
1584
		}
1585
	}
1586
}
1587
1588
function save_path_cache() {
1589
	if (defined('_SAUVER_CHEMIN')
1590
		and _SAUVER_CHEMIN
1591
	) {
1592
		ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
1593
	}
1594
}
1595
1596
1597
/**
1598
 * Trouve tous les fichiers du path correspondants à un pattern
1599
 *
1600
 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1601
 * par un `find_in_path()`
1602
 *
1603
 * @api
1604
 * @uses creer_chemin()
1605
 * @uses preg_files()
1606
 *
1607
 * @param string $dir
1608
 * @param string $pattern
1609
 * @param bool $recurs
1610
 * @return array
1611
 */
1612
function find_all_in_path($dir, $pattern, $recurs = false) {
1613
	$liste_fichiers = array();
1614
	$maxfiles = 10000;
1615
1616
	// cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1617
	// on a pas encore inclus flock.php
1618
	if (!function_exists('preg_files')) {
1619
		include_once _ROOT_RESTREINT . 'inc/flock.php';
1620
	}
1621
1622
	// Parcourir le chemin
1623
	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...
1624
		$f = $d . $dir;
1625
		if (@is_dir($f)) {
1626
			$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...
1627
			foreach ($liste as $chemin) {
1628
				$nom = basename($chemin);
1629
				// ne prendre que les fichiers pas deja trouves
1630
				// car find_in_path prend le premier qu'il trouve,
1631
				// les autres sont donc masques
1632
				if (!isset($liste_fichiers[$nom])) {
1633
					$liste_fichiers[$nom] = $chemin;
1634
				}
1635
			}
1636
		}
1637
	}
1638
1639
	return $liste_fichiers;
1640
}
1641
1642
/**
1643
 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1644
 * @param string $nom
1645
 * @return bool
1646
 */
1647
function autoriser_sans_cookie($nom) {
1648
	static $autsanscookie = array('install', 'base_repair');
1649
	$nom = preg_replace('/.php[3]?$/', '', basename($nom));
1650
1651
	return in_array($nom, $autsanscookie);
1652
}
1653
1654
/**
1655
 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1656
 *
1657
 * @api
1658
 * @param string $id
1659
 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
1660
 * @param string $entite
1661
 *   surnom de la table SQL (donne acces au nom de cle primaire)
1662
 * @param string $args
1663
 *   query_string a placer apres cle=$id&....
1664
 * @param string $ancre
1665
 *   ancre a mettre a la fin de l'URL a produire
1666
 * @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...
1667
 *   produire l'URL publique ou privee (par defaut: selon espace)
1668
 *   si string : serveur de base de donnee (nom du connect)
1669
 * @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...
1670
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
1671
 * @return string|array
1672
 *   url codee ou fonction de decodage
1673
 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1674
 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1675
 */
1676
function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1677
	if ($public === null) {
1678
		$public = !test_espace_prive();
1679
	}
1680
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1681
1682
	if (!$public) {
1683
		if (!$entite) {
1684
			return '';
1685
		}
1686
		if (!function_exists('generer_url_ecrire_objet')) {
1687
			include_spip('inc/urls');
1688
		}
1689
		$res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1690
	} else {
1691
		if ($type === null) {
1692
			$type = (isset($GLOBALS['type_urls']))
1693
				? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1694
				: ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1695
					? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1696
		}
1697
1698
		$f = charger_fonction($type, 'urls', true);
1699
		// se rabattre sur les urls page si les urls perso non dispo
1700
		if (!$f) {
1701
			$f = charger_fonction('page', 'urls', true);
1702
		}
1703
1704
		// si $entite='', on veut la fonction de passage URL ==> id
1705
		// sinon on veut effectuer le passage id ==> URL
1706
		if (!$entite) {
1707
			return $f;
1708
		}
1709
1710
		// mais d'abord il faut tester le cas des urls sur une
1711
		// base distante
1712
		if (is_string($public)
1713
			and $g = charger_fonction('connect', 'urls', true)
1714
		) {
1715
			$f = $g;
1716
		}
1717
1718
		$res = $f(intval($id), $entite, $args, $ancre, $public);
1719
1720
	}
1721
	if ($res) {
1722
		return $res;
1723
	}
1724
	// Sinon c'est un raccourci ou compat SPIP < 2
1725
	if (!function_exists($f = 'generer_url_' . $entite)) {
1726
		if (!function_exists($f .= '_dist')) {
1727
			$f = '';
1728
		}
1729
	}
1730
	if ($f) {
1731
		$url = $f($id, $args, $ancre);
1732
		if (strlen($args)) {
1733
			$url .= strstr($url, '?')
1734
				? '&amp;' . $args
1735
				: '?' . $args;
1736
		}
1737
1738
		return $url;
1739
	}
1740
	// On a ete gentil mais la ....
1741
	spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1742
1743
	return '';
1744
}
1745
1746
function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1747
	$exec = objet_info($entite, 'url_edit');
1748
	$url = generer_url_ecrire($exec, $args);
1749
	if (intval($id)) {
1750
		$url = parametre_url($url, id_table_objet($entite), $id);
1751
	} else {
1752
		$url = parametre_url($url, 'new', 'oui');
1753
	}
1754
	if ($ancre) {
1755
		$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...
1756
	}
1757
1758
	return $url;
1759
}
1760
1761
// http://code.spip.net/@urls_connect_dist
1762
function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1763
	include_spip('base/connect_sql');
1764
	$id_type = id_table_objet($entite, $public);
1765
1766
	return _DIR_RACINE . get_spip_script('./')
1767
	. "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1768
	. (!$args ? '' : "&$args")
1769
	. (!$ancre ? '' : "#$ancre");
1770
}
1771
1772
1773
/**
1774
 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1775
 *
1776
 * @param string $url
1777
 * @return string
1778
 */
1779
function urlencode_1738($url) {
1780
	if (preg_match(',[^\x00-\x7E],sS', $url)) {
1781
		$uri = '';
1782
		for ($i = 0; $i < strlen($url); $i++) {
1783
			if (ord($a = $url[$i]) > 127) {
1784
				$a = rawurlencode($a);
1785
			}
1786
			$uri .= $a;
1787
		}
1788
		$url = $uri;
1789
	}
1790
1791
	return quote_amp($url);
1792
}
1793
1794
// http://code.spip.net/@generer_url_entite_absolue
1795
function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1796
	if (!$connect) {
1797
		$connect = true;
1798
	}
1799
	$h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1800
	if (!preg_match(',^\w+:,', $h)) {
1801
		include_spip('inc/filtres_mini');
1802
		$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...
1803
	}
1804
1805
	return $h;
1806
}
1807
1808
1809
/**
1810
 * Tester qu'une variable d'environnement est active
1811
 *
1812
 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1813
 * variables d'environnement comme `$_SERVER['HTTPS']` ou `ini_get('display_errors')`
1814
 *
1815
 * @param string|bool $truc
1816
 *     La valeur de la variable d'environnement
1817
 * @return bool
1818
 *     true si la valeur est considérée active ; false sinon.
1819
 **/
1820
function test_valeur_serveur($truc) {
1821
	if (!$truc) {
1822
		return false;
1823
	}
1824
1825
	return (strtolower($truc) !== 'off');
1826
}
1827
1828
//
1829
// Fonctions de fabrication des URL des scripts de Spip
1830
//
1831
/**
1832
 * Calcule l'url de base du site
1833
 *
1834
 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1835
 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1836
 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1837
 *
1838
 * @note
1839
 *     La globale `$profondeur_url` doit être initialisée de manière à
1840
 *     indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1841
 *     racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1842
 *     la racine 0. Sur url/perso/ elle vaut 2
1843
 *
1844
 * @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...
1845
 *    - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1846
 *    - si int : indique que l'on veut l'url pour la profondeur indiquée
1847
 *    - si bool : retourne le tableau static complet
1848
 *    - si array : réinitialise le tableau static complet avec la valeur fournie
1849
 * @return string|array
1850
 */
1851
function url_de_base($profondeur = null) {
1852
1853
	static $url = array();
1854
	if (is_array($profondeur)) {
1855
		return $url = $profondeur;
1856
	}
1857
	if ($profondeur === false) {
1858
		return $url;
1859
	}
1860
1861
	if (is_null($profondeur)) {
1862
		$profondeur = $GLOBALS['profondeur_url'];
1863
	}
1864
1865
	if (isset($url[$profondeur])) {
1866
		return $url[$profondeur];
1867
	}
1868
1869
	$http = 'http';
1870
1871
	if (
1872
		isset($_SERVER["SCRIPT_URI"])
1873
		and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1874
	) {
1875
		$http = 'https';
1876
	} elseif (
1877
		isset($_SERVER['HTTPS'])
1878
		and test_valeur_serveur($_SERVER['HTTPS'])
1879
	) {
1880
		$http = 'https';
1881
	}
1882
1883
	// note : HTTP_HOST contient le :port si necessaire
1884
	$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
1885
	// si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1886
	if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1887
		$host = $GLOBALS['meta']['adresse_site'];
1888
		if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1889
			$http = $scheme;
1890
			$host = str_replace("{$scheme}://", '', $host);
1891
		}
1892
	}
1893
	if (isset($_SERVER['SERVER_PORT'])
1894
		and $port = $_SERVER['SERVER_PORT']
1895
		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...
1896
	) {
1897
		if (!defined('_PORT_HTTP_STANDARD')) {
1898
			define('_PORT_HTTP_STANDARD', '80');
1899
		}
1900
		if (!defined('_PORT_HTTPS_STANDARD')) {
1901
			define('_PORT_HTTPS_STANDARD', '443');
1902
		}
1903
		if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
1904
			$host .= ":$port";
1905
		}
1906
		if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
1907
			$host .= ":$port";
1908
		}
1909
	}
1910
1911 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...
1912
		if (isset($_SERVER['REQUEST_URI'])) {
1913
			$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1914
		} else {
1915
			$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
1916
			if (!empty($_SERVER['QUERY_STRING'])
1917
				and !strpos($_SERVER['REQUEST_URI'], '?')
1918
			) {
1919
				$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1920
			}
1921
		}
1922
	}
1923
1924
	$url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1925
1926
	return $url[$profondeur];
1927
}
1928
1929
/**
1930
 * fonction testable de construction d'une url appelee par url_de_base()
1931
 *
1932
 * @param string $http
1933
 * @param string $host
1934
 * @param string $request
1935
 * @param int $prof
1936
 * @return string
1937
 */
1938
function url_de_($http, $host, $request, $prof = 0) {
1939
	$prof = max($prof, 0);
1940
1941
	$myself = ltrim($request, '/');
1942
	# supprimer la chaine de GET
1943
	list($myself) = explode('?', $myself);
1944
	// vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
1945
	// protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
1946 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...
1947
		$myself = explode('://',$myself);
1948
		array_shift($myself);
1949
		$myself = implode('://',$myself);
1950
		$myself = explode('/',$myself);
1951
		array_shift($myself);
1952
		$myself = implode('/',$myself);
1953
	}
1954
	$url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
1955
1956
	$url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
1957
1958
	return $url;
1959
}
1960
1961
1962
// Pour une redirection, la liste des arguments doit etre separee par "&"
1963
// Pour du code XHTML, ca doit etre &amp;
1964
// Bravo au W3C qui n'a pas ete capable de nous eviter ca
1965
// faute de separer proprement langage et meta-langage
1966
1967
// Attention, X?y=z et "X/?y=z" sont completement differents!
1968
// http://httpd.apache.org/docs/2.0/mod/mod_dir.html
1969
1970
/**
1971
 * Crée une URL vers un script de l'espace privé
1972
 *
1973
 * @example
1974
 *     ```
1975
 *     generer_url_ecrire('admin_plugin')
1976
 *     ```
1977
 *
1978
 * @param string $script
1979
 *     Nom de la page privée (xx dans exec=xx)
1980
 * @param string $args
1981
 *     Arguments à transmettre, tel que `arg1=yy&arg2=zz`
1982
 * @param bool $no_entities
1983
 *     Si false : transforme les `&` en `&amp;`
1984
 * @param bool|string $rel
1985
 *     URL relative ?
1986
 *
1987
 *     - false : l’URL sera complète et contiendra l’URL du site
1988
 *     - true : l’URL sera relavive.
1989
 *     - string : on transmet l'url à la fonction
1990
 * @return string URL
1991
 **/
1992
function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
1993
	if (!$rel) {
1994
		$rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
1995
	} else {
1996
		if (!is_string($rel)) {
1997
			$rel = _DIR_RESTREINT ? _DIR_RESTREINT :
1998
				('./' . _SPIP_ECRIRE_SCRIPT);
1999
		}
2000
	}
2001
2002
	list($script, $ancre) = array_pad(explode('#', $script), 2, null);
2003
	if ($script and ($script <> 'accueil' or $rel)) {
2004
		$args = "?exec=$script" . (!$args ? '' : "&$args");
2005
	} elseif ($args) {
2006
		$args = "?$args";
2007
	}
2008
	if ($ancre) {
2009
		$args .= "#$ancre";
2010
	}
2011
2012
	return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
2013
}
2014
2015
//
2016
// Adresse des scripts publics (a passer dans inc-urls...)
2017
//
2018
2019
2020
/**
2021
 * Retourne le nom du fichier d'exécution de SPIP
2022
 *
2023
 * @see _SPIP_SCRIPT
2024
 * @note
2025
 *   Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2026
 *   dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2027
 *
2028
 * @param string $default
2029
 *     Script par défaut
2030
 * @return string
2031
 *     Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2032
 **/
2033
function get_spip_script($default = '') {
2034
	# cas define('_SPIP_SCRIPT', '');
2035
	if (_SPIP_SCRIPT) {
2036
		return _SPIP_SCRIPT;
2037
	} else {
2038
		return $default;
2039
	}
2040
}
2041
2042
/**
2043
 * Crée une URL vers une page publique de SPIP
2044
 *
2045
 * @example
2046
 *     ```
2047
 *     generer_url_public("rubrique","id_rubrique=$id_rubrique")
2048
 *     ```
2049
 *
2050
 * @param string $script
2051
 *     Nom de la page
2052
 * @param string|array $args
2053
 *     Arguments à transmettre a l'URL,
2054
 *      soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2055
 *      soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2056
 * @param bool $no_entities
2057
 *     Si false : transforme les `&` en `&amp;`
2058
 * @param bool $rel
2059
 *     URL relative ?
2060
 *
2061
 *     - false : l’URL sera complète et contiendra l’URL du site
2062
 *     - true : l’URL sera relavive.
2063
 * @param string $action
2064
 *     - Fichier d'exécution public (spip.php par défaut)
2065
 * @return string URL
2066
 **/
2067
function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2068
	// si le script est une action (spip_pass, spip_inscription),
2069
	// standardiser vers la nouvelle API
2070
2071
	if (!$action) {
2072
		$action = get_spip_script();
2073
	}
2074
	if ($script) {
2075
		$action = parametre_url($action, _SPIP_PAGE, $script, '&');
2076
	}
2077
2078
	if ($args) {
2079
		if (is_array($args)) {
2080
			$r = '';
2081
			foreach ($args as $k => $v) {
2082
				$r .= '&' . $k . '=' . $v;
2083
			}
2084
			$args = substr($r, 1);
2085
		}
2086
		$action .=
2087
			(strpos($action, '?') !== false ? '&' : '?') . $args;
2088
	}
2089
	if (!$no_entities) {
2090
		$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...
2091
	}
2092
2093
	// ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2094
	return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2095
}
2096
2097
// http://code.spip.net/@generer_url_prive
2098
function generer_url_prive($script, $args = "", $no_entities = false) {
2099
2100
	return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2101
}
2102
2103
// Pour les formulaires en methode POST,
2104
// mettre le nom du script a la fois en input-hidden et dans le champ action:
2105
// 1) on peut ainsi memoriser le signet comme si c'etait un GET
2106
// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2107
2108
/**
2109
 * Retourne un formulaire (POST par défaut) vers un script exec
2110
 * de l’interface privée
2111
 *
2112
 * @param string $script
2113
 *     Nom de la page exec
2114
 * @param string $corps
2115
 *     Contenu du formulaire
2116
 * @param string $atts
2117
 *     Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2118
 * @param string $submit
2119
 *     Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2120
 * @return string
2121
 *     Code HTML du formulaire
2122
 **/
2123
function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2124
2125
	$script1 = explode('&', $script);
2126
	$script1 = reset($script1);
2127
2128
	return "<form action='"
2129
	. ($script ? generer_url_ecrire($script) : '')
2130
	. "' "
2131
	. ($atts ? $atts : " method='post'")
2132
	. "><div>\n"
2133
	. "<input type='hidden' name='exec' value='$script1' />"
2134
	. $corps
2135
	. (!$submit ? '' :
2136
		("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2137
	. "</div></form>\n";
2138
}
2139
2140
/**
2141
 * Générer un formulaire pour lancer une action vers $script
2142
 *
2143
 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2144
 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2145
 * qui ne sont pas destines a etre mis en signets
2146
 *
2147
 * @param string $script
2148
 * @param string $corps
2149
 * @param string $atts
2150
 * @param bool $public
2151
 * @return string
2152
 */
2153
function generer_form_action($script, $corps, $atts = '', $public = false) {
2154
	// si l'on est dans l'espace prive, on garde dans l'url
2155
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2156
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2157
	$h = (_DIR_RACINE and !$public)
2158
		? generer_url_ecrire(_request('exec'))
2159
		: generer_url_public();
2160
2161
	return "\n<form action='" .
2162
	$h .
2163
	"'" .
2164
	$atts .
2165
	">\n" .
2166
	"<div>" .
2167
	"\n<input type='hidden' name='action' value='$script' />" .
2168
	$corps .
2169
	"</div></form>";
2170
}
2171
2172
/**
2173
 * Créer une URL
2174
 *
2175
 * @param  string $script
2176
 *     Nom du script à exécuter
2177
 * @param  string $args
2178
 *     Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2179
 * @param bool $no_entities
2180
 *     Si false : transforme les & en &amp;
2181
 * @param boolean $public
2182
 *     URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2183
 *     true : l’URL sera relative.
2184
 * @return string
2185
 *     URL
2186
 */
2187
function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2188
	// si l'on est dans l'espace prive, on garde dans l'url
2189
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2190
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2191
	$url = (_DIR_RACINE and !$public)
2192
		? generer_url_ecrire(_request('exec'))
2193
		: generer_url_public('', '', false, false);
2194
	$url = parametre_url($url, 'action', $script);
2195
	if ($args) {
2196
		$url .= quote_amp('&' . $args);
2197
	}
2198
2199
	if ($no_entities) {
2200
		$url = str_replace('&amp;', '&', $url);
2201
	}
2202
2203
	return $url;
2204
}
2205
2206
2207
/**
2208
 * Fonction d'initialisation groupée pour compatibilité ascendante
2209
 *
2210
 * @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...
2211
 * @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...
2212
 * @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...
2213
 * @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...
2214
 */
2215
function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2216
	spip_initialisation_core($pi, $pa, $ti, $ta);
2217
	spip_initialisation_suite();
2218
}
2219
2220
/**
2221
 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2222
 *
2223
 * Elle définit les répertoires et fichiers non partageables
2224
 * et indique dans $test_dirs ceux devant être accessibles en écriture
2225
 * mais ne touche pas à cette variable si elle est déjà définie
2226
 * afin que mes_options.php puisse en spécifier d'autres.
2227
 *
2228
 * Elle définit ensuite les noms des fichiers et les droits.
2229
 * Puis simule un register_global=on sécurisé.
2230
 *
2231
 * @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...
2232
 * @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...
2233
 * @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...
2234
 * @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...
2235
 */
2236
function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2237
	static $too_late = 0;
2238
	if ($too_late++) {
2239
		return;
2240
	}
2241
2242
	// Declaration des repertoires
2243
2244
	// le nom du repertoire plugins/ activables/desactivables
2245
	if (!defined('_DIR_PLUGINS')) {
2246
		define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2247
	}
2248
2249
	// le nom du repertoire des extensions/ permanentes du core, toujours actives
2250
	if (!defined('_DIR_PLUGINS_DIST')) {
2251
		define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2252
	}
2253
2254
	// le nom du repertoire des librairies
2255
	if (!defined('_DIR_LIB')) {
2256
		define('_DIR_LIB', _DIR_RACINE . "lib/");
2257
	}
2258
2259
	if (!defined('_DIR_IMG')) {
2260
		define('_DIR_IMG', $pa);
2261
	}
2262
	if (!defined('_DIR_LOGOS')) {
2263
		define('_DIR_LOGOS', $pa);
2264
	}
2265
	if (!defined('_DIR_IMG_ICONES')) {
2266
		define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2267
	}
2268
2269
	if (!defined('_DIR_DUMP')) {
2270
		define('_DIR_DUMP', $ti . "dump/");
2271
	}
2272
	if (!defined('_DIR_SESSIONS')) {
2273
		define('_DIR_SESSIONS', $ti . "sessions/");
2274
	}
2275
	if (!defined('_DIR_TRANSFERT')) {
2276
		define('_DIR_TRANSFERT', $ti . "upload/");
2277
	}
2278
	if (!defined('_DIR_CACHE')) {
2279
		define('_DIR_CACHE', $ti . "cache/");
2280
	}
2281
	if (!defined('_DIR_CACHE_XML')) {
2282
		define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2283
	}
2284
	if (!defined('_DIR_SKELS')) {
2285
		define('_DIR_SKELS', _DIR_CACHE . "skel/");
2286
	}
2287
	if (!defined('_DIR_AIDE')) {
2288
		define('_DIR_AIDE', _DIR_CACHE . "aide/");
2289
	}
2290
	if (!defined('_DIR_TMP')) {
2291
		define('_DIR_TMP', $ti);
2292
	}
2293
2294
	if (!defined('_DIR_VAR')) {
2295
		define('_DIR_VAR', $ta);
2296
	}
2297
2298
	if (!defined('_DIR_ETC')) {
2299
		define('_DIR_ETC', $pi);
2300
	}
2301
	if (!defined('_DIR_CONNECT')) {
2302
		define('_DIR_CONNECT', $pi);
2303
	}
2304
	if (!defined('_DIR_CHMOD')) {
2305
		define('_DIR_CHMOD', $pi);
2306
	}
2307
2308
	if (!isset($GLOBALS['test_dirs']))
2309
		// Pas $pi car il est bon de le mettre hors ecriture apres intstall
2310
		// il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2311
	{
2312
		$GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2313
	}
2314
2315
	// Declaration des fichiers
2316
2317
	if (!defined('_CACHE_PLUGINS_PATH')) {
2318
		define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2319
	}
2320
	if (!defined('_CACHE_PLUGINS_OPT')) {
2321
		define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2322
	}
2323
	if (!defined('_CACHE_PLUGINS_FCT')) {
2324
		define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2325
	}
2326
	if (!defined('_CACHE_PIPELINES')) {
2327
		define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2328
	}
2329
	if (!defined('_CACHE_CHEMIN')) {
2330
		define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2331
	}
2332
2333
	# attention .php obligatoire pour ecrire_fichier_securise
2334
	if (!defined('_FILE_META')) {
2335
		define('_FILE_META', $ti . 'meta_cache.php');
2336
	}
2337
	if (!defined('_DIR_LOG')) {
2338
		define('_DIR_LOG', _DIR_TMP . 'log/');
2339
	}
2340
	if (!defined('_FILE_LOG')) {
2341
		define('_FILE_LOG', 'spip');
2342
	}
2343
	if (!defined('_FILE_LOG_SUFFIX')) {
2344
		define('_FILE_LOG_SUFFIX', '.log');
2345
	}
2346
2347
	// Le fichier de connexion a la base de donnees
2348
	// tient compte des anciennes versions (inc_connect...)
2349
	if (!defined('_FILE_CONNECT_INS')) {
2350
		define('_FILE_CONNECT_INS', 'connect');
2351
	}
2352
	if (!defined('_FILE_CONNECT')) {
2353
		define('_FILE_CONNECT',
2354
		(@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2355
			: (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2356
				: false)));
2357
	}
2358
2359
	// Le fichier de reglages des droits
2360
	if (!defined('_FILE_CHMOD_INS')) {
2361
		define('_FILE_CHMOD_INS', 'chmod');
2362
	}
2363
	if (!defined('_FILE_CHMOD')) {
2364
		define('_FILE_CHMOD',
2365
		(@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2366
			: false));
2367
	}
2368
2369
	if (!defined('_FILE_LDAP')) {
2370
		define('_FILE_LDAP', 'ldap.php');
2371
	}
2372
2373
	if (!defined('_FILE_TMP_SUFFIX')) {
2374
		define('_FILE_TMP_SUFFIX', '.tmp.php');
2375
	}
2376
	if (!defined('_FILE_CONNECT_TMP')) {
2377
		define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2378
	}
2379
	if (!defined('_FILE_CHMOD_TMP')) {
2380
		define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2381
	}
2382
2383
	// Definition des droits d'acces en ecriture
2384
	if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2385
		include_once _FILE_CHMOD;
2386
	}
2387
2388
	// Se mefier des fichiers mal remplis!
2389
	if (!defined('_SPIP_CHMOD')) {
2390
		define('_SPIP_CHMOD', 0777);
2391
	}
2392
2393
	if (!defined('_DEFAULT_CHARSET')) {
2394
		/** Le charset par défaut lors de l'installation */
2395
		define('_DEFAULT_CHARSET', 'utf-8');
2396
	}
2397
	if (!defined('_ROOT_PLUGINS')) {
2398
		define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2399
	}
2400
	if (!defined('_ROOT_PLUGINS_DIST')) {
2401
		define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2402
	}
2403
	if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2404
		define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2405
	}
2406
2407
	// La taille des Log
2408
	if (!defined('_MAX_LOG')) {
2409
		define('_MAX_LOG', 100);
2410
	}
2411
2412
	// Sommes-nous dans l'empire du Mal ?
2413
	// (ou sous le signe du Pingouin, ascendant GNU ?)
2414
	if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2415
		if (!defined('_OS_SERVEUR')) {
2416
			define('_OS_SERVEUR', 'windows');
2417
		}
2418
		if (!defined('_SPIP_LOCK_MODE')) {
2419
			define('_SPIP_LOCK_MODE', 1);
2420
		} // utiliser le flock php
2421
	} else {
2422
		if (!defined('_OS_SERVEUR')) {
2423
			define('_OS_SERVEUR', '');
2424
		}
2425
		if (!defined('_SPIP_LOCK_MODE')) {
2426
			define('_SPIP_LOCK_MODE', 1);
2427
		} // utiliser le flock php
2428
		#if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2429
	}
2430
2431
	// Langue par defaut
2432
	if (!defined('_LANGUE_PAR_DEFAUT')) {
2433
		define('_LANGUE_PAR_DEFAUT', 'fr');
2434
	}
2435
2436
	//
2437
	// Module de lecture/ecriture/suppression de fichiers utilisant flock()
2438
	// (non surchargeable en l'etat ; attention si on utilise include_spip()
2439
	// pour le rendre surchargeable, on va provoquer un reecriture
2440
	// systematique du noyau ou une baisse de perfs => a etudier)
2441
	include_once _ROOT_RESTREINT . 'inc/flock.php';
2442
2443
	// charger tout de suite le path et son cache
2444
	load_path_cache();
2445
2446
	// *********** traiter les variables ************
2447
2448
	//
2449
	// Securite
2450
	//
2451
2452
	// Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2453
	if (isset($_REQUEST['GLOBALS'])) {
2454
		die();
0 ignored issues
show
Coding Style Compatibility introduced by
The function spip_initialisation_core() contains an exit expression.

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

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

Loading history...
2455
	}
2456
	// nettoyer les magic quotes \' et les caracteres nuls %00
2457
	spip_desinfecte($_GET);
2458
	spip_desinfecte($_POST);
2459
	spip_desinfecte($_COOKIE);
2460
	spip_desinfecte($_REQUEST);
2461
2462
	// appliquer le cookie_prefix
2463
	if ($GLOBALS['cookie_prefix'] != 'spip') {
2464
		include_spip('inc/cookie');
2465
		recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2466
	}
2467
2468
	//
2469
	// Capacites php (en fonction de la version)
2470
	//
2471
	$GLOBALS['flag_ob'] = (function_exists("ob_start")
2472
		&& function_exists("ini_get")
2473
		&& !strstr(@ini_get('disable_functions'), 'ob_'));
2474
	$GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2475
	$GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
2476
	$GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2477
		(get_cfg_var('upload_max_filesize') > 0));
2478
2479
2480
	// Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2481 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...
2482
		$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2483
	} else {
2484
		$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
2485
		if (!empty($_SERVER['QUERY_STRING'])
2486
			and !strpos($_SERVER['REQUEST_URI'], '?')
2487
		) {
2488
			$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2489
		}
2490
	}
2491
2492
	// Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2493
	if (!defined('_RENOUVELLE_ALEA')) {
2494
		define('_RENOUVELLE_ALEA', 12 * 3600);
2495
	}
2496
	if (!defined('_DUREE_COOKIE_ADMIN')) {
2497
		define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2498
	}
2499
2500
	// charger les meta si possible et renouveller l'alea au besoin
2501
	// charge aussi effacer_meta et ecrire_meta
2502
	$inc_meta = charger_fonction('meta', 'inc');
2503
	$inc_meta();
2504
2505
	// nombre de repertoires depuis la racine
2506
	// on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2507
	// ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2508
	// le calcul est faux)
2509
	if (!_DIR_RESTREINT) {
2510
		$GLOBALS['profondeur_url'] = 1;
2511
	} else {
2512
		$uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
2513
		$uri_ref = $_SERVER["SCRIPT_NAME"];
2514
		if (!$uri_ref
2515
			// si on est appele avec un autre ti, on est sans doute en mutu
2516
			// si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2517
			// a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2518
			// s'en remettre a l'adresse du site. alea jacta est.
2519
			or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2520
		) {
2521
2522
			if (isset($GLOBALS['meta']['adresse_site'])) {
2523
				$uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2524
				$uri_ref = $uri_ref['path'] . '/';
2525
			} else {
2526
				$uri_ref = "";
2527
			}
2528
		}
2529
		if (!$uri or !$uri_ref) {
2530
			$GLOBALS['profondeur_url'] = 0;
2531
		} else {
2532
			$GLOBALS['profondeur_url'] = max(0,
2533
				substr_count($uri[0], '/')
2534
				- substr_count($uri_ref, '/'));
2535
		}
2536
	}
2537
	// s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2538
	if (_FILE_CONNECT) {
2539
		if (verifier_visiteur() == '0minirezo'
2540
			// si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2541
			and !isset($_COOKIE['spip_admin'])
2542
		) {
2543
			clear_path_cache();
2544
		}
2545
	}
2546
2547
}
2548
2549
/**
2550
 * Complements d'initialisation non critiques pouvant etre realises
2551
 * par les plugins
2552
 *
2553
 */
2554
function spip_initialisation_suite() {
2555
	static $too_late = 0;
2556
	if ($too_late++) {
2557
		return;
2558
	}
2559
2560
	// taille mini des login
2561
	if (!defined('_LOGIN_TROP_COURT')) {
2562
		define('_LOGIN_TROP_COURT', 4);
2563
	}
2564
2565
	// la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2566
	#if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2567
	#if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2568
	#if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2569
2570
	// la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2571
	#if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2572
	#if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2573
	#if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2574
	#if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2575
2576
	if (!defined('_PASS_LONGUEUR_MINI')) {
2577
		define('_PASS_LONGUEUR_MINI', 6);
2578
	}
2579
2580
2581
	// Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2582
	if (!defined('_IMG_QUALITE')) {
2583
		define('_IMG_QUALITE', 85);
2584
	} # valeur par defaut
2585
	if (!defined('_IMG_GD_QUALITE')) {
2586
		define('_IMG_GD_QUALITE', _IMG_QUALITE);
2587
	} # surcharge pour la lib GD
2588
	if (!defined('_IMG_CONVERT_QUALITE')) {
2589
		define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
2590
	} # surcharge pour imagick en ligne de commande
2591
	// Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2592
	if (!defined('_IMG_IMAGICK_QUALITE')) {
2593
		define('_IMG_IMAGICK_QUALITE', 75);
2594
	} # surcharge pour imagick en PHP
2595
2596
	if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2597
		define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2598
	} // poids en octet
2599
2600
	// qq chaines standard
2601
	if (!defined('_ACCESS_FILE_NAME')) {
2602
		define('_ACCESS_FILE_NAME', '.htaccess');
2603
	}
2604
	if (!defined('_AUTH_USER_FILE')) {
2605
		define('_AUTH_USER_FILE', '.htpasswd');
2606
	}
2607
	if (!defined('_SPIP_DUMP')) {
2608
		define('_SPIP_DUMP', 'dump@nom_site@@[email protected]');
2609
	}
2610
	if (!defined('_CACHE_RUBRIQUES')) {
2611
		/** Fichier cache pour le navigateur de rubrique du bandeau */
2612
		define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
2613
	}
2614
	if (!defined('_CACHE_RUBRIQUES_MAX')) {
2615
		/** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2616
		define('_CACHE_RUBRIQUES_MAX', 500);
2617
	}
2618
2619
	if (!defined('_EXTENSION_SQUELETTES')) {
2620
		define('_EXTENSION_SQUELETTES', 'html');
2621
	}
2622
2623
	if (!defined('_DOCTYPE_ECRIRE')) {
2624
		/** Définit le doctype de l’espace privé */
2625
		define('_DOCTYPE_ECRIRE', "<!DOCTYPE html>\n");
2626
	}
2627
	if (!defined('_DOCTYPE_AIDE')) {
2628
		/** Définit le doctype de l’aide en ligne */
2629
		define('_DOCTYPE_AIDE',
2630
		"<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2631
	}
2632
2633
	if (!defined('_SPIP_SCRIPT')) {
2634
		/** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2635
		 * le script de l'espace public, alias index.php */
2636
		define('_SPIP_SCRIPT', 'spip.php');
2637
	}
2638
	if (!defined('_SPIP_PAGE')) {
2639
		/** Argument page, personalisable en cas de conflit avec un autre script */
2640
		define('_SPIP_PAGE', 'page');
2641
	}
2642
2643
	// le script de l'espace prive
2644
	// Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2645
	// les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2646
	// meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
2647
	if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2648
		define('_SPIP_ECRIRE_SCRIPT', (empty($_SERVER['SERVER_SOFTWARE']) ? '' :
2649
			preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE']) ?
2650
				'index.php' : ''));
2651
	}
2652
2653
2654
	if (!defined('_SPIP_AJAX')) {
2655
		define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2656
			? 1
2657
			: (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
2658
	}
2659
2660
	// La requete est-elle en ajax ?
2661
	if (!defined('_AJAX')) {
2662
		define('_AJAX',
2663
			(isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2664
				or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2665
				or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2666
				or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2667
			)
2668
			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
2669
		);
2670
	}
2671
2672
	# nombre de pixels maxi pour calcul de la vignette avec gd
2673
	# au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2674
	# les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2675
	if (!defined('_IMG_GD_MAX_PIXELS')) {
2676
		define('_IMG_GD_MAX_PIXELS',
2677
		(isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2678
			? $GLOBALS['meta']['max_taille_vignettes']
2679
			: 0);
2680
	}
2681
2682
	if (!defined('_MEMORY_LIMIT_MIN')) {
2683
		define('_MEMORY_LIMIT_MIN', 16);
2684
	} // en Mo
2685
	// si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2686
	// on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2687
	// il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2688
	if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
2689
		if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2690
			$unit = strtolower(substr($memory, -1));
2691
			$memory = substr($memory, 0, -1);
2692
			switch ($unit) {
2693
				// Le modifieur 'G' est disponible depuis PHP 5.1.0
2694
				case 'g':
2695
					$memory *= 1024;
2696
				case 'm':
2697
					$memory *= 1024;
2698
				case 'k':
2699
					$memory *= 1024;
2700
			}
2701
			if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
2702
				@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...
2703
				if (trim(ini_get('memory_limit')) != $m) {
2704
					if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2705
						define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2706
					} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2707
				}
2708
			}
2709
		} else {
2710
			if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2711
				define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2712
			}
2713
		} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2714
	}
2715
	// Protocoles a normaliser dans les chaines de langues
2716
	if (!defined('_PROTOCOLES_STD')) {
2717
		define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2718
	}
2719
2720
	init_var_mode();
2721
}
2722
2723
/**
2724
 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2725
 * la validité du cache ou certains affichages spéciaux.
2726
 *
2727
 * Le paramètre d'URL `var_mode` permet de
2728
 * modifier la pérennité du cache, recalculer des urls
2729
 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2730
 * d'afficher un écran de débug ou des traductions non réalisées.
2731
 *
2732
 * En fonction de ces paramètres dans l'URL appelante, on définit
2733
 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2734
 *
2735
 * Le paramètre `var_mode` accepte ces valeurs :
2736
 *
2737
 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2738
 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2739
 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2740
 * - `debug` :  modifie l'affichage activant le mode "debug"
2741
 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2742
 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2743
 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2744
 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2745
 *
2746
 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2747
 *
2748
 * @note
2749
 *     Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2750
 *     le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2751
 * `   var_mode` (calcul ou recalcul).
2752
 */
2753
function init_var_mode() {
2754
	static $done = false;
2755
	if (!$done) {
2756
2757
		if (isset($_GET['var_mode'])) {
2758
			$var_mode = explode(',', $_GET['var_mode']);
2759
			// tout le monde peut calcul/recalcul
2760
			if (!defined('_VAR_MODE')) {
2761
				if (in_array('recalcul', $var_mode)) {
2762
					define('_VAR_MODE', 'recalcul');
2763
				} elseif (in_array('calcul', $var_mode)) {
2764
					define('_VAR_MODE', 'calcul');
2765
				}
2766
			}
2767
			$var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2768
			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...
2769
				include_spip('inc/autoriser');
2770
				// autoriser preview si preview seulement, et sinon autoriser debug
2771
				if (autoriser(
2772
					($_GET['var_mode'] == 'preview')
2773
						? 'previsualiser'
2774
						: 'debug'
2775
				)) {
2776 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...
2777
						// forcer le calcul pour passer dans traduire
2778
						if (!defined('_VAR_MODE')) {
2779
							define('_VAR_MODE', 'calcul');
2780
						}
2781
						// et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2782
						if (!defined('_VAR_NOCACHE')) {
2783
							define('_VAR_NOCACHE', true);
2784
						}
2785
						$var_mode = array_diff($var_mode, array('traduction'));
2786
					}
2787 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...
2788
						// basculer sur les criteres de preview dans les boucles
2789
						if (!defined('_VAR_PREVIEW')) {
2790
							define('_VAR_PREVIEW', true);
2791
						}
2792
						// forcer le calcul
2793
						if (!defined('_VAR_MODE')) {
2794
							define('_VAR_MODE', 'calcul');
2795
						}
2796
						// et ne pas enregistrer de cache
2797
						if (!defined('_VAR_NOCACHE')) {
2798
							define('_VAR_NOCACHE', true);
2799
						}
2800
						$var_mode = array_diff($var_mode, array('preview'));
2801
					}
2802 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...
2803
						// forcer le compilo et ignorer les caches existants
2804
						if (!defined('_VAR_MODE')) {
2805
							define('_VAR_MODE', 'calcul');
2806
						}
2807
						if (!defined('_VAR_INCLURE')) {
2808
							define('_VAR_INCLURE', true);
2809
						}
2810
						// et ne pas enregistrer de cache
2811
						if (!defined('_VAR_NOCACHE')) {
2812
							define('_VAR_NOCACHE', true);
2813
						}
2814
						$var_mode = array_diff($var_mode, array('inclure'));
2815
					}
2816 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...
2817
						// forcer le compilo et ignorer les caches existants
2818
						if (!defined('_VAR_MODE')) {
2819
							define('_VAR_MODE', 'calcul');
2820
						}
2821
						if (!defined('_VAR_URLS')) {
2822
							define('_VAR_URLS', true);
2823
						}
2824
						$var_mode = array_diff($var_mode, array('urls'));
2825
					}
2826 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...
2827
						// forcer le compilo et ignorer les caches existants
2828
						if (!defined('_VAR_MODE')) {
2829
							define('_VAR_MODE', 'calcul');
2830
						}
2831
						// indiquer qu'on doit recalculer les images
2832
						if (!defined('_VAR_IMAGES')) {
2833
							define('_VAR_IMAGES', true);
2834
						}
2835
						$var_mode = array_diff($var_mode, array('images'));
2836
					}
2837 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...
2838
						if (!defined('_VAR_MODE')) {
2839
							define('_VAR_MODE', 'debug');
2840
						}
2841
						// et ne pas enregistrer de cache
2842
						if (!defined('_VAR_NOCACHE')) {
2843
							define('_VAR_NOCACHE', true);
2844
						}
2845
						$var_mode = array_diff($var_mode, array('debug'));
2846
					}
2847
					if (count($var_mode) and !defined('_VAR_MODE')) {
2848
						define('_VAR_MODE', reset($var_mode));
2849
					}
2850
					if (isset($GLOBALS['visiteur_session']['nom'])) {
2851
						spip_log($GLOBALS['visiteur_session']['nom']
2852
							. " " . _VAR_MODE);
2853
					}
2854
				} // pas autorise ?
2855
				else {
2856
					// si on n'est pas connecte on se redirige
2857
					if (!$GLOBALS['visiteur_session']) {
2858
						include_spip('inc/headers');
2859
						redirige_par_entete(generer_url_public('login',
2860
							'url=' . rawurlencode(
2861
								parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2862
							), true));
2863
					}
2864
					// sinon tant pis
2865
				}
2866
			}
2867
		}
2868
		if (!defined('_VAR_MODE')) {
2869
			/**
2870
			 * Indique le mode de calcul ou d'affichage de la page.
2871
			 * @see init_var_mode()
2872
			 */
2873
			define('_VAR_MODE', false);
2874
		}
2875
		$done = true;
2876
	}
2877
}
2878
2879
// Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2880
// supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2881
// la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2882
// http://code.spip.net/@spip_desinfecte
2883
function spip_desinfecte(&$t, $deep = true) {
2884
	static $magic_quotes;
2885
	if (!isset($magic_quotes)) {
2886
		$magic_quotes = @get_magic_quotes_gpc();
2887
	}
2888
2889
	foreach ($t as $key => $val) {
2890
		if (is_string($t[$key])) {
2891
			if ($magic_quotes) {
2892
				$t[$key] = stripslashes($t[$key]);
2893
			}
2894
			$t[$key] = str_replace(chr(0), '-', $t[$key]);
2895
		} // traiter aussi les "texte_plus" de article_edit
2896
		else {
2897
			if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2898
				spip_desinfecte($t[$key], $deep);
2899
			}
2900
		}
2901
	}
2902
}
2903
2904
//  retourne le statut du visiteur s'il s'annonce
2905
2906
// http://code.spip.net/@verifier_visiteur
2907
function verifier_visiteur() {
2908
	// Rq: pour que cette fonction marche depuis mes_options
2909
	// il faut forcer l'init si ce n'est fait
2910
	// mais on risque de perturber des plugins en initialisant trop tot
2911
	// certaines constantes
2912
	@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...
2913
		(_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
2914
		(_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
2915
		(_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
2916
		(_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
2917
	);
2918
2919
	// Demarrer une session NON AUTHENTIFIEE si on donne son nom
2920
	// dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
2921
	// Attention on separe bien session_nom et nom, pour eviter
2922
	// les melanges entre donnees SQL et variables plus aleatoires
2923
	$variables_session = array('session_nom', 'session_email');
2924
	foreach ($variables_session as $var) {
2925
		if (_request($var) !== null) {
2926
			$init = true;
2927
			break;
2928
		}
2929
	}
2930
	if (isset($init)) {
2931
		#@spip_initialisation_suite();
2932
		$session = charger_fonction('session', 'inc');
2933
		$session();
2934
		include_spip('inc/texte');
2935
		foreach ($variables_session as $var) {
2936
			if (($a = _request($var)) !== null) {
2937
				$GLOBALS['visiteur_session'][$var] = safehtml($a);
2938
			}
2939
		}
2940
		if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
2941
			$GLOBALS['visiteur_session']['id_auteur'] = 0;
2942
		}
2943
		$session($GLOBALS['visiteur_session']);
2944
2945
		return 0;
2946
	}
2947
2948
	$h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
2949
	if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
2950
2951
		$session = charger_fonction('session', 'inc');
2952
		if ($session()) {
2953
			return $GLOBALS['visiteur_session']['statut'];
2954
		}
2955
		if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
2956
			include_spip('inc/auth');
2957
			$h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
2958
		}
2959
		if ($h) {
2960
			$GLOBALS['visiteur_session'] = $h;
2961
2962
			return $GLOBALS['visiteur_session']['statut'];
2963
		}
2964
	}
2965
2966
	// au moins son navigateur nous dit la langue preferee de cet inconnu
2967
	include_spip('inc/lang');
2968
	utiliser_langue_visiteur();
2969
2970
	return false;
2971
}
2972
2973
2974
/**
2975
 * Sélectionne la langue donnée en argument et mémorise la courante
2976
 *
2977
 * Restaure l'ancienne langue si appellée sans argument.
2978
 *
2979
 * @note
2980
 *     On pourrait économiser l'empilement en cas de non changemnt
2981
 *     et lui faire retourner `False` pour prevenir l'appelant
2982
 *     Le noyau de Spip sait le faire, mais pour assurer la compatibilité
2983
 *     cette fonction retourne toujours non `False`
2984
 *
2985
 * @uses changer_langue()
2986
 * @param null|string $lang
2987
 *     - string : Langue à appliquer,
2988
 *     - null : Pour restituer la dernière langue mémorisée.
2989
 * @return string
2990
 *     - string Langue utilisée.
2991
 **/
2992
function lang_select($lang = null) {
2993
	static $pile_langues = array();
2994
	if (!function_exists('changer_langue')) {
2995
		include_spip('inc/lang');
2996
	}
2997
	if ($lang === null) {
2998
		$lang = array_pop($pile_langues);
2999
	} else {
3000
		array_push($pile_langues, $GLOBALS['spip_lang']);
3001
	}
3002
	if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
3003
		return $lang;
3004
	}
3005
	changer_langue($lang);
3006
3007
	return $lang;
3008
}
3009
3010
/**
3011
 * Renvoie une chaîne qui identifie la session courante
3012
 *
3013
 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
3014
 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
3015
 * de fichier cache.
3016
 *
3017
 * @pipeline_appel definir_session
3018
 *
3019
 * @param bool $force
3020
 * @return string
3021
 *     Identifiant de la session
3022
 **/
3023
function spip_session($force = false) {
3024
	static $session;
3025
	if ($force or !isset($session)) {
3026
		$s = pipeline('definir_session',
3027
			$GLOBALS['visiteur_session']
3028
				? serialize($GLOBALS['visiteur_session'])
3029
				. '_' . @$_COOKIE['spip_session']
3030
				: ''
3031
		);
3032
		$session = $s ? substr(md5($s), 0, 8) : '';
3033
	}
3034
3035
	#spip_log('session: '.$session);
3036
	return $session;
3037
}
3038
3039
3040
/**
3041
 * Retourne un lien vers une aide
3042
 *
3043
 * Aide, aussi depuis l'espace privé à présent.
3044
 * Surchargeable mais pas d'erreur fatale si indisponible.
3045
 *
3046
 * @param string $aide
3047
 *    Cle d'identification de l'aide desiree
3048
 * @param bool $distante
3049
 *    Generer une url locale (par defaut)
3050
 *    ou une url distante [directement sur spip.net]
3051
 * @return
3052
 *    Lien sur une icone d'aide
3053
 **/
3054
function aider($aide = '', $distante = false) {
3055
	$aider = charger_fonction('aide', 'inc', true);
3056
3057
	return $aider ? $aider($aide, '', array(), $distante) : '';
3058
}
3059
3060
/**
3061
 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3062
 *
3063
 * Si l’utiliseur est un webmestre.
3064
 */
3065
function exec_info_dist() {
3066
3067
	include_spip('inc/autoriser');
3068
	if (autoriser('webmestre')) {
3069
		phpinfo();
3070
	} else {
3071
		include_spip('inc/filtres');
3072
		sinon_interdire_acces();
3073
	}
3074
}
3075
3076
/**
3077
 * Génère une erreur de squelette
3078
 *
3079
 * Génère une erreur de squelette qui sera bien visible par un
3080
 * administrateur authentifié lors d'une visite de la page en erreur
3081
 *
3082
 * @param bool|string|array $message
3083
 *     - Message d'erreur (string|array)
3084
 *     - false pour retourner le texte des messages d'erreurs
3085
 *     - vide pour afficher les messages d'erreurs
3086
 * @param string|array|object $lieu
3087
 *     Lieu d'origine de l'erreur
3088
 * @return null|string
3089
 *     - Rien dans la plupart des cas
3090
 *     - string si $message à false.
3091
 **/
3092
function erreur_squelette($message = '', $lieu = '') {
3093
	$debusquer = charger_fonction('debusquer', 'public');
3094
	if (is_array($lieu)) {
3095
		include_spip('public/compiler');
3096
		$lieu = reconstruire_contexte_compil($lieu);
3097
	}
3098
3099
	return $debusquer($message, $lieu);
3100
}
3101
3102
/**
3103
 * Calcule un squelette avec un contexte et retourne son contenu
3104
 *
3105
 * La fonction de base de SPIP : un squelette + un contexte => une page.
3106
 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3107
 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3108
 * $options permet de selectionner les options suivantes :
3109
 *
3110
 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3111
 * - raw  => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3112
 *          pour chaque $fond fourni.
3113
 *
3114
 * @api
3115
 * @param string /array $fond
3116
 *     - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3117
 *     - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3118
 * @param array $contexte
3119
 *     - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3120
 *     - La langue est transmise automatiquement (sauf option étoile).
3121
 * @param array $options
3122
 *     Options complémentaires :
3123
 *
3124
 *     - trim   : applique un trim sur le résultat (true par défaut)
3125
 *     - raw    : retourne un tableau d'information sur le squelette (false par défaut)
3126
 *     - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3127
 *                équivalent de INCLURE*
3128
 *     - ajax   : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3129
 * @param string $connect
3130
 *     Non du connecteur de bdd a utiliser
3131
 * @return string|array
3132
 *     - Contenu du squelette calculé
3133
 *     - ou tableau d'information sur le squelette.
3134
 */
3135
function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3136
	if (!function_exists('evaluer_fond')) {
3137
		include_spip('public/assembler');
3138
	}
3139
	// assurer la compat avec l'ancienne syntaxe
3140
	// (trim etait le 3eme argument, par defaut a true)
3141
	if (!is_array($options)) {
3142
		$options = array('trim' => $options);
3143
	}
3144
	if (!isset($options['trim'])) {
3145
		$options['trim'] = true;
3146
	}
3147
3148
	if (isset($contexte['connect'])) {
3149
		$connect = $contexte['connect'];
3150
		unset($contexte['connect']);
3151
	}
3152
3153
	$texte = "";
3154
	$pages = array();
3155
	$lang_select = '';
3156
	if (!isset($options['etoile']) or !$options['etoile']) {
3157
		// Si on a inclus sans fixer le critere de lang, on prend la langue courante
3158
		if (!isset($contexte['lang'])) {
3159
			$contexte['lang'] = $GLOBALS['spip_lang'];
3160
		}
3161
3162
		if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3163
			$lang_select = lang_select($contexte['lang']);
3164
		}
3165
	}
3166
3167
	if (!isset($GLOBALS['_INC_PUBLIC'])) {
3168
		$GLOBALS['_INC_PUBLIC'] = 0;
3169
	}
3170
3171
	$GLOBALS['_INC_PUBLIC']++;
3172
3173
3174
	foreach (is_array($fond) ? $fond : array($fond) as $f) {
3175
		$page = evaluer_fond($f, $contexte, $connect);
3176
		if ($page === '') {
3177
			$c = isset($options['compil']) ? $options['compil'] : '';
3178
			$a = array('fichier' => $fond);
3179
			$erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3180
			erreur_squelette($erreur, $c);
3181
			// eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3182
			$page = array('texte' => '', 'erreur' => $erreur);
3183
		}
3184
3185
		$page = pipeline('recuperer_fond', array(
3186
			'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3187
			'data' => $page
3188
		));
3189
		if (isset($options['ajax']) and $options['ajax']) {
3190
			if (!function_exists('encoder_contexte_ajax')) {
3191
				include_spip('inc/filtres');
3192
			}
3193
			$page['texte'] = encoder_contexte_ajax(
3194
				array_merge(
3195
					$contexte,
3196
					array('fond' => $f),
3197
					($connect ? array('connect' => $connect) : array())
3198
				),
3199
				'',
3200
				$page['texte'],
3201
				$options['ajax']
3202
			);
3203
		}
3204
3205
		if (isset($options['raw']) and $options['raw']) {
3206
			$pages[] = $page;
3207
		} else {
3208
			$texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
3209
		}
3210
	}
3211
3212
	$GLOBALS['_INC_PUBLIC']--;
3213
3214
	if ($lang_select) {
3215
		lang_select();
3216
	}
3217
	if (isset($options['raw']) and $options['raw']) {
3218
		return is_array($fond) ? $pages : reset($pages);
3219
	} else {
3220
		return $options['trim'] ? ltrim($texte) : $texte;
3221
	}
3222
}
3223
3224
/**
3225
 * Trouve un squelette dans le repertoire modeles/
3226
 *
3227
 * @param  $nom
3228
 * @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...
3229
 */
3230
function trouve_modele($nom) {
3231
	return trouver_fond($nom, 'modeles/');
3232
}
3233
3234
/**
3235
 * Trouver un squelette dans le chemin
3236
 * on peut specifier un sous-dossier dans $dir
3237
 * si $pathinfo est a true, retourne un tableau avec
3238
 * les composantes du fichier trouve
3239
 * + le chemin complet sans son extension dans fond
3240
 *
3241
 * @param string $nom
3242
 * @param string $dir
3243
 * @param bool $pathinfo
3244
 * @return array|string
3245
 */
3246
function trouver_fond($nom, $dir = '', $pathinfo = false) {
3247
	$f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
3248
	if (!$pathinfo) {
3249
		return $f;
3250
	}
3251
	// renvoyer un tableau detaille si $pathinfo==true
3252
	$p = pathinfo($f);
3253
	if (!isset($p['extension']) or !$p['extension']) {
3254
		$p['extension'] = _EXTENSION_SQUELETTES;
3255
	}
3256
	if (!isset($p['extension']) or !$p['filename']) {
3257
		$p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3258
	}
3259
	$p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
3260
3261
	return $p;
3262
}
3263
3264
/**
3265
 * Teste, pour un nom de page de l'espace privé, s'il est possible
3266
 * de générer son contenu.
3267
 *
3268
 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3269
 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3270
 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3271
 *
3272
 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3273
 *          ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3274
 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3275
 *
3276
 * @param string $nom
3277
 *     Nom de la page
3278
 * @return string
3279
 *     Nom de l'exec, sinon chaîne vide.
3280
 **/
3281
function tester_url_ecrire($nom) {
3282
	static $exec = array();
3283
	if (isset($exec[$nom])) {
3284
		return $exec[$nom];
3285
	}
3286
	// tester si c'est une page en squelette
3287
	if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3288
		return $exec[$nom] = 'fond';
3289
	} // compat skels orthogonaux version precedente
3290
	elseif (trouver_fond($nom, 'prive/exec/')) {
3291
		return $exec[$nom] = 'fond_monobloc';
3292
	} // echafaudage d'un fond !
3293
	elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3294
		return $exec[$nom] = 'fond';
3295
	}
3296
	// attention, il ne faut pas inclure l'exec ici
3297
	// car sinon #URL_ECRIRE provoque des inclusions
3298
	// et des define intrusifs potentiels
3299
	return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
3300
}
3301
3302
3303
/**
3304
 * Teste la présence d’une extension PHP
3305
 *
3306
 * @deprected Utiliser directement la fonction native `extension_loaded($module)`
3307
 * @example
3308
 *     ```
3309
 *     $ok = charger_php_extension('sqlite');
3310
 *     ```
3311
 * @param string $module Nom du module à charger
3312
 * @return bool true si le module est chargé
3313
 **/
3314
function charger_php_extension($module) {
3315
	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...
3316
		return true;
3317
	}
3318
	return false;
3319
}
3320
3321
3322
/**
3323
 * Indique si le code HTML5 est permis sur le site public
3324
 *
3325
 * @return bool
3326
 *     true si et seulement si la configuration autorise le code HTML5 sur le site public
3327
 **/
3328
function html5_permis() {
3329
	return (isset($GLOBALS['meta']['version_html_max'])
3330
		and ('html5' == $GLOBALS['meta']['version_html_max']));
3331
}
3332
3333
/*
3334
 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3335
 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3336
 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3337
 */
3338
3339
/**
3340
 * lire_meta : fonction dépréciée
3341
 *
3342
 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3343
 * @see lire_config()
3344
 * @param string $nom Clé de meta à lire
3345
 * @return mixed Valeur de la meta.
3346
 **/
3347
function lire_meta($nom) {
3348
	return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
3349
}
3350
3351
3352
/**
3353
 * ecrire_metas : fonction dépréciée
3354
 *
3355
 * @deprecated
3356
 **/
3357
function ecrire_metas() { }
3358
3359
/**
3360
 * Retourne une ligne d'un résultat de requête mysql (déprécié)
3361
 *
3362
 * @see sql_fetch()
3363
 * @deprecated Utiliser sql_fetch()
3364
 * @param Ressource $r Ressource mysql
3365
 * @param int|null $t Type de retour
3366
 * @return array|void|bool Tableau de la ligne SQL
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array|null.

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...
3367
 **/
3368
function spip_fetch_array($r, $t = null) {
3369
	if (!isset($t)) {
3370
		if ($r) {
3371
			return sql_fetch($r);
3372
		}
3373
	} else {
3374
		if ($t == 'SPIP_NUM') {
3375
			$t = MYSQLI_NUM;
3376
		}
3377
		if ($t == 'SPIP_BOTH') {
3378
			$t = MYSQLI_BOTH;
3379
		}
3380
		if ($t == 'SPIP_ASSOC') {
3381
			$t = MYSQLI_ASSOC;
3382
		}
3383
		spip_log("appel deprecie de spip_fetch_array(..., $t)", 'vieilles_defs');
3384
		if ($r) {
3385
			return mysqli_fetch_array($r, $t);
3386
		}
3387
	}
3388
}
3389
3390
/**
3391
 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3392
 * au prochain passage dans l'espace prive
3393
 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3394
 * les alertes affichees une fois sont effacees
3395
 *
3396
 * @param string $nom
3397
 * @param string $message
3398
 * @param string $statut
3399
 */
3400
function avertir_auteurs($nom, $message, $statut = '') {
3401
	$alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3402
	if (!$alertes
3403
		or !is_array($alertes = unserialize($alertes))
3404
	) {
3405
		$alertes = array();
3406
	}
3407
3408
	if (!isset($alertes[$statut])) {
3409
		$alertes[$statut] = array();
3410
	}
3411
	$alertes[$statut][$nom] = $message;
3412
	ecrire_meta("message_alertes_auteurs", serialize($alertes));
3413
}
3414
3415
if (PHP_VERSION_ID < 50500) {
3416
	if (!function_exists('array_column')) {
3417
		/**
3418
		 * Returns the values from a single column of the input array, identified by
3419
		 * the $columnKey.
3420
		 *
3421
		 * Optionally, you may provide an $indexKey to index the values in the returned
3422
		 * array by the values from the $indexKey column in the input array.
3423
		 *
3424
		 * @link http://php.net/manual/fr/function.array-column.php
3425
		 * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
3426
		 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
3427
		 * @license http://opensource.org/licenses/MIT MIT
3428
		 *
3429
		 * @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...
3430
		 *                     a column of values.
3431
		 * @param mixed $columnKey The column of values to return. This value may be the
3432
		 *                         integer key of the column you wish to retrieve, or it
3433
		 *                         may be the string key name for an associative array.
3434
		 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
3435
		 *                        the returned array. This value may be the integer key
3436
		 *                        of the column, or it may be the string key name.
3437
		 * @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...
3438
		 */
3439
		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...
3440
		{
3441
			// Using func_get_args() in order to check for proper number of
3442
			// parameters and trigger errors exactly as the built-in array_column()
3443
			// does in PHP 5.5.
3444
			$argc = func_num_args();
3445
			$params = func_get_args();
3446
3447
			if ($argc < 2) {
3448
				trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
3449
				return null;
3450
			}
3451
3452
			if (!is_array($params[0])) {
3453
				trigger_error(
3454
					'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
3455
					E_USER_WARNING
3456
				);
3457
				return null;
3458
			}
3459
3460 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...
3461
				&& !is_float($params[1])
3462
				&& !is_string($params[1])
3463
				&& $params[1] !== null
3464
				&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
3465
			) {
3466
				trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
3467
				return false;
3468
			}
3469
3470 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...
3471
				&& !is_int($params[2])
3472
				&& !is_float($params[2])
3473
				&& !is_string($params[2])
3474
				&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
3475
			) {
3476
				trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
3477
				return false;
3478
			}
3479
3480
			$paramsInput = $params[0];
3481
			$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
3482
3483
			$paramsIndexKey = null;
3484
			if (isset($params[2])) {
3485
				if (is_float($params[2]) || is_int($params[2])) {
3486
					$paramsIndexKey = (int) $params[2];
3487
				} else {
3488
					$paramsIndexKey = (string) $params[2];
3489
				}
3490
			}
3491
3492
			$resultArray = array();
3493
3494
			foreach ($paramsInput as $row) {
3495
				$key = $value = null;
3496
				$keySet = $valueSet = false;
3497
3498
				if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
3499
					$keySet = true;
3500
					$key = (string) $row[$paramsIndexKey];
3501
				}
3502
3503
				if ($paramsColumnKey === null) {
3504
					$valueSet = true;
3505
					$value = $row;
3506
				} elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
3507
					$valueSet = true;
3508
					$value = $row[$paramsColumnKey];
3509
				}
3510
3511
				if ($valueSet) {
3512
					if ($keySet) {
3513
						$resultArray[$key] = $value;
3514
					} else {
3515
						$resultArray[] = $value;
3516
					}
3517
				}
3518
3519
			}
3520
3521
			return $resultArray;
3522
		}
3523
3524
	}
3525
}