Parce qu'il s'agit de résoudre le code ancestral J'ai également entendu des amis dire qu'il était toujours nécessaire de prendre en charge PHP5, j'ai donc travaillé dur pour résoudre ce problème de compatibilité de version pendant cette période. De façon inattendue, le problème est beaucoup plus difficile que prévu.
Après avoir enregistré les problèmes rencontrés lors du développement (principalement le travail physique) et avoir voulu les partager, Les amis intéressés peuvent forker
une copie et devenir moins familiers avec le code 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
. .
En PHP7, les fonctions ou méthodes sont dans execute_data->call->fbc
dans la structure zend_execute_data
lorsqu'elles sont exécutées, tandis que celles correspondantes en PHP5 Ce que le champ obtient est la fonction qui appelle la fonction, et la différence entre les deux est relativement grande.
Plus tard, j'ai découvert que les informations sur la fonction actuellement exécutée ont été trouvées dans opline
de PHP5 zend_execute_data
, mais il existe des différences dans la logique avant et après PHP5.4 et doivent être traité différemment (heureusement le champ du compilateur qui a provoqué l'erreur)
Le premier appel a été résolu, et il a été constaté que l'appel intégré n'était pas dans opline
, et que la version était différente, et le L'endroit où il a été obtenu était différent, et il était cohérent avec le jugement de l'opline ci-dessus. Le numéro de version est toujours différent, nous ne pouvons donc nous fier qu'à la force physique pour le mesurer. rrreeeL'acquisition finale des informations sur la fonction est un jugement multicouche