This is a stupid experiment about making a PHP source code preprocessor in PHP, and also try to implement some closure support in PHP 5.2, which I’m still using. Here’s how it works:
- Create a custom stream filter (thanks to this blog post) that process the source code using a function.
- Create a function that transforms anonymous functions into create_function calls, and also make it support the “use” keyword.
- Then include file like this:
include ('php://filter/read=preprocess/resource=(filename)'); - The transform function also adds
'php://filter/read=preprocess/resource=' .after each occurence ofinclude/include_once/require/require_once, this way all included files are proprocessed too!
After some hours of experimenting, I came up with this code. It reads in realindex.php that contains PHP code with closures and transform the source:
<?php
// index.php
// Closure Preprocessor by the DtTvB
$closure_preprocessor_data = array();
$closure_preprocessor_id = 0;
function closure_preprocessor__quote($x) {
return '\'' . strtr($x, array('\\' => '\\\\', '\'' => '\\\'')) . '\'';
}
function closure_preprocessor__create($args, $code, $imports) {
global $closure_preprocessor_id, $closure_preprocessor_data;
$id = $closure_preprocessor_id ++;
$closure_preprocessor_data[$id] = array();
$importcode = '';
$j = 0;
foreach ($imports as $k => $v) {
$closure_preprocessor_data[$id][$j] = &$imports[$k];
$importcode .= '$' . $k . ' = &$GLOBALS[\'closure_preprocessor_data\'][' . $id . '][' . $j . '];';
$j ++;
}
return create_function($args, $importcode . $code);
}
function closure_preprocessor__function($data) {
preg_match ('~function\s*([^\(]+)?\(((?:\'(?:\\\\.|[^\'])*\'|"(?:\\\\.|[^"])*"|.)*?)\)\s*(use\s*\(([^)]*)\)\s*)?\{~si', $data, $head);
// print_r ($head);
$body = substr(substr($data, strlen($head[0])), 0, -1);
$body = substr(substr(closure_preprocessor('<?php ' . $body . ' ?>'), 5), 0, -3);
if (!empty($head[1])) { // Has name. Leave unchanged.
return $head[0] . $body . '}';
}
if (!empty($head[4])) {
preg_match_all ('~&?\$([^,\s]*)~', $head[4], $imports, PREG_SET_ORDER);
$importcode = array();
foreach ($imports as $v) {
$importcode[] = closure_preprocessor__quote($v[1]) . ' => ' . $v[0];
}
return 'closure_preprocessor__create(' . closure_preprocessor__quote($head[2]) . ', ' . closure_preprocessor__quote($body) . ', array(' . implode(',', $importcode) . '))';
} else {
return 'create_function(' . closure_preprocessor__quote($head[2]) . ', ' . closure_preprocessor__quote($body) . ')';
}
}
function closure_preprocessor($data) {
$tokens = token_get_all($data);
$out = '';
$buffer = '';
$level = 0;
foreach ($tokens as $v) {
if ($level > 0) {
if (is_string($v) && $v == '{') {
$buffer .= $v;
$level ++;
} else if (is_string($v) && $v == '}') {
$level --;
$buffer .= $v;
if ($level == 1) {
$level --;
$out .= closure_preprocessor__function($buffer);
}
} else {
$buffer .= is_array($v) ? $v[1] : $v;
}
} else {
if (is_array($v) && $v[0] == T_FUNCTION) {
$buffer = $v[1];
$level ++;
} else if (is_array($v) && ($v[0] == T_INCLUDE || $v[0] == T_INCLUDE_ONCE || $v[0] == T_REQUIRE || $v[0] == T_REQUIRE_ONCE)) {
$out .= $v[1] . ' \'php://filter/read=preprocess/resource=\' . ';
} else {
$out .= is_array($v) ? $v[1] : $v;
}
}
}
// echo $out;
return $out;
}
class preprocessor_filter extends php_user_filter {
var $data = '';
function filter($in, $out, &$consumed, $closing) {
while($bucket = stream_bucket_make_writeable($in)) {
$this->data .= $bucket->data;
$this->bucket = $bucket;
$consumed = 0;
}
if ($closing) {
$consumed += strlen($this->data);
$this->bucket->data = closure_preprocessor($this->data);
//echo $this->bucket->data . "\n\n";
$this->bucket->datalen = strlen($this->bucket->data);
if (!empty($this->bucket->data))
stream_bucket_append($out, $this->bucket);
return PSFS_PASS_ON;
}
return PSFS_FEED_ME;
}
}
stream_filter_register ('preprocess', 'preprocessor_filter');
include ('php://filter/read=preprocess/resource=realindex.php');
?>
And here is realindex.php
<?php
include 'testinclude.php';
$h1 = makeTag('h1');
$p = makeTag('p');
echo $h1($greet('Closures'));
$incrementA = makeIncrementer();
$incrementB = makeIncrementer(10);
$incrementC = makeIncrementer(20);
echo $p('Increment A: ' . $incrementA());
echo $p('Increment A: ' . $incrementA());
echo $p('Increment A: ' . $incrementA());
echo $p('Increment A: ' . $incrementA());
echo $p('Increment B: ' . $incrementB());
echo $p('Increment B: ' . $incrementB());
echo $p('Increment B: ' . $incrementB());
echo $p('Increment C: ' . $incrementC());
echo $p('Increment C: ' . $incrementC());
echo $p('Increment C: ' . $incrementC());
echo $p('Increment A: ' . $incrementA());
echo $p('Increment B: ' . $incrementB());
echo $p('Increment C: ' . $incrementC());
$a = 1; $b = 2; $c = 3;
$adderA = makeAdder($a);
$adderB = $adderA($b);
echo $p($a . ' + ' . $b . ' + ' . $c . ' = ' . $adderB($c));
echo $p(camelCase('hello-world'));
?>
And another file testinclude.php
<?php
function makeIncrementer($start = 0) {
return function() use (&$start) {
return ++$start;
};
}
function makeAdder($a) {
return function($b) use ($a) {
return function($c) use ($a, $b) {
return $a + $b + $c;
};
};
}
function makeTag($tag) {
return function($content) use ($tag) {
return '<' . $tag . '>' . $content . '</' . $tag . '>';
};
}
function camelCase($x) {
return preg_replace_callback('~-([a-z])~', function ($match) {
return strtoupper($match[1]);
}, $x);
}
$greet = function($name) {
return sprintf("Hello %s!", $name);
};
?>
Running the code, it said:
Hello Closures!
Increment A: 1
Increment A: 2
Increment A: 3
Increment A: 4
Increment B: 11
Increment B: 12
Increment B: 13
Increment C: 21
Increment C: 22
Increment C: 23
Increment A: 5
Increment B: 14
Increment C: 24
1 + 2 + 3 = 6
helloWorld
The limitations is that it somehow does not work very well in some classes. For example, it can’t access private class members (not sure what it’s called, I don’t do much OOP).
And this code probably has bugs. If you found any bugs in this code and you have a fix please email me so I can update the code. Lol
Thank You, All about buy diflucan without no prescription veterinary [url=http://mag.ma/buydiflucanwithout#1]All about buy diflucan without no prescription veterinary[/url], 9710, darvocet without a prescription for you [url=http://mag.ma/darvocetwithoutaprescription#1]darvocet without a prescription for you[/url], 789, Cheap how long does percocet stay in your system [url=http://mag.ma/howlongdoespercocet#1]Cheap how long does percocet stay in your system[/url], zsbsa, does diovan have significant side effects [url=http://mag.ma/doesdiovanhave#1]does diovan have significant side effects[/url], wafvyz, buy percocet online without a prescription for you [url=http://mag.ma/buypercocetonlinewithout#1]buy percocet online without a prescription for you[/url], %))),
—All about darvocet without a prescription