Completed
Push — master ( b32f18...cfbd44 )
by cam
04:53
created

filtres.php ➔ appliquer_si_filtre()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
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/fonctions'); // 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_fichiers_fonctions(); // 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 s'il existe, sinon retourne une chaîne vide
114
 *
115
 * Fonction générique qui prend en argument l’objet (texte, etc) à modifier
116
 * et le nom du filtre.
117
 *
118
 * - À la différence de la fonction `filtrer()`, celle-ci ne lève
119
 *   pas d'erreur de squelettes si le filtre n'est pas trouvé.
120
 * - À la différence de la fonction `appliquer_si_filtre()` le contenu
121
 *   d'origine n'est pas retourné si le filtre est absent.
122
 *
123
 * Les arguments supplémentaires transmis à cette fonction sont utilisés
124
 * comme arguments pour le filtre appelé.
125
 *
126
 * @example
127
 *      ```
128
 *      [(#BALISE|appliquer_filtre{nom_du_filtre})]
129
 *      [(#BALISE|appliquer_filtre{nom_du_filtre, arg1, arg2, ...})]
130
 *
131
 *      // Applique le filtre minifier si on le trouve :
132
 *      // - Ne retourne rien si le filtre 'minifier' n'est pas trouvé
133
 *      [(#INCLURE{fichier.js}|appliquer_filtre{minifier, js})]
134
 *
135
 *      // - Retourne le contenu du fichier.js si le filtre n'est pas trouvé.
136
 *      [(#INCLURE{fichier.js}|appliquer_si_filtre{minifier, js})]
137
 *      ```
138
 *
139
 * @filtre
140
 * @see filtrer() Génère une erreur si le filtre est absent
141
 * @see appliquer_si_filtre() Proche : retourne le texte d'origine si le filtre est absent
142
 * @uses appliquer_filtre_sinon()
143
 *
144
 * @param mixed $arg
145
 *     Texte (le plus souvent) sur lequel appliquer le filtre
146
 * @param string $filtre
147
 *     Nom du filtre à appliquer
148
 * @return string
149
 *     Texte traité par le filtre si le filtre existe,
150
 *     Chaîne vide sinon.
151
 **/
152
function appliquer_filtre($arg, $filtre) {
153
	$args = func_get_args();
154
	return appliquer_filtre_sinon($arg, $filtre, $args, '');
155
}
156
157
/**
158
 * Applique un filtre s'il existe, sinon retourne le contenu d'origine sans modification
159
 *
160
 * Se référer à `appliquer_filtre()` pour les détails.
161
 *
162
 * @example
163
 *      ```
164
 *      [(#INCLURE{fichier.js}|appliquer_si_filtre{minifier, js})]
165
 *      ```
166
 * @filtre
167
 * @see appliquer_filtre() Proche : retourne vide si le filtre est absent
168
 * @uses appliquer_filtre_sinon()
169
 *
170
 * @param mixed $arg
171
 *     Texte (le plus souvent) sur lequel appliquer le filtre
172
 * @param string $filtre
173
 *     Nom du filtre à appliquer
174
 * @return string
175
 *     Texte traité par le filtre si le filtre existe,
176
 *     Texte d'origine sinon
177
 **/
178
function appliquer_si_filtre($arg, $filtre) {
179
	$args = func_get_args();
180
	return appliquer_filtre_sinon($arg, $filtre, $args, $arg);
181
}
182
183
/**
184
 * Retourne la version de SPIP
185
 *
186
 * Si l'on retrouve un numéro de révision GIT ou SVN, il est ajouté entre crochets.
187
 * Si effectivement le SPIP est installé par Git ou Svn, 'GIT' ou 'SVN' est ajouté avant sa révision.
188
 *
189
 * @global spip_version_affichee Contient la version de SPIP
190
 * @uses version_vcs_courante() Pour trouver le numéro de révision
191
 *
192
 * @return string
193
 *     Version de SPIP
194
 **/
195
function spip_version() {
196
	$version = $GLOBALS['spip_version_affichee'];
197
	if ($vcs_version = version_vcs_courante(_DIR_RACINE)) {
198
		$version .= " $vcs_version";
199
	}
200
201
	return $version;
202
}
203
204
/**
205
 * Retourne une courte description d’une révision VCS d’un répertoire
206
 *
207
 * @param string $dir Le répertoire à tester
208
 * @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...
209
 * @retun string|array|null
210
 *    - array|null si $raw = true,
211
 *    - string|null si $raw = false
212
 */
213
function version_vcs_courante($dir, $raw = false) {
214
	$desc = decrire_version_git($dir);
215
	if ($desc === null) {
216
		$desc = decrire_version_svn($dir);
217
	}
218
	if ($desc === null or $raw) {
219
		return $desc;
220
	}
221
	// affichage "GIT [master: abcdef]"
222
	$commit = isset($desc['commit_short']) ? $desc['commit_short'] : $desc['commit'];
223
	if ($desc['branch']) {
224
		$commit = $desc['branch'] . ': ' . $commit;
225
	}
226
	return "{$desc['vcs']} [$commit]";
227
}
228
229
/**
230
 * Retrouve un numéro de révision Git d'un répertoire
231
 *
232
 * @param string $dir Chemin du répertoire
233
 * @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...
234
 *      null si aucune info trouvée
235
 *      array ['branch' => xx, 'commit' => yy] sinon.
236
 **/
237
function decrire_version_git($dir) {
238
	if (!$dir) {
239
		$dir = '.';
240
	}
241
242
	// version installee par GIT
243
	if (lire_fichier($dir . '/.git/HEAD', $c)) {
244
		$currentHead = trim(substr($c, 4));
245
		if (lire_fichier($dir . '/.git/' . $currentHead, $hash)) {
246
			return [
247
				'vcs' => 'GIT',
248
				'branch' => basename($currentHead),
249
				'commit' => trim($hash),
250
				'commit_short' => substr(trim($hash), 0, 8),
251
			];
252
		}
253
	}
254
255
	return null;
256
}
257
258
259
/**
260
 * Retrouve un numéro de révision Svn d'un répertoire
261
 *
262
 * @param string $dir Chemin du répertoire
263
 * @return array|null
264
 *      null si aucune info trouvée
265
 *      array ['commit' => yy, 'date' => xx, 'author' => xx] sinon.
266
 **/
267
function decrire_version_svn($dir) {
268
	if (!$dir) {
269
		$dir = '.';
270
	}
271
	// version installee par SVN
272
	if (file_exists($dir . '/.svn/wc.db') && class_exists('SQLite3')) {
273
		$db = new SQLite3($dir . '/.svn/wc.db');
274
		$result = $db->query('SELECT changed_revision FROM nodes WHERE local_relpath = "" LIMIT 1');
275
		if ($result) {
276
			$row = $result->fetchArray();
277
			if ($row['changed_revision'] != "") {
278
				return [
279
					'vcs' => 'SVN',
280
					'branch' => '',
281
					'commit' => $row['changed_revision'],
282
				];
283
			}
284
		}
285
	}
286
	return null;
287
}
288
289
/**
290
 * Retrouve un numéro de révision SVN d'un répertoire
291
 *
292
 * Mention de la révision SVN courante d'un répertoire
293
 * /!\ Retourne un nombre négatif si on est sur .svn
294
 *
295
 * @deprecated Utiliser version_vcs_courante()
296
 * @param string $dir Chemin du répertoire
297
 * @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...
298
 *
299
 *     - 0 si aucune info trouvée
300
 *     - -NN (entier) si info trouvée par .svn/wc.db
301
 *
302
 **/
303
function version_svn_courante($dir) {
304
	if ($desc = decrire_version_svn($dir)) {
305
		return -$desc['commit'];
306
	}
307
	return 0;
308
}
309
310
// La matrice est necessaire pour ne filtrer _que_ des fonctions definies dans filtres_images
311
// et laisser passer les fonctions personnelles baptisees image_...
312
$GLOBALS['spip_matrice']['image_graver'] = true;//'inc/filtres_images_mini.php';
313
$GLOBALS['spip_matrice']['image_select'] = true;//'inc/filtres_images_mini.php';
314
$GLOBALS['spip_matrice']['image_reduire'] = true;//'inc/filtres_images_mini.php';
315
$GLOBALS['spip_matrice']['image_reduire_par'] = true;//'inc/filtres_images_mini.php';
316
$GLOBALS['spip_matrice']['image_passe_partout'] = true;//'inc/filtres_images_mini.php';
317
318
$GLOBALS['spip_matrice']['couleur_html_to_hex'] = 'inc/filtres_images_mini.php';
319
$GLOBALS['spip_matrice']['couleur_foncer'] = 'inc/filtres_images_mini.php';
320
$GLOBALS['spip_matrice']['couleur_eclaircir'] = 'inc/filtres_images_mini.php';
321
322
// ou pour inclure un script au moment ou l'on cherche le filtre
323
$GLOBALS['spip_matrice']['filtre_image_dist'] = 'inc/filtres_mime.php';
324
$GLOBALS['spip_matrice']['filtre_audio_dist'] = 'inc/filtres_mime.php';
325
$GLOBALS['spip_matrice']['filtre_video_dist'] = 'inc/filtres_mime.php';
326
$GLOBALS['spip_matrice']['filtre_application_dist'] = 'inc/filtres_mime.php';
327
$GLOBALS['spip_matrice']['filtre_message_dist'] = 'inc/filtres_mime.php';
328
$GLOBALS['spip_matrice']['filtre_multipart_dist'] = 'inc/filtres_mime.php';
329
$GLOBALS['spip_matrice']['filtre_text_dist'] = 'inc/filtres_mime.php';
330
$GLOBALS['spip_matrice']['filtre_text_csv_dist'] = 'inc/filtres_mime.php';
331
$GLOBALS['spip_matrice']['filtre_text_html_dist'] = 'inc/filtres_mime.php';
332
$GLOBALS['spip_matrice']['filtre_audio_x_pn_realaudio'] = 'inc/filtres_mime.php';
333
334
335
/**
336
 * Charge et exécute un filtre (graphique ou non)
337
 *
338
 * Recherche la fonction prévue pour un filtre (qui peut être un filtre graphique `image_*`)
339
 * et l'exécute avec les arguments transmis à la fonction, obtenus avec `func_get_args()`
340
 *
341
 * @api
342
 * @uses image_filtrer() Pour un filtre image
343
 * @uses chercher_filtre() Pour un autre filtre
344
 *
345
 * @param string $filtre
346
 *     Nom du filtre à appliquer
347
 * @return string
348
 *     Code HTML retourné par le filtre
349
 **/
350
function filtrer($filtre) {
351
	$tous = func_get_args();
352
	if (trouver_filtre_matrice($filtre) and substr($filtre, 0, 6) == 'image_') {
353
		return image_filtrer($tous);
354
	} elseif ($f = chercher_filtre($filtre)) {
355
		array_shift($tous);
356
		return call_user_func_array($f, $tous);
357
	} else {
358
		// le filtre n'existe pas, on provoque une erreur
359
		$msg = array('zbug_erreur_filtre', array('filtre' => texte_script($filtre)));
360
		erreur_squelette($msg);
361
		return '';
362
	}
363
}
364
365
/**
366
 * Cherche un filtre spécial indiqué dans la globale `spip_matrice`
367
 * et charge le fichier éventuellement associé contenant le filtre.
368
 *
369
 * Les filtres d'images par exemple sont déclarés de la sorte, tel que :
370
 * ```
371
 * $GLOBALS['spip_matrice']['image_reduire'] = true;
372
 * $GLOBALS['spip_matrice']['image_monochrome'] = 'filtres/images_complements.php';
373
 * ```
374
 *
375
 * @param string $filtre
376
 * @return bool true si on trouve le filtre dans la matrice, false sinon.
377
 */
378
function trouver_filtre_matrice($filtre) {
379
	if (isset($GLOBALS['spip_matrice'][$filtre]) and is_string($f = $GLOBALS['spip_matrice'][$filtre])) {
380
		find_in_path($f, '', true);
381
		$GLOBALS['spip_matrice'][$filtre] = true;
382
	}
383
	return !empty($GLOBALS['spip_matrice'][$filtre]);
384
}
385
386
387
/**
388
 * Filtre `set` qui sauve la valeur en entrée dans une variable
389
 *
390
 * La valeur pourra être retrouvée avec `#GET{variable}`.
391
 *
392
 * @example
393
 *     `[(#CALCUL|set{toto})]` enregistre le résultat de `#CALCUL`
394
 *     dans la variable `toto` et renvoie vide.
395
 *     C'est équivalent à `[(#SET{toto, #CALCUL})]` dans ce cas.
396
 *     `#GET{toto}` retourne la valeur sauvegardée.
397
 *
398
 * @example
399
 *     `[(#CALCUL|set{toto,1})]` enregistre le résultat de `#CALCUL`
400
 *      dans la variable toto et renvoie la valeur. Cela permet d'utiliser
401
 *      d'autres filtres ensuite. `#GET{toto}` retourne la valeur.
402
 *
403
 * @filtre
404
 * @param array $Pile Pile de données
405
 * @param mixed $val Valeur à sauver
406
 * @param string $key Clé d'enregistrement
407
 * @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...
408
 * @return mixed
409
 */
410
function filtre_set(&$Pile, $val, $key, $continue = null) {
411
	$Pile['vars'][$key] = $val;
412
	return $continue ? $val : '';
413
}
414
415
/**
416
 * Filtre `setenv` qui enregistre une valeur dans l'environnement du squelette
417
 *
418
 * La valeur pourra être retrouvée avec `#ENV{variable}`.
419
 * 
420
 * @example
421
 *     `[(#CALCUL|setenv{toto})]` enregistre le résultat de `#CALCUL`
422
 *      dans l'environnement toto et renvoie vide.
423
 *      `#ENV{toto}` retourne la valeur.
424
 *
425
 *      `[(#CALCUL|setenv{toto,1})]` enregistre le résultat de `#CALCUL`
426
 *      dans l'environnement toto et renvoie la valeur.
427
 *      `#ENV{toto}` retourne la valeur.
428
 *
429
 * @filtre
430
 *
431
 * @param array $Pile
432
 * @param mixed $val Valeur à enregistrer
433
 * @param mixed $key Nom de la variable
434
 * @param null|mixed $continue Si présent, retourne la valeur en sortie
435
 * @return string|mixed Retourne `$val` si `$continue` présent, sinon ''.
436
 */
437
function filtre_setenv(&$Pile, $val, $key, $continue = null) {
438
	$Pile[0][$key] = $val;
439
	return $continue ? $val : '';
440
}
441
442
/**
443
 * @param array $Pile
444
 * @param array|string $keys
445
 * @return string
446
 */
447
function filtre_sanitize_env(&$Pile, $keys) {
448
	$Pile[0] = spip_sanitize_from_request($Pile[0], $keys);
449
	return '';
450
}
451
452
453
/**
454
 * Filtre `debug` qui affiche un debug de la valeur en entrée
455
 *
456
 * Log la valeur dans `debug.log` et l'affiche si on est webmestre.
457
 *
458
 * @example
459
 *     `[(#TRUC|debug)]` affiche et log la valeur de `#TRUC`
460
 * @example
461
 *     `[(#TRUC|debug{avant}|calcul|debug{apres}|etc)]`
462
 *     affiche la valeur de `#TRUC` avant et après le calcul,
463
 *     en précisant "avant" et "apres".
464
 *
465
 * @filtre
466
 * @link https://www.spip.net/5695
467
 * @param mixed $val La valeur à debugguer
468
 * @param mixed|null $key Clé pour s'y retrouver
469
 * @return mixed Retourne la valeur (sans la modifier).
470
 */
471
function filtre_debug($val, $key = null) {
472
	$debug = (
473
		is_null($key) ? '' : (var_export($key, true) . " = ")
474
		) . var_export($val, true);
475
476
	include_spip('inc/autoriser');
477
	if (autoriser('webmestre')) {
478
		echo "<div class='spip_debug'>\n", $debug, "</div>\n";
479
	}
480
481
	spip_log($debug, 'debug');
482
483
	return $val;
484
}
485
486
487
/**
488
 * Exécute un filtre image
489
 *
490
 * Fonction générique d'entrée des filtres images.
491
 * Accepte en entrée :
492
 *
493
 * - un texte complet,
494
 * - un img-log (produit par #LOGO_XX),
495
 * - un tag `<img ...>` complet,
496
 * - un nom de fichier *local* (passer le filtre `|copie_locale` si on veut
497
 *   l'appliquer à un document distant).
498
 *
499
 * Applique le filtre demande à chacune des occurrences
500
 *
501
 * @param array $args
502
 *     Liste des arguments :
503
 *
504
 *     - le premier est le nom du filtre image à appliquer
505
 *     - le second est le texte sur lequel on applique le filtre
506
 *     - les suivants sont les arguments du filtre image souhaité.
507
 * @return string
508
 *     Texte qui a reçu les filtres
509
 **/
510
function image_filtrer($args) {
511
	$filtre = array_shift($args); # enlever $filtre
512
	$texte = array_shift($args);
513
	if (!strlen($texte)) {
514
		return;
515
	}
516
	find_in_path('filtres_images_mini.php', 'inc/', true);
517
	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...
518
	// Cas du nom de fichier local
519
	$is_file = trim($texte);
520
	if (strpos(substr($is_file, strlen(_DIR_RACINE)), '..') !== false
521
		  or strpbrk($is_file, "<>\n\r\t") !== false
522
		  or strpos($is_file, '/') === 0
523
	) {
524
		$is_file = false;
525
	}
526
	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...
527
		$is_local_file = function($path) {
528
			if (strpos($path, "?") !== false) {
529
				$path = supprimer_timestamp($path);
530
				// remove ?24px added by find_in_theme on .svg files
531
				$path = preg_replace(",\?[[:digit:]]+(px)$,", "", $path);
532
			}
533
			return file_exists($path);
534
		};
535
		if ($is_local_file($is_file) or tester_url_absolue($is_file)) {
536
			array_unshift($args, "<img src='$is_file' />");
537
			$res = call_user_func_array($filtre, $args);
538
			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...
539
			return $res;
540
		}
541
	}
542
543
	// Cas general : trier toutes les images, avec eventuellement leur <span>
544
	if (preg_match_all(
545
		',(<([a-z]+) [^<>]*spip_documents[^<>]*>)?\s*(<img\s.*>),UimsS',
546
		$texte, $tags, PREG_SET_ORDER)) {
547
		foreach ($tags as $tag) {
548
			$class = extraire_attribut($tag[3], 'class');
549
			if (!$class or
550
				(strpos($class, 'filtre_inactif') === false
551
					// compat historique a virer en 3.2
552
					and strpos($class, 'no_image_filtrer') === false)
553
			) {
554
				array_unshift($args, $tag[3]);
555
				if ($reduit = call_user_func_array($filtre, $args)) {
556
					// En cas de span spip_documents, modifier le style=...width:
557
					if ($tag[1]) {
558
						$w = extraire_attribut($reduit, 'width');
559
						if (!$w and preg_match(",width:\s*(\d+)px,S", extraire_attribut($reduit, 'style'), $regs)) {
560
							$w = $regs[1];
561
						}
562
						if ($w and ($style = extraire_attribut($tag[1], 'style'))) {
563
							$style = preg_replace(",width:\s*\d+px,S", "width:${w}px", $style);
564
							$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 563 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...
565
							$texte = str_replace($tag[1], $replace, $texte);
566
						}
567
					}
568
					// traiter aussi un eventuel mouseover
569
					if ($mouseover = extraire_attribut($reduit, 'onmouseover')) {
570
						if (preg_match(",this[.]src=['\"]([^'\"]+)['\"],ims", $mouseover, $match)) {
571
							$srcover = $match[1];
572
							array_shift($args);
573
							array_unshift($args, "<img src='" . $match[1] . "' />");
574
							$srcover_filter = call_user_func_array($filtre, $args);
575
							$srcover_filter = extraire_attribut($srcover_filter, 'src');
576
							$reduit = str_replace($srcover, $srcover_filter, $reduit);
577
						}
578
					}
579
					$texte = str_replace($tag[3], $reduit, $texte);
580
				}
581
				array_shift($args);
582
			}
583
		}
584
	}
585
	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...
586
	return $texte;
587
}
588
589
/**
590
 * Retourne les tailles d'une image
591
 *
592
 * Pour les filtres `largeur` et `hauteur`
593
 *
594
 * @param string $img
595
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
596
 * @return array
597
 *     Liste (hauteur, largeur) en pixels
598
 **/
599
function taille_image($img, $force_refresh = false) {
600
601
	static $largeur_img = array(), $hauteur_img = array();
602
	$srcWidth = 0;
603
	$srcHeight = 0;
604
605
	$src = extraire_attribut($img, 'src');
606
607
	if (!$src) {
608
		$src = $img;
609
	} else {
610
		$srcWidth = extraire_attribut($img, 'width');
611
		$srcHeight = extraire_attribut($img, 'height');
612
	}
613
614
	// ne jamais operer directement sur une image distante pour des raisons de perfo
615
	// la copie locale a toutes les chances d'etre la ou de resservir
616
	if (tester_url_absolue($src)) {
0 ignored issues
show
Bug introduced by
It seems like $src defined by extraire_attribut($img, 'src') on line 605 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...
617
		include_spip('inc/distant');
618
		$fichier = copie_locale($src);
0 ignored issues
show
Bug introduced by
It seems like $src defined by extraire_attribut($img, 'src') on line 605 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...
619
		$src = $fichier ? _DIR_RACINE . $fichier : $src;
620
	}
621 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...
622
		$src = substr($src, 0, $p);
623
	}
624
625
	$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...
626
	if (isset($largeur_img[$src]) and !$force_refresh) {
627
		$srcWidth = $largeur_img[$src];
628
	}
629
	if (isset($hauteur_img[$src]) and !$force_refresh) {
630
		$srcHeight = $hauteur_img[$src];
631
	}
632
	if (!$srcWidth or !$srcHeight) {
633
634
		if (file_exists($src)
635
			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...
636
		) {
637
			if (!$srcWidth) {
638
				$largeur_img[$src] = $srcWidth = $srcsize[0];
639
			}
640
			if (!$srcHeight) {
641
				$hauteur_img[$src] = $srcHeight = $srcsize[1];
642
			}
643
		}
644
		elseif(strpos($src, "<svg") !== false) {
645
			include_spip('inc/svg');
646
			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...
647
				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...
648
				if (!$srcWidth){
649
					$largeur_img[$src] = $srcWidth = $width;
650
				}
651
				if (!$srcHeight){
652
					$hauteur_img[$src] = $srcHeight = $height;
653
				}
654
			}
655
		}
656
		// $src peut etre une reference a une image temporaire dont a n'a que le log .src
657
		// on s'y refere, l'image sera reconstruite en temps utile si necessaire
658
		elseif (@file_exists($f = "$src.src")
659
			and lire_fichier($f, $valeurs)
660
			and $valeurs = unserialize($valeurs)
661
		) {
662
			if (!$srcWidth) {
663
				$largeur_img[$src] = $srcWidth = $valeurs["largeur_dest"];
664
			}
665
			if (!$srcHeight) {
666
				$hauteur_img[$src] = $srcHeight = $valeurs["hauteur_dest"];
667
			}
668
		}
669
	}
670
671
	return array($srcHeight, $srcWidth);
672
}
673
674
675
/**
676
 * Retourne la largeur d'une image
677
 *
678
 * @filtre
679
 * @link https://www.spip.net/4296
680
 * @uses taille_image()
681
 * @see  hauteur()
682
 *
683
 * @param string $img
684
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
685
 * @return int|null
686
 *     Largeur en pixels, NULL ou 0 si aucune image.
687
 **/
688
function largeur($img) {
689
	if (!$img) {
690
		return;
691
	}
692
	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...
693
694
	return $l;
695
}
696
697
/**
698
 * Retourne la hauteur d'une image
699
 *
700
 * @filtre
701
 * @link https://www.spip.net/4291
702
 * @uses taille_image()
703
 * @see  largeur()
704
 *
705
 * @param string $img
706
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
707
 * @return int|null
708
 *     Hauteur en pixels, NULL ou 0 si aucune image.
709
 **/
710
function hauteur($img) {
711
	if (!$img) {
712
		return;
713
	}
714
	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...
715
716
	return $h;
717
}
718
719
720
/**
721
 * Échappement des entités HTML avec correction des entités « brutes »
722
 *
723
 * Ces entités peuvent être générées par les butineurs lorsqu'on rentre des
724
 * caractères n'appartenant pas au charset de la page [iso-8859-1 par défaut]
725
 *
726
 * Attention on limite cette correction aux caracteres « hauts » (en fait > 99
727
 * pour aller plus vite que le > 127 qui serait logique), de manière à
728
 * préserver des eéhappements de caractères « bas » (par exemple `[` ou `"`)
729
 * et au cas particulier de `&amp;` qui devient `&amp;amp;` dans les URL
730
 *
731
 * @see corriger_toutes_entites_html()
732
 * @param string $texte
733
 * @return string
734
 **/
735
function corriger_entites_html($texte) {
736
	if (strpos($texte, '&amp;') === false) {
737
		return $texte;
738
	}
739
740
	return preg_replace(',&amp;(#[0-9][0-9][0-9]+;|amp;),iS', '&\1', $texte);
741
}
742
743
/**
744
 * Échappement des entités HTML avec correction des entités « brutes » ainsi
745
 * que les `&amp;eacute;` en `&eacute;`
746
 *
747
 * Identique à `corriger_entites_html()` en corrigeant aussi les
748
 * `&amp;eacute;` en `&eacute;`
749
 *
750
 * @see corriger_entites_html()
751
 * @param string $texte
752
 * @return string
753
 **/
754
function corriger_toutes_entites_html($texte) {
755
	if (strpos($texte, '&amp;') === false) {
756
		return $texte;
757
	}
758
759
	return preg_replace(',&amp;(#?[a-z0-9]+;),iS', '&\1', $texte);
760
}
761
762
/**
763
 * Échappe les `&` en `&amp;`
764
 *
765
 * @param string $texte
766
 * @return string
767
 **/
768
function proteger_amp($texte) {
769
	return str_replace('&', '&amp;', $texte);
770
}
771
772
773
/**
774
 * Échappe en entités HTML certains caractères d'un texte
775
 *
776
 * Traduira un code HTML en transformant en entités HTML les caractères
777
 * en dehors du charset de la page ainsi que les `"`, `<` et `>`.
778
 *
779
 * Ceci permet d’insérer le texte d’une balise dans un `<textarea> </textarea>`
780
 * sans dommages.
781
 *
782
 * @filtre
783
 * @link https://www.spip.net/4280
784
 *
785
 * @uses echappe_html()
786
 * @uses echappe_retour()
787
 * @uses proteger_amp()
788
 * @uses corriger_entites_html()
789
 * @uses corriger_toutes_entites_html()
790
 *
791
 * @param string $texte
792
 *   chaine a echapper
793
 * @param bool $tout
794
 *   corriger toutes les `&amp;xx;` en `&xx;`
795
 * @param bool $quote
796
 *   Échapper aussi les simples quotes en `&#039;`
797
 * @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...
798
 */
799
function entites_html($texte, $tout = false, $quote = true) {
800 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...
801
		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...
802
	) {
803
		return $texte;
804
	}
805
	include_spip('inc/texte');
806
	$flags = ($quote ? ENT_QUOTES : ENT_NOQUOTES);
807
	$flags |= ENT_HTML401;
808
	$texte = spip_htmlspecialchars(echappe_retour(echappe_html($texte, '', true), '', 'proteger_amp'), $flags);
809
	if ($tout) {
810
		return corriger_toutes_entites_html($texte);
811
	} else {
812
		return corriger_entites_html($texte);
813
	}
814
}
815
816
/**
817
 * Convertit les caractères spéciaux HTML dans le charset du site.
818
 *
819
 * @exemple
820
 *     Si le charset de votre site est `utf-8`, `&eacute;` ou `&#233;`
821
 *     sera transformé en `é`
822
 *
823
 * @filtre
824
 * @link https://www.spip.net/5513
825
 *
826
 * @param string $texte
827
 *     Texte à convertir
828
 * @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...
829
 *     Texte converti
830
 **/
831
function filtrer_entites($texte) {
832
	if (strpos($texte, '&') === false) {
833
		return $texte;
834
	}
835
	// filtrer
836
	$texte = html2unicode($texte);
837
	// remettre le tout dans le charset cible
838
	$texte = unicode2charset($texte);
839
	// cas particulier des " et ' qu'il faut filtrer aussi
840
	// (on le faisait deja avec un &quot;)
841 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...
842
		$texte = str_replace(array("&#039;", "&#39;", "&#034;", "&#34;"), array("'", "'", '"', '"'), $texte);
843
	}
844
845
	return $texte;
846
}
847
848
849
if (!function_exists('filtre_filtrer_entites_dist')) {
850
	/**
851
	 * Version sécurisée de filtrer_entites
852
	 * 
853
	 * @uses interdire_scripts()
854
	 * @uses filtrer_entites()
855
	 * 
856
	 * @param string $t
857
	 * @return string
858
	 */
859
	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 (L659-662) 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...
860
		include_spip('inc/texte');
861
		return interdire_scripts(filtrer_entites($t));
862
	}
863
}
864
865
866
/**
867
 * Supprime des caractères illégaux
868
 *
869
 * Remplace les caractères de controle par le caractère `-`
870
 *
871
 * @link http://www.w3.org/TR/REC-xml/#charsets
872
 *
873
 * @param string|array $texte
874
 * @return string|array
875
 **/
876
function supprimer_caracteres_illegaux($texte) {
877
	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";
878
	static $to = null;
879
880
	if (is_array($texte)) {
881
		return array_map('supprimer_caracteres_illegaux', $texte);
882
	}
883
884
	if (!$to) {
885
		$to = str_repeat('-', strlen($from));
886
	}
887
888
	return strtr($texte, $from, $to);
889
}
890
891
/**
892
 * Correction de caractères
893
 *
894
 * Supprimer les caracteres windows non conformes et les caracteres de controle illégaux
895
 *
896
 * @param string|array $texte
897
 * @return string|array
898
 **/
899
function corriger_caracteres($texte) {
900
	$texte = corriger_caracteres_windows($texte);
901
	$texte = supprimer_caracteres_illegaux($texte);
902
903
	return $texte;
904
}
905
906
/**
907
 * Encode du HTML pour transmission XML notamment dans les flux RSS
908
 *
909
 * Ce filtre transforme les liens en liens absolus, importe les entitées html et échappe les tags html.
910
 *
911
 * @filtre
912
 * @link https://www.spip.net/4287
913
 *
914
 * @param string $texte
915
 *     Texte à transformer
916
 * @return string
917
 *     Texte encodé pour XML
918
 */
919
function texte_backend($texte) {
920
921
	static $apostrophe = array("&#8217;", "'"); # n'allouer qu'une fois
922
923
	// si on a des liens ou des images, les passer en absolu
924
	$texte = liens_absolus($texte);
925
926
	// echapper les tags &gt; &lt;
927
	$texte = preg_replace(',&(gt|lt);,S', '&amp;\1;', $texte);
928
929
	// importer les &eacute;
930
	$texte = filtrer_entites($texte);
931
932
	// " -> &quot; et tout ce genre de choses
933
	$u = $GLOBALS['meta']['pcre_u'];
934
	$texte = str_replace("&nbsp;", " ", $texte);
935
	$texte = preg_replace('/\s{2,}/S' . $u, " ", $texte);
936
	// ne pas echapper les sinqle quotes car certains outils de syndication gerent mal
937
	$texte = entites_html($texte, false, false);
938
	// mais bien echapper les double quotes !
939
	$texte = str_replace('"', '&#034;', $texte);
940
941
	// verifier le charset
942
	$texte = charset2unicode($texte);
943
944
	// Caracteres problematiques en iso-latin 1
945
	if (isset($GLOBALS['meta']['charset']) and $GLOBALS['meta']['charset'] == 'iso-8859-1') {
946
		$texte = str_replace(chr(156), '&#156;', $texte);
947
		$texte = str_replace(chr(140), '&#140;', $texte);
948
		$texte = str_replace(chr(159), '&#159;', $texte);
949
	}
950
951
	// l'apostrophe curly pose probleme a certains lecteure de RSS
952
	// et le caractere apostrophe alourdit les squelettes avec PHP
953
	// ==> on les remplace par l'entite HTML
954
	return str_replace($apostrophe, "'", $texte);
955
}
956
957
/**
958
 * Encode et quote du HTML pour transmission XML notamment dans les flux RSS
959
 *
960
 * Comme texte_backend(), mais avec addslashes final pour squelettes avec PHP (rss)
961
 *
962
 * @uses texte_backend()
963
 * @filtre
964
 *
965
 * @param string $texte
966
 *     Texte à transformer
967
 * @return string
968
 *     Texte encodé et quote pour XML
969
 */
970
function texte_backendq($texte) {
971
	return addslashes(texte_backend($texte));
972
}
973
974
975
/**
976
 * Enlève un numéro préfixant un texte
977
 *
978
 * Supprime `10. ` dans la chaine `10. Titre`
979
 *
980
 * @filtre
981
 * @link https://www.spip.net/4314
982
 * @see recuperer_numero() Pour obtenir le numéro
983
 * @example
984
 *     ```
985
 *     [<h1>(#TITRE|supprimer_numero)</h1>]
986
 *     ```
987
 *
988
 * @param string $texte
989
 *     Texte
990
 * @return int|string
991
 *     Numéro de titre, sinon chaîne vide
992
 **/
993
function supprimer_numero($texte) {
994
	return preg_replace(
995
		",^[[:space:]]*([0-9]+)([.)]|" . chr(194) . '?' . chr(176) . ")[[:space:]]+,S",
996
		"", $texte);
997
}
998
999
/**
1000
 * Récupère un numéro préfixant un texte
1001
 *
1002
 * Récupère le numéro `10` dans la chaine `10. Titre`
1003
 *
1004
 * @filtre
1005
 * @link https://www.spip.net/5514
1006
 * @see supprimer_numero() Pour supprimer le numéro
1007
 * @see balise_RANG_dist() Pour obtenir un numéro de titre
1008
 * @example
1009
 *     ```
1010
 *     [(#TITRE|recuperer_numero)]
1011
 *     ```
1012
 *
1013
 * @param string $texte
1014
 *     Texte
1015
 * @return int|string
1016
 *     Numéro de titre, sinon chaîne vide
1017
 **/
1018
function recuperer_numero($texte) {
1019
	if (preg_match(
1020
		",^[[:space:]]*([0-9]+)([.)]|" . chr(194) . '?' . chr(176) . ")[[:space:]]+,S",
1021
		$texte, $regs)) {
1022
		return strval($regs[1]);
1023
	} else {
1024
		return '';
1025
	}
1026
}
1027
1028
/**
1029
 * Suppression basique et brutale de tous les tags
1030
 *
1031
 * Supprime tous les tags `<...>`.
1032
 * Utilisé fréquemment pour écrire des RSS.
1033
 *
1034
 * @filtre
1035
 * @link https://www.spip.net/4315
1036
 * @example
1037
 *     ```
1038
 *     <title>[(#TITRE|supprimer_tags|texte_backend)]</title>
1039
 *     ```
1040
 *
1041
 * @note
1042
 *     Ce filtre supprime aussi les signes inférieurs `<` rencontrés.
1043
 *
1044
 * @param string $texte
1045
 *     Texte à échapper
1046
 * @param string $rempl
1047
 *     Inutilisé.
1048
 * @return string
1049
 *     Texte converti
1050
 **/
1051
function supprimer_tags($texte, $rempl = "") {
1052
	$texte = preg_replace(",<(!--|\w|/|!\[endif|!\[if)[^>]*>,US", $rempl, $texte);
1053
	// ne pas oublier un < final non ferme car coupe
1054
	$texte = preg_replace(",<(!--|\w|/).*$,US", $rempl, $texte);
1055
	// mais qui peut aussi etre un simple signe plus petit que
1056
	$texte = str_replace('<', '&lt;', $texte);
1057
1058
	return $texte;
1059
}
1060
1061
/**
1062
 * Convertit les chevrons de tag en version lisible en HTML
1063
 *
1064
 * Transforme les chevrons de tag `<...>` en entité HTML.
1065
 *
1066
 * @filtre
1067
 * @link https://www.spip.net/5515
1068
 * @example
1069
 *     ```
1070
 *     <pre>[(#TEXTE|echapper_tags)]</pre>
1071
 *     ```
1072
 *
1073
 * @param string $texte
1074
 *     Texte à échapper
1075
 * @param string $rempl
1076
 *     Inutilisé.
1077
 * @return string
1078
 *     Texte converti
1079
 **/
1080
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...
1081
	$texte = preg_replace("/<([^>]*)>/", "&lt;\\1&gt;", $texte);
1082
1083
	return $texte;
1084
}
1085
1086
/**
1087
 * Convertit un texte HTML en texte brut
1088
 *
1089
 * Enlève les tags d'un code HTML, élimine les doubles espaces.
1090
 *
1091
 * @filtre
1092
 * @link https://www.spip.net/4317
1093
 * @example
1094
 *     ```
1095
 *     <title>[(#TITRE|textebrut) - ][(#NOM_SITE_SPIP|textebrut)]</title>
1096
 *     ```
1097
 *
1098
 * @param string $texte
1099
 *     Texte à convertir
1100
 * @return string
1101
 *     Texte converti
1102
 **/
1103
function textebrut($texte) {
1104
	$u = $GLOBALS['meta']['pcre_u'];
1105
	$texte = preg_replace('/\s+/S' . $u, " ", $texte);
1106
	$texte = preg_replace("/<(p|br)( [^>]*)?" . ">/iS", "\n\n", $texte);
1107
	$texte = preg_replace("/^\n+/", "", $texte);
1108
	$texte = preg_replace("/\n+$/", "", $texte);
1109
	$texte = preg_replace("/\n +/", "\n", $texte);
1110
	$texte = supprimer_tags($texte);
1111
	$texte = preg_replace("/(&nbsp;| )+/S", " ", $texte);
1112
	// nettoyer l'apostrophe curly qui pose probleme a certains rss-readers, lecteurs de mail...
1113
	$texte = str_replace("&#8217;", "'", $texte);
1114
1115
	return $texte;
1116
}
1117
1118
1119
/**
1120
 * Remplace les liens SPIP en liens ouvrant dans une nouvelle fenetre (target=blank)
1121
 *
1122
 * @filtre
1123
 * @link https://www.spip.net/4297
1124
 *
1125
 * @param string $texte
1126
 *     Texte avec des liens
1127
 * @return string
1128
 *     Texte avec liens ouvrants
1129
 **/
1130
function liens_ouvrants($texte) {
1131
	if (preg_match_all(",(<a\s+[^>]*https?://[^>]*class=[\"']spip_(out|url)\b[^>]+>),imsS",
1132
		$texte, $liens, PREG_PATTERN_ORDER)) {
1133
		foreach ($liens[0] as $a) {
1134
			$rel = 'noopener noreferrer ' . extraire_attribut($a, 'rel');
1135
			$ablank = inserer_attribut($a, 'rel', $rel);
1136
			$ablank = inserer_attribut($ablank, 'target', '_blank');
1137
			$texte = str_replace($a, $ablank, $texte);
1138
		}
1139
	}
1140
1141
	return $texte;
1142
}
1143
1144
/**
1145
 * Ajouter un attribut rel="nofollow" sur tous les liens d'un texte
1146
 *
1147
 * @param string $texte
1148
 * @return string
1149
 */
1150
function liens_nofollow($texte) {
1151
	if (stripos($texte, "<a") === false) {
1152
		return $texte;
1153
	}
1154
1155
	if (preg_match_all(",<a\b[^>]*>,UimsS", $texte, $regs, PREG_PATTERN_ORDER)) {
1156
		foreach ($regs[0] as $a) {
1157
			$rel = extraire_attribut($a, "rel");
1158
			if (strpos($rel, "nofollow") === false) {
1159
				$rel = "nofollow" . ($rel ? " $rel" : "");
1160
				$anofollow = inserer_attribut($a, "rel", $rel);
1161
				$texte = str_replace($a, $anofollow, $texte);
1162
			}
1163
		}
1164
	}
1165
1166
	return $texte;
1167
}
1168
1169
/**
1170
 * Transforme les sauts de paragraphe HTML `p` en simples passages à la ligne `br`
1171
 *
1172
 * @filtre
1173
 * @link https://www.spip.net/4308
1174
 * @example
1175
 *     ```
1176
 *     [<div>(#DESCRIPTIF|PtoBR)[(#NOTES|PtoBR)]</div>]
1177
 *     ```
1178
 *
1179
 * @param string $texte
1180
 *     Texte à transformer
1181
 * @return string
1182
 *     Texte sans paraghaphes
1183
 **/
1184
function PtoBR($texte) {
1185
	$u = $GLOBALS['meta']['pcre_u'];
1186
	$texte = preg_replace("@</p>@iS", "\n", $texte);
1187
	$texte = preg_replace("@<p\b.*>@UiS", "<br />", $texte);
1188
	$texte = preg_replace("@^\s*<br />@S" . $u, "", $texte);
1189
1190
	return $texte;
1191
}
1192
1193
1194
/**
1195
 * Assure qu'un texte ne vas pas déborder d'un bloc
1196
 * par la faute d'un mot trop long (souvent des URLs)
1197
 *
1198
 * Ne devrait plus être utilisé et fait directement en CSS par un style
1199
 * `word-wrap:break-word;`
1200
 *
1201
 * @note
1202
 *   Pour assurer la compatibilité du filtre, on encapsule le contenu par
1203
 *   un `div` ou `span` portant ce style CSS inline.
1204
 *
1205
 * @filtre
1206
 * @link https://www.spip.net/4298
1207
 * @link http://www.alsacreations.com/tuto/lire/1038-gerer-debordement-contenu-css.html
1208
 * @deprecated Utiliser le style CSS `word-wrap:break-word;`
1209
 *
1210
 * @param string $texte Texte
1211
 * @return string Texte encadré du style CSS
1212
 */
1213
function lignes_longues($texte) {
1214
	if (!strlen(trim($texte))) {
1215
		return $texte;
1216
	}
1217
	include_spip('inc/texte');
1218
	$tag = preg_match(',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS', $texte) ?
1219
		'div' : 'span';
1220
1221
	return "<$tag style='word-wrap:break-word;'>$texte</$tag>";
1222
}
1223
1224
/**
1225
 * Passe un texte en majuscules, y compris les accents, en HTML
1226
 *
1227
 * Encadre le texte du style CSS `text-transform: uppercase;`.
1228
 * Le cas spécifique du i turc est géré.
1229
 *
1230
 * @filtre
1231
 * @example
1232
 *     ```
1233
 *     [(#EXTENSION|majuscules)]
1234
 *     ```
1235
 *
1236
 * @param string $texte Texte
1237
 * @return string Texte en majuscule
1238
 */
1239
function majuscules($texte) {
1240
	if (!strlen($texte)) {
1241
		return '';
1242
	}
1243
1244
	// Cas du turc
1245
	if ($GLOBALS['spip_lang'] == 'tr') {
1246
		# remplacer hors des tags et des entites
1247 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...
1248
			foreach ($regs as $n => $match) {
1249
				$texte = str_replace($match[0], "@@SPIP_TURC$n@@", $texte);
1250
			}
1251
		}
1252
1253
		$texte = str_replace('i', '&#304;', $texte);
1254
1255 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...
1256
			foreach ($regs as $n => $match) {
1257
				$texte = str_replace("@@SPIP_TURC$n@@", $match[0], $texte);
1258
			}
1259
		}
1260
	}
1261
1262
	// Cas general
1263
	return "<span style='text-transform: uppercase;'>$texte</span>";
1264
}
1265
1266
/**
1267
 * Retourne une taille en octets humainement lisible
1268
 *
1269
 * Tel que "127.4 ko" ou "3.1 Mo"
1270
 *
1271
 * @example
1272
 *     - `[(#TAILLE|taille_en_octets)]`
1273
 *     - `[(#VAL{123456789}|taille_en_octets)]` affiche `117.7 Mo`
1274
 *
1275
 * @filtre
1276
 * @link https://www.spip.net/4316
1277
 * @param int $taille
1278
 * @return string
1279
 **/
1280
function taille_en_octets($taille) {
1281
	if (!defined('_KILOBYTE')) {
1282
		/**
1283
		 * Définit le nombre d'octets dans un Kilobyte
1284
		 *
1285
		 * @var int
1286
		 **/
1287
		define('_KILOBYTE', 1024);
1288
	}
1289
1290
	if ($taille < 1) {
1291
		return '';
1292
	}
1293
	if ($taille < _KILOBYTE) {
1294
		$taille = _T('taille_octets', array('taille' => $taille));
1295 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...
1296
		$taille = _T('taille_ko', array('taille' => round($taille / _KILOBYTE, 1)));
1297
	} elseif ($taille < _KILOBYTE * _KILOBYTE * _KILOBYTE) {
1298
		$taille = _T('taille_mo', array('taille' => round($taille / _KILOBYTE / _KILOBYTE, 1)));
1299 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...
1300
		$taille = _T('taille_go', array('taille' => round($taille / _KILOBYTE / _KILOBYTE / _KILOBYTE, 2)));
1301
	}
1302
1303
	return $taille;
1304
}
1305
1306
1307
/**
1308
 * Rend une chaine utilisable sans dommage comme attribut HTML
1309
 *
1310
 * @example `<a href="#URL_ARTICLE" title="[(#TITRE|attribut_html)]">#TITRE</a>`
1311
 *
1312
 * @filtre
1313
 * @link https://www.spip.net/4282
1314
 * @uses textebrut()
1315
 * @uses texte_backend()
1316
 *
1317
 * @param string $texte
1318
 *     Texte à mettre en attribut
1319
 * @param bool $textebrut
1320
 *     Passe le texte en texte brut (enlève les balises html) ?
1321
 * @return string
1322
 *     Texte prêt pour être utilisé en attribut HTML
1323
 **/
1324
function attribut_html($texte, $textebrut = true) {
1325
	$u = $GLOBALS['meta']['pcre_u'];
1326
	if ($textebrut) {
1327
		$texte = preg_replace(array(",\n,", ",\s(?=\s),msS" . $u), array(" ", ""), textebrut($texte));
1328
	}
1329
	$texte = texte_backend($texte);
1330
	$texte = str_replace(array("'", '"'), array('&#039;', '&#034;'), $texte);
1331
1332
	return preg_replace(array("/&(amp;|#38;)/", "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/"), array("&", "&#38;"),
1333
		$texte);
1334
}
1335
1336
1337
/**
1338
 * Vider les URL nulles
1339
 *
1340
 * - Vide les URL vides comme `http://` ou `mailto:` (sans rien d'autre)
1341
 * - échappe les entités et gère les `&amp;`
1342
 *
1343
 * @uses entites_html()
1344
 *
1345
 * @param string $url
1346
 *     URL à vérifier et échapper
1347
 * @param bool $entites
1348
 *     `true` pour échapper les entités HTML.
1349
 * @return string
1350
 *     URL ou chaîne vide
1351
 **/
1352
function vider_url($url, $entites = true) {
1353
	# un message pour abs_url
1354
	$GLOBALS['mode_abs_url'] = 'url';
1355
	$url = trim($url);
1356
	$r = ",^(?:" . _PROTOCOLES_STD . '):?/?/?$,iS';
1357
1358
	return preg_match($r, $url) ? '' : ($entites ? entites_html($url) : $url);
1359
}
1360
1361
1362
/**
1363
 * Maquiller une adresse e-mail
1364
 *
1365
 * Remplace `@` par 3 caractères aléatoires.
1366
 *
1367
 * @uses creer_pass_aleatoire()
1368
 *
1369
 * @param string $texte Adresse email
1370
 * @return string Adresse email maquillée
1371
 **/
1372
function antispam($texte) {
1373
	include_spip('inc/acces');
1374
	$masque = creer_pass_aleatoire(3);
1375
1376
	return preg_replace("/@/", " $masque ", $texte);
1377
}
1378
1379
/**
1380
 * Vérifie un accès à faible sécurité
1381
 *
1382
 * Vérifie qu'un visiteur peut accéder à la page demandée,
1383
 * qui est protégée par une clé, calculée à partir du low_sec de l'auteur,
1384
 * et des paramètres le composant l'appel (op, args)
1385
 *
1386
 * @example
1387
 *     `[(#ID_AUTEUR|securiser_acces{#ENV{cle}, rss, #ENV{op}, #ENV{args}}|sinon_interdire_acces)]`
1388
 *
1389
 * @see  bouton_spip_rss() pour générer un lien de faible sécurité pour les RSS privés
1390
 * @see  afficher_low_sec() pour calculer une clé valide
1391
 * @uses verifier_low_sec()
1392
 *
1393
 * @filtre
1394
 * @param int $id_auteur
1395
 *     L'auteur qui demande la page
1396
 * @param string $cle
1397
 *     La clé à tester
1398
 * @param string $dir
1399
 *     Un type d'accès (nom du répertoire dans lequel sont rangés les squelettes demandés, tel que 'rss')
1400
 * @param string $op
1401
 *     Nom de l'opération éventuelle
1402
 * @param string $args
1403
 *     Nom de l'argument calculé
1404
 * @return bool
1405
 *     True si on a le droit d'accès, false sinon.
1406
 **/
1407
function securiser_acces($id_auteur, $cle, $dir, $op = '', $args = '') {
1408
	include_spip('inc/acces');
1409
	if ($op) {
1410
		$dir .= " $op $args";
1411
	}
1412
1413
	return verifier_low_sec($id_auteur, $cle, $dir);
1414
}
1415
1416
/**
1417
 * Retourne le second paramètre lorsque
1418
 * le premier est considere vide, sinon retourne le premier paramètre.
1419
 *
1420
 * En php `sinon($a, 'rien')` retourne `$a`, ou `'rien'` si `$a` est vide.
1421
 * En filtre SPIP `|sinon{#TEXTE, rien}` : affiche `#TEXTE` ou `rien` si `#TEXTE` est vide,
1422
 *
1423
 * @filtre
1424
 * @see filtre_logique() pour la compilation du filtre dans un squelette
1425
 * @link https://www.spip.net/4313
1426
 * @note
1427
 *     L'utilisation de `|sinon` en tant que filtre de squelette
1428
 *     est directement compilé dans `public/references` par la fonction `filtre_logique()`
1429
 *
1430
 * @param mixed $texte
1431
 *     Contenu de reference a tester
1432
 * @param mixed $sinon
1433
 *     Contenu a retourner si le contenu de reference est vide
1434
 * @return mixed
1435
 *     Retourne $texte, sinon $sinon.
1436
 **/
1437
function sinon($texte, $sinon = '') {
1438
	if ($texte or (!is_array($texte) and strlen($texte))) {
1439
		return $texte;
1440
	} else {
1441
		return $sinon;
1442
	}
1443
}
1444
1445
/**
1446
 * Filtre `|choixsivide{vide, pas vide}` alias de `|?{si oui, si non}` avec les arguments inversés
1447
 *
1448
 * @example
1449
 *     `[(#TEXTE|choixsivide{vide, plein})]` affiche vide si le `#TEXTE`
1450
 *     est considéré vide par PHP (chaîne vide, false, 0, tableau vide, etc…).
1451
 *     C'est l'équivalent de `[(#TEXTE|?{plein, vide})]`
1452
 *
1453
 * @filtre
1454
 * @see choixsiegal()
1455
 * @link https://www.spip.net/4189
1456
 *
1457
 * @param mixed $a
1458
 *     La valeur à tester
1459
 * @param mixed $vide
1460
 *     Ce qui est retourné si `$a` est considéré vide
1461
 * @param mixed $pasvide
1462
 *     Ce qui est retourné sinon
1463
 * @return mixed
1464
 **/
1465
function choixsivide($a, $vide, $pasvide) {
1466
	return $a ? $pasvide : $vide;
1467
}
1468
1469
/**
1470
 * Filtre `|choixsiegal{valeur, sioui, sinon}`
1471
 *
1472
 * @example
1473
 *     `#LANG_DIR|choixsiegal{ltr,left,right}` retourne `left` si
1474
 *      `#LANG_DIR` vaut `ltr` et `right` sinon.
1475
 *
1476
 * @filtre
1477
 * @link https://www.spip.net/4148
1478
 *
1479
 * @param mixed $a1
1480
 *     La valeur à tester
1481
 * @param mixed $a2
1482
 *     La valeur de comparaison
1483
 * @param mixed $v
1484
 *     Ce qui est retourné si la comparaison est vraie
1485
 * @param mixed $f
1486
 *     Ce qui est retourné sinon
1487
 * @return mixed
1488
 **/
1489
function choixsiegal($a1, $a2, $v, $f) {
1490
	return ($a1 == $a2) ? $v : $f;
1491
}
1492
1493
//
1494
// Export iCal
1495
//
1496
1497
/**
1498
 * Adapte un texte pour être inséré dans une valeur d'un export ICAL
1499
 *
1500
 * Passe le texte en utf8, enlève les sauts de lignes et échappe les virgules.
1501
 *
1502
 * @example `SUMMARY:[(#TITRE|filtrer_ical)]`
1503
 * @filtre
1504
 *
1505
 * @param string $texte
1506
 * @return string
1507
 **/
1508
function filtrer_ical($texte) {
1509
	#include_spip('inc/charsets');
1510
	$texte = html2unicode($texte);
1511
	$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...
1512
	$texte = preg_replace("/\n/", " ", $texte);
1513
	$texte = preg_replace("/,/", "\,", $texte);
1514
1515
	return $texte;
1516
}
1517
1518
1519
/**
1520
 * Transforme les sauts de ligne simples en sauts forcés avec `_ `
1521
 *
1522
 * Ne modifie pas les sauts de paragraphe (2 sauts consécutifs au moins),
1523
 * ou les retours à l'intérieur de modèles ou de certaines balises html.
1524
 *
1525
 * @note
1526
 *     Cette fonction pouvait être utilisée pour forcer les alinéas,
1527
 *     (retours à la ligne sans saut de paragraphe), mais ce traitement
1528
 *     est maintenant automatique.
1529
 *     Cf. plugin Textwheel et la constante _AUTOBR
1530
 *
1531
 * @uses echappe_html()
1532
 * @uses echappe_retour()
1533
 *
1534
 * @param string $texte
1535
 * @param string $delim
1536
 *      Ce par quoi sont remplacés les sauts
1537
 * @return string
1538
 **/
1539
function post_autobr($texte, $delim = "\n_ ") {
1540
	if (!function_exists('echappe_html')) {
1541
		include_spip('inc/texte_mini');
1542
	}
1543
	$texte = str_replace("\r\n", "\r", $texte);
1544
	$texte = str_replace("\r", "\n", $texte);
1545
1546 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...
1547
		$texte = substr($texte, 0, -strlen($fin = $fin[0]));
1548
	} else {
1549
		$fin = '';
1550
	}
1551
1552
	$texte = echappe_html($texte, '', true);
1553
1554
	// echapper les modeles
1555
	if (strpos($texte, "<") !== false) {
1556
		include_spip('inc/lien');
1557
		if (defined('_PREG_MODELE')) {
1558
			$preg_modeles = "@" . _PREG_MODELE . "@imsS";
1559
			$texte = echappe_html($texte, '', true, $preg_modeles);
1560
		}
1561
	}
1562
1563
	$debut = '';
1564
	$suite = $texte;
1565
	while ($t = strpos('-' . $suite, "\n", 1)) {
1566
		$debut .= substr($suite, 0, $t - 1);
1567
		$suite = substr($suite, $t);
1568
		$car = substr($suite, 0, 1);
1569
		if (($car <> '-') and ($car <> '_') and ($car <> "\n") and ($car <> "|") and ($car <> "}")
1570
			and !preg_match(',^\s*(\n|</?(quote|div|dl|dt|dd)|$),S', ($suite))
1571
			and !preg_match(',</?(quote|div|dl|dt|dd)> *$,iS', $debut)
1572
		) {
1573
			$debut .= $delim;
1574
		} else {
1575
			$debut .= "\n";
1576
		}
1577 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...
1578
			$debut .= $regs[0];
1579
			$suite = substr($suite, strlen($regs[0]));
1580
		}
1581
	}
1582
	$texte = $debut . $suite;
1583
1584
	$texte = echappe_retour($texte);
1585
1586
	return $texte . $fin;
1587
}
1588
1589
1590
/**
1591
 * Expression régulière pour obtenir le contenu des extraits idiomes `<:module:cle:>`
1592
 *
1593
 * @var string
1594
 */
1595
define('_EXTRAIRE_IDIOME', '@<:(?:([a-z0-9_]+):)?([a-z0-9_]+):>@isS');
1596
1597
/**
1598
 * Extrait une langue des extraits idiomes (`<:module:cle_de_langue:>`)
1599
 *
1600
 * Retrouve les balises `<:cle_de_langue:>` d'un texte et remplace son contenu
1601
 * par l'extrait correspondant à la langue demandée (si possible), sinon dans la
1602
 * langue par défaut du site.
1603
 *
1604
 * Ne pas mettre de span@lang=fr si on est déjà en fr.
1605
 *
1606
 * @filtre
1607
 * @uses inc_traduire_dist()
1608
 * @uses code_echappement()
1609
 * @uses echappe_retour()
1610
 *
1611
 * @param string $letexte
1612
 * @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...
1613
 *     Langue à retrouver (si vide, utilise la langue en cours).
1614
 * @param array $options Options {
1615
 * @type bool $echappe_span
1616
 *         True pour échapper les balises span (false par défaut)
1617
 * @type string $lang_defaut
1618
 *         Code de langue : permet de définir la langue utilisée par défaut,
1619
 *         en cas d'absence de traduction dans la langue demandée.
1620
 *         Par défaut la langue du site.
1621
 *         Indiquer 'aucune' pour ne pas retourner de texte si la langue
1622
 *         exacte n'a pas été trouvée.
1623
 * }
1624
 * @return string
1625
 **/
1626
function extraire_idiome($letexte, $lang = null, $options = array()) {
1627
	static $traduire = false;
1628
	if ($letexte
1629
		and preg_match_all(_EXTRAIRE_IDIOME, $letexte, $regs, PREG_SET_ORDER)
1630
	) {
1631
		if (!$traduire) {
1632
			$traduire = charger_fonction('traduire', 'inc');
1633
			include_spip('inc/lang');
1634
		}
1635
		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...
1636
			$lang = $GLOBALS['spip_lang'];
1637
		}
1638
		// Compatibilité avec le prototype de fonction précédente qui utilisait un boolean
1639
		if (is_bool($options)) {
1640
			$options = array('echappe_span' => $options);
1641
		}
1642 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...
1643
			$options = array_merge($options, array('echappe_span' => false));
1644
		}
1645
1646
		foreach ($regs as $reg) {
1647
			$cle = ($reg[1] ? $reg[1] . ':' : '') . $reg[2];
1648
			$desc = $traduire($cle, $lang, true);
1649
			$l = $desc->langue;
1650
			// si pas de traduction, on laissera l'écriture de l'idiome entier dans le texte.
1651
			if (strlen($desc->texte)) {
1652
				$trad = code_echappement($desc->texte, 'idiome', false);
1653
				if ($l !== $lang) {
1654
					$trad = str_replace("'", '"', inserer_attribut($trad, 'lang', $l));
1655
				}
1656 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...
1657
					$trad = str_replace("'", '"', inserer_attribut($trad, 'dir', lang_dir($l)));
1658
				}
1659
				if (!$options['echappe_span']) {
1660
					$trad = echappe_retour($trad, 'idiome');
1661
				}
1662
				$letexte = str_replace($reg[0], $trad, $letexte);
1663
			}
1664
		}
1665
	}
1666
	return $letexte;
1667
}
1668
1669
/**
1670
 * Expression régulière pour obtenir le contenu des extraits polyglottes `<multi>`
1671
 *
1672
 * @var string
1673
 */
1674
define('_EXTRAIRE_MULTI', "@<multi>(.*?)</multi>@sS");
1675
1676
1677
/**
1678
 * Extrait une langue des extraits polyglottes (`<multi>`)
1679
 *
1680
 * Retrouve les balises `<multi>` d'un texte et remplace son contenu
1681
 * par l'extrait correspondant à la langue demandée.
1682
 *
1683
 * Si la langue demandée n'est pas trouvée dans le multi, ni une langue
1684
 * approchante (exemple `fr` si on demande `fr_TU`), on retourne l'extrait
1685
 * correspondant à la langue par défaut (option 'lang_defaut'), qui est
1686
 * par défaut la langue du site. Et si l'extrait n'existe toujours pas
1687
 * dans cette langue, ça utilisera la première langue utilisée
1688
 * dans la balise `<multi>`.
1689
 *
1690
 * Ne pas mettre de span@lang=fr si on est déjà en fr.
1691
 *
1692
 * @filtre
1693
 * @link https://www.spip.net/5332
1694
 *
1695
 * @uses extraire_trads()
1696
 * @uses approcher_langue()
1697
 * @uses lang_typo()
1698
 * @uses code_echappement()
1699
 * @uses echappe_retour()
1700
 *
1701
 * @param string $letexte
1702
 * @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...
1703
 *     Langue à retrouver (si vide, utilise la langue en cours).
1704
 * @param array $options Options {
1705
 * @type bool $echappe_span
1706
 *         True pour échapper les balises span (false par défaut)
1707
 * @type string $lang_defaut
1708
 *         Code de langue : permet de définir la langue utilisée par défaut,
1709
 *         en cas d'absence de traduction dans la langue demandée.
1710
 *         Par défaut la langue du site.
1711
 *         Indiquer 'aucune' pour ne pas retourner de texte si la langue
1712
 *         exacte n'a pas été trouvée.
1713
 * }
1714
 * @return string
1715
 **/
1716
function extraire_multi($letexte, $lang = null, $options = array()) {
1717
1718
	if ($letexte
1719
		and preg_match_all(_EXTRAIRE_MULTI, $letexte, $regs, PREG_SET_ORDER)
1720
	) {
1721
		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...
1722
			$lang = $GLOBALS['spip_lang'];
1723
		}
1724
1725
		// Compatibilité avec le prototype de fonction précédente qui utilisait un boolean
1726
		if (is_bool($options)) {
1727
			$options = array('echappe_span' => $options, 'lang_defaut' => _LANGUE_PAR_DEFAUT);
1728
		}
1729 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...
1730
			$options = array_merge($options, array('echappe_span' => false));
1731
		}
1732
		if (!isset($options['lang_defaut'])) {
1733
			$options = array_merge($options, array('lang_defaut' => _LANGUE_PAR_DEFAUT));
1734
		}
1735
1736
		include_spip('inc/lang');
1737
		foreach ($regs as $reg) {
1738
			// chercher la version de la langue courante
1739
			$trads = extraire_trads($reg[1]);
1740
			if ($l = approcher_langue($trads, $lang)) {
1741
				$trad = $trads[$l];
1742
			} else {
1743
				if ($options['lang_defaut'] == 'aucune') {
1744
					$trad = '';
1745
				} else {
1746
					// langue absente, prendre le fr ou une langue précisée (meme comportement que inc/traduire.php)
1747
					// ou la premiere dispo
1748
					// mais typographier le texte selon les regles de celle-ci
1749
					// Attention aux blocs multi sur plusieurs lignes
1750
					if (!$l = approcher_langue($trads, $options['lang_defaut'])) {
1751
						$l = key($trads);
1752
					}
1753
					$trad = $trads[$l];
1754
					$typographie = charger_fonction(lang_typo($l), 'typographie');
1755
					$trad = $typographie($trad);
1756
					// Tester si on echappe en span ou en div
1757
					// il ne faut pas echapper en div si propre produit un seul paragraphe
1758
					include_spip('inc/texte');
1759
					$trad_propre = preg_replace(",(^<p[^>]*>|</p>$),Uims", "", propre($trad));
1760
					$mode = preg_match(',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS', $trad_propre) ? 'div' : 'span';
1761
					if ($mode === 'div') {
1762
						$trad = rtrim($trad) . "\n\n";
1763
					}
1764
					$trad = code_echappement($trad, 'multi', false, $mode);
1765
					$trad = str_replace("'", '"', inserer_attribut($trad, 'lang', $l));
1766 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...
1767
						$trad = str_replace("'", '"', inserer_attribut($trad, 'dir', lang_dir($l)));
1768
					}
1769
					if (!$options['echappe_span']) {
1770
						$trad = echappe_retour($trad, 'multi');
1771
					}
1772
				}
1773
			}
1774
			$letexte = str_replace($reg[0], $trad, $letexte);
1775
		}
1776
	}
1777
1778
	return $letexte;
1779
}
1780
1781
/**
1782
 * Convertit le contenu d'une balise `<multi>` en un tableau
1783
 *
1784
 * Exemple de blocs.
1785
 * - `texte par défaut [fr] en français [en] en anglais`
1786
 * - `[fr] en français [en] en anglais`
1787
 *
1788
 * @param string $bloc
1789
 *     Le contenu intérieur d'un bloc multi
1790
 * @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...
1791
 *     Peut retourner un code de langue vide, lorsqu'un texte par défaut est indiqué.
1792
 **/
1793
function extraire_trads($bloc) {
1794
	$lang = '';
1795
// ce reg fait planter l'analyse multi s'il y a de l'{italique} dans le champ
1796
//	while (preg_match("/^(.*?)[{\[]([a-z_]+)[}\]]/siS", $bloc, $regs)) {
1797
	while (preg_match("/^(.*?)[\[]([a-z_]+)[\]]/siS", $bloc, $regs)) {
1798
		$texte = trim($regs[1]);
1799
		if ($texte or $lang) {
1800
			$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...
1801
		}
1802
		$bloc = substr($bloc, strlen($regs[0]));
1803
		$lang = $regs[2];
1804
	}
1805
	$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...
1806
1807
	return $trads;
1808
}
1809
1810
1811
/**
1812
 * Calculer l'initiale d'un nom
1813
 *
1814
 * @param string $nom
1815
 * @return string L'initiale en majuscule
1816
 */
1817
function filtre_initiale($nom) {
1818
	return spip_substr(trim(strtoupper(extraire_multi($nom))), 0, 1);
1819
}
1820
1821
1822
/**
1823
 * Retourne la donnée si c'est la première fois qu'il la voit
1824
 *
1825
 * Il est possible de gérer différentes "familles" de données avec
1826
 * le second paramètre.
1827
 *
1828
 * @filtre
1829
 * @link https://www.spip.net/4320
1830
 * @example
1831
 *     ```
1832
 *     [(#ID_SECTEUR|unique)]
1833
 *     [(#ID_SECTEUR|unique{tete})] n'a pas d'incidence sur
1834
 *     [(#ID_SECTEUR|unique{pied})]
1835
 *     [(#ID_SECTEUR|unique{pied,1})] affiche le nombre d'éléments.
1836
 *     Préférer totefois #TOTAL_UNIQUE{pied}
1837
 *     ```
1838
 *
1839
 * @todo
1840
 *    Ameliorations possibles :
1841
 *
1842
 *    1) si la donnée est grosse, mettre son md5 comme clé
1843
 *    2) purger $mem quand on change de squelette (sinon bug inclusions)
1844
 *
1845
 * @param string $donnee
1846
 *      Donnée que l'on souhaite unique
1847
 * @param string $famille
1848
 *      Famille de stockage (1 unique donnée par famille)
1849
 *
1850
 *      - _spip_raz_ : (interne) Vide la pile de mémoire et la retourne
1851
 *      - _spip_set_ : (interne) Affecte la pile de mémoire avec la donnée
1852
 * @param bool $cpt
1853
 *      True pour obtenir le nombre d'éléments différents stockés
1854
 * @return string|int|array|null|void
1855
 *
1856
 *      - string : Donnée si c'est la première fois qu'elle est vue
1857
 *      - void : si la donnée a déjà été vue
1858
 *      - int : si l'on demande le nombre d'éléments
1859
 *      - array (interne) : si on dépile
1860
 *      - null (interne) : si on empile
1861
 **/
1862
function unique($donnee, $famille = '', $cpt = false) {
1863
	static $mem = array();
1864
	// permettre de vider la pile et de la restaurer
1865
	// pour le calcul de introduction...
1866
	if ($famille == '_spip_raz_') {
1867
		$tmp = $mem;
1868
		$mem = array();
1869
1870
		return $tmp;
1871
	} elseif ($famille == '_spip_set_') {
1872
		$mem = $donnee;
1873
1874
		return;
1875
	}
1876
	// eviter une notice
1877
	if (!isset($mem[$famille])) {
1878
		$mem[$famille] = array();
1879
	}
1880
	if ($cpt) {
1881
		return count($mem[$famille]);
1882
	}
1883
	// eviter une notice
1884
	if (!isset($mem[$famille][$donnee])) {
1885
		$mem[$famille][$donnee] = 0;
1886
	}
1887
	if (!($mem[$famille][$donnee]++)) {
1888
		return $donnee;
1889
	}
1890
}
1891
1892
1893
/**
1894
 * Filtre qui alterne des valeurs en fonction d'un compteur
1895
 *
1896
 * Affiche à tour de rôle et dans l'ordre, un des arguments transmis
1897
 * à chaque incrément du compteur.
1898
 *
1899
 * S'il n'y a qu'un seul argument, et que c'est un tableau,
1900
 * l'alternance se fait sur les valeurs du tableau.
1901
 *
1902
 * Souvent appliqué à l'intérieur d'une boucle, avec le compteur `#COMPTEUR_BOUCLE`
1903
 *
1904
 * @example
1905
 *     - `[(#COMPTEUR_BOUCLE|alterner{bleu,vert,rouge})]`
1906
 *     - `[(#COMPTEUR_BOUCLE|alterner{#LISTE{bleu,vert,rouge}})]`
1907
 *
1908
 * @filtre
1909
 * @link https://www.spip.net/4145
1910
 *
1911
 * @param int $i
1912
 *     Le compteur
1913
 * @return mixed
1914
 *     Une des valeurs en fonction du compteur.
1915
 **/
1916
function alterner($i) {
1917
	// recuperer les arguments (attention fonctions un peu space)
1918
	$num = func_num_args();
1919
	$args = func_get_args();
1920
1921
	if ($num == 2 && is_array($args[1])) {
1922
		$args = $args[1];
1923
		array_unshift($args, '');
1924
		$num = count($args);
1925
	}
1926
1927
	// renvoyer le i-ieme argument, modulo le nombre d'arguments
1928
	return $args[(intval($i) - 1) % ($num - 1) + 1];
1929
}
1930
1931
1932
/**
1933
 * Récupérer un attribut d'une balise HTML
1934
 *
1935
 * la regexp est mortelle : cf. `tests/unit/filtres/extraire_attribut.php`
1936
 * Si on a passé un tableau de balises, renvoyer un tableau de résultats
1937
 * (dans ce cas l'option `$complet` n'est pas disponible)
1938
 *
1939
 * @param string|array $balise
1940
 *     Texte ou liste de textes dont on veut extraire des balises
1941
 * @param string $attribut
1942
 *     Nom de l'attribut désiré
1943
 * @param bool $complet
1944
 *     True pour retourner un tableau avec
1945
 *     - le texte de la balise
1946
 *     - l'ensemble des résultats de la regexp ($r)
1947
 * @return string|array
1948
 *     - Texte de l'attribut retourné, ou tableau des texte d'attributs
1949
 *       (si 1er argument tableau)
1950
 *     - Tableau complet (si 2e argument)
1951
 **/
1952
function extraire_attribut($balise, $attribut, $complet = false) {
1953
	if (is_array($balise)) {
1954
		array_walk(
1955
			$balise,
1956
			function(&$a, $key, $t){
1957
				$a = extraire_attribut($a, $t);
1958
			},
1959
			$attribut
1960
		);
1961
1962
		return $balise;
1963
	}
1964
	if (preg_match(
1965
		',(^.*?<(?:(?>\s*)(?>[\w:.-]+)(?>(?:=(?:"[^"]*"|\'[^\']*\'|[^\'"]\S*))?))*?)(\s+'
1966
		. $attribut
1967
		. '(?:=\s*("[^"]*"|\'[^\']*\'|[^\'"]\S*))?)()((?:[\s/][^>]*)?>.*),isS',
1968
1969
		$balise, $r)) {
1970
		if (isset($r[3][0]) and ($r[3][0] == '"' || $r[3][0] == "'")) {
1971
			$r[4] = substr($r[3], 1, -1);
1972
			$r[3] = $r[3][0];
1973
		} elseif ($r[3] !== '') {
1974
			$r[4] = $r[3];
1975
			$r[3] = '';
1976
		} else {
1977
			$r[4] = trim($r[2]);
1978
		}
1979
		$att = $r[4];
1980 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...
1981
			$att = str_replace(array("&#039;", "&#39;", "&#034;", "&#34;"), array("'", "'", '"', '"'), $att);
1982
		}
1983
		$att = filtrer_entites($att);
1984
	} else {
1985
		$att = null;
1986
	}
1987
1988
	if ($complet) {
1989
		return array($att, $r);
1990
	} else {
1991
		return $att;
1992
	}
1993
}
1994
1995
/**
1996
 * Insérer (ou modifier) un attribut html dans une balise
1997
 *
1998
 * @example
1999
 *     - `[(#LOGO_ARTICLE|inserer_attribut{class, logo article})]`
2000
 *     - `[(#LOGO_ARTICLE|inserer_attribut{alt, #TTTRE|attribut_html|couper{60}})]`
2001
 *     - `[(#FICHIER|image_reduire{40}|inserer_attribut{data-description, #DESCRIPTIF})]`
2002
 *       Laissera les balises HTML de la valeur (ici `#DESCRIPTIF`) si on n'applique pas le
2003
 *       filtre `attribut_html` dessus.
2004
 *
2005
 * @filtre
2006
 * @link https://www.spip.net/4294
2007
 * @uses attribut_html()
2008
 * @uses extraire_attribut()
2009
 *
2010
 * @param string $balise
2011
 *     Code html de la balise (ou contenant une balise)
2012
 * @param string $attribut
2013
 *     Nom de l'attribut html à modifier
2014
 * @param string $val
2015
 *     Valeur de l'attribut à appliquer
2016
 * @param bool $proteger
2017
 *     Prépare la valeur en tant qu'attribut de balise (mais conserve les balises html).
2018
 * @param bool $vider
2019
 *     True pour vider l'attribut. Une chaîne vide pour `$val` fera pareil.
2020
 * @return string
2021
 *     Code html modifié
2022
 **/
2023
function inserer_attribut($balise, $attribut, $val, $proteger = true, $vider = false) {
2024
	// preparer l'attribut
2025
	// supprimer les &nbsp; etc mais pas les balises html
2026
	// qui ont un sens dans un attribut value d'un input
2027
	if ($proteger) {
2028
		$val = attribut_html($val, false);
2029
	}
2030
2031
	// echapper les ' pour eviter tout bug
2032
	$val = str_replace("'", "&#039;", $val);
2033
	if ($vider and strlen($val) == 0) {
2034
		$insert = '';
2035
	} else {
2036
		$insert = " $attribut='$val'";
2037
	}
2038
2039
	list($old, $r) = extraire_attribut($balise, $attribut, true);
2040
2041
	if ($old !== null) {
2042
		// Remplacer l'ancien attribut du meme nom
2043
		$balise = $r[1] . $insert . $r[5];
2044
	} else {
2045
		// preferer une balise " />" (comme <img />)
2046
		if (preg_match(',/>,', $balise)) {
2047
			$balise = preg_replace(",\s?/>,S", $insert . " />", $balise, 1);
2048
		} // sinon une balise <a ...> ... </a>
2049
		else {
2050
			$balise = preg_replace(",\s?>,S", $insert . ">", $balise, 1);
2051
		}
2052
	}
2053
2054
	return $balise;
2055
}
2056
2057
/**
2058
 * Supprime un attribut HTML
2059
 *
2060
 * @example `[(#LOGO_ARTICLE|vider_attribut{class})]`
2061
 *
2062
 * @filtre
2063
 * @link https://www.spip.net/4142
2064
 * @uses inserer_attribut()
2065
 * @see  extraire_attribut()
2066
 *
2067
 * @param string $balise Code HTML de l'élément
2068
 * @param string $attribut Nom de l'attribut à enlever
2069
 * @return string Code HTML sans l'attribut
2070
 **/
2071
function vider_attribut($balise, $attribut) {
2072
	return inserer_attribut($balise, $attribut, '', false, true);
2073
}
2074
2075
/**
2076
 * Fonction support pour les filtres |ajouter_class |supprimer_class |commuter_class
2077
 *
2078
 * @param string $balise
2079
 * @param string|array $class
2080
 * @param string $operation
2081
 * @return string
2082
 */
2083
function modifier_class($balise, $class, $operation='ajouter') {
2084
	if (is_string($class)) {
2085
		$class = explode(' ', trim($class));
2086
	}
2087
	$class = array_filter($class);
2088
2089
	// si la ou les classes ont des caracteres invalides on ne fait rien
2090
	if (preg_match(",[^\w-],", implode('', $class))) {
2091
		return $balise;
2092
	}
2093
2094
	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...
2095
		$class = array_unique($class);
2096
		$class_courante = extraire_attribut($balise, 'class');
2097
2098
		$class_new = $class_courante;
2099
		foreach ($class as $c) {
2100
			if ($c) {
2101
				$is_class_presente = false;
2102
				if (strpos($class_courante, $c) !== false
2103
					and preg_match("/(^|\s)".preg_quote($c)."($|\s)/", $class_courante)) {
2104
					$is_class_presente = true;
2105
				}
2106
				if (in_array($operation, ['ajouter', 'commuter'])
2107
					and !$is_class_presente) {
2108
					$class_new = rtrim($class_new) . " " . $c;
2109
				}
2110
				elseif (in_array($operation, ['supprimer', 'commuter'])
2111
					and $is_class_presente) {
2112
					$class_new = trim(preg_replace("/(^|\s)".preg_quote($c)."($|\s)/", "\\1", $class_new));
2113
				}
2114
			}
2115
		}
2116
2117
		if ($class_new !== $class_courante) {
2118
			if (strlen($class_new)) {
2119
				$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 2098 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...
2120
			}
2121
			elseif ($class_courante) {
2122
				$balise = vider_attribut($balise, 'class');
2123
			}
2124
		}
2125
	}
2126
2127
	return $balise;
2128
}
2129
2130
/**
2131
 * Ajoute une ou plusieurs classes sur une balise (si pas deja presentes)
2132
 * @param string $balise
2133
 * @param string|array $class
2134
 * @return string
2135
 */
2136
function ajouter_class($balise, $class){
2137
	return modifier_class($balise, $class, 'ajouter');
2138
}
2139
2140
/**
2141
 * Supprime une ou plusieurs classes sur une balise (si presentes)
2142
 * @param string $balise
2143
 * @param string|array $class
2144
 * @return string
2145
 */
2146
function supprimer_class($balise, $class){
2147
	return modifier_class($balise, $class, 'supprimer');
2148
}
2149
2150
/**
2151
 * Bascule une ou plusieurs classes sur une balise : ajoutees si absentes, supprimees si presentes
2152
 *
2153
 * @param string $balise
2154
 * @param string|array $class
2155
 * @return string
2156
 */
2157
function commuter_class($balise, $class){
2158
	return modifier_class($balise, $class, 'commuter');
2159
}
2160
2161
/**
2162
 * Un filtre pour déterminer le nom du statut des inscrits
2163
 *
2164
 * @param void|int $id
2165
 * @param string $mode
2166
 * @return string
2167
 */
2168
function tester_config($id, $mode = '') {
2169
	include_spip('action/inscrire_auteur');
2170
2171
	return tester_statut_inscription($mode, $id);
2172
}
2173
2174
//
2175
// Quelques fonctions de calcul arithmetique
2176
//
2177
function floatstr($a) { return str_replace(',','.',(string)floatval($a)); }
2178
function strize($f, $a, $b) { return floatstr($f(floatstr($a),floatstr($b))); }
2179
2180
/**
2181
 * Additionne 2 nombres
2182
 *
2183
 * @filtre
2184
 * @link https://www.spip.net/4307
2185
 * @see moins()
2186
 * @example
2187
 *     ```
2188
 *     [(#VAL{28}|plus{14})]
2189
 *     ```
2190
 *
2191
 * @param int $a
2192
 * @param int $b
2193
 * @return int $a+$b
2194
 **/
2195
function plus($a, $b) {
2196
	return $a + $b;
2197
}
2198
function strplus($a, $b) {return strize('plus', $a, $b);}
2199
/**
2200
 * Soustrait 2 nombres
2201
 *
2202
 * @filtre
2203
 * @link https://www.spip.net/4302
2204
 * @see plus()
2205
 * @example
2206
 *     ```
2207
 *     [(#VAL{28}|moins{14})]
2208
 *     ```
2209
 *
2210
 * @param int $a
2211
 * @param int $b
2212
 * @return int $a-$b
2213
 **/
2214
function moins($a, $b) {
2215
	return $a - $b;
2216
}
2217
function strmoins($a, $b) {return strize('moins', $a, $b);}
2218
2219
/**
2220
 * Multiplie 2 nombres
2221
 *
2222
 * @filtre
2223
 * @link https://www.spip.net/4304
2224
 * @see div()
2225
 * @see modulo()
2226
 * @example
2227
 *     ```
2228
 *     [(#VAL{28}|mult{14})]
2229
 *     ```
2230
 *
2231
 * @param int $a
2232
 * @param int $b
2233
 * @return int $a*$b
2234
 **/
2235
function mult($a, $b) {
2236
	return $a * $b;
2237
}
2238
function strmult($a, $b) {return strize('mult', $a, $b);}
2239
2240
/**
2241
 * Divise 2 nombres
2242
 *
2243
 * @filtre
2244
 * @link https://www.spip.net/4279
2245
 * @see mult()
2246
 * @see modulo()
2247
 * @example
2248
 *     ```
2249
 *     [(#VAL{28}|div{14})]
2250
 *     ```
2251
 *
2252
 * @param int $a
2253
 * @param int $b
2254
 * @return int $a/$b (ou 0 si $b est nul)
2255
 **/
2256
function div($a, $b) {
2257
	return $b ? $a / $b : 0;
2258
}
2259
function strdiv($a, $b) {return strize('div', $a, $b);}
2260
2261
/**
2262
 * Retourne le modulo 2 nombres
2263
 *
2264
 * @filtre
2265
 * @link https://www.spip.net/4301
2266
 * @see mult()
2267
 * @see div()
2268
 * @example
2269
 *     ```
2270
 *     [(#VAL{28}|modulo{14})]
2271
 *     ```
2272
 *
2273
 * @param int $nb
2274
 * @param int $mod
2275
 * @param int $add
2276
 * @return int ($nb % $mod) + $add
2277
 **/
2278
function modulo($nb, $mod, $add = 0) {
2279
	return ($mod ? $nb % $mod : 0) + $add;
2280
}
2281
2282
2283
/**
2284
 * Vérifie qu'un nom (d'auteur) ne comporte pas d'autres tags que <multi>
2285
 * et ceux volontairement spécifiés dans la constante
2286
 *
2287
 * @param string $nom
2288
 *      Nom (signature) proposé
2289
 * @return bool
2290
 *      - false si pas conforme,
2291
 *      - true sinon
2292
 **/
2293
function nom_acceptable($nom) {
2294
	if (!is_string($nom)) {
2295
		return false;
2296
	}
2297
	if (!defined('_TAGS_NOM_AUTEUR')) {
2298
		define('_TAGS_NOM_AUTEUR', '');
2299
	}
2300
	$tags_acceptes = array_unique(explode(',', 'multi,' . _TAGS_NOM_AUTEUR));
2301
	foreach ($tags_acceptes as $tag) {
2302
		if (strlen($tag)) {
2303
			$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...
2304
			$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...
2305
			$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...
2306
			$remp2[] = '\x60/' . trim($tag) . '\x61';
2307
		}
2308
	}
2309
	$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...
2310
2311
	return str_replace('&lt;', '<', $v_nom) == $nom;
2312
}
2313
2314
2315
/**
2316
 * Vérifier la conformité d'une ou plusieurs adresses email (suivant RFC 822)
2317
 *
2318
 * @param string|array $adresses
2319
 *      Adresse ou liste d'adresse
2320
 *      si on fournit un tableau, il est filtre et la fonction renvoie avec uniquement les adresses email valides (donc possiblement vide)
2321
 * @return bool|string|array
2322
 *      - false si une des adresses n'est pas conforme,
2323
 *      - la normalisation de la dernière adresse donnée sinon
2324
 *      - renvoie un tableau si l'entree est un tableau
2325
 **/
2326
function email_valide($adresses) {
2327
	if (is_array($adresses)) {
2328
		$adresses = array_map('email_valide', $adresses);
2329
		$adresses = array_filter($adresses);
2330
		return $adresses;
2331
	}
2332
2333
	$email_valide = charger_fonction('email_valide', 'inc');
2334
	return $email_valide($adresses);
2335
}
2336
2337
/**
2338
 * Permet d'afficher un symbole à côté des liens pointant vers les
2339
 * documents attachés d'un article (liens ayant `rel=enclosure`).
2340
 *
2341
 * @filtre
2342
 * @link https://www.spip.net/4134
2343
 *
2344
 * @param string $tags Texte
2345
 * @return string Texte
2346
 **/
2347
function afficher_enclosures($tags) {
2348
	$s = array();
2349
	foreach (extraire_balises($tags, 'a') as $tag) {
2350
		if (extraire_attribut($tag, 'rel') == 'enclosure'
2351
			and $t = extraire_attribut($tag, 'href')
2352
		) {
2353
			$s[] = preg_replace(',>[^<]+</a>,S',
2354
				'>'
2355
				. 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 2351 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...
2356
					'title="' . attribut_html($t) . '"')
0 ignored issues
show
Bug introduced by
It seems like $t defined by extraire_attribut($tag, 'href') on line 2351 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...
2357
				. '</a>', $tag);
2358
		}
2359
	}
2360
2361
	return join('&nbsp;', $s);
2362
}
2363
2364
/**
2365
 * Filtre des liens HTML `<a>` selon la valeur de leur attribut `rel`
2366
 * et ne retourne que ceux là.
2367
 *
2368
 * @filtre
2369
 * @link https://www.spip.net/4187
2370
 *
2371
 * @param string $tags Texte
2372
 * @param string $rels Attribut `rel` à capturer (ou plusieurs séparés par des virgules)
2373
 * @return string Liens trouvés
2374
 **/
2375
function afficher_tags($tags, $rels = 'tag,directory') {
2376
	$s = array();
2377
	foreach (extraire_balises($tags, 'a') as $tag) {
2378
		$rel = extraire_attribut($tag, 'rel');
2379
		if (strstr(",$rels,", ",$rel,")) {
2380
			$s[] = $tag;
2381
		}
2382
	}
2383
2384
	return join(', ', $s);
2385
}
2386
2387
2388
/**
2389
 * Convertir les médias fournis par un flux RSS (podcasts)
2390
 * en liens conformes aux microformats
2391
 *
2392
 * Passe un `<enclosure url="fichier" length="5588242" type="audio/mpeg"/>`
2393
 * au format microformat `<a rel="enclosure" href="fichier" ...>fichier</a>`.
2394
 *
2395
 * Peut recevoir un `<link` ou un `<media:content` parfois.
2396
 *
2397
 * Attention : `length="zz"` devient `title="zz"`, pour rester conforme.
2398
 *
2399
 * @filtre
2400
 * @see microformat2enclosure() Pour l'inverse
2401
 *
2402
 * @param string $e Tag RSS `<enclosure>`
2403
 * @return string Tag HTML `<a>` avec microformat.
2404
 **/
2405
function enclosure2microformat($e) {
2406
	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...
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
	}
2409
	$type = extraire_attribut($e, 'type');
2410
	if (!$length = extraire_attribut($e, 'length')) {
2411
		# <media:content : longeur dans fileSize. On tente.
2412
		$length = extraire_attribut($e, 'fileSize');
2413
	}
2414
	$fichier = basename($url);
2415
2416
	return '<a rel="enclosure"'
2417
	. ($url ? ' href="' . spip_htmlspecialchars($url) . '"' : '')
2418
	. ($type ? ' type="' . spip_htmlspecialchars($type) . '"' : '')
0 ignored issues
show
Bug introduced by
It seems like $type defined by extraire_attribut($e, 'type') on line 2409 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...
2419
	. ($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...
2420
	. '>' . $fichier . '</a>';
2421
}
2422
2423
/**
2424
 * Convertir les liens conformes aux microformats en médias pour flux RSS,
2425
 * par exemple pour les podcasts
2426
 *
2427
 * Passe un texte ayant des liens avec microformat
2428
 * `<a rel="enclosure" href="fichier" ...>fichier</a>`
2429
 * au format RSS `<enclosure url="fichier" ... />`.
2430
 *
2431
 * @filtre
2432
 * @see enclosure2microformat() Pour l'inverse
2433
 *
2434
 * @param string $tags Texte HTML ayant des tag `<a>` avec microformat
2435
 * @return string Tags RSS `<enclosure>`.
2436
 **/
2437
function microformat2enclosure($tags) {
2438
	$enclosures = array();
2439
	foreach (extraire_balises($tags, 'a') as $e) {
2440
		if (extraire_attribut($e, 'rel') == 'enclosure') {
2441
			$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...
2442
			$type = extraire_attribut($e, 'type');
2443
			if (!$length = intval(extraire_attribut($e, 'title'))) {
2444
				$length = intval(extraire_attribut($e, 'length'));
2445
			} # vieux data
2446
			$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...
2447
			$enclosures[] = '<enclosure'
2448
				. ($url ? ' url="' . spip_htmlspecialchars($url) . '"' : '')
2449
				. ($type ? ' type="' . spip_htmlspecialchars($type) . '"' : '')
0 ignored issues
show
Bug introduced by
It seems like $type defined by extraire_attribut($e, 'type') on line 2442 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...
2450
				. ($length ? ' length="' . $length . '"' : '')
2451
				. ' />';
2452
		}
2453
	}
2454
2455
	return join("\n", $enclosures);
2456
}
2457
2458
2459
/**
2460
 * Créer les éléments ATOM `<dc:subject>` à partir des tags
2461
 *
2462
 * Convertit les liens avec attribut `rel="tag"`
2463
 * en balise `<dc:subject></dc:subject>` pour les flux RSS au format Atom.
2464
 *
2465
 * @filtre
2466
 *
2467
 * @param string $tags Texte
2468
 * @return string Tags RSS Atom `<dc:subject>`.
2469
 **/
2470
function tags2dcsubject($tags) {
2471
	$subjects = '';
2472
	foreach (extraire_balises($tags, 'a') as $e) {
2473
		if (extraire_attribut($e, rel) == 'tag') {
2474
			$subjects .= '<dc:subject>'
2475
				. texte_backend(textebrut($e))
2476
				. '</dc:subject>' . "\n";
2477
		}
2478
	}
2479
2480
	return $subjects;
2481
}
2482
2483
/**
2484
 * Retourne la premiere balise html du type demandé
2485
 *
2486
 * Retourne le contenu d'une balise jusqu'à la première fermeture rencontrée
2487
 * du même type.
2488
 * Si on a passe un tableau de textes, retourne un tableau de resultats.
2489
 *
2490
 * @example `[(#DESCRIPTIF|extraire_balise{img})]`
2491
 *
2492
 * @filtre
2493
 * @link https://www.spip.net/4289
2494
 * @see extraire_balises()
2495
 * @note
2496
 *     Attention : les résultats peuvent être incohérents sur des balises imbricables,
2497
 *     tel que demander à extraire `div` dans le texte `<div> un <div> mot </div> absent </div>`,
2498
 *     ce qui retournerait `<div> un <div> mot </div>` donc.
2499
 *
2500
 * @param string|array $texte
2501
 *     Texte(s) dont on souhaite extraire une balise html
2502
 * @param string $tag
2503
 *     Nom de la balise html à extraire
2504
 * @return void|string|array
2505
 *     - Code html de la balise, sinon rien
2506
 *     - Tableau de résultats, si tableau en entrée.
2507
 **/
2508 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...
2509
	if (is_array($texte)) {
2510
		array_walk(
2511
			$texte,
2512
			function(&$a, $key, $t){
2513
				$a = extraire_balise($a, $t);
2514
			},
2515
			$tag
2516
		);
2517
2518
		return $texte;
2519
	}
2520
2521
	if (preg_match(
2522
		",<$tag\b[^>]*(/>|>.*</$tag\b[^>]*>|>),UimsS",
2523
		$texte, $regs)) {
2524
		return $regs[0];
2525
	}
2526
}
2527
2528
/**
2529
 * Extrait toutes les balises html du type demandé
2530
 *
2531
 * Retourne dans un tableau le contenu de chaque balise jusqu'à la première
2532
 * fermeture rencontrée du même type.
2533
 * Si on a passe un tableau de textes, retourne un tableau de resultats.
2534
 *
2535
 * @example `[(#TEXTE|extraire_balises{img}|implode{" - "})]`
2536
 *
2537
 * @filtre
2538
 * @link https://www.spip.net/5618
2539
 * @see extraire_balise()
2540
 * @note
2541
 *     Attention : les résultats peuvent être incohérents sur des balises imbricables,
2542
 *     tel que demander à extraire `div` dans un texte.
2543
 *
2544
 * @param string|array $texte
2545
 *     Texte(s) dont on souhaite extraire une balise html
2546
 * @param string $tag
2547
 *     Nom de la balise html à extraire
2548
 * @return array
2549
 *     - Liste des codes html des occurrences de la balise, sinon tableau vide
2550
 *     - Tableau de résultats, si tableau en entrée.
2551
 **/
2552 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...
2553
	if (is_array($texte)) {
2554
		array_walk(
2555
			$texte,
2556
			function(&$a, $key, $t){
2557
				$a = extraire_balises($a, $t);
2558
			},
2559
			$tag
2560
		);
2561
2562
		return $texte;
2563
	}
2564
2565
	if (preg_match_all(
2566
		",<${tag}\b[^>]*(/>|>.*</${tag}\b[^>]*>|>),UimsS",
2567
		$texte, $regs, PREG_PATTERN_ORDER)) {
2568
		return $regs[0];
2569
	} else {
2570
		return array();
2571
	}
2572
}
2573
2574
/**
2575
 * Indique si le premier argument est contenu dans le second
2576
 *
2577
 * Cette fonction est proche de `in_array()` en PHP avec comme principale
2578
 * différence qu'elle ne crée pas d'erreur si le second argument n'est pas
2579
 * un tableau (dans ce cas elle tentera de le désérialiser, et sinon retournera
2580
 * la valeur par défaut transmise).
2581
 *
2582
 * @example `[(#VAL{deux}|in_any{#LISTE{un,deux,trois}}|oui) ... ]`
2583
 *
2584
 * @filtre
2585
 * @see filtre_find() Assez proche, avec les arguments valeur et tableau inversés.
2586
 *
2587
 * @param string $val
2588
 *     Valeur à chercher dans le tableau
2589
 * @param array|string $vals
2590
 *     Tableau des valeurs. S'il ce n'est pas un tableau qui est transmis,
2591
 *     la fonction tente de la désérialiser.
2592
 * @param string $def
2593
 *     Valeur par défaut retournée si `$vals` n'est pas un tableau.
2594
 * @return string
2595
 *     - ' ' si la valeur cherchée est dans le tableau
2596
 *     - '' si la valeur n'est pas dans le tableau
2597
 *     - `$def` si on n'a pas transmis de tableau
2598
 **/
2599
function in_any($val, $vals, $def = '') {
2600
	if (!is_array($vals) and $v = unserialize($vals)) {
2601
		$vals = $v;
2602
	}
2603
2604
	return (!is_array($vals) ? $def : (in_array($val, $vals) ? ' ' : ''));
2605
}
2606
2607
2608
/**
2609
 * Retourne le résultat d'une expression mathématique simple
2610
 *
2611
 * N'accepte que les *, + et - (à ameliorer si on l'utilise vraiment).
2612
 *
2613
 * @filtre
2614
 * @example
2615
 *      ```
2616
 *      valeur_numerique("3*2") retourne 6
2617
 *      ```
2618
 *
2619
 * @param string $expr
2620
 *     Expression mathématique `nombre operateur nombre` comme `3*2`
2621
 * @return int
2622
 *     Résultat du calcul
2623
 **/
2624
function valeur_numerique($expr) {
2625
	$a = 0;
2626
	if (preg_match(',^[0-9]+(\s*[+*-]\s*[0-9]+)*$,S', trim($expr))) {
2627
		eval("\$a = $expr;");
2628
	}
2629
2630
	return intval($a);
2631
}
2632
2633
/**
2634
 * Retourne un calcul de règle de trois
2635
 *
2636
 * @filtre
2637
 * @example
2638
 *     ```
2639
 *     [(#VAL{6}|regledetrois{4,3})] retourne 8
2640
 *     ```
2641
 *
2642
 * @param int $a
2643
 * @param int $b
2644
 * @param int $c
2645
 * @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...
2646
 *      Retourne `$a*$b/$c`
2647
 **/
2648
function regledetrois($a, $b, $c) {
2649
	return round($a * $b / $c);
2650
}
2651
2652
2653
/**
2654
 * Crée des tags HTML input hidden pour chaque paramètre et valeur d'une URL
2655
 *
2656
 * Fournit la suite de Input-Hidden correspondant aux paramètres de
2657
 * l'URL donnée en argument, compatible avec les types_urls
2658
 *
2659
 * @filtre
2660
 * @link https://www.spip.net/4286
2661
 * @see balise_ACTION_FORMULAIRE()
2662
 *     Également pour transmettre les actions à un formulaire
2663
 * @example
2664
 *     ```
2665
 *     [(#ENV{action}|form_hidden)] dans un formulaire
2666
 *     ```
2667
 *
2668
 * @param string $action URL
2669
 * @return string Suite de champs input hidden
2670
 **/
2671
function form_hidden($action) {
2672
2673
	$contexte = array();
2674
	include_spip('inc/urls');
2675
	if ($p = urls_decoder_url($action, '')
2676
		and reset($p)
2677
	) {
2678
		$fond = array_shift($p);
2679
		if ($fond != '404') {
2680
			$contexte = array_shift($p);
2681
			$contexte['page'] = $fond;
2682
			$action = preg_replace('/([?]' . preg_quote($fond) . '[^&=]*[0-9]+)(&|$)/', '?&', $action);
2683
		}
2684
	}
2685
	// defaire ce qu'a injecte urls_decoder_url : a revoir en modifiant la signature de urls_decoder_url
2686
	if (defined('_DEFINIR_CONTEXTE_TYPE') and _DEFINIR_CONTEXTE_TYPE) {
2687
		unset($contexte['type']);
2688
	}
2689
	if (defined('_DEFINIR_CONTEXTE_TYPE_PAGE') and _DEFINIR_CONTEXTE_TYPE_PAGE) {
2690
		unset($contexte['type-page']);
2691
	}
2692
2693
	// on va remplir un tableau de valeurs en prenant bien soin de ne pas
2694
	// ecraser les elements de la forme mots[]=1&mots[]=2
2695
	$values = array();
2696
2697
	// d'abord avec celles de l'url
2698
	if (false !== ($p = strpos($action, '?'))) {
2699
		foreach (preg_split('/&(amp;)?/S', substr($action, $p + 1)) as $c) {
2700
			$c = explode('=', $c, 2);
2701
			$var = array_shift($c);
2702
			$val = array_shift($c);
2703
			if ($var) {
2704
				$val = rawurldecode($val);
2705
				$var = rawurldecode($var); // decoder les [] eventuels
2706 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...
2707
					$values[] = array($var, $val);
2708
				} else {
2709
					if (!isset($values[$var])) {
2710
						$values[$var] = array($var, $val);
2711
					}
2712
				}
2713
			}
2714
		}
2715
	}
2716
2717
	// ensuite avec celles du contexte, sans doublonner !
2718
	foreach ($contexte as $var => $val) {
2719 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...
2720
			$values[] = array($var, $val);
2721
		} else {
2722
			if (!isset($values[$var])) {
2723
				$values[$var] = array($var, $val);
2724
			}
2725
		}
2726
	}
2727
2728
	// puis on rassemble le tout
2729
	$hidden = array();
2730
	foreach ($values as $value) {
2731
		list($var, $val) = $value;
2732
		$hidden[] = '<input name="'
2733
			. entites_html($var)
2734
			. '"'
2735
			. (is_null($val)
2736
				? ''
2737
				: ' value="' . entites_html($val) . '"'
2738
			)
2739
			. ' type="hidden"' . "\n/>";
2740
	}
2741
2742
	return join("", $hidden);
2743
}
2744
2745
/**
2746
 * Calcule les bornes d'une pagination
2747
 *
2748
 * @filtre
2749
 *
2750
 * @param int $courante
2751
 *     Page courante
2752
 * @param int $nombre
2753
 *     Nombre de pages
2754
 * @param int $max
2755
 *     Nombre d'éléments par page
2756
 * @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...
2757
 *     Liste (première page, dernière page).
2758
 **/
2759
function filtre_bornes_pagination_dist($courante, $nombre, $max = 10) {
2760
	if ($max <= 0 or $max >= $nombre) {
2761
		return array(1, $nombre);
2762
	}
2763
2764
	$premiere = max(1, $courante - floor(($max - 1) / 2));
2765
	$derniere = min($nombre, $premiere + $max - 2);
2766
	$premiere = $derniere == $nombre ? $derniere - $max + 1 : $premiere;
2767
2768
	return array($premiere, $derniere);
2769
}
2770
2771
2772
/**
2773
 * Retourne la première valeur d'un tableau
2774
 *
2775
 * Plus précisément déplace le pointeur du tableau sur la première valeur et la retourne.
2776
 *
2777
 * @example `[(#LISTE{un,deux,trois}|reset)]` retourne 'un'
2778
 *
2779
 * @filtre
2780
 * @link http://php.net/manual/fr/function.reset.php
2781
 * @see filtre_end()
2782
 *
2783
 * @param array $array
2784
 * @return mixed|null|false
2785
 *    - null si $array n'est pas un tableau,
2786
 *    - false si le tableau est vide
2787
 *    - la première valeur du tableau sinon.
2788
 **/
2789
function filtre_reset($array) {
2790
	return !is_array($array) ? null : reset($array);
2791
}
2792
2793
/**
2794
 * Retourne la dernière valeur d'un tableau
2795
 *
2796
 * Plus précisément déplace le pointeur du tableau sur la dernière valeur et la retourne.
2797
 *
2798
 * @example `[(#LISTE{un,deux,trois}|end)]` retourne 'trois'
2799
 *
2800
 * @filtre
2801
 * @link http://php.net/manual/fr/function.end.php
2802
 * @see filtre_reset()
2803
 *
2804
 * @param array $array
2805
 * @return mixed|null|false
2806
 *    - null si $array n'est pas un tableau,
2807
 *    - false si le tableau est vide
2808
 *    - la dernière valeur du tableau sinon.
2809
 **/
2810
function filtre_end($array) {
2811
	return !is_array($array) ? null : end($array);
2812
}
2813
2814
/**
2815
 * Empile une valeur à la fin d'un tableau
2816
 *
2817
 * @example `[(#LISTE{un,deux,trois}|push{quatre}|print)]`
2818
 *
2819
 * @filtre
2820
 * @link https://www.spip.net/4571
2821
 * @link http://php.net/manual/fr/function.array-push.php
2822
 *
2823
 * @param array $array
2824
 * @param mixed $val
2825
 * @return array|string
2826
 *     - '' si $array n'est pas un tableau ou si echec.
2827
 *     - le tableau complété de la valeur sinon.
2828
 *
2829
 **/
2830
function filtre_push($array, $val) {
2831
	if (!is_array($array) or !array_push($array, $val)) {
2832
		return '';
2833
	}
2834
2835
	return $array;
2836
}
2837
2838
/**
2839
 * Indique si une valeur est contenue dans un tableau
2840
 *
2841
 * @example `[(#LISTE{un,deux,trois}|find{quatre}|oui) ... ]`
2842
 *
2843
 * @filtre
2844
 * @link https://www.spip.net/4575
2845
 * @see in_any() Assez proche, avec les paramètres tableau et valeur inversés.
2846
 *
2847
 * @param array $array
2848
 * @param mixed $val
2849
 * @return bool
2850
 *     - `false` si `$array` n'est pas un tableau
2851
 *     - `true` si la valeur existe dans le tableau, `false` sinon.
2852
 **/
2853
function filtre_find($array, $val) {
2854
	return (is_array($array) and in_array($val, $array));
2855
}
2856
2857
2858
/**
2859
 * Passer les url relatives à la css d'origine en url absolues
2860
 *
2861
 * @uses suivre_lien()
2862
 *
2863
 * @param string $contenu
2864
 *     Contenu du fichier CSS
2865
 * @param string $source
2866
 *     Chemin du fichier CSS
2867
 * @return string
2868
 *     Contenu avec urls en absolus
2869
 **/
2870
function urls_absolues_css($contenu, $source) {
2871
	$path = suivre_lien(url_absolue($source), './');
2872
2873
	return preg_replace_callback(
2874
		",url\s*\(\s*['\"]?([^'\"/#\s][^:]*)['\"]?\s*\),Uims",
2875
		function($x) use ($path) {
2876
			return "url('" . suivre_lien($path, $x[1]) . "')";
2877
		},
2878
		$contenu
2879
	);
2880
}
2881
2882
2883
/**
2884
 * Inverse le code CSS (left <--> right) d'une feuille de style CSS
2885
 *
2886
 * Récupère le chemin d'une CSS existante et :
2887
 *
2888
 * 1. regarde si une CSS inversée droite-gauche existe dans le meme répertoire
2889
 * 2. sinon la crée (ou la recrée) dans `_DIR_VAR/cache_css/`
2890
 *
2891
 * Si on lui donne à manger une feuille nommée `*_rtl.css` il va faire l'inverse.
2892
 *
2893
 * @filtre
2894
 * @example
2895
 *     ```
2896
 *     [<link rel="stylesheet" href="(#CHEMIN{css/perso.css}|direction_css)" type="text/css" />]
2897
 *     ```
2898
 * @param string $css
2899
 *     Chemin vers le fichier CSS
2900
 * @param string $voulue
2901
 *     Permet de forcer le sens voulu (en indiquant `ltr`, `rtl` ou un
2902
 *     code de langue). En absence, prend le sens de la langue en cours.
2903
 *
2904
 * @return string
2905
 *     Chemin du fichier CSS inversé
2906
 **/
2907
function direction_css($css, $voulue = '') {
2908
	if (!preg_match(',(_rtl)?\.css$,i', $css, $r)) {
2909
		return $css;
2910
	}
2911
2912
	// si on a precise le sens voulu en argument, le prendre en compte
2913
	if ($voulue = strtolower($voulue)) {
2914
		if ($voulue != 'rtl' and $voulue != 'ltr') {
2915
			$voulue = lang_dir($voulue);
2916
		}
2917
	} else {
2918
		$voulue = lang_dir();
2919
	}
2920
2921
	$r = count($r) > 1;
2922
	$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...
2923
	$dir = $r ? 'rtl' : 'ltr';
2924
	$ndir = $r ? 'ltr' : 'rtl';
2925
2926
	if ($voulue == $dir) {
2927
		return $css;
2928
	}
2929
2930
	if (
2931
		// url absolue
2932
		preg_match(",^https?:,i", $css)
2933
		// ou qui contient un ?
2934
		or (($p = strpos($css, '?')) !== false)
2935
	) {
2936
		$distant = true;
2937
		$cssf = parse_url($css);
2938
		$cssf = $cssf['path'] . ($cssf['query'] ? "?" . $cssf['query'] : "");
2939
		$cssf = preg_replace(',[?:&=],', "_", $cssf);
2940
	} else {
2941
		$distant = false;
2942
		$cssf = $css;
2943
		// 1. regarder d'abord si un fichier avec la bonne direction n'est pas aussi
2944
		//propose (rien a faire dans ce cas)
2945
		$f = preg_replace(',(_rtl)?\.css$,i', '_' . $ndir . '.css', $css);
2946
		if (@file_exists($f)) {
2947
			return $f;
2948
		}
2949
	}
2950
2951
	// 2.
2952
	$dir_var = sous_repertoire(_DIR_VAR, 'cache-css');
2953
	$f = $dir_var
2954
		. preg_replace(',.*/(.*?)(_rtl)?\.css,', '\1', $cssf)
2955
		. '.' . substr(md5($cssf), 0, 4) . '_' . $ndir . '.css';
2956
2957
	// la css peut etre distante (url absolue !)
2958
	if ($distant) {
2959
		include_spip('inc/distant');
2960
		$res = recuperer_url($css);
2961
		if (!$res or !$contenu = $res['page']) {
2962
			return $css;
2963
		}
2964
	} else {
2965 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...
2966
			and (_VAR_MODE != 'recalcul')
2967
		) {
2968
			return $f;
2969
		}
2970
		if (!lire_fichier($css, $contenu)) {
2971
			return $css;
2972
		}
2973
	}
2974
2975
2976
	// Inverser la direction gauche-droite en utilisant CSSTidy qui gere aussi les shorthands
2977
	include_spip("lib/csstidy/class.csstidy");
2978
	$parser = new csstidy();
2979
	$parser->set_cfg('optimise_shorthands', 0);
2980
	$parser->set_cfg('reverse_left_and_right', true);
2981
	$parser->parse($contenu);
2982
2983
	$contenu = $parser->print->plain();
2984
2985
2986
	// reperer les @import auxquels il faut propager le direction_css
2987
	preg_match_all(",\@import\s*url\s*\(\s*['\"]?([^'\"/][^:]*)['\"]?\s*\),Uims", $contenu, $regs);
2988
	$src = array();
2989
	$src_direction_css = array();
2990
	$src_faux_abs = array();
2991
	$d = dirname($css);
2992
	foreach ($regs[1] as $k => $import_css) {
2993
		$css_direction = direction_css("$d/$import_css", $voulue);
2994
		// si la css_direction est dans le meme path que la css d'origine, on tronque le path, elle sera passee en absolue
2995
		if (substr($css_direction, 0, strlen($d) + 1) == "$d/") {
2996
			$css_direction = substr($css_direction, strlen($d) + 1);
2997
		} // si la css_direction commence par $dir_var on la fait passer pour une absolue
2998
		elseif (substr($css_direction, 0, strlen($dir_var)) == $dir_var) {
2999
			$css_direction = substr($css_direction, strlen($dir_var));
3000
			$src_faux_abs["/@@@@@@/" . $css_direction] = $css_direction;
3001
			$css_direction = "/@@@@@@/" . $css_direction;
3002
		}
3003
		$src[] = $regs[0][$k];
3004
		$src_direction_css[] = str_replace($import_css, $css_direction, $regs[0][$k]);
3005
	}
3006
	$contenu = str_replace($src, $src_direction_css, $contenu);
3007
3008
	$contenu = urls_absolues_css($contenu, $css);
3009
3010
	// virer les fausses url absolues que l'on a mis dans les import
3011
	if (count($src_faux_abs)) {
3012
		$contenu = str_replace(array_keys($src_faux_abs), $src_faux_abs, $contenu);
3013
	}
3014
3015
	if (!ecrire_fichier($f, $contenu)) {
3016
		return $css;
3017
	}
3018
3019
	return $f;
3020
}
3021
3022
3023
/**
3024
 * Transforme les urls relatives d'un fichier CSS en absolues
3025
 *
3026
 * Récupère le chemin d'une css existante et crée (ou recrée) dans `_DIR_VAR/cache_css/`
3027
 * une css dont les url relatives sont passées en url absolues
3028
 *
3029
 * Le calcul n'est pas refait si le fichier cache existe déjà et que
3030
 * la source n'a pas été modifiée depuis.
3031
 *
3032
 * @uses recuperer_page() si l'URL source n'est pas sur le même site
3033
 * @uses urls_absolues_css()
3034
 *
3035
 * @param string $css
3036
 *     Chemin ou URL du fichier CSS source
3037
 * @return string
3038
 *     - Chemin du fichier CSS transformé (si source lisible et mise en cache réussie)
3039
 *     - Chemin ou URL du fichier CSS source sinon.
3040
 **/
3041
function url_absolue_css($css) {
3042
	if (!preg_match(',\.css$,i', $css, $r)) {
3043
		return $css;
3044
	}
3045
3046
	$url_absolue_css = url_absolue($css);
3047
3048
	$f = basename($css, '.css');
3049
	$f = sous_repertoire(_DIR_VAR, 'cache-css')
3050
		. preg_replace(",(.*?)(_rtl|_ltr)?$,", "\\1-urlabs-" . substr(md5("$css-urlabs"), 0, 4) . "\\2", $f)
3051
		. '.css';
3052
3053 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...
3054
		return $f;
3055
	}
3056
3057
	if ($url_absolue_css == $css) {
3058
		if (strncmp($GLOBALS['meta']['adresse_site'], $css, $l = strlen($GLOBALS['meta']['adresse_site'])) != 0
3059
			or !lire_fichier(_DIR_RACINE . substr($css, $l), $contenu)
3060
		) {
3061
			include_spip('inc/distant');
3062
			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...
3063
				return $css;
3064
			}
3065
		}
3066
	} elseif (!lire_fichier($css, $contenu)) {
3067
		return $css;
3068
	}
3069
3070
	// passer les url relatives a la css d'origine en url absolues
3071
	$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...
3072
3073
	// ecrire la css
3074
	if (!ecrire_fichier($f, $contenu)) {
3075
		return $css;
3076
	}
3077
3078
	return $f;
3079
}
3080
3081
3082
/**
3083
 * Récupère la valeur d'une clé donnée
3084
 * dans un tableau (ou un objet).
3085
 *
3086
 * @filtre
3087
 * @link https://www.spip.net/4572
3088
 * @example
3089
 *     ```
3090
 *     [(#VALEUR|table_valeur{cle/sous/element})]
3091
 *     ```
3092
 *
3093
 * @param mixed $table
3094
 *     Tableau ou objet PHP
3095
 *     (ou chaîne serialisée de tableau, ce qui permet d'enchaîner le filtre)
3096
 * @param string $cle
3097
 *     Clé du tableau (ou paramètre public de l'objet)
3098
 *     Cette clé peut contenir des caractères / pour sélectionner
3099
 *     des sous éléments dans le tableau, tel que `sous/element/ici`
3100
 *     pour obtenir la valeur de `$tableau['sous']['element']['ici']`
3101
 * @param mixed $defaut
3102
 *     Valeur par defaut retournée si la clé demandée n'existe pas
3103
 * @param bool  $conserver_null
3104
 *     Permet de forcer la fonction à renvoyer la valeur null d'un index
3105
 *     et non pas $defaut comme cela est fait naturellement par la fonction
3106
 *     isset. On utilise alors array_key_exists() à la place de isset().
3107
 * 
3108
 * @return mixed
3109
 *     Valeur trouvée ou valeur par défaut.
3110
 **/
3111
function table_valeur($table, $cle, $defaut = '', $conserver_null = false) {
3112
	foreach (explode('/', $cle) as $k) {
3113
3114
		$table = is_string($table) ? @unserialize($table) : $table;
3115
3116
		if (is_object($table)) {
3117
			$table = (($k !== "") and isset($table->$k)) ? $table->$k : $defaut;
3118
		} elseif (is_array($table)) {
3119
			if ($conserver_null) {
3120
				$table = array_key_exists($k, $table) ? $table[$k] : $defaut;
3121
			} else {
3122
				$table = isset($table[$k]) ? $table[$k] : $defaut;
3123
			}
3124
		} else {
3125
			$table = $defaut;
3126
		}
3127
	}
3128
3129
	return $table;
3130
}
3131
3132
/**
3133
 * Retrouve un motif dans un texte à partir d'une expression régulière
3134
 *
3135
 * S'appuie sur la fonction `preg_match()` en PHP
3136
 *
3137
 * @example
3138
 *    - `[(#TITRE|match{toto})]`
3139
 *    - `[(#TEXTE|match{^ceci$,Uims})]`
3140
 *    - `[(#TEXTE|match{truc(...)$, UimsS, 1})]` Capture de la parenthèse indiquée
3141
 *    - `[(#TEXTE|match{truc(...)$, 1})]` Équivalent, sans indiquer les modificateurs
3142
 *
3143
 * @filtre
3144
 * @link https://www.spip.net/4299
3145
 * @link http://php.net/manual/fr/function.preg-match.php Pour des infos sur `preg_match()`
3146
 *
3147
 * @param string $texte
3148
 *     Texte dans lequel chercher
3149
 * @param string|int $expression
3150
 *     Expression régulière de recherche, sans le délimiteur
3151
 * @param string $modif
3152
 *     - string : Modificateurs de l'expression régulière
3153
 *     - int : Numéro de parenthèse capturante
3154
 * @param int $capte
3155
 *     Numéro de parenthèse capturante
3156
 * @return bool|string
3157
 *     - false : l'expression n'a pas été trouvée
3158
 *     - true : expression trouvée, mais pas la parenthèse capturante
3159
 *     - string : expression trouvée.
3160
 **/
3161
function filtre_match_dist($texte, $expression, $modif = "UimsS", $capte = 0) {
3162
	if (intval($modif) and $capte == 0) {
3163
		$capte = $modif;
3164
		$modif = "UimsS";
3165
	}
3166
	$expression = str_replace("\/", "/", $expression);
3167
	$expression = str_replace("/", "\/", $expression);
3168
3169
	if (preg_match('/' . $expression . '/' . $modif, $texte, $r)) {
3170
		if (isset($r[$capte])) {
3171
			return $r[$capte];
3172
		} else {
3173
			return true;
3174
		}
3175
	}
3176
3177
	return false;
3178
}
3179
3180
3181
/**
3182
 * Remplacement de texte à base d'expression régulière
3183
 *
3184
 * @filtre
3185
 * @link https://www.spip.net/4309
3186
 * @see match()
3187
 * @example
3188
 *     ```
3189
 *     [(#TEXTE|replace{^ceci$,cela,UimsS})]
3190
 *     ```
3191
 *
3192
 * @param string $texte
3193
 *     Texte
3194
 * @param string $expression
3195
 *     Expression régulière
3196
 * @param string $replace
3197
 *     Texte de substitution des éléments trouvés
3198
 * @param string $modif
3199
 *     Modificateurs pour l'expression régulière.
3200
 * @return string
3201
 *     Texte
3202
 **/
3203
function replace($texte, $expression, $replace = '', $modif = "UimsS") {
3204
	$expression = str_replace("\/", "/", $expression);
3205
	$expression = str_replace("/", "\/", $expression);
3206
3207
	return preg_replace('/' . $expression . '/' . $modif, $replace, $texte);
3208
}
3209
3210
3211
/**
3212
 * Cherche les documents numerotés dans un texte traite par `propre()`
3213
 *
3214
 * Affecte la liste des doublons['documents']
3215
 *
3216
 * @param array $doublons
3217
 *     Liste des doublons
3218
 * @param string $letexte
3219
 *     Le texte
3220
 * @return string
3221
 *     Le texte
3222
 **/
3223
function traiter_doublons_documents(&$doublons, $letexte) {
3224
3225
	// Verifier dans le texte & les notes (pas beau, helas)
3226
	$t = $letexte . $GLOBALS['les_notes'];
3227
3228
	if (strstr($t, 'spip_document_') // evite le preg_match_all si inutile
3229
		and preg_match_all(
3230
			',<[^>]+\sclass=["\']spip_document_([0-9]+)[\s"\'],imsS',
3231
			$t, $matches, PREG_PATTERN_ORDER)
3232
	) {
3233
		if (!isset($doublons['documents'])) {
3234
			$doublons['documents'] = "";
3235
		}
3236
		$doublons['documents'] .= "," . join(',', $matches[1]);
3237
	}
3238
3239
	return $letexte;
3240
}
3241
3242
/**
3243
 * Filtre vide qui ne renvoie rien
3244
 *
3245
 * @example
3246
 *     `[(#CALCUL|vide)]` n'affichera pas le résultat du calcul
3247
 * @filtre
3248
 *
3249
 * @param mixed $texte
3250
 * @return string Chaîne vide
3251
 **/
3252
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...
3253
	return "";
3254
}
3255
3256
//
3257
// Filtres pour le modele/emb (embed document)
3258
//
3259
3260
/**
3261
 * Écrit des balises HTML `<param...>` à partir d'un tableau de données tel que `#ENV`
3262
 *
3263
 * Permet d'écrire les balises `<param>` à indiquer dans un `<object>`
3264
 * en prenant toutes les valeurs du tableau transmis.
3265
 *
3266
 * Certaines clés spécifiques à SPIP et aux modèles embed sont omises :
3267
 * id, lang, id_document, date, date_redac, align, fond, recurs, emb, dir_racine
3268
 *
3269
 * @example `[(#ENV*|env_to_params)]`
3270
 *
3271
 * @filtre
3272
 * @link https://www.spip.net/4005
3273
 *
3274
 * @param array|string $env
3275
 *      Tableau cle => valeur des paramètres à écrire, ou chaine sérialisée de ce tableau
3276
 * @param array $ignore_params
3277
 *      Permet de compléter les clés ignorées du tableau.
3278
 * @return string
3279
 *      Code HTML résultant
3280
 **/
3281 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...
3282
	$ignore_params = array_merge(
3283
		array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
3284
		$ignore_params
3285
	);
3286
	if (!is_array($env)) {
3287
		$env = unserialize($env);
3288
	}
3289
	$texte = "";
3290
	if ($env) {
3291
		foreach ($env as $i => $j) {
3292
			if (is_string($j) and !in_array($i, $ignore_params)) {
3293
				$texte .= "<param name='" . attribut_html($i) . "'\n\tvalue='" . attribut_html($j) . "' />";
3294
			}
3295
		}
3296
	}
3297
3298
	return $texte;
3299
}
3300
3301
/**
3302
 * Écrit des attributs HTML à partir d'un tableau de données tel que `#ENV`
3303
 *
3304
 * Permet d'écrire des attributs d'une balise HTML en utilisant les données du tableau transmis.
3305
 * Chaque clé deviendra le nom de l'attribut (et la valeur, sa valeur)
3306
 *
3307
 * Certaines clés spécifiques à SPIP et aux modèles embed sont omises :
3308
 * id, lang, id_document, date, date_redac, align, fond, recurs, emb, dir_racine
3309
 *
3310
 * @example `<embed src='#URL_DOCUMENT' [(#ENV*|env_to_attributs)] width='#GET{largeur}' height='#GET{hauteur}'></embed>`
3311
 * @filtre
3312
 *
3313
 * @param array|string $env
3314
 *      Tableau cle => valeur des attributs à écrire, ou chaine sérialisée de ce tableau
3315
 * @param array $ignore_params
3316
 *      Permet de compléter les clés ignorées du tableau.
3317
 * @return string
3318
 *      Code HTML résultant
3319
 **/
3320 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...
3321
	$ignore_params = array_merge(
3322
		array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
3323
		$ignore_params
3324
	);
3325
	if (!is_array($env)) {
3326
		$env = unserialize($env);
3327
	}
3328
	$texte = "";
3329
	if ($env) {
3330
		foreach ($env as $i => $j) {
3331
			if (is_string($j) and !in_array($i, $ignore_params)) {
3332
				$texte .= attribut_html($i) . "='" . attribut_html($j) . "' ";
3333
			}
3334
		}
3335
	}
3336
3337
	return $texte;
3338
}
3339
3340
3341
/**
3342
 * Concatène des chaînes
3343
 *
3344
 * @filtre
3345
 * @link https://www.spip.net/4150
3346
 * @example
3347
 *     ```
3348
 *     #TEXTE|concat{texte1,texte2,...}
3349
 *     ```
3350
 *
3351
 * @return string Chaînes concaténés
3352
 **/
3353
function concat() {
3354
	$args = func_get_args();
3355
3356
	return join('', $args);
3357
}
3358
3359
3360
/**
3361
 * Retourne le contenu d'un ou plusieurs fichiers
3362
 *
3363
 * Les chemins sont cherchés dans le path de SPIP
3364
 *
3365
 * @see balise_INCLURE_dist() La balise `#INCLURE` peut appeler cette fonction
3366
 *
3367
 * @param array|string $files
3368
 *     - array : Liste de fichiers
3369
 *     - string : fichier ou fichiers séparés par `|`
3370
 * @param bool $script
3371
 *     - si true, considère que c'est un fichier js à chercher `javascript/`
3372
 * @return string
3373
 *     Contenu du ou des fichiers, concaténé
3374
 **/
3375
function charge_scripts($files, $script = true) {
3376
	$flux = "";
3377
	foreach (is_array($files) ? $files : explode("|", $files) as $file) {
3378
		if (!is_string($file)) {
3379
			continue;
3380
		}
3381
		if ($script) {
3382
			$file = preg_match(",^\w+$,", $file) ? "javascript/$file.js" : '';
3383
		}
3384
		if ($file) {
3385
			$path = find_in_path($file);
3386
			if ($path) {
3387
				$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 3385 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...
3388
			}
3389
		}
3390
	}
3391
3392
	return $flux;
3393
}
3394
3395
3396
/**
3397
 * Produit une balise img avec un champ alt d'office si vide
3398
 *
3399
 * Attention le htmlentities et la traduction doivent être appliqués avant.
3400
 *
3401
 * @param string $img
3402
 * @param string $alt
3403
 * @param string $atts
3404
 * @param string $title
3405
 * @param array $options
3406
 *   chemin_image : utiliser chemin_image sur $img fourni, ou non (oui par dafaut)
3407
 *   utiliser_suffixe_size : utiliser ou non le suffixe de taille dans le nom de fichier de l'image
3408
 *   sous forme -xx.png (pour les icones essentiellement) (oui par defaut)
3409
 *   variante_svg_si_possible: utiliser l'image -xx.svg au lieu de -32.png par exemple (si la variante svg est disponible)
3410
 * @return string
3411
 */
3412
function http_img_pack($img, $alt, $atts = '', $title = '', $options = array()) {
3413
3414
	$img_file = $img;
3415
	if ($p = strpos($img_file, '?')) {
3416
		$img_file = substr($img_file,0, $p);
3417
	}
3418
	if (!isset($options['chemin_image']) or $options['chemin_image'] == true) {
3419
		$img_file = chemin_image($img);
3420
	}
3421
	else {
3422
		if (!isset($options['variante_svg_si_possible']) or $options['variante_svg_si_possible'] == true){
3423
			// on peut fournir une icone generique -xx.svg qui fera le job dans toutes les tailles, et qui est prioritaire sur le png
3424
			// si il y a un .svg a la bonne taille (-16.svg) a cote, on l'utilise en remplacement du -16.png
3425
			if (preg_match(',-(\d+)[.](png|gif|svg)$,', $img_file, $m)
3426
			  and $variante_svg_generique = substr($img_file, 0, -strlen($m[0])) . "-xx.svg"
3427
			  and file_exists($variante_svg_generique)) {
3428
				if ($variante_svg_size = substr($variante_svg_generique,0,-6) . $m[1] . ".svg" and file_exists($variante_svg_size)) {
3429
					$img_file = $variante_svg_size;
3430
				}
3431
				else {
3432
					$img_file = $variante_svg_generique;
3433
				}
3434
			}
3435
		}
3436
	}
3437
	if (stripos($atts, 'width') === false) {
3438
		// utiliser directement l'info de taille presente dans le nom
3439
		if ((!isset($options['utiliser_suffixe_size'])
3440
				or $options['utiliser_suffixe_size'] == true
3441
			  or strpos($img_file, '-xx.svg') !== false)
3442
			and (preg_match(',-([0-9]+)[.](png|gif|svg)$,', $img, $regs)
3443
					 or preg_match(',\?([0-9]+)px$,', $img, $regs))
3444
		) {
3445
			$largeur = $hauteur = intval($regs[1]);
3446
		} else {
3447
			$taille = taille_image($img_file);
3448
			list($hauteur, $largeur) = $taille;
3449
			if (!$hauteur or !$largeur) {
3450
				return "";
3451
			}
3452
		}
3453
		$atts .= " width='" . $largeur . "' height='" . $hauteur . "'";
3454
	}
3455
3456
	if (file_exists($img_file)) {
3457
		$img_file = timestamp($img_file);
3458
	}
3459
	if ($alt === false) {
3460
		$alt = '';
3461
	}
3462
	elseif($alt or $alt==='') {
3463
		$alt = " alt='".attribut_html($alt)."'";
3464
	}
3465
	else {
3466
		$alt = " alt='".attribut_html($title)."'";
3467
	}
3468
	return "<img src='$img_file'$alt"
3469
	. ($title ? ' title="' . attribut_html($title) . '"' : '')
3470
	. " " . ltrim($atts)
3471
	. " />";
3472
}
3473
3474
/**
3475
 * Générer une directive `style='background:url()'` à partir d'un fichier image
3476
 *
3477
 * @param string $img
3478
 * @param string $att
3479
 * @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...
3480
 * @return string
3481
 */
3482
function http_style_background($img, $att = '', $size=null) {
3483
	if ($size and is_numeric($size)){
3484
		$size = trim($size) . "px";
3485
	}
3486
	return " style='background" .
3487
		($att ? "" : "-image") . ": url(\"" . chemin_image($img) . "\")" . ($att ? (' ' . $att) : '') . ";"
3488
		. ($size ? "background-size:{$size};" : '')
3489
		. "'";
3490
}
3491
3492
3493
function helper_filtre_balise_img_svg_arguments($alt_or_size, $class_or_size, $size) {
3494
	$args = [$alt_or_size, $class_or_size, $size];
3495
	while (is_null(end($args)) and count($args)) {
3496
		array_pop($args);
3497
	}
3498
	if (!count($args)) {
3499
		return [null, null, null];
3500
	}
3501
	if (count($args) < 3) {
3502
		$maybe_size = array_pop($args);
3503
		// @2x
3504
		// @1.5x
3505
		// 512
3506
		// 512x*
3507
		// 512x300
3508
		if (!strlen($maybe_size)
3509
			or !preg_match(',^(@\d+(\.\d+)?x|\d+(x\*)?|\d+x\d+)$,', trim($maybe_size))) {
3510
			$args[] = $maybe_size;
3511
			$maybe_size = null;
3512
		}
3513
		while (count($args)<2) {
3514
			$args[] = null; // default alt or class
3515
		}
3516
		$args[] = $maybe_size;
3517
	}
3518
	return $args;
3519
}
3520
3521
function helper_filtre_balise_img_svg_size($img, $size) {
3522
	// si size est de la forme '@2x' c'est un coeff multiplicateur sur la densite
3523
	if (strpos($size, '@') === 0 and substr($size,-1) === 'x') {
3524
		$coef = floatval(substr($size, 1, -1));
3525
		list($h, $w) = taille_image($img);
3526
		$height = intval(round($h / $coef));
3527
		$width = intval(round($w / $coef));
3528
	}
3529
	// sinon c'est une valeur seule si image caree ou largeurxhauteur
3530
	else {
3531
		$size = explode('x', $size, 2);
3532
		$size = array_map('trim', $size);
3533
		$height = $width = intval(array_shift($size));
3534
3535
		if (count($size) and reset($size)) {
3536
			$height = array_shift($size);
3537
			if ($height === '*') {
3538
				list($h, $w) = taille_image($img);
3539
				$height = intval(round($h * $width / $w));
3540
			}
3541
		}
3542
	}
3543
3544
	return [$width, $height];
3545
}
3546
3547
/**
3548
 * Générer une balise HTML `img` à partir d'un nom de fichier et/ou renseigne son alt/class/width/height
3549
 * selon les arguments passés
3550
 *
3551
 * Le class et le alt peuvent etre omis et dans ce cas size peut-être renseigné comme dernier argument :
3552
 * [(#FICHIER|balise_img{@2x})]
3553
 * [(#FICHIER|balise_img{1024})]
3554
 * [(#FICHIER|balise_img{1024x*})]
3555
 * [(#FICHIER|balise_img{1024x640})]
3556
 * [(#FICHIER|balise_img{'un nuage',1024x640})]
3557
 * [(#FICHIER|balise_img{'un nuage','spip_logo',1024x640})]
3558
 * 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
3559
 * [(#FICHIER|balise_img{'@2x','',''})]
3560
 *
3561
 * @uses http_img_pack()
3562
 *
3563
 * @param string $img
3564
 *   chemin vers un fichier ou balise `<img src='...' />` (generee par un filtre image par exemple)
3565
 * @param string $alt
3566
 *   texte alternatif ; une valeur nulle pour explicitement ne pas avoir de balise alt sur l'image (au lieu d'un alt vide)
3567
 * @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...
3568
 *   attribut class ; null par defaut (ie si img est une balise, son attribut class sera inchange. pas de class inseree
3569
 * @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...
3570
 *   taille imposee
3571
 *     @2x : pour imposer une densite x2 (widht et height seront divisees par 2)
3572
 *     largeur : pour une image carree
3573
 *     largeurx* : pour imposer uniquement la largeur (un attribut height sera aussi ajoute, mais calcule automatiquement pour respecter le ratio initial de l'image)
3574
 *     largeurxhauteur pour fixer les 2 dimensions
3575
 * @return string
3576
 *     Code HTML de la balise IMG
3577
 */
3578
function filtre_balise_img_dist($img, $alt = '', $class = null, $size=null) {
3579
3580
	list($alt, $class, $size) = helper_filtre_balise_img_svg_arguments($alt, $class, $size);
3581
3582
	$img = trim($img);
3583
	if (strpos($img, '<img') === 0) {
3584
		if (!is_null($alt)) {
3585
			$img = inserer_attribut($img, 'alt', $alt);
3586
		}
3587 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...
3588
			if (strlen($class)) {
3589
				$img = inserer_attribut($img, 'class', $class);
3590
			}
3591
			else {
3592
				$img = vider_attribut($img, 'class');
3593
			}
3594
		}
3595
	}
3596
	else {
3597
		$img = http_img_pack($img, $alt, $class ? " class='" . attribut_html($class) . "'" : '', '',
3598
				array('chemin_image' => false, 'utiliser_suffixe_size' => false));
3599
		if (is_null($alt)) {
3600
			$img = vider_attribut($img, 'alt');
3601
		}
3602
	}
3603
3604
	if ($img and !is_null($size) and strlen($size = trim($size))) {
3605
		list($width, $height) = helper_filtre_balise_img_svg_size($img, $size);
3606
3607
		$img = inserer_attribut($img, 'width', $width);
3608
		$img = inserer_attribut($img, 'height', $height);
3609
	}
3610
3611
	return $img;
3612
}
3613
3614
3615
/**
3616
 * Inserer un svg inline
3617
 * http://www.accede-web.com/notices/html-css-javascript/6-images-icones/6-2-svg-images-vectorielles/
3618
 *
3619
 * pour l'inserer avec une balise <img>, utiliser le filtre |balise_img
3620
 *
3621
 * Le class et le alt peuvent etre omis et dans ce cas size peut-être renseigné comme dernier argument :
3622
 * [(#FICHIER|balise_svg{1024x640})]
3623
 * [(#FICHIER|balise_svg{'un nuage',1024x640})]
3624
 * [(#FICHIER|balise_svg{'un nuage','spip_logo',1024x640})]
3625
 * 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
3626
 * [(#FICHIER|balise_svg{'un nuage','@2x',''})]
3627
 *
3628
 * @param string $img
3629
 *   chemin vers un fichier ou balise `<svg ... >... </svg>` deja preparee
3630
 * @param string $alt
3631
 *   texte alternatif ; une valeur nulle pour explicitement ne pas avoir de balise alt sur l'image (au lieu d'un alt vide)
3632
 * @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...
3633
 *   attribut class ; null par defaut (ie si img est une balise, son attribut class sera inchange. pas de class inseree
3634
 * @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...
3635
 *   taille imposee
3636
 *     @2x : pour imposer une densite x2 (widht et height seront divisees par 2)
3637
 *     largeur : pour une image carree
3638
 *     largeurx* : pour imposer uniquement la largeur (un attribut height sera aussi ajoute, mais calcule automatiquement pour respecter le ratio initial de l'image)
3639
 *     largeurxhauteur pour fixer les 2 dimensions
3640
 * @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...
3641
 *     Code HTML de la balise SVG
3642
 */
3643
function filtre_balise_svg_dist($img, $alt = '', $class = null, $size=null) {
3644
3645
	$img = trim($img);
3646
	$img_file = $img;
3647
	if (strpos($img, '<svg') === false){
3648
		if ($p = strpos($img_file, '?')){
3649
			$img_file = substr($img_file, 0, $p);
3650
		}
3651
3652
		if (!$img_file or !$svg = file_get_contents($img_file)){
3653
			return '';
3654
		}
3655
	}
3656
3657
	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...
3658
		return '';
3659
	}
3660
3661
	list($alt, $class, $size) = helper_filtre_balise_img_svg_arguments($alt, $class, $size);
3662
3663
	$balise_svg = $match[0];
3664
	$balise_svg_source = $balise_svg;
3665
3666
	// entete XML à supprimer
3667
	$svg = preg_replace(',^\s*<\?xml[^>]*\?' . '>,', '', $svg);
3668
3669
	// IE est toujours mon ami
3670
	$balise_svg = inserer_attribut($balise_svg, 'focusable', 'false');
3671
3672
	// regler la classe
3673 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...
3674
		if (strlen($class)) {
3675
			$balise_svg = inserer_attribut($balise_svg, 'class', $class);
3676
		}
3677
		else {
3678
			$balise_svg = vider_attribut($balise_svg, 'class');
3679
		}
3680
	}
3681
3682
	// regler le alt
3683
	if ($alt){
3684
		$balise_svg = inserer_attribut($balise_svg, 'role', 'img');
3685
		$id = "img-svg-title-" . substr(md5("$img_file:$svg:$alt"),0,4);
3686
		$balise_svg = inserer_attribut($balise_svg, 'aria-labelledby', $id);
3687
		$title = "<title id=\"$id\">" . entites_html($alt)."</title>\n";
3688
		$balise_svg .= $title;
3689
	}
3690
	else {
3691
		$balise_svg = inserer_attribut($balise_svg, 'aria-hidden', 'true');
3692
	}
3693
3694
	$svg = str_replace($balise_svg_source, $balise_svg, $svg);
3695
3696
	if (!is_null($size) and strlen($size = trim($size))) {
3697
		list($width, $height) = helper_filtre_balise_img_svg_size($svg, $size);
3698
3699
		if (!function_exists('svg_redimensionner')) {
3700
			include_spip('inc/svg');
3701
		}
3702
		$svg = svg_redimensionner($svg, $width, $height);
3703
	}
3704
3705
	return $svg;
3706
}
3707
3708
3709
3710
/**
3711
 * Affiche chaque valeur d'un tableau associatif en utilisant un modèle
3712
 *
3713
 * @example
3714
 *     - `[(#ENV*|unserialize|foreach)]`
3715
 *     - `[(#ARRAY{a,un,b,deux}|foreach)]`
3716
 *
3717
 * @filtre
3718
 * @link https://www.spip.net/4248
3719
 *
3720
 * @param array $tableau
3721
 *     Tableau de données à afficher
3722
 * @param string $modele
3723
 *     Nom du modèle à utiliser
3724
 * @return string
3725
 *     Code HTML résultant
3726
 **/
3727
function filtre_foreach_dist($tableau, $modele = 'foreach') {
3728
	$texte = '';
3729
	if (is_array($tableau)) {
3730
		foreach ($tableau as $k => $v) {
3731
			$res = recuperer_fond('modeles/' . $modele,
3732
				array_merge(array('cle' => $k), (is_array($v) ? $v : array('valeur' => $v)))
3733
			);
3734
			$texte .= $res;
3735
		}
3736
	}
3737
3738
	return $texte;
3739
}
3740
3741
3742
/**
3743
 * Obtient des informations sur les plugins actifs
3744
 *
3745
 * @filtre
3746
 * @uses liste_plugin_actifs() pour connaître les informations affichables
3747
 *
3748
 * @param string $plugin
3749
 *     Préfixe du plugin ou chaîne vide
3750
 * @param string $type_info
3751
 *     Type d'info demandée
3752
 * @param bool $reload
3753
 *     true (à éviter) pour forcer le recalcul du cache des informations des plugins.
3754
 * @return array|string|bool
3755
 *
3756
 *     - Liste sérialisée des préfixes de plugins actifs (si $plugin = '')
3757
 *     - Suivant $type_info, avec $plugin un préfixe
3758
 *         - est_actif : renvoie true s'il est actif, false sinon
3759
 *         - x : retourne l'information x du plugin si présente (et plugin actif)
3760
 *         - tout : retourne toutes les informations du plugin actif
3761
 **/
3762
function filtre_info_plugin_dist($plugin, $type_info, $reload = false) {
3763
	include_spip('inc/plugin');
3764
	$plugin = strtoupper($plugin);
3765
	$plugins_actifs = liste_plugin_actifs();
3766
3767
	if (!$plugin) {
3768
		return serialize(array_keys($plugins_actifs));
3769
	} elseif (empty($plugins_actifs[$plugin]) and !$reload) {
3770
		return '';
3771
	} elseif (($type_info == 'est_actif') and !$reload) {
3772
		return $plugins_actifs[$plugin] ? 1 : 0;
3773
	} elseif (isset($plugins_actifs[$plugin][$type_info]) and !$reload) {
3774
		return $plugins_actifs[$plugin][$type_info];
3775
	} else {
3776
		$get_infos = charger_fonction('get_infos', 'plugins');
3777
		// On prend en compte les extensions
3778
		if (!is_dir($plugins_actifs[$plugin]['dir_type'])) {
3779
			$dir_plugins = constant($plugins_actifs[$plugin]['dir_type']);
3780
		} else {
3781
			$dir_plugins = $plugins_actifs[$plugin]['dir_type'];
3782
		}
3783
		if (!$infos = $get_infos($plugins_actifs[$plugin]['dir'], $reload, $dir_plugins)) {
3784
			return '';
3785
		}
3786
		if ($type_info == 'tout') {
3787
			return $infos;
3788
		} elseif ($type_info == 'est_actif') {
3789
			return $infos ? 1 : 0;
3790
		} else {
3791
			return strval($infos[$type_info]);
3792
		}
3793
	}
3794
}
3795
3796
3797
/**
3798
 * Affiche la puce statut d'un objet, avec un menu rapide pour changer
3799
 * de statut si possibilité de l'avoir
3800
 *
3801
 * @see inc_puce_statut_dist()
3802
 *
3803
 * @filtre
3804
 *
3805
 * @param int $id_objet
3806
 *     Identifiant de l'objet
3807
 * @param string $statut
3808
 *     Statut actuel de l'objet
3809
 * @param int $id_rubrique
3810
 *     Identifiant du parent
3811
 * @param string $type
3812
 *     Type d'objet
3813
 * @param bool $ajax
3814
 *     Indique s'il ne faut renvoyer que le coeur du menu car on est
3815
 *     dans une requete ajax suite à un post de changement rapide
3816
 * @return string
3817
 *     Code HTML de l'image de puce de statut à insérer (et du menu de changement si présent)
3818
 */
3819
function puce_changement_statut($id_objet, $statut, $id_rubrique, $type, $ajax = false) {
3820
	$puce_statut = charger_fonction('puce_statut', 'inc');
3821
3822
	return $puce_statut($id_objet, $statut, $id_rubrique, $type, $ajax);
3823
}
3824
3825
3826
/**
3827
 * Affiche la puce statut d'un objet, avec un menu rapide pour changer
3828
 * de statut si possibilité de l'avoir
3829
 *
3830
 * Utilisable sur tout objet qui a declaré ses statuts
3831
 *
3832
 * @example
3833
 *     [(#STATUT|puce_statut{article})] affiche une puce passive
3834
 *     [(#STATUT|puce_statut{article,#ID_ARTICLE,#ID_RUBRIQUE})] affiche une puce avec changement rapide
3835
 *
3836
 * @see inc_puce_statut_dist()
3837
 *
3838
 * @filtre
3839
 *
3840
 * @param string $statut
3841
 *     Statut actuel de l'objet
3842
 * @param string $objet
3843
 *     Type d'objet
3844
 * @param int $id_objet
3845
 *     Identifiant de l'objet
3846
 * @param int $id_parent
3847
 *     Identifiant du parent
3848
 * @return string
3849
 *     Code HTML de l'image de puce de statut à insérer (et du menu de changement si présent)
3850
 */
3851
function filtre_puce_statut_dist($statut, $objet, $id_objet = 0, $id_parent = 0) {
3852
	static $puce_statut = null;
3853
	if (!$puce_statut) {
3854
		$puce_statut = charger_fonction('puce_statut', 'inc');
3855
	}
3856
3857
	return $puce_statut($id_objet, $statut, $id_parent, $objet, false,
3858
		objet_info($objet, 'editable') ? _ACTIVER_PUCE_RAPIDE : false);
3859
}
3860
3861
3862
/**
3863
 * Encoder un contexte pour l'ajax
3864
 *
3865
 * Encoder le contexte, le signer avec une clé, le crypter
3866
 * avec le secret du site, le gziper si possible.
3867
 *
3868
 * L'entrée peut-être sérialisée (le `#ENV**` des fonds ajax et ajax_stat)
3869
 *
3870
 * @see  decoder_contexte_ajax()
3871
 * @uses calculer_cle_action()
3872
 *
3873
 * @param string|array $c
3874
 *   contexte, peut etre un tableau serialize
3875
 * @param string $form
3876
 *   nom du formulaire eventuel
3877
 * @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...
3878
 *   contenu a emboiter dans le conteneur ajax
3879
 * @param string $ajaxid
3880
 *   ajaxid pour cibler le bloc et forcer sa mise a jour
3881
 * @return string
3882
 *   hash du contexte
3883
 */
3884
function encoder_contexte_ajax($c, $form = '', $emboite = null, $ajaxid = '') {
3885
	if (is_string($c)
3886
		and @unserialize($c) !== false
3887
	) {
3888
		$c = unserialize($c);
3889
	}
3890
3891
	// supprimer les parametres debut_x
3892
	// pour que la pagination ajax ne soit pas plantee
3893
	// si on charge la page &debut_x=1 : car alors en cliquant sur l'item 0,
3894
	// le debut_x=0 n'existe pas, et on resterait sur 1
3895
	if (is_array($c)) {
3896
		foreach ($c as $k => $v) {
3897
			if (strpos($k, 'debut_') === 0) {
3898
				unset($c[$k]);
3899
			}
3900
		}
3901
	}
3902
3903
	if (!function_exists('calculer_cle_action')) {
3904
		include_spip("inc/securiser_action");
3905
	}
3906
3907
	$c = serialize($c);
3908
	$cle = calculer_cle_action($form . $c);
3909
	$c = "$cle:$c";
3910
3911
	// on ne stocke pas les contextes dans des fichiers en cache
3912
	// par defaut, sauf si cette configuration a été forcée
3913
	// OU que la longueur de l’argument géneré est plus long
3914
	// que ce qui est toléré.
3915
	$cache_contextes_ajax = (defined('_CACHE_CONTEXTES_AJAX') and _CACHE_CONTEXTES_AJAX);
3916
	if (!$cache_contextes_ajax) {
3917
		$env = $c;
3918
		if (function_exists('gzdeflate') && function_exists('gzinflate')) {
3919
			$env = gzdeflate($env);
3920
			// https://core.spip.net/issues/2667 | https://bugs.php.net/bug.php?id=61287
3921
			if ((PHP_VERSION_ID == 50400) and !@gzinflate($env)) {
3922
				$cache_contextes_ajax = true;
3923
				spip_log("Contextes AJAX forces en fichiers ! Erreur PHP 5.4.0", _LOG_AVERTISSEMENT);
3924
			}
3925
		}
3926
		$env = _xor($env);
3927
		$env = base64_encode($env);
3928
		$len = strlen($env);
3929
		// Si l’url est trop longue pour le navigateur
3930
		$max_len = _CACHE_CONTEXTES_AJAX_SUR_LONGUEUR;
3931
		if ($len > $max_len) {
3932
			$cache_contextes_ajax = true;
3933
			spip_log("Contextes AJAX forces en fichiers !"
3934
				. " Cela arrive lorsque la valeur du contexte" 
3935
				. " depasse la longueur maximale autorisee ($max_len). Ici : $len."
3936
				, _LOG_AVERTISSEMENT);
3937
		}
3938
		// Sinon si Suhosin est actif et a une la valeur maximale des variables en GET...
3939
		elseif (
3940
			$max_len = @ini_get('suhosin.get.max_value_length')
3941
			and $max_len < $len
3942
		) {
3943
			$cache_contextes_ajax = true;
3944
			spip_log("Contextes AJAX forces en fichiers !"
3945
				. " Cela arrive lorsque la valeur du contexte"
3946
				. " depasse la longueur maximale autorisee par Suhosin"
3947
				. " ($max_len) dans 'suhosin.get.max_value_length'. Ici : $len."
3948
				. " Vous devriez modifier les parametres de Suhosin"
3949
				. " pour accepter au moins 1024 caracteres.", _LOG_AVERTISSEMENT);
3950
		} 
3951
3952
	}
3953
3954
	if ($cache_contextes_ajax) {
3955
		$dir = sous_repertoire(_DIR_CACHE, 'contextes');
3956
		// stocker les contextes sur disque et ne passer qu'un hash dans l'url
3957
		$md5 = md5($c);
3958
		ecrire_fichier("$dir/c$md5", $c);
3959
		$env = $md5;
3960
	}
3961
3962
	if ($emboite === null) {
3963
		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...
3964
	}
3965
	if (!trim($emboite)) {
3966
		return "";
3967
	}
3968
	// toujours encoder l'url source dans le bloc ajax
3969
	$r = self();
3970
	$r = ' data-origin="' . $r . '"';
3971
	$class = 'ajaxbloc';
3972
	if ($ajaxid and is_string($ajaxid)) {
3973
		// ajaxid est normalement conforme a un nom de classe css
3974
		// on ne verifie pas la conformite, mais on passe entites_html par dessus par precaution
3975
		$class .= ' ajax-id-' . entites_html($ajaxid);
3976
	}
3977
3978
	return "<div class='$class' " . "data-ajax-env='$env'$r>\n$emboite</div><!--ajaxbloc-->\n";
3979
}
3980
3981
/**
3982
 * Décoder un hash de contexte pour l'ajax
3983
 *
3984
 * Précude inverse de `encoder_contexte_ajax()`
3985
 *
3986
 * @see  encoder_contexte_ajax()
3987
 * @uses calculer_cle_action()
3988
 *
3989
 * @param string $c
3990
 *   hash du contexte
3991
 * @param string $form
3992
 *   nom du formulaire eventuel
3993
 * @return array|string|bool
3994
 *   - array|string : contexte d'environnement, possiblement sérialisé
3995
 *   - false : erreur de décodage
3996
 */
3997
function decoder_contexte_ajax($c, $form = '') {
3998
	if (!function_exists('calculer_cle_action')) {
3999
		include_spip("inc/securiser_action");
4000
	}
4001
	if (((defined('_CACHE_CONTEXTES_AJAX') and _CACHE_CONTEXTES_AJAX) or strlen($c) == 32)
4002
		and $dir = sous_repertoire(_DIR_CACHE, 'contextes')
4003
		and lire_fichier("$dir/c$c", $contexte)
4004
	) {
4005
		$c = $contexte;
4006
	} else {
4007
		$c = @base64_decode($c);
4008
		$c = _xor($c);
4009
		if (function_exists('gzdeflate') && function_exists('gzinflate')) {
4010
			$c = @gzinflate($c);
4011
		}
4012
	}
4013
4014
	// extraire la signature en debut de contexte
4015
	// et la verifier avant de deserializer
4016
	// format : signature:donneesserializees
4017 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...
4018
		$cle = substr($c,0,$p);
4019
		$c = substr($c,$p+1);
4020
4021
		if ($cle == calculer_cle_action($form . $c)) {
4022
			$env = @unserialize($c);
4023
			return $env;
4024
		}
4025
	}
4026
4027
	return false;
4028
}
4029
4030
4031
/**
4032
 * Encrypte ou décrypte un message
4033
 *
4034
 * @link http://www.php.net/manual/fr/language.operators.bitwise.php#81358
4035
 *
4036
 * @param string $message
4037
 *    Message à encrypter ou décrypter
4038
 * @param null|string $key
4039
 *    Clé de cryptage / décryptage.
4040
 *    Une clé sera calculée si non transmise
4041
 * @return string
4042
 *    Message décrypté ou encrypté
4043
 **/
4044
function _xor($message, $key = null) {
4045
	if (is_null($key)) {
4046
		if (!function_exists('calculer_cle_action')) {
4047
			include_spip("inc/securiser_action");
4048
		}
4049
		$key = pack("H*", calculer_cle_action('_xor'));
4050
	}
4051
4052
	$keylen = strlen($key);
4053
	$messagelen = strlen($message);
4054
	for ($i = 0; $i < $messagelen; $i++) {
4055
		$message[$i] = ~($message[$i] ^ $key[$i % $keylen]);
4056
	}
4057
4058
	return $message;
4059
}
4060
4061
/**
4062
 * Retourne une URL de réponse de forum (aucune action ici)
4063
 *
4064
 * @see filtre_url_reponse_forum() du plugin forum (prioritaire)
4065
 * @note
4066
 *   La vraie fonction est dans le plugin forum,
4067
 *   mais on évite ici une erreur du compilateur en absence du plugin
4068
 * @param string $texte
4069
 * @return string
4070
 */
4071
function url_reponse_forum($texte) { return $texte; }
4072
4073
/**
4074
 * retourne une URL de suivi rss d'un forum (aucune action ici)
4075
 *
4076
 * @see filtre_url_rss_forum() du plugin forum (prioritaire)
4077
 * @note
4078
 *   La vraie fonction est dans le plugin forum,
4079
 *   mais on évite ici une erreur du compilateur en absence du plugin
4080
 * @param string $texte
4081
 * @return string
4082
 */
4083
function url_rss_forum($texte) { return $texte; }
4084
4085
4086
/**
4087
 * Génère des menus avec liens ou `<strong class='on'>` non clicable lorsque
4088
 * l'item est sélectionné
4089
 * Le parametre $on peut recevoir un selecteur simplifié de type 'a.active' 'strong.active.expose'
4090
 * pour préciser la balise (a, span ou strong uniquement) et la/les classes à utiliser quand on est expose
4091
 *
4092
 * @filtre
4093
 * @link https://www.spip.net/4004
4094
 * @example
4095
 *   ```
4096
 *   [(#URL_RUBRIQUE|lien_ou_expose{#TITRE, #ENV{test}|=={en_cours}})]
4097
 *   [(#URL_RUBRIQUE|lien_ou_expose{#TITRE, #ENV{test}|=={en_cours}|?{a.monlien.active}, 'monlien'})]
4098
 *   ```
4099
 *
4100
 *
4101
 * @param string $url
4102
 *   URL du lien
4103
 * @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...
4104
 *   Texte du lien
4105
 * @param bool $on
4106
 *   É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>
4107
 *   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
4108
 * @param string $class
4109
 *   Classes CSS ajoutées au lien
4110
 * @param string $title
4111
 *   Title ajouté au lien
4112
 * @param string $rel
4113
 *   Attribut `rel` ajouté au lien
4114
 * @param string $evt
4115
 *   Complement à la balise `a` pour gérer un événement javascript,
4116
 *   de la forme ` onclick='...'`
4117
 * @return string
4118
 *   Code HTML
4119
 */
4120
function lien_ou_expose($url, $libelle = null, $on = false, $class = "", $title = "", $rel = "", $evt = '') {
4121
	if ($on) {
4122
		$bal = 'strong';
4123
		$class = "";
4124
		$att = "";
4125
		// si $on passe la balise et optionnelement une ou ++classe
4126
		// a.active span.selected.active etc....
4127
		if (is_string($on) and (strncmp($on, 'a', 1)==0 or strncmp($on, 'span', 4)==0 or strncmp($on, 'strong', 6)==0)){
4128
			$on = explode(".", $on);
4129
			// on verifie que c'est exactement une des 3 balises a, span ou strong
4130
			if (in_array(reset($on), array('a', 'span', 'strong'))){
4131
				$bal = array_shift($on);
4132
				$class = implode(" ", $on);
4133
				if ($bal=="a"){
4134
					$att = 'href="#" ';
4135
				}
4136
			}
4137
		}
4138
		$att .= 'class="' . ($class ? attribut_html($class) . ' ' : '') . (defined('_LIEN_OU_EXPOSE_CLASS_ON') ? _LIEN_OU_EXPOSE_CLASS_ON : 'on') . '"';
4139
	} else {
4140
		$bal = 'a';
4141
		$att = "href='$url'"
4142
			. ($title ? " title='" . attribut_html($title) . "'" : '')
4143
			. ($class ? " class='" . attribut_html($class) . "'" : '')
4144
			. ($rel ? " rel='" . attribut_html($rel) . "'" : '')
4145
			. $evt;
4146
	}
4147
	if ($libelle === null) {
4148
		$libelle = $url;
4149
	}
4150
4151
	return "<$bal $att>$libelle</$bal>";
4152
}
4153
4154
4155
/**
4156
 * Afficher un message "un truc"/"N trucs"
4157
 * Les items sont à indiquer comme pour la fonction _T() sous la forme :
4158
 * "module:chaine"
4159
 *
4160
 * @param int $nb : le nombre
4161
 * @param string $chaine_un : l'item de langue si $nb vaut un
4162
 * @param string $chaine_plusieurs : l'item de lanque si $nb >= 2
4163
 * @param string $var : La variable à remplacer par $nb dans l'item de langue (facultatif, défaut "nb")
4164
 * @param array $vars : Les autres variables nécessaires aux chaines de langues (facultatif)
4165
 * @return string : la chaine de langue finale en utilisant la fonction _T()
4166
 */
4167
function singulier_ou_pluriel($nb, $chaine_un, $chaine_plusieurs, $var = 'nb', $vars = array()) {
4168
	static $local_singulier_ou_pluriel = array();
4169
4170
	// si nb=0 ou pas de $vars valide on retourne une chaine vide, a traiter par un |sinon
4171
	if (!is_numeric($nb) or $nb == 0) {
4172
		return "";
4173
	}
4174
	if (!is_array($vars)) {
4175
		return "";
4176
	}
4177
4178
	$langue = $GLOBALS['spip_lang'];
4179
	if (!isset($local_singulier_ou_pluriel[$langue])) {
4180
		$local_singulier_ou_pluriel[$langue] = false;
4181
		if ($f = charger_fonction("singulier_ou_pluriel_${langue}", 'inc', true)
4182
		  or $f = charger_fonction("singulier_ou_pluriel", 'inc', true)) {
4183
			$local_singulier_ou_pluriel[$langue] = $f;
4184
		}
4185
	}
4186
4187
	// si on a une surcharge on l'utilise
4188
	if ($local_singulier_ou_pluriel[$langue]) {
4189
		return ($local_singulier_ou_pluriel[$langue])($nb, $chaine_un, $chaine_plusieurs, $var, $vars);
4190
	}
4191
4192
	// sinon traitement par defaut
4193
	$vars[$var] = $nb;
4194
	if ($nb >= 2) {
4195
		return _T($chaine_plusieurs, $vars);
4196
	} else {
4197
		return _T($chaine_un, $vars);
4198
	}
4199
}
4200
4201
4202
/**
4203
 * Fonction de base pour une icone dans un squelette
4204
 * structure html : `<span><a><img><b>texte</b></span>`
4205
 *
4206
 * @param string $type
4207
 *  'lien' ou 'bouton'
4208
 * @param string $lien
4209
 *  url
4210
 * @param string $texte
4211
 *  texte du lien / alt de l'image
4212
 * @param string $fond
4213
 *  objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4214
 * @param string $fonction
4215
 *  new/del/edit
4216
 * @param string $class
4217
 *  classe supplementaire (horizontale, verticale, ajax ...)
4218
 * @param string $javascript
4219
 *  "onclick='...'" par exemple
4220
 * @return string
4221
 */
4222
function prepare_icone_base($type, $lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4223
	if (in_array($fonction, array("del", "supprimer.gif"))) {
4224
		$class .= ' danger';
4225
	} elseif ($fonction == "rien.gif") {
4226
		$fonction = "";
4227
	} elseif ($fonction == "delsafe") {
4228
		$fonction = "del";
4229
	}
4230
4231
	$fond_origine = $fond;
4232
	// remappage des icone : article-24.png+new => article-new-24.png
4233 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...
4234
		list($fond, $fonction) = $icone_renommer($fond, $fonction);
4235
	}
4236
4237
	// ajouter le type d'objet dans la class de l'icone
4238
	$class .= " " . substr(basename($fond), 0, -4);
4239
4240
	$alt = attribut_html($texte);
4241
	$title = " title=\"$alt\""; // est-ce pertinent de doubler le alt par un title ?
4242
4243
	$ajax = "";
4244
	if (strpos($class, "ajax") !== false) {
4245
		$ajax = "ajax";
4246
		if (strpos($class, "preload") !== false) {
4247
			$ajax .= " preload";
4248
		}
4249
		if (strpos($class, "nocache") !== false) {
4250
			$ajax .= " nocache";
4251
		}
4252
		$ajax = " class='$ajax'";
4253
	}
4254
4255
	$size = 24;
4256
	if (preg_match("/-([0-9]{1,3})[.](gif|png|svg)$/i", $fond, $match)
4257
	  or preg_match("/-([0-9]{1,3})([.](gif|png|svg))?$/i", $fond_origine, $match)) {
4258
		$size = $match[1];
4259
	}
4260
4261
	$icone = http_img_pack($fond, $alt, "width='$size' height='$size'");
4262
	$icone = "<span class=\"icone-image".($fonction ? " icone-fonction icone-fonction-$fonction" : "") . "\">$icone</span>";
4263
4264
	if ($type == 'lien') {
4265
		return "<span class='icone s$size $class'>"
4266
		. "<a href='$lien'$title$ajax$javascript>"
4267
		. $icone
4268
		. "<b>$texte</b>"
4269
		. "</a></span>\n";
4270
	} else {
4271
		return bouton_action("$icone<b>$texte</b>", $lien, "icone s$size $class", $javascript, $alt);
4272
	}
4273
}
4274
4275
/**
4276
 * Crée un lien ayant une icone
4277
 *
4278
 * @uses prepare_icone_base()
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, tel que `left`, `right` pour définir un alignement
4290
 * @param string $javascript
4291
 *     Javascript ajouté sur le lien
4292
 * @return string
4293
 *     Code HTML du lien
4294
 **/
4295
function icone_base($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4296
	return prepare_icone_base('lien', $lien, $texte, $fond, $fonction, $class, $javascript);
4297
}
4298
4299
/**
4300
 * Crée un lien précédé d'une icone au dessus du texte
4301
 *
4302
 * @uses icone_base()
4303
 * @see  icone_verticale() Pour un usage dans un code PHP.
4304
 *
4305
 * @filtre
4306
 * @example
4307
 *     ```
4308
 *     [(#AUTORISER{voir,groupemots,#ID_GROUPE})
4309
 *         [(#URL_ECRIRE{groupe_mots,id_groupe=#ID_GROUPE}
4310
 *            |icone_verticale{<:mots:icone_voir_groupe_mots:>,groupe_mots-24.png,'',left})]
4311
 *    ]
4312
 *     ```
4313
 *
4314
 * @param string $lien
4315
 *     URL du lien
4316
 * @param string $texte
4317
 *     Texte du lien
4318
 * @param string $fond
4319
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4320
 * @param string $fonction
4321
 *     Fonction du lien (`edit`, `new`, `del`)
4322
 * @param string $class
4323
 *     Classe CSS à ajouter, tel que `left`, `right`, `center` pour définir un alignement.
4324
 *     Il peut y en avoir plusieurs : `left ajax`
4325
 * @param string $javascript
4326
 *     Javascript ajouté sur le lien
4327
 * @return string
4328
 *     Code HTML du lien
4329
 **/
4330
function filtre_icone_verticale_dist($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4331
	return icone_base($lien, $texte, $fond, $fonction, "verticale $class", $javascript);
4332
}
4333
4334
/**
4335
 * Crée un lien précédé d'une icone horizontale
4336
 *
4337
 * @uses icone_base()
4338
 * @see  icone_horizontale() Pour un usage dans un code PHP.
4339
 *
4340
 * @filtre
4341
 * @example
4342
 *     En tant que filtre dans un squelettes :
4343
 *     ```
4344
 *     [(#URL_ECRIRE{sites}|icone_horizontale{<:sites:icone_voir_sites_references:>,site-24.png})]
4345
 *
4346
 *     [(#AUTORISER{supprimer,groupemots,#ID_GROUPE}|oui)
4347
 *         [(#URL_ACTION_AUTEUR{supprimer_groupe_mots,#ID_GROUPE,#URL_ECRIRE{mots}}
4348
 *             |icone_horizontale{<:mots:icone_supprimer_groupe_mots:>,groupe_mots,del})]
4349
 *     ]
4350
 *     ```
4351
 *
4352
 *     En tant que filtre dans un code php :
4353
 *     ```
4354
 *     $icone_horizontale=chercher_filtre('icone_horizontale');
4355
 *     $icone = $icone_horizontale(generer_url_ecrire("stats_visites","id_article=$id_article"),
4356
 *         _T('statistiques:icone_evolution_visites', array('visites' => $visites)),
4357
 *         "statistique-24.png");
4358
 *     ```
4359
 *
4360
 * @param string $lien
4361
 *     URL du lien
4362
 * @param string $texte
4363
 *     Texte du lien
4364
 * @param string $fond
4365
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4366
 * @param string $fonction
4367
 *     Fonction du lien (`edit`, `new`, `del`)
4368
 * @param string $class
4369
 *     Classe CSS à ajouter
4370
 * @param string $javascript
4371
 *     Javascript ajouté sur le lien
4372
 * @return string
4373
 *     Code HTML du lien
4374
 **/
4375
function filtre_icone_horizontale_dist($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4376
	return icone_base($lien, $texte, $fond, $fonction, "horizontale $class", $javascript);
4377
}
4378
4379
/**
4380
 * Crée un bouton d'action intégrant une icone horizontale
4381
 *
4382
 * @uses prepare_icone_base()
4383
 *
4384
 * @filtre
4385
 * @example
4386
 *     ```
4387
 *     [(#URL_ACTION_AUTEUR{supprimer_mot, #ID_MOT, #URL_ECRIRE{groupe_mots,id_groupe=#ID_GROUPE}}
4388
 *         |bouton_action_horizontal{<:mots:info_supprimer_mot:>,mot-24.png,del})]
4389
 *     ```
4390
 *
4391
 * @param string $lien
4392
 *     URL de l'action
4393
 * @param string $texte
4394
 *     Texte du bouton
4395
 * @param string $fond
4396
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4397
 * @param string $fonction
4398
 *     Fonction du bouton (`edit`, `new`, `del`)
4399
 * @param string $class
4400
 *     Classe CSS à ajouter
4401
 * @param string $confirm
4402
 *     Message de confirmation à ajouter en javascript sur le bouton
4403
 * @return string
4404
 *     Code HTML du lien
4405
 **/
4406
function filtre_bouton_action_horizontal_dist($lien, $texte, $fond, $fonction = "", $class = "", $confirm = "") {
4407
	return prepare_icone_base('bouton', $lien, $texte, $fond, $fonction, "horizontale $class", $confirm);
4408
}
4409
4410
/**
4411
 * Filtre `icone` pour compatibilité mappé sur `icone_base`
4412
 *
4413
 * @uses icone_base()
4414
 * @see  filtre_icone_verticale_dist()
4415
 *
4416
 * @filtre
4417
 * @deprecated Utiliser le filtre `icone_verticale`
4418
 *
4419
 * @param string $lien
4420
 *     URL du lien
4421
 * @param string $texte
4422
 *     Texte du lien
4423
 * @param string $fond
4424
 *     Nom de l'image utilisée
4425
 * @param string $align
4426
 *     Classe CSS d'alignement (`left`, `right`, `center`)
4427
 * @param string $fonction
4428
 *     Fonction du lien (`edit`, `new`, `del`)
4429
 * @param string $class
4430
 *     Classe CSS à ajouter
4431
 * @param string $javascript
4432
 *     Javascript ajouté sur le lien
4433
 * @return string
4434
 *     Code HTML du lien
4435
 */
4436
function filtre_icone_dist($lien, $texte, $fond, $align = "", $fonction = "", $class = "", $javascript = "") {
4437
	return icone_base($lien, $texte, $fond, $fonction, "verticale $align $class", $javascript);
4438
}
4439
4440
4441
/**
4442
 * Explose un texte en tableau suivant un séparateur
4443
 *
4444
 * @note
4445
 *     Inverse l'écriture de la fonction PHP de même nom
4446
 *     pour que le filtre soit plus pratique dans les squelettes
4447
 *
4448
 * @filtre
4449
 * @example
4450
 *     ```
4451
 *     [(#GET{truc}|explode{-})]
4452
 *     ```
4453
 *
4454
 * @param string $a Texte
4455
 * @param string $b Séparateur
4456
 * @return array Liste des éléments
4457
 */
4458
function filtre_explode_dist($a, $b) { return explode($b, $a); }
4459
4460
/**
4461
 * Implose un tableau en chaine en liant avec un séparateur
4462
 *
4463
 * @note
4464
 *     Inverse l'écriture de la fonction PHP de même nom
4465
 *     pour que le filtre soit plus pratique dans les squelettes
4466
 *
4467
 * @filtre
4468
 * @example
4469
 *     ```
4470
 *     [(#GET{truc}|implode{-})]
4471
 *     ```
4472
 *
4473
 * @param array $a Tableau
4474
 * @param string $b Séparateur
4475
 * @return string Texte
4476
 */
4477
function filtre_implode_dist($a, $b) { return is_array($a) ? implode($b, $a) : $a; }
4478
4479
/**
4480
 * Produire les styles privés qui associent item de menu avec icone en background
4481
 *
4482
 * @return string Code CSS
4483
 */
4484
function bando_images_background() {
4485
	include_spip('inc/bandeau');
4486
	// recuperer tous les boutons et leurs images
4487
	$boutons = definir_barre_boutons(definir_barre_contexte(), true, false);
4488
4489
	$res = "";
4490
	foreach ($boutons as $page => $detail) {
4491
		if ($detail->icone and strlen(trim($detail->icone))) {
4492
			$res .= "\n.navigation_avec_icones #bando1_$page {background-image:url(" . $detail->icone . ");}";
4493
		}
4494
		$selecteur = (in_array($page, array('outils_rapides', 'outils_collaboratifs')) ? "" : ".navigation_avec_icones ");
4495
		if (is_array($detail->sousmenu)) {
4496
			foreach ($detail->sousmenu as $souspage => $sousdetail) {
4497
				if ($sousdetail->icone and strlen(trim($sousdetail->icone))) {
4498
					$res .= "\n$selecteur.bando2_$souspage {background-image:url(" . $sousdetail->icone . ");}";
4499
				}
4500
			}
4501
		}
4502
	}
4503
4504
	return $res;
4505
}
4506
4507
/**
4508
 * Generer un bouton_action
4509
 * utilise par #BOUTON_ACTION
4510
 *
4511
 * @param string $libelle
4512
 * @param string $url
4513
 * @param string $class
4514
 * @param string $confirm
4515
 *   message de confirmation oui/non avant l'action
4516
 * @param string $title
4517
 * @param string $callback
4518
 *   callback js a appeler lors de l'evenement action (apres confirmation eventuelle si $confirm est non vide)
4519
 *   et avant execution de l'action. Si la callback renvoie false, elle annule le declenchement de l'action
4520
 * @return string
4521
 */
4522
function bouton_action($libelle, $url, $class = "", $confirm = "", $title = "", $callback = "") {
4523
	if ($confirm) {
4524
		$confirm = "confirm(\"" . attribut_html($confirm) . "\")";
4525
		if ($callback) {
4526
			$callback = "$confirm?($callback):false";
4527
		} else {
4528
			$callback = $confirm;
4529
		}
4530
	}
4531
	$onclick = $callback ? " onclick='return " . addcslashes($callback, "'") . "'" : "";
4532
	$title = $title ? " title='$title'" : "";
4533
4534
	return "<form class='bouton_action_post $class' method='post' action='$url'><div>" . form_hidden($url)
4535
	. "<button type='submit' class='submit'$title$onclick>$libelle</button></div></form>";
4536
}
4537
4538
/**
4539
 * Donner n'importe quelle information sur un objet de maniere generique.
4540
 *
4541
 * La fonction va gerer en interne deux cas particuliers les plus utilises :
4542
 * l'URL et le titre (qui n'est pas forcemment le champ SQL "titre").
4543
 *
4544
 * On peut ensuite personnaliser les autres infos en creant une fonction
4545
 * generer_<nom_info>_entite($id_objet, $type_objet, $ligne).
4546
 * $ligne correspond a la ligne SQL de tous les champs de l'objet, les fonctions
4547
 * de personnalisation n'ont donc pas a refaire de requete.
4548
 *
4549
 * @param int $id_objet
4550
 * @param string $type_objet
4551
 * @param string $info
4552
 * @param string $etoile
4553
 * @return string
4554
 */
4555
function generer_info_entite($id_objet, $type_objet, $info, $etoile = "") {
4556
	static $trouver_table = null;
4557
	static $objets;
4558
4559
	// On verifie qu'on a tout ce qu'il faut
4560
	$id_objet = intval($id_objet);
4561
	if (!($id_objet and $type_objet and $info)) {
4562
		return '';
4563
	}
4564
4565
	// si on a deja note que l'objet n'existe pas, ne pas aller plus loin
4566
	if (isset($objets[$type_objet]) and $objets[$type_objet] === false) {
4567
		return '';
4568
	}
4569
4570
	// Si on demande l'url, on retourne direct la fonction
4571
	if ($info == 'url') {
4572
		return generer_url_entite($id_objet, $type_objet);
4573
	}
4574
4575
	// Sinon on va tout chercher dans la table et on garde en memoire
4576
	$demande_titre = ($info == 'titre');
4577
4578
	// 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
4579
	if (!isset($objets[$type_objet][$id_objet])
4580
		or
4581
		($demande_titre and !isset($objets[$type_objet][$id_objet]['titre']))
4582
	) {
4583
		if (!$trouver_table) {
4584
			$trouver_table = charger_fonction('trouver_table', 'base');
4585
		}
4586
		$desc = $trouver_table(table_objet_sql($type_objet));
4587
		if (!$desc) {
4588
			return $objets[$type_objet] = false;
4589
		}
4590
4591
		// Si on demande le titre, on le gere en interne
4592
		$champ_titre = "";
4593
		if ($demande_titre) {
4594
			// si pas de titre declare mais champ titre, il sera peuple par le select *
4595
			$champ_titre = (!empty($desc['titre'])) ? ', ' . $desc['titre'] : '';
4596
		}
4597
		include_spip('base/abstract_sql');
4598
		include_spip('base/connect_sql');
4599
		$objets[$type_objet][$id_objet] = sql_fetsel(
4600
			'*' . $champ_titre,
4601
			$desc['table_sql'],
4602
			id_table_objet($type_objet) . ' = ' . intval($id_objet)
4603
		);
4604
	}
4605
4606
	// Si la fonction generer_TRUC_TYPE existe, on l'utilise pour formater $info_generee
4607
	if ($generer = charger_fonction("generer_${info}_${type_objet}", '', true)) {
4608
		$info_generee = $generer($id_objet, $objets[$type_objet][$id_objet]);
4609
	} // Si la fonction generer_TRUC_entite existe, on l'utilise pour formater $info_generee
4610
	else {
4611
		if ($generer = charger_fonction("generer_${info}_entite", '', true)) {
4612
			$info_generee = $generer($id_objet, $type_objet, $objets[$type_objet][$id_objet]);
4613
		} // Sinon on prend directement le champ SQL tel quel
4614
		else {
4615
			$info_generee = (isset($objets[$type_objet][$id_objet][$info]) ? $objets[$type_objet][$id_objet][$info] : '');
4616
		}
4617
	}
4618
4619
	// On va ensuite appliquer les traitements automatiques si besoin
4620
	if (!$etoile) {
4621
		// FIXME: on fournit un ENV minimum avec id et type et connect=''
4622
		// mais ce fonctionnement est a ameliorer !
4623
		$info_generee = appliquer_traitement_champ($info_generee, $info, table_objet($type_objet),
4624
			array('id_objet' => $id_objet, 'objet' => $type_objet, ''));
4625
	}
4626
4627
	return $info_generee;
4628
}
4629
4630
/**
4631
 * Appliquer a un champ SQL le traitement qui est configure pour la balise homonyme dans les squelettes
4632
 *
4633
 * @param string $texte
4634
 * @param string $champ
4635
 * @param string $table_objet
4636
 * @param array $env
4637
 * @param string $connect
4638
 * @return string
4639
 */
4640
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...
4641
	if (!$champ) {
4642
		return $texte;
4643
	}
4644
	
4645
	// On charge toujours les filtres de texte car la majorité des traitements les utilisent
4646
	// et il ne faut pas partir du principe que c'est déjà chargé (form ajax, etc)
4647
	include_spip('inc/texte');
4648
	
4649
	$champ = strtoupper($champ);
4650
	$traitements = isset($GLOBALS['table_des_traitements'][$champ]) ? $GLOBALS['table_des_traitements'][$champ] : false;
4651
	if (!$traitements or !is_array($traitements)) {
4652
		return $texte;
4653
	}
4654
4655
	$traitement = '';
4656
	if ($table_objet and (!isset($traitements[0]) or count($traitements) > 1)) {
4657
		// necessaire pour prendre en charge les vieux appels avec un table_objet_sql en 3e arg
4658
		$table_objet = table_objet($table_objet);
4659
		if (isset($traitements[$table_objet])) {
4660
			$traitement = $traitements[$table_objet];
4661
		}
4662
	}
4663
	if (!$traitement and isset($traitements[0])) {
4664
		$traitement = $traitements[0];
4665
	}
4666
	// (sinon prendre le premier de la liste par defaut ?)
4667
4668
	if (!$traitement) {
4669
		return $texte;
4670
	}
4671
4672
	$traitement = str_replace('%s', "'" . texte_script($texte) . "'", $traitement);
4673
4674
	// Fournir $connect et $Pile[0] au traitement si besoin
4675
	$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...
4676
	eval("\$texte = $traitement;");
4677
4678
	return $texte;
4679
}
4680
4681
4682
/**
4683
 * Generer un lien (titre clicable vers url) vers un objet
4684
 *
4685
 * @param int $id_objet
4686
 * @param $objet
4687
 * @param int $longueur
4688
 * @param null|string $connect
4689
 * @return string
4690
 */
4691
function generer_lien_entite($id_objet, $objet, $longueur = 80, $connect = null) {
4692
	include_spip('inc/liens');
4693
	$titre = traiter_raccourci_titre($id_objet, $objet, $connect);
4694
	// lorsque l'objet n'est plus declare (plugin desactive par exemple)
4695
	// le raccourcis n'est plus valide
4696
	$titre = isset($titre['titre']) ? typo($titre['titre']) : '';
4697
	// on essaye avec generer_info_entite ?
4698
	if (!strlen($titre) and !$connect) {
4699
		$titre = generer_info_entite($id_objet, $objet, 'titre');
4700
	}
4701
	if (!strlen($titre)) {
4702
		$titre = _T('info_sans_titre');
4703
	}
4704
	$url = generer_url_entite($id_objet, $objet, '', '', $connect);
4705
4706
	return "<a href='$url' class='$objet'>" . couper($titre, $longueur) . "</a>";
4707
}
4708
4709
4710
/**
4711
 * Englobe (Wrap) un texte avec des balises
4712
 *
4713
 * @example `wrap('mot','<b>')` donne `<b>mot</b>'`
4714
 *
4715
 * @filtre
4716
 * @uses extraire_balises()
4717
 *
4718
 * @param string $texte
4719
 * @param string $wrap
4720
 * @return string
4721
 */
4722
function wrap($texte, $wrap) {
4723
	$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...
4724
	if (preg_match_all(",<([a-z]\w*)\b[^>]*>,UimsS", $wrap, $regs, PREG_PATTERN_ORDER)) {
4725
		$texte = $wrap . $texte;
4726
		$regs = array_reverse($regs[1]);
4727
		$wrap = "</" . implode("></", $regs) . ">";
4728
		$texte = $texte . $wrap;
4729
	}
4730
4731
	return $texte;
4732
}
4733
4734
4735
/**
4736
 * afficher proprement n'importe quoi
4737
 * On affiche in fine un pseudo-yaml qui premet de lire humainement les tableaux et de s'y reperer
4738
 *
4739
 * Les textes sont retournes avec simplement mise en forme typo
4740
 *
4741
 * 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
4742
 * les tableaux-listes (qui n'ont que des cles numeriques), sont affiches sous forme de liste separee par des virgules :
4743
 * c'est VOULU !
4744
 *
4745
 * @param $u
4746
 * @param string $join
4747
 * @param int $indent
4748
 * @return array|mixed|string
4749
 */
4750
function filtre_print_dist($u, $join = "<br />", $indent = 0) {
4751
	if (is_string($u)) {
4752
		$u = typo($u);
4753
4754
		return $u;
4755
	}
4756
4757
	// caster $u en array si besoin
4758
	if (is_object($u)) {
4759
		$u = (array)$u;
4760
	}
4761
4762
	if (is_array($u)) {
4763
		$out = "";
4764
		// toutes les cles sont numeriques ?
4765
		// et aucun enfant n'est un tableau
4766
		// liste simple separee par des virgules
4767
		$numeric_keys = array_map('is_numeric', array_keys($u));
4768
		$array_values = array_map('is_array', $u);
4769
		$object_values = array_map('is_object', $u);
4770
		if (array_sum($numeric_keys) == count($numeric_keys)
4771
			and !array_sum($array_values)
4772
			and !array_sum($object_values)
4773
		) {
4774
			return join(", ", array_map('filtre_print_dist', $u));
4775
		}
4776
4777
		// sinon on passe a la ligne et on indente
4778
		$i_str = str_pad("", $indent, " ");
4779
		foreach ($u as $k => $v) {
4780
			$out .= $join . $i_str . "$k: " . filtre_print_dist($v, $join, $indent + 2);
4781
		}
4782
4783
		return $out;
4784
	}
4785
4786
	// on sait pas quoi faire...
4787
	return $u;
4788
}
4789
4790
4791
/**
4792
 * Renvoyer l'info d'un objet
4793
 * telles que definies dans declarer_tables_objets_sql
4794
 *
4795
 * @param string $objet
4796
 * @param string $info
4797
 * @return string|array
4798
 */
4799
function objet_info($objet, $info) {
4800
	$table = table_objet_sql($objet);
4801
	$infos = lister_tables_objets_sql($table);
4802
4803
	return (isset($infos[$info]) ? $infos[$info] : '');
4804
}
4805
4806
/**
4807
 * Filtre pour afficher 'Aucun truc' ou '1 truc' ou 'N trucs'
4808
 * avec la bonne chaîne de langue en fonction de l'objet utilisé
4809
 *
4810
 * @param int $nb
4811
 *     Nombre d'éléments
4812
 * @param string $objet
4813
 *     Objet
4814
 * @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...
4815
 *     Texte traduit du comptage, tel que '3 articles'
4816
 */
4817
function objet_afficher_nb($nb, $objet) {
4818
	if (!$nb) {
4819
		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...
4820
	} else {
4821
		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...
4822
	}
4823
}
4824
4825
/**
4826
 * Filtre pour afficher l'img icone d'un objet
4827
 *
4828
 * @param string $objet
4829
 * @param int $taille
4830
 * @param string $class
4831
 * @return string
4832
 */
4833
function objet_icone($objet, $taille = 24, $class='') {
4834
	$icone = objet_info($objet, 'icone_objet') . "-" . $taille . ".png";
4835
	$icone = chemin_image($icone);
4836
	$balise_img = charger_filtre('balise_img');
4837
4838
	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...
4839
}
4840
4841
/**
4842
 * Renvoyer une traduction d'une chaine de langue contextuelle à un objet si elle existe,
4843
 * la traduction de la chaine generique
4844
 *
4845
 * Ex : [(#ENV{objet}|objet_label{trad_reference})]
4846
 * va chercher si une chaine objet:trad_reference existe et renvoyer sa trad le cas echeant
4847
 * sinon renvoie la trad de la chaine trad_reference
4848
 * Si la chaine fournie contient un prefixe il est remplacé par celui de l'objet pour chercher la chaine contextuelle
4849
 *
4850
 * Les arguments $args et $options sont ceux de la fonction _T
4851
 *
4852
 * @param string $objet
4853
 * @param string $chaine
4854
 * @param array $args
4855
 * @param array $options
4856
 * @return string
4857
 */
4858
function objet_T($objet, $chaine, $args = array(), $options = array()){
4859
	$chaine = explode(':',$chaine);
4860
	if ($t = _T($objet . ':' . end($chaine), $args, array_merge($options, array('force'=>false)))) {
4861
		return $t;
4862
	}
4863
	$chaine = implode(':',$chaine);
4864
	return _T($chaine, $args, $options);
4865
}
4866
4867
/**
4868
 * Fonction de secours pour inserer le head_css de facon conditionnelle
4869
 *
4870
 * Appelée en filtre sur le squelette qui contient #INSERT_HEAD,
4871
 * elle vérifie l'absence éventuelle de #INSERT_HEAD_CSS et y suplée si besoin
4872
 * pour assurer la compat avec les squelettes qui n'utilisent pas.
4873
 *
4874
 * @param string $flux Code HTML
4875
 * @return string      Code HTML
4876
 */
4877
function insert_head_css_conditionnel($flux) {
4878
	if (strpos($flux, '<!-- insert_head_css -->') === false
4879
		and $p = strpos($flux, '<!-- insert_head -->')
4880
	) {
4881
		// plutot avant le premier js externe (jquery) pour etre non bloquant
4882
		if ($p1 = stripos($flux, '<script src=') and $p1 < $p) {
4883
			$p = $p1;
4884
		}
4885
		$flux = substr_replace($flux, pipeline('insert_head_css', '<!-- insert_head_css -->'), $p, 0);
4886
	}
4887
4888
	return $flux;
4889
}
4890
4891
/**
4892
 * Produire un fichier statique à partir d'un squelette dynamique
4893
 *
4894
 * Permet ensuite à Apache de le servir en statique sans repasser
4895
 * par spip.php à chaque hit sur le fichier.
4896
 *
4897
 * Si le format (css ou js) est passe dans `contexte['format']`, on l'utilise
4898
 * sinon on regarde si le fond finit par .css ou .js, sinon on utilie "html"
4899
 *
4900
 * @uses urls_absolues_css()
4901
 *
4902
 * @param string $fond
4903
 * @param array $contexte
4904
 * @param array $options
4905
 * @param string $connect
4906
 * @return string
4907
 */
4908
function produire_fond_statique($fond, $contexte = array(), $options = array(), $connect = '') {
4909
	if (isset($contexte['format'])) {
4910
		$extension = $contexte['format'];
4911
		unset($contexte['format']);
4912
	} else {
4913
		$extension = "html";
4914
		if (preg_match(',[.](css|js|json)$,', $fond, $m)) {
4915
			$extension = $m[1];
4916
		}
4917
	}
4918
	// recuperer le contenu produit par le squelette
4919
	$options['raw'] = true;
4920
	$cache = recuperer_fond($fond, $contexte, $options, $connect);
4921
4922
	// calculer le nom de la css
4923
	$dir_var = sous_repertoire(_DIR_VAR, 'cache-' . $extension);
4924
	$nom_safe = preg_replace(",\W,", '_', str_replace('.', '_', $fond));
4925
	$contexte_implicite = calculer_contexte_implicite();
4926
4927
	// par defaut on hash selon les contextes qui sont a priori moins variables
4928
	// mais on peut hasher selon le contenu a la demande, si plusieurs contextes produisent un meme contenu
4929
	// reduit la variabilite du nom et donc le nombre de css concatenees possibles in fine
4930
	if (isset($options['hash_on_content']) and $options['hash_on_content']) {
4931
		$hash = md5($contexte_implicite['host'] . '::'. $cache);
4932
	}
4933
	else {
4934
		unset($contexte_implicite['notes']); // pas pertinent pour signaler un changeemnt de contenu pour des css/js
4935
		ksort($contexte);
4936
		$hash = md5($fond . json_encode($contexte_implicite) . json_encode($contexte) . $connect);
4937
	}
4938
	$filename = $dir_var . $extension . "dyn-$nom_safe-" . substr($hash, 0, 8) . ".$extension";
4939
4940
	// mettre a jour le fichier si il n'existe pas
4941
	// ou trop ancien
4942
	// le dernier fichier produit est toujours suffixe par .last
4943
	// et recopie sur le fichier cible uniquement si il change
4944
	if (!file_exists($filename)
4945
		or !file_exists($filename . ".last")
4946
		or (isset($cache['lastmodified']) and $cache['lastmodified'] and filemtime($filename . ".last") < $cache['lastmodified'])
4947
		or (defined('_VAR_MODE') and _VAR_MODE == 'recalcul')
4948
	) {
4949
		$contenu = $cache['texte'];
4950
		// passer les urls en absolu si c'est une css
4951
		if ($extension == "css") {
4952
			$contenu = urls_absolues_css($contenu,
4953
				test_espace_prive() ? generer_url_ecrire('accueil') : generer_url_public($fond));
4954
		}
4955
4956
		$comment = '';
4957
		// ne pas insérer de commentaire si c'est du json
4958
		if ($extension != "json") {
4959
			$comment = "/* #PRODUIRE{fond=$fond";
4960
			foreach ($contexte as $k => $v) {
4961
				$comment .= ",$k=$v";
4962
			}
4963
			// pas de date dans le commentaire car sinon ca invalide le md5 et force la maj
4964
			// mais on peut mettre un md5 du contenu, ce qui donne un aperu rapide si la feuille a change ou non
4965
			$comment .= "}\n   md5:" . md5($contenu) . " */\n";
4966
		}
4967
		// et ecrire le fichier si il change
4968
		ecrire_fichier_calcule_si_modifie($filename, $comment . $contenu, false, true);
4969
	}
4970
4971
	return timestamp($filename);
4972
}
4973
4974
/**
4975
 * Ajouter un timestamp a une url de fichier
4976
 * [(#CHEMIN{monfichier}|timestamp)]
4977
 *
4978
 * @param string $fichier
4979
 *    Le chemin du fichier sur lequel on souhaite ajouter le timestamp
4980
 * @return string
4981
 *    $fichier auquel on a ajouté le timestamp
4982
 */
4983
function timestamp($fichier) {
4984
	if (!$fichier
4985
		or !file_exists($fichier)
4986
		or !$m = filemtime($fichier)
4987
	) {
4988
		return $fichier;
4989
	}
4990
4991
	return "$fichier?$m";
4992
}
4993
4994
/**
4995
 * Supprimer le timestamp d'une url
4996
 *
4997
 * @param string $url
4998
 * @return string
4999
 */
5000
function supprimer_timestamp($url) {
5001
	if (strpos($url, "?") === false) {
5002
		return $url;
5003
	}
5004
5005
	return preg_replace(",\?[[:digit:]]+$,", "", $url);
5006
}
5007
5008
/**
5009
 * Nettoyer le titre d'un email
5010
 *
5011
 * Éviter une erreur lorsqu'on utilise `|nettoyer_titre_email` dans un squelette de mail
5012
 *
5013
 * @filtre
5014
 * @uses nettoyer_titre_email()
5015
 *
5016
 * @param string $titre
5017
 * @return string
5018
 */
5019
function filtre_nettoyer_titre_email_dist($titre) {
5020
	include_spip('inc/envoyer_mail');
5021
5022
	return nettoyer_titre_email($titre);
5023
}
5024
5025
/**
5026
 * Afficher le sélecteur de rubrique
5027
 *
5028
 * Il permet de placer un objet dans la hiérarchie des rubriques de SPIP
5029
 *
5030
 * @uses chercher_rubrique()
5031
 *
5032
 * @param string $titre
5033
 * @param int $id_objet
5034
 * @param int $id_parent
5035
 * @param string $objet
5036
 * @param int $id_secteur
5037
 * @param bool $restreint
5038
 * @param bool $actionable
5039
 *   true : fournit le selecteur dans un form directement postable
5040
 * @param bool $retour_sans_cadre
5041
 * @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...
5042
 */
5043
function filtre_chercher_rubrique_dist(
5044
	$titre,
5045
	$id_objet,
5046
	$id_parent,
5047
	$objet,
5048
	$id_secteur,
5049
	$restreint,
5050
	$actionable = false,
5051
	$retour_sans_cadre = false
5052
) {
5053
	include_spip('inc/filtres_ecrire');
5054
5055
	return chercher_rubrique($titre, $id_objet, $id_parent, $objet, $id_secteur, $restreint, $actionable,
5056
		$retour_sans_cadre);
5057
}
5058
5059
/**
5060
 * Rediriger une page suivant une autorisation,
5061
 * et ce, n'importe où dans un squelette, même dans les inclusions.
5062
 *
5063
 * En l'absence de redirection indiquée, la fonction redirige par défaut
5064
 * sur une 403 dans l'espace privé et 404 dans l'espace public.
5065
 *
5066
 * @example
5067
 *     ```
5068
 *     [(#AUTORISER{non}|sinon_interdire_acces)]
5069
 *     [(#AUTORISER{non}|sinon_interdire_acces{#URL_PAGE{login}, 401})]
5070
 *     ```
5071
 *
5072
 * @filtre
5073
 * @param bool $ok
5074
 *     Indique si l'on doit rediriger ou pas
5075
 * @param string $url
5076
 *     Adresse eventuelle vers laquelle rediriger
5077
 * @param int $statut
5078
 *     Statut HTML avec lequel on redirigera
5079
 * @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...
5080
 *     message d'erreur
5081
 * @return string|void
5082
 *     Chaîne vide si l'accès est autorisé
5083
 */
5084
function sinon_interdire_acces($ok = false, $url = '', $statut = 0, $message = null) {
5085
	if ($ok) {
5086
		return '';
5087
	}
5088
5089
	// Vider tous les tampons
5090
	$level = @ob_get_level();
5091
	while ($level--) {
5092
		@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...
5093
	}
5094
5095
	include_spip('inc/headers');
5096
5097
	// S'il y a une URL, on redirige (si pas de statut, la fonction mettra 302 par défaut)
5098
	if ($url) {
5099
		redirige_par_entete($url, '', $statut);
5100
	}
5101
5102
	// ecriture simplifiee avec message en 3eme argument (= statut 403)
5103
	if (!is_numeric($statut) and is_null($message)) {
5104
		$message = $statut;
5105
		$statut = 0;
5106
	}
5107
	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...
5108
		$message = '';
5109
	}
5110
	$statut = intval($statut);
5111
5112
	// Si on est dans l'espace privé, on génère du 403 Forbidden par defaut ou du 404
5113
	if (test_espace_prive()) {
5114
		if (!$statut or !in_array($statut, array(404, 403))) {
5115
			$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...
5116
		}
5117
		http_status(403);
5118
		$echec = charger_fonction('403', 'exec');
5119
		$echec($message);
5120
	} else {
5121
		// Sinon dans l'espace public on redirige vers une 404 par défaut, car elle toujours présente normalement
5122
		if (!$statut) {
5123
			$statut = 404;
5124
		}
5125
		// Dans tous les cas on modifie l'entité avec ce qui est demandé
5126
		http_status($statut);
5127
		// Si le statut est une erreur et qu'il n'y a pas de redirection on va chercher le squelette du même nom
5128
		if ($statut >= 400) {
5129
			echo recuperer_fond("$statut", array('erreur' => $message));
5130
		}
5131
	}
5132
5133
5134
	exit;
5135
}
5136
5137
/**
5138
 * Assurer le fonctionnement de |compacte meme sans l'extension compresseur
5139
 *
5140
 * @param string $source
5141
 * @param null|string $format
5142
 * @return string
5143
 */
5144
function filtre_compacte_dist($source, $format = null) {
5145
	if (function_exists('compacte')) {
5146
		return compacte($source, $format);
5147
	}
5148
5149
	return $source;
5150
}
5151
5152
5153
/**
5154
 * Afficher partiellement un mot de passe que l'on ne veut pas rendre lisible par un champ hidden
5155
 * @param string $passe
5156
 * @param bool $afficher_partiellement
5157
 * @param int|null $portion_pourcent
5158
 * @return string
5159
 */
5160
function spip_affiche_mot_de_passe_masque($passe, $afficher_partiellement = false, $portion_pourcent = null) {
5161
	$l = strlen($passe);
5162
5163
	if ($l<=8 or !$afficher_partiellement){
5164
		if (!$l) {
5165
			return ''; // montrer qu'il y a pas de mot de passe si il y en a pas
5166
		}
5167
		return str_pad('',$afficher_partiellement ? $l : 16,'*');
5168
	}
5169
5170
	if (is_null($portion_pourcent)) {
5171
		if (!defined('_SPIP_AFFICHE_MOT_DE_PASSE_MASQUE_PERCENT')) {
5172
			define('_SPIP_AFFICHE_MOT_DE_PASSE_MASQUE_PERCENT', 20); // 20%
5173
		}
5174
		$portion_pourcent = _SPIP_AFFICHE_MOT_DE_PASSE_MASQUE_PERCENT;
5175
	}
5176
	if ($portion_pourcent >= 100) {
5177
		return $passe;
5178
	}
5179
	$e = intval(ceil($l * $portion_pourcent / 100 / 2));
5180
	$e = max($e, 0);
5181
	$mid = str_pad('',$l-2*$e,'*');
5182
	if ($e>0 and strlen($mid)>8){
5183
		$mid = '***...***';
5184
	}
5185
	return substr($passe,0,$e) . $mid . ($e > 0 ? substr($passe,-$e) : '');
5186
}
5187
5188
5189
/**
5190
 * Cette fonction permet de transformer un texte clair en nom court pouvant servir d'identifiant, class, id, url...
5191
 * en ne conservant que des caracteres alphanumeriques et un separateur
5192
 *
5193
 * @param string $texte
5194
 *   Texte à transformer en nom machine
5195
 * @param string $type
5196
 *
5197
 * @param array $options
5198
 *   string separateur : par défaut, un underscore `_`.
5199
 *   int longueur_maxi : par defaut 60
5200
 *   int longueur_mini : par defaut 0
5201
 *
5202
 * @return string
5203
 */
5204
function identifiant_slug($texte, $type = '', $options = array()) {
5205
5206
	$original = $texte;
5207
	$separateur = (isset($options['separateur'])?$options['separateur']:'_');
5208
	$longueur_maxi = (isset($options['longueur_maxi'])?$options['longueur_maxi']:60);
5209
	$longueur_mini = (isset($options['longueur_mini'])?$options['longueur_mini']:0);
5210
5211
	if (!function_exists('translitteration')) {
5212
		include_spip('inc/charsets');
5213
	}
5214
5215
	// pas de balise html
5216
	if (strpos($texte, '<') !== false) {
5217
		$texte = strip_tags($texte);
5218
	}
5219
	if (strpos($texte, '&') !== false) {
5220
		$texte = unicode2charset($texte);
5221
	}
5222
	// On enlève les espaces indésirables
5223
	$texte = trim($texte);
5224
5225
	// On enlève les accents et cie
5226
	$texte = translitteration($texte);
5227
5228
	// On remplace tout ce qui n'est pas un mot par un séparateur
5229
	$texte = preg_replace(',[\W_]+,ms', $separateur, $texte);
5230
5231
	// nettoyer les doubles occurences du separateur si besoin
5232
	while (strpos($texte, "$separateur$separateur") !== false) {
5233
		$texte = str_replace("$separateur$separateur", $separateur, $texte);
5234
	}
5235
5236
	// pas de separateur au debut ni a la fin
5237
	$texte = trim($texte, $separateur);
5238
5239
	// en minuscules
5240
	$texte = strtolower($texte);
5241
5242
	switch ($type) {
5243
		case 'class':
5244
		case 'id':
5245
		case 'anchor':
5246
			if (preg_match(',^\d,', $texte)) {
5247
				$texte = substr($type, 0, 1).$texte;
5248
			}
5249
	}
5250
5251
	if (strlen($texte)>$longueur_maxi) {
5252
		$texte = substr($texte, 0, $longueur_maxi);
5253
	}
5254
5255
	if (strlen($texte) < $longueur_mini and $longueur_mini < $longueur_maxi) {
5256
		if (preg_match(',^\d,', $texte)) {
5257
			$texte = ($type ? substr($type,0,1) : "s") . $texte;
5258
		}
5259
		$texte .= $separateur . md5($original);
5260
		$texte = substr($texte, 0, $longueur_mini);
5261
	}
5262
5263
	return $texte;
5264
}
5265