PhpRiot
News Archive
PhpRiot Newsletter
Your Email Address:

More information

PHP generated code tricks

Note: This article was originally published at Planet PHP on 19 June 2010.
Planet PHP
Something that is great about PHP is that you can write code that generates more PHP code to be used later. Now, I am not saying this a best practice. I am sure it violates some rule in some book somewhere. But, sometimes you need to be a rule breaker.

A simple example is taking a database of configuration information and dumping it to an array. We do this for each publication we operate. We have a publication table. It contains the name, base URL and other stuff that is specific to that publication. But, why query the database for something that only changes once in a blue moon? We could cache it, but that would still require an on demand database hit. The easy solution is to just dump the data to a PHP array and put it on disk.

$sql = "select * from publications";

$res = $mysqli-query($sql);

while($row = $res-fetch_assoc()){

AAA $pubs[$row["publication_id"]] = $row;

}

$pubs_ser = str_replace("'", "\\'", serialize($pubs));

$php_code = "";

file_put_contents("/some/path/publications.php", $php_code);

?
Now you can include the publications.php file and have a global variable named $PUBLICATIONS that holds the publication settings. But, how do we load a single publication without knowing numeric ids? Well, you could make some constants.
$sql = "select * from publications";

$res = $mysqli-query($sql);

while($row = $res-fetch_assoc()){

AAA $pubs[$row["publication_id"]] = $row;

AAA $constants[$row["publication_id"]] = strtoupper($row["name"]);

}

$pubs_ser = str_replace("'", "\\'", serialize($pubs));

$php_code = "
$php_code.= "global \$PUBLICATIONS;\n";

$php_code.= "\$PUBLICATIONS = unserialize('$pubs_ser');\n";

foreach($constants as $id=$const){

AAA $php_code.= "define('$const', $id);\n";

}

$php_code.= "?";

file_put_contents("/some/path/publications.php", $php_code);

?

So, now, we have constants. We can do stuff like:
//load a publication

require_once "publications.php";

echo $PUBLICATIONS[DEALNEWS]["name"];

?But, how about autoloading? It would be nice if I could just autoload the constants.
$sql = "select * from publications";

$res = $mysqli-query($sql);

while($row = $res-fetch_assoc()){

AAA $pubs[$row["publication_id"]] = $row;

AAA $constants[$row["publication_id"]] = strtoupper($row["name"]);

}

$pubs_ser = str_replace("'", "\\'", serialize($pubs));

$php_code = "
$php_code.= "class PUB_DATA {\n";

foreach($constants as $id=$const){

AAA $php_code.= " const $const = $id;\n";

}

$php_code.= "AAA protected \$pubs_ser = '$pubs_ser';\n";

$php_code.= "}";

$php_code.= "?";

file_put_contents("/some/path/pub_data.php", $php_code);

? Then we create a class in our autoloading directory that extends that object.

require_once "pub_data.php";

class Publication extends PUB_DATA {

AAA private $pub;

AAA public function __construct($pub_id) {

AAAAAAA $pubs = unserialize($this-pubs_ser);

AAAAAAA $this-pub = $pubs[$pub_id];

AAA }

AAA public function __get($var) {

AAAAAAA if(isset($this-pub[$var])){

AAAAAAAAAAA return $this-pub[$var];

AAAAAAA } else {

AAAAAAAAAAA // Exception

AAAAAAA }

AAA }

}

? Great, now we can do things like: $pub = new Publication(Publication::DEALNEWS);

echo $pub-name;
The only problem that remains is dealing with getting the generated code to all your servers. We use rsync. It works quite well. You may have a different solution for your team. Back when we ran our own in house ad server we did all the ad work this way. None of the ad calls ever hit the database to get ads. We stored stats on disk in logs and processed them on a schedule. It was a very solid solution.

One more benefit of using generated files on disk is that they can be cached by APC or XCache. This means you don't have to actually hit disk for them all the time.