Un article de Mmmm!!!

"Pointeur" de fonction ou comment passer une fonction en argument d'une autre PHP 3

Introduction

J'ecris ce petit article car je n'ai pas trouvé sur le site de php de documentation sur le passage de pointeur de fonction à une autres fonctions ou méthodes d'un object. Je savais pourtant qu'il etait possible de réaliser cela en l'ayant vu dans un source php. Cependant il y a la fonction: call_user_func -- Call a user function given by the first parameter documentée sur le site de php.
Le terme de pointeur de fonction est erroné dans cette article, j'espere que vous ne prendrez pas la mouche pour cette abu de langage.

On debute par une fonction

Donc je debute a chercher comment faire et me dis que je vais tenter au plus simple, et en fait cela marche. Voici l'exemple:

function toto ( ) {
	echo "toto";
	}
function tutu ( $pfunc ) {
	for ($i=0;$i<5;$i++) $pfunc( );
	}
tutu(toto);
Et donc j'obtiens:
toto
toto
toto
toto
toto
Super que je me dis ca marche plutot bien :).

On continue par une fonction avec parametre

Donc je continue en regardant si on peut passer un parametre. Voici l'exemple:
function toto ($s ) {
	echo "$s ";
	}
function tutu ( $pfunc ) {
	for ($i=0;$i<5;$i++) $pfunc( "toto" );
	}
tutu(toto);
Et meme traitement, meme chatiment :)
toto
toto
toto
toto
toto

On s'oriente vers l'objet

Heureux de voir que je n'ai pas de probleme, je regarde si cela marche aussi avec des objects. Je definit donc un object tete ( j'ai deja utilise les toto tutu :).

class tete {
	var $name;
	function tete($s) {
		$this->name=$s;
		}
	function tutu($pfunc) {
		for ($i=0;$i<5;$i++) $pfunc( $this->name );
		}
	function toto() {
		echo "$this->name 
" } }
Et je l'appelle par la suite.
function toto ( $s ) {
	echo "$s ";
	}
$tete=new tete("toto");
echo "Appel de la methode tutu avec comme pointeur de fonction toto";
$tete->tutu(toto);
La encore j'ai mes cinq toto ( plus un present dans le echo ).
Bon je me dis c'est bien de passer une fonction a une methode mais si on ne peut pas acceder au instance de la methode cela n'a pas grand interet. Du coup, j'essais ( au hasard , la chance etait avec moi , peut etre la force , n'est-ce pas pere :).
Me moquant de la raison, j'ecris une fonction en dehors de la classe, ne sachant pas comment php gere les variables,methodes et fonction.
function totowiththis() {
	echo "$this->name";
	}
He bien cela ne marche pas du tout coup , la force n'est pas avec moi et mon reve d'enfant est detruit :(.
J'ai de la chance mais pas le cue brodé de nouille tout de meme :). Mais je continue dans mes investigations et trouve comment acceder a l'object dans une fonction externe.
	function toto($obj) {
		echo "$obj->name";
	}
cinq toto, ce qui nous fait dix to,t,o soit cinquante caracteres :).
Remarque au passage ceci marche aussi:
	function toto($this) {
		echo "$this->name";
	}

Une fonction d'accord mais une methode ?

L'essai avec une methode s'impose, je ne pouvais pas reculer. Alors j'appelle la fonction tutu avec la methode toto.

tutu($tete->pfunction);
Et la arggg, j'obtiens l'erreur php:
Fatal error: Function names must be strings in [...]/pointfunc.php3 on line xxx
Ceci nous indique qu'il n'est en rien question de pointeur de fonction mais plutot d'une chaine de caractère interpreté a la volée par php.
tutu("tete->pfunction");
Ne marche pas non plus , et pour cause , il ne connait pas la variable tete, cependant en ajoutant la variable global $tete dans la fonction pas de probleme; mais la cela n'a aucun interet.
Donc je decide de passer l'objet et la methode par deux arguments.
function tutu ($obj,$pfunc ) {
	for ($i=0;$i<5;$i++) $obj->$pfunc( "toto" );
	}
$tete=new tete("toto");
echo "
***Objet et Methode *********
"; tutu($tete,"pfunction");
Voila cette solution fonctionne.Et de meme dans l'appel dans l'objet s'effectue en ne passant que le nom de la methode.

Une methode ca marche mais un construteur d'objet ?

Bon, alors la c'est un peu space , mais comme me dirait mon ami pierre cela se tente :). La encore pas de grosse difficulte.

[ modifiez le constructeur de tete ]
function tete($s) {
		$this->name=$s;
		echo "toto $s";
		}

function tutuconstruteur ($obj) {
	global $tete;
	for ($i=0;$i<5;$i++) $tab[$i]=new $obj("$i");
	}

tutuconstruteur("tete");
Nous donne : toto 0
toto 1
toto 2
toto 3
toto 4

Conclusion

Bon, j'espere que ce petit article vous a interresse.
Les exemples en Live
Je suis d'accord qu'il est difficile de parler de pointeur de fonction alors cela que l'on passe est une chaine de caractere. Et me direz vous ce n'est pas si etonnant que cela marche car php est un langage interprete. Ceci n'a rien d'etonnant avec d'autre langage comme le tcl par exemple.
Avec la fonction call_user_func on obtient les memes resultats lors de l'appel d'une fonction cependant pour les objets je ne sais pas comment faire pour passer le $this ?
Mais me direz-vous que faire de pointeur de fonction alors que php ce veut un langage orienté objet. Premierement c'est toujours interressant de connaitre les possibilitées du langage. Sinon, si l'on a une classe mappant un objet d'un base de donnée mais que l'on veut extraire la representation des donnée de l'objet de l'objet lui meme ( ce qui est d'pares moi une bonne chose ) on peut tout de meme créer dans l'objet un iteration sur les raws et ecrire une fonction representant les donnees. Exemple ( j'ai ecris cet exemple sans le tester reelement merci de m'excusez s'il y a des erreurs ):

Exemple d'utilisation

class mapunetable extends class_basededonne {
		var $nom; 
		var $prenom;
		// Constructeur
		function mapunetable() {
			$this->class_basededonne();
			}
		function getnom() { return($nom);}
		function getprenom() { return($prenom);}
		[...]
		// On iterateur sur la base et on apelle une fonction
		function listpersonne($pfunc,$query) {
		        $nb=$this->execute($query);
               		  for ($i=0; $i<$nb; $i++)  {
                            	 $resultsql = $this->get_next_row();
                               	$this->getfrombd($tmp["idx"]);
                               	$pfunc($this);
                               	}
			}
		// On iterateur sur la base et on appelle une methode d'un objet
		function listpersonneOO($obj,$pfunc,$query) {
			$nb=$this->execute($query);
               		  for ($i=0; $i<$nb; $i++)  {
                            	 $resultsql = $this->get_next_row();
                               	$this->getfrombd($tmp["idx"]);
                               	$obj->$pfunc($this);
                               	}
			}
	}
[Dans un autre fichier fait par quelqu'un d'autre qui ne s'appele pas toto]
function affpersonne($obj) {
	echo "$obj->getnom()
"; echo "$obj->getprenom()
"; } $m=new mapunetable() $m->listpersonne(affpersonne);

Sinon voici une autre utilisation possible:
Dans beaucoup de programme PHP on utilise une variable genre "operation" pour dire au scripte ce que l'on va faire. Et bien au lieu de faire du gros switch case ( petit mais deviendra grand ;).On peut mettre simplement les nom cle de l'operation dans une hastable relie a un nom de fonction. Exemple:

Deuxieme Exemple d'utilisation

Au lieu de :
switch ($op) {
	case "showsubcat" : 
			[...]
			break;
	case "showobj" : 
			[...]
			break;
	}
Que pensez vous de cela:
function showsubcat() {
		[...]
		}
function showobj() {
		[...]
		}
$pfuncop=array("showsubcat"=>showsubcat,
                "showobj"=>showobj);
( teste pour verifier si op est presente et est dans le tableau $pfuncop )
$pfuncop["$op"]();

Un collegue m'a demande est-ce que cela marchera avec php4 ?
mais j'ignore la reponse , peut etre quelqu'un le sait-il ?
Avec php4, le langage se dote de nouvelles fonctionnalités comme get_class_methods et autres get_class_vars , get_object_vars ... permettant peut etre une introspection des objets et tout cela pour afficher du HTML :).

Si quelqu'un a la reponse a la question pourquoi pourquoi a-t-il mis des totos, j'aimerais avoir la reponse merci.
Pour tout commentaire, suggestion, et autre recette de cuisine voir bon resto , ecrivez moi.


L'equipe de Mmmm!!! copyright 2000