Google’s Closure Compiler Service API: Fundamentals

Looking for a fast way to optimize your JavaScript files, but don’t want to give up total control? Google offers Closure Compiler, which makes use of an intuitive REST API and several other features to get you exactly the optimization you need for your JavaScript.

The web has become a dynamic creature in permanent evolution that is changing faster and faster all the time. This statement is more than a simple metaphor since it can be interpreted literally: considering the large number of JavaScript libraries available nowadays, it’s not surprising that more and more websites use them extensively. This process generates tons of JavaScript files, which in many cases are heavier that the markup itself. Does this scenario ring any bells for you? I bet it does.

Of course, you might argue that the situation isn’t so critical after all, as many of these libraries, including jQuery, Mootools, Dojo and Prototype (feel free to add your own one to the list) also provide minified and compressed versions, which are quicker to download and cache. While I have to say that you’re right, things can get much more complex when you need to optimize custom JavaScript code. The process can be tedious, time-consuming and extremely annoying, especially if you plan to tackle it manually.

Although it’s fair to admit that currently there are some websites that offer free optimization of CSS and JavaScript files, either via a copy/paste procedure or by specifying the URLs of the files, the truth is that only a few of them will let you perform the optimization process programmatically. However, not all is lost, since in this apparently arid and pessimistic panorama, Google shines a hopeful light, with quite impressive results. The company has released a brand new web service called Closure Compiler (http://code.google.com/intl/en/closure/compiler/docs/overview.html) which will automatically optimize your JavaScript files by means of an intuitive REST API that can be consumed by any server-side programming language. What’s more, the service has several options that allow you to control certain things either manually or programmatically, such as the level of optimization applied to the target files, the format in which they’ll be delivered to the client (text, XML or JSON) and what kind of information will be displayed on screen (compiled code, statistics and so forth).

Provided that the topic has already caught your attention and you’re interested in learning how to work with Google’s Closure Compiler Service API, in this article series I’m going to show you how to interact with the API using some PHP classes. All the examples that you will see in the tutorials, however, can be easily adapted to function with the language of your choice, trust me.

Now, it’s time to start digging deeper into the features provided by the aforementioned Closure Compiler Service API. Let’s get going!

Setting the scenario to interact with Google’s Closure Compiler Service API: building an abstract HTTP request handler PHP class

As I expressed in the introduction, my goal here is to show in a step-by-step fashion how to consume Google’s Closure Compiler Service API by using a few sample PHP classes. This shouldn’t stop you, however, from giving the API a try through its UI, which can be found here. All that you’ll need to do is paste into the corresponding text area the code of the JavaScript file that you want to compile (or its URL), and then select the appropriate compiling options. The results will be displayed on the right-placed tab. The whole process is that easy, really.

Having clarified that, it’s time to get our hands dirty and start creating the PHP classes that will interact with the Closure Compiler. Since the compiler’s API must be queried via a POST HTTP request, I will first define an abstract class capable of triggering HTTP requests to a specified host on a given TCP port.

With that said, here’s the definition of this abstract class at its initial stage:

(HttpRequestHandlerAbstract.php)

<?php

abstract class HttpRequestHandlerAbstract
{
    protected $_data = array();
    protected $_url;
    protected $_port;
    protected $_method;
    protected $_headers = array();
    protected $_responseHeader;
    protected $_responseContent;
   
    // constructor
    public function __construct(array $data, array $settings = array())
    {
        $this->setData($data);
        if (array_key_exists(‘url’, $settings)) {
            $this->setUrl($settings['url']);
        }  
        if (array_key_exists(‘port’, $settings)) {
            $this->setPort($settings['port']);
        }
        if (array_key_exists(‘method’, $settings)) {
            $this->setMethod($settings['method']);
        }
    }
   
    // set the data that will be passed with the request
    public function setData(array $data)
    {
        if (empty($data)) {
            throw new HttpRequestHandlerException(‘The request arguments are not valid.’);
        }
        $this->_data = $data;
        return $this;
    }
     
    // get the specified request data
    public function getData()
    {
        return $this->_data;
    }
   
    // set the URL the request will be made to (implemented by subclasses)
    abstract public function setUrl($url);
     
    // get the given URL
    public function getUrl()
    {
        return $this->_url;
    }
   
    // set the TCP port the request will be made on (implemented by subclasses)
    abstract public function setPort($port);
     
    // get the given port
    public function getPort()
    {
        return $this->_port;
    }
   
    // set the request method (GET or POST)
    public function setMethod($method = ‘GET’)
    {
        $method = strtoupper($method);
        if (!in_array($method, array(‘GET’, ‘POST’), TRUE)) {
            throw new HttpRequestHandlerException(‘The request method is not valid.’);
        }
        $this->_method = $method;
        return $this;
    }
   
    // get the request method
    public function getMethod()
    {
        return $this->_method;
    }
       
    // add a new request header
    public function addHeader($key, $header)
    {
        $key = strtolower($key);
        if (!array_key_exists($key, $this->_headers)) {
            $this->_headers[$key] = $header;
        }
        return $this;
    }
   
    // remove a specified request header
    public function removeHeader($key)
    {
        $key = strtolower($key);
        if (array_key_exists($key, $this->_headers)) {
            unset($this->_headers[$key]);
        }
        return $this;
    }
     
    // get a specified request header
    public function getHeader($key)
    {
        $key = strtolower($key);
        if (array_key_exists($key, $this->_headers)) {
            return $this->_headers[$key];
        }
    }
     
    // get the header included in the response
    public function getResponseHeader()
    {
        return $this->_responseHeader;
    }
   
    // get the content included in the response
    public function getReponseContent()
    {
        return $this->_responseContent;
    }
              
    // send the specified header
    public function sendHeader($key)
    {
        if ($header = $this->getHeader($key)) {
            header($header);
        }
    } 
}

 

(HttpRequestHandlerException.php)
          
<?php

class HttpRequestHandlerException extends Exception{}

Even though at first glance the above “HttpRequestHandlerAbstract” class seems to be complicated, this is only a misleading impression. Its driving logic is quite easy to follow. Simply put, the class implements a few straightforward methods for adding, removing and getting request headers, and for sending them explicitly as well. In addition, the class accepts, via its constructor (or alternatively via its mutator methods), an array of data containing the parameters that will be passed along with the request, together with the corresponding URL and the TCP port.

It’s valid to note that the methods that set the URL and TCP port associated with the request have been declared abstract. This is done so that they’ll be implemented by a concrete subclass. This subclass will be responsible for talking directly to the Closure Compiler API, in this way taking advantage of the refined functionality provided by Inheritance. Got that point? Great.

In its current state, the previous abstract class is capable of handling most of the options required for triggering an HTTP request to a given host on a specific TCP port, but it still lacks a method that performs the actual requests. Fortunately, this issue can be solved in a snap. To prove that, in the following section I’m going to show you the full implementation of this pending method.

Now, click the link below and keep reading.

{mospagebreak title=Adding a method to perform HTTP requests}

As I mentioned in the preceding section, it’s mandatory to provide the earlier “HttpRequestHandlerAbstract” class with the ability to send HTTP requests to the specified URL and port. To do this, below I defined an additional method, not surprisingly called “sendRequest(),” which performs this task in a fairly simple manner. Here’s how the method has been implemented: 

// send an HTTP request to the specified URL and TCP port
public function sendRequest()
{
    // parse and urlencode the request data
    $data = ”;
    foreach ($this->_data as $key => $value) {
        $data .= ‘&’ . $key . ‘=’ . urlencode($value);
    }
    $data = trim($data, ‘&’);
    // parse the given URL
    $url = parse_url($this->_url);
    if (!isset($url['host']) OR !isset($url['path'])) {
        throw new HttpRequestHandlerException(‘No host or path was specified.’);
    }
    $host = $url['host'];
    $path = $url['path'];
    // open a socket connection on the specified TCP port
    if (!$fp = fsockopen($host, $this->_port)) {
        throw new HttpRequestHandlerException(‘Error opening socket connection to the URL ‘. $this->_url . ‘ on port.’ . $this->_port);
    }
    fputs($fp, "$this->_method $path HTTP/1.0rn");
    fputs($fp, "Host: $hostrn");
    fputs($fp, "Content-type: application/x-www-form-urlencodedrn");
    fputs($fp, "Content-length: ". strlen($data) . "rn");
    fputs($fp, "Connection: closernrn");
    fputs($fp, $data);
    // get the response of the request
    $response = ”;
    while(!feof($fp)) {
        $response .= fgets($fp, 128);
    }
    // close the socket connection:
    fclose($fp);
    // process the response
    $response = explode("rnrn", $response, 2);
    $this->_responseHeader = $response[0];
    $this->_responseContent = $response[1];
    return $this->_responseContent;
}

Well, if you’re not familiar with the internals of the HTTP protocol, fear not; the above method simply sends an HTTP request to the given host and port via the “fsockopen()” PHP native function. Once the response is received from the queried host, the header and contents are stored separately in the $_responseHeader and $_responseContent class properties for further processing.

So far, so good. Assuming that you understand how the previous “sendRequest()” method does its business, the next logical step is to add it to the definition of the “HttpRequestHandlerAbstract” class, so you can see how the pertinent class looks after giving it the final touches.

This will be done in the following section, therefore jump forward and read the lines to come.

The finished version of the earlier “HttpRequestHandlerAbstract” class

Below I’ve listed the full source code corresponding to the previous “HttpRequestHandlerAbstract” class, after adding to it the “sendRequest()” method discussed previously. Here it is: 

(HttpRequestHandlerAbstract.php)

<?php

abstract class HttpRequestHandlerAbstract
{
    protected $_data = array();
    protected $_url;
    protected $_port;
    protected $_method;
    protected $_headers = array();
    protected $_responseHeader;
    protected $_responseContent;
   
    // constructor
    public function __construct(array $data, array $settings = array())
    {
        $this->setData($data);
        if (array_key_exists(‘url’, $settings)) {
            $this->setUrl($settings['url']);
        }  
        if (array_key_exists(‘port’, $settings)) {
            $this->setPort($settings['port']);
        }
        if (array_key_exists(‘method’, $settings)) {
            $this->setMethod($settings['method']);
        }
    }
   
    // set the data that will be passed with the request
    public function setData(array $data)
    {
        if (empty($data)) {
            throw new HttpRequestHandlerException(‘The request arguments are not valid.’);
        }
        $this->_data = $data;
        return $this;
    }
     
    // get the specified request data
    public function getData()
    {
        return $this->_data;
    }
   
    // set the URL the request will be made to (implemented by subclasses)
    abstract public function setUrl($url);
     
    // get the given URL
    public function getUrl()
    {
        return $this->_url;
    }
   
    // set the TCP port the request will be made on (implemented by subclasses)
    abstract public function setPort($port);
     
    // get the given port
    public function getPort()
    {
        return $this->_port;
    }
   
    // set the request method (GET or POST)
    public function setMethod($method = ‘GET’)
    {
        $method = strtoupper($method);
        if (!in_array($method, array(‘GET’, ‘POST’), TRUE)) {
            throw new HttpRequestHandlerException(‘The request method is not valid.’);
        }
        $this->_method = $method;
        return $this;
    }
   
    // get the request method
    public function getMethod()
    {
        return $this->_method;
    }
       
    // add a new request header
    public function addHeader($key, $header)
    {
        $key = strtolower($key);
        if (!array_key_exists($key, $this->_headers)) {
            $this->_headers[$key] = $header;
        }
        return $this;
    }
   
    // remove a specified request header
    public function removeHeader($key)
    {
        $key = strtolower($key);
        if (array_key_exists($key, $this->_headers)) {
            unset($this->_headers[$key]);
        }
        return $this;
    }
     
    // get a specified request header
    public function getHeader($key)
    {
        $key = strtolower($key);
        if (array_key_exists($key, $this->_headers)) {
            return $this->_headers[$key];
        }
    }
     
    // get the header included in the response
    public function getResponseHeader()
    {
        return $this->_responseHeader;
    }
   
    // get the content included in the response
    public function getReponseContent()
    {
        return $this->_responseContent;
    }
               
    // send an HTTP request to the specified URL and TCP port
    public function sendRequest()
    {
        // parse and urlencode the request data
        $data = ”;
        foreach ($this->_data as $key => $value) {
            $data .= ‘&’ . $key . ‘=’ . urlencode($value);
        }
        $data = trim($data, ‘&’);
        // parse the given URL
        $url = parse_url($this->_url);
        if (!isset($url['host']) OR !isset($url['path'])) {
            throw new HttpRequestHandlerException(‘No host or path was specified.’);
        }
        $host = $url['host'];
        $path = $url['path'];
        // open a socket connection on the specified TCP port
        if (!$fp = fsockopen($host, $this->_port)) {
            throw new HttpRequestHandlerException(‘Error opening socket connection to the URL ‘. $this->_url . ‘ on port.’ . $this->_port);
        }
        fputs($fp, "$this->_method $path HTTP/1.0rn");
        fputs($fp, "Host: $hostrn");
        fputs($fp, "Content-type: application/x-www-form-urlencodedrn");
        fputs($fp, "Content-length: ". strlen($data) . "rn");
        fputs($fp, "Connection: closernrn");
        fputs($fp, $data);
        // get the response of the request
        $response = ”;
        while(!feof($fp)) {
            $response .= fgets($fp, 128);
        }
        // close the socket connection:
        fclose($fp);
        // process the response
        $response = explode("rnrn", $response, 2);
        $this->_responseHeader = $response[0];
        $this->_responseContent = $response[1];
        return $this->_responseContent;
    }
   
    // send the specified header
    public function sendHeader($key)
    {
        if ($header = $this->getHeader($key)) {
            header($header);
        }
    } 
}

Mission accomplished, at least for the moment. At this point, I’ve created an abstract class that allows us to easily make HTTP requests to a specified host. This is pretty useful, since Google’s Closure Compiler Service API can only be queried programmatically via POST requests.

Even though this first task may seem unnecessary, don’t feel disappointed; the abstract class just defined will make interacting with the Closure Compiler API a breeze, believe me. Keep in mind that this is a work in progress.

Final thoughts

In this first part of the series, I provided you with a quick overview of Google’s Closure Compiler Service API, including an introduction to the features that it offers and how you can access it via its GUI. Nevertheless, the main objective of the series is to demonstrate how to consume the API with PHP, and in consonance with this concept I developed an abstract class capable of making HTTP requests to a given host.

At this time, you may be wondering what this buys us, right? Well, with that abstract parent already up and running, it’s ridiculously simple to derive a subclass from it that talks directly to the API, thus making it easy to optimize JavaScript files programmatically.

That’s exactly what I plan to cover in the coming installment of the series. Now that you know what to expect from that tutorial, you don’t have any excuses to miss it!

[gp-comments width="770" linklove="off" ]

antalya escort bayan antalya escort bayan Antalya escort diyarbakir escort