如何在AST中获取变量名称和值

时间:2020-04-06 18:34:28

标签: php debugging abstract-syntax-tree php-parser

我正在使用PHP-PArser查找PHP程序的AST。例如:

代码

<?php
use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;

$code = <<<'CODE'
<?php
$variable = $_POST['first'];
$new = $nonexist; 
CODE;

$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
    $ast = $parser->parse($code);
} catch (Error $error) {
    echo "Parse error: {$error->getMessage()}\n";
    return;
}

$dumper = new NodeDumper;
echo $dumper->dump($ast) . "\n";

以上示例的AST结果如下:

array( 0: Stmt_Expression( expr: Expr_Assign( var: Expr_Variable( name: variable ) expr: Expr_ArrayDimFetch( var: Expr_Variable( name: _POST_first_symbol ) dim: Scalar_String( value: first ) ) ) ) 1: Stmt_Expression( expr: Expr_Assign( var: Expr_Variable( name: new ) expr: Expr_Variable( name: nonexist ) ) ) )

我要查找的是variable = _POSTnew = nonexist 我使用了leavenode函数来访问_POSTvariable。我的代码来查找_POSTvariable,如下所示:

public function leaveNode(Node $node)
    {
        $collect_to_print= array();

        if ($node instanceof ArrayDimFetch
            && $node->var instanceof Variable
            && $node->var->name === '_POST')
        {
            $variableName = (string) $node->var->name;
            $collect_to_print[$node->dim->value] = $node->var->name; // will store the variables in array in a way to print them all later such as variable => _POST , how to get the name `variable` in this case
            return $node;
        }
        else
            if ($node instanceof Variable
        && !($node->var->name === '_POST' ))
        {
            $collect_to_print[$node->name] = 'Empty' ;
        }

    }

到目前为止,我的结果在单独的行中显示每个变量,如下所示:

variable => 
first => _POST  // This _POST should be the value of variable (above)
new => Empty
nonexist => Empty

但是,我希望结果是:

variable => _POST
new => Empty
nonexist => Empty

请帮忙

1 个答案:

答案 0 :(得分:1)

这比您提出的其他问题要复杂得多,但是学习如何编写它很有趣。

我已经在代码中添加了注释,但是基本上它会分析代码并查找分配(PhpParser\Node\Expr\Assign节点的实例)。然后将其分为左右两部分,然后递归地提取这两个部分中的所有变量。

该代码允许在表达式的任一侧嵌套变量,我更改了示例代码以提供一些更广泛的示例。

对代码的注释(假定您了解解析器如何与节点等一起使用)...

$traverser = new NodeTraverser;

class ExtractVars extends NodeVisitorAbstract {
    private $prettyPrinter = null;

    private $variables = [];
    private $expressions = [];

    public function __construct() {
        $this->prettyPrinter = new PhpParser\PrettyPrinter\Standard;
    }

    public function leaveNode(Node $node) {
        if ( $node instanceof PhpParser\Node\Expr\Assign  ) {
                $assignVars = $this->extractVarRefs ( $node->var );
                // Get string of what assigned to actually is
                $assign = $this->prettyPrinter->prettyPrintExpr($node->var);
                // Store the variables associated with the left hand side
                $this->expressions[$assign]["to"] = $assignVars;
                // Store variables from right
                $this->expressions[$assign][] = $this->extractVarRefs ( $node->expr );
         }
    }

    private function extractVarRefs ( Node $node ) : array  {
        $variableList = [];
        // If it's a variable, store the name
        if ( $node instanceof PhpParser\Node\Expr\Variable )   {
            $variable = $this->prettyPrinter->prettyPrintExpr($node);
            $this->variables[] = $variable;
            $variableList[] = $variable;
        }
        // Look for any further variables in the node
        foreach ( $node->getSubNodeNames() as $newNodeName )   {
            $newNode = $node->$newNodeName;
            if ( $newNode instanceof Node && $newNode->getSubNodeNames())   {
                // Recursive call to extract variables
                $toAdd = $this->extractVarRefs ( $newNode );
                // Add new list to current list
                $variableList = array_merge($variableList, $toAdd);
            }
        }
        return $variableList;
    }

    public function getVariables() : array  {
        return array_unique($this->variables);
    }

    public function getExpressions() : array    {
        return $this->expressions;
    }

}

$varExtract = new ExtractVars();
$traverser->addVisitor ($varExtract);

$traverser->traverse($ast);

print_r($varExtract->getVariables());

print_r($varExtract->getExpressions());

哪个给出的变量列表为...

Array
(
    [0] => $_POST
    [1] => $b
    [3] => $new
    [4] => $nonexist
)

表达式列表为

Array
(
    [$_POST[$b]] => Array
        (
            [to] => Array
                (
                    [0] => $_POST
                    [1] => $b
                )

            [0] => Array
                (
                    [0] => $_POST
                )

        )

    [$new] => Array
        (
            [to] => Array
                (
                    [0] => $new
                )

            [0] => Array
                (
                    [0] => $nonexist
                )

            [1] => Array
                (
                    [0] => $_POST
                    [1] => $b
                )

        )

)

请注意,数组的[to]元素包含=左侧涉及的所有变量。

相关问题