HomePHP Page 4 - Building Object-oriented Web Pages with HTTP Compression in PHP
Putting the pieces together: compressing the output of a web page generator class - PHP
Here we go! Welcome to the third -– and last -- part of the series “Using HTTP compression in PHP.” Just in case you didn’t know, this set of tutorials explores the advantages of using HTTP compression on dynamic PHP pages, reducing their download times and increasing the overall performance of PHP applications.
One of the best things about PHP is the way it allows you to develop different mechanisms, focused on speeding up the performance of certain applications. This is especially applicable when working with object-oriented web pages, since the page rendering process demands the instantiation of numerous objects which must coexist with each other, at least until the page has been completely assembled on the server.
Considering the eventual issues of working with various objects that might affect the performance of the entire application, reducing the amount of data transferred from the server to the client is something that can be easily achieved with HTTP compression. In this particular case, first I’ll define an object factory class, tasked with creating and returning web page objects; then I'll create a page generator class, which will use “Gzip” encoding to compress the dynamic output of object-based web documents.
Here’s the signature of these two classes, so have a look at them:
// abstract 'ObjectFactory' class abstract class ObjectFactory{ private function __construct(){} public function createObject($type,$attributes=array()){ if(!class_exists($type)||!is_array($attributes)){ throw new Exception('Invalid object parameters'); } return new $type($attributes); } } // 'PageGenerator' class class PageGenerator{ private $output; private $title; private $keywords; private $cssFile; private $jsFile; public function __construct(){ $this->output=''; $this->title='Sample Page'; $this->keywords='PHP,OOP,Output buffering,HTTP Compression'; $this->cssFile='styles.css'; $this->jsFile='global_functions.js'; } public function setTitle($title){ $this->title=$title; } public function setKeywords($keywords){ $this->keywords=$keywords; } public function setCssFile($cssFile){ $this->cssFile=$cssFile; } public function setJsFile($jsFile){ $this->jsFile=$jsFile; } public function makeHeader(){ $this->output='<html><head><title>'.$this- >title.'</title><meta name="keywords" content="'.$this- >keywords.'" /><link rel="stylesheet" href="'.$this->cssFile.'" type="text/css"><script language="javascript" src="'.$this- >jsFile.'"></script></head><body>'; } public function addHTMLElement(HTMLElement $htmlElement){ $this->output.=$htmlElement->getHTML(); } public function makeFooter(){ $this->output.='</body></html>'; } public function getHTML(){ return $this->output; } public function getCompressedHTML(){ // check if browser supports gzip encoding if(strstr($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip')){ // start output buffer ob_start(); // echo page contents to output buffer echo $this->getHTML(); // crunch (X)HTML content & compress it with gzip $compHTML=gzencode(preg_replace("/ (rn|n)/","",ob_get_contents()),9); // clean up output buffer ob_end_clean(); // return compressed (X)HTML content return $compHTML; } return false; } public function sendEncodingHeader(){ header('Content-Encoding: gzip'); } }
As shown above, the first “Object Factory” class accepts two arguments: the type of web page object being created, along with its corresponding attributes. This means that if all goes well, the new object is returned to client code. As you can see, this class uses the factory design pattern for instantiating web page objects, thus if you’ve used this pattern before, you should have no problems applying it.
The second class, that is “PageGenerator,” is the most interesting one. It’s responsible for building up web pages, using input objects of type “HTMLElement,” which are passed on to its “addHTMLElement()” method. However, aside from creating the web document in question, this class exposes two additional methods, handy for compressing the corresponding (X)HTML output: “getCompressedHTML()” and “sendEncodingHeader()” respectively.
The first method uses the “gzencode()” function, in order to compress the web page’s output after fetching the contents from an output buffer. Then the compressed data is returned to the point from which this method is called. Additionally, the second method, “sendEncodingHeader(),” simply sends out the appropriate HTTP header, in order to indicate to the browser that data will be sent as “gzip-encoded” content.
At this stage, you hopefully understand how the above classes work together. Now, look at the following example, which illustrates the entire process for rendering and encoding the contents of a web page:
try{ // instantiate some (X)HTML widget objects $h1=ObjectFactory::createObject('Header1',array ('class'=>'header1')); $h1->setData('This is a H1 header'); $h2=ObjectFactory::createObject('Header2',array ('class'=>'header2')); $h2->setData('This is a H2 header'); $h3=ObjectFactory::createObject('Header3',array ('class'=>'header3')); $h3->setData('This is a H3 header'); $h4=ObjectFactory::createObject('Header4',array ('class'=>'header4')); $h4->setData('This is a H4 header'); $h5=ObjectFactory::createObject('Header5',array ('class'=>'header5')); $h5->setData('This is a H5 header'); $div=ObjectFactory::createObject('Div',array ('id'=>'content','class'=>'divclass')); $div->setData('Div content goes here'); $par=ObjectFactory::createObject('Paragraph',array ('class'=>'paragraph')); $table=ObjectFactory::createObject('Table',array ('width'=>'500','class'=>'tableclass')); $table->setData(array('content'=>'Table content goes here')); $form=ObjectFactory::createObject('Form',array ('action'=>'formprocessor.php','method'=>'post')); $form->setData('Please enter your data below<br />'); // add form fields $form->addInputField(array ('type'=>'text','name'=>'fname','size'=>'30'),'First Name'); $form->addInputField(array ('type'=>'text','name'=>'lname','size'=>'30'),'<br />Last Name'); $form->addInputField(array ('type'=>'text','name'=>'email','size'=>'30'),'<br />Email'); $form->addTextArea(array ('name'=>'comments','rows'=>'20','cols'=>'30'),'<br />'); $form->addSelectField(array('name'=>'options'),array ('value1'=>'1','value2'=>'2','value3'=>'3'),'<br />'); $form->addInputField(array ('type'=>'submit','name'=>'send','value'=>'Send Data'),'<br /><br />'); // instantiate a new PageGenerator object $pageGen=new PageGenerator(); // build web page header $pageGen->makeHeader(); // add 'HTMLElement' objects $pageGen->addHTMLElement($h1); $pageGen->addHTMLElement($h2); $pageGen->addHTMLElement($h3); $pageGen->addHTMLElement($h4); $pageGen->addHTMLElement($h5); $pageGen->addHTMLElement($par); $pageGen->addHTMLElement($table); $pageGen->addHTMLElement($div); $pageGen->addHTMLElement($form); // build web page footer $pageGen->makeFooter(); // send HTTP gzip encoding header $pageGen->sendEncodingHeader(); // display compressed web page echo $pageGen->getCompressedHTML(); } catch(Exception $e){ echo $e->getMessage(); exit(); }
As shown above, a basic web page is populated with a few (X)HTML objects, including a bunch of headers, a Div, a paragraph, and finally a simple web form. After these elements have been added by the corresponding “addHTMLElement()” method, the web document is generated on the server and then compressed. At the end of the script, web page contents are uncompressed in the client, after calling the proper “sendEncodingHeader()” method.
As you’ve seen, using HTTP compression within an object-based PHP application is a straightforward process, and certainly doesn’t require much effort from us to code a class that takes advantage of its benefits.
Of course, before compressing dynamic output, you should evaluate carefully if this is really worthwhile, since the benefits that you can obtain in the end will vary, depending of the nature and size of the data being compressed. In all these cases, a detailed benchmarking of a specific PHP application can help a lot in deciding when and how to implement HTTP compression.
Summary
Now this series is concluded. Over the different articles, I hope you learned several approaches for using HTTP compression in PHP, in order to decrease the download times of dynamic pages, and consequently reduce the amount of data transferred from the server to the client.
The numerous code samples that you saw here covered both procedural and object-based applications, and hopefully will serve as an instructive experience from which to develop more complex scripts that utilize HTTP compression. Now that you have the required knowledge, go ahead and empower your dynamic PHP applications with HTTP compression. Meet you in the next PHP tutorial!