Một số cách viết callback trong PHP
Viết callback bằng hàm ẩn danh trong PHP là cách tiện lợi nhất, nhưng ngoài cách callback bằng hàm ẩn danh, PHP còn có những cách viết callback khác. Dưới đây là ví dụ về một số cách viết callback trong PHP.
1. Callback bằng hàm ẩn danh
<?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 bằng hàm ẩn danh
$http_worker->onMessage = function(TcpConnection $connection, Request $data)
{
// Gửi hello world đến trình duyệt
$connection->send('hello world');
};
Worker::runAll();
2. Callback bằng hàm thông thường
<?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 bằng hàm
$http_worker->onMessage = 'on_message';
// Hàm thông thường
function on_message(TcpConnection $connection, Request $request)
{
// Gửi hello world đến trình duyệt
$connection->send('hello world');
}
Worker::runAll();
3. Phương thức của lớp làm 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){}
}
Tập lệnh khởi động start.php
<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
// Tải MyClass
require_once __DIR__.'/MyClass.php';
$worker = new Worker("websocket://0.0.0.0:2346");
// Tạo một đối tượng
$my_object = new MyClass();
// Gọi các phương thức của lớp
$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();
Chú ý:
Cấu trúc mã trên không cho phép khởi tạo tài nguyên (kết nối MySQL, kết nối Redis, kết nối Memcache, v.v.) trong hàm khởi tạo, vì $my_object = new MyClass(); chạy trong tiến trình chính. Lấy MySQL làm ví dụ, nếu khởi tạo kết nối MySQL và các tài nguyên trong tiến trình chính sẽ bị các tiến trình con kế thừa, mỗi tiến trình con đều có thể thao tác với kết nối cơ sở dữ liệu này, nhưng những kết nối này trên máy chủ MySQL tương ứng chỉ là một kết nối, dẫn đến các lỗi không thể đoán trước, chẳng hạn như lỗi mysql gone away.
Nếu muốn khởi tạo tài nguyên trong hàm khởi tạo của lớp, bạn có thể sử dụng cách viết sau đây.
MyClass.php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
class MyClass{
protected $db = null;
public function __construct(){
// Giả sử lớp kết nối cơ sở dữ liệu là 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){}
}
Tập lệnh khởi động start.php
<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker("websocket://0.0.0.0:2346");
// Khởi tạo lớp trong onWorkerStart
$worker->onWorkerStart = function($worker) {
// Tải MyClass
require_once __DIR__.'/MyClass.php';
// Tạo một đối tượng
$my_object = new MyClass();
// Gọi các phương thức của lớp
$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();
Trong cấu trúc mã trên, onWorkerStart khi chạy đã thuộc về tiến trình con, nghĩa là mỗi tiến trình con sẽ tự thiết lập kết nối MySQL của riêng mình, vì vậy sẽ không có vấn đề về kết nối chia sẻ.
Điều này còn có một lợi ích là hỗ trợ reload mã nguồn nghiệp vụ. Bởi vì MyClass.php được tải trong tiến trình con, theo quy tắc reload, khi thay đổi MyClass.php thì có thể reload ngay lập tức có hiệu lực.
4. Phương thức tĩnh của lớp làm callback
Lớp tĩnh 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){}
}
Tập lệnh khởi động start.php
<?php
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
// Tải MyClass
require_once __DIR__.'/MyClass.php';
$worker = new Worker("websocket://0.0.0.0:2346");
// Gọi các phương thức tĩnh của lớp.
$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');
// Nếu lớp có không gian tên, cú pháp sẽ giống như sau
// $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();
Chú ý: Theo cơ chế hoạt động của PHP, nếu không gọi new thì sẽ không gọi hàm khởi tạo, hơn nữa phương thức của lớp tĩnh không được phép sử dụng $this.