Completed
Push — master ( 27dd6e...9ef4c0 )
by cam
04:23
created

utils.php ➔ find_in_theme()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
427
 *     - array $c complété si un $c est transmis,
428
 *     - false sinon
429
 **/
430
function set_request($var, $val = null, $c = false) {
431
	if (is_array($c)) {
432
		unset($c[$var]);
433
		if ($val !== null) {
434
			$c[$var] = $val;
435
		}
436
437
		return $c;
438
	}
439
440
	unset($_GET[$var]);
441
	unset($_POST[$var]);
442
	if ($val !== null) {
443
		$_GET[$var] = $val;
444
	}
445
446
	return false; # n'affecte pas $c
447
}
448
449
450
/**
451
 * Tester si une URL est absolue
452
 * 
453
 * On est sur le web, on exclut certains protocoles, 
454
 * notamment 'file://', 'php://' et d'autres…
455
456
 * @param string $url
457
 * @return bool
458
 */
459
function tester_url_absolue($url) {
460
	$url = trim($url);
461
	if (preg_match(";^([a-z]{3,7}:)?//;Uims", $url, $m)) {
462
		if (
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return !(isset($m[1]) an...g', 'expect', 'zip')));.
Loading history...
463
			isset($m[1])
464
			and $p = strtolower(rtrim($m[1], ':'))
465
			and in_array($p, array('file', 'php', 'zlib', 'glob', 'phar', 'ssh2', 'rar', 'ogg', 'expect', 'zip'))
466
		  ) {
467
			return false;
468
		}
469
		return true;
470
	}
471
	return false;
472
}
473
474
/**
475
 * Prend une URL et lui ajoute/retire un paramètre
476
 *
477
 * @filtre
478
 * @link http://www.spip.net/4255
479
 * @example
480
 *     ```
481
 *     [(#SELF|parametre_url{suite,18})] (ajout)
482
 *     [(#SELF|parametre_url{suite,''})] (supprime)
483
 *     [(#SELF|parametre_url{suite[],1})] (tableaux valeurs multiples)
484
 *     ```
485
 *
486
 * @param string $url URL
487
 * @param string $c Nom du paramètre
488
 * @param string|array|null $v Valeur du paramètre
489
 * @param string $sep Séparateur entre les paramètres
490
 * @return string URL
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string|string[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
491
 */
492
function parametre_url($url, $c, $v = null, $sep = '&amp;') {
493
	// requete erronnee : plusieurs variable dans $c et aucun $v
494
	if (strpos($c, "|") !== false and is_null($v)) {
495
		return null;
496
	}
497
498
	// lever l'#ancre
499
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
500
		$url = $r[1];
501
		$ancre = $r[2];
502
	} else {
503
		$ancre = '';
504
	}
505
506
	// eclater
507
	$url = preg_split(',[?]|&amp;|&,', $url);
508
509
	// recuperer la base
510
	$a = array_shift($url);
511
	if (!$a) {
512
		$a = './';
513
	}
514
515
	$regexp = ',^(' . str_replace('[]', '\[\]', $c) . '[[]?[]]?)(=.*)?$,';
516
	$ajouts = array_flip(explode('|', $c));
517
	$u = is_array($v) ? $v : rawurlencode($v);
518
	$testv = (is_array($v) ? count($v) : strlen($v));
519
	$v_read = null;
520
	// lire les variables et agir
521
	foreach ($url as $n => $val) {
522
		if (preg_match($regexp, urldecode($val), $r)) {
523
			$r = array_pad($r, 3, null);
524
			if ($v === null) {
525
				// c'est un tableau, on memorise les valeurs
526
				if (substr($r[1], -2) == "[]") {
527
					if (!$v_read) {
528
						$v_read = array();
529
					}
530
					$v_read[] = $r[2] ? substr($r[2], 1) : '';
531
				} // c'est un scalaire, on retourne direct
532
				else {
533
					return $r[2] ? substr($r[2], 1) : '';
534
				}
535
			} // suppression
536
			elseif (!$testv) {
537
				unset($url[$n]);
538
			}
539
			// Ajout. Pour une variable, remplacer au meme endroit,
540
			// pour un tableau ce sera fait dans la prochaine boucle
541
			elseif (substr($r[1], -2) != '[]') {
542
				$url[$n] = $r[1] . '=' . $u;
543
				unset($ajouts[$r[1]]);
544
			}
545
			// Pour les tableaux on laisse tomber les valeurs de
546
			// départ, on remplira à l'étape suivante
547
			else {
548
				unset($url[$n]);
549
			}
550
		}
551
	}
552
553
	// traiter les parametres pas encore trouves
554
	if ($v === null
555
		and $args = func_get_args()
556
		and count($args) == 2
557
	) {
558
		return $v_read; // rien trouve ou un tableau
559
	} elseif ($testv) {
560
		foreach ($ajouts as $k => $n) {
561
			if (!is_array($v)) {
562
				$url[] = $k . '=' . $u;
563
			} else {
564
				$id = (substr($k, -2) == '[]') ? $k : ($k . "[]");
565
				foreach ($v as $w) {
566
					$url[] = $id . '=' . (is_array($w) ? 'Array' : $w);
567
				}
568
			}
569
		}
570
	}
571
572
	// eliminer les vides
573
	$url = array_filter($url);
574
575
	// recomposer l'adresse
576
	if ($url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $url of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
577
		$a .= '?' . join($sep, $url);
578
	}
579
580
	return $a . $ancre;
581
}
582
583
/**
584
 * Ajoute (ou retire) une ancre sur une URL
585
 *
586
 * L’ancre est nettoyée : on translitère, vire les non alphanum du début,
587
 * et on remplace ceux à l'interieur ou au bout par `-`
588
 *
589
 * @example
590
 *     - `$url = ancre_url($url, 'navigation'); // => mettra l’ancre #navigation
591
 *     - `$url = ancre_url($url, ''); // => enlèvera une éventuelle ancre
592
 * @uses translitteration()
593
 * @param string $url
594
 * @param string $ancre
595
 * @return string
596
 */
597
function ancre_url($url, $ancre) {
598
	// lever l'#ancre
599
	if (preg_match(',^([^#]*)(#.*)$,', $url, $r)) {
600
		$url = $r[1];
601
	}
602
	if (preg_match('/[^-_a-zA-Z0-9]+/S', $ancre)) {
603
		if (!function_exists('translitteration')) {
604
			include_spip('inc/charsets');
605
		}
606
		$ancre = preg_replace(
607
			array('/^[^-_a-zA-Z0-9]+/', '/[^-_a-zA-Z0-9]/'),
608
			array('', '-'),
609
			translitteration($ancre)
610
		);
611
	}
612
	return $url . (strlen($ancre) ? '#' . $ancre : '');
613
}
614
615
/**
616
 * Pour le nom du cache, les `types_urls` et `self`
617
 *
618
 * @param string|null $reset
619
 * @return string
620
 */
621
function nettoyer_uri($reset = null) {
622
	static $done = false;
623
	static $propre = '';
624
	if (!is_null($reset)) {
625
		return $propre = $reset;
626
	}
627
	if ($done) {
628
		return $propre;
629
	}
630
	$done = true;
631
	return $propre = nettoyer_uri_var($GLOBALS['REQUEST_URI']);
632
}
633
634
/**
635
 * Nettoie une request_uri des paramètres var_xxx
636
 * 
637
 * Attention, la regexp doit suivre _CONTEXTE_IGNORE_VARIABLES défini au début de public/assembler.php
638
 * 
639
 * @param $request_uri
640
 * @return string
641
 */
642
function nettoyer_uri_var($request_uri) {
643
	$uri1 = $request_uri;
644
	do {
645
		$uri = $uri1;
646
		$uri1 = preg_replace(',([?&])(var_[^=&]*|PHPSESSID|fbclid|utm_[^=&]*)=[^&]*(&|$),i',
647
			'\1', $uri);
648
	} while ($uri <> $uri1);
649
	return preg_replace(',[?&]$,', '', $uri1);
650
}
651
652
653
/**
654
 * Donner l'URL de base d'un lien vers "soi-meme", modulo les trucs inutiles
655
 *
656
 * @param string $amp
657
 *    Style des esperluettes
658
 * @param bool $root
659
 * @return string
660
 *    URL vers soi-même
661
 **/
662
function self($amp = '&amp;', $root = false) {
663
	$url = nettoyer_uri();
664
	if (!$root
665
		and (
666
			// si pas de profondeur on peut tronquer
667
			$GLOBALS['profondeur_url'] < (_DIR_RESTREINT ? 1 : 2)
668
			// sinon c'est OK si _SET_HTML_BASE a ete force a false
669
			or (defined('_SET_HTML_BASE') and !_SET_HTML_BASE))
670
	) {
671
		$url = preg_replace(',^[^?]*/,', '', $url);
672
	}
673
	// ajouter le cas echeant les variables _POST['id_...']
674
	foreach ($_POST as $v => $c) {
675
		if (substr($v, 0, 3) == 'id_') {
676
			$url = parametre_url($url, $v, $c, '&');
677
		}
678
	}
679
680
	// supprimer les variables sans interet
681
	if (test_espace_prive()) {
682
		$url = preg_replace(',([?&])('
683
			. 'lang|show_docs|'
684
			. 'changer_lang|var_lang|action)=[^&]*,i', '\1', $url);
685
		$url = preg_replace(',([?&])[&]+,', '\1', $url);
686
		$url = preg_replace(',[&]$,', '\1', $url);
687
	}
688
689
	// eviter les hacks
690
	include_spip('inc/filtres_mini');
691
	$url = spip_htmlspecialchars($url);
0 ignored issues
show
Bug introduced by
It seems like $url can also be of type array<integer,string> or null; however, spip_htmlspecialchars() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
692
	
693
	$url = str_replace(array("'", '"', '<', '[', ']', ':'), array('%27', '%22', '%3C', '%5B', '%5D', '%3A'), $url);
694
695
	// &amp; ?
696
	if ($amp != '&amp;') {
697
		$url = str_replace('&amp;', $amp, $url);
698
	}
699
700
	// Si ca demarre par ? ou vide, donner './'
701
	$url = preg_replace(',^([?].*)?$,', './\1', $url);
702
703
	return $url;
704
}
705
706
707
/**
708
 * Indique si on est dans l'espace prive
709
 *
710
 * @return bool
711
 *     true si c'est le cas, false sinon.
712
 */
713
function test_espace_prive() {
714
	return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
715
}
716
717
/**
718
 * Vérifie la présence d'un plugin actif, identifié par son préfixe
719
 *
720
 * @param string $plugin
721
 * @return bool
722
 */
723
function test_plugin_actif($plugin) {
724
	return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ? true : false;
725
}
726
727
/**
728
 * Traduction des textes de SPIP
729
 *
730
 * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
731
 *
732
 * @api
733
 * @uses inc_traduire_dist()
734
 * @uses _L()
735
 * @example
736
 *     ```
737
 *     _T('bouton_enregistrer')
738
 *     _T('medias:image_tourner_droite')
739
 *     _T('medias:erreurs', array('nb'=>3))
740
 *     _T("email_sujet", array('spip_lang'=>$lang_usager))
741
 *     ```
742
 *
743
 * @param string $texte
744
 *     Clé de traduction
745
 * @param array $args
746
 *     Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
747
 * @param array $options
748
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
749
 *     - bool force : forcer un retour meme si la chaine n'a pas de traduction
750
 *     - bool sanitize : nettoyer le html suspect dans les arguments
751
 * @return string
752
 *     Texte
753
 */
754
function _T($texte, $args = array(), $options = array()) {
755
	static $traduire = false;
756
	$o = array('class' => '', 'force' => true, 'sanitize' => true);
757
	if ($options) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
758
		// support de l'ancien argument $class
759
		if (is_string($options)) {
760
			$options = array('class' => $options);
761
		}
762
		$o = array_merge($o, $options);
763
	}
764
765
	if (!$traduire) {
766
		$traduire = charger_fonction('traduire', 'inc');
767
		include_spip('inc/lang');
768
	}
769
770
	// On peut passer explicitement la langue dans le tableau
771
	// On utilise le même nom de variable que la globale
772 View Code Duplication
	if (isset($args['spip_lang'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
773
		$lang = $args['spip_lang'];
774
		// On l'enleve pour ne pas le passer au remplacement
775
		unset($args['spip_lang']);
776
	} // Sinon on prend la langue du contexte
777
	else {
778
		$lang = $GLOBALS['spip_lang'];
779
	}
780
	$text = $traduire($texte, $lang);
781
782
	if (!strlen($text)) {
783
		if (!$o['force']) {
784
			return '';
785
		}
786
787
		$text = $texte;
788
789
		// pour les chaines non traduites, assurer un service minimum
790
		if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
791
			$text = str_replace('_', ' ',
792
				(($n = strpos($text, ':')) === false ? $texte :
793
					substr($texte, $n + 1)));
794
		}
795
		$o['class'] = null;
796
797
	}
798
799
	return _L($text, $args, $o);
800
801
}
802
803
804
/**
805
 * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
806
 *
807
 * Cette fonction est également appelée dans le code source de SPIP quand une
808
 * chaîne n'est pas encore dans les fichiers de langue.
809
 *
810
 * @see _T()
811
 * @example
812
 *     ```
813
 *     _L('Texte avec @nb@ ...', array('nb'=>3)
814
 *     ```
815
 *
816
 * @param string $text
817
 *     Texte
818
 * @param array $args
819
 *     Couples (variable => valeur) à transformer dans le texte
820
 * @param array $options
821
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
822
 *     - bool sanitize : nettoyer le html suspect dans les arguments
823
 * @return string
824
 *     Texte
825
 */
826
function _L($text, $args = array(), $options = array()) {
827
	$f = $text;
828
	$defaut_options = array(
829
		'class' => null,
830
		'sanitize' => true,
831
	);
832
	// support de l'ancien argument $class
833
	if ($options and is_string($options)) {
834
		$options = array('class' => $options);
835
	}
836
	if (is_array($options)) {
837
		$options += $defaut_options;
838
	} else {
839
		$options = $defaut_options;
840
	}
841
842
	if (is_array($args) and count($args)) {
843
		if (!function_exists('interdire_scripts')) {
844
			include_spip('inc/texte');
845
		}
846
		if (!function_exists('echapper_html_suspect')) {
847
			include_spip('inc/texte_mini');
848
		}
849
		foreach ($args as $name => $value) {
850
			if (strpos($text, "@$name@") !== false) {
851
				if ($options['sanitize']) {
852
					$value = echapper_html_suspect($value);
853
					$value = interdire_scripts($value, -1);
854
				}
855
				if (!empty($options['class'])) {
856
					$value = "<span class='".$options['class']."'>$value</span>";
857
				}
858
				$text = str_replace("@$name@", $value, $text);
859
				unset($args[$name]);
860
			}
861
		}
862
		// Si des variables n'ont pas ete inserees, le signaler
863
		// (chaines de langues pas a jour)
864
		if ($args) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $args of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
865
			spip_log("$f:  variables inutilisees " . join(', ', array_keys($args)), _LOG_DEBUG);
866
		}
867
	}
868
869
	if (($GLOBALS['test_i18n'] or (_request('var_mode') == 'traduction')) and is_null($options['class'])) {
870
		return "<span class=debug-traduction-erreur>$text</span>";
871
	} else {
872
		return $text;
873
	}
874
}
875
876
877
/**
878
 * Retourne un joli chemin de répertoire
879
 *
880
 * Pour afficher `ecrire/action/` au lieu de `action/` dans les messages
881
 * ou `tmp/` au lieu de `../tmp/`
882
 *
883
 * @param stirng $rep Chemin d’un répertoire
884
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|string[]?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
885
 */
886
function joli_repertoire($rep) {
887
	$a = substr($rep, 0, 1);
888
	if ($a <> '.' and $a <> '/') {
889
		$rep = (_DIR_RESTREINT ? '' : _DIR_RESTREINT_ABS) . $rep;
890
	}
891
	$rep = preg_replace(',(^\.\.\/),', '', $rep);
892
893
	return $rep;
894
}
895
896
897
/**
898
 * Débute ou arrête un chronomètre et retourne sa valeur
899
 *
900
 * On exécute 2 fois la fonction, la première fois pour démarrer le chrono,
901
 * la seconde fois pour l’arrêter et récupérer la valeur
902
 *
903
 * @example
904
 *     ```
905
 *     spip_timer('papoter');
906
 *     // actions
907
 *     $duree = spip_timer('papoter');
908
 *     ```
909
 *
910
 * @param string $t
911
 *     Nom du chronomètre
912
 * @param bool $raw
913
 *     - false : retour en texte humainement lisible
914
 *     - true : retour en millisecondes
915
 * @return float|int|string|void
916
 */
917
function spip_timer($t = 'rien', $raw = false) {
918
	static $time;
919
	$a = time();
920
	$b = microtime();
921
	// microtime peut contenir les microsecondes et le temps
922
	$b = explode(' ', $b);
923
	if (count($b) == 2) {
924
		$a = end($b);
925
	} // plus precis !
926
	$b = reset($b);
927
	if (!isset($time[$t])) {
928
		$time[$t] = $a + $b;
929
	} else {
930
		$p = ($a + $b - $time[$t]) * 1000;
931
		unset($time[$t]);
932
#			echo "'$p'";exit;
933
		if ($raw) {
934
			return $p;
935
		}
936
		if ($p < 1000) {
937
			$s = '';
938
		} else {
939
			$s = sprintf("%d ", $x = floor($p / 1000));
940
			$p -= ($x * 1000);
941
		}
942
943
		return $s . sprintf($s ? "%07.3f ms" : "%.3f ms", $p);
944
	}
945
}
946
947
948
// Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
949
// sinon renvoie True et le date sauf si ca n'est pas souhaite
950
// http://code.spip.net/@spip_touch
951
function spip_touch($fichier, $duree = 0, $touch = true) {
952
	if ($duree) {
953
		clearstatcache();
954
		if ((@$f = filemtime($fichier)) and ($f >= time() - $duree)) {
955
			return false;
956
		}
957
	}
958
	if ($touch !== false) {
959
		if (!@touch($fichier)) {
960
			spip_unlink($fichier);
961
			@touch($fichier);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
962
		};
963
		@chmod($fichier, _SPIP_CHMOD & ~0111);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
964
	}
965
966
	return true;
967
}
968
969
970
/**
971
 * Action qui déclenche une tache de fond
972
 *
973
 * @see  queue_affichage_cron()
974
 * @see  action_super_cron_dist()
975
 * @uses cron()
976
 **/
977
function action_cron() {
978
	include_spip('inc/headers');
979
	http_status(204); // No Content
980
	header("Connection: close");
981
	define('_DIRECT_CRON_FORCE', true);
982
	cron();
983
}
984
985
/**
986
 * Exécution des tâches de fond
987
 *
988
 * @uses inc_genie_dist()
989
 *
990
 * @param array $taches
991
 *     Tâches forcées
992
 * @param array $taches_old
993
 *     Tâches forcées, pour compat avec ancienne syntaxe
994
 * @return bool
995
 *     True si la tache a pu être effectuée
996
 */
997
function cron($taches = array(), $taches_old = array()) {
998
	// si pas en mode cron force, laisser tomber.
999
	if (!defined('_DIRECT_CRON_FORCE')) {
1000
		return false;
1001
	}
1002
	if (!is_array($taches)) {
1003
		$taches = $taches_old;
1004
	} // compat anciens appels
1005
	// si taches a inserer en base et base inaccessible, laisser tomber
1006
	// sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
1007
	// queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
1008
	// et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
1009
	if ($taches and count($taches) and !spip_connect()) {
1010
		return false;
1011
	}
1012
	spip_log("cron !", 'jq' . _LOG_DEBUG);
1013
	if ($genie = charger_fonction('genie', 'inc', true)) {
1014
		return $genie($taches);
1015
	}
1016
1017
	return false;
1018
}
1019
1020
/**
1021
 * Ajout d'une tache dans la file d'attente
1022
 *
1023
 * @param string $function
1024
 *     Le nom de la fonction PHP qui doit être appelée.
1025
 * @param string $description
1026
 *     Une description humainement compréhensible de ce que fait la tâche
1027
 *     (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
1028
 * @param array $arguments
1029
 *     Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
1030
 * @param string $file
1031
 *     Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
1032
 *     exemple : `'inc/mail'` : il ne faut pas indiquer .php
1033
 *     Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
1034
 * @param bool $no_duplicate
1035
 *     Facultatif, `false` par défaut
1036
 *
1037
 *     - si `true` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction et les mêmes arguments.
1038
 *     - si `function_only` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction indépendamment de ses arguments
1039
 * @param int $time
1040
 *     Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
1041
 *     Si `0` ou une date passée, la tâche sera exécutée aussitôt que possible (en général en fin hit, en asynchrone).
1042
 * @param int $priority
1043
 *     Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
1044
 *     Les tâches sont exécutées par ordre de priorité décroissante, une fois leur date d’exécution passée. La priorité est surtout utilisée quand une tâche cron indique qu’elle n’a pas fini et doit être relancée : dans ce cas SPIP réduit sa priorité pour être sûr que celle tâche ne monopolise pas la file d’attente.
1045
 * @return int
1046
 *     Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
1047
 */
1048
function job_queue_add(
1049
	$function,
1050
	$description,
1051
	$arguments = array(),
1052
	$file = '',
1053
	$no_duplicate = false,
1054
	$time = 0,
1055
	$priority = 0
1056
) {
1057
	include_spip('inc/queue');
1058
1059
	return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
1060
}
1061
1062
/**
1063
 * Supprimer une tache de la file d'attente
1064
 *
1065
 * @param int $id_job
1066
 *  id of jonb to delete
1067
 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1068
 */
1069
function job_queue_remove($id_job) {
1070
	include_spip('inc/queue');
1071
1072
	return queue_remove_job($id_job);
1073
}
1074
1075
/**
1076
 * Associer une tache a un/des objets de SPIP
1077
 *
1078
 * @param int $id_job
1079
 *     id of job to link
1080
 * @param array $objets
1081
 *     can be a simple array('objet'=>'article', 'id_objet'=>23)
1082
 *     or an array of simple array to link multiples objet in one time
1083
 */
1084
function job_queue_link($id_job, $objets) {
1085
	include_spip('inc/queue');
1086
1087
	return queue_link_job($id_job, $objets);
1088
}
1089
1090
1091
/**
1092
 * Renvoyer le temps de repos restant jusqu'au prochain job
1093
 *
1094
 * @staticvar int $queue_next_job_time
1095
 * @see queue_set_next_job_time()
1096
 * @param int|bool $force
0 ignored issues
show
Documentation introduced by
Should the type for parameter $force not be integer|boolean|null?

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

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

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

Loading history...
1097
 *    Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
1098
 *
1099
 *    - si `true`, force la relecture depuis le fichier
1100
 *    - si int, affecte la static directement avec la valeur
1101
 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be null|integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
1102
 *
1103
 *  - `0` si un job est à traiter
1104
 *  - `null` si la queue n'est pas encore initialisée
1105
 */
1106
function queue_sleep_time_to_next_job($force = null) {
1107
	static $queue_next_job_time = -1;
1108
	if ($force === true) {
1109
		$queue_next_job_time = -1;
1110
	} elseif ($force) {
1111
		$queue_next_job_time = $force;
1112
	}
1113
1114
	if ($queue_next_job_time == -1) {
1115
		if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
1116
			define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP . "job_queue_next.txt");
1117
		}
1118
		// utiliser un cache memoire si dispo
1119
		if (function_exists("cache_get") and defined('_MEMOIZE_MEMORY') and _MEMOIZE_MEMORY) {
1120
			$queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
1121
		} else {
1122
			$queue_next_job_time = null;
1123
			if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu)) {
1124
				$queue_next_job_time = intval($contenu);
1125
			}
1126
		}
1127
	}
1128
1129
	if (is_null($queue_next_job_time)) {
1130
		return null;
1131
	}
1132
	if (!$_SERVER['REQUEST_TIME']) {
1133
		$_SERVER['REQUEST_TIME'] = time();
1134
	}
1135
1136
	return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
1137
}
1138
1139
1140
/**
1141
 * Transformation XML des `&` en `&amp;`
1142
 * 
1143
 * @pipeline post_typo
1144
 * @param string $u
1145
 * @return string
1146
 */
1147
function quote_amp($u) {
1148
	return preg_replace(
1149
		"/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i",
1150
		"&amp;", $u);
1151
}
1152
1153
1154
/**
1155
 * Produit une balise `<script>` valide
1156
 *
1157
 * @example
1158
 *     ```
1159
 *     echo http_script('alert("ok");');
1160
 *     echo http_script('','js/jquery.js');
1161
 *     ```
1162
 *
1163
 * @param string $script
1164
 *     Code source du script
1165
 * @param string $src
1166
 *     Permet de faire appel à un fichier javascript distant
1167
 * @param string $noscript
1168
 *     Contenu de la balise  `<noscript>`
1169
 * @return string
1170
 *     Balise HTML `<script>` et son contenu
1171
 **/
1172
function http_script($script, $src = '', $noscript = '') {
1173
	static $done = array();
1174
1175
	if ($src && !isset($done[$src])) {
1176
		$done[$src] = true;
1177
		$src = find_in_path($src, _JAVASCRIPT);
1178
		$src = " src='$src'";
1179
	} else {
1180
		$src = '';
1181
	}
1182
	if ($script) {
1183
		$script = ("/*<![CDATA[*/\n" .
1184
			preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
1185
			"/*]]>*/");
1186
	}
1187
	if ($noscript) {
1188
		$noscript = "<noscript>\n\t$noscript\n</noscript>\n";
1189
	}
1190
1191
	return ($src or $script or $noscript)
1192
		? "<script type='text/javascript'$src>$script</script>$noscript"
1193
		: '';
1194
}
1195
1196
1197
/**
1198
 * Sécurise du texte à écrire dans du PHP ou du Javascript.
1199
 *
1200
 * Transforme n'importe quel texte en une chaîne utilisable
1201
 * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
1202
 * simples (`'` uniquement ; pas `"`)
1203
 *
1204
 * Utile particulièrement en filtre dans un squelettes
1205
 * pour écrire un contenu dans une variable JS ou PHP.
1206
 *
1207
 * Échappe les apostrophes (') du contenu transmis.
1208
 *
1209
 * @link http://www.spip.net/4281
1210
 * @example
1211
 *     PHP dans un squelette
1212
 *     ```
1213
 *     $x = '[(#TEXTE|texte_script)]';
1214
 *     ```
1215
 *
1216
 *     JS dans un squelette (transmettre une chaîne de langue)
1217
 *     ```
1218
 *     $x = '<:afficher_calendrier|texte_script:>';
1219
 *     ```
1220
 *
1221
 * @filtre
1222
 * @param string $texte
1223
 *     Texte à échapper
1224
 * @return string
1225
 *     Texte échappé
1226
 **/
1227
function texte_script($texte) {
1228
	return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
1229
}
1230
1231
1232
/**
1233
 * Gestion des chemins (ou path) de recherche de fichiers par SPIP
1234
 *
1235
 * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
1236
 * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
1237
 * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
1238
 *
1239
 * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
1240
 * en tête du path, dans cet ordre (hormis `squelettes` & la globale
1241
 * `$dossier_squelette` si définie qui resteront devant)
1242
 *
1243
 * Retourne dans tous les cas la liste des chemins.
1244
 *
1245
 * @note
1246
 *     Cette fonction est appelée à plusieurs endroits et crée une liste
1247
 *     de chemins finale à peu près de la sorte :
1248
 *
1249
 *     - dossiers squelettes (si globale précisée)
1250
 *     - squelettes/
1251
 *     - plugins (en fonction de leurs dépendances) : ceux qui dépendent
1252
 *       d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
1253
 *     - racine du site
1254
 *     - squelettes-dist/
1255
 *     - prive/
1256
 *     - ecrire/
1257
 *
1258
 * @param string $dir_path
0 ignored issues
show
Documentation introduced by
Should the type for parameter $dir_path not be string|null?

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

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

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

Loading history...
1259
 *     - Répertoire(s) à empiler au path
1260
 *     - '' provoque un recalcul des chemins.
1261
 * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1262
 *     Liste des chemins, par ordre de priorité.
1263
 **/
1264
function _chemin($dir_path = null) {
1265
	static $path_base = null;
1266
	static $path_full = null;
1267
	if ($path_base == null) {
1268
		// Chemin standard depuis l'espace public
1269
		$path = defined('_SPIP_PATH') ? _SPIP_PATH :
1270
			_DIR_RACINE . ':' .
1271
			_DIR_RACINE . 'squelettes-dist/:' .
1272
			_DIR_RACINE . 'prive/:' .
1273
			_DIR_RESTREINT;
1274
		// Ajouter squelettes/
1275
		if (@is_dir(_DIR_RACINE . 'squelettes')) {
1276
			$path = _DIR_RACINE . 'squelettes/:' . $path;
1277
		}
1278
		foreach (explode(':', $path) as $dir) {
1279
			if (strlen($dir) and substr($dir, -1) != '/') {
1280
				$dir .= "/";
1281
			}
1282
			$path_base[] = $dir;
1283
		}
1284
		$path_full = $path_base;
1285
		// Et le(s) dossier(s) des squelettes nommes
1286 View Code Duplication
		if (strlen($GLOBALS['dossier_squelettes'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1287
			foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1288
				array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
1289
			}
1290
		}
1291
		$GLOBALS['path_sig'] = md5(serialize($path_full));
1292
	}
1293
	if ($dir_path === null) {
1294
		return $path_full;
1295
	}
1296
1297
	if (strlen($dir_path)) {
1298
		$tete = "";
1299
		if (reset($path_base) == _DIR_RACINE . 'squelettes/') {
1300
			$tete = array_shift($path_base);
1301
		}
1302
		$dirs = array_reverse(explode(':', $dir_path));
1303
		foreach ($dirs as $dir_path) {
1304
			#if ($dir_path{0}!='/')
1305
			#	$dir_path = $dir_path;
1306
			if (substr($dir_path, -1) != '/') {
1307
				$dir_path .= "/";
1308
			}
1309
			if (!in_array($dir_path, $path_base)) {
1310
				array_unshift($path_base, $dir_path);
1311
			}
1312
		}
1313
		if (strlen($tete)) {
1314
			array_unshift($path_base, $tete);
1315
		}
1316
	}
1317
	$path_full = $path_base;
1318
	// Et le(s) dossier(s) des squelettes nommes
1319 View Code Duplication
	if (strlen($GLOBALS['dossier_squelettes'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1320
		foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
1321
			array_unshift($path_full, ((isset($d[0]) and $d[0] == '/') ? '' : _DIR_RACINE) . $d . '/');
1322
		}
1323
	}
1324
1325
	$GLOBALS['path_sig'] = md5(serialize($path_full));
1326
1327
	return $path_full;
1328
}
1329
1330
/**
1331
 * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
1332
 *
1333
 * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
1334
 *
1335
 * @uses _chemin()
1336
 *
1337
 * @return array Liste de chemins
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
1338
 **/
1339
function creer_chemin() {
1340
	$path_a = _chemin();
1341
	static $c = '';
1342
1343
	// on calcule le chemin si le dossier skel a change
1344
	if ($c != $GLOBALS['dossier_squelettes']) {
1345
		// assurer le non plantage lors de la montee de version :
1346
		$c = $GLOBALS['dossier_squelettes'];
1347
		$path_a = _chemin(''); // forcer un recalcul du chemin
1348
	}
1349
1350
	return $path_a;
1351
}
1352
1353
1354
function lister_themes_prives() {
1355
	static $themes = null;
1356
	if (is_null($themes)) {
1357
		// si pas encore definie
1358
		if (!defined('_SPIP_THEME_PRIVE')) {
1359
			define('_SPIP_THEME_PRIVE', 'spip');
1360
		}
1361
		$themes = array(_SPIP_THEME_PRIVE);
1362
		// lors d'une installation neuve, prefs n'est pas definie.
1363 View Code Duplication
		if (isset($GLOBALS['visiteur_session']['prefs'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1364
			$prefs = $GLOBALS['visiteur_session']['prefs'];
1365
		} else {
1366
			$prefs = array();
1367
		}
1368
		if (is_string($prefs)) {
1369
			$prefs = unserialize($GLOBALS['visiteur_session']['prefs']);
1370
		}
1371
		if (
1372
			((isset($prefs['theme']) and $theme = $prefs['theme'])
1373
				or (isset($GLOBALS['theme_prive_defaut']) and $theme = $GLOBALS['theme_prive_defaut']))
1374
			and $theme != _SPIP_THEME_PRIVE
1375
		) {
1376
			array_unshift($themes, $theme);
1377
		} // placer le theme choisi en tete
1378
	}
1379
1380
	return $themes;
1381
}
1382
1383
function find_in_theme($file, $subdir = '', $include = false) {
1384
	static $themefiles = array();
1385
	if (isset($themefiles["$subdir$file"])) {
1386
		return $themefiles["$subdir$file"];
1387
	}
1388
	// on peut fournir une icone generique -xx.svg qui fera le job dans toutes les tailles, et qui est prioritaire
1389
	if (preg_match(',-\d+[.](png|gif|svg)$,', $file, $m)
1390
	  and $file_svg_generique = substr($file,0, -strlen($m[0])) . "-xx.svg"
1391
		and $f = find_in_theme("$file_svg_generique")) {
1392
		return $themefiles["$subdir$file"] = $f;
1393
	}
1394
1395
	$themes = lister_themes_prives();
1396
	foreach ($themes as $theme) {
1397
		if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
1398
			return $themefiles["$subdir$file"] = $f;
1399
		}
1400
	}
1401
	spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');
1402
1403
	return $themefiles["$subdir$file"] = "";
1404
}
1405
1406
1407
/**
1408
 * Cherche une image dans les dossiers d'images
1409
 *
1410
 * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
1411
 * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
1412
 * de facon temporaire le temps de la migration, et cherche de nouveau.
1413
 *
1414
 * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
1415
 * dans le répertoire défini par la constante `_NOM_IMG_PACK`
1416
 *
1417
 * @see find_in_theme()
1418
 * @see inc_icone_renommer_dist()
1419
 *
1420
 * @param string $icone
1421
 *     Nom de l'icone cherchée
1422
 * @return string
1423
 *     Chemin complet de l'icone depuis la racine si l'icone est trouée,
1424
 *     sinon chaîne vide.
1425
 **/
1426
function chemin_image($icone) {
1427
	static $icone_renommer;
1428
	// gerer le cas d'un double appel en evitant de refaire le travail inutilement
1429
	if (strpos($icone, "/") !== false and file_exists($icone)) {
1430
		return $icone;
1431
	}
1432
1433
	// si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
1434
	if (preg_match(',[.](png|gif|jpg|svg)$,', $icone) and $f = find_in_theme("images/$icone")) {
1435
		return $f;
1436
	}
1437
	// sinon passer par le module de renommage
1438
	if (is_null($icone_renommer)) {
1439
		$icone_renommer = charger_fonction('icone_renommer', 'inc', true);
1440
	}
1441
	if ($icone_renommer) {
1442
		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...
1443
		if (file_exists($icone)) {
1444
			return $icone;
1445
		}
1446
	}
1447
1448
	return find_in_path($icone, _NOM_IMG_PACK);
1449
}
1450
1451
//
1452
// chercher un fichier $file dans le SPIP_PATH
1453
// si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
1454
// si 3e arg vrai, on inclut si ce n'est fait.
1455
$GLOBALS['path_sig'] = '';
1456
$GLOBALS['path_files'] = null;
1457
1458
/**
1459
 * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
1460
 *
1461
 * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
1462
 * suivant l'ordre des chemins connus de SPIP.
1463
 *
1464
 * @api
1465
 * @see  charger_fonction()
1466
 * @uses creer_chemin() Pour la liste des chemins.
1467
 * @example
1468
 *     ```
1469
 *     $f = find_in_path('css/perso.css');
1470
 *     $f = find_in_path('perso.css', 'css');
1471
 *     ```
1472
 *
1473
 * @param string $file
1474
 *     Fichier recherché
1475
 * @param string $dirname
1476
 *     Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
1477
 * @param bool|string $include
1478
 *     - false : ne fait rien de plus
1479
 *     - true : inclut le fichier (include_once)
1480
 *     - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
1481
 * @return string|bool
1482
 *     - string : chemin du fichier trouvé
1483
 *     - false : fichier introuvable
1484
 **/
1485
function find_in_path($file, $dirname = '', $include = false) {
1486
	static $dirs = array();
1487
	static $inc = array(); # cf http://trac.rezo.net/trac/spip/changeset/14743
1488
	static $c = '';
1489
1490
	// on calcule le chemin si le dossier skel a change
1491
	if ($c != $GLOBALS['dossier_squelettes']) {
1492
		// assurer le non plantage lors de la montee de version :
1493
		$c = $GLOBALS['dossier_squelettes'];
1494
		creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
1495
	}
1496
1497
	if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
1498
		if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
1499
			return false;
1500
		}
1501
		if ($include and !isset($inc[$dirname][$file])) {
1502
			include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1503
			$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1504
		}
1505
1506
		return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
1507
	}
1508
1509
	$a = strrpos($file, '/');
1510
	if ($a !== false) {
1511
		$dirname .= substr($file, 0, ++$a);
1512
		$file = substr($file, $a);
1513
	}
1514
1515
	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...
1516 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...
1517
			$dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a);
1518
		}
1519
		if ($dirs[$a]) {
1520
			if (file_exists(_ROOT_CWD . ($a .= $file))) {
1521
				if ($include and !isset($inc[$dirname][$file])) {
1522
					include_once _ROOT_CWD . $a;
1523
					$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
1524
				}
1525 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...
1526
					// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1527
					if (is_null($GLOBALS['path_files'])) {
1528
						return $a;
1529
					}
1530
					define('_SAUVER_CHEMIN', true);
1531
				}
1532
1533
				return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
1534
			}
1535
		}
1536
	}
1537
1538
	if ($include) {
1539
		spip_log("include_spip $dirname$file non trouve");
1540
		if ($include === 'required') {
1541
			echo '<pre>',
1542
			"<strong>Erreur Fatale</strong><br />";
1543
			if (function_exists('debug_print_backtrace')) {
1544
				echo debug_print_backtrace();
1545
			}
1546
			echo '</pre>';
1547
			die("Erreur interne: ne peut inclure $dirname$file");
1548
		}
1549
	}
1550
1551 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...
1552
		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1553
		if (is_null($GLOBALS['path_files'])) {
1554
			return false;
1555
		}
1556
		define('_SAUVER_CHEMIN', true);
1557
	}
1558
1559
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1560
}
1561
1562
function clear_path_cache() {
1563
	$GLOBALS['path_files'] = array();
1564
	spip_unlink(_CACHE_CHEMIN);
1565
}
1566
1567
function load_path_cache() {
1568
	// charger le path des plugins
1569
	if (@is_readable(_CACHE_PLUGINS_PATH)) {
1570
		include_once(_CACHE_PLUGINS_PATH);
1571
	}
1572
	$GLOBALS['path_files'] = array();
1573
	// si le visiteur est admin,
1574
	// on ne recharge pas le cache pour forcer sa mise a jour
1575
	if (
1576
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1577
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1578
		// utiliser le cookie est un pis aller qui marche 'en general'
1579
		// on blinde par un second test au moment de la lecture de la session
1580
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1581
		// et en ignorant ce cache en cas de recalcul explicite
1582
	!_request('var_mode')
1583
	) {
1584
		// on essaye de lire directement sans verrou pour aller plus vite
1585
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
1586
			// mais si semble corrompu on relit avec un verrou
1587
			if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1588
				lire_fichier(_CACHE_CHEMIN, $contenu);
1589
				if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1590
					$GLOBALS['path_files'] = array();
1591
				}
1592
			}
1593
		}
1594
	}
1595
}
1596
1597
function save_path_cache() {
1598
	if (defined('_SAUVER_CHEMIN')
1599
		and _SAUVER_CHEMIN
1600
	) {
1601
		ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
1602
	}
1603
}
1604
1605
1606
/**
1607
 * Trouve tous les fichiers du path correspondants à un pattern
1608
 *
1609
 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1610
 * par un `find_in_path()`
1611
 *
1612
 * @api
1613
 * @uses creer_chemin()
1614
 * @uses preg_files()
1615
 *
1616
 * @param string $dir
1617
 * @param string $pattern
1618
 * @param bool $recurs
1619
 * @return array
1620
 */
1621
function find_all_in_path($dir, $pattern, $recurs = false) {
1622
	$liste_fichiers = array();
1623
	$maxfiles = 10000;
1624
1625
	// cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1626
	// on a pas encore inclus flock.php
1627
	if (!function_exists('preg_files')) {
1628
		include_once _ROOT_RESTREINT . 'inc/flock.php';
1629
	}
1630
1631
	// Parcourir le chemin
1632
	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...
1633
		$f = $d . $dir;
1634
		if (@is_dir($f)) {
1635
			$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...
1636
			foreach ($liste as $chemin) {
1637
				$nom = basename($chemin);
1638
				// ne prendre que les fichiers pas deja trouves
1639
				// car find_in_path prend le premier qu'il trouve,
1640
				// les autres sont donc masques
1641
				if (!isset($liste_fichiers[$nom])) {
1642
					$liste_fichiers[$nom] = $chemin;
1643
				}
1644
			}
1645
		}
1646
	}
1647
1648
	return $liste_fichiers;
1649
}
1650
1651
/**
1652
 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1653
 * et beneficient d'une exception
1654
 *
1655
 * @param string $nom
1656
 * @param bool $strict
1657
 * @return bool
1658
 */
1659
function autoriser_sans_cookie($nom, $strict = false) {
1660
	static $autsanscookie = array('install', 'base_repair');
1661
1662
	if (in_array($nom, $autsanscookie)) {
1663
		if (test_espace_prive()){
1664
			include_spip('base/connect_sql');
1665
			if (!$strict or !spip_connect()){
1666
				return true;
1667
			}
1668
		}
1669
	}
1670
	return false;
1671
}
1672
1673
/**
1674
 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1675
 *
1676
 * @api
1677
 * @param string $id
1678
 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
1679
 * @param string $entite
1680
 *   surnom de la table SQL (donne acces au nom de cle primaire)
1681
 * @param string $args
1682
 *   query_string a placer apres cle=$id&....
1683
 * @param string $ancre
1684
 *   ancre a mettre a la fin de l'URL a produire
1685
 * @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...
1686
 *   produire l'URL publique ou privee (par defaut: selon espace)
1687
 *   si string : serveur de base de donnee (nom du connect)
1688
 * @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...
1689
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
1690
 * @return string|array
1691
 *   url codee ou fonction de decodage
1692
 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1693
 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1694
 */
1695
function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1696
	if ($public === null) {
1697
		$public = !test_espace_prive();
1698
	}
1699
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1700
1701
	if (!$public) {
1702
		if (!$entite) {
1703
			return '';
1704
		}
1705
		if (!function_exists('generer_url_ecrire_objet')) {
1706
			include_spip('inc/urls');
1707
		}
1708
		$res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1709
	} else {
1710
		if ($type === null) {
1711
			$type = (isset($GLOBALS['type_urls']))
1712
				? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1713
				: ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1714
					? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1715
		}
1716
1717
		$f = charger_fonction($type, 'urls', true);
1718
		// se rabattre sur les urls page si les urls perso non dispo
1719
		if (!$f) {
1720
			$f = charger_fonction('page', 'urls', true);
1721
		}
1722
1723
		// si $entite='', on veut la fonction de passage URL ==> id
1724
		// sinon on veut effectuer le passage id ==> URL
1725
		if (!$entite) {
1726
			return $f;
1727
		}
1728
1729
		// mais d'abord il faut tester le cas des urls sur une
1730
		// base distante
1731
		if (is_string($public)
1732
			and $g = charger_fonction('connect', 'urls', true)
1733
		) {
1734
			$f = $g;
1735
		}
1736
1737
		$res = $f(intval($id), $entite, $args, $ancre, $public);
1738
1739
	}
1740
	if ($res) {
1741
		return $res;
1742
	}
1743
	// Sinon c'est un raccourci ou compat SPIP < 2
1744
	if (!function_exists($f = 'generer_url_' . $entite)) {
1745
		if (!function_exists($f .= '_dist')) {
1746
			$f = '';
1747
		}
1748
	}
1749
	if ($f) {
1750
		$url = $f($id, $args, $ancre);
1751
		if (strlen($args)) {
1752
			$url .= strstr($url, '?')
1753
				? '&amp;' . $args
1754
				: '?' . $args;
1755
		}
1756
1757
		return $url;
1758
	}
1759
	// On a ete gentil mais la ....
1760
	spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1761
1762
	return '';
1763
}
1764
1765
function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1766
	$exec = objet_info($entite, 'url_edit');
1767
	$url = generer_url_ecrire($exec, $args);
1768
	if (intval($id)) {
1769
		$url = parametre_url($url, id_table_objet($entite), $id);
1770
	} else {
1771
		$url = parametre_url($url, 'new', 'oui');
1772
	}
1773
	if ($ancre) {
1774
		$url = ancre_url($url, $ancre);
0 ignored issues
show
Bug introduced by
It seems like $url can also be of type array<integer,string> or null; however, ancre_url() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1775
	}
1776
1777
	return $url;
1778
}
1779
1780
// http://code.spip.net/@urls_connect_dist
1781
function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1782
	include_spip('base/connect_sql');
1783
	$id_type = id_table_objet($entite, $public);
1784
1785
	return _DIR_RACINE . get_spip_script('./')
1786
	. "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1787
	. (!$args ? '' : "&$args")
1788
	. (!$ancre ? '' : "#$ancre");
1789
}
1790
1791
1792
/**
1793
 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1794
 *
1795
 * @param string $url
1796
 * @return string
1797
 */
1798
function urlencode_1738($url) {
1799
	if (preg_match(',[^\x00-\x7E],sS', $url)) {
1800
		$uri = '';
1801
		for ($i = 0; $i < strlen($url); $i++) {
1802
			if (ord($a = $url[$i]) > 127) {
1803
				$a = rawurlencode($a);
1804
			}
1805
			$uri .= $a;
1806
		}
1807
		$url = $uri;
1808
	}
1809
1810
	return quote_amp($url);
1811
}
1812
1813
// http://code.spip.net/@generer_url_entite_absolue
1814
function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1815
	if (!$connect) {
1816
		$connect = true;
1817
	}
1818
	$h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1819
	if (!preg_match(',^\w+:,', $h)) {
1820
		include_spip('inc/filtres_mini');
1821
		$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...
1822
	}
1823
1824
	return $h;
1825
}
1826
1827
1828
/**
1829
 * Tester qu'une variable d'environnement est active
1830
 *
1831
 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1832
 * variables d'environnement comme `$_SERVER['HTTPS']` ou `ini_get('display_errors')`
1833
 *
1834
 * @param string|bool $truc
1835
 *     La valeur de la variable d'environnement
1836
 * @return bool
1837
 *     true si la valeur est considérée active ; false sinon.
1838
 **/
1839
function test_valeur_serveur($truc) {
1840
	if (!$truc) {
1841
		return false;
1842
	}
1843
1844
	return (strtolower($truc) !== 'off');
1845
}
1846
1847
//
1848
// Fonctions de fabrication des URL des scripts de Spip
1849
//
1850
/**
1851
 * Calcule l'url de base du site
1852
 *
1853
 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1854
 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1855
 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1856
 *
1857
 * @note
1858
 *     La globale `$profondeur_url` doit être initialisée de manière à
1859
 *     indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1860
 *     racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1861
 *     la racine 0. Sur url/perso/ elle vaut 2
1862
 *
1863
 * @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...
1864
 *    - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1865
 *    - si int : indique que l'on veut l'url pour la profondeur indiquée
1866
 *    - si bool : retourne le tableau static complet
1867
 *    - si array : réinitialise le tableau static complet avec la valeur fournie
1868
 * @return string|array
1869
 */
1870
function url_de_base($profondeur = null) {
1871
1872
	static $url = array();
1873
	if (is_array($profondeur)) {
1874
		return $url = $profondeur;
1875
	}
1876
	if ($profondeur === false) {
1877
		return $url;
1878
	}
1879
1880
	if (is_null($profondeur)) {
1881
		$profondeur = $GLOBALS['profondeur_url'];
1882
	}
1883
1884
	if (isset($url[$profondeur])) {
1885
		return $url[$profondeur];
1886
	}
1887
1888
	$http = 'http';
1889
1890
	if (
1891
		isset($_SERVER["SCRIPT_URI"])
1892
		and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1893
	) {
1894
		$http = 'https';
1895
	} elseif (
1896
		isset($_SERVER['HTTPS'])
1897
		and test_valeur_serveur($_SERVER['HTTPS'])
1898
	) {
1899
		$http = 'https';
1900
	}
1901
1902
	// note : HTTP_HOST contient le :port si necessaire
1903
	$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : null;
1904
	// si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1905
	if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1906
		$host = $GLOBALS['meta']['adresse_site'];
1907
		if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1908
			$http = $scheme;
1909
			$host = str_replace("{$scheme}://", '', $host);
1910
		}
1911
	}
1912
	if (isset($_SERVER['SERVER_PORT'])
1913
		and $port = $_SERVER['SERVER_PORT']
1914
		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...
1915
	) {
1916
		if (!defined('_PORT_HTTP_STANDARD')) {
1917
			define('_PORT_HTTP_STANDARD', '80');
1918
		}
1919
		if (!defined('_PORT_HTTPS_STANDARD')) {
1920
			define('_PORT_HTTPS_STANDARD', '443');
1921
		}
1922
		if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
1923
			$host .= ":$port";
1924
		}
1925
		if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
1926
			$host .= ":$port";
1927
		}
1928
	}
1929
1930 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...
1931
		if (isset($_SERVER['REQUEST_URI'])) {
1932
			$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1933
		} else {
1934
			$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
1935
			if (!empty($_SERVER['QUERY_STRING'])
1936
				and !strpos($_SERVER['REQUEST_URI'], '?')
1937
			) {
1938
				$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1939
			}
1940
		}
1941
	}
1942
1943
	$url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1944
1945
	return $url[$profondeur];
1946
}
1947
1948
/**
1949
 * fonction testable de construction d'une url appelee par url_de_base()
1950
 *
1951
 * @param string $http
1952
 * @param string $host
1953
 * @param string $request
1954
 * @param int $prof
1955
 * @return string
1956
 */
1957
function url_de_($http, $host, $request, $prof = 0) {
1958
	$prof = max($prof, 0);
1959
1960
	$myself = ltrim($request, '/');
1961
	# supprimer la chaine de GET
1962
	list($myself) = explode('?', $myself);
1963
	// vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
1964
	// protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
1965 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...
1966
		$myself = explode('://',$myself);
1967
		array_shift($myself);
1968
		$myself = implode('://',$myself);
1969
		$myself = explode('/',$myself);
1970
		array_shift($myself);
1971
		$myself = implode('/',$myself);
1972
	}
1973
	$url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
1974
1975
	$url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
1976
1977
	return $url;
1978
}
1979
1980
1981
// Pour une redirection, la liste des arguments doit etre separee par "&"
1982
// Pour du code XHTML, ca doit etre &amp;
1983
// Bravo au W3C qui n'a pas ete capable de nous eviter ca
1984
// faute de separer proprement langage et meta-langage
1985
1986
// Attention, X?y=z et "X/?y=z" sont completement differents!
1987
// http://httpd.apache.org/docs/2.0/mod/mod_dir.html
1988
1989
/**
1990
 * Crée une URL vers un script de l'espace privé
1991
 *
1992
 * @example
1993
 *     ```
1994
 *     generer_url_ecrire('admin_plugin')
1995
 *     ```
1996
 *
1997
 * @param string $script
1998
 *     Nom de la page privée (xx dans exec=xx)
1999
 * @param string $args
2000
 *     Arguments à transmettre, tel que `arg1=yy&arg2=zz`
2001
 * @param bool $no_entities
2002
 *     Si false : transforme les `&` en `&amp;`
2003
 * @param bool|string $rel
2004
 *     URL relative ?
2005
 *
2006
 *     - false : l’URL sera complète et contiendra l’URL du site
2007
 *     - true : l’URL sera relavive.
2008
 *     - string : on transmet l'url à la fonction
2009
 * @return string URL
2010
 **/
2011
function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
2012
	if (!$rel) {
2013
		$rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
2014
	} else {
2015
		if (!is_string($rel)) {
2016
			$rel = _DIR_RESTREINT ? _DIR_RESTREINT :
2017
				('./' . _SPIP_ECRIRE_SCRIPT);
2018
		}
2019
	}
2020
2021
	list($script, $ancre) = array_pad(explode('#', $script), 2, null);
2022
	if ($script and ($script <> 'accueil' or $rel)) {
2023
		$args = "?exec=$script" . (!$args ? '' : "&$args");
2024
	} elseif ($args) {
2025
		$args = "?$args";
2026
	}
2027
	if ($ancre) {
2028
		$args .= "#$ancre";
2029
	}
2030
2031
	return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
2032
}
2033
2034
//
2035
// Adresse des scripts publics (a passer dans inc-urls...)
2036
//
2037
2038
2039
/**
2040
 * Retourne le nom du fichier d'exécution de SPIP
2041
 *
2042
 * @see _SPIP_SCRIPT
2043
 * @note
2044
 *   Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2045
 *   dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2046
 *
2047
 * @param string $default
2048
 *     Script par défaut
2049
 * @return string
2050
 *     Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2051
 **/
2052
function get_spip_script($default = '') {
2053
	# cas define('_SPIP_SCRIPT', '');
2054
	if (_SPIP_SCRIPT) {
2055
		return _SPIP_SCRIPT;
2056
	} else {
2057
		return $default;
2058
	}
2059
}
2060
2061
/**
2062
 * Crée une URL vers une page publique de SPIP
2063
 *
2064
 * @example
2065
 *     ```
2066
 *     generer_url_public("rubrique","id_rubrique=$id_rubrique")
2067
 *     ```
2068
 *
2069
 * @param string $script
2070
 *     Nom de la page
2071
 * @param string|array $args
2072
 *     Arguments à transmettre a l'URL,
2073
 *      soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2074
 *      soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2075
 * @param bool $no_entities
2076
 *     Si false : transforme les `&` en `&amp;`
2077
 * @param bool $rel
2078
 *     URL relative ?
2079
 *
2080
 *     - false : l’URL sera complète et contiendra l’URL du site
2081
 *     - true : l’URL sera relavive.
2082
 * @param string $action
2083
 *     - Fichier d'exécution public (spip.php par défaut)
2084
 * @return string URL
2085
 **/
2086
function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2087
	// si le script est une action (spip_pass, spip_inscription),
2088
	// standardiser vers la nouvelle API
2089
2090
	if (!$action) {
2091
		$action = get_spip_script();
2092
	}
2093
	if ($script) {
2094
		$action = parametre_url($action, _SPIP_PAGE, $script, '&');
2095
	}
2096
2097
	if ($args) {
2098
		if (is_array($args)) {
2099
			$r = '';
2100
			foreach ($args as $k => $v) {
2101
				$r .= '&' . $k . '=' . $v;
2102
			}
2103
			$args = substr($r, 1);
2104
		}
2105
		$action .=
2106
			(strpos($action, '?') !== false ? '&' : '?') . $args;
2107
	}
2108
	if (!$no_entities) {
2109
		$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...
2110
	}
2111
2112
	// ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2113
	return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2114
}
2115
2116
// http://code.spip.net/@generer_url_prive
2117
function generer_url_prive($script, $args = "", $no_entities = false) {
2118
2119
	return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2120
}
2121
2122
// Pour les formulaires en methode POST,
2123
// mettre le nom du script a la fois en input-hidden et dans le champ action:
2124
// 1) on peut ainsi memoriser le signet comme si c'etait un GET
2125
// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2126
2127
/**
2128
 * Retourne un formulaire (POST par défaut) vers un script exec
2129
 * de l’interface privée
2130
 *
2131
 * @param string $script
2132
 *     Nom de la page exec
2133
 * @param string $corps
2134
 *     Contenu du formulaire
2135
 * @param string $atts
2136
 *     Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2137
 * @param string $submit
2138
 *     Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2139
 * @return string
2140
 *     Code HTML du formulaire
2141
 **/
2142
function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2143
2144
	$script1 = explode('&', $script);
2145
	$script1 = reset($script1);
2146
2147
	return "<form action='"
2148
	. ($script ? generer_url_ecrire($script) : '')
2149
	. "' "
2150
	. ($atts ? $atts : " method='post'")
2151
	. "><div>\n"
2152
	. "<input type='hidden' name='exec' value='$script1' />"
2153
	. $corps
2154
	. (!$submit ? '' :
2155
		("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2156
	. "</div></form>\n";
2157
}
2158
2159
/**
2160
 * Générer un formulaire pour lancer une action vers $script
2161
 *
2162
 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2163
 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2164
 * qui ne sont pas destines a etre mis en signets
2165
 *
2166
 * @param string $script
2167
 * @param string $corps
2168
 * @param string $atts
2169
 * @param bool $public
2170
 * @return string
2171
 */
2172
function generer_form_action($script, $corps, $atts = '', $public = false) {
2173
	// si l'on est dans l'espace prive, on garde dans l'url
2174
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2175
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2176
	$h = (_DIR_RACINE and !$public)
2177
		? generer_url_ecrire(_request('exec'))
2178
		: generer_url_public();
2179
2180
	return "\n<form action='" .
2181
	$h .
2182
	"'" .
2183
	$atts .
2184
	">\n" .
2185
	"<div>" .
2186
	"\n<input type='hidden' name='action' value='$script' />" .
2187
	$corps .
2188
	"</div></form>";
2189
}
2190
2191
/**
2192
 * Créer une URL
2193
 *
2194
 * @param  string $script
2195
 *     Nom du script à exécuter
2196
 * @param  string $args
2197
 *     Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2198
 * @param bool $no_entities
2199
 *     Si false : transforme les & en &amp;
2200
 * @param boolean $public
2201
 *     URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2202
 *     true : l’URL sera relative.
2203
 * @return string
2204
 *     URL
2205
 */
2206
function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2207
	// si l'on est dans l'espace prive, on garde dans l'url
2208
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2209
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2210
	$url = (_DIR_RACINE and !$public)
2211
		? generer_url_ecrire(_request('exec'))
2212
		: generer_url_public('', '', false, false);
2213
	$url = parametre_url($url, 'action', $script);
2214
	if ($args) {
2215
		$url .= quote_amp('&' . $args);
2216
	}
2217
2218
	if ($no_entities) {
2219
		$url = str_replace('&amp;', '&', $url);
2220
	}
2221
2222
	return $url;
2223
}
2224
2225
2226
/**
2227
 * Fonction d'initialisation groupée pour compatibilité ascendante
2228
 *
2229
 * @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...
2230
 * @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...
2231
 * @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...
2232
 * @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...
2233
 */
2234
function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2235
	spip_initialisation_core($pi, $pa, $ti, $ta);
2236
	spip_initialisation_suite();
2237
}
2238
2239
/**
2240
 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2241
 *
2242
 * Elle définit les répertoires et fichiers non partageables
2243
 * et indique dans $test_dirs ceux devant être accessibles en écriture
2244
 * mais ne touche pas à cette variable si elle est déjà définie
2245
 * afin que mes_options.php puisse en spécifier d'autres.
2246
 *
2247
 * Elle définit ensuite les noms des fichiers et les droits.
2248
 * Puis simule un register_global=on sécurisé.
2249
 *
2250
 * @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...
2251
 * @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...
2252
 * @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...
2253
 * @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...
2254
 */
2255
function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2256
	static $too_late = 0;
2257
	if ($too_late++) {
2258
		return;
2259
	}
2260
2261
	// Declaration des repertoires
2262
2263
	// le nom du repertoire plugins/ activables/desactivables
2264
	if (!defined('_DIR_PLUGINS')) {
2265
		define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2266
	}
2267
2268
	// le nom du repertoire des extensions/ permanentes du core, toujours actives
2269
	if (!defined('_DIR_PLUGINS_DIST')) {
2270
		define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2271
	}
2272
2273
	// le nom du repertoire des librairies
2274
	if (!defined('_DIR_LIB')) {
2275
		define('_DIR_LIB', _DIR_RACINE . "lib/");
2276
	}
2277
2278
	if (!defined('_DIR_IMG')) {
2279
		define('_DIR_IMG', $pa);
2280
	}
2281
	if (!defined('_DIR_LOGOS')) {
2282
		define('_DIR_LOGOS', $pa);
2283
	}
2284
	if (!defined('_DIR_IMG_ICONES')) {
2285
		define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2286
	}
2287
2288
	if (!defined('_DIR_DUMP')) {
2289
		define('_DIR_DUMP', $ti . "dump/");
2290
	}
2291
	if (!defined('_DIR_SESSIONS')) {
2292
		define('_DIR_SESSIONS', $ti . "sessions/");
2293
	}
2294
	if (!defined('_DIR_TRANSFERT')) {
2295
		define('_DIR_TRANSFERT', $ti . "upload/");
2296
	}
2297
	if (!defined('_DIR_CACHE')) {
2298
		define('_DIR_CACHE', $ti . "cache/");
2299
	}
2300
	if (!defined('_DIR_CACHE_XML')) {
2301
		define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2302
	}
2303
	if (!defined('_DIR_SKELS')) {
2304
		define('_DIR_SKELS', _DIR_CACHE . "skel/");
2305
	}
2306
	if (!defined('_DIR_AIDE')) {
2307
		define('_DIR_AIDE', _DIR_CACHE . "aide/");
2308
	}
2309
	if (!defined('_DIR_TMP')) {
2310
		define('_DIR_TMP', $ti);
2311
	}
2312
2313
	if (!defined('_DIR_VAR')) {
2314
		define('_DIR_VAR', $ta);
2315
	}
2316
2317
	if (!defined('_DIR_ETC')) {
2318
		define('_DIR_ETC', $pi);
2319
	}
2320
	if (!defined('_DIR_CONNECT')) {
2321
		define('_DIR_CONNECT', $pi);
2322
	}
2323
	if (!defined('_DIR_CHMOD')) {
2324
		define('_DIR_CHMOD', $pi);
2325
	}
2326
2327
	if (!isset($GLOBALS['test_dirs']))
2328
		// Pas $pi car il est bon de le mettre hors ecriture apres intstall
2329
		// il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2330
	{
2331
		$GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2332
	}
2333
2334
	// Declaration des fichiers
2335
2336
	if (!defined('_CACHE_PLUGINS_PATH')) {
2337
		define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2338
	}
2339
	if (!defined('_CACHE_PLUGINS_OPT')) {
2340
		define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2341
	}
2342
	if (!defined('_CACHE_PLUGINS_FCT')) {
2343
		define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2344
	}
2345
	if (!defined('_CACHE_PIPELINES')) {
2346
		define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2347
	}
2348
	if (!defined('_CACHE_CHEMIN')) {
2349
		define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2350
	}
2351
2352
	# attention .php obligatoire pour ecrire_fichier_securise
2353
	if (!defined('_FILE_META')) {
2354
		define('_FILE_META', $ti . 'meta_cache.php');
2355
	}
2356
	if (!defined('_DIR_LOG')) {
2357
		define('_DIR_LOG', _DIR_TMP . 'log/');
2358
	}
2359
	if (!defined('_FILE_LOG')) {
2360
		define('_FILE_LOG', 'spip');
2361
	}
2362
	if (!defined('_FILE_LOG_SUFFIX')) {
2363
		define('_FILE_LOG_SUFFIX', '.log');
2364
	}
2365
2366
	// Le fichier de connexion a la base de donnees
2367
	// tient compte des anciennes versions (inc_connect...)
2368
	if (!defined('_FILE_CONNECT_INS')) {
2369
		define('_FILE_CONNECT_INS', 'connect');
2370
	}
2371
	if (!defined('_FILE_CONNECT')) {
2372
		define('_FILE_CONNECT',
2373
		(@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2374
			: (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2375
				: false)));
2376
	}
2377
2378
	// Le fichier de reglages des droits
2379
	if (!defined('_FILE_CHMOD_INS')) {
2380
		define('_FILE_CHMOD_INS', 'chmod');
2381
	}
2382
	if (!defined('_FILE_CHMOD')) {
2383
		define('_FILE_CHMOD',
2384
		(@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2385
			: false));
2386
	}
2387
2388
	if (!defined('_FILE_LDAP')) {
2389
		define('_FILE_LDAP', 'ldap.php');
2390
	}
2391
2392
	if (!defined('_FILE_TMP_SUFFIX')) {
2393
		define('_FILE_TMP_SUFFIX', '.tmp.php');
2394
	}
2395
	if (!defined('_FILE_CONNECT_TMP')) {
2396
		define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2397
	}
2398
	if (!defined('_FILE_CHMOD_TMP')) {
2399
		define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2400
	}
2401
2402
	// Definition des droits d'acces en ecriture
2403
	if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2404
		include_once _FILE_CHMOD;
2405
	}
2406
2407
	// Se mefier des fichiers mal remplis!
2408
	if (!defined('_SPIP_CHMOD')) {
2409
		define('_SPIP_CHMOD', 0777);
2410
	}
2411
2412
	if (!defined('_DEFAULT_CHARSET')) {
2413
		/** Le charset par défaut lors de l'installation */
2414
		define('_DEFAULT_CHARSET', 'utf-8');
2415
	}
2416
	if (!defined('_ROOT_PLUGINS')) {
2417
		define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2418
	}
2419
	if (!defined('_ROOT_PLUGINS_DIST')) {
2420
		define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2421
	}
2422
	if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2423
		define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2424
	}
2425
2426
	// La taille des Log
2427
	if (!defined('_MAX_LOG')) {
2428
		define('_MAX_LOG', 100);
2429
	}
2430
2431
	// Sommes-nous dans l'empire du Mal ?
2432
	// (ou sous le signe du Pingouin, ascendant GNU ?)
2433
	if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2434
		if (!defined('_OS_SERVEUR')) {
2435
			define('_OS_SERVEUR', 'windows');
2436
		}
2437
		if (!defined('_SPIP_LOCK_MODE')) {
2438
			define('_SPIP_LOCK_MODE', 1);
2439
		} // utiliser le flock php
2440
	} else {
2441
		if (!defined('_OS_SERVEUR')) {
2442
			define('_OS_SERVEUR', '');
2443
		}
2444
		if (!defined('_SPIP_LOCK_MODE')) {
2445
			define('_SPIP_LOCK_MODE', 1);
2446
		} // utiliser le flock php
2447
		#if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2448
	}
2449
2450
	// Langue par defaut
2451
	if (!defined('_LANGUE_PAR_DEFAUT')) {
2452
		define('_LANGUE_PAR_DEFAUT', 'fr');
2453
	}
2454
2455
	//
2456
	// Module de lecture/ecriture/suppression de fichiers utilisant flock()
2457
	// (non surchargeable en l'etat ; attention si on utilise include_spip()
2458
	// pour le rendre surchargeable, on va provoquer un reecriture
2459
	// systematique du noyau ou une baisse de perfs => a etudier)
2460
	include_once _ROOT_RESTREINT . 'inc/flock.php';
2461
2462
	// charger tout de suite le path et son cache
2463
	load_path_cache();
2464
2465
	// *********** traiter les variables ************
2466
2467
	//
2468
	// Securite
2469
	//
2470
2471
	// Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2472
	if (isset($_REQUEST['GLOBALS'])) {
2473
		die();
2474
	}
2475
	// nettoyer les magic quotes \' et les caracteres nuls %00
2476
	spip_desinfecte($_GET);
2477
	spip_desinfecte($_POST);
2478
	spip_desinfecte($_COOKIE);
2479
	spip_desinfecte($_REQUEST);
2480
2481
	// appliquer le cookie_prefix
2482
	if ($GLOBALS['cookie_prefix'] != 'spip') {
2483
		include_spip('inc/cookie');
2484
		recuperer_cookies_spip($GLOBALS['cookie_prefix']);
2485
	}
2486
2487
	//
2488
	// Capacites php (en fonction de la version)
2489
	//
2490
	$GLOBALS['flag_ob'] = (function_exists("ob_start")
2491
		&& function_exists("ini_get")
2492
		&& !strstr(@ini_get('disable_functions'), 'ob_'));
2493
	$GLOBALS['flag_sapi_name'] = function_exists("php_sapi_name");
2494
	$GLOBALS['flag_get_cfg_var'] = (@get_cfg_var('error_reporting') != "");
2495
	$GLOBALS['flag_upload'] = (!$GLOBALS['flag_get_cfg_var'] ||
2496
		(get_cfg_var('upload_max_filesize') > 0));
2497
2498
2499
	// Compatibilite avec serveurs ne fournissant pas $REQUEST_URI
2500 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...
2501
		$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
2502
	} else {
2503
		$GLOBALS['REQUEST_URI'] = (php_sapi_name() !== 'cli') ? $_SERVER['PHP_SELF'] : '';
2504
		if (!empty($_SERVER['QUERY_STRING'])
2505
			and !strpos($_SERVER['REQUEST_URI'], '?')
2506
		) {
2507
			$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
2508
		}
2509
	}
2510
2511
	// Duree de validite de l'alea pour les cookies et ce qui s'ensuit.
2512
	if (!defined('_RENOUVELLE_ALEA')) {
2513
		define('_RENOUVELLE_ALEA', 12 * 3600);
2514
	}
2515
	if (!defined('_DUREE_COOKIE_ADMIN')) {
2516
		define('_DUREE_COOKIE_ADMIN', 14 * 24 * 3600);
2517
	}
2518
2519
	// charger les meta si possible et renouveller l'alea au besoin
2520
	// charge aussi effacer_meta et ecrire_meta
2521
	$inc_meta = charger_fonction('meta', 'inc');
2522
	$inc_meta();
2523
2524
	// nombre de repertoires depuis la racine
2525
	// on compare a l'adresse de spip.php : $_SERVER["SCRIPT_NAME"]
2526
	// ou a defaut celle donnee en meta ; (mais si celle-ci est fausse
2527
	// le calcul est faux)
2528
	if (!_DIR_RESTREINT) {
2529
		$GLOBALS['profondeur_url'] = 1;
2530
	} else {
2531
		$uri = isset($_SERVER['REQUEST_URI']) ? explode('?', $_SERVER['REQUEST_URI']) : '';
2532
		$uri_ref = $_SERVER["SCRIPT_NAME"];
2533
		if (!$uri_ref
2534
			// si on est appele avec un autre ti, on est sans doute en mutu
2535
			// si jamais c'est de la mutu avec sous rep, on est perdu si on se fie
2536
			// a spip.php qui est a la racine du spip, et vue qu'on sait pas se reperer
2537
			// s'en remettre a l'adresse du site. alea jacta est.
2538
			or $ti !== _NOM_TEMPORAIRES_INACCESSIBLES
2539
		) {
2540
2541
			if (isset($GLOBALS['meta']['adresse_site'])) {
2542
				$uri_ref = parse_url($GLOBALS['meta']['adresse_site']);
2543
				$uri_ref = (isset($uri_ref['path']) ? $uri_ref['path'] : '') . '/';
2544
			} else {
2545
				$uri_ref = "";
2546
			}
2547
		}
2548
		if (!$uri or !$uri_ref) {
2549
			$GLOBALS['profondeur_url'] = 0;
2550
		} else {
2551
			$GLOBALS['profondeur_url'] = max(0,
2552
				substr_count($uri[0], '/')
2553
				- substr_count($uri_ref, '/'));
2554
		}
2555
	}
2556
	// s'il y a un cookie ou PHP_AUTH, initialiser visiteur_session
2557
	if (_FILE_CONNECT) {
2558
		if (verifier_visiteur() == '0minirezo'
2559
			// si c'est un admin sans cookie admin, il faut ignorer le cache chemin !
2560
			and !isset($_COOKIE['spip_admin'])
2561
		) {
2562
			clear_path_cache();
2563
		}
2564
	}
2565
2566
}
2567
2568
/**
2569
 * Complements d'initialisation non critiques pouvant etre realises
2570
 * par les plugins
2571
 *
2572
 */
2573
function spip_initialisation_suite() {
2574
	static $too_late = 0;
2575
	if ($too_late++) {
2576
		return;
2577
	}
2578
2579
	// taille mini des login
2580
	if (!defined('_LOGIN_TROP_COURT')) {
2581
		define('_LOGIN_TROP_COURT', 4);
2582
	}
2583
2584
	// la taille maxi des logos (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2585
	#if (!defined('_LOGO_MAX_SIZE')) define('_LOGO_MAX_SIZE', 0); # poids en ko
2586
	#if (!defined('_LOGO_MAX_WIDTH')) define('_LOGO_MAX_WIDTH', 0); # largeur en pixels
2587
	#if (!defined('_LOGO_MAX_HEIGHT')) define('_LOGO_MAX_HEIGHT', 0); # hauteur en pixels
2588
2589
	// la taille maxi des images (0 : pas de limite) (pas de define par defaut, ce n'est pas utile)
2590
	#if (!defined('_DOC_MAX_SIZE')) define('_DOC_MAX_SIZE', 0); # poids en ko
2591
	#if (!defined('_IMG_MAX_SIZE')) define('_IMG_MAX_SIZE', 0); # poids en ko
2592
	#if (!defined('_IMG_MAX_WIDTH')) define('_IMG_MAX_WIDTH', 0); # largeur en pixels
2593
	#if (!defined('_IMG_MAX_HEIGHT')) define('_IMG_MAX_HEIGHT', 0); # hauteur en pixels
2594
2595
	if (!defined('_PASS_LONGUEUR_MINI')) {
2596
		define('_PASS_LONGUEUR_MINI', 6);
2597
	}
2598
2599
2600
	// Qualite des images calculees automatiquement. C'est un nombre entre 0 et 100, meme pour imagick (on ramene a 0..1 par la suite)
2601
	if (!defined('_IMG_QUALITE')) {
2602
		define('_IMG_QUALITE', 85);
2603
	} # valeur par defaut
2604
	if (!defined('_IMG_GD_QUALITE')) {
2605
		define('_IMG_GD_QUALITE', _IMG_QUALITE);
2606
	} # surcharge pour la lib GD
2607
	if (!defined('_IMG_CONVERT_QUALITE')) {
2608
		define('_IMG_CONVERT_QUALITE', _IMG_QUALITE);
2609
	} # surcharge pour imagick en ligne de commande
2610
	// Historiquement la valeur pour imagick semble differente. Si ca n'est pas necessaire, il serait preferable de garder _IMG_QUALITE
2611
	if (!defined('_IMG_IMAGICK_QUALITE')) {
2612
		define('_IMG_IMAGICK_QUALITE', 75);
2613
	} # surcharge pour imagick en PHP
2614
2615
	if (!defined('_COPIE_LOCALE_MAX_SIZE')) {
2616
		define('_COPIE_LOCALE_MAX_SIZE', 33554432);
2617
	} // poids en octet
2618
2619
	// qq chaines standard
2620
	if (!defined('_ACCESS_FILE_NAME')) {
2621
		define('_ACCESS_FILE_NAME', '.htaccess');
2622
	}
2623
	if (!defined('_AUTH_USER_FILE')) {
2624
		define('_AUTH_USER_FILE', '.htpasswd');
2625
	}
2626
	if (!defined('_SPIP_DUMP')) {
2627
		define('_SPIP_DUMP', 'dump@nom_site@@[email protected]');
2628
	}
2629
	if (!defined('_CACHE_RUBRIQUES')) {
2630
		/** Fichier cache pour le navigateur de rubrique du bandeau */
2631
		define('_CACHE_RUBRIQUES', _DIR_TMP . 'menu-rubriques-cache.txt');
2632
	}
2633
	if (!defined('_CACHE_RUBRIQUES_MAX')) {
2634
		/** Nombre maxi de rubriques enfants affichées pour chaque rubrique du navigateur de rubrique du bandeau */
2635
		define('_CACHE_RUBRIQUES_MAX', 500);
2636
	}
2637
2638
	if (!defined('_EXTENSION_SQUELETTES')) {
2639
		define('_EXTENSION_SQUELETTES', 'html');
2640
	}
2641
2642
	if (!defined('_DOCTYPE_ECRIRE')) {
2643
		/** Définit le doctype de l’espace privé */
2644
		define('_DOCTYPE_ECRIRE', "<!DOCTYPE html>\n");
2645
	}
2646
	if (!defined('_DOCTYPE_AIDE')) {
2647
		/** Définit le doctype de l’aide en ligne */
2648
		define('_DOCTYPE_AIDE',
2649
		"<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Frameset//EN' 'http://www.w3.org/TR/1999/REC-html401-19991224/frameset.dtd'>");
2650
	}
2651
2652
	if (!defined('_SPIP_SCRIPT')) {
2653
		/** L'adresse de base du site ; on peut mettre '' si la racine est gerée par
2654
		 * le script de l'espace public, alias index.php */
2655
		define('_SPIP_SCRIPT', 'spip.php');
2656
	}
2657
	if (!defined('_SPIP_PAGE')) {
2658
		/** Argument page, personalisable en cas de conflit avec un autre script */
2659
		define('_SPIP_PAGE', 'page');
2660
	}
2661
2662
	// le script de l'espace prive
2663
	// Mettre a "index.php" si DirectoryIndex ne le fait pas ou pb connexes:
2664
	// les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
2665
	// meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
2666
	if (!defined('_SPIP_ECRIRE_SCRIPT')) {
2667
		if (!empty($_SERVER['SERVER_SOFTWARE']) and preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE'])) {
2668
			define('_SPIP_ECRIRE_SCRIPT', 'index.php');
2669
		} else {
2670
			define('_SPIP_ECRIRE_SCRIPT', '');
2671
		}
2672
	}
2673
2674
2675
	if (!defined('_SPIP_AJAX')) {
2676
		define('_SPIP_AJAX', ((!isset($_COOKIE['spip_accepte_ajax']))
2677
			? 1
2678
			: (($_COOKIE['spip_accepte_ajax'] != -1) ? 1 : 0)));
2679
	}
2680
2681
	// La requete est-elle en ajax ?
2682
	if (!defined('_AJAX')) {
2683
		define('_AJAX',
2684
			(isset($_SERVER['HTTP_X_REQUESTED_WITH']) # ajax jQuery
2685
				or !empty($_REQUEST['var_ajax_redir']) # redirection 302 apres ajax jQuery
2686
				or !empty($_REQUEST['var_ajaxcharset']) # compat ascendante pour plugins
2687
				or !empty($_REQUEST['var_ajax']) # forms ajax & inclure ajax de spip
2688
			)
2689
			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
2690
		);
2691
	}
2692
2693
	# nombre de pixels maxi pour calcul de la vignette avec gd
2694
	# au dela de 5500000 on considere que php n'est pas limite en memoire pour cette operation
2695
	# les configurations limitees en memoire ont un seuil plutot vers 1MPixel
2696
	if (!defined('_IMG_GD_MAX_PIXELS')) {
2697
		define('_IMG_GD_MAX_PIXELS',
2698
		(isset($GLOBALS['meta']['max_taille_vignettes']) and $GLOBALS['meta']['max_taille_vignettes'])
2699
			? $GLOBALS['meta']['max_taille_vignettes']
2700
			: 0);
2701
	}
2702
2703
	if (!defined('_MEMORY_LIMIT_MIN')) {
2704
		define('_MEMORY_LIMIT_MIN', 16);
2705
	} // en Mo
2706
	// si on est dans l'espace prive et si le besoin est superieur a 8Mo (qui est vraiment le standard)
2707
	// on verifie que la memoire est suffisante pour le compactage css+js pour eviter la page blanche
2708
	// il y aura d'autres problemes et l'utilisateur n'ira pas tres loin, mais ce sera plus comprehensible qu'une page blanche
2709
	if (test_espace_prive() and _MEMORY_LIMIT_MIN > 8) {
2710
		if ($memory = trim(ini_get('memory_limit')) and $memory != -1) {
2711
			$unit = strtolower(substr($memory, -1));
2712
			$memory = substr($memory, 0, -1);
2713
			switch ($unit) {
2714
				// Le modifieur 'G' est disponible depuis PHP 5.1.0
2715
				case 'g':
2716
					$memory *= 1024;
2717
				case 'm':
2718
					$memory *= 1024;
2719
				case 'k':
2720
					$memory *= 1024;
2721
			}
2722
			if ($memory < _MEMORY_LIMIT_MIN * 1024 * 1024) {
2723
				@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...
2724
				if (trim(ini_get('memory_limit')) != $m) {
2725
					if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2726
						define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2727
					} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2728
				}
2729
			}
2730
		} else {
2731
			if (!defined('_INTERDIRE_COMPACTE_HEAD_ECRIRE')) {
2732
				define('_INTERDIRE_COMPACTE_HEAD_ECRIRE', true);
2733
			}
2734
		} // evite une page blanche car on ne saura pas calculer la css dans ce hit
2735
	}
2736
	// Protocoles a normaliser dans les chaines de langues
2737
	if (!defined('_PROTOCOLES_STD')) {
2738
		define('_PROTOCOLES_STD', 'http|https|ftp|mailto|webcal');
2739
	}
2740
2741
	init_var_mode();
2742
}
2743
2744
/**
2745
 * Repérer les variables d'URL spéciales `var_mode` qui conditionnent
2746
 * la validité du cache ou certains affichages spéciaux.
2747
 *
2748
 * Le paramètre d'URL `var_mode` permet de
2749
 * modifier la pérennité du cache, recalculer des urls
2750
 * ou d'autres petit caches (trouver_table, css et js compactes ...),
2751
 * d'afficher un écran de débug ou des traductions non réalisées.
2752
 *
2753
 * En fonction de ces paramètres dans l'URL appelante, on définit
2754
 * da constante `_VAR_MODE` qui servira ensuite à SPIP.
2755
 *
2756
 * Le paramètre `var_mode` accepte ces valeurs :
2757
 *
2758
 * - `calcul` : force un calcul du cache de la page (sans forcément recompiler les squelettes)
2759
 * - `recalcul` : force un calcul du cache de la page en recompilant au préabable les squelettes
2760
 * - `inclure` : modifie l'affichage en ajoutant visuellement le nom de toutes les inclusions qu'elle contient
2761
 * - `debug` :  modifie l'affichage activant le mode "debug"
2762
 * - `preview` : modifie l'affichage en ajoutant aux boucles les éléments prévisualisables
2763
 * - `traduction` : modifie l'affichage en affichant des informations sur les chaînes de langues utilisées
2764
 * - `urls` : permet de recalculer les URLs des objets appelés dans la page par les balises `#URL_xx`
2765
 * - `images` : permet de recalculer les filtres d'images utilisés dans la page
2766
 *
2767
 * En dehors des modes `calcul` et `recalcul`, une autorisation 'previsualiser' ou 'debug' est testée.
2768
 *
2769
 * @note
2770
 *     Il éxiste également le paramètre `var_profile` qui modifie l'affichage pour incruster
2771
 *     le nombre de requêtes SQL utilisées dans la page, qui peut se compléter avec le paramètre
2772
 * `   var_mode` (calcul ou recalcul).
2773
 */
2774
function init_var_mode() {
2775
	static $done = false;
2776
	if (!$done) {
2777
2778
		if (isset($_GET['var_mode'])) {
2779
			$var_mode = explode(',', $_GET['var_mode']);
2780
			// tout le monde peut calcul/recalcul
2781
			if (!defined('_VAR_MODE')) {
2782
				if (in_array('recalcul', $var_mode)) {
2783
					define('_VAR_MODE', 'recalcul');
2784
				} elseif (in_array('calcul', $var_mode)) {
2785
					define('_VAR_MODE', 'calcul');
2786
				}
2787
			}
2788
			$var_mode = array_diff($var_mode, array('calcul', 'recalcul'));
2789
			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...
2790
				include_spip('inc/autoriser');
2791
				// autoriser preview si preview seulement, et sinon autoriser debug
2792
				if (autoriser(
2793
					($_GET['var_mode'] == 'preview')
2794
						? 'previsualiser'
2795
						: 'debug'
2796
				)) {
2797 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...
2798
						// forcer le calcul pour passer dans traduire
2799
						if (!defined('_VAR_MODE')) {
2800
							define('_VAR_MODE', 'calcul');
2801
						}
2802
						// et ne pas enregistrer de cache pour ne pas trainer les surlignages sur d'autres pages
2803
						if (!defined('_VAR_NOCACHE')) {
2804
							define('_VAR_NOCACHE', true);
2805
						}
2806
						$var_mode = array_diff($var_mode, array('traduction'));
2807
					}
2808 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...
2809
						// basculer sur les criteres de preview dans les boucles
2810
						if (!defined('_VAR_PREVIEW')) {
2811
							define('_VAR_PREVIEW', true);
2812
						}
2813
						// forcer le calcul
2814
						if (!defined('_VAR_MODE')) {
2815
							define('_VAR_MODE', 'calcul');
2816
						}
2817
						// et ne pas enregistrer de cache
2818
						if (!defined('_VAR_NOCACHE')) {
2819
							define('_VAR_NOCACHE', true);
2820
						}
2821
						$var_mode = array_diff($var_mode, array('preview'));
2822
					}
2823 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...
2824
						// forcer le compilo et ignorer les caches existants
2825
						if (!defined('_VAR_MODE')) {
2826
							define('_VAR_MODE', 'calcul');
2827
						}
2828
						if (!defined('_VAR_INCLURE')) {
2829
							define('_VAR_INCLURE', true);
2830
						}
2831
						// et ne pas enregistrer de cache
2832
						if (!defined('_VAR_NOCACHE')) {
2833
							define('_VAR_NOCACHE', true);
2834
						}
2835
						$var_mode = array_diff($var_mode, array('inclure'));
2836
					}
2837 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...
2838
						// forcer le compilo et ignorer les caches existants
2839
						if (!defined('_VAR_MODE')) {
2840
							define('_VAR_MODE', 'calcul');
2841
						}
2842
						if (!defined('_VAR_URLS')) {
2843
							define('_VAR_URLS', true);
2844
						}
2845
						$var_mode = array_diff($var_mode, array('urls'));
2846
					}
2847 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...
2848
						// forcer le compilo et ignorer les caches existants
2849
						if (!defined('_VAR_MODE')) {
2850
							define('_VAR_MODE', 'calcul');
2851
						}
2852
						// indiquer qu'on doit recalculer les images
2853
						if (!defined('_VAR_IMAGES')) {
2854
							define('_VAR_IMAGES', true);
2855
						}
2856
						$var_mode = array_diff($var_mode, array('images'));
2857
					}
2858 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...
2859
						if (!defined('_VAR_MODE')) {
2860
							define('_VAR_MODE', 'debug');
2861
						}
2862
						// et ne pas enregistrer de cache
2863
						if (!defined('_VAR_NOCACHE')) {
2864
							define('_VAR_NOCACHE', true);
2865
						}
2866
						$var_mode = array_diff($var_mode, array('debug'));
2867
					}
2868
					if (count($var_mode) and !defined('_VAR_MODE')) {
2869
						define('_VAR_MODE', reset($var_mode));
2870
					}
2871
					if (isset($GLOBALS['visiteur_session']['nom'])) {
2872
						spip_log($GLOBALS['visiteur_session']['nom']
2873
							. " " . _VAR_MODE);
2874
					}
2875
				} // pas autorise ?
2876
				else {
2877
					// si on n'est pas connecte on se redirige
2878
					if (!$GLOBALS['visiteur_session']) {
2879
						include_spip('inc/headers');
2880
						redirige_par_entete(generer_url_public('login',
2881
							'url=' . rawurlencode(
2882
								parametre_url(self(), 'var_mode', $_GET['var_mode'], '&')
2883
							), true));
2884
					}
2885
					// sinon tant pis
2886
				}
2887
			}
2888
		}
2889
		if (!defined('_VAR_MODE')) {
2890
			/**
2891
			 * Indique le mode de calcul ou d'affichage de la page.
2892
			 * @see init_var_mode()
2893
			 */
2894
			define('_VAR_MODE', false);
2895
		}
2896
		$done = true;
2897
	}
2898
}
2899
2900
// Annuler les magic quotes \' sur GET POST COOKIE et GLOBALS ;
2901
// supprimer aussi les eventuels caracteres nuls %00, qui peuvent tromper
2902
// la commande is_readable('chemin/vers/fichier/interdit%00truc_normal')
2903
// http://code.spip.net/@spip_desinfecte
2904
function spip_desinfecte(&$t, $deep = true) {
2905
	static $magic_quotes;
2906
	if (!isset($magic_quotes)) {
2907
		$magic_quotes = @get_magic_quotes_gpc();
2908
	}
2909
2910
	foreach ($t as $key => $val) {
2911
		if (is_string($t[$key])) {
2912
			if ($magic_quotes) {
2913
				$t[$key] = stripslashes($t[$key]);
2914
			}
2915
			$t[$key] = str_replace(chr(0), '-', $t[$key]);
2916
		} // traiter aussi les "texte_plus" de article_edit
2917
		else {
2918
			if ($deep and is_array($t[$key]) and $key !== 'GLOBALS') {
2919
				spip_desinfecte($t[$key], $deep);
2920
			}
2921
		}
2922
	}
2923
}
2924
2925
//  retourne le statut du visiteur s'il s'annonce
2926
2927
// http://code.spip.net/@verifier_visiteur
2928
function verifier_visiteur() {
2929
	// Rq: pour que cette fonction marche depuis mes_options
2930
	// il faut forcer l'init si ce n'est fait
2931
	// mais on risque de perturber des plugins en initialisant trop tot
2932
	// certaines constantes
2933
	@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...
2934
		(_DIR_RACINE . _NOM_PERMANENTS_INACCESSIBLES),
2935
		(_DIR_RACINE . _NOM_PERMANENTS_ACCESSIBLES),
2936
		(_DIR_RACINE . _NOM_TEMPORAIRES_INACCESSIBLES),
2937
		(_DIR_RACINE . _NOM_TEMPORAIRES_ACCESSIBLES)
2938
	);
2939
2940
	// Demarrer une session NON AUTHENTIFIEE si on donne son nom
2941
	// dans un formulaire sans login (ex: #FORMULAIRE_FORUM)
2942
	// Attention on separe bien session_nom et nom, pour eviter
2943
	// les melanges entre donnees SQL et variables plus aleatoires
2944
	$variables_session = array('session_nom', 'session_email');
2945
	foreach ($variables_session as $var) {
2946
		if (_request($var) !== null) {
2947
			$init = true;
2948
			break;
2949
		}
2950
	}
2951
	if (isset($init)) {
2952
		#@spip_initialisation_suite();
2953
		$session = charger_fonction('session', 'inc');
2954
		$session();
2955
		include_spip('inc/texte');
2956
		foreach ($variables_session as $var) {
2957
			if (($a = _request($var)) !== null) {
2958
				$GLOBALS['visiteur_session'][$var] = safehtml($a);
2959
			}
2960
		}
2961
		if (!isset($GLOBALS['visiteur_session']['id_auteur'])) {
2962
			$GLOBALS['visiteur_session']['id_auteur'] = 0;
2963
		}
2964
		$session($GLOBALS['visiteur_session']);
2965
2966
		return 0;
2967
	}
2968
2969
	$h = (isset($_SERVER['PHP_AUTH_USER']) and !$GLOBALS['ignore_auth_http']);
2970
	if ($h or isset($_COOKIE['spip_session']) or isset($_COOKIE[$GLOBALS['cookie_prefix'] . '_session'])) {
2971
2972
		$session = charger_fonction('session', 'inc');
2973
		if ($session()) {
2974
			return $GLOBALS['visiteur_session']['statut'];
2975
		}
2976
		if ($h and isset($_SERVER['PHP_AUTH_PW'])) {
2977
			include_spip('inc/auth');
2978
			$h = lire_php_auth($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
2979
		}
2980
		if ($h) {
2981
			$GLOBALS['visiteur_session'] = $h;
2982
2983
			return $GLOBALS['visiteur_session']['statut'];
2984
		}
2985
	}
2986
2987
	// au moins son navigateur nous dit la langue preferee de cet inconnu
2988
	include_spip('inc/lang');
2989
	utiliser_langue_visiteur();
2990
2991
	return false;
2992
}
2993
2994
2995
/**
2996
 * Sélectionne la langue donnée en argument et mémorise la courante
2997
 *
2998
 * Restaure l'ancienne langue si appellée sans argument.
2999
 *
3000
 * @note
3001
 *     On pourrait économiser l'empilement en cas de non changemnt
3002
 *     et lui faire retourner `False` pour prevenir l'appelant
3003
 *     Le noyau de Spip sait le faire, mais pour assurer la compatibilité
3004
 *     cette fonction retourne toujours non `False`
3005
 *
3006
 * @uses changer_langue()
3007
 * @param null|string $lang
3008
 *     - string : Langue à appliquer,
3009
 *     - null : Pour restituer la dernière langue mémorisée.
3010
 * @return string
3011
 *     - string Langue utilisée.
3012
 **/
3013
function lang_select($lang = null) {
3014
	static $pile_langues = array();
3015
	if (!function_exists('changer_langue')) {
3016
		include_spip('inc/lang');
3017
	}
3018
	if ($lang === null) {
3019
		$lang = array_pop($pile_langues);
3020
	} else {
3021
		array_push($pile_langues, $GLOBALS['spip_lang']);
3022
	}
3023
	if (isset($GLOBALS['spip_lang']) and $lang == $GLOBALS['spip_lang']) {
3024
		return $lang;
3025
	}
3026
	changer_langue($lang);
3027
3028
	return $lang;
3029
}
3030
3031
/**
3032
 * Renvoie une chaîne qui identifie la session courante
3033
 *
3034
 * Permet de savoir si on peut utiliser un cache enregistré pour cette session.
3035
 * Cette chaîne est courte (8 cars) pour pouvoir être utilisée dans un nom
3036
 * de fichier cache.
3037
 *
3038
 * @pipeline_appel definir_session
3039
 *
3040
 * @param bool $force
3041
 * @return string
3042
 *     Identifiant de la session
3043
 **/
3044
function spip_session($force = false) {
3045
	static $session;
3046
	if ($force or !isset($session)) {
3047
		$s = pipeline('definir_session',
3048
			$GLOBALS['visiteur_session']
3049
				? serialize($GLOBALS['visiteur_session'])
3050
				. '_' . @$_COOKIE['spip_session']
3051
				: ''
3052
		);
3053
		$session = $s ? substr(md5($s), 0, 8) : '';
3054
	}
3055
3056
	#spip_log('session: '.$session);
3057
	return $session;
3058
}
3059
3060
3061
/**
3062
 * Retourne un lien vers une aide
3063
 *
3064
 * Aide, aussi depuis l'espace privé à présent.
3065
 * Surchargeable mais pas d'erreur fatale si indisponible.
3066
 *
3067
 * @param string $aide
3068
 *    Cle d'identification de l'aide desiree
3069
 * @param bool $distante
3070
 *    Generer une url locale (par defaut)
3071
 *    ou une url distante [directement sur spip.net]
3072
 * @return
3073
 *    Lien sur une icone d'aide
3074
 **/
3075
function aider($aide = '', $distante = false) {
3076
	$aider = charger_fonction('aide', 'inc', true);
3077
3078
	return $aider ? $aider($aide, '', array(), $distante) : '';
3079
}
3080
3081
/**
3082
 * Page `exec=info` : retourne le contenu de la fonction php `phpinfo()`
3083
 *
3084
 * Si l’utiliseur est un webmestre.
3085
 */
3086
function exec_info_dist() {
3087
3088
	include_spip('inc/autoriser');
3089
	if (autoriser('webmestre')) {
3090
		phpinfo();
3091
	} else {
3092
		include_spip('inc/filtres');
3093
		sinon_interdire_acces();
3094
	}
3095
}
3096
3097
/**
3098
 * Génère une erreur de squelette
3099
 *
3100
 * Génère une erreur de squelette qui sera bien visible par un
3101
 * administrateur authentifié lors d'une visite de la page en erreur
3102
 *
3103
 * @param bool|string|array $message
3104
 *     - Message d'erreur (string|array)
3105
 *     - false pour retourner le texte des messages d'erreurs
3106
 *     - vide pour afficher les messages d'erreurs
3107
 * @param string|array|object $lieu
3108
 *     Lieu d'origine de l'erreur
3109
 * @return null|string
3110
 *     - Rien dans la plupart des cas
3111
 *     - string si $message à false.
3112
 **/
3113
function erreur_squelette($message = '', $lieu = '') {
3114
	$debusquer = charger_fonction('debusquer', 'public');
3115
	if (is_array($lieu)) {
3116
		include_spip('public/compiler');
3117
		$lieu = reconstruire_contexte_compil($lieu);
3118
	}
3119
3120
	return $debusquer($message, $lieu);
3121
}
3122
3123
/**
3124
 * Calcule un squelette avec un contexte et retourne son contenu
3125
 *
3126
 * La fonction de base de SPIP : un squelette + un contexte => une page.
3127
 * $fond peut etre un nom de squelette, ou une liste de squelette au format array.
3128
 * Dans ce dernier cas, les squelettes sont tous evalues et mis bout a bout
3129
 * $options permet de selectionner les options suivantes :
3130
 *
3131
 * - trim => true (valeur par defaut) permet de ne rien renvoyer si le fond ne produit que des espaces ;
3132
 * - raw  => true permet de recuperer la strucure $page complete avec entetes et invalideurs
3133
 *          pour chaque $fond fourni.
3134
 *
3135
 * @api
3136
 * @param string /array $fond
3137
 *     - Le ou les squelettes à utiliser, sans l'extension, {@example prive/liste/auteurs}
3138
 *     - Le fichier sera retrouvé dans la liste des chemins connus de SPIP (squelettes, plugins, spip)
3139
 * @param array $contexte
3140
 *     - Informations de contexte envoyées au squelette, {@example array('id_rubrique' => 8)}
3141
 *     - La langue est transmise automatiquement (sauf option étoile).
3142
 * @param array $options
3143
 *     Options complémentaires :
3144
 *
3145
 *     - trim   : applique un trim sur le résultat (true par défaut)
3146
 *     - raw    : retourne un tableau d'information sur le squelette (false par défaut)
3147
 *     - etoile : ne pas transmettre la langue au contexte automatiquement (false par défaut),
3148
 *                équivalent de INCLURE*
3149
 *     - ajax   : gere les liens internes du squelette en ajax (équivalent du paramètre {ajax})
3150
 * @param string $connect
3151
 *     Non du connecteur de bdd a utiliser
3152
 * @return string|array
3153
 *     - Contenu du squelette calculé
3154
 *     - ou tableau d'information sur le squelette.
3155
 */
3156
function recuperer_fond($fond, $contexte = array(), $options = array(), $connect = '') {
3157
	if (!function_exists('evaluer_fond')) {
3158
		include_spip('public/assembler');
3159
	}
3160
	// assurer la compat avec l'ancienne syntaxe
3161
	// (trim etait le 3eme argument, par defaut a true)
3162
	if (!is_array($options)) {
3163
		$options = array('trim' => $options);
3164
	}
3165
	if (!isset($options['trim'])) {
3166
		$options['trim'] = true;
3167
	}
3168
3169
	if (isset($contexte['connect'])) {
3170
		$connect = $contexte['connect'];
3171
		unset($contexte['connect']);
3172
	}
3173
3174
	$texte = "";
3175
	$pages = array();
3176
	$lang_select = '';
3177 View Code Duplication
	if (!isset($options['etoile']) or !$options['etoile']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3178
		// Si on a inclus sans fixer le critere de lang, on prend la langue courante
3179
		if (!isset($contexte['lang'])) {
3180
			$contexte['lang'] = $GLOBALS['spip_lang'];
3181
		}
3182
3183
		if ($contexte['lang'] != $GLOBALS['meta']['langue_site']) {
3184
			$lang_select = lang_select($contexte['lang']);
3185
		}
3186
	}
3187
3188
	if (!isset($GLOBALS['_INC_PUBLIC'])) {
3189
		$GLOBALS['_INC_PUBLIC'] = 0;
3190
	}
3191
3192
	$GLOBALS['_INC_PUBLIC']++;
3193
3194
	// fix #4235
3195
	$cache_utilise_session_appelant	= (isset($GLOBALS['cache_utilise_session']) ? $GLOBALS['cache_utilise_session'] : null);
3196
3197
3198
	foreach (is_array($fond) ? $fond : array($fond) as $f) {
3199
		
3200
		unset($GLOBALS['cache_utilise_session']);	// fix #4235
3201
3202
		$page = evaluer_fond($f, $contexte, $connect);
3203
		if ($page === '') {
3204
			$c = isset($options['compil']) ? $options['compil'] : '';
3205
			$a = array('fichier' => $f);
3206
			$erreur = _T('info_erreur_squelette2', $a); // squelette introuvable
3207
			erreur_squelette($erreur, $c);
3208
			// eviter des erreurs strictes ensuite sur $page['cle'] en PHP >= 5.4
3209
			$page = array('texte' => '', 'erreur' => $erreur);
3210
		}
3211
3212
		$page = pipeline('recuperer_fond', array(
3213
			'args' => array('fond' => $f, 'contexte' => $contexte, 'options' => $options, 'connect' => $connect),
3214
			'data' => $page
3215
		));
3216
		if (isset($options['ajax']) and $options['ajax']) {
3217
			if (!function_exists('encoder_contexte_ajax')) {
3218
				include_spip('inc/filtres');
3219
			}
3220
			$page['texte'] = encoder_contexte_ajax(
3221
				array_merge(
3222
					$contexte,
3223
					array('fond' => $f),
3224
					($connect ? array('connect' => $connect) : array())
3225
				),
3226
				'',
3227
				$page['texte'],
3228
				$options['ajax']
3229
			);
3230
		}
3231
3232
		if (isset($options['raw']) and $options['raw']) {
3233
			$pages[] = $page;
3234
		} else {
3235
			$texte .= $options['trim'] ? rtrim($page['texte']) : $page['texte'];
3236
		}
3237
		
3238
		// contamination de la session appelante, pour les inclusions statiques
3239
		if (isset($page['invalideurs']['session'])){
3240
			$cache_utilise_session_appelant = $page['invalideurs']['session'];
3241
		}
3242
	}
3243
3244
	// restaurer le sessionnement du contexte appelant, 
3245
	// éventuellement contaminé si on vient de récupérer une inclusion statique sessionnée
3246
	if (isset($cache_utilise_session_appelant)) {
3247
		$GLOBALS['cache_utilise_session'] = $cache_utilise_session_appelant;
3248
	}
3249
3250
	$GLOBALS['_INC_PUBLIC']--;
3251
3252
	if ($lang_select) {
3253
		lang_select();
3254
	}
3255
	if (isset($options['raw']) and $options['raw']) {
3256
		return is_array($fond) ? $pages : reset($pages);
3257
	} else {
3258
		return $options['trim'] ? ltrim($texte) : $texte;
3259
	}
3260
}
3261
3262
/**
3263
 * Trouve un squelette dans le repertoire modeles/
3264
 *
3265
 * @param  $nom
3266
 * @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...
3267
 */
3268
function trouve_modele($nom) {
3269
	return trouver_fond($nom, 'modeles/');
3270
}
3271
3272
/**
3273
 * Trouver un squelette dans le chemin
3274
 * on peut specifier un sous-dossier dans $dir
3275
 * si $pathinfo est a true, retourne un tableau avec
3276
 * les composantes du fichier trouve
3277
 * + le chemin complet sans son extension dans fond
3278
 *
3279
 * @param string $nom
3280
 * @param string $dir
3281
 * @param bool $pathinfo
3282
 * @return array|string
3283
 */
3284
function trouver_fond($nom, $dir = '', $pathinfo = false) {
3285
	$f = find_in_path($nom . '.' . _EXTENSION_SQUELETTES, $dir ? rtrim($dir, '/') . '/' : '');
3286
	if (!$pathinfo) {
3287
		return $f;
3288
	}
3289
	// renvoyer un tableau detaille si $pathinfo==true
3290
	$p = pathinfo($f);
3291
	if (!isset($p['extension']) or !$p['extension']) {
3292
		$p['extension'] = _EXTENSION_SQUELETTES;
3293
	}
3294
	if (!isset($p['extension']) or !$p['filename']) {
3295
		$p['filename'] = ($p['basename'] ? substr($p['basename'], 0, -strlen($p['extension']) - 1) : '');
3296
	}
3297
	$p['fond'] = ($f ? substr($f, 0, -strlen($p['extension']) - 1) : '');
3298
3299
	return $p;
3300
}
3301
3302
/**
3303
 * Teste, pour un nom de page de l'espace privé, s'il est possible
3304
 * de générer son contenu.
3305
 *
3306
 * Dans ce cas, on retourne la fonction d'exécution correspondante à utiliser
3307
 * (du répertoire `ecrire/exec`). Deux cas particuliers et prioritaires :
3308
 * `fond` ou `fond_monobloc` sont retournés si des squelettes existent.
3309
 *
3310
 * - `fond` : pour des squelettes de `prive/squelettes/contenu`
3311
 *          ou pour des objets éditoriaux dont les suqelettes seront échaffaudés
3312
 * - `fond_monobloc` (compatibilité avec SPIP 2.1) : pour des squelettes de `prive/exec`
3313
 *
3314
 * @param string $nom
3315
 *     Nom de la page
3316
 * @return string
3317
 *     Nom de l'exec, sinon chaîne vide.
3318
 **/
3319
function tester_url_ecrire($nom) {
3320
	static $exec = array();
3321
	if (isset($exec[$nom])) {
3322
		return $exec[$nom];
3323
	}
3324
	// tester si c'est une page en squelette
3325
	if (trouver_fond($nom, 'prive/squelettes/contenu/')) {
3326
		return $exec[$nom] = 'fond';
3327
	} // compat skels orthogonaux version precedente
3328
	elseif (trouver_fond($nom, 'prive/exec/')) {
3329
		return $exec[$nom] = 'fond_monobloc';
3330
	} // echafaudage d'un fond !
3331
	elseif (include_spip('public/styliser_par_z') and z_echafaudable($nom)) {
3332
		return $exec[$nom] = 'fond';
3333
	}
3334
	// attention, il ne faut pas inclure l'exec ici
3335
	// car sinon #URL_ECRIRE provoque des inclusions
3336
	// et des define intrusifs potentiels
3337
	return $exec[$nom] = ((find_in_path("{$nom}.php", 'exec/') or charger_fonction($nom, 'exec', true)) ? $nom : '');
3338
}
3339
3340
3341
/**
3342
 * Teste la présence d’une extension PHP
3343
 *
3344
 * @deprected Utiliser directement la fonction native `extension_loaded($module)`
3345
 * @example
3346
 *     ```
3347
 *     $ok = charger_php_extension('sqlite');
3348
 *     ```
3349
 * @param string $module Nom du module à charger
3350
 * @return bool true si le module est chargé
3351
 **/
3352
function charger_php_extension($module) {
3353
	if (extension_loaded($module)) {
0 ignored issues
show
Unused Code introduced by
This if statement, and the following return statement can be replaced with return extension_loaded($module);.
Loading history...
3354
		return true;
3355
	}
3356
	return false;
3357
}
3358
3359
3360
/**
3361
 * Indique si le code HTML5 est permis sur le site public
3362
 *
3363
 * @return bool
3364
 *     true si et seulement si la configuration autorise le code HTML5 sur le site public
3365
 **/
3366
function html5_permis() {
3367
	return (isset($GLOBALS['meta']['version_html_max'])
3368
		and ('html5' == $GLOBALS['meta']['version_html_max']));
3369
}
3370
3371
/**
3372
 * Lister les formats image acceptes par les lib et fonctions images
3373
 * @param bool $gd
3374
 * @param bool $svg_allowed
3375
 * @return array
3376
 */
3377
function formats_image_acceptables($gd = false, $svg_allowed = true) {
3378
	$config = ($gd ? "gd_formats" : "formats_graphiques");
3379
	$formats = (isset($GLOBALS['meta'][$config]) ? $GLOBALS['meta'][$config] : 'png,gif,jpg');
3380
	$formats = explode(',', $formats);
3381
	$formats = array_filter($formats);
3382
	$formats = array_map('trim', $formats);
3383
3384
	if ($svg_allowed) {
3385
		$formats[] = 'svg';
3386
	}
3387
3388
	return $formats;
3389
}
3390
3391
/**
3392
 * Extension de la fonction getimagesize pour supporter aussi les images SVG
3393
 * @param string $fichier
3394
 * @return array|bool
3395
 */
3396
function spip_getimagesize($fichier) {
3397
	if (!$imagesize = @getimagesize($fichier)) {
3398
3399
		include_spip("inc/svg");
3400
		if ($attrs = svg_lire_attributs($fichier)) {
3401
			$width = 350; // default width
3402
			$height = 150; // default height
3403
			if (isset($attrs['width'])
3404
			  and $w = svg_dimension_to_pixels($attrs['width'])
3405
				and isset($attrs['height'])
3406
			  and $h = svg_dimension_to_pixels($attrs['height'])) {
3407
				$width = $w;
3408
				$height = $h;
3409
			}
3410
			elseif (isset($attrs['viewBox'])) {
3411
				$viewbox = trim($attrs['viewBox']);
3412
				$viewbox = preg_replace(",\s+,", " ", $viewbox);
3413
				$viewbox = explode(" ", $viewbox);
3414
				if (count($viewbox) === 4
3415
				  and $w = svg_dimension_to_pixels($viewbox[2])
3416
				  and $h = svg_dimension_to_pixels($viewbox[3])) {
3417
					$width = $w;
3418
					$height = $h;
3419
				}
3420
			}
3421
			$imagesize = [
3422
				$width,
3423
				$height,
3424
				IMAGETYPE_SVG,
3425
				"width=\"{$width}\" height=\"{$height}\"",
3426
				"mime" => "image/svg+xml"
3427
			];
3428
		}
3429
	}
3430
	return $imagesize;
3431
}
3432
3433
3434
3435
/*
3436
 * Bloc de compatibilite : quasiment tous les plugins utilisent ces fonctions
3437
 * desormais depreciees ; plutot que d'obliger tout le monde a charger
3438
 * vieilles_defs, on va assumer l'histoire de ces 3 fonctions ubiquitaires
3439
 */
3440
3441
/**
3442
 * lire_meta : fonction dépréciée
3443
 *
3444
 * @deprecated Utiliser `$GLOBALS['meta'][$nom]` ou `lire_config('nom')`
3445
 * @see lire_config()
3446
 * @param string $nom Clé de meta à lire
3447
 * @return mixed Valeur de la meta.
3448
 **/
3449
function lire_meta($nom) {
3450
	return isset($GLOBALS['meta'][$nom]) ? $GLOBALS['meta'][$nom] : null;
3451
}
3452
3453
3454
/**
3455
 * ecrire_metas : fonction dépréciée
3456
 *
3457
 * @deprecated
3458
 **/
3459
function ecrire_metas() { }
3460
3461
/**
3462
 * Poser une alerte qui sera affiche aux auteurs de bon statut ('' = tous)
3463
 * au prochain passage dans l'espace prive
3464
 * chaque alerte doit avoir un nom pour eviter duplication a chaque hit
3465
 * les alertes affichees une fois sont effacees
3466
 *
3467
 * @param string $nom
3468
 * @param string $message
3469
 * @param string $statut
3470
 */
3471
function avertir_auteurs($nom, $message, $statut = '') {
3472
	$alertes = $GLOBALS['meta']['message_alertes_auteurs'];
3473
	if (!$alertes
3474
		or !is_array($alertes = unserialize($alertes))
3475
	) {
3476
		$alertes = array();
3477
	}
3478
3479
	if (!isset($alertes[$statut])) {
3480
		$alertes[$statut] = array();
3481
	}
3482
	$alertes[$statut][$nom] = $message;
3483
	ecrire_meta("message_alertes_auteurs", serialize($alertes));
3484
}
3485
3486
if (PHP_VERSION_ID < 50500) {
3487
	if (!function_exists('array_column')) {
3488
		/**
3489
		 * Returns the values from a single column of the input array, identified by
3490
		 * the $columnKey.
3491
		 *
3492
		 * Optionally, you may provide an $indexKey to index the values in the returned
3493
		 * array by the values from the $indexKey column in the input array.
3494
		 *
3495
		 * @link http://php.net/manual/fr/function.array-column.php
3496
		 * @link https://github.com/ramsey/array_column/blob/master/src/array_column.php
3497
		 * @copyright Copyright (c) Ben Ramsey (http://benramsey.com)
3498
		 * @license http://opensource.org/licenses/MIT MIT
3499
		 *
3500
		 * @param array $input A multi-dimensional array (record set) from which to pull
0 ignored issues
show
Documentation introduced by
Should the type for parameter $input not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

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

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

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

Loading history...
3501
		 *                     a column of values.
3502
		 * @param mixed $columnKey The column of values to return. This value may be the
3503
		 *                         integer key of the column you wish to retrieve, or it
3504
		 *                         may be the string key name for an associative array.
3505
		 * @param mixed $indexKey (Optional.) The column to use as the index/keys for
3506
		 *                        the returned array. This value may be the integer key
3507
		 *                        of the column, or it may be the string key name.
3508
		 * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be null|false|array? Also, consider making the array more specific, something like array<String>, or String[].

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

If the return type contains the type array, this check recommends the use of a more specific type like String[] or array<String>.

Loading history...
3509
		 */
3510
		function array_column($input = null, $columnKey = null, $indexKey = null)
0 ignored issues
show
Unused Code introduced by
The parameter $input is not used and could be removed.

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

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

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

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

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

Loading history...
3511
		{
3512
			// Using func_get_args() in order to check for proper number of
3513
			// parameters and trigger errors exactly as the built-in array_column()
3514
			// does in PHP 5.5.
3515
			$argc = func_num_args();
3516
			$params = func_get_args();
3517
3518
			if ($argc < 2) {
3519
				trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
3520
				return null;
3521
			}
3522
3523
			if (!is_array($params[0])) {
3524
				trigger_error(
3525
					'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
3526
					E_USER_WARNING
3527
				);
3528
				return null;
3529
			}
3530
3531 View Code Duplication
			if (!is_int($params[1])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3532
				&& !is_float($params[1])
3533
				&& !is_string($params[1])
3534
				&& $params[1] !== null
3535
				&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
3536
			) {
3537
				trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
3538
				return false;
3539
			}
3540
3541 View Code Duplication
			if (isset($params[2])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
3542
				&& !is_int($params[2])
3543
				&& !is_float($params[2])
3544
				&& !is_string($params[2])
3545
				&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
3546
			) {
3547
				trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
3548
				return false;
3549
			}
3550
3551
			$paramsInput = $params[0];
3552
			$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
3553
3554
			$paramsIndexKey = null;
3555
			if (isset($params[2])) {
3556
				if (is_float($params[2]) || is_int($params[2])) {
3557
					$paramsIndexKey = (int) $params[2];
3558
				} else {
3559
					$paramsIndexKey = (string) $params[2];
3560
				}
3561
			}
3562
3563
			$resultArray = array();
3564
3565
			foreach ($paramsInput as $row) {
3566
				$key = $value = null;
3567
				$keySet = $valueSet = false;
3568
3569
				if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
3570
					$keySet = true;
3571
					$key = (string) $row[$paramsIndexKey];
3572
				}
3573
3574
				if ($paramsColumnKey === null) {
3575
					$valueSet = true;
3576
					$value = $row;
3577
				} elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
3578
					$valueSet = true;
3579
					$value = $row[$paramsColumnKey];
3580
				}
3581
3582
				if ($valueSet) {
3583
					if ($keySet) {
3584
						$resultArray[$key] = $value;
3585
					} else {
3586
						$resultArray[] = $value;
3587
					}
3588
				}
3589
3590
			}
3591
3592
			return $resultArray;
3593
		}
3594
3595
	}
3596
}
3597