More on Aspect Oriented PHP

I saw a post today from Sebastian today talking about bring Aspect Oriented Programming to PHP. This is something I was playing with a while back (1, 2, 3, 4) but haven’t touched in a while.


I think Sebastian is correct in that you _can_ get a somewhat working version of Aspect Oriented Programming working in PHP with not much effort thanks to __autoload, __call, and __set (see code below). While the PHP only version is fun and helps prove the concept, what is really needed is a PECL extension. Marcus and I have been talking about implementing AOP as a C extension, but I am not quite convinced it can really work even then.

I know that we can create a good AOP syntax for PHP and that it will perform the required functionality, but I am not sure if it can perform at a level that people interested in using aspects demand (I assume people wanting to do AOP manage big apps and want a way to simplify the code/maintenence).

It is possible that I am just missing the best way to implement it, but it seems to me the only way to implement AOP will involve altering the classloader to create meta(ish) classes in-between a class’s public methods and it actual object reference.

I do want to see AOP in PHP so I will go ahead and throw out my working example. be forwarned that I am not an AOP expert and my naming conventions may be a little haphazard. Also, my "aspects" are simple PHP functions in this example. They can be methods as well.

test.php – the file that gets executed


include_once('aspect.php');
aspect_join_method('TestClass','TestMethod','show_return','after');
aspect_join_property('TestClass','test_property','watch_property');

$result = new TestClass();
$result->TestMethod('1','2','3');
$result->test_property = 'wh00t';

function show_return($args) {
    print_r($args);
}

function watch_property($name,$value) {
    echo "$name is now set to $value...\n";
}

aspect.php – PHP only implementation of limited AOP


/**
 * You must allow autoload to load your classfiles.
 * Do not require/include your class files.
 *
 * @param string classname
 */
function __autoload($classname) {

    // this looks pretty nasty, but it works.
    $class = str_replace('?>','',
                 str_replace('<?','',
                     str_replace('<?php','',
                         str_replace("$classname","aspect".$classname,
                             file_get_contents("$classname.php")))));

    // a new class is created with the name of the requested class.
    // this class extends the AspectProxy class.
    $new_class = "class $classname extends AspectProxy { protected ".'$'."class = \"aspect$classname\";}\n";
    eval($class.$new_class);
}

/**
 * Allows aspects to be attached (before or after) to classes and methods
 */
function aspect_join_method($class,$method,$callback,$type = 'after') {
    $GLOBALS['ASPECTS'][$class]['methods'][$method][$type][] = $callback;
}

/**
 * Allows aspects to be attached to property changes
 */
function aspect_join_property($class,$property,$callback){
    $GLOBALS['ASPECTS'][$class]['properties'][$property][] = $callback;
}

/**
 * Classes extending AspectProxy are able to provide method and property pointcuts to join aspects to.
 */
class AspectProxy {
    /**
     * The instantiated object that will need to provide pointcuts
     */
    protected $obj;

    /**
     * The name of the class that will need to provide pointcuts
     */
    protected $class;

    /**
     * This constructor is inherited and creates $this->obj from $this->class;
     */
    function __construct() {
        $classname = $this->class;
        $this->obj = new $classname();
        if (!isset($GLOBALS['ASPECTS'][get_class($this)])) {
            $GLOBALS['ASPECTS'][get_class($this)] = array();
        }
    }

    /**
     * provides access to method join points
     */
    function __call($name, $args) {
        if ($joins = $this->checkMethod($name)) {
            if (isset($joins['before'])) {
                foreach($joins['before'] as $callback) {
                    call_user_func_array($callback,$args);
                }
            }

            $ret = call_user_func_array(array($this->obj,$name),$args);

            if (isset($joins['after'])) {
                foreach($joins['after'] as $callback) {
                    call_user_func_array($callback,$ret);
                }
            }

            return $ret;
        }
        else {
            return call_user_func_array(array($this->obj,$name),$args);
        }
    }

    /**
     * provides access to property join points
     */
    function __set($name, $value) {
        if ($joins = $this->checkProperty($name)) {
            foreach ($joins as $callback) {
                call_user_func_array($callback,array($name,$value));
            }
        }
        return $this->obj->$name = $value;
    }

    /**
     * provides access to object properties
     */
    function __get($name) {
        return $this->obj->$name;
    }

    function checkMethod($method) {
        echo "checking $method for joins...\n";
        if (isset($GLOBALS['ASPECTS'][get_class($this)]['methods'][$method])) {
            return $GLOBALS['ASPECTS'][get_class($this)]['methods'][$method];
        }
        return false;
    }

    function checkProperty($property) {
        echo "checking $property for joins...\n";
        if (isset($GLOBALS['ASPECTS'][get_class($this)]['properties'][$property])) {
            return $GLOBALS['ASPECTS'][get_class($this)]['properties'][$property];
        }

    }
}

TestClass.php – Simple class needed for example


class TestClass {
    function TestClass() {
        echo "I created a test class...\n";
    }

    function TestMethod($a,$b,$c) {
        $out = "$a $b $c is what TestMethod returns...\n";
        //echo "$out\n";
        return $out;
    }
}

Maybe that will give a little more momentum to the slight push for AOP in PHP. If only there were more hours in the day…

Someone had registered aspectphp.com, so there could be others working on it. I have aspectphp.net. I may just make it a mini-planet of PHP AOP related blog categories.

-Jackson

6 Comments Short URL

6 Responses to “More on Aspect Oriented PHP”

  1. Harry Fuecks October 16, 2004 at 5:43 am #

    Isn’t Classkit (http://pecl.php.net/package-info.php?package=classkit) what you’re looking for?

  2. Jackson October 16, 2004 at 9:52 am #

    Yeah. I was not very clear as to why I think it is not a feasible solution, but that is it.

  3. Jackson October 16, 2004 at 9:58 am #

    Thanks for the tip. Classkit could be a better way to implement AOP as a PHP only solution. I do think that a compiled extension has a better chance of being a real world solution.

    The important part is not changing the classes, but rather the functionality that AOP allows. So classkit isn’t exactly what I am looking for. With AOP you can easily do thing like:
    “When in debug mode, log all queries”

    That is much better than stopping your database and restarting it with query logging on.

    Maybe tonight I will sit down and try to show some good uses of the concept.

  4. Alan Knowles October 16, 2004 at 7:27 pm #

    All the versions of a PHP side implementation of AOP look pretty horific.

    AOP done right is quite clean, but it has very limited real uses (large object node type systems, like compiler trees)

    treecc works OK, but it is a bit of annoying having to rebuild all the time.

    The syntax is not too bad (although it could do with ‘phpizing’.)

    The best way to do this would probably hack something like treecc as an extension, and use this as the php side sytnax.

    aop_include_once(‘nodes.aop’);
    aop_include_once(‘somemethod.aop’);
    aop_include_once(‘someswitch.aop’);
    ….

  5. Jackson Miller June 24, 2005 at 3:37 pm #

    I am writing some PHP again. Among other things I am using some Aspect Oriented stuff. I had written a proof of concept of a PHP only aspect oriented package (here) last year. I know Sebastian also had something similar.

    My implementation proves the

  6. [...] I really like the idea of Aspect Oriented Programming. When I was regularly programming in PHP I played with implementing aspect oriented PHP. Sebastian is still working on creating a good Aspect implementation for PHP. [...]

Leave a Reply