Les fetch modes de PDO 2 : les modes orientés objet
Suite de ma série consacrée aux modes de récupération de PDO, voici les modes qui permettent de travailler avec des objets.
Les fetch modes de PDO
PDO propose une quantité assez impressionante de “modes de récupération” (fetch mode) des données. Entendez par là qu’il est possible de personnaliser le comportement des méthodes fetch et fetchAll pour obtenir les résultats des requêtes SQL sous des formes très diverses. Si certains modes sont relativement classiques et ont des équivalents avec les précédents drivers MySQL (mysql_fetch_array, mysql_fetch_row, etc.), d’autres sont totalement inédits… mais très mal documentés ! Et pourtant, certains sont *très* pratiques… J’ai donc décidé d’écrire une série d’articles présentant tous les modes de fetch, leur fonctionnement et surtout leurs performances.
Requêtes pour faire du CSV avec MySQL
Voici une petite requête pour MySQL ≥ 5 qui peut être utile si vous cherchez à obtenir la liste des champs d’une table, pour l’exporter en tant qu’entête d’un fichier CSV par exemple.
select group_concat(column_name SEPARATOR ";") from information_schema.columns where table_schema = 'DB_NAME' and table_name = 'TABLE_NAME' group by table_name;
La requête suivant permet d’exporter une table complète en CSV, sans passer par un langage de script (à condition d’avoir la permission FILE).
select * into outfile '/tmp/foo.csv' fields terminated by ';' optionally enclosed by '"' lines terminated by '\n' from DB_NAME.TABLE_NAME;
Plus d’infos dans la doc de MySQL.
Afficher un hash dans l’ordre en Ruby
Ruby, contrairement à PHP (mais comme beaucoup d’autres langages), fait la différence entre un tableau (avec des index numériques) et un hash (avec n’importe quel type d’index, mais sans notion d’ordre). Etant habitué à PHP, je me suis heurté au problème suivant : comment afficher un hash dans l’ordre ?
Considerons le code suivant, où le hash contient les éléments indexés par leur id numérique (je n’utilise pas un tableau volontairement, car j’ai besoin de ces id).
hash = {
1 => "toto",
2 => "tata",
3 => "titi",
4 => "tutu",
5 => "tete",
6 => "tyty"
}
hash.each_pair do |key, value|
puts "#{key} = #{value}"
end
Il va afficher :
5 = tete 6 = tyty 1 = toto 2 = tata 3 = titi 4 = tutu
Ce résultat correspond à la documentation de la classe Hash :
A Hash is a collection of key-value pairs. It is similar to an Array, except that indexing is done via arbitrary keys of any object type, not an integer index. The order in which you traverse a hash by either key or value may seem arbitrary, and will generally not be in the insertion order.
Bref, c’est tout à fait normal, mais pas du tout ce que je recherche. Pour l’afficher dans l’ordre, il suffit en fait de trier et parcourir les clefs du hash :
hash.keys.sort!.each do |key|
puts "#{key} = " + hash[key]
end
Et on obtient le bon ordre :
1 = toto 2 = tata 3 = titi 4 = tutu 5 = tete 6 = tyty
Youpi ! Bon, on peut toujours critiquer PHP, mais son concept de tableau/hash mélangé et ordonné, c’est quand même bien pratique :-)
Cannot re-assign $this (en théorie)
Décidemment le modèle objet de PHP n’est pas mon meilleur ami. Après avoir eu la suprise de découvrir que certaines méthodes ne peuvent pas lancer d’exception, voila que $this change mystérieusement de valeur.
Comme le suggère le dollar dans le nom, $this est bien une simple variable. Donc à ce titre elle peut surement être modifiée, non ? Heureusement, la changer par une assignation directe est impossible, et conduira à une erreur fatale. Ainsi, le code suivant affichera l’erreur Cannot re-assign $this.
class ThisIsFun {
function reassignThis()
{
$this = 'something else';
}
}
$obj = new ThisIsFun();
$obj->reassignThis();
Mais certaines fonctions, comme extract peuvent aussi manipuler les variables (je l’utilise pour exporter certaines variables vers la vue dans le cadre d’une architecture MVC). Résultat, avec un code comme ci-après, $this est modifié…
class ThisIsFun {
function reassignThis($vars)
{
var_dump($this); // object(ThisIsFun)[1]
extract($vars);
var_dump($this); // string 'something else' (length=14)
}
}
$obj = new ThisIsFun();
$obj->reassignThis(array('this' => 'something else'));
Heureusement la modification n’est effective que dans le contexte de la fonction, mais quand même, c’est plutôt étonnant que ça soit possible. Pour l’éviter il faudrait utiliser le paramètre optionnel supplémentaire extract_type qui contrôlle la gestion des collisions.
Du coup je me suis amusé un peu à chercher d’autres moyens de modifier $this, même si je ne sais pas encore trop à quoi ça pourrait bien servir… Allez, avec une référence c’est élégant :)
class ThisIsFun {
function reassignThis()
{
var_dump($this); // object(ThisIsFun)[1]
$that = & $this;
$that = 'references are cool';
var_dump($this); // string 'references are cool' (length=19)
}
}
$obj = new ThisIsFun();
$obj->reassignThis();
__toString() must not throw an exception
La bonne blague PHP du jour concerne la méthode “magique” __toString, qui est appellée automatiquement lorsqu’un objet doit être converti en chaine de caractère. Pour je ne sais quelle raison, PHP à partir de la version 5.2 retourne une erreur fatale lorsque une exception est lancée depuis cette méthode…
Ainsi, le code suivant n’affichera jamais “Catched”.
class Foo {
public function __toString() {
throw new Exception('woupsi !');
}
}
try {
echo new Foo();
} catch (Exception $e) {
echo 'Catched';
}
Au lieu de ça, j’ai le droit à :
Fatal error: Method Foo::__toString() must not throw an exception
Je trouve ça vraiment étonnant qu’une méthode particulière ne puisse pas lancer d’exception, d’autant qu’entre une erreur fatale et une exception je ne vois pas vraiment la différence. Si j’étais mauvaise langue, je dirais qu’il s’agit d’un hack placé là parceque l’implémentation des exceptions en PHP 5 et/ou de la méthode __toString est moisie, mais je ne le ferais pas.
Evidemment, ce n’est précisé nulle part dans la documentation. Il y a d’ailleurs un bug report ouvert depuis l’année dernière (sic) demandant une mise à jour de la documentation pour préciser cette feature…
Bon, comme j’ai vraiment besoin d’utiliser __toString et que j’ai aussi besoin de traiter les erreurs, voici le workaround (pas très propre) que je vais utiliser en attendant de trouver mieux :
class Foo {
public function __toString() {
try {
return $this->functionThatMayThrowAnException();
} catch ( Exception $e ) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
}
Utilisation de l’opérateur “or”
Recemment je me suis heurté à quelques soucis avec l’opérateur “or”, notamment lorsqu’il est utilisé en dehors d’une condition. Bien que l’ordre de précédence des opérateurs soit défini dans la documentation de PHP (et commun à beaucoup d’autres langages), il m’arrive encore de me faire surprendre. Aussi, voici une petite clarification.
Un exemple très répandu de l’utilisation de “or” en dehors d’une condition est lorsqu’il est utilisé conjointement avec die() celui-ci :
doSomething() or die();
Si doSomething() retourne “vrai”, alors l’évaluation s’arrette, parceque l’opération “or” retourne toujours “vrai” si au moins un des éléments est “vrai”, donc le moteur optimise en passant les tests inutiles.
Si doSomething() retourne faux, alors l’évaluation continue, et die() est executé (adieu petit script).
Performances des packers javascript
Les librairies et frameworks javascript sont de plus en plus nombreux et de plus en plus complets. Ca, c’est une bonne nouvelle, mais la conséquence logique est que leur taille augmente jusqu’à un point qui commence à devenir problématique. Heureusement, des outils de “compression” tels que JSMin sont apparus ; ils permettent de réduire sensiblement la taille des scripts en supprimant les commentaires, l’indentation, et autres caractères inutiles. Le principe est de conserver une version complète et documentée des scripts pour le développement, puis, au moment de la mise en production, les fichiers passent dans cette moulinette de compression qui les “amaigris”.
Depuis quelques temps, une autre technique de compression emerge. Au lieu de simplement purger le code de tous les caractères inutiles, le code est “empaqueté” (packed) dans une fonction qui contient l’agorithme permettant de décompresser le paquet. Ainsi il est possible d’utiliser des méthodes de compression plus efficaces (changement de base, remplacement de patterns, etc.) et obtenir des fichiers encore plus petits, mais totalement illisibles. Ces solutions ont été très vites adoptées par de nombreux projets et sites web, mais sont-elles vraiment si efficaces ?
Problème de backspace avec screen sous Ubuntu
Si toi aussi tu t’arraches les cheveux depuis une heure parceque la touche d’effacement (backspace) ne fonctionne pas avec screen dans le terminal par défaut de XUbuntu (sobremement intitulé “Terminal”), range ce cable réseau avec lequel tu comptais te pendre, voici la solution : dans le menu “Editer > Préférénces”, aller dans la partie “Avancé” et choisir “La touche Retour arrière émet : Control-H”.
Solution alternative : changer de terminal.
Installer Ubuntu sur un Sony PCH-GRT815E
Derrière ce nom barbare se cache mon ordinateur portable Sony VAIO de quelques années, sur lequel j’ai décidé d’installer Ubuntu GNU/Linux (hé oui, il faut bien s’occuper en vacances).