Come posso implementare un callback in PHP?


181

Come vengono scritti i callback in PHP?


1
Collegherò un'altra domanda a questa, perché stavo cercando di chiamare una chiusura.
akinuri,

Risposte:


173

Il manuale usa i termini "callback" e "callable" in modo intercambiabile, tuttavia, "callback" si riferisce tradizionalmente a un valore di stringa o matrice che agisce come un puntatore a funzione , facendo riferimento a una funzione o a un metodo di classe per invocazioni future. Ciò ha consentito alcuni elementi di programmazione funzionale da PHP 4. I sapori sono:

$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');

Questo è un modo sicuro per utilizzare i valori richiamabili in generale:

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);
}

Le moderne versioni di PHP consentono di richiamare direttamente i primi tre formati sopra $cb(). call_user_funce call_user_func_arraysupporta tutto quanto sopra.

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

Note / avvertimenti:

  1. Se la funzione / classe ha lo spazio dei nomi, la stringa deve contenere il nome completo. Per esempio['Vendor\Package\Foo', 'method']
  2. call_user_funcnon supporta il passaggio non-oggetti con riferimento, in modo da poter usare sia call_user_func_arrayo, nelle versioni di PHP successive, Salva la richiamata ad una var e utilizzare la sintassi diretta: $cb();
  3. Gli oggetti con un __invoke()metodo (comprese le funzioni anonime) rientrano nella categoria "richiamabile" e possono essere utilizzati allo stesso modo, ma personalmente non li associo al termine "callback" legacy.
  4. L'eredità create_function()crea una funzione globale e restituisce il suo nome. È un wrapper per eval()e dovrebbero essere usate funzioni anonime.

In effetti, l'uso della funzione è il modo corretto di farlo. Mentre usi una variabile e poi la chiami, come suggerito nella risposta accettata è bello, è brutto e non si ridimensionerà bene con il codice.
Icco,

4
Risposta accettata modificata. D'accordo con i commenti, questa è un'ottima risposta.
Nick Stinemates

Sarebbe utile per i lettori della programmazione Python sottolineare nella risposta che in 'someGlobalFunction'effetti è una funzione definita.
TMOTTM

1
A partire da PHP 5.3, ci sono chiusure, vedi la risposta di Bart van Heukelom . È molto più semplice e "standard" di tutto questo pasticcio legacy.
reallynice

Sì, menziono funzioni anonime nella mia risposta, ma l'OP ha chiesto "callback" (nel 2008) e questi callback vecchio stile sono ancora molto utilizzati in tonnellate di basi di codice PHP.
Steve Clay,

74

Con PHP 5.3, ora puoi farlo:

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

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

Finalmente un bel modo di farlo. Un'ottima aggiunta a PHP, perché i callback sono fantastici.


1
Questa deve essere la risposta migliore.
GROVER.

30

L'implementazione di un callback avviene in questo modo

// 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');

Visualizza: I dati sono: questi sono i miei dati


21
Oh mio Dio. È quello lo standard? È terribile!
Nick Retallack,

Ci sono alcuni altri modi per farlo come mostrato sopra. Ho pensato davvero lo stesso.
Nick Stinemates

3
@Nick Retallack, non vedo cosa sia così orribile al riguardo. Per le lingue che conosco, come JavaScript e C #, tutti possono strutturare la loro funzione di callback in questo schema. Provenendo da JavaScirpt e C #, non sono davvero abituato a call_user_func (). Mi fa sentire come se dovessi adattarmi a PHP, invece del contrario.
Antony,

4
@Antony Stavo obiettando sul fatto che le stringhe sono puntatori di funzioni in questo linguaggio. Ho pubblicato quel commento tre anni fa, quindi ormai ci sono abbastanza abituato, ma penso che PHP sia l'unica lingua che conosco (oltre agli script di shell) in cui questo è il caso.
Nick Retallack,

1
@Antony Preferisco usare la stessa sintassi che uso in JavaScript. Quindi non capisco nemmeno perché le persone vogliono usare call_user_func()Quando hanno una sintassi che consente loro di chiamare dinamicamente le funzioni ed effettuare callback. Sono d'accordo con te!
botenvouwer,

9

Un trucco ingegnoso che ho trovato di recente è usare i PHP create_function()per creare una funzione anonima / lambda per l'uso one-shot. E 'utile per le funzioni PHP come array_map(), preg_replace_callback(), o usort()che utilizzano callback per l'elaborazione personalizzata. Sembra più o eval()meno un sottocoperta, ma è comunque un bel modo funzionale di usare PHP.


6
Sfortunatamente, il garbage collector non gioca molto bene con questo costrutto producendo potenziali perdite di memoria. Se sei fuori per prestazioni, evita create_function ().
fuxia,

1
Ahia. Grazie per il testa a testa.
yukondude,

Ti dispiacerebbe aggiornare la risposta con la versione di PHP 7.4 (funzioni freccia) e aggiungere un avviso su obsoleto create_function()?
Dharman


7

Dovrai verificare qualunque sia la tua chiamata valida. Ad esempio, nel caso di una funzione specifica, dovrai verificare e vedere se la funzione esiste:

function doIt($callback) {
    if(function_exists($callback)) {
        $callback();
    } else {
        // some error handling
    }
}

Cosa succede se il callback non è una funzione, ma un array contenente oggetto e metodo?
d -_- b

3
o megliois_callable( $callback )
Roko C. Buljan,

1
Entrambi sono buoni suggerimenti - Sarà necessario verificare la propria implementazione particolare di come si effettua una richiamata. Speravo di mettere in guardia dal chiamare qualcosa che non esisteva causando un fatale. Ho reso la risposta più generale
SeanDowney il

5

create_functionnon ha funzionato per me all'interno di una classe. Ho dovuto usare call_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!" );
        }
    }

}

Quindi, per usare:

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

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

[Modifica] Aggiunta una parentesi mancante. Inoltre, ha aggiunto la dichiarazione di richiamata, la preferisco così.


1
La classe Dispatcher non richiede un attributo per $ this-> callback = $ callback per funzionare?
James P.

@james poulson: PHP è un linguaggio dinamico, quindi funziona. Ma ero pigro. Di solito dichiaro proprietà, facilita la vita a tutti. La tua domanda mi ha fatto rivedere quel codice e individuare un errore di sintassi, tu. Grazie
goliatone,

Non sapevo che fosse possibile. Grazie per la risposta :) .
James P.

Non sarebbe più sicuro dichiarare la funzione do_callback prima di creare l'oggetto Dispatcher e chiamare il metodo asincrono?
ejectamenta,

3

Io rabbrividisco ogni volta che uso create_function()in php.

I parametri sono una stringa separata da coma, l'intero corpo della funzione in una stringa ... Argh ... Penso che non avrebbero potuto renderlo più brutto anche se ci avessero provato.

Sfortunatamente, è l'unica scelta quando la creazione di una funzione con nome non vale la pena.


1
E, naturalmente, è la stringa di runtime eval, quindi non viene verificata la sintassi valida o qualsiasi altra cosa al momento della compilazione.
Hobbs,

Questa risposta è stata superata negli ultimi 2 anni. create_function()è ora obsoleto e non deve essere utilizzato.
Dharman

2

Per coloro che non si preoccupano di interrompere la compatibilità con PHP < 5.4, suggerirei di utilizzare il suggerimento sul tipo per realizzare un'implementazione più pulita.

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"

L'avvertimento create_function() è stato DEPRECATO a partire da PHP 7.2.0. Affidarsi a questa funzione è altamente scoraggiato.
Dharman
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.