手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表分类:PHP

PHP的XSS攻击过滤函数

XSS攻击在最近很是流行,往往在某段代码里一不小心就会被人放上XSS攻击的代码,看到国外有人写上了函数,咱也偷偷懒,悄悄的贴上来。。。
原文如下:
The goal of this function is to be a generic function that can be used to parse almost any input and render it XSS safe. For more information on actual XSS attacks, check out http://ha.ckers.org/xss.html. Another excellent site is the XSS Database which details each attack and how it works.

PHP代码
  1. <?php  
  2. function RemoveXSS($val) {  
  3.    // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed  
  4.    // this prevents some character re-spacing such as <java\0script>  
  5.    // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs  
  6.    $val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/'''$val);  
  7.      
  8.    // straight replacements, the user should never need these since they're normal characters  
  9.    // this prevents like <IMG SRC=@avascript:alert('XSS')>  
  10.    $search = 'abcdefghijklmnopqrstuvwxyz'; 
  11.    $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';  
  12.    $search .= '1234567890!@#$%^&*()'; 
  13.    $search .= '~`";:?+/={}[]-_|\'\\'; 
  14.    for ($i = 0; $i < strlen($search); $i++) { 
  15.       // ;? matches the ;, which is optional 
  16.       // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars 
  17.     
  18.       // @ @ search for the hex values 
  19.       $val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; 
  20.       // @ @ 0{0,7} matches '0' zero to seven times  
  21.       $val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; 
  22.    } 
  23.     
  24.    // now the only remaining whitespace attacks are \t, \n, and \r 
  25.    $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base'); 
  26.    $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); 
  27.    $ra = array_merge($ra1, $ra2); 
  28.     
  29.    $found = true; // keep replacing as long as the previous round replaced something 
  30.    while ($found == true) { 
  31.       $val_before = $val; 
  32.       for ($i = 0; $i < sizeof($ra); $i++) { 
  33.          $pattern = '/'; 
  34.          for ($j = 0; $j < strlen($ra[$i]); $j++) { 
  35.             if ($j > 0) { 
  36.                $pattern .= '(';  
  37.                $pattern .= '(&#[xX]0{0,8}([9ab]);)'; 
  38.                $pattern .= '|';  
  39.                $pattern .= '|(&#0{0,8}([9|10|13]);)'; 
  40.                $pattern .= ')*'; 
  41.             } 
  42.             $pattern .= $ra[$i][$j]; 
  43.          } 
  44.          $pattern .= '/i';  
  45.          $replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag  
  46.          $val = preg_replace($pattern$replacement$val); // filter out the hex tags  
  47.          if ($val_before == $val) {  
  48.             // no replacements were made, so exit the loop  
  49.             $found = false;  
  50.          }  
  51.       }  
  52.    }  
  53.    return $val;  
  54. }   

经过这样的过滤后,应该被攻击的机会会少上很多吧?试试看呢?

Tags: php, xss, filter, function

使用PHP得到所有的HTTP请求头

PHP中一般采用getallheaders来获取头部,但事实上,有些模式下是获取不到的(以前真没有注意过在fastcgi下这个函数不能用,当然我现在也没有测试。是老王说的)

他说:

在PHP里,想要得到所有的HTTP请求头,可以使用getallheaders方法,不过此方法并不是在任何环境下都存在,比如说,你使用fastcgi方式运行PHP的话,就没有这个方法,所以说我们还需要考虑别的方法,幸运的是$_SERVER里有我们想要的东西,它里面键名以HTTP_开头的就是HTTP请求头:

$headers = array();
foreach (
$_SERVER as $key => $value) {
    if (
'HTTP_' == substr($key, 0, 5)) {
       
$headers[str_replace('_', '-', substr($key, 5))] = $value;
    }
}


代码很简单,需要说明的是RFC里明确指出了信息头的名字是不区分大小写的。

不过并不是所有的HTTP请求头都是以HTTP_开头的的键的形式存在与$_SERVER里,比如说Authorization,Content-Length,Content-Type就不是这样,所以说为了取得所有的HTTP请求头,还需要加上下面这段代码:

if (isset($_SERVER['PHP_AUTH_DIGEST'])) {
    
$header['AUTHORIZATION'] = $_SERVER['PHP_AUTH_DIGEST']);
} elseif (isset(
$_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
    
$header['AUTHORIZATION'] = base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW']));
}
if (isset(
$_SERVER['CONTENT_LENGTH'])) {
    
$header['CONTENT-LENGTH'] = $_SERVER['CONTENT_LENGTH'];
}
if (isset(
$_SERVER['CONTENT_TYPE'])) {
    
$header['CONTENT-TYPE'] = $_SERVER['CONTENT_TYPE'];
}


搞定!

网址为:http://hi.baidu.com/thinkinginlamp/blog/item/c0bff01f3beb66f2e1fe0b7e.html

Tags: php, header, getallheaders

Jadu: 将 PHP 编译成 .NET

新闻来源:itnews.com.au
内容管理公司 Jadu 最近发布了一个工具,可以让 PHP 和 .NET 这对冤家和平共处。他们开发了一个叫做 Phalanger PHP compiler 的工具,可以将 PHP 程序编译成本地 .NET 程序执行。他们还准备将这一工具开源。


据 Jadu CEO Suraj Kika 介绍,这个工具对 PHP 程序进行编译,编译成 .NET 框架下下的本地程序。比如,你想用 WordPress,但你属于微软阵营,你可以将 WordPress 编译成可执行文件,放到 .NET 中并在 Visual Studio 中针对这个编译过的 WordPress 做进一步开发。

这个工具将为 PHP 和 .NET 开发工程师带来职业上的便利,避免在各自对方的技术领域内再培训。Kika 表示,我们会看到大量 PHP 开发者在微软阵营找到客户群。

Kika 还表示,开源和商业软件之间向来泾渭分明,这一工具将让这两个阵营的开发者走到一起。

本文来源:http://www.itnews.com.au/News/90129,jadu-brings-php-and-net-closer-together.aspx
中文翻译:COMSHARP CMS
膘叔:好妖的功能呀,一直以为PHP要想在windows下独立运行,得使用php GTK的,结果现在居然有人编译成.net,真是心有多大,世界有多大呀

Tags: php, .net, 编译

Good and Bad PHP Code

by Kevin Yank

The following is republished from the Tech Times #165.

When interviewing a PHP developer candidate for a job at SitePoint, there is one question that I almost always ask, because their answer tells me so much about the kind of programmer they are. Here’s the question: “In your mind, what are the differences between good PHP code and bad PHP code?”

The reason I like this question is because it tests more than just a candidate’s encyclopedic knowledge of PHP’s functions. Zend’s PHP certification does a good job of that (as does the test that Yahoo! issues to applicants for its PHP developer jobs, apparently).

Rather, the answer to this question tells me whether a PHP developer has, for example, experienced the pain of working with poorly-written code inherited from a careless predecessor, and whether he or she will go the extra mile to save the rest of the team from that same pain.

I don’t have a set notion of the perfect answer to the question, but I do know the kinds of things I’m hoping to hear. Just off the top of my head:

Good PHP code should be structured. Long chunks of code can be broken up into functions or methods that achieve sub-tasks with simple code, while non-obvious snippets should be commented to make their meaning plain. As much as possible, you should separate frontend HTML/CSS/JavaScript code from the server-side logic of your applications. PHP’s object oriented programming features give you some especially powerful tools to break up your applications into sensible units.

Good PHP code should be consistent. Whether that means setting rules for the names of variables and functions, adopting standard approaches to recurring tasks like database access and error handling, or simply making sure all of your code is indented the same way, consistency makes your code easier for others to read.

Good PHP code should be portable. PHP has a number of features, such as magic quotes and short tags, that can break fragile code when they are switched on or off. If you know what you’re doing, however, you can write code that works by adapting to its environment.

Good PHP code should be secure. While PHP offers excellent performance and flexibility out of the box, it leaves important issues like security entirely in the hands of the developer. A deep understanding of potential security holes like Cross-Site Scripting (XSS), Cross-Site Request Forgeries (CSRF), code injection vulnerabilities, and character encoding loopholes is essential for a professional PHP developer these days.

Once a candidate has answered this question, I usually have a pretty good idea of whether they’ll be hired or not. Of course, there’s always the possibility that an interviewee simply isn’t able to articulate these types of things, so we also have our candidates sit a PHP developer exam.

Many of the questions in this exam seem straightforward on the surface, but they give candidates plenty of opportunity to show how much they care about the little details.

The following “bad” code is a highly simplified example of the sort of thing we might put in our PHP developer exam. The question might be something like “How would you rewrite this code to make it better?”
 
<?php
    echo("<p>Search results for query: " .
        $_GET['query'] . ".</p>");
?>

<?php
    echo("<p>Search results for query: " .     $_GET['query'] . ".</p>");
?>

The main problem in this code is that the user-submitted value ($_GET['query']) is output directly to the page, resulting in a Cross Site Scripting (XSS) vulnerability. But there are plenty of other ways in which it can be improved.

So, what sort of answer are we hoping for?

Good:
 
<?php
     echo("<p>Search results for query: " .
         htmlspecialchars($_GET['query']) . ".</p>");
?>


<?php
    echo("<p>Search results for query: " .
         htmlspecialchars($_GET['query']) . ".</p>");
?>

This is the least we expect. The XSS vulnerability has been remedied using htmlspecialchars to escape dangerous characters in the submitted value.

Better:
 
<?php
     if (isset($_GET['query']))
     {
       echo '<p>Search results for query: ',
           htmlspecialchars($_GET['query'], ENT_QUOTES), '.</p>';
     }
?>


<?php
    if (isset($_GET['query'])) {
        echo '<p>Search results for query: ',
          htmlspecialchars($_GET['query'], ENT_QUOTES), '.</p>';
    }
?>

Now this looks like someone we might want to hire:

    * The “short” opening PHP tag (<?) has been replaced with the more portable (and XML-friendly) <?php form.
    * Before attempting to output the value of $_GET['query'], isset is used to verify that it actually has a value.
    * The unnecessary brackets (()) around the value passed to echo have been removed.
    * Strings are delimited by single quotes instead of double quotes to avoid the performance hit of PHP searching for variables to interpolate within the strings.
    * Rather than using the string concatenation operator (.) to pass a single string to the echo statement, the strings to be output by echo are separated by commas for a tiny performance boost.
    * Passing the ENT_QUOTES argument to htmlspecialchars to ensure that single quotes (') are also escaped isn’t strictly necessary in this case, but it’s a good habit to get into.

Somewhat distressingly, the number of PHP developers looking for work that are able to give a fully satisfactory answer to this sort of question—at least here in Melbourne—are few and far between. We spent a good three months interviewing for this latest position before we found someone with whom we were happy!

So, how would you do when asked a question like this one? Are there any factors that make PHP code good or bad that you feel I’ve left out? And what else would you look for in a PHP developer?

來自風雪之隅的:深入理解PHP原理之foreach

原文地址:http://www.laruence.com/2008/11/20/630.html

膘叔的話:foreach是PHP語言里最常用的語法結構之一了,只要是數組操作,極大可能是會用到它,自從PHP5之后,它也能操作對象了。自此,foreach的使用率又上升了很多。但是有多少人知道它的背後原理呢?記得在以前的時候,好象是說foreach其實就是while( list )等的封裝。但由于一直沒有看過PHP的源碼也不能這么確定。(PS:如果用dezender等破解軟件破解PHP代碼,那么你會發現,代碼是foreach的那塊,都是被替換成while( list each )這種,所以我才會有上面的猜測)正好,看到風雪之隅有這個介紹,就轉貼一下。自己也可以學習一下。呵呵

foreach是PHP中很常用的一个用作数组循环的控制语句。
因为它的方便和易用,自然也就在后端隐藏着很复杂的具体实现方式(对用户透明)
今天,我们就来一起分析分析,foreach是如何实现数组(对象)的遍历的。
本节内容涉及到较多编译原理(lex and yacc)的知识,所以如果您觉得看不太懂,可以先找相关的资料看看。

我们知道PHP是一个脚本语言,也就是说,用户编写的PHP代码最终都是会被PHP解释器解释执行,
特别的,对于PHP来说,所有的用户编写的PHP代码,都会被翻译成PHP的虚拟机ZE的虚拟指令(OPCODES)来执行(参看:深入理解PHP原理之Opcodes).

不论细节的话,就是说,我们所编写的任何PHP脚本,都会最终被翻译成一条条的指令,从而根据指令,由相应的C编写的函数来执行。

那么foreach会被翻译成什么样子呢?

foreach($arr as $key => $val){
    
echo $key . '=>' . $val . "\n";
}

在词法分析阶段,foreach会被识别为一个TOKEN:T_FOREACH,
在语法分析阶段,会被规则:

unticked_statement//没有被绑定ticks的语句
    
//有省略
    |   
T_FOREACH '(' variable T_AS
        
{ zend_do_foreach_begin(&$1, &$2, &$3, &$4, 1 TSRMLS_CC); }
        
foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); }
        
foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); }
    |   
T_FOREACH '(' expr_without_variable T_AS
        
{ zend_do_foreach_begin(&$1, &$2, &$3, &$4, 0 TSRMLS_CC); }
        
variable foreach_optional_arg ')' { zend_check_writable_variable(&$6); zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); }
        
foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); }
    
//有省略
;

仔细分析这段语法规则,我们可以发现,对于:
foreach($arr as $key => $val){
echo $key . ‘=>’ . $val .”\n”;
}

会被分析为:

T_FOREACH '(' variable T_AS   { zend_do_foreach_begin('foreach', '(', $arr, 'as', 1 TSRMLS_CC); }
    
foreach_variable  foreach_optional_arg(T_DOUBLE_ARROW  foreach_variable)   ')'  { zend_do_foreach_cont('foreach', '(', 'as', $val, ')' TSRMLS_CC); }
    
foreach_satement {zend_do_foreach_end('foreach', 'as');}
 
如果你懂语法分析,或者你懂
yacc,你会奇怪,为什么zend_do_foreach_cont的$6$val 而不是 $key呢? 注意另外一个语法规则:
foreach_optional_arg:
        
/* empty */                     { $$.op_type = IS_UNUSED; }
    |   
T_DOUBLE_ARROW foreach_variable { $$ = $2; }
;

也就是说,对于$key => $val的情况,yacc的内容栈对应的foreach_variable会被替换 {$$=$2};

然后,让我们来看看foreach_statement:
它其实就是一个代码块,体现了我们的 echo $key . ‘=>’ . $val .”\n”;
T_ECHO expr;

显然,实现foreach的核心就是如下3个函数:
zend_do_foreach_begin
zend_do_foreach_cont
zend_do_foreach_end

其中,zend_do_foreach_begin (代码太长,直接写伪码) 主要做了:
1. 记录当前的opline行数(为以后跳转而记录)
2. 对数组进行RESET(讲内部指针指向第一个元素)
3. 获取临时变量 ($val)
4. 设置获取变量的OPCODE FE_FETCH,结果存第3步的临时变量
4. 记录获取变量的OPCODES的行数

而对于 zend_do_foreach_cont来说:
1. 根据foreach_variable的u.EA.type来判断是否引用
2. 根据是否引用来调整zend_do_foreach_begin中生成的FE_FETCH方式
3. 根据zend_do_foreach_begin中记录的取变量的OPCODES的行数,来初始化循环(主要处理在循环内部的循环:do_begin_loop)

最后zend_do_foreach_end:
1. 根据zend_do_foreach_begin中记录的行数信息,设置ZEND_JMP OPCODES
2. 根据当前行数,设置循环体下一条opline, 用以跳出循环
3. 结束循环(处理循环内循环:do_end_loop)
4. 清理临时变量

当然, 在zend_do_foreach_cont 和 zend_do_foreach_end之间 会在语法分析阶段被填充foreach_satement的语句代码。

这样,就实现了foreach的OPCODES line。
比如对于我们开头的实例代码,最终生成的OPCODES是:

filename:       /home/huixinchen/foreach.php
function name(null)
number of ops17
compiled vars:  !0 = $arr, !1 = $key, !2 = $val
line     #  op                           fetch          ext  return  operands
-------------------------------------------------------------------------------
  
2     0  SEND_VAL                                                 1
        
1  SEND_VAL                                                 100
        
2  DO_FCALL                                      2          'range'
        
3  ASSIGN                                                   !0, $0
  
3     4  FE_RESET                                         $2      !0, ->14
        
5  FE_FETCH                                         $3      $2, ->14
        
6  ZEND_OP_DATA                                     ~5
        
7  ASSIGN                                                   !2, $3
        
8  ASSIGN                                                   !1, ~5
  
4     9  CONCAT                                           ~7      !1, '-'
        
10  CONCAT                                           ~8      ~7, !2
        
11  CONCAT                                           ~9      ~8, '%0A'
        
12  ECHO                                                     ~9
  
5    13  JMP                                                      ->5
        
14  SWITCH_FREE                                              $2
  
7    15  RETURN                                                   1
        
16* ZEND_HANDLE_EXCEPTION

我们注意到FE_FETCH的op2的操作数是14,也就是JMP后一条opline,也就是说,在获取完最后一个数组元素以后,FE_FETCH失败的情况下,会跳到第14行opline,从而实现了循环的结束。
而15行opline的op1的操作数是指向了FE_FETCH,也就是无条件跳转到第5行opline,从而实现了循环。

附录:

void zend_do_foreach_begin(znode *foreach_token, znode *open_brackets_token, znode *array, znode *as_token, int variable TSRMLS_DC)
{
    
zend_op *opline;
    
zend_bool is_variable;
    
zend_bool push_container = 0;
    
zend_op dummy_opline;
 
    
if (variable) {
        
//是否是匿名数组
        
if (zend_is_function_or_method_call(array)) {
            
//是否是函数返回值
            
is_variable = 0;
        
} else {
            
is_variable = 1;
        
}
        
/* 使用括号记录FE_RESET的opline行数 */
        
open_brackets_token->u.opline_num = get_next_op_number(CG(active_op_array));
        
zend_do_end_variable_parse(BP_VAR_W, 0 TSRMLS_CC); //获取数组/对象和zend_do_begin_variable_parse对应
        
if (CG(active_op_array)->last > 0 &&
            
CG(active_op_array)->opcodes[CG(active_op_array)->last-1].opcode == ZEND_FETCH_OBJ_W) {
            
/* Only lock the container if we are fetching from a real container and not $this */
            
if (CG(active_op_array)->opcodes[CG(active_op_array)->last-1].op1.op_type == IS_VAR) {
                
CG(active_op_array)->opcodes[CG(active_op_array)->last-1].extended_value |= ZEND_FETCH_ADD_LOCK;
                
push_container = 1;
            
}
        
}
    
} else {
        
is_variable = 0;
        
open_brackets_token->u.opline_num = get_next_op_number(CG(active_op_array));
    
}
 
    
foreach_token->u.opline_num = get_next_op_number(CG(active_op_array)); //记录数组Reset Opline number
 
    
opline = get_next_op(CG(active_op_array) TSRMLS_CC); //生成Reset数组Opcode
 
    
opline->opcode = ZEND_FE_RESET;
    
opline->result.op_type = IS_VAR;
    
opline->result.u.var = get_temporary_variable(CG(active_op_array));
    
opline->op1 = *array;
    
SET_UNUSED(opline->op2);
    
opline->extended_value = is_variable ? ZEND_FE_RESET_VARIABLE : 0;
 
    
dummy_opline.result = opline->result;
    
if (push_container) {
        
dummy_opline.op1 = CG(active_op_array)->opcodes[CG(active_op_array)->last-2].op1;
    
} else {
        
znode tmp;
 
        
tmp.op_type = IS_UNUSED;
        
dummy_opline.op1 = tmp;
    
}
    
zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline, sizeof(zend_op));
 
    
as_token->u.opline_num = get_next_op_number(CG(active_op_array)); //记录循环起始点
 
    
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
    
opline->opcode = ZEND_FE_FETCH;
    
opline->result.op_type = IS_VAR;
    
opline->result.u.var = get_temporary_variable(CG(active_op_array));
    
opline->op1 = dummy_opline.result;    //被操作数组
    
opline->extended_value = 0;
    
SET_UNUSED(opline->op2);
 
    
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
    
opline->opcode = ZEND_OP_DATA; //当使用key的时候附属操作数,当foreach中不包含key时忽略
    
SET_UNUSED(opline->op1);
    
SET_UNUSED(opline->op2);
    
SET_UNUSED(opline->result);
}
void zend_do_foreach_end(znode *foreach_token, znode *as_token TSRMLS_DC)
{
    
zend_op *container_ptr;
    
zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); //生成JMP opcode
 
    
opline->opcode = ZEND_JMP;
    
opline->op1.u.opline_num = as_token->u.opline_num; //设置JMP到FE_FETCH opline行
    
SET_UNUSED(opline->op1);
    
SET_UNUSED(opline->op2);
 
    
CG(active_op_array)->opcodes[foreach_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); //设置跳出循环的opline行
    
CG(active_op_array)->opcodes[as_token->u.opline_num].op2.u.opline_num = get_next_op_number(CG(active_op_array)); //同上
 
    
do_end_loop(as_token->u.opline_num, 1 TSRMLS_CC); //为循环嵌套而设置
 
    
zend_stack_top(&CG(foreach_copy_stack), (void **) &container_ptr);
    
generate_free_foreach_copy(container_ptr TSRMLS_CC);
    
zend_stack_del_top(&CG(foreach_copy_stack));
 
    
DEC_BPC(CG(active_op_array)); //为PHP interactive模式而设置
}

Tags: foreach, php, zend, while, 深入