Weil es darum geht, den Ahnencode zu lösen Ich habe auch einige Stimmen von Freunden gehört, dass es immer noch notwendig ist, PHP5 zu unterstützen, daher habe ich in dieser Zeit hart daran gearbeitet, dieses Problem mit der Versionskompatibilität zu lösen. Unerwarteterweise ist das Problem viel schwieriger als ich erwartet hatte.
Nachdem Sie die während der Entwicklung aufgetretenen Probleme (hauptsächlich körperliche Arbeit) aufgezeichnet haben und diese teilen möchten, Interessierte Freunde können eine Kopie forken
und sich mit dem Code weniger auskennen, und ich freue mich auf Ihre PR 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
.
In PHP7 befinden sich Funktionen oder Methoden bei der Ausführung in execute_data->call->fbc
in der Struktur zend_execute_data
, während die entsprechenden in PHP5 das Feld erhalten ist die Funktion, die die Funktion aufruft, und der Unterschied zwischen den beiden ist relativ groß.
Später stellte ich fest, dass die Informationen zur aktuell ausgeführten Funktion in opline
von PHP5 zend_execute_data
gefunden wurden, es jedoch Unterschiede in der Logik vor und nach PHP5.4 gab und dies auch der Fall sein muss Anders behandelt (zum Glück der Compiler Das Feld, das den Fehler verursacht hat)
Der erste Aufruf wurde gelöst und es wurde festgestellt, dass der eingebettete Aufruf nicht in opline
war und die Version und der Ort unterschiedlich waren Wo es erhalten wurde, war anders und es stimmte mit dem Urteil der obigen Meinung überein. Die Versionsnummer ist immer noch unterschiedlich, daher können wir uns nur auf die körperliche Stärke verlassen, um es zu messen. rrreeeDie endgültige Erfassung von Funktionsinformationen ist eine mehrschichtige Beurteilung