版权声明:转载请注明来源 https://blog.csdn.net/u013702678/article/details/81160288
接着上一篇继续分析,上次分析到swoole_server的创建完成,对着我们之前的demo,现在到swoole_server的回调函数的注册流程了。
//监听连接进入事件
$serv->on('connect', function ($serv, $fd) {
echo "Client: Connect.\n";
});
//监听数据接收事件
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
$serv->send($fd, "Server: ".$data);
});
//监听连接关闭事件
$serv->on('close', function ($serv, $fd) {
echo "Client: Close.\n";
});
这几个注册函数对到swoole底层实现,其实是同一个,即swoole_server的on函数,只是传入参数不同。这三个回调函数的参数也是类似,有一个动作名和回调函数实现,下面我们进入代码看实现,这里基本上都是PHP扩展相关的实现,有些不是很清楚,后面再来完善下。
PHP_METHOD(swoole_server, on)
{
zval *name;//变量,后续用于存储PHP侧传入的动作名
zval *cb;//变量,后续用于存储PHP侧传入的回调函数指针信息
//如上一篇提到的,在new swoole_server后,swoole会缓存该对象,这里直接从缓存读取
swServer *serv = swoole_get_object(getThis());
//swoole_server已经启动,抛异常,停止程序,serv->gs->start是在swoole_server的start方法调用后设置值的
if (serv->gs->start > 0)
{
swoole_php_fatal_error(E_WARNING, "server is running. unable to register event callback function.");
RETURN_FALSE;
}
//PHP扩展API,用于获取PHP侧的输入,输入的动作名和回调函数分别放在name和cb中
if (zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "zz", &name, &cb) == FAILURE)
{
return;
}
//PHP Zend的API,用于判断cb是否可以被调用,类似PHP里面的is_callable函数,如果不可以调用,则打印日志,中断流程,这个函数同时会把这个回调函数的输入参数和实现分别通过func_name和func_cache回写回来,func_cache用于后续缓存函数
char *func_name = NULL;
zend_fcall_info_cache *func_cache = emalloc(sizeof(zend_fcall_info_cache));
if (!sw_zend_is_callable_ex(cb, NULL, 0, &func_name, NULL, func_cache, NULL TSRMLS_CC))
{
swoole_php_fatal_error(E_ERROR, "function '%s' is not callable", func_name);
efree(func_name);//释放空间
return;
}
//释放变量值
efree(func_name);
//这个函数的实现没有找到,但是预测是将char*类型的变量转换为PHP内部的zval变量
convert_to_string(name);
//定义所有的回调动作名,swoole_server可以注册的动作必须得在里面
char *callback_name[PHP_SERVER_CALLBACK_NUM] = {
"Connect",
"Receive",
"Close",
"Packet",
"Start",
"Shutdown",
"WorkerStart",
"WorkerStop",
"Task",
"Finish",
"WorkerExit",
"WorkerError",
"ManagerStart",
"ManagerStop",
"PipeMessage",
NULL,
NULL,
NULL,
NULL,
"BufferFull",
"BufferEmpty",
};
int i;
char property_name[128];//定义完整的函数名地址信息,完整的函数名类型onReceive
int l_property_name = 0;
memcpy(property_name, "on", 2);//这里先把on两个字符拷贝到property_name中
//遍历所有的动作名信息
for (i = 0; i < PHP_SERVER_CALLBACK_NUM; i++)
{
if (callback_name[i] == NULL)
{
continue;
}
//如果遍历过程中,找到和PHP侧传入的动作名相等的,则将on***的函数更新到swoole_server对象上,类似如果PHP侧传入的动作名是receive,会将onreceive注册到swoole_server对象上,当然这里还有想要函数的实现也会带过去。
if (strncasecmp(callback_name[i], Z_STRVAL_P(name), Z_STRLEN_P(name)) == 0)
{
memcpy(property_name + 2, callback_name[i], Z_STRLEN_P(name));//拷贝name信息
l_property_name = Z_STRLEN_P(name) + 2;
property_name[l_property_name] = '\0';//C语言字符串
zend_update_property(swoole_server_class_entry_ptr, getThis(), property_name, l_property_name, cb TSRMLS_CC);//更新到对象上去。
php_sw_server_callbacks[i] = sw_zend_read_property(swoole_server_class_entry_ptr, getThis(), property_name, l_property_name, 0 TSRMLS_CC);//缓存回调函数实现
php_sw_server_caches[i] = func_cache;//缓存函数实现
sw_copy_to_stack(php_sw_server_callbacks[i], _php_sw_server_callbacks[i]);
break;
}
}
//如果PHP侧输入的动作名没在swoole允许的注册名内,则抛错,中断程序
if (l_property_name == 0)
{
swoole_php_error(E_WARNING, "unknown event types[%s]", Z_STRVAL_P(name));
efree(func_cache);
RETURN_FALSE;
}
//这里如果是小于SW_SERVER_CB_onStart,也就是用户注册的动作为swoole_server执行start之前的动作,则将动作也注册到port_object对象上,这里后面再看哪里有用到。
if (i < SW_SERVER_CB_onStart)
{
zval *port_object = server_port_list.zobjects[0];
zval *retval = NULL;
sw_zval_add_ref(&port_object);
sw_zend_call_method_with_2_params(&port_object, swoole_server_port_class_entry_ptr, NULL, "on", &retval, name, cb);
}
else
{
RETURN_TRUE;
}
}