title: 初识PHP-Parser
date: 2020-9-13 03:11:21
categories: 
- PHP-Parser
 tags:
- PHP-Parser
- web
- 
php
 toc: true
PHP-Parser 用于代码混淆的代码
learn from
http://j0k3r.top/2020/03/24/php-Deobfuscator/#0x01-php-parser
下载
curl -s http://getcomposer.org/installer | php 
遇到权限问题时,可以输入一下命令
sudo chown -R 'user-name' /home/'user-name'/.composer
之后再在所需项目中php composer.phar require nikic/php-parser
为了更方便调用composer,可以输入一下命令
mv composer.phar /usr/local/bin/composer
如果composer require nikic/php-parse遇到长时间卡住,可以尝试更新源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
项目: https://github.com/nikic/PHP-Parser
runoob 再次好用
生成AST
首先介绍一下PHP-Parser中定义的一些节点类型:
(1)PhpParser\Node\Stmt是语句节点,不带任何返回信息(return)的结构,如赋值语句”$a = $b” ;
(2)PhpParser\Node\Expr是表达式节点,可以返回一个值的语言结构,如$var和func()。
(3)PhpParser\Node\Scalar是常量节点,可以用来表示任何常量值。如’string’,0,以及常量表达式。
(4)还有一些节点没有包括进去,如参数节点(PhpParser\Node\Arg)。
一些节点类的名称使用了下划线,这是为了避免和PHP关键字冲突。
PHP-parser的test.php下,该代码片段会生成AST
<?php
use PhpParser\Error;
use PhpParser\ParserFactory;
require dirname(__FILE__).'/vendor/autoload.php';  #composer require nikic/php-parse 成功后会生成这个文件,当然还有其他的文件
$code = <<<'CODE'
<?php
echo 'Hello PHP';
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);// 优先解析 PHP7 代码
try {
    $stmts = $parser->parse($code);//使用 ParserFactory 的 parse 方法解析代码,得到一个 statement 节点数组
    var_dump($stmts);
} catch (Error $e) {
    echo 'Parse Error: ', $e->getMessage();//语法错误可以通过 PhpParser\Error 来捕获
}output:
/home/fe1w0/babyshop/ttt.php:15:
array(1) {
  [0] =>
  class PhpParser\Node\Stmt\Echo_#1131 (2) {
    public $exprs =>
    array(1) {
      [0] =>
      class PhpParser\Node\Scalar\String_#1130 (2) {
        ...
      }
    }
    protected $attributes =>
    array(2) {
      'startLine' =>
      int(3)
      'endLine' =>
      int(3)
    }
  }
}使用 NodeDumper来查看
<?php
use PhpParser\Error;
use PhpParser\ParserFactory;
use PhpParser\NodeDumper;
require dirname(__FILE__).'/vendor/autoload.php';  #composer require nikic/php-parse 成功后会生成这个文件,当然还有其他的文件
$code = <<<'CODE'
<?php
echo 'Hello PHP';
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);// 优先解析 PHP7 代码
try {
    $stmts = $parser->parse($code);//使用 ParserFactory 的 parse 方法解析代码,得到一个 statement 节点数组
    $nodeDumper = new NodeDumper;
    echo $nodeDumper->dump($stmts), "\n";
    #var_dump($stmts);
} catch (Error $e) {
    echo 'Parse Error: ', $e->getMessage();//语法错误可以通过 PhpParser\Error 来捕获
}output:
array(
    0: Stmt_Echo(
        exprs: array(
            0: Scalar_String(
                value: Hello PHP
            )
        )
    )
)修改节点值 将Hello PHP改为Hello world
<?php
use PhpParser\Error;
use PhpParser\ParserFactory;
use PhpParser\NodeDumper;
use PhpParser\NodeTraverser;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use PhpParser\PrettyPrinter;
require dirname(__FILE__).'/vendor/autoload.php';
$code = <<<'CODE'
<?php
echo 'Hello PHP';
CODE;
class MyNodeVisitor extends NodeVisitorAbstract
{
    public function leaveNode(Node $node) {
        if ($node instanceof Node\Scalar\String_) {
            $node->value = 'Hello Word';
        }
    }
}
class PrintNodeVisitor extends NodeVisitorAbstract
{
    public function leaveNode(Node $node) {
        if ($node instanceof Node\Stmt\Echo_) {
            return new PhpParser\Node\Stmt\Expression( new Node\Expr\Print_(new Node\Scalar\String_(($node->exprs)[0]->value)) );
        }
    }
}
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
    $stmts = $parser->parse($code);
    $traverser = new NodeTraverser;
    // 添加 node visitors
    $traverser->addVisitor(new MyNodeVisitor);
    $traverser->addVisitor(new PrintNodeVisitor);
    // 遍历 AST
    $new_stmts = $traverser->traverse($stmts);
    // 将 AST 转为 PHP 代码
    $prettyPrinter = new PrettyPrinter\Standard;
    $new_code = $prettyPrinter->prettyPrintFile($new_stmts);
    echo $code.PHP_EOL;
    echo "--After parser:--\n\n".$new_code;
} catch (Error $e) {
    echo 'Parse Error: ', $e->getMessage();
}




