Including only once

This week I finally got around to the implementation of a strategy that combines constant-prorogation and some sort of type-state. It was a bit tricky to get it completely right, resulting in some very weird behavior during testing. Fortunately, the problem has been solved and the result is pretty good!

So what problem does this strategy solve? In order to explain this we will first take a quick look at how constant propagation is being handled within PHP-Front (and other Stratego-libraries).

When a value of a variable is known, for example when it is defined in an expression like $foo = 5, PHP-Front will add a dynamic rule rewriting the variable to the known value. This dynamic rule can later be used to retrieve the value, for example in the expression $bar = $foo+1. This last expression will then be rewritten to $bar = 5+1 which can be completely calculated again.

Using dynamic rules for linear code is easy, but when code branches it becomes a little bit more complicated. Take this example code:
  $foo = 'say';
  if($val){
     $bar = 'hello';
  } else {
     $bar = 'world';
  }

The value of $bar can not be statically determined, but we want to keep the knowledge about the variable $foo. A valid way to do this is to take the intersection of the two sets of dynamic rules, the normal one and the one after evaluating the if-branch. This will keep all information about variables that are not used, or given the same value, within the branches of the if.
If you want to know more about dynamic rules and constant propagation I suggest you take a look at this paper.

Besides the information about the constant propagation we need to take a look at the inclusion mechanism of PHP. You might be aware of the fact that PHP offers both an include as well as an include_once construct. The first one will always include a file, the latter one will only include a file when it wasn't included before.

Dealing with files that are always included in combination with constant propagation is rather easy. We just retrieve/parse/evaluate the file during the constant propagation. Dealing with files that are only included once is also easy with linear code, but things get tricky when branching is involved. Take this piece of code for example:
  if($val){
    include_once 'foo.php';
  } else {
    //other things
  }
   //some code
    include_once 'foo.php';

Within the if-branch the file foo.php might be included and introduce some dynamic-rules. The code for the if will handle this probably so that is not a problem.

But what must we do when we arrive at the other include_once-construct for the same file? Well, there are three different scenario's that can occur:
  1. File has not been included
  2. File might have been included
  3. File is definitely included
The first and last one are easy, just include the file or not, the second one is more complicated with respect to dynamic rules. We will again have to take the intersection of the current set of dynamic rules and the set of dynamic rules after inclusion of the file. This will make sure that we only keep the rules that will be the same in both cases.
Notice that we will have to keep track of the inclusion-state of every file. This can (and is) implemented as a state for a file, again with dynamic rules.

Implementing both of these solutions with dynamic rules separately is not that difficult, there are enough sources to figure things out. However, combining these approaches is completely trivial because you will have to deal with dynamic rule-sets and all sorts of merge-strategies. Fortunately for you this is all done behind the scenes. Please enjoy the better constant propagation as of revision 332.

No comments: