Je m’occupe actuellement du développement du site myBonjour.fr qui permet le recensement et l’affichage de tous les blogs du type Bonjour Madame et Bonjour Poney.
Pour optimiser la vitesse de chargement des pages, je me suis intéressé à la technique aujourd’hui très populaire des Sprites CSS.
Un Sprite, qu’est-ce que c’est ? C’est une grande image qui contient plusieurs petites images utilisées au sein de votre application web, via les feuilles de style. En utilisant la propriété background CSS, il est assez facile de dire quelle partie du sprite doit être affichée en image de fond.
L’avantage de regrouper plusieurs petites images au sein d’un sprite est double. D’une part, l’image ainsi formée est souvent moins lourde que l’ensemble des petites images additionnées et, d’autre part, le nombre de requête HTTP pour charger les images est réduit à un. C’est ce dernier point qui est le plus important et le plus significatif en terme de gain de performances.
Voici un exemple de sprite :
De nombreuses applications Web apportent une aide intéressante pour la création de Sprite à partir d’un site existant non optimisé.
J’ai retenu tout particulièrement le site http://spriteme.org qui offre un bouton à glisser dans sa barre de favoris et qui ouvre une popup en Javascript qui permet de créer un sprite à la volée et même de générer le CSS correspondant à partir du CSS existant.
Maintenant, comment générer un Sprite soit même, directement dans son code symfony ? Vous allez voir ci-dessous le code que j’ai utilisé pour myBonjour.fr.
Pour cela, j’ai ajouté dans un premier temps une méthode privée dans mon modèle, s’occupant de générer le Sprite en question :
private function makeSpriteForFavicons()
{
$items = Doctrine_Core::getTable('MyBonjourItem')->getAllActiveItems();
$width = 25;
$height = 16;
$space = 14;
// On crée l'image vide
$image = imagecreatetruecolor($width, ($height+$space) * count($items));
$background = imagecolorallocatealpha($image, 255, 255, 255, 127);
imagefill($image, 0, 0, $background);
imagealphablending($image, false);
imagesavealpha($image, true);
// Ajout de tous les favicons à l'image
$pos = 7;
foreach($items as $item)
{
$tmp = imagecreatefrompng($item->getItemFilePath());
imagecopy($image, $tmp, 0, $pos, 0, 0, $width, $height);
$pos += $height + $space;
imagedestroy($tmp);
}
imagepng($image, sfConfig::get('sf_web_dir').'/images/items_icons.png');
}
Il suffit ensuite d’appeler cette méthode, quand la colonne correspondant à l’image est modifiée. Pour cela, on implémente la méthode save du modèle :
public function save(Doctrine_Connection $conn = null)
{
if(in_array('favicon_url', array_keys($this->getModified())))
{
$return = parent::save($conn);
$this->makeSpriteForFavicons();
}
else
$return = parent::save($conn);
return $return;
}
Et enfin, on génère la partie correspondante dans le fichier CSS, à la volée :
// makeCSSSuccess.php
<?php $pos = 0; foreach($items as $item): ?>
div#<?php echo $item->getSlug() ?> > h3{
background: url("/images/items_icons.png") no-repeat scroll 5px -<?php echo $pos*$width ?>px;
}
<?php $pos++; endforeach; ?>
Le fichier CSS généré par une action symfony doit être mis en cache pour éviter toute chute des performances. Il suffit de déclencher ensuite une regénération du cache lorsqu’un item est ajouté en base.
Sur myBonjour, j’ai ainsi supprimé le chargement de 60 requêtes HTTP, correspondant aux différents bonjours présents dans la liste.