Server : Apache System : Linux webd348.cluster026.gra.hosting.ovh.net 5.15.148-ovh-vps-grsec-zfs-classid #1 SMP Thu Feb 8 09:41:04 UTC 2024 x86_64 User : hednacluml ( 122243) PHP Version : 8.3.9 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl Directory : /home/hednacluml/enfants/plugins-dist/compresseur/inc/ |
<?php /***************************************************************************\ * SPIP, Système de publication pour l'internet * * * * Copyright © avec tendresse depuis 2001 * * Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribué sous licence GNU/GPL. * \***************************************************************************/ /** * Fonctions d'aide pour le compresseur * * @package SPIP\Compresseur\Fonctions */ if (!defined('_ECRIRE_INC_VERSION')) { return; } /** * Ecrire la balise javascript pour insérer le fichier compressé * * C'est cette fonction qui décide où il est le plus pertinent * d'insérer le fichier, et dans quelle forme d'ecriture * * @param string $flux * Contenu du head nettoyé des fichiers qui ont été compressé * @param int $pos * Position initiale du premier fichier inclu dans le fichier compressé * @param string $src * Nom du fichier compressé * @param string $comments * Commentaires à insérer devant * @return string * Code HTML de la balise <script> */ function compresseur_ecrire_balise_js_dist(&$flux, $pos, $src, $comments = '') { $jQl = null; $src = timestamp($src); // option chargement JS async par jQl if (defined('_JS_ASYNC_LOAD') and !test_espace_prive()) { lire_fichier(find_in_path('lib/jQl/jQl.min.js'), $jQl); if ($jQl) { $comments .= "<script type='text/javascript'>\n$jQl\njQl.loadjQ('$src')\n</script>"; } else { $comments .= "<script type='text/javascript' src='$src'></script>"; } } else { $comments .= "<script type='text/javascript' src='$src'></script>"; } $flux = substr_replace($flux, $comments, $pos, 0); return $flux; } /** * Ecrire la balise CSS pour insérer le fichier compressé * * C'est cette fonction qui décide ou il est le plus pertinent * d'insérer le fichier, et dans quelle forme d'écriture * * @param string $flux * Contenu du head nettoyé des fichiers qui ont ete compressé * @param int $pos * Position initiale du premier fichier inclu dans le fichier compressé * @param string $src * Nom du fichier compressé * @param string $comments * Commentaires à insérer devant * @param string $media * Type de media si précisé (print|screen...) * @return string * Code HTML de la balise <link> */ function compresseur_ecrire_balise_css_dist(&$flux, $pos, $src, $comments = '', $media = '') { $src = timestamp($src); $comments .= "<link rel='stylesheet'" . ($media ? " media='$media'" : '') . " href='$src' type='text/css' />"; // Envoyer aussi un entete http pour demarer le chargement de la CSS plus tot // Link: <http://href.here/to/resource.html>;rel="preload";as="style" $comments .= '<' . "?php header('Link: <' . url_de_base() . (_DIR_RACINE ? _DIR_RESTREINT_ABS : '') . '$src>;rel=\"preload\";as=\"style\"'); ?>"; $flux = substr_replace($flux, $comments, $pos, 0); return $flux; } /** * Extraire les balises CSS à compacter * * @param string $flux * Contenu HTML dont on extrait les balises CSS * @param string $url_base * @return array * Couples (balise => src) */ function compresseur_extraire_balises_css_dist($flux, $url_base) { $balises = extraire_balises($flux, 'link'); $files = []; foreach ($balises as $s) { if ( extraire_attribut($s, 'rel') === 'stylesheet' and (!($type = extraire_attribut($s, 'type')) or $type == 'text/css') and is_null(extraire_attribut($s, 'name')) # css nommee : pas touche and is_null(extraire_attribut($s, 'id')) # idem and !strlen(strip_tags($s)) and $src = preg_replace(",^$url_base,", _DIR_RACINE, extraire_attribut($s, 'href')) ) { $files[$s] = $src; } } return $files; } /** * Extraire les balises JS à compacter * * @param string $flux * Contenu HTML dont on extrait les balises CSS * @param string $url_base * @return array * Couples (balise => src) */ function compresseur_extraire_balises_js_dist($flux, $url_base) { $balises = extraire_balises($flux, 'script'); $files = []; foreach ($balises as $s) { if ( in_array(extraire_attribut($s, 'type'), [null, 'text/javascript'], true) and is_null(extraire_attribut($s, 'id')) # script avec un id : pas touche and $src = extraire_attribut($s, 'src') and !strlen(strip_tags($s)) ) { $files[$s] = $src; } } return $files; } /** * Compacter (concaténer+minifier) les fichiers format CSS ou JS * du head. * * Repérer fichiers statiques vs. url squelettes * Compacte le tout dans un fichier statique posé dans local/ * * @param string $flux * Contenu du <head> de la page html * @param string $format * css ou js * @return string * Contenu compressé du <head> de la page html */ function compacte_head_files($flux, $format) { $url_base = url_de_base(); $url_page = substr(generer_url_public('A'), 0, -1); $dir = preg_quote($url_page, ',') . '|' . preg_quote(preg_replace(",^$url_base,", _DIR_RACINE, $url_page), ','); if (!$extraire_balises = charger_fonction("compresseur_extraire_balises_$format", '', true)) { return $flux; } $files = []; $flux_nocomment = preg_replace(',<!--.*-->,Uims', '', $flux); foreach ($extraire_balises($flux_nocomment, $url_base) as $s => $src) { if ( preg_match(',^(' . $dir . ')(.*)$,', $src, $r) or ( // ou si c'est un fichier $src = preg_replace(',^' . preg_quote(url_de_base(), ',') . ',', '', $src) // enlever un timestamp eventuel derriere un nom de fichier statique and $src2 = preg_replace(",[.]{$format}[?].+$,", ".$format", $src) // verifier qu'il n'y a pas de ../ ni / au debut (securite) and !preg_match(',(^/|\.\.),', substr($src, strlen(_DIR_RACINE))) // et si il est lisible and @is_readable($src2) ) ) { if ($r) { $files[$s] = explode('&', str_replace('&', '&', $r[2]), 2); } else { $files[$s] = $src; } } } $callbacks = ['each_min' => 'callback_minifier_' . $format . '_file']; if ($format == 'css') { $callbacks['each_pre'] = 'compresseur_callback_prepare_css'; $callbacks['all_min'] = 'css_regroup_atimport'; // ce n'est pas une callback, mais en injectant l'url de base ici // on differencie les caches quand l'url de base change // puisque la css compresse inclue l'url courante du site (en url absolue) // on exclue le protocole car la compression se fait en url relative au protocole $callbacks[] = protocole_implicite($url_base); // et l'URL des ressources statiques si configuree if (isset($GLOBALS['meta']['url_statique_ressources']) and $GLOBALS['meta']['url_statique_ressources']) { $callbacks[] = protocole_implicite($GLOBALS['meta']['url_statique_ressources']); } } include_spip('inc/compresseur_concatener'); include_spip('inc/compresseur_minifier'); if ( [$src, $comms] = concatener_fichiers($files, $format, $callbacks) and $src ) { $compacte_ecrire_balise = charger_fonction("compresseur_ecrire_balise_$format", ''); $files = array_keys($files); // retrouver la position du premier fichier compacte $pos = strpos($flux, (string) reset($files)); // supprimer tous les fichiers compactes du flux $flux = str_replace($files, '', $flux); // inserer la balise (deleguer a la fonction, en lui donnant le necessaire) $flux = $compacte_ecrire_balise($flux, $pos, $src, $comms); } return $flux; } /** * Lister les fonctions de préparation des feuilles css * avant minification * * @return array * Liste des fonctions à appliquer sur les feuilles CSS */ function compresseur_liste_fonctions_prepare_css() { static $fonctions = null; if (is_null($fonctions)) { $fonctions = ['css_resolve_atimport', 'urls_absolues_css', 'css_url_statique_ressources']; // les fonctions de preparation aux CSS peuvent etre personalisees // via la globale $compresseur_filtres_css sous forme de tableau de fonctions ordonnees if (isset($GLOBALS['compresseur_filtres_css']) and is_array($GLOBALS['compresseur_filtres_css'])) { $fonctions = $GLOBALS['compresseur_filtres_css'] + $fonctions; } } return $fonctions; } /** * Préparer un fichier CSS avant sa minification * * @param string $css * @param bool|string $is_inline * @param string $fonctions * @return bool|int|null|string */ function &compresseur_callback_prepare_css(&$css, $is_inline = false, $fonctions = null) { $contenu = []; if ($is_inline) { return compresseur_callback_prepare_css_inline($css, $is_inline); } if (!preg_match(',\.css$,i', $css, $r)) { return $css; } $url_absolue_css = url_absolue($css); // retirer le protocole de $url_absolue_css $url_absolue_css_implicite = protocole_implicite($url_absolue_css); if (!$fonctions) { $fonctions = compresseur_liste_fonctions_prepare_css(); } elseif (is_string($fonctions)) { $fonctions = [$fonctions]; } $sign = implode(',', $fonctions); $sign = substr(md5("$url_absolue_css_implicite-$sign"), 0, 8); $file = basename($css, '.css'); $file = sous_repertoire(_DIR_VAR, 'cache-css') . preg_replace(',^(.*?)(_rtl|_ltr)?$,', "\\1-f-" . $sign . "\\2", $file) . '.css'; if ( (@filemtime($file) > @filemtime($css)) and (!defined('_VAR_MODE') or _VAR_MODE != 'recalcul') ) { return $file; } if ($url_absolue_css == $css) { if ( strncmp($GLOBALS['meta']['adresse_site'] . '/', $css, $l = strlen($GLOBALS['meta']['adresse_site'] . '/')) != 0 or !lire_fichier(_DIR_RACINE . substr($css, $l), $contenu) ) { include_spip('inc/distant'); $contenu = recuperer_url($css); $contenu = $contenu['page'] ?? ''; if (!$contenu) { return $css; } } } elseif (!lire_fichier($css, $contenu)) { return $css; } $contenu = compresseur_callback_prepare_css_inline($contenu, $url_absolue_css_implicite, $css, $fonctions); // ecrire la css if (ecrire_fichier_calcule_si_modifie($file, $contenu) === false) { return $css; } return $file; } /** * Préparer du contenu CSS inline avant minification * * @param string $contenu * contenu de la CSS * @param string $url_base * url de la CSS ou de la page si c'est un style inline * @param string $filename * nom du fichier de la CSS (ou vide si c'est un style inline) * @param array $fonctions * liste des fonctions appliquees a la CSS * @return string */ function &compresseur_callback_prepare_css_inline(&$contenu, $url_base, $filename = '', $fonctions = null) { if (!$fonctions) { $fonctions = compresseur_liste_fonctions_prepare_css(); } elseif (is_string($fonctions)) { $fonctions = [$fonctions]; } // retirer le protocole de $url_base $url_base = protocole_implicite(url_absolue($url_base)); foreach ($fonctions as $f) { if (!function_exists($f)) { $f = chercher_filtre($f); } if ($f and function_exists($f)) { $contenu = $f($contenu, $url_base, $filename); } } return $contenu; } /** * Resoudre et inliner les @import * ceux-ci ne peuvent etre presents qu'en debut de CSS et on ne veut pas changer l'ordre des directives * * @param string $contenu * @param string $url_base * @param string $filename * @return string */ function css_resolve_atimport($contenu, $url_base, $filename) { // vite si rien a faire if (strpos($contenu, '@import') === false) { return $contenu; } $imports_non_resolvables = []; preg_match_all(',@import ([^;]*);,UmsS', $contenu, $matches, PREG_SET_ORDER); if ($matches and count($matches)) { foreach ($matches as $m) { $url = $media = $erreur = ''; if (preg_match(",^\s*url\s*\(\s*['\"]?([^'\"]*)['\"]?\s*\),Ums", $m[1], $r)) { $url = $r[1]; $media = trim(substr($m[1], strlen($r[0]))); } elseif (preg_match(",^\s*['\"]([^'\"]+)['\"],Ums", $m[1], $r)) { $url = $r[1]; $media = trim(substr($m[1], strlen($r[0]))); } if (!$url) { $erreur = 'Compresseur : <tt>' . $m[0] . ";</tt> non resolu dans <tt>$url_base</tt>"; } else { $url = suivre_lien($url_base, $url); // url relative ? $root = protocole_implicite($GLOBALS['meta']['adresse_site'] . '/'); if (strncmp($url, $root, strlen($root)) == 0) { $url = _DIR_RACINE . substr($url, strlen($root)); } else { // si l'url a un protocole http(s):// on ne considère qu'on ne peut pas // résoudre le stockage. Par exemple // @import url(https://fonts.googleapis.com/css?family=Ubuntu); // retournant un contenu différent en fonction navigateur // tous les @import restant seront remontes en tete de CSS en fin de concatenation if (preg_match(',^https?://,', $url)) { $url = ''; } else { // protocole implicite // $url = "http:$url"; } } if ($url) { // on renvoit dans la boucle pour que le fichier inclus // soit aussi processe (@import, url absolue etc...) $css = compresseur_callback_prepare_css($url); if ( $css == $url or !lire_fichier($css, $contenu_imported) ) { $erreur = "Compresseur : url $url de <tt>" . $m[0] . ";</tt> non resolu dans <tt>$url_base</tt>"; } else { if ($media) { $contenu_imported = "@media $media{\n$contenu_imported\n}\n"; } $contenu = str_replace($m[0], $contenu_imported, $contenu); } } } if ($erreur) { $contenu = str_replace($m[0], '/* erreur @ import ' . $m[1] . '*/', $contenu); erreur_squelette($erreur); } } } return $contenu; } /** * Regrouper les @import restants dans la CSS concatenee en debut de celle-ci * * @param string $nom_tmp * @param string $nom * @return bool|string */ function css_regroup_atimport($nom_tmp, $nom) { $contenu = null; lire_fichier($nom_tmp, $contenu); if (!$contenu or strpos($contenu, '@import') === false) { return false; } // rien a faire preg_match_all(',@import ([^;]*);,UmsS', $contenu, $matches, PREG_SET_ORDER); $imports = array_column($matches, 0); $contenu = str_replace($imports, '', $contenu); $contenu = implode("\n", $imports) . "\n" . $contenu; ecrire_fichier_calcule_si_modifie($nom, $contenu); return $nom; } /** * Remplacer l'URL du site par une url de ressource genre static.example.org * qui evite les echanges de cookie pour les ressources images * (peut aussi etre l'URL d'un CDN ou autre provider de ressources statiques) * * @param string $contenu * @param string $url_base * @param string $filename * @return mixed */ function css_url_statique_ressources($contenu, $url_base, $filename) { if ( isset($GLOBALS['meta']['url_statique_ressources']) and $url_statique = $GLOBALS['meta']['url_statique_ressources'] ) { $url_statique = rtrim(protocole_implicite($url_statique), '/') . '/'; $url_site = rtrim(protocole_implicite($GLOBALS['meta']['adresse_site']), '/') . '/'; $contenu = str_replace($url_site, $url_statique, $contenu); } return $contenu; }