Swoole 回调函数的注册与调用

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));
        }
    }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!