วิธีการเขียน Callback ใน PHP
การเขียน Callback โดยใช้ฟังก์ชันนิรนามใน PHP นั้นสะดวกที่สุด แต่ใน PHP ยังมีรูปแบบการเขียน Callback อื่น ๆ อีกมากมาย ด้านล่างนี้เป็นตัวอย่างวิธีการเขียน Callback ใน PHP หลายวิธี
1. Callback ด้วยฟังก์ชันนิรนาม
<?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 ด้วยฟังก์ชันนิรนาม
$http_worker->onMessage = function(TcpConnection $connection, Request $data)
{
// ส่ง hello world ไปที่เบราว์เซอร์
$connection->send('hello world');
};
Worker::runAll();
2. Callback ด้วยฟังก์ชันปกติ
<?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 ด้วยฟังก์ชันปกติ
$http_worker->onMessage = 'on_message';
// ฟังก์ชันปกติ
function on_message(TcpConnection $connection, Request $request)
{
// ส่ง hello world ไปที่เบราว์เซอร์
$connection->send('hello world');
}
Worker::runAll();
3. วิธีการเรียกใช้เมธอดของคลาสเป็น 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){}
}
สคริปต์เริ่มต้น 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) ใน constructor เนื่องจาก $my_object = new MyClass(); ทำงานในกระบวนการหลัก ในตัวอย่างการเชื่อมต่อ MySQL การเริ่มต้นในกระบวนการหลักจะถูกสืบทอดไปยัง subprocess แต่ละตัว ซึ่งทำให้ subprocess แต่ละตัวสามารถจัดการกับการเชื่อมต่อนี้ได้ แต่การเชื่อมต่อนี้ในฝั่งบริการ MySQL จะเป็นการเชื่อมต่อเดียวกัน ส่งผลให้เกิดข้อผิดพลาดเช่น mysql gone away
หากต้องการเริ่มต้นทรัพยากรใน constructor ของคลาสตามโครงสร้างโค้ดข้างต้น สามารถใช้วิธีการดังต่อไปนี้
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 ทำงานอยู่ใน subprocess แล้ว ซึ่งหมายความว่ากระบวนการแต่ละตัวจะสร้างการเชื่อมต่อ MySQL ของตัวเองด้วย ทำให้ไม่มีปัญหาเรื่องการแชร์การเชื่อมต่อ ข้อดีอีกอย่างของวิธีนี้คือสนับสนุนการโหลดโค้ดธุรกิจใหม่ เนื่องจาก MyClass.php จะถูกโหลดใน subprocess ดังนั้นตามกฎการโหลดใหม่ หากมีการเปลี่ยนแปลงใน MyClass.php ก็สามารถโหลดใหม่ได้ทันที
4. เมธอดสถิตของคลาสเป็น Callback
คลาสสถิติ 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');
// หากคลาสมี namespace จะเขียนแบบนี้
// $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 จะไม่เรียกใช้ constructor และไม่อนุญาตให้ใช้ $this ในเมธอดของคลาสสถิติ