Fonctions anonymes et $this

ViPHP
ViPHP | 928 Messages

12 juil. 2010, 16:55

Tout content de l'ajout des fonction anonymes dans PHP 5.3 qui donnent une nouvelle dimension à la programmation avec des hooks, je suis tombé sur un os qui a pas mal calmé mes ardeurs.

Voilà un exemple pour le contexte (voir la suite).
class Test
{
   protected $myVar = 42;

   public function __construct()
   {
      // Juste pour donner un exemple de fonction nécessitant un callback
      preg_replace_callback('#world#', function($matches)
      {
         var_dump($this->myVar); // Il ne connait pas $this
      }, 'hello world');
   }
}
Dans ma jolie fonction anonyme j'ai besoin d'accéder à mon objet Test (donc en utilisant $this), qu'à cela ne tienne il suffit d'utiliser le mot clef use comme ceci :
      preg_replace_callback('#world#', function($matches) use($this)
      {
         var_dump($this->myVar); // Erreur PHP
      }, 'hello world');
Sauf que ça ne fonctionne pas. PHP n'aime pas ce $this, nouvel essai donc en assignant par référence $this à une autre variable :
      $self = $this;
      preg_replace_callback('#world#', function($matches) use($self)
      {
         var_dump($self->myVar); // Ne peut pas lire la propriété car protégée
      }, 'hello world');
Ca fonctionne quasiment ... si on enlève les soucis de portée ! En effet, exit les propriétés / méthodes protégées et privées ... Bref c'est une mauvaise surprise, qui sera réglée rapidement je l'espère. En attendant, j'ai trouvé une astuce sur un commentaire php.net, d'où la présence de ce sujet, pour la partager, dans le cas rare ou vous tomberiez sur le même os que moi :
http://www.php.net/manual/en/functions. ... .php#98384

ViPHP
ViPHP | 5462 Messages

12 juil. 2010, 19:01

ca me choque pas moi, au niveau visibilité c'est une fonction pas une méthode

ca reviens a la même logique que ca serai correct :
$var = 'hello';
	
function test()
{	
   echo $var;
}
	
test();
on rajoute un global
$var = 'hello';
	
function test()
{		
    global $var;
    echo $var;
}
	
test();
ton exemple c'est plus ou moins la même chose sauf qu'au lieu de global c'est use

ViPHP
ViPHP | 928 Messages

12 juil. 2010, 19:40

Oui tout à fait, sauf que le problème c'est qu'on peut avoir besoin d'accéder au $this d'une façon ou d'une autre dans sa globalité (méthodes / propriétés protégées inclues), dans une fonction anonyme définie au sein d'une méthode de la classe, et que PHP ne le gère pas. A partir du moment ou je la définie dans une méthode il est normal de vouloir accéder au contexte. Ca fait partie des minuscules défauts de PHP qui n'arrivent que dans des cas assez poussés, et je suis sur que ce sera corrigé dans des branches futures.

ViPHP
ViPHP | 5462 Messages

12 juil. 2010, 20:34

Oui tout à fait, sauf que le problème c'est qu'on peut avoir besoin d'accéder au $this d'une façon ou d'une autre dans sa globalité (méthodes / propriétés protégées inclues), dans une fonction anonyme définie au sein d'une méthode de la classe, et que PHP ne le gère pas. A partir du moment ou je la définie dans une méthode il est normal de vouloir accéder au contexte. Ca fait partie des minuscules défauts de PHP qui n'arrivent que dans des cas assez poussés, et je suis sur que ce sera corrigé dans des branches futures.
ta fonction anonyme peux être définie ailleurs que dans ta class, tu peux pas la différence entre une fonction appelée dans une class et une fonction appelée hors class, c'était la même chose que pour create_function
$truc = function($matches)
{
    //je mal mettre un $this ici
    var_dump('hello');
};

class Test
{    
    protected $myVar = 42;

    public function __construct()
    { 
        global $truc;

        preg_replace_callback('#world#', $truc, 'hello world');
    }
}

$test = new Test();
le problème viens surtout du "pourquoi une fonction anonyme ?" et encore plus "pourquoi dans une classe ?"

ViPHP
ViPHP | 928 Messages

12 juil. 2010, 21:15

:roll:

Parce que
class toto {
public function myMethod($array)
{
   usort($array, function($a, $b))
   {
      return (strcmp($a, $b));
   });
}
}
est infiniment plus élégant que
class toto {
public function myMethod($array)
{
   usort($array, 'myCallable');
}
}

function myCallable($a, $b)
{
   return (strcmp($a, $b));
}
Et les fonctions anonymes ont été ajoutées justement pour palier à cette horreur de create_function(). En Javascript il est parfaitement logique de les utiliser car la programmation évènementielle requiert des callback, c'est le même cas pour PHP même si on programme rarement de cette façon.

ViPHP
ViPHP | 5462 Messages

12 juil. 2010, 21:35

je suis pas d'accord sur l'élégance

dans une class ca serai (cas n°3)
class toto
{
    public function myMethod($array)
    {
        usort($array, array($this, 'myCallable'));
    }

    private function myCallable($a, $b)
    {
        return(strcmp($a, $b));
    }
}
et la pas de fonction extérieur et en plus j'ai mon $this

imagine le cas oui on creer une erreur par exemple il attend un 3eme paramètre

cas 1 :
Warning: Missing argument 3 for {closure}()

cas 2 :
Warning: Missing argument 3 for myCallable()

cas 3 :
Warning: Missing argument 3 for toto::myCallable()


y'a pas photo niveau débugage, niveau documentation ca va être balèze aussi va falloir tout rajouter a la mano

ViPHP
ViPHP | 928 Messages

12 juil. 2010, 22:08

Autant pour moi, j'avais toujours cru que le problème de portée se posait dans ton exemple, après test ce n'est pas le cas, donc effectivement je vais procéder comme cela dorénavant :)

ViPHP
ViPHP | 3300 Messages

12 juil. 2010, 23:49

souvent quand il s'agit de passer un $this à une fonction on lui passe array($this, 'methodeDeLaClasse') ca marche avec usort, et avec pas mal d'autres fonctions, est ce que ca serait pas ici aussi le cas?
Fait du php depuis que ca existe ou presque :)

ViPHP
AB
ViPHP | 5818 Messages

13 juil. 2010, 00:24

ça n'a pas de rapport avec $this et les fonction anonymes mais ça me rappelle un peu la syntaxe pour faire fonctionner array_walk à l'intérieur d'une classe en utilisant une fonction de cette classe
array_walk($tab, array('nom_de_la_classe','nom_de_la_fonction'),$parametre);
enfin bon c'était juste en passant :)

ViPHP
ViPHP | 5462 Messages

13 juil. 2010, 00:47

ça n'a pas de rapport avec $this et les fonction anonymes mais ça me rappelle un peu la syntaxe pour faire fonctionner array_walk à l'intérieur d'une classe en utilisant une fonction de cette classe
array_walk($tab, array('nom_de_la_classe','nom_de_la_fonction'),$parametre);
enfin bon c'était juste en passant :)

oui c'est l'une syntax du type callback

- function
- array(class, method)
- class::method
et depuis php 5.3 (d'ou les fonction anonyme)
- $func ($func etant une contion anonyme)
- function(){ }

sinon au sujet des Closures dans la doc c'est dis
Les fonctions anonymes sont actuellement implémentée en utilisant la classe Closure. C'est un détail d'implémentation, et il est recommandé de ne pas s'appuyer dessus.
On peux se demander le pourquoi du comment la :roll:, ca sera peu etre plus souple plus tard...

ViPHP
ViPHP | 5462 Messages

12 août 2010, 02:53

Bref c'est une mauvaise surprise, qui sera réglée rapidement je l'espère.
en allant faire un tour dans le SVN trunk j'ai vu
- Added closure $this support back. (Stas)