Completed
Push — master ( cc4985...b1dd17 )
by cam
04:35
created

filtres.php ➔ filtre_match_dist()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 4
dl 0
loc 18
rs 9.3554
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2019                                                *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
10
 *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Déclaration de filtres pour les squelettes
15
 *
16
 * @package SPIP\Core\Filtres
17
 **/
18
if (!defined('_ECRIRE_INC_VERSION')) {
19
	return;
20
}
21
22
include_spip('inc/charsets');
23
include_spip('inc/filtres_mini');
24
include_spip('inc/filtres_dates');
25
include_spip('inc/filtres_selecteur_generique');
26
include_spip('base/objets');
27
include_spip('public/parametrer'); // charger les fichiers fonctions
28
29
/**
30
 * Charger un filtre depuis le php
31
 *
32
 * - on inclue tous les fichiers fonctions des plugins et du skel
33
 * - on appelle chercher_filtre
34
 *
35
 * Pour éviter de perdre le texte si le filtre demandé est introuvable,
36
 * on transmet `filtre_identite_dist` en filtre par défaut.
37
 *
38
 * @uses filtre_identite_dist() Comme fonction par défaut
39
 *
40
 * @param string $fonc Nom du filtre
41
 * @param string $default Filtre par défaut
42
 * @return string Fonction PHP correspondante du filtre
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string?

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

Loading history...
43
 */
44
function charger_filtre($fonc, $default = 'filtre_identite_dist') {
45
	include_spip('public/parametrer'); // inclure les fichiers fonctions
46
	return chercher_filtre($fonc, $default);
47
}
48
49
/**
50
 * Retourne le texte tel quel
51
 *
52
 * @param string $texte Texte
53
 * @return string Texte
54
 **/
55
function filtre_identite_dist($texte) { return $texte; }
56
57
/**
58
 * Cherche un filtre
59
 *
60
 * Pour une filtre `F` retourne la première fonction trouvée parmis :
61
 *
62
 * - filtre_F
63
 * - filtre_F_dist
64
 * - F
65
 *
66
 * Peut gérer des appels par des fonctions statiques de classes tel que `Foo::Bar`
67
 *
68
 * En absence de fonction trouvée, retourne la fonction par défaut indiquée.
69
 *
70
 * @param string $fonc
71
 *     Nom du filtre
72
 * @param null $default
73
 *     Nom du filtre appliqué par défaut si celui demandé n'est pas trouvé
74
 * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be null|string?

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

Loading history...
75
 *     Fonction PHP correspondante du filtre demandé
76
 */
77
function chercher_filtre($fonc, $default = null) {
78
	if (!$fonc) {
79
		return $default;
80
	}
81
	// Cas des types mime, sans confondre avec les appels de fonction de classe
82
	// Foo::Bar
83
	// qui peuvent etre avec un namespace : space\Foo::Bar
84
	if (preg_match(',^[\w]+/,', $fonc)) {
85
		$nom = preg_replace(',\W,', '_', $fonc);
86
		$f = chercher_filtre($nom);
87
		// cas du sous-type MIME sans filtre associe, passer au type:
88
		// si filtre_text_plain pas defini, passe a filtre_text
89
		if (!$f and $nom !== $fonc) {
90
			$f = chercher_filtre(preg_replace(',\W.*$,', '', $fonc));
91
		}
92
93
		return $f;
94
	}
95
	foreach (array('filtre_' . $fonc, 'filtre_' . $fonc . '_dist', $fonc) as $f) {
96
		trouver_filtre_matrice($f); // charge des fichiers spécifiques éventuels
97
		// fonction ou name\space\fonction
98
		if (is_callable($f)) {
99
			return $f;
100
		}
101
		// méthode statique d'une classe Classe::methode ou name\space\Classe::methode
102
		elseif (false === strpos($f, '::') and is_callable(array($f))) {
103
			return $f;
104
		}
105
	}
106
107
	return $default;
108
}
109
110
/**
111
 * Applique un filtre
112
 *
113
 * Fonction générique qui prend en argument l’objet (texte, etc) à modifier
114
 * et le nom du filtre. Retrouve les arguments du filtre demandé dans les arguments
115
 * transmis à cette fonction, via func_get_args().
116
 *
117
 * @see filtrer() Assez proche
118
 *
119
 * @param mixed $arg
120
 *     Texte (le plus souvent) sur lequel appliquer le filtre
121
 * @param string $filtre
122
 *     Nom du filtre à appliquer
123
 * @param bool $force
0 ignored issues
show
Documentation introduced by
Should the type for parameter $force not be boolean|null?

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

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

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

Loading history...
124
 *     La fonction doit-elle retourner le texte ou rien si le filtre est absent ?
125
 * @return string
126
 *     Texte traité par le filtre si le filtre existe,
127
 *     Texte d'origine si le filtre est introuvable et si $force à `true`
128
 *     Chaîne vide sinon (filtre introuvable).
129
 **/
130
function appliquer_filtre($arg, $filtre, $force = null) {
131
	$f = chercher_filtre($filtre);
132
	if (!$f) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $f of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

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

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

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

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
133
		if (!$force) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $force of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

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

$a = canBeFalseAndNull();

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

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