Metaprogramming in PHP

Not so long ago I wrote about few meta programming tricks in Javascript. These are really powerful programming techniques that let you create elegant frameworks with generic code that adapts to your needs at runtime. But Javascript is not the only language that can accomplish stuff like that. You can use pretty much the same tricks in PHP. Yes, you heard me right – the ugly cousin of Perl, and the most hated and most used server side language can do this stuff.

Let me start this with a real life problem which was very often pointed out as a flaw in PHP 4. As you can remember, in PHP 4 you defined a constructor of a class like this:

class Foo
{
   function Foo()
   {
      // initialize
   }
}

In other words construction was a function with the same name as a class. PHP 5 introduced new constructor semantics:

class Foo
{
   function __construct()
   {
      // initialize
   }
}

This was done to solve a problem that most PHP developers always had issues with – namely how to call the constructor of a parent, if you don’t know it’s name. In PHP 5 it is easy:

class Foo extends Bar
{
   function __construct()
   {
      // do something
      parent::__construct();
   }
}

Easy, peasey! How do we do that in PHP 4? Well, we either hard code it or use a little bit of meta programming:

class Foo extends Bar
{
   function Foo()
   {
      $parent_name = get_parent_class();
      parent::$parent_name();
   }
}

What would be the problem with simply hard-coding parent::Bar() here? Think about what will happen when you create subclass of Foo. SubFoo class will try to call function Bar() which does not exist in Foo. The way we route around this issue is utilization of variable functions. Let me try to make it clearer by using a much simpler example:

function foo() { echo "Hello World!"; }
 
$my_string = "foo";
 
$my_string(); // outputs Hello World!

In other worrds, if you put a set of parenthesis after a variable, PHP will try to evaluate that variable and use it as a function name. It works in similar way to the PHP method call_user_func which lets you pass in a string representation of a function name, and have it called for you. And since you can generate that string on the fly, it makes for some interesting programming techniques. For example you can create a nifty function like this one:

function foobar($my_class_name, $my_method_name)
{
   if(class_exists($my_class_name))
   {
      $my_object = new $my_class_name();
 
      if(method_exists($my_object, $my_method_name))
         $my_object->$my_method();
   }
}

This function takes two strings. One is a name of a class, and one is a name of a method. It then initializes the class if one exists, and then calls an instance method of that class specified by name. You can pass any class and any method name, and if they are correct, the method will be executed.

There is also something called variable variables that may often come in handy. For example consider the following:

$foo = 'bar'; 
$$foo = 'baz'; 
 
echo $bar;   // outputs baz
echo $$foo; // also outputs bas

The contents of $foo are evaluated prior to assignment in $$foo so it dynamically becomes $bar on runtime. Combine that with variable functions and you got yourself a whole arsenal of meta programming weapons. An if that’s not enough for you then there is eval – a function that takes a string and tries to evaluate it as PHP code. Eval has it’s own little quirks though, and may cause somewhat unique issues. For example, code that is built dynamically by concatenating strings is notoriously hard to debug. It also won’t be properly highlighted by the IDE, and it which makes properly formating and escaping it will be difficult. So it probably deserves it’s own post.

I won’t deny it – PHP is not the prettiest language, and the lack of namespaces in it’s function library makes for some colorful naming patterns. But it wouldn’t be so prevalent and so popular if it didn’t have a lot of power underneath the hood.

[tags]php, meta programming, programming[/tags]

This entry was posted in programming and tagged . Bookmark the permalink.



Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>