Completed
Push — master ( d8d4cc...6b056b )
by cam
07:55
created

utils.php ➔ nettoyer_uri_var()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 9
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 1
nop 1
dl 0
loc 9
rs 9.6666
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
// chargement du pipeline sous la forme d'un fichier php prepare
216
// http://code.spip.net/@pipeline
217
function pipeline($action, $val = null) {
218
	static $charger;
219
220
	// chargement initial des fonctions mises en cache, ou generation du cache
221
	if (!$charger) {
222
		if (!($ok = @is_readable($charger = _CACHE_PIPELINES))) {
223
			include_spip('inc/plugin');
224
			// generer les fichiers php precompiles
225
			// de chargement des plugins et des pipelines
226
			actualise_plugins_actifs();
227
			if (!($ok = @is_readable($charger))) {
228
				spip_log("fichier $charger pas cree");
229
			}
230
		}
231
232
		if ($ok) {
233
			include_once $charger;
234
		}
235
	}
236
237
	// appliquer notre fonction si elle existe
238
	$fonc = 'execute_pipeline_' . strtolower($action);
239
	if (function_exists($fonc)) {
240
		$val = $fonc($val);
241
	} // plantage ?
242
	else {
243
		spip_log("fonction $fonc absente : pipeline desactive", _LOG_ERREUR);
244
	}
245
246
	// si le flux est une table avec 2 cle args&data
247
	// on ne ressort du pipe que les donnees dans 'data'
248
	// array_key_exists pour php 4.1.0
249
	if (is_array($val)
250
		and count($val) == 2
251
		and (array_key_exists('data', $val))
252
	) {
253
		$val = $val['data'];
254
	}
255
256
	return $val;
257
}
258
259
/**
260
 * Enregistrement des événements
261
 *
262
 * Signature : `spip_log(message[,niveau|type|type.niveau])`
263
 *
264
 * Le niveau de log par défaut est la valeur de la constante `_LOG_INFO`
265
 *
266
 * Les différents niveaux possibles sont :
267
 *
268
 * - `_LOG_HS` : écrira 'HS' au début de la ligne logguée
269
 * - `_LOG_ALERTE_ROUGE` : 'ALERTE'
270
 * - `_LOG_CRITIQUE` :  'CRITIQUE'
271
 * - `_LOG_ERREUR` : 'ERREUR'
272
 * - `_LOG_AVERTISSEMENT` : 'WARNING'
273
 * - `_LOG_INFO_IMPORTANTE` : '!INFO'
274
 * - `_LOG_INFO` : 'info'
275
 * - `_LOG_DEBUG` : 'debug'
276
 *
277
 * @example
278
 *   ```
279
 *   spip_log($message)
280
 *   spip_log($message, 'recherche')
281
 *   spip_log($message, _LOG_DEBUG)
282
 *   spip_log($message, 'recherche.'._LOG_DEBUG)
283
 *   ```
284
 *
285
 * @api
286
 * @link http://programmer.spip.net/spip_log
287
 * @uses inc_log_dist()
288
 *
289
 * @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...
290
 *     Message à loger
291
 * @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...
292
 *
293
 *     - int indique le niveau de log, tel que `_LOG_DEBUG`
294
 *     - string indique le type de log
295
 *     - `string.int` indique les 2 éléments.
296
 *     Cette dernière notation est controversée mais le 3ème
297
 *     paramètre est planté pour cause de compatibilité ascendante.
298
 */
299
function spip_log($message = null, $name = null) {
300
	static $pre = array();
301
	static $log;
302
	preg_match('/^([a-z_]*)\.?(\d)?$/iS', (string)$name, $regs);
303
	if (!isset($regs[1]) or !$logname = $regs[1]) {
304
		$logname = null;
305
	}
306
	if (!isset($regs[2]) or !$niveau = $regs[2]) {
307
		$niveau = _LOG_INFO;
308
	}
309
310
	if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE : _LOG_INFO_IMPORTANTE)) {
311
		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...
312
			$pre = array(
313
				_LOG_HS => 'HS:',
314
				_LOG_ALERTE_ROUGE => 'ALERTE:',
315
				_LOG_CRITIQUE => 'CRITIQUE:',
316
				_LOG_ERREUR => 'ERREUR:',
317
				_LOG_AVERTISSEMENT => 'WARNING:',
318
				_LOG_INFO_IMPORTANTE => '!INFO:',
319
				_LOG_INFO => 'info:',
320
				_LOG_DEBUG => 'debug:'
321
			);
322
			$log = charger_fonction('log', 'inc');
323
		}
324
		if (!is_string($message)) {
325
			$message = print_r($message, true);
326
		}
327
		$log($pre[$niveau] . ' ' . $message, $logname);
328
	}
329
}
330
331
/**
332
 * Enregistrement des journaux
333
 *
334
 * @uses inc_journal_dist()
335
 * @param string $phrase Texte du journal
336
 * @param array $opt Tableau d'options
337
 **/
338
function journal($phrase, $opt = array()) {
339
	$journal = charger_fonction('journal', 'inc');
340
	$journal($phrase, $opt);
341
}
342
343
344
/**
345
 * Renvoie le `$_GET` ou le `$_POST` émis par l'utilisateur
346
 * ou pioché dans un tableau transmis
347
 *
348
 * @api
349
 * @param string $var
350
 *     Clé souhaitée
351
 * @param bool|array $c
352
 *     Tableau transmis (sinon cherche dans GET ou POST)
353
 * @return mixed|null
354
 *     - null si la clé n'a pas été trouvée
355
 *     - la valeur de la clé sinon.
356
 **/
357
function _request($var, $c = false) {
358
359
	if (is_array($c)) {
360
		return isset($c[$var]) ? $c[$var] : null;
361
	}
362
363
	if (isset($_GET[$var])) {
364
		$a = $_GET[$var];
365
	} elseif (isset($_POST[$var])) {
366
		$a = $_POST[$var];
367
	} else {
368
		return null;
369
	}
370
371
	// Si on est en ajax et en POST tout a ete encode
372
	// via encodeURIComponent, il faut donc repasser
373
	// dans le charset local...
374
	if (defined('_AJAX')
375
		and _AJAX
376
		and isset($GLOBALS['meta']['charset'])
377
		and $GLOBALS['meta']['charset'] != 'utf-8'
378
		and is_string($a)
379
		// check rapide mais pas fiable
380
		and preg_match(',[\x80-\xFF],', $a)
381
		// check fiable
382
		and include_spip('inc/charsets')
383
		and is_utf8($a)
384
	) {
385
		return importer_charset($a, 'utf-8');
386
	}
387
388
	return $a;
389
}
390
391
392
/**
393
 * Affecte une valeur à une clé (pour usage avec `_request()`)
394
 *
395
 * @see _request() Pour obtenir la valeur
396
 * @note Attention au cas ou l'on fait `set_request('truc', NULL);`
397
 *
398
 * @param string $var Nom de la clé
399
 * @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...
400
 * @param bool|array $c Tableu de données (sinon utilise `$_GET` et `$_POST`)
401
 * @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...
402
 *     - array $c complété si un $c est transmis,
403
 *     - false sinon
404
 **/
405
function set_request($var, $val = null, $c = false) {
406
	if (is_array($c)) {
407
		unset($c[$var]);
408
		if ($val !== null) {
409
			$c[$var] = $val;
410
		}
411
412
		return $c;
413
	}
414
415
	unset($_GET[$var]);
416
	unset($_POST[$var]);
417
	if ($val !== null) {
418
		$_GET[$var] = $val;
419
	}
420
421
	return false; # n'affecte pas $c
422
}
423
424
425
/**
426
 * Tester si une URL est absolue
427
 * 
428
 * On est sur le web, on exclut certains protocoles, 
429
 * notamment 'file://', 'php://' et d'autres…
430
431
 * @param string $url
432
 * @return bool
433
 */
434
function tester_url_absolue($url) {
435
	$url = trim($url);
436
	if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
437
		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...
438
			isset($m[1])
439
			and $p = strtolower(rtrim($m[1], ':'))
440
			and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
441
		  ) {
442
			return false;
443
		}
444
		return true;
445
	}
446
	return false;
447
}
448
449
/**
450
 * Prend une URL et lui ajoute/retire un paramètre
451
 *
452
 * @filtre
453
 * @link http://www.spip.net/4255
454
 * @example
455
 *     ```
456
 *     [(#SELF|parametre_url{suite,18})] (ajout)
457
 *     [(#SELF|parametre_url{suite,''})] (supprime)
458
 *     [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
459
 *     ```
460
 *
461
 * @param string $url URL
462
 * @param string $c Nom du paramètre
463
 * @param string|array|null $v Valeur du paramètre
464
 * @param string $sep Séparateur entre les paramètres
465
 * @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...
466
 */
467
function parametre_url($url, $c, $v = null, $sep = '&amp;') {
468
	// requete erronnee : plusieurs variable dans $c et aucun $v
469
	if (strpos($c, "|") !== false and is_null($v)) {
470
		return null;
471
	}
472
473
	// lever l'#ancre
474
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
475
		$url = $r[1];
476
		$ancre = $r[2];
477
	} else {
478
		$ancre = '';
479
	}
480
481
	// eclater
482
	$url = preg_split(',[?]|&amp;|&,', $url);
483
484
	// recuperer la base
485
	$a = array_shift($url);
486
	if (!$a) {
487
		$a = './';
488
	}
489
490
	$regexp = ',^(' . str_replace('[]', '\[\]', $c) . '[[]?[]]?)(=.*)?$,';
491
	$ajouts = array_flip(explode('|', $c));
492
	$u = is_array($v) ? $v : rawurlencode($v);
493
	$testv = (is_array($v) ? count($v) : strlen($v));
494
	$v_read = null;
495
	// lire les variables et agir
496
	foreach ($url as $n => $val) {
497
		if (preg_match($regexp, urldecode($val), $r)) {
498
			$r = array_pad($r, 3, null);
499
			if ($v === null) {
500
				// c'est un tableau, on memorise les valeurs
501
				if (substr($r[1], -2) == "[]") {
502
					if (!$v_read) {
503
						$v_read = array();
504
					}
505
					$v_read[] = $r[2] ? substr($r[2], 1) : '';
506
				} // c'est un scalaire, on retourne direct
507
				else {
508
					return $r[2] ? substr($r[2], 1) : '';
509
				}
510
			} // suppression
511
			elseif (!$testv) {
512
				unset($url[$n]);
513
			}
514
			// Ajout. Pour une variable, remplacer au meme endroit,
515
			// pour un tableau ce sera fait dans la prochaine boucle
516
			elseif (substr($r[1], -2) != '[]') {
517
				$url[$n] = $r[1] . '=' . $u;
518
				unset($ajouts[$r[1]]);
519
			}
520
			// Pour les tableaux on laisse tomber les valeurs de
521
			// départ, on remplira à l'étape suivante
522
			else {
523
				unset($url[$n]);
524
			}
525
		}
526
	}
527
528
	// traiter les parametres pas encore trouves
529
	if ($v === null
530
		and $args = func_get_args()
531
		and count($args) == 2
532
	) {
533
		return $v_read; // rien trouve ou un tableau
534
	} elseif ($testv) {
535
		foreach ($ajouts as $k => $n) {
536
			if (!is_array($v)) {
537
				$url[] = $k . '=' . $u;
538
			} else {
539
				$id = (substr($k, -2) == '[]') ? $k : ($k . "[]");
540
				foreach ($v as $w) {
541
					$url[] = $id . '=' . (is_array($w) ? 'Array' : $w);
542
				}
543
			}
544
		}
545
	}
546
547
	// eliminer les vides
548
	$url = array_filter($url);
549
550
	// recomposer l'adresse
551
	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...
552
		$a .= '?' . join($sep, $url);
553
	}
554
555
	return $a . $ancre;
556
}
557
558
// Prend une URL et lui ajoute/retire une ancre apres l'avoir nettoyee
559
// pour l'ancre on translitere, vire les non alphanum du debut,
560
// et on remplace ceux a l'interieur ou au bout par -
561
// http://code.spip.net/@ancre_url
562
function ancre_url($url, $ancre) {
563
	// lever l'#ancre
564
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
565
		$url = $r[1];
566
	}
567
	if (preg_match('/[^-_a-zA-Z0-9]+/S', $ancre)) {
568
		if (!function_exists('translitteration')) {
569
			include_spip('inc/charsets');
570
		}
571
		$ancre = preg_replace(array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'), array('', '-'),
572
			translitteration($ancre));
573
	}
574
575
	return $url . (strlen($ancre) ? '#' . $ancre : '');
576
}
577
578
/**
579
 * Pour le nom du cache, les `types_urls` et `self`
580
 *
581
 * @param string|null $reset
582
 * @return string
583
 */
584
function nettoyer_uri($reset = null) {
585
	static $done = false;
586
	static $propre = '';
587
	if (!is_null($reset)) {
588
		return $propre = $reset;
589
	}
590
	if ($done) {
591
		return $propre;
592
	}
593
	$done = true;
594
	return $propre = nettoyer_uri_var($GLOBALS['REQUEST_URI']);
595
}
596
597
/**
598
 * Nettoie une request_uri des paramètres var_xxx
599
 * @param $request_uri
600
 * @return string
601
 */
602
function nettoyer_uri_var($request_uri) {
603
	$uri1 = $request_uri;
604
	do {
605
		$uri = $uri1;
606
		$uri1 = preg_replace(',([?&])(PHPSESSID|(var_[^=&]*))=[^&]*(&|$),i',
607
			'\1', $uri);
608
	} while ($uri <> $uri1);
609
	return preg_replace(',[?&]$,', '', $uri1);
610
}
611
612
613
/**
614
 * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
615
 *
616
 * @param string $amp
617
 *    Style des esperluettes
618
 * @param bool $root
619
 * @return string
620
 *    URL vers soi-même
621
 **/
622
function self($amp = '&amp;', $root = false) {
623
	$url = nettoyer_uri();
624
	if (!$root
625
		and (
626
			// si pas de profondeur on peut tronquer
627
			$GLOBALS['profondeur_url'] < (_DIR_RESTREINT ? 1 : 2)
628
			// sinon c'est OK si _SET_HTML_BASE a ete force a false
629
			or (defined('_SET_HTML_BASE') and !_SET_HTML_BASE))
630
	) {
631
		$url = preg_replace(',^[^?]*/,', '', $url);
632
	}
633
	// ajouter le cas echeant les variables _POST['id_...']
634
	foreach ($_POST as $v => $c) {
635
		if (substr($v, 0, 3) == 'id_') {
636
			$url = parametre_url($url, $v, $c, '&');
637
		}
638
	}
639
640
	// supprimer les variables sans interet
641
	if (test_espace_prive()) {
642
		$url = preg_replace(',([?&])('
643
			. 'lang|show_docs|'
644
			. 'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
645
		$url = preg_replace(',([?&])[&]+,', '\1', $url);
646
		$url = preg_replace(',[&]$,', '\1', $url);
647
	}
648
649
	// eviter les hacks
650
	include_spip('inc/filtres_mini');
651
	$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...
652
	
653
	$url = str_replace(array('[', ']'), array('%5B', '%5D'), $url);
654
655
	// &amp; ?
656
	if ($amp != '&amp;') {
657
		$url = str_replace('&amp;', $amp, $url);
658
	}
659
660
	// Si ca demarre par ? ou vide, donner './'
661
	$url = preg_replace(',^([?].*)?$,', './\1', $url);
662
663
	return $url;
664
}
665
666
667
/**
668
 * Indique si on est dans l'espace prive
669
 *
670
 * @return bool
671
 *     true si c'est le cas, false sinon.
672
 */
673
function test_espace_prive() {
674
	return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
675
}
676
677
/**
678
 * Vérifie la présence d'un plugin actif, identifié par son préfixe
679
 *
680
 * @param string $plugin
681
 * @return bool
682
 */
683
function test_plugin_actif($plugin) {
684
	return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ? true : false;
685
}
686
687
/**
688
 * Traduction des textes de SPIP
689
 *
690
 * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
691
 *
692
 * @api
693
 * @uses inc_traduire_dist()
694
 * @uses _L()
695
 * @example
696
 *     ```
697
 *     _T('bouton_enregistrer')
698
 *     _T('medias:image_tourner_droite')
699
 *     _T('medias:erreurs', array('nb'=>3))
700
 *     _T("email_sujet", array('spip_lang'=>$lang_usager))
701
 *     ```
702
 *
703
 * @param string $texte
704
 *     Clé de traduction
705
 * @param array $args
706
 *     Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
707
 * @param array $options
708
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
709
 *     - bool force : forcer un retour meme si la chaine n'a pas de traduction
710
 * @return string
711
 *     Texte
712
 */
713
function _T($texte, $args = array(), $options = array()) {
714
	static $traduire = false;
715
	$o = array('class' => '', 'force' => true);
716
	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...
717
		// support de l'ancien argument $class
718
		if (is_string($options)) {
719
			$options = array('class' => $options);
720
		}
721
		$o = array_merge($o, $options);
722
	}
723
724
	if (!$traduire) {
725
		$traduire = charger_fonction('traduire', 'inc');
726
		include_spip('inc/lang');
727
	}
728
729
	// On peut passer explicitement la langue dans le tableau
730
	// On utilise le même nom de variable que la globale
731 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...
732
		$lang = $args['spip_lang'];
733
		// On l'enleve pour ne pas le passer au remplacement
734
		unset($args['spip_lang']);
735
	} // Sinon on prend la langue du contexte
736
	else {
737
		$lang = $GLOBALS['spip_lang'];
738
	}
739
	$text = $traduire($texte, $lang);
740
741
	if (!strlen($text)) {
742
		if (!$o['force']) {
743
			return '';
744
		}
745
746
		$text = $texte;
747
748
		// pour les chaines non traduites, assurer un service minimum
749
		if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
750
			$text = str_replace('_', ' ',
751
				(($n = strpos($text, ':')) === false ? $texte :
752
					substr($texte, $n + 1)));
753
		}
754
		$o['class'] = null;
755
756
	}
757
758
	return _L($text, $args, $o['class']);
759
760
}
761
762
763
/**
764
 * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
765
 *
766
 * Cette fonction est également appelée dans le code source de SPIP quand une
767
 * chaîne n'est pas encore dans les fichiers de langue.
768
 *
769
 * @see _T()
770
 * @example
771
 *     ```
772
 *     _L('Texte avec @nb@ ...', array('nb'=>3)
773
 *     ```
774
 *
775
 * @param string $text
776
 *     Texte
777
 * @param array $args
778
 *     Couples (variable => valeur) à transformer dans le texte
779
 * @param string|null $class
780
 *     Encapsule les valeurs dans un span avec cette classe si transmis.
781
 * @return string
782
 *     Texte
783
 */
784
function _L($text, $args = array(), $class = null) {
785
	$f = $text;
786
	if (is_array($args)) {
787
		foreach ($args as $name => $value) {
788
			if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
789
				$value = "<span class='$class'>$value</span>";
790
			}
791
			$t = str_replace("@$name@", $value, $text);
792
			if ($text !== $t) {
793
				unset($args[$name]);
794
				$text = $t;
795
			}
796
		}
797
		// Si des variables n'ont pas ete inserees, le signaler
798
		// (chaines de langues pas a jour)
799
		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...
800
			spip_log("$f:  variables inutilisees " . join(', ', array_keys($args)), _LOG_DEBUG);
801
		}
802
	}
803
804
	if (($GLOBALS['test_i18n'] or (_request('var_mode') == 'traduction')) and $class === null) {
805
		return "<span class=debug-traduction-erreur>$text</span>";
806
	} else {
807
		return $text;
808
	}
809
}
810
811
// Afficher "ecrire/data/" au lieu de "data/" dans les messages
812
// ou tmp/ au lieu de ../tmp/
813
// http://code.spip.net/@joli_repertoire
814
function joli_repertoire($rep) {
815
	$a = substr($rep, 0, 1);
816
	if ($a <> '.' and $a <> '/') {
817
		$rep = (_DIR_RESTREINT ? '' : _DIR_RESTREINT_ABS) . $rep;
818
	}
819
	$rep = preg_replace(',(^\.\.\/),', '', $rep);
820
821
	return $rep;
822
}
823
824
825
//
826
// spip_timer : on l'appelle deux fois et on a la difference, affichable
827
//
828
// http://code.spip.net/@spip_timer
829
function spip_timer($t = 'rien', $raw = false) {
830
	static $time;
831
	$a = time();
832
	$b = microtime();
833
	// microtime peut contenir les microsecondes et le temps
834
	$b = explode(' ', $b);
835
	if (count($b) == 2) {
836
		$a = end($b);
837
	} // plus precis !
838
	$b = reset($b);
839
	if (!isset($time[$t])) {
840
		$time[$t] = $a + $b;
841
	} else {
842
		$p = ($a + $b - $time[$t]) * 1000;
843
		unset($time[$t]);
844
#			echo "'$p'";exit;
845
		if ($raw) {
846
			return $p;
847
		}
848
		if ($p < 1000) {
849
			$s = '';
850
		} else {
851
			$s = sprintf("%d ", $x = floor($p / 1000));
852
			$p -= ($x * 1000);
853
		}
854
855
		return $s . sprintf($s ? "%07.3f ms" : "%.3f ms", $p);
856
	}
857
}
858
859
860
// Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
861
// sinon renvoie True et le date sauf si ca n'est pas souhaite
862
// http://code.spip.net/@spip_touch
863
function spip_touch($fichier, $duree = 0, $touch = true) {
864
	if ($duree) {
865
		clearstatcache();
866
		if ((@$f = filemtime($fichier)) and ($f >= time() - $duree)) {
867
			return false;
868
		}
869
	}
870
	if ($touch !== false) {
871
		if (!@touch($fichier)) {
872
			spip_unlink($fichier);
873
			@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...
874
		};
875
		@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...
876
	}
877
878
	return true;
879
}
880
881
882
/**
883
 * Action qui déclenche une tache de fond
884
 *
885
 * @see  queue_affichage_cron()
886
 * @see  action_super_cron_dist()
887
 * @uses cron()
888
 **/
889
function action_cron() {
890
	include_spip('inc/headers');
891
	http_status(204); // No Content
892
	header("Connection: close");
893
	define('_DIRECT_CRON_FORCE', true);
894
	cron();
895
}
896
897
/**
898
 * Exécution des tâches de fond
899
 *
900
 * @uses inc_genie_dist()
901
 *
902
 * @param array $taches
903
 *     Tâches forcées
904
 * @param array $taches_old
905
 *     Tâches forcées, pour compat avec ancienne syntaxe
906
 * @return bool
907
 *     True si la tache a pu être effectuée
908
 */
909
function cron($taches = array(), $taches_old = array()) {
910
	// si pas en mode cron force, laisser tomber.
911
	if (!defined('_DIRECT_CRON_FORCE')) {
912
		return false;
913
	}
914
	if (!is_array($taches)) {
915
		$taches = $taches_old;
916
	} // compat anciens appels
917
	// si taches a inserer en base et base inaccessible, laisser tomber
918
	// sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
919
	// queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
920
	// et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
921
	if ($taches and count($taches) and !spip_connect()) {
922
		return false;
923
	}
924
	spip_log("cron !", 'jq' . _LOG_DEBUG);
925
	if ($genie = charger_fonction('genie', 'inc', true)) {
926
		return $genie($taches);
927
	}
928
929
	return false;
930
}
931
932
/**
933
 * Ajout d'une tache dans la file d'attente
934
 *
935
 * @param string $function
936
 *     Le nom de la fonction PHP qui doit être appelée.
937
 * @param string $description
938
 *     Une description humainement compréhensible de ce que fait la tâche
939
 *     (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
940
 * @param array $arguments
941
 *     Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
942
 * @param string $file
943
 *     Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
944
 *     exemple : `'inc/mail'` : il ne faut pas indiquer .php
945
 *     Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
946
 * @param bool $no_duplicate
947
 *     Facultatif, `false` par défaut
948
 *
949
 *     - 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.
950
 *     - 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
951
 * @param int $time
952
 *     Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
953
 *     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).
954
 * @param int $priority
955
 *     Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
956
 *     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.
957
 * @return int
958
 *     Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
959
 */
960
function job_queue_add(
961
	$function,
962
	$description,
963
	$arguments = array(),
964
	$file = '',
965
	$no_duplicate = false,
966
	$time = 0,
967
	$priority = 0
968
) {
969
	include_spip('inc/queue');
970
971
	return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
972
}
973
974
/**
975
 * Supprimer une tache de la file d'attente
976
 *
977
 * @param int $id_job
978
 *  id of jonb to delete
979
 * @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...
980
 */
981
function job_queue_remove($id_job) {
982
	include_spip('inc/queue');
983
984
	return queue_remove_job($id_job);
985
}
986
987
/**
988
 * Associer une tache a un/des objets de SPIP
989
 *
990
 * @param int $id_job
991
 *     id of job to link
992
 * @param array $objets
993
 *     can be a simple array('objet'=>'article', 'id_objet'=>23)
994
 *     or an array of simple array to link multiples objet in one time
995
 */
996
function job_queue_link($id_job, $objets) {
997
	include_spip('inc/queue');
998
999
	return queue_link_job($id_job, $objets);
1000
}
1001
1002
1003
/**
1004
 * Renvoyer le temps de repos restant jusqu'au prochain job
1005
 *
1006
 * @staticvar int $queue_next_job_time
1007
 * @see queue_set_next_job_time()
1008
 * @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...
1009
 *    Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
1010
 *
1011
 *    - si `true`, force la relecture depuis le fichier
1012
 *    - si int, affecte la static directement avec la valeur
1013
 * @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...
1014
 *
1015
 *  - `0` si un job est à traiter
1016
 *  - `null` si la queue n'est pas encore initialisée
1017
 */
1018
function queue_sleep_time_to_next_job($force = null) {
1019
	static $queue_next_job_time = -1;
1020
	if ($force === true) {
1021
		$queue_next_job_time = -1;
1022
	} elseif ($force) {
1023
		$queue_next_job_time = $force;
1024
	}
1025
1026
	if ($queue_next_job_time == -1) {
1027
		if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
1028
			define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP . "job_queue_next.txt");
1029
		}
1030
		// utiliser un cache memoire si dispo
1031
		if (function_exists("cache_get") and defined('_MEMOIZE_MEMORY') and _MEMOIZE_MEMORY) {
1032
			$queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
1033
		} else {
1034
			$queue_next_job_time = null;
1035
			if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu)) {
1036
				$queue_next_job_time = intval($contenu);
1037
			}
1038
		}
1039
	}
1040
1041
	if (is_null($queue_next_job_time)) {
1042
		return null;
1043
	}
1044
	if (!$_SERVER['REQUEST_TIME']) {
1045
		$_SERVER['REQUEST_TIME'] = time();
1046
	}
1047
1048
	return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
1049
}
1050
1051
1052
/**
1053
 * Transformation XML des `&` en `&amp;`
1054
 * 
1055
 * @pipeline post_typo
1056
 * @param string $u
1057
 * @return string
1058
 */
1059
function quote_amp($u) {
1060
	return preg_replace(
1061
		"/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i",
1062
		"&amp;", $u);
1063
}
1064
1065
1066
/**
1067
 * Produit une balise `<script>` valide
1068
 *
1069
 * @example
1070
 *     ```
1071
 *     echo http_script('alert("ok");');
1072
 *     echo http_script('','js/jquery.js');
1073
 *     ```
1074
 *
1075
 * @param string $script
1076
 *     Code source du script
1077
 * @param string $src
1078
 *     Permet de faire appel à un fichier javascript distant
1079
 * @param string $noscript
1080
 *     Contenu de la balise  `<noscript>`
1081
 * @return string
1082
 *     Balise HTML `<script>` et son contenu
1083
 **/
1084
function http_script($script, $src = '', $noscript = '') {
1085
	static $done = array();
1086
1087
	if ($src && !isset($done[$src])) {
1088
		$done[$src] = true;
1089
		$src = find_in_path($src, _JAVASCRIPT);
1090
		$src = " src='$src'";
1091
	} else {
1092
		$src = '';
1093
	}
1094
	if ($script) {
1095
		$script = ("/*<![CDATA[*/\n" .
1096
			preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
1097
			"/*]]>*/");
1098
	}
1099
	if ($noscript) {
1100
		$noscript = "<noscript>\n\t$noscript\n</noscript>\n";
1101
	}
1102
1103
	return ($src or $script or $noscript)
1104
		? "<script type='text/javascript'$src>$script</script>$noscript"
1105
		: '';
1106
}
1107
1108
1109
/**
1110
 * Sécurise du texte à écrire dans du PHP ou du Javascript.
1111
 *
1112
 * Transforme n'importe quel texte en une chaîne utilisable
1113
 * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
1114
 * simples (`'` uniquement ; pas `"`)
1115
 *
1116
 * Utile particulièrement en filtre dans un squelettes
1117
 * pour écrire un contenu dans une variable JS ou PHP.
1118
 *
1119
 * Échappe les apostrophes (') du contenu transmis.
1120
 *
1121
 * @link http://www.spip.net/4281
1122
 * @example
1123
 *     PHP dans un squelette
1124
 *     ```
1125
 *     $x = '[(#TEXTE|texte_script)]';
1126
 *     ```
1127
 *
1128
 *     JS dans un squelette (transmettre une chaîne de langue)
1129
 *     ```
1130
 *     $x = '<:afficher_calendrier|texte_script:>';
1131
 *     ```
1132
 *
1133
 * @filtre
1134
 * @param string $texte
1135
 *     Texte à échapper
1136
 * @return string
1137
 *     Texte échappé
1138
 **/
1139
function texte_script($texte) {
1140
	return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
1141
}
1142
1143
1144
/**
1145
 * Gestion des chemins (ou path) de recherche de fichiers par SPIP
1146
 *
1147
 * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
1148
 * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
1149
 * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
1150
 *
1151
 * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
1152
 * en tête du path, dans cet ordre (hormis `squelettes` & la globale
1153
 * `$dossier_squelette` si définie qui resteront devant)
1154
 *
1155
 * Retourne dans tous les cas la liste des chemins.
1156
 *
1157
 * @note
1158
 *     Cette fonction est appelée à plusieurs endroits et crée une liste
1159
 *     de chemins finale à peu près de la sorte :
1160
 *
1161
 *     - dossiers squelettes (si globale précisée)
1162
 *     - squelettes/
1163
 *     - plugins (en fonction de leurs dépendances) : ceux qui dépendent
1164
 *       d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
1165
 *     - racine du site
1166
 *     - squelettes-dist/
1167
 *     - prive/
1168
 *     - ecrire/
1169
 *
1170
 * @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...
1171
 *     - Répertoire(s) à empiler au path
1172
 *     - '' provoque un recalcul des chemins.
1173
 * @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...
1174
 *     Liste des chemins, par ordre de priorité.
1175
 **/
1176
function _chemin($dir_path = null) {
1177
	static $path_base = null;
1178
	static $path_full = null;
1179
	if ($path_base == null) {
1180
		// Chemin standard depuis l'espace public
1181
		$path = defined('_SPIP_PATH') ? _SPIP_PATH :
1182
			_DIR_RACINE . ':' .
1183
			_DIR_RACINE . 'squelettes-dist/:' .
1184
			_DIR_RACINE . 'prive/:' .
1185
			_DIR_RESTREINT;
1186
		// Ajouter squelettes/
1187
		if (@is_dir(_DIR_RACINE . 'squelettes')) {
1188
			$path = _DIR_RACINE . 'squelettes/:' . $path;
1189
		}
1190
		foreach (explode(':', $path) as $dir) {
1191
			if (strlen($dir) and substr($dir, -1) != '/') {
1192
				$dir .= "/";
1193
			}
1194
			$path_base[] = $dir;
1195
		}
1196
		$path_full = $path_base;
1197
		// Et le(s) dossier(s) des squelettes nommes
1198 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...
1199
			foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1200
				array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
1201
			}
1202
		}
1203
		$GLOBALS['path_sig'] = md5(serialize($path_full));
1204
	}
1205
	if ($dir_path === null) {
1206
		return $path_full;
1207
	}
1208
1209
	if (strlen($dir_path)) {
1210
		$tete = "";
1211
		if (reset($path_base) == _DIR_RACINE . 'squelettes/') {
1212
			$tete = array_shift($path_base);
1213
		}
1214
		$dirs = array_reverse(explode(':', $dir_path));
1215
		foreach ($dirs as $dir_path) {
1216
			#if ($dir_path{0}!='/')
1217
			#	$dir_path = $dir_path;
1218
			if (substr($dir_path, -1) != '/') {
1219
				$dir_path .= "/";
1220
			}
1221
			if (!in_array($dir_path, $path_base)) {
1222
				array_unshift($path_base, $dir_path);
1223
			}
1224
		}
1225
		if (strlen($tete)) {
1226
			array_unshift($path_base, $tete);
1227
		}
1228
	}
1229
	$path_full = $path_base;
1230
	// Et le(s) dossier(s) des squelettes nommes
1231 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...
1232
		foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1233
			array_unshift($path_full, ((isset($d[0]) and $d[0] == '/') ? '' : _DIR_RACINE) . $d . '/');
1234
		}
1235
	}
1236
1237
	$GLOBALS['path_sig'] = md5(serialize($path_full));
1238
1239
	return $path_full;
1240
}
1241
1242
/**
1243
 * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
1244
 *
1245
 * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
1246
 *
1247
 * @uses _chemin()
1248
 *
1249
 * @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...
1250
 **/
1251
function creer_chemin() {
1252
	$path_a = _chemin();
1253
	static $c = '';
1254
1255
	// on calcule le chemin si le dossier skel a change
1256
	if ($c != $GLOBALS['dossier_squelettes']) {
1257
		// assurer le non plantage lors de la montee de version :
1258
		$c = $GLOBALS['dossier_squelettes'];
1259
		$path_a = _chemin(''); // forcer un recalcul du chemin
1260
	}
1261
1262
	return $path_a;
1263
}
1264
1265
1266
function lister_themes_prives() {
1267
	static $themes = null;
1268
	if (is_null($themes)) {
1269
		// si pas encore definie
1270
		if (!defined('_SPIP_THEME_PRIVE')) {
1271
			define('_SPIP_THEME_PRIVE', 'spip');
1272
		}
1273
		$themes = array(_SPIP_THEME_PRIVE);
1274
		// lors d'une installation neuve, prefs n'est pas definie.
1275 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...
1276
			$prefs = $GLOBALS['visiteur_session']['prefs'];
1277
		} else {
1278
			$prefs = array();
1279
		}
1280
		if (is_string($prefs)) {
1281
			$prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
1282
		}
1283
		if (
1284
			((isset($prefs['theme']) and $theme = $prefs['theme'])
1285
				or (isset($GLOBALS['theme_prive_defaut']) and $theme = $GLOBALS['theme_prive_defaut']))
1286
			and $theme != _SPIP_THEME_PRIVE
1287
		) {
1288
			array_unshift($themes, $theme);
1289
		} // placer le theme choisi en tete
1290
	}
1291
1292
	return $themes;
1293
}
1294
1295
function find_in_theme($file, $subdir = '', $include = false) {
1296
	static $themefiles = array();
1297
	if (isset($themefiles["$subdir$file"])) {
1298
		return $themefiles["$subdir$file"];
1299
	}
1300
	$themes = lister_themes_prives();
1301
	foreach ($themes as $theme) {
1302
		if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
1303
			return $themefiles["$subdir$file"] = $f;
1304
		}
1305
	}
1306
	spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');
1307
1308
	return $themefiles["$subdir$file"] = "";
1309
}
1310
1311
1312
/**
1313
 * Cherche une image dans les dossiers d'images
1314
 *
1315
 * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
1316
 * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
1317
 * de facon temporaire le temps de la migration, et cherche de nouveau.
1318
 *
1319
 * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
1320
 * dans le répertoire défini par la constante `_NOM_IMG_PACK`
1321
 *
1322
 * @see find_in_theme()
1323
 * @see inc_icone_renommer_dist()
1324
 *
1325
 * @param string $icone
1326
 *     Nom de l'icone cherchée
1327
 * @return string
1328
 *     Chemin complet de l'icone depuis la racine si l'icone est trouée,
1329
 *     sinon chaîne vide.
1330
 **/
1331
function chemin_image($icone) {
1332
	static $icone_renommer;
1333
	// gerer le cas d'un double appel en evitant de refaire le travail inutilement
1334
	if (strpos($icone, "/") !== false and file_exists($icone)) {
1335
		return $icone;
1336
	}
1337
1338
	// si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
1339
	if (preg_match(',[.](png|gif|jpg)$,', $icone) and $f = find_in_theme("images/$icone")) {
1340
		return $f;
1341
	}
1342
	// sinon passer par le module de renommage
1343
	if (is_null($icone_renommer)) {
1344
		$icone_renommer = charger_fonction('icone_renommer', 'inc', true);
1345
	}
1346
	if ($icone_renommer) {
1347
		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...
1348
		if (file_exists($icone)) {
1349
			return $icone;
1350
		}
1351
	}
1352
1353
	return find_in_path($icone, _NOM_IMG_PACK);
1354
}
1355
1356
//
1357
// chercher un fichier $file dans le SPIP_PATH
1358
// si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
1359
// si 3e arg vrai, on inclut si ce n'est fait.
1360
$GLOBALS['path_sig'] = '';
1361
$GLOBALS['path_files'] = null;
1362
1363
/**
1364
 * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
1365
 *
1366
 * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
1367
 * suivant l'ordre des chemins connus de SPIP.
1368
 *
1369
 * @api
1370
 * @see  charger_fonction()
1371
 * @uses creer_chemin() Pour la liste des chemins.
1372
 * @example
1373
 *     ```
1374
 *     $f = find_in_path('css/perso.css');
1375
 *     $f = find_in_path('perso.css', 'css');
1376
 *     ```
1377
 *
1378
 * @param string $file
1379
 *     Fichier recherché
1380
 * @param string $dirname
1381
 *     Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
1382
 * @param bool|string $include
1383
 *     - false : ne fait rien de plus
1384
 *     - true : inclut le fichier (include_once)
1385
 *     - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
1386
 * @return string|bool
1387
 *     - string : chemin du fichier trouvé
1388
 *     - false : fichier introuvable
1389
 **/
1390
function find_in_path($file, $dirname = '', $include = false) {
1391
	static $dirs = array();
1392
	static $inc = array(); # cf http://trac.rezo.net/trac/spip/changeset/14743
1393
	static $c = '';
1394
1395
	// on calcule le chemin si le dossier skel a change
1396
	if ($c != $GLOBALS['dossier_squelettes']) {
1397
		// assurer le non plantage lors de la montee de version :
1398
		$c = $GLOBALS['dossier_squelettes'];
1399
		creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
1400
	}
1401
1402
	if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
1403
		if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
1404
			return false;
1405
		}
1406
		if ($include and !isset($inc[$dirname][$file])) {
1407
			include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1408
			$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1409
		}
1410
1411
		return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1412
	}
1413
1414
	$a = strrpos($file, '/');
1415
	if ($a !== false) {
1416
		$dirname .= substr($file, 0, ++$a);
1417
		$file = substr($file, $a);
1418
	}
1419
1420
	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...
1421 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...
1422
			$dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a);
1423
		}
1424
		if ($dirs[$a]) {
1425
			if (file_exists(_ROOT_CWD . ($a .= $file))) {
1426
				if ($include and !isset($inc[$dirname][$file])) {
1427
					include_once _ROOT_CWD . $a;
1428
					$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1429
				}
1430 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...
1431
					// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1432
					if (is_null($GLOBALS['path_files'])) {
1433
						return $a;
1434
					}
1435
					define('_SAUVER_CHEMIN', true);
1436
				}
1437
1438
				return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
1439
			}
1440
		}
1441
	}
1442
1443
	if ($include) {
1444
		spip_log("include_spip $dirname$file non trouve");
1445
		if ($include === 'required') {
1446
			echo '<pre>',
1447
			"<strong>Erreur Fatale</strong><br />";
1448
			if (function_exists('debug_print_backtrace')) {
1449
				echo debug_print_backtrace();
1450
			}
1451
			echo '</pre>';
1452
			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...
1453
		}
1454
	}
1455
1456 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...
1457
		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1458
		if (is_null($GLOBALS['path_files'])) {
1459
			return false;
1460
		}
1461
		define('_SAUVER_CHEMIN', true);
1462
	}
1463
1464
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1465
}
1466
1467
function clear_path_cache() {
1468
	$GLOBALS['path_files'] = array();
1469
	spip_unlink(_CACHE_CHEMIN);
1470
}
1471
1472
function load_path_cache() {
1473
	// charger le path des plugins
1474
	if (@is_readable(_CACHE_PLUGINS_PATH)) {
1475
		include_once(_CACHE_PLUGINS_PATH);
1476
	}
1477
	$GLOBALS['path_files'] = array();
1478
	// si le visiteur est admin,
1479
	// on ne recharge pas le cache pour forcer sa mise a jour
1480
	if (
1481
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1482
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1483
		// utiliser le cookie est un pis aller qui marche 'en general'
1484
		// on blinde par un second test au moment de la lecture de la session
1485
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1486
		// et en ignorant ce cache en cas de recalcul explicite
1487
	!_request('var_mode')
1488
	) {
1489
		// on essaye de lire directement sans verrou pour aller plus vite
1490
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
1491
			// mais si semble corrompu on relit avec un verrou
1492
			if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1493
				lire_fichier(_CACHE_CHEMIN, $contenu);
1494
				if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1495
					$GLOBALS['path_files'] = array();
1496
				}
1497
			}
1498
		}
1499
	}
1500
}
1501
1502
function save_path_cache() {
1503
	if (defined('_SAUVER_CHEMIN')
1504
		and _SAUVER_CHEMIN
1505
	) {
1506
		ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
1507
	}
1508
}
1509
1510
1511
/**
1512
 * Trouve tous les fichiers du path correspondants à un pattern
1513
 *
1514
 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1515
 * par un `find_in_path()`
1516
 *
1517
 * @api
1518
 * @uses creer_chemin()
1519
 * @uses preg_files()
1520
 *
1521
 * @param string $dir
1522
 * @param string $pattern
1523
 * @param bool $recurs
1524
 * @return array
1525
 */
1526
function find_all_in_path($dir, $pattern, $recurs = false) {
1527
	$liste_fichiers = array();
1528
	$maxfiles = 10000;
1529
1530
	// cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1531
	// on a pas encore inclus flock.php
1532
	if (!function_exists('preg_files')) {
1533
		include_once _ROOT_RESTREINT . 'inc/flock.php';
1534
	}
1535
1536
	// Parcourir le chemin
1537
	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...
1538
		$f = $d . $dir;
1539
		if (@is_dir($f)) {
1540
			$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...
1541
			foreach ($liste as $chemin) {
1542
				$nom = basename($chemin);
1543
				// ne prendre que les fichiers pas deja trouves
1544
				// car find_in_path prend le premier qu'il trouve,
1545
				// les autres sont donc masques
1546
				if (!isset($liste_fichiers[$nom])) {
1547
					$liste_fichiers[$nom] = $chemin;
1548
				}
1549
			}
1550
		}
1551
	}
1552
1553
	return $liste_fichiers;
1554
}
1555
1556
/**
1557
 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1558
 * @param string $nom
1559
 * @return bool
1560
 */
1561
function autoriser_sans_cookie($nom) {
1562
	static $autsanscookie = array('install', 'base_repair');
1563
	$nom = preg_replace('/.php[3]?$/', '', basename($nom));
1564
1565
	return in_array($nom, $autsanscookie);
1566
}
1567
1568
/**
1569
 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1570
 *
1571
 * @api
1572
 * @param string $id
1573
 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
1574
 * @param string $entite
1575
 *   surnom de la table SQL (donne acces au nom de cle primaire)
1576
 * @param string $args
1577
 *   query_string a placer apres cle=$id&....
1578
 * @param string $ancre
1579
 *   ancre a mettre a la fin de l'URL a produire
1580
 * @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...
1581
 *   produire l'URL publique ou privee (par defaut: selon espace)
1582
 *   si string : serveur de base de donnee (nom du connect)
1583
 * @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...
1584
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
1585
 * @return string|array
1586
 *   url codee ou fonction de decodage
1587
 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1588
 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1589
 */
1590
function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1591
	if ($public === null) {
1592
		$public = !test_espace_prive();
1593
	}
1594
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1595
1596
	if (!$public) {
1597
		if (!$entite) {
1598
			return '';
1599
		}
1600
		if (!function_exists('generer_url_ecrire_objet')) {
1601
			include_spip('inc/urls');
1602
		}
1603
		$res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1604
	} else {
1605
		if ($type === null) {
1606
			$type = (isset($GLOBALS['type_urls']))
1607
				? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1608
				: ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1609
					? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1610
		}
1611
1612
		$f = charger_fonction($type, 'urls', true);
1613
		// se rabattre sur les urls page si les urls perso non dispo
1614
		if (!$f) {
1615
			$f = charger_fonction('page', 'urls', true);
1616
		}
1617
1618
		// si $entite='', on veut la fonction de passage URL ==> id
1619
		// sinon on veut effectuer le passage id ==> URL
1620
		if (!$entite) {
1621
			return $f;
1622
		}
1623
1624
		// mais d'abord il faut tester le cas des urls sur une
1625
		// base distante
1626
		if (is_string($public)
1627
			and $g = charger_fonction('connect', 'urls', true)
1628
		) {
1629
			$f = $g;
1630
		}
1631
1632
		$res = $f(intval($id), $entite, $args, $ancre, $public);
1633
1634
	}
1635
	if ($res) {
1636
		return $res;
1637
	}
1638
	// Sinon c'est un raccourci ou compat SPIP < 2
1639
	if (!function_exists($f = 'generer_url_' . $entite)) {
1640
		if (!function_exists($f .= '_dist')) {
1641
			$f = '';
1642
		}
1643
	}
1644
	if ($f) {
1645
		$url = $f($id, $args, $ancre);
1646
		if (strlen($args)) {
1647
			$url .= strstr($url, '?')
1648
				? '&amp;' . $args
1649
				: '?' . $args;
1650
		}
1651
1652
		return $url;
1653
	}
1654
	// On a ete gentil mais la ....
1655
	spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1656
1657
	return '';
1658
}
1659
1660
function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1661
	$exec = objet_info($entite, 'url_edit');
1662
	$url = generer_url_ecrire($exec, $args);
1663
	if (intval($id)) {
1664
		$url = parametre_url($url, id_table_objet($entite), $id);
1665
	} else {
1666
		$url = parametre_url($url, 'new', 'oui');
1667
	}
1668
	if ($ancre) {
1669
		$url = ancre_url($url, $ancre);
1670
	}
1671
1672
	return $url;
1673
}
1674
1675
// http://code.spip.net/@urls_connect_dist
1676
function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1677
	include_spip('base/connect_sql');
1678
	$id_type = id_table_objet($entite, $public);
1679
1680
	return _DIR_RACINE . get_spip_script('./')
1681
	. "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1682
	. (!$args ? '' : "&$args")
1683
	. (!$ancre ? '' : "#$ancre");
1684
}
1685
1686
1687
/**
1688
 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1689
 *
1690
 * @param string $url
1691
 * @return string
1692
 */
1693
function urlencode_1738($url) {
1694
	if (preg_match(',[^\x00-\x7E],sS', $url)) {
1695
		$uri = '';
1696
		for ($i = 0; $i < strlen($url); $i++) {
1697
			if (ord($a = $url[$i]) > 127) {
1698
				$a = rawurlencode($a);
1699
			}
1700
			$uri .= $a;
1701
		}
1702
		$url = $uri;
1703
	}
1704
1705
	return quote_amp($url);
1706
}
1707
1708
// http://code.spip.net/@generer_url_entite_absolue
1709
function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1710
	if (!$connect) {
1711
		$connect = true;
1712
	}
1713
	$h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1714
	if (!preg_match(',^\w+:,', $h)) {
1715
		include_spip('inc/filtres_mini');
1716
		$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...
1717
	}
1718
1719
	return $h;
1720
}
1721
1722
1723
/**
1724
 * Tester qu'une variable d'environnement est active
1725
 *
1726
 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1727
 * variables d'environnement comme $_SERVER[HTTPS] ou ini_get(register_globals)
1728
 *
1729
 * @param string|bool $truc
1730
 *     La valeur de la variable d'environnement
1731
 * @return bool
1732
 *     true si la valeur est considérée active ; false sinon.
1733
 **/
1734
function test_valeur_serveur($truc) {
1735
	if (!$truc) {
1736
		return false;
1737
	}
1738
1739
	return (strtolower($truc) !== 'off');
1740
}
1741
1742
//
1743
// Fonctions de fabrication des URL des scripts de Spip
1744
//
1745
/**
1746
 * Calcule l'url de base du site
1747
 *
1748
 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1749
 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1750
 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1751
 *
1752
 * @note
1753
 *     La globale `$profondeur_url` doit être initialisée de manière à
1754
 *     indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1755
 *     racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1756
 *     la racine 0. Sur url/perso/ elle vaut 2
1757
 *
1758
 * @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...
1759
 *    - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1760
 *    - si int : indique que l'on veut l'url pour la profondeur indiquée
1761
 *    - si bool : retourne le tableau static complet
1762
 *    - si array : réinitialise le tableau static complet avec la valeur fournie
1763
 * @return string|array
1764
 */
1765
function url_de_base($profondeur = null) {
1766
1767
	static $url = array();
1768
	if (is_array($profondeur)) {
1769
		return $url = $profondeur;
1770
	}
1771
	if ($profondeur === false) {
1772
		return $url;
1773
	}
1774
1775
	if (is_null($profondeur)) {
1776
		$profondeur = $GLOBALS['profondeur_url'];
1777
	}
1778
1779
	if (isset($url[$profondeur])) {
1780
		return $url[$profondeur];
1781
	}
1782
1783
	$http = 'http';
1784
1785
	if (
1786
		isset($_SERVER["SCRIPT_URI"])
1787
		and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1788
	) {
1789
		$http = 'https';
1790
	} elseif (
1791
		isset($_SERVER['HTTPS'])
1792
		and test_valeur_serveur($_SERVER['HTTPS'])
1793
	) {
1794
		$http = 'https';
1795
	}
1796
1797
	// note : HTTP_HOST contient le :port si necessaire
1798
	$host = $_SERVER['HTTP_HOST'];
1799
	// si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1800
	if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1801
		$host = $GLOBALS['meta']['adresse_site'];
1802
		if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1803
			$http = $scheme;
1804
			$host = str_replace("{$scheme}://", '', $host);
1805
		}
1806
	}
1807
	if (isset($_SERVER['SERVER_PORT'])
1808
		and $port = $_SERVER['SERVER_PORT']
1809
		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...
1810
	) {
1811
		if (!defined('_PORT_HTTP_STANDARD')) {
1812
			define('_PORT_HTTP_STANDARD', '80');
1813
		}
1814
		if (!defined('_PORT_HTTPS_STANDARD')) {
1815
			define('_PORT_HTTPS_STANDARD', '443');
1816
		}
1817
		if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
1818
			$host .= ":$port";
1819
		}
1820
		if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
1821
			$host .= ":$port";
1822
		}
1823
	}
1824
1825 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...
1826
		if (isset($_SERVER['REQUEST_URI'])) {
1827
			$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1828
		} else {
1829
			$GLOBALS['REQUEST_URI'] = $_SERVER['PHP_SELF'];
1830
			if ($_SERVER['QUERY_STRING']
1831
				and !strpos($_SERVER['REQUEST_URI'], '?')
1832
			) {
1833
				$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1834
			}
1835
		}
1836
	}
1837
1838
	$url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1839
1840
	return $url[$profondeur];
1841
}
1842
1843
/**
1844
 * fonction testable de construction d'une url appelee par url_de_base()
1845
 *
1846
 * @param string $http
1847
 * @param string $host
1848
 * @param string $request
1849
 * @param int $prof
1850
 * @return string
1851
 */
1852
function url_de_($http, $host, $request, $prof = 0) {
1853
	$prof = max($prof, 0);
1854
1855
	$myself = ltrim($request, '/');
1856
	# supprimer la chaine de GET
1857
	list($myself) = explode('?', $myself);
1858
	// vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
1859
	// protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
1860 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...
1861
		$myself = explode('://',$myself);
1862
		array_shift($myself);
1863
		$myself = implode('://',$myself);
1864
		$myself = explode('/',$myself);
1865
		array_shift($myself);
1866
		$myself = implode('/',$myself);
1867
	}
1868
	$url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
1869
1870
	$url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
1871
1872
	return $url;
1873
}
1874
1875
1876
// Pour une redirection, la liste des arguments doit etre separee par "&"
1877
// Pour du code XHTML, ca doit etre &amp;
1878
// Bravo au W3C qui n'a pas ete capable de nous eviter ca
1879
// faute de separer proprement langage et meta-langage
1880
1881
// Attention, X?y=z et "X/?y=z" sont completement differents!
1882
// http://httpd.apache.org/docs/2.0/mod/mod_dir.html
1883
1884
/**
1885
 * Crée une URL vers un script de l'espace privé
1886
 *
1887
 * @example
1888
 *     ```
1889
 *     generer_url_ecrire('admin_plugin')
1890
 *     ```
1891
 *
1892
 * @param string $script
1893
 *     Nom de la page privée (xx dans exec=xx)
1894
 * @param string $args
1895
 *     Arguments à transmettre, tel que `arg1=yy&arg2=zz`
1896
 * @param bool $no_entities
1897
 *     Si false : transforme les `&` en `&amp;`
1898
 * @param bool|string $rel
1899
 *     URL relative ?
1900
 *
1901
 *     - false : l’URL sera complète et contiendra l’URL du site
1902
 *     - true : l’URL sera relavive.
1903
 *     - string : on transmet l'url à la fonction
1904
 * @return string URL
1905
 **/
1906
function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
1907
	if (!$rel) {
1908
		$rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
1909
	} else {
1910
		if (!is_string($rel)) {
1911
			$rel = _DIR_RESTREINT ? _DIR_RESTREINT :
1912
				('./' . _SPIP_ECRIRE_SCRIPT);
1913
		}
1914
	}
1915
1916
	list($script, $ancre) = array_pad(explode('#', $script), 2, null);
1917
	if ($script and ($script <> 'accueil' or $rel)) {
1918
		$args = "?exec=$script" . (!$args ? '' : "&$args");
1919
	} elseif ($args) {
1920
		$args = "?$args";
1921
	}
1922
	if ($ancre) {
1923
		$args .= "#$ancre";
1924
	}
1925
1926
	return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
1927
}
1928
1929
/**
1930
 * Permet d'ajouter lien vers une page privée à un paramètre d'url (déprécié)
1931
 *
1932
 *     ```
1933
 *     // deprecié
1934
 *     $h = generer_url_ecrire('article', "id_article=$id_article&redirect=" . generer_url_retour('articles'));
1935
 *     // utiliser
1936
 *     $h = generer_url_ecrire('article');
1937
 *     $h = parametre_url($h, 'id_article', $id_article);
1938
 *     $h = parametre_url($h, 'redirect', generer_url_ecrire('articles'));
1939
 *     ```
1940
 *
1941
 * @deprecated Utiliser parametre_url() et generer_url_ecrire()
1942
 * @see parametre_url()
1943
 * @see generer_url_ecrire()
1944
 *
1945
 * @param string $script
1946
 * @param string $args
1947
 * @return string
1948
 */
1949
function generer_url_retour($script, $args = "") {
1950
	return rawurlencode(generer_url_ecrire($script, $args, true, true));
1951
}
1952
1953
//
1954
// Adresse des scripts publics (a passer dans inc-urls...)
1955
//
1956
1957
1958
/**
1959
 * Retourne le nom du fichier d'exécution de SPIP
1960
 *
1961
 * @see _SPIP_SCRIPT
1962
 * @note
1963
 *   Detecter le fichier de base, a la racine, comme etant spip.php ou ''
1964
 *   dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
1965
 *
1966
 * @param string $default
1967
 *     Script par défaut
1968
 * @return string
1969
 *     Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
1970
 **/
1971
function get_spip_script($default = '') {
1972
	# cas define('_SPIP_SCRIPT', '');
1973
	if (_SPIP_SCRIPT) {
1974
		return _SPIP_SCRIPT;
1975
	} else {
1976
		return $default;
1977
	}
1978
}
1979
1980
/**
1981
 * Crée une URL vers une page publique de SPIP
1982
 *
1983
 * @example
1984
 *     ```
1985
 *     generer_url_public("rubrique","id_rubrique=$id_rubrique")
1986
 *     ```
1987
 *
1988
 * @param string $script
1989
 *     Nom de la page
1990
 * @param string|array $args
1991
 *     Arguments à transmettre a l'URL,
1992
 *      soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
1993
 *      soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
1994
 * @param bool $no_entities
1995
 *     Si false : transforme les `&` en `&amp;`
1996
 * @param bool $rel
1997
 *     URL relative ?
1998
 *
1999
 *     - false : l’URL sera complète et contiendra l’URL du site
2000
 *     - true : l’URL sera relavive.
2001
 * @param string $action
2002
 *     - Fichier d'exécution public (spip.php par défaut)
2003
 * @return string URL
2004
 **/
2005
function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2006
	// si le script est une action (spip_pass, spip_inscription),
2007
	// standardiser vers la nouvelle API
2008
2009
	if (!$action) {
2010
		$action = get_spip_script();
2011
	}
2012
	if ($script) {
2013
		$action = parametre_url($action, _SPIP_PAGE, $script, '&');
2014
	}
2015
2016
	if ($args) {
2017
		if (is_array($args)) {
2018
			$r = '';
2019
			foreach ($args as $k => $v) {
2020
				$r .= '&' . $k . '=' . $v;
2021
			}
2022
			$args = substr($r, 1);
2023
		}
2024
		$action .=
2025
			(strpos($action, '?') !== false ? '&' : '?') . $args;
2026
	}
2027
	if (!$no_entities) {
2028
		$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...
2029
	}
2030
2031
	// ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2032
	return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2033
}
2034
2035
// http://code.spip.net/@generer_url_prive
2036
function generer_url_prive($script, $args = "", $no_entities = false) {
2037
2038
	return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2039
}
2040
2041
// Pour les formulaires en methode POST,
2042
// mettre le nom du script a la fois en input-hidden et dans le champ action:
2043
// 1) on peut ainsi memoriser le signet comme si c'etait un GET
2044
// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2045
2046
/**
2047
 * Retourne un formulaire (POST par défaut) vers un script exec
2048
 * de l’interface privée
2049
 *
2050
 * @param string $script
2051
 *     Nom de la page exec
2052
 * @param string $corps
2053
 *     Contenu du formulaire
2054
 * @param string $atts
2055
 *     Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2056
 * @param string $submit
2057
 *     Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2058
 * @return string
2059
 *     Code HTML du formulaire
2060
 **/
2061
function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2062
2063
	$script1 = explode('&', $script);
2064
	$script1 = reset($script1);
2065
2066
	return "<form action='"
2067
	. ($script ? generer_url_ecrire($script) : '')
2068
	. "' "
2069
	. ($atts ? $atts : " method='post'")
2070
	. "><div>\n"
2071
	. "<input type='hidden' name='exec' value='$script1' />"
2072
	. $corps
2073
	. (!$submit ? '' :
2074
		("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2075
	. "</div></form>\n";
2076
}
2077
2078
/**
2079
 * Générer un formulaire pour lancer une action vers $script
2080
 *
2081
 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2082
 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2083
 * qui ne sont pas destines a etre mis en signets
2084
 *
2085
 * @param string $script
2086
 * @param string $corps
2087
 * @param string $atts
2088
 * @param bool $public
2089
 * @return string
2090
 */
2091
function generer_form_action($script, $corps, $atts = '', $public = false) {
2092
	// si l'on est dans l'espace prive, on garde dans l'url
2093
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2094
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2095
	$h = (_DIR_RACINE and !$public)
2096
		? generer_url_ecrire(_request('exec'))
2097
		: generer_url_public();
2098
2099
	return "\n<form action='" .
2100
	$h .
2101
	"'" .
2102
	$atts .
2103
	">\n" .
2104
	"<div>" .
2105
	"\n<input type='hidden' name='action' value='$script' />" .
2106
	$corps .
2107
	"</div></form>";
2108
}
2109
2110
/**
2111
 * Créer une URL
2112
 *
2113
 * @param  string $script
2114
 *     Nom du script à exécuter
2115
 * @param  string $args
2116
 *     Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2117
 * @param bool $no_entities
2118
 *     Si false : transforme les & en &amp;
2119
 * @param boolean $public
2120
 *     URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2121
 *     true : l’URL sera relative.
2122
 * @return string
2123
 *     URL
2124
 */
2125
function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2126
	// si l'on est dans l'espace prive, on garde dans l'url
2127
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2128
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2129
	$url = (_DIR_RACINE and !$public)
2130
		? generer_url_ecrire(_request('exec'))
2131
		: generer_url_public('', '', false, false);
2132
	$url = parametre_url($url, 'action', $script);
2133
	if ($args) {
2134
		$url .= quote_amp('&' . $args);
2135
	}
2136
2137
	if ($no_entities) {
2138
		$url = str_replace('&amp;', '&', $url);
2139
	}
2140
2141
	return $url;
2142
}
2143
2144
2145
/**
2146
 * Fonction d'initialisation groupée pour compatibilité ascendante
2147
 *
2148
 * @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...
2149
 * @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...
2150
 * @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...
2151
 * @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...
2152
 */
2153
function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2154
	spip_initialisation_core($pi, $pa, $ti, $ta);
2155
	spip_initialisation_suite();
2156
}
2157
2158
/**
2159
 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2160
 *
2161
 * Elle définit les répertoires et fichiers non partageables
2162
 * et indique dans $test_dirs ceux devant être accessibles en écriture
2163
 * mais ne touche pas à cette variable si elle est déjà définie
2164
 * afin que mes_options.php puisse en spécifier d'autres.
2165
 *
2166
 * Elle définit ensuite les noms des fichiers et les droits.
2167
 * Puis simule un register_global=on sécurisé.
2168
 *
2169
 * @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...
2170
 * @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...
2171
 * @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...
2172
 * @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...
2173
 */
2174
function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2175
	static $too_late = 0;
2176
	if ($too_late++) {
2177
		return;
2178
	}
2179
2180
	// Declaration des repertoires
2181
2182
	// le nom du repertoire plugins/ activables/desactivables
2183
	if (!defined('_DIR_PLUGINS')) {
2184
		define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2185
	}
2186
2187
	// le nom du repertoire des extensions/ permanentes du core, toujours actives
2188
	if (!defined('_DIR_PLUGINS_DIST')) {
2189
		define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2190
	}
2191
2192
	// le nom du repertoire des librairies
2193
	if (!defined('_DIR_LIB')) {
2194
		define('_DIR_LIB', _DIR_RACINE . "lib/");
2195
	}
2196
2197
	if (!defined('_DIR_IMG')) {
2198
		define('_DIR_IMG', $pa);
2199
	}
2200
	if (!defined('_DIR_LOGOS')) {
2201
		define('_DIR_LOGOS', $pa);
2202
	}
2203
	if (!defined('_DIR_IMG_ICONES')) {
2204
		define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2205
	}
2206
2207
	if (!defined('_DIR_DUMP')) {
2208
		define('_DIR_DUMP', $ti . "dump/");
2209
	}
2210
	if (!defined('_DIR_SESSIONS')) {
2211
		define('_DIR_SESSIONS', $ti . "sessions/");
2212
	}
2213
	if (!defined('_DIR_TRANSFERT')) {
2214
		define('_DIR_TRANSFERT', $ti . "upload/");
2215
	}
2216
	if (!defined('_DIR_CACHE')) {
2217
		define('_DIR_CACHE', $ti . "cache/");
2218
	}
2219
	if (!defined('_DIR_CACHE_XML')) {
2220
		define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2221
	}
2222
	if (!defined('_DIR_SKELS')) {
2223
		define('_DIR_SKELS', _DIR_CACHE . "skel/");
2224
	}
2225
	if (!defined('_DIR_AIDE')) {
2226
		define('_DIR_AIDE', _DIR_CACHE . "aide/");
2227
	}
2228
	if (!defined('_DIR_TMP')) {
2229
		define('_DIR_TMP', $ti);
2230
	}
2231
2232
	if (!defined('_DIR_VAR')) {
2233
		define('_DIR_VAR', $ta);
2234
	}
2235
2236
	if (!defined('_DIR_ETC')) {
2237
		define('_DIR_ETC', $pi);
2238
	}
2239
	if (!defined('_DIR_CONNECT')) {
2240
		define('_DIR_CONNECT', $pi);
2241
	}
2242
	if (!defined('_DIR_CHMOD')) {
2243
		define('_DIR_CHMOD', $pi);
2244
	}
2245
2246
	if (!isset($GLOBALS['test_dirs']))
2247
		// Pas $pi car il est bon de le mettre hors ecriture apres intstall
2248
		// il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2249
	{
2250
		$GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2251
	}
2252
2253
	// Declaration des fichiers
2254
2255
	if (!defined('_CACHE_PLUGINS_PATH')) {
2256
		define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2257
	}
2258
	if (!defined('_CACHE_PLUGINS_OPT')) {
2259
		define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2260
	}
2261
	if (!defined('_CACHE_PLUGINS_FCT')) {
2262
		define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2263
	}
2264
	if (!defined('_CACHE_PIPELINES')) {
2265
		define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2266
	}
2267
	if (!defined('_CACHE_CHEMIN')) {
2268
		define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2269
	}
2270
2271
	# attention .php obligatoire pour ecrire_fichier_securise
2272
	if (!defined('_FILE_META')) {
2273
		define('_FILE_META', $ti . 'meta_cache.php');
2274
	}
2275
	if (!defined('_DIR_LOG')) {
2276
		define('_DIR_LOG', _DIR_TMP . 'log/');
2277
	}
2278
	if (!defined('_FILE_LOG')) {
2279
		define('_FILE_LOG', 'spip');
2280
	}
2281
	if (!defined('_FILE_LOG_SUFFIX')) {
2282
		define('_FILE_LOG_SUFFIX', '.log');
2283
	}
2284
2285
	// Le fichier de connexion a la base de donnees
2286
	// tient compte des anciennes versions (inc_connect...)
2287
	if (!defined('_FILE_CONNECT_INS')) {
2288
		define('_FILE_CONNECT_INS', 'connect');
2289
	}
2290
	if (!defined('_FILE_CONNECT')) {
2291
		define('_FILE_CONNECT',
2292
		(@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2293
			: (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2294
				: false)));
2295
	}
2296
2297
	// Le fichier de reglages des droits
2298
	if (!defined('_FILE_CHMOD_INS')) {
2299
		define('_FILE_CHMOD_INS', 'chmod');
2300
	}
2301
	if (!defined('_FILE_CHMOD')) {
2302
		define('_FILE_CHMOD',
2303
		(@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2304
			: false));
2305
	}
2306
2307
	if (!defined('_FILE_LDAP')) {
2308
		define('_FILE_LDAP', 'ldap.php');
2309
	}
2310
2311
	if (!defined('_FILE_TMP_SUFFIX')) {
2312
		define('_FILE_TMP_SUFFIX', '.tmp.php');
2313
	}
2314
	if (!defined('_FILE_CONNECT_TMP')) {
2315
		define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2316
	}
2317
	if (!defined('_FILE_CHMOD_TMP')) {
2318
		define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2319
	}
2320
2321
	// Definition des droits d'acces en ecriture
2322
	if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2323
		include_once _FILE_CHMOD;
2324
	}
2325
2326
	// Se mefier des fichiers mal remplis!
2327
	if (!defined('_SPIP_CHMOD')) {
2328
		define('_SPIP_CHMOD', 0777);
2329
	}
2330
2331
	if (!defined('_DEFAULT_CHARSET')) {
2332
		/** Le charset par défaut lors de l'installation */
2333
		define('_DEFAULT_CHARSET', 'utf-8');
2334
	}
2335
	if (!defined('_ROOT_PLUGINS')) {
2336
		define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2337
	}
2338
	if (!defined('_ROOT_PLUGINS_DIST')) {
2339
		define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2340
	}
2341
	if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2342
		define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2343
	}
2344
2345
	// La taille des Log
2346
	if (!defined('_MAX_LOG')) {
2347
		define('_MAX_LOG', 100);
2348
	}
2349
2350
	// Sommes-nous dans l'empire du Mal ?
2351
	// (ou sous le signe du Pingouin, ascendant GNU ?)
2352
	if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2353
		if (!defined('_OS_SERVEUR')) {
2354
			define('_OS_SERVEUR', 'windows');
2355
		}
2356
		if (!defined('_SPIP_LOCK_MODE')) {
2357
			define('_SPIP_LOCK_MODE', 1);
2358
		} // utiliser le flock php
2359
	} else {
2360
		if (!defined('_OS_SERVEUR')) {
2361
			define('_OS_SERVEUR', '');
2362
		}
2363
		if (!defined('_SPIP_LOCK_MODE')) {
2364
			define('_SPIP_LOCK_MODE', 1);
2365
		} // utiliser le flock php
2366
		#if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2367
	}
2368
2369
	// Langue par defaut
2370
	if (!defined('_LANGUE_PAR_DEFAUT')) {
2371
		define('_LANGUE_PAR_DEFAUT', 'fr');
2372
	}
2373
2374
	//
2375
	// Module de lecture/ecriture/suppression de fichiers utilisant flock()
2376
	// (non surchargeable en l'etat ; attention si on utilise include_spip()
2377
	// pour le rendre surchargeable, on va provoquer un reecriture
2378
	// systematique du noyau ou une baisse de perfs => a etudier)
2379
	include_once _ROOT_RESTREINT . 'inc/flock.php';
2380
2381
	// charger tout de suite le path et son cache
2382
	load_path_cache();
2383
2384
	// *********** traiter les variables ************
2385
2386
	//
2387
	// Securite
2388
	//
2389
2390
	// Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2391
	if (isset($_REQUEST['GLOBALS'])) {
2392
		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...
2393
	}
2394
	// nettoyer les magic quotes \' et les caracteres nuls %00
2395
	spip_desinfecte($_GET);
2396
	spip_desinfecte($_POST);
2397
	spip_desinfecte($_COOKIE);
2398
	spip_desinfecte($_REQUEST);
2399
2400
	// Si les variables sont passees en global par le serveur,
2401
	// il faut faire quelques verifications de base
2402
	// Todo: test à supprimer lorsque version PHP minimum >= 5.4.
2403
	$avertir_register_globals = false;
2404
	if (test_valeur_serveur(@ini_get('register_globals'))) {
2405
		// ne pas desinfecter les globales en profondeur car elle contient aussi les
2406
		// precedentes, qui seraient desinfectees 2 fois.
2407
		spip_desinfecte($GLOBALS, false);
2408
		// plugin grenier
2409
		if (include_spip('inc/php3')) {
2410
			spip_register_globals(true);
2411
		}
2412
2413
		$avertir_register_globals = true;
2414
	}
2415
2416
	// appliquer le cookie_prefix
2417
	if ($GLOBALS['cookie_prefix'] != 'spip') {
2418
		include_spip('inc/cookie');
2419
		recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2420
	}
2421
2422
	//
2423
	// Capacites php (en fonction de la version)
2424
	//
2425
	$GLOBALS['flag_ob'] = (function_exists("ob_start")
2426
		&& function_exists("ini_get")
2427
		&& !strstr(@ini_get('disable_functions'), 'ob_'));
2428
	$GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2429
	$GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
2430
	$GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2431
		(get_cfg_var('upload_max_filesize') > 0));
2432
2433
2434
	// Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2435 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...
2436
		$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2437
	} else {
2438
		$GLOBALS['REQUEST_URI'] = $_SERVER['PHP_SELF'];
2439
		if (!empty($_SERVER['QUERY_STRING'])
2440
			and !strpos($_SERVER['REQUEST_URI'], '?')
2441
		) {
2442
			$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2443
		}
2444
	}
2445
2446
	// Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2447
	if (!defined('_RENOUVELLE_ALEA')) {
2448
		define('_RENOUVELLE_ALEA', 12 * 3600);
2449
	}
2450
	if (!defined('_DUREE_COOKIE_ADMIN')) {
2451
		define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2452
	}
2453
2454
	// charger les meta si possible et renouveller l'alea au besoin
2455
	// charge aussi effacer_meta et ecrire_meta
2456
	$inc_meta = charger_fonction('meta', 'inc');
2457
	$inc_meta();
2458
2459
	// on a pas pu le faire plus tot
2460
	if ($avertir_register_globals) {
2461
		avertir_auteurs("register_globals",
2462
			_L("Probl&egrave;me de s&eacute;curit&eacute; : register_globals=on; dans php.ini &agrave; corriger."));
2463
	}
2464
2465
	// nombre de repertoires depuis la racine
2466
	// on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2467
	// ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2468
	// le calcul est faux)
2469
	if (!_DIR_RESTREINT) {
2470
		$GLOBALS['profondeur_url'] = 1;
2471
	} else {
2472
		$uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
2473
		$uri_ref = $_SERVER["SCRIPT_NAME"];
2474
		if (!$uri_ref
2475
			// si on est appele avec un autre ti, on est sans doute en mutu
2476
			// si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2477
			// a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2478
			// s'en remettre a l'adresse du site. alea jacta est.
2479
			or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2480
		) {
2481
2482
			if (isset($GLOBALS['meta']['adresse_site'])) {
2483
				$uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2484
				$uri_ref = $uri_ref['path'] . '/';
2485
			} else {
2486
				$uri_ref = "";
2487
			}
2488
		}
2489
		if (!$uri or !$uri_ref) {
2490
			$GLOBALS['profondeur_url'] = 0;
2491
		} else {
2492
			$GLOBALS['profondeur_url'] = max(0,
2493
				substr_count($uri[0], '/')
2494
				- substr_count($uri_ref, '/'));
2495
		}
2496
	}
2497
	// s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2498
	if (_FILE_CONNECT) {
2499
		if (verifier_visiteur() == '0minirezo'
2500
			// si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2501
			and !isset($_COOKIE['spip_admin'])
2502
		) {
2503
			clear_path_cache();
2504
		}
2505
	}
2506
2507
}
2508
2509
/**
2510
 * Complements d'initialisation non critiques pouvant etre realises
2511
 * par les plugins
2512
 *
2513
 */
2514
function spip_initialisation_suite() {
2515
	static $too_late = 0;
2516
	if ($too_late++) {
2517
		return;
2518
	}
2519
2520
	// taille mini des login
2521
	if (!defined('_LOGIN_TROP_COURT')) {
2522
		define('_LOGIN_TROP_COURT', 4);
2523
	}
2524
2525
	// la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2526
	#if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2527
	#if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2528
	#if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2529
2530
	// la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2531
	#if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2532
	#if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2533
	#if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2534
	#if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2535
2536
	if (!defined('_PASS_LONGUEUR_MINI')) {
2537
		define('_PASS_LONGUEUR_MINI', 6);
2538
	}
2539
2540
2541
	// Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2542
	if (!defined('_IMG_QUALITE')) {
2543
		define('_IMG_QUALITE', 85);
2544
	} # valeur par defaut
2545
	if (!defined('_IMG_GD_QUALITE')) {
2546
		define('_IMG_GD_QUALITE', _IMG_QUALITE);
2547
	} # surcharge pour la lib GD
2548
	if (!defined('_IMG_CONVERT_QUALITE')) {
2549
		define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
2550
	} # surcharge pour imagick en ligne de commande
2551
	// Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2552
	if (!defined('_IMG_IMAGICK_QUALITE')) {
2553
		define('_IMG_IMAGICK_QUALITE', 75);
2554
	} # surcharge pour imagick en PHP
2555
2556
	if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2557
		define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2558
	} // poids en octet
2559
2560
	// qq chaines standard
2561
	if (!defined('_ACCESS_FILE_NAME')) {
2562
		define('_ACCESS_FILE_NAME', '.htaccess');
2563
	}
2564
	if (!defined('_AUTH_USER_FILE')) {
2565
		define('_AUTH_USER_FILE', '.htpasswd');
2566
	}
2567
	if (!defined('_SPIP_DUMP')) {
2568
		define('_SPIP_DUMP', 'dump@nom_site@@[email protected]');
2569
	}
2570
	if (!defined('_CACHE_RUBRIQUES')) {
2571
		/** Fichier cache pour le navigateur de rubrique du bandeau */
2572
		define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
2573
	}
2574
	if (!defined('_CACHE_RUBRIQUES_MAX')) {
2575
		/** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2576
		define('_CACHE_RUBRIQUES_MAX', 500);
2577
	}
2578
2579
	if (!defined('_EXTENSION_SQUELETTES')) {
2580
		define('_EXTENSION_SQUELETTES', 'html');
2581
	}
2582
2583
	if (!defined('_DOCTYPE_ECRIRE')) {
2584
		define('_DOCTYPE_ECRIRE',
2585
			// "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>\n");
2586
			//"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n");
2587
			//"<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n");
2588
			// "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.1 //EN' 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'>\n");
2589
		"<!DOCTYPE html>\n");
2590
	}
2591
	if (!defined('_DOCTYPE_AIDE')) {
2592
		define('_DOCTYPE_AIDE',
2593
		"<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2594
	}
2595
2596
	/** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2597
	 * le script de l'espace public, alias index.php */
2598
	if (!defined('_SPIP_SCRIPT')) {
2599
		define('_SPIP_SCRIPT', 'spip.php');
2600
	}
2601
	/** Argument page, personalisable en cas de conflit avec un autre script */
2602
	if (!defined('_SPIP_PAGE')) {
2603
		define('_SPIP_PAGE', 'page');
2604
	}
2605
2606
	// le script de l'espace prive
2607
	// Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2608
	// les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2609
	// meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
2610
	if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2611
		define('_SPIP_ECRIRE_SCRIPT', (empty($_SERVER['SERVER_SOFTWARE']) ? '' :
2612
			preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE']) ?
2613
				'index.php' : ''));
2614
	}
2615
2616
2617
	if (!defined('_SPIP_AJAX')) {
2618
		define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2619
			? 1
2620
			: (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
2621
	}
2622
2623
	// La requete est-elle en ajax ?
2624
	if (!defined('_AJAX')) {
2625
		define('_AJAX',
2626
			(isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2627
				or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2628
				or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2629
				or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2630
			)
2631
			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
2632
		);
2633
	}
2634
2635
	# nombre de pixels maxi pour calcul de la vignette avec gd
2636
	# au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2637
	# les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2638
	if (!defined('_IMG_GD_MAX_PIXELS')) {
2639
		define('_IMG_GD_MAX_PIXELS',
2640
		(isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2641
			? $GLOBALS['meta']['max_taille_vignettes']
2642
			: 0);
2643
	}
2644
2645
	if (!defined('_MEMORY_LIMIT_MIN')) {
2646
		define('_MEMORY_LIMIT_MIN', 16);
2647
	} // en Mo
2648
	// si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2649
	// on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2650
	// il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2651
	if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
2652
		if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2653
			$unit = strtolower(substr($memory, -1));
2654
			$memory = substr($memory, 0, -1);
2655
			switch ($unit) {
2656
				// Le modifieur 'G' est disponible depuis PHP 5.1.0
2657
				case 'g':
2658
					$memory *= 1024;
2659
				case 'm':
2660
					$memory *= 1024;
2661
				case 'k':
2662
					$memory *= 1024;
2663
			}
2664
			if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
2665
				@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...
2666
				if (trim(ini_get('memory_limit')) != $m) {
2667
					if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2668
						define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2669
					} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2670
				}
2671
			}
2672
		} else {
2673
			if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2674
				define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2675
			}
2676
		} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2677
	}
2678
	// Protocoles a normaliser dans les chaines de langues
2679
	if (!defined('_PROTOCOLES_STD')) {
2680
		define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2681
	}
2682
2683
	init_var_mode();
2684
}
2685
2686
/**
2687
 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2688
 * la validité du cache ou certains affichages spéciaux.
2689
 *
2690
 * Le paramètre d'URL `var_mode` permet de
2691
 * modifier la pérennité du cache, recalculer des urls
2692
 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2693
 * d'afficher un écran de débug ou des traductions non réalisées.
2694
 *
2695
 * En fonction de ces paramètres dans l'URL appelante, on définit
2696
 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2697
 *
2698
 * Le paramètre `var_mode` accepte ces valeurs :
2699
 *
2700
 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2701
 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2702
 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2703
 * - `debug` :  modifie l'affichage activant le mode "debug"
2704
 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2705
 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2706
 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2707
 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2708
 *
2709
 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2710
 *
2711
 * @note
2712
 *     Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2713
 *     le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2714
 * `   var_mode` (calcul ou recalcul).
2715
 */
2716
function init_var_mode() {
2717
	static $done = false;
2718
	if (!$done) {
2719
2720
		if (isset($_GET['var_mode'])) {
2721
			$var_mode = explode(',', $_GET['var_mode']);
2722
			// tout le monde peut calcul/recalcul
2723
			if (!defined('_VAR_MODE')) {
2724
				if (in_array('recalcul', $var_mode)) {
2725
					define('_VAR_MODE', 'recalcul');
2726
				} elseif (in_array('calcul', $var_mode)) {
2727
					define('_VAR_MODE', 'calcul');
2728
				}
2729
				$var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2730
			}
2731
			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...
2732
				include_spip('inc/autoriser');
2733
				// autoriser preview si preview seulement, et sinon autoriser debug
2734
				if (autoriser(
2735
					($_GET['var_mode'] == 'preview')
2736
						? 'previsualiser'
2737
						: 'debug'
2738
				)) {
2739 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...
2740
						// forcer le calcul pour passer dans traduire
2741
						if (!defined('_VAR_MODE')) {
2742
							define('_VAR_MODE', 'calcul');
2743
						}
2744
						// et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2745
						if (!defined('_VAR_NOCACHE')) {
2746
							define('_VAR_NOCACHE', true);
2747
						}
2748
						$var_mode = array_diff($var_mode, array('traduction'));
2749
					}
2750 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...
2751
						// basculer sur les criteres de preview dans les boucles
2752
						if (!defined('_VAR_PREVIEW')) {
2753
							define('_VAR_PREVIEW', true);
2754
						}
2755
						// forcer le calcul
2756
						if (!defined('_VAR_MODE')) {
2757
							define('_VAR_MODE', 'calcul');
2758
						}
2759
						// et ne pas enregistrer de cache
2760
						if (!defined('_VAR_NOCACHE')) {
2761
							define('_VAR_NOCACHE', true);
2762
						}
2763
						$var_mode = array_diff($var_mode, array('preview'));
2764
					}
2765 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...
2766
						// forcer le compilo et ignorer les caches existants
2767
						if (!defined('_VAR_MODE')) {
2768
							define('_VAR_MODE', 'calcul');
2769
						}
2770
						if (!defined('_VAR_INCLURE')) {
2771
							define('_VAR_INCLURE', true);
2772
						}
2773
						// et ne pas enregistrer de cache
2774
						if (!defined('_VAR_NOCACHE')) {
2775
							define('_VAR_NOCACHE', true);
2776
						}
2777
						$var_mode = array_diff($var_mode, array('inclure'));
2778
					}
2779 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...
2780
						// forcer le compilo et ignorer les caches existants
2781
						if (!defined('_VAR_MODE')) {
2782
							define('_VAR_MODE', 'calcul');
2783
						}
2784
						if (!defined('_VAR_URLS')) {
2785
							define('_VAR_URLS', true);
2786
						}
2787
						$var_mode = array_diff($var_mode, array('urls'));
2788
					}
2789 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...
2790
						// forcer le compilo et ignorer les caches existants
2791
						if (!defined('_VAR_MODE')) {
2792
							define('_VAR_MODE', 'calcul');
2793
						}
2794
						// indiquer qu'on doit recalculer les images
2795
						if (!defined('_VAR_IMAGES')) {
2796
							define('_VAR_IMAGES', true);
2797
						}
2798
						$var_mode = array_diff($var_mode, array('images'));
2799
					}
2800 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...
2801
						if (!defined('_VAR_MODE')) {
2802
							define('_VAR_MODE', 'debug');
2803
						}
2804
						// et ne pas enregistrer de cache
2805
						if (!defined('_VAR_NOCACHE')) {
2806
							define('_VAR_NOCACHE', true);
2807
						}
2808
						$var_mode = array_diff($var_mode, array('debug'));
2809
					}
2810
					if (count($var_mode) and !defined('_VAR_MODE')) {
2811
						define('_VAR_MODE', reset($var_mode));
2812
					}
2813
					if (isset($GLOBALS['visiteur_session']['nom'])) {
2814
						spip_log($GLOBALS['visiteur_session']['nom']
2815
							. " " . _VAR_MODE);
2816
					}
2817
				} // pas autorise ?
2818
				else {
2819
					// si on n'est pas connecte on se redirige
2820
					if (!$GLOBALS['visiteur_session']) {
2821
						include_spip('inc/headers');
2822
						redirige_par_entete(generer_url_public('login',
2823
							'url=' . rawurlencode(
2824
								parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2825
							), true));
2826
					}
2827
					// sinon tant pis
2828
				}
2829
			}
2830
		}
2831
		if (!defined('_VAR_MODE')) {
2832
			/**
2833
			 * Indique le mode de calcul ou d'affichage de la page.
2834
			 * @see init_var_mode()
2835
			 */
2836
			define('_VAR_MODE', false);
2837
		}
2838
		$done = true;
2839
	}
2840
}
2841
2842
// Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2843
// supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2844
// la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2845
// http://code.spip.net/@spip_desinfecte
2846
function spip_desinfecte(&$t, $deep = true) {
2847
	static $magic_quotes;
2848
	if (!isset($magic_quotes)) {
2849
		$magic_quotes = @get_magic_quotes_gpc();
2850
	}
2851
2852
	foreach ($t as $key => $val) {
2853
		if (is_string($t[$key])) {
2854
			if ($magic_quotes) {
2855
				$t[$key] = stripslashes($t[$key]);
2856
			}
2857
			$t[$key] = str_replace(chr(0), '-', $t[$key]);
2858
		} // traiter aussi les "texte_plus" de article_edit
2859
		else {
2860
			if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2861
				spip_desinfecte($t[$key], $deep);
2862
			}
2863
		}
2864
	}
2865
}
2866
2867
//  retourne le statut du visiteur s'il s'annonce
2868
2869
// http://code.spip.net/@verifier_visiteur
2870
function verifier_visiteur() {
2871
	// Rq: pour que cette fonction marche depuis mes_options
2872
	// il faut forcer l'init si ce n'est fait
2873
	// mais on risque de perturber des plugins en initialisant trop tot
2874
	// certaines constantes
2875
	@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...
2876
		(_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
2877
		(_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
2878
		(_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
2879
		(_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
2880
	);
2881
2882
	// Demarrer une session NON AUTHENTIFIEE si on donne son nom
2883
	// dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
2884
	// Attention on separe bien session_nom et nom, pour eviter
2885
	// les melanges entre donnees SQL et variables plus aleatoires
2886
	$variables_session = array('session_nom', 'session_email');
2887
	foreach ($variables_session as $var) {
2888
		if (_request($var) !== null) {
2889
			$init = true;
2890
			break;
2891
		}
2892
	}
2893
	if (isset($init)) {
2894
		#@spip_initialisation_suite();
2895
		$session = charger_fonction('session', 'inc');
2896
		$session();
2897
		include_spip('inc/texte');
2898
		foreach ($variables_session as $var) {
2899
			if (($a = _request($var)) !== null) {
2900
				$GLOBALS['visiteur_session'][$var] = safehtml($a);
2901
			}
2902
		}
2903
		if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
2904
			$GLOBALS['visiteur_session']['id_auteur'] = 0;
2905
		}
2906
		$session($GLOBALS['visiteur_session']);
2907
2908
		return 0;
2909
	}
2910
2911
	$h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
2912
	if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
2913
2914
		$session = charger_fonction('session', 'inc');
2915
		if ($session()) {
2916
			return $GLOBALS['visiteur_session']['statut'];
2917
		}
2918
		if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
2919
			include_spip('inc/auth');
2920
			$h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
2921
		}
2922
		if ($h) {
2923
			$GLOBALS['visiteur_session'] = $h;
2924
2925
			return $GLOBALS['visiteur_session']['statut'];
2926
		}
2927
	}
2928
2929
	// au moins son navigateur nous dit la langue preferee de cet inconnu
2930
	include_spip('inc/lang');
2931
	utiliser_langue_visiteur();
2932
2933
	return false;
2934
}
2935
2936
2937
/**
2938
 * Sélectionne la langue donnée en argument et mémorise la courante
2939
 *
2940
 * Restaure l'ancienne langue si appellée sans argument.
2941
 *
2942
 * @note
2943
 *     On pourrait économiser l'empilement en cas de non changemnt
2944
 *     et lui faire retourner `False` pour prevenir l'appelant
2945
 *     Le noyau de Spip sait le faire, mais pour assurer la compatibilité
2946
 *     cette fonction retourne toujours non `False`
2947
 *
2948
 * @uses changer_langue()
2949
 * @param null|string $lang
2950
 *     - string : Langue à appliquer,
2951
 *     - null : Pour restituer la dernière langue mémorisée.
2952
 * @return string
2953
 *     - string Langue utilisée.
2954
 **/
2955
function lang_select($lang = null) {
2956
	static $pile_langues = array();
2957
	if (!function_exists('changer_langue')) {
2958
		include_spip('inc/lang');
2959
	}
2960
	if ($lang === null) {
2961
		$lang = array_pop($pile_langues);
2962
	} else {
2963
		array_push($pile_langues, $GLOBALS['spip_lang']);
2964
	}
2965
	if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
2966
		return $lang;
2967
	}
2968
	changer_langue($lang);
2969
2970
	return $lang;
2971
}
2972
2973
/**
2974
 * Renvoie une chaîne qui identifie la session courante
2975
 *
2976
 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
2977
 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
2978
 * de fichier cache.
2979
 *
2980
 * @pipeline_appel definir_session
2981
 *
2982
 * @param bool $force
2983
 * @return string
2984
 *     Identifiant de la session
2985
 **/
2986
function spip_session($force = false) {
2987
	static $session;
2988
	if ($force or !isset($session)) {
2989
		$s = pipeline('definir_session',
2990
			$GLOBALS['visiteur_session']
2991
				? serialize($GLOBALS['visiteur_session'])
2992
				. '_' . @$_COOKIE['spip_session']
2993
				: ''
2994
		);
2995
		$session = $s ? substr(md5($s), 0, 8) : '';
2996
	}
2997
2998
	#spip_log('session: '.$session);
2999
	return $session;
3000
}
3001
3002
3003
/**
3004
 * Retourne un lien vers une aide
3005
 *
3006
 * Aide, aussi depuis l'espace privé à présent.
3007
 * Surchargeable mais pas d'erreur fatale si indisponible.
3008
 *
3009
 * @param string $aide
3010
 *    Cle d'identification de l'aide desiree
3011
 * @param bool $distante
3012
 *    Generer une url locale (par defaut)
3013
 *    ou une url distante [directement sur spip.net]
3014
 * @return
3015
 *    Lien sur une icone d'aide
3016
 **/
3017
function aider($aide = '', $distante = false) {
3018
	$aider = charger_fonction('aide', 'inc', true);
3019
3020
	return $aider ? $aider($aide, '', array(), $distante) : '';
3021
}
3022
3023
/**
3024
 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3025
 *
3026
 * Si l’utiliseur est un webmestre.
3027
 */
3028
function exec_info_dist() {
3029
3030
	include_spip('inc/autoriser');
3031
	if (autoriser('webmestre')) {
3032
		phpinfo();
3033
	} else {
3034
		include_spip('inc/filtres');
3035
		sinon_interdire_acces();
3036
	}
3037
}
3038
3039
/**
3040
 * Génère une erreur de squelette
3041
 *
3042
 * Génère une erreur de squelette qui sera bien visible par un
3043
 * administrateur authentifié lors d'une visite de la page en erreur
3044
 *
3045
 * @param bool|string|array $message
3046
 *     - Message d'erreur (string|array)
3047
 *     - false pour retourner le texte des messages d'erreurs
3048
 *     - vide pour afficher les messages d'erreurs
3049
 * @param string|array|object $lieu
3050
 *     Lieu d'origine de l'erreur
3051
 * @return null|string
3052
 *     - Rien dans la plupart des cas
3053
 *     - string si $message à false.
3054
 **/
3055
function erreur_squelette($message = '', $lieu = '') {
3056
	$debusquer = charger_fonction('debusquer', 'public');
3057
	if (is_array($lieu)) {
3058
		include_spip('public/compiler');
3059
		$lieu = reconstruire_contexte_compil($lieu);
3060
	}
3061
3062
	return $debusquer($message, $lieu);
3063
}
3064
3065
/**
3066
 * Calcule un squelette avec un contexte et retourne son contenu
3067
 *
3068
 * La fonction de base de SPIP : un squelette + un contexte => une page.
3069
 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3070
 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3071
 * $options permet de selectionner les options suivantes :
3072
 *
3073
 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3074
 * - raw  => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3075
 *          pour chaque $fond fourni.
3076
 *
3077
 * @api
3078
 * @param string /array $fond
3079
 *     - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3080
 *     - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3081
 * @param array $contexte
3082
 *     - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3083
 *     - La langue est transmise automatiquement (sauf option étoile).
3084
 * @param array $options
3085
 *     Options complémentaires :
3086
 *
3087
 *     - trim   : applique un trim sur le résultat (true par défaut)
3088
 *     - raw    : retourne un tableau d'information sur le squelette (false par défaut)
3089
 *     - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3090
 *                équivalent de INCLURE*
3091
 *     - ajax   : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3092
 * @param string $connect
3093
 *     Non du connecteur de bdd a utiliser
3094
 * @return string|array
3095
 *     - Contenu du squelette calculé
3096
 *     - ou tableau d'information sur le squelette.
3097
 */
3098
function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3099
	if (!function_exists('evaluer_fond')) {
3100
		include_spip('public/assembler');
3101
	}
3102
	// assurer la compat avec l'ancienne syntaxe
3103
	// (trim etait le 3eme argument, par defaut a true)
3104
	if (!is_array($options)) {
3105
		$options = array('trim' => $options);
3106
	}
3107
	if (!isset($options['trim'])) {
3108
		$options['trim'] = true;
3109
	}
3110
3111
	if (isset($contexte['connect'])) {
3112
		$connect = $contexte['connect'];
3113
		unset($contexte['connect']);
3114
	}
3115
3116
	$texte = "";
3117
	$pages = array();
3118
	$lang_select = '';
3119
	if (!isset($options['etoile']) or !$options['etoile']) {
3120
		// Si on a inclus sans fixer le critere de lang, on prend la langue courante
3121
		if (!isset($contexte['lang'])) {
3122
			$contexte['lang'] = $GLOBALS['spip_lang'];
3123
		}
3124
3125
		if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3126
			$lang_select = lang_select($contexte['lang']);
3127
		}
3128
	}
3129
3130
	if (!isset($GLOBALS['_INC_PUBLIC'])) {
3131
		$GLOBALS['_INC_PUBLIC'] = 0;
3132
	}
3133
3134
	$GLOBALS['_INC_PUBLIC']++;
3135
3136
3137
	foreach (is_array($fond) ? $fond : array($fond) as $f) {
3138
		$page = evaluer_fond($f, $contexte, $connect);
3139
		if ($page === '') {
3140
			$c = isset($options['compil']) ? $options['compil'] : '';
3141
			$a = array('fichier' => $fond);
3142
			$erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3143
			erreur_squelette($erreur, $c);
3144
			// eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3145
			$page = array('texte' => '', 'erreur' => $erreur);
3146
		}
3147
3148
		$page = pipeline('recuperer_fond', array(
3149
			'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3150
			'data' => $page
3151
		));
3152
		if (isset($options['ajax']) and $options['ajax']) {
3153
			if (!function_exists('encoder_contexte_ajax')) {
3154
				include_spip('inc/filtres');
3155
			}
3156
			$page['texte'] = encoder_contexte_ajax(
3157
				array_merge(
3158
					$contexte,
3159
					array('fond' => $f),
3160
					($connect ? array('connect' => $connect) : array())
3161
				),
3162
				'',
3163
				$page['texte'],
3164
				$options['ajax']
3165
			);
3166
		}
3167
3168
		if (isset($options['raw']) and $options['raw']) {
3169
			$pages[] = $page;
3170
		} else {
3171
			$texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
3172
		}
3173
	}
3174
3175
	$GLOBALS['_INC_PUBLIC']--;
3176
3177
	if ($lang_select) {
3178
		lang_select();
3179
	}
3180
	if (isset($options['raw']) and $options['raw']) {
3181
		return is_array($fond) ? $pages : reset($pages);
3182
	} else {
3183
		return $options['trim'] ? ltrim($texte) : $texte;
3184
	}
3185
}
3186
3187
/**
3188
 * Trouve un squelette dans le repertoire modeles/
3189
 *
3190
 * @param  $nom
3191
 * @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...
3192
 */
3193
function trouve_modele($nom) {
3194
	return trouver_fond($nom, 'modeles/');
3195
}
3196
3197
/**
3198
 * Trouver un squelette dans le chemin
3199
 * on peut specifier un sous-dossier dans $dir
3200
 * si $pathinfo est a true, retourne un tableau avec
3201
 * les composantes du fichier trouve
3202
 * + le chemin complet sans son extension dans fond
3203
 *
3204
 * @param string $nom
3205
 * @param string $dir
3206
 * @param bool $pathinfo
3207
 * @return array|string
3208
 */
3209
function trouver_fond($nom, $dir = '', $pathinfo = false) {
3210
	$f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
3211
	if (!$pathinfo) {
3212
		return $f;
3213
	}
3214
	// renvoyer un tableau detaille si $pathinfo==true
3215
	$p = pathinfo($f);
3216
	if (!isset($p['extension']) or !$p['extension']) {
3217
		$p['extension'] = _EXTENSION_SQUELETTES;
3218
	}
3219
	if (!isset($p['extension']) or !$p['filename']) {
3220
		$p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3221
	}
3222
	$p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
3223
3224
	return $p;
3225
}
3226
3227
/**
3228
 * Teste, pour un nom de page de l'espace privé, s'il est possible
3229
 * de générer son contenu.
3230
 *
3231
 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3232
 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3233
 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3234
 *
3235
 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3236
 *          ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3237
 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3238
 *
3239
 * @param string $nom
3240
 *     Nom de la page
3241
 * @return string
3242
 *     Nom de l'exec, sinon chaîne vide.
3243
 **/
3244
function tester_url_ecrire($nom) {
3245
	static $exec = array();
3246
	if (isset($exec[$nom])) {
3247
		return $exec[$nom];
3248
	}
3249
	// tester si c'est une page en squelette
3250
	if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3251
		return $exec[$nom] = 'fond';
3252
	} // compat skels orthogonaux version precedente
3253
	elseif (trouver_fond($nom, 'prive/exec/')) {
3254
		return $exec[$nom] = 'fond_monobloc';
3255
	} // echafaudage d'un fond !
3256
	elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3257
		return $exec[$nom] = 'fond';
3258
	}
3259
	// attention, il ne faut pas inclure l'exec ici
3260
	// car sinon #URL_ECRIRE provoque des inclusions
3261
	// et des define intrusifs potentiels
3262
	return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
3263
}
3264
3265
3266
/**
3267
 * Tente de charger dynamiquement une extension PHP
3268
 *
3269
 * @example
3270
 *     ```
3271
 *     $ok = charger_php_extension('sqlite');
3272
 *     ```
3273
 * @uses inc_charger_php_extension_dist() Si la librairie n'est pas déjà charchée
3274
 *
3275
 * @param string $module Nom du module à charger
3276
 * @return bool true si le module est chargé
3277
 **/
3278
function charger_php_extension($module) {
3279
	if (extension_loaded($module)) {
3280
		return true;
3281
	} else {
3282
		$charger_php_extension = charger_fonction('charger_php_extension', 'inc');
3283
3284
		return $charger_php_extension($module);
3285
	}
3286
}
3287
3288
3289
/**
3290
 * Indique si le code HTML5 est permis sur le site public
3291
 *
3292
 * @return bool
3293
 *     true si et seulement si la configuration autorise le code HTML5 sur le site public
3294
 **/
3295
function html5_permis() {
3296
	return (isset($GLOBALS['meta']['version_html_max'])
3297
		and ('html5' == $GLOBALS['meta']['version_html_max']));
3298
}
3299
3300
/*
3301
 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3302
 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3303
 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3304
 */
3305
3306
/**
3307
 * lire_meta : fonction dépréciée
3308
 *
3309
 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3310
 * @see lire_config()
3311
 * @param string $nom Clé de meta à lire
3312
 * @return mixed Valeur de la meta.
3313
 **/
3314
function lire_meta($nom) {
3315
	return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
3316
}
3317
3318
3319
/**
3320
 * ecrire_metas : fonction dépréciée
3321
 *
3322
 * @deprecated
3323
 **/
3324
function ecrire_metas() { }
3325
3326
/**
3327
 * Retourne une ligne d'un résultat de requête mysql (déprécié)
3328
 *
3329
 * @see sql_fetch()
3330
 * @deprecated Utiliser sql_fetch()
3331
 * @param Ressource $r Ressource mysql
3332
 * @param int|null $t Type de retour
3333
 * @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...
3334
 **/
3335
function spip_fetch_array($r, $t = null) {
3336
	if (!isset($t)) {
3337
		if ($r) {
3338
			return sql_fetch($r);
3339
		}
3340
	} else {
3341
		if ($t == 'SPIP_NUM') {
3342
			$t = MYSQLI_NUM;
3343
		}
3344
		if ($t == 'SPIP_BOTH') {
3345
			$t = MYSQLI_BOTH;
3346
		}
3347
		if ($t == 'SPIP_ASSOC') {
3348
			$t = MYSQLI_ASSOC;
3349
		}
3350
		spip_log("appel deprecie de spip_fetch_array(..., $t)", 'vieilles_defs');
3351
		if ($r) {
3352
			return mysqli_fetch_array($r, $t);
3353
		}
3354
	}
3355
}
3356
3357
/**
3358
 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3359
 * au prochain passage dans l'espace prive
3360
 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3361
 * les alertes affichees une fois sont effacees
3362
 *
3363
 * @param string $nom
3364
 * @param string $message
3365
 * @param string $statut
3366
 */
3367
function avertir_auteurs($nom, $message, $statut = '') {
3368
	$alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3369
	if (!$alertes
3370
		or !is_array($alertes = unserialize($alertes))
3371
	) {
3372
		$alertes = array();
3373
	}
3374
3375
	if (!isset($alertes[$statut])) {
3376
		$alertes[$statut] = array();
3377
	}
3378
	$alertes[$statut][$nom] = $message;
3379
	ecrire_meta("message_alertes_auteurs", serialize($alertes));
3380
}
3381