Swoole 回撥函式的註冊與呼叫

jackbot發表於2019-09-29

swoole的回撥函式大致分為兩種。一種是事件回撥,是swoole啟動執行時觸發的回撥。另一種是埠回撥,這類回撥的特點是都有fd引數傳入。

//註冊回撥的函式。
static PHP_METHOD(swoole_server, on)
{
    zval *name;
    zval *cb;

    swServer *serv = (swServer *) swoole_get_object(ZEND_THIS);
    //必須是swoole呼叫start前註冊匿名函式回撥。
    if (serv->gs->start > 0)
    {
        php_swoole_fatal_error(E_WARNING, "server is running, unable to register event callback function");
        RETURN_FALSE;
    }
    //解析出回撥函式名和匿名函式
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "zz", &name, &cb) == FAILURE)
    {
        RETURN_FALSE;
    }
    //下面是做引數檢測以及格式化引數。
    char *func_name = NULL;
    zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache));
    if (!sw_zend_is_callable_ex(cb, NULL, 0, &func_name, NULL, fci_cache, NULL))
    {
        php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name);
        return;
    }
    efree(func_name);

    zend::string _event_name_ori(name);
    zend::string _event_name_tolower(zend_string_tolower(_event_name_ori.get()));
    //server_event_map裡儲存的就是所說的事件回撥。根據註冊的函式名查詢
    auto i = server_event_map.find(_event_name_tolower.to_std_string());
    if (i == server_event_map.end())
    {
        //沒找到,初步判斷為埠回撥。
        zval *port_object = server_port_list.zobjects[0];
        zval retval;
        efree(fci_cache);
        //呼叫zim_swoole_server_port_on函式。
        sw_zend_call_method_with_2_params(port_object, swoole_server_port_ce, NULL, "on", &retval, name, cb);
        RETURN_BOOL(Z_BVAL_P(&retval));
    }
    else
    {
        //找到了則更新為匿名函式。這個匿名函式回在php_swoole_onXxxx裡呼叫。
        int event_type = i->second.type;
        string property_name = "on" + i->second.name;

        zend_update_property(swoole_server_ce, ZEND_THIS, property_name.c_str(), property_name.length(), cb);

        if (server_callbacks[event_type])
        {
            efree(server_callbacks[event_type]);
        }
        server_callbacks[event_type] = fci_cache;

        RETURN_TRUE;
    }
}
//註冊埠回撥函式
static PHP_METHOD(swoole_server_port, on)
{
    char *name = NULL;
    size_t len, i;
    zval *cb;

    swoole_server_port_property *property = (swoole_server_port_property *) swoole_get_property(ZEND_THIS, 0);
    swServer *serv = property->serv;
    //環境檢測
    if (serv->gs->start > 0)
    {
        php_swoole_fatal_error(E_WARNING, "can't register event callback function after server started");
        RETURN_FALSE;
    }

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &name, &len, &cb) == FAILURE)
    {
        RETURN_FALSE;
    }
    //引數檢測
    char *func_name = NULL;
    zend_fcall_info_cache *fci_cache = (zend_fcall_info_cache *) emalloc(sizeof(zend_fcall_info_cache));
    if (!sw_zend_is_callable_ex(cb, NULL, 0, &func_name, NULL, fci_cache, NULL))
    {
        php_swoole_fatal_error(E_ERROR, "function '%s' is not callable", func_name);
        return;
    }
    efree(func_name);

    const char *callback_name[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM] = {
        "Connect",
        "Receive",
        "Close",
        "Packet",
        "Request",
        "HandShake",
        "Open",
        "Message",
        "BufferFull",
        "BufferEmpty",
    };

    char property_name[128];
    int l_property_name = 0;
    memcpy(property_name, "on", 2);
    //迴圈匹配引數名
    for (i = 0; i < PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM; i++)
    {   
        if (strncasecmp(callback_name[i], name, len) != 0)
        {
            continue;
        }
        //找到對應的回撥函式名,將函式名格式化為如onConnect等。
        memcpy(property_name + 2, callback_name[i], len);
        l_property_name = len + 2;
        property_name[l_property_name] = '\0';
        //更新埠物件的屬性用來記錄回撥函式資訊。用與將來呼叫
        zend_update_property(swoole_server_port_ce, ZEND_THIS, property_name, l_property_name, cb);
        property->callbacks[i] = sw_zend_read_property(swoole_server_port_ce, ZEND_THIS, property_name, l_property_name, 0);
        sw_copy_to_stack(property->callbacks[i], property->_callbacks[i]);
        if (property->caches[i])
        {
            efree(property->caches[i]);
        }
        property->caches[i] = fci_cache;
        //下面就是註冊swoole自己寫的回撥函式。使用者自定義的回撥會在下面的回撥裡呼叫。
        if (i == SW_SERVER_CB_onConnect && !serv->onConnect)
        {
            serv->onConnect = php_swoole_onConnect;
        }
        else if (i == SW_SERVER_CB_onPacket && !serv->onPacket)
        {
            serv->onPacket = php_swoole_onPacket;
        }
        else if (i == SW_SERVER_CB_onClose && !serv->onClose)
        {
            serv->onClose = php_swoole_onClose;
        }
        else if (i == SW_SERVER_CB_onBufferFull && !serv->onBufferFull)
        {
            serv->onBufferFull = php_swoole_onBufferFull;
        }
        else if (i == SW_SERVER_CB_onBufferEmpty && !serv->onBufferEmpty)
        {
            serv->onBufferEmpty = php_swoole_onBufferEmpty;
        }
        else if (i == SW_SERVER_CB_onMessage || i == SW_SERVER_CB_onRequest)
        {
            serv->onReceive = php_swoole_http_onReceive;
        }
        break;
    }

    if (l_property_name == 0)
    {
        php_swoole_error(E_WARNING, "unknown event types[%s]", name);
        efree(fci_cache);
        RETURN_FALSE;
    }
    RETURN_TRUE;
}
//自定義的閉包函式呼叫過程。閉包函式都是通過php_swoole_onXxxx呼叫。
//zend::function::call()就相當與php的call_user_func
static void php_swoole_onStart(swServer *serv)
{
    swServer_lock(serv);
    zval *zserv = (zval *) serv->ptr2;
    zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("master_pid"), serv->gs->master_pid);
    zend_update_property_long(swoole_server_ce, zserv, ZEND_STRL("manager_pid"), serv->gs->manager_pid);
    if (UNEXPECTED(!zend::function::call(server_callbacks[SW_SERVER_CB_onStart], 1, zserv, NULL, false)))
    {
        php_swoole_error(E_WARNING, "%s->onStart handler error", SW_Z_OBJCE_NAME_VAL_P(zserv));
    }
    swServer_unlock(serv);
}

void php_swoole_onConnect(swServer *serv, swDataHead *info)
{
    zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onConnect);
    if (fci_cache)
    {
        zval *zserv = (zval *) serv->ptr2;
        zval args[3];
        args[0] = *zserv;
        ZVAL_LONG(&args[1], info->fd);
        ZVAL_LONG(&args[2], info->reactor_id);
        if (UNEXPECTED(!zend::function::call(fci_cache, 3, args, NULL, SwooleG.enable_coroutine)))
        {
            php_swoole_error(E_WARNING, "%s->onConnect handler error", SW_Z_OBJCE_NAME_VAL_P(zserv));
        }
    }
}

相關文章