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.
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:
// 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!