自动解析PHP,将PHP代码与HTML分离

5
我正在处理一个大型PHP代码库,希望将PHP代码与HTML和JavaScript分离。(我需要在PHP代码上执行几个自动搜索和替换操作,并在HTML和JS上分别进行不同的操作)。是否有一个良好的解析引擎可以为我单独分离出PHP代码?我可以使用正则表达式来完成此操作,但它们并不完美。也许我可以使用ANTLR构建一些东西,但最好是一个现成的解决方案。
我应该明确:我不想或不需要一个完整的PHP解析器。只需要知道给定标记是: - PHP代码 - PHP单引号字符串 - PHP双引号字符串 - PHP注释 - 非PHP,而是HTML / JavaScript。

1
只是一个快速的提醒。从你的列表中划掉正则表达式! - Alin Purcaru
@Alin 我不太确定。请看我的回答为什么。 - Pekka
1
我认为你无法为PHP、HTML或JavaScript构建正则表达式。可能只能为它们的特定子集构建。 - Alin Purcaru
3个回答

4

PHP本身内置了tokenizer,它与PHP解释器嵌入在Zend引擎中。使用这些函数,您可以编写自己的PHP源代码分析或修改工具,而无需处理词法级别的语言规范。

您在评论中问是否可以从标记化输出中重新生成代码 - 可以的,所有空格都保留为T_WHITESPACE标记。以下是将标记化输出转换回代码的示例:

$regenerated='';

$tokens = token_get_all($code);
foreach($tokens as $idx=>$t)
{
    if (is_array($t))
    {

         //do something with string and comments here?
         switch($t[0])
         {
             case T_CONSTANT_ENCAPSED_STRING:
                  break;
             case T_COMMENT:
             case T_DOC_COMMENT:
                 break;

         }
         $regenerated.=$t[1];


    }
    else
    {
         $regenerated.=$t;
    }
}

如果我有令牌,如何重新生成源代码?使用原始缩进和行间距吗?包括注释吗?原始程序员会接受结果吗? - Ira Baxter
是的,空格和注释被视为任何其他标记 - 请参阅我的详细答案,了解如何从令牌化的数据重建原始源代码。 - Paul Dixon

3

为了将PHP与其他内容分离,PHP内置的标记解析器是您最好的选择:请参见token_get_all()

对于其他部分,您可能最好使用DOM解析器。以这种方式隔离<script>部分(以及外部脚本,甚至onXXXX事件)很容易。

不过,从解析的DOM树中重新构建相同的文档可能会很困难 - 我想这取决于您需要对结果进行什么操作以及原始HTML有多干净。在那部分中,正则表达式(呃!)可能效果更好。


-1

如果你只是想要检查标记,那么像其他人建议的那样,PHP分词器可能是一个不错的选择。

如果你想要以可靠的方式自动更改源代码,我不确定那会对你有所帮助。你将如何重新生成修改后的源文本?

另一种方法是使用程序转换引擎。这样的引擎可以解析源文本为抽象语法树,捕获程序的结构(以及所有标记的有效内容),并允许使用可靠的模式匹配/转换搜索和转换这些AST。要做到这一点,你需要一个能够可靠地解析PHP的引擎,并且能够从更改后的AST中产生可编译的源文本。

我们的DMS软件重构工具包是一个程序转换系统,它拥有一个强大的PHP前端,可以准确地处理PHP5的解析、转换和漂亮地打印回文本。 (正确地获取PHP解析器是很难的,因为该语言文档不完善)。由于前端可以准确地捕捉HTML和PHP代码,您不需要将文本分开;它们将被放置在独特的树节点中,以清晰可辨的方式。

要将所有回显字符串从小写转换为大写,您可以使用DMS解析PHP,然后应用以下转换规则:

 rule uppercase_echoed_string(s: STRING): statement -> statement
 =   "echo \s;" ->  "echo \uppercase\(\s\);".

这条规则是用DMS的规则规范语言(RSL)编写的,显然不是PHP。引号内的东西是PHP代码;这些是包裹在被操作的程序语言文本周围的引号。\字符是一个元转义符:\s表示必须匹配字符串文字的元变量,\uppercase是一个外部于RSL语言的DMS函数名称,( )是元括号,用于将大写应用于已匹配的字符串\s的元函数调用。由于该规则操作AST,因此不会产生混淆;它不会更改/* echo 'def' */的文本,因为那不是语句。

您可能需要几条规则来处理各种语法组合:在这种情况下,STRING仅指单引号的文字字符串;双引号字符串不是整体实体,而是由一系列与双引号字符串内的PHP表达式对应的QUOTED_STRING_FRAGMENTS组成的文本。

在转换过程结束时,更改后的AST将以原始缩进和注释的形式发出,除了应用变换的地方。

对于DMS来说,还有一个完全准确的JavaScript解析器,如果您想要准确地处理SCRIPT标签的内容,那么您就需要它。

如果您想要对源代码进行可靠的更改,我认为这是唯一好的方法。您可以尝试字符串操作和正则表达式,但解析PHP需要一个上下文无关的解析器,而REs不会做到这一点,因此您得到的任何结果都不可信。


非常有趣,Ira。这确实是一种强大的方法。具有讽刺意味的是,它可能会遇到一个困难,即当PHP代码被注释掉但仍需要修改时(正则表达式的技巧仍然可以找到它...)。另外,你提供的“程序转换”链接无法访问(404错误)。 - SRobertJames
@SRobertJames:如果你的代码中有时有注释,有时没有,那么你将无法进行可靠的更改。你如何区分哪些注释只是注释,哪些包含真正的代码?如果你坚持混合使用它们,你可以考虑使用标记编辑包含真正代码的注释,然后使用DMS处理常规代码,对于注释,你可以检查标记并将DMS解析器应用于注释主体,进行更改,美化修改并将其放回注释中。(链接已修复,抱歉)。 - Ira Baxter
@SRobertJames: 这引出了一个问题,你为什么要注释掉代码。如果你的目的是注释掉它,那么最好将该代码放在由配置开关控制的条件语句中,例如*if feature7 { code }*。这样可以让你禁用代码、通过设置功能开关启用代码,而不必处理“这个注释是否包含实际的代码?”这种模棱两可的情况。 - Ira Baxter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接