background image

却不能够使用它。

  你可以理直气壮地说,理想的解决方案就是编写良好、干净的、一致的代码。这当然不
错;但是,在一个象 PHP 解释器这样的环境中,这种观点仅对了一半。

  

 

三、 错误处理

  为了实现"跳出"对用户空间脚本及其依赖的扩展函数的一个活动请求,需要使用一
种方法来完全"跳出"一个活动请求。这是在 Zend 引擎内实现的:在一个请求的开始设置
一个"跳出"地址,然后在任何 die()或 exit()调用或在遇到任何关键错误(E_ERROR)时执
行一个 longjmp()以跳转到该"跳出"地址。

尽管这个"跳出"进程能够简化程序执行的流程,但是,在绝大多数情况下,这会意

味着将会跳过资源清除代码部分(例如 free()调用)并最终导致出现内存漏洞。现在,让我
们来考虑下面这个简化版本的处理函数调用的引擎代码:

以下为引用的内容:

  void call_function(const char *fname  

, int fname_len TSRMLS_DC){

  zend_function *fe;

  char *lcase_fname;

  /* PHP 函数名是大小写不敏感的,

  *为了简化在函数表中对它们的定位,

  *所有函数名都隐含地翻译为小写的

  */

  lcase_fname = estrndup(fname  

, fname_len);

  zend_str_tolower(lcase_fname  

, fname_len);

  if (zend_hash_find(EG(function_table),lcase_fname  

, fname_len + 1, 

(void **)&fe) == FAILURE) {

  zend_execute(fe->op_array TSRMLS_CC);

  } else {

  php_error_docref(NULL TSRMLS_CC  

, E_ERROR,"Call to undefined 

function: %s()"  

, fname);

  }

  efree(lcase_fname);

  }

  当执行到 php_error_docref()这一行时,内部错误处理器就会明白该错误级别是
critical,并相应地调用 longjmp()来中断当前程序流程并离开 call_function()函数,甚
至根本不会执行到 efree(lcase_fname)这一行。你可能想把 efree()代码行移动到
zend_error()代码行的上面;但是,调用这个 call_function()例程的代码行会怎么样呢?
fname 本身很可能就是一个分配的字符串,并且,在它被错误消息处理使用完之前,你
根本不能释放它。