PHP code generator

Posted on 18/9/08 by Felix Geisendörfer

Hey folks,

this is post #29 of my 30 day challenge. Yes, I know its a little late, but I just got out of the plane from NYC to Berlin. Turns out nobody asked for the 50 EUR so far, too bad ; ).

Anyway, if you find yourself writing PHP code that writes PHP code, you might appreciate a little class I just wrote. You can use it like this:

$Code = new PhpCode();
$Code->write('$foo = array(%d, %d, %d);', 1, 2, 3);
$Code->open('foreach ($foo as $bar) {');
$Code->write('echo $bar');
$Code->close('}');
echo $Code->compile();

And in return you will get

$foo = array(1, 2, 3);
foreach ($foo as $bar) {
  echo $bar;
}

So what it does it essentially provides you with a wrapper for sprintf and also automatically does all the code indention for you so you don't have to go crazy with putting "\t"'s everywhere.

Anyway here is the code of the class:

class PhpCode{
  var $lines = array();
  var $mode = 'append';

  public function compile() {
    $out = array();
    $depths = 0;
    foreach ($this->lines as $line) {
      if ($line['block'] == 'close') {
        $depths--;
      }
      $out[] = str_repeat("\t", $depths).$line['code'];
      if ($line['block'] == 'open') {
        $depths++;
      }
    }
    return join("\n", $out);
  }

  public function write($code) {
    $args = func_get_args();
    $this->inject(array(
      'code' => vsprintf($code, array_slice($args, 1)),
      'block' => false,
    ));
  }

  public function newLine() {
    $this->inject(array(
      'code' => '',
      'block' => 'open',
    ));
  }
 
  public function open($code) {
    $args = func_get_args();
    $this->inject(array(
      'code' => vsprintf($code, array_slice($args, 1)),
      'block' => 'open',
    ));
  }
 
  public function close($code) {
    $args = func_get_args();
    $this->inject(array(
      'code' => vsprintf($code, array_slice($args, 1)),
      'block' => 'close',
    ));
  }

  public function prepend() {
    $this->mode = 'prepend-once';
    return $this;
  }

  public function inject($line) {
    switch ($this->mode) {
      case 'append':
        array_push($this->lines, $line);
        break;
      case 'prepend':
      case 'prepend-once':
        array_unshift($this->lines, $line);
        break;
    }
 
    if ($this->mode == 'prepend-once') {
      $this->mode = 'append';
    }
  }
}

Let me know what you think, suggestions are more than welcome!

-- Felix Geisendörfer aka the_undefined

 
&nsbp;