March 1, 2006 - Disclaimer: Since a lot of people seem to me misunderstanding this article. It isn't about OOP vs. Procedural programming styles. I happen to lean more towards procedural, but could easily have gone more OOP. I simplified the code a bit for brevity, but have added a light OO layer back in the model now. Not that it makes a difference. What I was hoping to get across here is a simple example of how you can use PHP as-is, without additional complex external layers, to apply an MVC approach with clean and simple views and still have all the goodness of fancy Web 2.0 features. If you think I am out to personally offend you and your favourite framework, then you have the wrong idea. I just happen find most of them too complex for my needs and this is a proposed alternative. If you have found a framework that works for you, great.
So you want to build the next fancy Web 2.0 site? You'll need some gear. Most likely in the form of a big complex MVC framework with plenty of layers that abstracts away your database, your HTML, your Javascript and in the end your application itself. If it is a really good framework it will provide a dozen things you'll never need.
I am obviously not a fan of such frameworks. I like stuff I can understand in an instant. Both because it lets me be productive right away and because 6 months from now when I come back to fix something, again I will only need an instant to figure out what is going on. So, here is my current approach to building rich web applications. The main pieces are:
I don't have much of a problem with MVC itself. It's the framework baggage that usually comes along with it that I avoid. Parts of frameworks can be useful as long as you can separate the parts out that you need. As for MVC, if you use it carefully, it can be useful in a web application. Just make sure you avoid the temptation of creating a single monolithic controller. A web application by its very nature is a series of small discrete requests. If you send all of your requests through a single controller on a single machine you have just defeated this very important architecture. Discreteness gives you scalability and modularity. You can break large problems up into a series of very small and modular solutions and you can deploy these across as many servers as you like. You need to tie them together to some extent most likely through some backend datastore, but keep them as separate as possible. This means you want your views and controllers very close to each other and you want to keep your controllers as small as possible.
Goals for this approach
Clean and simple design
HTML should look like HTML
Keep the PHP code in the views extremely simple: function calls, simple loops and variable substitutions should be all you need
Secure
Input validation using pecl/filter as a data firewall
When possible, avoid layers and other complexities to make code easier to audit
Fast
Avoid include_once and require_once
Use APC and apc_store/apc_fetch for caching data that rarely changes
Stay with procedural style unless something is truly an object
Debian's Apache1 package doesn't quite do what I need. I have been building my own and overwriting the files from the Debian package, but that can get annoying. So I hacked my changes into the Debian source package and built real .debs. I figure they might be useful to others.
The main changes are to get rid of -lpthread (needed by mod_perl) and -lexpat and to add mod_deflate.
To enable mod_deflate make sure your /etc/apache/modules.conf file has:
Have a look at the source. There is a source link at the top-right on the page. It is about 90 lines of HTML and Javascript and virtually no server-side scripting.
I usually throw PHP code at all sorts of problems, but in this case I could do pretty much everything I wanted to directly from Javascript via the excellent API. The initial zooming from way out is a bit distracting and doesn't work too well, but I wanted to play with that a bit. Once you are zoomed in and searching for things, the search is updated as you move around the map and everything is event-based so the page doesn't need to be redrawn from scratch.
Both input fields are free-form. You can put a city name, a zip code or a full address in the Location field and in the What field you put what you are looking for. There is a special hack that checks for something like 4* and the filters the results to only show you those entries that were rated 4* or higher on Yahoo! Local. You can of course also just put something like "pizza" or "mexican" in that field.
Most of the magic here is done by 2 things. The LocalSearchOverlay and the event handling. Note how Map.EVENT_MOVE and Map.EVENT_ZOOM_END are registered events in the onInitialize() function. When you scroll the map or zoom it the onOverlayInit function will get called and the LocalSearchOverlay will be recalculated for the new map coordinates. Same thing happens when you change something in the input fields. The updateMap() function is called which will center the map at the new location and update the LocalSearchOverlay appropriately. There are a few more Javascript tricks here and there in it, like updating the link at the top so you always have a way to grab a link to your current search and send it to someone, but other than that there really isn't all that much to figure out here. Once you understand which events happen when and which methods are available where, you can do some really powerful things with this. It is all documented here:
In my last entry I showed how to parse a geocoded XML file and put markers for each entry on the map. Someone asked me how I would do the using the JS-DHTML API instead of the JS-Flash API. It's a whole lot harder in DHTML, but it works. You can see it here: