20/02/2012

Le point sur les limites du typage de PHP

Les limites

PHP a ceci de particulier qu'il est est un langage de typage faible (le type des variables peut changer en cours de route), mais qu'il autorise un typage fort partiel des paramètres de fonctions pour ce qui concerne les objets et les tableaux. On pourra ainsi écrire :

function test(array $argument) {
    (...)
}
 
function test(monObjet $argument) {
    (...)
}

mais par contre il n'est pas possible de typer les paramètres de méthodes pour les types scalaires (entier, chaînes de caractères...)

function test(string $argument) {
    // qui a dit que PHP ne savait pas être drôle ?
    // Argument 1 passed to test() must be an instance of string, string given
}

Pourtant, ça semble bien souvent manquer : combien de fois vois t-on des tests de type dans un code ?

function test($argument) {
    if(!is_string($argument)) {
        throw new InvalidArgumentException("eh ! on voulait une chaîne !");
    }
}

Bref, on se retrouve avec du code pas vraiment utile (qui n'est ni métier ni applicatif), qui, bien souvent, nuit à la lecture et à la bonne compréhension des sources.

Les solutions pistes

il existe cependant des solutions, ou au moins des tentatives de solutions, pour pallier à ces inconvénients.

Patch

La première provient d'un modification du langage lui-même, sous forme d'un patch ou d'une extension. Ilia Alshanetsky propose depuis longtemps un patch pour PHP 5.3. Ce patch, complet, modifie le comportement du parseur, mais complète également Reflection.

Malheureusement, l'utilisation de ce patch nécessite de devoir patcher systématiquement PHP, avec d'autant plus de risques que le patch n'est pas officiel. Mais surtout ce patch n'est pas mis à jour, et n'est plus compatible avec les versions récentes de PHP 5.3 (ne parlons même pas de PHP 5.4 :-) )

La Standard Php Libary

Bon, c'est un idée, mais il y en a eu d'autres. Marcus Börger et David Coallier ont développé l'extension PECL SPL_Types pour insérer ce comportement dans la SPL.

Ici, pas vraiment de typage fort scalaire, mais une surcouche à utiliser :

function test(SplInt $integer){
    // (...)
}

$value = new SplInt(5);
<p>test($value);

Ça semble marcher, mais personnellement je trouve ca peu élégant et pas vraiment optimal : quid de la compatibilité avec des librairies externes qui n'utiliseront pas les typages Spl ? Et bon, on ne type pas les scalaires ; dans notre exemple ce qui suit ne fonctionnera pas, ce qui montre en soi que cette solution n'est qu'une rustine :
assert(5 instanceof SplInt); // faux
<p>assert(is_int( new SplInt(5) )); // faux

La rustine de la mort qui tue

Très vite, de nombreux développeurs ont eu l'idée de passer par un gestionnaire d'erreur personnalisé pour résoudre ce problème. Personnellement, je l'avais mis en place sur un framework dans une ancienne boite. À l'époque j'avais trouvé ça tout seul et ça m'avait bien amusé. Après j'avais fait des benchs et je l'avais vite retiré ;-)

L'idée est d'intercepter les erreurs, d'analyser le message de l'erreur et de voir s'il correspond pas à un problème de type. Si c'est le cas on fait nous même le contrôle "a la main", sinon on renvoie vers le gestionnaire d'erreur par défaut.

Voici une version basique :

set_error_handler(function($errno, $errstr, $errfile, $errline) {
    if(preg_match('!must be an instance of (\w*), (\w*) given!', $errstr, $matches)) {
        $matches[2] = str_replace('double','float', $matches[2]);
        return strtolower($matches[1]) == strtolower($matches[2]);
    }
});
En 6 petites lignes de code, magique : on peut désormais écrire ceci :
function example1(integer $v) {
    echo 'Cool, integer was given !'.PHP_EOL;
}
function example2(string $v) {
    echo 'Cool, string was given !'. PHP_EOL;
}
function example3(float $v) {
    echo 'Cool, float was given !';
}
function example4(boolean $v) {
    echo 'Cool, boolean was given !';
}
function example5(object $v) {
    echo 'Cool, object was given !';
}
function example6(resource $v) {
    echo 'Cool, resource was given !';
}

N'oubliez pas que c'est catastrophique côté perf :-)

Conclusion

Bref, on se retrouve avec des rustines et pas vraiment des solutions exploitables en production. Pourtant, si je comprend que le typage fort puisse gêner, je trouve que le typage des paramètres et des retours de de fonction (comme en Java par exemple) manque beaucoup, et pourrait apporter du grain dans la professionnalisation du langage.

D'ailleurs, si on observe bien, on retrouve tout de même de nombreuses RFC à ce sujet, et le sujet à été débattu à de nombreuses reprises, et par de nombreuses "figures" de PHP (Derick Rethans, Sebastian Bergmann...), on a même un moment que c'était bon. Aux dernières nouvelles (mais je dois avouer que je ne sais pas trop ce qu'il en est ) il me semble que l'introduction du typage fort des arguments des fonctions reste très largement discutée, voire contestée, et qu'alors même on l'espérait pour php 5.4, elle n'est prévue dans aucune des futures versions sur les rails.

Quel est votre avis sur la question ? Le typage fort des paramètres et des retours de fonctions vous manque t-il au quotidien ? Ou bien au contraire trouvez-vous que c'est justement la force de PHP que d'offrir une grande souplesse aux développeurs et de s'en passer ? Etes-vous déçus de ne pas avoir cette nouveauté dans PHP 5.4 ?
blog comments powered by Disqus