Completed
Push — master ( 6b056b...712138 )
by cam
08:09
created

utils.php ➔ array_column()   D

Complexity

Conditions 27
Paths 118

Size

Total Lines 84
Code Lines 52

Duplication

Lines 18
Ratio 21.43 %

Importance

Changes 0
Metric Value
cc 27
eloc 52
nc 118
nop 3
dl 18
loc 84
rs 4.5372
c 0
b 0
f 0

How to fix   Long Method    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-2017                                                *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Utilitaires indispensables autour du serveur Http.
15
 *
16
 * @package SPIP\Core\Utilitaires
17
 **/
18
19
if (!defined('_ECRIRE_INC_VERSION')) {
20
	return;
21
}
22
23
24
/**
25
 * Cherche une fonction surchargeable et en retourne le nom exact,
26
 * après avoir chargé le fichier la contenant si nécessaire.
27
 *
28
 * Charge un fichier (suivant les chemins connus) et retourne si elle existe
29
 * le nom de la fonction homonyme `$dir_$nom`, ou suffixé `$dir_$nom_dist`
30
 *
31
 * Peut être appelé plusieurs fois, donc optimisé.
32
 *
33
 * @api
34
 * @uses include_spip() Pour charger le fichier
35
 * @example
36
 *     ```
37
 *     $envoyer_mail = charger_fonction('envoyer_mail', 'inc');
38
 *     $envoyer_mail($email, $sujet, $texte);
39
 *     ```
40
 *
41
 * @param string $nom
42
 *     Nom de la fonction (et du fichier)
43
 * @param string $dossier
44
 *     Nom du dossier conteneur
45
 * @param bool $continue
46
 *     true pour ne pas râler si la fonction n'est pas trouvée
47
 * @return string
48
 *     Nom de la fonction, ou false.
49
 */
50
function charger_fonction($nom, $dossier = 'exec', $continue = false) {
51
	static $echecs = array();
52
53
	if (strlen($dossier) and substr($dossier, -1) != '/') {
54
		$dossier .= '/';
55
	}
56
	$f = str_replace('/', '_', $dossier) . $nom;
57
58
	if (function_exists($f)) {
59
		return $f;
60
	}
61
	if (function_exists($g = $f . '_dist')) {
62
		return $g;
63
	}
64
65
	if (isset($echecs[$f])) {
66
		return $echecs[$f];
67
	}
68
	// Sinon charger le fichier de declaration si plausible
69
70
	if (!preg_match(',^\w+$,', $f)) {
71
		if ($continue) {
72
			return false;
73
		} //appel interne, on passe
74
		include_spip('inc/minipres');
75
		echo minipres();
76
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function charger_fonction() contains an exit expression.

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

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

Loading history...
77
	}
78
79
	// passer en minuscules (cf les balises de formulaires)
80
	// et inclure le fichier
81
	if (!$inc = include_spip($dossier . ($d = strtolower($nom)))
82
		// si le fichier truc/machin/nom.php n'existe pas,
83
		// la fonction peut etre definie dans truc/machin.php qui regroupe plusieurs petites fonctions
84
		and strlen(dirname($dossier)) and dirname($dossier) != '.'
85
	) {
86
		include_spip(substr($dossier, 0, -1));
87
	}
88
	if (function_exists($f)) {
89
		return $f;
90
	}
91
	if (function_exists($g)) {
92
		return $g;
93
	}
94
95
	if ($continue) {
96
		return $echecs[$f] = false;
97
	}
98
99
	// Echec : message d'erreur
100
	spip_log("fonction $nom ($f ou $g) indisponible" .
101
		($inc ? "" : " (fichier $d absent de $dossier)"));
102
103
	include_spip('inc/minipres');
104
	echo minipres(_T('forum_titre_erreur'),
105
		_T('fichier_introuvable', array('fichier' => '<b>' . spip_htmlentities($d) . '</b>')),
106
		array('all_inline'=>true,'status'=>404));
107
	exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function charger_fonction() contains an exit expression.

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

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

Loading history...
108
}
109
110
/**
111
 * Inclusion unique avec verification d'existence du fichier + log en crash sinon
112
 *
113
 * @param string $file
114
 * @return bool
115
 */
116
function include_once_check($file) {
117
	if (file_exists($file)) {
118
		include_once $file;
119
120
		return true;
121
	}
122
	$crash = (isset($GLOBALS['meta']['message_crash_plugins']) ? unserialize($GLOBALS['meta']['message_crash_plugins']) : '');
123
	$crash = ($crash ? $crash : array());
124
	$crash[$file] = true;
125
	ecrire_meta('message_crash_plugins', serialize($crash));
126
127
	return false;
128
}
129
130
131
/**
132
 * Inclut un fichier PHP (en le cherchant dans les chemins)
133
 *
134
 * @api
135
 * @uses find_in_path()
136
 * @example
137
 *     ```
138
 *     include_spip('inc/texte');
139
 *     ```
140
 *
141
 * @param string $f
142
 *     Nom du fichier (sans l'extension)
143
 * @param bool $include
144
 *     - true pour inclure le fichier,
145
 *     - false ne fait que le chercher
146
 * @return string|bool
147
 *     - false : fichier introuvable
148
 *     - string : chemin du fichier trouvé
149
 **/
150
function include_spip($f, $include = true) {
151
	return find_in_path($f . '.php', '', $include);
152
}
153
154
/**
155
 * Requiert un fichier PHP (en le cherchant dans les chemins)
156
 *
157
 * @uses find_in_path()
158
 * @see  include_spip()
159
 * @example
160
 *     ```
161
 *     require_spip('inc/texte');
162
 *     ```
163
 *
164
 * @param string $f
165
 *     Nom du fichier (sans l'extension)
166
 * @return string|bool
167
 *     - false : fichier introuvable
168
 *     - string : chemin du fichier trouvé
169
 **/
170
function require_spip($f) {
171
	return find_in_path($f . '.php', '', 'required');
172
}
173
174
/**
175
 * Exécute une fonction (appellée par un pipeline) avec la donnée transmise.
176
 *
177
 * Un pipeline est lie a une action et une valeur
178
 * chaque element du pipeline est autorise a modifier la valeur
179
 * le pipeline execute les elements disponibles pour cette action,
180
 * les uns apres les autres, et retourne la valeur finale
181
 *
182
 * Cf. compose_filtres dans references.php, qui est la
183
 * version compilee de cette fonctionnalite
184
 * appel unitaire d'une fonction du pipeline
185
 * utilisee dans le script pipeline precompile
186
 *
187
 * on passe $val par reference pour limiter les allocations memoire
188
 *
189
 * @param string $fonc
190
 *     Nom de la fonction appelée par le pipeline
191
 * @param string|array $val
192
 *     Les paramètres du pipeline, son environnement
193
 * @return string|array $val
194
 *     Les paramètres du pipeline modifiés
195
 **/
196
function minipipe($fonc, &$val) {
197
	// fonction
198
	if (function_exists($fonc)) {
199
		$val = call_user_func($fonc, $val);
200
	} // Class::Methode
201
	else {
202
		if (preg_match("/^(\w*)::(\w*)$/S", $fonc, $regs)
203
			and $methode = array($regs[1], $regs[2])
204
			and is_callable($methode)
205
		) {
206
			$val = call_user_func($methode, $val);
207
		} else {
208
			spip_log("Erreur - '$fonc' non definie !");
209
		}
210
	}
211
212
	return $val;
213
}
214
215
/**
216
 * Appel d’un pipeline
217
 *
218
 * Exécute le pipeline souhaité, éventuellement avec des données initiales.
219
 * Chaque plugin qui a demandé à voir ce pipeline vera sa fonction spécifique appelée.
220
 * Les fonctions (des plugins) appelées peuvent modifier à leur guise le contenu.
221
 *
222
 * Deux types de retours. Si `$val` est un tableau de 2 éléments, avec une clé `data`
223
 * on retourne uniquement ce contenu (`$val['data']`) sinon on retourne tout `$val`.
224
 *
225
 *
226
 * @example
227
 *     Appel du pipeline `pre_insertion`
228
 *     ```
229
 *     $champs = pipeline('pre_insertion', array(
230
 *         'args' => array('table' => 'spip_articles'),
231
 *         'data' => $champs
232
 *     ));
233
 *     ```
234
 *
235
 * @param string $action
236
 *     Nom du pipeline
237
 * @param null|string|array $val
238
 *     Données à l’entrée du pipeline
239
 * @return mixed|null
240
 *     Résultat
241
 */
242
function pipeline($action, $val = null) {
243
	static $charger;
244
245
	// chargement initial des fonctions mises en cache, ou generation du cache
246
	if (!$charger) {
247
		if (!($ok = @is_readable($charger = _CACHE_PIPELINES))) {
248
			include_spip('inc/plugin');
249
			// generer les fichiers php precompiles
250
			// de chargement des plugins et des pipelines
251
			actualise_plugins_actifs();
252
			if (!($ok = @is_readable($charger))) {
253
				spip_log("fichier $charger pas cree");
254
			}
255
		}
256
257
		if ($ok) {
258
			include_once $charger;
259
		}
260
	}
261
262
	// appliquer notre fonction si elle existe
263
	$fonc = 'execute_pipeline_' . strtolower($action);
264
	if (function_exists($fonc)) {
265
		$val = $fonc($val);
266
	} // plantage ?
267
	else {
268
		spip_log("fonction $fonc absente : pipeline desactive", _LOG_ERREUR);
269
	}
270
271
	// si le flux est une table avec 2 cle args&data
272
	// on ne ressort du pipe que les donnees dans 'data'
273
	// array_key_exists pour php 4.1.0
274
	if (is_array($val)
275
		and count($val) == 2
276
		and (array_key_exists('data', $val))
277
	) {
278
		$val = $val['data'];
279
	}
280
281
	return $val;
282
}
283
284
/**
285
 * Enregistrement des événements
286
 *
287
 * Signature : `spip_log(message[,niveau|type|type.niveau])`
288
 *
289
 * Le niveau de log par défaut est la valeur de la constante `_LOG_INFO`
290
 *
291
 * Les différents niveaux possibles sont :
292
 *
293
 * - `_LOG_HS` : écrira 'HS' au début de la ligne logguée
294
 * - `_LOG_ALERTE_ROUGE` : 'ALERTE'
295
 * - `_LOG_CRITIQUE` :  'CRITIQUE'
296
 * - `_LOG_ERREUR` : 'ERREUR'
297
 * - `_LOG_AVERTISSEMENT` : 'WARNING'
298
 * - `_LOG_INFO_IMPORTANTE` : '!INFO'
299
 * - `_LOG_INFO` : 'info'
300
 * - `_LOG_DEBUG` : 'debug'
301
 *
302
 * @example
303
 *   ```
304
 *   spip_log($message)
305
 *   spip_log($message, 'recherche')
306
 *   spip_log($message, _LOG_DEBUG)
307
 *   spip_log($message, 'recherche.'._LOG_DEBUG)
308
 *   ```
309
 *
310
 * @api
311
 * @link http://programmer.spip.net/spip_log
312
 * @uses inc_log_dist()
313
 *
314
 * @param string $message
0 ignored issues
show
Documentation introduced by
Should the type for parameter $message not be string|null?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    return array();
}

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

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

Loading history...
689
	
690
	$url = str_replace(array('[', ']'), array('%5B', '%5D'), $url);
691
692
	// &amp; ?
693
	if ($amp != '&amp;') {
694
		$url = str_replace('&amp;', $amp, $url);
695
	}
696
697
	// Si ca demarre par ? ou vide, donner './'
698
	$url = preg_replace(',^([?].*)?$,', './\1', $url);
699
700
	return $url;
701
}
702
703
704
/**
705
 * Indique si on est dans l'espace prive
706
 *
707
 * @return bool
708
 *     true si c'est le cas, false sinon.
709
 */
710
function test_espace_prive() {
711
	return defined('_ESPACE_PRIVE') ? _ESPACE_PRIVE : false;
712
}
713
714
/**
715
 * Vérifie la présence d'un plugin actif, identifié par son préfixe
716
 *
717
 * @param string $plugin
718
 * @return bool
719
 */
720
function test_plugin_actif($plugin) {
721
	return ($plugin and defined('_DIR_PLUGIN_' . strtoupper($plugin))) ? true : false;
722
}
723
724
/**
725
 * Traduction des textes de SPIP
726
 *
727
 * Traduit une clé de traduction en l'obtenant dans les fichiers de langues.
728
 *
729
 * @api
730
 * @uses inc_traduire_dist()
731
 * @uses _L()
732
 * @example
733
 *     ```
734
 *     _T('bouton_enregistrer')
735
 *     _T('medias:image_tourner_droite')
736
 *     _T('medias:erreurs', array('nb'=>3))
737
 *     _T("email_sujet", array('spip_lang'=>$lang_usager))
738
 *     ```
739
 *
740
 * @param string $texte
741
 *     Clé de traduction
742
 * @param array $args
743
 *     Couples (variable => valeur) pour passer des variables à la chaîne traduite. la variable spip_lang permet de forcer la langue
744
 * @param array $options
745
 *     - string class : nom d'une classe a ajouter sur un span pour encapsuler la chaine
746
 *     - bool force : forcer un retour meme si la chaine n'a pas de traduction
747
 * @return string
748
 *     Texte
749
 */
750
function _T($texte, $args = array(), $options = array()) {
751
	static $traduire = false;
752
	$o = array('class' => '', 'force' => true);
753
	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...
754
		// support de l'ancien argument $class
755
		if (is_string($options)) {
756
			$options = array('class' => $options);
757
		}
758
		$o = array_merge($o, $options);
759
	}
760
761
	if (!$traduire) {
762
		$traduire = charger_fonction('traduire', 'inc');
763
		include_spip('inc/lang');
764
	}
765
766
	// On peut passer explicitement la langue dans le tableau
767
	// On utilise le même nom de variable que la globale
768 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...
769
		$lang = $args['spip_lang'];
770
		// On l'enleve pour ne pas le passer au remplacement
771
		unset($args['spip_lang']);
772
	} // Sinon on prend la langue du contexte
773
	else {
774
		$lang = $GLOBALS['spip_lang'];
775
	}
776
	$text = $traduire($texte, $lang);
777
778
	if (!strlen($text)) {
779
		if (!$o['force']) {
780
			return '';
781
		}
782
783
		$text = $texte;
784
785
		// pour les chaines non traduites, assurer un service minimum
786
		if (!$GLOBALS['test_i18n'] and (_request('var_mode') != 'traduction')) {
787
			$text = str_replace('_', ' ',
788
				(($n = strpos($text, ':')) === false ? $texte :
789
					substr($texte, $n + 1)));
790
		}
791
		$o['class'] = null;
792
793
	}
794
795
	return _L($text, $args, $o['class']);
796
797
}
798
799
800
/**
801
 * Remplace les variables `@...@` par leur valeur dans une chaîne de langue.
802
 *
803
 * Cette fonction est également appelée dans le code source de SPIP quand une
804
 * chaîne n'est pas encore dans les fichiers de langue.
805
 *
806
 * @see _T()
807
 * @example
808
 *     ```
809
 *     _L('Texte avec @nb@ ...', array('nb'=>3)
810
 *     ```
811
 *
812
 * @param string $text
813
 *     Texte
814
 * @param array $args
815
 *     Couples (variable => valeur) à transformer dans le texte
816
 * @param string|null $class
817
 *     Encapsule les valeurs dans un span avec cette classe si transmis.
818
 * @return string
819
 *     Texte
820
 */
821
function _L($text, $args = array(), $class = null) {
822
	$f = $text;
823
	if (is_array($args)) {
824
		foreach ($args as $name => $value) {
825
			if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

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

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

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

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

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

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

Loading history...
1513
		}
1514
	}
1515
1516 View Code Duplication
	if (!defined('_SAUVER_CHEMIN')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1517
		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
1518
		if (is_null($GLOBALS['path_files'])) {
1519
			return false;
1520
		}
1521
		define('_SAUVER_CHEMIN', true);
1522
	}
1523
1524
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
1525
}
1526
1527
function clear_path_cache() {
1528
	$GLOBALS['path_files'] = array();
1529
	spip_unlink(_CACHE_CHEMIN);
1530
}
1531
1532
function load_path_cache() {
1533
	// charger le path des plugins
1534
	if (@is_readable(_CACHE_PLUGINS_PATH)) {
1535
		include_once(_CACHE_PLUGINS_PATH);
1536
	}
1537
	$GLOBALS['path_files'] = array();
1538
	// si le visiteur est admin,
1539
	// on ne recharge pas le cache pour forcer sa mise a jour
1540
	if (
1541
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
1542
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
1543
		// utiliser le cookie est un pis aller qui marche 'en general'
1544
		// on blinde par un second test au moment de la lecture de la session
1545
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
1546
		// et en ignorant ce cache en cas de recalcul explicite
1547
	!_request('var_mode')
1548
	) {
1549
		// on essaye de lire directement sans verrou pour aller plus vite
1550
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
1551
			// mais si semble corrompu on relit avec un verrou
1552
			if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1553
				lire_fichier(_CACHE_CHEMIN, $contenu);
1554
				if (!$GLOBALS['path_files'] = unserialize($contenu)) {
1555
					$GLOBALS['path_files'] = array();
1556
				}
1557
			}
1558
		}
1559
	}
1560
}
1561
1562
function save_path_cache() {
1563
	if (defined('_SAUVER_CHEMIN')
1564
		and _SAUVER_CHEMIN
1565
	) {
1566
		ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
1567
	}
1568
}
1569
1570
1571
/**
1572
 * Trouve tous les fichiers du path correspondants à un pattern
1573
 *
1574
 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
1575
 * par un `find_in_path()`
1576
 *
1577
 * @api
1578
 * @uses creer_chemin()
1579
 * @uses preg_files()
1580
 *
1581
 * @param string $dir
1582
 * @param string $pattern
1583
 * @param bool $recurs
1584
 * @return array
1585
 */
1586
function find_all_in_path($dir, $pattern, $recurs = false) {
1587
	$liste_fichiers = array();
1588
	$maxfiles = 10000;
1589
1590
	// cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
1591
	// on a pas encore inclus flock.php
1592
	if (!function_exists('preg_files')) {
1593
		include_once _ROOT_RESTREINT . 'inc/flock.php';
1594
	}
1595
1596
	// Parcourir le chemin
1597
	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...
1598
		$f = $d . $dir;
1599
		if (@is_dir($f)) {
1600
			$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...
1601
			foreach ($liste as $chemin) {
1602
				$nom = basename($chemin);
1603
				// ne prendre que les fichiers pas deja trouves
1604
				// car find_in_path prend le premier qu'il trouve,
1605
				// les autres sont donc masques
1606
				if (!isset($liste_fichiers[$nom])) {
1607
					$liste_fichiers[$nom] = $chemin;
1608
				}
1609
			}
1610
		}
1611
	}
1612
1613
	return $liste_fichiers;
1614
}
1615
1616
/**
1617
 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
1618
 * @param string $nom
1619
 * @return bool
1620
 */
1621
function autoriser_sans_cookie($nom) {
1622
	static $autsanscookie = array('install', 'base_repair');
1623
	$nom = preg_replace('/.php[3]?$/', '', basename($nom));
1624
1625
	return in_array($nom, $autsanscookie);
1626
}
1627
1628
/**
1629
 * Fonction codant et décodant les URLs des objets SQL mis en page par SPIP
1630
 *
1631
 * @api
1632
 * @param string $id
1633
 *   numero de la cle primaire si nombre, URL a decoder si pas numerique
1634
 * @param string $entite
1635
 *   surnom de la table SQL (donne acces au nom de cle primaire)
1636
 * @param string $args
1637
 *   query_string a placer apres cle=$id&....
1638
 * @param string $ancre
1639
 *   ancre a mettre a la fin de l'URL a produire
1640
 * @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...
1641
 *   produire l'URL publique ou privee (par defaut: selon espace)
1642
 *   si string : serveur de base de donnee (nom du connect)
1643
 * @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...
1644
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
1645
 * @return string|array
1646
 *   url codee ou fonction de decodage
1647
 *   array : derogatoire, la fonction d'url retourne (objet,id_objet) utilises par nettoyer_raccourcis_typo() pour generer un lien titre
1648
 *           (cas des raccourcis personalises [->spip20] : il faut implementer une fonction generer_url_spip et une fonction generer_url_ecrire_spip)
1649
 */
1650
function generer_url_entite($id = '', $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
1651
	if ($public === null) {
1652
		$public = !test_espace_prive();
1653
	}
1654
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
1655
1656
	if (!$public) {
1657
		if (!$entite) {
1658
			return '';
1659
		}
1660
		if (!function_exists('generer_url_ecrire_objet')) {
1661
			include_spip('inc/urls');
1662
		}
1663
		$res = generer_url_ecrire_objet($entite, $id, $args, $ancre, false);
1664
	} else {
1665
		if ($type === null) {
1666
			$type = (isset($GLOBALS['type_urls']))
1667
				? $GLOBALS['type_urls'] // pour surcharge via fichier d'options
1668
				: ((isset($GLOBALS['meta']['type_urls'])) // sinon la config url_etendues
1669
					? ($GLOBALS['meta']['type_urls']) : "page"); // sinon type "page" par défaut
1670
		}
1671
1672
		$f = charger_fonction($type, 'urls', true);
1673
		// se rabattre sur les urls page si les urls perso non dispo
1674
		if (!$f) {
1675
			$f = charger_fonction('page', 'urls', true);
1676
		}
1677
1678
		// si $entite='', on veut la fonction de passage URL ==> id
1679
		// sinon on veut effectuer le passage id ==> URL
1680
		if (!$entite) {
1681
			return $f;
1682
		}
1683
1684
		// mais d'abord il faut tester le cas des urls sur une
1685
		// base distante
1686
		if (is_string($public)
1687
			and $g = charger_fonction('connect', 'urls', true)
1688
		) {
1689
			$f = $g;
1690
		}
1691
1692
		$res = $f(intval($id), $entite, $args, $ancre, $public);
1693
1694
	}
1695
	if ($res) {
1696
		return $res;
1697
	}
1698
	// Sinon c'est un raccourci ou compat SPIP < 2
1699
	if (!function_exists($f = 'generer_url_' . $entite)) {
1700
		if (!function_exists($f .= '_dist')) {
1701
			$f = '';
1702
		}
1703
	}
1704
	if ($f) {
1705
		$url = $f($id, $args, $ancre);
1706
		if (strlen($args)) {
1707
			$url .= strstr($url, '?')
1708
				? '&amp;' . $args
1709
				: '?' . $args;
1710
		}
1711
1712
		return $url;
1713
	}
1714
	// On a ete gentil mais la ....
1715
	spip_log("generer_url_entite: entite $entite ($f) inconnue $type $public");
1716
1717
	return '';
1718
}
1719
1720
function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
1721
	$exec = objet_info($entite, 'url_edit');
1722
	$url = generer_url_ecrire($exec, $args);
1723
	if (intval($id)) {
1724
		$url = parametre_url($url, id_table_objet($entite), $id);
1725
	} else {
1726
		$url = parametre_url($url, 'new', 'oui');
1727
	}
1728
	if ($ancre) {
1729
		$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...
1730
	}
1731
1732
	return $url;
1733
}
1734
1735
// http://code.spip.net/@urls_connect_dist
1736
function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
1737
	include_spip('base/connect_sql');
1738
	$id_type = id_table_objet($entite, $public);
1739
1740
	return _DIR_RACINE . get_spip_script('./')
1741
	. "?" . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
1742
	. (!$args ? '' : "&$args")
1743
	. (!$ancre ? '' : "#$ancre");
1744
}
1745
1746
1747
/**
1748
 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
1749
 *
1750
 * @param string $url
1751
 * @return string
1752
 */
1753
function urlencode_1738($url) {
1754
	if (preg_match(',[^\x00-\x7E],sS', $url)) {
1755
		$uri = '';
1756
		for ($i = 0; $i < strlen($url); $i++) {
1757
			if (ord($a = $url[$i]) > 127) {
1758
				$a = rawurlencode($a);
1759
			}
1760
			$uri .= $a;
1761
		}
1762
		$url = $uri;
1763
	}
1764
1765
	return quote_amp($url);
1766
}
1767
1768
// http://code.spip.net/@generer_url_entite_absolue
1769
function generer_url_entite_absolue($id = '', $entite = '', $args = '', $ancre = '', $connect = null) {
1770
	if (!$connect) {
1771
		$connect = true;
1772
	}
1773
	$h = generer_url_entite($id, $entite, $args, $ancre, $connect);
1774
	if (!preg_match(',^\w+:,', $h)) {
1775
		include_spip('inc/filtres_mini');
1776
		$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...
1777
	}
1778
1779
	return $h;
1780
}
1781
1782
1783
/**
1784
 * Tester qu'une variable d'environnement est active
1785
 *
1786
 * Sur certains serveurs, la valeur 'Off' tient lieu de false dans certaines
1787
 * variables d'environnement comme $_SERVER[HTTPS] ou ini_get(register_globals)
1788
 *
1789
 * @param string|bool $truc
1790
 *     La valeur de la variable d'environnement
1791
 * @return bool
1792
 *     true si la valeur est considérée active ; false sinon.
1793
 **/
1794
function test_valeur_serveur($truc) {
1795
	if (!$truc) {
1796
		return false;
1797
	}
1798
1799
	return (strtolower($truc) !== 'off');
1800
}
1801
1802
//
1803
// Fonctions de fabrication des URL des scripts de Spip
1804
//
1805
/**
1806
 * Calcule l'url de base du site
1807
 *
1808
 * Calcule l'URL de base du site, en priorité sans se fier à la méta (adresse_site) qui
1809
 * peut être fausse (sites avec plusieurs noms d’hôtes, déplacements, erreurs).
1810
 * En dernier recours, lorsqu'on ne trouve rien, on utilise adresse_site comme fallback.
1811
 *
1812
 * @note
1813
 *     La globale `$profondeur_url` doit être initialisée de manière à
1814
 *     indiquer le nombre de sous-répertoires de l'url courante par rapport à la
1815
 *     racine de SPIP : par exemple, sur ecrire/ elle vaut 1, sur sedna/ 1, et à
1816
 *     la racine 0. Sur url/perso/ elle vaut 2
1817
 *
1818
 * @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...
1819
 *    - si non renseignée : retourne l'url pour la profondeur $GLOBALS['profondeur_url']
1820
 *    - si int : indique que l'on veut l'url pour la profondeur indiquée
1821
 *    - si bool : retourne le tableau static complet
1822
 *    - si array : réinitialise le tableau static complet avec la valeur fournie
1823
 * @return string|array
1824
 */
1825
function url_de_base($profondeur = null) {
1826
1827
	static $url = array();
1828
	if (is_array($profondeur)) {
1829
		return $url = $profondeur;
1830
	}
1831
	if ($profondeur === false) {
1832
		return $url;
1833
	}
1834
1835
	if (is_null($profondeur)) {
1836
		$profondeur = $GLOBALS['profondeur_url'];
1837
	}
1838
1839
	if (isset($url[$profondeur])) {
1840
		return $url[$profondeur];
1841
	}
1842
1843
	$http = 'http';
1844
1845
	if (
1846
		isset($_SERVER["SCRIPT_URI"])
1847
		and substr($_SERVER["SCRIPT_URI"], 0, 5) == 'https'
1848
	) {
1849
		$http = 'https';
1850
	} elseif (
1851
		isset($_SERVER['HTTPS'])
1852
		and test_valeur_serveur($_SERVER['HTTPS'])
1853
	) {
1854
		$http = 'https';
1855
	}
1856
1857
	// note : HTTP_HOST contient le :port si necessaire
1858
	$host = $_SERVER['HTTP_HOST'];
1859
	// si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback
1860
	if (is_null($host) and isset($GLOBALS['meta']['adresse_site'])) {
1861
		$host = $GLOBALS['meta']['adresse_site'];
1862
		if ($scheme = parse_url($host, PHP_URL_SCHEME)) {
1863
			$http = $scheme;
1864
			$host = str_replace("{$scheme}://", '', $host);
1865
		}
1866
	}
1867
	if (isset($_SERVER['SERVER_PORT'])
1868
		and $port = $_SERVER['SERVER_PORT']
1869
		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...
1870
	) {
1871
		if (!defined('_PORT_HTTP_STANDARD')) {
1872
			define('_PORT_HTTP_STANDARD', '80');
1873
		}
1874
		if (!defined('_PORT_HTTPS_STANDARD')) {
1875
			define('_PORT_HTTPS_STANDARD', '443');
1876
		}
1877
		if ($http == "http" and !in_array($port, explode(',', _PORT_HTTP_STANDARD))) {
1878
			$host .= ":$port";
1879
		}
1880
		if ($http == "https" and !in_array($port, explode(',', _PORT_HTTPS_STANDARD))) {
1881
			$host .= ":$port";
1882
		}
1883
	}
1884
1885 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...
1886
		if (isset($_SERVER['REQUEST_URI'])) {
1887
			$GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
1888
		} else {
1889
			$GLOBALS['REQUEST_URI'] = $_SERVER['PHP_SELF'];
1890
			if ($_SERVER['QUERY_STRING']
1891
				and !strpos($_SERVER['REQUEST_URI'], '?')
1892
			) {
1893
				$GLOBALS['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
1894
			}
1895
		}
1896
	}
1897
1898
	$url[$profondeur] = url_de_($http, $host, $GLOBALS['REQUEST_URI'], $profondeur);
1899
1900
	return $url[$profondeur];
1901
}
1902
1903
/**
1904
 * fonction testable de construction d'une url appelee par url_de_base()
1905
 *
1906
 * @param string $http
1907
 * @param string $host
1908
 * @param string $request
1909
 * @param int $prof
1910
 * @return string
1911
 */
1912
function url_de_($http, $host, $request, $prof = 0) {
1913
	$prof = max($prof, 0);
1914
1915
	$myself = ltrim($request, '/');
1916
	# supprimer la chaine de GET
1917
	list($myself) = explode('?', $myself);
1918
	// vieux mode HTTP qui envoie après le nom de la methode l'URL compléte
1919
	// protocole, "://", nom du serveur avant le path dans _SERVER["REQUEST_URI"]
1920 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...
1921
		$myself = explode('://',$myself);
1922
		array_shift($myself);
1923
		$myself = implode('://',$myself);
1924
		$myself = explode('/',$myself);
1925
		array_shift($myself);
1926
		$myself = implode('/',$myself);
1927
	}
1928
	$url = join('/', array_slice(explode('/', $myself), 0, -1 - $prof)) . '/';
1929
1930
	$url = $http . '://' . rtrim($host, '/') . '/' . ltrim($url, '/');
1931
1932
	return $url;
1933
}
1934
1935
1936
// Pour une redirection, la liste des arguments doit etre separee par "&"
1937
// Pour du code XHTML, ca doit etre &amp;
1938
// Bravo au W3C qui n'a pas ete capable de nous eviter ca
1939
// faute de separer proprement langage et meta-langage
1940
1941
// Attention, X?y=z et "X/?y=z" sont completement differents!
1942
// http://httpd.apache.org/docs/2.0/mod/mod_dir.html
1943
1944
/**
1945
 * Crée une URL vers un script de l'espace privé
1946
 *
1947
 * @example
1948
 *     ```
1949
 *     generer_url_ecrire('admin_plugin')
1950
 *     ```
1951
 *
1952
 * @param string $script
1953
 *     Nom de la page privée (xx dans exec=xx)
1954
 * @param string $args
1955
 *     Arguments à transmettre, tel que `arg1=yy&arg2=zz`
1956
 * @param bool $no_entities
1957
 *     Si false : transforme les `&` en `&amp;`
1958
 * @param bool|string $rel
1959
 *     URL relative ?
1960
 *
1961
 *     - false : l’URL sera complète et contiendra l’URL du site
1962
 *     - true : l’URL sera relavive.
1963
 *     - string : on transmet l'url à la fonction
1964
 * @return string URL
1965
 **/
1966
function generer_url_ecrire($script = '', $args = "", $no_entities = false, $rel = false) {
1967
	if (!$rel) {
1968
		$rel = url_de_base() . _DIR_RESTREINT_ABS . _SPIP_ECRIRE_SCRIPT;
1969
	} else {
1970
		if (!is_string($rel)) {
1971
			$rel = _DIR_RESTREINT ? _DIR_RESTREINT :
1972
				('./' . _SPIP_ECRIRE_SCRIPT);
1973
		}
1974
	}
1975
1976
	list($script, $ancre) = array_pad(explode('#', $script), 2, null);
1977
	if ($script and ($script <> 'accueil' or $rel)) {
1978
		$args = "?exec=$script" . (!$args ? '' : "&$args");
1979
	} elseif ($args) {
1980
		$args = "?$args";
1981
	}
1982
	if ($ancre) {
1983
		$args .= "#$ancre";
1984
	}
1985
1986
	return $rel . ($no_entities ? $args : str_replace('&', '&amp;', $args));
1987
}
1988
1989
/**
1990
 * Permet d'ajouter lien vers une page privée à un paramètre d'url (déprécié)
1991
 *
1992
 *     ```
1993
 *     // deprecié
1994
 *     $h = generer_url_ecrire('article', "id_article=$id_article&redirect=" . generer_url_retour('articles'));
1995
 *     // utiliser
1996
 *     $h = generer_url_ecrire('article');
1997
 *     $h = parametre_url($h, 'id_article', $id_article);
1998
 *     $h = parametre_url($h, 'redirect', generer_url_ecrire('articles'));
1999
 *     ```
2000
 *
2001
 * @deprecated Utiliser parametre_url() et generer_url_ecrire()
2002
 * @see parametre_url()
2003
 * @see generer_url_ecrire()
2004
 *
2005
 * @param string $script
2006
 * @param string $args
2007
 * @return string
2008
 */
2009
function generer_url_retour($script, $args = "") {
2010
	return rawurlencode(generer_url_ecrire($script, $args, true, true));
2011
}
2012
2013
//
2014
// Adresse des scripts publics (a passer dans inc-urls...)
2015
//
2016
2017
2018
/**
2019
 * Retourne le nom du fichier d'exécution de SPIP
2020
 *
2021
 * @see _SPIP_SCRIPT
2022
 * @note
2023
 *   Detecter le fichier de base, a la racine, comme etant spip.php ou ''
2024
 *   dans le cas de '', un $default = './' peut servir (comme dans urls/page.php)
2025
 *
2026
 * @param string $default
2027
 *     Script par défaut
2028
 * @return string
2029
 *     Nom du fichier (constante _SPIP_SCRIPT), sinon nom par défaut
2030
 **/
2031
function get_spip_script($default = '') {
2032
	# cas define('_SPIP_SCRIPT', '');
2033
	if (_SPIP_SCRIPT) {
2034
		return _SPIP_SCRIPT;
2035
	} else {
2036
		return $default;
2037
	}
2038
}
2039
2040
/**
2041
 * Crée une URL vers une page publique de SPIP
2042
 *
2043
 * @example
2044
 *     ```
2045
 *     generer_url_public("rubrique","id_rubrique=$id_rubrique")
2046
 *     ```
2047
 *
2048
 * @param string $script
2049
 *     Nom de la page
2050
 * @param string|array $args
2051
 *     Arguments à transmettre a l'URL,
2052
 *      soit sous la forme d'un string tel que `arg1=yy&arg2=zz`
2053
 *      soit sous la forme d'un array tel que array( `arg1` => `yy`, `arg2` => `zz` )
2054
 * @param bool $no_entities
2055
 *     Si false : transforme les `&` en `&amp;`
2056
 * @param bool $rel
2057
 *     URL relative ?
2058
 *
2059
 *     - false : l’URL sera complète et contiendra l’URL du site
2060
 *     - true : l’URL sera relavive.
2061
 * @param string $action
2062
 *     - Fichier d'exécution public (spip.php par défaut)
2063
 * @return string URL
2064
 **/
2065
function generer_url_public($script = '', $args = "", $no_entities = false, $rel = true, $action = '') {
2066
	// si le script est une action (spip_pass, spip_inscription),
2067
	// standardiser vers la nouvelle API
2068
2069
	if (!$action) {
2070
		$action = get_spip_script();
2071
	}
2072
	if ($script) {
2073
		$action = parametre_url($action, _SPIP_PAGE, $script, '&');
2074
	}
2075
2076
	if ($args) {
2077
		if (is_array($args)) {
2078
			$r = '';
2079
			foreach ($args as $k => $v) {
2080
				$r .= '&' . $k . '=' . $v;
2081
			}
2082
			$args = substr($r, 1);
2083
		}
2084
		$action .=
2085
			(strpos($action, '?') !== false ? '&' : '?') . $args;
2086
	}
2087
	if (!$no_entities) {
2088
		$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...
2089
	}
2090
2091
	// ne pas generer une url avec /./?page= en cas d'url absolue et de _SPIP_SCRIPT vide
2092
	return ($rel ? _DIR_RACINE . $action : rtrim(url_de_base(), '/') . preg_replace(",^/[.]/,", "/", "/$action"));
2093
}
2094
2095
// http://code.spip.net/@generer_url_prive
2096
function generer_url_prive($script, $args = "", $no_entities = false) {
2097
2098
	return generer_url_public($script, $args, $no_entities, false, _DIR_RESTREINT_ABS . 'prive.php');
2099
}
2100
2101
// Pour les formulaires en methode POST,
2102
// mettre le nom du script a la fois en input-hidden et dans le champ action:
2103
// 1) on peut ainsi memoriser le signet comme si c'etait un GET
2104
// 2) ca suit http://en.wikipedia.org/wiki/Representational_State_Transfer
2105
2106
/**
2107
 * Retourne un formulaire (POST par défaut) vers un script exec
2108
 * de l’interface privée
2109
 *
2110
 * @param string $script
2111
 *     Nom de la page exec
2112
 * @param string $corps
2113
 *     Contenu du formulaire
2114
 * @param string $atts
2115
 *     Si présent, remplace les arguments par défaut (method=post) par ceux indiqués
2116
 * @param string $submit
2117
 *     Si indiqué, un bouton de soumission est créé avec texte sa valeur.
2118
 * @return string
2119
 *     Code HTML du formulaire
2120
 **/
2121
function generer_form_ecrire($script, $corps, $atts = '', $submit = '') {
2122
2123
	$script1 = explode('&', $script);
2124
	$script1 = reset($script1);
2125
2126
	return "<form action='"
2127
	. ($script ? generer_url_ecrire($script) : '')
2128
	. "' "
2129
	. ($atts ? $atts : " method='post'")
2130
	. "><div>\n"
2131
	. "<input type='hidden' name='exec' value='$script1' />"
2132
	. $corps
2133
	. (!$submit ? '' :
2134
		("<div style='text-align: " . $GLOBALS['spip_lang_right'] . "'><input class='fondo' type='submit' value=\"" . entites_html($submit) . "\" /></div>"))
2135
	. "</div></form>\n";
2136
}
2137
2138
/**
2139
 * Générer un formulaire pour lancer une action vers $script
2140
 *
2141
 * Attention, JS/Ajax n'aime pas le melange de param GET/POST
2142
 * On n'applique pas la recommandation ci-dessus pour les scripts publics
2143
 * qui ne sont pas destines a etre mis en signets
2144
 *
2145
 * @param string $script
2146
 * @param string $corps
2147
 * @param string $atts
2148
 * @param bool $public
2149
 * @return string
2150
 */
2151
function generer_form_action($script, $corps, $atts = '', $public = false) {
2152
	// si l'on est dans l'espace prive, on garde dans l'url
2153
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2154
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2155
	$h = (_DIR_RACINE and !$public)
2156
		? generer_url_ecrire(_request('exec'))
2157
		: generer_url_public();
2158
2159
	return "\n<form action='" .
2160
	$h .
2161
	"'" .
2162
	$atts .
2163
	">\n" .
2164
	"<div>" .
2165
	"\n<input type='hidden' name='action' value='$script' />" .
2166
	$corps .
2167
	"</div></form>";
2168
}
2169
2170
/**
2171
 * Créer une URL
2172
 *
2173
 * @param  string $script
2174
 *     Nom du script à exécuter
2175
 * @param  string $args
2176
 *     Arguments à transmettre a l'URL sous la forme `arg1=yy&arg2=zz`
2177
 * @param bool $no_entities
2178
 *     Si false : transforme les & en &amp;
2179
 * @param boolean $public
2180
 *     URL relative ? false : l’URL sera complète et contiendra l’URL du site.
2181
 *     true : l’URL sera relative.
2182
 * @return string
2183
 *     URL
2184
 */
2185
function generer_url_action($script, $args = "", $no_entities = false, $public = false) {
2186
	// si l'on est dans l'espace prive, on garde dans l'url
2187
	// l'exec a l'origine de l'action, qui permet de savoir si il est necessaire
2188
	// ou non de proceder a l'authentification (cas typique de l'install par exemple)
2189
	$url = (_DIR_RACINE and !$public)
2190
		? generer_url_ecrire(_request('exec'))
2191
		: generer_url_public('', '', false, false);
2192
	$url = parametre_url($url, 'action', $script);
2193
	if ($args) {
2194
		$url .= quote_amp('&' . $args);
2195
	}
2196
2197
	if ($no_entities) {
2198
		$url = str_replace('&amp;', '&', $url);
2199
	}
2200
2201
	return $url;
2202
}
2203
2204
2205
/**
2206
 * Fonction d'initialisation groupée pour compatibilité ascendante
2207
 *
2208
 * @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...
2209
 * @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...
2210
 * @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...
2211
 * @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...
2212
 */
2213
function spip_initialisation($pi = null, $pa = null, $ti = null, $ta = null) {
2214
	spip_initialisation_core($pi, $pa, $ti, $ta);
2215
	spip_initialisation_suite();
2216
}
2217
2218
/**
2219
 * Fonction d'initialisation, appellée dans inc_version ou mes_options
2220
 *
2221
 * Elle définit les répertoires et fichiers non partageables
2222
 * et indique dans $test_dirs ceux devant être accessibles en écriture
2223
 * mais ne touche pas à cette variable si elle est déjà définie
2224
 * afin que mes_options.php puisse en spécifier d'autres.
2225
 *
2226
 * Elle définit ensuite les noms des fichiers et les droits.
2227
 * Puis simule un register_global=on sécurisé.
2228
 *
2229
 * @param string $pi Répertoire permanent inaccessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pi not be string|null?

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

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

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

Loading history...
2230
 * @param string $pa Répertoire permanent accessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pa not be string|null?

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

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

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

Loading history...
2231
 * @param string $ti Répertoire temporaire inaccessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $ti not be string|null?

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

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

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

Loading history...
2232
 * @param string $ta Répertoire temporaire accessible
0 ignored issues
show
Documentation introduced by
Should the type for parameter $ta not be string|null?

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

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

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

Loading history...
2233
 */
2234
function spip_initialisation_core($pi = null, $pa = null, $ti = null, $ta = null) {
2235
	static $too_late = 0;
2236
	if ($too_late++) {
2237
		return;
2238
	}
2239
2240
	// Declaration des repertoires
2241
2242
	// le nom du repertoire plugins/ activables/desactivables
2243
	if (!defined('_DIR_PLUGINS')) {
2244
		define('_DIR_PLUGINS', _DIR_RACINE . "plugins/");
2245
	}
2246
2247
	// le nom du repertoire des extensions/ permanentes du core, toujours actives
2248
	if (!defined('_DIR_PLUGINS_DIST')) {
2249
		define('_DIR_PLUGINS_DIST', _DIR_RACINE . "plugins-dist/");
2250
	}
2251
2252
	// le nom du repertoire des librairies
2253
	if (!defined('_DIR_LIB')) {
2254
		define('_DIR_LIB', _DIR_RACINE . "lib/");
2255
	}
2256
2257
	if (!defined('_DIR_IMG')) {
2258
		define('_DIR_IMG', $pa);
2259
	}
2260
	if (!defined('_DIR_LOGOS')) {
2261
		define('_DIR_LOGOS', $pa);
2262
	}
2263
	if (!defined('_DIR_IMG_ICONES')) {
2264
		define('_DIR_IMG_ICONES', _DIR_LOGOS . "icones/");
2265
	}
2266
2267
	if (!defined('_DIR_DUMP')) {
2268
		define('_DIR_DUMP', $ti . "dump/");
2269
	}
2270
	if (!defined('_DIR_SESSIONS')) {
2271
		define('_DIR_SESSIONS', $ti . "sessions/");
2272
	}
2273
	if (!defined('_DIR_TRANSFERT')) {
2274
		define('_DIR_TRANSFERT', $ti . "upload/");
2275
	}
2276
	if (!defined('_DIR_CACHE')) {
2277
		define('_DIR_CACHE', $ti . "cache/");
2278
	}
2279
	if (!defined('_DIR_CACHE_XML')) {
2280
		define('_DIR_CACHE_XML', _DIR_CACHE . "xml/");
2281
	}
2282
	if (!defined('_DIR_SKELS')) {
2283
		define('_DIR_SKELS', _DIR_CACHE . "skel/");
2284
	}
2285
	if (!defined('_DIR_AIDE')) {
2286
		define('_DIR_AIDE', _DIR_CACHE . "aide/");
2287
	}
2288
	if (!defined('_DIR_TMP')) {
2289
		define('_DIR_TMP', $ti);
2290
	}
2291
2292
	if (!defined('_DIR_VAR')) {
2293
		define('_DIR_VAR', $ta);
2294
	}
2295
2296
	if (!defined('_DIR_ETC')) {
2297
		define('_DIR_ETC', $pi);
2298
	}
2299
	if (!defined('_DIR_CONNECT')) {
2300
		define('_DIR_CONNECT', $pi);
2301
	}
2302
	if (!defined('_DIR_CHMOD')) {
2303
		define('_DIR_CHMOD', $pi);
2304
	}
2305
2306
	if (!isset($GLOBALS['test_dirs']))
2307
		// Pas $pi car il est bon de le mettre hors ecriture apres intstall
2308
		// il sera rajoute automatiquement si besoin a l'etape 2 de l'install
2309
	{
2310
		$GLOBALS['test_dirs'] = array($pa, $ti, $ta);
2311
	}
2312
2313
	// Declaration des fichiers
2314
2315
	if (!defined('_CACHE_PLUGINS_PATH')) {
2316
		define('_CACHE_PLUGINS_PATH', _DIR_CACHE . "charger_plugins_chemins.php");
2317
	}
2318
	if (!defined('_CACHE_PLUGINS_OPT')) {
2319
		define('_CACHE_PLUGINS_OPT', _DIR_CACHE . "charger_plugins_options.php");
2320
	}
2321
	if (!defined('_CACHE_PLUGINS_FCT')) {
2322
		define('_CACHE_PLUGINS_FCT', _DIR_CACHE . "charger_plugins_fonctions.php");
2323
	}
2324
	if (!defined('_CACHE_PIPELINES')) {
2325
		define('_CACHE_PIPELINES', _DIR_CACHE . "charger_pipelines.php");
2326
	}
2327
	if (!defined('_CACHE_CHEMIN')) {
2328
		define('_CACHE_CHEMIN', _DIR_CACHE . "chemin.txt");
2329
	}
2330
2331
	# attention .php obligatoire pour ecrire_fichier_securise
2332
	if (!defined('_FILE_META')) {
2333
		define('_FILE_META', $ti . 'meta_cache.php');
2334
	}
2335
	if (!defined('_DIR_LOG')) {
2336
		define('_DIR_LOG', _DIR_TMP . 'log/');
2337
	}
2338
	if (!defined('_FILE_LOG')) {
2339
		define('_FILE_LOG', 'spip');
2340
	}
2341
	if (!defined('_FILE_LOG_SUFFIX')) {
2342
		define('_FILE_LOG_SUFFIX', '.log');
2343
	}
2344
2345
	// Le fichier de connexion a la base de donnees
2346
	// tient compte des anciennes versions (inc_connect...)
2347
	if (!defined('_FILE_CONNECT_INS')) {
2348
		define('_FILE_CONNECT_INS', 'connect');
2349
	}
2350
	if (!defined('_FILE_CONNECT')) {
2351
		define('_FILE_CONNECT',
2352
		(@is_readable($f = _DIR_CONNECT . _FILE_CONNECT_INS . '.php') ? $f
2353
			: (@is_readable($f = _DIR_RESTREINT . 'inc_connect.php') ? $f
2354
				: false)));
2355
	}
2356
2357
	// Le fichier de reglages des droits
2358
	if (!defined('_FILE_CHMOD_INS')) {
2359
		define('_FILE_CHMOD_INS', 'chmod');
2360
	}
2361
	if (!defined('_FILE_CHMOD')) {
2362
		define('_FILE_CHMOD',
2363
		(@is_readable($f = _DIR_CHMOD . _FILE_CHMOD_INS . '.php') ? $f
2364
			: false));
2365
	}
2366
2367
	if (!defined('_FILE_LDAP')) {
2368
		define('_FILE_LDAP', 'ldap.php');
2369
	}
2370
2371
	if (!defined('_FILE_TMP_SUFFIX')) {
2372
		define('_FILE_TMP_SUFFIX', '.tmp.php');
2373
	}
2374
	if (!defined('_FILE_CONNECT_TMP')) {
2375
		define('_FILE_CONNECT_TMP', _DIR_CONNECT . _FILE_CONNECT_INS . _FILE_TMP_SUFFIX);
2376
	}
2377
	if (!defined('_FILE_CHMOD_TMP')) {
2378
		define('_FILE_CHMOD_TMP', _DIR_CHMOD . _FILE_CHMOD_INS . _FILE_TMP_SUFFIX);
2379
	}
2380
2381
	// Definition des droits d'acces en ecriture
2382
	if (!defined('_SPIP_CHMOD') and _FILE_CHMOD) {
2383
		include_once _FILE_CHMOD;
2384
	}
2385
2386
	// Se mefier des fichiers mal remplis!
2387
	if (!defined('_SPIP_CHMOD')) {
2388
		define('_SPIP_CHMOD', 0777);
2389
	}
2390
2391
	if (!defined('_DEFAULT_CHARSET')) {
2392
		/** Le charset par défaut lors de l'installation */
2393
		define('_DEFAULT_CHARSET', 'utf-8');
2394
	}
2395
	if (!defined('_ROOT_PLUGINS')) {
2396
		define('_ROOT_PLUGINS', _ROOT_RACINE . "plugins/");
2397
	}
2398
	if (!defined('_ROOT_PLUGINS_DIST')) {
2399
		define('_ROOT_PLUGINS_DIST', _ROOT_RACINE . "plugins-dist/");
2400
	}
2401
	if (!defined('_ROOT_PLUGINS_SUPPL') && defined('_DIR_PLUGINS_SUPPL') && _DIR_PLUGINS_SUPPL) {
2402
		define('_ROOT_PLUGINS_SUPPL', _ROOT_RACINE . str_replace(_DIR_RACINE, '', _DIR_PLUGINS_SUPPL));
2403
	}
2404
2405
	// La taille des Log
2406
	if (!defined('_MAX_LOG')) {
2407
		define('_MAX_LOG', 100);
2408
	}
2409
2410
	// Sommes-nous dans l'empire du Mal ?
2411
	// (ou sous le signe du Pingouin, ascendant GNU ?)
2412
	if (isset($_SERVER['SERVER_SOFTWARE']) and strpos($_SERVER['SERVER_SOFTWARE'], '(Win') !== false) {
2413
		if (!defined('_OS_SERVEUR')) {
2414
			define('_OS_SERVEUR', 'windows');
2415
		}
2416
		if (!defined('_SPIP_LOCK_MODE')) {
2417
			define('_SPIP_LOCK_MODE', 1);
2418
		} // utiliser le flock php
2419
	} else {
2420
		if (!defined('_OS_SERVEUR')) {
2421
			define('_OS_SERVEUR', '');
2422
		}
2423
		if (!defined('_SPIP_LOCK_MODE')) {
2424
			define('_SPIP_LOCK_MODE', 1);
2425
		} // utiliser le flock php
2426
		#if (!defined('_SPIP_LOCK_MODE')) define('_SPIP_LOCK_MODE',2); // utiliser le nfslock de spip mais link() est tres souvent interdite
2427
	}
2428
2429
	// Langue par defaut
2430
	if (!defined('_LANGUE_PAR_DEFAUT')) {
2431
		define('_LANGUE_PAR_DEFAUT', 'fr');
2432
	}
2433
2434
	//
2435
	// Module de lecture/ecriture/suppression de fichiers utilisant flock()
2436
	// (non surchargeable en l'etat ; attention si on utilise include_spip()
2437
	// pour le rendre surchargeable, on va provoquer un reecriture
2438
	// systematique du noyau ou une baisse de perfs => a etudier)
2439
	include_once _ROOT_RESTREINT . 'inc/flock.php';
2440
2441
	// charger tout de suite le path et son cache
2442
	load_path_cache();
2443
2444
	// *********** traiter les variables ************
2445
2446
	//
2447
	// Securite
2448
	//
2449
2450
	// Ne pas se faire manger par un bug php qui accepte ?GLOBALS[truc]=toto
2451
	if (isset($_REQUEST['GLOBALS'])) {
2452
		die();
0 ignored issues
show
Coding Style Compatibility introduced by
The function spip_initialisation_core() contains an exit expression.

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

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

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

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

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