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 연결을 구축하게 되어 연결 공유 문제는 발생하지 않습니다.
또한 이 방법은 비즈니스 코드 재로드를 지원한다는 이점이 있습니다. MyClass.php는 자식 프로세스에서 로드되기 때문에, 재로드 규칙에 따라 비즈니스가 MyClass.php를 변경한 경우 직접 재로드하여 적용할 수 있습니다.

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를 사용할 수 없습니다.