2009-01-07 15 views
7

Ich habe eine Vorliebe für jQuery/Javascript's Art der Erweiterung der Funktionalität über Schließungen. Ist es möglich, etwas Ähnliches in PHP 5.3 zu tun?Verschlüsse als Klassenmitglieder?

class Foo 
{ 
    public $bar; 
} 

$foo = new Foo; 
$foo->bar = function($baz) { echo strtoupper($baz); }; 
$foo->bar('lorem ipsum dolor sit amet'); 
// LOREM IPSUM DOLOR SIT AMET 

[Bearbeiten] durcheinander "es" und "ist" in meiner Frage. heh.

UPDATE

ich heruntergeladen 5.3a3 und es funktioniert!

class Foo 
{ 
    protected $bar; 
    public $baz; 

    public function __construct($closure) 
    { 
     $this->bar = $closure; 
    } 

    public function __call($method, $args) 
    { 
     $closure = $this->$method; 
     call_user_func_array($closure, $args); 
    } 

} 

$foo = new Foo(function($name) { echo "Hello, $name!\n"; }); 
$foo->bar('Mon'); 
// Hello, Mon! 
$foo->baz = function($s) { echo strtoupper($s); }; 
$foo->baz('the quick brown fox jumps over the lazy dog'); 
// THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 
+0

Das ist so ziemlich ein Duplikat http://stackoverflow.com/questions/146737/closures-in-php-was-genau-sind-sie-und-wann-würden-Sie-brauchen-sie-zu-verwenden-sie – troelskn

+0

Siehe auch: [Aufruf Schließung direkt Objekt Eigenschaft zugewiesen] (http: // stackoverflow .com/q/4535330/367456) – hakre

Antwort

1

PHP über create_function() können. Die Syntax ist kreuzend, obwohl sie fast vollständig nutzlos ist.

Es scheint PHP5.3+ ist/hat für closures and lambda's!

jedoch auch von den RFC

Lambda-Funktionen/Verschlüsse sind kein Weg, der dynamisch verlaufenden Klassen durch zusätzliche Methoden zur Laufzeit Unterstützung zu bekommen. Es gibt mehrere andere Möglichkeiten, dies zu tun, einschließlich der bereits vorhandenen _ _call Semantik.

+0

Ich denke, dieses Zitat beantwortet meine Frage. Ich frage mich, ob die "mehrere andere Möglichkeiten" einen Weg enthalten, der die Verschlusssyntax irgendwie ausnutzen könnte ... – monzee

1

Korrekte anonyme Funktionen/Schließungen sind nur in PHP 5.3 verfügbar, das für eine Weile nicht allgemein verfügbar sein wird - some useful information. Ich glaube nicht, dass es wirklich möglich ist, zu tun, was Sie wollen.

Sie create_function() in einem Umfang nutzen können:

$func = create_function('$baz', ' echo strtoupper($baz); '); 
$func("hello"); 

jedoch die folgende funktioniert nicht wie man erwarten würde:

$foo = new Foo; 
$foo->bar = create_function('$baz', ' echo strtoupper($baz); '); 
$foo->bar('lorem ipsum dolor sit amet'); 

Sie müssten stattdessen tun:

call_user_func($foo->bar, 'lorem ipsum dolor sit amet'); 

Sie können $this auch nicht in der erstellten Funktion beca verwenden Verwenden Sie den Bereich nicht als eine Methode.

bearbeiten

ich einige Martijn Antwort versehentlich dupliziert, als er seine bearbeitet, während ich schreibe mir war. Ich werde meine Antwort intakt aus Gründen der Vollständigkeit

Ein weiteres bearbeiten

Man könnte vielleicht auch etwas tun, wie

class Foo 
{ 
    public $bar; 

    protected $anonFuncs = array(); 

    public function create_method($name, $params, $code) { 
     if (strlen($params)) $params .= ','; 
     $params .= '$self'; 
     $this->anonFuncs[$name] = create_function($params, $code); 
    } 

    public function __call($name, $args) { 
     if (!isset($this->anonFuncs[$name])) { 
      trigger_error("No method $name", E_USER_ERROR); 
     } 

     $args[] = $this; 

     return call_user_func_array($this->anonFuncs[$name], $args); 
    } 
} 

$foo = new Foo; 
$foo->create_method('bar', '$baz', ' echo strtoupper($baz) . get_class($self); '); 
$foo->bar('lorem ipsum dolor sit amet'); 
+1

hüten Sie sich davor, benutzen Sie es nicht in einer Schleife, da es Speicher frisst. Funktionen werden nicht freigegeben, sobald sie erstellt wurden. –

+0

Putting Code in Anführungszeichen ist ... ugh. Ich hatte gehofft, die Schließung würde uns davon abbringen. – monzee

+0

ja vereinbart. Die Schließungen in 5.3 werden Sie davon abhalten. –

4

PHP 5.3 ermöglicht die Erstellung von Lambda-Funktionen und Verschlüssen, die würde verlassen ermöglicht Ihnen, Ihr Codebeispiel zu implementieren. Von den PHP RFC:

function & (parameters) use (lexical vars) { body } 

Die Funktion wird als Referenz zurückgegeben und kann als Rückruf übergeben werden.Zum Beispiel die Schaffung einer Funktion:

$lambda = function() { echo "Hello World!\n"; }; 

eine Lambda-Funktion als Callback Passing:

function replace_spaces ($text) { 
    $replacement = function ($matches) { 
     return str_replace ($matches[1], ' ', ' ').' '; 
    }; 
    return preg_replace_callback ('/(+) /', $replacement, $text); 
} 

Closures auch erlauben Sie Variablen aus dem lokalen Bereich zu importieren:

$map = function ($text) use ($search, $replacement) { 
    if (strpos ($text, $search) > 50) { 
     return str_replace ($search, $replacement, $text); 
    } else { 
     return $text; 
    } 
}; 

Wo $ search und $ replacement werden beide außerhalb des Closings deklariert und $ text ist der Funktionsparameter.

+0

Achtung, Sie definieren nicht die "Welt!" Namensraum.

+0

Ja, ich habe gelesen, dass Closures in PHP 5.3 kommen werden, ich frage mich nur, ob wir es einem Klassenmitglied zuweisen dürfen. – monzee

+0

Verschlüsse und Lambdas sind Verweise auf die Funktion (Funktionszuordnung). Sie können es beliebig zuweisen. –

3

Sie können die Methode __call nutzen, um nicht vorhandene Methodenaufrufe umzuleiten. Fügen Sie Ihrer Klasse eine Registrierung hinzu, damit Sie Methoden direkt hinzufügen/entfernen können. Aufrufkonvention für externe Funktion ist, dass der erste Parameter das Objekt ist, an das die Funktion gebunden wurde.

clas Foo { 

    private $registry; 

    public function __call($alias, $args) { 
     if (array_key_exists($alias, $this->registry)) { 
      // prepend list of parameters with $this object 
      array_unshift($args, $this); 
      return call_user_func_array($this->registry[$alias], $args) 
     } 
    } 

    public function register($method, $alias = null) { 
     $alias or $alias = $method; // use original method name if no alias 
     $this->registry[$alias] = $method; 
    } 

    public function unregister($alias) { 
     unset($this->registry[$alias]); 
    } 

} 

Stellen Funktion clone_object das Array von geklonten Objekten zurückgibt. Der erste Parameter ist das Objekt, das geklont werden soll, und der zweite Parameter ist die Anzahl der Klone.

function clone_object($foo, $n) { 
    $a = array(); 
    while ($n--) { 
     $a[] = clone $foo; 
    } 
    return $a; 
} 

Nun, auf diese Weise können Sie die Methode clone_object in Klasse injizieren Foo und geben ihm einen Alias-Namen bar:

$f = new Foo(); 
$f->register('clone_object', 'bar'); 
$f->bar(5); // this will return array of 5 cloned objects 

In der letzten Zeile Methode aufrufen bar wird Umleitung verursachen clone_object zu funktionieren. Beachten Sie, dass die Aufrufkonvention den Parameter $this in die Parameterliste einfügt, sodass (5) in ($this, 5) geändert wird.

Der Nachteil besteht darin, dass externe Funktionen nicht mit privaten/geschützten Eigenschaften und Methoden arbeiten können.

Nur ein abschließendes Wort, wenn Sie eine Lösung denken können, die Objekte nicht im laufenden Betrieb ändert, sollten Sie es wahrscheinlich verwenden. Das obige ist eine schwarze Zauberei und sollte mit äußerster Vorsicht verwendet werden.

+0

Danke, das ist ein interessanter Ausschnitt. – monzee

0

die Funktion erstellen Verwenden Sie so etwas wie tun könnte:

public function __set($methodname, $function) 
{ 
    $this->methods[$methodname] = $function; 
} 

public function __get($methodname) 
{ 
    return $this->methods[$methodname]; 
} 

public function __call($method, $args) 
{ 
    $funct = $this->{$method}; 
    //$args[] = $this; --to add the current object as a parameter option 
    return call_user_func_array($funct, $args); 
} 

Hinweis:

$Dynamic->lambda = create_function('$var', 'return $var." world."; '); 
$classic = $Dynamic->lambda("hello"); // classic would contain "hello world." 

Der Klassencode etwas wie enthalten würde, dass Sie nicht in der Lage, das Objekt Bereich zu verwenden ($this Variable) in Ihrer create-Funktion, da im Grunde genommen nur eine Objektschnittstelle zu create_function verwendet wird, die sich im globalen Namespace befindet. Sie können jedoch problemlos eine Instanz des Objekts an die Parameterliste anfügen und in die Klasse ziehen, aber dann hätten Sie nur Zugriff auf die öffentlichen Methoden der eingefügten Klasse.

0

Seit PHP5.4 Release Sie noch mehr tun können:

  1. dynamisch fügen Sie Ihre Methoden, um Objekte
  2. Verwenden diese $ innerhalb dieser Methoden
  3. Inherit ein dynamisches Objekt aus einem anderen

Grundidee ist hier implementiert: https://github.com/ptrofimov/jslikeobject

0

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

<?php 
function create_closure($fun, $args, $uses) 
     {$params=explode(',', $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) 
            {//... 
            } 
           ); 
     } 
?>