Completed
Push — master ( 230805...a75156 )
by cam
05:02
created

utils.php ➔ spip_getimagesize()   B

Complexity

Conditions 11
Paths 6

Size

Total Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
nc 6
nop 1
dl 0
loc 36
rs 7.3166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

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

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

Commonly applied refactorings include:

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