首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >除了使用Parser的Function_节点之外,还有更好的方法来删除所有其他代码吗?

除了使用Parser的Function_节点之外,还有更好的方法来删除所有其他代码吗?
EN

Stack Overflow用户
提问于 2021-02-19 12:03:47
回答 2查看 195关注 0票数 1

这里是PHP的新手。我正在使用Parser的尼基奇.

我想删除所有不是Node\Stmt\Function_类型的节点。

目前,我可以通过以下代码来完成这一任务:

代码语言:javascript
复制
$traverser = new NodeTraverser();
$traverser->addVisitor(new class extends NodeVisitorAbstract {
    
    // To leave functions alone
    public function enterNode(Node $node) {
        if ($node instanceof Node\Stmt\Function_) {
            return NodeTraverser::DONT_TRAVERSE_CHILDREN;
        }
    }

    // To remove any other nodes that are not 'Function_' type
    // Currently can only remove 'If', 'For' and 'Expression' types
    public function leaveNode(Node $node) {
        if ($node instanceof Node\Stmt\If_ || 
        $node instanceof Node\Stmt\For_ || 
        $node instanceof Node\Stmt\Expression) {
            return NodeTraverser::REMOVE_NODE;
        }
    }
});

但是这将导致一个包含所有不同节点类型的大型if(...||)语句。我尝试使用下面所示的if(!$node instanceof Node\Stmt\Function_),但这将导致错误。我尝试过if(!($node instanceof Node\Stmt\Function_))也有同样的错误。

代码语言:javascript
复制
public function leaveNode(Node $node) {
    if (!$node instanceof Node\Stmt\Function_) {
        return NodeTraverser::REMOVE_NODE;
    }
}

#错误:

代码语言:javascript
复制
PHP Fatal error:  Uncaught LogicException: leaveNode() returned invalid value of type integer in /usr/local/bin/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php:168
Stack trace:
#0 /usr/local/bin/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(146): PhpParser\NodeTraverser->traverseNode()
#1 /usr/local/bin/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(223): PhpParser\NodeTraverser->traverseNode()
#2 /usr/local/bin/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php(91): PhpParser\NodeTraverser->traverseArray()
#3 /home/test/Desktop/test.php(130): PhpParser\NodeTraverser->traverse()
#4 {main}
  thrown in /usr/local/bin/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php on line 168

我理解删除节点只能在leaveNode中实现。因此,我在想,是否有更好的方法来做到这一点,而不是分块巨大的if语句来匹配每一种类型,而不是“Function_”?

谢谢!

编辑让我尝试更多地分享更大的情况以及预期的输入和输出。

总体情况:我想构建一个程序,当给定PHP代码时,它可以根据请求检索特定的节点(例如,案例、函数、循环)。在这种情况下,它是函数节点。为了做到这一点,我现在正在使用Parser。如果这样做不起作用,我可能会在将来探索使用令牌化。

输入:如下所示的PHP代码示例。

代码语言:javascript
复制
function writeMsg() {
  echo "Hello world!";
}

writeMsg(); 

$x = 5;

if ($x = 5) {
  echo "x is 5";
} else {
  echo "Something is wrong";
}

如果用户希望检索函数节点,则输出将仅仅是包含其内容的函数,而不是其他任何内容:

代码语言:javascript
复制
function writeMsg()
{
    echo "Hello world!";
}

如果用户希望检索If语句,则输出如下:

代码语言:javascript
复制
if ($x = 5) {
    echo "x is 5";
} else {
    echo "Something is wrong";
}

大图将是检索用户指定的任何类型的节点。不过,就目前而言,函数可以使用。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-02-20 09:57:32

作为另一种解决方案,它在找到函数时从AST中提取函数。这还可以提取类中的函数(使用instanceof Node\Stmt\ClassMethod)以及嵌套函数.

代码语言:javascript
复制
$traverser = new NodeTraverser();
class FunctionExtract extends NodeVisitorAbstract {
    // List of functions
    public $functionList = [];

    public function enterNode(Node $node) {
        if ($node instanceof Node\Stmt\Function_
                || $node instanceof Node\Stmt\ClassMethod ) {
            $this->functionList[] = $node;
            return NodeTraverser::DONT_TRAVERSE_CHILDREN;
        }
    }
}
$extract = new FunctionExtract();
$traverser->addVisitor($extract);
$traverser->traverse($ast);

$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$newCode = $prettyPrinter->prettyPrintFile($extract->functionList);

echo $newCode;
票数 1
EN

Stack Overflow用户

发布于 2021-02-20 08:19:23

经过一些实验,我发现使用节点遍历方法可以工作,但它不能删除代码基础中的语句。

所以代码就像..。

代码语言:javascript
复制
<?php
if ( $a == 1) return;
$b=1;
function test2(string $a): int {
    $c = 1;
    if ($c == 2) {
        $c = 1;
    }
    return 2;
}

用..。

代码语言:javascript
复制
$traverser = new NodeTraverser();
$traverser->addVisitor(new class extends NodeVisitorAbstract {
    public function enterNode(Node $node) {
        if ($node instanceof Node\Stmt\Function_
                || $node instanceof Node\Stmt\If_ ) {
            return NodeTraverser::DONT_TRAVERSE_CHILDREN;
        }
    }

    public function leaveNode(Node $node) {
        if ($node instanceof Node\Stmt &&
                ! $node instanceof Node\Stmt\Function_ &&
                ! $node instanceof Node\Stmt\InlineHTML &&
                ! $node instanceof Node\Stmt\Class_) {
            return NodeTraverser::REMOVE_NODE;
        }
    }
});

产生

代码语言:javascript
复制
<?php 
if ($a == 1) {
}
$b = 1;
function test2(string $a) : int
{
    $c = 1;
    if ($c == 2) {
        $c = 1;
    }
    return 2;
}

因此,只有第一个return下的if被删除。

注意,我使用$node instanceof Node\Stmt来表示它是一个语句类型节点,然后只说离开! $node instanceof Node\Stmt\Function_节点。

所以更改为简单地传递原始AST数组并复制任何函数.

代码语言:javascript
复制
$newAst = [];
foreach ( $ast as $node )   {
    if ($node instanceof Node\Stmt\Function_) {
        $newAst[] = $node;
    }
}
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$newCode = $prettyPrinter->prettyPrintFile($newAst);

echo $newCode;

发出去..。

代码语言:javascript
复制
<?php

function test2(string $a) : int
{
    $c = 1;
    if ($c == 2) {
        $c = 1;
    }
    return 2;
}

如果只需要函数签名,也可以在复制时重置语句.

代码语言:javascript
复制
    $node->stmts = [];
    $newAst[] = $node;

而你最终会..。

代码语言:javascript
复制
<?php

function test2(string $a) : int
{
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66277240

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档