execute php script without loops or branching statement in it

79 views Asked by At

A PHP parser that can strip all the looping statements and branching statements and execute other

PHP codes eg:-

input

<?php
if(1){
echo "hello";
}
while(1){
echo "world";
}

output

<?php
echo "hello";
echo "world";
1

There are 1 answers

2
Ira Baxter On BEST ANSWER

This is extremely hard to do without a full language parser.

You can do this with a program transformation system (PTS). These are tools that can parse source code into compiler data structures (often Abstract Syntax Trees [ASTs]), can make changes to the ASTs, and then can regenerate valid source text from the modified compiler data structures.

A weak PTS will only let you walk the tree/inspect/change it procedurally, in the classic way a compiler does it. With this approach, you must be intimately familiar with the data structures (e.g., you have to know the precise structure of the tree), and for a real language, there's a lot of detail to know and get right. This works if you have lots of enthusiasm.

A good PTS will let you provide it source-to-source transformations that it will use to search/change the tree. Such rewrites look like:

  when you see *thispattern*, replace it by *thatpattern*, if *condition*

where thispattern and thatpattern are patterns written in the source langauge being transformed. The PTS takes care of converting them to corresponding compiler data structures so you can get by with a lot less knolwedge.

In OP's case, he needs a PTS that can parse and transform PHP.

The only "good" PTS I know that can do this off-the-shelf is our DMS Software Reengineering Toolkit with its PHP front end.

You have to write a short DMS metaprogram to open and read the file, get the transforms and apply them, and then prettyprint the result (oversimplifying a bit for clarity):

 (define main
    (action (procedure void)
        (= AST  (Registry:Parse PHPDomain `my_file.php'))
        (Registry:ApplyTransforms AST (. `my_rewrite_rules.rsl') (. `strip_control_flow'))
        (local (= [os OutputStream:Stream] (OutputSteam:Open `updated_my_file.php'))=
               (Registry:PrettyPrint os PHPDomain AST))
               (= os (OutputStream:Close os))
        )local
    )action
  )define

The bulk of the work is accomplished by DMS rewrite rules in the file "my_rewrite_rules.rsl":

 domain PHP~PHP5.

 rule strip if_then(c: expression, s: statement):
     statement -> statement =
 " if (\c) \s" ->  "\s".

 rule strip if_then_else(c: expression, s1: statement, s2: statement):
     statement -> statement =
 " if (\c) \s1 else \s2" -> " { \s1 \s2 } ".

 rule strip while(c: expression, s: statement):
     statement -> statement =
 " while (\c) \s" ->  "\s".

 rule strip catch( b1: statements, l: catch_clauses, t: type, e: expression, b2: statements):
     statement -> statement =
 " try { \b1 } \l catch ( \t \e ) { \b2 } "
 -> " { try { \b1 } \l ; \b2 } ".

 rule strip_trivial_try( b1: statements):
     statement -> statement =
 " try { \b1 } " -> "{ \b1 }".

 rule strip_useless_block( b:statements, s: statements):
    statements -> statements =
 "  { \b } \s " ->  " \b \s ".

 ruleset strip_control_flow = {
     strip_if_then,
     strip_if_then_else,
     strip_while,
     strip_catch,
     strip_trivial_try,
     strip_useless_block }

etc. I didn't cover all the cases but it should be obvious how to proceed.

To explain the above: DMS rewrite rules take the form

   rule rulename ( pattern_variable_declarations):
       syntaxcategory -> syntaxcategory
   "thispattern" -> "thatpattern".

thispattern and thatpattern are written inside metaquotes "..." that distinguish source program pattern text from the syntax of the rewrite rule language itself. Withing metaquotes, one finds source language text intermixed with pattern variables \x whose syntax category is declared as x: category in the pattern variable declarations. You do have to know the major syntax categories of the language (e.g., "statement" vs. "statements" vs "expression" but you don't have know about all the internal structure of while loop.

The ruleset groups a set of interesting named rules into a convenient bundle that can be applied in a batch; you can see how this ruleset is mentioned in the DMS metaprogram.

A trick used when writing this ruleset is to have each rule strip its controlled-content elements into a block { ... }, because blocks are acceptable as statements. The cleanup rule strip_useless_blocks then gets rid of any egregious blocks that are created.

You can see more about how DMS rewrite rules are written here.

These rewrite rules will incrementally convert OP's program throught a series of stages that follow (you could prettyprint the full ASTs after each transform to see this):

Start:

<?php
if(1){
echo "hello";
}
while(1){
echo "world";
}

after strip_if_then:

<?php
{
echo "hello";
}
while(1){
echo "world";
}

after strip_while:

<?php
{
echo "hello";
}
{
echo "world";
}

after 1st application of strip_useless_block:

<?php
echo "hello";
{
echo "world";
}

after 2nd application of strip_useless_block:

<?php
echo "hello";
echo "world";

and we have OP's desired result. This is a lot more spectacular on a big file.

So, OP's task is fairly easy to do with a good PTS.

I'll admit I have no clue as to why somebody would want strip out the control flow like this. But the point of a PTS is you can configure to carry out arbitrary code change tasks that are difficult to do by hand.