PHP幾種回調寫法
PHP 裡通過匿名函數寫回調是最方便的,但除了匿名函數方式的回調,PHP 還有其它的回調寫法。以下是 PHP 幾種回調寫法的範例。
1、匿名函數回調
<?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");
// 匿名函數回調
$http_worker->onMessage = function(TcpConnection $connection, Request $data)
{
// 向瀏覽器發送 hello world
$connection->send('hello world');
};
Worker::runAll();
2、普通函數回調
<?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");
// 匿名函數回調
$http_worker->onMessage = 'on_message';
// 普通函數
function on_message(TcpConnection $connection, Request $request)
{
// 向瀏覽器發送 hello world
$connection->send('hello world');
}
Worker::runAll();
3、類方法作為回調
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){}
}
啟動腳本 start.php
<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
// 载入 MyClass
require_once __DIR__.'/MyClass.php';
$worker = new Worker("websocket://0.0.0.0:2346");
// 創建一個對象
$my_object = new MyClass();
// 調用類的方法
$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();
注意:
以上的代碼結構不允許在建構函數裡初始化資源(MySQL 連接、Redis 連接、Memcache 連接等),因為 $my_object = new MyClass(); 運行在主進程。以 MySQL 為例,在主進程初始化的 MySQL 連接等資源會被子進程繼承,每個子進程都可以操作這個資料庫連接,但這些連接在 MySQL 服務端對應的是同一個連接,會發生不可預期的錯誤,例如 mysql gone away 錯誤。
以上代碼結構如果需要在類的建構函數裡初始化資源,可以採用以下寫法。
MyClass.php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
class MyClass{
protected $db = null;
public function __construct(){
// 假設資料庫連接類是 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){}
}
啟動腳本 start.php
<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker("websocket://0.0.0.0:2346");
// 在 onWorkerStart 裡初始化類
$worker->onWorkerStart = function($worker) {
// 载入 MyClass
require_once __DIR__.'/MyClass.php';
// 創建一個對象
$my_object = new MyClass();
// 調用類的方法
$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();
上面代碼結構中 onWorkerStart 運行時已經是屬於子進程,等於每個子進程各自建立自己的 MySQL 連接,所以不會有共享連接的問題。
這樣還有一個好處就是支持業務代碼 reload。因為 MyClass.php 是在子進程載入的,根據 reload 規則業務更改 MyClass.php 後直接 reload 即可生效。
4、類的靜態方法作為回調
靜態類 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){}
}
啟動腳本 start.php
<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
// 载入 MyClass
require_once __DIR__.'/MyClass.php';
$worker = new Worker("websocket://0.0.0.0:2346");
// 調用類的靜態方法。
$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');
// 如果類帶命名空間,則是類似這樣的寫法
// $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();
注意:根據 PHP 的運行機制,如果沒用調用 new 則不會調用建構函數,另外靜態類的方法裡不允許使用 $this。