Ourren

关注技术,记录生活.

浅谈php中的词法分析

| 留言

php中提供了token_get_all函数来针对php代码进行词法分析,通过词法分析我们可以做恶意代码分析,安全代码审计等深层次研究。该函数可以直接把php代码分为php tokens,而每个token又包含类型、值、行数这三样。 首先对最简单的代码进行分析(当然不是hello world): [code lang=“php”]code.php <?php echo ‘token’; ?>[/code] [code lang=“php”]token.php <?php print_r(token_get_all(file_get_contents(‘code.php’))); ?>[/code] 看效果,为了效果更明显,建议你测试的时候直接看浏览器源码。 [code lang=“php”]Array ( [0] => Array ( [0] => 366 [1] => <?php

        [2] => 1
    )

[1] => Array
    (
        [0] => 315
        [1] => echo
        [2] => 2
    )

[2] => Array
    (
        [0] => 369
        [1] =>  
        [2] => 2
    )

[3] => Array
    (
        [0] => 314
        [1] => 'token'
        [2] => 2
    )

[4] => ;
[5] => Array
    (
        [0] => 369
        [1] => 

        [2] => 2
    )

[6] => Array
    (
        [0] => 368
        [1] => ?>
        [2] => 3
    )

)[/code] 每个token数组中的第一项表示其类型,可以用函数token_name()得到起文字说明,第二项表示其内容,第三项表示在文件中的行数。还不错吧,可以得到这么多有用的信息(至少我第一次从monyer文章中看到的时候很欢喜的)。如果你只关注一些关键函数及其参数,那么你就需要对这些内容进行过滤,借鉴php手册重点的一个例子实现如下: [code lang=“php”]<?php define(’T_NEW_LINE’, -1);

$tokens = token_get_all_nl(file_get_contents(‘code.php’));

foreach ($tokens as $token) { if (is_array($token)) { echo (‘Type:’.token_name_nl($token[0]) .“t”. ‘Code: “’ . htmlspecialchars($token[1]) .”t”.‘“ line:’.$token[2].‘
’); } else { echo (‘”’ . $token . ‘“
’); } }

function token_get_all_nl($source) { $new_tokens = array();

// Get the tokens
$tokens = token_get_all($source);

// Split newlines into their own tokens
foreach ($tokens as $token)
{
    $token_name = is_array($token) ? $token[0] : null;
    $token_data = is_array($token) ? $token[1] : $token;
    $token_number = is_array($token) ? $token[2] : $token;

    // Do not split encapsed strings or multiline comments
    if ($token_name == T_CONSTANT_ENCAPSED_STRING || substr($token_data, 0, 2) == '/*')
    {
        $new_tokens[] = array($token_name, $token_data, $token_number);
        continue;
    }

    // Split the data up by newlines
    $split_data = preg_split('#(rn|n)#', $token_data, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

    foreach ($split_data as $data)
    {
        if ($data == "rn" || $data == "n")
        {
            // This is a new line token
            $new_tokens[] = array(T_NEW_LINE, $data, $token_number);
        }
        else
        {
            // Add the token under the original token name
            $new_tokens[] = is_array($token) ? array($token_name, $data, $token_number) : $data;
        }
    }
}

return $new_tokens;

}

function token_name_nl($token) { if ($token === T_NEW_LINE) { return ’T_NEW_LINE’; }

return token_name($token);

} ?>[/code] 此外,你可以在这个基础上进行一些有目的的开发。 BTW:今天看rips源码才知道也是这么做的,详细的实现要看完了再分享。