PHP
Maitrisez la taille de vos images
25/01/10
Voici une solution générique au problème souvent rencontré de la gestion de la taille des images. En effet, et comme vous le savez, redimensionner un objet <img> a tendance à faire charger à l’utilisateur une image beaucoup trop grosse pour ce qu’il en verra, de plus les navigateurs n’ont pas toujours de modules d’anti-aliasing. La solution bien connue est de générer, coté serveur, des images de la bonne dimension (qu’il s’agisse de miniature ou pas).
J’ai écrit cette classe pour essayer de me débarasser de ce problème ennuyeux, voici comment elle fonctionne.
Chargement d’une image
Tout d’abord, il faut instancier l’image, puis lui dire quel fichier charger
$i = new Image;
$i->fromFile("image.jpg");
Le paramètre peut aussi être passé directement dans le constructeur, afin de gagner de la place:
$i = new Image("image.jpg");
Appliquer une redimension
Il existe ensuite plusieurs façon de redimensionner l’image, voici des exemples pour mieux comprendre.

$i->;resize(300,200), réduit éventuellement l'image mais ne l'agrandit jamais (pratique pour les miniatures)

$i->scaleResize(300,200), redimensionne l'image en gardant le ratio de maniere a la faire tenir dans la place attribuée

$i->forceResize(300,200), force l'image a faire exactement la taille donnée, sans que le ratio ne compte (peut déformer)

$i->cropResize(150,100), fonctionne comme resize mais coupe l'espace blanc autour de l'image (revient à limiter la largeur et la hauteur)
Chaîne d’opération
Notons que tout l’interêt du code que je présente ici est que les opérations ne sont pas appliquées. En effet apres une ouverture (fromFile) et une redimension (resize…) la classe n’a strictement rien fait d’autre que d’enregistrer tout ce qui lui est demandé. Il est alors possible de la forcer à effectuer les opérations et à sauvegarder l’image avec la fonction save() (voir plus bas). Mais l’option la plus interessante est de lui demander d’utiliser son système de cache de manière à tout gérer automatiquement.
En appellant la méthode cacheFile(), la classe commencera à calculer un hash de tout ce qui lui est demandé (les opérations, le fichier d’origine, le format demandé etc.) et générera alors un nom de fichier de cache. Si ce fichier existe, elle ne fera strictement rien d’autre que de retourner le nom de ce fichier. Si il n’existe pas, elle appellera save(), la fonction permettant de générer l’image en la sortant dans le fichier de cache.
Les fonctions jpeg(), gif() et png() sont des raccourcis de cacheFile().
Exemples et raccourcis
Voici un exemple d’execution détaillé:
//Créé l'instance
$i = new Image;
//Charge l'image "image.jpg"
$i->fromFile("image.jpg");
//Utilise une redimension non-agrandissante dans une boite 300x200
$i->resize(300, 200);
//Applique les modifications ou alors récupere le cache correspondant
// $file contiendra le nom du fichier cache obtenu
$file = $i->jpeg();
Et la version raccourcie, grace à la définition de la fonction Image() et des return $this; Imaginez que vous écriviez une gallerie photo dans laquelle vous souhaitez mettre des miniatures en 120×120:
<img src="<? echo Image($file)->resize(120,120)->jpeg(); ?>" alt="" />
L’interêt est de mêler étroitement la simplicité d’écriture et les performances. Lorsque le fichier de cache existe déja, l’utilisation de la classe Image ne nécessite que le calcul d’un hash et quelques vérifications. Une fois le dossier de cache correctement configuré, cette ligne suffira a maitriser le rendu des images.
Autre utilisation, rendu dans un fichier
Nous voudrons parfois pouvoir utiliser ce système non pas à l’affichage, mais à un instant précis du code. Pour cela vous pouvez utiliser la fonction save($fichier, $format= »jpg », $qualite=80).
//Ouverture de l'image $i = new Image($file_name); //Utilise une redimension non-agrandissante dans une boite 120x120 $i->resize(120, 120); //Sauvegarde le résultat dans le répertoire des miniatures (thumb) $i->save($thumb_dir.$file_name, "jpg");
Pour conclure voici les sources de la classe en question:
Voir le code source de la classe Image
Fonctionnalités obscures de PHP
21/01/10
Un petit article pour parler des fonctionalités « obscures » de PHP. J’ai trouvé en effet mal documenté certains features qui me semblent intéréssant du coté des interfaces. Je vous laisse regarder la doc si vous n’avez pas envie de lire l’article en cliquant ici.
Des pseudos-tableaux
Il est en effet possible d’utiliser la syntaxe suivante (utiliser des objets comme des arrays):
$m = new Machin; $m["test"] = 12
Ca peut parraitre bizarre, mais c’est parfois pratique. Voici une petite description de l’utilisation de ce feature:
class Machin implements ArrayAccess {
//Est-ce que isset($obj[$var] ?
public function offsetExists($var) {
return true;
}
//Quelle est la valeur de $obj[$var] ?
public function offsetGet($var) {
return 42;
}
//Définition de la valeur $obj[$var]
public function offsetSet($var, $val) {
echo "$var = $val!\n";
}
//Appel à unset($var[$obj])
public function offsetUnset($var) {
echo "removing $var\n";
}
}
$m = new Machin;
if (isset($m["toto"])) { //true
echo $m["toto"]."\n"; //42
$m["truc"] = "bidule"; //truc = bidule!
unset($m["blork"]); //removing blork
}
Il est par exemple plus facile de créer un système de session customizées, enfin, laissez votre imagination tourner je pense que vous aurez bien plus d’idées que moi, sachant que chaque acces au tableau provoque un appel de fonction et peut donc être dynamique (pourquoi pas requêter un SGBD). Il est aussi possible de « mapper » un serveur memcache par exemple, créer une classe qui permettra d’utiliser le serveur de cache comme un simple tableau.
Des objets itérables
Vous qui utilisez des templates php à tour de bras vous devez apprécier fortement l’instruction foreach. Je vous laisse donc découvrir ce que vous pouvez faire en implémentant Iterator:
- function rewind() : retourne à la premiere position de l’itération
- function current() : retourne l’élément courant
- function key() : retourne la clé courante
- function next() : passe à l’élément suivant
- function valide() : retourne vrai si l’élément est valide (permet d’arrêter l’itération)
Il est alors possible de faire:
$trucs = new Trucs;
foreach ($trucs as $t) {
//Faire quelque chose avec $t
}
Ce qui peut s’avérer pratique lorsque la sémantique de votre objet est justement d’être un containers. Pour plus de détails je vous laisse consulter la documentation.
Appels de méthodes enchaînés
Ce n’est pas vraiment quelque chose d’obscur ou de caché mais plus une astuce. Lorsque vous souhaitez appeller plusieurs fonctions sur le même objet, et que ces fonctions ne retournent rien, rien ne coute d’ajouter un return $this; à la fin de la fonction en question, ce qui vous permet d’enchainer les appels sur la même ligne. Un petit exemple:
class Query {
private $sql;
public function __construct() {
$this->sql = "SELECT * FROM ";
}
public function from($table) {
$this->sql.= "`$table` ";
return $this;
}
public function where($where) {
$this->sql.= "WHERE $where ";
return $this;
}
public function exec() {
echo "Requete: ".$this->sql."\n";
}
}
$q = new Query;
$q->from("users")->where("id=12")->exec();
//Requete: SELECT * FROM `users` WHERE id=12
L’exemple est très caricatural, mais on voit vite les avantages d’un tels procedé. Cette syntaxe est largement utilsiée par certaines libs comme Doctrine.
Les fonctions magiques
Chaque classes définit ses propres méthodes, mais peut aussi définir des méthodes dynamiquement. Lorsqu’une méthode n’existe pas, php appelle la fonction __call() si elle existe. un petit exemple:
class User {
public function __call($func, $params) {
if (substr($func, 0, 2) == "by") {
$name = trim(strtolower(substr($func, 2)));
$param = $params[0];
echo "SELECT * FROM users WHERE `$name`=\"".$params[0]."\"";
}
}
}
$u = new User;
$u->byName("toto");
//SELECT * FROM users WHERE `name`="toto"
Encore très simplifié, mais je pense que le concept transparait bien, les fonctions peuvent être parfois plus parlantes que des noms d’arguments.
A noter qu’il existe d’autres fonctions magiques, comme celles permettant d’acceder à des pseudos-propriétés ($u->name par exemple) de manière dynamique (avec un appel de fonction). Là aussi, jetez un oeil au manuel.
Le mécanisme de réflexion
Un autre aspect sur lequele je ne vais pas m’éterniser, le mécanisme de réflexion. Comme tous les langages de haut niveau, PHP peut s’analyser lui même, il est ainsi possible de savoir dynamiquement si une fonction existe, si une classe implémente telle ou telle interface, dans quel fichier elle est définie ou encore le noms et les valeurs des propriétés statiques.
Voila je m’arrête ici, j’éspère vous avoir donné quelques idées pour vos projets. La souplesse du langage permet de faire beaucoup de choses assez sympas qui rendent le code (celui qui utilise vos classes bizarres!) plus lisible et facile à maintenir, donc à vos claviers.

