Как получить имя и значение переменной в AST - PullRequest
0 голосов
/ 06 апреля 2020

Я использую PHP -PArser , чтобы найти AST программы PHP. Например:

код

<?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 = _POST И new = nonexist Я использовал функцию leavenode для достижения _POST и variable. мой код для поиска _POST и variable выглядит следующим образом:

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 Ответ

1 голос
/ 07 апреля 2020

Это намного сложнее, чем другие вопросы, которые вы задавали, но было интересно узнать, как это написать.

Я разместил комментарии в коде, но в основном он анализирует код и ищет назначения (экземпляры PhpParser\Node\Expr\Assign узлов). Затем он разбивает его на левую и правую части и рекурсивно извлекает любые переменные в обеих частях.

Код учитывает вложенные переменные по обе стороны выражения, я изменил пример кода, чтобы предоставить более широкие примеры.

Комментарии в коде (предполагаются некоторые знания о том, как синтаксический анализатор работает с узлами и т. Д. c.) ...

$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] массива содержит любые переменные, включенные слева от =.

...