mercredi 28 mars 2007, 18:23
Une version optimisée pour PHP de JSMin
Les Javascripts permettent de faire tout pleins de choses sur un site web, c'est cool. Mais à force d'empiler les librairies, les frameworks et autres fonctions, on peut vite se retrouver avec deux kilo-tonnes de script, ce qui n'est ni très agréable à charger et ni très léger pour la bande passante.
Heureusement, plusieurs librairies (plus ou mois efficaces) existent et permettent de compresser les scripts. JSMin est l'une d'entre elle.
A l'origine, JSMin est un petit programme écrit en C par Douglas Crokford. Il prend un fichier Javascript et supprime tout ce qui n'est strictement nécessaire, c'est-à-dire :
- les commentaires (
/* */
et//
), - les lignes vides et retour à la ligne,
- les espaces (sauf dans les chaines de caractères et les expressions régulières).
Contrairement à d'autres packer, JSMin ne brouille pas le code, c'est-à-dire que le fichier compressé reste lisible, débuggable et modifiable (même si une petite ré-indentation est nécessaire), ce qui est quand même un avantage très appréciable.
Il existe une version PHP de JSMin, mais, comme son auteur l'indique, il s'agit d'une « conversion simple et brutale du C vers le PHP », entendez par là qu'elle n'est pas du tout optimisée et donc super lente. Evidemment, la version C est théoriquement utilisable en PHP via la fonction shell_exec
, mais c'est totalement impensable sur un hébergement mutualisé.
Récemment, j'ai dû mettre en place un système de compression et de concaténation des javascripts « à la volée » ; c'est-à-dire que les javascripts sont compressés la première fois qu'un utilisateur les demande, puis mis en cache (vaguement inspiré de ce post). Dès qu'un fichier est mis à jour, le cache est regénéré. Ainsi, je conserve les versions commentées et indentées pour développer, et je n'ai pas à me soucier de les compresser avant de les mettre en ligne. C'est magique.
Mais, la version originale de JSMin en PHP met parfois plus de 3 secondes à compresser certains fichiers, ce qui est bien trop long. Donc je n'ai pas pû m'empêcher de faire un tour dans le code pour l'optimiser. J'ai remplacé toutes les boucles de lecture caractère par caractère (typique du C) par des fonctions natives PHP comme strpos
et strlen
.
J'ai également viré toute la partie qui concernait les fichiers (dans la version originale, il est possible de passer un nom de fichier à JSMin pour qu'il le lise directement), parceque en PHP c'est beaucoup plus rapide de lire d'abord le fichier (par une fonction comme file_get_contents
) et de travailler sur la chaine de caractère représentant le contenu.
Le gain en vitesse dépend fortement de la taille et du contenu du fichier ; dans mon cas c'est entre 10% et 50% plus rapide.
Utilisation
Le constructeur prend 2 paramètres : le code à compresser (string
) et, si besoin, un tableau de commentaires à ajouter en début de fichier (array
ou rien). Ensuite il suffit d'appeller la fonction minify()
.
Exemple :
$code = file_get_contents('foo.js'); $compressor = new MyJsMin($code, array('Hello', 'World')); echo $compressor->minify();
Le fichier foo.js
contient :
function foo(bar) { bar += 1; alert('hello world !'); }
Le résultat sera :
// hello // world function foo(bar) {bar+=1;alert('hello world !');}
Sources
- Sources de MyJsMin.
- Pour JSMin en PHP, voir le blog de l'auteur.
- La page de la version originale de JSMin en C (avec des liens vers d'autres langages)
Test en ligne
Vous pouvez tester les performances du script grace à cette page (en anglais, parceque c'est la classe internationnale).
Par cgo2, dans Développement web
Commentaires
J'ai essayé ta version (j'utilisais l'original en php avant de découvrir la tienne), elle ne me renvoie rien. Un pb mineur probablement.
Ah tiens, c'est étonnant ça. Pourtant sur la page de test elle fonctionne niquel... Es-tu sûr de l'initialiser correctement ? Comme je l'ai expliqué, j'ai viré le code qui s'occupe des fichiers, donc si tu lui passes un nom de fichier elle ne fera rien.
Oui, ta page de test fonctionne.
Voilà comment je l'utilise : je l'inclus avec un include en début de page, puis j'utilise ton exemple en <head> :
<?php
$code = file_get_contents('js/prototype.js');
$comp = new JSMin($code);
echo $comp->minify();
?>
Le fichier js est lu puis le serveur s'arrête de renvoyer après la première ligne.
Comme dit plus haut, si j'inclus l'original, le js est renvoyé correctement. Je ne comprend pas non plus. Peut-être un pb de charset.
Je vais consulter le fichier de log de php.
Ma classe s'appelle MyJsMin ! Je corrige l'article.
Effectivement, ca va beaucoup mieux avec le bon nom :). J'aurai pu regarder de plus près. Corrige aussi le bout de code dans le fichier lui-même au alentours de la ligne 74.
Testé sur Apache 2.0.55/PHP 5.1.6 et Apache 2.0.59/PHP 5.2.1.
Merci pour ton code et bonne continuation.
Bye,
TMTisFree
En essayant de généraliser sur toutes les librairies js que j'utilise, certaines refuse de passer à la moulinette : prototype.js et overlib.js en particulier. Une idée ?
Rha ! C'est à cause des strings sur plusieurs lignes... Vu que je n'utilise jamais cette syntaxe pour mes JS je n'avais pas vu ce bug. Merci pour l'info, je vais voir s'il est possible de le corriger proprement.
Voila, j'ai mis en ligne une nouvelle version qui fonctionne avec prototype. Malheureusement, le gain en performance est moins flagrant (entre 5% et 20% ; dans certains cas c'est même moins rapide, argh !).
Merci, je vais l'essayer.
J'ai optimisé mes sites hier soir (sans jsmin pour l'instant donc !), résultat : temps de chargement divisé par 2. Je vais faire quelques tests avec ta nouvelles version.
Quid des commentaires conditionnels de IE ?
/*@cc_on
if ( @_jscript_version == 5.6 )
document.execCommand("BackgroundImageCache", false, true);
@*/
La version originale ne les supporte pas. Mais il y a des méthodes plus "propres" pour mettre du code spécifique à IE...