Diversi modi di callback in PHP

In PHP, utilizzare funzioni anonime per scrivere callback è il modo più comodo, ma oltre al metodo delle funzioni anonime, PHP ha altri modi per scrivere callback. Ecco alcuni esempi di diversi modi di callback in PHP.

1. Callback con funzione anonima

<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
require_once __DIR__ . '/vendor/autoload.php';

$http_worker = new Worker("http://0.0.0.0:2345");

// Callback con funzione anonima
$http_worker->onMessage = function(TcpConnection $connection, Request $data)
{
    // Invia 'hello world' al browser
    $connection->send('hello world');
};

Worker::runAll();

2. Callback con funzione normale

<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\Request;
require_once __DIR__ . '/vendor/autoload.php';

$http_worker = new Worker("http://0.0.0.0:2345");

// Callback con funzione normale
$http_worker->onMessage = 'on_message';

// Funzione normale
function on_message(TcpConnection $connection, Request $request)
{
    // Invia 'hello world' al browser
    $connection->send('hello world');
}

Worker::runAll();

3. Metodo di classe come callback

MyClass.php

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
class MyClass{
    public function __construct(){}
    public function onWorkerStart(Worker $worker){}
    public function onConnect(TcpConnection $connection){}
    public function onMessage(TcpConnection $connection, $message) {}
    public function onClose(TcpConnection $connection){}
    public function onWorkerStop(Worker $worker){}
}

Script di avvio start.php

<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

// Carica MyClass
require_once __DIR__.'/MyClass.php';

$worker = new Worker("websocket://0.0.0.0:2346");

// Crea un oggetto
$my_object = new MyClass();

// Chiama i metodi della classe
$worker->onWorkerStart = array($my_object, 'onWorkerStart');
$worker->onConnect     = array($my_object, 'onConnect');
$worker->onMessage     = array($my_object, 'onMessage');
$worker->onClose       = array($my_object, 'onClose');
$worker->onWorkerStop  = array($my_object, 'onWorkerStop');

Worker::runAll();

Attenzione:
La struttura del codice sopra non consente di inizializzare le risorse (connessione MySQL, connessione Redis, connessione Memcache, ecc.) nel costruttore, poiché $my_object = new MyClass(); viene eseguito nel processo principale. Per esempio, se si inizializza la connessione MySQL nel processo principale, le risorse verranno ereditate dai processi figlio, e ogni processo figlio potrà operare su questa connessione al database, ma queste connessioni corrisponderanno al medesimo collegamento nel server MySQL, il che porterà a errori imprevedibili, come l'errore mysql gone away.

Se la struttura del codice sopra richiede di inizializzare risorse nel costruttore della classe, si può utilizzare il seguente approccio.
MyClass.php

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
class MyClass{
    protected $db = null;
    public function __construct(){
        // Supponiamo che la classe di connessione al database sia MyDbClass
        $db = new MyDbClass();
    }
    public function onWorkerStart(Worker $worker){}
    public function onConnect(TcpConnection $connection){}
    public function onMessage(TcpConnection $connection, $message) {}
    public function onClose(TcpConnection $connection){}
    public function onWorkerStop(Worker $worker){}
}

Script di avvio start.php

<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

$worker = new Worker("websocket://0.0.0.0:2346");

// Inizializza la classe in onWorkerStart
$worker->onWorkerStart = function($worker) {
    // Carica MyClass
    require_once __DIR__.'/MyClass.php';

    // Crea un oggetto
    $my_object = new MyClass();

    // Chiama i metodi della classe
    $worker->onConnect    = array($my_object, 'onConnect');
    $worker->onMessage    = array($my_object, 'onMessage');
    $worker->onClose      = array($my_object, 'onClose');
    $worker->onWorkerStop = array($my_object, 'onWorkerStop');
};

Worker::runAll();

Nella struttura di codice sopra, onWorkerStart viene eseguito nel processo figlio, quindi ogni processo figlio stabilisce la propria connessione MySQL, evitando così problemi di connessione condivisa.
Un ulteriore vantaggio è che il codice di business supporta il caricamento automatico. Poiché MyClass.php viene caricato nel processo figlio, secondo le regole di ricarica, le modifiche effettuate a MyClass.php verranno applicate semplicemente ricaricando.

4. Metodo statico della classe come callback

Classe statica MyClass.php

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
class MyClass{
    public static function onWorkerStart(Worker $worker){}
    public static function onConnect(TcpConnection $connection){}
    public static function onMessage(TcpConnection $connection, $message) {}
    public static function onClose(TcpConnection $connection){}
    public static function onWorkerStop(Worker $worker){}
}

Script di avvio start.php

<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';

// Carica MyClass
require_once __DIR__.'/MyClass.php';

$worker = new Worker("websocket://0.0.0.0:2346");

// Chiama i metodi statici della classe.
$worker->onWorkerStart = array('MyClass', 'onWorkerStart');
$worker->onConnect     = array('MyClass', 'onConnect');
$worker->onMessage     = array('MyClass', 'onMessage');
$worker->onClose       = array('MyClass', 'onClose');
$worker->onWorkerStop  = array('MyClass', 'onWorkerStop');

// Se la classe ha uno spazio dei nomi, la scrittura sarà simile a questa
// $worker->onWorkerStart = array('your\namesapce\MyClass', 'onWorkerStart');
// $worker->onConnect     = array('your\namesapce\MyClass', 'onConnect');
// $worker->onMessage     = array('your\namesapce\MyClass', 'onMessage');
// $worker->onClose       = array('your\namesapce\MyClass', 'onClose');
// $worker->onWorkerStop  = array('your\namesapce\MyClass', 'onWorkerStop');

Worker::runAll();

Attenzione: secondo il meccanismo di esecuzione di PHP, se non si utilizza il new per chiamare il costruttore, questo non verrà invocato; inoltre, all'interno dei metodi della classe statica non è consentito utilizzare $this.