乐趣区

关于php:Deliverer-108-支持-PHP5-了

经验了两周的迭代,Deliverer 目前更新到了 1.0.8 https://github.com/zhoumengka…

因为是为了解决祖传代码的问题,也听到群友的一些声音,还是很有必要反对下 PHP5 的,所以这段时间始终在着力解决这个版本兼容问题,没想到问题比拟我预计的要难很多。

记录下开发中遇到的问题(次要是体力活)也想分享之后,有趣味的敌人能够 fork 一份之后,对代码不那么生疏,更期待各位个 PR

在 PHP7 中,函数或者办法在执行的时候都在 zend_execute_data 构造体中的 execute_data->call->fbc 中,而 PHP5 中对应的字段拿到的却是调用该函数的函数,二者差距比拟大。

起初发现 PHP5 zend_execute_dataopline 中查到了以后执行的函数信息,但在 PHP5.4 前后逻辑还有差别,须要区别对待(还好编译器提醒报错的字段)

#if PHP_VERSION_ID < 50400
#define OP1_FUNCTION_PTR(n) (&(n)->op1.u.constant)
#else
#define OP1_FUNCTION_PTR(n) ((n)->op1.zv)
#endif

首次调用解决了,发现内嵌的调用,又不在 opline 外面了,而且版本不一样,获得中央也不一样,并且和下面的 opline 的判断版本号还不一样,这就只能靠膂力来测了。

#if PHP_VERSION_ID < 50500
    if (execute_data->fbc != NULL)
    {fbc = execute_data->fbc;}
#else
    if (execute_data->call != NULL && execute_data->call->fbc != NULL)
    {fbc = execute_data->call->fbc;}
#endif

最终获取函数信息就是多层判断

    zend_function *fbc;

#if PHP_VERSION_ID < 70000
#if PHP_VERSION_ID < 50500
    if (execute_data->fbc != NULL)
    {fbc = execute_data->fbc;}
#else
    if (execute_data->call != NULL && execute_data->call->fbc != NULL)
    {fbc = execute_data->call->fbc;}
#endif
    if (fbc == NULL)
    {fbc = get_function_from_opline(execute_data->opline);
    }
#else
    if (execute_data->call != NULL && execute_data->call->func != NULL)
    {fbc = execute_data->call->fbc;}
#endif

在从 opline 里查问到的只是函数的名字,须要再去全局函数表里找到对应的函数指针

static zend_function *get_function_from_opline(zend_op *opline)
{
    zend_function *fbc;

    zval *function_name = OP1_FUNCTION_PTR(opline);

    if (Z_STRVAL_P(function_name) == NULL)
    {return NULL;}

    if (zend_hash_find(EG(function_table), Z_STRVAL_P(function_name), Z_STRLEN_P(function_name) + 1, (void **)&fbc) ==
        FAILURE)
    {return NULL;}

    return fbc;
}

整体来说 PHP5 的解决要比 PHP7 简单很多,这也阐明 PHP7 做的更好了,点赞。

这些就是这几天的开发工作,可能还有很多理论的线上环境没有遇到过,如果你有趣味,能够应用这个工具来解决问题,也能够一起来欠缺这个小工具。

下周或者下下周我将加上 watch 性能,反对函数和办法的参数打印。次要是类比 Java 的 Arthas,因为在其中 trace 和 watch 性能是我最罕用的,尽管 PHP 能够线上间接批改加日志,然而这样毕竟不标准,走公布流程又太慢,我想这个 watch 性能还是很有必要的,大略是

$ ./bin/deliverer -w foo -n 3

示意监控 foo 函数 3 次调用后退出,并且打印出 deliverer-request-id 和其入参,能够再依据 deliverer-request-id 查看残缺的调用栈。

如果感觉有这趣味,就请给个星星吧,https://github.com/zhoumengka…

装置和应用上有任何问题,欢送加我微信 zhoumengkang 前来骚扰,暗号:deliverer

退出移动版