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));
}
}
}