Week 4 - Making efficient code (aka "the daddy function")

Let me start by telling you a story that my mentor once told me. There are four stages in the work cycle of a developer.

  • Stage 1 - Make it work
    It's about getting something to work- don't think much about other factors- just get it to work.
  • Stage 2 - Make it efficient
    It's about solving a problem in a productive way- make least number of SQL queries, use less space, do not write unnecessary code.
  • Stage 3 - Make it elegant
    Write your code in a very scalable and extendable way. Make your code re-usable, make use of existing paradigms and write as less code as possible. Think of it this way- if you copy paste a few lines from one function or class to another, you are doing it wrong.
  • Stage 4 - Do not code at all!
    You might be surprised to know but sometimes it's good not to code at all. Before you start, you should check the feasibility of what you are about to make. Does it have a use case? Is it worth the effort you are going to put into it?

Although I have faced all four stages during this GSoC period, this week primarily consisted of stage 3.

Alex was quick to notice that all of my existing calls (those get and post functions in the router classes) essentially did the same thing- generate a log, check the token, check access level, make a query and print a response. Although I had functions for each of these tasks (with ATutor's queryDB for SQL queries), the functions looked the same!

That is why, this week involved creation of a "daddy" function- a backbone function which would run (almost) all API calls- which would be re-used for almost everything else. However, let us first look at a function that created SQL clauses.

function create_SQL_clause($terms, $requests, $prefix = "") {  
    /*
     * Function to create SQL clause
     * $terms is an associative array
     * The keys of $terms represent the variables in $requests
     * The values of $terms represent the column names that must be present
     * For example, create_SQL_clause(array(
     *                  "title" => "c.title",
     *                  "language" => "c.language"), $_GET) should return
     * "WHERE c.title = 'My Course' AND c.language = 'en'"
     * provided title and language are present in $_GET
     */
    $query = $prefix;
    foreach ($terms as $key => $value) {
        if ($requests["$key"]) {
            if ($query != "") {
                $query = $query."AND ";
            }
            $query = $query.$value." = '".$requests["$key"]."' ";
        }
    }
    return $query;
}

This function would essentially generate the SQL clauses (either WHERE in SELECT queries or SET in UPDATE queries) for the use in various functions. I believe the comments are enough to explain how it works.

This brings us to our backbone function. I rightfully call it api_backbone. Let us have a look at the skeleton.

function api_backbone($request_type, $token, $access_level, $query, $array = array(), $one_row = false, $callback_func = "mysql_affected_rows") {  
    /*
     * Function to perform all API calls
     * Every call has a token, checks access level and performs a query
     * This function takes those as argument and logs the request
     */
     ...
}

The $request_type stands for the HTTP request method (using some pre defined constants). The $token and $access_level have been discussed before.

queryDB uses vsprintf() to generate the SQL query, which is why it needs a string and an array (which it feeds to vsprintf). Those are effectively contained within $query and $array respectively. $one_row and $callback_func are also parameters passed to queryDB, standing for whether the result consists of a single row and what PHP/MySQL function should be run on the query before the result is presented, respectively.

The function is a bit long considering the things that I have to take care of, and you can have a look at the code here.