fork
하여 코드에 대한 낯설음을 덜 수 있기를 바랍니다 . .
PHP7에서 함수나 메소드는 실행될 때 zend_execute_data
구조의 execute_data->call->fbc
에 있는 반면, PHP5에서는 해당하는 것 필드가 얻는 것 는 함수를 호출하는 함수이며 둘 사이의 차이는 상대적으로 큽니다. fork
一份之后,对代码不那么陌生,更期待各位个 PR。
在 PHP7 中,函数或者方法在执行的时候都在zend_execute_data
结构体中的execute_data->call->fbc
中,而 PHP5 中对应的字段拿到的却是调用该函数的函数,二者差距比较大。
后来发现 PHP5 zend_execute_data
的opline
中查到了当前执行的函数信息,但在 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
나중에 확인해보니 현재 실행되는 함수 정보가 PHP5 zend_execute_data
의 opline
에서 발견되었는데, PHP5.4 전후의 로직에 차이가 있어서 꼭 확인해야 합니다. 다르게 처리됨 (다행히 컴파일러 오류가 발생한 필드)
첫 번째 호출은 해결되었으며, 포함된 호출이 opline
에 없고 버전도 다르고 위치도 다른 것으로 확인되었습니다. 어디서 얻었는지가 달랐고 위의 opline의 판단과 일치했습니다. 버전 번호는 여전히 다르기 때문에 물리적인 힘에 의존해서만 측정할 수 있습니다.
함수 정보의 최종 획득은 다층 판단
rrreeeopline에서 쿼리하는 것은 함수 이름뿐이고 전역 함수 테이블에서 해당 함수 포인터를 찾아야 합니다rrreee일반적으로 , PHP5는 PHP7보다 더 잘 처리됩니다. 훨씬 더 복잡합니다. 이는 또한 PHP7이 더 잘 수행되고 있음을 보여줍니다.