phpabstract-syntax-treephp-parser

How to use PHP-Parser to get the global variables name and change it


I want to use PHP-Parser library to get the global method (_POST, _GET, _REQUEST) to get values in PHP. I'm using PHP-Parser where I want to check the node name if it equal to (_POST, _GET, _REQUEST). I'm still beginner in PHP-Parser and not figure out how to get these global variables. For example, if I have the following source code:

code:

<?php 
$firstname = $_POST['firstname];

The PHP-parser I used until now looks like this:

<?php

require_once('vendor/autoload.php');
use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;


$code = <<<'CODE'
<?php
$firstname=  $_POST['firstname'];
CODE;


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


use PhpParser\{Node, NodeTraverser, NodeVisitorAbstract};



$traverser = new NodeTraverser;


// This code exist in the PHP-Parser documentation
$traverser->addVisitor(new class extends NodeVisitorAbstract {
    public function leaveNode(Node $node) {
    if ($node instanceof Node\Stmt\Expression
        && $node->expr instanceof Node\Expr\FuncCall
        && $node->expr->name instanceof Node\Name
        && $node->expr->name->toString() === '_POST'
    ) {
        // Change the $_POST['firstname'] and replace it with XXX value 
    }
}

});


print_r($modifiedStmts = $traverser->traverse($ast) );

after implement the above PHP-Parser AST, I got the following result:

Array ( [0] => PhpParser\Node\Stmt\Expression Object ( [expr] => PhpParser\Node\Expr\Assign Object ( [var] => PhpParser\Node\Expr\Variable Object ( [name] => firstname [attributes:protected] => Array ( [startLine] => 2 [endLine] => 2 ) ) [expr] => PhpParser\Node\Expr\ArrayDimFetch Object ( [var] => PhpParser\Node\Expr\Variable Object ( [name] => _POST [attributes:protected] => Array ( [startLine] => 2 [endLine] => 2 ) ) [dim] => PhpParser\Node\Scalar\String_ Object ( [value] => firstname [attributes:protected] => Array ( [startLine] => 2 [endLine] => 2 [kind] => 1 ) ) [attributes:protected] => Array ( [startLine] => 2 [endLine] => 2 ) ) [attributes:protected] => Array ( [startLine] => 2 [endLine] => 2 ) ) [attributes:protected] => Array ( [startLine] => 2 [endLine] => 2 ) ) )
  1. How to detect if the test code have _POST, _GET, REQUEST? for example:
// Something like this
if($node->value === '_POST' OR $node->value === '_GET' or $node->value === '_REQUEST') 
{
     // Do next step to change the global variable to specific text value
}
  1. How to change the $_POST[firstname] by hello world! or any text value ? for example: Before
$firstname = $_POST['firstname']; 

After

$firstname = "hello World!"; 

Thanks for your help


Solution

  • This should work for the particular instance you have highlighted, it only does the POST instance, but that should be easy to expand.

    The main part is when you see the AST for the code, try and make sure you can identify the base of the _POST access. This turns out to be a Node\Expr\ArrayDimFetch, then inside this you want to check if the variable it is using is _POST.

    Once you have identified this, you can replace that node with a new one which is just a string Node\Scalar\String_("Hello World!");.

    $traverser->addVisitor(new class extends NodeVisitorAbstract {
        public function leaveNode(Node $node) {
            if ($node instanceof Node\Expr\ArrayDimFetch
                && $node->var instanceof Node\Expr\Variable
                && $node->var->name === '_POST'
                ) {
                    // Change the $_POST['firstname'] and replace it with XXX value
                    return new Node\Scalar\String_("Hello World!");
                }
        }
    
    });
    
    $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
    echo $prettyPrinter->prettyPrintFile($traverser->traverse($ast));
    

    From your original code of

    <?php
    $firstname=  $_POST['firstname'];
    

    this code outputs....

    <?php
    
    $firstname = 'Hello World!';