2010-05-21 5 views
14

Ich versuche, mit php5.3 und Schließung zu spielen.Definieren Sie eine Schließung als Methode aus der Klasse

Ich sehe hier (Listing 7. Schließung in einem Objekt: http://www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html), dass es möglich ist, $ dies in der Callback-Funktion zu verwenden, aber es ist nicht. Also versuche ich $ dies als Nutzung variabel zu geben:

$self = $this; 
$foo = function() use($self) { //do something with $self } 

So das gleiche Beispiel zu verwenden:

class Dog 
{ 
private $_name; 
protected $_color; 

public function __construct($name, $color) 
{ 
    $this->_name = $name; 
    $this->_color = $color; 
} 
public function greet($greeting) 
{ 
    $self = $this; 
    return function() use ($greeting, $self) { 
     echo "$greeting, I am a {$self->_color} dog named {$self->_name}."; 
    }; 
} 
} 

$dog = new Dog("Rover","red"); 
$dog->greet("Hello"); 

Output: 
Hello, I am a red dog named Rover. 

Zu allererst diesem Beispiel nicht die Zeichenfolge nicht gedruckt, aber die Funktion zurück, aber das ist nicht mein Problem.

Zweitens kann ich nicht auf private oder geschützt zugreifen, da die Callback-Funktion eine globale Funktion und nicht im Kontext von dem Hund Objekt ist. Das ist mein Problem. Es ist das gleiche wie:

function greet($greeting, $object) { 
    echo "$greeting, I am a {$self->_color} dog named {$self->_name}."; 
} 

Und ich will:

public function greet($greeting) { 
    echo "$greeting, I am a {$self->_color} dog named {$self->_name}."; 
} 

die sich von Hund und nicht global ist.

+0

Nein, ich habe nicht verstanden, was Sie wollen. Wenn "greet" eine Methode ist, wird die Sichtbarkeit auf "public" festgelegt, sodass Ihre beiden letzten Codebausteine ​​gleichwertig sind. – Artefacto

+0

Die magische Injektion von '$ this' in einem Clojure wurde fallen gelassen, bevor PHP 5.3 veröffentlicht wurde. Eigentlich wurde es für eine kommende Version von PHP verschoben. Es wird etwas wie 'Closure :: bindTo' geben, mit dem Objekte als Kontexte an Lambdas gebunden werden können. –

Antwort

2

Nun ist es sinnvoll, dass Sie nicht auf private und geschützte Felder eines Objekts zugreifen können. Und indem Sie die Funktion $self explizit an Ihre Funktion übergeben, wird sie wie ein normales Objekt behandelt.
Sie sollten Getter, um diese Werte zugreifen, das heißt erstellen:

class Dog 
{ 
    private $_name; 
    protected $_color; 

    public function __construct($name, $color) 
    { 
     $this->_name = $name; 
     $this->_color = $color; 
    } 
    public function getName() { 
     return $this->_name; 
    } 
    public function getColor() { 
     return $this->_color; 
    } 
    public function greet($greeting) 
    { 
     $self = $this; 
     return function() use ($greeting, $self) { 
      echo "$greeting, I am a {$self->getColor()} dog named {$self->getName()}."; 
     }; 
    } 
} 

Sie sollten Getter (und Setter) wie auch immer, für die Materie von encapsulation erstellen.


Noch ein Hinweis: Der Artikel, den Sie zu verbinden, bevor die endgültige Version von PHP 5.3 veröffentlicht wurde veröffentlicht. Vielleicht wurde diese implizite Objektübergabe entfernt.

+0

Ja ich weiß, es ist nicht wie die aktuelle Version. Das möchte ich eine neue Funktion der Klasse Dog erstellen und dann in der Funktion kann ich auf $ zugreifen, ohne $ selbst übergeben. – Charles

8

Nun, der ganze Grund, dass Sie $ dies nicht verwenden können, ist, weil die Schließung ein Objekt im Hintergrund ist (die Closure-Klasse).

Dafür gibt es zwei Möglichkeiten. Erstens ist die __invoke Methode hinzufügen (Was ist, wenn Sie $ genannt obj() aufrufen) ..

class Dog { 

    public function __invoke($method) { 
     $args = func_get_args(); 
     array_shift($args); //get rid of the method name 
     if (is_callable(array($this, $method))) { 
      return call_user_func_array(array($this, $method), $args); 
     } else { 
      throw new BadMethodCallException('Unknown method: '.$method); 
     } 
    } 

    public function greet($greeting) { 
     $self = $this; 
     return function() use ($greeting, $self) { 
      $self('do_greet', $greeting); 
     }; 
    } 

    protected function do_greet($greeting) { 
     echo "$greeting, I am a {$this->_color} dog named {$this->_name}."; 
    } 
} 

Wenn Sie den Verschluss nicht ändern möchten, wenn Sie das Host-Objekt ändern, können Sie einfach die Return-Funktion ändern zu so etwas wie:

public function greet($greeting) { 
    $self = (clone) $this; 
    return function() use ($greeting, $self) { 
     $self('do_greet', $greeting); 
    }; 
} 

Die andere Option ist eine generische Getter zur Verfügung zu stellen:

class Dog { 

    public function __get($name) { 
     return isset($this->$name) ? $this->$name : null; 
    } 

} 

weitere Informationen finden Sie unter: http://www.php.net/manual/en/language.oop5.magic.php

+0

Ich würde hinzufügen, dass Sie das Feld durch Reflexion zugänglich machen können. – Artefacto

+1

Sie können '$ this' in closures ab PHP 5.4 verwenden – dave1010

+0

Dies ist eine alte Antwort, die ich kenne. Aber es ist erwähnenswert, dass Closure :: bind() (http://www.php.net/manual/en/closure.bind.php) auch hier eine praktikable Lösung ist. –

4

Ab PHP 5.4.0 Alpha1 können Sie $this aus im Rahmen einer Objektinstanz zuzugreifen:

<?php 
class Dog 
{ 
    private $_name; 
    protected $_color; 

    public function __construct($name, $color) 
    { 
    $this->_name = $name; 
    $this->_color = $color; 
    } 

    public function greet($greeting) 
    { 
    $func = function() use ($greeting) { 
     echo "$greeting, I am a {$this->_color} dog named {$this->_name}."; 
    }; 

    $func(); 
    } 
} 

$dog = new Dog("Rover","red"); 
$dog->greet("Hello"); 

Sie dies auch tun können:

$dog = new Dog("Rover", "red"); 
$getname = Closure::bind($dog, function() { return $this->_name; }); 
echo $getname(); // Rover 

Wie Sie sehen können, ist es möglich, leicht verwirrt mit privaten Daten. .. also sei vorsichtig.

0

Ich benutze diese create_closure() in meiner Arbeit Rückrufe in Klassen zu trennen:

<?php 
function create_closure($fun, $args, $uses) 
     {$params=explode(',', trim($args.','.$uses, ',')); 
      $str_params=''; 
      foreach ($params as $v) 
        {$v=trim($v, ' &$'); 
        $str_params.='\''.$v.'\'=>&$'.$v.', '; 
        } 
      return "return function({$args}) use ({$uses}) {{$fun}(array({$str_params}));};"; 
     } 
?> 

Beispiel:

<?php 
$loop->addPeriodicTimer(1, eval(create_closure('pop_message', '$timer', '$cache_key, $n, &$response, &$redis_client'))); 

function pop_message($params) 
     {extract($params, EXTR_REFS); 
      $redis_client->ZRANGE($cache_key, 0, $n) 
          ->then(//normal 
            function($data) use ($cache_key, $n, &$timer, &$response, &$redis_client) 
            {//... 
            }, 
            //exception 
            function ($e) use (&$timer, &$response, &$redis_client) 
            {//... 
            } 
           ); 
     } 
?> 
Verwandte Themen