Completed
Push — master ( c1ab59...d1d834 )
by cam
04:22
created

filtres.php ➔ identifiant_slug()   F

Complexity

Conditions 17
Paths 7168

Size

Total Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 17
nc 7168
nop 3
dl 0
loc 61
rs 1.0499
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, Système de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright © avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
10
 *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Déclaration de filtres pour les squelettes
15
 *
16
 * @package SPIP\Core\Filtres
17
 **/
18
if (!defined('_ECRIRE_INC_VERSION')) {
19
	return;
20
}
21
22
include_spip('inc/charsets');
23
include_spip('inc/filtres_mini');
24
include_spip('inc/filtres_dates');
25
include_spip('inc/filtres_selecteur_generique');
26
include_spip('base/objets');
27
include_spip('public/parametrer'); // charger les fichiers fonctions
28
29
/**
30
 * Charger un filtre depuis le php
31
 *
32
 * - on inclue tous les fichiers fonctions des plugins et du skel
33
 * - on appelle chercher_filtre
34
 *
35
 * Pour éviter de perdre le texte si le filtre demandé est introuvable,
36
 * on transmet `filtre_identite_dist` en filtre par défaut.
37
 *
38
 * @uses filtre_identite_dist() Comme fonction par défaut
39
 *
40
 * @param string $fonc Nom du filtre
41
 * @param string $default Filtre par défaut
42
 * @return string Fonction PHP correspondante du filtre
0 ignored issues
show
Documentation introduced by
Should the return type not be null|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...
43
 */
44
function charger_filtre($fonc, $default = 'filtre_identite_dist') {
45
	include_spip('public/parametrer'); // inclure les fichiers fonctions
46
	return chercher_filtre($fonc, $default);
47
}
48
49
/**
50
 * Retourne le texte tel quel
51
 *
52
 * @param string $texte Texte
53
 * @return string Texte
54
 **/
55
function filtre_identite_dist($texte) { return $texte; }
56
57
/**
58
 * Cherche un filtre
59
 *
60
 * Pour une filtre `F` retourne la première fonction trouvée parmis :
61
 *
62
 * - filtre_F
63
 * - filtre_F_dist
64
 * - F
65
 *
66
 * Peut gérer des appels par des fonctions statiques de classes tel que `Foo::Bar`
67
 *
68
 * En absence de fonction trouvée, retourne la fonction par défaut indiquée.
69
 *
70
 * @param string $fonc
71
 *     Nom du filtre
72
 * @param null $default
73
 *     Nom du filtre appliqué par défaut si celui demandé n'est pas trouvé
74
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be null|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...
75
 *     Fonction PHP correspondante du filtre demandé
76
 */
77
function chercher_filtre($fonc, $default = null) {
78
	if (!$fonc) {
79
		return $default;
80
	}
81
	// Cas des types mime, sans confondre avec les appels de fonction de classe
82
	// Foo::Bar
83
	// qui peuvent etre avec un namespace : space\Foo::Bar
84
	if (preg_match(',^[\w]+/,', $fonc)) {
85
		$nom = preg_replace(',\W,', '_', $fonc);
86
		$f = chercher_filtre($nom);
87
		// cas du sous-type MIME sans filtre associe, passer au type:
88
		// si filtre_text_plain pas defini, passe a filtre_text
89
		if (!$f and $nom !== $fonc) {
90
			$f = chercher_filtre(preg_replace(',\W.*$,', '', $fonc));
91
		}
92
93
		return $f;
94
	}
95
96
	include_fichiers_fonctions();
97
	foreach (array('filtre_' . $fonc, 'filtre_' . $fonc . '_dist', $fonc) as $f) {
98
		trouver_filtre_matrice($f); // charge des fichiers spécifiques éventuels
99
		// fonction ou name\space\fonction
100
		if (is_callable($f)) {
101
			return $f;
102
		}
103
		// méthode statique d'une classe Classe::methode ou name\space\Classe::methode
104
		elseif (false === strpos($f, '::') and is_callable(array($f))) {
105
			return $f;
106
		}
107
	}
108
109
	return $default;
110
}
111
112
/**
113
 * Applique un filtre
114
 *
115
 * Fonction générique qui prend en argument l’objet (texte, etc) à modifier
116
 * et le nom du filtre. Retrouve les arguments du filtre demandé dans les arguments
117
 * transmis à cette fonction, via func_get_args().
118
 *
119
 * @see filtrer() Assez proche
120
 *
121
 * @param mixed $arg
122
 *     Texte (le plus souvent) sur lequel appliquer le filtre
123
 * @param string $filtre
124
 *     Nom du filtre à appliquer
125
 * @param bool $force
0 ignored issues
show
Documentation introduced by
Should the type for parameter $force not be 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...
126
 *     La fonction doit-elle retourner le texte ou rien si le filtre est absent ?
127
 * @return string
128
 *     Texte traité par le filtre si le filtre existe,
129
 *     Texte d'origine si le filtre est introuvable et si $force à `true`
130
 *     Chaîne vide sinon (filtre introuvable).
131
 **/
132
function appliquer_filtre($arg, $filtre, $force = null) {
133
	$f = chercher_filtre($filtre);
134
	if (!$f) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $f of type null|string is loosely compared to false; 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...
135
		if (!$force) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $force of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
136
			return '';
137
		} else {
138
			return $arg;
139
		}
140
	}
141
142
	$args = func_get_args();
143
	array_shift($args); // enlever $arg
144
	array_shift($args); // enlever $filtre
145
	array_unshift($args, $arg); // remettre $arg
146
	return call_user_func_array($f, $args);
147
}
148
149
/**
150
 * Retourne la version de SPIP
151
 *
152
 * Si l'on retrouve un numéro de révision GIT ou SVN, il est ajouté entre crochets.
153
 * Si effectivement le SPIP est installé par Git ou Svn, 'GIT' ou 'SVN' est ajouté avant sa révision.
154
 *
155
 * @global spip_version_affichee Contient la version de SPIP
156
 * @uses version_vcs_courante() Pour trouver le numéro de révision
157
 *
158
 * @return string
159
 *     Version de SPIP
160
 **/
161
function spip_version() {
162
	$version = $GLOBALS['spip_version_affichee'];
163
	if ($vcs_version = version_vcs_courante(_DIR_RACINE)) {
164
		$version .= " $vcs_version";
165
	}
166
167
	return $version;
168
}
169
170
/**
171
 * Retourne une courte description d’une révision VCS d’un répertoire
172
 *
173
 * @param string $dir Le répertoire à tester
174
 * @param array $raw True pour avoir les données brutes, false pour un texte à afficher
0 ignored issues
show
Documentation introduced by
Should the type for parameter $raw not be false|array? 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...
175
 * @retun string|array|null
176
 *    - array|null si $raw = true,
177
 *    - string|null si $raw = false
178
 */
179
function version_vcs_courante($dir, $raw = false) {
180
	$desc = decrire_version_git($dir);
181
	if ($desc === null) {
182
		$desc = decrire_version_svn($dir);
183
	}
184
	if ($desc === null or $raw) {
185
		return $desc;
186
	}
187
	// affichage "GIT [master: abcdef]"
188
	$commit = isset($desc['commit_short']) ? $desc['commit_short'] : $desc['commit'];
189
	if ($desc['branch']) {
190
		$commit = $desc['branch'] . ': ' . $commit;
191
	}
192
	return "{$desc['vcs']} [$commit]";
193
}
194
195
/**
196
 * Retrouve un numéro de révision Git d'un répertoire
197
 *
198
 * @param string $dir Chemin du répertoire
199
 * @return array|null
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>|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...
200
 *      null si aucune info trouvée
201
 *      array ['branch' => xx, 'commit' => yy] sinon.
202
 **/
203
function decrire_version_git($dir) {
204
	if (!$dir) {
205
		$dir = '.';
206
	}
207
208
	// version installee par GIT
209
	if (lire_fichier($dir . '/.git/HEAD', $c)) {
210
		$currentHead = trim(substr($c, 4));
211
		if (lire_fichier($dir . '/.git/' . $currentHead, $hash)) {
212
			return [
213
				'vcs' => 'GIT',
214
				'branch' => basename($currentHead),
215
				'commit' => trim($hash),
216
				'commit_short' => substr(trim($hash), 0, 8),
217
			];
218
		}
219
	}
220
221
	return null;
222
}
223
224
225
/**
226
 * Retrouve un numéro de révision Svn d'un répertoire
227
 *
228
 * @param string $dir Chemin du répertoire
229
 * @return array|null
230
 *      null si aucune info trouvée
231
 *      array ['commit' => yy, 'date' => xx, 'author' => xx] sinon.
232
 **/
233
function decrire_version_svn($dir) {
234
	if (!$dir) {
235
		$dir = '.';
236
	}
237
	// version installee par SVN
238
	if (file_exists($dir . '/.svn/wc.db') && class_exists('SQLite3')) {
239
		$db = new SQLite3($dir . '/.svn/wc.db');
240
		$result = $db->query('SELECT changed_revision FROM nodes WHERE local_relpath = "" LIMIT 1');
241
		if ($result) {
242
			$row = $result->fetchArray();
243
			if ($row['changed_revision'] != "") {
244
				return [
245
					'vcs' => 'SVN',
246
					'branch' => '',
247
					'commit' => $row['changed_revision'],
248
				];
249
			}
250
		}
251
	}
252
	return null;
253
}
254
255
/**
256
 * Retrouve un numéro de révision SVN d'un répertoire
257
 *
258
 * Mention de la révision SVN courante d'un répertoire
259
 * /!\ Retourne un nombre négatif si on est sur .svn
260
 *
261
 * @deprecated Utiliser version_vcs_courante()
262
 * @param string $dir Chemin du répertoire
263
 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be 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...
264
 *
265
 *     - 0 si aucune info trouvée
266
 *     - -NN (entier) si info trouvée par .svn/wc.db
267
 *
268
 **/
269
function version_svn_courante($dir) {
270
	if ($desc = decrire_version_svn($dir)) {
271
		return -$desc['commit'];
272
	}
273
	return 0;
274
}
275
276
// La matrice est necessaire pour ne filtrer _que_ des fonctions definies dans filtres_images
277
// et laisser passer les fonctions personnelles baptisees image_...
278
$GLOBALS['spip_matrice']['image_graver'] = true;//'inc/filtres_images_mini.php';
279
$GLOBALS['spip_matrice']['image_select'] = true;//'inc/filtres_images_mini.php';
280
$GLOBALS['spip_matrice']['image_reduire'] = true;//'inc/filtres_images_mini.php';
281
$GLOBALS['spip_matrice']['image_reduire_par'] = true;//'inc/filtres_images_mini.php';
282
$GLOBALS['spip_matrice']['image_passe_partout'] = true;//'inc/filtres_images_mini.php';
283
284
$GLOBALS['spip_matrice']['couleur_html_to_hex'] = 'inc/filtres_images_mini.php';
285
$GLOBALS['spip_matrice']['couleur_foncer'] = 'inc/filtres_images_mini.php';
286
$GLOBALS['spip_matrice']['couleur_eclaircir'] = 'inc/filtres_images_mini.php';
287
288
// ou pour inclure un script au moment ou l'on cherche le filtre
289
$GLOBALS['spip_matrice']['filtre_image_dist'] = 'inc/filtres_mime.php';
290
$GLOBALS['spip_matrice']['filtre_audio_dist'] = 'inc/filtres_mime.php';
291
$GLOBALS['spip_matrice']['filtre_video_dist'] = 'inc/filtres_mime.php';
292
$GLOBALS['spip_matrice']['filtre_application_dist'] = 'inc/filtres_mime.php';
293
$GLOBALS['spip_matrice']['filtre_message_dist'] = 'inc/filtres_mime.php';
294
$GLOBALS['spip_matrice']['filtre_multipart_dist'] = 'inc/filtres_mime.php';
295
$GLOBALS['spip_matrice']['filtre_text_dist'] = 'inc/filtres_mime.php';
296
$GLOBALS['spip_matrice']['filtre_text_csv_dist'] = 'inc/filtres_mime.php';
297
$GLOBALS['spip_matrice']['filtre_text_html_dist'] = 'inc/filtres_mime.php';
298
$GLOBALS['spip_matrice']['filtre_audio_x_pn_realaudio'] = 'inc/filtres_mime.php';
299
300
301
/**
302
 * Charge et exécute un filtre (graphique ou non)
303
 *
304
 * Recherche la fonction prévue pour un filtre (qui peut être un filtre graphique `image_*`)
305
 * et l'exécute avec les arguments transmis à la fonction, obtenus avec `func_get_args()`
306
 *
307
 * @api
308
 * @uses image_filtrer() Pour un filtre image
309
 * @uses chercher_filtre() Pour un autre filtre
310
 *
311
 * @param string $filtre
312
 *     Nom du filtre à appliquer
313
 * @return string
314
 *     Code HTML retourné par le filtre
315
 **/
316
function filtrer($filtre) {
317
	$tous = func_get_args();
318
	if (trouver_filtre_matrice($filtre) and substr($filtre, 0, 6) == 'image_') {
319
		return image_filtrer($tous);
320
	} elseif ($f = chercher_filtre($filtre)) {
321
		array_shift($tous);
322
		return call_user_func_array($f, $tous);
323
	} else {
324
		// le filtre n'existe pas, on provoque une erreur
325
		$msg = array('zbug_erreur_filtre', array('filtre' => texte_script($filtre)));
326
		erreur_squelette($msg);
327
		return '';
328
	}
329
}
330
331
/**
332
 * Cherche un filtre spécial indiqué dans la globale `spip_matrice`
333
 * et charge le fichier éventuellement associé contenant le filtre.
334
 *
335
 * Les filtres d'images par exemple sont déclarés de la sorte, tel que :
336
 * ```
337
 * $GLOBALS['spip_matrice']['image_reduire'] = true;
338
 * $GLOBALS['spip_matrice']['image_monochrome'] = 'filtres/images_complements.php';
339
 * ```
340
 *
341
 * @param string $filtre
342
 * @return bool true si on trouve le filtre dans la matrice, false sinon.
343
 */
344
function trouver_filtre_matrice($filtre) {
345
	if (isset($GLOBALS['spip_matrice'][$filtre]) and is_string($f = $GLOBALS['spip_matrice'][$filtre])) {
346
		find_in_path($f, '', true);
347
		$GLOBALS['spip_matrice'][$filtre] = true;
348
	}
349
	return !empty($GLOBALS['spip_matrice'][$filtre]);
350
}
351
352
353
/**
354
 * Filtre `set` qui sauve la valeur en entrée dans une variable
355
 *
356
 * La valeur pourra être retrouvée avec `#GET{variable}`.
357
 *
358
 * @example
359
 *     `[(#CALCUL|set{toto})]` enregistre le résultat de `#CALCUL`
360
 *     dans la variable `toto` et renvoie vide.
361
 *     C'est équivalent à `[(#SET{toto, #CALCUL})]` dans ce cas.
362
 *     `#GET{toto}` retourne la valeur sauvegardée.
363
 *
364
 * @example
365
 *     `[(#CALCUL|set{toto,1})]` enregistre le résultat de `#CALCUL`
366
 *      dans la variable toto et renvoie la valeur. Cela permet d'utiliser
367
 *      d'autres filtres ensuite. `#GET{toto}` retourne la valeur.
368
 *
369
 * @filtre
370
 * @param array $Pile Pile de données
371
 * @param mixed $val Valeur à sauver
372
 * @param string $key Clé d'enregistrement
373
 * @param bool $continue True pour retourner la valeur
0 ignored issues
show
Documentation introduced by
Should the type for parameter $continue not be 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...
374
 * @return mixed
375
 */
376
function filtre_set(&$Pile, $val, $key, $continue = null) {
377
	$Pile['vars'][$key] = $val;
378
	return $continue ? $val : '';
379
}
380
381
/**
382
 * Filtre `setenv` qui enregistre une valeur dans l'environnement du squelette
383
 *
384
 * La valeur pourra être retrouvée avec `#ENV{variable}`.
385
 * 
386
 * @example
387
 *     `[(#CALCUL|setenv{toto})]` enregistre le résultat de `#CALCUL`
388
 *      dans l'environnement toto et renvoie vide.
389
 *      `#ENV{toto}` retourne la valeur.
390
 *
391
 *      `[(#CALCUL|setenv{toto,1})]` enregistre le résultat de `#CALCUL`
392
 *      dans l'environnement toto et renvoie la valeur.
393
 *      `#ENV{toto}` retourne la valeur.
394
 *
395
 * @filtre
396
 *
397
 * @param array $Pile
398
 * @param mixed $val Valeur à enregistrer
399
 * @param mixed $key Nom de la variable
400
 * @param null|mixed $continue Si présent, retourne la valeur en sortie
401
 * @return string|mixed Retourne `$val` si `$continue` présent, sinon ''.
402
 */
403
function filtre_setenv(&$Pile, $val, $key, $continue = null) {
404
	$Pile[0][$key] = $val;
405
	return $continue ? $val : '';
406
}
407
408
/**
409
 * @param array $Pile
410
 * @param array|string $keys
411
 * @return string
412
 */
413
function filtre_sanitize_env(&$Pile, $keys) {
414
	$Pile[0] = spip_sanitize_from_request($Pile[0], $keys);
415
	return '';
416
}
417
418
419
/**
420
 * Filtre `debug` qui affiche un debug de la valeur en entrée
421
 *
422
 * Log la valeur dans `debug.log` et l'affiche si on est webmestre.
423
 *
424
 * @example
425
 *     `[(#TRUC|debug)]` affiche et log la valeur de `#TRUC`
426
 * @example
427
 *     `[(#TRUC|debug{avant}|calcul|debug{apres}|etc)]`
428
 *     affiche la valeur de `#TRUC` avant et après le calcul,
429
 *     en précisant "avant" et "apres".
430
 *
431
 * @filtre
432
 * @link https://www.spip.net/5695
433
 * @param mixed $val La valeur à debugguer
434
 * @param mixed|null $key Clé pour s'y retrouver
435
 * @return mixed Retourne la valeur (sans la modifier).
436
 */
437
function filtre_debug($val, $key = null) {
438
	$debug = (
439
		is_null($key) ? '' : (var_export($key, true) . " = ")
440
		) . var_export($val, true);
441
442
	include_spip('inc/autoriser');
443
	if (autoriser('webmestre')) {
444
		echo "<div class='spip_debug'>\n", $debug, "</div>\n";
445
	}
446
447
	spip_log($debug, 'debug');
448
449
	return $val;
450
}
451
452
453
/**
454
 * Exécute un filtre image
455
 *
456
 * Fonction générique d'entrée des filtres images.
457
 * Accepte en entrée :
458
 *
459
 * - un texte complet,
460
 * - un img-log (produit par #LOGO_XX),
461
 * - un tag `<img ...>` complet,
462
 * - un nom de fichier *local* (passer le filtre `|copie_locale` si on veut
463
 *   l'appliquer à un document distant).
464
 *
465
 * Applique le filtre demande à chacune des occurrences
466
 *
467
 * @param array $args
468
 *     Liste des arguments :
469
 *
470
 *     - le premier est le nom du filtre image à appliquer
471
 *     - le second est le texte sur lequel on applique le filtre
472
 *     - les suivants sont les arguments du filtre image souhaité.
473
 * @return string
474
 *     Texte qui a reçu les filtres
475
 **/
476
function image_filtrer($args) {
477
	$filtre = array_shift($args); # enlever $filtre
478
	$texte = array_shift($args);
479
	if (!strlen($texte)) {
480
		return;
481
	}
482
	find_in_path('filtres_images_mini.php', 'inc/', true);
483
	statut_effacer_images_temporaires(true); // activer la suppression des images temporaires car le compilo finit la chaine par un image_graver
0 ignored issues
show
Unused Code introduced by
The call to the function statut_effacer_images_temporaires() seems unnecessary as the function has no side-effects.
Loading history...
484
	// Cas du nom de fichier local
485
	$is_file = trim($texte);
486
	if (strpos(substr($is_file, strlen(_DIR_RACINE)), '..') !== false
487
		  or strpbrk($is_file, "<>\n\r\t") !== false
488
		  or strpos($is_file, '/') === 0
489
	) {
490
		$is_file = false;
491
	}
492
	if ($is_file) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_file of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false 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...
493
		$is_local_file = function($path) {
494
			if (strpos($path, "?") !== false) {
495
				$path = supprimer_timestamp($path);
496
				// remove ?24px added by find_in_theme on .svg files
497
				$path = preg_replace(",\?[[:digit:]]+(px)$,", "", $path);
498
			}
499
			return file_exists($path);
500
		};
501
		if ($is_local_file($is_file) or tester_url_absolue($is_file)) {
502
			array_unshift($args, "<img src='$is_file' />");
503
			$res = call_user_func_array($filtre, $args);
504
			statut_effacer_images_temporaires(false); // desactiver pour les appels hors compilo
0 ignored issues
show
Unused Code introduced by
The call to the function statut_effacer_images_temporaires() seems unnecessary as the function has no side-effects.
Loading history...
505
			return $res;
506
		}
507
	}
508
509
	// Cas general : trier toutes les images, avec eventuellement leur <span>
510
	if (preg_match_all(
511
		',(<([a-z]+) [^<>]*spip_documents[^<>]*>)?\s*(<img\s.*>),UimsS',
512
		$texte, $tags, PREG_SET_ORDER)) {
513
		foreach ($tags as $tag) {
514
			$class = extraire_attribut($tag[3], 'class');
515
			if (!$class or
516
				(strpos($class, 'filtre_inactif') === false
517
					// compat historique a virer en 3.2
518
					and strpos($class, 'no_image_filtrer') === false)
519
			) {
520
				array_unshift($args, $tag[3]);
521
				if ($reduit = call_user_func_array($filtre, $args)) {
522
					// En cas de span spip_documents, modifier le style=...width:
523
					if ($tag[1]) {
524
						$w = extraire_attribut($reduit, 'width');
525
						if (!$w and preg_match(",width:\s*(\d+)px,S", extraire_attribut($reduit, 'style'), $regs)) {
526
							$w = $regs[1];
527
						}
528
						if ($w and ($style = extraire_attribut($tag[1], 'style'))) {
529
							$style = preg_replace(",width:\s*\d+px,S", "width:${w}px", $style);
530
							$replace = inserer_attribut($tag[1], 'style', $style);
0 ignored issues
show
Bug introduced by
It seems like $style defined by preg_replace(',width:\\s..."width:{$w}px", $style) on line 529 can also be of type array<integer,string>; however, inserer_attribut() 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...
531
							$texte = str_replace($tag[1], $replace, $texte);
532
						}
533
					}
534
					// traiter aussi un eventuel mouseover
535
					if ($mouseover = extraire_attribut($reduit, 'onmouseover')) {
536
						if (preg_match(",this[.]src=['\"]([^'\"]+)['\"],ims", $mouseover, $match)) {
537
							$srcover = $match[1];
538
							array_shift($args);
539
							array_unshift($args, "<img src='" . $match[1] . "' />");
540
							$srcover_filter = call_user_func_array($filtre, $args);
541
							$srcover_filter = extraire_attribut($srcover_filter, 'src');
542
							$reduit = str_replace($srcover, $srcover_filter, $reduit);
543
						}
544
					}
545
					$texte = str_replace($tag[3], $reduit, $texte);
546
				}
547
				array_shift($args);
548
			}
549
		}
550
	}
551
	statut_effacer_images_temporaires(false); // desactiver pour les appels hors compilo
0 ignored issues
show
Unused Code introduced by
The call to the function statut_effacer_images_temporaires() seems unnecessary as the function has no side-effects.
Loading history...
552
	return $texte;
553
}
554
555
/**
556
 * Retourne les tailles d'une image
557
 *
558
 * Pour les filtres `largeur` et `hauteur`
559
 *
560
 * @param string $img
561
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
562
 * @return array
563
 *     Liste (hauteur, largeur) en pixels
564
 **/
565
function taille_image($img, $force_refresh = false) {
566
567
	static $largeur_img = array(), $hauteur_img = array();
568
	$srcWidth = 0;
569
	$srcHeight = 0;
570
571
	$src = extraire_attribut($img, 'src');
572
573
	if (!$src) {
574
		$src = $img;
575
	} else {
576
		$srcWidth = extraire_attribut($img, 'width');
577
		$srcHeight = extraire_attribut($img, 'height');
578
	}
579
580
	// ne jamais operer directement sur une image distante pour des raisons de perfo
581
	// la copie locale a toutes les chances d'etre la ou de resservir
582
	if (tester_url_absolue($src)) {
0 ignored issues
show
Bug introduced by
It seems like $src defined by extraire_attribut($img, 'src') on line 571 can also be of type array; however, tester_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...
583
		include_spip('inc/distant');
584
		$fichier = copie_locale($src);
0 ignored issues
show
Bug introduced by
It seems like $src defined by extraire_attribut($img, 'src') on line 571 can also be of type array; however, copie_locale() 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...
585
		$src = $fichier ? _DIR_RACINE . $fichier : $src;
586
	}
587 View Code Duplication
	if (($p = strpos($src, '?')) !== 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...
588
		$src = substr($src, 0, $p);
589
	}
590
591
	$srcsize = false;
0 ignored issues
show
Unused Code introduced by
$srcsize is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
592
	if (isset($largeur_img[$src]) and !$force_refresh) {
593
		$srcWidth = $largeur_img[$src];
594
	}
595
	if (isset($hauteur_img[$src]) and !$force_refresh) {
596
		$srcHeight = $hauteur_img[$src];
597
	}
598
	if (!$srcWidth or !$srcHeight) {
599
600
		if (file_exists($src)
601
			and $srcsize = spip_getimagesize($src)
0 ignored issues
show
Bug introduced by
It seems like $src can also be of type array; however, spip_getimagesize() 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...
602
		) {
603
			if (!$srcWidth) {
604
				$largeur_img[$src] = $srcWidth = $srcsize[0];
605
			}
606
			if (!$srcHeight) {
607
				$hauteur_img[$src] = $srcHeight = $srcsize[1];
608
			}
609
		}
610
		elseif(strpos($src, "<svg") !== false) {
611
			include_spip('inc/svg');
612
			if ($attrs = svg_lire_attributs($src)){
0 ignored issues
show
Bug introduced by
It seems like $src can also be of type array; however, svg_lire_attributs() 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...
613
				list($width, $height, $viewbox) = svg_getimagesize_from_attr($attrs);
0 ignored issues
show
Unused Code introduced by
The assignment to $viewbox 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...
614
				if (!$srcWidth){
615
					$largeur_img[$src] = $srcWidth = $width;
616
				}
617
				if (!$srcHeight){
618
					$hauteur_img[$src] = $srcHeight = $height;
619
				}
620
			}
621
		}
622
		// $src peut etre une reference a une image temporaire dont a n'a que le log .src
623
		// on s'y refere, l'image sera reconstruite en temps utile si necessaire
624
		elseif (@file_exists($f = "$src.src")
625
			and lire_fichier($f, $valeurs)
626
			and $valeurs = unserialize($valeurs)
627
		) {
628
			if (!$srcWidth) {
629
				$largeur_img[$src] = $srcWidth = $valeurs["largeur_dest"];
630
			}
631
			if (!$srcHeight) {
632
				$hauteur_img[$src] = $srcHeight = $valeurs["hauteur_dest"];
633
			}
634
		}
635
	}
636
637
	return array($srcHeight, $srcWidth);
638
}
639
640
641
/**
642
 * Retourne la largeur d'une image
643
 *
644
 * @filtre
645
 * @link https://www.spip.net/4296
646
 * @uses taille_image()
647
 * @see  hauteur()
648
 *
649
 * @param string $img
650
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
651
 * @return int|null
652
 *     Largeur en pixels, NULL ou 0 si aucune image.
653
 **/
654
function largeur($img) {
655
	if (!$img) {
656
		return;
657
	}
658
	list($h, $l) = taille_image($img);
0 ignored issues
show
Unused Code introduced by
The assignment to $h 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...
659
660
	return $l;
661
}
662
663
/**
664
 * Retourne la hauteur d'une image
665
 *
666
 * @filtre
667
 * @link https://www.spip.net/4291
668
 * @uses taille_image()
669
 * @see  largeur()
670
 *
671
 * @param string $img
672
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
673
 * @return int|null
674
 *     Hauteur en pixels, NULL ou 0 si aucune image.
675
 **/
676
function hauteur($img) {
677
	if (!$img) {
678
		return;
679
	}
680
	list($h, $l) = taille_image($img);
0 ignored issues
show
Unused Code introduced by
The assignment to $l 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...
681
682
	return $h;
683
}
684
685
686
/**
687
 * Échappement des entités HTML avec correction des entités « brutes »
688
 *
689
 * Ces entités peuvent être générées par les butineurs lorsqu'on rentre des
690
 * caractères n'appartenant pas au charset de la page [iso-8859-1 par défaut]
691
 *
692
 * Attention on limite cette correction aux caracteres « hauts » (en fait > 99
693
 * pour aller plus vite que le > 127 qui serait logique), de manière à
694
 * préserver des eéhappements de caractères « bas » (par exemple `[` ou `"`)
695
 * et au cas particulier de `&amp;` qui devient `&amp;amp;` dans les URL
696
 *
697
 * @see corriger_toutes_entites_html()
698
 * @param string $texte
699
 * @return string
700
 **/
701
function corriger_entites_html($texte) {
702
	if (strpos($texte, '&amp;') === false) {
703
		return $texte;
704
	}
705
706
	return preg_replace(',&amp;(#[0-9][0-9][0-9]+;|amp;),iS', '&\1', $texte);
707
}
708
709
/**
710
 * Échappement des entités HTML avec correction des entités « brutes » ainsi
711
 * que les `&amp;eacute;` en `&eacute;`
712
 *
713
 * Identique à `corriger_entites_html()` en corrigeant aussi les
714
 * `&amp;eacute;` en `&eacute;`
715
 *
716
 * @see corriger_entites_html()
717
 * @param string $texte
718
 * @return string
719
 **/
720
function corriger_toutes_entites_html($texte) {
721
	if (strpos($texte, '&amp;') === false) {
722
		return $texte;
723
	}
724
725
	return preg_replace(',&amp;(#?[a-z0-9]+;),iS', '&\1', $texte);
726
}
727
728
/**
729
 * Échappe les `&` en `&amp;`
730
 *
731
 * @param string $texte
732
 * @return string
733
 **/
734
function proteger_amp($texte) {
735
	return str_replace('&', '&amp;', $texte);
736
}
737
738
739
/**
740
 * Échappe en entités HTML certains caractères d'un texte
741
 *
742
 * Traduira un code HTML en transformant en entités HTML les caractères
743
 * en dehors du charset de la page ainsi que les `"`, `<` et `>`.
744
 *
745
 * Ceci permet d’insérer le texte d’une balise dans un `<textarea> </textarea>`
746
 * sans dommages.
747
 *
748
 * @filtre
749
 * @link https://www.spip.net/4280
750
 *
751
 * @uses echappe_html()
752
 * @uses echappe_retour()
753
 * @uses proteger_amp()
754
 * @uses corriger_entites_html()
755
 * @uses corriger_toutes_entites_html()
756
 *
757
 * @param string $texte
758
 *   chaine a echapper
759
 * @param bool $tout
760
 *   corriger toutes les `&amp;xx;` en `&xx;`
761
 * @param bool $quote
762
 *   Échapper aussi les simples quotes en `&#039;`
763
 * @return mixed|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string.

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...
764
 */
765
function entites_html($texte, $tout = false, $quote = true) {
766 View Code Duplication
	if (!is_string($texte) or !$texte
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...
767
		or strpbrk($texte, "&\"'<>") == false
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpbrk($texte, '&"\'<>') of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
768
	) {
769
		return $texte;
770
	}
771
	include_spip('inc/texte');
772
	$flags = ($quote ? ENT_QUOTES : ENT_NOQUOTES);
773
	$flags |= ENT_HTML401;
774
	$texte = spip_htmlspecialchars(echappe_retour(echappe_html($texte, '', true), '', 'proteger_amp'), $flags);
775
	if ($tout) {
776
		return corriger_toutes_entites_html($texte);
777
	} else {
778
		return corriger_entites_html($texte);
779
	}
780
}
781
782
/**
783
 * Convertit les caractères spéciaux HTML dans le charset du site.
784
 *
785
 * @exemple
786
 *     Si le charset de votre site est `utf-8`, `&eacute;` ou `&#233;`
787
 *     sera transformé en `é`
788
 *
789
 * @filtre
790
 * @link https://www.spip.net/5513
791
 *
792
 * @param string $texte
793
 *     Texte à convertir
794
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

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...
795
 *     Texte converti
796
 **/
797
function filtrer_entites($texte) {
798
	if (strpos($texte, '&') === false) {
799
		return $texte;
800
	}
801
	// filtrer
802
	$texte = html2unicode($texte);
803
	// remettre le tout dans le charset cible
804
	$texte = unicode2charset($texte);
805
	// cas particulier des " et ' qu'il faut filtrer aussi
806
	// (on le faisait deja avec un &quot;)
807 View Code Duplication
	if (strpos($texte, "&#") !== 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...
808
		$texte = str_replace(array("&#039;", "&#39;", "&#034;", "&#34;"), array("'", "'", '"', '"'), $texte);
809
	}
810
811
	return $texte;
812
}
813
814
815
if (!function_exists('filtre_filtrer_entites_dist')) {
816
	/**
817
	 * Version sécurisée de filtrer_entites
818
	 * 
819
	 * @uses interdire_scripts()
820
	 * @uses filtrer_entites()
821
	 * 
822
	 * @param string $t
823
	 * @return string
824
	 */
825
	function filtre_filtrer_entites_dist($t) {
0 ignored issues
show
Best Practice introduced by
The function filtre_filtrer_entites_dist() has been defined more than once; this definition is ignored, only the first definition in config/ecran_securite.php (L658-661) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
826
		include_spip('inc/texte');
827
		return interdire_scripts(filtrer_entites($t));
828
	}
829
}
830
831
832
/**
833
 * Supprime des caractères illégaux
834
 *
835
 * Remplace les caractères de controle par le caractère `-`
836
 *
837
 * @link http://www.w3.org/TR/REC-xml/#charsets
838
 *
839
 * @param string|array $texte
840
 * @return string|array
841
 **/
842
function supprimer_caracteres_illegaux($texte) {
843
	static $from = "\x0\x1\x2\x3\x4\x5\x6\x7\x8\xB\xC\xE\xF\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
844
	static $to = null;
845
846
	if (is_array($texte)) {
847
		return array_map('supprimer_caracteres_illegaux', $texte);
848
	}
849
850
	if (!$to) {
851
		$to = str_repeat('-', strlen($from));
852
	}
853
854
	return strtr($texte, $from, $to);
855
}
856
857
/**
858
 * Correction de caractères
859
 *
860
 * Supprimer les caracteres windows non conformes et les caracteres de controle illégaux
861
 *
862
 * @param string|array $texte
863
 * @return string|array
864
 **/
865
function corriger_caracteres($texte) {
866
	$texte = corriger_caracteres_windows($texte);
867
	$texte = supprimer_caracteres_illegaux($texte);
868
869
	return $texte;
870
}
871
872
/**
873
 * Encode du HTML pour transmission XML notamment dans les flux RSS
874
 *
875
 * Ce filtre transforme les liens en liens absolus, importe les entitées html et échappe les tags html.
876
 *
877
 * @filtre
878
 * @link https://www.spip.net/4287
879
 *
880
 * @param string $texte
881
 *     Texte à transformer
882
 * @return string
883
 *     Texte encodé pour XML
884
 */
885
function texte_backend($texte) {
886
887
	static $apostrophe = array("&#8217;", "'"); # n'allouer qu'une fois
888
889
	// si on a des liens ou des images, les passer en absolu
890
	$texte = liens_absolus($texte);
891
892
	// echapper les tags &gt; &lt;
893
	$texte = preg_replace(',&(gt|lt);,S', '&amp;\1;', $texte);
894
895
	// importer les &eacute;
896
	$texte = filtrer_entites($texte);
897
898
	// " -> &quot; et tout ce genre de choses
899
	$u = $GLOBALS['meta']['pcre_u'];
900
	$texte = str_replace("&nbsp;", " ", $texte);
901
	$texte = preg_replace('/\s{2,}/S' . $u, " ", $texte);
902
	// ne pas echapper les sinqle quotes car certains outils de syndication gerent mal
903
	$texte = entites_html($texte, false, false);
904
	// mais bien echapper les double quotes !
905
	$texte = str_replace('"', '&#034;', $texte);
906
907
	// verifier le charset
908
	$texte = charset2unicode($texte);
909
910
	// Caracteres problematiques en iso-latin 1
911
	if (isset($GLOBALS['meta']['charset']) and $GLOBALS['meta']['charset'] == 'iso-8859-1') {
912
		$texte = str_replace(chr(156), '&#156;', $texte);
913
		$texte = str_replace(chr(140), '&#140;', $texte);
914
		$texte = str_replace(chr(159), '&#159;', $texte);
915
	}
916
917
	// l'apostrophe curly pose probleme a certains lecteure de RSS
918
	// et le caractere apostrophe alourdit les squelettes avec PHP
919
	// ==> on les remplace par l'entite HTML
920
	return str_replace($apostrophe, "'", $texte);
921
}
922
923
/**
924
 * Encode et quote du HTML pour transmission XML notamment dans les flux RSS
925
 *
926
 * Comme texte_backend(), mais avec addslashes final pour squelettes avec PHP (rss)
927
 *
928
 * @uses texte_backend()
929
 * @filtre
930
 *
931
 * @param string $texte
932
 *     Texte à transformer
933
 * @return string
934
 *     Texte encodé et quote pour XML
935
 */
936
function texte_backendq($texte) {
937
	return addslashes(texte_backend($texte));
938
}
939
940
941
/**
942
 * Enlève un numéro préfixant un texte
943
 *
944
 * Supprime `10. ` dans la chaine `10. Titre`
945
 *
946
 * @filtre
947
 * @link https://www.spip.net/4314
948
 * @see recuperer_numero() Pour obtenir le numéro
949
 * @example
950
 *     ```
951
 *     [<h1>(#TITRE|supprimer_numero)</h1>]
952
 *     ```
953
 *
954
 * @param string $texte
955
 *     Texte
956
 * @return int|string
957
 *     Numéro de titre, sinon chaîne vide
958
 **/
959
function supprimer_numero($texte) {
960
	return preg_replace(
961
		",^[[:space:]]*([0-9]+)([.)]|" . chr(194) . '?' . chr(176) . ")[[:space:]]+,S",
962
		"", $texte);
963
}
964
965
/**
966
 * Récupère un numéro préfixant un texte
967
 *
968
 * Récupère le numéro `10` dans la chaine `10. Titre`
969
 *
970
 * @filtre
971
 * @link https://www.spip.net/5514
972
 * @see supprimer_numero() Pour supprimer le numéro
973
 * @see balise_RANG_dist() Pour obtenir un numéro de titre
974
 * @example
975
 *     ```
976
 *     [(#TITRE|recuperer_numero)]
977
 *     ```
978
 *
979
 * @param string $texte
980
 *     Texte
981
 * @return int|string
982
 *     Numéro de titre, sinon chaîne vide
983
 **/
984
function recuperer_numero($texte) {
985
	if (preg_match(
986
		",^[[:space:]]*([0-9]+)([.)]|" . chr(194) . '?' . chr(176) . ")[[:space:]]+,S",
987
		$texte, $regs)) {
988
		return strval($regs[1]);
989
	} else {
990
		return '';
991
	}
992
}
993
994
/**
995
 * Suppression basique et brutale de tous les tags
996
 *
997
 * Supprime tous les tags `<...>`.
998
 * Utilisé fréquemment pour écrire des RSS.
999
 *
1000
 * @filtre
1001
 * @link https://www.spip.net/4315
1002
 * @example
1003
 *     ```
1004
 *     <title>[(#TITRE|supprimer_tags|texte_backend)]</title>
1005
 *     ```
1006
 *
1007
 * @note
1008
 *     Ce filtre supprime aussi les signes inférieurs `<` rencontrés.
1009
 *
1010
 * @param string $texte
1011
 *     Texte à échapper
1012
 * @param string $rempl
1013
 *     Inutilisé.
1014
 * @return string
1015
 *     Texte converti
1016
 **/
1017
function supprimer_tags($texte, $rempl = "") {
1018
	$texte = preg_replace(",<(!--|\w|/|!\[endif|!\[if)[^>]*>,US", $rempl, $texte);
1019
	// ne pas oublier un < final non ferme car coupe
1020
	$texte = preg_replace(",<(!--|\w|/).*$,US", $rempl, $texte);
1021
	// mais qui peut aussi etre un simple signe plus petit que
1022
	$texte = str_replace('<', '&lt;', $texte);
1023
1024
	return $texte;
1025
}
1026
1027
/**
1028
 * Convertit les chevrons de tag en version lisible en HTML
1029
 *
1030
 * Transforme les chevrons de tag `<...>` en entité HTML.
1031
 *
1032
 * @filtre
1033
 * @link https://www.spip.net/5515
1034
 * @example
1035
 *     ```
1036
 *     <pre>[(#TEXTE|echapper_tags)]</pre>
1037
 *     ```
1038
 *
1039
 * @param string $texte
1040
 *     Texte à échapper
1041
 * @param string $rempl
1042
 *     Inutilisé.
1043
 * @return string
1044
 *     Texte converti
1045
 **/
1046
function echapper_tags($texte, $rempl = "") {
0 ignored issues
show
Unused Code introduced by
The parameter $rempl 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...
1047
	$texte = preg_replace("/<([^>]*)>/", "&lt;\\1&gt;", $texte);
1048
1049
	return $texte;
1050
}
1051
1052
/**
1053
 * Convertit un texte HTML en texte brut
1054
 *
1055
 * Enlève les tags d'un code HTML, élimine les doubles espaces.
1056
 *
1057
 * @filtre
1058
 * @link https://www.spip.net/4317
1059
 * @example
1060
 *     ```
1061
 *     <title>[(#TITRE|textebrut) - ][(#NOM_SITE_SPIP|textebrut)]</title>
1062
 *     ```
1063
 *
1064
 * @param string $texte
1065
 *     Texte à convertir
1066
 * @return string
1067
 *     Texte converti
1068
 **/
1069
function textebrut($texte) {
1070
	$u = $GLOBALS['meta']['pcre_u'];
1071
	$texte = preg_replace('/\s+/S' . $u, " ", $texte);
1072
	$texte = preg_replace("/<(p|br)( [^>]*)?" . ">/iS", "\n\n", $texte);
1073
	$texte = preg_replace("/^\n+/", "", $texte);
1074
	$texte = preg_replace("/\n+$/", "", $texte);
1075
	$texte = preg_replace("/\n +/", "\n", $texte);
1076
	$texte = supprimer_tags($texte);
1077
	$texte = preg_replace("/(&nbsp;| )+/S", " ", $texte);
1078
	// nettoyer l'apostrophe curly qui pose probleme a certains rss-readers, lecteurs de mail...
1079
	$texte = str_replace("&#8217;", "'", $texte);
1080
1081
	return $texte;
1082
}
1083
1084
1085
/**
1086
 * Remplace les liens SPIP en liens ouvrant dans une nouvelle fenetre (target=blank)
1087
 *
1088
 * @filtre
1089
 * @link https://www.spip.net/4297
1090
 *
1091
 * @param string $texte
1092
 *     Texte avec des liens
1093
 * @return string
1094
 *     Texte avec liens ouvrants
1095
 **/
1096
function liens_ouvrants($texte) {
1097
	if (preg_match_all(",(<a\s+[^>]*https?://[^>]*class=[\"']spip_(out|url)\b[^>]+>),imsS",
1098
		$texte, $liens, PREG_PATTERN_ORDER)) {
1099
		foreach ($liens[0] as $a) {
1100
			$rel = 'noopener noreferrer ' . extraire_attribut($a, 'rel');
1101
			$ablank = inserer_attribut($a, 'rel', $rel);
1102
			$ablank = inserer_attribut($ablank, 'target', '_blank');
1103
			$texte = str_replace($a, $ablank, $texte);
1104
		}
1105
	}
1106
1107
	return $texte;
1108
}
1109
1110
/**
1111
 * Ajouter un attribut rel="nofollow" sur tous les liens d'un texte
1112
 *
1113
 * @param string $texte
1114
 * @return string
1115
 */
1116
function liens_nofollow($texte) {
1117
	if (stripos($texte, "<a") === false) {
1118
		return $texte;
1119
	}
1120
1121
	if (preg_match_all(",<a\b[^>]*>,UimsS", $texte, $regs, PREG_PATTERN_ORDER)) {
1122
		foreach ($regs[0] as $a) {
1123
			$rel = extraire_attribut($a, "rel");
1124
			if (strpos($rel, "nofollow") === false) {
1125
				$rel = "nofollow" . ($rel ? " $rel" : "");
1126
				$anofollow = inserer_attribut($a, "rel", $rel);
1127
				$texte = str_replace($a, $anofollow, $texte);
1128
			}
1129
		}
1130
	}
1131
1132
	return $texte;
1133
}
1134
1135
/**
1136
 * Transforme les sauts de paragraphe HTML `p` en simples passages à la ligne `br`
1137
 *
1138
 * @filtre
1139
 * @link https://www.spip.net/4308
1140
 * @example
1141
 *     ```
1142
 *     [<div>(#DESCRIPTIF|PtoBR)[(#NOTES|PtoBR)]</div>]
1143
 *     ```
1144
 *
1145
 * @param string $texte
1146
 *     Texte à transformer
1147
 * @return string
1148
 *     Texte sans paraghaphes
1149
 **/
1150
function PtoBR($texte) {
1151
	$u = $GLOBALS['meta']['pcre_u'];
1152
	$texte = preg_replace("@</p>@iS", "\n", $texte);
1153
	$texte = preg_replace("@<p\b.*>@UiS", "<br />", $texte);
1154
	$texte = preg_replace("@^\s*<br />@S" . $u, "", $texte);
1155
1156
	return $texte;
1157
}
1158
1159
1160
/**
1161
 * Assure qu'un texte ne vas pas déborder d'un bloc
1162
 * par la faute d'un mot trop long (souvent des URLs)
1163
 *
1164
 * Ne devrait plus être utilisé et fait directement en CSS par un style
1165
 * `word-wrap:break-word;`
1166
 *
1167
 * @note
1168
 *   Pour assurer la compatibilité du filtre, on encapsule le contenu par
1169
 *   un `div` ou `span` portant ce style CSS inline.
1170
 *
1171
 * @filtre
1172
 * @link https://www.spip.net/4298
1173
 * @link http://www.alsacreations.com/tuto/lire/1038-gerer-debordement-contenu-css.html
1174
 * @deprecated Utiliser le style CSS `word-wrap:break-word;`
1175
 *
1176
 * @param string $texte Texte
1177
 * @return string Texte encadré du style CSS
1178
 */
1179
function lignes_longues($texte) {
1180
	if (!strlen(trim($texte))) {
1181
		return $texte;
1182
	}
1183
	include_spip('inc/texte');
1184
	$tag = preg_match(',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS', $texte) ?
1185
		'div' : 'span';
1186
1187
	return "<$tag style='word-wrap:break-word;'>$texte</$tag>";
1188
}
1189
1190
/**
1191
 * Passe un texte en majuscules, y compris les accents, en HTML
1192
 *
1193
 * Encadre le texte du style CSS `text-transform: uppercase;`.
1194
 * Le cas spécifique du i turc est géré.
1195
 *
1196
 * @filtre
1197
 * @example
1198
 *     ```
1199
 *     [(#EXTENSION|majuscules)]
1200
 *     ```
1201
 *
1202
 * @param string $texte Texte
1203
 * @return string Texte en majuscule
1204
 */
1205
function majuscules($texte) {
1206
	if (!strlen($texte)) {
1207
		return '';
1208
	}
1209
1210
	// Cas du turc
1211
	if ($GLOBALS['spip_lang'] == 'tr') {
1212
		# remplacer hors des tags et des entites
1213 View Code Duplication
		if (preg_match_all(',<[^<>]+>|&[^;]+;,S', $texte, $regs, PREG_SET_ORDER)) {
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...
1214
			foreach ($regs as $n => $match) {
1215
				$texte = str_replace($match[0], "@@SPIP_TURC$n@@", $texte);
1216
			}
1217
		}
1218
1219
		$texte = str_replace('i', '&#304;', $texte);
1220
1221 View Code Duplication
		if ($regs) {
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...
1222
			foreach ($regs as $n => $match) {
1223
				$texte = str_replace("@@SPIP_TURC$n@@", $match[0], $texte);
1224
			}
1225
		}
1226
	}
1227
1228
	// Cas general
1229
	return "<span style='text-transform: uppercase;'>$texte</span>";
1230
}
1231
1232
/**
1233
 * Retourne une taille en octets humainement lisible
1234
 *
1235
 * Tel que "127.4 ko" ou "3.1 Mo"
1236
 *
1237
 * @example
1238
 *     - `[(#TAILLE|taille_en_octets)]`
1239
 *     - `[(#VAL{123456789}|taille_en_octets)]` affiche `117.7 Mo`
1240
 *
1241
 * @filtre
1242
 * @link https://www.spip.net/4316
1243
 * @param int $taille
1244
 * @return string
1245
 **/
1246
function taille_en_octets($taille) {
1247
	if (!defined('_KILOBYTE')) {
1248
		/**
1249
		 * Définit le nombre d'octets dans un Kilobyte
1250
		 *
1251
		 * @var int
1252
		 **/
1253
		define('_KILOBYTE', 1024);
1254
	}
1255
1256
	if ($taille < 1) {
1257
		return '';
1258
	}
1259
	if ($taille < _KILOBYTE) {
1260
		$taille = _T('taille_octets', array('taille' => $taille));
1261 View Code Duplication
	} elseif ($taille < _KILOBYTE * _KILOBYTE) {
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...
1262
		$taille = _T('taille_ko', array('taille' => round($taille / _KILOBYTE, 1)));
1263
	} elseif ($taille < _KILOBYTE * _KILOBYTE * _KILOBYTE) {
1264
		$taille = _T('taille_mo', array('taille' => round($taille / _KILOBYTE / _KILOBYTE, 1)));
1265 View Code Duplication
	} else {
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...
1266
		$taille = _T('taille_go', array('taille' => round($taille / _KILOBYTE / _KILOBYTE / _KILOBYTE, 2)));
1267
	}
1268
1269
	return $taille;
1270
}
1271
1272
1273
/**
1274
 * Rend une chaine utilisable sans dommage comme attribut HTML
1275
 *
1276
 * @example `<a href="#URL_ARTICLE" title="[(#TITRE|attribut_html)]">#TITRE</a>`
1277
 *
1278
 * @filtre
1279
 * @link https://www.spip.net/4282
1280
 * @uses textebrut()
1281
 * @uses texte_backend()
1282
 *
1283
 * @param string $texte
1284
 *     Texte à mettre en attribut
1285
 * @param bool $textebrut
1286
 *     Passe le texte en texte brut (enlève les balises html) ?
1287
 * @return string
1288
 *     Texte prêt pour être utilisé en attribut HTML
1289
 **/
1290
function attribut_html($texte, $textebrut = true) {
1291
	$u = $GLOBALS['meta']['pcre_u'];
1292
	if ($textebrut) {
1293
		$texte = preg_replace(array(",\n,", ",\s(?=\s),msS" . $u), array(" ", ""), textebrut($texte));
1294
	}
1295
	$texte = texte_backend($texte);
1296
	$texte = str_replace(array("'", '"'), array('&#039;', '&#034;'), $texte);
1297
1298
	return preg_replace(array("/&(amp;|#38;)/", "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/"), array("&", "&#38;"),
1299
		$texte);
1300
}
1301
1302
1303
/**
1304
 * Vider les URL nulles
1305
 *
1306
 * - Vide les URL vides comme `http://` ou `mailto:` (sans rien d'autre)
1307
 * - échappe les entités et gère les `&amp;`
1308
 *
1309
 * @uses entites_html()
1310
 *
1311
 * @param string $url
1312
 *     URL à vérifier et échapper
1313
 * @param bool $entites
1314
 *     `true` pour échapper les entités HTML.
1315
 * @return string
1316
 *     URL ou chaîne vide
1317
 **/
1318
function vider_url($url, $entites = true) {
1319
	# un message pour abs_url
1320
	$GLOBALS['mode_abs_url'] = 'url';
1321
	$url = trim($url);
1322
	$r = ",^(?:" . _PROTOCOLES_STD . '):?/?/?$,iS';
1323
1324
	return preg_match($r, $url) ? '' : ($entites ? entites_html($url) : $url);
1325
}
1326
1327
1328
/**
1329
 * Maquiller une adresse e-mail
1330
 *
1331
 * Remplace `@` par 3 caractères aléatoires.
1332
 *
1333
 * @uses creer_pass_aleatoire()
1334
 *
1335
 * @param string $texte Adresse email
1336
 * @return string Adresse email maquillée
1337
 **/
1338
function antispam($texte) {
1339
	include_spip('inc/acces');
1340
	$masque = creer_pass_aleatoire(3);
1341
1342
	return preg_replace("/@/", " $masque ", $texte);
1343
}
1344
1345
/**
1346
 * Vérifie un accès à faible sécurité
1347
 *
1348
 * Vérifie qu'un visiteur peut accéder à la page demandée,
1349
 * qui est protégée par une clé, calculée à partir du low_sec de l'auteur,
1350
 * et des paramètres le composant l'appel (op, args)
1351
 *
1352
 * @example
1353
 *     `[(#ID_AUTEUR|securiser_acces{#ENV{cle}, rss, #ENV{op}, #ENV{args}}|sinon_interdire_acces)]`
1354
 *
1355
 * @see  bouton_spip_rss() pour générer un lien de faible sécurité pour les RSS privés
1356
 * @see  afficher_low_sec() pour calculer une clé valide
1357
 * @uses verifier_low_sec()
1358
 *
1359
 * @filtre
1360
 * @param int $id_auteur
1361
 *     L'auteur qui demande la page
1362
 * @param string $cle
1363
 *     La clé à tester
1364
 * @param string $dir
1365
 *     Un type d'accès (nom du répertoire dans lequel sont rangés les squelettes demandés, tel que 'rss')
1366
 * @param string $op
1367
 *     Nom de l'opération éventuelle
1368
 * @param string $args
1369
 *     Nom de l'argument calculé
1370
 * @return bool
1371
 *     True si on a le droit d'accès, false sinon.
1372
 **/
1373
function securiser_acces($id_auteur, $cle, $dir, $op = '', $args = '') {
1374
	include_spip('inc/acces');
1375
	if ($op) {
1376
		$dir .= " $op $args";
1377
	}
1378
1379
	return verifier_low_sec($id_auteur, $cle, $dir);
1380
}
1381
1382
/**
1383
 * Retourne le second paramètre lorsque
1384
 * le premier est considere vide, sinon retourne le premier paramètre.
1385
 *
1386
 * En php `sinon($a, 'rien')` retourne `$a`, ou `'rien'` si `$a` est vide.
1387
 * En filtre SPIP `|sinon{#TEXTE, rien}` : affiche `#TEXTE` ou `rien` si `#TEXTE` est vide,
1388
 *
1389
 * @filtre
1390
 * @see filtre_logique() pour la compilation du filtre dans un squelette
1391
 * @link https://www.spip.net/4313
1392
 * @note
1393
 *     L'utilisation de `|sinon` en tant que filtre de squelette
1394
 *     est directement compilé dans `public/references` par la fonction `filtre_logique()`
1395
 *
1396
 * @param mixed $texte
1397
 *     Contenu de reference a tester
1398
 * @param mixed $sinon
1399
 *     Contenu a retourner si le contenu de reference est vide
1400
 * @return mixed
1401
 *     Retourne $texte, sinon $sinon.
1402
 **/
1403
function sinon($texte, $sinon = '') {
1404
	if ($texte or (!is_array($texte) and strlen($texte))) {
1405
		return $texte;
1406
	} else {
1407
		return $sinon;
1408
	}
1409
}
1410
1411
/**
1412
 * Filtre `|choixsivide{vide, pas vide}` alias de `|?{si oui, si non}` avec les arguments inversés
1413
 *
1414
 * @example
1415
 *     `[(#TEXTE|choixsivide{vide, plein})]` affiche vide si le `#TEXTE`
1416
 *     est considéré vide par PHP (chaîne vide, false, 0, tableau vide, etc…).
1417
 *     C'est l'équivalent de `[(#TEXTE|?{plein, vide})]`
1418
 *
1419
 * @filtre
1420
 * @see choixsiegal()
1421
 * @link https://www.spip.net/4189
1422
 *
1423
 * @param mixed $a
1424
 *     La valeur à tester
1425
 * @param mixed $vide
1426
 *     Ce qui est retourné si `$a` est considéré vide
1427
 * @param mixed $pasvide
1428
 *     Ce qui est retourné sinon
1429
 * @return mixed
1430
 **/
1431
function choixsivide($a, $vide, $pasvide) {
1432
	return $a ? $pasvide : $vide;
1433
}
1434
1435
/**
1436
 * Filtre `|choixsiegal{valeur, sioui, sinon}`
1437
 *
1438
 * @example
1439
 *     `#LANG_DIR|choixsiegal{ltr,left,right}` retourne `left` si
1440
 *      `#LANG_DIR` vaut `ltr` et `right` sinon.
1441
 *
1442
 * @filtre
1443
 * @link https://www.spip.net/4148
1444
 *
1445
 * @param mixed $a1
1446
 *     La valeur à tester
1447
 * @param mixed $a2
1448
 *     La valeur de comparaison
1449
 * @param mixed $v
1450
 *     Ce qui est retourné si la comparaison est vraie
1451
 * @param mixed $f
1452
 *     Ce qui est retourné sinon
1453
 * @return mixed
1454
 **/
1455
function choixsiegal($a1, $a2, $v, $f) {
1456
	return ($a1 == $a2) ? $v : $f;
1457
}
1458
1459
//
1460
// Export iCal
1461
//
1462
1463
/**
1464
 * Adapte un texte pour être inséré dans une valeur d'un export ICAL
1465
 *
1466
 * Passe le texte en utf8, enlève les sauts de lignes et échappe les virgules.
1467
 *
1468
 * @example `SUMMARY:[(#TITRE|filtrer_ical)]`
1469
 * @filtre
1470
 *
1471
 * @param string $texte
1472
 * @return string
1473
 **/
1474
function filtrer_ical($texte) {
1475
	#include_spip('inc/charsets');
1476
	$texte = html2unicode($texte);
1477
	$texte = unicode2charset(charset2unicode($texte, $GLOBALS['meta']['charset'], 1), 'utf-8');
0 ignored issues
show
Unused Code introduced by
The call to charset2unicode() has too many arguments starting with 1.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1478
	$texte = preg_replace("/\n/", " ", $texte);
1479
	$texte = preg_replace("/,/", "\,", $texte);
1480
1481
	return $texte;
1482
}
1483
1484
1485
/**
1486
 * Transforme les sauts de ligne simples en sauts forcés avec `_ `
1487
 *
1488
 * Ne modifie pas les sauts de paragraphe (2 sauts consécutifs au moins),
1489
 * ou les retours à l'intérieur de modèles ou de certaines balises html.
1490
 *
1491
 * @note
1492
 *     Cette fonction pouvait être utilisée pour forcer les alinéas,
1493
 *     (retours à la ligne sans saut de paragraphe), mais ce traitement
1494
 *     est maintenant automatique.
1495
 *     Cf. plugin Textwheel et la constante _AUTOBR
1496
 *
1497
 * @uses echappe_html()
1498
 * @uses echappe_retour()
1499
 *
1500
 * @param string $texte
1501
 * @param string $delim
1502
 *      Ce par quoi sont remplacés les sauts
1503
 * @return string
1504
 **/
1505
function post_autobr($texte, $delim = "\n_ ") {
1506
	if (!function_exists('echappe_html')) {
1507
		include_spip('inc/texte_mini');
1508
	}
1509
	$texte = str_replace("\r\n", "\r", $texte);
1510
	$texte = str_replace("\r", "\n", $texte);
1511
1512 View Code Duplication
	if (preg_match(",\n+$,", $texte, $fin)) {
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...
1513
		$texte = substr($texte, 0, -strlen($fin = $fin[0]));
1514
	} else {
1515
		$fin = '';
1516
	}
1517
1518
	$texte = echappe_html($texte, '', true);
1519
1520
	// echapper les modeles
1521
	if (strpos($texte, "<") !== false) {
1522
		include_spip('inc/lien');
1523
		if (defined('_PREG_MODELE')) {
1524
			$preg_modeles = "@" . _PREG_MODELE . "@imsS";
1525
			$texte = echappe_html($texte, '', true, $preg_modeles);
1526
		}
1527
	}
1528
1529
	$debut = '';
1530
	$suite = $texte;
1531
	while ($t = strpos('-' . $suite, "\n", 1)) {
1532
		$debut .= substr($suite, 0, $t - 1);
1533
		$suite = substr($suite, $t);
1534
		$car = substr($suite, 0, 1);
1535
		if (($car <> '-') and ($car <> '_') and ($car <> "\n") and ($car <> "|") and ($car <> "}")
1536
			and !preg_match(',^\s*(\n|</?(quote|div|dl|dt|dd)|$),S', ($suite))
1537
			and !preg_match(',</?(quote|div|dl|dt|dd)> *$,iS', $debut)
1538
		) {
1539
			$debut .= $delim;
1540
		} else {
1541
			$debut .= "\n";
1542
		}
1543 View Code Duplication
		if (preg_match(",^\n+,", $suite, $regs)) {
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...
1544
			$debut .= $regs[0];
1545
			$suite = substr($suite, strlen($regs[0]));
1546
		}
1547
	}
1548
	$texte = $debut . $suite;
1549
1550
	$texte = echappe_retour($texte);
1551
1552
	return $texte . $fin;
1553
}
1554
1555
1556
/**
1557
 * Expression régulière pour obtenir le contenu des extraits idiomes `<:module:cle:>`
1558
 *
1559
 * @var string
1560
 */
1561
define('_EXTRAIRE_IDIOME', '@<:(?:([a-z0-9_]+):)?([a-z0-9_]+):>@isS');
1562
1563
/**
1564
 * Extrait une langue des extraits idiomes (`<:module:cle_de_langue:>`)
1565
 *
1566
 * Retrouve les balises `<:cle_de_langue:>` d'un texte et remplace son contenu
1567
 * par l'extrait correspondant à la langue demandée (si possible), sinon dans la
1568
 * langue par défaut du site.
1569
 *
1570
 * Ne pas mettre de span@lang=fr si on est déjà en fr.
1571
 *
1572
 * @filtre
1573
 * @uses inc_traduire_dist()
1574
 * @uses code_echappement()
1575
 * @uses echappe_retour()
1576
 *
1577
 * @param string $letexte
1578
 * @param string $lang
0 ignored issues
show
Documentation introduced by
Should the type for parameter $lang 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...
1579
 *     Langue à retrouver (si vide, utilise la langue en cours).
1580
 * @param array $options Options {
1581
 * @type bool $echappe_span
1582
 *         True pour échapper les balises span (false par défaut)
1583
 * @type string $lang_defaut
1584
 *         Code de langue : permet de définir la langue utilisée par défaut,
1585
 *         en cas d'absence de traduction dans la langue demandée.
1586
 *         Par défaut la langue du site.
1587
 *         Indiquer 'aucune' pour ne pas retourner de texte si la langue
1588
 *         exacte n'a pas été trouvée.
1589
 * }
1590
 * @return string
1591
 **/
1592
function extraire_idiome($letexte, $lang = null, $options = array()) {
1593
	static $traduire = false;
1594
	if ($letexte
1595
		and preg_match_all(_EXTRAIRE_IDIOME, $letexte, $regs, PREG_SET_ORDER)
1596
	) {
1597
		if (!$traduire) {
1598
			$traduire = charger_fonction('traduire', 'inc');
1599
			include_spip('inc/lang');
1600
		}
1601
		if (!$lang) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lang of type string|null is loosely compared to false; 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...
1602
			$lang = $GLOBALS['spip_lang'];
1603
		}
1604
		// Compatibilité avec le prototype de fonction précédente qui utilisait un boolean
1605
		if (is_bool($options)) {
1606
			$options = array('echappe_span' => $options);
1607
		}
1608 View Code Duplication
		if (!isset($options['echappe_span'])) {
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...
1609
			$options = array_merge($options, array('echappe_span' => false));
1610
		}
1611
1612
		foreach ($regs as $reg) {
1613
			$cle = ($reg[1] ? $reg[1] . ':' : '') . $reg[2];
1614
			$desc = $traduire($cle, $lang, true);
1615
			$l = $desc->langue;
1616
			// si pas de traduction, on laissera l'écriture de l'idiome entier dans le texte.
1617
			if (strlen($desc->texte)) {
1618
				$trad = code_echappement($desc->texte, 'idiome', false);
1619
				if ($l !== $lang) {
1620
					$trad = str_replace("'", '"', inserer_attribut($trad, 'lang', $l));
1621
				}
1622 View Code Duplication
				if (lang_dir($l) !== lang_dir($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...
1623
					$trad = str_replace("'", '"', inserer_attribut($trad, 'dir', lang_dir($l)));
1624
				}
1625
				if (!$options['echappe_span']) {
1626
					$trad = echappe_retour($trad, 'idiome');
1627
				}
1628
				$letexte = str_replace($reg[0], $trad, $letexte);
1629
			}
1630
		}
1631
	}
1632
	return $letexte;
1633
}
1634
1635
/**
1636
 * Expression régulière pour obtenir le contenu des extraits polyglottes `<multi>`
1637
 *
1638
 * @var string
1639
 */
1640
define('_EXTRAIRE_MULTI', "@<multi>(.*?)</multi>@sS");
1641
1642
1643
/**
1644
 * Extrait une langue des extraits polyglottes (`<multi>`)
1645
 *
1646
 * Retrouve les balises `<multi>` d'un texte et remplace son contenu
1647
 * par l'extrait correspondant à la langue demandée.
1648
 *
1649
 * Si la langue demandée n'est pas trouvée dans le multi, ni une langue
1650
 * approchante (exemple `fr` si on demande `fr_TU`), on retourne l'extrait
1651
 * correspondant à la langue par défaut (option 'lang_defaut'), qui est
1652
 * par défaut la langue du site. Et si l'extrait n'existe toujours pas
1653
 * dans cette langue, ça utilisera la première langue utilisée
1654
 * dans la balise `<multi>`.
1655
 *
1656
 * Ne pas mettre de span@lang=fr si on est déjà en fr.
1657
 *
1658
 * @filtre
1659
 * @link https://www.spip.net/5332
1660
 *
1661
 * @uses extraire_trads()
1662
 * @uses approcher_langue()
1663
 * @uses lang_typo()
1664
 * @uses code_echappement()
1665
 * @uses echappe_retour()
1666
 *
1667
 * @param string $letexte
1668
 * @param string $lang
0 ignored issues
show
Documentation introduced by
Should the type for parameter $lang 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...
1669
 *     Langue à retrouver (si vide, utilise la langue en cours).
1670
 * @param array $options Options {
1671
 * @type bool $echappe_span
1672
 *         True pour échapper les balises span (false par défaut)
1673
 * @type string $lang_defaut
1674
 *         Code de langue : permet de définir la langue utilisée par défaut,
1675
 *         en cas d'absence de traduction dans la langue demandée.
1676
 *         Par défaut la langue du site.
1677
 *         Indiquer 'aucune' pour ne pas retourner de texte si la langue
1678
 *         exacte n'a pas été trouvée.
1679
 * }
1680
 * @return string
1681
 **/
1682
function extraire_multi($letexte, $lang = null, $options = array()) {
1683
1684
	if ($letexte
1685
		and preg_match_all(_EXTRAIRE_MULTI, $letexte, $regs, PREG_SET_ORDER)
1686
	) {
1687
		if (!$lang) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lang of type string|null is loosely compared to false; 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...
1688
			$lang = $GLOBALS['spip_lang'];
1689
		}
1690
1691
		// Compatibilité avec le prototype de fonction précédente qui utilisait un boolean
1692
		if (is_bool($options)) {
1693
			$options = array('echappe_span' => $options, 'lang_defaut' => _LANGUE_PAR_DEFAUT);
1694
		}
1695 View Code Duplication
		if (!isset($options['echappe_span'])) {
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...
1696
			$options = array_merge($options, array('echappe_span' => false));
1697
		}
1698
		if (!isset($options['lang_defaut'])) {
1699
			$options = array_merge($options, array('lang_defaut' => _LANGUE_PAR_DEFAUT));
1700
		}
1701
1702
		include_spip('inc/lang');
1703
		foreach ($regs as $reg) {
1704
			// chercher la version de la langue courante
1705
			$trads = extraire_trads($reg[1]);
1706
			if ($l = approcher_langue($trads, $lang)) {
1707
				$trad = $trads[$l];
1708
			} else {
1709
				if ($options['lang_defaut'] == 'aucune') {
1710
					$trad = '';
1711
				} else {
1712
					// langue absente, prendre le fr ou une langue précisée (meme comportement que inc/traduire.php)
1713
					// ou la premiere dispo
1714
					// mais typographier le texte selon les regles de celle-ci
1715
					// Attention aux blocs multi sur plusieurs lignes
1716
					if (!$l = approcher_langue($trads, $options['lang_defaut'])) {
1717
						$l = key($trads);
1718
					}
1719
					$trad = $trads[$l];
1720
					$typographie = charger_fonction(lang_typo($l), 'typographie');
1721
					$trad = $typographie($trad);
1722
					// Tester si on echappe en span ou en div
1723
					// il ne faut pas echapper en div si propre produit un seul paragraphe
1724
					include_spip('inc/texte');
1725
					$trad_propre = preg_replace(",(^<p[^>]*>|</p>$),Uims", "", propre($trad));
1726
					$mode = preg_match(',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS', $trad_propre) ? 'div' : 'span';
1727
					if ($mode === 'div') {
1728
						$trad = rtrim($trad) . "\n\n";
1729
					}
1730
					$trad = code_echappement($trad, 'multi', false, $mode);
1731
					$trad = str_replace("'", '"', inserer_attribut($trad, 'lang', $l));
1732 View Code Duplication
					if (lang_dir($l) !== lang_dir($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...
1733
						$trad = str_replace("'", '"', inserer_attribut($trad, 'dir', lang_dir($l)));
1734
					}
1735
					if (!$options['echappe_span']) {
1736
						$trad = echappe_retour($trad, 'multi');
1737
					}
1738
				}
1739
			}
1740
			$letexte = str_replace($reg[0], $trad, $letexte);
1741
		}
1742
	}
1743
1744
	return $letexte;
1745
}
1746
1747
/**
1748
 * Convertit le contenu d'une balise `<multi>` en un tableau
1749
 *
1750
 * Exemple de blocs.
1751
 * - `texte par défaut [fr] en français [en] en anglais`
1752
 * - `[fr] en français [en] en anglais`
1753
 *
1754
 * @param string $bloc
1755
 *     Le contenu intérieur d'un bloc multi
1756
 * @return array [code de langue => texte]
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,string>.

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...
1757
 *     Peut retourner un code de langue vide, lorsqu'un texte par défaut est indiqué.
1758
 **/
1759
function extraire_trads($bloc) {
1760
	$lang = '';
1761
// ce reg fait planter l'analyse multi s'il y a de l'{italique} dans le champ
1762
//	while (preg_match("/^(.*?)[{\[]([a-z_]+)[}\]]/siS", $bloc, $regs)) {
1763
	while (preg_match("/^(.*?)[\[]([a-z_]+)[\]]/siS", $bloc, $regs)) {
1764
		$texte = trim($regs[1]);
1765
		if ($texte or $lang) {
1766
			$trads[$lang] = $texte;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$trads was never initialized. Although not strictly required by PHP, it is generally a good practice to add $trads = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1767
		}
1768
		$bloc = substr($bloc, strlen($regs[0]));
1769
		$lang = $regs[2];
1770
	}
1771
	$trads[$lang] = $bloc;
0 ignored issues
show
Bug introduced by
The variable $trads does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1772
1773
	return $trads;
1774
}
1775
1776
1777
/**
1778
 * Calculer l'initiale d'un nom
1779
 *
1780
 * @param string $nom
1781
 * @return string L'initiale en majuscule
1782
 */
1783
function filtre_initiale($nom) {
1784
	return spip_substr(trim(strtoupper(extraire_multi($nom))), 0, 1);
1785
}
1786
1787
1788
/**
1789
 * Retourne la donnée si c'est la première fois qu'il la voit
1790
 *
1791
 * Il est possible de gérer différentes "familles" de données avec
1792
 * le second paramètre.
1793
 *
1794
 * @filtre
1795
 * @link https://www.spip.net/4320
1796
 * @example
1797
 *     ```
1798
 *     [(#ID_SECTEUR|unique)]
1799
 *     [(#ID_SECTEUR|unique{tete})] n'a pas d'incidence sur
1800
 *     [(#ID_SECTEUR|unique{pied})]
1801
 *     [(#ID_SECTEUR|unique{pied,1})] affiche le nombre d'éléments.
1802
 *     Préférer totefois #TOTAL_UNIQUE{pied}
1803
 *     ```
1804
 *
1805
 * @todo
1806
 *    Ameliorations possibles :
1807
 *
1808
 *    1) si la donnée est grosse, mettre son md5 comme clé
1809
 *    2) purger $mem quand on change de squelette (sinon bug inclusions)
1810
 *
1811
 * @param string $donnee
1812
 *      Donnée que l'on souhaite unique
1813
 * @param string $famille
1814
 *      Famille de stockage (1 unique donnée par famille)
1815
 *
1816
 *      - _spip_raz_ : (interne) Vide la pile de mémoire et la retourne
1817
 *      - _spip_set_ : (interne) Affecte la pile de mémoire avec la donnée
1818
 * @param bool $cpt
1819
 *      True pour obtenir le nombre d'éléments différents stockés
1820
 * @return string|int|array|null|void
1821
 *
1822
 *      - string : Donnée si c'est la première fois qu'elle est vue
1823
 *      - void : si la donnée a déjà été vue
1824
 *      - int : si l'on demande le nombre d'éléments
1825
 *      - array (interne) : si on dépile
1826
 *      - null (interne) : si on empile
1827
 **/
1828
function unique($donnee, $famille = '', $cpt = false) {
1829
	static $mem = array();
1830
	// permettre de vider la pile et de la restaurer
1831
	// pour le calcul de introduction...
1832
	if ($famille == '_spip_raz_') {
1833
		$tmp = $mem;
1834
		$mem = array();
1835
1836
		return $tmp;
1837
	} elseif ($famille == '_spip_set_') {
1838
		$mem = $donnee;
1839
1840
		return;
1841
	}
1842
	// eviter une notice
1843
	if (!isset($mem[$famille])) {
1844
		$mem[$famille] = array();
1845
	}
1846
	if ($cpt) {
1847
		return count($mem[$famille]);
1848
	}
1849
	// eviter une notice
1850
	if (!isset($mem[$famille][$donnee])) {
1851
		$mem[$famille][$donnee] = 0;
1852
	}
1853
	if (!($mem[$famille][$donnee]++)) {
1854
		return $donnee;
1855
	}
1856
}
1857
1858
1859
/**
1860
 * Filtre qui alterne des valeurs en fonction d'un compteur
1861
 *
1862
 * Affiche à tour de rôle et dans l'ordre, un des arguments transmis
1863
 * à chaque incrément du compteur.
1864
 *
1865
 * S'il n'y a qu'un seul argument, et que c'est un tableau,
1866
 * l'alternance se fait sur les valeurs du tableau.
1867
 *
1868
 * Souvent appliqué à l'intérieur d'une boucle, avec le compteur `#COMPTEUR_BOUCLE`
1869
 *
1870
 * @example
1871
 *     - `[(#COMPTEUR_BOUCLE|alterner{bleu,vert,rouge})]`
1872
 *     - `[(#COMPTEUR_BOUCLE|alterner{#LISTE{bleu,vert,rouge}})]`
1873
 *
1874
 * @filtre
1875
 * @link https://www.spip.net/4145
1876
 *
1877
 * @param int $i
1878
 *     Le compteur
1879
 * @return mixed
1880
 *     Une des valeurs en fonction du compteur.
1881
 **/
1882
function alterner($i) {
1883
	// recuperer les arguments (attention fonctions un peu space)
1884
	$num = func_num_args();
1885
	$args = func_get_args();
1886
1887
	if ($num == 2 && is_array($args[1])) {
1888
		$args = $args[1];
1889
		array_unshift($args, '');
1890
		$num = count($args);
1891
	}
1892
1893
	// renvoyer le i-ieme argument, modulo le nombre d'arguments
1894
	return $args[(intval($i) - 1) % ($num - 1) + 1];
1895
}
1896
1897
1898
/**
1899
 * Récupérer un attribut d'une balise HTML
1900
 *
1901
 * la regexp est mortelle : cf. `tests/unit/filtres/extraire_attribut.php`
1902
 * Si on a passé un tableau de balises, renvoyer un tableau de résultats
1903
 * (dans ce cas l'option `$complet` n'est pas disponible)
1904
 *
1905
 * @param string|array $balise
1906
 *     Texte ou liste de textes dont on veut extraire des balises
1907
 * @param string $attribut
1908
 *     Nom de l'attribut désiré
1909
 * @param bool $complet
1910
 *     True pour retourner un tableau avec
1911
 *     - le texte de la balise
1912
 *     - l'ensemble des résultats de la regexp ($r)
1913
 * @return string|array
1914
 *     - Texte de l'attribut retourné, ou tableau des texte d'attributs
1915
 *       (si 1er argument tableau)
1916
 *     - Tableau complet (si 2e argument)
1917
 **/
1918
function extraire_attribut($balise, $attribut, $complet = false) {
1919
	if (is_array($balise)) {
1920
		array_walk(
1921
			$balise,
1922
			function(&$a, $key, $t){
1923
				$a = extraire_attribut($a, $t);
1924
			},
1925
			$attribut
1926
		);
1927
1928
		return $balise;
1929
	}
1930
	if (preg_match(
1931
		',(^.*?<(?:(?>\s*)(?>[\w:.-]+)(?>(?:=(?:"[^"]*"|\'[^\']*\'|[^\'"]\S*))?))*?)(\s+'
1932
		. $attribut
1933
		. '(?:=\s*("[^"]*"|\'[^\']*\'|[^\'"]\S*))?)()((?:[\s/][^>]*)?>.*),isS',
1934
1935
		$balise, $r)) {
1936
		if (isset($r[3][0]) and ($r[3][0] == '"' || $r[3][0] == "'")) {
1937
			$r[4] = substr($r[3], 1, -1);
1938
			$r[3] = $r[3][0];
1939
		} elseif ($r[3] !== '') {
1940
			$r[4] = $r[3];
1941
			$r[3] = '';
1942
		} else {
1943
			$r[4] = trim($r[2]);
1944
		}
1945
		$att = $r[4];
1946 View Code Duplication
		if (strpos($att, "&#") !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1947
			$att = str_replace(array("&#039;", "&#39;", "&#034;", "&#34;"), array("'", "'", '"', '"'), $att);
1948
		}
1949
		$att = filtrer_entites($att);
1950
	} else {
1951
		$att = null;
1952
	}
1953
1954
	if ($complet) {
1955
		return array($att, $r);
1956
	} else {
1957
		return $att;
1958
	}
1959
}
1960
1961
/**
1962
 * Insérer (ou modifier) un attribut html dans une balise
1963
 *
1964
 * @example
1965
 *     - `[(#LOGO_ARTICLE|inserer_attribut{class, logo article})]`
1966
 *     - `[(#LOGO_ARTICLE|inserer_attribut{alt, #TTTRE|attribut_html|couper{60}})]`
1967
 *     - `[(#FICHIER|image_reduire{40}|inserer_attribut{data-description, #DESCRIPTIF})]`
1968
 *       Laissera les balises HTML de la valeur (ici `#DESCRIPTIF`) si on n'applique pas le
1969
 *       filtre `attribut_html` dessus.
1970
 *
1971
 * @filtre
1972
 * @link https://www.spip.net/4294
1973
 * @uses attribut_html()
1974
 * @uses extraire_attribut()
1975
 *
1976
 * @param string $balise
1977
 *     Code html de la balise (ou contenant une balise)
1978
 * @param string $attribut
1979
 *     Nom de l'attribut html à modifier
1980
 * @param string $val
1981
 *     Valeur de l'attribut à appliquer
1982
 * @param bool $proteger
1983
 *     Prépare la valeur en tant qu'attribut de balise (mais conserve les balises html).
1984
 * @param bool $vider
1985
 *     True pour vider l'attribut. Une chaîne vide pour `$val` fera pareil.
1986
 * @return string
1987
 *     Code html modifié
1988
 **/
1989
function inserer_attribut($balise, $attribut, $val, $proteger = true, $vider = false) {
1990
	// preparer l'attribut
1991
	// supprimer les &nbsp; etc mais pas les balises html
1992
	// qui ont un sens dans un attribut value d'un input
1993
	if ($proteger) {
1994
		$val = attribut_html($val, false);
1995
	}
1996
1997
	// echapper les ' pour eviter tout bug
1998
	$val = str_replace("'", "&#039;", $val);
1999
	if ($vider and strlen($val) == 0) {
2000
		$insert = '';
2001
	} else {
2002
		$insert = " $attribut='$val'";
2003
	}
2004
2005
	list($old, $r) = extraire_attribut($balise, $attribut, true);
2006
2007
	if ($old !== null) {
2008
		// Remplacer l'ancien attribut du meme nom
2009
		$balise = $r[1] . $insert . $r[5];
2010
	} else {
2011
		// preferer une balise " />" (comme <img />)
2012
		if (preg_match(',/>,', $balise)) {
2013
			$balise = preg_replace(",\s?/>,S", $insert . " />", $balise, 1);
2014
		} // sinon une balise <a ...> ... </a>
2015
		else {
2016
			$balise = preg_replace(",\s?>,S", $insert . ">", $balise, 1);
2017
		}
2018
	}
2019
2020
	return $balise;
2021
}
2022
2023
/**
2024
 * Supprime un attribut HTML
2025
 *
2026
 * @example `[(#LOGO_ARTICLE|vider_attribut{class})]`
2027
 *
2028
 * @filtre
2029
 * @link https://www.spip.net/4142
2030
 * @uses inserer_attribut()
2031
 * @see  extraire_attribut()
2032
 *
2033
 * @param string $balise Code HTML de l'élément
2034
 * @param string $attribut Nom de l'attribut à enlever
2035
 * @return string Code HTML sans l'attribut
2036
 **/
2037
function vider_attribut($balise, $attribut) {
2038
	return inserer_attribut($balise, $attribut, '', false, true);
2039
}
2040
2041
/**
2042
 * Fonction support pour les filtres |ajouter_class |supprimer_class |commuter_class
2043
 *
2044
 * @param string $balise
2045
 * @param string|array $class
2046
 * @param string $operation
2047
 * @return string
2048
 */
2049
function modifier_class($balise, $class, $operation='ajouter') {
2050
	if (is_string($class)) {
2051
		$class = explode(' ', trim($class));
2052
	}
2053
	$class = array_filter($class);
2054
2055
	// si la ou les classes ont des caracteres invalides on ne fait rien
2056
	if (preg_match(",[^\w-],", implode('', $class))) {
2057
		return $balise;
2058
	}
2059
2060
	if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class 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...
2061
		$class = array_unique($class);
2062
		$class_courante = extraire_attribut($balise, 'class');
2063
2064
		$class_new = $class_courante;
2065
		foreach ($class as $c) {
2066
			if ($c) {
2067
				$is_class_presente = false;
2068
				if (strpos($class_courante, $c) !== false
2069
					and preg_match("/(^|\s)".preg_quote($c)."($|\s)/", $class_courante)) {
2070
					$is_class_presente = true;
2071
				}
2072
				if (in_array($operation, ['ajouter', 'commuter'])
2073
					and !$is_class_presente) {
2074
					$class_new = rtrim($class_new) . " " . $c;
2075
				}
2076
				elseif (in_array($operation, ['supprimer', 'commuter'])
2077
					and $is_class_presente) {
2078
					$class_new = trim(preg_replace("/(^|\s)".preg_quote($c)."($|\s)/", "\\1", $class_new));
2079
				}
2080
			}
2081
		}
2082
2083
		if ($class_new !== $class_courante) {
2084
			if (strlen($class_new)) {
2085
				$balise = inserer_attribut($balise, 'class', $class_new);
0 ignored issues
show
Bug introduced by
It seems like $class_new defined by $class_courante on line 2064 can also be of type array; however, inserer_attribut() 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...
2086
			}
2087
			elseif ($class_courante) {
2088
				$balise = vider_attribut($balise, 'class');
2089
			}
2090
		}
2091
	}
2092
2093
	return $balise;
2094
}
2095
2096
/**
2097
 * Ajoute une ou plusieurs classes sur une balise (si pas deja presentes)
2098
 * @param string $balise
2099
 * @param string|array $class
2100
 * @return string
2101
 */
2102
function ajouter_class($balise, $class){
2103
	return modifier_class($balise, $class, 'ajouter');
2104
}
2105
2106
/**
2107
 * Supprime une ou plusieurs classes sur une balise (si presentes)
2108
 * @param string $balise
2109
 * @param string|array $class
2110
 * @return string
2111
 */
2112
function supprimer_class($balise, $class){
2113
	return modifier_class($balise, $class, 'supprimer');
2114
}
2115
2116
/**
2117
 * Bascule une ou plusieurs classes sur une balise : ajoutees si absentes, supprimees si presentes
2118
 *
2119
 * @param string $balise
2120
 * @param string|array $class
2121
 * @return string
2122
 */
2123
function commuter_class($balise, $class){
2124
	return modifier_class($balise, $class, 'commuter');
2125
}
2126
2127
/**
2128
 * Un filtre pour déterminer le nom du statut des inscrits
2129
 *
2130
 * @param void|int $id
2131
 * @param string $mode
2132
 * @return string
2133
 */
2134
function tester_config($id, $mode = '') {
2135
	include_spip('action/inscrire_auteur');
2136
2137
	return tester_statut_inscription($mode, $id);
2138
}
2139
2140
//
2141
// Quelques fonctions de calcul arithmetique
2142
//
2143
function floatstr($a) { return str_replace(',','.',(string)floatval($a)); }
2144
function strize($f, $a, $b) { return floatstr($f(floatstr($a),floatstr($b))); }
2145
2146
/**
2147
 * Additionne 2 nombres
2148
 *
2149
 * @filtre
2150
 * @link https://www.spip.net/4307
2151
 * @see moins()
2152
 * @example
2153
 *     ```
2154
 *     [(#VAL{28}|plus{14})]
2155
 *     ```
2156
 *
2157
 * @param int $a
2158
 * @param int $b
2159
 * @return int $a+$b
2160
 **/
2161
function plus($a, $b) {
2162
	return $a + $b;
2163
}
2164
function strplus($a, $b) {return strize('plus', $a, $b);}
2165
/**
2166
 * Soustrait 2 nombres
2167
 *
2168
 * @filtre
2169
 * @link https://www.spip.net/4302
2170
 * @see plus()
2171
 * @example
2172
 *     ```
2173
 *     [(#VAL{28}|moins{14})]
2174
 *     ```
2175
 *
2176
 * @param int $a
2177
 * @param int $b
2178
 * @return int $a-$b
2179
 **/
2180
function moins($a, $b) {
2181
	return $a - $b;
2182
}
2183
function strmoins($a, $b) {return strize('moins', $a, $b);}
2184
2185
/**
2186
 * Multiplie 2 nombres
2187
 *
2188
 * @filtre
2189
 * @link https://www.spip.net/4304
2190
 * @see div()
2191
 * @see modulo()
2192
 * @example
2193
 *     ```
2194
 *     [(#VAL{28}|mult{14})]
2195
 *     ```
2196
 *
2197
 * @param int $a
2198
 * @param int $b
2199
 * @return int $a*$b
2200
 **/
2201
function mult($a, $b) {
2202
	return $a * $b;
2203
}
2204
function strmult($a, $b) {return strize('mult', $a, $b);}
2205
2206
/**
2207
 * Divise 2 nombres
2208
 *
2209
 * @filtre
2210
 * @link https://www.spip.net/4279
2211
 * @see mult()
2212
 * @see modulo()
2213
 * @example
2214
 *     ```
2215
 *     [(#VAL{28}|div{14})]
2216
 *     ```
2217
 *
2218
 * @param int $a
2219
 * @param int $b
2220
 * @return int $a/$b (ou 0 si $b est nul)
2221
 **/
2222
function div($a, $b) {
2223
	return $b ? $a / $b : 0;
2224
}
2225
function strdiv($a, $b) {return strize('div', $a, $b);}
2226
2227
/**
2228
 * Retourne le modulo 2 nombres
2229
 *
2230
 * @filtre
2231
 * @link https://www.spip.net/4301
2232
 * @see mult()
2233
 * @see div()
2234
 * @example
2235
 *     ```
2236
 *     [(#VAL{28}|modulo{14})]
2237
 *     ```
2238
 *
2239
 * @param int $nb
2240
 * @param int $mod
2241
 * @param int $add
2242
 * @return int ($nb % $mod) + $add
2243
 **/
2244
function modulo($nb, $mod, $add = 0) {
2245
	return ($mod ? $nb % $mod : 0) + $add;
2246
}
2247
2248
2249
/**
2250
 * Vérifie qu'un nom (d'auteur) ne comporte pas d'autres tags que <multi>
2251
 * et ceux volontairement spécifiés dans la constante
2252
 *
2253
 * @param string $nom
2254
 *      Nom (signature) proposé
2255
 * @return bool
2256
 *      - false si pas conforme,
2257
 *      - true sinon
2258
 **/
2259
function nom_acceptable($nom) {
2260
	if (!is_string($nom)) {
2261
		return false;
2262
	}
2263
	if (!defined('_TAGS_NOM_AUTEUR')) {
2264
		define('_TAGS_NOM_AUTEUR', '');
2265
	}
2266
	$tags_acceptes = array_unique(explode(',', 'multi,' . _TAGS_NOM_AUTEUR));
2267
	foreach ($tags_acceptes as $tag) {
2268
		if (strlen($tag)) {
2269
			$remp1[] = '<' . trim($tag) . '>';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$remp1 was never initialized. Although not strictly required by PHP, it is generally a good practice to add $remp1 = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2270
			$remp1[] = '</' . trim($tag) . '>';
0 ignored issues
show
Bug introduced by
The variable $remp1 does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2271
			$remp2[] = '\x60' . trim($tag) . '\x61';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$remp2 was never initialized. Although not strictly required by PHP, it is generally a good practice to add $remp2 = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2272
			$remp2[] = '\x60/' . trim($tag) . '\x61';
2273
		}
2274
	}
2275
	$v_nom = str_replace($remp2, $remp1, supprimer_tags(str_replace($remp1, $remp2, $nom)));
0 ignored issues
show
Bug introduced by
The variable $remp2 does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2276
2277
	return str_replace('&lt;', '<', $v_nom) == $nom;
2278
}
2279
2280
2281
/**
2282
 * Vérifier la conformité d'une ou plusieurs adresses email (suivant RFC 822)
2283
 *
2284
 * @param string|array $adresses
2285
 *      Adresse ou liste d'adresse
2286
 *      si on fournit un tableau, il est filtre et la fonction renvoie avec uniquement les adresses email valides (donc possiblement vide)
2287
 * @return bool|string|array
2288
 *      - false si une des adresses n'est pas conforme,
2289
 *      - la normalisation de la dernière adresse donnée sinon
2290
 *      - renvoie un tableau si l'entree est un tableau
2291
 **/
2292
function email_valide($adresses) {
2293
	if (is_array($adresses)) {
2294
		$adresses = array_map('email_valide', $adresses);
2295
		$adresses = array_filter($adresses);
2296
		return $adresses;
2297
	}
2298
2299
	$email_valide = charger_fonction('email_valide', 'inc');
2300
	return $email_valide($adresses);
2301
}
2302
2303
/**
2304
 * Permet d'afficher un symbole à côté des liens pointant vers les
2305
 * documents attachés d'un article (liens ayant `rel=enclosure`).
2306
 *
2307
 * @filtre
2308
 * @link https://www.spip.net/4134
2309
 *
2310
 * @param string $tags Texte
2311
 * @return string Texte
2312
 **/
2313
function afficher_enclosures($tags) {
2314
	$s = array();
2315
	foreach (extraire_balises($tags, 'a') as $tag) {
2316
		if (extraire_attribut($tag, 'rel') == 'enclosure'
2317
			and $t = extraire_attribut($tag, 'href')
2318
		) {
2319
			$s[] = preg_replace(',>[^<]+</a>,S',
2320
				'>'
2321
				. http_img_pack('attachment-16.png', $t,
0 ignored issues
show
Bug introduced by
It seems like $t defined by extraire_attribut($tag, 'href') on line 2317 can also be of type array; however, http_img_pack() 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...
2322
					'title="' . attribut_html($t) . '"')
0 ignored issues
show
Bug introduced by
It seems like $t defined by extraire_attribut($tag, 'href') on line 2317 can also be of type array; however, attribut_html() 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...
2323
				. '</a>', $tag);
2324
		}
2325
	}
2326
2327
	return join('&nbsp;', $s);
2328
}
2329
2330
/**
2331
 * Filtre des liens HTML `<a>` selon la valeur de leur attribut `rel`
2332
 * et ne retourne que ceux là.
2333
 *
2334
 * @filtre
2335
 * @link https://www.spip.net/4187
2336
 *
2337
 * @param string $tags Texte
2338
 * @param string $rels Attribut `rel` à capturer (ou plusieurs séparés par des virgules)
2339
 * @return string Liens trouvés
2340
 **/
2341
function afficher_tags($tags, $rels = 'tag,directory') {
2342
	$s = array();
2343
	foreach (extraire_balises($tags, 'a') as $tag) {
2344
		$rel = extraire_attribut($tag, 'rel');
2345
		if (strstr(",$rels,", ",$rel,")) {
2346
			$s[] = $tag;
2347
		}
2348
	}
2349
2350
	return join(', ', $s);
2351
}
2352
2353
2354
/**
2355
 * Convertir les médias fournis par un flux RSS (podcasts)
2356
 * en liens conformes aux microformats
2357
 *
2358
 * Passe un `<enclosure url="fichier" length="5588242" type="audio/mpeg"/>`
2359
 * au format microformat `<a rel="enclosure" href="fichier" ...>fichier</a>`.
2360
 *
2361
 * Peut recevoir un `<link` ou un `<media:content` parfois.
2362
 *
2363
 * Attention : `length="zz"` devient `title="zz"`, pour rester conforme.
2364
 *
2365
 * @filtre
2366
 * @see microformat2enclosure() Pour l'inverse
2367
 *
2368
 * @param string $e Tag RSS `<enclosure>`
2369
 * @return string Tag HTML `<a>` avec microformat.
2370
 **/
2371
function enclosure2microformat($e) {
2372
	if (!$url = filtrer_entites(extraire_attribut($e, 'url'))) {
0 ignored issues
show
Bug introduced by
It seems like extraire_attribut($e, 'url') targeting extraire_attribut() can also be of type array; however, filtrer_entites() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2373
		$url = filtrer_entites(extraire_attribut($e, 'href'));
0 ignored issues
show
Bug introduced by
It seems like extraire_attribut($e, 'href') targeting extraire_attribut() can also be of type array; however, filtrer_entites() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2374
	}
2375
	$type = extraire_attribut($e, 'type');
2376
	if (!$length = extraire_attribut($e, 'length')) {
2377
		# <media:content : longeur dans fileSize. On tente.
2378
		$length = extraire_attribut($e, 'fileSize');
2379
	}
2380
	$fichier = basename($url);
2381
2382
	return '<a rel="enclosure"'
2383
	. ($url ? ' href="' . spip_htmlspecialchars($url) . '"' : '')
2384
	. ($type ? ' type="' . spip_htmlspecialchars($type) . '"' : '')
0 ignored issues
show
Bug introduced by
It seems like $type defined by extraire_attribut($e, 'type') on line 2375 can also be of type array; 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...
2385
	. ($length ? ' title="' . spip_htmlspecialchars($length) . '"' : '')
0 ignored issues
show
Bug introduced by
It seems like $length can also be of type array; 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...
2386
	. '>' . $fichier . '</a>';
2387
}
2388
2389
/**
2390
 * Convertir les liens conformes aux microformats en médias pour flux RSS,
2391
 * par exemple pour les podcasts
2392
 *
2393
 * Passe un texte ayant des liens avec microformat
2394
 * `<a rel="enclosure" href="fichier" ...>fichier</a>`
2395
 * au format RSS `<enclosure url="fichier" ... />`.
2396
 *
2397
 * @filtre
2398
 * @see enclosure2microformat() Pour l'inverse
2399
 *
2400
 * @param string $tags Texte HTML ayant des tag `<a>` avec microformat
2401
 * @return string Tags RSS `<enclosure>`.
2402
 **/
2403
function microformat2enclosure($tags) {
2404
	$enclosures = array();
2405
	foreach (extraire_balises($tags, 'a') as $e) {
2406
		if (extraire_attribut($e, 'rel') == 'enclosure') {
2407
			$url = filtrer_entites(extraire_attribut($e, 'href'));
0 ignored issues
show
Bug introduced by
It seems like extraire_attribut($e, 'href') targeting extraire_attribut() can also be of type array; however, filtrer_entites() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2408
			$type = extraire_attribut($e, 'type');
2409
			if (!$length = intval(extraire_attribut($e, 'title'))) {
2410
				$length = intval(extraire_attribut($e, 'length'));
2411
			} # vieux data
2412
			$fichier = basename($url);
0 ignored issues
show
Unused Code introduced by
$fichier is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2413
			$enclosures[] = '<enclosure'
2414
				. ($url ? ' url="' . spip_htmlspecialchars($url) . '"' : '')
2415
				. ($type ? ' type="' . spip_htmlspecialchars($type) . '"' : '')
0 ignored issues
show
Bug introduced by
It seems like $type defined by extraire_attribut($e, 'type') on line 2408 can also be of type array; 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...
2416
				. ($length ? ' length="' . $length . '"' : '')
2417
				. ' />';
2418
		}
2419
	}
2420
2421
	return join("\n", $enclosures);
2422
}
2423
2424
2425
/**
2426
 * Créer les éléments ATOM `<dc:subject>` à partir des tags
2427
 *
2428
 * Convertit les liens avec attribut `rel="tag"`
2429
 * en balise `<dc:subject></dc:subject>` pour les flux RSS au format Atom.
2430
 *
2431
 * @filtre
2432
 *
2433
 * @param string $tags Texte
2434
 * @return string Tags RSS Atom `<dc:subject>`.
2435
 **/
2436
function tags2dcsubject($tags) {
2437
	$subjects = '';
2438
	foreach (extraire_balises($tags, 'a') as $e) {
2439
		if (extraire_attribut($e, rel) == 'tag') {
2440
			$subjects .= '<dc:subject>'
2441
				. texte_backend(textebrut($e))
2442
				. '</dc:subject>' . "\n";
2443
		}
2444
	}
2445
2446
	return $subjects;
2447
}
2448
2449
/**
2450
 * Retourne la premiere balise html du type demandé
2451
 *
2452
 * Retourne le contenu d'une balise jusqu'à la première fermeture rencontrée
2453
 * du même type.
2454
 * Si on a passe un tableau de textes, retourne un tableau de resultats.
2455
 *
2456
 * @example `[(#DESCRIPTIF|extraire_balise{img})]`
2457
 *
2458
 * @filtre
2459
 * @link https://www.spip.net/4289
2460
 * @see extraire_balises()
2461
 * @note
2462
 *     Attention : les résultats peuvent être incohérents sur des balises imbricables,
2463
 *     tel que demander à extraire `div` dans le texte `<div> un <div> mot </div> absent </div>`,
2464
 *     ce qui retournerait `<div> un <div> mot </div>` donc.
2465
 *
2466
 * @param string|array $texte
2467
 *     Texte(s) dont on souhaite extraire une balise html
2468
 * @param string $tag
2469
 *     Nom de la balise html à extraire
2470
 * @return void|string|array
2471
 *     - Code html de la balise, sinon rien
2472
 *     - Tableau de résultats, si tableau en entrée.
2473
 **/
2474 View Code Duplication
function extraire_balise($texte, $tag = 'a') {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in 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...
2475
	if (is_array($texte)) {
2476
		array_walk(
2477
			$texte,
2478
			function(&$a, $key, $t){
2479
				$a = extraire_balise($a, $t);
2480
			},
2481
			$tag
2482
		);
2483
2484
		return $texte;
2485
	}
2486
2487
	if (preg_match(
2488
		",<$tag\b[^>]*(/>|>.*</$tag\b[^>]*>|>),UimsS",
2489
		$texte, $regs)) {
2490
		return $regs[0];
2491
	}
2492
}
2493
2494
/**
2495
 * Extrait toutes les balises html du type demandé
2496
 *
2497
 * Retourne dans un tableau le contenu de chaque balise jusqu'à la première
2498
 * fermeture rencontrée du même type.
2499
 * Si on a passe un tableau de textes, retourne un tableau de resultats.
2500
 *
2501
 * @example `[(#TEXTE|extraire_balises{img}|implode{" - "})]`
2502
 *
2503
 * @filtre
2504
 * @link https://www.spip.net/5618
2505
 * @see extraire_balise()
2506
 * @note
2507
 *     Attention : les résultats peuvent être incohérents sur des balises imbricables,
2508
 *     tel que demander à extraire `div` dans un texte.
2509
 *
2510
 * @param string|array $texte
2511
 *     Texte(s) dont on souhaite extraire une balise html
2512
 * @param string $tag
2513
 *     Nom de la balise html à extraire
2514
 * @return array
2515
 *     - Liste des codes html des occurrences de la balise, sinon tableau vide
2516
 *     - Tableau de résultats, si tableau en entrée.
2517
 **/
2518 View Code Duplication
function extraire_balises($texte, $tag = 'a') {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in 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...
2519
	if (is_array($texte)) {
2520
		array_walk(
2521
			$texte,
2522
			function(&$a, $key, $t){
2523
				$a = extraire_balises($a, $t);
2524
			},
2525
			$tag
2526
		);
2527
2528
		return $texte;
2529
	}
2530
2531
	if (preg_match_all(
2532
		",<${tag}\b[^>]*(/>|>.*</${tag}\b[^>]*>|>),UimsS",
2533
		$texte, $regs, PREG_PATTERN_ORDER)) {
2534
		return $regs[0];
2535
	} else {
2536
		return array();
2537
	}
2538
}
2539
2540
/**
2541
 * Indique si le premier argument est contenu dans le second
2542
 *
2543
 * Cette fonction est proche de `in_array()` en PHP avec comme principale
2544
 * différence qu'elle ne crée pas d'erreur si le second argument n'est pas
2545
 * un tableau (dans ce cas elle tentera de le désérialiser, et sinon retournera
2546
 * la valeur par défaut transmise).
2547
 *
2548
 * @example `[(#VAL{deux}|in_any{#LISTE{un,deux,trois}}|oui) ... ]`
2549
 *
2550
 * @filtre
2551
 * @see filtre_find() Assez proche, avec les arguments valeur et tableau inversés.
2552
 *
2553
 * @param string $val
2554
 *     Valeur à chercher dans le tableau
2555
 * @param array|string $vals
2556
 *     Tableau des valeurs. S'il ce n'est pas un tableau qui est transmis,
2557
 *     la fonction tente de la désérialiser.
2558
 * @param string $def
2559
 *     Valeur par défaut retournée si `$vals` n'est pas un tableau.
2560
 * @return string
2561
 *     - ' ' si la valeur cherchée est dans le tableau
2562
 *     - '' si la valeur n'est pas dans le tableau
2563
 *     - `$def` si on n'a pas transmis de tableau
2564
 **/
2565
function in_any($val, $vals, $def = '') {
2566
	if (!is_array($vals) and $v = unserialize($vals)) {
2567
		$vals = $v;
2568
	}
2569
2570
	return (!is_array($vals) ? $def : (in_array($val, $vals) ? ' ' : ''));
2571
}
2572
2573
2574
/**
2575
 * Retourne le résultat d'une expression mathématique simple
2576
 *
2577
 * N'accepte que les *, + et - (à ameliorer si on l'utilise vraiment).
2578
 *
2579
 * @filtre
2580
 * @example
2581
 *      ```
2582
 *      valeur_numerique("3*2") retourne 6
2583
 *      ```
2584
 *
2585
 * @param string $expr
2586
 *     Expression mathématique `nombre operateur nombre` comme `3*2`
2587
 * @return int
2588
 *     Résultat du calcul
2589
 **/
2590
function valeur_numerique($expr) {
2591
	$a = 0;
2592
	if (preg_match(',^[0-9]+(\s*[+*-]\s*[0-9]+)*$,S', trim($expr))) {
2593
		eval("\$a = $expr;");
2594
	}
2595
2596
	return intval($a);
2597
}
2598
2599
/**
2600
 * Retourne un calcul de règle de trois
2601
 *
2602
 * @filtre
2603
 * @example
2604
 *     ```
2605
 *     [(#VAL{6}|regledetrois{4,3})] retourne 8
2606
 *     ```
2607
 *
2608
 * @param int $a
2609
 * @param int $b
2610
 * @param int $c
2611
 * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be 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...
2612
 *      Retourne `$a*$b/$c`
2613
 **/
2614
function regledetrois($a, $b, $c) {
2615
	return round($a * $b / $c);
2616
}
2617
2618
2619
/**
2620
 * Crée des tags HTML input hidden pour chaque paramètre et valeur d'une URL
2621
 *
2622
 * Fournit la suite de Input-Hidden correspondant aux paramètres de
2623
 * l'URL donnée en argument, compatible avec les types_urls
2624
 *
2625
 * @filtre
2626
 * @link https://www.spip.net/4286
2627
 * @see balise_ACTION_FORMULAIRE()
2628
 *     Également pour transmettre les actions à un formulaire
2629
 * @example
2630
 *     ```
2631
 *     [(#ENV{action}|form_hidden)] dans un formulaire
2632
 *     ```
2633
 *
2634
 * @param string $action URL
2635
 * @return string Suite de champs input hidden
2636
 **/
2637
function form_hidden($action) {
2638
2639
	$contexte = array();
2640
	include_spip('inc/urls');
2641
	if ($p = urls_decoder_url($action, '')
2642
		and reset($p)
2643
	) {
2644
		$fond = array_shift($p);
2645
		if ($fond != '404') {
2646
			$contexte = array_shift($p);
2647
			$contexte['page'] = $fond;
2648
			$action = preg_replace('/([?]' . preg_quote($fond) . '[^&=]*[0-9]+)(&|$)/', '?&', $action);
2649
		}
2650
	}
2651
	// defaire ce qu'a injecte urls_decoder_url : a revoir en modifiant la signature de urls_decoder_url
2652
	if (defined('_DEFINIR_CONTEXTE_TYPE') and _DEFINIR_CONTEXTE_TYPE) {
2653
		unset($contexte['type']);
2654
	}
2655
	if (defined('_DEFINIR_CONTEXTE_TYPE_PAGE') and _DEFINIR_CONTEXTE_TYPE_PAGE) {
2656
		unset($contexte['type-page']);
2657
	}
2658
2659
	// on va remplir un tableau de valeurs en prenant bien soin de ne pas
2660
	// ecraser les elements de la forme mots[]=1&mots[]=2
2661
	$values = array();
2662
2663
	// d'abord avec celles de l'url
2664
	if (false !== ($p = strpos($action, '?'))) {
2665
		foreach (preg_split('/&(amp;)?/S', substr($action, $p + 1)) as $c) {
2666
			$c = explode('=', $c, 2);
2667
			$var = array_shift($c);
2668
			$val = array_shift($c);
2669
			if ($var) {
2670
				$val = rawurldecode($val);
2671
				$var = rawurldecode($var); // decoder les [] eventuels
2672 View Code Duplication
				if (preg_match(',\[\]$,S', $var)) {
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...
2673
					$values[] = array($var, $val);
2674
				} else {
2675
					if (!isset($values[$var])) {
2676
						$values[$var] = array($var, $val);
2677
					}
2678
				}
2679
			}
2680
		}
2681
	}
2682
2683
	// ensuite avec celles du contexte, sans doublonner !
2684
	foreach ($contexte as $var => $val) {
2685 View Code Duplication
		if (preg_match(',\[\]$,S', $var)) {
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...
2686
			$values[] = array($var, $val);
2687
		} else {
2688
			if (!isset($values[$var])) {
2689
				$values[$var] = array($var, $val);
2690
			}
2691
		}
2692
	}
2693
2694
	// puis on rassemble le tout
2695
	$hidden = array();
2696
	foreach ($values as $value) {
2697
		list($var, $val) = $value;
2698
		$hidden[] = '<input name="'
2699
			. entites_html($var)
2700
			. '"'
2701
			. (is_null($val)
2702
				? ''
2703
				: ' value="' . entites_html($val) . '"'
2704
			)
2705
			. ' type="hidden"' . "\n/>";
2706
	}
2707
2708
	return join("", $hidden);
2709
}
2710
2711
/**
2712
 * Calcule les bornes d'une pagination
2713
 *
2714
 * @filtre
2715
 *
2716
 * @param int $courante
2717
 *     Page courante
2718
 * @param int $nombre
2719
 *     Nombre de pages
2720
 * @param int $max
2721
 *     Nombre d'éléments par page
2722
 * @return int[]
0 ignored issues
show
Documentation introduced by
Should the return type not be array<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...
2723
 *     Liste (première page, dernière page).
2724
 **/
2725
function filtre_bornes_pagination_dist($courante, $nombre, $max = 10) {
2726
	if ($max <= 0 or $max >= $nombre) {
2727
		return array(1, $nombre);
2728
	}
2729
2730
	$premiere = max(1, $courante - floor(($max - 1) / 2));
2731
	$derniere = min($nombre, $premiere + $max - 2);
2732
	$premiere = $derniere == $nombre ? $derniere - $max + 1 : $premiere;
2733
2734
	return array($premiere, $derniere);
2735
}
2736
2737
2738
/**
2739
 * Retourne la première valeur d'un tableau
2740
 *
2741
 * Plus précisément déplace le pointeur du tableau sur la première valeur et la retourne.
2742
 *
2743
 * @example `[(#LISTE{un,deux,trois}|reset)]` retourne 'un'
2744
 *
2745
 * @filtre
2746
 * @link http://php.net/manual/fr/function.reset.php
2747
 * @see filtre_end()
2748
 *
2749
 * @param array $array
2750
 * @return mixed|null|false
2751
 *    - null si $array n'est pas un tableau,
2752
 *    - false si le tableau est vide
2753
 *    - la première valeur du tableau sinon.
2754
 **/
2755
function filtre_reset($array) {
2756
	return !is_array($array) ? null : reset($array);
2757
}
2758
2759
/**
2760
 * Retourne la dernière valeur d'un tableau
2761
 *
2762
 * Plus précisément déplace le pointeur du tableau sur la dernière valeur et la retourne.
2763
 *
2764
 * @example `[(#LISTE{un,deux,trois}|end)]` retourne 'trois'
2765
 *
2766
 * @filtre
2767
 * @link http://php.net/manual/fr/function.end.php
2768
 * @see filtre_reset()
2769
 *
2770
 * @param array $array
2771
 * @return mixed|null|false
2772
 *    - null si $array n'est pas un tableau,
2773
 *    - false si le tableau est vide
2774
 *    - la dernière valeur du tableau sinon.
2775
 **/
2776
function filtre_end($array) {
2777
	return !is_array($array) ? null : end($array);
2778
}
2779
2780
/**
2781
 * Empile une valeur à la fin d'un tableau
2782
 *
2783
 * @example `[(#LISTE{un,deux,trois}|push{quatre}|print)]`
2784
 *
2785
 * @filtre
2786
 * @link https://www.spip.net/4571
2787
 * @link http://php.net/manual/fr/function.array-push.php
2788
 *
2789
 * @param array $array
2790
 * @param mixed $val
2791
 * @return array|string
2792
 *     - '' si $array n'est pas un tableau ou si echec.
2793
 *     - le tableau complété de la valeur sinon.
2794
 *
2795
 **/
2796
function filtre_push($array, $val) {
2797
	if (!is_array($array) or !array_push($array, $val)) {
2798
		return '';
2799
	}
2800
2801
	return $array;
2802
}
2803
2804
/**
2805
 * Indique si une valeur est contenue dans un tableau
2806
 *
2807
 * @example `[(#LISTE{un,deux,trois}|find{quatre}|oui) ... ]`
2808
 *
2809
 * @filtre
2810
 * @link https://www.spip.net/4575
2811
 * @see in_any() Assez proche, avec les paramètres tableau et valeur inversés.
2812
 *
2813
 * @param array $array
2814
 * @param mixed $val
2815
 * @return bool
2816
 *     - `false` si `$array` n'est pas un tableau
2817
 *     - `true` si la valeur existe dans le tableau, `false` sinon.
2818
 **/
2819
function filtre_find($array, $val) {
2820
	return (is_array($array) and in_array($val, $array));
2821
}
2822
2823
2824
/**
2825
 * Passer les url relatives à la css d'origine en url absolues
2826
 *
2827
 * @uses suivre_lien()
2828
 *
2829
 * @param string $contenu
2830
 *     Contenu du fichier CSS
2831
 * @param string $source
2832
 *     Chemin du fichier CSS
2833
 * @return string
2834
 *     Contenu avec urls en absolus
2835
 **/
2836
function urls_absolues_css($contenu, $source) {
2837
	$path = suivre_lien(url_absolue($source), './');
2838
2839
	return preg_replace_callback(
2840
		",url\s*\(\s*['\"]?([^'\"/#\s][^:]*)['\"]?\s*\),Uims",
2841
		function($x) use ($path) {
2842
			return "url('" . suivre_lien($path, $x[1]) . "')";
2843
		},
2844
		$contenu
2845
	);
2846
}
2847
2848
2849
/**
2850
 * Inverse le code CSS (left <--> right) d'une feuille de style CSS
2851
 *
2852
 * Récupère le chemin d'une CSS existante et :
2853
 *
2854
 * 1. regarde si une CSS inversée droite-gauche existe dans le meme répertoire
2855
 * 2. sinon la crée (ou la recrée) dans `_DIR_VAR/cache_css/`
2856
 *
2857
 * Si on lui donne à manger une feuille nommée `*_rtl.css` il va faire l'inverse.
2858
 *
2859
 * @filtre
2860
 * @example
2861
 *     ```
2862
 *     [<link rel="stylesheet" href="(#CHEMIN{css/perso.css}|direction_css)" type="text/css" />]
2863
 *     ```
2864
 * @param string $css
2865
 *     Chemin vers le fichier CSS
2866
 * @param string $voulue
2867
 *     Permet de forcer le sens voulu (en indiquant `ltr`, `rtl` ou un
2868
 *     code de langue). En absence, prend le sens de la langue en cours.
2869
 *
2870
 * @return string
2871
 *     Chemin du fichier CSS inversé
2872
 **/
2873
function direction_css($css, $voulue = '') {
2874
	if (!preg_match(',(_rtl)?\.css$,i', $css, $r)) {
2875
		return $css;
2876
	}
2877
2878
	// si on a precise le sens voulu en argument, le prendre en compte
2879
	if ($voulue = strtolower($voulue)) {
2880
		if ($voulue != 'rtl' and $voulue != 'ltr') {
2881
			$voulue = lang_dir($voulue);
2882
		}
2883
	} else {
2884
		$voulue = lang_dir();
2885
	}
2886
2887
	$r = count($r) > 1;
2888
	$right = $r ? 'left' : 'right'; // 'right' de la css lue en entree
0 ignored issues
show
Unused Code introduced by
$right is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2889
	$dir = $r ? 'rtl' : 'ltr';
2890
	$ndir = $r ? 'ltr' : 'rtl';
2891
2892
	if ($voulue == $dir) {
2893
		return $css;
2894
	}
2895
2896
	if (
2897
		// url absolue
2898
		preg_match(",^https?:,i", $css)
2899
		// ou qui contient un ?
2900
		or (($p = strpos($css, '?')) !== false)
2901
	) {
2902
		$distant = true;
2903
		$cssf = parse_url($css);
2904
		$cssf = $cssf['path'] . ($cssf['query'] ? "?" . $cssf['query'] : "");
2905
		$cssf = preg_replace(',[?:&=],', "_", $cssf);
2906
	} else {
2907
		$distant = false;
2908
		$cssf = $css;
2909
		// 1. regarder d'abord si un fichier avec la bonne direction n'est pas aussi
2910
		//propose (rien a faire dans ce cas)
2911
		$f = preg_replace(',(_rtl)?\.css$,i', '_' . $ndir . '.css', $css);
2912
		if (@file_exists($f)) {
2913
			return $f;
2914
		}
2915
	}
2916
2917
	// 2.
2918
	$dir_var = sous_repertoire(_DIR_VAR, 'cache-css');
2919
	$f = $dir_var
2920
		. preg_replace(',.*/(.*?)(_rtl)?\.css,', '\1', $cssf)
2921
		. '.' . substr(md5($cssf), 0, 4) . '_' . $ndir . '.css';
2922
2923
	// la css peut etre distante (url absolue !)
2924
	if ($distant) {
2925
		include_spip('inc/distant');
2926
		$res = recuperer_url($css);
2927
		if (!$res or !$contenu = $res['page']) {
2928
			return $css;
2929
		}
2930
	} else {
2931 View Code Duplication
		if ((@filemtime($f) > @filemtime($css))
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...
2932
			and (_VAR_MODE != 'recalcul')
2933
		) {
2934
			return $f;
2935
		}
2936
		if (!lire_fichier($css, $contenu)) {
2937
			return $css;
2938
		}
2939
	}
2940
2941
2942
	// Inverser la direction gauche-droite en utilisant CSSTidy qui gere aussi les shorthands
2943
	include_spip("lib/csstidy/class.csstidy");
2944
	$parser = new csstidy();
2945
	$parser->set_cfg('optimise_shorthands', 0);
2946
	$parser->set_cfg('reverse_left_and_right', true);
2947
	$parser->parse($contenu);
2948
2949
	$contenu = $parser->print->plain();
2950
2951
2952
	// reperer les @import auxquels il faut propager le direction_css
2953
	preg_match_all(",\@import\s*url\s*\(\s*['\"]?([^'\"/][^:]*)['\"]?\s*\),Uims", $contenu, $regs);
2954
	$src = array();
2955
	$src_direction_css = array();
2956
	$src_faux_abs = array();
2957
	$d = dirname($css);
2958
	foreach ($regs[1] as $k => $import_css) {
2959
		$css_direction = direction_css("$d/$import_css", $voulue);
2960
		// si la css_direction est dans le meme path que la css d'origine, on tronque le path, elle sera passee en absolue
2961
		if (substr($css_direction, 0, strlen($d) + 1) == "$d/") {
2962
			$css_direction = substr($css_direction, strlen($d) + 1);
2963
		} // si la css_direction commence par $dir_var on la fait passer pour une absolue
2964
		elseif (substr($css_direction, 0, strlen($dir_var)) == $dir_var) {
2965
			$css_direction = substr($css_direction, strlen($dir_var));
2966
			$src_faux_abs["/@@@@@@/" . $css_direction] = $css_direction;
2967
			$css_direction = "/@@@@@@/" . $css_direction;
2968
		}
2969
		$src[] = $regs[0][$k];
2970
		$src_direction_css[] = str_replace($import_css, $css_direction, $regs[0][$k]);
2971
	}
2972
	$contenu = str_replace($src, $src_direction_css, $contenu);
2973
2974
	$contenu = urls_absolues_css($contenu, $css);
2975
2976
	// virer les fausses url absolues que l'on a mis dans les import
2977
	if (count($src_faux_abs)) {
2978
		$contenu = str_replace(array_keys($src_faux_abs), $src_faux_abs, $contenu);
2979
	}
2980
2981
	if (!ecrire_fichier($f, $contenu)) {
2982
		return $css;
2983
	}
2984
2985
	return $f;
2986
}
2987
2988
2989
/**
2990
 * Transforme les urls relatives d'un fichier CSS en absolues
2991
 *
2992
 * Récupère le chemin d'une css existante et crée (ou recrée) dans `_DIR_VAR/cache_css/`
2993
 * une css dont les url relatives sont passées en url absolues
2994
 *
2995
 * Le calcul n'est pas refait si le fichier cache existe déjà et que
2996
 * la source n'a pas été modifiée depuis.
2997
 *
2998
 * @uses recuperer_page() si l'URL source n'est pas sur le même site
2999
 * @uses urls_absolues_css()
3000
 *
3001
 * @param string $css
3002
 *     Chemin ou URL du fichier CSS source
3003
 * @return string
3004
 *     - Chemin du fichier CSS transformé (si source lisible et mise en cache réussie)
3005
 *     - Chemin ou URL du fichier CSS source sinon.
3006
 **/
3007
function url_absolue_css($css) {
3008
	if (!preg_match(',\.css$,i', $css, $r)) {
3009
		return $css;
3010
	}
3011
3012
	$url_absolue_css = url_absolue($css);
3013
3014
	$f = basename($css, '.css');
3015
	$f = sous_repertoire(_DIR_VAR, 'cache-css')
3016
		. preg_replace(",(.*?)(_rtl|_ltr)?$,", "\\1-urlabs-" . substr(md5("$css-urlabs"), 0, 4) . "\\2", $f)
3017
		. '.css';
3018
3019 View Code Duplication
	if ((@filemtime($f) > @filemtime($css)) and (_VAR_MODE != 'recalcul')) {
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...
3020
		return $f;
3021
	}
3022
3023
	if ($url_absolue_css == $css) {
3024
		if (strncmp($GLOBALS['meta']['adresse_site'], $css, $l = strlen($GLOBALS['meta']['adresse_site'])) != 0
3025
			or !lire_fichier(_DIR_RACINE . substr($css, $l), $contenu)
3026
		) {
3027
			include_spip('inc/distant');
3028
			if (!$contenu = recuperer_page($css)) {
0 ignored issues
show
Deprecated Code introduced by
The function recuperer_page() has been deprecated.

This function has been deprecated.

Loading history...
3029
				return $css;
3030
			}
3031
		}
3032
	} elseif (!lire_fichier($css, $contenu)) {
3033
		return $css;
3034
	}
3035
3036
	// passer les url relatives a la css d'origine en url absolues
3037
	$contenu = urls_absolues_css($contenu, $css);
0 ignored issues
show
Bug introduced by
It seems like $contenu can also be of type boolean; however, urls_absolues_css() 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...
3038
3039
	// ecrire la css
3040
	if (!ecrire_fichier($f, $contenu)) {
3041
		return $css;
3042
	}
3043
3044
	return $f;
3045
}
3046
3047
3048
/**
3049
 * Récupère la valeur d'une clé donnée
3050
 * dans un tableau (ou un objet).
3051
 *
3052
 * @filtre
3053
 * @link https://www.spip.net/4572
3054
 * @example
3055
 *     ```
3056
 *     [(#VALEUR|table_valeur{cle/sous/element})]
3057
 *     ```
3058
 *
3059
 * @param mixed $table
3060
 *     Tableau ou objet PHP
3061
 *     (ou chaîne serialisée de tableau, ce qui permet d'enchaîner le filtre)
3062
 * @param string $cle
3063
 *     Clé du tableau (ou paramètre public de l'objet)
3064
 *     Cette clé peut contenir des caractères / pour sélectionner
3065
 *     des sous éléments dans le tableau, tel que `sous/element/ici`
3066
 *     pour obtenir la valeur de `$tableau['sous']['element']['ici']`
3067
 * @param mixed $defaut
3068
 *     Valeur par defaut retournée si la clé demandée n'existe pas
3069
 * @param bool  $conserver_null
3070
 *     Permet de forcer la fonction à renvoyer la valeur null d'un index
3071
 *     et non pas $defaut comme cela est fait naturellement par la fonction
3072
 *     isset. On utilise alors array_key_exists() à la place de isset().
3073
 * 
3074
 * @return mixed
3075
 *     Valeur trouvée ou valeur par défaut.
3076
 **/
3077
function table_valeur($table, $cle, $defaut = '', $conserver_null = false) {
3078
	foreach (explode('/', $cle) as $k) {
3079
3080
		$table = is_string($table) ? @unserialize($table) : $table;
3081
3082
		if (is_object($table)) {
3083
			$table = (($k !== "") and isset($table->$k)) ? $table->$k : $defaut;
3084
		} elseif (is_array($table)) {
3085
			if ($conserver_null) {
3086
				$table = array_key_exists($k, $table) ? $table[$k] : $defaut;
3087
			} else {
3088
				$table = isset($table[$k]) ? $table[$k] : $defaut;
3089
			}
3090
		} else {
3091
			$table = $defaut;
3092
		}
3093
	}
3094
3095
	return $table;
3096
}
3097
3098
/**
3099
 * Retrouve un motif dans un texte à partir d'une expression régulière
3100
 *
3101
 * S'appuie sur la fonction `preg_match()` en PHP
3102
 *
3103
 * @example
3104
 *    - `[(#TITRE|match{toto})]`
3105
 *    - `[(#TEXTE|match{^ceci$,Uims})]`
3106
 *    - `[(#TEXTE|match{truc(...)$, UimsS, 1})]` Capture de la parenthèse indiquée
3107
 *    - `[(#TEXTE|match{truc(...)$, 1})]` Équivalent, sans indiquer les modificateurs
3108
 *
3109
 * @filtre
3110
 * @link https://www.spip.net/4299
3111
 * @link http://php.net/manual/fr/function.preg-match.php Pour des infos sur `preg_match()`
3112
 *
3113
 * @param string $texte
3114
 *     Texte dans lequel chercher
3115
 * @param string|int $expression
3116
 *     Expression régulière de recherche, sans le délimiteur
3117
 * @param string $modif
3118
 *     - string : Modificateurs de l'expression régulière
3119
 *     - int : Numéro de parenthèse capturante
3120
 * @param int $capte
3121
 *     Numéro de parenthèse capturante
3122
 * @return bool|string
3123
 *     - false : l'expression n'a pas été trouvée
3124
 *     - true : expression trouvée, mais pas la parenthèse capturante
3125
 *     - string : expression trouvée.
3126
 **/
3127
function filtre_match_dist($texte, $expression, $modif = "UimsS", $capte = 0) {
3128
	if (intval($modif) and $capte == 0) {
3129
		$capte = $modif;
3130
		$modif = "UimsS";
3131
	}
3132
	$expression = str_replace("\/", "/", $expression);
3133
	$expression = str_replace("/", "\/", $expression);
3134
3135
	if (preg_match('/' . $expression . '/' . $modif, $texte, $r)) {
3136
		if (isset($r[$capte])) {
3137
			return $r[$capte];
3138
		} else {
3139
			return true;
3140
		}
3141
	}
3142
3143
	return false;
3144
}
3145
3146
3147
/**
3148
 * Remplacement de texte à base d'expression régulière
3149
 *
3150
 * @filtre
3151
 * @link https://www.spip.net/4309
3152
 * @see match()
3153
 * @example
3154
 *     ```
3155
 *     [(#TEXTE|replace{^ceci$,cela,UimsS})]
3156
 *     ```
3157
 *
3158
 * @param string $texte
3159
 *     Texte
3160
 * @param string $expression
3161
 *     Expression régulière
3162
 * @param string $replace
3163
 *     Texte de substitution des éléments trouvés
3164
 * @param string $modif
3165
 *     Modificateurs pour l'expression régulière.
3166
 * @return string
3167
 *     Texte
3168
 **/
3169
function replace($texte, $expression, $replace = '', $modif = "UimsS") {
3170
	$expression = str_replace("\/", "/", $expression);
3171
	$expression = str_replace("/", "\/", $expression);
3172
3173
	return preg_replace('/' . $expression . '/' . $modif, $replace, $texte);
3174
}
3175
3176
3177
/**
3178
 * Cherche les documents numerotés dans un texte traite par `propre()`
3179
 *
3180
 * Affecte la liste des doublons['documents']
3181
 *
3182
 * @param array $doublons
3183
 *     Liste des doublons
3184
 * @param string $letexte
3185
 *     Le texte
3186
 * @return string
3187
 *     Le texte
3188
 **/
3189
function traiter_doublons_documents(&$doublons, $letexte) {
3190
3191
	// Verifier dans le texte & les notes (pas beau, helas)
3192
	$t = $letexte . $GLOBALS['les_notes'];
3193
3194
	if (strstr($t, 'spip_document_') // evite le preg_match_all si inutile
3195
		and preg_match_all(
3196
			',<[^>]+\sclass=["\']spip_document_([0-9]+)[\s"\'],imsS',
3197
			$t, $matches, PREG_PATTERN_ORDER)
3198
	) {
3199
		if (!isset($doublons['documents'])) {
3200
			$doublons['documents'] = "";
3201
		}
3202
		$doublons['documents'] .= "," . join(',', $matches[1]);
3203
	}
3204
3205
	return $letexte;
3206
}
3207
3208
/**
3209
 * Filtre vide qui ne renvoie rien
3210
 *
3211
 * @example
3212
 *     `[(#CALCUL|vide)]` n'affichera pas le résultat du calcul
3213
 * @filtre
3214
 *
3215
 * @param mixed $texte
3216
 * @return string Chaîne vide
3217
 **/
3218
function vide($texte) {
0 ignored issues
show
Unused Code introduced by
The parameter $texte 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...
3219
	return "";
3220
}
3221
3222
//
3223
// Filtres pour le modele/emb (embed document)
3224
//
3225
3226
/**
3227
 * Écrit des balises HTML `<param...>` à partir d'un tableau de données tel que `#ENV`
3228
 *
3229
 * Permet d'écrire les balises `<param>` à indiquer dans un `<object>`
3230
 * en prenant toutes les valeurs du tableau transmis.
3231
 *
3232
 * Certaines clés spécifiques à SPIP et aux modèles embed sont omises :
3233
 * id, lang, id_document, date, date_redac, align, fond, recurs, emb, dir_racine
3234
 *
3235
 * @example `[(#ENV*|env_to_params)]`
3236
 *
3237
 * @filtre
3238
 * @link https://www.spip.net/4005
3239
 *
3240
 * @param array|string $env
3241
 *      Tableau cle => valeur des paramètres à écrire, ou chaine sérialisée de ce tableau
3242
 * @param array $ignore_params
3243
 *      Permet de compléter les clés ignorées du tableau.
3244
 * @return string
3245
 *      Code HTML résultant
3246
 **/
3247 View Code Duplication
function env_to_params($env, $ignore_params = array()) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in 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...
3248
	$ignore_params = array_merge(
3249
		array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
3250
		$ignore_params
3251
	);
3252
	if (!is_array($env)) {
3253
		$env = unserialize($env);
3254
	}
3255
	$texte = "";
3256
	if ($env) {
3257
		foreach ($env as $i => $j) {
3258
			if (is_string($j) and !in_array($i, $ignore_params)) {
3259
				$texte .= "<param name='" . attribut_html($i) . "'\n\tvalue='" . attribut_html($j) . "' />";
3260
			}
3261
		}
3262
	}
3263
3264
	return $texte;
3265
}
3266
3267
/**
3268
 * Écrit des attributs HTML à partir d'un tableau de données tel que `#ENV`
3269
 *
3270
 * Permet d'écrire des attributs d'une balise HTML en utilisant les données du tableau transmis.
3271
 * Chaque clé deviendra le nom de l'attribut (et la valeur, sa valeur)
3272
 *
3273
 * Certaines clés spécifiques à SPIP et aux modèles embed sont omises :
3274
 * id, lang, id_document, date, date_redac, align, fond, recurs, emb, dir_racine
3275
 *
3276
 * @example `<embed src='#URL_DOCUMENT' [(#ENV*|env_to_attributs)] width='#GET{largeur}' height='#GET{hauteur}'></embed>`
3277
 * @filtre
3278
 *
3279
 * @param array|string $env
3280
 *      Tableau cle => valeur des attributs à écrire, ou chaine sérialisée de ce tableau
3281
 * @param array $ignore_params
3282
 *      Permet de compléter les clés ignorées du tableau.
3283
 * @return string
3284
 *      Code HTML résultant
3285
 **/
3286 View Code Duplication
function env_to_attributs($env, $ignore_params = array()) {
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in 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...
3287
	$ignore_params = array_merge(
3288
		array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
3289
		$ignore_params
3290
	);
3291
	if (!is_array($env)) {
3292
		$env = unserialize($env);
3293
	}
3294
	$texte = "";
3295
	if ($env) {
3296
		foreach ($env as $i => $j) {
3297
			if (is_string($j) and !in_array($i, $ignore_params)) {
3298
				$texte .= attribut_html($i) . "='" . attribut_html($j) . "' ";
3299
			}
3300
		}
3301
	}
3302
3303
	return $texte;
3304
}
3305
3306
3307
/**
3308
 * Concatène des chaînes
3309
 *
3310
 * @filtre
3311
 * @link https://www.spip.net/4150
3312
 * @example
3313
 *     ```
3314
 *     #TEXTE|concat{texte1,texte2,...}
3315
 *     ```
3316
 *
3317
 * @return string Chaînes concaténés
3318
 **/
3319
function concat() {
3320
	$args = func_get_args();
3321
3322
	return join('', $args);
3323
}
3324
3325
3326
/**
3327
 * Retourne le contenu d'un ou plusieurs fichiers
3328
 *
3329
 * Les chemins sont cherchés dans le path de SPIP
3330
 *
3331
 * @see balise_INCLURE_dist() La balise `#INCLURE` peut appeler cette fonction
3332
 *
3333
 * @param array|string $files
3334
 *     - array : Liste de fichiers
3335
 *     - string : fichier ou fichiers séparés par `|`
3336
 * @param bool $script
3337
 *     - si true, considère que c'est un fichier js à chercher `javascript/`
3338
 * @return string
3339
 *     Contenu du ou des fichiers, concaténé
3340
 **/
3341
function charge_scripts($files, $script = true) {
3342
	$flux = "";
3343
	foreach (is_array($files) ? $files : explode("|", $files) as $file) {
3344
		if (!is_string($file)) {
3345
			continue;
3346
		}
3347
		if ($script) {
3348
			$file = preg_match(",^\w+$,", $file) ? "javascript/$file.js" : '';
3349
		}
3350
		if ($file) {
3351
			$path = find_in_path($file);
3352
			if ($path) {
3353
				$flux .= spip_file_get_contents($path);
0 ignored issues
show
Bug introduced by
It seems like $path defined by find_in_path($file) on line 3351 can also be of type boolean; however, spip_file_get_contents() 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...
3354
			}
3355
		}
3356
	}
3357
3358
	return $flux;
3359
}
3360
3361
3362
/**
3363
 * Produit une balise img avec un champ alt d'office si vide
3364
 *
3365
 * Attention le htmlentities et la traduction doivent être appliqués avant.
3366
 *
3367
 * @param string $img
3368
 * @param string $alt
3369
 * @param string $atts
3370
 * @param string $title
3371
 * @param array $options
3372
 *   chemin_image : utiliser chemin_image sur $img fourni, ou non (oui par dafaut)
3373
 *   utiliser_suffixe_size : utiliser ou non le suffixe de taille dans le nom de fichier de l'image
3374
 *   sous forme -xx.png (pour les icones essentiellement) (oui par defaut)
3375
 *   variante_svg_si_possible: utiliser l'image -xx.svg au lieu de -32.png par exemple (si la variante svg est disponible)
3376
 * @return string
3377
 */
3378
function http_img_pack($img, $alt, $atts = '', $title = '', $options = array()) {
3379
3380
	$img_file = $img;
3381
	if ($p = strpos($img_file, '?')) {
3382
		$img_file = substr($img_file,0, $p);
3383
	}
3384
	if (!isset($options['chemin_image']) or $options['chemin_image'] == true) {
3385
		$img_file = chemin_image($img);
3386
	}
3387
	else {
3388
		if (!isset($options['variante_svg_si_possible']) or $options['variante_svg_si_possible'] == true){
3389
			// on peut fournir une icone generique -xx.svg qui fera le job dans toutes les tailles, et qui est prioritaire sur le png
3390
			// si il y a un .svg a la bonne taille (-16.svg) a cote, on l'utilise en remplacement du -16.png
3391
			if (preg_match(',-(\d+)[.](png|gif|svg)$,', $img_file, $m)
3392
			  and $variante_svg_generique = substr($img_file, 0, -strlen($m[0])) . "-xx.svg"
3393
			  and file_exists($variante_svg_generique)) {
3394
				if ($variante_svg_size = substr($variante_svg_generique,0,-6) . $m[1] . ".svg" and file_exists($variante_svg_size)) {
3395
					$img_file = $variante_svg_size;
3396
				}
3397
				else {
3398
					$img_file = $variante_svg_generique;
3399
				}
3400
			}
3401
		}
3402
	}
3403
	if (stripos($atts, 'width') === false) {
3404
		// utiliser directement l'info de taille presente dans le nom
3405
		if ((!isset($options['utiliser_suffixe_size'])
3406
				or $options['utiliser_suffixe_size'] == true
3407
			  or strpos($img_file, '-xx.svg') !== false)
3408
			and (preg_match(',-([0-9]+)[.](png|gif|svg)$,', $img, $regs)
3409
					 or preg_match(',\?([0-9]+)px$,', $img, $regs))
3410
		) {
3411
			$largeur = $hauteur = intval($regs[1]);
3412
		} else {
3413
			$taille = taille_image($img_file);
3414
			list($hauteur, $largeur) = $taille;
3415
			if (!$hauteur or !$largeur) {
3416
				return "";
3417
			}
3418
		}
3419
		$atts .= " width='" . $largeur . "' height='" . $hauteur . "'";
3420
	}
3421
3422
	if (file_exists($img_file)) {
3423
		$img_file = timestamp($img_file);
3424
	}
3425
	if ($alt === false) {
3426
		$alt = '';
3427
	}
3428
	elseif($alt or $alt==='') {
3429
		$alt = " alt='".attribut_html($alt)."'";
3430
	}
3431
	else {
3432
		$alt = " alt='".attribut_html($title)."'";
3433
	}
3434
	return "<img src='$img_file'$alt"
3435
	. ($title ? ' title="' . attribut_html($title) . '"' : '')
3436
	. " " . ltrim($atts)
3437
	. " />";
3438
}
3439
3440
/**
3441
 * Générer une directive `style='background:url()'` à partir d'un fichier image
3442
 *
3443
 * @param string $img
3444
 * @param string $att
3445
 * @param string $size
0 ignored issues
show
Documentation introduced by
Should the type for parameter $size 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...
3446
 * @return string
3447
 */
3448
function http_style_background($img, $att = '', $size=null) {
3449
	if ($size and is_numeric($size)){
3450
		$size = trim($size) . "px";
3451
	}
3452
	return " style='background" .
3453
		($att ? "" : "-image") . ": url(\"" . chemin_image($img) . "\")" . ($att ? (' ' . $att) : '') . ";"
3454
		. ($size ? "background-size:{$size};" : '')
3455
		. "'";
3456
}
3457
3458
3459
function helper_filtre_balise_img_svg_arguments($alt_or_size, $class_or_size, $size) {
3460
	$args = [$alt_or_size, $class_or_size, $size];
3461
	while (is_null(end($args)) and count($args)) {
3462
		array_pop($args);
3463
	}
3464
	if (!count($args)) {
3465
		return [null, null, null];
3466
	}
3467
	if (count($args) < 3) {
3468
		$maybe_size = array_pop($args);
3469
		// @2x
3470
		// @1.5x
3471
		// 512
3472
		// 512x*
3473
		// 512x300
3474
		if (!strlen($maybe_size)
3475
			or !preg_match(',^(@\d+(\.\d+)?x|\d+(x\*)?|\d+x\d+)$,', trim($maybe_size))) {
3476
			$args[] = $maybe_size;
3477
			$maybe_size = null;
3478
		}
3479
		while (count($args)<2) {
3480
			$args[] = null; // default alt or class
3481
		}
3482
		$args[] = $maybe_size;
3483
	}
3484
	return $args;
3485
}
3486
3487
function helper_filtre_balise_img_svg_size($img, $size) {
3488
	// si size est de la forme '@2x' c'est un coeff multiplicateur sur la densite
3489
	if (strpos($size, '@') === 0 and substr($size,-1) === 'x') {
3490
		$coef = floatval(substr($size, 1, -1));
3491
		list($h, $w) = taille_image($img);
3492
		$height = intval(round($h / $coef));
3493
		$width = intval(round($w / $coef));
3494
	}
3495
	// sinon c'est une valeur seule si image caree ou largeurxhauteur
3496
	else {
3497
		$size = explode('x', $size, 2);
3498
		$size = array_map('trim', $size);
3499
		$height = $width = intval(array_shift($size));
3500
3501
		if (count($size) and reset($size)) {
3502
			$height = array_shift($size);
3503
			if ($height === '*') {
3504
				list($h, $w) = taille_image($img);
3505
				$height = intval(round($h * $width / $w));
3506
			}
3507
		}
3508
	}
3509
3510
	return [$width, $height];
3511
}
3512
3513
/**
3514
 * Générer une balise HTML `img` à partir d'un nom de fichier et/ou renseigne son alt/class/width/height
3515
 * selon les arguments passés
3516
 *
3517
 * Le class et le alt peuvent etre omis et dans ce cas size peut-être renseigné comme dernier argument :
3518
 * [(#FICHIER|balise_img{@2x})]
3519
 * [(#FICHIER|balise_img{1024})]
3520
 * [(#FICHIER|balise_img{1024x*})]
3521
 * [(#FICHIER|balise_img{1024x640})]
3522
 * [(#FICHIER|balise_img{'un nuage',1024x640})]
3523
 * [(#FICHIER|balise_img{'un nuage','spip_logo',1024x640})]
3524
 * Si le alt ou la class sont ambigu et peuvent etre interpretes comme une taille, il suffit d'indiquer une taille vide pour lever l'ambiguite
3525
 * [(#FICHIER|balise_img{'@2x','',''})]
3526
 *
3527
 * @uses http_img_pack()
3528
 *
3529
 * @param string $img
3530
 *   chemin vers un fichier ou balise `<img src='...' />` (generee par un filtre image par exemple)
3531
 * @param string $alt
3532
 *   texte alternatif ; une valeur nulle pour explicitement ne pas avoir de balise alt sur l'image (au lieu d'un alt vide)
3533
 * @param string $class
0 ignored issues
show
Documentation introduced by
Should the type for parameter $class 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...
3534
 *   attribut class ; null par defaut (ie si img est une balise, son attribut class sera inchange. pas de class inseree
3535
 * @param string|int $size
0 ignored issues
show
Documentation introduced by
Should the type for parameter $size 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...
3536
 *   taille imposee
3537
 *     @2x : pour imposer une densite x2 (widht et height seront divisees par 2)
3538
 *     largeur : pour une image carree
3539
 *     largeurx* : pour imposer uniquement la largeur (un attribut height sera aussi ajoute, mais calcule automatiquement pour respecter le ratio initial de l'image)
3540
 *     largeurxhauteur pour fixer les 2 dimensions
3541
 * @return string
3542
 *     Code HTML de la balise IMG
3543
 */
3544
function filtre_balise_img_dist($img, $alt = '', $class = null, $size=null) {
3545
3546
	list($alt, $class, $size) = helper_filtre_balise_img_svg_arguments($alt, $class, $size);
3547
3548
	$img = trim($img);
3549
	if (strpos($img, '<img') === 0) {
3550
		if (!is_null($alt)) {
3551
			$img = inserer_attribut($img, 'alt', $alt);
3552
		}
3553 View Code Duplication
		if (!is_null($class)) {
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...
3554
			if (strlen($class)) {
3555
				$img = inserer_attribut($img, 'class', $class);
3556
			}
3557
			else {
3558
				$img = vider_attribut($img, 'class');
3559
			}
3560
		}
3561
	}
3562
	else {
3563
		$img = http_img_pack($img, $alt, $class ? " class='" . attribut_html($class) . "'" : '', '',
3564
				array('chemin_image' => false, 'utiliser_suffixe_size' => false));
3565
		if (is_null($alt)) {
3566
			$img = vider_attribut($img, 'alt');
3567
		}
3568
	}
3569
3570
	if ($img and !is_null($size) and strlen($size = trim($size))) {
3571
		list($width, $height) = helper_filtre_balise_img_svg_size($img, $size);
3572
3573
		$img = inserer_attribut($img, 'width', $width);
3574
		$img = inserer_attribut($img, 'height', $height);
3575
	}
3576
3577
	return $img;
3578
}
3579
3580
3581
/**
3582
 * Inserer un svg inline
3583
 * http://www.accede-web.com/notices/html-css-javascript/6-images-icones/6-2-svg-images-vectorielles/
3584
 *
3585
 * pour l'inserer avec une balise <img>, utiliser le filtre |balise_img
3586
 *
3587
 * Le class et le alt peuvent etre omis et dans ce cas size peut-être renseigné comme dernier argument :
3588
 * [(#FICHIER|balise_svg{1024x640})]
3589
 * [(#FICHIER|balise_svg{'un nuage',1024x640})]
3590
 * [(#FICHIER|balise_svg{'un nuage','spip_logo',1024x640})]
3591
 * Si le alt ou la class sont ambigu et peuvent etre interpretes comme une taille, il suffit d'indiquer une taille vide pour lever l'ambiguite
3592
 * [(#FICHIER|balise_svg{'un nuage','@2x',''})]
3593
 *
3594
 * @param string $img
3595
 *   chemin vers un fichier ou balise `<svg ... >... </svg>` deja preparee
3596
 * @param string $alt
3597
 *   texte alternatif ; une valeur nulle pour explicitement ne pas avoir de balise alt sur l'image (au lieu d'un alt vide)
3598
 * @param string $class
0 ignored issues
show
Documentation introduced by
Should the type for parameter $class 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...
3599
 *   attribut class ; null par defaut (ie si img est une balise, son attribut class sera inchange. pas de class inseree
3600
 * @param string|int $size
0 ignored issues
show
Documentation introduced by
Should the type for parameter $size 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...
3601
 *   taille imposee
3602
 *     @2x : pour imposer une densite x2 (widht et height seront divisees par 2)
3603
 *     largeur : pour une image carree
3604
 *     largeurx* : pour imposer uniquement la largeur (un attribut height sera aussi ajoute, mais calcule automatiquement pour respecter le ratio initial de l'image)
3605
 *     largeurxhauteur pour fixer les 2 dimensions
3606
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|boolean?

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...
3607
 *     Code HTML de la balise SVG
3608
 */
3609
function filtre_balise_svg_dist($img, $alt = '', $class = null, $size=null) {
3610
3611
	$img = trim($img);
3612
	$img_file = $img;
3613
	if (strpos($img, '<svg') === false){
3614
		if ($p = strpos($img_file, '?')){
3615
			$img_file = substr($img_file, 0, $p);
3616
		}
3617
3618
		if (!$img_file or !$svg = file_get_contents($img_file)){
3619
			return '';
3620
		}
3621
	}
3622
3623
	if (!preg_match(",<svg\b[^>]*>,UimsS", $svg, $match)) {
0 ignored issues
show
Bug introduced by
The variable $svg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3624
		return '';
3625
	}
3626
3627
	list($alt, $class, $size) = helper_filtre_balise_img_svg_arguments($alt, $class, $size);
3628
3629
	$balise_svg = $match[0];
3630
	$balise_svg_source = $balise_svg;
3631
3632
	// entete XML à supprimer
3633
	$svg = preg_replace(',^\s*<\?xml[^>]*\?' . '>,', '', $svg);
3634
3635
	// IE est toujours mon ami
3636
	$balise_svg = inserer_attribut($balise_svg, 'focusable', 'false');
3637
3638
	// regler la classe
3639 View Code Duplication
	if (!is_null($class)) {
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...
3640
		if (strlen($class)) {
3641
			$balise_svg = inserer_attribut($balise_svg, 'class', $class);
3642
		}
3643
		else {
3644
			$balise_svg = vider_attribut($balise_svg, 'class');
3645
		}
3646
	}
3647
3648
	// regler le alt
3649
	if ($alt){
3650
		$balise_svg = inserer_attribut($balise_svg, 'role', 'img');
3651
		$id = "img-svg-title-" . substr(md5("$img_file:$svg:$alt"),0,4);
3652
		$balise_svg = inserer_attribut($balise_svg, 'aria-labelledby', $id);
3653
		$title = "<title id=\"$id\">" . entites_html($alt)."</title>\n";
3654
		$balise_svg .= $title;
3655
	}
3656
	else {
3657
		$balise_svg = inserer_attribut($balise_svg, 'aria-hidden', 'true');
3658
	}
3659
3660
	$svg = str_replace($balise_svg_source, $balise_svg, $svg);
3661
3662
	if (!is_null($size) and strlen($size = trim($size))) {
3663
		list($width, $height) = helper_filtre_balise_img_svg_size($svg, $size);
3664
3665
		if (!function_exists('svg_redimensionner')) {
3666
			include_spip('inc/svg');
3667
		}
3668
		$svg = svg_redimensionner($svg, $width, $height);
3669
	}
3670
3671
	return $svg;
3672
}
3673
3674
3675
3676
/**
3677
 * Affiche chaque valeur d'un tableau associatif en utilisant un modèle
3678
 *
3679
 * @example
3680
 *     - `[(#ENV*|unserialize|foreach)]`
3681
 *     - `[(#ARRAY{a,un,b,deux}|foreach)]`
3682
 *
3683
 * @filtre
3684
 * @link https://www.spip.net/4248
3685
 *
3686
 * @param array $tableau
3687
 *     Tableau de données à afficher
3688
 * @param string $modele
3689
 *     Nom du modèle à utiliser
3690
 * @return string
3691
 *     Code HTML résultant
3692
 **/
3693
function filtre_foreach_dist($tableau, $modele = 'foreach') {
3694
	$texte = '';
3695
	if (is_array($tableau)) {
3696
		foreach ($tableau as $k => $v) {
3697
			$res = recuperer_fond('modeles/' . $modele,
3698
				array_merge(array('cle' => $k), (is_array($v) ? $v : array('valeur' => $v)))
3699
			);
3700
			$texte .= $res;
3701
		}
3702
	}
3703
3704
	return $texte;
3705
}
3706
3707
3708
/**
3709
 * Obtient des informations sur les plugins actifs
3710
 *
3711
 * @filtre
3712
 * @uses liste_plugin_actifs() pour connaître les informations affichables
3713
 *
3714
 * @param string $plugin
3715
 *     Préfixe du plugin ou chaîne vide
3716
 * @param string $type_info
3717
 *     Type d'info demandée
3718
 * @param bool $reload
3719
 *     true (à éviter) pour forcer le recalcul du cache des informations des plugins.
3720
 * @return array|string|bool
3721
 *
3722
 *     - Liste sérialisée des préfixes de plugins actifs (si $plugin = '')
3723
 *     - Suivant $type_info, avec $plugin un préfixe
3724
 *         - est_actif : renvoie true s'il est actif, false sinon
3725
 *         - x : retourne l'information x du plugin si présente (et plugin actif)
3726
 *         - tout : retourne toutes les informations du plugin actif
3727
 **/
3728
function filtre_info_plugin_dist($plugin, $type_info, $reload = false) {
3729
	include_spip('inc/plugin');
3730
	$plugin = strtoupper($plugin);
3731
	$plugins_actifs = liste_plugin_actifs();
3732
3733
	if (!$plugin) {
3734
		return serialize(array_keys($plugins_actifs));
3735
	} elseif (empty($plugins_actifs[$plugin]) and !$reload) {
3736
		return '';
3737
	} elseif (($type_info == 'est_actif') and !$reload) {
3738
		return $plugins_actifs[$plugin] ? 1 : 0;
3739
	} elseif (isset($plugins_actifs[$plugin][$type_info]) and !$reload) {
3740
		return $plugins_actifs[$plugin][$type_info];
3741
	} else {
3742
		$get_infos = charger_fonction('get_infos', 'plugins');
3743
		// On prend en compte les extensions
3744
		if (!is_dir($plugins_actifs[$plugin]['dir_type'])) {
3745
			$dir_plugins = constant($plugins_actifs[$plugin]['dir_type']);
3746
		} else {
3747
			$dir_plugins = $plugins_actifs[$plugin]['dir_type'];
3748
		}
3749
		if (!$infos = $get_infos($plugins_actifs[$plugin]['dir'], $reload, $dir_plugins)) {
3750
			return '';
3751
		}
3752
		if ($type_info == 'tout') {
3753
			return $infos;
3754
		} elseif ($type_info == 'est_actif') {
3755
			return $infos ? 1 : 0;
3756
		} else {
3757
			return strval($infos[$type_info]);
3758
		}
3759
	}
3760
}
3761
3762
3763
/**
3764
 * Affiche la puce statut d'un objet, avec un menu rapide pour changer
3765
 * de statut si possibilité de l'avoir
3766
 *
3767
 * @see inc_puce_statut_dist()
3768
 *
3769
 * @filtre
3770
 *
3771
 * @param int $id_objet
3772
 *     Identifiant de l'objet
3773
 * @param string $statut
3774
 *     Statut actuel de l'objet
3775
 * @param int $id_rubrique
3776
 *     Identifiant du parent
3777
 * @param string $type
3778
 *     Type d'objet
3779
 * @param bool $ajax
3780
 *     Indique s'il ne faut renvoyer que le coeur du menu car on est
3781
 *     dans une requete ajax suite à un post de changement rapide
3782
 * @return string
3783
 *     Code HTML de l'image de puce de statut à insérer (et du menu de changement si présent)
3784
 */
3785
function puce_changement_statut($id_objet, $statut, $id_rubrique, $type, $ajax = false) {
3786
	$puce_statut = charger_fonction('puce_statut', 'inc');
3787
3788
	return $puce_statut($id_objet, $statut, $id_rubrique, $type, $ajax);
3789
}
3790
3791
3792
/**
3793
 * Affiche la puce statut d'un objet, avec un menu rapide pour changer
3794
 * de statut si possibilité de l'avoir
3795
 *
3796
 * Utilisable sur tout objet qui a declaré ses statuts
3797
 *
3798
 * @example
3799
 *     [(#STATUT|puce_statut{article})] affiche une puce passive
3800
 *     [(#STATUT|puce_statut{article,#ID_ARTICLE,#ID_RUBRIQUE})] affiche une puce avec changement rapide
3801
 *
3802
 * @see inc_puce_statut_dist()
3803
 *
3804
 * @filtre
3805
 *
3806
 * @param string $statut
3807
 *     Statut actuel de l'objet
3808
 * @param string $objet
3809
 *     Type d'objet
3810
 * @param int $id_objet
3811
 *     Identifiant de l'objet
3812
 * @param int $id_parent
3813
 *     Identifiant du parent
3814
 * @return string
3815
 *     Code HTML de l'image de puce de statut à insérer (et du menu de changement si présent)
3816
 */
3817
function filtre_puce_statut_dist($statut, $objet, $id_objet = 0, $id_parent = 0) {
3818
	static $puce_statut = null;
3819
	if (!$puce_statut) {
3820
		$puce_statut = charger_fonction('puce_statut', 'inc');
3821
	}
3822
3823
	return $puce_statut($id_objet, $statut, $id_parent, $objet, false,
3824
		objet_info($objet, 'editable') ? _ACTIVER_PUCE_RAPIDE : false);
3825
}
3826
3827
3828
/**
3829
 * Encoder un contexte pour l'ajax
3830
 *
3831
 * Encoder le contexte, le signer avec une clé, le crypter
3832
 * avec le secret du site, le gziper si possible.
3833
 *
3834
 * L'entrée peut-être sérialisée (le `#ENV**` des fonds ajax et ajax_stat)
3835
 *
3836
 * @see  decoder_contexte_ajax()
3837
 * @uses calculer_cle_action()
3838
 *
3839
 * @param string|array $c
3840
 *   contexte, peut etre un tableau serialize
3841
 * @param string $form
3842
 *   nom du formulaire eventuel
3843
 * @param string $emboite
0 ignored issues
show
Documentation introduced by
Should the type for parameter $emboite 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...
3844
 *   contenu a emboiter dans le conteneur ajax
3845
 * @param string $ajaxid
3846
 *   ajaxid pour cibler le bloc et forcer sa mise a jour
3847
 * @return string
3848
 *   hash du contexte
3849
 */
3850
function encoder_contexte_ajax($c, $form = '', $emboite = null, $ajaxid = '') {
3851
	if (is_string($c)
3852
		and @unserialize($c) !== false
3853
	) {
3854
		$c = unserialize($c);
3855
	}
3856
3857
	// supprimer les parametres debut_x
3858
	// pour que la pagination ajax ne soit pas plantee
3859
	// si on charge la page &debut_x=1 : car alors en cliquant sur l'item 0,
3860
	// le debut_x=0 n'existe pas, et on resterait sur 1
3861
	if (is_array($c)) {
3862
		foreach ($c as $k => $v) {
3863
			if (strpos($k, 'debut_') === 0) {
3864
				unset($c[$k]);
3865
			}
3866
		}
3867
	}
3868
3869
	if (!function_exists('calculer_cle_action')) {
3870
		include_spip("inc/securiser_action");
3871
	}
3872
3873
	$c = serialize($c);
3874
	$cle = calculer_cle_action($form . $c);
3875
	$c = "$cle:$c";
3876
3877
	// on ne stocke pas les contextes dans des fichiers en cache
3878
	// par defaut, sauf si cette configuration a été forcée
3879
	// OU que la longueur de l’argument géneré est plus long
3880
	// que ce qui est toléré.
3881
	$cache_contextes_ajax = (defined('_CACHE_CONTEXTES_AJAX') and _CACHE_CONTEXTES_AJAX);
3882
	if (!$cache_contextes_ajax) {
3883
		$env = $c;
3884
		if (function_exists('gzdeflate') && function_exists('gzinflate')) {
3885
			$env = gzdeflate($env);
3886
			// https://core.spip.net/issues/2667 | https://bugs.php.net/bug.php?id=61287
3887
			if ((PHP_VERSION_ID == 50400) and !@gzinflate($env)) {
3888
				$cache_contextes_ajax = true;
3889
				spip_log("Contextes AJAX forces en fichiers ! Erreur PHP 5.4.0", _LOG_AVERTISSEMENT);
3890
			}
3891
		}
3892
		$env = _xor($env);
3893
		$env = base64_encode($env);
3894
		$len = strlen($env);
3895
		// Si l’url est trop longue pour le navigateur
3896
		$max_len = _CACHE_CONTEXTES_AJAX_SUR_LONGUEUR;
3897
		if ($len > $max_len) {
3898
			$cache_contextes_ajax = true;
3899
			spip_log("Contextes AJAX forces en fichiers !"
3900
				. " Cela arrive lorsque la valeur du contexte" 
3901
				. " depasse la longueur maximale autorisee ($max_len). Ici : $len."
3902
				, _LOG_AVERTISSEMENT);
3903
		}
3904
		// Sinon si Suhosin est actif et a une la valeur maximale des variables en GET...
3905
		elseif (
3906
			$max_len = @ini_get('suhosin.get.max_value_length')
3907
			and $max_len < $len
3908
		) {
3909
			$cache_contextes_ajax = true;
3910
			spip_log("Contextes AJAX forces en fichiers !"
3911
				. " Cela arrive lorsque la valeur du contexte"
3912
				. " depasse la longueur maximale autorisee par Suhosin"
3913
				. " ($max_len) dans 'suhosin.get.max_value_length'. Ici : $len."
3914
				. " Vous devriez modifier les parametres de Suhosin"
3915
				. " pour accepter au moins 1024 caracteres.", _LOG_AVERTISSEMENT);
3916
		} 
3917
3918
	}
3919
3920
	if ($cache_contextes_ajax) {
3921
		$dir = sous_repertoire(_DIR_CACHE, 'contextes');
3922
		// stocker les contextes sur disque et ne passer qu'un hash dans l'url
3923
		$md5 = md5($c);
3924
		ecrire_fichier("$dir/c$md5", $c);
3925
		$env = $md5;
3926
	}
3927
3928
	if ($emboite === null) {
3929
		return $env;
0 ignored issues
show
Bug introduced by
The variable $env does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
3930
	}
3931
	if (!trim($emboite)) {
3932
		return "";
3933
	}
3934
	// toujours encoder l'url source dans le bloc ajax
3935
	$r = self();
3936
	$r = ' data-origin="' . $r . '"';
3937
	$class = 'ajaxbloc';
3938
	if ($ajaxid and is_string($ajaxid)) {
3939
		// ajaxid est normalement conforme a un nom de classe css
3940
		// on ne verifie pas la conformite, mais on passe entites_html par dessus par precaution
3941
		$class .= ' ajax-id-' . entites_html($ajaxid);
3942
	}
3943
3944
	return "<div class='$class' " . "data-ajax-env='$env'$r>\n$emboite</div><!--ajaxbloc-->\n";
3945
}
3946
3947
/**
3948
 * Décoder un hash de contexte pour l'ajax
3949
 *
3950
 * Précude inverse de `encoder_contexte_ajax()`
3951
 *
3952
 * @see  encoder_contexte_ajax()
3953
 * @uses calculer_cle_action()
3954
 *
3955
 * @param string $c
3956
 *   hash du contexte
3957
 * @param string $form
3958
 *   nom du formulaire eventuel
3959
 * @return array|string|bool
3960
 *   - array|string : contexte d'environnement, possiblement sérialisé
3961
 *   - false : erreur de décodage
3962
 */
3963
function decoder_contexte_ajax($c, $form = '') {
3964
	if (!function_exists('calculer_cle_action')) {
3965
		include_spip("inc/securiser_action");
3966
	}
3967
	if (((defined('_CACHE_CONTEXTES_AJAX') and _CACHE_CONTEXTES_AJAX) or strlen($c) == 32)
3968
		and $dir = sous_repertoire(_DIR_CACHE, 'contextes')
3969
		and lire_fichier("$dir/c$c", $contexte)
3970
	) {
3971
		$c = $contexte;
3972
	} else {
3973
		$c = @base64_decode($c);
3974
		$c = _xor($c);
3975
		if (function_exists('gzdeflate') && function_exists('gzinflate')) {
3976
			$c = @gzinflate($c);
3977
		}
3978
	}
3979
3980
	// extraire la signature en debut de contexte
3981
	// et la verifier avant de deserializer
3982
	// format : signature:donneesserializees
3983 View Code Duplication
	if ($p = strpos($c,":")){
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...
3984
		$cle = substr($c,0,$p);
3985
		$c = substr($c,$p+1);
3986
3987
		if ($cle == calculer_cle_action($form . $c)) {
3988
			$env = @unserialize($c);
3989
			return $env;
3990
		}
3991
	}
3992
3993
	return false;
3994
}
3995
3996
3997
/**
3998
 * Encrypte ou décrypte un message
3999
 *
4000
 * @link http://www.php.net/manual/fr/language.operators.bitwise.php#81358
4001
 *
4002
 * @param string $message
4003
 *    Message à encrypter ou décrypter
4004
 * @param null|string $key
4005
 *    Clé de cryptage / décryptage.
4006
 *    Une clé sera calculée si non transmise
4007
 * @return string
4008
 *    Message décrypté ou encrypté
4009
 **/
4010
function _xor($message, $key = null) {
4011
	if (is_null($key)) {
4012
		if (!function_exists('calculer_cle_action')) {
4013
			include_spip("inc/securiser_action");
4014
		}
4015
		$key = pack("H*", calculer_cle_action('_xor'));
4016
	}
4017
4018
	$keylen = strlen($key);
4019
	$messagelen = strlen($message);
4020
	for ($i = 0; $i < $messagelen; $i++) {
4021
		$message[$i] = ~($message[$i] ^ $key[$i % $keylen]);
4022
	}
4023
4024
	return $message;
4025
}
4026
4027
/**
4028
 * Retourne une URL de réponse de forum (aucune action ici)
4029
 *
4030
 * @see filtre_url_reponse_forum() du plugin forum (prioritaire)
4031
 * @note
4032
 *   La vraie fonction est dans le plugin forum,
4033
 *   mais on évite ici une erreur du compilateur en absence du plugin
4034
 * @param string $texte
4035
 * @return string
4036
 */
4037
function url_reponse_forum($texte) { return $texte; }
4038
4039
/**
4040
 * retourne une URL de suivi rss d'un forum (aucune action ici)
4041
 *
4042
 * @see filtre_url_rss_forum() du plugin forum (prioritaire)
4043
 * @note
4044
 *   La vraie fonction est dans le plugin forum,
4045
 *   mais on évite ici une erreur du compilateur en absence du plugin
4046
 * @param string $texte
4047
 * @return string
4048
 */
4049
function url_rss_forum($texte) { return $texte; }
4050
4051
4052
/**
4053
 * Génère des menus avec liens ou `<strong class='on'>` non clicable lorsque
4054
 * l'item est sélectionné
4055
 * Le parametre $on peut recevoir un selecteur simplifié de type 'a.active' 'strong.active.expose'
4056
 * pour préciser la balise (a, span ou strong uniquement) et la/les classes à utiliser quand on est expose
4057
 *
4058
 * @filtre
4059
 * @link https://www.spip.net/4004
4060
 * @example
4061
 *   ```
4062
 *   [(#URL_RUBRIQUE|lien_ou_expose{#TITRE, #ENV{test}|=={en_cours}})]
4063
 *   [(#URL_RUBRIQUE|lien_ou_expose{#TITRE, #ENV{test}|=={en_cours}|?{a.monlien.active}, 'monlien'})]
4064
 *   ```
4065
 *
4066
 *
4067
 * @param string $url
4068
 *   URL du lien
4069
 * @param string $libelle
0 ignored issues
show
Documentation introduced by
Should the type for parameter $libelle 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...
4070
 *   Texte du lien
4071
 * @param bool $on
4072
 *   État exposé ou non (génère un lien). Si $on vaut true ou 1 ou ' ', l'etat expose est rendu par un <strong class='on'>...</strong>
4073
 *   Si $on est de la forme span.active.truc par exemple, l'etat expose est rendu par <span class='active truc'>...</span> Seules les balises a, span et strong sont acceptees dans ce cas
4074
 * @param string $class
4075
 *   Classes CSS ajoutées au lien
4076
 * @param string $title
4077
 *   Title ajouté au lien
4078
 * @param string $rel
4079
 *   Attribut `rel` ajouté au lien
4080
 * @param string $evt
4081
 *   Complement à la balise `a` pour gérer un événement javascript,
4082
 *   de la forme ` onclick='...'`
4083
 * @return string
4084
 *   Code HTML
4085
 */
4086
function lien_ou_expose($url, $libelle = null, $on = false, $class = "", $title = "", $rel = "", $evt = '') {
4087
	if ($on) {
4088
		$bal = 'strong';
4089
		$class = "";
4090
		$att = "";
4091
		// si $on passe la balise et optionnelement une ou ++classe
4092
		// a.active span.selected.active etc....
4093
		if (is_string($on) and (strncmp($on, 'a', 1)==0 or strncmp($on, 'span', 4)==0 or strncmp($on, 'strong', 6)==0)){
4094
			$on = explode(".", $on);
4095
			// on verifie que c'est exactement une des 3 balises a, span ou strong
4096
			if (in_array(reset($on), array('a', 'span', 'strong'))){
4097
				$bal = array_shift($on);
4098
				$class = implode(" ", $on);
4099
				if ($bal=="a"){
4100
					$att = 'href="#" ';
4101
				}
4102
			}
4103
		}
4104
		$att .= 'class="' . ($class ? attribut_html($class) . ' ' : '') . (defined('_LIEN_OU_EXPOSE_CLASS_ON') ? _LIEN_OU_EXPOSE_CLASS_ON : 'on') . '"';
4105
	} else {
4106
		$bal = 'a';
4107
		$att = "href='$url'"
4108
			. ($title ? " title='" . attribut_html($title) . "'" : '')
4109
			. ($class ? " class='" . attribut_html($class) . "'" : '')
4110
			. ($rel ? " rel='" . attribut_html($rel) . "'" : '')
4111
			. $evt;
4112
	}
4113
	if ($libelle === null) {
4114
		$libelle = $url;
4115
	}
4116
4117
	return "<$bal $att>$libelle</$bal>";
4118
}
4119
4120
4121
/**
4122
 * Afficher un message "un truc"/"N trucs"
4123
 * Les items sont à indiquer comme pour la fonction _T() sous la forme :
4124
 * "module:chaine"
4125
 *
4126
 * @param int $nb : le nombre
4127
 * @param string $chaine_un : l'item de langue si $nb vaut un
4128
 * @param string $chaine_plusieurs : l'item de lanque si $nb >= 2
4129
 * @param string $var : La variable à remplacer par $nb dans l'item de langue (facultatif, défaut "nb")
4130
 * @param array $vars : Les autres variables nécessaires aux chaines de langues (facultatif)
4131
 * @return string : la chaine de langue finale en utilisant la fonction _T()
4132
 */
4133
function singulier_ou_pluriel($nb, $chaine_un, $chaine_plusieurs, $var = 'nb', $vars = array()) {
4134
	static $local_singulier_ou_pluriel = array();
4135
4136
	// si nb=0 ou pas de $vars valide on retourne une chaine vide, a traiter par un |sinon
4137
	if (!is_numeric($nb) or $nb == 0) {
4138
		return "";
4139
	}
4140
	if (!is_array($vars)) {
4141
		return "";
4142
	}
4143
4144
	$langue = $GLOBALS['spip_lang'];
4145
	if (!isset($local_singulier_ou_pluriel[$langue])) {
4146
		$local_singulier_ou_pluriel[$langue] = false;
4147
		if ($f = charger_fonction("singulier_ou_pluriel_${langue}", 'inc', true)
4148
		  or $f = charger_fonction("singulier_ou_pluriel", 'inc', true)) {
4149
			$local_singulier_ou_pluriel[$langue] = $f;
4150
		}
4151
	}
4152
4153
	// si on a une surcharge on l'utilise
4154
	if ($local_singulier_ou_pluriel[$langue]) {
4155
		return ($local_singulier_ou_pluriel[$langue])($nb, $chaine_un, $chaine_plusieurs, $var, $vars);
4156
	}
4157
4158
	// sinon traitement par defaut
4159
	$vars[$var] = $nb;
4160
	if ($nb >= 2) {
4161
		return _T($chaine_plusieurs, $vars);
4162
	} else {
4163
		return _T($chaine_un, $vars);
4164
	}
4165
}
4166
4167
4168
/**
4169
 * Fonction de base pour une icone dans un squelette
4170
 * structure html : `<span><a><img><b>texte</b></span>`
4171
 *
4172
 * @param string $type
4173
 *  'lien' ou 'bouton'
4174
 * @param string $lien
4175
 *  url
4176
 * @param string $texte
4177
 *  texte du lien / alt de l'image
4178
 * @param string $fond
4179
 *  objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4180
 * @param string $fonction
4181
 *  new/del/edit
4182
 * @param string $class
4183
 *  classe supplementaire (horizontale, verticale, ajax ...)
4184
 * @param string $javascript
4185
 *  "onclick='...'" par exemple
4186
 * @return string
4187
 */
4188
function prepare_icone_base($type, $lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4189
	if (in_array($fonction, array("del", "supprimer.gif"))) {
4190
		$class .= ' danger';
4191
	} elseif ($fonction == "rien.gif") {
4192
		$fonction = "";
4193
	} elseif ($fonction == "delsafe") {
4194
		$fonction = "del";
4195
	}
4196
4197
	$fond_origine = $fond;
4198
	// remappage des icone : article-24.png+new => article-new-24.png
4199 View Code Duplication
	if ($icone_renommer = charger_fonction('icone_renommer', 'inc', true)) {
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...
4200
		list($fond, $fonction) = $icone_renommer($fond, $fonction);
4201
	}
4202
4203
	// ajouter le type d'objet dans la class de l'icone
4204
	$class .= " " . substr(basename($fond), 0, -4);
4205
4206
	$alt = attribut_html($texte);
4207
	$title = " title=\"$alt\""; // est-ce pertinent de doubler le alt par un title ?
4208
4209
	$ajax = "";
4210
	if (strpos($class, "ajax") !== false) {
4211
		$ajax = "ajax";
4212
		if (strpos($class, "preload") !== false) {
4213
			$ajax .= " preload";
4214
		}
4215
		if (strpos($class, "nocache") !== false) {
4216
			$ajax .= " nocache";
4217
		}
4218
		$ajax = " class='$ajax'";
4219
	}
4220
4221
	$size = 24;
4222
	if (preg_match("/-([0-9]{1,3})[.](gif|png|svg)$/i", $fond, $match)
4223
	  or preg_match("/-([0-9]{1,3})([.](gif|png|svg))?$/i", $fond_origine, $match)) {
4224
		$size = $match[1];
4225
	}
4226
4227
	$icone = http_img_pack($fond, $alt, "width='$size' height='$size'");
4228
	$icone = "<span class=\"icone-image".($fonction ? " icone-fonction icone-fonction-$fonction" : "") . "\">$icone</span>";
4229
4230
	if ($type == 'lien') {
4231
		return "<span class='icone s$size $class'>"
4232
		. "<a href='$lien'$title$ajax$javascript>"
4233
		. $icone
4234
		. "<b>$texte</b>"
4235
		. "</a></span>\n";
4236
	} else {
4237
		return bouton_action("$icone<b>$texte</b>", $lien, "icone s$size $class", $javascript, $alt);
4238
	}
4239
}
4240
4241
/**
4242
 * Crée un lien ayant une icone
4243
 *
4244
 * @uses prepare_icone_base()
4245
 *
4246
 * @param string $lien
4247
 *     URL du lien
4248
 * @param string $texte
4249
 *     Texte du lien
4250
 * @param string $fond
4251
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4252
 * @param string $fonction
4253
 *     Fonction du lien (`edit`, `new`, `del`)
4254
 * @param string $class
4255
 *     Classe CSS, tel que `left`, `right` pour définir un alignement
4256
 * @param string $javascript
4257
 *     Javascript ajouté sur le lien
4258
 * @return string
4259
 *     Code HTML du lien
4260
 **/
4261
function icone_base($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4262
	return prepare_icone_base('lien', $lien, $texte, $fond, $fonction, $class, $javascript);
4263
}
4264
4265
/**
4266
 * Crée un lien précédé d'une icone au dessus du texte
4267
 *
4268
 * @uses icone_base()
4269
 * @see  icone_verticale() Pour un usage dans un code PHP.
4270
 *
4271
 * @filtre
4272
 * @example
4273
 *     ```
4274
 *     [(#AUTORISER{voir,groupemots,#ID_GROUPE})
4275
 *         [(#URL_ECRIRE{groupe_mots,id_groupe=#ID_GROUPE}
4276
 *            |icone_verticale{<:mots:icone_voir_groupe_mots:>,groupe_mots-24.png,'',left})]
4277
 *    ]
4278
 *     ```
4279
 *
4280
 * @param string $lien
4281
 *     URL du lien
4282
 * @param string $texte
4283
 *     Texte du lien
4284
 * @param string $fond
4285
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4286
 * @param string $fonction
4287
 *     Fonction du lien (`edit`, `new`, `del`)
4288
 * @param string $class
4289
 *     Classe CSS à ajouter, tel que `left`, `right`, `center` pour définir un alignement.
4290
 *     Il peut y en avoir plusieurs : `left ajax`
4291
 * @param string $javascript
4292
 *     Javascript ajouté sur le lien
4293
 * @return string
4294
 *     Code HTML du lien
4295
 **/
4296
function filtre_icone_verticale_dist($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4297
	return icone_base($lien, $texte, $fond, $fonction, "verticale $class", $javascript);
4298
}
4299
4300
/**
4301
 * Crée un lien précédé d'une icone horizontale
4302
 *
4303
 * @uses icone_base()
4304
 * @see  icone_horizontale() Pour un usage dans un code PHP.
4305
 *
4306
 * @filtre
4307
 * @example
4308
 *     En tant que filtre dans un squelettes :
4309
 *     ```
4310
 *     [(#URL_ECRIRE{sites}|icone_horizontale{<:sites:icone_voir_sites_references:>,site-24.png})]
4311
 *
4312
 *     [(#AUTORISER{supprimer,groupemots,#ID_GROUPE}|oui)
4313
 *         [(#URL_ACTION_AUTEUR{supprimer_groupe_mots,#ID_GROUPE,#URL_ECRIRE{mots}}
4314
 *             |icone_horizontale{<:mots:icone_supprimer_groupe_mots:>,groupe_mots,del})]
4315
 *     ]
4316
 *     ```
4317
 *
4318
 *     En tant que filtre dans un code php :
4319
 *     ```
4320
 *     $icone_horizontale=chercher_filtre('icone_horizontale');
4321
 *     $icone = $icone_horizontale(generer_url_ecrire("stats_visites","id_article=$id_article"),
4322
 *         _T('statistiques:icone_evolution_visites', array('visites' => $visites)),
4323
 *         "statistique-24.png");
4324
 *     ```
4325
 *
4326
 * @param string $lien
4327
 *     URL du lien
4328
 * @param string $texte
4329
 *     Texte du lien
4330
 * @param string $fond
4331
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4332
 * @param string $fonction
4333
 *     Fonction du lien (`edit`, `new`, `del`)
4334
 * @param string $class
4335
 *     Classe CSS à ajouter
4336
 * @param string $javascript
4337
 *     Javascript ajouté sur le lien
4338
 * @return string
4339
 *     Code HTML du lien
4340
 **/
4341
function filtre_icone_horizontale_dist($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4342
	return icone_base($lien, $texte, $fond, $fonction, "horizontale $class", $javascript);
4343
}
4344
4345
/**
4346
 * Crée un bouton d'action intégrant une icone horizontale
4347
 *
4348
 * @uses prepare_icone_base()
4349
 *
4350
 * @filtre
4351
 * @example
4352
 *     ```
4353
 *     [(#URL_ACTION_AUTEUR{supprimer_mot, #ID_MOT, #URL_ECRIRE{groupe_mots,id_groupe=#ID_GROUPE}}
4354
 *         |bouton_action_horizontal{<:mots:info_supprimer_mot:>,mot-24.png,del})]
4355
 *     ```
4356
 *
4357
 * @param string $lien
4358
 *     URL de l'action
4359
 * @param string $texte
4360
 *     Texte du bouton
4361
 * @param string $fond
4362
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4363
 * @param string $fonction
4364
 *     Fonction du bouton (`edit`, `new`, `del`)
4365
 * @param string $class
4366
 *     Classe CSS à ajouter
4367
 * @param string $confirm
4368
 *     Message de confirmation à ajouter en javascript sur le bouton
4369
 * @return string
4370
 *     Code HTML du lien
4371
 **/
4372
function filtre_bouton_action_horizontal_dist($lien, $texte, $fond, $fonction = "", $class = "", $confirm = "") {
4373
	return prepare_icone_base('bouton', $lien, $texte, $fond, $fonction, "horizontale $class", $confirm);
4374
}
4375
4376
/**
4377
 * Filtre `icone` pour compatibilité mappé sur `icone_base`
4378
 *
4379
 * @uses icone_base()
4380
 * @see  filtre_icone_verticale_dist()
4381
 *
4382
 * @filtre
4383
 * @deprecated Utiliser le filtre `icone_verticale`
4384
 *
4385
 * @param string $lien
4386
 *     URL du lien
4387
 * @param string $texte
4388
 *     Texte du lien
4389
 * @param string $fond
4390
 *     Nom de l'image utilisée
4391
 * @param string $align
4392
 *     Classe CSS d'alignement (`left`, `right`, `center`)
4393
 * @param string $fonction
4394
 *     Fonction du lien (`edit`, `new`, `del`)
4395
 * @param string $class
4396
 *     Classe CSS à ajouter
4397
 * @param string $javascript
4398
 *     Javascript ajouté sur le lien
4399
 * @return string
4400
 *     Code HTML du lien
4401
 */
4402
function filtre_icone_dist($lien, $texte, $fond, $align = "", $fonction = "", $class = "", $javascript = "") {
4403
	return icone_base($lien, $texte, $fond, $fonction, "verticale $align $class", $javascript);
4404
}
4405
4406
4407
/**
4408
 * Explose un texte en tableau suivant un séparateur
4409
 *
4410
 * @note
4411
 *     Inverse l'écriture de la fonction PHP de même nom
4412
 *     pour que le filtre soit plus pratique dans les squelettes
4413
 *
4414
 * @filtre
4415
 * @example
4416
 *     ```
4417
 *     [(#GET{truc}|explode{-})]
4418
 *     ```
4419
 *
4420
 * @param string $a Texte
4421
 * @param string $b Séparateur
4422
 * @return array Liste des éléments
4423
 */
4424
function filtre_explode_dist($a, $b) { return explode($b, $a); }
4425
4426
/**
4427
 * Implose un tableau en chaine en liant avec un séparateur
4428
 *
4429
 * @note
4430
 *     Inverse l'écriture de la fonction PHP de même nom
4431
 *     pour que le filtre soit plus pratique dans les squelettes
4432
 *
4433
 * @filtre
4434
 * @example
4435
 *     ```
4436
 *     [(#GET{truc}|implode{-})]
4437
 *     ```
4438
 *
4439
 * @param array $a Tableau
4440
 * @param string $b Séparateur
4441
 * @return string Texte
4442
 */
4443
function filtre_implode_dist($a, $b) { return is_array($a) ? implode($b, $a) : $a; }
4444
4445
/**
4446
 * Produire les styles privés qui associent item de menu avec icone en background
4447
 *
4448
 * @return string Code CSS
4449
 */
4450
function bando_images_background() {
4451
	include_spip('inc/bandeau');
4452
	// recuperer tous les boutons et leurs images
4453
	$boutons = definir_barre_boutons(definir_barre_contexte(), true, false);
4454
4455
	$res = "";
4456
	foreach ($boutons as $page => $detail) {
4457
		if ($detail->icone and strlen(trim($detail->icone))) {
4458
			$res .= "\n.navigation_avec_icones #bando1_$page {background-image:url(" . $detail->icone . ");}";
4459
		}
4460
		$selecteur = (in_array($page, array('outils_rapides', 'outils_collaboratifs')) ? "" : ".navigation_avec_icones ");
4461
		if (is_array($detail->sousmenu)) {
4462
			foreach ($detail->sousmenu as $souspage => $sousdetail) {
4463
				if ($sousdetail->icone and strlen(trim($sousdetail->icone))) {
4464
					$res .= "\n$selecteur.bando2_$souspage {background-image:url(" . $sousdetail->icone . ");}";
4465
				}
4466
			}
4467
		}
4468
	}
4469
4470
	return $res;
4471
}
4472
4473
/**
4474
 * Generer un bouton_action
4475
 * utilise par #BOUTON_ACTION
4476
 *
4477
 * @param string $libelle
4478
 * @param string $url
4479
 * @param string $class
4480
 * @param string $confirm
4481
 *   message de confirmation oui/non avant l'action
4482
 * @param string $title
4483
 * @param string $callback
4484
 *   callback js a appeler lors de l'evenement action (apres confirmation eventuelle si $confirm est non vide)
4485
 *   et avant execution de l'action. Si la callback renvoie false, elle annule le declenchement de l'action
4486
 * @return string
4487
 */
4488
function bouton_action($libelle, $url, $class = "", $confirm = "", $title = "", $callback = "") {
4489
	if ($confirm) {
4490
		$confirm = "confirm(\"" . attribut_html($confirm) . "\")";
4491
		if ($callback) {
4492
			$callback = "$confirm?($callback):false";
4493
		} else {
4494
			$callback = $confirm;
4495
		}
4496
	}
4497
	$onclick = $callback ? " onclick='return " . addcslashes($callback, "'") . "'" : "";
4498
	$title = $title ? " title='$title'" : "";
4499
4500
	return "<form class='bouton_action_post $class' method='post' action='$url'><div>" . form_hidden($url)
4501
	. "<button type='submit' class='submit'$title$onclick>$libelle</button></div></form>";
4502
}
4503
4504
/**
4505
 * Donner n'importe quelle information sur un objet de maniere generique.
4506
 *
4507
 * La fonction va gerer en interne deux cas particuliers les plus utilises :
4508
 * l'URL et le titre (qui n'est pas forcemment le champ SQL "titre").
4509
 *
4510
 * On peut ensuite personnaliser les autres infos en creant une fonction
4511
 * generer_<nom_info>_entite($id_objet, $type_objet, $ligne).
4512
 * $ligne correspond a la ligne SQL de tous les champs de l'objet, les fonctions
4513
 * de personnalisation n'ont donc pas a refaire de requete.
4514
 *
4515
 * @param int $id_objet
4516
 * @param string $type_objet
4517
 * @param string $info
4518
 * @param string $etoile
4519
 * @return string
4520
 */
4521
function generer_info_entite($id_objet, $type_objet, $info, $etoile = "") {
4522
	static $trouver_table = null;
4523
	static $objets;
4524
4525
	// On verifie qu'on a tout ce qu'il faut
4526
	$id_objet = intval($id_objet);
4527
	if (!($id_objet and $type_objet and $info)) {
4528
		return '';
4529
	}
4530
4531
	// si on a deja note que l'objet n'existe pas, ne pas aller plus loin
4532
	if (isset($objets[$type_objet]) and $objets[$type_objet] === false) {
4533
		return '';
4534
	}
4535
4536
	// Si on demande l'url, on retourne direct la fonction
4537
	if ($info == 'url') {
4538
		return generer_url_entite($id_objet, $type_objet);
4539
	}
4540
4541
	// Sinon on va tout chercher dans la table et on garde en memoire
4542
	$demande_titre = ($info == 'titre');
4543
4544
	// On ne fait la requete que si on a pas deja l'objet ou si on demande le titre mais qu'on ne l'a pas encore
4545
	if (!isset($objets[$type_objet][$id_objet])
4546
		or
4547
		($demande_titre and !isset($objets[$type_objet][$id_objet]['titre']))
4548
	) {
4549
		if (!$trouver_table) {
4550
			$trouver_table = charger_fonction('trouver_table', 'base');
4551
		}
4552
		$desc = $trouver_table(table_objet_sql($type_objet));
4553
		if (!$desc) {
4554
			return $objets[$type_objet] = false;
4555
		}
4556
4557
		// Si on demande le titre, on le gere en interne
4558
		$champ_titre = "";
4559
		if ($demande_titre) {
4560
			// si pas de titre declare mais champ titre, il sera peuple par le select *
4561
			$champ_titre = (!empty($desc['titre'])) ? ', ' . $desc['titre'] : '';
4562
		}
4563
		include_spip('base/abstract_sql');
4564
		include_spip('base/connect_sql');
4565
		$objets[$type_objet][$id_objet] = sql_fetsel(
4566
			'*' . $champ_titre,
4567
			$desc['table_sql'],
4568
			id_table_objet($type_objet) . ' = ' . intval($id_objet)
4569
		);
4570
	}
4571
4572
	// Si la fonction generer_TRUC_TYPE existe, on l'utilise pour formater $info_generee
4573
	if ($generer = charger_fonction("generer_${info}_${type_objet}", '', true)) {
4574
		$info_generee = $generer($id_objet, $objets[$type_objet][$id_objet]);
4575
	} // Si la fonction generer_TRUC_entite existe, on l'utilise pour formater $info_generee
4576
	else {
4577
		if ($generer = charger_fonction("generer_${info}_entite", '', true)) {
4578
			$info_generee = $generer($id_objet, $type_objet, $objets[$type_objet][$id_objet]);
4579
		} // Sinon on prend directement le champ SQL tel quel
4580
		else {
4581
			$info_generee = (isset($objets[$type_objet][$id_objet][$info]) ? $objets[$type_objet][$id_objet][$info] : '');
4582
		}
4583
	}
4584
4585
	// On va ensuite appliquer les traitements automatiques si besoin
4586
	if (!$etoile) {
4587
		// FIXME: on fournit un ENV minimum avec id et type et connect=''
4588
		// mais ce fonctionnement est a ameliorer !
4589
		$info_generee = appliquer_traitement_champ($info_generee, $info, table_objet($type_objet),
4590
			array('id_objet' => $id_objet, 'objet' => $type_objet, ''));
4591
	}
4592
4593
	return $info_generee;
4594
}
4595
4596
/**
4597
 * Appliquer a un champ SQL le traitement qui est configure pour la balise homonyme dans les squelettes
4598
 *
4599
 * @param string $texte
4600
 * @param string $champ
4601
 * @param string $table_objet
4602
 * @param array $env
4603
 * @param string $connect
4604
 * @return string
4605
 */
4606
function appliquer_traitement_champ($texte, $champ, $table_objet = '', $env = array(), $connect = '') {
0 ignored issues
show
Unused Code introduced by
The parameter $connect 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...
4607
	if (!$champ) {
4608
		return $texte;
4609
	}
4610
	
4611
	// On charge toujours les filtres de texte car la majorité des traitements les utilisent
4612
	// et il ne faut pas partir du principe que c'est déjà chargé (form ajax, etc)
4613
	include_spip('inc/texte');
4614
	
4615
	$champ = strtoupper($champ);
4616
	$traitements = isset($GLOBALS['table_des_traitements'][$champ]) ? $GLOBALS['table_des_traitements'][$champ] : false;
4617
	if (!$traitements or !is_array($traitements)) {
4618
		return $texte;
4619
	}
4620
4621
	$traitement = '';
4622
	if ($table_objet and (!isset($traitements[0]) or count($traitements) > 1)) {
4623
		// necessaire pour prendre en charge les vieux appels avec un table_objet_sql en 3e arg
4624
		$table_objet = table_objet($table_objet);
4625
		if (isset($traitements[$table_objet])) {
4626
			$traitement = $traitements[$table_objet];
4627
		}
4628
	}
4629
	if (!$traitement and isset($traitements[0])) {
4630
		$traitement = $traitements[0];
4631
	}
4632
	// (sinon prendre le premier de la liste par defaut ?)
4633
4634
	if (!$traitement) {
4635
		return $texte;
4636
	}
4637
4638
	$traitement = str_replace('%s', "'" . texte_script($texte) . "'", $traitement);
4639
4640
	// Fournir $connect et $Pile[0] au traitement si besoin
4641
	$Pile = array(0 => $env);
0 ignored issues
show
Unused Code introduced by
$Pile is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
4642
	eval("\$texte = $traitement;");
4643
4644
	return $texte;
4645
}
4646
4647
4648
/**
4649
 * Generer un lien (titre clicable vers url) vers un objet
4650
 *
4651
 * @param int $id_objet
4652
 * @param $objet
4653
 * @param int $longueur
4654
 * @param null|string $connect
4655
 * @return string
4656
 */
4657
function generer_lien_entite($id_objet, $objet, $longueur = 80, $connect = null) {
4658
	include_spip('inc/liens');
4659
	$titre = traiter_raccourci_titre($id_objet, $objet, $connect);
4660
	// lorsque l'objet n'est plus declare (plugin desactive par exemple)
4661
	// le raccourcis n'est plus valide
4662
	$titre = isset($titre['titre']) ? typo($titre['titre']) : '';
4663
	// on essaye avec generer_info_entite ?
4664
	if (!strlen($titre) and !$connect) {
4665
		$titre = generer_info_entite($id_objet, $objet, 'titre');
4666
	}
4667
	if (!strlen($titre)) {
4668
		$titre = _T('info_sans_titre');
4669
	}
4670
	$url = generer_url_entite($id_objet, $objet, '', '', $connect);
4671
4672
	return "<a href='$url' class='$objet'>" . couper($titre, $longueur) . "</a>";
4673
}
4674
4675
4676
/**
4677
 * Englobe (Wrap) un texte avec des balises
4678
 *
4679
 * @example `wrap('mot','<b>')` donne `<b>mot</b>'`
4680
 *
4681
 * @filtre
4682
 * @uses extraire_balises()
4683
 *
4684
 * @param string $texte
4685
 * @param string $wrap
4686
 * @return string
4687
 */
4688
function wrap($texte, $wrap) {
4689
	$balises = extraire_balises($wrap);
0 ignored issues
show
Unused Code introduced by
$balises is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
4690
	if (preg_match_all(",<([a-z]\w*)\b[^>]*>,UimsS", $wrap, $regs, PREG_PATTERN_ORDER)) {
4691
		$texte = $wrap . $texte;
4692
		$regs = array_reverse($regs[1]);
4693
		$wrap = "</" . implode("></", $regs) . ">";
4694
		$texte = $texte . $wrap;
4695
	}
4696
4697
	return $texte;
4698
}
4699
4700
4701
/**
4702
 * afficher proprement n'importe quoi
4703
 * On affiche in fine un pseudo-yaml qui premet de lire humainement les tableaux et de s'y reperer
4704
 *
4705
 * Les textes sont retournes avec simplement mise en forme typo
4706
 *
4707
 * le $join sert a separer les items d'un tableau, c'est en general un \n ou <br /> selon si on fait du html ou du texte
4708
 * les tableaux-listes (qui n'ont que des cles numeriques), sont affiches sous forme de liste separee par des virgules :
4709
 * c'est VOULU !
4710
 *
4711
 * @param $u
4712
 * @param string $join
4713
 * @param int $indent
4714
 * @return array|mixed|string
4715
 */
4716
function filtre_print_dist($u, $join = "<br />", $indent = 0) {
4717
	if (is_string($u)) {
4718
		$u = typo($u);
4719
4720
		return $u;
4721
	}
4722
4723
	// caster $u en array si besoin
4724
	if (is_object($u)) {
4725
		$u = (array)$u;
4726
	}
4727
4728
	if (is_array($u)) {
4729
		$out = "";
4730
		// toutes les cles sont numeriques ?
4731
		// et aucun enfant n'est un tableau
4732
		// liste simple separee par des virgules
4733
		$numeric_keys = array_map('is_numeric', array_keys($u));
4734
		$array_values = array_map('is_array', $u);
4735
		$object_values = array_map('is_object', $u);
4736
		if (array_sum($numeric_keys) == count($numeric_keys)
4737
			and !array_sum($array_values)
4738
			and !array_sum($object_values)
4739
		) {
4740
			return join(", ", array_map('filtre_print_dist', $u));
4741
		}
4742
4743
		// sinon on passe a la ligne et on indente
4744
		$i_str = str_pad("", $indent, " ");
4745
		foreach ($u as $k => $v) {
4746
			$out .= $join . $i_str . "$k: " . filtre_print_dist($v, $join, $indent + 2);
4747
		}
4748
4749
		return $out;
4750
	}
4751
4752
	// on sait pas quoi faire...
4753
	return $u;
4754
}
4755
4756
4757
/**
4758
 * Renvoyer l'info d'un objet
4759
 * telles que definies dans declarer_tables_objets_sql
4760
 *
4761
 * @param string $objet
4762
 * @param string $info
4763
 * @return string|array
4764
 */
4765
function objet_info($objet, $info) {
4766
	$table = table_objet_sql($objet);
4767
	$infos = lister_tables_objets_sql($table);
4768
4769
	return (isset($infos[$info]) ? $infos[$info] : '');
4770
}
4771
4772
/**
4773
 * Filtre pour afficher 'Aucun truc' ou '1 truc' ou 'N trucs'
4774
 * avec la bonne chaîne de langue en fonction de l'objet utilisé
4775
 *
4776
 * @param int $nb
4777
 *     Nombre d'éléments
4778
 * @param string $objet
4779
 *     Objet
4780
 * @return mixed|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string.

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...
4781
 *     Texte traduit du comptage, tel que '3 articles'
4782
 */
4783
function objet_afficher_nb($nb, $objet) {
4784
	if (!$nb) {
4785
		return _T(objet_info($objet, 'info_aucun_objet'));
0 ignored issues
show
Bug introduced by
It seems like objet_info($objet, 'info_aucun_objet') targeting objet_info() can also be of type array; however, _T() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
4786
	} else {
4787
		return _T(objet_info($objet, $nb == 1 ? 'info_1_objet' : 'info_nb_objets'), array('nb' => $nb));
0 ignored issues
show
Bug introduced by
It seems like objet_info($objet, $nb =...et' : 'info_nb_objets') targeting objet_info() can also be of type array; however, _T() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
4788
	}
4789
}
4790
4791
/**
4792
 * Filtre pour afficher l'img icone d'un objet
4793
 *
4794
 * @param string $objet
4795
 * @param int $taille
4796
 * @param string $class
4797
 * @return string
4798
 */
4799
function objet_icone($objet, $taille = 24, $class='') {
4800
	$icone = objet_info($objet, 'icone_objet') . "-" . $taille . ".png";
4801
	$icone = chemin_image($icone);
4802
	$balise_img = charger_filtre('balise_img');
4803
4804
	return $icone ? $balise_img($icone, _T(objet_info($objet, 'texte_objet')), $class, $taille) : '';
0 ignored issues
show
Bug introduced by
It seems like objet_info($objet, 'texte_objet') targeting objet_info() can also be of type array; however, _T() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
4805
}
4806
4807
/**
4808
 * Renvoyer une traduction d'une chaine de langue contextuelle à un objet si elle existe,
4809
 * la traduction de la chaine generique
4810
 *
4811
 * Ex : [(#ENV{objet}|objet_label{trad_reference})]
4812
 * va chercher si une chaine objet:trad_reference existe et renvoyer sa trad le cas echeant
4813
 * sinon renvoie la trad de la chaine trad_reference
4814
 * Si la chaine fournie contient un prefixe il est remplacé par celui de l'objet pour chercher la chaine contextuelle
4815
 *
4816
 * Les arguments $args et $options sont ceux de la fonction _T
4817
 *
4818
 * @param string $objet
4819
 * @param string $chaine
4820
 * @param array $args
4821
 * @param array $options
4822
 * @return string
4823
 */
4824
function objet_T($objet, $chaine, $args = array(), $options = array()){
4825
	$chaine = explode(':',$chaine);
4826
	if ($t = _T($objet . ':' . end($chaine), $args, array_merge($options, array('force'=>false)))) {
4827
		return $t;
4828
	}
4829
	$chaine = implode(':',$chaine);
4830
	return _T($chaine, $args, $options);
4831
}
4832
4833
/**
4834
 * Fonction de secours pour inserer le head_css de facon conditionnelle
4835
 *
4836
 * Appelée en filtre sur le squelette qui contient #INSERT_HEAD,
4837
 * elle vérifie l'absence éventuelle de #INSERT_HEAD_CSS et y suplée si besoin
4838
 * pour assurer la compat avec les squelettes qui n'utilisent pas.
4839
 *
4840
 * @param string $flux Code HTML
4841
 * @return string      Code HTML
4842
 */
4843
function insert_head_css_conditionnel($flux) {
4844
	if (strpos($flux, '<!-- insert_head_css -->') === false
4845
		and $p = strpos($flux, '<!-- insert_head -->')
4846
	) {
4847
		// plutot avant le premier js externe (jquery) pour etre non bloquant
4848
		if ($p1 = stripos($flux, '<script src=') and $p1 < $p) {
4849
			$p = $p1;
4850
		}
4851
		$flux = substr_replace($flux, pipeline('insert_head_css', '<!-- insert_head_css -->'), $p, 0);
4852
	}
4853
4854
	return $flux;
4855
}
4856
4857
/**
4858
 * Produire un fichier statique à partir d'un squelette dynamique
4859
 *
4860
 * Permet ensuite à Apache de le servir en statique sans repasser
4861
 * par spip.php à chaque hit sur le fichier.
4862
 *
4863
 * Si le format (css ou js) est passe dans `contexte['format']`, on l'utilise
4864
 * sinon on regarde si le fond finit par .css ou .js, sinon on utilie "html"
4865
 *
4866
 * @uses urls_absolues_css()
4867
 *
4868
 * @param string $fond
4869
 * @param array $contexte
4870
 * @param array $options
4871
 * @param string $connect
4872
 * @return string
4873
 */
4874
function produire_fond_statique($fond, $contexte = array(), $options = array(), $connect = '') {
4875
	if (isset($contexte['format'])) {
4876
		$extension = $contexte['format'];
4877
		unset($contexte['format']);
4878
	} else {
4879
		$extension = "html";
4880
		if (preg_match(',[.](css|js|json)$,', $fond, $m)) {
4881
			$extension = $m[1];
4882
		}
4883
	}
4884
	// recuperer le contenu produit par le squelette
4885
	$options['raw'] = true;
4886
	$cache = recuperer_fond($fond, $contexte, $options, $connect);
4887
4888
	// calculer le nom de la css
4889
	$dir_var = sous_repertoire(_DIR_VAR, 'cache-' . $extension);
4890
	$nom_safe = preg_replace(",\W,", '_', str_replace('.', '_', $fond));
4891
	$contexte_implicite = calculer_contexte_implicite();
4892
4893
	// par defaut on hash selon les contextes qui sont a priori moins variables
4894
	// mais on peut hasher selon le contenu a la demande, si plusieurs contextes produisent un meme contenu
4895
	// reduit la variabilite du nom et donc le nombre de css concatenees possibles in fine
4896
	if (isset($options['hash_on_content']) and $options['hash_on_content']) {
4897
		$hash = md5($contexte_implicite['host'] . '::'. $cache);
4898
	}
4899
	else {
4900
		unset($contexte_implicite['notes']); // pas pertinent pour signaler un changeemnt de contenu pour des css/js
4901
		ksort($contexte);
4902
		$hash = md5($fond . json_encode($contexte_implicite) . json_encode($contexte) . $connect);
4903
	}
4904
	$filename = $dir_var . $extension . "dyn-$nom_safe-" . substr($hash, 0, 8) . ".$extension";
4905
4906
	// mettre a jour le fichier si il n'existe pas
4907
	// ou trop ancien
4908
	// le dernier fichier produit est toujours suffixe par .last
4909
	// et recopie sur le fichier cible uniquement si il change
4910
	if (!file_exists($filename)
4911
		or !file_exists($filename . ".last")
4912
		or (isset($cache['lastmodified']) and $cache['lastmodified'] and filemtime($filename . ".last") < $cache['lastmodified'])
4913
		or (defined('_VAR_MODE') and _VAR_MODE == 'recalcul')
4914
	) {
4915
		$contenu = $cache['texte'];
4916
		// passer les urls en absolu si c'est une css
4917
		if ($extension == "css") {
4918
			$contenu = urls_absolues_css($contenu,
4919
				test_espace_prive() ? generer_url_ecrire('accueil') : generer_url_public($fond));
4920
		}
4921
4922
		$comment = '';
4923
		// ne pas insérer de commentaire si c'est du json
4924
		if ($extension != "json") {
4925
			$comment = "/* #PRODUIRE{fond=$fond";
4926
			foreach ($contexte as $k => $v) {
4927
				$comment .= ",$k=$v";
4928
			}
4929
			// pas de date dans le commentaire car sinon ca invalide le md5 et force la maj
4930
			// mais on peut mettre un md5 du contenu, ce qui donne un aperu rapide si la feuille a change ou non
4931
			$comment .= "}\n   md5:" . md5($contenu) . " */\n";
4932
		}
4933
		// et ecrire le fichier si il change
4934
		ecrire_fichier_calcule_si_modifie($filename, $comment . $contenu, false, true);
4935
	}
4936
4937
	return timestamp($filename);
4938
}
4939
4940
/**
4941
 * Ajouter un timestamp a une url de fichier
4942
 * [(#CHEMIN{monfichier}|timestamp)]
4943
 *
4944
 * @param string $fichier
4945
 *    Le chemin du fichier sur lequel on souhaite ajouter le timestamp
4946
 * @return string
4947
 *    $fichier auquel on a ajouté le timestamp
4948
 */
4949
function timestamp($fichier) {
4950
	if (!$fichier
4951
		or !file_exists($fichier)
4952
		or !$m = filemtime($fichier)
4953
	) {
4954
		return $fichier;
4955
	}
4956
4957
	return "$fichier?$m";
4958
}
4959
4960
/**
4961
 * Supprimer le timestamp d'une url
4962
 *
4963
 * @param string $url
4964
 * @return string
4965
 */
4966
function supprimer_timestamp($url) {
4967
	if (strpos($url, "?") === false) {
4968
		return $url;
4969
	}
4970
4971
	return preg_replace(",\?[[:digit:]]+$,", "", $url);
4972
}
4973
4974
/**
4975
 * Nettoyer le titre d'un email
4976
 *
4977
 * Éviter une erreur lorsqu'on utilise `|nettoyer_titre_email` dans un squelette de mail
4978
 *
4979
 * @filtre
4980
 * @uses nettoyer_titre_email()
4981
 *
4982
 * @param string $titre
4983
 * @return string
4984
 */
4985
function filtre_nettoyer_titre_email_dist($titre) {
4986
	include_spip('inc/envoyer_mail');
4987
4988
	return nettoyer_titre_email($titre);
4989
}
4990
4991
/**
4992
 * Afficher le sélecteur de rubrique
4993
 *
4994
 * Il permet de placer un objet dans la hiérarchie des rubriques de SPIP
4995
 *
4996
 * @uses chercher_rubrique()
4997
 *
4998
 * @param string $titre
4999
 * @param int $id_objet
5000
 * @param int $id_parent
5001
 * @param string $objet
5002
 * @param int $id_secteur
5003
 * @param bool $restreint
5004
 * @param bool $actionable
5005
 *   true : fournit le selecteur dans un form directement postable
5006
 * @param bool $retour_sans_cadre
5007
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|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...
5008
 */
5009
function filtre_chercher_rubrique_dist(
5010
	$titre,
5011
	$id_objet,
5012
	$id_parent,
5013
	$objet,
5014
	$id_secteur,
5015
	$restreint,
5016
	$actionable = false,
5017
	$retour_sans_cadre = false
5018
) {
5019
	include_spip('inc/filtres_ecrire');
5020
5021
	return chercher_rubrique($titre, $id_objet, $id_parent, $objet, $id_secteur, $restreint, $actionable,
5022
		$retour_sans_cadre);
5023
}
5024
5025
/**
5026
 * Rediriger une page suivant une autorisation,
5027
 * et ce, n'importe où dans un squelette, même dans les inclusions.
5028
 *
5029
 * En l'absence de redirection indiquée, la fonction redirige par défaut
5030
 * sur une 403 dans l'espace privé et 404 dans l'espace public.
5031
 *
5032
 * @example
5033
 *     ```
5034
 *     [(#AUTORISER{non}|sinon_interdire_acces)]
5035
 *     [(#AUTORISER{non}|sinon_interdire_acces{#URL_PAGE{login}, 401})]
5036
 *     ```
5037
 *
5038
 * @filtre
5039
 * @param bool $ok
5040
 *     Indique si l'on doit rediriger ou pas
5041
 * @param string $url
5042
 *     Adresse eventuelle vers laquelle rediriger
5043
 * @param int $statut
5044
 *     Statut HTML avec lequel on redirigera
5045
 * @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...
5046
 *     message d'erreur
5047
 * @return string|void
5048
 *     Chaîne vide si l'accès est autorisé
5049
 */
5050
function sinon_interdire_acces($ok = false, $url = '', $statut = 0, $message = null) {
5051
	if ($ok) {
5052
		return '';
5053
	}
5054
5055
	// Vider tous les tampons
5056
	$level = @ob_get_level();
5057
	while ($level--) {
5058
		@ob_end_clean();
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...
5059
	}
5060
5061
	include_spip('inc/headers');
5062
5063
	// S'il y a une URL, on redirige (si pas de statut, la fonction mettra 302 par défaut)
5064
	if ($url) {
5065
		redirige_par_entete($url, '', $statut);
5066
	}
5067
5068
	// ecriture simplifiee avec message en 3eme argument (= statut 403)
5069
	if (!is_numeric($statut) and is_null($message)) {
5070
		$message = $statut;
5071
		$statut = 0;
5072
	}
5073
	if (!$message) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $message of type string|null is loosely compared to false; 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...
5074
		$message = '';
5075
	}
5076
	$statut = intval($statut);
5077
5078
	// Si on est dans l'espace privé, on génère du 403 Forbidden par defaut ou du 404
5079
	if (test_espace_prive()) {
5080
		if (!$statut or !in_array($statut, array(404, 403))) {
5081
			$statut = 403;
0 ignored issues
show
Unused Code introduced by
$statut is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
5082
		}
5083
		http_status(403);
5084
		$echec = charger_fonction('403', 'exec');
5085
		$echec($message);
5086
	} else {
5087
		// Sinon dans l'espace public on redirige vers une 404 par défaut, car elle toujours présente normalement
5088
		if (!$statut) {
5089
			$statut = 404;
5090
		}
5091
		// Dans tous les cas on modifie l'entité avec ce qui est demandé
5092
		http_status($statut);
5093
		// Si le statut est une erreur et qu'il n'y a pas de redirection on va chercher le squelette du même nom
5094
		if ($statut >= 400) {
5095
			echo recuperer_fond("$statut", array('erreur' => $message));
5096
		}
5097
	}
5098
5099
5100
	exit;
5101
}
5102
5103
/**
5104
 * Assurer le fonctionnement de |compacte meme sans l'extension compresseur
5105
 *
5106
 * @param string $source
5107
 * @param null|string $format
5108
 * @return string
5109
 */
5110
function filtre_compacte_dist($source, $format = null) {
5111
	if (function_exists('compacte')) {
5112
		return compacte($source, $format);
5113
	}
5114
5115
	return $source;
5116
}
5117
5118
5119
/**
5120
 * Afficher partiellement un mot de passe que l'on ne veut pas rendre lisible par un champ hidden
5121
 * @param string $passe
5122
 * @param bool $afficher_partiellement
5123
 * @param int|null $portion_pourcent
5124
 * @return string
5125
 */
5126
function spip_affiche_mot_de_passe_masque($passe, $afficher_partiellement = false, $portion_pourcent = null) {
5127
	$l = strlen($passe);
5128
5129
	if ($l<=8 or !$afficher_partiellement){
5130
		if (!$l) {
5131
			return ''; // montrer qu'il y a pas de mot de passe si il y en a pas
5132
		}
5133
		return str_pad('',$afficher_partiellement ? $l : 16,'*');
5134
	}
5135
5136
	if (is_null($portion_pourcent)) {
5137
		if (!defined('_SPIP_AFFICHE_MOT_DE_PASSE_MASQUE_PERCENT')) {
5138
			define('_SPIP_AFFICHE_MOT_DE_PASSE_MASQUE_PERCENT', 20); // 20%
5139
		}
5140
		$portion_pourcent = _SPIP_AFFICHE_MOT_DE_PASSE_MASQUE_PERCENT;
5141
	}
5142
	if ($portion_pourcent >= 100) {
5143
		return $passe;
5144
	}
5145
	$e = intval(ceil($l * $portion_pourcent / 100 / 2));
5146
	$e = max($e, 0);
5147
	$mid = str_pad('',$l-2*$e,'*');
5148
	if ($e>0 and strlen($mid)>8){
5149
		$mid = '***...***';
5150
	}
5151
	return substr($passe,0,$e) . $mid . ($e > 0 ? substr($passe,-$e) : '');
5152
}
5153
5154
5155
/**
5156
 * Cette fonction permet de transformer un texte clair en nom court pouvant servir d'identifiant, class, id, url...
5157
 * en ne conservant que des caracteres alphanumeriques et un separateur
5158
 *
5159
 * @param string $texte
5160
 *   Texte à transformer en nom machine
5161
 * @param string $type
5162
 *
5163
 * @param array $options
5164
 *   string separateur : par défaut, un underscore `_`.
5165
 *   int longueur_maxi : par defaut 60
5166
 *   int longueur_mini : par defaut 0
5167
 *
5168
 * @return string
5169
 */
5170
function identifiant_slug($texte, $type = '', $options = array()) {
5171
5172
	$original = $texte;
5173
	$separateur = (isset($options['separateur'])?$options['separateur']:'_');
5174
	$longueur_maxi = (isset($options['longueur_maxi'])?$options['longueur_maxi']:60);
5175
	$longueur_mini = (isset($options['longueur_mini'])?$options['longueur_mini']:0);
5176
5177
	if (!function_exists('translitteration')) {
5178
		include_spip('inc/charsets');
5179
	}
5180
5181
	// pas de balise html
5182
	if (strpos($texte, '<') !== false) {
5183
		$texte = strip_tags($texte);
5184
	}
5185
	if (strpos($texte, '&') !== false) {
5186
		$texte = unicode2charset($texte);
5187
	}
5188
	// On enlève les espaces indésirables
5189
	$texte = trim($texte);
5190
5191
	// On enlève les accents et cie
5192
	$texte = translitteration($texte);
5193
5194
	// On remplace tout ce qui n'est pas un mot par un séparateur
5195
	$texte = preg_replace(',[\W_]+,ms', $separateur, $texte);
5196
5197
	// nettoyer les doubles occurences du separateur si besoin
5198
	while (strpos($texte, "$separateur$separateur") !== false) {
5199
		$texte = str_replace("$separateur$separateur", $separateur, $texte);
5200
	}
5201
5202
	// pas de separateur au debut ni a la fin
5203
	$texte = trim($texte, $separateur);
5204
5205
	// en minuscules
5206
	$texte = strtolower($texte);
5207
5208
	switch ($type) {
5209
		case 'class':
5210
		case 'id':
5211
		case 'anchor':
5212
			if (preg_match(',^\d,', $texte)) {
5213
				$texte = substr($type, 0, 1).$texte;
5214
			}
5215
	}
5216
5217
	if (strlen($texte)>$longueur_maxi) {
5218
		$texte = substr($texte, 0, $longueur_maxi);
5219
	}
5220
5221
	if (strlen($texte) < $longueur_mini and $longueur_mini < $longueur_maxi) {
5222
		if (preg_match(',^\d,', $texte)) {
5223
			$texte = ($type ? substr($type,0,1) : "s") . $texte;
5224
		}
5225
		$texte .= $separateur . md5($original);
5226
		$texte = substr($texte, 0, $longueur_mini);
5227
	}
5228
5229
	return $texte;
5230
}
5231