PHP: surcharger les fonctions de l'API avec runkit
Par Clochix le vendredi 24 octobre 2008, 00:27 - Technoweb - Lien permanent
Devant tester une application PHP dont le comportement change en fonction du
temps, j'ai cherché une solution relativement élégante pour le faire. Le plus
simple aurait été de remplacer dans le code tous les appels à la fonction
time par une fonction personnalisée. Fastidieux et pas très
amusant. Exécuter l'application dans un serveur virtuel serait sans doute la
solution la plus propre, mais elle est un peu lourde. Je suis alors tombé sur
runkit, qui permet de modifier le code d'un script à l'exécution...
runkit est une extension utile lors de la mise au point d'un programme. Elle permet de contourner un certain nombre de règles du langage. En ce sens, elle peut donc être très dangereuse, et je décommande fortement de l'utiliser pour autre chose que du debuggage.
Elle permet entre autre:
- d'exécuter un programme dans un environnement entièrement contrôlé, un bac à sable sécurisé (sandbox);
- de manipuler les classes définies pas l'utilisateur, par exemple leur ajouter des méthodes
- de manipuler les constantes: les ajouter, modifier, supprimer,
- de manipuler toutes les fonctions, y compris les fonctions de l'API de PHP. C'est cette dernière fonctionnalité que j'ai utilisée.
Installation de runkit
Runkit est disponible sous forme d'extension PECL. Malheureusement la dernière version a plus de deux ans et ne s'installe pas par la méthode classique sur mon Héron. Voici comment j'ai du procéder pour l'installer (un certain nombre de manipulations nécessitent des droits, donc pour simplifier j'ai ouvert un shell root. Je sais, c'est mal).
1ère étape: créer un répertoire et y récupérer la dernière version
de l'extension dans le gestionnaire de sources : # cvs -d :pserver:cvsread@cvs.php.net:/repository checkout pecl/runkit 2ième étape: compiler et installer: # cd pecl/runkit # pecl build package.xml # ./configure # make # make install
3ième étape: dire à PHP de charger l'extension. Sous Debian et filles,
créez simplement un fichier runkit.ini dans
/etc/php5/conf.d et insérez-y la ligne suivante:
extension=runkit.so
Dernière étape, runkit ne permet pas par défaut de re-définir les
fonctions de l'API PHP. Vous devez l'autoriser explicitement: dans votre
php.ini, ajoutez les lignes suivantes:
[runkit] runkit.internal_override = On
Relancez si nécessaire Apache, regardez le phpinfo, si tout s'est bien passé l'extension runkit devrait être disponible.
Utilisation
Mon application n'utilisant pour déterminer la date courante que la fonction
time, j'ai redéfinie icelle en utilisant microtime,
non surchargée, pour récupérer la "vrai" heure et la modifier à la volée. Ce
qui donne, pour simuler un bond de 10 jours dans le passé:
runkit_function_redefine('time','','list($usec, $sec) = explode(" ", microtime()); return (int)$sec - 3600*24*10;');
Cette syntaxe n'est cependant pas très pratique. Il est possible de définir les surcharges dans un fichier et d'importer celui-ci:
runkit_import("runkit.php", RUNKIT_IMPORT_OVERRIDE | RUNKIT_IMPORT_FUNCTIONS );
runkit.php étant dans ce cas un fichier classique contenant vos
fonctions. Malheureusement, runkit_import provoque de nombreux
plantages, je n'ai sans doute pas compris comment elle fonctionnait et les
paramètres à lui passer. Si quelqu'un a une idée...
Après avoir joué un peu avec, runkit reste peu aisée d'emploi: elle a peu de documentation, et son développement semble arrêté. Mais elle peut se révéler très pratique dans certains cas, je la garde donc sous le coude, d'où ce pense-bête.
Enfin, faites quant même attention si comme moi vous cherchez à tromper votre application sur la date courante. Vérifiez bien qu'elle n'utilise pas de données temporelles provenant d'autres source, comme une base de données. Si elle utilise par exemple les fonctions de la base pour fixer la date de création des enregistrements, vous risquez de créer des incohérences.
Pour plus d'informations, allez jeter un œil à la documentation PHP.