Topic: "meta-php" ... using php to generate ... php!

I'd like to share an idea that I've been toying with ... meta-programming in php. Let's start out directly with an example:

file: "test.meta.php"

<#

function complicated($number) {
    echo floor((10 / $number*123 + 5) * $number);
}

#>

#for($i=1; $i<10; $i++) {
    echo "<# complicated($i) #><br />";
#}

In the above example, the code blocks surrounded by <# and #> are considered "meta" code. The same goes for lines beginning with # - this means the same thing, but for readability, I chose to have two different syntaxes ... so you use # when you want to write a single line of meta-code, or <# and #> when you want to write an entire block of meta-code, such as the "meta function" called "complicated" at the beginning of the script above.

Obviously, the above code won't execute, so we need a "real" php script in front of it, like the example below.

file: "test.php"

<?php

require "_meta.php";

require __meta("test.meta.php");

?>

The function "__meta" is a function that "compiles" the meta-script, and returns the name of the generated "real" php script.

The include file is provided below.

file: "_meta.php"

<?php

$__meta_buffer = "";

function __meta_accu($str) {
    
    global $__meta_buffer;
    $__meta_buffer .= $str;
    
}

function __meta_exec($path) {
    
    ob_clean();
    ob_start("__meta_accu");
    require $path;
    ob_end_flush();
    
}

function __meta_prep($inpath, $outpath) {
    
    $code = file_get_contents($inpath);
    
    $search = array(
        '/<#(.*?)#>/s',
        '/[#]([^\n\r]+)/'
    );
    
    $replace = array(
        '<'.'?php \1 ?>',
        '<'.'?php \1 ?>'
    );
    
    $out_file = fopen($outpath, "w");
    fwrite($out_file, preg_replace($search, $replace, $code));
    fclose($out_file);
    
}

function __meta($path) {

    global $__meta_buffer;
    
    $out_path = str_replace(".meta.php", ".build.php", $path);
    $temp_path = str_replace(".meta.php", ".temp.php", $path);
    
    if (@filemtime($out_path) < filemtime($path) || !file_exists($out_path)) {
        __meta_prep($path, $temp_path);
        __meta_exec($temp_path);
        $out_file = fopen($out_path, "w");
        fwrite($out_file, '<'."?php \n".$__meta_buffer."\n?".'>');
        fclose($out_file);
    }
    
    return $out_path;
    
}

?>

If you save all three files with the filenames provided, you should have a working program. When run, two new files will be created in your folder - "test.temp.php", which is your meta-script with the meta-code delimiters turned into ordinary <?php ... ?> delimiters. And "test.build.php", which is the "real" php-script generated by your meta-script.

So how is this useful?

Well, in the above example, "complicated" could be a very complicated function, containing lots of database lookups, complicated recursive complications and other heavy processing. Any meta-code will be run only once, then the resulting files are "cached" in the "*.build.php" files, which are then included when the script is run again. (unless of course the original "*.meta.php" file has changed, in which case the files will be built again)

Now picture, for example, a database-interface wrapper class, written in meta-code instead of regular php. Now instead of your code calling a set-value-method in your table-abstraction class, which then calls a method in a parent database abstraction class, which in turn calls a database wrapper class, which then finally calls mysql_query (or whatever interface your database wrapper class is currently configured to use) ... *whew* ... now instead of all those function calls, you make *one* call to a meta-function of a class, which results in *one* clean php function call. That means considerably less overhead.

Or picture an entire application framework in metacode ... a form generator/validator library for example, could be implemented, which would generate code with zero function calls. Not only that, but php would no longer have to parse generator/validator-code for 20 different types of form controls if you only happen to be using a dropdown and a text-input.

One last thought, picture a template-engine with no required includes or parsing of any kind - with a few modifications, the script I provided above, could be used as a powerful template engine; you would have no overhead from any real "engine", and you wouldn't have to learn the syntax of any template engine to get started, because it would simply be php's native syntax.

...

Well, just thought I'd share the idea with everyone ... enjoy! smile