Page 1 sur 1

Question éthique

Posté : 30 déc. 2008, 23:17
par savageman
Après quelques benchs, il s'avère plus rapide de faire ceci:

$test = new test;
$test->methode();

que

call_user_func(array('test', 'methode'));

Je sais très bien que ma méthode est static, mais PHP le reconnait très bien quand on instancie la classe, ce qui est 1) louche 2) plus rapide 3) plus pratique pour moi.

Alors, mieux vaut éviter ou je peux l'utiliser ?

Posté : 31 déc. 2008, 00:27
par Hywan
Hey :),

Premièrement, on a pour habitude de mettre des parenthèses lors de l'utilisation du mot-clé new après le nom de la classe :
$object = new StdClass(); // bien :)
// au lieu de :
$object = new StdClass; // pas bien :(
Ensuite, quand tu utilises new, c'est la procédure classique/habituelle. On peut facilement interagir dessus via des optimisateurs de code (XCache par exemple). Alors que quand tu utilises un callback (un appel dynamique) avec call_user_func(), c'est forcément plus long car on n'utilise pas les même techniques. Je n'arrive pas à expliquer précisément les différences mais c'est logique que ce soit plus long. Au niveau de l'interprétation du code et du nombre de tests, ça n'a rien à voir.

En plus, écrire deux lignes lisibles, compréhensibles et « standards » ne va pas te tuer, alors qu'écrire une ligne moins lisibles, moins compréhensibles, moins fréquentes baisse les performances. Je ne saurai que trop de conseiller d'utiliser le mot-clé new qui est prévu pour ça, alors que call_user_func() est un moyen dynamique détourné pour arriver à nos fins. À utiliser que dans de rare cas (quand la dynamique est vraiment nécessaire, sinon ça ne se justifie pas).

Posté : 31 déc. 2008, 01:03
par naholyr
Ta comparaison est faussée savageman. Tu compares un appel direct de méthode à un appel via une fonction "call_user_func".

Si tu veux appeler une méthode statique, alors utilise simplement "test::methode()".

Et bizarrement, j'ai l'intuition que ça sera plus rapide que tes deux autres essais...

Posté : 31 déc. 2008, 01:23
par savageman
En fait, le nom de ma classe est dans une variable, je ne peux pas l'écrire directement, ça dépend de l'utilisateur qui me fournit de quoi connaitre la classe à utiliser...

En temps normal, je fais bien entendu Classe::methodeStatique(), mais je ne peux pas faire $classe::methodeStatique(), j'essaye donc de trouver un compromis rapide...

Les 3 solutions que j'ai trouvées sont :
- l'instanciation et l'appel comme une méthode dynamique avec la flèche ->
- call_user_func()
- ReflectionMethod() + invoke

Aucune d'elle ne me convient vraiment, c'est pourquoi j'ai posté mon message. ;) Quoi que c'est que vous me conseillez ?

Posté : 31 déc. 2008, 01:40
par naholyr
À mon sens, si une partie de l'appel est dynamique (nom ou partie du nom de la fonction/méthode, nombre d'arguments), alors on devrait toujours passer par call_user_func().

L'instanciation fonctionne en effet, mais c'est une erreur de la part de PHP (et de son implémentation très très partielle de l'objet), qui ne devrait pas mener à ce genre de bidouille à mon avis ;)

Il faut peser le pour et le contre entre :
- la logique de ce que tu écris en respect avec l'API (pour : call_user_func)
- le risque pris (pour : call_user_func, car on n'est pas à l'abri qu'une future release de PHP lève une E_STRICT quand on appelle une méthode statique comme une méthode d'instance, puisque justement l'inverse lève actuellement une E_STRICT)
- la rapidité d'exécution (pour : instanciation)
- la lisibilité (subjectif)
C'est à toi de voir quel poids tu accordes à chacun de ses points pour faire ton choix. Mais il n'appartient qu'à toi.

Posté : 31 déc. 2008, 01:42
par Hywan
Ça changera en PHP 5.3, mais pour l'instant, voici comment PHP traite tout ça :
  • quand on écrit class::constante, class est de type T_STRING et constante de type T_STRING ;
  • quand on écrit class::$attribute, class est de type T_STRING et $attribute de type T_VARIABLE ;
  • quand on écrit class::methode(), class est de type T_STRING et methode de type T_STRING ou T_VARIABLE.
C'est à dire que tu peux appeler une méthode façon dynamique :
class A {

    public static function method ( ) {

        return 'methode';
    }
}

$m = 'method';
var_dump(A::method());
var_dump(A::$m());
Par contre, on note que la classe est invariablement de type T_STRING (à ne surtout pas confondre avec T_CONSTANT_ENCAPSED_STRING qui est une chaîne de caractères à proprement parler). Donc impossible de rendre ça dynamique.
En PHP 5.3, ce sera possible (yepee).

Donc il te reste deux solutions : appels dynamiques (callback) ou introspection (reflection). À mon avis, les appels dynamiques sont plus rapides que l'introspection car l'introspection va regarder le « bytecode » et agir en conséquence. Cette opération est plus lourde qu'un appel dynamique.

Après relecture : oops, il te reste bien sûr la possibilité d'appeler ta méthode statique directement sur l'objet (qui en fait va rediriger sur la classe). Lors de l'utilisation du mot-clé new, on doit préciser le nom de la classe qui est soit de type T_STRING soit … T_VARIABLE ! Donc on peut avoir du dynamisme à cet endroit :
// Suite du code précédent.
$a = 'A';
var_dump(new A());
var_dump(new $a());
Donc tu peux faire un truc du genre :
$object = new $class();
$return = $object->$method();
ça reste possible. Par contre, pas sûr que d'instancier une classe soit systématiquement plus rapide que d'utiliser des appels dynamiques … À voir :-k.

Conclusion partielle : comme ta classe est dynamique, les appels dynamiques peuvent être nécessaires, donc utiliser call_user_func() se justifie très bien :).

Posté : 31 déc. 2008, 13:38
par savageman
Ok, merci à vous.
Ca m'emmerde carrément d'utiliser un truc plus lent pour le faire, mais bon... Si je n'ai pas d'autres solutions...

Posté : 31 déc. 2008, 17:14
par Hywan
Oui enfin, c'est plus lent à quel point (la flemme de me faire un p'tit benchmark …) ?