Home arrow AJAX & Prototype arrow Google's Closure Compiler Service API: the SIMPLE_OPTIMIZATIONS Option

Google's Closure Compiler Service API: the SIMPLE_OPTIMIZATIONS Option

In this fourth part of the series, I show you how to apply a more rigorous level of optimization to a JavaScript file by passing the SIMPLE_OPTIMIZATIONS option to the Closure Compiler API. This process removes white space and comments, and shortens function and variable names, which significantly reduces the size of the target file.

TABLE OF CONTENTS:
  1. Google's Closure Compiler Service API: the SIMPLE_OPTIMIZATIONS Option
  2. Using the SIMPLE OPTIMIZATIONS option
By: Alejandro Gervasio
Rating: starstarstarstarstar / 1
October 05, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

Are you looking for a quick and effective way to optimize your carefully-crafted JavaScript files without having to perform the whole process from scratch? If your answer is yes, I suggest you take an in-depth look at a new web service released in the last few weeks by Google, called Closure Compiler Service API. This service will let you compile your JavaScript snippets in a truly painless fashion through a set of POST arguments, which will allow you control the format (text, XML or JSON) in which the compiled files will be sent back to the client, and the level of optimization that will be applied to them.

Moreover, to demonstrate how to work with the Closure Compiler Service API using a PHP-based backend, in the previous tutorial of this series I created an introductory example that showed how to basically compile a sample JavaScript file by assigning the value of “WHITESPACE_ONLY” to the “compilation_level” argument provided by the API. As one might expect, the compiled output returned to the browser was the original file with all of its white space nicely removed.

If you think, however, that the abilities of the Closure Compiler Service API are limited to striping white space from JavaScript code, I should say that you’re underestimating it. The service is capable of performing a more advanced optimization process on target files. Quite possibly, you’ll be wondering how, right? Assigning the value “SIMPLE_OPTIMIZATIONS” to the aforementioned “compilation_level” parameter will cause the compiler to remove white space and make variables and functions names much shorter (when possible, of course).

Having explained that, in this fourth part of the series I’m going to develop another approachable example, which will illustrate how to apply this more sophisticated level of optimization to the JavaScript file coded in the previous tutorial.

Eager to learn how this will be accomplished? Then start reading right now!

Review: basic optimization of a JavaScript file with Google’s Closure Compiler Service API

Before I begin demonstrating how to more efficiently compile a JavaScript file by using the “SIMPLE_OPTIMIZATIONS” option mentioned in the introduction, first I’d like to spend a few moments reintroducing all of the sample PHP classes developed so far. They are the building blocks of the PHP backend that interacts either directly or indirectly with the Closure Compiler Service API.

That said, here are the first two classes, which are tasked with handling HTTP requests via the “fsockopen()” PHP built-in function. Check them out, please:

(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);
        }
    } 
}

 

(ClosureCompilerHandler.php)

<?php

class ClosureCompilerHandler extends HttpRequestHandlerAbstract
{
    protected $_url = 'http://closure-compiler.appspot.com/compile';
    protected $_port = 80;
    protected $_method = 'POST';
    protected $_responseHeader;
    protected $_responseContent;
    protected $_headers = array(
                  'js' => 'Content-type: text/javascript',
                  'xml' => 'Content-type: text/xml',
                  'json' => 'Content-type: application/json'
              ); 
   
    // set the URL of the closure compiler API
    public function setUrl($url)
    {
    if (strpos('http://closure-compiler.appspot.com', $url) !== 0) {
        throw new HttpRequestHandlerException('The specified URL for the closure compiler API is not valid.');
    }
    $this->_url = $url;
    return $this;
    }

    // set the TCP port of the closure compiler API (throws an exception, as it's been already set)
    public function setPort($port)
    {
    throw new HttpRequestHandlerException('The specified TCP port for the closure compiler API has been already set.');
    }    
}

As you can see from the previous code snippet, the two classes above make up a simple hierarchy that allows you to send HTTP requests to a given URL on a TCP port. While the abstract parent encapsulates most of the generic functionality required to perform this task, the concrete “ClosureCompilerHandler” subclass is responsible for interacting specifically with the Closure Compiler Service API.

Now that you've grasped the underlying logic of these classes, it’s time to show the definition of a couple more. The first one is charged with reading and writing data to a given file, and the last one is tasked with lazy-loading classes via the PHP SPL stack. Here they are: 

(FileHandler.php)

<?php

class FileHandler
{
    protected $_path = 'function.js';
    protected $_data;
   
    // constructor
    public function __construct($path = '')
    {
        if ($path !== '') {
            $this->setPath($path);
        }
    }
   
    // set the path to read/write data
    public function setPath($path)
    {
        if (!file_exists($path)) {
            throw new FileHandlerException('The specified path is not valid.');
        }
        $this->_path = $path;
    }
   
    // get the specified path
    public function getPath()
    {
        return $this->_path;
    }
   
    // set new data to be written
    public function setData($data)
    {
        $this->_data = $data;
    }
   
    // get the stored data
    public function getData()
    {
        return $this->_data;
    }
       
    // read data from the specified path
    public function read()
    {
        if (!$this->_data = file_get_contents($this->_path)) {
            throw new FileHandlerException('Error reading from the target file.');
        }
        return $this->_data;
    }
   
    // write data to the specified path
    public function write($data)
    {
        if (!file_put_contents($this->_path, $data)) {
            throw new FileHandlerException('Error reading from the target file.');
        }
        $this->_data = $data;
    }           
}

 

(Autoloader.php)

<?php

class Autoloader
{
    private static $_instance;
   
    // get the Singleton instance of the autoloader
    public static function getInstance()
    {
        if (!self::$_instance) {
            self::$_instance = new self;
        }
        return self::$_instance;
    } 
   
    // constructor
    private function __construct()
    {
        spl_autoload_register(array($this, 'load'));
    }
   
    // prevent cloning instance of the autoloader
    private function __clone(){}
   
    // load a given class or interface
    public static function load($class)
    {
        $file = $class . '.php';
        if (!file_exists($file)) {
            throw new ClassNotFoundException('The file containing the requested class or interface ' . $class . ' was not found.');
        }
        require $file;
        unset($file);
        if (!class_exists($class, FALSE) AND !interface_exists($class, FALSE)) {
            throw new ClassNotFoundException('The requested class or interface ' . $class . ' was not found.');
        }
    }  
}

Mission accomplished. Even though the first impression is that developing all of the classes that you just saw is a rather time-consuming process that isn’t worth the hassle, the truth is that when they’re put to work together with the Closure Compiler API, they make optimizing JavaScript snippets a breeze. Of course, the best way to demonstrate this is by example, so in the following lines I included the one created in the previous tutorial, which optimizes the JavaScript file below:  

(function.js)

function createDiv(idAttr, classAttr) {
    // create the div
    var div = document.createElement('div');
    // assign the 'id' attribute to the div element
    div.setAttribute('id', idAttr);
    // assign the 'class' attribute to the div element
    div.setAttribute('class', classAttr);
    // append the div element to the document
    document.getElementsByTagName('body')[0].appendChild(div);
};
// call the previous function
createDiv('divid', 'divclass');

In this case, the target JavaScript file is really trivial, as it’s only tasked with creating a div element on the fly, which is appended to the DOM afterward. However, the most interesting aspect of this example is the PHP script that uses the Closure Compiler Service API behind the scenes to optimize the file in question. Here it is:

<?php

// example using optimization level = WHITESPACE_ONLY
try {
 
    // include the autoloader class
    require_once 'Autoloader.php';
    Autoloader::getInstance();

    // create an instance of the file handler class and read the specified JavaScript file
    $fileHandler = new FileHandler;
    $js = $fileHandler->read();
   
    // build the array of arguments that will be passed to the closure compiler API
    $data = array(
         'js_code' => $js,
         'compilation_level' => 'WHITESPACE_ONLY',
         'output_format' => 'text',
         'output_info' => 'compiled_code'
    );
   
    // create an instance of the ClosureCompilerHandler class
    $compilerHandler = new ClosureCompilerHandler($data);
    // query the closure compiler API and get the compiled JavaScript function
    $response = $compilerHandler->sendRequest();
    $compilerHandler->sendHeader('js');
    // print to screen the compiled JavaScript function
    echo $response;
    /*
    displays the following
 
    function createDiv(idAttr,classAttr){var div=document.createElement("div");div.setAttribute("id",idAttr);div.setAttribute("class",classAttr);document.getElementsByTagName("body")[0].appendChild(div)}createDiv("divid","divclass");
    */   
}
catch (Exception $e) {
    echo $e->getMessage();
    exit();
}

From the previous code sample, it’s clear to see that the Closure Compiler Service API does remarkable work when optimizing JavaScript files programmatically. In this particular case, the API has been called with the value “WHITESPACE_ONLY” assigned to its “compilation_level” argument. This process not surprisingly removes all the white space and comments included in the target file, in this way significantly reducing it from its original size.

It’s valid to note, though, that the API is capable of compiling JavaScript files more “cleverly.” It’s possible to assign different values to the aforementioned “compilation_level” parameter and get even better results. In consonance with this concept, in the section to come I’m going to set up an example similar to the previous one, which will show you how to accomplish this with the API’s SIMPLE_OPTIMIZATIONS option.

To see how this brand new example will be created, jump ahead and read the next few lines.     



 
 
>>> More AJAX & Prototype Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

AJAX & PROTOTYPE ARTICLES

- Best AJAX Tutorials for Forms
- The Best AJAX Tutorials
- 8 Great Ajax Tutorials
- Using Ajax and jQuery
- Using Ajax and jQuery with HTML Forms
- Ajax.org Offers Cloud9 IDE for JavaScript
- Java Technologies Provider ICEsoft Releases ...
- Using Recaptcha in AJAX Prototype Framework ...
- Google's Closure Compiler Service API: Addit...
- Installing Google Web Toolkit: Introducing t...
- Google's Closure Compiler Service API: Displ...
- Google's Closure Compiler Service API: Deliv...
- Google's Closure Compiler Service API: the A...
- Google's Closure Compiler Service API: the S...
- Google's Closure Compiler Service API: Optim...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: