*/
function compresseur_ecrire_balise_js_dist(&$flux, $pos, $src, $comments = "") {
$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 .= "";
} else {
$comments .= "";
}
} else {
$comments .= "";
}
$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
*/
function compresseur_ecrire_balise_css_dist(&$flux, $pos, $src, $comments = "", $media = "") {
$src = timestamp($src);
$comments .= "";
// Envoyer aussi un entete http pour demarer le chargement de la CSS plus tot
// Link: ;rel="stylesheet prefetch"
$comments .= "<" . "?php header('Link: <' . url_de_base() . (_DIR_RACINE ? _DIR_RESTREINT_ABS : '') . '$src>;rel=\"stylesheet prefetch\"'); ?>";
$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 = array();
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 = array();
foreach ($balises as $s) {
if (extraire_attribut($s, 'type') === 'text/javascript'
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 de la page html
* @param string $format
* css ou js
* @return string
* Contenu compressé du 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 = array();
$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 = array('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 (list($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, 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 = array('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) {
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 = array($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');
if (!$contenu = recuperer_page($css)) {
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($file, $contenu)) {
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 = array($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 = array();
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 : " . $m[0] . "; non resolu dans $url_base";
} 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 " . $m[0] . "; non resolu dans $url_base";
} 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) {
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_map("reset", $matches);
$contenu = str_replace($imports, "", $contenu);
$contenu = implode("\n", $imports) . "\n" . $contenu;
ecrire_fichier($nom, $contenu, true);
// ecrire une version .gz pour content-negociation par apache, cf. [11539]
ecrire_fichier("$nom.gz", $contenu, true);
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;
}