Back References in PHP: How to Create Variable Variables Using Them

I found and interesting problem today. I am working on a wordpress plugin and I wanted to use some sort of template to display the results of a search. I came out with a nice solution that would let me use some text wrapped around % and %. This should be replaced by some variable value. Since I wanted the template to be future proof, I did not want to do something like:

$template = preg_replace(‘/%name%/’, $name);
$template = preg_replace(‘/%last%/’, $last);

but rather, I wanted something that would “guess” the variable names from the text to be replaced. This seemed like an easy thing to do. The first thing I thought was backreferences.

The idea was that if I created a variable variable from the backreference I would be able to accomplish the job. However, it was not that easy.

This is what I have.

I have a function that takes an argument. This argument is an array of arrays containing the data to be placed in the template. A simplified version of the array would look like this:

array(
  [0] = array(
    [name]="John",
    [last]="Smith",
    [phone]="(123)123-1234"
  ),
  [1] = array(
    [name]="John",
    [last]="Smith",
    [phone]="(123)123-1234"
  ),
  [2] = array(
    [name]="John",
    [last]="Smith",
    [phone]="(123)123-1234"
  )
)

so, that is what the function would receive. The function looks like this:

function templating($data){
  foreach($data as $d){
    extract($d);
    ob_start();
    ?>
    <p>
      Name: %name% %last%
      <br />
      Phone: %phone%
    </p>
    <?php
    $mkp = ob_get_contents();
    ob_end_clean();
    $mkp = preg_replace('/%(.+?)%/', $"$1", $mkp);
    echo $mkp;
  }
}

Well, this doesn’t work. It turns out you cannot create a variable variable out of the backreference like I tried to do there. After some failed attempts to create a variable variable out of a backreference, I tried something different.

Before we continue, if you don’t get it. Let me explain the thoughts that led me to write the code up there.

Since I have the array structured showed earlier, I thought I could extract the values into their own variables and reference them in the replacement. The commented code might help you understand better, (and maybe even me in the future when I forget all this XD)

function templating($data){
  foreach($data as $d){
    extract($d);
    //at this point I have individual variables holding the data for each loop run
    //so, for the first loop I have:
    //$name = "John"
    //$last = "Smith"
    //$phone = "(123)123-1234"
    ob_start();
    ?>
    <p>
      Name: %name% %last%
      <br />
      Phone: %phone%
    </p>
    <?php
    $mkp = ob_get_contents();
    ob_end_clean();
    $mkp = preg_replace('/%(.+?)%/', $"$1", $mkp);//the regexp matched %name%, %last%, %phone%, and returned name, last, and phone as backreference values.
    //I thought I could create a variable variable using $"$1", since that would
    //become $name, $last, and $phone, but that is not what happens.
    echo $mkp;
  }
}

I decided to take a look at preg_replace_callback.

I rewrote the function like this:

function templating($data){
  foreach($data as $d){
    extract($d);
    ob_start();
    ?>
    <p>
      Name: %name% %last%
      <br />
      Phone: %phone%
    </p>
    <?php
    $mkp = ob_get_contents();
    ob_end_clean();
    $mkp = preg_replace_callback('/%(.+?)%/', function($m){
      global ${$m[1]};
      return ${$m[1]};
    }, $mkp);
    echo $mkp;
  }
}

But that didn’t work either, although it was closer.
I kept on modifying the function a bit more, and came out with this:

function templating($data){
  foreach($data as $d){
    ob_start();
    ?>
    <p>
      Name: %name% %last%
      <br />
      Phone: %phone%
    </p>
    <?php
    $mkp = ob_get_contents();
    ob_end_clean();
    $mkp = preg_replace_callback('/%(.+?)%/', function($m){
      extract($d);
      return ${$m[1]};
    }, $mkp);
    echo $mkp;
  }
}

This did not work either, but it gave me a clue of why the previous function hadn’t worked. I thought that since the callback was within templating, it would have access to the variables, but it did not. Then I made $d global in the callback, but it still didn’t work. So I figured that it was not accessing the variable. I finally made $d global in templating, and that worked. Note that we declare $d global before the loop, otherwise the $d declared in the loop will be overwritten by the global declaration of $d.

The function ended up like this:

function templating($data){
  global $d;
  foreach($data as $d){
    ob_start();
    ?>
    <p>
      Name: %name% %last%
      <br />
      Phone: %phone%
    </p>
    <?php
    $mkp = ob_get_contents();
    ob_end_clean();
    $mkp = preg_replace_callback('/%(.+?)%/', function($m){
      global $d;
      extract($d);
      return ${$m[1]};
    }, $mkp);
    echo $mkp;
  }
}

This way I was able to accomplish the goal, and I got a pretty nice templating system. Now I can even move the HTML into a template file and load that template file instead of having the markup in the functions. Why would I do that? Because that keeps the code cleaner and makes it easy to edit the template without touching the function, which is always a good thing.

I was curios about why the very first attempt did not work, so I asked a question on one of those Q&A sites, you can follow it here: http://programmers.stackexchange.com/questions/133685/is-it-possible-to-create-a-variable-variable-from-a-back-reference-in-php

I also found this page useful since I didn’t remember how to get the backreferences in php:
http://stackoverflow.com/questions/4694169/how-to-use-backreferences-in-php

And finally, if you want to read more about variable variables, you should read:
http://www.php.net/manual/en/language.variables.variable.php