QCubed: An Excellent PHP Framework

I haven't been writing about it on this blog, but I've been intimately involved in the development of one of the best PHP5 frameworks out there. The framework is called QCubed; it's a model-view-controller framework that's meant to save the time for all PHP developers out there.

If you've ever written a form in PHP, you know what i'm talking about. Those pesky HTML tags, client-side validation, server-side validation, flow control (multi-page forms...), database interactions... Mess that nobody wants to deal with. Well, QCubed solves all of these problems. An object-relational mapper creates nice PHP5 objects out of your database entities. A library of powerful AJAX controls makes interacting with your form a breeze.

Event-driven, stateful approach to form development gives PHP developers the time to do what actually matters - code the business logic (and not the stupid SQL queries). It also enforces strict standards that set good precedents for separation of model, view, and controller - so that the junior developer on your team is not tempted to put the SQL, PHP, and HTML all in one file. And man, am I guilty of this :-).

Anyway... the QCubed framework recently hit a major milestone - the second release candidate for the 1.0 release. It's stable enough to build businesses on (and, in fact, there are many businesses using it already). There are numerous tutorials to help you get started. Give it a shot!

Looking for a designer

Cocktail Builder needs a talented graphic designer to take the site to the next level. Do you like the site and think you can make it even better? Are you a Photoshop guru with a great understanding of CSS and web standards? If so, I want to hear from you.

This is an equity share opportunity - depending on your skills and contribution, you will earn a share of ownership and revenue of the site.

Contact me (Alex) at [alex94040 at yahoo dot com] if interested; please make sure to include your portfolio in the email.


The Query to Power It All

This article is only meant for major, major SQL geeks. You'll be bored out of your mind if you're not one; don't tell me I didn't warn you.

As you might have guessed, there's one, only one query powering the basic cocktail builder functionality. It's the query that answers the question "if I have ingredients A, B, and C, give me a list of all cocktails that I can make". Also:

  • give me cocktails that I can almost make, ordered by the number of missing ingredients (i.e. those that I can make right away go first, those that are missing a single ingredient second, etc).
  • consider ingredient substitutions (a concept I refer to as "normalized ingredients"): if you have Sky Vodka, and the recipe calls for Stoli, you can make the cocktail just fine.
  • calculate cocktail ratings from the user feedback

As you might guess, the SQL is rather involved. Here's the statement that gets executed when the user has two ingredients - items with ID's 73 and 76:

    FORMAT(round((AVG(rating.Rating)*2))/2, 1) AS Rating, 
    (CountPresent - CountMissing) as Difference 
        cocktail.ID as CocktailID, 
        cocktail.Name as CocktailName, 
        cocktail.ShortName as CocktailShortName, 
        cocktail.Instructions as CocktailInstructions,
        SUM(IF(userHas.NormalizedIngredientID IS NULL, 1, 0)) 
            as CountMissing, 
        SUM(IF(userHas.NormalizedIngredientID IS NULL, 0, 1))
            as CountPresent 
            mix INNER JOIN ingredient 
                ON mix.IngredientID = ingredient.ID 
            LEFT JOIN (
                SELECT NormalizedIngredientID 
                FROM ingredient 
                WHERE ID=76 
                SELECT NormalizedIngredientID 
                FROM ingredient 
                WHERE ID=73) AS userHas 
                ON userHas.NormalizedIngredientID =
            INNER JOIN cocktail ON mix.CocktailID = cocktail.ID 
            WHERE NOT (cocktail.Status = 'Cut') 
            GROUP BY mix.CocktailID) AS InnerCountsTable 
LEFT JOIN rating 
    ON InnerCountsTable.CocktailID = rating.CocktailID 
WHERE CountPresent > 0 
GROUP BY InnerCountsTable.CocktailID 
    CountMissing ASC, 
    Difference DESC, 
    CountPresent DESC LIMIT 30

A weird part of the query is the "UNION" part inside (the one that's creating the userHas part). Basically, I don't believe there's any other way to create an in-memory virtual table in MySQL to do a JOIN with. Anyone got better ideas?

Or, maybe you can offer a completely different approach that will work faster?


The first place!

So, here I am, looking at the Google Analytics for CocktailBuilder last week, and the graph looks approximately like this:

image Whaaaaat? was exactly my reaction. Here I am, having about 30 visitors on a usual day for about a month or so, and suddenly, a jump to 300, and then 700 visitors a day!

2007 Web 2.0 Awards WinnerWooooooot?


The reason was really easy to find. ranked Web 2.0 sites, and CocktailBuilder won the first place in the Fun Stuff category!

Here's their full review, for historical purposes.

Woooo hoo! :-)


Cross-Domain Ajax


Oh, friends, friends. Whoever thought of setting up the so-called Same Origin Policy was, ahem, not very forward-looking.

A bit of history: Microsoft. Internet Explorer 5.0 (released in 1999). Outlook Web Access (OWA) team comes to Trident (IE) and asks for a way for the web page to talk to the Exchange server, asynchronously. They get the XMLHTTPRequest object, currently more famously known as XHR. Seven years later, the industry catches up and understands that XHR can be, indeed, used for Ajax.

That XHR object has a very interesting property: you can't initiate requests to pages other than the one that the web page is originating from. For example: your site is, but you want to asynchronously communicate with through their REST API to get to something useful.

Tough luck, not gonna happen. Your XHR object has a security restriction: talk only to Why? Well, because you could, theoretically, sniff the local network, then send the results to

BUT WHY IN THE WORLD DO YOU HAVE TO USE XHR FOR THAT??? Who in his right mind was setting up that restriction??? If I'm running JavaScript on your browser, I can just freaking insert an IMG tag, point it to, and through clever parameters, pass any info you want.

There's a huge hole, and they put a tiny little plug over it.


1) It's much harder to code legitimate applications - mashups - and any kind of JavaScript-based services.

2) Security hole is as glaring as it ever was.


OK, now to the useful stuff. There are a couple ways to set up two-way asynchronous communications across domains.

1) Outright ugly methods: Flash and iframes.

2) Proxy. You set up a service on your domain that relays all requests to the target domain. Your XHR is talking to your box, your box is being the middleman between the other box and the browser.
+ You can still use XHR and related frameworks.

- Load on your server goes up; plus, if you're thinking about providing a service (I am -, this means that your clients will need to install some PHP/ASP/JSP on their server. That's a very bad thing; this implies lots of trust between the service provider and the client (if I have my PHP code running on your server, I own your box). That's why guys from are not going to make it big any time soon.

3) Script tag monkeying, also known as on-demand JavaScript. You all know that you can insert a <script> tag into your HTML, and have that tag point to an external server. But wait, you can insert that script tag dynamically! What does that mean? That you can dynamically inject JS code into your app, and that JS is coming asynchronously from the server.

Here's the code to make that "fake Ajax" call:

function hackyAjaxCall (op, params, callbackFunctionName) {
    // Inject the script tag into the document and pass parameters 
    // through the URL. Get and execute the JS that the server .
    // returns. No, there are no security risks: the server is mine.
    var head = document.getElementsByTagName("head")[0];
    var script = document.createElement('script');
    script.type = 'text/javascript';
    if (op.length < 1) {
        alert("Error: Unspecified op parameter for the Ajax call");
    var opUrlParam = "op=" + op + "&";
    var callbackUrlParam = callbackFunctionName ? "callback=" +
               callbackFunctionName + "&": "";
    var UrlParams = "p=" + escape(Object.toJSON(params));
    script.src = "http://server/endpoint.php?"+ opUrlParam +
               callbackUrlParam + UrlParams;

+ True, asynchronous communication between the browser and a web service on a different domain.

- This method is not as pleasant to work with as XHR, and there are no nice frameworks like Prototype, YUI, and Dojo. There was a project called "Ajax Extended", which added things to Prototype to do this, but the website is now dead. I'd love to see this feature in core Prototype.

- Security implications: if you don't trust the other end, don't do this. They can inject any JS they want into your browser-side app, and steal your cookies.

Further reading:

1) article of both mechanisms mentioned above.
2) Good article on the subject from




You've seen my recent post about the importance of knowing what the users do on your site. My friend David gave me great feedback to actually productize the idea, considering there really aren't any good solutions out there - so I put together a site where I discuss the problem in detail, and where eventually the tool will be available.

Check it out; it's called


Homework: Analyzing Competition

It's good to know who else is trying to do the same thing. Why?

Obvious answers - so that you don't reinvent the wheel; so that you know what they are doing; so that you can differentiate yourself properly.

Non-obvious answers - presence of competition is actually a good thing, it means that the market you're about to enter in fact exists, and you're not delusional. If you're planning to provide Internet services using toilets as hotspots, and noone else is doing anything similar, you are probably a bit off base (crap, how did they - CityNet - get the VC funding with that idea??). On the other hand, if other [sucky] solutions to the same problem exist, you're on the right track.

Well, of course, there are revolutionary things out there, too, but hey, if you're Einstein, you better realize it yourself. As one of my favorite bloggers Guy Kawasaki says, toilet paper is radically better than leaves.

But back to the topic. Competitive analysis. Let's assume for a moment that you decided to believe the crazy author of this blog, and actually do it. Well, I admire your trust in me - even I don't have that kind of trust in me :-).

All right, all right, back on track. Here's what you may want to cover:

1) What's the problem that you're trying to solve? For cocktail builder: find out cocktails you can make from ingredients in your bar.

2) What are adjacent problems? Where to buy liquor; history of cocktails; cocktail pictures; etc.

3) What are the Google search terms for the problem you're trying to solve? For me, it's cocktail, bar, ingredient, etc.

4) Who comes up on top in search results for these keywords? These are likely your top current competitors, if you're doing an Internet business.

5) Who does things right nearby? For cocktail builder, it's food sites that let you put in ingredients you have and find out the meals you can make from 'em. These folks may come in to your market at any moment.

To inspire you a little bit, I'm attaching the competitive analysis doc I used when doing the initial homework for my site. Take a look if you'd like.




Vision vNext: Organizing a Party

I'm thinking about the next version of the CocktailBuilder, and I'd like to share an idea that a friend of mine gave me. Basically, it's about one good end-to-end scenario for the website that I'm almost achieving, but not quite.

What's the most obvious use case for the site today? "Enter what you have in your bar, and you'll see the cocktails you can make from it". Great.

We want more.

We want to think end-to-end. What is the user trying to do? Probably, organize a party. A good party host wants to post a bar menu somewhere, to disincline the cute and pleasantly un-intelligent freshmen from the nearby dorm from asking "hey, can you make a sex on the beach?" RTFM, baby.

To make that scenario work, you need to help the savvy sysadmin of the party print out that M, I mean the manual, oops, the bar menu. And you need to help him manage that bar menu. Really, the bar menu is just a list of favorite cocktails; and printing of the menu is just a matter of converting the pretty HTML page to a pretty PDF file.

I'm half-way there on the implementation - favorites are done, but I'm still thinking about whether doing a PDF exporter is the right way to go .

What do you think about this scenario? Anything I'm missing? Any nearby scenario that we can get for cheap?



What are users doing on your site?

So, you have a great-looking AJAX website. You're Zillow, or Google Maps, or whatever. You have a billion visitors, but as a great entrepreneur, you understand that the only way to keep afloat is to give users the features that they want.

How do you do this?

1) Ask your users. Focus groups and similar methods still apply.

2) Talk to your users. Open a blog, get the comments. Put a comment box right there on the website. Have you noticed the "rate this content" concept on MSDN?


Do it.

3) Gather statistics on what users are doing on your site. We'll be talking about this method in depth here.

You surely noticed that the first two methods require either a major money investment, or proactive users; the vast majority of your users, however, won't be this "early adopters" crowd, willing to debug your JavaScript and bring you coffee just for making the coolest Ajax app around.

Average users want their problems solved. They don't want to help you. They want to help themselves. You want to help them, too, but how do you help, if they don't say what they want?? You track their behavior. You use tracking cookies. You record every single click they make within your application, look through the session information, and research interesting sessions. You personally, the site owner, need to read through these logs to understand what users do in your app.

Let me give you a more concrete feeling of what I'm talking about. Here's how a sample log excerpt may look like in CocktailBuilder:

  1. Added ingredient "absolut vodka"
  2. Tried to add an ingredient "absinte", but failed, because there was no match in the database of ingredients.
  3. Added ingredient "orange juice"
  4. Expanded the details for the "melon ball" cocktail
  5. Expanded the details for the "mandarin passion" cocktail
  6. Added the "mandarin passion" cocktail to favorites

This is just like conducting a customer visit: you're observing the user do their thing. Except that it's not happening in real-time, and you're not really seeing a video, but you know your app enough to make sense of it.

Plus, it's all quantitative, unlike videos: you can make a query such as "how many times in the last month folks tried to add an ingredient, but failed". Or you can draw conclusions over time, determining whether your efforts to improve the ingredient database is paying off. This, my friends, really is business intelligence for the Ajax world.

How do you set this kind of logging system up? You need to do this by hand; I haven't seen any good frameworks for this just yet, unfortunately. I told you about my love for Google Analytics - it's a great product to measure your site-wide performance (i.e. how many visitors came to your site in the last month? which country were they from?), but it's utterly useless for any kind application usage analysis for Ajax apps (since everything is on the same page, likely, with the same URL). So old-school metrics tools won't help you, and Apache log files won't, either.

You need a custom solution. At it's basis, there are 2 components:

1) A database table that defines the "datapoints" that you want to collect. Sample:


2) Client-side data collection mechanism. Really, just a backlog of things that happened on the client side. Things that may happen are defined in the first step. The backlog will be sent to the server every 5-10 seconds, and when that's done, the backlog is back to empty.

// submit user actions to the server every X milliseconds
window.setInterval(submitUserActions, 5000); 

function registerUserAction(id, data) {
   userActions[userActions.length] = [id, data];

// Send the usage data back to the server to track what
// the user was doing on the site
function submitUserActions() {
   if (userActions.length > 0) {
        // Ajax call sending userActions to the server

        // We've sent the data to the server,
        // so let's clear the buffer        
        userActions = [];

Every time something interesting happens in the app, I'd just call registerUserAction; for example, when the user is adding an ingredient, I call


Simple, isn't it?

One question remains: where is the DataPoint array coming from? At some point in the past, I mentioned a technique for generating semi-dynamic content. You'd use just that here: at build time, your script will dump the contents of the datapoints table into a .js file (it would have to do some minimal formatting to be syntactically correct). Then, at runtime, you just include that .js file as you would any other JavaScript resource. Several good things about this approach:

  1. Your code remains readable. You can easily tell that in the code fragment above I was trying to capture the fact that someone entered an ingredient. That fact could have been captured by using the datapoint id (1), not it's name (ADD_INGREDIENT_BY_NAME). This would have saved us some JS size, but I'll trade maintainability for download size any day.
  2. It's really easy to add new datapoints. Just add them to your database table, and code a single registerUserAction() call. Then, at build-time, everything will start working automagically. Again, ease of maintenance.

For dessert, take a look at some reports that I was able to generate from CocktailBuilder usage data. Answering the "how did you make this??" question: these were done using Rico/Prototype and the LiveGridPlus extension. 200 lines of code (PHP, JS, HTML, SQL, all intermingled - yeah, I don't drink my own Koolaid when I build quick-and-dirty things).