Aspect Oriented Programming: Lithium's Filters
What is aspect oriented programming (AOP)? Well, you could search Google and find tons of information about it and also read Wikipedia. Or you can essentially just understand that it's a very high level approach of handling program functionality, making it really modular in design. IBM has a great article that's a little easier to read.
So how does Lithium handle this idea of AOP? With it's filter system. Since it takes advantage of PHP 5.3 it has the benefit of lambdas and therefore its filter system was made possible. Essentially, any method you write or many (not all, but many) within the core framework can be filtered. What does "filtered" mean? Well essentially you call a method on the class passing the method as an argument, Class::applyFilter('method' ...); and you will inside the applyFilter() method also define and pass an anonymous function and pass along the chain. Within that function you can perform other logic and then return the chain so that everything can carry on. Or maybe you don't return the chain, maybe you stop right there and exit out with some data. This is basically linear event handling. Each filter is going to do its thing in order as called.
So now what's the big deal? Why is it so much better? Well, using the example of CakePHP's callback methods in the model...Let's say you have one tiny specific situation where you need to perform some logic before you find a record. You could use the beforeFind() callback method. Great, but then let's say you have to perform that in another model. Oh snap, well, we'll just add that into the other model. Then another model later on? Really? Come on the client must be beating you up. Then only at a certain time of day? Under other crazy conditions? Ugh. Ok, so you can understand that you are very quickly ending up with messy, borderline unmaintainable, code. You have to go back to many different places and add in new code. You may even need to write entirely new classes and access methods in those classes to get the job done.
So if instead we applied filters from a different area in our code, we can keep all of our "advice" or ways of dealing with our "cross-cutting concerns" in one place. Furthermore, since we're in an event chain with Lithium's filter system, we can bypass things once we have what we need and structure things in an efficient order.
The new CMS I'm working on, Minerva, will take full advantage of Lithium's filter system. Essentially, it will account for a very large piece of how the CMS is extended with plugins (Lithium libraries written specifically for Minerva). For example, the "Page" model is the main model that deals with records for, well, pages on the site. These pages can be basic simple pages or blog entries or photo galleries or anything really. What "flavors" or "decorates" pages? Libraries. So there's a "blog" library that gets dropped in and poof, there's a blog. In fact, there won't even be any "installation" or database setup required (thanks to MongoDB). Drop the files and literally - poof. Now the blog library has a Page model itself and it extends Minerva's Page model. It adds new schema so we can gain extra fields that we may need for a blog and, more on topic here, it applies filters.
One filter it applies is to the model's find method. It has an index listing method in the controller and when called, it will make a call to the find method to get the records. The blog library's filter says, "Hey, since you're talking about me, go grab just my records." Now you have an index listing of blog entries instead of all pages. Sure, this is a very basic example and you could simply have the controller method pass along a URL parameter that changes the find query to only return records belonging to the library just the same. Let's say it was just a tiny bit more involved so that each time we added a new library, we'd have to go back and add in some more code so we can actually handle the new library.
Ok, so you can see in this admittedly poor example (use your imagination for more sophisticated cases), that you have to ultimately keep going back and altering a "core" piece of code each time you add in some new functionality. Maybe it's not the "core" framework, but still in our case the "core" app that we really don't want to touch because when there's an update, we want to ensure we can get that update without conflict. With the filter system, you don't have to touch the core app. It's a more hands off, more abstract way of handling the concern. Also look at where it's been set? It's been set inside the 3rd party add-on code. The main application doesn't even have a clue about what's going on and the core code base was never touched. It's not dependent at all. All this without having to setup some big API system that adds more beef to the app and has to have every little detail worked out. Then what if our "API" updated? Oh, well then we have plugins for the CMS that are for version 1.x and plugins for version 2.x etc. You'd need to update the plugins to run with the latest CMS. Minerva specifically aims to not fall into the same problems that most other CMS' out there do.
Of course you can also get away solving some of these problems with simple class extension, but you'll ultimately run into problems. Obviously maintaining the code. Say you do extend and override some methods then one day you pull down the latest framework code from the repository and poof your site no longer loads. You have a white screen and you have no idea what happened. Then you have to debug. Yuck. Hmmm, and wouldn't a nice logging system help you out here? Oh look, another great use for the filter system.
So Lithium's filter system is extremely valuable. It's something that I believe will distinguish the framework from others and will help developers build faster and more maintainable code. Stay tuned for some more examples and also more information about my new CMS, Minerva. You can actually dive into some of the code and check out some example uses of the filter system in Minerva's code found on github. Minerva is still in it's early stages and is subject to change, but it should accurately represent the direction it's heading and the blog library's Page model should give you an easy to understand example of the filter system.