Archive

Archives pour 04/2011

Prototype et les interpréteurs JSON natifs des navigateurs : NATIVE_JSON_PARSE_SUPPORT

J’ai aujourd’hui expérimenté un curieux problème en essayant de mettre à jour Prototype sur un site web. Du code qui fonctionnait bien, subitement, ne fonctionnait plus avec la version 1.7.

En cherchant bien, j’ai fini pas comprendre qu’il y avait un problème avec du code JSON. Or, à première vue, ce code paraissait tout à fait correct :

{
	mode : "exact",
	elements : "textarea_bouzi_txt_index_para_1",
	theme : "advanced",
	skin : "o2k7",
	plugins : "",
	theme_advanced_toolbar_location : "top",
	theme_advanced_toolbar_align : "left",
	theme_advanced_statusbar_location : "bottom",
	convert_urls : false,
	relative_urls : false,
	forced_root_block : false,
	extended_valid_elements : "div[*],iframe[*]"
}

Pourquoi cela ne fonctionnait-il pas alors ? C’est qu’en y regardant de plus près, on voit qu’il n’est pas correct : les propriétés ne sont pas entre guillemets. Ce code (qui provient je crois de TinyMCE - c’est à vérifier) devrait donc plutôt être :

{
	"mode" : "exact",
	"elements" : "textarea_bouzi_txt_index_para_1",
	"theme" : "advanced",
	"skin" : "o2k7",
	"plugins" : "",
	"theme_advanced_toolbar_location" : "top",
	"theme_advanced_toolbar_align" : "left",
	"theme_advanced_statusbar_location" : "bottom",
	"convert_urls" : false,
	"relative_urls" : false,
	"forced_root_block" : false,
	"extended_valid_elements" : "div[*],iframe[*]"
}

Reste que cela n’explique pas pourquoi ce code, aussi erroné soit-il, ne fonctionnait subitement plus.

La réponse est que les navigateurs récents implémentent désormais d’une manière native le propre interpréteur JSON. Plus besoin de passer par une bibliothèque JavaScript JSON ; on fait simplement :

JSON.parse('["du code JSON"]');

Or, dans sa dernière version, Prototype essaye de s’appuyer sur l’interpréteur JSON natif du navigateur quand il en existe un, ceci à des fins de performance. Mais c’est là qu’il y a un problème : cet interpréteur est en général beaucoup plus rigoureux. J’ai en effet essayé avec Firefox 3.6 et Internet Explorer 9.0, et dans les deux cas, le code JSON qui n’utilise pas de guillemets pour les propriétés provoque une erreur.

Au sens strict, on ne peut pas parler d’un bug de ces interpréteurs, car le code JSON est effectivement mal formé. Cependant, il existe un grand nombre de bibliothèques qui ne rendent pas un code JSON avec les propriétés entre guillemets (comme apparemment la version de TinyMCE que j’utilise). Tout ce code fera immanquablement planter la dernière version de Prototype, ce qui est problématique.

La meilleure solution consisterait à fournir du code JSON bien formé à Prototype, mais ce n’est pas toujours possible. Reste alors une autre solution simple, consistant à downgrader prototype afin qu’il ne cherche pas à utiliser le parser natif du navigateur lorsqu’il y en a un, et le forcer à utiliser son propre interpréteur.

Pour ce faire, il y a plusieurs solutions, qui nécessitent toutes de modifier un peu le fichier prototype.js. Je les liste par ordre de préférence personnelle :

  • Remplacer la ligne 772 par :
    evalJSON: evalJSON,
  • Remplacer le bloc des lignes 535-537 par :
    var NATIVE_JSON_PARSE_SUPPORT = false;
  • Remplacer le bloc des lignes 718-721 par :
    function parseJSON() {
        var json = this.unfilterJSON();
        return evalJSON(json);
    }

Faites votre propre choix ! Même si la meilleure solution reste d’avoir du code JSON propre dès le départ :)

PHP : programmer sans erreurs du type « notice »

J’en connais certains qui aiment avoir un code PHP qui ne génère aucune erreur du type « notice« . Une erreur notice, c’est lorsque, par exemple, on essaye de traiter une variable qui n’est pas initialisée :

//$bob = "pas initialisée, c'est un commentaire";
print $bob; // cela va générer une erreur de type notice

Il ne s’agit pas d’erreur au sens propre du terme. PHP est un langage de script, beaucoup moins rigoureux qu’un « vrai » langage, comme Java par exemple. S’accorder de telles libertés dans le code ne l’empêchera pas de fonctionner. Pour ma part, un code qui générerait d’innombrable erreur de ce type notice ne me dérangerait pas. Ainsi, une solution simple pour en finir avec elle consiste à changer le niveau du rapport d’erreur. Par exemple, en tout début de code, quelque chose comme :

error_reporting(E_ALL | ~E_NOTICE);

Ou dans le fichier php.ini :

error_reporting = E_ALL | ~E_NOTICE

Cependant, cela ne fait que masquer le problème – si tant est que ça en soit un -, puisque les erreurs de ce type continuent d’exister ; elles ne sont simplement plus affichée.

Pour bien faire, la vraie solution rigoureuse consistera alors à récrire son code de façon à ce qu’il n’en génère plus. Comme la plupart de ces erreurs sont souvent dues à des variables non initialisées que l’on utilise sans les tester, une solution simple consiste à s’assurer qu’elles ont bien une valeur avant toute chose. Par exemple, le code de tout à l’heure devient :

//$bob = "pas initialisée, c'est un commentaire";
if(isset($bob))
{
	print $bob; // cela va générer une erreur notice
}
else
{
	// faire autre chose
}

On comprend que répéter ce code à chaque fois peut être facilement très fastidieux… Mais on peut s’éviter cette peine grâce à la petite fonction suivante :

function getIfSet(& $var, $default = null)
{
	if(isset($var))
	{
		return $var;
	}
	else
	{
		return $default;
	}
}

Cette fonction prend en premier argument la variable que l’on souhaite utiliser, mais dont on ignore si elle est initialisée ou non. Le deuxième argument, qui est optionnel (et est à null par défaut), est la valeur qui sera renvoyée s’il se trouve que la variable n’est pas initialisée :

//$bob = "pas initialisée, c'est un commentaire";
print $bob; // cela va générer une erreur de type notice
print getIfSet($bob); // cela ne génère pas d'erreur et n'affiche rien
print getIfSet($bob, "pas initialisée"); // cela affiche "pas initialisée"

Cette fonction sera à coup sûr très utile à tous ceux qui souhaitent débarrasser leur code de ce type d’erreurs !

Categories: PHP Tags: ,