Completed
Push — master ( 2142b8...901fc5 )
by cam
27:22 queued 18:57
created

filtres.php ➔ form_hidden()   D

Complexity

Conditions 18
Paths 324

Size

Total Lines 73
Code Lines 45

Duplication

Lines 8
Ratio 10.96 %

Importance

Changes 0
Metric Value
cc 18
eloc 45
nc 324
nop 1
dl 8
loc 73
rs 4.074
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Systeme de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright (c) 2001-2016                                                *
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 SVN, il est ajouté entre crochets.
151
 * Si effectivement le SPIP est installé par SVN, 'SVN' est ajouté avant sa révision.
152
 *
153
 * @global spip_version_affichee Contient la version de SPIP
154
 * @uses version_svn_courante() Pour trouver le numéro de révision SVN
155
 *
156
 * @return string
157
 *     Version de SPIP
158
 **/
159
function spip_version() {
160
	$version = $GLOBALS['spip_version_affichee'];
161 View Code Duplication
	if ($svn_revision = version_svn_courante(_DIR_RACINE)) {
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...
162
		$version .= ($svn_revision < 0 ? ' SVN' : '') . ' [' . abs($svn_revision) . ']';
163
	}
164
165
	return $version;
166
}
167
168
169
/**
170
 * Retrouve un numéro de révision SVN d'un répertoire
171
 *
172
 * Mention de la révision SVN courante d'un répertoire
173
 * Retourne un nombre négatif si on est sur .svn, et positif si on utilise svn.revision
174
 *
175
 * @param string $dir Chemin du répertoire
176
 * @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...
177
 *
178
 *     - 0 si aucune info trouvée
179
 *     - NN (entier) si info trouvée par svn.revision (créé par le générateur de paquet Zip)
180
 *     - -NN (entier) si info trouvée par .svn/entries
181
 *
182
 **/
183
function version_svn_courante($dir) {
184
	if (!$dir) {
185
		$dir = '.';
186
	}
187
188
	// version installee par paquet ZIP
189
	if (lire_fichier($dir . '/svn.revision', $c)
190
		and preg_match(',Revision: (\d+),', $c, $d)
191
	) {
192
		return intval($d[1]);
193
	}
194
195
	// version installee par SVN
196
	if (file_exists($dir . '/.svn/wc.db') && class_exists('SQLite3')) {
197
		$db = new SQLite3($dir . '/.svn/wc.db');
198
		$result = $db->query('SELECT changed_revision FROM nodes WHERE local_relpath = "" LIMIT 1');
199
		if ($result) {
200
			$row = $result->fetchArray();
201
			if ($row['changed_revision'] != "") {
202
				return -$row['changed_revision'];
203
			}
204
		}
205
	} else if (lire_fichier($dir . '/.svn/entries', $c)
206
		and (
207
			(preg_match_all(
208
					',committed-rev="([0-9]+)",', $c, $r1, PREG_PATTERN_ORDER)
209
				and $v = max($r1[1])
210
			)
211
			or
212
			(preg_match(',^\d.*dir[\r\n]+(\d+),ms', $c, $r1) # svn >= 1.4
213
				and $v = $r1[1]
214
			))
215
	) {
216
		return -$v;
217
	}
218
219
	// Bug ou paquet fait main
220
	return 0;
221
}
222
223
// La matrice est necessaire pour ne filtrer _que_ des fonctions definies dans filtres_images
224
// et laisser passer les fonctions personnelles baptisees image_...
225
$GLOBALS['spip_matrice']['image_graver'] = true;//'inc/filtres_images_mini.php';
226
$GLOBALS['spip_matrice']['image_select'] = true;//'inc/filtres_images_mini.php';
227
$GLOBALS['spip_matrice']['image_reduire'] = true;//'inc/filtres_images_mini.php';
228
$GLOBALS['spip_matrice']['image_reduire_par'] = true;//'inc/filtres_images_mini.php';
229
$GLOBALS['spip_matrice']['image_passe_partout'] = true;//'inc/filtres_images_mini.php';
230
231
$GLOBALS['spip_matrice']['couleur_html_to_hex'] = 'inc/filtres_images_mini.php';
232
$GLOBALS['spip_matrice']['couleur_foncer'] = 'inc/filtres_images_mini.php';
233
$GLOBALS['spip_matrice']['couleur_eclaircir'] = 'inc/filtres_images_mini.php';
234
235
// ou pour inclure un script au moment ou l'on cherche le filtre
236
$GLOBALS['spip_matrice']['filtre_image_dist'] = 'inc/filtres_mime.php';
237
$GLOBALS['spip_matrice']['filtre_audio_dist'] = 'inc/filtres_mime.php';
238
$GLOBALS['spip_matrice']['filtre_video_dist'] = 'inc/filtres_mime.php';
239
$GLOBALS['spip_matrice']['filtre_application_dist'] = 'inc/filtres_mime.php';
240
$GLOBALS['spip_matrice']['filtre_message_dist'] = 'inc/filtres_mime.php';
241
$GLOBALS['spip_matrice']['filtre_multipart_dist'] = 'inc/filtres_mime.php';
242
$GLOBALS['spip_matrice']['filtre_text_dist'] = 'inc/filtres_mime.php';
243
$GLOBALS['spip_matrice']['filtre_text_csv_dist'] = 'inc/filtres_mime.php';
244
$GLOBALS['spip_matrice']['filtre_text_html_dist'] = 'inc/filtres_mime.php';
245
$GLOBALS['spip_matrice']['filtre_audio_x_pn_realaudio'] = 'inc/filtres_mime.php';
246
247
248
/**
249
 * Charge et exécute un filtre (graphique ou non)
250
 *
251
 * Recherche la fonction prévue pour un filtre (qui peut être un filtre graphique `image_*`)
252
 * et l'exécute avec les arguments transmis à la fonction, obtenus avec `func_get_args()`
253
 *
254
 * @api
255
 * @uses image_filtrer() Pour un filtre image
256
 * @uses chercher_filtre() Pour un autre filtre
257
 *
258
 * @param string $filtre
259
 *     Nom du filtre à appliquer
260
 * @return string
261
 *     Code HTML retourné par le filtre
262
 **/
263
function filtrer($filtre) {
264
	$tous = func_get_args();
265
	if (trouver_filtre_matrice($filtre) and substr($filtre, 0, 6) == 'image_') {
266
		return image_filtrer($tous);
267
	} elseif ($f = chercher_filtre($filtre)) {
268
		array_shift($tous);
269
		return call_user_func_array($f, $tous);
270
	} else {
271
		// le filtre n'existe pas, on provoque une erreur
272
		$msg = array('zbug_erreur_filtre', array('filtre' => texte_script($filtre)));
273
		erreur_squelette($msg);
274
		return '';
275
	}
276
}
277
278
/**
279
 * Cherche un filtre spécial indiqué dans la globale `spip_matrice`
280
 * et charge le fichier éventuellement associé contenant le filtre.
281
 *
282
 * Les filtres d'images par exemple sont déclarés de la sorte, tel que :
283
 * ```
284
 * $GLOBALS['spip_matrice']['image_reduire'] = true;
285
 * $GLOBALS['spip_matrice']['image_monochrome'] = 'filtres/images_complements.php';
286
 * ```
287
 *
288
 * @param string $filtre
289
 * @return bool true si on trouve le filtre dans la matrice, false sinon.
290
 */
291
function trouver_filtre_matrice($filtre) {
292
	if (isset($GLOBALS['spip_matrice'][$filtre]) and is_string($f = $GLOBALS['spip_matrice'][$filtre])) {
293
		find_in_path($f, '', true);
294
		$GLOBALS['spip_matrice'][$filtre] = true;
295
	}
296
	return !empty($GLOBALS['spip_matrice'][$filtre]);
297
}
298
299
300
/**
301
 * Filtre `set` qui sauve la valeur en entrée dans une variable
302
 *
303
 * La valeur pourra être retrouvée avec `#GET{variable}`.
304
 *
305
 * @example
306
 *     `[(#CALCUL|set{toto})]` enregistre le résultat de `#CALCUL`
307
 *     dans la variable `toto` et renvoie vide.
308
 *     C'est équivalent à `[(#SET{toto, #CALCUL})]` dans ce cas.
309
 *     `#GET{toto}` retourne la valeur sauvegardée.
310
 *
311
 * @example
312
 *     `[(#CALCUL|set{toto,1})]` enregistre le résultat de `#CALCUL`
313
 *      dans la variable toto et renvoie la valeur. Cela permet d'utiliser
314
 *      d'autres filtres ensuite. `#GET{toto}` retourne la valeur.
315
 *
316
 * @filtre
317
 * @param array $Pile Pile de données
318
 * @param mixed $val Valeur à sauver
319
 * @param string $key Clé d'enregistrement
320
 * @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...
321
 * @return mixed
322
 */
323
function filtre_set(&$Pile, $val, $key, $continue = null) {
324
	$Pile['vars'][$key] = $val;
325
	return $continue ? $val : '';
326
}
327
328
/**
329
 * Filtre `setenv` qui enregistre une valeur dans l'environnement du squelette
330
 *
331
 * La valeur pourra être retrouvée avec `#ENV{variable}`.
332
 * 
333
 * @example
334
 *     `[(#CALCUL|setenv{toto})]` enregistre le résultat de `#CALCUL`
335
 *      dans l'environnement toto et renvoie vide.
336
 *      `#ENV{toto}` retourne la valeur.
337
 *
338
 *      `[(#CALCUL|setenv{toto,1})]` enregistre le résultat de `#CALCUL`
339
 *      dans l'environnement toto et renvoie la valeur.
340
 *      `#ENV{toto}` retourne la valeur.
341
 *
342
 * @filtre
343
 *
344
 * @param array $Pile
345
 * @param mixed $val Valeur à enregistrer
346
 * @param mixed $key Nom de la variable
347
 * @param null|mixed $continue Si présent, retourne la valeur en sortie
348
 * @return string|mixed Retourne `$val` si `$continue` présent, sinon ''.
349
 */
350
function filtre_setenv(&$Pile, $val, $key, $continue = null) {
351
	$Pile[0][$key] = $val;
352
	return $continue ? $val : '';
353
}
354
355
/**
356
 * Filtre `debug` qui affiche un debug de la valeur en entrée
357
 *
358
 * Log la valeur dans `debug.log` et l'affiche si on est webmestre.
359
 *
360
 * @example
361
 *     `[(#TRUC|debug)]` affiche et log la valeur de `#TRUC`
362
 * @example
363
 *     `[(#TRUC|debug{avant}|calcul|debug{apres}|etc)]`
364
 *     affiche la valeur de `#TRUC` avant et après le calcul,
365
 *     en précisant "avant" et "apres".
366
 *
367
 * @filtre
368
 * @link http://www.spip.net/5695
369
 * @param mixed $val La valeur à debugguer
370
 * @param mixed|null $key Clé pour s'y retrouver
371
 * @return mixed Retourne la valeur (sans la modifier).
372
 */
373
function filtre_debug($val, $key = null) {
374
	$debug = (
375
		is_null($key) ? '' : (var_export($key, true) . " = ")
376
		) . var_export($val, true);
377
378
	include_spip('inc/autoriser');
379
	if (autoriser('webmestre')) {
380
		echo "<div class='spip_debug'>\n", $debug, "</div>\n";
381
	}
382
383
	spip_log($debug, 'debug');
384
385
	return $val;
386
}
387
388
389
/**
390
 * Exécute un filtre image
391
 *
392
 * Fonction générique d'entrée des filtres images.
393
 * Accepte en entrée :
394
 *
395
 * - un texte complet,
396
 * - un img-log (produit par #LOGO_XX),
397
 * - un tag `<img ...>` complet,
398
 * - un nom de fichier *local* (passer le filtre `|copie_locale` si on veut
399
 *   l'appliquer à un document distant).
400
 *
401
 * Applique le filtre demande à chacune des occurrences
402
 *
403
 * @param array $args
404
 *     Liste des arguments :
405
 *
406
 *     - le premier est le nom du filtre image à appliquer
407
 *     - le second est le texte sur lequel on applique le filtre
408
 *     - les suivants sont les arguments du filtre image souhaité.
409
 * @return string
410
 *     Texte qui a reçu les filtres
411
 **/
412
function image_filtrer($args) {
413
	$filtre = array_shift($args); # enlever $filtre
414
	$texte = array_shift($args);
415
	if (!strlen($texte)) {
416
		return;
417
	}
418
	find_in_path('filtres_images_mini.php', 'inc/', true);
419
	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...
420
	// Cas du nom de fichier local
421
	if (strpos(substr($texte, strlen(_DIR_RACINE)), '..') === false
422
		and !preg_match(',^/|[<>]|\s,S', $texte)
423
		and (
424
			file_exists(preg_replace(',[?].*$,', '', $texte))
425
			or tester_url_absolue($texte)
426
		)
427
	) {
428
		array_unshift($args, "<img src='$texte' />");
429
		$res = call_user_func_array($filtre, $args);
430
		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...
431
		return $res;
432
	}
433
434
	// Cas general : trier toutes les images, avec eventuellement leur <span>
435
	if (preg_match_all(
436
		',(<([a-z]+) [^<>]*spip_documents[^<>]*>)?\s*(<img\s.*>),UimsS',
437
		$texte, $tags, PREG_SET_ORDER)) {
438
		foreach ($tags as $tag) {
439
			$class = extraire_attribut($tag[3], 'class');
440
			if (!$class or
441
				(strpos($class, 'filtre_inactif') == false
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($class, 'filtre_inactif') of type integer to the boolean false. If you are specifically checking for 0, consider using something more explicit like === 0 instead.
Loading history...
442
					// compat historique a virer en 3.2
443
					and strpos($class, 'no_image_filtrer') === false)
444
			) {
445
				array_unshift($args, $tag[3]);
446
				if ($reduit = call_user_func_array($filtre, $args)) {
447
					// En cas de span spip_documents, modifier le style=...width:
448
					if ($tag[1]) {
449
						$w = extraire_attribut($reduit, 'width');
450
						if (!$w and preg_match(",width:\s*(\d+)px,S", extraire_attribut($reduit, 'style'), $regs)) {
451
							$w = $regs[1];
452
						}
453
						if ($w and ($style = extraire_attribut($tag[1], 'style'))) {
454
							$style = preg_replace(",width:\s*\d+px,S", "width:${w}px", $style);
455
							$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 454 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...
456
							$texte = str_replace($tag[1], $replace, $texte);
457
						}
458
					}
459
					// traiter aussi un eventuel mouseover
460
					if ($mouseover = extraire_attribut($reduit, 'onmouseover')) {
461
						if (preg_match(",this[.]src=['\"]([^'\"]+)['\"],ims", $mouseover, $match)) {
462
							$srcover = $match[1];
463
							array_shift($args);
464
							array_unshift($args, "<img src='" . $match[1] . "' />");
465
							$srcover_filter = call_user_func_array($filtre, $args);
466
							$srcover_filter = extraire_attribut($srcover_filter, 'src');
467
							$reduit = str_replace($srcover, $srcover_filter, $reduit);
468
						}
469
					}
470
					$texte = str_replace($tag[3], $reduit, $texte);
471
				}
472
				array_shift($args);
473
			}
474
		}
475
	}
476
	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...
477
	return $texte;
478
}
479
480
481
/**
482
 * Retourne les tailles d'une image
483
 *
484
 * Pour les filtres `largeur` et `hauteur`
485
 *
486
 * @param string $img
487
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
488
 * @return array
489
 *     Liste (hauteur, largeur) en pixels
490
 **/
491
function taille_image($img) {
492
493
	static $largeur_img = array(), $hauteur_img = array();
494
	$srcWidth = 0;
495
	$srcHeight = 0;
496
497
	$logo = extraire_attribut($img, 'src');
498
499
	if (!$logo) {
500
		$logo = $img;
501
	} else {
502
		$srcWidth = extraire_attribut($img, 'width');
503
		$srcHeight = extraire_attribut($img, 'height');
504
	}
505
506
	// ne jamais operer directement sur une image distante pour des raisons de perfo
507
	// la copie locale a toutes les chances d'etre la ou de resservir
508
	if (tester_url_absolue($logo)) {
0 ignored issues
show
Bug introduced by
It seems like $logo defined by extraire_attribut($img, 'src') on line 497 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...
509
		include_spip('inc/distant');
510
		$fichier = copie_locale($logo);
0 ignored issues
show
Bug introduced by
It seems like $logo defined by extraire_attribut($img, 'src') on line 497 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...
511
		$logo = $fichier ? _DIR_RACINE . $fichier : $logo;
512
	}
513 View Code Duplication
	if (($p = strpos($logo, '?')) !== 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...
514
		$logo = substr($logo, 0, $p);
515
	}
516
517
	$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...
518
	if (isset($largeur_img[$logo])) {
519
		$srcWidth = $largeur_img[$logo];
520
	}
521
	if (isset($hauteur_img[$logo])) {
522
		$srcHeight = $hauteur_img[$logo];
523
	}
524
	if (!$srcWidth or !$srcHeight) {
525
		if (file_exists($logo)
526
			and $srcsize = @getimagesize($logo)
527
		) {
528
			if (!$srcWidth) {
529
				$largeur_img[$logo] = $srcWidth = $srcsize[0];
530
			}
531
			if (!$srcHeight) {
532
				$hauteur_img[$logo] = $srcHeight = $srcsize[1];
533
			}
534
		}
535
		// $logo peut etre une reference a une image temporaire dont a n'a que le log .src
536
		// on s'y refere, l'image sera reconstruite en temps utile si necessaire
537
		elseif (@file_exists($f = "$logo.src")
538
			and lire_fichier($f, $valeurs)
539
			and $valeurs = unserialize($valeurs)
540
		) {
541
			if (!$srcWidth) {
542
				$largeur_img[$logo] = $srcWidth = $valeurs["largeur_dest"];
543
			}
544
			if (!$srcHeight) {
545
				$hauteur_img[$logo] = $srcHeight = $valeurs["hauteur_dest"];
546
			}
547
		}
548
	}
549
550
	return array($srcHeight, $srcWidth);
551
}
552
553
554
/**
555
 * Retourne la largeur d'une image
556
 *
557
 * @filtre
558
 * @link http://www.spip.net/4296
559
 * @uses taille_image()
560
 * @see  hauteur()
561
 *
562
 * @param string $img
563
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
564
 * @return int|null
565
 *     Largeur en pixels, NULL ou 0 si aucune image.
566
 **/
567
function largeur($img) {
568
	if (!$img) {
569
		return;
570
	}
571
	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...
572
573
	return $l;
574
}
575
576
/**
577
 * Retourne la hauteur d'une image
578
 *
579
 * @filtre
580
 * @link http://www.spip.net/4291
581
 * @uses taille_image()
582
 * @see  largeur()
583
 *
584
 * @param string $img
585
 *     Balise HTML `<img ... />` ou chemin de l'image (qui peut être une URL distante).
586
 * @return int|null
587
 *     Hauteur en pixels, NULL ou 0 si aucune image.
588
 **/
589
function hauteur($img) {
590
	if (!$img) {
591
		return;
592
	}
593
	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...
594
595
	return $h;
596
}
597
598
599
/**
600
 * Échappement des entités HTML avec correction des entités « brutes »
601
 *
602
 * Ces entités peuvent être générées par les butineurs lorsqu'on rentre des
603
 * caractères n'appartenant pas au charset de la page [iso-8859-1 par défaut]
604
 *
605
 * Attention on limite cette correction aux caracteres « hauts » (en fait > 99
606
 * pour aller plus vite que le > 127 qui serait logique), de manière à
607
 * préserver des eéhappements de caractères « bas » (par exemple `[` ou `"`)
608
 * et au cas particulier de `&amp;` qui devient `&amp;amp;` dans les URL
609
 *
610
 * @see corriger_toutes_entites_html()
611
 * @param string $texte
612
 * @return string
613
 **/
614
function corriger_entites_html($texte) {
615
	if (strpos($texte, '&amp;') === false) {
616
		return $texte;
617
	}
618
619
	return preg_replace(',&amp;(#[0-9][0-9][0-9]+;|amp;),iS', '&\1', $texte);
620
}
621
622
/**
623
 * Échappement des entités HTML avec correction des entités « brutes » ainsi
624
 * que les `&amp;eacute;` en `&eacute;`
625
 *
626
 * Identique à `corriger_entites_html()` en corrigeant aussi les
627
 * `&amp;eacute;` en `&eacute;`
628
 *
629
 * @see corriger_entites_html()
630
 * @param string $texte
631
 * @return string
632
 **/
633
function corriger_toutes_entites_html($texte) {
634
	if (strpos($texte, '&amp;') === false) {
635
		return $texte;
636
	}
637
638
	return preg_replace(',&amp;(#?[a-z0-9]+;),iS', '&\1', $texte);
639
}
640
641
/**
642
 * Échappe les `&` en `&amp;`
643
 *
644
 * @param string $texte
645
 * @return string
646
 **/
647
function proteger_amp($texte) {
648
	return str_replace('&', '&amp;', $texte);
649
}
650
651
652
/**
653
 * Échappe en entités HTML certains caractères d'un texte
654
 *
655
 * Traduira un code HTML en transformant en entités HTML les caractères
656
 * en dehors du charset de la page ainsi que les `"`, `<` et `>`.
657
 *
658
 * Ceci permet d’insérer le texte d’une balise dans un `<textarea> </textarea>`
659
 * sans dommages.
660
 *
661
 * @filtre
662
 * @link http://www.spip.net/4280
663
 *
664
 * @uses echappe_html()
665
 * @uses echappe_retour()
666
 * @uses proteger_amp()
667
 * @uses corriger_entites_html()
668
 * @uses corriger_toutes_entites_html()
669
 *
670
 * @param string $texte
671
 *   chaine a echapper
672
 * @param bool $tout
673
 *   corriger toutes les `&amp;xx;` en `&xx;`
674
 * @param bool $quote
675
 *   Échapper aussi les simples quotes en `&#039;`
676
 * @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...
677
 */
678
function entites_html($texte, $tout = false, $quote = true) {
679 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...
680
		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...
681
	) {
682
		return $texte;
683
	}
684
	include_spip('inc/texte');
685
	$flags = ($quote ? ENT_QUOTES : ENT_NOQUOTES);
686
	if (defined('ENT_HTML401')) {
687
		$flags |= ENT_HTML401;
688
	}
689
	$texte = spip_htmlspecialchars(echappe_retour(echappe_html($texte, '', true), '', 'proteger_amp'), $flags);
690
	if ($tout) {
691
		return corriger_toutes_entites_html($texte);
692
	} else {
693
		return corriger_entites_html($texte);
694
	}
695
}
696
697
/**
698
 * Convertit les caractères spéciaux HTML dans le charset du site.
699
 *
700
 * @exemple
701
 *     Si le charset de votre site est `utf-8`, `&eacute;` ou `&#233;`
702
 *     sera transformé en `é`
703
 *
704
 * @filtre
705
 * @link http://www.spip.net/5513
706
 *
707
 * @param string $texte
708
 *     Texte à convertir
709
 * @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...
710
 *     Texte converti
711
 **/
712
function filtrer_entites($texte) {
713
	if (strpos($texte, '&') === false) {
714
		return $texte;
715
	}
716
	// filtrer
717
	$texte = html2unicode($texte);
718
	// remettre le tout dans le charset cible
719
	$texte = unicode2charset($texte);
720
	// cas particulier des " et ' qu'il faut filtrer aussi
721
	// (on le faisait deja avec un &quot;)
722 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...
723
		$texte = str_replace(array("&#039;", "&#39;", "&#034;", "&#34;"), array("'", "'", '"', '"'), $texte);
724
	}
725
726
	return $texte;
727
}
728
729
730
if (!function_exists('filtre_filtrer_entites_dist')) {
731
	/**
732
	 * Version sécurisée de filtrer_entites
733
	 * 
734
	 * @uses interdire_scripts()
735
	 * @uses filtrer_entites()
736
	 * 
737
	 * @param string $t
738
	 * @return string
739
	 */
740
	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 (L337-340) 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...
741
		include_spip('inc/texte');
742
		return interdire_scripts(filtrer_entites($t));
743
	}
744
}
745
746
747
/**
748
 * Supprime des caractères illégaux
749
 *
750
 * Remplace les caractères de controle par le caractère `-`
751
 *
752
 * @link http://www.w3.org/TR/REC-xml/#charsets
753
 *
754
 * @param string|array $texte
755
 * @return string|array
756
 **/
757
function supprimer_caracteres_illegaux($texte) {
758
	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";
759
	static $to = null;
760
761
	if (is_array($texte)) {
762
		return array_map('supprimer_caracteres_illegaux', $texte);
763
	}
764
765
	if (!$to) {
766
		$to = str_repeat('-', strlen($from));
767
	}
768
769
	return strtr($texte, $from, $to);
770
}
771
772
/**
773
 * Correction de caractères
774
 *
775
 * Supprimer les caracteres windows non conformes et les caracteres de controle illégaux
776
 *
777
 * @param string|array $texte
778
 * @return string|array
779
 **/
780
function corriger_caracteres($texte) {
781
	$texte = corriger_caracteres_windows($texte);
782
	$texte = supprimer_caracteres_illegaux($texte);
783
784
	return $texte;
785
}
786
787
/**
788
 * Encode du HTML pour transmission XML notamment dans les flux RSS
789
 *
790
 * Ce filtre transforme les liens en liens absolus, importe les entitées html et échappe les tags html.
791
 *
792
 * @filtre
793
 * @link http://www.spip.net/4287
794
 *
795
 * @param string $texte
796
 *     Texte à transformer
797
 * @return string
798
 *     Texte encodé pour XML
799
 */
800
function texte_backend($texte) {
801
802
	static $apostrophe = array("&#8217;", "'"); # n'allouer qu'une fois
803
804
	// si on a des liens ou des images, les passer en absolu
805
	$texte = liens_absolus($texte);
806
807
	// echapper les tags &gt; &lt;
808
	$texte = preg_replace(',&(gt|lt);,S', '&amp;\1;', $texte);
809
810
	// importer les &eacute;
811
	$texte = filtrer_entites($texte);
812
813
	// " -> &quot; et tout ce genre de choses
814
	$u = $GLOBALS['meta']['pcre_u'];
815
	$texte = str_replace("&nbsp;", " ", $texte);
816
	$texte = preg_replace('/\s{2,}/S' . $u, " ", $texte);
817
	// ne pas echapper les sinqle quotes car certains outils de syndication gerent mal
818
	$texte = entites_html($texte, false, false);
819
	// mais bien echapper les double quotes !
820
	$texte = str_replace('"', '&#034;', $texte);
821
822
	// verifier le charset
823
	$texte = charset2unicode($texte);
824
825
	// Caracteres problematiques en iso-latin 1
826
	if (isset($GLOBALS['meta']['charset']) and $GLOBALS['meta']['charset'] == 'iso-8859-1') {
827
		$texte = str_replace(chr(156), '&#156;', $texte);
828
		$texte = str_replace(chr(140), '&#140;', $texte);
829
		$texte = str_replace(chr(159), '&#159;', $texte);
830
	}
831
832
	// l'apostrophe curly pose probleme a certains lecteure de RSS
833
	// et le caractere apostrophe alourdit les squelettes avec PHP
834
	// ==> on les remplace par l'entite HTML
835
	return str_replace($apostrophe, "'", $texte);
836
}
837
838
/**
839
 * Encode et quote du HTML pour transmission XML notamment dans les flux RSS
840
 *
841
 * Comme texte_backend(), mais avec addslashes final pour squelettes avec PHP (rss)
842
 *
843
 * @uses texte_backend()
844
 * @filtre
845
 *
846
 * @param string $texte
847
 *     Texte à transformer
848
 * @return string
849
 *     Texte encodé et quote pour XML
850
 */
851
function texte_backendq($texte) {
852
	return addslashes(texte_backend($texte));
853
}
854
855
856
/**
857
 * Enlève un numéro préfixant un texte
858
 *
859
 * Supprime `10. ` dans la chaine `10. Titre`
860
 *
861
 * @filtre
862
 * @link http://www.spip.net/4314
863
 * @see recuperer_numero() Pour obtenir le numéro
864
 * @example
865
 *     ```
866
 *     [<h1>(#TITRE|supprimer_numero)</h1>]
867
 *     ```
868
 *
869
 * @param string $texte
870
 *     Texte
871
 * @return int|string
872
 *     Numéro de titre, sinon chaîne vide
873
 **/
874
function supprimer_numero($texte) {
875
	return preg_replace(
876
		",^[[:space:]]*([0-9]+)([.)]|" . chr(194) . '?' . chr(176) . ")[[:space:]]+,S",
877
		"", $texte);
878
}
879
880
/**
881
 * Récupère un numéro préfixant un texte
882
 *
883
 * Récupère le numéro `10` dans la chaine `10. Titre`
884
 *
885
 * @filtre
886
 * @link http://www.spip.net/5514
887
 * @see supprimer_numero() Pour supprimer le numéro
888
 * @see balise_RANG_dist() Pour obtenir un numéro de titre
889
 * @example
890
 *     ```
891
 *     [(#TITRE|recuperer_numero)]
892
 *     ```
893
 *
894
 * @param string $texte
895
 *     Texte
896
 * @return int|string
897
 *     Numéro de titre, sinon chaîne vide
898
 **/
899
function recuperer_numero($texte) {
900
	if (preg_match(
901
		",^[[:space:]]*([0-9]+)([.)]|" . chr(194) . '?' . chr(176) . ")[[:space:]]+,S",
902
		$texte, $regs)) {
903
		return strval($regs[1]);
904
	} else {
905
		return '';
906
	}
907
}
908
909
/**
910
 * Suppression basique et brutale de tous les tags
911
 *
912
 * Supprime tous les tags `<...>`.
913
 * Utilisé fréquemment pour écrire des RSS.
914
 *
915
 * @filtre
916
 * @link http://www.spip.net/4315
917
 * @example
918
 *     ```
919
 *     <title>[(#TITRE|supprimer_tags|texte_backend)]</title>
920
 *     ```
921
 *
922
 * @note
923
 *     Ce filtre supprime aussi les signes inférieurs `<` rencontrés.
924
 *
925
 * @param string $texte
926
 *     Texte à échapper
927
 * @param string $rempl
928
 *     Inutilisé.
929
 * @return string
930
 *     Texte converti
931
 **/
932
function supprimer_tags($texte, $rempl = "") {
933
	$texte = preg_replace(",<(!--|\w|/)[^>]*>,US", $rempl, $texte);
934
	// ne pas oublier un < final non ferme car coupe
935
	$texte = preg_replace(",<(!--|\w|/).*$,US", $rempl, $texte);
936
	// mais qui peut aussi etre un simple signe plus petit que
937
	$texte = str_replace('<', '&lt;', $texte);
938
939
	return $texte;
940
}
941
942
/**
943
 * Convertit les chevrons de tag en version lisible en HTML
944
 *
945
 * Transforme les chevrons de tag `<...>` en entité HTML.
946
 *
947
 * @filtre
948
 * @link http://www.spip.net/5515
949
 * @example
950
 *     ```
951
 *     <pre>[(#TEXTE|echapper_tags)]</pre>
952
 *     ```
953
 *
954
 * @param string $texte
955
 *     Texte à échapper
956
 * @param string $rempl
957
 *     Inutilisé.
958
 * @return string
959
 *     Texte converti
960
 **/
961
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...
962
	$texte = preg_replace("/<([^>]*)>/", "&lt;\\1&gt;", $texte);
963
964
	return $texte;
965
}
966
967
/**
968
 * Convertit un texte HTML en texte brut
969
 *
970
 * Enlève les tags d'un code HTML, élimine les doubles espaces.
971
 *
972
 * @filtre
973
 * @link http://www.spip.net/4317
974
 * @example
975
 *     ```
976
 *     <title>[(#TITRE|textebrut) - ][(#NOM_SITE_SPIP|textebrut)]</title>
977
 *     ```
978
 *
979
 * @param string $texte
980
 *     Texte à convertir
981
 * @return string
982
 *     Texte converti
983
 **/
984
function textebrut($texte) {
985
	$u = $GLOBALS['meta']['pcre_u'];
986
	$texte = preg_replace('/\s+/S' . $u, " ", $texte);
987
	$texte = preg_replace("/<(p|br)( [^>]*)?" . ">/iS", "\n\n", $texte);
988
	$texte = preg_replace("/^\n+/", "", $texte);
989
	$texte = preg_replace("/\n+$/", "", $texte);
990
	$texte = preg_replace("/\n +/", "\n", $texte);
991
	$texte = supprimer_tags($texte);
992
	$texte = preg_replace("/(&nbsp;| )+/S", " ", $texte);
993
	// nettoyer l'apostrophe curly qui pose probleme a certains rss-readers, lecteurs de mail...
994
	$texte = str_replace("&#8217;", "'", $texte);
995
996
	return $texte;
997
}
998
999
1000
/**
1001
 * Remplace les liens SPIP en liens ouvrant dans une nouvelle fenetre (target=blank)
1002
 *
1003
 * @filtre
1004
 * @link http://www.spip.net/4297
1005
 *
1006
 * @param string $texte
1007
 *     Texte avec des liens
1008
 * @return string
1009
 *     Texte avec liens ouvrants
1010
 **/
1011
function liens_ouvrants($texte) {
1012
	if (preg_match_all(",(<a\s+[^>]*https?://[^>]*class=[\"']spip_(out|url)\b[^>]+>),imsS",
1013
		$texte, $liens, PREG_SET_ORDER)) {
1014
		foreach ($liens[0] as $a) {
1015
			$rel = 'noopener noreferrer ' . extraire_attribut($a, 'rel');
1016
			$ablank = inserer_attribut($a, 'rel', $rel);
1017
			$ablank = inserer_attribut($ablank, 'target', '_blank');
1018
			$texte = str_replace($a, $ablank, $texte);
1019
		}
1020
	}
1021
1022
	return $texte;
1023
}
1024
1025
/**
1026
 * Ajouter un attribut rel="nofollow" sur tous les liens d'un texte
1027
 *
1028
 * @param string $texte
1029
 * @return string
1030
 */
1031
function liens_nofollow($texte) {
1032
	if (stripos($texte, "<a") === false) {
1033
		return $texte;
1034
	}
1035
1036
	if (preg_match_all(",<a\b[^>]*>,UimsS", $texte, $regs, PREG_PATTERN_ORDER)) {
1037
		foreach ($regs[0] as $a) {
1038
			$rel = extraire_attribut($a, "rel");
1039
			if (strpos($rel, "nofollow") === false) {
1040
				$rel = "nofollow" . ($rel ? " $rel" : "");
1041
				$anofollow = inserer_attribut($a, "rel", $rel);
1042
				$texte = str_replace($a, $anofollow, $texte);
1043
			}
1044
		}
1045
	}
1046
1047
	return $texte;
1048
}
1049
1050
/**
1051
 * Transforme les sauts de paragraphe HTML `p` en simples passages à la ligne `br`
1052
 *
1053
 * @filtre
1054
 * @link http://www.spip.net/4308
1055
 * @example
1056
 *     ```
1057
 *     [<div>(#DESCRIPTIF|PtoBR)[(#NOTES|PtoBR)]</div>]
1058
 *     ```
1059
 *
1060
 * @param string $texte
1061
 *     Texte à transformer
1062
 * @return string
1063
 *     Texte sans paraghaphes
1064
 **/
1065
function PtoBR($texte) {
1066
	$u = $GLOBALS['meta']['pcre_u'];
1067
	$texte = preg_replace("@</p>@iS", "\n", $texte);
1068
	$texte = preg_replace("@<p\b.*>@UiS", "<br />", $texte);
1069
	$texte = preg_replace("@^\s*<br />@S" . $u, "", $texte);
1070
1071
	return $texte;
1072
}
1073
1074
1075
/**
1076
 * Assure qu'un texte ne vas pas déborder d'un bloc
1077
 * par la faute d'un mot trop long (souvent des URLs)
1078
 *
1079
 * Ne devrait plus être utilisé et fait directement en CSS par un style
1080
 * `word-wrap:break-word;`
1081
 *
1082
 * @note
1083
 *   Pour assurer la compatibilité du filtre, on encapsule le contenu par
1084
 *   un `div` ou `span` portant ce style CSS inline.
1085
 *
1086
 * @filtre
1087
 * @link http://www.spip.net/4298
1088
 * @link http://www.alsacreations.com/tuto/lire/1038-gerer-debordement-contenu-css.html
1089
 * @deprecated Utiliser le style CSS `word-wrap:break-word;`
1090
 *
1091
 * @param string $texte Texte
1092
 * @return string Texte encadré du style CSS
1093
 */
1094
function lignes_longues($texte) {
1095
	if (!strlen(trim($texte))) {
1096
		return $texte;
1097
	}
1098
	include_spip('inc/texte');
1099
	$tag = preg_match(',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS', $texte) ?
1100
		'div' : 'span';
1101
1102
	return "<$tag style='word-wrap:break-word;'>$texte</$tag>";
1103
}
1104
1105
/**
1106
 * Passe un texte en majuscules, y compris les accents, en HTML
1107
 *
1108
 * Encadre le texte du style CSS `text-transform: uppercase;`.
1109
 * Le cas spécifique du i turc est géré.
1110
 *
1111
 * @filtre
1112
 * @example
1113
 *     ```
1114
 *     [(#EXTENSION|majuscules)]
1115
 *     ```
1116
 *
1117
 * @param string $texte Texte
1118
 * @return string Texte en majuscule
1119
 */
1120
function majuscules($texte) {
1121
	if (!strlen($texte)) {
1122
		return '';
1123
	}
1124
1125
	// Cas du turc
1126
	if ($GLOBALS['spip_lang'] == 'tr') {
1127
		# remplacer hors des tags et des entites
1128 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...
1129
			foreach ($regs as $n => $match) {
1130
				$texte = str_replace($match[0], "@@SPIP_TURC$n@@", $texte);
1131
			}
1132
		}
1133
1134
		$texte = str_replace('i', '&#304;', $texte);
1135
1136 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...
1137
			foreach ($regs as $n => $match) {
1138
				$texte = str_replace("@@SPIP_TURC$n@@", $match[0], $texte);
1139
			}
1140
		}
1141
	}
1142
1143
	// Cas general
1144
	return "<span style='text-transform: uppercase;'>$texte</span>";
1145
}
1146
1147
/**
1148
 * Retourne une taille en octets humainement lisible
1149
 *
1150
 * Tel que "127.4 ko" ou "3.1 Mo"
1151
 *
1152
 * @example
1153
 *     - `[(#TAILLE|taille_en_octets)]`
1154
 *     - `[(#VAL{123456789}|taille_en_octets)]` affiche `117.7 Mo`
1155
 *
1156
 * @filtre
1157
 * @link http://www.spip.net/4316
1158
 * @param int $taille
1159
 * @return string
1160
 **/
1161
function taille_en_octets($taille) {
1162
	if (!defined('_KILOBYTE')) {
1163
		/**
1164
		 * Définit le nombre d'octets dans un Kilobyte
1165
		 *
1166
		 * @var int
1167
		 **/
1168
		define('_KILOBYTE', 1024);
1169
	}
1170
1171
	if ($taille < 1) {
1172
		return '';
1173
	}
1174
	if ($taille < _KILOBYTE) {
1175
		$taille = _T('taille_octets', array('taille' => $taille));
1176 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...
1177
		$taille = _T('taille_ko', array('taille' => round($taille / _KILOBYTE, 1)));
1178
	} elseif ($taille < _KILOBYTE * _KILOBYTE * _KILOBYTE) {
1179
		$taille = _T('taille_mo', array('taille' => round($taille / _KILOBYTE / _KILOBYTE, 1)));
1180 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...
1181
		$taille = _T('taille_go', array('taille' => round($taille / _KILOBYTE / _KILOBYTE / _KILOBYTE, 2)));
1182
	}
1183
1184
	return $taille;
1185
}
1186
1187
1188
/**
1189
 * Rend une chaine utilisable sans dommage comme attribut HTML
1190
 *
1191
 * @example `<a href="#URL_ARTICLE" title="[(#TITRE|attribut_html)]">#TITRE</a>`
1192
 *
1193
 * @filtre
1194
 * @link http://www.spip.net/4282
1195
 * @uses textebrut()
1196
 * @uses texte_backend()
1197
 *
1198
 * @param string $texte
1199
 *     Texte à mettre en attribut
1200
 * @param bool $textebrut
1201
 *     Passe le texte en texte brut (enlève les balises html) ?
1202
 * @return string
1203
 *     Texte prêt pour être utilisé en attribut HTML
1204
 **/
1205
function attribut_html($texte, $textebrut = true) {
1206
	$u = $GLOBALS['meta']['pcre_u'];
1207
	if ($textebrut) {
1208
		$texte = preg_replace(array(",\n,", ",\s(?=\s),msS" . $u), array(" ", ""), textebrut($texte));
1209
	}
1210
	$texte = texte_backend($texte);
1211
	$texte = str_replace(array("'", '"'), array('&#039;', '&#034;'), $texte);
1212
1213
	return preg_replace(array("/&(amp;|#38;)/", "/&(?![A-Za-z]{0,4}\w{2,3};|#[0-9]{2,5};)/"), array("&", "&#38;"),
1214
		$texte);
1215
}
1216
1217
1218
/**
1219
 * Vider les URL nulles
1220
 *
1221
 * - Vide les URL vides comme `http://` ou `mailto:` (sans rien d'autre)
1222
 * - échappe les entités et gère les `&amp;`
1223
 *
1224
 * @uses entites_html()
1225
 *
1226
 * @param string $url
1227
 *     URL à vérifier et échapper
1228
 * @param bool $entites
1229
 *     `true` pour échapper les entités HTML.
1230
 * @return string
1231
 *     URL ou chaîne vide
1232
 **/
1233
function vider_url($url, $entites = true) {
1234
	# un message pour abs_url
1235
	$GLOBALS['mode_abs_url'] = 'url';
1236
	$url = trim($url);
1237
	$r = ",^(?:" . _PROTOCOLES_STD . '):?/?/?$,iS';
1238
1239
	return preg_match($r, $url) ? '' : ($entites ? entites_html($url) : $url);
1240
}
1241
1242
1243
/**
1244
 * Maquiller une adresse e-mail
1245
 *
1246
 * Remplace `@` par 3 caractères aléatoires.
1247
 *
1248
 * @uses creer_pass_aleatoire()
1249
 *
1250
 * @param string $texte Adresse email
1251
 * @return string Adresse email maquillée
1252
 **/
1253
function antispam($texte) {
1254
	include_spip('inc/acces');
1255
	$masque = creer_pass_aleatoire(3);
1256
1257
	return preg_replace("/@/", " $masque ", $texte);
1258
}
1259
1260
/**
1261
 * Vérifie un accès à faible sécurité
1262
 *
1263
 * Vérifie qu'un visiteur peut accéder à la page demandée,
1264
 * qui est protégée par une clé, calculée à partir du low_sec de l'auteur,
1265
 * et des paramètres le composant l'appel (op, args)
1266
 *
1267
 * @example
1268
 *     `[(#ID_AUTEUR|securiser_acces{#ENV{cle}, rss, #ENV{op}, #ENV{args}}|sinon_interdire_acces)]`
1269
 *
1270
 * @see  bouton_spip_rss() pour générer un lien de faible sécurité pour les RSS privés
1271
 * @see  afficher_low_sec() pour calculer une clé valide
1272
 * @uses verifier_low_sec()
1273
 *
1274
 * @filtre
1275
 * @param int $id_auteur
1276
 *     L'auteur qui demande la page
1277
 * @param string $cle
1278
 *     La clé à tester
1279
 * @param string $dir
1280
 *     Un type d'accès (nom du répertoire dans lequel sont rangés les squelettes demandés, tel que 'rss')
1281
 * @param string $op
1282
 *     Nom de l'opération éventuelle
1283
 * @param string $args
1284
 *     Nom de l'argument calculé
1285
 * @return bool
1286
 *     True si on a le droit d'accès, false sinon.
1287
 **/
1288
function securiser_acces($id_auteur, $cle, $dir, $op = '', $args = '') {
1289
	include_spip('inc/acces');
1290
	if ($op) {
1291
		$dir .= " $op $args";
1292
	}
1293
1294
	return verifier_low_sec($id_auteur, $cle, $dir);
1295
}
1296
1297
/**
1298
 * Retourne le second paramètre lorsque
1299
 * le premier est considere vide, sinon retourne le premier paramètre.
1300
 *
1301
 * En php `sinon($a, 'rien')` retourne `$a`, ou `'rien'` si `$a` est vide.
1302
 * En filtre SPIP `|sinon{#TEXTE, rien}` : affiche `#TEXTE` ou `rien` si `#TEXTE` est vide,
1303
 *
1304
 * @filtre
1305
 * @see filtre_logique() pour la compilation du filtre dans un squelette
1306
 * @link http://www.spip.net/4313
1307
 * @note
1308
 *     L'utilisation de `|sinon` en tant que filtre de squelette
1309
 *     est directement compilé dans `public/references` par la fonction `filtre_logique()`
1310
 *
1311
 * @param mixed $texte
1312
 *     Contenu de reference a tester
1313
 * @param mixed $sinon
1314
 *     Contenu a retourner si le contenu de reference est vide
1315
 * @return mixed
1316
 *     Retourne $texte, sinon $sinon.
1317
 **/
1318
function sinon($texte, $sinon = '') {
1319
	if ($texte or (!is_array($texte) and strlen($texte))) {
1320
		return $texte;
1321
	} else {
1322
		return $sinon;
1323
	}
1324
}
1325
1326
/**
1327
 * Filtre `|choixsivide{vide, pas vide}` alias de `|?{si oui, si non}` avec les arguments inversés
1328
 *
1329
 * @example
1330
 *     `[(#TEXTE|choixsivide{vide, plein})]` affiche vide si le `#TEXTE`
1331
 *     est considéré vide par PHP (chaîne vide, false, 0, tableau vide, etc…).
1332
 *     C'est l'équivalent de `[(#TEXTE|?{plein, vide})]`
1333
 *
1334
 * @filtre
1335
 * @see choixsiegal()
1336
 * @link http://www.spip.net/4189
1337
 *
1338
 * @param mixed $a
1339
 *     La valeur à tester
1340
 * @param mixed $vide
1341
 *     Ce qui est retourné si `$a` est considéré vide
1342
 * @param mixed $pasvide
1343
 *     Ce qui est retourné sinon
1344
 * @return mixed
1345
 **/
1346
function choixsivide($a, $vide, $pasvide) {
1347
	return $a ? $pasvide : $vide;
1348
}
1349
1350
/**
1351
 * Filtre `|choixsiegal{valeur, sioui, sinon}`
1352
 *
1353
 * @example
1354
 *     `#LANG_DIR|choixsiegal{ltr,left,right}` retourne `left` si
1355
 *      `#LANG_DIR` vaut `ltr` et `right` sinon.
1356
 *
1357
 * @filtre
1358
 * @link http://www.spip.net/4148
1359
 *
1360
 * @param mixed $a1
1361
 *     La valeur à tester
1362
 * @param mixed $a2
1363
 *     La valeur de comparaison
1364
 * @param mixed $v
1365
 *     Ce qui est retourné si la comparaison est vraie
1366
 * @param mixed $f
1367
 *     Ce qui est retourné sinon
1368
 * @return mixed
1369
 **/
1370
function choixsiegal($a1, $a2, $v, $f) {
1371
	return ($a1 == $a2) ? $v : $f;
1372
}
1373
1374
/**
1375
 * Alignements en HTML (Old-style, préférer CSS)
1376
 *
1377
 * Cette fonction ne crée pas de paragraphe
1378
 *
1379
 * @deprecated Utiliser CSS
1380
 * @param string $letexte
1381
 * @param string $justif
1382
 * @return string
1383
 */
1384
function aligner($letexte, $justif = '') {
1385
	$letexte = trim($letexte);
1386
	if (!strlen($letexte)) {
1387
		return '';
1388
	}
1389
1390
	// Paragrapher rapidement
1391
	$letexte = "<div style='text-align:$justif'>"
1392
		. $letexte
1393
		. "</div>";
1394
1395
	return $letexte;
1396
}
1397
1398
/**
1399
 * Justifie en HTML (Old-style, préférer CSS)
1400
 *
1401
 * @deprecated Utiliser CSS
1402
 * @uses aligner()
1403
 * @param string $letexte
1404
 * @return string
1405
 */
1406
function justifier($letexte) { return aligner($letexte, 'justify'); }
0 ignored issues
show
Deprecated Code introduced by
The function aligner() has been deprecated with message: Utiliser CSS

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1407
1408
/**
1409
 * Aligne à droite en HTML (Old-style, préférer CSS)
1410
 *
1411
 * @deprecated Utiliser CSS
1412
 * @uses aligner()
1413
 * @param string $letexte
1414
 * @return string
1415
 */
1416
function aligner_droite($letexte) { return aligner($letexte, 'right'); }
0 ignored issues
show
Deprecated Code introduced by
The function aligner() has been deprecated with message: Utiliser CSS

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1417
1418
/**
1419
 * Aligne à gauche en HTML (Old-style, préférer CSS)
1420
 *
1421
 * @deprecated Utiliser CSS
1422
 * @uses aligner()
1423
 * @param string $letexte
1424
 * @return string
1425
 */
1426
function aligner_gauche($letexte) { return aligner($letexte, 'left'); }
0 ignored issues
show
Deprecated Code introduced by
The function aligner() has been deprecated with message: Utiliser CSS

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1427
1428
/**
1429
 * Centre en HTML (Old-style, préférer CSS)
1430
 *
1431
 * @deprecated Utiliser CSS
1432
 * @uses aligner()
1433
 * @param string $letexte
1434
 * @return string
1435
 */
1436
function centrer($letexte) { return aligner($letexte, 'center'); }
0 ignored issues
show
Deprecated Code introduced by
The function aligner() has been deprecated with message: Utiliser CSS

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1437
1438
/**
1439
 * Retourne un texte de style CSS aligné sur la langue en cours
1440
 *
1441
 * @deprecated
1442
 * @param mixed $bof Inutilisé
1443
 * @return string Style CSS
1444
 **/
1445
function style_align($bof) {
0 ignored issues
show
Unused Code introduced by
The parameter $bof 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...
1446
1447
	return "text-align: " . $GLOBALS['spip_lang_left'];
1448
}
1449
1450
//
1451
// Export iCal
1452
//
1453
1454
/**
1455
 * Adapte un texte pour être inséré dans une valeur d'un export ICAL
1456
 *
1457
 * Passe le texte en utf8, enlève les sauts de lignes et échappe les virgules.
1458
 *
1459
 * @example `SUMMARY:[(#TITRE|filtrer_ical)]`
1460
 * @filtre
1461
 *
1462
 * @param string $texte
1463
 * @return string
1464
 **/
1465
function filtrer_ical($texte) {
1466
	#include_spip('inc/charsets');
1467
	$texte = html2unicode($texte);
1468
	$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...
1469
	$texte = preg_replace("/\n/", " ", $texte);
1470
	$texte = preg_replace("/,/", "\,", $texte);
1471
1472
	return $texte;
1473
}
1474
1475
1476
/**
1477
 * Transforme les sauts de ligne simples en sauts forcés avec `_ `
1478
 *
1479
 * Ne modifie pas les sauts de paragraphe (2 sauts consécutifs au moins),
1480
 * ou les retours à l'intérieur de modèles ou de certaines balises html.
1481
 *
1482
 * @note
1483
 *     Cette fonction pouvait être utilisée pour forcer les alinéas,
1484
 *     (retours à la ligne sans saut de paragraphe), mais ce traitement
1485
 *     est maintenant automatique.
1486
 *     Cf. plugin Textwheel et la constante _AUTOBR
1487
 *
1488
 * @uses echappe_html()
1489
 * @uses echappe_retour()
1490
 *
1491
 * @param string $texte
1492
 * @param string $delim
1493
 *      Ce par quoi sont remplacés les sauts
1494
 * @return string
1495
 **/
1496
function post_autobr($texte, $delim = "\n_ ") {
1497
	if (!function_exists('echappe_html')) {
1498
		include_spip('inc/texte_mini');
1499
	}
1500
	$texte = str_replace("\r\n", "\r", $texte);
1501
	$texte = str_replace("\r", "\n", $texte);
1502
1503 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...
1504
		$texte = substr($texte, 0, -strlen($fin = $fin[0]));
1505
	} else {
1506
		$fin = '';
1507
	}
1508
1509
	$texte = echappe_html($texte, '', true);
1510
1511
	// echapper les modeles
1512
	if (strpos($texte, "<") !== false) {
1513
		include_spip('inc/lien');
1514
		if (defined('_PREG_MODELE')) {
1515
			$preg_modeles = "@" . _PREG_MODELE . "@imsS";
1516
			$texte = echappe_html($texte, '', true, $preg_modeles);
1517
		}
1518
	}
1519
1520
	$debut = '';
1521
	$suite = $texte;
1522
	while ($t = strpos('-' . $suite, "\n", 1)) {
1523
		$debut .= substr($suite, 0, $t - 1);
1524
		$suite = substr($suite, $t);
1525
		$car = substr($suite, 0, 1);
1526
		if (($car <> '-') and ($car <> '_') and ($car <> "\n") and ($car <> "|") and ($car <> "}")
1527
			and !preg_match(',^\s*(\n|</?(quote|div|dl|dt|dd)|$),S', ($suite))
1528
			and !preg_match(',</?(quote|div|dl|dt|dd)> *$,iS', $debut)
1529
		) {
1530
			$debut .= $delim;
1531
		} else {
1532
			$debut .= "\n";
1533
		}
1534 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...
1535
			$debut .= $regs[0];
1536
			$suite = substr($suite, strlen($regs[0]));
1537
		}
1538
	}
1539
	$texte = $debut . $suite;
1540
1541
	$texte = echappe_retour($texte);
1542
1543
	return $texte . $fin;
1544
}
1545
1546
1547
/**
1548
 * Expression régulière pour obtenir le contenu des extraits idiomes `<:module:cle:>`
1549
 *
1550
 * @var string
1551
 */
1552
define('_EXTRAIRE_IDIOME', '@<:(?:([a-z0-9_]+):)?([a-z0-9_]+):>@isS');
1553
1554
/**
1555
 * Extrait une langue des extraits idiomes (`<:module:cle_de_langue:>`)
1556
 *
1557
 * Retrouve les balises `<:cle_de_langue:>` d'un texte et remplace son contenu
1558
 * par l'extrait correspondant à la langue demandée (si possible), sinon dans la
1559
 * langue par défaut du site.
1560
 *
1561
 * Ne pas mettre de span@lang=fr si on est déjà en fr.
1562
 *
1563
 * @filtre
1564
 * @uses inc_traduire_dist()
1565
 * @uses code_echappement()
1566
 * @uses echappe_retour()
1567
 *
1568
 * @param string $letexte
1569
 * @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...
1570
 *     Langue à retrouver (si vide, utilise la langue en cours).
1571
 * @param array $options Options {
1572
 * @type bool $echappe_span
1573
 *         True pour échapper les balises span (false par défaut)
1574
 * @type string $lang_defaut
1575
 *         Code de langue : permet de définir la langue utilisée par défaut,
1576
 *         en cas d'absence de traduction dans la langue demandée.
1577
 *         Par défaut la langue du site.
1578
 *         Indiquer 'aucune' pour ne pas retourner de texte si la langue
1579
 *         exacte n'a pas été trouvée.
1580
 * }
1581
 * @return string
1582
 **/
1583
function extraire_idiome($letexte, $lang = null, $options = array()) {
1584
	static $traduire = false;
1585
	if ($letexte
1586
		and preg_match_all(_EXTRAIRE_IDIOME, $letexte, $regs, PREG_SET_ORDER)
1587
	) {
1588
		if (!$traduire) {
1589
			$traduire = charger_fonction('traduire', 'inc');
1590
			include_spip('inc/lang');
1591
		}
1592
		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...
1593
			$lang = $GLOBALS['spip_lang'];
1594
		}
1595
		// Compatibilité avec le prototype de fonction précédente qui utilisait un boolean
1596
		if (is_bool($options)) {
1597
			$options = array('echappe_span' => $options);
1598
		}
1599 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...
1600
			$options = array_merge($options, array('echappe_span' => false));
1601
		}
1602
1603
		foreach ($regs as $reg) {
1604
			$cle = ($reg[1] ? $reg[1] . ':' : '') . $reg[2];
1605
			$desc = $traduire($cle, $lang, true);
1606
			$l = $desc->langue;
1607
			// si pas de traduction, on laissera l'écriture de l'idiome entier dans le texte.
1608
			if (strlen($desc->texte)) {
1609
				$trad = code_echappement($desc->texte, 'idiome', false);
1610
				if ($l !== $lang) {
1611
					$trad = str_replace("'", '"', inserer_attribut($trad, 'lang', $l));
1612
				}
1613 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...
1614
					$trad = str_replace("'", '"', inserer_attribut($trad, 'dir', lang_dir($l)));
1615
				}
1616
				if (!$options['echappe_span']) {
1617
					$trad = echappe_retour($trad, 'idiome');
1618
				}
1619
				$letexte = str_replace($reg[0], $trad, $letexte);
1620
			}
1621
		}
1622
	}
1623
	return $letexte;
1624
}
1625
1626
/**
1627
 * Expression régulière pour obtenir le contenu des extraits polyglottes `<multi>`
1628
 *
1629
 * @var string
1630
 */
1631
define('_EXTRAIRE_MULTI', "@<multi>(.*?)</multi>@sS");
1632
1633
1634
/**
1635
 * Extrait une langue des extraits polyglottes (`<multi>`)
1636
 *
1637
 * Retrouve les balises `<multi>` d'un texte et remplace son contenu
1638
 * par l'extrait correspondant à la langue demandée.
1639
 *
1640
 * Si la langue demandée n'est pas trouvée dans le multi, ni une langue
1641
 * approchante (exemple `fr` si on demande `fr_TU`), on retourne l'extrait
1642
 * correspondant à la langue par défaut (option 'lang_defaut'), qui est
1643
 * par défaut la langue du site. Et si l'extrait n'existe toujours pas
1644
 * dans cette langue, ça utilisera la première langue utilisée
1645
 * dans la balise `<multi>`.
1646
 *
1647
 * Ne pas mettre de span@lang=fr si on est déjà en fr.
1648
 *
1649
 * @filtre
1650
 * @link http://www.spip.net/5332
1651
 *
1652
 * @uses extraire_trads()
1653
 * @uses approcher_langue()
1654
 * @uses lang_typo()
1655
 * @uses code_echappement()
1656
 * @uses echappe_retour()
1657
 *
1658
 * @param string $letexte
1659
 * @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...
1660
 *     Langue à retrouver (si vide, utilise la langue en cours).
1661
 * @param array $options Options {
1662
 * @type bool $echappe_span
1663
 *         True pour échapper les balises span (false par défaut)
1664
 * @type string $lang_defaut
1665
 *         Code de langue : permet de définir la langue utilisée par défaut,
1666
 *         en cas d'absence de traduction dans la langue demandée.
1667
 *         Par défaut la langue du site.
1668
 *         Indiquer 'aucune' pour ne pas retourner de texte si la langue
1669
 *         exacte n'a pas été trouvée.
1670
 * }
1671
 * @return string
1672
 **/
1673
function extraire_multi($letexte, $lang = null, $options = array()) {
1674
1675
	if ($letexte
1676
		and preg_match_all(_EXTRAIRE_MULTI, $letexte, $regs, PREG_SET_ORDER)
1677
	) {
1678
		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...
1679
			$lang = $GLOBALS['spip_lang'];
1680
		}
1681
1682
		// Compatibilité avec le prototype de fonction précédente qui utilisait un boolean
1683
		if (is_bool($options)) {
1684
			$options = array('echappe_span' => $options, 'lang_defaut' => _LANGUE_PAR_DEFAUT);
1685
		}
1686 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...
1687
			$options = array_merge($options, array('echappe_span' => false));
1688
		}
1689
		if (!isset($options['lang_defaut'])) {
1690
			$options = array_merge($options, array('lang_defaut' => _LANGUE_PAR_DEFAUT));
1691
		}
1692
1693
		include_spip('inc/lang');
1694
		foreach ($regs as $reg) {
1695
			// chercher la version de la langue courante
1696
			$trads = extraire_trads($reg[1]);
1697
			if ($l = approcher_langue($trads, $lang)) {
1698
				$trad = $trads[$l];
1699
			} else {
1700
				if ($options['lang_defaut'] == 'aucune') {
1701
					$trad = '';
1702
				} else {
1703
					// langue absente, prendre le fr ou une langue précisée (meme comportement que inc/traduire.php)
1704
					// ou la premiere dispo
1705
					// mais typographier le texte selon les regles de celle-ci
1706
					// Attention aux blocs multi sur plusieurs lignes
1707
					if (!$l = approcher_langue($trads, $options['lang_defaut'])) {
1708
						$l = key($trads);
1709
					}
1710
					$trad = $trads[$l];
1711
					$typographie = charger_fonction(lang_typo($l), 'typographie');
1712
					$trad = $typographie($trad);
1713
					// Tester si on echappe en span ou en div
1714
					// il ne faut pas echapper en div si propre produit un seul paragraphe
1715
					include_spip('inc/texte');
1716
					$trad_propre = preg_replace(",(^<p[^>]*>|</p>$),Uims", "", propre($trad));
1717
					$mode = preg_match(',</?(' . _BALISES_BLOCS . ')[>[:space:]],iS', $trad_propre) ? 'div' : 'span';
1718
					$trad = code_echappement($trad, 'multi', false, $mode);
1719
					$trad = str_replace("'", '"', inserer_attribut($trad, 'lang', $l));
1720 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...
1721
						$trad = str_replace("'", '"', inserer_attribut($trad, 'dir', lang_dir($l)));
1722
					}
1723
					if (!$options['echappe_span']) {
1724
						$trad = echappe_retour($trad, 'multi');
1725
					}
1726
				}
1727
			}
1728
			$letexte = str_replace($reg[0], $trad, $letexte);
1729
		}
1730
	}
1731
1732
	return $letexte;
1733
}
1734
1735
/**
1736
 * Convertit le contenu d'une balise `<multi>` en un tableau
1737
 *
1738
 * Exemple de blocs.
1739
 * - `texte par défaut [fr] en français [en] en anglais`
1740
 * - `[fr] en français [en] en anglais`
1741
 *
1742
 * @param string $bloc
1743
 *     Le contenu intérieur d'un bloc multi
1744
 * @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...
1745
 *     Peut retourner un code de langue vide, lorsqu'un texte par défaut est indiqué.
1746
 **/
1747
function extraire_trads($bloc) {
1748
	$lang = '';
1749
// ce reg fait planter l'analyse multi s'il y a de l'{italique} dans le champ
1750
//	while (preg_match("/^(.*?)[{\[]([a-z_]+)[}\]]/siS", $bloc, $regs)) {
1751
	while (preg_match("/^(.*?)[\[]([a-z_]+)[\]]/siS", $bloc, $regs)) {
1752
		$texte = trim($regs[1]);
1753
		if ($texte or $lang) {
1754
			$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...
1755
		}
1756
		$bloc = substr($bloc, strlen($regs[0]));
1757
		$lang = $regs[2];
1758
	}
1759
	$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...
1760
1761
	return $trads;
1762
}
1763
1764
1765
/**
1766
 * Calculer l'initiale d'un nom
1767
 *
1768
 * @param string $nom
1769
 * @return string L'initiale en majuscule
1770
 */
1771
function filtre_initiale($nom) {
1772
	return spip_substr(trim(strtoupper(extraire_multi($nom))), 0, 1);
1773
}
1774
1775
1776
/**
1777
 * Retourne la donnée si c'est la première fois qu'il la voit
1778
 *
1779
 * Il est possible de gérer différentes "familles" de données avec
1780
 * le second paramètre.
1781
 *
1782
 * @filtre
1783
 * @link http://www.spip.net/4320
1784
 * @example
1785
 *     ```
1786
 *     [(#ID_SECTEUR|unique)]
1787
 *     [(#ID_SECTEUR|unique{tete})] n'a pas d'incidence sur
1788
 *     [(#ID_SECTEUR|unique{pied})]
1789
 *     [(#ID_SECTEUR|unique{pied,1})] affiche le nombre d'éléments.
1790
 *     Préférer totefois #TOTAL_UNIQUE{pied}
1791
 *     ```
1792
 *
1793
 * @todo
1794
 *    Ameliorations possibles :
1795
 *
1796
 *    1) si la donnée est grosse, mettre son md5 comme clé
1797
 *    2) purger $mem quand on change de squelette (sinon bug inclusions)
1798
 *
1799
 * @param string $donnee
1800
 *      Donnée que l'on souhaite unique
1801
 * @param string $famille
1802
 *      Famille de stockage (1 unique donnée par famille)
1803
 *
1804
 *      - _spip_raz_ : (interne) Vide la pile de mémoire et la retourne
1805
 *      - _spip_set_ : (interne) Affecte la pile de mémoire avec la donnée
1806
 * @param bool $cpt
1807
 *      True pour obtenir le nombre d'éléments différents stockés
1808
 * @return string|int|array|null|void
1809
 *
1810
 *      - string : Donnée si c'est la première fois qu'elle est vue
1811
 *      - void : si la donnée a déjà été vue
1812
 *      - int : si l'on demande le nombre d'éléments
1813
 *      - array (interne) : si on dépile
1814
 *      - null (interne) : si on empile
1815
 **/
1816
function unique($donnee, $famille = '', $cpt = false) {
1817
	static $mem = array();
1818
	// permettre de vider la pile et de la restaurer
1819
	// pour le calcul de introduction...
1820
	if ($famille == '_spip_raz_') {
1821
		$tmp = $mem;
1822
		$mem = array();
1823
1824
		return $tmp;
1825
	} elseif ($famille == '_spip_set_') {
1826
		$mem = $donnee;
1827
1828
		return;
1829
	}
1830
	// eviter une notice
1831
	if (!isset($mem[$famille])) {
1832
		$mem[$famille] = array();
1833
	}
1834
	if ($cpt) {
1835
		return count($mem[$famille]);
1836
	}
1837
	// eviter une notice
1838
	if (!isset($mem[$famille][$donnee])) {
1839
		$mem[$famille][$donnee] = 0;
1840
	}
1841
	if (!($mem[$famille][$donnee]++)) {
1842
		return $donnee;
1843
	}
1844
}
1845
1846
1847
/**
1848
 * Filtre qui alterne des valeurs en fonction d'un compteur
1849
 *
1850
 * Affiche à tour de rôle et dans l'ordre, un des arguments transmis
1851
 * à chaque incrément du compteur.
1852
 *
1853
 * S'il n'y a qu'un seul argument, et que c'est un tableau,
1854
 * l'alternance se fait sur les valeurs du tableau.
1855
 *
1856
 * Souvent appliqué à l'intérieur d'une boucle, avec le compteur `#COMPTEUR_BOUCLE`
1857
 *
1858
 * @example
1859
 *     - `[(#COMPTEUR_BOUCLE|alterner{bleu,vert,rouge})]`
1860
 *     - `[(#COMPTEUR_BOUCLE|alterner{#LISTE{bleu,vert,rouge}})]`
1861
 *
1862
 * @filtre
1863
 * @link http://www.spip.net/4145
1864
 *
1865
 * @param int $i
1866
 *     Le compteur
1867
 * @return mixed
1868
 *     Une des valeurs en fonction du compteur.
1869
 **/
1870
function alterner($i) {
1871
	// recuperer les arguments (attention fonctions un peu space)
1872
	$num = func_num_args();
1873
	$args = func_get_args();
1874
1875
	if ($num == 2 && is_array($args[1])) {
1876
		$args = $args[1];
1877
		array_unshift($args, '');
1878
		$num = count($args);
1879
	}
1880
1881
	// renvoyer le i-ieme argument, modulo le nombre d'arguments
1882
	return $args[(intval($i) - 1) % ($num - 1) + 1];
1883
}
1884
1885
1886
/**
1887
 * Récupérer un attribut d'une balise HTML
1888
 *
1889
 * la regexp est mortelle : cf. `tests/unit/filtres/extraire_attribut.php`
1890
 * Si on a passé un tableau de balises, renvoyer un tableau de résultats
1891
 * (dans ce cas l'option `$complet` n'est pas disponible)
1892
 *
1893
 * @param string|array $balise
1894
 *     Texte ou liste de textes dont on veut extraire des balises
1895
 * @param string $attribut
1896
 *     Nom de l'attribut désiré
1897
 * @param bool $complet
1898
 *     True pour retourner un tableau avec
1899
 *     - le texte de la balise
1900
 *     - l'ensemble des résultats de la regexp ($r)
1901
 * @return string|array
1902
 *     - Texte de l'attribut retourné, ou tableau des texte d'attributs
1903
 *       (si 1er argument tableau)
1904
 *     - Tableau complet (si 2e argument)
1905
 **/
1906
function extraire_attribut($balise, $attribut, $complet = false) {
1907
	if (is_array($balise)) {
1908
		array_walk($balise,
1909
			create_function('&$a,$key,$t',
1910
				'$a = extraire_attribut($a,$t);'
1911
			),
1912
			$attribut);
1913
1914
		return $balise;
1915
	}
1916
	if (preg_match(
1917
		',(^.*?<(?:(?>\s*)(?>[\w:.-]+)(?>(?:=(?:"[^"]*"|\'[^\']*\'|[^\'"]\S*))?))*?)(\s+'
1918
		. $attribut
1919
		. '(?:=\s*("[^"]*"|\'[^\']*\'|[^\'"]\S*))?)()((?:[\s/][^>]*)?>.*),isS',
1920
1921
		$balise, $r)) {
1922
		if (isset($r[3][0]) and ($r[3][0] == '"' || $r[3][0] == "'")) {
1923
			$r[4] = substr($r[3], 1, -1);
1924
			$r[3] = $r[3][0];
1925
		} elseif ($r[3] !== '') {
1926
			$r[4] = $r[3];
1927
			$r[3] = '';
1928
		} else {
1929
			$r[4] = trim($r[2]);
1930
		}
1931
		$att = $r[4];
1932 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...
1933
			$att = str_replace(array("&#039;", "&#39;", "&#034;", "&#34;"), array("'", "'", '"', '"'), $att);
1934
		}
1935
		$att = filtrer_entites($att);
1936
	} else {
1937
		$att = null;
1938
	}
1939
1940
	if ($complet) {
1941
		return array($att, $r);
1942
	} else {
1943
		return $att;
1944
	}
1945
}
1946
1947
/**
1948
 * Insérer (ou modifier) un attribut html dans une balise
1949
 *
1950
 * @example
1951
 *     - `[(#LOGO_ARTICLE|inserer_attribut{class, logo article})]`
1952
 *     - `[(#LOGO_ARTICLE|inserer_attribut{alt, #TTTRE|attribut_html|couper{60}})]`
1953
 *     - `[(#FICHIER|image_reduire{40}|inserer_attribut{data-description, #DESCRIPTIF})]`
1954
 *       Laissera les balises HTML de la valeur (ici `#DESCRIPTIF`) si on n'applique pas le
1955
 *       filtre `attribut_html` dessus.
1956
 *
1957
 * @filtre
1958
 * @link http://www.spip.net/4294
1959
 * @uses attribut_html()
1960
 * @uses extraire_attribut()
1961
 *
1962
 * @param string $balise
1963
 *     Code html de la balise (ou contenant une balise)
1964
 * @param string $attribut
1965
 *     Nom de l'attribut html à modifier
1966
 * @param string $val
1967
 *     Valeur de l'attribut à appliquer
1968
 * @param bool $proteger
1969
 *     Prépare la valeur en tant qu'attribut de balise (mais conserve les balises html).
1970
 * @param bool $vider
1971
 *     True pour vider l'attribut. Une chaîne vide pour `$val` fera pareil.
1972
 * @return string
1973
 *     Code html modifié
1974
 **/
1975
function inserer_attribut($balise, $attribut, $val, $proteger = true, $vider = false) {
1976
	// preparer l'attribut
1977
	// supprimer les &nbsp; etc mais pas les balises html
1978
	// qui ont un sens dans un attribut value d'un input
1979
	if ($proteger) {
1980
		$val = attribut_html($val, false);
1981
	}
1982
1983
	// echapper les ' pour eviter tout bug
1984
	$val = str_replace("'", "&#039;", $val);
1985
	if ($vider and strlen($val) == 0) {
1986
		$insert = '';
1987
	} else {
1988
		$insert = " $attribut='$val'";
1989
	}
1990
1991
	list($old, $r) = extraire_attribut($balise, $attribut, true);
1992
1993
	if ($old !== null) {
1994
		// Remplacer l'ancien attribut du meme nom
1995
		$balise = $r[1] . $insert . $r[5];
1996
	} else {
1997
		// preferer une balise " />" (comme <img />)
1998
		if (preg_match(',/>,', $balise)) {
1999
			$balise = preg_replace(",\s?/>,S", $insert . " />", $balise, 1);
2000
		} // sinon une balise <a ...> ... </a>
2001
		else {
2002
			$balise = preg_replace(",\s?>,S", $insert . ">", $balise, 1);
2003
		}
2004
	}
2005
2006
	return $balise;
2007
}
2008
2009
/**
2010
 * Supprime un attribut HTML
2011
 *
2012
 * @example `[(#LOGO_ARTICLE|vider_attribut{class})]`
2013
 *
2014
 * @filtre
2015
 * @link http://www.spip.net/4142
2016
 * @uses inserer_attribut()
2017
 * @see  extraire_attribut()
2018
 *
2019
 * @param string $balise Code HTML de l'élément
2020
 * @param string $attribut Nom de l'attribut à enlever
2021
 * @return string Code HTML sans l'attribut
2022
 **/
2023
function vider_attribut($balise, $attribut) {
2024
	return inserer_attribut($balise, $attribut, '', false, true);
2025
}
2026
2027
2028
/**
2029
 * Un filtre pour déterminer le nom du statut des inscrits
2030
 *
2031
 * @param void|int $id
2032
 * @param string $mode
2033
 * @return string
2034
 */
2035
function tester_config($id, $mode = '') {
2036
	include_spip('action/inscrire_auteur');
2037
2038
	return tester_statut_inscription($mode, $id);
2039
}
2040
2041
//
2042
// Quelques fonctions de calcul arithmetique
2043
//
2044
function floatstr($a) { return str_replace(',','.',(string)floatval($a)); }
2045
function strize($f, $a, $b) { return floatstr($f(floatstr($a),floatstr($b))); }
2046
2047
/**
2048
 * Additionne 2 nombres
2049
 *
2050
 * @filtre
2051
 * @link http://www.spip.net/4307
2052
 * @see moins()
2053
 * @example
2054
 *     ```
2055
 *     [(#VAL{28}|plus{14})]
2056
 *     ```
2057
 *
2058
 * @param int $a
2059
 * @param int $b
2060
 * @return int $a+$b
2061
 **/
2062
function plus($a, $b) {
2063
	return $a + $b;
2064
}
2065
function strplus($a, $b) {return strize('plus', $a, $b);}
2066
/**
2067
 * Soustrait 2 nombres
2068
 *
2069
 * @filtre
2070
 * @link http://www.spip.net/4302
2071
 * @see plus()
2072
 * @example
2073
 *     ```
2074
 *     [(#VAL{28}|moins{14})]
2075
 *     ```
2076
 *
2077
 * @param int $a
2078
 * @param int $b
2079
 * @return int $a-$b
2080
 **/
2081
function moins($a, $b) {
2082
	return $a - $b;
2083
}
2084
function strmoins($a, $b) {return strize('moins', $a, $b);}
2085
2086
/**
2087
 * Multiplie 2 nombres
2088
 *
2089
 * @filtre
2090
 * @link http://www.spip.net/4304
2091
 * @see div()
2092
 * @see modulo()
2093
 * @example
2094
 *     ```
2095
 *     [(#VAL{28}|mult{14})]
2096
 *     ```
2097
 *
2098
 * @param int $a
2099
 * @param int $b
2100
 * @return int $a*$b
2101
 **/
2102
function mult($a, $b) {
2103
	return $a * $b;
2104
}
2105
function strmult($a, $b) {return strize('mult', $a, $b);}
2106
2107
/**
2108
 * Divise 2 nombres
2109
 *
2110
 * @filtre
2111
 * @link http://www.spip.net/4279
2112
 * @see mult()
2113
 * @see modulo()
2114
 * @example
2115
 *     ```
2116
 *     [(#VAL{28}|div{14})]
2117
 *     ```
2118
 *
2119
 * @param int $a
2120
 * @param int $b
2121
 * @return int $a/$b (ou 0 si $b est nul)
2122
 **/
2123
function div($a, $b) {
2124
	return $b ? $a / $b : 0;
2125
}
2126
function strdiv($a, $b) {return strize('div', $a, $b);}
2127
2128
/**
2129
 * Retourne le modulo 2 nombres
2130
 *
2131
 * @filtre
2132
 * @link http://www.spip.net/4301
2133
 * @see mult()
2134
 * @see div()
2135
 * @example
2136
 *     ```
2137
 *     [(#VAL{28}|modulo{14})]
2138
 *     ```
2139
 *
2140
 * @param int $nb
2141
 * @param int $mod
2142
 * @param int $add
2143
 * @return int ($nb % $mod) + $add
2144
 **/
2145
function modulo($nb, $mod, $add = 0) {
2146
	return ($mod ? $nb % $mod : 0) + $add;
2147
}
2148
2149
2150
/**
2151
 * Vérifie qu'un nom (d'auteur) ne comporte pas d'autres tags que <multi>
2152
 * et ceux volontairement spécifiés dans la constante
2153
 *
2154
 * @param string $nom
2155
 *      Nom (signature) proposé
2156
 * @return bool
2157
 *      - false si pas conforme,
2158
 *      - true sinon
2159
 **/
2160
function nom_acceptable($nom) {
2161
	if (!is_string($nom)) {
2162
		return false;
2163
	}
2164
	if (!defined('_TAGS_NOM_AUTEUR')) {
2165
		define('_TAGS_NOM_AUTEUR', '');
2166
	}
2167
	$tags_acceptes = array_unique(explode(',', 'multi,' . _TAGS_NOM_AUTEUR));
2168
	foreach ($tags_acceptes as $tag) {
2169
		if (strlen($tag)) {
2170
			$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...
2171
			$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...
2172
			$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...
2173
			$remp2[] = '\x60/' . trim($tag) . '\x61';
2174
		}
2175
	}
2176
	$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...
2177
2178
	return str_replace('&lt;', '<', $v_nom) == $nom;
2179
}
2180
2181
2182
/**
2183
 * Vérifier la conformité d'une ou plusieurs adresses email (suivant RFC 822)
2184
 *
2185
 * @param string $adresses
2186
 *      Adresse ou liste d'adresse
2187
 * @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...
2188
 *      - false si pas conforme,
2189
 *      - la normalisation de la dernière adresse donnée sinon
2190
 **/
2191
function email_valide($adresses) {
2192
	// eviter d'injecter n'importe quoi dans preg_match
2193
	if (!is_string($adresses)) {
2194
		return false;
2195
	}
2196
2197
	// Si c'est un spammeur autant arreter tout de suite
2198
	if (preg_match(",[\n\r].*(MIME|multipart|Content-),i", $adresses)) {
2199
		spip_log("Tentative d'injection de mail : $adresses");
2200
2201
		return false;
2202
	}
2203
2204
	foreach (explode(',', $adresses) as $v) {
2205
		// nettoyer certains formats
2206
		// "Marie Toto <[email protected]>"
2207
		$adresse = trim(preg_replace(",^[^<>\"]*<([^<>\"]+)>$,i", "\\1", $v));
2208
		// RFC 822
2209
		if (!preg_match('#^[^()<>@,;:\\"/[:space:]]+(@([-_0-9a-z]+\.)*[-_0-9a-z]+)$#i', $adresse)) {
2210
			return false;
2211
		}
2212
	}
2213
2214
	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...
2215
}
2216
2217
/**
2218
 * Permet d'afficher un symbole à côté des liens pointant vers les
2219
 * documents attachés d'un article (liens ayant `rel=enclosure`).
2220
 *
2221
 * @filtre
2222
 * @link http://www.spip.net/4134
2223
 *
2224
 * @param string $tags Texte
2225
 * @return string Texte
2226
 **/
2227
function afficher_enclosures($tags) {
2228
	$s = array();
2229
	foreach (extraire_balises($tags, 'a') as $tag) {
2230
		if (extraire_attribut($tag, 'rel') == 'enclosure'
2231
			and $t = extraire_attribut($tag, 'href')
2232
		) {
2233
			$s[] = preg_replace(',>[^<]+</a>,S',
2234
				'>'
2235
				. 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 2231 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...
2236
					'title="' . attribut_html($t) . '"')
0 ignored issues
show
Bug introduced by
It seems like $t defined by extraire_attribut($tag, 'href') on line 2231 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...
2237
				. '</a>', $tag);
2238
		}
2239
	}
2240
2241
	return join('&nbsp;', $s);
2242
}
2243
2244
/**
2245
 * Filtre des liens HTML `<a>` selon la valeur de leur attribut `rel`
2246
 * et ne retourne que ceux là.
2247
 *
2248
 * @filtre
2249
 * @link http://www.spip.net/4187
2250
 *
2251
 * @param string $tags Texte
2252
 * @param string $rels Attribut `rel` à capturer (ou plusieurs séparés par des virgules)
2253
 * @return string Liens trouvés
2254
 **/
2255
function afficher_tags($tags, $rels = 'tag, directory') {
2256
	$s = array();
2257
	foreach (extraire_balises($tags, 'a') as $tag) {
2258
		$rel = extraire_attribut($tag, 'rel');
2259
		if (strstr(",$rels,", ",$rel,")) {
2260
			$s[] = $tag;
2261
		}
2262
	}
2263
2264
	return join(', ', $s);
2265
}
2266
2267
2268
/**
2269
 * Convertir les médias fournis par un flux RSS (podcasts)
2270
 * en liens conformes aux microformats
2271
 *
2272
 * Passe un `<enclosure url="fichier" length="5588242" type="audio/mpeg"/>`
2273
 * au format microformat `<a rel="enclosure" href="fichier" ...>fichier</a>`.
2274
 *
2275
 * Peut recevoir un `<link` ou un `<media:content` parfois.
2276
 *
2277
 * Attention : `length="zz"` devient `title="zz"`, pour rester conforme.
2278
 *
2279
 * @filtre
2280
 * @see microformat2enclosure() Pour l'inverse
2281
 *
2282
 * @param string $e Tag RSS `<enclosure>`
2283
 * @return string Tag HTML `<a>` avec microformat.
2284
 **/
2285
function enclosure2microformat($e) {
2286
	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...
2287
		$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...
2288
	}
2289
	$type = extraire_attribut($e, 'type');
2290
	if (!$length = extraire_attribut($e, 'length')) {
2291
		# <media:content : longeur dans fileSize. On tente.
2292
		$length = extraire_attribut($e, 'fileSize');
2293
	}
2294
	$fichier = basename($url);
2295
2296
	return '<a rel="enclosure"'
2297
	. ($url ? ' href="' . spip_htmlspecialchars($url) . '"' : '')
2298
	. ($type ? ' type="' . spip_htmlspecialchars($type) . '"' : '')
0 ignored issues
show
Bug introduced by
It seems like $type defined by extraire_attribut($e, 'type') on line 2289 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...
2299
	. ($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...
2300
	. '>' . $fichier . '</a>';
2301
}
2302
2303
/**
2304
 * Convertir les liens conformes aux microformats en médias pour flux RSS,
2305
 * par exemple pour les podcasts
2306
 *
2307
 * Passe un texte ayant des liens avec microformat
2308
 * `<a rel="enclosure" href="fichier" ...>fichier</a>`
2309
 * au format RSS `<enclosure url="fichier" ... />`.
2310
 *
2311
 * @filtre
2312
 * @see enclosure2microformat() Pour l'inverse
2313
 *
2314
 * @param string $tags Texte HTML ayant des tag `<a>` avec microformat
2315
 * @return string Tags RSS `<enclosure>`.
2316
 **/
2317
function microformat2enclosure($tags) {
2318
	$enclosures = array();
2319
	foreach (extraire_balises($tags, 'a') as $e) {
2320
		if (extraire_attribut($e, 'rel') == 'enclosure') {
2321
			$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...
2322
			$type = extraire_attribut($e, 'type');
2323
			if (!$length = intval(extraire_attribut($e, 'title'))) {
2324
				$length = intval(extraire_attribut($e, 'length'));
2325
			} # vieux data
2326
			$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...
2327
			$enclosures[] = '<enclosure'
2328
				. ($url ? ' url="' . spip_htmlspecialchars($url) . '"' : '')
2329
				. ($type ? ' type="' . spip_htmlspecialchars($type) . '"' : '')
0 ignored issues
show
Bug introduced by
It seems like $type defined by extraire_attribut($e, 'type') on line 2322 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...
2330
				. ($length ? ' length="' . $length . '"' : '')
2331
				. ' />';
2332
		}
2333
	}
2334
2335
	return join("\n", $enclosures);
2336
}
2337
2338
2339
/**
2340
 * Créer les éléments ATOM `<dc:subject>` à partir des tags
2341
 *
2342
 * Convertit les liens avec attribut `rel="tag"`
2343
 * en balise `<dc:subject></dc:subject>` pour les flux RSS au format Atom.
2344
 *
2345
 * @filtre
2346
 *
2347
 * @param string $tags Texte
2348
 * @return string Tags RSS Atom `<dc:subject>`.
2349
 **/
2350
function tags2dcsubject($tags) {
2351
	$subjects = '';
2352
	foreach (extraire_balises($tags, 'a') as $e) {
2353
		if (extraire_attribut($e, rel) == 'tag') {
2354
			$subjects .= '<dc:subject>'
2355
				. texte_backend(textebrut($e))
2356
				. '</dc:subject>' . "\n";
2357
		}
2358
	}
2359
2360
	return $subjects;
2361
}
2362
2363
/**
2364
 * Retourne la premiere balise html du type demandé
2365
 *
2366
 * Retourne le contenu d'une balise jusqu'à la première fermeture rencontrée
2367
 * du même type.
2368
 * Si on a passe un tableau de textes, retourne un tableau de resultats.
2369
 *
2370
 * @example `[(#DESCRIPTIF|extraire_balise{img})]`
2371
 *
2372
 * @filtre
2373
 * @link http://www.spip.net/4289
2374
 * @see extraire_balises()
2375
 * @note
2376
 *     Attention : les résultats peuvent être incohérents sur des balises imbricables,
2377
 *     tel que demander à extraire `div` dans le texte `<div> un <div> mot </div> absent </div>`,
2378
 *     ce qui retournerait `<div> un <div> mot </div>` donc.
2379
 *
2380
 * @param string|array $texte
2381
 *     Texte(s) dont on souhaite extraire une balise html
2382
 * @param string $tag
2383
 *     Nom de la balise html à extraire
2384
 * @return void|string|array
2385
 *     - Code html de la balise, sinon rien
2386
 *     - Tableau de résultats, si tableau en entrée.
2387
 **/
2388
function extraire_balise($texte, $tag = 'a') {
2389
	if (is_array($texte)) {
2390
		array_walk(
2391
			$texte,
2392
			create_function('&$a,$key,$t', '$a = extraire_balise($a,$t);'),
2393
			$tag
2394
		);
2395
2396
		return $texte;
2397
	}
2398
2399
	if (preg_match(
2400
		",<$tag\b[^>]*(/>|>.*</$tag\b[^>]*>|>),UimsS",
2401
		$texte, $regs)) {
2402
		return $regs[0];
2403
	}
2404
}
2405
2406
/**
2407
 * Extrait toutes les balises html du type demandé
2408
 *
2409
 * Retourne dans un tableau le contenu de chaque balise jusqu'à la première
2410
 * fermeture rencontrée du même type.
2411
 * Si on a passe un tableau de textes, retourne un tableau de resultats.
2412
 *
2413
 * @example `[(#TEXTE|extraire_balises{img}|implode{" - "})]`
2414
 *
2415
 * @filtre
2416
 * @link http://www.spip.net/5618
2417
 * @see extraire_balise()
2418
 * @note
2419
 *     Attention : les résultats peuvent être incohérents sur des balises imbricables,
2420
 *     tel que demander à extraire `div` dans un texte.
2421
 *
2422
 * @param string|array $texte
2423
 *     Texte(s) dont on souhaite extraire une balise html
2424
 * @param string $tag
2425
 *     Nom de la balise html à extraire
2426
 * @return array
2427
 *     - Liste des codes html des occurrences de la balise, sinon tableau vide
2428
 *     - Tableau de résultats, si tableau en entrée.
2429
 **/
2430
function extraire_balises($texte, $tag = 'a') {
2431
	if (is_array($texte)) {
2432
		array_walk(
2433
			$texte,
2434
			create_function('&$a,$key,$t', '$a = extraire_balises($a,$t);'),
2435
			$tag
2436
		);
2437
2438
		return $texte;
2439
	}
2440
2441
	if (preg_match_all(
2442
		",<${tag}\b[^>]*(/>|>.*</${tag}\b[^>]*>|>),UimsS",
2443
		$texte, $regs, PREG_PATTERN_ORDER)) {
2444
		return $regs[0];
2445
	} else {
2446
		return array();
2447
	}
2448
}
2449
2450
/**
2451
 * Indique si le premier argument est contenu dans le second
2452
 *
2453
 * Cette fonction est proche de `in_array()` en PHP avec comme principale
2454
 * différence qu'elle ne crée pas d'erreur si le second argument n'est pas
2455
 * un tableau (dans ce cas elle tentera de le désérialiser, et sinon retournera
2456
 * la valeur par défaut transmise).
2457
 *
2458
 * @example `[(#VAL{deux}|in_any{#LISTE{un,deux,trois}}|oui) ... ]`
2459
 *
2460
 * @filtre
2461
 * @see filtre_find() Assez proche, avec les arguments valeur et tableau inversés.
2462
 *
2463
 * @param string $val
2464
 *     Valeur à chercher dans le tableau
2465
 * @param array|string $vals
2466
 *     Tableau des valeurs. S'il ce n'est pas un tableau qui est transmis,
2467
 *     la fonction tente de la désérialiser.
2468
 * @param string $def
2469
 *     Valeur par défaut retournée si `$vals` n'est pas un tableau.
2470
 * @return string
2471
 *     - ' ' si la valeur cherchée est dans le tableau
2472
 *     - '' si la valeur n'est pas dans le tableau
2473
 *     - `$def` si on n'a pas transmis de tableau
2474
 **/
2475
function in_any($val, $vals, $def = '') {
2476
	if (!is_array($vals) and $v = unserialize($vals)) {
2477
		$vals = $v;
2478
	}
2479
2480
	return (!is_array($vals) ? $def : (in_array($val, $vals) ? ' ' : ''));
2481
}
2482
2483
2484
/**
2485
 * Retourne le résultat d'une expression mathématique simple
2486
 *
2487
 * N'accepte que les *, + et - (à ameliorer si on l'utilise vraiment).
2488
 *
2489
 * @filtre
2490
 * @example
2491
 *      ```
2492
 *      valeur_numerique("3*2") retourne 6
2493
 *      ```
2494
 *
2495
 * @param string $expr
2496
 *     Expression mathématique `nombre operateur nombre` comme `3*2`
2497
 * @return int
2498
 *     Résultat du calcul
2499
 **/
2500
function valeur_numerique($expr) {
2501
	$a = 0;
2502
	if (preg_match(',^[0-9]+(\s*[+*-]\s*[0-9]+)*$,S', trim($expr))) {
2503
		eval("\$a = $expr;");
2504
	}
2505
2506
	return intval($a);
2507
}
2508
2509
/**
2510
 * Retourne un calcul de règle de trois
2511
 *
2512
 * @filtre
2513
 * @example
2514
 *     ```
2515
 *     [(#VAL{6}|regledetrois{4,3})] retourne 8
2516
 *     ```
2517
 *
2518
 * @param int $a
2519
 * @param int $b
2520
 * @param int $c
2521
 * @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...
2522
 *      Retourne `$a*$b/$c`
2523
 **/
2524
function regledetrois($a, $b, $c) {
2525
	return round($a * $b / $c);
2526
}
2527
2528
2529
/**
2530
 * Crée des tags HTML input hidden pour chaque paramètre et valeur d'une URL
2531
 *
2532
 * Fournit la suite de Input-Hidden correspondant aux paramètres de
2533
 * l'URL donnée en argument, compatible avec les types_urls
2534
 *
2535
 * @filtre
2536
 * @link http://www.spip.net/4286
2537
 * @see balise_ACTION_FORMULAIRE()
2538
 *     Également pour transmettre les actions à un formulaire
2539
 * @example
2540
 *     ```
2541
 *     [(#ENV{action}|form_hidden)] dans un formulaire
2542
 *     ```
2543
 *
2544
 * @param string $action URL
2545
 * @return string Suite de champs input hidden
2546
 **/
2547
function form_hidden($action) {
2548
2549
	$contexte = array();
2550
	include_spip('inc/urls');
2551
	if ($p = urls_decoder_url($action, '')
2552
		and reset($p)
2553
	) {
2554
		$fond = array_shift($p);
2555
		if ($fond != '404') {
2556
			$contexte = array_shift($p);
2557
			$contexte['page'] = $fond;
2558
			$action = preg_replace('/([?]' . preg_quote($fond) . '[^&=]*[0-9]+)(&|$)/', '?&', $action);
2559
		}
2560
	}
2561
	// defaire ce qu'a injecte urls_decoder_url : a revoir en modifiant la signature de urls_decoder_url
2562
	if (defined('_DEFINIR_CONTEXTE_TYPE') and _DEFINIR_CONTEXTE_TYPE) {
2563
		unset($contexte['type']);
2564
	}
2565
	if (defined('_DEFINIR_CONTEXTE_TYPE_PAGE') and _DEFINIR_CONTEXTE_TYPE_PAGE) {
2566
		unset($contexte['type-page']);
2567
	}
2568
2569
	// on va remplir un tableau de valeurs en prenant bien soin de ne pas
2570
	// ecraser les elements de la forme mots[]=1&mots[]=2
2571
	$values = array();
2572
2573
	// d'abord avec celles de l'url
2574
	if (false !== ($p = strpos($action, '?'))) {
2575
		foreach (preg_split('/&(amp;)?/S', substr($action, $p + 1)) as $c) {
2576
			$c = explode('=', $c, 2);
2577
			$var = array_shift($c);
2578
			$val = array_shift($c);
2579
			if ($var) {
2580
				$val = rawurldecode($val);
2581
				$var = rawurldecode($var); // decoder les [] eventuels
2582 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...
2583
					$values[] = array($var, $val);
2584
				} else {
2585
					if (!isset($values[$var])) {
2586
						$values[$var] = array($var, $val);
2587
					}
2588
				}
2589
			}
2590
		}
2591
	}
2592
2593
	// ensuite avec celles du contexte, sans doublonner !
2594
	foreach ($contexte as $var => $val) {
2595 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...
2596
			$values[] = array($var, $val);
2597
		} else {
2598
			if (!isset($values[$var])) {
2599
				$values[$var] = array($var, $val);
2600
			}
2601
		}
2602
	}
2603
2604
	// puis on rassemble le tout
2605
	$hidden = array();
2606
	foreach ($values as $value) {
2607
		list($var, $val) = $value;
2608
		$hidden[] = '<input name="'
2609
			. entites_html($var)
2610
			. '"'
2611
			. (is_null($val)
2612
				? ''
2613
				: ' value="' . entites_html($val) . '"'
2614
			)
2615
			. ' type="hidden"' . "\n/>";
2616
	}
2617
2618
	return join("", $hidden);
2619
}
2620
2621
/**
2622
 * Calcule les bornes d'une pagination
2623
 *
2624
 * @filtre
2625
 *
2626
 * @param int $courante
2627
 *     Page courante
2628
 * @param int $nombre
2629
 *     Nombre de pages
2630
 * @param int $max
2631
 *     Nombre d'éléments par page
2632
 * @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...
2633
 *     Liste (première page, dernière page).
2634
 **/
2635
function filtre_bornes_pagination_dist($courante, $nombre, $max = 10) {
2636
	if ($max <= 0 or $max >= $nombre) {
2637
		return array(1, $nombre);
2638
	}
2639
2640
	$premiere = max(1, $courante - floor(($max - 1) / 2));
2641
	$derniere = min($nombre, $premiere + $max - 2);
2642
	$premiere = $derniere == $nombre ? $derniere - $max + 1 : $premiere;
2643
2644
	return array($premiere, $derniere);
2645
}
2646
2647
2648
/**
2649
 * Retourne la première valeur d'un tableau
2650
 *
2651
 * Plus précisément déplace le pointeur du tableau sur la première valeur et la retourne.
2652
 *
2653
 * @example `[(#LISTE{un,deux,trois}|reset)]` retourne 'un'
2654
 *
2655
 * @filtre
2656
 * @link http://php.net/manual/fr/function.reset.php
2657
 * @see filtre_end()
2658
 *
2659
 * @param array $array
2660
 * @return mixed|null|false
2661
 *    - null si $array n'est pas un tableau,
2662
 *    - false si le tableau est vide
2663
 *    - la première valeur du tableau sinon.
2664
 **/
2665
function filtre_reset($array) {
2666
	return !is_array($array) ? null : reset($array);
2667
}
2668
2669
/**
2670
 * Retourne la dernière valeur d'un tableau
2671
 *
2672
 * Plus précisément déplace le pointeur du tableau sur la dernière valeur et la retourne.
2673
 *
2674
 * @example `[(#LISTE{un,deux,trois}|end)]` retourne 'trois'
2675
 *
2676
 * @filtre
2677
 * @link http://php.net/manual/fr/function.end.php
2678
 * @see filtre_reset()
2679
 *
2680
 * @param array $array
2681
 * @return mixed|null|false
2682
 *    - null si $array n'est pas un tableau,
2683
 *    - false si le tableau est vide
2684
 *    - la dernière valeur du tableau sinon.
2685
 **/
2686
function filtre_end($array) {
2687
	return !is_array($array) ? null : end($array);
2688
}
2689
2690
/**
2691
 * Empile une valeur à la fin d'un tableau
2692
 *
2693
 * @example `[(#LISTE{un,deux,trois}|push{quatre}|print)]`
2694
 *
2695
 * @filtre
2696
 * @link http://www.spip.net/4571
2697
 * @link http://php.net/manual/fr/function.array-push.php
2698
 *
2699
 * @param array $array
2700
 * @param mixed $val
2701
 * @return array|string
2702
 *     - '' si $array n'est pas un tableau ou si echec.
2703
 *     - le tableau complété de la valeur sinon.
2704
 *
2705
 **/
2706
function filtre_push($array, $val) {
2707
	if (!is_array($array) or !array_push($array, $val)) {
2708
		return '';
2709
	}
2710
2711
	return $array;
2712
}
2713
2714
/**
2715
 * Indique si une valeur est contenue dans un tableau
2716
 *
2717
 * @example `[(#LISTE{un,deux,trois}|find{quatre}|oui) ... ]`
2718
 *
2719
 * @filtre
2720
 * @link http://www.spip.net/4575
2721
 * @see in_any() Assez proche, avec les paramètres tableau et valeur inversés.
2722
 *
2723
 * @param array $array
2724
 * @param mixed $val
2725
 * @return bool
2726
 *     - `false` si `$array` n'est pas un tableau
2727
 *     - `true` si la valeur existe dans le tableau, `false` sinon.
2728
 **/
2729
function filtre_find($array, $val) {
2730
	return (is_array($array) and in_array($val, $array));
2731
}
2732
2733
2734
/**
2735
 * Filtre calculant une pagination, utilisé par la balise `#PAGINATION`
2736
 *
2737
 * Le filtre cherche le modèle `pagination.html` par défaut, mais peut
2738
 * chercher un modèle de pagination particulier avec l'argument `$modele`.
2739
 * S'il `$modele='prive'`, le filtre cherchera le modèle `pagination_prive.html`.
2740
 *
2741
 * @filtre
2742
 * @see balise_PAGINATION_dist()
2743
 *
2744
 * @param int $total
2745
 *     Nombre total d'éléments
2746
 * @param string $nom
2747
 *     Nom identifiant la pagination
2748
 * @param int $position
2749
 *     Page à afficher (tel que la 3è page)
2750
 * @param int $pas
2751
 *     Nombre d'éléments par page
2752
 * @param bool $liste
2753
 *     - True pour afficher toute la liste des éléments,
2754
 *     - False pour n'afficher que l'ancre
2755
 * @param string $modele
2756
 *     Nom spécifique du modèle de pagination
2757
 * @param string $connect
2758
 *     Nom du connecteur à la base de données
2759
 * @param array $env
2760
 *     Environnement à transmettre au modèle
2761
 * @return string
2762
 *     Code HTML de la pagination
2763
 **/
2764
function filtre_pagination_dist(
2765
	$total,
2766
	$nom,
2767
	$position,
2768
	$pas,
2769
	$liste = true,
2770
	$modele = '',
2771
	$connect = '',
2772
	$env = array()
2773
) {
2774
	static $ancres = array();
2775
	if ($pas < 1) {
2776
		return '';
2777
	}
2778
	$ancre = 'pagination' . $nom; // #pagination_articles
2779
	$debut = 'debut' . $nom; // 'debut_articles'
2780
2781
	// n'afficher l'ancre qu'une fois
2782
	if (!isset($ancres[$ancre])) {
2783
		$bloc_ancre = $ancres[$ancre] = "<a name='" . $ancre . "' id='" . $ancre . "'></a>";
2784
	} else {
2785
		$bloc_ancre = '';
2786
	}
2787
	// liste = false : on ne veut que l'ancre
2788
	if (!$liste) {
2789
		return $ancres[$ancre];
2790
	}
2791
2792
	$pagination = array(
2793
		'debut' => $debut,
2794
		'url' => parametre_url(self(), 'fragment', ''), // nettoyer l'id ahah eventuel
2795
		'total' => $total,
2796
		'position' => intval($position),
2797
		'pas' => $pas,
2798
		'nombre_pages' => floor(($total - 1) / $pas) + 1,
2799
		'page_courante' => floor(intval($position) / $pas) + 1,
2800
		'ancre' => $ancre,
2801
		'bloc_ancre' => $bloc_ancre
2802
	);
2803
	if (is_array($env)) {
2804
		$pagination = array_merge($env, $pagination);
2805
	}
2806
2807
	// Pas de pagination
2808
	if ($pagination['nombre_pages'] <= 1) {
2809
		return '';
2810
	}
2811
2812
	if ($modele) {
2813
		$modele = '_' . $modele;
2814
	}
2815
2816
	return recuperer_fond("modeles/pagination$modele", $pagination, array('trim' => true), $connect);
2817
}
2818
2819
2820
/**
2821
 * Passer les url relatives à la css d'origine en url absolues
2822
 *
2823
 * @uses suivre_lien()
2824
 *
2825
 * @param string $contenu
2826
 *     Contenu du fichier CSS
2827
 * @param string $source
2828
 *     Chemin du fichier CSS
2829
 * @return string
2830
 *     Contenu avec urls en absolus
2831
 **/
2832
function urls_absolues_css($contenu, $source) {
2833
	$path = suivre_lien(url_absolue($source), './');
2834
2835
	return preg_replace_callback(
2836
		",url\s*\(\s*['\"]?([^'\"/#\s][^:]*)['\"]?\s*\),Uims",
2837
		create_function('$x',
2838
			'return "url(\"".suivre_lien("' . $path . '",$x[1])."\")";'
2839
		), $contenu);
2840
}
2841
2842
2843
/**
2844
 * Inverse le code CSS (left <--> right) d'une feuille de style CSS
2845
 *
2846
 * Récupère le chemin d'une CSS existante et :
2847
 *
2848
 * 1. regarde si une CSS inversée droite-gauche existe dans le meme répertoire
2849
 * 2. sinon la crée (ou la recrée) dans `_DIR_VAR/cache_css/`
2850
 *
2851
 * Si on lui donne à manger une feuille nommée `*_rtl.css` il va faire l'inverse.
2852
 *
2853
 * @filtre
2854
 * @example
2855
 *     ```
2856
 *     [<link rel="stylesheet" href="(#CHEMIN{css/perso.css}|direction_css)" type="text/css" />]
2857
 *     ```
2858
 * @param string $css
2859
 *     Chemin vers le fichier CSS
2860
 * @param string $voulue
2861
 *     Permet de forcer le sens voulu (en indiquant `ltr`, `rtl` ou un
2862
 *     code de langue). En absence, prend le sens de la langue en cours.
2863
 *
2864
 * @return string
2865
 *     Chemin du fichier CSS inversé
2866
 **/
2867
function direction_css($css, $voulue = '') {
2868
	if (!preg_match(',(_rtl)?\.css$,i', $css, $r)) {
2869
		return $css;
2870
	}
2871
2872
	// si on a precise le sens voulu en argument, le prendre en compte
2873
	if ($voulue = strtolower($voulue)) {
2874
		if ($voulue != 'rtl' and $voulue != 'ltr') {
2875
			$voulue = lang_dir($voulue);
2876
		}
2877
	} else {
2878
		$voulue = lang_dir();
2879
	}
2880
2881
	$r = count($r) > 1;
2882
	$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...
2883
	$dir = $r ? 'rtl' : 'ltr';
2884
	$ndir = $r ? 'ltr' : 'rtl';
2885
2886
	if ($voulue == $dir) {
2887
		return $css;
2888
	}
2889
2890
	if (
2891
		// url absolue
2892
		preg_match(",^http:,i", $css)
2893
		// ou qui contient un ?
2894
		or (($p = strpos($css, '?')) !== false)
2895
	) {
2896
		$distant = true;
2897
		$cssf = parse_url($css);
2898
		$cssf = $cssf['path'] . ($cssf['query'] ? "?" . $cssf['query'] : "");
2899
		$cssf = preg_replace(',[?:&=],', "_", $cssf);
2900
	} else {
2901
		$distant = false;
2902
		$cssf = $css;
2903
		// 1. regarder d'abord si un fichier avec la bonne direction n'est pas aussi
2904
		//propose (rien a faire dans ce cas)
2905
		$f = preg_replace(',(_rtl)?\.css$,i', '_' . $ndir . '.css', $css);
2906
		if (@file_exists($f)) {
2907
			return $f;
2908
		}
2909
	}
2910
2911
	// 2.
2912
	$dir_var = sous_repertoire(_DIR_VAR, 'cache-css');
2913
	$f = $dir_var
2914
		. preg_replace(',.*/(.*?)(_rtl)?\.css,', '\1', $cssf)
2915
		. '.' . substr(md5($cssf), 0, 4) . '_' . $ndir . '.css';
2916
2917
	// la css peut etre distante (url absolue !)
2918
	if ($distant) {
2919
		include_spip('inc/distant');
2920
		$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...
2921
		if (!$contenu) {
2922
			return $css;
2923
		}
2924
	} else {
2925 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...
2926
			and (_VAR_MODE != 'recalcul')
2927
		) {
2928
			return $f;
2929
		}
2930
		if (!lire_fichier($css, $contenu)) {
2931
			return $css;
2932
		}
2933
	}
2934
2935
	$contenu = str_replace(
2936
		array('right', 'left', '@@@@L E F T@@@@'),
2937
		array('@@@@L E F T@@@@', 'right', 'left'),
2938
		$contenu);
2939
2940
	// reperer les @import auxquels il faut propager le direction_css
2941
	preg_match_all(",\@import\s*url\s*\(\s*['\"]?([^'\"/][^:]*)['\"]?\s*\),Uims", $contenu, $regs);
2942
	$src = array();
2943
	$src_direction_css = array();
2944
	$src_faux_abs = array();
2945
	$d = dirname($css);
2946
	foreach ($regs[1] as $k => $import_css) {
2947
		$css_direction = direction_css("$d/$import_css", $voulue);
2948
		// si la css_direction est dans le meme path que la css d'origine, on tronque le path, elle sera passee en absolue
2949
		if (substr($css_direction, 0, strlen($d) + 1) == "$d/") {
2950
			$css_direction = substr($css_direction, strlen($d) + 1);
2951
		} // si la css_direction commence par $dir_var on la fait passer pour une absolue
2952
		elseif (substr($css_direction, 0, strlen($dir_var)) == $dir_var) {
2953
			$css_direction = substr($css_direction, strlen($dir_var));
2954
			$src_faux_abs["/@@@@@@/" . $css_direction] = $css_direction;
2955
			$css_direction = "/@@@@@@/" . $css_direction;
2956
		}
2957
		$src[] = $regs[0][$k];
2958
		$src_direction_css[] = str_replace($import_css, $css_direction, $regs[0][$k]);
2959
	}
2960
	$contenu = str_replace($src, $src_direction_css, $contenu);
2961
2962
	$contenu = urls_absolues_css($contenu, $css);
2963
2964
	// virer les fausses url absolues que l'on a mis dans les import
2965
	if (count($src_faux_abs)) {
2966
		$contenu = str_replace(array_keys($src_faux_abs), $src_faux_abs, $contenu);
2967
	}
2968
2969
	if (!ecrire_fichier($f, $contenu)) {
2970
		return $css;
2971
	}
2972
2973
	return $f;
2974
}
2975
2976
2977
/**
2978
 * Transforme les urls relatives d'un fichier CSS en absolues
2979
 *
2980
 * Récupère le chemin d'une css existante et crée (ou recrée) dans `_DIR_VAR/cache_css/`
2981
 * une css dont les url relatives sont passées en url absolues
2982
 *
2983
 * Le calcul n'est pas refait si le fichier cache existe déjà et que
2984
 * la source n'a pas été modifiée depuis.
2985
 *
2986
 * @uses recuperer_page() si l'URL source n'est pas sur le même site
2987
 * @uses urls_absolues_css()
2988
 *
2989
 * @param string $css
2990
 *     Chemin ou URL du fichier CSS source
2991
 * @return string
2992
 *     - Chemin du fichier CSS transformé (si source lisible et mise en cache réussie)
2993
 *     - Chemin ou URL du fichier CSS source sinon.
2994
 **/
2995
function url_absolue_css($css) {
2996
	if (!preg_match(',\.css$,i', $css, $r)) {
2997
		return $css;
2998
	}
2999
3000
	$url_absolue_css = url_absolue($css);
3001
3002
	$f = basename($css, '.css');
3003
	$f = sous_repertoire(_DIR_VAR, 'cache-css')
3004
		. preg_replace(",(.*?)(_rtl|_ltr)?$,", "\\1-urlabs-" . substr(md5("$css-urlabs"), 0, 4) . "\\2", $f)
3005
		. '.css';
3006
3007 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...
3008
		return $f;
3009
	}
3010
3011
	if ($url_absolue_css == $css) {
3012
		if (strncmp($GLOBALS['meta']['adresse_site'], $css, $l = strlen($GLOBALS['meta']['adresse_site'])) != 0
3013
			or !lire_fichier(_DIR_RACINE . substr($css, $l), $contenu)
3014
		) {
3015
			include_spip('inc/distant');
3016
			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...
3017
				return $css;
3018
			}
3019
		}
3020
	} elseif (!lire_fichier($css, $contenu)) {
3021
		return $css;
3022
	}
3023
3024
	// passer les url relatives a la css d'origine en url absolues
3025
	$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...
3026
3027
	// ecrire la css
3028
	if (!ecrire_fichier($f, $contenu)) {
3029
		return $css;
3030
	}
3031
3032
	return $f;
3033
}
3034
3035
3036
/**
3037
 * Récupère la valeur d'une clé donnée
3038
 * dans un tableau (ou un objet).
3039
 *
3040
 * @filtre
3041
 * @link http://www.spip.net/4572
3042
 * @example
3043
 *     ```
3044
 *     [(#VALEUR|table_valeur{cle/sous/element})]
3045
 *     ```
3046
 *
3047
 * @param mixed $table
3048
 *     Tableau ou objet PHP
3049
 *     (ou chaîne serialisée de tableau, ce qui permet d'enchaîner le filtre)
3050
 * @param string $cle
3051
 *     Clé du tableau (ou paramètre public de l'objet)
3052
 *     Cette clé peut contenir des caractères / pour sélectionner
3053
 *     des sous éléments dans le tableau, tel que `sous/element/ici`
3054
 *     pour obtenir la valeur de `$tableau['sous']['element']['ici']`
3055
 * @param mixed $defaut
3056
 *     Valeur par defaut retournée si la clé demandée n'existe pas
3057
 * @param bool  $conserver_null
3058
 *     Permet de forcer la fonction à renvoyer la valeur null d'un index
3059
 *     et non pas $defaut comme cela est fait naturellement par la fonction
3060
 *     isset. On utilise alors array_key_exists() à la place de isset().
3061
 * 
3062
 * @return mixed
3063
 *     Valeur trouvée ou valeur par défaut.
3064
 **/
3065
function table_valeur($table, $cle, $defaut = '', $conserver_null = false) {
3066
	foreach (explode('/', $cle) as $k) {
3067
3068
		$table = is_string($table) ? @unserialize($table) : $table;
3069
3070
		if (is_object($table)) {
3071
			$table = (($k !== "") and isset($table->$k)) ? $table->$k : $defaut;
3072
		} elseif (is_array($table)) {
3073
			if ($conserver_null) {
3074
				$table = array_key_exists($k, $table) ? $table[$k] : $defaut;
3075
			} else {
3076
				$table = isset($table[$k]) ? $table[$k] : $defaut;
3077
			}
3078
		} else {
3079
			$table = $defaut;
3080
		}
3081
	}
3082
3083
	return $table;
3084
}
3085
3086
/**
3087
 * Retrouve un motif dans un texte à partir d'une expression régulière
3088
 *
3089
 * S'appuie sur la fonction `preg_match()` en PHP
3090
 *
3091
 * @example
3092
 *    - `[(#TITRE|match{toto})]`
3093
 *    - `[(#TEXTE|match{^ceci$,Uims})]`
3094
 *    - `[(#TEXTE|match{truc(...)$, UimsS, 1})]` Capture de la parenthèse indiquée
3095
 *    - `[(#TEXTE|match{truc(...)$, 1})]` Équivalent, sans indiquer les modificateurs
3096
 *
3097
 * @filtre
3098
 * @link http://www.spip.net/4299
3099
 * @link http://php.net/manual/fr/function.preg-match.php Pour des infos sur `preg_match()`
3100
 *
3101
 * @param string $texte
3102
 *     Texte dans lequel chercher
3103
 * @param string|int $expression
3104
 *     Expression régulière de recherche, sans le délimiteur
3105
 * @param string $modif
3106
 *     - string : Modificateurs de l'expression régulière
3107
 *     - int : Numéro de parenthèse capturante
3108
 * @param int $capte
3109
 *     Numéro de parenthèse capturante
3110
 * @return bool|string
3111
 *     - false : l'expression n'a pas été trouvée
3112
 *     - true : expression trouvée, mais pas la parenthèse capturante
3113
 *     - string : expression trouvée.
3114
 **/
3115
function match($texte, $expression, $modif = "UimsS", $capte = 0) {
3116
	if (intval($modif) and $capte == 0) {
3117
		$capte = $modif;
3118
		$modif = "UimsS";
3119
	}
3120
	$expression = str_replace("\/", "/", $expression);
3121
	$expression = str_replace("/", "\/", $expression);
3122
3123
	if (preg_match('/' . $expression . '/' . $modif, $texte, $r)) {
3124
		if (isset($r[$capte])) {
3125
			return $r[$capte];
3126
		} else {
3127
			return true;
3128
		}
3129
	}
3130
3131
	return false;
3132
}
3133
3134
3135
/**
3136
 * Remplacement de texte à base d'expression régulière
3137
 *
3138
 * @filtre
3139
 * @link http://www.spip.net/4309
3140
 * @see match()
3141
 * @example
3142
 *     ```
3143
 *     [(#TEXTE|replace{^ceci$,cela,UimsS})]
3144
 *     ```
3145
 *
3146
 * @param string $texte
3147
 *     Texte
3148
 * @param string $expression
3149
 *     Expression régulière
3150
 * @param string $replace
3151
 *     Texte de substitution des éléments trouvés
3152
 * @param string $modif
3153
 *     Modificateurs pour l'expression régulière.
3154
 * @return string
3155
 *     Texte
3156
 **/
3157
function replace($texte, $expression, $replace = '', $modif = "UimsS") {
3158
	$expression = str_replace("\/", "/", $expression);
3159
	$expression = str_replace("/", "\/", $expression);
3160
3161
	return preg_replace('/' . $expression . '/' . $modif, $replace, $texte);
3162
}
3163
3164
3165
/**
3166
 * Cherche les documents numerotés dans un texte traite par `propre()`
3167
 *
3168
 * Affecte la liste des doublons['documents']
3169
 *
3170
 * @param array $doublons
3171
 *     Liste des doublons
3172
 * @param string $letexte
3173
 *     Le texte
3174
 * @return string
3175
 *     Le texte
3176
 **/
3177
function traiter_doublons_documents(&$doublons, $letexte) {
3178
3179
	// Verifier dans le texte & les notes (pas beau, helas)
3180
	$t = $letexte . $GLOBALS['les_notes'];
3181
3182
	if (strstr($t, 'spip_document_') // evite le preg_match_all si inutile
3183
		and preg_match_all(
3184
			',<[^>]+\sclass=["\']spip_document_([0-9]+)[\s"\'],imsS',
3185
			$t, $matches, PREG_PATTERN_ORDER)
3186
	) {
3187
		if (!isset($doublons['documents'])) {
3188
			$doublons['documents'] = "";
3189
		}
3190
		$doublons['documents'] .= "," . join(',', $matches[1]);
3191
	}
3192
3193
	return $letexte;
3194
}
3195
3196
/**
3197
 * Filtre vide qui ne renvoie rien
3198
 *
3199
 * @example
3200
 *     `[(#CALCUL|vide)]` n'affichera pas le résultat du calcul
3201
 * @filtre
3202
 *
3203
 * @param mixed $texte
3204
 * @return string Chaîne vide
3205
 **/
3206
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...
3207
	return "";
3208
}
3209
3210
//
3211
// Filtres pour le modele/emb (embed document)
3212
//
3213
3214
/**
3215
 * Écrit des balises HTML `<param...>` à partir d'un tableau de données tel que `#ENV`
3216
 *
3217
 * Permet d'écrire les balises `<param>` à indiquer dans un `<object>`
3218
 * en prenant toutes les valeurs du tableau transmis.
3219
 *
3220
 * Certaines clés spécifiques à SPIP et aux modèles embed sont omises :
3221
 * id, lang, id_document, date, date_redac, align, fond, recurs, emb, dir_racine
3222
 *
3223
 * @example `[(#ENV*|env_to_params)]`
3224
 *
3225
 * @filtre
3226
 * @link http://www.spip.net/4005
3227
 *
3228
 * @param array|string $env
3229
 *      Tableau cle => valeur des paramètres à écrire, ou chaine sérialisée de ce tableau
3230
 * @param array $ignore_params
3231
 *      Permet de compléter les clés ignorées du tableau.
3232
 * @return string
3233
 *      Code HTML résultant
3234
 **/
3235 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...
3236
	$ignore_params = array_merge(
3237
		array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
3238
		$ignore_params
3239
	);
3240
	if (!is_array($env)) {
3241
		$env = unserialize($env);
3242
	}
3243
	$texte = "";
3244
	if ($env) {
3245
		foreach ($env as $i => $j) {
3246
			if (is_string($j) and !in_array($i, $ignore_params)) {
3247
				$texte .= "<param name='" . $i . "'\n\tvalue='" . $j . "' />";
3248
			}
3249
		}
3250
	}
3251
3252
	return $texte;
3253
}
3254
3255
/**
3256
 * Écrit des attributs HTML à partir d'un tableau de données tel que `#ENV`
3257
 *
3258
 * Permet d'écrire des attributs d'une balise HTML en utilisant les données du tableau transmis.
3259
 * Chaque clé deviendra le nom de l'attribut (et la valeur, sa valeur)
3260
 *
3261
 * Certaines clés spécifiques à SPIP et aux modèles embed sont omises :
3262
 * id, lang, id_document, date, date_redac, align, fond, recurs, emb, dir_racine
3263
 *
3264
 * @example `<embed src='#URL_DOCUMENT' [(#ENV*|env_to_attributs)] width='#GET{largeur}' height='#GET{hauteur}'></embed>`
3265
 * @filtre
3266
 *
3267
 * @param array|string $env
3268
 *      Tableau cle => valeur des attributs à écrire, ou chaine sérialisée de ce tableau
3269
 * @param array $ignore_params
3270
 *      Permet de compléter les clés ignorées du tableau.
3271
 * @return string
3272
 *      Code HTML résultant
3273
 **/
3274 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...
3275
	$ignore_params = array_merge(
3276
		array('id', 'lang', 'id_document', 'date', 'date_redac', 'align', 'fond', '', 'recurs', 'emb', 'dir_racine'),
3277
		$ignore_params
3278
	);
3279
	if (!is_array($env)) {
3280
		$env = unserialize($env);
3281
	}
3282
	$texte = "";
3283
	if ($env) {
3284
		foreach ($env as $i => $j) {
3285
			if (is_string($j) and !in_array($i, $ignore_params)) {
3286
				$texte .= $i . "='" . $j . "' ";
3287
			}
3288
		}
3289
	}
3290
3291
	return $texte;
3292
}
3293
3294
3295
/**
3296
 * Concatène des chaînes
3297
 *
3298
 * @filtre
3299
 * @link http://www.spip.net/4150
3300
 * @example
3301
 *     ```
3302
 *     #TEXTE|concat{texte1,texte2,...}
3303
 *     ```
3304
 *
3305
 * @return string Chaînes concaténés
3306
 **/
3307
function concat() {
3308
	$args = func_get_args();
3309
3310
	return join('', $args);
3311
}
3312
3313
3314
/**
3315
 * Retourne le contenu d'un ou plusieurs fichiers
3316
 *
3317
 * Les chemins sont cherchés dans le path de SPIP
3318
 *
3319
 * @see balise_INCLURE_dist() La balise `#INCLURE` peut appeler cette fonction
3320
 *
3321
 * @param array|string $files
3322
 *     - array : Liste de fichiers
3323
 *     - string : fichier ou fichiers séparés par `|`
3324
 * @param bool $script
3325
 *     - si true, considère que c'est un fichier js à chercher `javascript/`
3326
 * @return string
3327
 *     Contenu du ou des fichiers, concaténé
3328
 **/
3329
function charge_scripts($files, $script = true) {
3330
	$flux = "";
3331
	foreach (is_array($files) ? $files : explode("|", $files) as $file) {
3332
		if (!is_string($file)) {
3333
			continue;
3334
		}
3335
		if ($script) {
3336
			$file = preg_match(",^\w+$,", $file) ? "javascript/$file.js" : '';
3337
		}
3338
		if ($file) {
3339
			$path = find_in_path($file);
3340
			if ($path) {
3341
				$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 3339 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...
3342
			}
3343
		}
3344
	}
3345
3346
	return $flux;
3347
}
3348
3349
3350
/**
3351
 * Produit une balise img avec un champ alt d'office si vide
3352
 *
3353
 * Attention le htmlentities et la traduction doivent être appliqués avant.
3354
 *
3355
 * @param string $img
3356
 * @param string $alt
3357
 * @param string $atts
3358
 * @param string $title
3359
 * @param array $options
3360
 *   chemin_image : utiliser chemin_image sur $img fourni, ou non (oui par dafaut)
3361
 *   utiliser_suffixe_size : utiliser ou non le suffixe de taille dans le nom de fichier de l'image
3362
 *   sous forme -xx.png (pour les icones essentiellement) (oui par defaut)
3363
 * @return string
3364
 */
3365
function http_img_pack($img, $alt, $atts = '', $title = '', $options = array()) {
3366
	if (!isset($options['chemin_image']) or $options['chemin_image'] == true) {
3367
		$img = chemin_image($img);
3368
	}
3369
	if (stripos($atts, 'width') === false) {
3370
		// utiliser directement l'info de taille presente dans le nom
3371
		if ((!isset($options['utiliser_suffixe_size']) or $options['utiliser_suffixe_size'] == true)
3372
			and preg_match(',-([0-9]+)[.](png|gif)$,', $img, $regs)
3373
		) {
3374
			$largeur = $hauteur = intval($regs[1]);
3375
		} else {
3376
			$taille = taille_image($img);
3377
			list($hauteur, $largeur) = $taille;
3378
			if (!$hauteur or !$largeur) {
3379
				return "";
3380
			}
3381
		}
3382
		$atts .= " width='" . $largeur . "' height='" . $hauteur . "'";
3383
	}
3384
3385
	return "<img src='$img' alt='" . attribut_html($alt ? $alt : $title) . "'"
3386
	. ($title ? ' title="' . attribut_html($title) . '"' : '')
3387
	. " " . ltrim($atts)
3388
	. " />";
3389
}
3390
3391
/**
3392
 * Générer une directive `style='background:url()'` à partir d'un fichier image
3393
 *
3394
 * @param string $img
3395
 * @param string $att
3396
 * @return string
3397
 */
3398
function http_style_background($img, $att = '') {
3399
	return " style='background" . ($att ? "" : "-image") . ": url(\"" . chemin_image($img) . "\")" . ($att ? (' ' . $att) : '') . ";'";
3400
}
3401
3402
/**
3403
 * Générer une balise HTML `img` à partir d'un nom de fichier
3404
 *
3405
 * @uses http_img_pack()
3406
 *
3407
 * @param string $img
3408
 * @param string $alt
3409
 * @param string $class
3410
 * @return string
3411
 *     Code HTML de la balise IMG
3412
 */
3413
function filtre_balise_img_dist($img, $alt = "", $class = "") {
3414
	return http_img_pack($img, $alt, $class ? " class='" . attribut_html($class) . "'" : '', '',
3415
		array('chemin_image' => false, 'utiliser_suffixe_size' => false));
3416
}
3417
3418
3419
/**
3420
 * Affiche chaque valeur d'un tableau associatif en utilisant un modèle
3421
 *
3422
 * @example
3423
 *     - `[(#ENV*|unserialize|foreach)]`
3424
 *     - `[(#ARRAY{a,un,b,deux}|foreach)]`
3425
 *
3426
 * @filtre
3427
 * @link http://www.spip.net/4248
3428
 *
3429
 * @param array $tableau
3430
 *     Tableau de données à afficher
3431
 * @param string $modele
3432
 *     Nom du modèle à utiliser
3433
 * @return string
3434
 *     Code HTML résultant
3435
 **/
3436
function filtre_foreach_dist($tableau, $modele = 'foreach') {
3437
	$texte = '';
3438
	if (is_array($tableau)) {
3439
		foreach ($tableau as $k => $v) {
3440
			$res = recuperer_fond('modeles/' . $modele,
3441
				array_merge(array('cle' => $k), (is_array($v) ? $v : array('valeur' => $v)))
3442
			);
3443
			$texte .= $res;
3444
		}
3445
	}
3446
3447
	return $texte;
3448
}
3449
3450
3451
/**
3452
 * Obtient des informations sur les plugins actifs
3453
 *
3454
 * @filtre
3455
 * @uses liste_plugin_actifs() pour connaître les informations affichables
3456
 *
3457
 * @param string $plugin
3458
 *     Préfixe du plugin ou chaîne vide
3459
 * @param string $type_info
3460
 *     Type d'info demandée
3461
 * @param bool $reload
3462
 *     true (à éviter) pour forcer le recalcul du cache des informations des plugins.
3463
 * @return array|string|bool
3464
 *
3465
 *     - Liste sérialisée des préfixes de plugins actifs (si $plugin = '')
3466
 *     - Suivant $type_info, avec $plugin un préfixe
3467
 *         - est_actif : renvoie true s'il est actif, false sinon
3468
 *         - x : retourne l'information x du plugin si présente (et plugin actif)
3469
 *         - tout : retourne toutes les informations du plugin actif
3470
 **/
3471
function filtre_info_plugin_dist($plugin, $type_info, $reload = false) {
3472
	include_spip('inc/plugin');
3473
	$plugin = strtoupper($plugin);
3474
	$plugins_actifs = liste_plugin_actifs();
3475
3476
	if (!$plugin) {
3477
		return serialize(array_keys($plugins_actifs));
3478
	} elseif (empty($plugins_actifs[$plugin]) and !$reload) {
3479
		return '';
3480
	} elseif (($type_info == 'est_actif') and !$reload) {
3481
		return $plugins_actifs[$plugin] ? 1 : 0;
3482
	} elseif (isset($plugins_actifs[$plugin][$type_info]) and !$reload) {
3483
		return $plugins_actifs[$plugin][$type_info];
3484
	} else {
3485
		$get_infos = charger_fonction('get_infos', 'plugins');
3486
		// On prend en compte les extensions
3487
		if (!is_dir($plugins_actifs[$plugin]['dir_type'])) {
3488
			$dir_plugins = constant($plugins_actifs[$plugin]['dir_type']);
3489
		} else {
3490
			$dir_plugins = $plugins_actifs[$plugin]['dir_type'];
3491
		}
3492
		if (!$infos = $get_infos($plugins_actifs[$plugin]['dir'], $reload, $dir_plugins)) {
3493
			return '';
3494
		}
3495
		if ($type_info == 'tout') {
3496
			return $infos;
3497
		} elseif ($type_info == 'est_actif') {
3498
			return $infos ? 1 : 0;
3499
		} else {
3500
			return strval($infos[$type_info]);
3501
		}
3502
	}
3503
}
3504
3505
3506
/**
3507
 * Affiche la puce statut d'un objet, avec un menu rapide pour changer
3508
 * de statut si possibilité de l'avoir
3509
 *
3510
 * @see inc_puce_statut_dist()
3511
 *
3512
 * @filtre
3513
 *
3514
 * @param int $id_objet
3515
 *     Identifiant de l'objet
3516
 * @param string $statut
3517
 *     Statut actuel de l'objet
3518
 * @param int $id_rubrique
3519
 *     Identifiant du parent
3520
 * @param string $type
3521
 *     Type d'objet
3522
 * @param bool $ajax
3523
 *     Indique s'il ne faut renvoyer que le coeur du menu car on est
3524
 *     dans une requete ajax suite à un post de changement rapide
3525
 * @return string
3526
 *     Code HTML de l'image de puce de statut à insérer (et du menu de changement si présent)
3527
 */
3528
function puce_changement_statut($id_objet, $statut, $id_rubrique, $type, $ajax = false) {
3529
	$puce_statut = charger_fonction('puce_statut', 'inc');
3530
3531
	return $puce_statut($id_objet, $statut, $id_rubrique, $type, $ajax);
3532
}
3533
3534
3535
/**
3536
 * Affiche la puce statut d'un objet, avec un menu rapide pour changer
3537
 * de statut si possibilité de l'avoir
3538
 *
3539
 * Utilisable sur tout objet qui a declaré ses statuts
3540
 *
3541
 * @example
3542
 *     [(#STATUT|puce_statut{article})] affiche une puce passive
3543
 *     [(#STATUT|puce_statut{article,#ID_ARTICLE,#ID_RUBRIQUE})] affiche une puce avec changement rapide
3544
 *
3545
 * @see inc_puce_statut_dist()
3546
 *
3547
 * @filtre
3548
 *
3549
 * @param string $statut
3550
 *     Statut actuel de l'objet
3551
 * @param string $objet
3552
 *     Type d'objet
3553
 * @param int $id_objet
3554
 *     Identifiant de l'objet
3555
 * @param int $id_parent
3556
 *     Identifiant du parent
3557
 * @return string
3558
 *     Code HTML de l'image de puce de statut à insérer (et du menu de changement si présent)
3559
 */
3560
function filtre_puce_statut_dist($statut, $objet, $id_objet = 0, $id_parent = 0) {
3561
	static $puce_statut = null;
3562
	if (!$puce_statut) {
3563
		$puce_statut = charger_fonction('puce_statut', 'inc');
3564
	}
3565
3566
	return $puce_statut($id_objet, $statut, $id_parent, $objet, false,
3567
		objet_info($objet, 'editable') ? _ACTIVER_PUCE_RAPIDE : false);
3568
}
3569
3570
3571
/**
3572
 * Encoder un contexte pour l'ajax
3573
 *
3574
 * Encoder le contexte, le signer avec une clé, le crypter
3575
 * avec le secret du site, le gziper si possible.
3576
 *
3577
 * L'entrée peut-être sérialisée (le `#ENV**` des fonds ajax et ajax_stat)
3578
 *
3579
 * @see  decoder_contexte_ajax()
3580
 * @uses calculer_cle_action()
3581
 *
3582
 * @param string|array $c
3583
 *   contexte, peut etre un tableau serialize
3584
 * @param string $form
3585
 *   nom du formulaire eventuel
3586
 * @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...
3587
 *   contenu a emboiter dans le conteneur ajax
3588
 * @param string $ajaxid
3589
 *   ajaxid pour cibler le bloc et forcer sa mise a jour
3590
 * @return string
3591
 *   hash du contexte
3592
 */
3593
function encoder_contexte_ajax($c, $form = '', $emboite = null, $ajaxid = '') {
3594
	if (is_string($c)
3595
		and @unserialize($c) !== false
3596
	) {
3597
		$c = unserialize($c);
3598
	}
3599
3600
	// supprimer les parametres debut_x
3601
	// pour que la pagination ajax ne soit pas plantee
3602
	// si on charge la page &debut_x=1 : car alors en cliquant sur l'item 0,
3603
	// le debut_x=0 n'existe pas, et on resterait sur 1
3604
	if (is_array($c)) {
3605
		foreach ($c as $k => $v) {
3606
			if (strpos($k, 'debut_') === 0) {
3607
				unset($c[$k]);
3608
			}
3609
		}
3610
	}
3611
3612
	if (!function_exists('calculer_cle_action')) {
3613
		include_spip("inc/securiser_action");
3614
	}
3615
3616
	$c = serialize($c);
3617
	$cle = calculer_cle_action($form . $c);
3618
	$c = "$cle:$c";
3619
3620
	// on ne stocke pas les contextes dans des fichiers caches
3621
	// par defaut, sauf si cette configuration a ete forcee
3622
	// OU que la longueur de l''argument generee est plus long
3623
	// que ce que telere Suhosin.
3624
	$cache_contextes_ajax = (defined('_CACHE_CONTEXTES_AJAX') and _CACHE_CONTEXTES_AJAX);
3625
	if (!$cache_contextes_ajax) {
3626
		$env = $c;
3627
		if (function_exists('gzdeflate') && function_exists('gzinflate')) {
3628
			$env = gzdeflate($env);
3629
			// http://core.spip.net/issues/2667 | https://bugs.php.net/bug.php?id=61287
3630
			if ((PHP_VERSION_ID == 50400) and !@gzinflate($env)) {
3631
				$cache_contextes_ajax = true;
3632
				spip_log("Contextes AJAX forces en fichiers ! Erreur PHP 5.4.0", _LOG_AVERTISSEMENT);
3633
			}
3634
		}
3635
		$env = _xor($env);
3636
		$env = base64_encode($env);
3637
		// tester Suhosin et la valeur maximale des variables en GET...
3638
		if ($max_len = @ini_get('suhosin.get.max_value_length')
3639
			and $max_len < ($len = strlen($env))
3640
		) {
3641
			$cache_contextes_ajax = true;
3642
			spip_log("Contextes AJAX forces en fichiers !"
3643
				. " Cela arrive lorsque la valeur du contexte"
3644
				. " depasse la longueur maximale autorisee par Suhosin"
3645
				. " ($max_len) dans 'suhosin.get.max_value_length'. Ici : $len."
3646
				. " Vous devriez modifier les parametres de Suhosin"
3647
				. " pour accepter au moins 1024 caracteres.", _LOG_AVERTISSEMENT);
3648
		}
3649
	}
3650
3651
	if ($cache_contextes_ajax) {
3652
		$dir = sous_repertoire(_DIR_CACHE, 'contextes');
3653
		// stocker les contextes sur disque et ne passer qu'un hash dans l'url
3654
		$md5 = md5($c);
3655
		ecrire_fichier("$dir/c$md5", $c);
3656
		$env = $md5;
3657
	}
3658
3659
	if ($emboite === null) {
3660
		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...
3661
	}
3662
	if (!trim($emboite)) {
3663
		return "";
3664
	}
3665
	// toujours encoder l'url source dans le bloc ajax
3666
	$r = self();
3667
	$r = ' data-origin="' . $r . '"';
3668
	$class = 'ajaxbloc';
3669
	if ($ajaxid and is_string($ajaxid)) {
3670
		// ajaxid est normalement conforme a un nom de classe css
3671
		// on ne verifie pas la conformite, mais on passe entites_html par dessus par precaution
3672
		$class .= ' ajax-id-' . entites_html($ajaxid);
3673
	}
3674
3675
	return "<div class='$class' " . "data-ajax-env='$env'$r>\n$emboite</div><!--ajaxbloc-->\n";
3676
}
3677
3678
/**
3679
 * Décoder un hash de contexte pour l'ajax
3680
 *
3681
 * Précude inverse de `encoder_contexte_ajax()`
3682
 *
3683
 * @see  encoder_contexte_ajax()
3684
 * @uses calculer_cle_action()
3685
 *
3686
 * @param string $c
3687
 *   hash du contexte
3688
 * @param string $form
3689
 *   nom du formulaire eventuel
3690
 * @return array|string|bool
3691
 *   - array|string : contexte d'environnement, possiblement sérialisé
3692
 *   - false : erreur de décodage
3693
 */
3694
function decoder_contexte_ajax($c, $form = '') {
3695
	if (!function_exists('calculer_cle_action')) {
3696
		include_spip("inc/securiser_action");
3697
	}
3698
	if (((defined('_CACHE_CONTEXTES_AJAX') and _CACHE_CONTEXTES_AJAX) or strlen($c) == 32)
3699
		and $dir = sous_repertoire(_DIR_CACHE, 'contextes')
3700
		and lire_fichier("$dir/c$c", $contexte)
3701
	) {
3702
		$c = $contexte;
3703
	} else {
3704
		$c = @base64_decode($c);
3705
		$c = _xor($c);
3706
		if (function_exists('gzdeflate') && function_exists('gzinflate')) {
3707
			$c = @gzinflate($c);
3708
		}
3709
	}
3710
3711
	// extraire la signature en debut de contexte
3712
	// et la verifier avant de deserializer
3713
	// format : signature:donneesserializees
3714 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...
3715
		$cle = substr($c,0,$p);
3716
		$c = substr($c,$p+1);
3717
3718
		if ($cle == calculer_cle_action($form . $c)) {
3719
			$env = @unserialize($c);
3720
			return $env;
3721
		}
3722
	}
3723
3724
	return false;
3725
}
3726
3727
3728
/**
3729
 * Encrypte ou décrypte un message
3730
 *
3731
 * @link http://www.php.net/manual/fr/language.operators.bitwise.php#81358
3732
 *
3733
 * @param string $message
3734
 *    Message à encrypter ou décrypter
3735
 * @param null|string $key
3736
 *    Clé de cryptage / décryptage.
3737
 *    Une clé sera calculée si non transmise
3738
 * @return string
3739
 *    Message décrypté ou encrypté
3740
 **/
3741
function _xor($message, $key = null) {
3742
	if (is_null($key)) {
3743
		if (!function_exists('calculer_cle_action')) {
3744
			include_spip("inc/securiser_action");
3745
		}
3746
		$key = pack("H*", calculer_cle_action('_xor'));
3747
	}
3748
3749
	$keylen = strlen($key);
3750
	$messagelen = strlen($message);
3751
	for ($i = 0; $i < $messagelen; $i++) {
3752
		$message[$i] = ~($message[$i] ^ $key[$i % $keylen]);
3753
	}
3754
3755
	return $message;
3756
}
3757
3758
/**
3759
 * Retourne une URL de réponse de forum (aucune action ici)
3760
 *
3761
 * @see filtre_url_reponse_forum() du plugin forum (prioritaire)
3762
 * @note
3763
 *   La vraie fonction est dans le plugin forum,
3764
 *   mais on évite ici une erreur du compilateur en absence du plugin
3765
 * @param string $texte
3766
 * @return string
3767
 */
3768
function url_reponse_forum($texte) { return $texte; }
3769
3770
/**
3771
 * retourne une URL de suivi rss d'un forum (aucune action ici)
3772
 *
3773
 * @see filtre_url_rss_forum() du plugin forum (prioritaire)
3774
 * @note
3775
 *   La vraie fonction est dans le plugin forum,
3776
 *   mais on évite ici une erreur du compilateur en absence du plugin
3777
 * @param string $texte
3778
 * @return string
3779
 */
3780
function url_rss_forum($texte) { return $texte; }
3781
3782
3783
/**
3784
 * Génère des menus avec liens ou `<strong class='on'>` non clicable lorsque
3785
 * l'item est sélectionné
3786
 *
3787
 * @filtre
3788
 * @link http://www.spip.net/4004
3789
 * @example
3790
 *   ```
3791
 *   [(#URL_RUBRIQUE|lien_ou_expose{#TITRE, #ENV{test}|=={en_cours}})]
3792
 *   ```
3793
 *
3794
 * @param string $url
3795
 *   URL du lien
3796
 * @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...
3797
 *   Texte du lien
3798
 * @param bool $on
3799
 *   État exposé (génère un strong) ou non (génère un lien)
3800
 * @param string $class
3801
 *   Classes CSS ajoutées au lien
3802
 * @param string $title
3803
 *   Title ajouté au lien
3804
 * @param string $rel
3805
 *   Attribut `rel` ajouté au lien
3806
 * @param string $evt
3807
 *   Complement à la balise `a` pour gérer un événement javascript,
3808
 *   de la forme ` onclick='...'`
3809
 * @return string
3810
 *   Code HTML
3811
 */
3812
function lien_ou_expose($url, $libelle = null, $on = false, $class = "", $title = "", $rel = "", $evt = '') {
3813
	if ($on) {
3814
		$bal = "strong";
3815
		$att = "class='on'";
3816
	} else {
3817
		$bal = 'a';
3818
		$att = "href='$url'"
3819
			. ($title ? " title='" . attribut_html($title) . "'" : '')
3820
			. ($class ? " class='" . attribut_html($class) . "'" : '')
3821
			. ($rel ? " rel='" . attribut_html($rel) . "'" : '')
3822
			. $evt;
3823
	}
3824
	if ($libelle === null) {
3825
		$libelle = $url;
3826
	}
3827
3828
	return "<$bal $att>$libelle</$bal>";
3829
}
3830
3831
3832
/**
3833
 * Afficher un message "un truc"/"N trucs"
3834
 * Les items sont à indiquer comme pour la fonction _T() sous la forme :
3835
 * "module:chaine"
3836
 *
3837
 * @param int $nb : le nombre
3838
 * @param string $chaine_un : l'item de langue si $nb vaut un
3839
 * @param string $chaine_plusieurs : l'item de lanque si $nb > 1
3840
 * @param string $var : La variable à remplacer par $nb dans l'item de langue (facultatif, défaut "nb")
3841
 * @param array $vars : Les autres variables nécessaires aux chaines de langues (facultatif)
3842
 * @return string : la chaine de langue finale en utilisant la fonction _T()
3843
 */
3844
function singulier_ou_pluriel($nb, $chaine_un, $chaine_plusieurs, $var = 'nb', $vars = array()) {
3845
	if (!$nb = intval($nb)) {
3846
		return "";
3847
	}
3848
	if (!is_array($vars)) {
3849
		return "";
3850
	}
3851
	$vars[$var] = $nb;
3852
	if ($nb > 1) {
3853
		return _T($chaine_plusieurs, $vars);
3854
	} else {
3855
		return _T($chaine_un, $vars);
3856
	}
3857
}
3858
3859
3860
/**
3861
 * Fonction de base pour une icone dans un squelette
3862
 * structure html : `<span><a><img><b>texte</b></span>`
3863
 *
3864
 * @param string $type
3865
 *  'lien' ou 'bouton'
3866
 * @param string $lien
3867
 *  url
3868
 * @param string $texte
3869
 *  texte du lien / alt de l'image
3870
 * @param string $fond
3871
 *  objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
3872
 * @param string $fonction
3873
 *  new/del/edit
3874
 * @param string $class
3875
 *  classe supplementaire (horizontale, verticale, ajax ...)
3876
 * @param string $javascript
3877
 *  "onclick='...'" par exemple
3878
 * @return string
3879
 */
3880
function prepare_icone_base($type, $lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
3881
	if (in_array($fonction, array("del", "supprimer.gif"))) {
3882
		$class .= ' danger';
3883
	} elseif ($fonction == "rien.gif") {
3884
		$fonction = "";
3885
	} elseif ($fonction == "delsafe") {
3886
		$fonction = "del";
3887
	}
3888
3889
	// remappage des icone : article-24.png+new => article-new-24.png
3890 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...
3891
		list($fond, $fonction) = $icone_renommer($fond, $fonction);
3892
	}
3893
3894
	// ajouter le type d'objet dans la class de l'icone
3895
	$class .= " " . substr(basename($fond), 0, -4);
3896
3897
	$alt = attribut_html($texte);
3898
	$title = " title=\"$alt\""; // est-ce pertinent de doubler le alt par un title ?
3899
3900
	$ajax = "";
3901
	if (strpos($class, "ajax") !== false) {
3902
		$ajax = "ajax";
3903
		if (strpos($class, "preload") !== false) {
3904
			$ajax .= " preload";
3905
		}
3906
		if (strpos($class, "nocache") !== false) {
3907
			$ajax .= " nocache";
3908
		}
3909
		$ajax = " class='$ajax'";
3910
	}
3911
3912
	$size = 24;
3913
	if (preg_match("/-([0-9]{1,3})[.](gif|png)$/i", $fond, $match)) {
3914
		$size = $match[1];
3915
	}
3916
3917
	if ($fonction) {
3918
		// 2 images pour composer l'icone : le fond (article) en background,
3919
		// la fonction (new) en image
3920
		$icone = http_img_pack($fonction, $alt, "width='$size' height='$size'\n" .
3921
			http_style_background($fond));
3922
	} else {
3923
		$icone = http_img_pack($fond, $alt, "width='$size' height='$size'");
3924
	}
3925
3926
	if ($type == 'lien') {
3927
		return "<span class='icone s$size $class'>"
3928
		. "<a href='$lien'$title$ajax$javascript>"
3929
		. $icone
3930
		. "<b>$texte</b>"
3931
		. "</a></span>\n";
3932
	} else {
3933
		return bouton_action("$icone<b>$texte</b>", $lien, "icone s$size $class", $javascript, $alt);
3934
	}
3935
}
3936
3937
/**
3938
 * Crée un lien ayant une icone
3939
 *
3940
 * @uses prepare_icone_base()
3941
 *
3942
 * @param string $lien
3943
 *     URL du lien
3944
 * @param string $texte
3945
 *     Texte du lien
3946
 * @param string $fond
3947
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
3948
 * @param string $fonction
3949
 *     Fonction du lien (`edit`, `new`, `del`)
3950
 * @param string $class
3951
 *     Classe CSS, tel que `left`, `right` pour définir un alignement
3952
 * @param string $javascript
3953
 *     Javascript ajouté sur le lien
3954
 * @return string
3955
 *     Code HTML du lien
3956
 **/
3957
function icone_base($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
3958
	return prepare_icone_base('lien', $lien, $texte, $fond, $fonction, $class, $javascript);
3959
}
3960
3961
/**
3962
 * Crée un lien précédé d'une icone au dessus du texte
3963
 *
3964
 * @uses icone_base()
3965
 * @see  icone_verticale() Pour un usage dans un code PHP.
3966
 *
3967
 * @filtre
3968
 * @example
3969
 *     ```
3970
 *     [(#AUTORISER{voir,groupemots,#ID_GROUPE})
3971
 *         [(#URL_ECRIRE{groupe_mots,id_groupe=#ID_GROUPE}
3972
 *            |icone_verticale{<:mots:icone_voir_groupe_mots:>,groupe_mots-24.png,'',left})]
3973
 *    ]
3974
 *     ```
3975
 *
3976
 * @param string $lien
3977
 *     URL du lien
3978
 * @param string $texte
3979
 *     Texte du lien
3980
 * @param string $fond
3981
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
3982
 * @param string $fonction
3983
 *     Fonction du lien (`edit`, `new`, `del`)
3984
 * @param string $class
3985
 *     Classe CSS à ajouter, tel que `left`, `right`, `center` pour définir un alignement.
3986
 *     Il peut y en avoir plusieurs : `left ajax`
3987
 * @param string $javascript
3988
 *     Javascript ajouté sur le lien
3989
 * @return string
3990
 *     Code HTML du lien
3991
 **/
3992
function filtre_icone_verticale_dist($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
3993
	return icone_base($lien, $texte, $fond, $fonction, "verticale $class", $javascript);
3994
}
3995
3996
/**
3997
 * Crée un lien précédé d'une icone horizontale
3998
 *
3999
 * @uses icone_base()
4000
 * @see  icone_horizontale() Pour un usage dans un code PHP.
4001
 *
4002
 * @filtre
4003
 * @example
4004
 *     En tant que filtre dans un squelettes :
4005
 *     ```
4006
 *     [(#URL_ECRIRE{sites}|icone_horizontale{<:sites:icone_voir_sites_references:>,site-24.png})]
4007
 *
4008
 *     [(#AUTORISER{supprimer,groupemots,#ID_GROUPE}|oui)
4009
 *         [(#URL_ACTION_AUTEUR{supprimer_groupe_mots,#ID_GROUPE,#URL_ECRIRE{mots}}
4010
 *             |icone_horizontale{<:mots:icone_supprimer_groupe_mots:>,groupe_mots,del})]
4011
 *     ]
4012
 *     ```
4013
 *
4014
 *     En tant que filtre dans un code php :
4015
 *     ```
4016
 *     $icone_horizontale=chercher_filtre('icone_horizontale');
4017
 *     $icone = $icone_horizontale(generer_url_ecrire("stats_visites","id_article=$id_article"),
4018
 *         _T('statistiques:icone_evolution_visites', array('visites' => $visites)),
4019
 *         "statistique-24.png");
4020
 *     ```
4021
 *
4022
 * @param string $lien
4023
 *     URL du lien
4024
 * @param string $texte
4025
 *     Texte du lien
4026
 * @param string $fond
4027
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4028
 * @param string $fonction
4029
 *     Fonction du lien (`edit`, `new`, `del`)
4030
 * @param string $class
4031
 *     Classe CSS à ajouter
4032
 * @param string $javascript
4033
 *     Javascript ajouté sur le lien
4034
 * @return string
4035
 *     Code HTML du lien
4036
 **/
4037
function filtre_icone_horizontale_dist($lien, $texte, $fond, $fonction = "", $class = "", $javascript = "") {
4038
	return icone_base($lien, $texte, $fond, $fonction, "horizontale $class", $javascript);
4039
}
4040
4041
/**
4042
 * Crée un bouton d'action intégrant une icone horizontale
4043
 *
4044
 * @uses prepare_icone_base()
4045
 *
4046
 * @filtre
4047
 * @example
4048
 *     ```
4049
 *     [(#URL_ACTION_AUTEUR{supprimer_mot, #ID_MOT, #URL_ECRIRE{groupe_mots,id_groupe=#ID_GROUPE}}
4050
 *         |bouton_action_horizontal{<:mots:info_supprimer_mot:>,mot-24.png,del})]
4051
 *     ```
4052
 *
4053
 * @param string $lien
4054
 *     URL de l'action
4055
 * @param string $texte
4056
 *     Texte du bouton
4057
 * @param string $fond
4058
 *     Objet avec ou sans son extension et sa taille (article, article-24, article-24.png)
4059
 * @param string $fonction
4060
 *     Fonction du bouton (`edit`, `new`, `del`)
4061
 * @param string $class
4062
 *     Classe CSS à ajouter
4063
 * @param string $confirm
4064
 *     Message de confirmation à ajouter en javascript sur le bouton
4065
 * @return string
4066
 *     Code HTML du lien
4067
 **/
4068
function filtre_bouton_action_horizontal_dist($lien, $texte, $fond, $fonction = "", $class = "", $confirm = "") {
4069
	return prepare_icone_base('bouton', $lien, $texte, $fond, $fonction, "horizontale $class", $confirm);
4070
}
4071
4072
/**
4073
 * Filtre `icone` pour compatibilité mappé sur `icone_base`
4074
 *
4075
 * @uses icone_base()
4076
 * @see  filtre_icone_verticale_dist()
4077
 *
4078
 * @filtre
4079
 * @deprecated Utiliser le filtre `icone_verticale`
4080
 *
4081
 * @param string $lien
4082
 *     URL du lien
4083
 * @param string $texte
4084
 *     Texte du lien
4085
 * @param string $fond
4086
 *     Nom de l'image utilisée
4087
 * @param string $align
4088
 *     Classe CSS d'alignement (`left`, `right`, `center`)
4089
 * @param string $fonction
4090
 *     Fonction du lien (`edit`, `new`, `del`)
4091
 * @param string $class
4092
 *     Classe CSS à ajouter
4093
 * @param string $javascript
4094
 *     Javascript ajouté sur le lien
4095
 * @return string
4096
 *     Code HTML du lien
4097
 */
4098
function filtre_icone_dist($lien, $texte, $fond, $align = "", $fonction = "", $class = "", $javascript = "") {
4099
	return icone_base($lien, $texte, $fond, $fonction, "verticale $align $class", $javascript);
4100
}
4101
4102
4103
/**
4104
 * Explose un texte en tableau suivant un séparateur
4105
 *
4106
 * @note
4107
 *     Inverse l'écriture de la fonction PHP de même nom
4108
 *     pour que le filtre soit plus pratique dans les squelettes
4109
 *
4110
 * @filtre
4111
 * @example
4112
 *     ```
4113
 *     [(#GET{truc}|explode{-})]
4114
 *     ```
4115
 *
4116
 * @param string $a Texte
4117
 * @param string $b Séparateur
4118
 * @return array Liste des éléments
4119
 */
4120
function filtre_explode_dist($a, $b) { return explode($b, $a); }
4121
4122
/**
4123
 * Implose un tableau en chaine en liant avec un séparateur
4124
 *
4125
 * @note
4126
 *     Inverse l'écriture de la fonction PHP de même nom
4127
 *     pour que le filtre soit plus pratique dans les squelettes
4128
 *
4129
 * @filtre
4130
 * @example
4131
 *     ```
4132
 *     [(#GET{truc}|implode{-})]
4133
 *     ```
4134
 *
4135
 * @param array $a Tableau
4136
 * @param string $b Séparateur
4137
 * @return string Texte
4138
 */
4139
function filtre_implode_dist($a, $b) { return is_array($a) ? implode($b, $a) : $a; }
4140
4141
/**
4142
 * Produire les styles privés qui associent item de menu avec icone en background
4143
 *
4144
 * @return string Code CSS
4145
 */
4146
function bando_images_background() {
4147
	include_spip('inc/bandeau');
4148
	// recuperer tous les boutons et leurs images
4149
	$boutons = definir_barre_boutons(definir_barre_contexte(), true, false);
4150
4151
	$res = "";
4152
	foreach ($boutons as $page => $detail) {
4153
		if ($detail->icone and strlen(trim($detail->icone))) {
4154
			$res .= "\n.navigation_avec_icones #bando1_$page {background-image:url(" . $detail->icone . ");}";
4155
		}
4156
		$selecteur = (in_array($page, array('outils_rapides', 'outils_collaboratifs')) ? "" : ".navigation_avec_icones ");
4157
		if (is_array($detail->sousmenu)) {
4158
			foreach ($detail->sousmenu as $souspage => $sousdetail) {
4159
				if ($sousdetail->icone and strlen(trim($sousdetail->icone))) {
4160
					$res .= "\n$selecteur.bando2_$souspage {background-image:url(" . $sousdetail->icone . ");}";
4161
				}
4162
			}
4163
		}
4164
	}
4165
4166
	return $res;
4167
}
4168
4169
/**
4170
 * Generer un bouton_action
4171
 * utilise par #BOUTON_ACTION
4172
 *
4173
 * @param string $libelle
4174
 * @param string $url
4175
 * @param string $class
4176
 * @param string $confirm
4177
 *   message de confirmation oui/non avant l'action
4178
 * @param string $title
4179
 * @param string $callback
4180
 *   callback js a appeler lors de l'evenement action (apres confirmation eventuelle si $confirm est non vide)
4181
 *   et avant execution de l'action. Si la callback renvoie false, elle annule le declenchement de l'action
4182
 * @return string
4183
 */
4184
function bouton_action($libelle, $url, $class = "", $confirm = "", $title = "", $callback = "") {
4185
	if ($confirm) {
4186
		$confirm = "confirm(\"" . attribut_html($confirm) . "\")";
4187
		if ($callback) {
4188
			$callback = "$confirm?($callback):false";
4189
		} else {
4190
			$callback = $confirm;
4191
		}
4192
	}
4193
	$onclick = $callback ? " onclick='return " . addcslashes($callback, "'") . "'" : "";
4194
	$title = $title ? " title='$title'" : "";
4195
4196
	return "<form class='bouton_action_post $class' method='post' action='$url'><div>" . form_hidden($url)
4197
	. "<button type='submit' class='submit'$title$onclick>$libelle</button></div></form>";
4198
}
4199
4200
4201
/**
4202
 * Proteger les champs passes dans l'url et utiliser dans {tri ...}
4203
 * preserver l'espace pour interpreter ensuite num xxx et multi xxx
4204
 *
4205
 * @param string $t
4206
 * @return string
4207
 */
4208
function tri_protege_champ($t) {
4209
	return preg_replace(',[^\s\w.+],', '', $t);
4210
}
4211
4212
/**
4213
 * Interpreter les multi xxx et num xxx utilise comme tri
4214
 * pour la clause order
4215
 * 'multi xxx' devient simplement 'multi' qui est calcule dans le select
4216
 *
4217
 * @param string $t
4218
 * @param array $from
0 ignored issues
show
Documentation introduced by
Should the type for parameter $from not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

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

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

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

Loading history...
4219
 * @return string
4220
 */
4221
function tri_champ_order($t, $from = null) {
4222
	if (strncmp($t, 'multi ', 6) == 0) {
4223
		return "multi";
4224
	}
4225
4226
	$champ = $t;
4227
4228
	if (strncmp($t, 'num ', 4) == 0) {
4229
		$champ = substr($t, 4);
4230
	}
4231
	// enlever les autres espaces non evacues par tri_protege_champ
4232
	$champ = preg_replace(',\s,', '', $champ);
4233
4234
	if (is_array($from)) {
4235
		$trouver_table = charger_fonction('trouver_table', 'base');
4236
		foreach ($from as $idt => $table_sql) {
4237
			if ($desc = $trouver_table($table_sql)
4238
				and isset($desc['field'][$champ])
4239
			) {
4240
				$champ = "$idt.$champ";
4241
				break;
4242
			}
4243
		}
4244
	}
4245
	if (strncmp($t, 'num ', 4) == 0) {
4246
		return "0+$champ";
4247
	} else {
4248
		return $champ;
4249
	}
4250
}
4251
4252
/**
4253
 * Interpreter les multi xxx et num xxx utilise comme tri
4254
 * pour la clause select
4255
 * 'multi xxx' devient select "...." as multi
4256
 * les autres cas ne produisent qu'une chaine vide '' en select
4257
 * 'hasard' devient 'rand() AS hasard' dans le select
4258
 *
4259
 * @param string $t
4260
 * @return string
4261
 */
4262
function tri_champ_select($t) {
4263
	if (strncmp($t, 'multi ', 6) == 0) {
4264
		$t = substr($t, 6);
4265
		$t = preg_replace(',\s,', '', $t);
4266
		$t = sql_multi($t, $GLOBALS['spip_lang']);
4267
4268
		return $t;
4269
	}
4270
	if (trim($t) == 'hasard') {
4271
		return 'rand() AS hasard';
4272
	}
4273
4274
	return "''";
4275
}
4276
4277
4278
/**
4279
 * Donner n'importe quelle information sur un objet de maniere generique.
4280
 *
4281
 * La fonction va gerer en interne deux cas particuliers les plus utilises :
4282
 * l'URL et le titre (qui n'est pas forcemment le champ SQL "titre").
4283
 *
4284
 * On peut ensuite personnaliser les autres infos en creant une fonction
4285
 * generer_<nom_info>_entite($id_objet, $type_objet, $ligne).
4286
 * $ligne correspond a la ligne SQL de tous les champs de l'objet, les fonctions
4287
 * de personnalisation n'ont donc pas a refaire de requete.
4288
 *
4289
 * @param int $id_objet
4290
 * @param string $type_objet
4291
 * @param string $info
4292
 * @param string $etoile
4293
 * @return string
4294
 */
4295
function generer_info_entite($id_objet, $type_objet, $info, $etoile = "") {
4296
	static $trouver_table = null;
4297
	static $objets;
4298
4299
	// On verifie qu'on a tout ce qu'il faut
4300
	$id_objet = intval($id_objet);
4301
	if (!($id_objet and $type_objet and $info)) {
4302
		return '';
4303
	}
4304
4305
	// si on a deja note que l'objet n'existe pas, ne pas aller plus loin
4306
	if (isset($objets[$type_objet]) and $objets[$type_objet] === false) {
4307
		return '';
4308
	}
4309
4310
	// Si on demande l'url, on retourne direct la fonction
4311
	if ($info == 'url') {
4312
		return generer_url_entite($id_objet, $type_objet);
4313
	}
4314
4315
	// Sinon on va tout chercher dans la table et on garde en memoire
4316
	$demande_titre = ($info == 'titre');
4317
4318
	// 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
4319
	if (!isset($objets[$type_objet][$id_objet])
4320
		or
4321
		($demande_titre and !isset($objets[$type_objet][$id_objet]['titre']))
4322
	) {
4323
		if (!$trouver_table) {
4324
			$trouver_table = charger_fonction('trouver_table', 'base');
4325
		}
4326
		$desc = $trouver_table(table_objet_sql($type_objet));
4327
		if (!$desc) {
4328
			return $objets[$type_objet] = false;
4329
		}
4330
4331
		// Si on demande le titre, on le gere en interne
4332
		$champ_titre = "";
4333
		if ($demande_titre) {
4334
			// si pas de titre declare mais champ titre, il sera peuple par le select *
4335
			$champ_titre = (!empty($desc['titre'])) ? ', ' . $desc['titre'] : '';
4336
		}
4337
		include_spip('base/abstract_sql');
4338
		include_spip('base/connect_sql');
4339
		$objets[$type_objet][$id_objet] = sql_fetsel(
4340
			'*' . $champ_titre,
4341
			$desc['table_sql'],
4342
			id_table_objet($type_objet) . ' = ' . intval($id_objet)
4343
		);
4344
	}
4345
4346
	// Si la fonction generer_TRUC_TYPE existe, on l'utilise pour formater $info_generee
4347
	if ($generer = charger_fonction("generer_${info}_${type_objet}", '', true)) {
4348
		$info_generee = $generer($id_objet, $objets[$type_objet][$id_objet]);
4349
	} // Si la fonction generer_TRUC_entite existe, on l'utilise pour formater $info_generee
4350
	else {
4351
		if ($generer = charger_fonction("generer_${info}_entite", '', true)) {
4352
			$info_generee = $generer($id_objet, $type_objet, $objets[$type_objet][$id_objet]);
4353
		} // Sinon on prend directement le champ SQL tel quel
4354
		else {
4355
			$info_generee = (isset($objets[$type_objet][$id_objet][$info]) ? $objets[$type_objet][$id_objet][$info] : '');
4356
		}
4357
	}
4358
4359
	// On va ensuite appliquer les traitements automatiques si besoin
4360
	if (!$etoile) {
4361
		// FIXME: on fournit un ENV minimum avec id et type et connect=''
4362
		// mais ce fonctionnement est a ameliorer !
4363
		$info_generee = appliquer_traitement_champ($info_generee, $info, table_objet($type_objet),
4364
			array('id_objet' => $id_objet, 'objet' => $type_objet, ''));
4365
	}
4366
4367
	return $info_generee;
4368
}
4369
4370
/**
4371
 * Appliquer a un champ SQL le traitement qui est configure pour la balise homonyme dans les squelettes
4372
 *
4373
 * @param string $texte
4374
 * @param string $champ
4375
 * @param string $table_objet
4376
 * @param array $env
4377
 * @param string $connect
4378
 * @return string
4379
 */
4380
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...
4381
	if (!$champ) {
4382
		return $texte;
4383
	}
4384
	
4385
	// On charge toujours les filtres de texte car la majorité des traitements les utilisent
4386
	// et il ne faut pas partir du principe que c'est déjà chargé (form ajax, etc)
4387
	include_spip('inc/texte');
4388
	
4389
	$champ = strtoupper($champ);
4390
	$traitements = isset($GLOBALS['table_des_traitements'][$champ]) ? $GLOBALS['table_des_traitements'][$champ] : false;
4391
	if (!$traitements or !is_array($traitements)) {
4392
		return $texte;
4393
	}
4394
4395
	$traitement = '';
4396
	if ($table_objet and (!isset($traitements[0]) or count($traitements) > 1)) {
4397
		// necessaire pour prendre en charge les vieux appels avec un table_objet_sql en 3e arg
4398
		$table_objet = table_objet($table_objet);
4399
		if (isset($traitements[$table_objet])) {
4400
			$traitement = $traitements[$table_objet];
4401
		}
4402
	}
4403
	if (!$traitement and isset($traitements[0])) {
4404
		$traitement = $traitements[0];
4405
	}
4406
	// (sinon prendre le premier de la liste par defaut ?)
4407
4408
	if (!$traitement) {
4409
		return $texte;
4410
	}
4411
4412
	$traitement = str_replace('%s', "'" . texte_script($texte) . "'", $traitement);
4413
4414
	// Fournir $connect et $Pile[0] au traitement si besoin
4415
	$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...
4416
	eval("\$texte = $traitement;");
4417
4418
	return $texte;
4419
}
4420
4421
4422
/**
4423
 * Generer un lien (titre clicable vers url) vers un objet
4424
 *
4425
 * @param int $id_objet
4426
 * @param $objet
4427
 * @param int $longueur
4428
 * @param null|string $connect
4429
 * @return string
4430
 */
4431
function generer_lien_entite($id_objet, $objet, $longueur = 80, $connect = null) {
4432
	include_spip('inc/liens');
4433
	$titre = traiter_raccourci_titre($id_objet, $objet, $connect);
4434
	// lorsque l'objet n'est plus declare (plugin desactive par exemple)
4435
	// le raccourcis n'est plus valide
4436
	$titre = isset($titre['titre']) ? typo($titre['titre']) : '';
4437
	// on essaye avec generer_info_entite ?
4438
	if (!strlen($titre) and !$connect) {
4439
		$titre = generer_info_entite($id_objet, $objet, 'titre');
4440
	}
4441
	if (!strlen($titre)) {
4442
		$titre = _T('info_sans_titre');
4443
	}
4444
	$url = generer_url_entite($id_objet, $objet, '', '', $connect);
4445
4446
	return "<a href='$url' class='$objet'>" . couper($titre, $longueur) . "</a>";
4447
}
4448
4449
4450
/**
4451
 * Englobe (Wrap) un texte avec des balises
4452
 *
4453
 * @example `wrap('mot','<b>')` donne `<b>mot</b>'`
4454
 *
4455
 * @filtre
4456
 * @uses extraire_balises()
4457
 *
4458
 * @param string $texte
4459
 * @param string $wrap
4460
 * @return string
4461
 */
4462
function wrap($texte, $wrap) {
4463
	$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...
4464
	if (preg_match_all(",<([a-z]\w*)\b[^>]*>,UimsS", $wrap, $regs, PREG_PATTERN_ORDER)) {
4465
		$texte = $wrap . $texte;
4466
		$regs = array_reverse($regs[1]);
4467
		$wrap = "</" . implode("></", $regs) . ">";
4468
		$texte = $texte . $wrap;
4469
	}
4470
4471
	return $texte;
4472
}
4473
4474
4475
/**
4476
 * afficher proprement n'importe quoi
4477
 * On affiche in fine un pseudo-yaml qui premet de lire humainement les tableaux et de s'y reperer
4478
 *
4479
 * Les textes sont retournes avec simplement mise en forme typo
4480
 *
4481
 * 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
4482
 * les tableaux-listes (qui n'ont que des cles numeriques), sont affiches sous forme de liste separee par des virgules :
4483
 * c'est VOULU !
4484
 *
4485
 * @param $u
4486
 * @param string $join
4487
 * @param int $indent
4488
 * @return array|mixed|string
4489
 */
4490
function filtre_print_dist($u, $join = "<br />", $indent = 0) {
4491
	if (is_string($u)) {
4492
		$u = typo($u);
4493
4494
		return $u;
4495
	}
4496
4497
	// caster $u en array si besoin
4498
	if (is_object($u)) {
4499
		$u = (array)$u;
4500
	}
4501
4502
	if (is_array($u)) {
4503
		$out = "";
4504
		// toutes les cles sont numeriques ?
4505
		// et aucun enfant n'est un tableau
4506
		// liste simple separee par des virgules
4507
		$numeric_keys = array_map('is_numeric', array_keys($u));
4508
		$array_values = array_map('is_array', $u);
4509
		$object_values = array_map('is_object', $u);
4510
		if (array_sum($numeric_keys) == count($numeric_keys)
4511
			and !array_sum($array_values)
4512
			and !array_sum($object_values)
4513
		) {
4514
			return join(", ", array_map('filtre_print_dist', $u));
4515
		}
4516
4517
		// sinon on passe a la ligne et on indente
4518
		$i_str = str_pad("", $indent, " ");
4519
		foreach ($u as $k => $v) {
4520
			$out .= $join . $i_str . "$k: " . filtre_print_dist($v, $join, $indent + 2);
4521
		}
4522
4523
		return $out;
4524
	}
4525
4526
	// on sait pas quoi faire...
4527
	return $u;
4528
}
4529
4530
4531
/**
4532
 * Renvoyer l'info d'un objet
4533
 * telles que definies dans declarer_tables_objets_sql
4534
 *
4535
 * @param string $objet
4536
 * @param string $info
4537
 * @return string
4538
 */
4539
function objet_info($objet, $info) {
4540
	$table = table_objet_sql($objet);
4541
	$infos = lister_tables_objets_sql($table);
4542
4543
	return (isset($infos[$info]) ? $infos[$info] : '');
4544
}
4545
4546
/**
4547
 * Filtre pour afficher 'Aucun truc' ou '1 truc' ou 'N trucs'
4548
 * avec la bonne chaîne de langue en fonction de l'objet utilisé
4549
 *
4550
 * @param int $nb
4551
 *     Nombre d'éléments
4552
 * @param string $objet
4553
 *     Objet
4554
 * @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...
4555
 *     Texte traduit du comptage, tel que '3 articles'
4556
 */
4557
function objet_afficher_nb($nb, $objet) {
4558
	if (!$nb) {
4559
		return _T(objet_info($objet, 'info_aucun_objet'));
4560
	} else {
4561
		return _T(objet_info($objet, $nb == 1 ? 'info_1_objet' : 'info_nb_objets'), array('nb' => $nb));
4562
	}
4563
}
4564
4565
/**
4566
 * Filtre pour afficher l'img icone d'un objet
4567
 *
4568
 * @param string $objet
4569
 * @param int $taille
4570
 * @return string
4571
 */
4572
function objet_icone($objet, $taille = 24) {
4573
	$icone = objet_info($objet, 'icone_objet') . "-" . $taille . ".png";
4574
	$icone = chemin_image($icone);
4575
	$balise_img = charger_filtre('balise_img');
4576
4577
	return $icone ? $balise_img($icone, _T(objet_info($objet, 'texte_objet'))) : '';
4578
}
4579
4580
/**
4581
 * Renvoyer une traduction d'une chaine de langue contextuelle à un objet si elle existe,
4582
 * la traduction de la chaine generique
4583
 *
4584
 * Ex : [(#ENV{objet}|objet_label{trad_reference})]
4585
 * va chercher si une chaine objet:trad_reference existe et renvoyer sa trad le cas echeant
4586
 * sinon renvoie la trad de la chaine trad_reference
4587
 * Si la chaine fournie contient un prefixe il est remplacé par celui de l'objet pour chercher la chaine contextuelle
4588
 *
4589
 * Les arguments $args et $options sont ceux de la fonction _T
4590
 *
4591
 * @param string $objet
4592
 * @param string $chaine
4593
 * @param array $args
4594
 * @param array $options
4595
 * @return string
4596
 */
4597
function objet_T($objet, $chaine, $args = array(), $options = array()){
4598
	$chaine = explode(':',$chaine);
4599
	if ($t = _T($objet . ':' . end($chaine), $args, array_merge($options, array('force'=>false)))) {
4600
		return $t;
4601
	}
4602
	$chaine = implode(':',$chaine);
4603
	return _T($chaine, $args, $options);
4604
}
4605
4606
/**
4607
 * Fonction de secours pour inserer le head_css de facon conditionnelle
4608
 *
4609
 * Appelée en filtre sur le squelette qui contient #INSERT_HEAD,
4610
 * elle vérifie l'absence éventuelle de #INSERT_HEAD_CSS et y suplée si besoin
4611
 * pour assurer la compat avec les squelettes qui n'utilisent pas.
4612
 *
4613
 * @param string $flux Code HTML
4614
 * @return string      Code HTML
4615
 */
4616
function insert_head_css_conditionnel($flux) {
4617
	if (strpos($flux, '<!-- insert_head_css -->') === false
4618
		and $p = strpos($flux, '<!-- insert_head -->')
4619
	) {
4620
		// plutot avant le premier js externe (jquery) pour etre non bloquant
4621
		if ($p1 = stripos($flux, '<script src=') and $p1 < $p) {
4622
			$p = $p1;
4623
		}
4624
		$flux = substr_replace($flux, pipeline('insert_head_css', '<!-- insert_head_css -->'), $p, 0);
4625
	}
4626
4627
	return $flux;
4628
}
4629
4630
/**
4631
 * Produire un fichier statique à partir d'un squelette dynamique
4632
 *
4633
 * Permet ensuite à Apache de le servir en statique sans repasser
4634
 * par spip.php à chaque hit sur le fichier.
4635
 *
4636
 * Si le format (css ou js) est passe dans `contexte['format']`, on l'utilise
4637
 * sinon on regarde si le fond finit par .css ou .js, sinon on utilie "html"
4638
 *
4639
 * @uses urls_absolues_css()
4640
 *
4641
 * @param string $fond
4642
 * @param array $contexte
4643
 * @param array $options
4644
 * @param string $connect
4645
 * @return string
4646
 */
4647
function produire_fond_statique($fond, $contexte = array(), $options = array(), $connect = '') {
4648
	if (isset($contexte['format'])) {
4649
		$extension = $contexte['format'];
4650
		unset($contexte['format']);
4651
	} else {
4652
		$extension = "html";
4653
		if (preg_match(',[.](css|js|json)$,', $fond, $m)) {
4654
			$extension = $m[1];
4655
		}
4656
	}
4657
	// recuperer le contenu produit par le squelette
4658
	$options['raw'] = true;
4659
	$cache = recuperer_fond($fond, $contexte, $options, $connect);
4660
4661
	// calculer le nom de la css
4662
	$dir_var = sous_repertoire(_DIR_VAR, 'cache-' . $extension);
4663
	$nom_safe = preg_replace(",\W,", '_', str_replace('.', '_', $fond));
4664
	$contexte_implicite = calculer_contexte_implicite();
4665
	$filename = $dir_var . $extension . "dyn-$nom_safe-"
4666
		. substr(md5($fond . serialize($contexte_implicite) . serialize($contexte) . $connect), 0, 8)
4667
		. ".$extension";
4668
4669
	// mettre a jour le fichier si il n'existe pas
4670
	// ou trop ancien
4671
	// le dernier fichier produit est toujours suffixe par .last
4672
	// et recopie sur le fichier cible uniquement si il change
4673
	if (!file_exists($filename)
4674
		or !file_exists($filename . ".last")
4675
		or (isset($cache['lastmodified']) and $cache['lastmodified'] and filemtime($filename . ".last") < $cache['lastmodified'])
4676
		or (defined('_VAR_MODE') and _VAR_MODE == 'recalcul')
4677
	) {
4678
		$contenu = $cache['texte'];
4679
		// passer les urls en absolu si c'est une css
4680
		if ($extension == "css") {
4681
			$contenu = urls_absolues_css($contenu,
4682
				test_espace_prive() ? generer_url_ecrire('accueil') : generer_url_public($fond));
4683
		}
4684
4685
		$comment = '';
4686
		// ne pas insérer de commentaire si c'est du json
4687
		if ($extension != "json") {
4688
			$comment = "/* #PRODUIRE{fond=$fond";
4689
			foreach ($contexte as $k => $v) {
4690
				$comment .= ",$k=$v";
4691
			}
4692
			// pas de date dans le commentaire car sinon ca invalide le md5 et force la maj
4693
			// mais on peut mettre un md5 du contenu, ce qui donne un aperu rapide si la feuille a change ou non
4694
			$comment .= "}\n   md5:" . md5($contenu) . " */\n";
4695
		}
4696
		// et ecrire le fichier
4697
		ecrire_fichier($filename . ".last", $comment . $contenu);
4698
		// regarder si on recopie
4699
		if (!file_exists($filename)
4700
			or md5_file($filename) !== md5_file($filename . ".last")
4701
		) {
4702
			@copy($filename . ".last", $filename);
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...
4703
			clearstatcache(true, $filename); // eviter que PHP ne reserve le vieux timestamp
4704
		}
4705
	}
4706
4707
	return timestamp($filename);
4708
}
4709
4710
/**
4711
 * Ajouter un timestamp a une url de fichier
4712
 * [(#CHEMIN{monfichier}|timestamp)]
4713
 *
4714
 * @param string $fichier
4715
 *    Le chemin du fichier sur lequel on souhaite ajouter le timestamp
4716
 * @return string
4717
 *    $fichier auquel on a ajouté le timestamp
4718
 */
4719
function timestamp($fichier) {
4720
	if (!$fichier
4721
		or !file_exists($fichier)
4722
		or !$m = filemtime($fichier)
4723
	) {
4724
		return $fichier;
4725
	}
4726
4727
	return "$fichier?$m";
4728
}
4729
4730
/**
4731
 * Supprimer le timestamp d'une url
4732
 *
4733
 * @param string $url
4734
 * @return string
4735
 */
4736
function supprimer_timestamp($url) {
4737
	if (strpos($url, "?") === false) {
4738
		return $url;
4739
	}
4740
4741
	return preg_replace(",\?[[:digit:]]+$,", "", $url);
4742
}
4743
4744
/**
4745
 * Nettoyer le titre d'un email
4746
 *
4747
 * Éviter une erreur lorsqu'on utilise `|nettoyer_titre_email` dans un squelette de mail
4748
 *
4749
 * @filtre
4750
 * @uses nettoyer_titre_email()
4751
 *
4752
 * @param string $titre
4753
 * @return string
4754
 */
4755
function filtre_nettoyer_titre_email_dist($titre) {
4756
	include_spip('inc/envoyer_mail');
4757
4758
	return nettoyer_titre_email($titre);
4759
}
4760
4761
/**
4762
 * Afficher le sélecteur de rubrique
4763
 *
4764
 * Il permet de placer un objet dans la hiérarchie des rubriques de SPIP
4765
 *
4766
 * @uses chercher_rubrique()
4767
 *
4768
 * @param string $titre
4769
 * @param int $id_objet
4770
 * @param int $id_parent
4771
 * @param string $objet
4772
 * @param int $id_secteur
4773
 * @param bool $restreint
4774
 * @param bool $actionable
4775
 *   true : fournit le selecteur dans un form directement postable
4776
 * @param bool $retour_sans_cadre
4777
 * @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...
4778
 */
4779
function filtre_chercher_rubrique_dist(
4780
	$titre,
4781
	$id_objet,
4782
	$id_parent,
4783
	$objet,
4784
	$id_secteur,
4785
	$restreint,
4786
	$actionable = false,
4787
	$retour_sans_cadre = false
4788
) {
4789
	include_spip('inc/filtres_ecrire');
4790
4791
	return chercher_rubrique($titre, $id_objet, $id_parent, $objet, $id_secteur, $restreint, $actionable,
4792
		$retour_sans_cadre);
4793
}
4794
4795
/**
4796
 * Rediriger une page suivant une autorisation,
4797
 * et ce, n'importe où dans un squelette, même dans les inclusions.
4798
 *
4799
 * En l'absence de redirection indiquée, la fonction redirige par défaut
4800
 * sur une 403 dans l'espace privé et 404 dans l'espace public.
4801
 *
4802
 * @example
4803
 *     ```
4804
 *     [(#AUTORISER{non}|sinon_interdire_acces)]
4805
 *     [(#AUTORISER{non}|sinon_interdire_acces{#URL_PAGE{login}, 401})]
4806
 *     ```
4807
 *
4808
 * @filtre
4809
 * @param bool $ok
4810
 *     Indique si l'on doit rediriger ou pas
4811
 * @param string $url
4812
 *     Adresse eventuelle vers laquelle rediriger
4813
 * @param int $statut
4814
 *     Statut HTML avec lequel on redirigera
4815
 * @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...
4816
 *     message d'erreur
4817
 * @return string|void
4818
 *     Chaîne vide si l'accès est autorisé
4819
 */
4820
function sinon_interdire_acces($ok = false, $url = '', $statut = 0, $message = null) {
4821
	if ($ok) {
4822
		return '';
4823
	}
4824
4825
	// Vider tous les tampons
4826
	$level = @ob_get_level();
4827
	while ($level--) {
4828
		@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...
4829
	}
4830
4831
	include_spip('inc/headers');
4832
4833
	// S'il y a une URL, on redirige (si pas de statut, la fonction mettra 302 par défaut)
4834
	if ($url) {
4835
		redirige_par_entete($url, '', $statut);
4836
	}
4837
4838
	// ecriture simplifiee avec message en 3eme argument (= statut 403)
4839
	if (!is_numeric($statut) and is_null($message)) {
4840
		$message = $statut;
4841
		$statut = 0;
4842
	}
4843
	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...
4844
		$message = '';
4845
	}
4846
	$statut = intval($statut);
4847
4848
	// Si on est dans l'espace privé, on génère du 403 Forbidden par defaut ou du 404
4849
	if (test_espace_prive()) {
4850
		if (!$statut or !in_array($statut, array(404, 403))) {
4851
			$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...
4852
		}
4853
		http_status(403);
4854
		$echec = charger_fonction('403', 'exec');
4855
		$echec($message);
4856
	} else {
4857
		// Sinon dans l'espace public on redirige vers une 404 par défaut, car elle toujours présente normalement
4858
		if (!$statut) {
4859
			$statut = 404;
4860
		}
4861
		// Dans tous les cas on modifie l'entité avec ce qui est demandé
4862
		http_status($statut);
4863
		// Si le statut est une erreur et qu'il n'y a pas de redirection on va chercher le squelette du même nom
4864
		if ($statut >= 400) {
4865
			echo recuperer_fond("$statut", array('erreur' => $message));
4866
		}
4867
	}
4868
4869
4870
	exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function sinon_interdire_acces() contains an exit expression.

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

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

Loading history...
4871
}
4872
4873
/**
4874
 * Assurer le fonctionnement de |compacte meme sans l'extension compresseur
4875
 *
4876
 * @param string $source
4877
 * @param null|string $format
4878
 * @return string
4879
 */
4880
function filtre_compacte_dist($source, $format = null) {
4881
	if (function_exists('compacte')) {
4882
		return compacte($source, $format);
4883
	}
4884
4885
	return $source;
4886
}
4887