Ce n'est pas dans mes habitudes d'annoncer la disponibilité d'une nouvelle version d'un logiciel, mais comme celle-ci m'est assez particulière, je me permets d'insister sur la release fraîchement sortie de PhpMyAdmin version 3.3.0.0. Cette version est effectivement une peu spéciale pour moi car c'est la première à intégrer officiellement ma maigre contribution au projet, à savoir une nouvelle fonctionnalité dont j'avais déjà parlé précédemment qui permet d'exporter les données d'une table MySQL sous forme d'un tableau PHP associatif.
J'ai donc eu l'honneur d'être cité dans dans le changelog de la release, dont l'extrait concerné figure ci-dessous. Une précision qui n'a bien entendu d'autre utilité que la postérité ;)
+ patch #2805828 [export] PHP array export plugin, thanks to Geoffray Warnants
Dans un ancien article, je parlais de l'utilisation des sprites en CSS pour afficher des onglets. Il est vrai que l'exemple choisi était assez médiocre, très loin d'illustrer l'étendue des possibilités offertes par cette technique, qui rappelons le, trouve son origine dans le monde des jeux vidéos. A titre d'exemple plus explicite, j'ai ici créé une petite ébauche d'application graphique inspirée du célèbre jeu "Grand Theft Auto", dans laquelle les véhicules et différents états du personnage sont issus de sprites. Cet exemple est écrit très rapidement grâce à la librairie jQuery, le plus fastidieux étant de composer les sprites dans Photoshop ;)
Dans un environnement de développement normalement configuré pour afficher les erreurs de type E_NOTICE mais aussi E_STRICT, on se retrouve parfois confronté à une curieuse alerte, provoquée par une séquence d'instructions qui semble pourtant anodine.
Par exemple, pour extraire l'extension d'un nom de fichier, j'écris souvent de façon machinale :
$extension = end(explode('.', $filename));
Ce qui provoque une alerte assez déroutante :
Pour comprendre ce message, il faut d'abord se rappeler que la fonction end() accepte en réalité une référence de tableau, ce qui est tout à fait compréhensible puisqu'elle va le modifier en positionnant le pointeur interne sur son dernier élément.
Ensuite [1], il faut savoir que lorsqu'une fonction retourne une valeur, une variable temporaire est créée. Généralement, on récupère cette variable temporaire en l'assignant à une autre variable déclarée explicitement, mais dans notre cas, la variable temporaire retournée par la fonction explode() est directement transmise à la fonction end() qui, bien que ce soit effectivement un Array, la refuse et provoque l'alerte.
Ce comportement tout à fait légitime ne date pas d'hier. Il a été introduit en PHP 5.0.5 afin d'éviter des corruptions mémoires. On peut certainement mieux comprendre ce risque en se souvenant de la différence significative entre les pointeurs et les références du C++.
Selon Rasmus Lerdorf, fondateur de PHP, il est recommandé d'effectuer l'opération en deux étapes, en transitant par une variable déclarée.
$split = explode('.', $filename); $extension = end($split);
Il souligne aussi à très juste titre que dans ce cas bien précis qui consiste en une opération fondamentale sur une chaîne de caractères, il est absolument injustifié de passer par un tableau pour résoudre ce problème alors que des fonctions spécifiquement dédiées aux chaînes de caractères suffisent. J'ajouterai même qu'elles seront aussi plus performantes !
$extension = substr(strrchr($filename, '.'), 1);
J'ai cependant découvert que la fonction current() ne provoque pas d'erreur avec une variable temporaire alors que son prototype signale aussi un passage par référence. On peut donc supposer, en sachant que current() ne modifie pas le pointeur interne du tableau, que le message d'erreur généré par PHP n'est pas tout à fait pertinent. Il devrait plutôt mentionner "Only variables should be updated by reference". Rasmus, si tu pouvais confirmer... ;)
Sachant cela, les fanatiques qui voudraient à tout prix s'en tirer avec une seule ligne de code peuvent toujours le faire, au prix d'une opération certainement assez coûteuse :
$extension = current(array_reverse(explode('.', $filename)));
Les problèmes de casting (transtypage) sont souvent considérés comme le propre des langages à typage fort tels que par exemple Java, C++ ou C#. L'upcasting (communément traduit en français par "transtypage vers le haut" ou encore "surclassement") consiste à transformer une classe dérivée en une classe dont elle hérite, lui faisant ainsi volontairement perdre de sa spécificité. C'est cette notion d'ascendance qui lui vaut le nom de casting vers le haut, par opposition au downcasting.
Bien que PHP ne puisse pas réaliser cette transformation nativement, voyons comme faire pour y remédier en imaginant l'exemple suivant :
class Person { } class Child extends Person { }
Ainsi, avec une hiérarchie similaire écrite en Java, convertir une instance de Child en un objet de type Person aurait pu se faire tout naturellement.
Child c = new Child(); Person m = c;
Ce n'est malheureusement pas pareil en PHP, puisque comme l'indique la documentation sur le type casting, seule la conversion vers les types natifs est possible. La tentative suivante se solderait donc par une erreur de syntaxe.
$c = new Child(); $m = (Person)$c;
Pour remédier à ce problème, je m'inspire ici du concept de "constructeur de copie" qu'on retrouve en C++ et qui a pour but de réaliser une copie d'un objet via le constructeur de la classe. Bien souvent, la copie consiste tout bêtement en une initialisation dite membre à membre. On peut donc ajouter à la classe mère un constructeur jouant ce rôle :
class Person { /** * Constructeur de copie */ public function __construct(Person $c=null) { if ($c !== null) { foreach (get_object_vars($c) as $property => $value) { if (!is_object($value)) { $this->$property = $value; } else { $this->$property = clone $value; } } } } }
On peut alors simuler l'upcasting :
$c = new Child(); $m = new Person($c);
Un tel besoin peut paraître déroutant, puisque en réalité, une instance de Child est déjà par héritage une instance de Person, à laquelle des spécificités sont apportées. Néanmoins, dans certains cas, il peut s'avérer utile de retirer les atouts d'une instance dérivée pour n'en retrouver que les comportement et propriétés héritées. Par exemple, une méthode qui accepte un objet en paramètre pourrait interdire la réception d'une classe dérivée dont les caractéristiques seraient jugées inopportunes.
class Person { public function haveSex(Person $p) { if ($p instanceof Child) { throw new Exception('Tu devrais pas être au lit toi ?'); } // n'golo golo ! } }
Finalement, voici comment conclure par un malheureux inceste programmatique ;)
$serge = new Person(); $charlotte = new Child(); $serge->haveSex(new Person($charlotte));
Dans la continuité du précedent billet, voici un clône du mythique "Pong" adapté en Javascript avec jQuery.