經曆了兩周的疊代,Deliverer 目前更新到了 1.0.8 https://github.com/zhoumengkang/deliverer
因為是為了解決祖傳代碼的問題,也聽到群友的一些聲音,還是很有必要支援下 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
檢視完整的調用棧。
如果覺得有這興趣,就請給個星星吧,