静态分析-RIPS-源码解析记录-01

news2024/11/19 4:44:41

token流扫描重构部分,这一部分主要利用php的token解析api解析出来的token流,对其中的特定token进行删除、替换、对于特定的语法结构进行重构,保持php语法结构上的一致性

解析主要在lib/scanner.php中通过Tokenizer这个类来实现,也就是在main.php中开始调用new scanner的对象,准备开始扫描漏洞,此时在scanner的构造函数中完成token流的解析

这里主要在scanner这个类的构造函数里面完整待扫描的文件的token解析,主要由以下四个步骤完成

接着到tokenize中进行具体的token解析,这里要借助php zend引擎自带的一个词法分析的函数,token_get_all();

 

 对于上面这个文件,解析以后如下图所示:

array(11) {
  [0] =>
  array(3) {
    [0] =>
    int(379)
    [1] =>
    string(6) "<?php
"
    [2] =>
    int(1)
  }
  [1] =>
  array(3) {
    [0] =>
    int(320)
    [1] =>
    string(2) "$a"
    [2] =>
    int(2)
  }
  [2] =>
  string(1) "="
  [3] =>
  string(1) "["
  [4] =>
  array(3) {
    [0] =>
    int(323)
    [1] =>
    string(3) ""a""
    [2] =>
    int(2)
  }
  [5] =>
  string(1) ","
  [6] =>
  array(3) {
    [0] =>
    int(323)
    [1] =>
    string(3) ""b""
    [2] =>
    int(2)
  }
  [7] =>
  string(1) "]"
  [8] =>
  string(1) ";"
  [9] =>
  array(3) {
    [0] =>
    int(382)
    [1] =>
    string(1) "
"
    [2] =>
    int(2)
  }
  [10] =>
  array(3) {
    [0] =>
    int(381)
    [1] =>
    string(3) "?>
"
    [2] =>
    int(3)
  }
}

解析出来的每个token以数组形式存在,默认一个token数组包括三个元素,0代表token常量,1代表token值,2代表行号,此时得到的是最初的token组,接下来要经过一系列的token处理,以为php中实际上变量表示方法多样,要做到统一才方便后面分析。

第一部分: 

prepare_tokens:

 function prepare_tokens()
    {    
        // delete whitespaces and other unimportant tokens, rewrite some special tokens
        for($i=0, $max=count($this->tokens); $i<$max; $i++) //遍历token数组
        {
            if( is_array($this->tokens[$i]) )  //a. unset掉可忽略token,比如php的开始标签<?php以及一些空格,比如if   ()变为if(),方便后面进行条件语句的解析,以及像if(): xxx endif 中间的文本unset掉
 b. 闭合标签变分号;  c.<?= 标签表echo
            {
                if( in_array($this->tokens[$i][0], Tokens::$T_IGNORE) )
                    unset($this->tokens[$i]);
                else if( $this->tokens[$i][0] === T_CLOSE_TAG )
                    $this->tokens[$i] = ';';    
                else if( $this->tokens[$i][0] === T_OPEN_TAG_WITH_ECHO )
                    $this->tokens[$i][1] = 'echo';
            } 
            // @ (depress errors) disturbs connected token handling
            else if($this->tokens[$i] === '@')  //unset掉@符号
            {
                unset($this->tokens[$i]);
            }    
            // rewrite $array{index} to $array[index]  //对于数组处理如果当前是花括号并且前一个token是变量
            else if( $this->tokens[$i] === '{'
            && isset($this->tokens[$i-1]) && ((is_array($this->tokens[$i-1]) && $this->tokens[$i-1][0] === T_VARIABLE)
            || $this->tokens[$i-1] === ']') ) //或者上一个token是[,当前是{,则肯定是数组变量(主要是多维数组)
            {
                $this->tokens[$i] = '['; //则令当前token为左方括号
                $f=1;
                while($this->tokens[$i+$f] !== '}') //此时while循环找下一个与当前花括号对应的右花括号
                {
                    $f++;
                    if(!isset($this->tokens[$i+$f]))
                    {
                        addError('Could not find closing brace of '.$this->tokens[$i-1][1].'{}.', array_slice($this->tokens, $i-1, 2), $this->tokens[$i-1][2], $this->filename);
                        break;    //没找到则退出,说明语法有问题
                    }
                }
                $this->tokens[$i+$f] = ']';  //否则令右花括号为]
            }    
        }
        
        // rearranged key index of tokens
        $this->tokens = array_values($this->tokens);
    }    

第二部分:

接着是对多维数组的重构:

// rewrite $arrays[] to $variables and save keys in $tokens[$i][3]

从代码注释可以看出这个函数将多维数组的所有键名保存在当前解析为变量token的数组第四个元素中

function array_reconstruct_tokens()
    {    
        for($i=0,$max=count($this->tokens); $i<$max; $i++) //遍历所有token数组
        {
            // check for arrays
            if( is_array($this->tokens[$i]) && $this->tokens[$i][0] === T_VARIABLE && $this->tokens[$i+1] === '[' ) //当前token是个变量,并且下一个token是[,则最少即为一维数组
            {    
                $this->tokens[$i][3] = array(); //初始化第四个元素为数组
                $has_more_keys = true;  
                $index = -1;
                $c=2;
                
                // loop until no more index found: array[1][2][3]
                while($has_more_keys && $index < MAX_ARRAY_KEYS) //while循环遍历多维数组,max默认为10(这个数已经够了)
                {
                    $index++;
                    // save constant index as constant   //找到当前变量对应的右括号,主要是针对常量
                    if(($this->tokens[$i+$c][0] === T_CONSTANT_ENCAPSED_STRING || $this->tokens[$i+$c][0] === T_LNUMBER || $this->tokens[$i+$c][0] === T_NUM_STRING || $this->tokens[$i+$c][0] === T_STRING) && $this->tokens[$i+$c+1] === ']')
                    {         
                        unset($this->tokens[$i+$c-1]); //unset掉左括号
                        $this->tokens[$i][3][$index] = str_replace(array('"', "'"), '', $this->tokens[$i+$c][1]); //把键名放到第四个数组元素中
                        unset($this->tokens[$i+$c]); //unset掉键名
                        unset($this->tokens[$i+$c+1]); //unset掉右括号
                        $c+=2; #c+2尝试找到下一维数组
                    // save tokens of non-constant index as token-array for backtrace later  //$a[$b][][]对于这种非常量索引的情况  
                    } else
                    {
                        $this->tokens[$i][3][$index] = array(); 
                        $newbraceopen = 1; //就当作是左括号的个数
                        unset($this->tokens[$i+$c-1]); //unset掉左括号
                        while($newbraceopen !== 0) 
                        {    
                            if( $this->tokens[$i+$c] === '[' )
                            {
                                $newbraceopen++;  //哇,又遇到新的一个左括号
                            }
                            else if( $this->tokens[$i+$c] === ']' ) 
                            {
                                $newbraceopen--; //此时说明一组左右括号遍历完
                            }
                            else
                            {
                                $this->tokens[$i][3][$index][] = $this->tokens[$i+$c]; //此时将变量索引对应的数组保存在第四个元素中 
                            }    
                            unset($this->tokens[$i+$c]); //unset掉该变量索引或unset掉右括号或左括号
                            $c++; //就把它当作游标吧,游标不断滑动
                            
                            if(!isset($this->tokens[$i+$c])) //尝试找到=或者分号,实际就是结束当前数组的符号,没有找到则break退出
                            {
                                addError('Could not find closing bracket of '.$this->tokens[$i][1].'[].', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename);
                                break;    
                            }
                        }
                        unset($this->tokens[$i+$c-1]); //这一处unset为了处理特殊情况
                    }
                    if($this->tokens[$i+$c] !== '[')
                        $has_more_keys = false;
                    $c++;    
                }    
                
                $i+=$c-1;
            }
        }
    
        // return tokens with rearranged key index
        $this->tokens = array_values($this->tokens);        
    }

 比如对于下面这种索引为变量的数组:

 解析以后将所有维度的键名存储在$a这个token的第四个数组元素中

 

 这个算法设计的还是挺巧妙的,每一处unset都设计的很恰当

 

 这个unset就刚刚假设当前游标为数组索引或右括号,-1直接unset掉左括号

 

 ①处当遍历数组变量完成后则置为true,此时因为游标在=或者;处,因此c++完后$i应该+c-1回到=或者;处

对于$a[$b][][]处理完后就是:

 

第三部分:

fix_tokens:

这一部分主要是重构一些token信息

function fix_tokens()
    {    
        for($i=0; $i<($max=count($this->tokens)); $i++)
        {
        // convert `backticks` to backticks()  #处理反引号
            if( $this->tokens[$i] === '`' )
            {        
                $f=1;
                while( $this->tokens[$i+$f] !== '`' )  #通过while循环来,将`xxx` 转换成backticks标识的token
                {    
                    // get line_nr of any near token
                    if( is_array($this->tokens[$i+$f]) )
                        $line_nr = $this->tokens[$i+$f][2];  #此时反引号中间内容的行号

                    $f++; #f++走到右反引号
                    if(!isset($this->tokens[$i+$f]) || $this->tokens[$i+$f] === ';')  #无闭合则报错
                    {
                        addError('Could not find closing backtick `.', array_slice($this->tokens, $i, 5), $this->tokens[$i+1][2], $this->filename);
                        break;    
                    }
                }
                if(!empty($line_nr)) #若反引号中间内容不为空,则进行重构
                { 
                    $this->tokens[$i+$f] = ')'; #将右引号变为圆括号)
                    $this->tokens[$i] = array(T_STRING, 'backticks', $line_nr);  #将左反引号声明一个backticks的token
                
                    // add element backticks() to array             
                    $this->tokens = array_merge(   #对当前token进行重构
                        array_slice($this->tokens, 0, $i+1), array('('),  #因为刚才将左反引号替换了,所以此时需要再添加一个左圆括号
                        array_slice($this->tokens, $i+1) #拼接上后面从右圆括号开始的token,所以就是`xxx`  变为 backtricks(xxx)
                    );    
                }

            }
        #接下来要对一些条件语句、循环语句进行解析,主要为IF,else if,for,foreach,while
        // real token
            else if( is_array($this->tokens[$i]) )  
            {    
            // rebuild if-clauses, for(), foreach(), while() without { } #首先重构没有花括号的,即只有方法体只有单条语句
                if ( ($this->tokens[$i][0] === T_IF || $this->tokens[$i][0] === T_ELSEIF || $this->tokens[$i][0] === T_FOR 
                || $this->tokens[$i][0] === T_FOREACH || $this->tokens[$i][0] === T_WHILE) && $this->tokens[$i+1] === '(' )
                {        
                    // skip condition in ( ) #这个while主要是跳过条件判断(),继续扫描后面的token,对token数组并不做处理
                    $f=2;
                    $braceopen = 1;
                    while($braceopen !== 0 ) 
                    {
                        if($this->tokens[$i+$f] === '(')
                            $braceopen++;
                        else if($this->tokens[$i+$f] === ')')
                            $braceopen--;
                        $f++;

                        if(!isset($this->tokens[$i+$f]))
                        {
                            addError('Could not find closing parenthesis of '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename);
                            break;    
                        }
                    }    
                    
                    // alternate syntax while(): endwhile; #这个if条件主要是为了给php的替代语法结构加上左右花括号,对于每一种条件或者循环关键字,都对应了相应的
                    结束标记,因此结合一个c变量,通过其自增来找到相应关键字的闭合token,然后再将:和endif都放到花括号内
                    if($this->tokens[$i+$f] === ':')
                    {
                        switch($this->tokens[$i][0])
                        {
                            case T_IF:
                            case T_ELSEIF: $endtoken = T_ENDIF; break;
                            case T_FOR: $endtoken = T_ENDFOR; break;
                            case T_FOREACH: $endtoken = T_ENDFOREACH; break;
                            case T_WHILE: $endtoken = T_ENDWHILE; break;
                            default: $endtoken = ';';
                        }
                    
                        $c=1;
                        while( $this->tokens[$i+$f+$c][0] !== $endtoken)
                        {
                            $c++;
                            if(!isset($this->tokens[$i+$f+$c]))
                            {
                                addError('Could not find end'.$this->tokens[$i][1].'; of alternate '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, $f+1), $this->tokens[$i][2], $this->filename);
                                break;    
                            }
                        }
                        $this->wrapbraces($i+$f+1, $c+1, $i+$f+$c+2);
                    }
                    #这个if条件主要是为了针对 if() echo "1";只有一条指令,将其放到花括号内
                    // if body not in { (and not a do ... while();) wrap next instruction in braces
                    else if($this->tokens[$i+$f] !== '{' && $this->tokens[$i+$f] !== ';')
                    {
                        $c=1;
                        while($this->tokens[$i+$f+$c] !== ';' && $c<$max)
                        {
                            $c++;
                        }
                        $this->wrapbraces($i+$f, $c+1, $i+$f+$c+1);
                    }
                } 
              #为else if 添加花括号
            // rebuild else without { }    
                else if( $this->tokens[$i][0] === T_ELSE 
                && $this->tokens[$i+1][0] !== T_IF
                && $this->tokens[$i+1] !== '{')
                {    
                    $f=2;
                    while( $this->tokens[$i+$f] !== ';' && $f<$max)
                    {        
                        $f++;
                    }
                    $this->wrapbraces($i+1, $f, $i+$f+1);
                }
                 
            // rebuild switch (): endswitch;   
                #switch语句的处理,和if while等差不多,先扫描跳过判断语句     
                else if( $this->tokens[$i][0] === T_SWITCH && $this->tokens[$i+1] === '(')
                {
                    $newbraceopen = 1;
                    $c=2;
                    while( $newbraceopen !== 0 )
                    {
                        // watch function calls in function call
                        if( $this->tokens[$i + $c] === '(' )
                        {
                            $newbraceopen++;
                        }
                        else if( $this->tokens[$i + $c] === ')' )
                        {
                            $newbraceopen--;
                        }                    
                        else if(!isset($this->tokens[$i+$c]) || $this->tokens[$i + $c] === ';')
                        {
                            addError('Could not find closing parenthesis of switch-statement.', array_slice($this->tokens, $i, 10), $this->tokens[$i][2], $this->filename);
                            break;    
                        }
                        $c++;
                    }
                    #此时达到switch的方法体,因为switch一般来说是带花括号的,但对于endswitch的情况需要特殊处理一下,变为花括号形式
                    // switch(): ... endswitch;
                    if($this->tokens[$i + $c] === ':')
                    {
                        $f=1;
                        while( $this->tokens[$i+$c+$f][0] !== T_ENDSWITCH) #这里是通过f来找endswitch,c找:
                        {
                            $f++;
                            if(!isset($this->tokens[$i+$c+$f]))
                            {
                                addError('Could not find endswitch; of alternate switch-statement.', array_slice($this->tokens, $i, $c+1), $this->tokens[$i][2], $this->filename);
                                break;    
                            }
                        }
                        $this->wrapbraces($i+$c+1, $f+1, $i+$c+$f+2);
                    }
                }
            // rebuild switch case: without { }    
               #switch处理完了,此时处理switch内部的case
               这一部分主要是将每一条case后面的全部补全花括号
                else if( $this->tokens[$i][0] === T_CASE )
                {
                    $e=1;
                    while($this->tokens[$i+$e] !== ':' && $this->tokens[$i+$e] !== ';') #找到分号
                    {
                        $e++;
                        
                        if(!isset($this->tokens[$i+$e]))
                        {
                            addError('Could not find : or ; after '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename);
                            break;    
                        }
                    }
                    $f=$e+1;
                    if(($this->tokens[$i+$e] === ':' || $this->tokens[$i+$e] === ';')
                    && $this->tokens[$i+$f] !== '{' 
                    && $this->tokens[$i+$f][0] !== T_CASE && $this->tokens[$i+$f][0] !== T_DEFAULT)
                    {
                        $newbraceopen = 0;
                        while($newbraceopen || (isset($this->tokens[$i+$f]) && $this->tokens[$i+$f] !== '}' 
                        && !(is_array($this->tokens[$i+$f]) 
                        && ($this->tokens[$i+$f][0] === T_BREAK || $this->tokens[$i+$f][0] === T_CASE 
                        || $this->tokens[$i+$f][0] === T_DEFAULT || $this->tokens[$i+$f][0] === T_ENDSWITCH) ) ))
                        {        
                            if($this->tokens[$i+$f] === '{')
                                $newbraceopen++;
                            else if($this->tokens[$i+$f] === '}')    
                                $newbraceopen--;
                            $f++;
                            
                            if(!isset($this->tokens[$i+$f]))
                            {
                                addError('Could not find ending of '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, $e+5), $this->tokens[$i][2], $this->filename);
                                break;    
                            }
                        }
                        if($this->tokens[$i+$f][0] === T_BREAK)
                        {
                            if($this->tokens[$i+$f+1] === ';')
                                $this->wrapbraces($i+$e+1, $f-$e+1, $i+$f+2);
                            // break 3;    
                            else
                                $this->wrapbraces($i+$e+1, $f-$e+2, $i+$f+3);
                        }    
                        else
                        {   # 无break的情况
                            $this->wrapbraces($i+$e+1, $f-$e-1, $i+$f);
                        }    
                        $i++;
                    }
                }
            // rebuild switch default: without { }  
                #针对default的情况,如果没有花括号,则添加花括号  
                else if( $this->tokens[$i][0] === T_DEFAULT
                && $this->tokens[$i+2] !== '{' )
                {
                    $f=2;
                    $newbraceopen = 0;
                    while( $this->tokens[$i+$f] !== ';' && $this->tokens[$i+$f] !== '}' || $newbraceopen )
                    {        
                        if($this->tokens[$i+$f] === '{')
                            $newbraceopen++;
                        else if($this->tokens[$i+$f] === '}')    
                            $newbraceopen--;
                        $f++;
                        
                        if(!isset($this->tokens[$i+$f]))
                        {
                            addError('Could not find ending of '.$this->tokens[$i][1].'-statement.', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename);
                            break;    
                        }
                    }
                    $this->wrapbraces($i+2, $f-1, $i+$f+1);
                }
                #函数名小写
            // lowercase all function names because PHP doesn't care    
                else if( $this->tokens[$i][0] === T_FUNCTION )
                {
                    $this->tokens[$i+1][1] = strtolower($this->tokens[$i+1][1]);
                } 
                #函数调用小写   
                else if( $this->tokens[$i][0] === T_STRING && $this->tokens[$i+1] === '(')
                {
                    $this->tokens[$i][1] = strtolower($this->tokens[$i][1]);
                }    
            // switch a do while with a while (the difference in loop rounds doesnt matter
            // and we need the condition to be parsed before the loop tokens)
                else if( $this->tokens[$i][0] === T_DO )
                {
                    $f=2;
                    $otherDOs = 0;
                    // f = T_WHILE token position relative to i
                   #此时去找到对应该DO的while的token
                    while( $this->tokens[$i+$f][0] !== T_WHILE || $otherDOs )
                    {        
                        #忽略内层的DO while体
                        if($this->tokens[$i+$f][0] === T_DO)
                            $otherDOs++;
                        else if($this->tokens[$i+$f][0] === T_WHILE)
                            $otherDOs--;
                        $f++; #用f来标志找到的while位置
                        
                        if(!isset($this->tokens[$i+$f]))
                        {
                            addError('Could not find WHILE of DO-WHILE-statement.', array_slice($this->tokens, $i, 5), $this->tokens[$i][2], $this->filename);
                            break;    
                        }
                    }
                    
                    // rebuild do while without {} (should never happen but we want to be sure)
                    
                    #对于do后不带花括号的情况,带上花括号
                     if($this->tokens[$i+1] !== '{')
                    {
                        $this->wrapbraces($i+1, $f-1, $i+$f);
                        // by adding braces we added two new tokens
                        $f+=2; #因为在最外层的while前加了两个花括号占位,因此f+2才代表while的位置
                    }
                    #d代表while后的分号位置,这样不改变f所指的位置方便后面替换结构
                    $d=1;
                    // d = END of T_WHILE condition relative to i
                    while( $this->tokens[$i+$f+$d] !== ';' && $d<$max )
                    {
                        $d++;
                    }
                    #对do while语句进行重构,变成while结构
                    // reorder tokens and replace DO WHILE with WHILE
                    $this->tokens = array_merge(
                        array_slice($this->tokens, 0, $i), // before DO  
                        array_slice($this->tokens, $i+$f, $d), // WHILE condition f指向while d指向while结束
                        array_slice($this->tokens, $i+1, $f-1), // DO WHILE loop tokens i指向do循环体,f-1即内容结束
                        array_slice($this->tokens, $i+$f+$d+1, count($this->tokens)) // rest of tokens without while condition  while之后的token数组
                    );    
                }
            }    
        }
        // return tokens with rearranged key index
        $this->tokens = array_values($this->tokens);
    }

上面函数名小写要注意一点,php是弱类型语言,这里其本身不支持函数重载,即没有java的类的多态特性,但是同为解释型语言的python是支持函数重载的

第四部分:

token解析的最后一部分为:

function fix_ternary()
    {
        for($i=0,$max=count($this->tokens); $i<$max; $i++)
        {
            if( $this->tokens[$i] === '?' )
            {
                unset($this->tokens[$i]);
                // condition in brackets: fine, delete condition
                if($this->tokens[$i-1] === ')')
                {   #先找到)右括号,然后减f一直找到左括号,一直unset到左括号(
                    unset($this->tokens[$i-1]);
                    // delete tokens till ( 
                    $newbraceopen = 1;
                    $f = 2;
                    while( $newbraceopen !== 0 && $this->tokens[$i - $f] !== ';')
                    {
                        if( $this->tokens[$i - $f] === '(' )
                        {
                            $newbraceopen--;
                        }
                        else if( $this->tokens[$i - $f] === ')' )
                        {
                            $newbraceopen++;
                        }
                        unset($this->tokens[$i - $f]);    
                        $f++;
                        
                        if(($i-$f)<0)
                        {
                            addError('Could not find opening parenthesis in ternary operator (1).', array_slice($this->tokens, $i-5, 10), $this->tokens[$i+1][2], $this->filename);
                            break;    
                        }
                    }
                    #判断左括号左边是否是!或是自定义函数调用或者是isset、empty函数调用
                    //delete token before, if T_STRING
                    if($this->tokens[$i-$f] === '!' || (is_array($this->tokens[$i-$f]) 
                    && ($this->tokens[$i-$f][0] === T_STRING || $this->tokens[$i-$f][0] === T_EMPTY || $this->tokens[$i-$f][0] === T_ISSET)))
                    {
                        unset($this->tokens[$i-$f]);
                    }
                    
                }
                // condition is a check or assignment
                #判断问号之前是不是逻辑比较,是的话肯定有操作数,需要unset掉
                else if(in_array($this->tokens[$i-2][0], Tokens::$T_ASSIGNMENT) || in_array($this->tokens[$i-2][0], Tokens::$T_OPERATOR) )
                {
                    // remove both operands
                    unset($this->tokens[$i-1]); #右操作数删除
                    unset($this->tokens[$i-2]); #删除运算符
                    // if operand is in braces
                    if($this->tokens[$i-3] === ')')  
                   #判断左边是否是函数调用,跟上面unset过程差不多,理想情况下是a()==1,但是对于1==a()没有考虑进去,因此对于这种unset并不能完全消除token,就直接走上面第一种a()这种形式的token解析
                    {
                        // delete tokens till ( 
                        $newbraceopen = 1;
                        $f = 4;
                        while( $newbraceopen !== 0 )
                        {
                            if( $this->tokens[$i - $f] === '(' )
                            {
                                $newbraceopen--;
                            }
                            else if( $this->tokens[$i - $f] === ')' )
                            {
                                $newbraceopen++;
                            }
                            unset($this->tokens[$i - $f]);    
                            $f++;
                            
                            if(($i-$f)<0 || $this->tokens[$i - $f] === ';')
                            {
                                addError('Could not find opening parenthesis in ternary operator (2).', array_slice($this->tokens, $i-8, 6), $this->tokens[$i+1][2], $this->filename);
                                break;    
                            }
                        }
                        #删除函数调用
                        //delete token before, if T_STRING
                        if(is_array($this->tokens[$i-$f]) 
                        && ($this->tokens[$i-$f][0] === T_STRING || $this->tokens[$i-$f][0] === T_EMPTY || $this->tokens[$i-$f][0] === T_ISSET))
                        {
                            unset($this->tokens[$i-$f]);
                        }
                    }

                    unset($this->tokens[$i-3]);
                    
                }
                // condition is a single variable, delete
                #对于单变量  $a? unset掉
                else if(is_array($this->tokens[$i-1]) && $this->tokens[$i-1][0] === T_VARIABLE)
                {
                    unset($this->tokens[$i-1]);
                }
            }    
        }
        // return tokens with rearranged key index
        $this->tokens = array_values($this->toknes);    
    }

这一部分主要就是对于三元操作符删除掉?前面的判断条件,此时只保留?后面的两种取值情况

参考:

https://xz.aliyun.com/t/2605#toc-6  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1663988.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频号小店做店的最新最全攻略,小白也能快速上手轻松变现!

大家好&#xff0c;我是电商花花。 从开始接触视频号小店到现在已经两年多时间了&#xff0c;关于视频号小店也有不少经验和感触。 最近越来越多的人开始进入视频号小店的电商赛道&#xff0c;有人日均销售额做到几万甚至十几万。 想在视频号上变现赚钱&#xff0c;但是苦于…

APP广告转化流程对广告变现收益有影响吗?

对接广告平台做广告变现的APP开发者都清楚&#xff0c;广告变现的价格、收益不是一成不变的&#xff0c;经常会遇到eCPM波动对广告收益产生较大影响。 导致APP收益产生波动的因素包括&#xff1a;用户质量、广告类型、广告平台的资源波动、广告预算的季节性、广告展示量级等。…

R语言数据探索与分析-中国GDP回归分析与预测

首先读取数据&#xff1a; 将GDP列转换为常规数字格式 # 可视化GDP数据 # 查看数据结构 # 确保数据类型是正确的 第一张图片展示了中国2002年到2021年间的GDP增长趋势&#xff0c;这是一个时间序列图&#xff0c;其中横轴表示年份&#xff0c;纵轴表示GDP&#xff08;单位未…

springcloud alibaba微服务框架涉及的技术

一、微服务架构中核心模块及其使用技术总览 二、各模块详细说明 1、注册中心 该模块主要功能为 自动提供服务的注册与发现&#xff0c;集中式管理服务&#xff0c;让 服务调用端发现服务&#xff0c;让服务提供端注册服务&#xff0c;倘若没有注册中心&#xff0c;那客户端就…

Acwing2024蓝桥杯FloodFill

AcWing 687. 扫雷 模拟以下样例(10X10): 把扫雷地图转变为数字记录的地图:地雷记作-1,其余表示8个方向有几个地雷,完成后如下图: 接着搜索所有0联通块(为红色矩形),并且把联通块附近不是地雷的点(红色圆形)全标记为-1,如下图: 而答案就是当前该图中大于0的数的数目之和,再加上…

GPT-ArcGIS数据处理、空间分析、可视化及多案例综合应用教程

原文链接&#xff1a;GPT-ArcGIS数据处理、空间分析、可视化及多案例综合应用教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247603080&idx1&sn3e0d7015a2a717c320ebea3a673388ee&chksmfa82126fcdf59b795c7e2cea575470d42480ab809b926be5f94633eac…

国产银河麒麟V10SP1系统下搭建TiDB数据库操作步骤图文

开发目的&#xff1a;在国产银河麒麟系统中搭建TiDB数据库运行环境。 开发工具&#xff1a;银河麒麟系统V10SP1TiDBMySql数据库8.0。 具体步骤&#xff1a; 1、在VmWare虚拟机中安装好国产银河麒麟V10Sp1操作系统。 2、打开终端命令&#xff0c;安装TiDB相关软件&#xff1…

张驰咨询:AI与六西格玛——携手共进,非彼此替代

在历史的洪流中&#xff0c;技术与方法的演进如同波澜壮阔的画卷&#xff0c;不断书写着人类文明的篇章。六西格玛&#xff0c;作为一种追求极致品质与效率的方法论&#xff0c;是现代工业文明中的瑰宝。而当我们面对AI&#xff08;人工智能&#xff09;这一新时代的产物时&…

安卓模拟器Frida环境搭建 (mumu+adb+frida)

安卓模拟器Frida环境搭建 &#xff08;mumuadbfrida&#xff09; mumu模拟器安装adb与frida下载mumuadbfrida配置一般的报错实战 针对apk抓包问题&#xff0c;有的时候Android9已经不适用于现在的需求&#xff0c;需要更高的Android版本&#xff0c;mumu模拟器提供了Android12的…

【经验总结】Vue2中的全局变量(store

需求场景 需要在vue中存储一个可变的&#xff0c;可读写的全局变量在不同的js、页面中均可调用和读写 技术&#xff1a;使用vue的store 用法总结 一、定义变量 1、找到vue的/src/store路径&#xff0c;在modules文件夹下创建文件&#xff08;这里便于测试创建demo.js&…

Linux下安装JDK并配置环境变量

一、Oracle官网下载jdk 1、官网地址 https://www.oracle.com/java/technologies/downloads/#java17 2、命令下载 wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.tar.gz 3、解压 tar -zxvf jdk-17_linux-x64_bin.tar.gz 4、配置环境变量 ec…

HarmonyOS开发案例:【生活健康app之获取成就】(3)

获取成就 本节将介绍成就页面。 功能概述 成就页面展示用户可以获取的所有勋章&#xff0c;当用户满足一定的条件时&#xff0c;将点亮本页面对应的勋章&#xff0c;没有得到的成就勋章处于熄灭状态。共有六种勋章&#xff0c;当用户连续完成任务打卡3天、7天、30天、50天、…

IP地址127.0.0.1的误解:一次投标监管的技术失误

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

selenium进行xhs图片爬虫:03获取一篇图文的图片

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️感谢大家点赞&#x1f44d;&…

出海企业哪种组网方案更省事?

对于出海企业而言&#xff0c;建立跨地区的数据传输和协同工作至关重要&#xff0c;以提升运营效率。因此&#xff0c;网络构建变得迫在眉睫。通过构建企业组网&#xff0c;企业能够加强与海外分支、客户和合作伙伴之间的联系&#xff0c;加速海外业务的发展。 然而&#xff0c…

深兰科技荣获中国机器人行业年度独角兽企业奖

近日&#xff0c;“维科杯OFweek 2023人工智能产业大会暨行业年度评选颁奖典礼“在深圳隆重举行。经OFweek网络投票、专家组评审及组委会综合评审三轮激烈紧张的评审筛选&#xff0c;通过对近300个参评项目的综合实力考量&#xff0c;最终深兰科技成功荣膺“维科杯OFweek2023中…

计算机视觉——OpenCV实现Lucas-Kanade 光流追踪

1.光流 光流法是计算机视觉中用于估计图像序列中物体运动的关键技术。它类似于观察夜空中的彗星&#xff0c;通过其在天空中的运动轨迹来追踪它的路径。在图像处理中&#xff0c;光流帮助我们理解像素点如何在连续的帧之间移动。 1.1 稀疏光流法 稀疏光流法关注于图像中的关…

js实现复制功能

/*** 复制* param {*} val 要复制的内容* returns*/ export const copyToClipboard async val > {try {// 使用现代 API 尝试复制if (navigator.clipboard && navigator.permissions) {await navigator.clipboard.writeText(val)return // 如果成功&#xff0c;直接…

pikachu靶场-全套学习

文章目录 配置pikachu靶场浏览器访问过程burpsuite配置代理hackbar安装使用kali安装中国蚁剑暴力破解cookie简化场景解释各部分含义如何工作 基于表单的暴力破解验证码绕过(On server)验证码绕过(on client)token防爆破? XSS&#xff08;Cross-Site Scripting跨站脚本攻击 &am…

Linux下安装mysql8.0(以tar.xz包安装--编译安装)

前言&#xff1a;原文在我的博客网站中&#xff0c;持续更新数通、系统方面的知识&#xff0c;欢迎来访&#xff01; Linux下安装mysql8.0&#xff08;以tar.xz包安装--编译安装&#xff09;https://myweb.myskillstree.cn/126.html 目录 一、下载对应自己glic版本的MySQL …