N’utilisez pas isset pour vérifier l’existence d’une clef !

La bonne blague PHP du jour concerne la fonction isset qui permet, comme son nom l’indique, de tester si une variable est affectée. Si vous êtes comme moi, vous avez pris l’habitude de l’utiliser pour vérifier qu’une certaine clef existe dans un tableau avant d’y accéder, afin d’éviter le traditionnel message “Notice: Undefined index”.

Seulement voila, isset retourne false si la variable contient null… Exemple :

$array = array(
	'toto' => null
);
var_dump(isset($array['toto'])); // boolean false

Résultat : on n’obtient pas du tout le résultat attendu, puisque selon PHP la clef n’est pas définie alors que selon le programmeur (moi, en l’occurence) l’index toto existe bien ! D’après la documentation, il faut donc utiliser array_key_exists pour tester qu’une clef existe…

Bon, que PHP considère qu’une variable définie mais de valeur null et qu’une variable non-définie soit la même chose, c’est déjà un peu curieux, mais là où ça devient complètement ridicule c’est que isset renverra bien true pour une chaine vide (ainsi que pour false), alors que c’est équivalent à null avec l’opérateur de comparaison non-typé == ! Alors pourquoi isset n’a pas le même comportement pour toutes ces valeurs ?

$array = array(
	'toto' => null,
	'tata' => ''
);

var_dump(isset($array['toto'])); // boolean false
var_dump(isset($array['tata'])); // boolean true
var_dump($array['toto'] == $array['tata']); // boolean true

Les fetch modes de PDO 4 : les modes modificateurs

Dernière partie de ma série consacrée aux modes de récupération de PDO, voici les modes que j’appelle “modificateurs”, car ils ne peuvent pas être utilisés tout seuls et se contentent de modifier le comportement d’un autre mode.

Pour utiliser un modificateur, il suffit d’effectuer un “ou” binaire, par exemple :

$results->fetchAll(PDO::FETCH_TRUC | PDO::FETCH_BIDULE | PDO::FETCH_CHOUETTE);

La plupart de ces modes ne fonctionnent pas lorsqu’ils sont définis par la méthode setFetchMode. Ça ressemble a un bug, mais vu que rien n’est documenté, c’est peut-être normal…

Read more

Les fetch modes de PDO 3 : les modes spéciaux

Suite de ma série consacrée aux modes de récupération de PDO, voici les modes “spéciaux”, c’est à dire des modes au comportement particulier, très mal documentés mais souvent très pratiques !

Read more

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.

Read more

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.

Read more

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).

Read more

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.

Read more

OVH et les sessions

Voila, c’est officiel, je ne supporte plus l’hébergeur français OVH.com. Propriétaire d’un 90plan chez eux (hebergement mutualisé labellisé « pro ») depuis plusieurs mois, je subis à longueur de temps les bugs ou les modifications intempestives de leur plateforme : sous-domaines qui ne fonctionnent pas, ftp capricieux, scripts php mis en cache sans avoir rien demandé, etc. Leur dernière lubie : désactiver la fonction PHP session_save_path (qui permet, rappellons le, de spécifier le dossier dans le lequel sont stockées les sessions), évidemment sans prévenir personne.
Read more

« Previous PageNext Page »