Main Page | Directories | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages | Examples

class.gzip_encode.php

Go to the documentation of this file.
00001 <?php
00002 
00003 // News: I had once said that when PHP4.0.5 comes out I will reccomend the built in
00004 // ob_gzhandler over my code unless you are generating flash or images on the fly.
00005 //
00006 // I was wrong. PHP4.0.5 is out and ob_gzhandler doesn't work for me.
00007 
00008 // Note: This is rather cool: http://Leknor.com/code/gziped.php
00009 // It will calculate the effects of this class on a page.
00010 // compression level, cpu time, download time, etc
00011 
00012 // Note: this may better for some sites:
00013 // http://www.remotecommunications.com/apache/mod_gzip/
00014 // I've read that the above doesn't work with php output.
00015 
00016 class gzip_encode {
00017     /*
00018      * gzip_encode - a class to gzip encode php output
00019      *
00020      * By Sandy McArthur, Jr. <Leknor@Leknor.com>
00021      *
00022      * Copyright 2001 (c) All Rights Reserved, All Responsibility Yours.
00023      *
00024      * This code is released under the GNU LGPL Go read it over here:
00025      * http://www.gnu.org/copyleft/lesser.html
00026      *
00027      * I do make one optional request, I would like an account on or a
00028      * copy of where this code is used. If that is not possible then
00029      * an email would be cool.
00030      *
00031      * How to use:
00032      * 1. Output buffering has to be turned on. You can do this with ob_start()
00033      *    <http://php.net/manual/function.ob-start.php> or in the php config
00034      *    file. Nothing bad happens if output buffering isn't turned on, your
00035      *    page just won't get compressed.
00036      * 2. Include the class file.
00037      * 3. At the _very_ end of your script create an instance of the encode
00038      *    class.
00039      *
00040      * eg:
00041      *   ------------Start of file----------
00042      *   |<?php
00043      *   | ob_start();
00044      *   | include('class.gzip_encode.php');
00045      *   |?>
00046      *   |<HTML>
00047      *   |... the page ...
00048      *   |</HTML>
00049      *   |<?php
00050      *   | new gzip_encode();
00051      *   |?>
00052      *   -------------End of file-----------
00053      *
00054      * Things to note:
00055      * 1. There is no space before the beginning of the file and the '<?php ' tag
00056      * 2. The ob_start() line is optional if output buffering is turned on in
00057      *    the main config file.
00058      * 3. Turning on and off output buffering just won't work.
00059      * 4. There must be nothing after the last '?>' tag at the end of the file.
00060      *    Be careful of a space hiding there.
00061      * 5. There are better ways to compress served content but I think this is
00062      *    the only way to compress php output.
00063      * 6. Your auto_prepend_file is a good place for the ob_start() and
00064      *    your auto_append_file is a good place for new gzip_encode().
00065      * 7. If you put new gzip_encode() in your auto.append file then you can
00066      *    call ob_end_flush() in your script to disable compression.
00067      *
00068      * This was written from scratch from info freely available on the web.
00069      *
00070      * These site(s) were useful to me:
00071      *   http://www.php.net/manual/
00072      *   http://www.ietf.org/rfc/rfc2616.txt (Sections: 3.5, 14.3, 14.11)
00073      *
00074      * Requirments:
00075      *   PHP 4.0.1+: I use the '===' operator, and output buffering, crc32();
00076      *   zlib:    Needed for the gzip encoding. (Odds are you have it)
00077      *
00078      * Benchmarks:
00079      *   Take a look at http://Leknor.com/code/gziped.php and feed it a page to
00080      *   get an idea of how it will preform on your data or page.
00081      *
00082      * To Do:
00083      * 1. I have reports of no content errors. I can't seem to duplicate this.
00084      *    Please visit my discussion boards if you think you may be able to help
00085      * 2. The Accept-Encoding isn't handled to spec. Check out 14.3 in RFC 2616
00086      *    to see how it should be done.
00087      *
00088      * Change Log:
00089      *   0.66: Big bug fix. It wouldn't compress when it should.
00090      *   0.65: Fix for PHP-4.0.5 suddenly removing the connection_timeout() function.
00091      *   0.62: Fixed a typo
00092      *   0.61: Detect file types more like described in the magic number files, also
00093      *      added detection for gzip and pk zip files.
00094      *   0.6:  Detect common file types that shouldn't be compressed, mainly
00095      *      for images and swf (Shockwave Flash doesn't really accept gzip)
00096      *   0.53: Made gzip_accepted() method so everyone can detect if a page
00097      *      will be gzip'ed with ease.
00098      *   0.52: Detection and graceful handling of improper install/missing libs
00099      *   0.51: Added FreeBSD load average detection.
00100      *   0.5:  Passing true as the first parameter will try to calculate the
00101      *      compression level from the server's load average. Passing true
00102      *      as the second parameter will turn on debugging.
00103      *   0.4:  No longer uses a temp file to compress the output. Should speed
00104      *      thing up a bit and reduce wear on your hard disk. Also test if
00105      *      the http headers have been sent.
00106      *   0.31: Made a small change to the tempnam() line to hopefully be more
00107      *      portable.
00108      *   0.3:  Added code for the 'x-gzip'. This is untested, I don't know of
00109      *      any browser that uses it but the RFC said to look out for it.
00110      *   0.2:  Checks for 'gzip' in the Accept-Encoding header
00111      *   0.1:  First working version.
00112      *
00113      * Thanks To (Suggestions and stuff):
00114      *   ?@boas.anthro.mnsu.edu  http://php.net/manual/function.gzcompress.php
00115      *   Kaoslord    <kaoslord@chaos-productions.com>
00116      *   Michael R. Gile      <gilem@wsg.net>
00117      *   Christian Hamm    <chh@admaster.de>
00118      *
00119      * The most recent version is available at:
00120      *   http://Leknor.com/code/
00121      *
00122      */
00123 
00124     var $_version = 0.66; // Version of the gzip_encode class
00125 
00126     var $level;      // Compression level
00127     var $encoding;   // Encoding type
00128     var $crc;     // crc of the output
00129     var $size;    // size of the uncompressed content
00130     var $gzsize;  // size of the compressed content
00131 
00132     /*
00133      * gzip_encode constructor - gzip encodes the current output buffer
00134      * if the browser supports it.
00135      *
00136      * Note: all arguments are optionial.
00137      *
00138      * You can specify one of the following for the first argument:
00139      *   0: No compression
00140      *   1: Min compression
00141      *   ...   Some compression (integer from 1 to 9)
00142      *   9: Max compression
00143      *   true: Determin the compression level from the system load. The
00144      *      higher the load the less the compression.
00145      *
00146      * You can specify one of the following for the second argument:
00147      *   true: Don't actully output the compressed form but run as if it
00148      *      had. Used for debugging.
00149      */
00150     function gzip_encode($level = 3, $debug = false, $outputCompressedSizes=0) {
00151    if (!function_exists('gzcompress')) {
00152        trigger_error('gzcompress not found, ' .
00153           'zlib needs to be installed for gzip_encode',
00154           E_USER_WARNING);
00155        return;
00156    }
00157    if (!function_exists('crc32')) {
00158        trigger_error('crc32() not found, ' .
00159           'PHP >= 4.0.1 needed for gzip_encode', E_USER_WARNING);
00160        return;
00161    }
00162    if (headers_sent()) return;
00163    if (connection_status() !== 0) return;
00164    $encoding = $this->gzip_accepted();
00165    if (!$encoding) return;
00166    $this->encoding = $encoding;
00167 
00168    if ($level === true) {
00169        $level = $this->get_complevel();
00170    }
00171    $this->level = $level;
00172 
00173    $contents = ob_get_contents();
00174    if ($contents === false) return;
00175 
00176    $gzdata = "\x1f\x8b\x08\x00\x00\x00\x00\x00"; // gzip header
00177 
00178       // By Kasper Skaarhoj, start
00179    if ($outputCompressedSizes)   {
00180       $contents.=chr(10)."<!-- Compressed, level ".$level.", original size was ".strlen($contents)." bytes. New size is ".strlen(gzcompress($contents, $level))." bytes -->";
00181       $size = strlen($contents); // Must set again!
00182    }
00183       // By Kasper Skaarhoj, end
00184 
00185    $size = strlen($contents);
00186    $crc = crc32($contents);
00187    $gzdata .= gzcompress($contents, $level);
00188    $gzdata = substr($gzdata, 0, strlen($gzdata) - 4); // fix crc bug
00189    $gzdata .= pack("V",$crc) . pack("V", $size);
00190 
00191    $this->size = $size;
00192    $this->crc = $crc;
00193    $this->gzsize = strlen($gzdata);
00194 
00195    if ($debug) {
00196        return;
00197    }
00198 
00199    ob_end_clean();
00200    Header('Content-Encoding: ' . $encoding);
00201    Header('Content-Length: ' . strlen($gzdata));
00202    Header('X-Content-Encoded-By: class.gzip_encode '.$this->_version);
00203 
00204    echo $gzdata;
00205     }
00206 
00207 
00208     /*
00209      * gzip_accepted() - Test headers for Accept-Encoding: gzip
00210      *
00211      * Returns: if proper headers aren't found: false
00212      *          if proper headers are found: 'gzip' or 'x-gzip'
00213      *
00214      * Tip: using this function you can test if the class will gzip the output
00215      *  without actually compressing it yet, eg:
00216      *    if (gzip_encode::gzip_accepted()) {
00217      *       echo "Page will be gziped";
00218      *    }
00219      *  note the double colon syntax, I don't know where it is documented but
00220      *  somehow it got in my brain.
00221      */
00222     function gzip_accepted() {
00223    if (strpos(getenv("HTTP_ACCEPT_ENCODING"), 'gzip') === false) return false;
00224    if (strpos(getenv("HTTP_ACCEPT_ENCODING"), 'x-gzip') === false) {
00225        $encoding = 'gzip';
00226    } else {
00227        $encoding = 'x-gzip';
00228    }
00229 
00230    // Test file type. I wish I could get HTTP response headers.
00231    $magic = substr(ob_get_contents(),0,4);
00232    if (substr($magic,0,2) === '^_') {
00233        // gzip data
00234        $encoding = false;
00235    } else if (substr($magic,0,3) === 'GIF') {
00236        // gif images
00237        $encoding = false;
00238    } else if (substr($magic,0,2) === "\xFF\xD8") {
00239        // jpeg images
00240        $encoding = false;
00241    } else if (substr($magic,0,4) === "\x89PNG") {
00242        // png images
00243        $encoding = false;
00244    } else if (substr($magic,0,3) === 'FWS') {
00245        // Don't gzip Shockwave Flash files. Flash on windows incorrectly
00246        // claims it accepts gzip'd content.
00247        $encoding = false;
00248    } else if (substr($magic,0,2) === 'PK') {
00249        // pk zip file
00250        $encoding = false;
00251    }
00252 
00253    return $encoding;
00254     }
00255 
00256     /*
00257      * get_complevel() - The level of compression we should use.
00258      *
00259      * Returns an int between 0 and 9 inclusive.
00260      *
00261      * Tip: $gzleve = gzip_encode::get_complevel(); to get the compression level
00262      *      that will be used with out actually compressing the output.
00263      *
00264      * Help: if you use an OS other then linux please send me code to make
00265      * this work with your OS - Thanks
00266      */
00267     function get_complevel() {
00268    $uname = posix_uname();
00269    switch ($uname['sysname']) {
00270        case 'Linux':
00271       $cl = (1 - $this->linux_loadavg()) * 10;
00272       $level = (int)max(min(9, $cl), 0);
00273       break;
00274        case 'FreeBSD':
00275       $cl = (1 - $this->freebsd_loadavg()) * 10;
00276       $level = (int)max(min(9, $cl), 0);
00277       break;
00278        default:
00279       $level = 3;
00280       break;
00281    }
00282    return $level;
00283     }
00284 
00285     /*
00286      * linux_loadavg() - Gets the max() system load average from /proc/loadavg
00287      *
00288      * The max() Load Average will be returned
00289      */
00290     function linux_loadavg() {
00291    $buffer = "0 0 0";
00292    $f = fopen("/proc/loadavg","rb");
00293    if (!feof($f)) {
00294        $buffer = fgets($f, 1024);
00295    }
00296    fclose($f);
00297    $load = explode(" ",$buffer);
00298    return max((float)$load[0], (float)$load[1], (float)$load[2]);
00299     }
00300 
00301     /*
00302      * freebsd_loadavg() - Gets the max() system load average from uname(1)
00303      *
00304      * The max() Load Average will be returned
00305      *
00306      * I've been told the code below will work on solaris too, anyone wanna
00307      * test it?
00308      */
00309     function freebsd_loadavg() {
00310    $buffer= `uptime`;
00311    ereg("averag(es|e): ([0-9][.][0-9][0-9]), ([0-9][.][0-9][0-9]), ([0-9][.][0-9][0-9]*)", $buffer, $load);
00312 
00313    return max((float)$load[2], (float)$load[3], (float)$load[4]);
00314     }
00315 }
00316 
00317 ?>

Generated on Sun Oct 3 01:05:45 2004 for TYPO3core 3.7.0 dev by  doxygen 1.3.8-20040913