2010-07-29 7 views
4

class myClass { $ myVariable = 'myCallback';PHP/Portée/Rappel

function myFunction() { 
     $body = false; 
     $callback = $this->myVariable; 

     function test($handle, $line) { 
      global $body, $callback; 

      if ($body) { 
       call_user_func($callback, $line); 
      } 

      if ($line === "\r\n") { 
       $body = true; 
      } 

      return strlen($line); 
     } 

     ... 
     curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'test'); 
     ... 
    } 
} 

function myCallback($data) { 
    print $data; 
} 

$myCls = new myClass(); 
$myCls->myFunction(); 

Attention: call_user_func() [function.call-user-func]: Le premier argument devrait être un rappel valide!

Ma valeur de rappel $ est vide, comment puis-je résoudre ce problème? Restriction: la fonction myCallback ne peut pas être modifiée!

Répondre

2

Juste pour le fun les uns les autres ... (non testé cependant).

class myClass 
{ 
    protected $callback = 'myCallback'; 
    protected $body = false; 

    public function myFunction() 
    { 
     ... 
     curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, 'test')); 
     ... 
    } 


    public function test($handle, $line) 
    { 
     if ($this->body) { 
      call_user_func($this->callback, $line); 
     } 

     if ($line === "\r\n") { 
      $body = true; 
     } 

     return strlen($line); 
    } 
} 

function myCallback($data) { 
    print $data; 
} 

$myCls = new myClass(); 
$myCls->myFunction(); 
10

Important: Ceci n'est possible qu'en PHP> = 5.3. L'OP n'utilise pas PHP 5.3 mais je vais laisser la réponse ici si quelqu'un a un problème similaire et utilise PHP 5.3.


$callback n'est pas une variable globale, elle est locale dans le cadre des méthodes. De l'documentation:

Closures peut également hériter des variables du champ parent. Toutes ces variables doivent être déclarées dans l'en-tête de la fonction. Hériter des variables de la portée parent n'est pas la même chose que d'utiliser des variables globales. Les variables globales existent dans la portée globale, qui est la même quelle que soit la fonction exécutée. La portée parent d'une fermeture est la fonction dans laquelle la fermeture a été déclarée (pas nécessairement la fonction à partir de laquelle elle a été appelée).

Faire usage de use (et affecter la fonction à une variable):

$test = function($handle, $line) use ($callback, $body){ 

    if ($body) { 
     call_user_func($callback, $line); 
    } 

    if ($line === "\r\n") { 
     $body = true; 
    } 

    return strlen($line); 
}; 

et plus tard:

curl_setopt($ch, CURLOPT_WRITEFUNCTION, $test); 
+0

Je n'aime pas ça ** x.9 ** chiffre, +1 bonne réponse :) – Sarfraz

+0

Vous ne pouvez pas définir les fermetures sans les affecter à une variable – Gordon

+0

@Gordon: Bon point, vous avez raison, au moins quand vous voulez utiliser 'use', vous devez l'assigner à une variable. Je reçois une erreur de syntaxe sinon (ou je fais quelque chose de stupide;)) –

2

À l'exception du mot-clé manquant lors de la déclaration $myVariable, cela devrait pas jeter une erreur du tout, car à l'intérieur de la test() la fonction $body est NULL sauf si vous l'avez défini dans la portée globale. En d'autres termes, votre myCallback ne devrait jamais être appelé. Apparemment, vous avez défini $body dans la portée globale et en a fait un excellent exemple de l'utilisation de globals conduisant à toutes sortes de comportements inattendus. Si vous définissiez $callback dans la portée globale de 'myCallback', cela devrait fonctionner, mais vous ne voulez pas de globals.

Et vous une faveur et de se débarrasser de la fonction dans la méthode:

class myClass { 

    protected $_myVariable = 'myCallback'; 

    public function myFunction() 
    { 
     // calling callback that calls callback (should make you think ;)) 
     // that would be your curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'test'); 
     echo call_user_func_array(
      array($this, '_test'), 
      array('body', 'foo', 'bar')); 
    } 

    protected function _test($body, $handle, $line) 
    { 
     if ($body) { 
      call_user_func($this->_myVariable, $line); 
     } 
     if ($line === "\r\n") { 
      $body = true; 
     } 
     return strlen($line); 
    } 
} 

La fonction test() est maintenant une méthode dans votre classe. Ceci est beaucoup plus clair et plus facile à maintenir que de mettre le flux de code dans une fonction à l'intérieur d'une méthode. Notez que je passe en $body comme premier argument. Je ne sais pas comment cURL accepte les rappels ou ce qu'il leur passe. Si vous ne pouvez pas faire $body le premier argument, faire un chèque de membre de classe pour son état avec $this->body à la place (as now shown by Philippe below)

+1

Ha, j'aurais dû jeter un oeil à ça;) Quoi qu'il en soit, je l'ai essayé aussi et ça marche en effet +1 (supprimé mes autres commentaires absurdes) –