debuggable

 
Contact Us
 

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;

You can skip to the end and add a comment.

Lucian Lature  said on Sep 18, 2008:

It seems that the RSS feeds are broken.
I receive this into my Thunderbird: http://debuggable.com/posts/48d2112f-53b4-4a15-b6c5-5d5f4834cda3 .

Pls take a look at this.

rafaelbandeira3 said on Sep 18, 2008:

is it too late? I'm claiming those 50 bucks!

Mark Story said on Sep 18, 2008:

Neat stuff Felix. I've always detested inserting piles of \t and \n into code generation code.

Felix Geisendörfer said on Sep 18, 2008:

rafaelbandeira3: Yes, you have to claim the money before I get the post up and point out the time delay. You guys have to do your job as well ; ).

rafaelbandeira3 said on Sep 18, 2008:

blah... ;-)
ok, ok...

now, not that I tried or ever had a good why to generate my own PHP, but your solution is really elegant.

gwoo said on Sep 18, 2008:

This might be a nice way to make scaffold/bake templates more readable.

rafaelbandeira3 said on Sep 18, 2008:

I was thinking now... wouldn't it be less verborragic if it could be parsed by regex, or line-by-line?

$Code->compile('class %sController extends AppController {
function paginate() {

return getPaginatedData();

}

}');

Nate Abele said on Sep 18, 2008:

@rafaelbandeira3 Oh... so kinda like http://us2.php.net/token_get_all ?

rafaelbandeira3 said on Sep 19, 2008:

@Nate Abele hum... it's not what I thought, but it's even better! Let's say you managed learning all those tokens - or not, maybe just a few of them to let you know when a block was opened and when it was closed - and based on them one can go calculating it's indentation.
Of course it should be enclosed in a class like Felix's approach, the would be reduce all verborragic and overhead - ok I don't really bother about overhead, but anyway...

Silver Knight  said on Sep 19, 2008:

I also had the same problem with the RSS feeds as Lucian Lature had. The named parameters are not coming through correctly. I was still able to get to the articles properly by manually editing the URLs as follows:

http://debuggable.com/posts/48d2112f-53b4-4a15-b6c5-5d5f4834cda3
to

http://debuggable.com/posts/anything:48d2112f-53b4-4a15-b6c5-5d5f4834cda3

The addition of a name for the parameter (I just used a single letter) caused the site to do a proper lookup for each article and changed the URL appropriately.

HTH someone...

In case such a thing ever happens again, Felix or Tim or whoever writes the code for this blog should maybe have the Posts controller do a lookup on the article based on the UUID if a bare UUID is passed instead of a named parameter UUID so that the problem is automagically fixed on the fly.

Felix Geisendörfer said on Sep 19, 2008:

Lucian Lature, Silver Knight: I messed with the regular RSS feed when implementing the new one for comments - sorry about that.

Bart said on Sep 20, 2008:

Funny post! The code you provide is actually longer than the code it produces :) But maybe it's just the example...

Tim Koschützki said on Sep 21, 2008:

Might also be cool if it could automagically detect indentation levels once it detects a foreach, for, switch, if, function, class, etc.

Simon said on Sep 24, 2008:

To check the code against syntax errors you could implement something like this: http://www.php.net/manual/en/function.php-check-syntax.php#77318 .

There is also a nice PEAR package to get the required PHP version for a pice of code: http://pear.php.net/package/PHP_CompatInfo .

nathan  said on Jul 05, 2009:

this would provide a slick interface for creating php files directly from database queries. i would suggest creating a set of method that act as open(), but would be shorthand, like $code->foreach('foo','bar'), $code->class('foo','bar') ('class foo extends bar {'), $code->if('foo') and so forth. Also $code->comment('bar!')

Driersunrendy  said on Nov 24, 2009:

Je vous conseille de visiter le site, sur lequel il y a beaucoup d'articles sur cette question. viagra sur le net acheter du viagra http://lettresdudroit.com>acheter viagra generique

This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.