Wie implementiere ich einen Rückruf in PHP?

181

Wie werden Rückrufe in PHP geschrieben?

Nick Stinemates
quelle
1
Ich werde eine andere Frage mit dieser verknüpfen , weil ich versucht habe, eine Schließung zu nennen.
Akinuri

Antworten:

173

Das Handbuch verwendet die Begriffe "Rückruf" und "aufrufbar" synonym. "Rückruf" bezieht sich jedoch traditionell auf einen Zeichenfolgen- oder Arraywert, der wie ein Funktionszeiger fungiert und auf eine Funktion oder Klassenmethode für zukünftige Aufrufe verweist. Dies hat einige Elemente der funktionalen Programmierung seit PHP 4 ermöglicht. Die Aromen sind:

$cb1 = 'someGlobalFunction';
$cb2 = ['ClassName', 'someStaticMethod'];
$cb3 = [$object, 'somePublicMethod'];

// this syntax is callable since PHP 5.2.3 but a string containing it
// cannot be called directly
$cb2 = 'ClassName::someStaticMethod';
$cb2(); // fatal error

// legacy syntax for PHP 4
$cb3 = array(&$object, 'somePublicMethod');

Dies ist eine sichere Methode, um aufrufbare Werte im Allgemeinen zu verwenden:

if (is_callable($cb2)) {
    // Autoloading will be invoked to load the class "ClassName" if it's not
    // yet defined, and PHP will check that the class has a method
    // "someStaticMethod". Note that is_callable() will NOT verify that the
    // method can safely be executed in static context.

    $returnValue = call_user_func($cb2, $arg1, $arg2);
}

In modernen PHP-Versionen können die ersten drei oben genannten Formate direkt als aufgerufen werden $cb(). call_user_funcund call_user_func_arrayunterstütze alle oben genannten.

Siehe: http://php.net/manual/en/language.types.callable.php

Anmerkungen / Vorsichtsmaßnahmen:

  1. Wenn die Funktion / Klasse einen Namespace hat, muss die Zeichenfolge den vollständig qualifizierten Namen enthalten. Z.B['Vendor\Package\Foo', 'method']
  2. call_user_funcunterstützt die Übergabe von Nichtobjekten als Referenz nicht, daher können Sie call_user_func_arrayden Rückruf entweder verwenden oder in späteren PHP-Versionen in einer Variablen speichern und die direkte Syntax verwenden : $cb();
  3. Objekte mit einer __invoke()Methode (einschließlich anonymer Funktionen) fallen unter die Kategorie "aufrufbar" und können auf die gleiche Weise verwendet werden, aber ich persönlich verbinde diese nicht mit dem alten Begriff "Rückruf".
  4. Das Erbe create_function()erstellt eine globale Funktion und gibt ihren Namen zurück. Es ist ein Wrapper für eval()und anonyme Funktionen sollten stattdessen verwendet werden.
Steve Clay
quelle
In der Tat ist die Verwendung der Funktion der richtige Weg, dies zu tun. Wenn Sie eine Variable verwenden und sie dann einfach aufrufen, wie in der akzeptierten Antwort vorgeschlagen, ist dies cool, hässlich und lässt sich nicht gut mit Code skalieren.
icco
4
Akzeptierte Antwort geändert. Mit Kommentaren einverstanden, ist dies eine großartige Antwort.
Nick Stinemates
Für Leser aus der Python-Programmierung wäre es hilfreich, in der Antwort darauf hinzuweisen, dass es sich 'someGlobalFunction'tatsächlich um eine definierte Funktion handelt.
TMOTTM
1
Ab PHP 5.3 gibt es Schließungen, siehe Bart van Heukeloms Antwort . Es ist viel einfacher und "Standard" als all dieses alte Durcheinander.
wirklich schön
Ja, ich erwähne in meiner Antwort anonyme Funktionen, aber das OP hat nach "Rückruf" (2008) gefragt, und diese Rückrufe älteren Stils werden in Tonnen von PHP-Codebasen immer noch sehr häufig verwendet.
Steve Clay
74

Mit PHP 5.3 können Sie dies jetzt tun:

function doIt($callback) { $callback(); }

doIt(function() {
    // this will be done
});

Endlich ein schöner Weg, es zu tun. Eine großartige Ergänzung zu PHP, denn Rückrufe sind fantastisch.

Bart van Heukelom
quelle
1
Dies muss die beste Antwort sein.
GROVER.
30

Die Implementierung eines Rückrufs erfolgt auf diese Weise

// This function uses a callback function. 
function doIt($callback) 
{ 
    $data = "this is my data";
    $callback($data); 
} 


// This is a sample callback function for doIt(). 
function myCallback($data) 
{ 
    print 'Data is: ' .  $data .  "\n"; 
} 


// Call doIt() and pass our sample callback function's name. 
doIt('myCallback');

Anzeigen: Daten sind: Dies sind meine Daten

Nick Stinemates
quelle
21
Oh mein Gott. Ist das der Standard? Das ist schrecklich!
Nick Retallack
Es gibt einige andere Möglichkeiten, wie oben gezeigt. Ich dachte wirklich das gleiche.
Nick Stinemates
3
@ Nick Retallack, ich sehe nicht, was daran so schrecklich ist. Für die mir bekannten Sprachen wie JavaScript und C # können alle ihre Rückruffunktion in einem solchen Muster strukturieren. Ich komme aus JavaScirpt und C # und bin es wirklich nicht gewohnt ,_user_func () aufzurufen. Ich habe das Gefühl, dass ich mich an PHP anpassen muss, anstatt umgekehrt.
Antony
4
@Antony Ich habe Einwände gegen die Tatsache erhoben, dass Strings Funktionszeiger in dieser Sprache sind. Ich habe diesen Kommentar vor drei Jahren gepostet, daher bin ich mittlerweile ziemlich daran gewöhnt, aber ich denke, PHP ist die einzige mir bekannte Sprache (außer Shell-Scripting), in der dies der Fall ist.
Nick Retallack
1
@Antony Ich bevorzuge die gleiche Syntax wie in Javascript. Ich verstehe also nicht einmal, warum Leute verwenden wollen, call_user_func()wenn sie eine Syntax haben, die es ihnen ermöglicht, Funktionen dynamisch aufzurufen und Rückrufe zu tätigen. Ich stimme mit Ihnen ein!
Botenvouwer
9

Ein raffinierter Trick, den ich kürzlich gefunden habe, ist die Verwendung von PHPs create_function(), um eine anonyme / Lambda-Funktion für die einmalige Verwendung zu erstellen. Es ist nützlich für die PHP - Funktionen wie array_map(), preg_replace_callback()oder usort()dass die Verwendung Rückrufe für benutzerdefinierte Verarbeitung. Es sieht ziemlich ähnlich aus wie eval()unter der Decke, aber es ist immer noch eine nette funktionale Art, PHP zu verwenden.

Yukondude
quelle
6
Leider spielt der Garbage Collector mit diesem Konstrukt nicht sehr gut, was zu potenziellen Speicherlecks führt. Wenn Sie keine Leistung erbringen möchten, vermeiden Sie create_function ().
Fuxia
1
Autsch. Danke für die Warnung.
Yukondude
Würde es Ihnen etwas ausmachen, die Antwort mit der PHP 7.4-Version (Pfeilfunktionen) zu aktualisieren und eine Warnung über veraltete create_function()Elemente hinzuzufügen ?
Dharman
7

Sie möchten überprüfen, ob Ihr Anruf gültig ist. Im Fall einer bestimmten Funktion möchten Sie beispielsweise überprüfen, ob die Funktion vorhanden ist:

function doIt($callback) {
    if(function_exists($callback)) {
        $callback();
    } else {
        // some error handling
    }
}
SeanDowney
quelle
Was ist, wenn Callback keine Funktion ist, sondern ein Array, das Objekt und Methode enthält?
d -_- b
3
oder besser gesagtis_callable( $callback )
Roko C. Buljan
1
Beides sind gute Vorschläge. Sie müssen Ihre eigene Implementierung überprüfen, wie Sie einen Rückruf durchführen. Ich hatte gehofft, davor zu warnen, etwas anzurufen, das es nicht gab und das tödlich war. Ich machte die Antwort allgemeiner
SeanDowney
5

create_functionhat in einer Klasse nicht für mich gearbeitet. Ich musste benutzencall_user_func .

<?php

class Dispatcher {
    //Added explicit callback declaration.
    var $callback;

    public function Dispatcher( $callback ){
         $this->callback = $callback;
    }

    public function asynchronous_method(){
       //do asynch stuff, like fwrite...then, fire callback.
       if ( isset( $this->callback ) ) {
            if (function_exists( $this->callback )) call_user_func( $this->callback, "File done!" );
        }
    }

}

Dann zu verwenden:

<?php 
include_once('Dispatcher.php');
$d = new Dispatcher( 'do_callback' );
$d->asynchronous_method();

function do_callback( $data ){
   print 'Data is: ' .  $data .  "\n";
}
?>

[Bearbeiten] Eine fehlende Klammer wurde hinzugefügt. Außerdem habe ich die Rückrufdeklaration hinzugefügt, ich bevorzuge es so.

Goliaton
quelle
1
Benötigt die Dispatcher-Klasse kein Attribut, damit $ this-> callback = $ callback funktioniert?
James P.
@ James Poulson: PHP ist eine dynamische Sprache, also funktioniert es. Aber ich war faul. Normalerweise erkläre ich Eigenschaften, die jedem das Leben erleichtern. Ihre Frage hat mich dazu gebracht, diesen Code noch einmal zu betrachten und einen Syntaxfehler zu entdecken, du. Danke
Goliatone
Ich wusste nicht, dass das möglich ist. Danke für die Antwort :) .
James P.
Wäre es nicht sicherer, die Funktion do_callback zu deklarieren, bevor das Dispatcher-Objekt erstellt und die asynchrone Methode aufgerufen wird?
Ejectamenta
3

Ich erschrecke jedes Mal, wenn ich create_function()in PHP benutze .

Parameter sind eine durch Koma getrennte Zeichenfolge, der gesamte Funktionskörper in einer Zeichenfolge ... Argh ... Ich denke, sie hätten es nicht hässlicher machen können, selbst wenn sie es versucht hätten.

Leider ist es die einzige Wahl, wenn eine benannte Funktion erstellt wird, die Mühe nicht wert ist.

Klopfen
quelle
1
Und natürlich ist es eine Laufzeit-String-Auswertung, sodass sie beim Kompilieren nicht auf gültige Syntax oder irgendetwas anderes überprüft wird.
Hobbs
Diese Antwort ist seit 2 Jahren veraltet. create_function()ist jetzt veraltet und sollte nicht verwendet werden.
Dharman
2

Für diejenigen, denen es nicht wichtig ist, die Kompatibilität mit PHP zu beeinträchtigen < 5.4, würde ich die Verwendung von Typhinweisen empfehlen, um eine sauberere Implementierung zu erzielen.

function call_with_hello_and_append_world( callable $callback )
{
     // No need to check $closure because of the type hint
     return $callback( "hello" )."world";
}

function append_space( $string )
{
     return $string." ";
}

$output1 = call_with_hello_and_append_world( function( $string ) { return $string." "; } );
var_dump( $output1 ); // string(11) "hello world"

$output2 = call_with_hello_and_append_world( "append_space" );
var_dump( $output2 ); // string(11) "hello world"

$old_lambda = create_function( '$string', 'return $string." ";' );
$output3 = call_with_hello_and_append_world( $old_lambda );
var_dump( $output3 ); // string(11) "hello world"
Kendall Hopkins
quelle
Die Warnung create_function() wurde ab PHP 7.2.0 VERRINGERT. Es wird dringend davon abgeraten, sich auf diese Funktion zu verlassen.
Dharman