Monday, March 6. 2006
Aspect Oriented Programming in PHP as a contrast to other languages.
It is often said that when you travel the world, you will learn more about your home country then you will of the host country. I feel that the same is true of programming languages. Learning a new language is like visiting a country for awhile, and sometimes you do more then just visit, you move. By jumping across the boundary of languages, you can really gain some insight on some very basic (to borrow the term...) cross-cutting concerns with programming languages. How does the language handle data and code abstraction? What kind of reflective qualities does it have?
One of the biggest eye openers for me is when I made the jump from PHP to Scheme. Trying to work with concepts I was familiar with and learnt from PHP inside of Scheme illustrated some monumentally flawed assumptions that I had made about programming in general. Essentially I had become the ugly Canadian, who shouts, carries on, and runs roughshod over the local customs. It doesn't always have to be like this, sometimes the returning traveller imports the foreign culture with excellent results; consider the effect Disneys animation had on the Japanese and how out of that style grew Anime.
Okay, so what does this have to do with Aspect Oriented Programming you might ask? Well, importing concepts like Aspect Oriented Programming is a lot like importing Buddhism from the East. You can try to import Tibetan Buddhism, or you can try to import Zen Buddhism. Imported Tibetan Buddhism feels very foreign to me, and seems more like a trans-plant. Imported Zen Buddhism seems a much closer fit.
Okay, enough ranting of foreign culture and religion, lets talk about what Aspect Oriented Programming is, and talk about its implementations, especially inside of PHP. Aspect Oriented Programming is, in its basest form, adding extra behaviour to a unit of abstracted code (a function, a method, what-have-you) seperate from the definition of that behaviour.
Generally, there are 3 major concepts of AOP: The pointcut, advice, and a weaving. The pointcut defines which unit of code is being modified, the advice describes the extra behaviour, and how it is applied, and the weave is simply the combination of a pointcut with a specific piece of advice (thus binding extra behaviour to a particular unit).
Now in Java, a strongly statically typed compiled language, aspects are woven at compiletime. You weave in extra behaviour on the class level. For instance, one of the chief uses (or examples at least) of AOP is to add logging to a class. So in Java, once we have woven the logging advice to the class, every object that is instantiated from that class will have that advice.
In Javascript however, things are MUCH different. First of all, Javascript is a dynamically weakly typed interpreted language. Second of all, functions inside of Javascript are first class entities, and third of all, objects and classes don't play the same role as a fundamental unit of code that they do inside of Java. Implementing an "aspect system" inside of Javascript is much different, and in fact, the guts of a Javascript AOP implementation are mind-bogglingly simple, mostly due to Javascripts first class functions.
The Current State of AOP in PHP
Yoinked from the venerable Mr. Bergmann
This subtitle is yoinked from Sebastian Bergmanns blog. He too has been ruminating about AOP inside of PHP, and he is poised to present quite an implementation, if his work on PHPUnit is any indication. He will be using the Runkit extension, which is a different (and stronger) implementation then my own, but carries the weight of depending on an experimental extension.
So now, lets talk about PHP. PHP, like Javascript is a dynamically weakly typed interpreted language. However, like Java, classes (and in this case functions) are special entities within the language, they can't be directly referenced. There are already a few different AOP implementations for PHP, but all of them require pre-processing (that is, running your PHP source through a processor first) or patches against the engine.
So what is wrong with the first case of pre-processing code? Well there are two main issues. Preprocessing adds an extra step to your development: it's no longer code then test; but has become code, preprocess then test. The benifit of php being an interpreted language is lost. The second issue is that you have stopped wirting PHP, but have started writing a dialect of PHP; your new dialect won't run natively on any interpreter. There seem to be a few preprocessors out there that move the step of preprocessing out of the programmers hands and into the hands of apache, so that apache will preprocess the PHP before handing it off to the Zend engine, but we're talking about Pre-processing the PHP Hypertext PreProcessor language. This seems highly redundant and very slow.
Now, when it comes to patching the engine, the problems associated with that should be blatently obvious: you loose code portability. This isn't bad if you are just concerned with your application on your server, but once you start getting into the world of class libraries, frameworks, or applications... well you start having problems. And you are going to be in for a surprise if you ever need to switch servers, or upgrade versions of PHP.
The problem here is that the implementors seem to have looked at the Java method of weaving aspects (which admitedly is quite powerful and full of mojo) and trying to apply that to PHP. In fact, this is really indicative of a deeper problem inside of the PHP community: some of us are trying to hard to be Java. Now this is not a slam against PHP5's object system (in fact, I think it is about the sexiest thing added to PHP, second only to Runkit.) Nor is this a slam against Java—Javas bondage and dicepline makes for some very strong, and robust code. what this is really a slam on is trying to turn PHP into Java. It is, as Alan Watts loves to say, like putting legs on a snake.
An AOP implementation in PHP
So what does a proper PHP implementation of aspect oriented programming look like? Well first of all, there should be no extra steps between writing the code, and the execution of said code; this means no pre-processing. Second of all, it must utilize the existing qualities and abilities of the language, this means that weaving an aspect should be a dynamic action, rather then a static one. Again, when the Japanese imported Disney, they re-purposed it into Anime; they didn't just re-animate snow-white.
AOP for procedures in PHP?
It can be done, but it isn't easy, or particularly graceful. The first method is to refer to each function as a variable, instead of by its name. So to use nl2br(), you would use $nl2br(), which may be bound to the string 'nl2br' and calling the proper function, it might instead be bound to a the name of a function containing the weaving of a particular set of advice with nl2br, or it might just be bound to 'exit'. You also have to either chose a reasonable subset of the function list, both the php function list and your own, that are referenced by variable name instead of directly by name, or spend an inordinate amount of time binding each function to a variable of the same name. Ugh.
Another method would be to write a simple meta-circular evaluator. Instead of calling functions directly, you call them through an intercessory function, which could then handle the weaving for you.
As you can see, each solution is possible, but neither are desireable.
There is no way to effectively weave aspects with procedures in PHP (see the right sidebar). About the best we can do is with objects. However, as you will soon see, there is a lot of power and flexability that we are about to gain. The linchpin to the implementation of a very basic AOP system inside of PHP is in the form of the magic method __call(). You can even see the germ of this idea inside of my Fluenter class. Instead of aspects being woven to classes, they will be woven to objects instead. Now, this might not seem as powerful, it is however, just in a different way.
BunnyAspects is my implementation of AOP inside of pure PHP5. You do not need any extensions and it uses the existing qualities and abilities of the language—which carries its own set of problems, as we will see later. You can download bunny aspects Here, and its software (wiki) page is Here
The essence of BunnyAspects is to wrap a BunnyAspect object around our target object. This BunnyAspect object keeps track of what is being woven into it, and then with the magic __call method, intercedes between all of the method calls, and calls before or after advice as needed.
Because you are weaving aspects to objects (versus classes), you can weave much closer to the focus of your weaving. For instance, say you wanted to log the output of a particular "RequestProcessor" object, you simply weave the logging advice to the required instance, rather then all instances across your application. However, this also has its negative implications, in that you cannot weave an aspect to a class.
Enough talk, here is an example:
include('warren/lang/BunnyAspect.php'); //tihs might change depending on where you have it
class MyFoo
{
function Bar($a)
{
return $a * $a;
}
}
function double($method, $retval)
{
return $retval * 2;
}
$foo = new MyFoo();
// Prep the foo object to be wowen
$fooAspected = BunnyAspect($foo);
// $foo = new BunnyAspect($foo); works just as well here
// Weave the function "double" after the Bar method of the foo object
$fooAspected->weave(array('after','Bar'), 'double');
$foo->Bar(5);
// Returns 25
$fooAspected->Bar(5);
// Returns 50
One of the things you'll notice is that I have renamed the aspected object to $fooAspected. This is to show off the fact that the object you have aspected can become a different object from the original. As you can see in the comment, we can bind $foo to the apsected object instead. Which method you choose will depend on your needs.
The weaving is performed by the weave method of BunnyAspect, which takes two arguments. The first argument is the point-cut, or the exact location that the advice is to be applied. Currently a pointcut is an array, with the first element containing the location of the weaving ('before' or 'after') and the second element containing the method to weave. The second argument is the piece of advice to weave, which is a 'callable' php pseudo type (it can be a string naming a procedure, or an array containing: an object or class name, and a method). You might wonder why I opted to refer to the pointcut as an array, rather then seperate it out, and pass 3 arguments to weave. I want a point-cut to be a first-class entity. Because a pointcut has at least 2 pieces of information: where the advice is woven, and what it is woven to; pointcuts would lose their first-class-ness if that information was separated. I would also have a harder time refactoring the class later to handle different pointcuts. In fact, there is no technical reason why BunnyRegex cannot handle pointcuts containing regular expressions, or pointcuts that are applied to the getting, setting, unsetting, or even testing of object properties (using the __get, __isset, __unset, and __isset magic methods). It just needs to be implemented
Bunny Aspects and Types
There is a major issue here: because you are wrapping around the object, its associated type information (class, subclass, and interfaces) is lost. I do not believe that typecasting the class would work (either using Runkit or the serialize-<unserialize trick) because once the aspected class has been typecast, it would inherit the methods that were previously over-ridden, and therefore the __call magic method wouldn't be called, bypassing the aspect system.
Really what I would love to see is a __instance_of() magic method that allows us to override the type checking. Some might think it is dangerous. Really, PHP is a dynamically typed language, so lets get some of those dynamics back, and give it back to the programmer. Right now, PHP is in this weird state where arrays, resources and scalar values are dynamically typed, but objects are in a state where they can be dynamically typed, or implicitly typed but not casted.
Runkit. SexXxy PHP.
Runkit can change all of this. Using Runkit_method_rename to rename the original method to a new (unique) name, and then use Runkit_method_add to add a new method that calls the old one with the applied advice would give us static weavings (that is aspects woven into classes versus objects) that would not affect object type. using the runkit_function_rename and runkit_function_add, we can do the same with procedures.
As I mentioned earlier, Sebastian Bergmann is already working on an AOP system for PHP using Runkit, and I am eagerly awaiting his invention. As soon it comes out, I'll post a trackback to this article, so stay tuned.
Wednesday, September 8. 2004
R4RS Scheme Aspect Oriented Programming Implementation
Here is an overview of the overall design.
A Weave is a combination of a Pointcut Descriptor and the Advice applied to it.
A Pointcut Descriptor is quite simply a predicate (A procedure emitting a boolean). Well... kinda.
Advice (according to the user) is the application of a lambda taking 3 args (function symbol, primitive procedure, argument list) to one of the advice functions (instead, before, after). For example: (before (lambda (s f a) ((display "in") (display n) a)))
Pointcut Descriptors
The statement that Pointcut Descriptors are just predicates is a bit of a falsehood. For example, in BunnyAspects there are currently 2 kinds of pointcuts, call and within. To create a pointcut, one would just call (call 'my-procedure) which then emits a predicate to see if that pointcuts condition has been satisfied. (that is, the procedure my-procedure has been called).Deep in the guts however, something quite different is happening altogether. The my-procedure function is completely redefined to:
- add its name to the global list of currently executing pointcuts (*pointcuts*)
- call the aspect-dispatch mechanism with the procedure symbol, and arguments and store the result
- remove its name from the global list of currently executing pointcuts
- emit the stored result
Once "my-procedure" has been redefined, it then emits the predicate to see if the pointcut has been called. (the definition for call looks a lot like this: (equal? (car *pointcuts*) procedure-name)
The Aspect Dispatcher
The aspect dispatcher first checks to see if it has already tried to weave the current function or not. This is important, because if Display has a weave associated to it, and in its advice we call display, we will end up weaving display over and over, without actually executing it, and instead run into an infinite loop. If the check succeeds we add the current procedure to the list of woven procedures. Then we recurse over the list of each predicate, and test to see if they are true. For every true aspect, we set the primitive procedure to be the weaving of the advice with the primitive procedure. (thus allowing multiple aspects to be woven in this iterative process) Finally, whether or not we have woven, we apply the primitive procedure, or the woven procedure to the arguments, and remove ourselves from the list of current weaves.Advice
The handling of advice is by far the funkiest piece of code in here. And in all sense of the word funky. For instance, the before function is defined as:
(define before (lambda (aa)
(lambda (app/prim psym)
(lambda args
(apply app/prim
(apply aa (list app/prim
psym
args)))))))So... in a nutshell it is a procedure that takes in the advice procedure (which takes 3 arguments, the primitive procedure, the symbol of that procedure, and its arguments) and emits another procedure that takes 2 arguments. That being the primitive procedure, and its symbol. Which emits one final procedure, which is the result of the weaving of the advice with the procedure. *WHEW!*The reason why the user-supplied advice procedure takes both the primitive procedure, and the symbol, is that app/prim doesn't necessarily equal (eval psym). That is, if it is fed the arguments ([closure 2] 'my-procedure ('foo 23)), [closure 2] may in fact be a procedure with previous advice woven into it, instead of the base level 'my-procedure.
Putting it all together
So this is how to use Bunny-Advice, and what happens.
- The user performs the weaving by calling (weave (call 'my-procedure) (before my-before-advice)) where my-before-advice is a function taking the three arguments for advice (primitive procedure, procedure symbol, args)
- (call 'my-procedure) rewrites my-procedure to update the pointcuts list, and call the low-level aspect dispatcher, and emits the predicate (equal? 'my-procedure (car *pointcuts*))
- (before my-before-advice) emits the lambda taking the primitive procedure and its symbol.
- weave stores the list ('bunny-aspect (lambda () ...call predicate...) (lambda (app/prim psym) ...advice weaver...)) in the *weavings* list.
- The user then calls my-procedure
- first the pointcut list is updated
- the aspect dispatcher is called with the primitive procedure, its name, and arguments
- the name of the procedure is added to the list of current weavings
- each pointcut-predicate member of *weaves* is tested, and if its true, the primitive procedure becomes a lambda taking a variable number of args, that executes the primitive procedure and advice
- the woven, or primitive procedure is applied with the arguments
- the procedure is removed from the current weavings
Listening to:
One World (13-dec-2001) - Blame (1:56:51)
Radio One
Wednesday, September 1. 2004
Non-Trivial Aspects in Scheme
A few very important questions are:
- should an After advice be in the direct line of computation of the function where the advice is being applied? That is, if a function returns the result '4', and the After advice returns the result #n (or nil), what should be the result of the computation of the function with the advice weaved in, '4', or #n? the short answer is that #n should be the result of the computation. The long answer is the after advice should take an argument, (which is the result of the previous computation) and return an argument (again, said result). For ease of use, a special Thunk-After advice should be built that emits the result of the function, in case you have advice that is exclusively side-effect based (i.e. logging, etc.)
- Along a similar track, should Before advice affect the arguments of a function in any way, should they be allowed to? Again, yes and yes. And again, a Thunk-Before advice should be constructed
- What about function weaves that happen inside of advice? If I apply the following Around Advice to display: (display "Around Advice") and then perform (display "Infinite looping...") the first call to display will be intercepted, so that the advice can be woven. But, on weaving the advice, the call to display will be intercepted... The question is, is it of utility to have meta advice? That is, advice applied to advice? The solution to the loop problem is to bottom out, that is, when weaving advice, keep a list of weavings-to-functions that is happening, and on a call to a member of the weavings-to-functions list, call the primitive procedure instead.
- Is "Around" a good name for advice that is executed Instead of the function? No. There should really be 2 separate kids of advice, Around, which is a combination of Before and After, and Instead, which does a full replacement.
- What about continuations? Besides being a class example of why continuations can be a Bad Thing, this will be hard to deal with. Right now my idea is to do a lower-level weaving of call/cc, to some how alter the continuation flow to perform all applicable After aspects. I am not sure about the implementation, or if it could even be done however.
- And more about continuations, what about invoking a continuation inside of advice? Eek!
- What about the order of Aspect Evaluation? If I have 2 begin aspects, which one should be executed first? I have no answer.
By the time I actually HAVE that implemented, I will have more questions.
Comments? Advice? Answers? Anyone? Beauler?
Listening to:
Slow Poke (Twilight Zone Mix) - Plastikman
Closer
Wednesday, August 25. 2004
Aspect Oriented Programming Redux
So then I fought with what With-Continuation-Mark and Current-Continuation-Mark is, what they mean, and how possible it would be to implement them in LispMe (not a freakin chance man). While doing that, I took a look at the disasm function in LispMe, and wrote a simple little translator. More work could be done on it for sure, but I'll post what I have to the LispMe list soon.
Then I find This. Which is a very simple AOP system in Javascript, that could be EASILY ported to scheme. Hell, the LispMe tracer is very similar to this. The problem of course, is that the pointcuts are very simple. I guess I'll see how extendable this is.
I found it when I went searching for Continuations in (Mozilla) Javascript. Sweet Fucking Christ, don't tell the web-guys about this! Can you imagine? Jesus. Might as well set up Harmony the bomb!
Listening to:
Where Have They Gone? (BeatBlender 128k: A late night blend of deep-house & downtempo chill [SomaFM]) - Harmonic 33 (0:-1)
Tuesday, August 17. 2004
Scheme Papers to read...
The basics? With scheme, you can implement aspect oriented programming dynamically. This has a few benifits. One being that the weaving is no longer an extra programming step. (no more weave, compile, execute. just the combined eval and apply please!). The next benefit is that pointcuts (the places where aspects are weaved) and advice (the aspect code itself) can be represented as first class objects. Just like functions.
MMMmmmm. Tastey.
Listening to:
Contrast - Phd (7:25)
textures




