Skip to content
Extraits de code Groupes Projets
utils.php 107 ko
Newer Older
		$rep = (_DIR_RESTREINT ? '' : _DIR_RESTREINT_ABS) . $rep;
	}
	$rep = preg_replace(',(^\.\.\/),', '', $rep);
/**
 * Débute ou arrête un chronomètre et retourne sa valeur
 *
 * On exécute 2 fois la fonction, la première fois pour démarrer le chrono,
 * la seconde fois pour l’arrêter et récupérer la valeur
 *
 * @example
 *     ```
 *     spip_timer('papoter');
 *     // actions
 *     $duree = spip_timer('papoter');
 *     ```
 *
 * @param string $t
 *     Nom du chronomètre
 * @param bool $raw
 *     - false : retour en texte humainement lisible
 *     - true : retour en millisecondes
 * @return float|int|string|void
 */
function spip_timer($t = 'rien', $raw = false) {
cerdic's avatar
cerdic a validé
	// microtime peut contenir les microsecondes et le temps
	$b = explode(' ', $b);
	if (count($b) == 2) {
		$a = end($b);
	} // plus precis !
cerdic's avatar
cerdic a validé
	$b = reset($b);
JamesRezo's avatar
JamesRezo a validé
			$s = sprintf('%d ', $x = floor($p / 1000));
JamesRezo's avatar
JamesRezo a validé
		return $s . sprintf($s ? '%07.3f ms' : '%.3f ms', $p);
// Renvoie False si un fichier n'est pas plus vieux que $duree secondes,
// sinon renvoie True et le date sauf si ca n'est pas souhaite
function spip_touch($fichier, $duree = 0, $touch = true) {
		if (($f = @filemtime($fichier)) && $f >= time() - $duree) {
	if ($touch !== false) {
		if (!@touch($fichier)) {
			spip_unlink($fichier);
			@touch($fichier);
		};
		@chmod($fichier, _SPIP_CHMOD & ~0111);
	http_response_code(204); // No Content
JamesRezo's avatar
JamesRezo a validé
	header('Connection: close');
 *     Tâches forcées, pour compat avec ancienne syntaxe
 *     True si la tache a pu être effectuée
JamesRezo's avatar
JamesRezo a validé
function cron($taches = [], $taches_old = []) {
	// si pas en mode cron force, laisser tomber.
	if (!defined('_DIRECT_CRON_FORCE')) {
		return false;
	}
	if (!is_array($taches)) {
		$taches = $taches_old;
	} // compat anciens appels
	// si taches a inserer en base et base inaccessible, laisser tomber
	// sinon on ne verifie pas la connexion tout de suite, car si ca se trouve
	// queue_sleep_time_to_next_job() dira qu'il n'y a rien a faire
	// et on evite d'ouvrir une connexion pour rien (utilisation de _DIRECT_CRON_FORCE dans mes_options.php)
	if ($taches && count($taches) && !spip_connect()) {
JamesRezo's avatar
JamesRezo a validé
	spip_log('cron !', 'jq' . _LOG_DEBUG);
	if ($genie = charger_fonction('genie', 'inc', true)) {
/**
 * Ajout d'une tache dans la file d'attente
 *
amemo's avatar
amemo a validé
 *     Le nom de la fonction PHP qui doit être appelée.
amemo's avatar
amemo a validé
 *     Une description humainement compréhensible de ce que fait la tâche
 *     (essentiellement pour l’affichage dans la page de suivi de l’espace privé)
amemo's avatar
amemo a validé
 *     Facultatif, vide par défaut : les arguments qui seront passés à la fonction, sous forme de tableau PHP
amemo's avatar
amemo a validé
 *     Facultatif, vide par défaut : nom du fichier à inclure, via `include_spip($file)`
 *     exemple : `'inc/mail'` : il ne faut pas indiquer .php
 *     Si le nom finit par un '/' alors on considère que c’est un répertoire et SPIP fera un `charger_fonction($function, $file)`
amemo's avatar
amemo a validé
 *     Facultatif, `false` par défaut
 *
 *     - si `true` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction et les mêmes arguments.
 *     - si `function_only` la tâche ne sera pas ajoutée si elle existe déjà en file d’attente avec la même fonction indépendamment de ses arguments
amemo's avatar
amemo a validé
 *     Facultatif, `0` par défaut : indique la date sous forme de timestamp à laquelle la tâche doit être programmée.
 *     Si `0` ou une date passée, la tâche sera exécutée aussitôt que possible (en général en fin hit, en asynchrone).
amemo's avatar
amemo a validé
 *     Facultatif, `0` par défaut : indique un niveau de priorité entre -10 et +10.
 *     Les tâches sont exécutées par ordre de priorité décroissante, une fois leur date d’exécution passée. La priorité est surtout utilisée quand une tâche cron indique qu’elle n’a pas fini et doit être relancée : dans ce cas SPIP réduit sa priorité pour être sûr que celle tâche ne monopolise pas la file d’attente.
amemo's avatar
amemo a validé
 *     Le numéro de travail ajouté ou `0` si aucun travail n’a été ajouté.
JamesRezo's avatar
JamesRezo a validé
	$arguments = [],
	$file = '',
	$no_duplicate = false,
	$time = 0,
	$priority = 0
) {
	return queue_add_job($function, $description, $arguments, $file, $no_duplicate, $time, $priority);
}

/**
 * Supprimer une tache de la file d'attente
 * @param int $id_job
 *  id of jonb to delete
 * @return bool
 */
	return queue_remove_job($id_job);
}

/**
 * Associer une tache a un/des objets de SPIP
 *     can be a simple array('objet'=>'article', 'id_objet'=>23)
 *     or an array of simple array to link multiples objet in one time
function job_queue_link($id_job, $objets) {
}


/**
 * Renvoyer le temps de repos restant jusqu'au prochain job
 * @staticvar int $queue_next_job_time
marcimat's avatar
marcimat a validé
 * @see queue_set_next_job_time()
 * @param int|bool $force
 *    Utilisée par `queue_set_next_job_time()` pour mettre à jour la valeur :
marcimat's avatar
marcimat a validé
 *    - si `true`, force la relecture depuis le fichier
 *    - si int, affecte la static directement avec la valeur
marcimat's avatar
marcimat a validé
 * @return int|null
marcimat's avatar
marcimat a validé
 *  - `0` si un job est à traiter
 *  - `null` si la queue n'est pas encore initialisée
function queue_sleep_time_to_next_job($force = null) {
marcimat's avatar
marcimat a validé
	static $queue_next_job_time = -1;
		$queue_next_job_time = $force;
		if (!defined('_JQ_NEXT_JOB_TIME_FILENAME')) {
JamesRezo's avatar
JamesRezo a validé
			define('_JQ_NEXT_JOB_TIME_FILENAME', _DIR_TMP . 'job_queue_next.txt');
		// utiliser un cache memoire si dispo
		if (function_exists('cache_get') && defined('_MEMOIZE_MEMORY') && _MEMOIZE_MEMORY) {
			$queue_next_job_time = cache_get(_JQ_NEXT_JOB_TIME_FILENAME);
marcimat's avatar
marcimat a validé
			$contenu = null;
			if (lire_fichier(_JQ_NEXT_JOB_TIME_FILENAME, $contenu)) {
				$queue_next_job_time = intval($contenu);
		$_SERVER['REQUEST_TIME'] = time();
	return $queue_next_job_time - $_SERVER['REQUEST_TIME'];
marcimat's avatar
marcimat a validé
/**
 * Transformation XML des `&` en `&`
JamesRezo's avatar
JamesRezo a validé
 *
marcimat's avatar
marcimat a validé
 * @pipeline post_typo
 * @param string $u
 * @return string
 */
function quote_amp($u) {
	return preg_replace(
JamesRezo's avatar
JamesRezo a validé
		'/&(?![a-z]{0,4}\w{2,3};|#x?[0-9a-f]{2,6};)/i',
		'&',
		$u
	);

/**
 * Produit une balise `<script>` valide
 *
 * @example
 *     ```
 *     echo http_script('alert("ok");');
 *     echo http_script('','js/jquery.js');
 *     ```
 * @param string $script
 *     Code source du script
 * @param string $src
 *     Permet de faire appel à un fichier javascript distant
 * @param string $noscript
 *     Contenu de la balise  `<noscript>`
 * @return string
 *     Balise HTML `<script>` et son contenu
function http_script($script, $src = '', $noscript = '') {
JamesRezo's avatar
JamesRezo a validé
	static $done = [];
		$done[$src] = true;
		$src = find_in_path($src, _JAVASCRIPT);
		$script = ("/*<![CDATA[*/\n" .
			preg_replace(',</([^>]*)>,', '<\/\1>', $script) .
JamesRezo's avatar
JamesRezo a validé
			'/*]]>*/');
		$noscript = "<noscript>\n\t$noscript\n</noscript>\n";
	return ($src || $script || $noscript)
		? "<script type='text/javascript'$src>$script</script>$noscript"
		: '';

/**
 * Sécurise du texte à écrire dans du PHP ou du Javascript.
 * Transforme n'importe quel texte en une chaîne utilisable
 * en PHP ou Javascript en toute sécurité, à l'intérieur d'apostrophes
 * simples (`'` uniquement ; pas `"`)
 *
 * Utile particulièrement en filtre dans un squelettes
 * pour écrire un contenu dans une variable JS ou PHP.
 *
 * Échappe les apostrophes (') du contenu transmis.
 *
 * @link https://www.spip.net/4281
 * @example
 *     PHP dans un squelette
 *     ```
 *     $x = '[(#TEXTE|texte_script)]';
 *     ```
 *
 *     JS dans un squelette (transmettre une chaîne de langue)
 *     ```
 *     $x = '<:afficher_calendrier|texte_script:>';
 *     ```
 *
 * @filtre
 * @return string
function texte_script(?string $texte): string {
	if ($texte === null || $texte === '') {
		return '';
	}
	return str_replace('\'', '\\\'', str_replace('\\', '\\\\', $texte));
}


/**
 * Gestion des chemins (ou path) de recherche de fichiers par SPIP
 *
amemo's avatar
amemo a validé
 * Empile de nouveaux chemins (à la suite de ceux déjà présents, mais avant
 * le répertoire `squelettes` ou les dossiers squelettes), si un répertoire
 * (ou liste de répertoires séparés par `:`) lui est passé en paramètre.
 *
 * Ainsi, si l'argument est de la forme `dir1:dir2:dir3`, ces 3 chemins sont placés
amemo's avatar
amemo a validé
 * en tête du path, dans cet ordre (hormis `squelettes` & la globale
 * `$dossier_squelette` si définie qui resteront devant)
 * Retourne dans tous les cas la liste des chemins.
 *
 * @note
 *     Cette fonction est appelée à plusieurs endroits et crée une liste
 *     de chemins finale à peu près de la sorte :
 *     - dossiers squelettes (si globale précisée)
 *     - squelettes/
 *     - plugins (en fonction de leurs dépendances) : ceux qui dépendent
 *       d'un plugin sont devant eux (ils peuvent surcharger leurs fichiers)
 *     - racine du site
 *     - squelettes-dist/
 *     - prive/
 *     - ecrire/
 *     - Répertoire(s) à empiler au path
 *     - '' provoque un recalcul des chemins.
 * @return array
 *     Liste des chemins, par ordre de priorité.
 **/
function _chemin($dir_path = null) {
	static $path_base = null;
	static $path_full = null;
	if ($path_base == null) {
Fil's avatar
Fil a validé
		// Chemin standard depuis l'espace public
		$path = defined('_SPIP_PATH') ? _SPIP_PATH :
			_DIR_RACINE . ':' .
			_DIR_RACINE . 'squelettes-dist/:' .
			_DIR_RACINE . 'prive/:' .
Fil's avatar
Fil a validé
		// Ajouter squelettes/
		if (@is_dir(_DIR_RACINE . 'squelettes')) {
			$path = _DIR_RACINE . 'squelettes/:' . $path;
		}
Fil's avatar
Fil a validé
		foreach (explode(':', $path) as $dir) {
			if (strlen($dir) && !str_ends_with($dir, '/')) {
JamesRezo's avatar
JamesRezo a validé
				$dir .= '/';
			$path_base[] = $dir;
		}
		$path_full = $path_base;
		// Et le(s) dossier(s) des squelettes nommes
		if (strlen($GLOBALS['dossier_squelettes'])) {
			foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
				array_unshift($path_full, ($d[0] == '/' ? '' : _DIR_RACINE) . $d . '/');
		$GLOBALS['path_sig'] = md5(serialize($path_full));
	if (is_array($dir_path) || strlen($dir_path)) {
JamesRezo's avatar
JamesRezo a validé
		$tete = '';
		if (reset($path_base) == _DIR_RACINE . 'squelettes/') {
		$dirs = (is_array($dir_path) ? $dir_path : explode(':', $dir_path));
		$dirs = array_reverse($dirs);
			if (!str_ends_with($dir_path, '/')) {
JamesRezo's avatar
JamesRezo a validé
				$dir_path .= '/';
			}
			if (!in_array($dir_path, $path_base)) {
				array_unshift($path_base, $dir_path);
			}
		}
		if (strlen($tete)) {
			array_unshift($path_base, $tete);
		}
Fil's avatar
Fil a validé
	}
	$path_full = $path_base;
	// Et le(s) dossier(s) des squelettes nommes
	if (strlen($GLOBALS['dossier_squelettes'])) {
		foreach (array_reverse(explode(':', $GLOBALS['dossier_squelettes'])) as $d) {
			array_unshift($path_full, ((isset($d[0]) && $d[0] == '/') ? '' : _DIR_RACINE) . $d . '/');
	$GLOBALS['path_sig'] = md5(serialize($path_full));
/**
 * Retourne la liste des chemins connus de SPIP, dans l'ordre de priorité
 *
 * Recalcule la liste si le nom ou liste de dossier squelettes a changé.
 *
 * @uses _chemin()
 * @return array Liste de chemins
function creer_chemin() {
	$path_a = _chemin();
	static $c = '';
	// on calcule le chemin si le dossier skel a change
	if ($c != $GLOBALS['dossier_squelettes']) {
		// assurer le non plantage lors de la montee de version :
		$c = $GLOBALS['dossier_squelettes'];
		$path_a = _chemin(''); // forcer un recalcul du chemin
	}
/**
 * Retourne la liste des thèmes du privé utilisables pour cette session
 *
 * @see inscription_nouveau() pour une particularité historique du champ 'prefs'
 *
 * @return string[] Nom des thèmes.
 */
function lister_themes_prives(): array {
	static $themes = null;
cerdic's avatar
cerdic a validé
		// si pas encore definie
denisb's avatar
denisb a validé
			define('_SPIP_THEME_PRIVE', 'spip');
JamesRezo's avatar
JamesRezo a validé
		$themes = [_SPIP_THEME_PRIVE];
		// Lors d'une installation neuve, prefs n'est pas definie ; sinon, c'est un tableau sérialisé
		// FIXME: Aussitôt après une demande d'inscription, $prefs vaut une chaine statut_tmp;
		$prefs = $GLOBALS['visiteur_session']['prefs'] ?? [];
		if (is_string($prefs) && stripos($prefs, 'a:') === 0) {
			$prefs = unserialize($prefs);
JamesRezo's avatar
JamesRezo a validé
			$prefs = [];

		$theme = $prefs['theme'] ?? $GLOBALS['theme_prive_defaut'] ?? null;
		if ($theme && $theme !== _SPIP_THEME_PRIVE) {
			// placer le theme choisi en tete
cerdic's avatar
cerdic a validé
	}
	return $themes;
cerdic's avatar
cerdic a validé
}

function find_in_theme($file, $subdir = '', $include = false) {
JamesRezo's avatar
JamesRezo a validé
	static $themefiles = [];
	if (isset($themefiles["$subdir$file"])) {
		return $themefiles["$subdir$file"];
	}
	// on peut fournir une icone generique -xx.svg qui fera le job dans toutes les tailles, et qui est prioritaire sur le png
	// si il y a un .svg a la bonne taille (-16.svg) a cote, on l'utilise en remplacement du -16.png
JamesRezo's avatar
JamesRezo a validé
	if (
		preg_match(',-(\d+)[.](png|gif|svg)$,', $file, $m)
		&& ($file_svg_generique = substr($file, 0, -strlen($m[0])) . '-xx.svg')
		&& ($f = find_in_theme("$file_svg_generique"))
JamesRezo's avatar
JamesRezo a validé
	) {
		if (($fsize = substr($f, 0, -6) . $m[1] . '.svg') && file_exists($fsize)) {
			return $themefiles["$subdir$file"] = $fsize;
		}
		else {
JamesRezo's avatar
JamesRezo a validé
			return $themefiles["$subdir$file"] = "$f?" . $m[1] . 'px';
	$themes = lister_themes_prives();
	foreach ($themes as $theme) {
		if ($f = find_in_path($file, "prive/themes/$theme/$subdir", $include)) {
			return $themefiles["$subdir$file"] = $f;
cerdic's avatar
cerdic a validé
	}
	spip_log("$file introuvable dans le theme prive " . reset($themes), 'theme');

JamesRezo's avatar
JamesRezo a validé
	return $themefiles["$subdir$file"] = '';
cerdic's avatar
cerdic a validé
}
marcimat's avatar
marcimat a validé

/**
 * Cherche une image dans les dossiers d'images
 *
 * Cherche en priorité dans les thèmes d'image (prive/themes/X/images)
 * et si la fonction n'en trouve pas, gère le renommage des icones (ex: 'supprimer' => 'del')
 * de facon temporaire le temps de la migration, et cherche de nouveau.
 *
 * Si l'image n'est toujours pas trouvée, on la cherche dans les chemins,
 * dans le répertoire défini par la constante `_NOM_IMG_PACK`
marcimat's avatar
marcimat a validé
 *
 * @see find_in_theme()
 * @see inc_icone_renommer_dist()
marcimat's avatar
marcimat a validé
 * @param string $icone
 *     Nom de l'icone cherchée
 * @return string
 *     Chemin complet de l'icone depuis la racine si l'icone est trouée,
 *     sinon chaîne vide.
	static $icone_renommer;
JamesRezo's avatar
JamesRezo a validé
		$icone = substr($icone, 0, $p);
	// gerer le cas d'un double appel en evitant de refaire le travail inutilement
	if (str_contains($icone, '/') && file_exists($icone)) {
	// si c'est un nom d'image complet (article-24.png) essayer de le renvoyer direct
	if (preg_match(',[.](png|gif|jpg|webp|svg)$,', $icone) && ($f = find_in_theme("images/$icone"))) {
	// sinon passer par le module de renommage
	if (is_null($icone_renommer)) {
		$icone_renommer = charger_fonction('icone_renommer', 'inc', true);
	}
	if ($icone_renommer) {
		[$icone, $fonction] = $icone_renommer($icone, '');
cerdic's avatar
cerdic a validé
			return $icone;
cerdic's avatar
cerdic a validé
	}
//
// chercher un fichier $file dans le SPIP_PATH
// si on donne un sous-repertoire en 2e arg optionnel, il FAUT le / final
// si 3e arg vrai, on inclut si ce n'est fait.
$GLOBALS['path_sig'] = '';
cerdic's avatar
cerdic a validé
$GLOBALS['path_files'] = null;
Fil's avatar
Fil a validé

/**
 * Recherche un fichier dans les chemins de SPIP (squelettes, plugins, core)
 *
 * Retournera le premier fichier trouvé (ayant la plus haute priorité donc),
 * suivant l'ordre des chemins connus de SPIP.
 *
 * @uses creer_chemin() Pour la liste des chemins.
 * @example
 *     ```
 *     $f = find_in_path('css/perso.css');
 *     $f = find_in_path('perso.css', 'css');
 *     ```
 * @param string $file
 *     Fichier recherché
 * @param string $dirname
 *     Répertoire éventuel de recherche (est aussi extrait automatiquement de $file)
 * @param bool|string $include
 *     - false : ne fait rien de plus
 *     - true : inclut le fichier (include_once)
 *     - 'require' : idem, mais tue le script avec une erreur si le fichier n'est pas trouvé.
 * @return string|bool
 *     - string : chemin du fichier trouvé
 *     - false : fichier introuvable
 **/
function find_in_path($file, $dirname = '', $include = false) {
JamesRezo's avatar
JamesRezo a validé
	static $dirs = [];
	static $inc = []; # cf https://git.spip.net/spip/spip/commit/42e4e028e38c839121efaee84308d08aee307eec
	if (!$file && !strlen($file)) {
	// on calcule le chemin si le dossier skel a change
	if ($c != $GLOBALS['dossier_squelettes']) {
		// assurer le non plantage lors de la montee de version :
		$c = $GLOBALS['dossier_squelettes'];
		creer_chemin(); // forcer un recalcul du chemin et la mise a jour de path_sig
	}

	if (isset($GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file])) {
		if (!$GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file]) {
cerdic's avatar
cerdic a validé
			return false;
		if ($include && !isset($inc[$dirname][$file])) {
			include_once _ROOT_CWD . $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
cerdic's avatar
cerdic a validé
			$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
		}
		return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file];
esj's avatar
esj a validé
	if ($a !== false) {
		$dirname .= substr($file, 0, ++$a);
		$file = substr($file, $a);
	foreach (creer_chemin() as $dir) {
		if (!isset($dirs[$a = $dir . $dirname])) {
			$dirs[$a] = (is_dir(_ROOT_CWD . $a) || !$a);
		}
cerdic's avatar
cerdic a validé
			if (file_exists(_ROOT_CWD . ($a .= $file))) {
				if ($include && !isset($inc[$dirname][$file])) {
cerdic's avatar
cerdic a validé
					include_once _ROOT_CWD . $a;
					$inc[$dirname][$file] = $inc[''][$dirname . $file] = true;
				}
					// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
				return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = $a;
esj's avatar
esj a validé
		}
		spip_log("include_spip $dirname$file non trouve");
JamesRezo's avatar
JamesRezo a validé
			'<strong>Erreur Fatale</strong><br />';
			if (function_exists('debug_print_backtrace')) {
				debug_print_backtrace();
			echo '</pre>';
			die("Erreur interne: ne peut inclure $dirname$file");
		}
	}
cerdic's avatar
cerdic a validé

		// si le chemin n'a pas encore ete charge, ne pas lever le flag, ne pas cacher
		if (is_null($GLOBALS['path_files'])) {
			return false;
		}
	return $GLOBALS['path_files'][$GLOBALS['path_sig']][$dirname][$file] = $GLOBALS['path_files'][$GLOBALS['path_sig']][''][$dirname . $file] = false;
cerdic's avatar
cerdic a validé
}

JamesRezo's avatar
JamesRezo a validé
	$GLOBALS['path_files'] = [];
cerdic's avatar
cerdic a validé
	spip_unlink(_CACHE_CHEMIN);
}
	// charger le path des plugins
		include_once(_CACHE_PLUGINS_PATH);
	}
JamesRezo's avatar
JamesRezo a validé
	$GLOBALS['path_files'] = [];
cerdic's avatar
cerdic a validé
	// si le visiteur est admin,
	// on ne recharge pas le cache pour forcer sa mise a jour
cerdic's avatar
cerdic a validé
		// la session n'est pas encore chargee a ce moment, on ne peut donc pas s'y fier
		//AND (!isset($GLOBALS['visiteur_session']['statut']) OR $GLOBALS['visiteur_session']['statut']!='0minirezo')
cerdic's avatar
cerdic a validé
		// utiliser le cookie est un pis aller qui marche 'en general'
		// on blinde par un second test au moment de la lecture de la session
		// !isset($_COOKIE[$GLOBALS['cookie_prefix'].'_admin'])
cerdic's avatar
cerdic a validé
		// et en ignorant ce cache en cas de recalcul explicite
JamesRezo's avatar
JamesRezo a validé
		!_request('var_mode')
		// on essaye de lire directement sans verrou pour aller plus vite
		if ($contenu = spip_file_get_contents(_CACHE_CHEMIN)) {
			// mais si semble corrompu on relit avec un verrou
			if (!$GLOBALS['path_files'] = unserialize($contenu)) {
				lire_fichier(_CACHE_CHEMIN, $contenu);
				if (!$GLOBALS['path_files'] = unserialize($contenu)) {
JamesRezo's avatar
JamesRezo a validé
					$GLOBALS['path_files'] = [];
cerdic's avatar
cerdic a validé
	}
JamesRezo's avatar
JamesRezo a validé
	if (
		defined('_SAUVER_CHEMIN')
	) {
		ecrire_fichier(_CACHE_CHEMIN, serialize($GLOBALS['path_files']));
	}
cerdic's avatar
cerdic a validé
/**
 * Trouve tous les fichiers du path correspondants à un pattern
 * Pour un nom de fichier donné, ne retourne que le premier qui sera trouvé
 * par un `find_in_path()`
cerdic's avatar
cerdic a validé
 *
 * @api
marcimat's avatar
marcimat a validé
 * @uses creer_chemin()
 * @uses preg_files()
cerdic's avatar
cerdic a validé
 * @param string $dir
 * @param string $pattern
 * @param bool $recurs
 * @return array
 */
function find_all_in_path($dir, $pattern, $recurs = false) {
JamesRezo's avatar
JamesRezo a validé
	$liste_fichiers = [];
	// cas borderline si dans mes_options on appelle redirige_par_entete qui utilise _T et charge un fichier de langue
	// on a pas encore inclus flock.php
	if (!function_exists('preg_files')) {
		include_once _ROOT_RESTREINT . 'inc/flock.php';
	}

	foreach (creer_chemin() as $d) {
JamesRezo's avatar
JamesRezo a validé
			$liste = preg_files($f, $pattern, $maxfiles - count($liste_fichiers), $recurs === true ? [] : $recurs);
				$nom = basename($chemin);
				// ne prendre que les fichiers pas deja trouves
				// car find_in_path prend le premier qu'il trouve,
				// les autres sont donc masques
					$liste_fichiers[$nom] = $chemin;
marcimat's avatar
marcimat a validé
/**
 * Prédicat sur les scripts de ecrire qui n'authentifient pas par cookie
 * et beneficient d'une exception
 *
marcimat's avatar
marcimat a validé
 * @param string $nom
marcimat's avatar
marcimat a validé
 * @return bool
 */
function autoriser_sans_cookie($nom, $strict = false) {
JamesRezo's avatar
JamesRezo a validé
	static $autsanscookie = ['install', 'base_repair'];
	if (in_array($nom, $autsanscookie)) {
JamesRezo's avatar
JamesRezo a validé
		if (test_espace_prive()) {
			include_spip('base/connect_sql');
			if (!$strict || !spip_connect()) {
/**
 * Charger la fonction de gestion des urls si elle existe
marcimat's avatar
marcimat a validé
 * @param string $quoi
 *     'page' 'objet' 'decoder' ou objet spip pour lequel on cherche la fonction url par defaut (si type==='defaut')
 * @param string $type
 * 		type des urls (par defaut la meta type_urls) ou 'defaut' pour trouver la fonction par defaut d'un type d'objet
 * @return string
 */
function charger_fonction_url(string $quoi, string $type = '') {
	if ($type === 'defaut') {
		$objet = objet_type($quoi);
marcimat's avatar
marcimat a validé
		if (
			($f = charger_fonction('generer_' . $objet . '_url', 'urls', true))
			|| ($f = charger_fonction('generer_url_' . $objet, 'urls', true)) // deprecated
	$url_type = $type;
	if (!$url_type) {
		$url_type = $GLOBALS['type_urls'] ?? $GLOBALS['meta']['type_urls'] ?? 'page'; // sinon type "page" par défaut
	include_spip('urls/' . $url_type);
				function_exists($f = "urls_{$url_type}_generer_url_page")
				|| function_exists($f .= '_dist')
marcimat's avatar
marcimat a validé
				// ou une fonction custom utilisateur independante du type d'url
				|| function_exists($f = 'generer_url_page')
				|| function_exists($f .= '_dist')
			// pas de compat ancienne version ici, c'est une nouvelle feature
		case 'decoder':
		default:
			$fquoi = ($quoi === 'objet' ? 'generer_url_objet' : 'decoder_url');
marcimat's avatar
marcimat a validé
			if (
				function_exists($f = "urls_{$url_type}_{$fquoi}")
				|| function_exists($f .= '_dist')
marcimat's avatar
marcimat a validé
			) {
			// est-ce qu'on a une ancienne fonction urls_xxx_dist() ?
			// c'est un ancien module d'url, on appelle l'ancienne fonction qui fait tout
			if ($f = charger_fonction($url_type, 'urls', true)) {
			// sinon on se rabat sur les urls page si ce n'est pas un type demande explicitement
			if (!$type && $url_type !== 'page') {
				return charger_fonction_url($quoi, 'page');
			}
			return '';
 * Fonction codant  les URLs des objets SQL mis en page par SPIP
 * @api
 * @param int|string|null $id
 *   numero de la cle primaire si nombre
 * @param string $entite
 *   surnom de la table SQL (donne acces au nom de cle primaire)
 * @param string $args
 *   query_string a placer apres cle=$id&....
 * @param string $ancre
 *   ancre a mettre a la fin de l'URL a produire
 *   produire l'URL publique ou privee (par defaut: selon espace)
 * @param string $type
 *   fichier dans le repertoire ecrire/urls determinant l'apparence
cerdic's avatar
cerdic a validé
 *   serveur de base de donnee (nom du connect)
 *   url codee ou fonction de decodage
 */
function generer_objet_url($id, string $entite, string $args = '', string $ancre = '', ?bool $public = null, string $type = '', string $connect = ''): string {
	if ($public === null) {
		$public = !test_espace_prive();
	}
cerdic's avatar
cerdic a validé
	$entite = objet_type($entite); // cas particulier d'appels sur objet/id_objet...
		if (!function_exists('generer_objet_url_ecrire')) {
			include_spip('inc/urls');
		$res = generer_objet_url_ecrire($id, $entite, $args, $ancre, false, $connect);
		$f = charger_fonction_url('objet', $type ?? '');
		// @deprecated si $entite='', on veut la fonction de passage URL ==> id
		// @see charger_fonction_url

		// mais d'abord il faut tester le cas des urls sur une
		// base distante
marcimat's avatar
marcimat a validé
		if (
			$connect
			&& ($g = charger_fonction('connect', 'urls', true))
		$res = $f(intval($id), $entite, $args ?: '', $ancre ?: '', $connect);
	// On a ete gentil mais la ....
	spip_log("generer_objet_url: entite $entite " . ($public ? "($f)" : '') . " inconnue $type $public $connect", _LOG_ERREUR);
 * @see generer_objet_url
 */
marcimat's avatar
marcimat a validé
function generer_url_entite($id = 0, $entite = '', $args = '', $ancre = '', $public = null, $type = null) {
	trigger_deprecation('spip', '4.1', 'Using "%s" is deprecated, use "%s" instead', __FUNCTION__, 'generer_objet_url');
	if ($public && is_string($public)) {
		return generer_objet_url(intval($id), $entite, $args ?: '', $ancre ?: '', true, $type ?? '', $public);
	return generer_objet_url(intval($id), $entite, $args ?: '', $ancre ?: '', $public, $type ?? '');
/**
 * Generer l'url vers la page d'edition dans ecrire/
function generer_objet_url_ecrire_edit($id, string $entite, string $args = '', string $ancre = ''): string {
	$id = intval($id);
	$exec = objet_info($entite, 'url_edit');
	$url = generer_url_ecrire($exec, $args);
	if (intval($id)) {
		$url = parametre_url($url, id_table_objet($entite), $id);
	} else {
		$url = parametre_url($url, 'new', 'oui');
	}
	if ($ancre) {
		$url = ancre_url($url, $ancre);
	}

 * @see generer_objet_url_ecrire_edit
 */
marcimat's avatar
marcimat a validé
function generer_url_ecrire_entite_edit($id, $entite, $args = '', $ancre = '') {
	trigger_deprecation('spip', '4.1', 'Using "%s" is deprecated, use "%s" instead', __FUNCTION__, 'generer_objet_url_ecrire_edit');
	return generer_objet_url_ecrire_edit(intval($id), $entite, $args, $ancre);
function urls_connect_dist($i, &$entite, $args = '', $ancre = '', $public = null) {
	include_spip('base/connect_sql');
	return _DIR_RACINE . get_spip_script('./')
JamesRezo's avatar
JamesRezo a validé
	. '?' . _SPIP_PAGE . "=$entite&$id_type=$i&connect=$public"
	. (!$args ? '' : "&$args")
	. (!$ancre ? '' : "#$ancre");
/**
 * Transformer les caractères utf8 d'une URL (farsi par exemple) selon la RFC 1738
 *
 * @param string $url
 * @return string
 */
function urlencode_1738($url) {
	if (preg_match(',[^\x00-\x7E],sS', $url)) {