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->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
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:
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
You can skip to the end and add a comment.
is it too late? I'm claiming those 50 bucks!
Neat stuff Felix. I've always detested inserting piles of \t and \n into code generation code.
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 ; ).
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.
This might be a nice way to make scaffold/bake templates more readable.
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();
}
}');
@rafaelbandeira3 Oh... so kinda like http://us2.php.net/token_get_all ?
@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...
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.
Lucian Lature, Silver Knight: I messed with the regular RSS feed when implementing the new one for comments - sorry about that.
Funny post! The code you provide is actually longer than the code it produces :) But maybe it's just the example...
Might also be cool if it could automagically detect indentation levels once it detects a foreach, for, switch, if, function, class, etc.
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 .
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!')
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.
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.