Skip to content

Instantly share code, notes, and snippets.

@joshfreemanIO
Created March 28, 2013 04:31
Show Gist options
  • Save joshfreemanIO/5260646 to your computer and use it in GitHub Desktop.
Save joshfreemanIO/5260646 to your computer and use it in GitHub Desktop.
<?php
class gzipper
{
public function gzipFile($file,$destination_directory = '')
{
try
{
$this->validateFile($file);
if(empty($destination_directory)) $destination_directory = $this->setDestinationDirectory($file);
$this->validateDestination($destination_directory);
$fileinfo = pathinfo($file);
$filegz = $this->normalizeDirectoryPath($destination_directory) . $fileinfo['basename'] . '.gz';
file_put_contents($filegz, gzencode(file_get_contents($file),9));
chmod($filegz, fileperms($file));
$this->validateCompressedOutput($filegz);
}
catch(Exception $e)
{
gzipperException::returnError($e);
}
return $filegz;
}
public function gunzipFile($file,$destination_directory = '')
{
try
{
$this->validateFile($file);
$this->validateAsDecompressableFile($file);
if(empty($destination_directory)) $destination_directory = $this->setDestinationDirectory($file);
$this->validateDestination($destination_directory);
$fileinfo = pathinfo($file);
$fileuz = $this->normalizeDirectoryPath($destination_directory) . $fileinfo['filename'];
file_put_contents($fileuz, $this->gzdecode(file_get_contents($file),9));
chmod($fileuz, fileperms($file));
$this->validateDecompressedOutput($fileuz);
}
catch(Exception $e)
{
gzipperException::returnError($e);
}
return $fileuz;
}
public function gzipStringToFile($destination_directory,$file_name,$string)
{
try
{
if(empty($string)) throw new gzipperException(gzipperError::STRINGEMPTY);
if(empty($destination_directory)) throw new gzipperException(gzipperError::DIRECTORYNOTSET);
$this->validateDestination($destination_directory);
$filegz = $this->normalizeDirectoryPath($destination_directory) . $file_name . '.gz';
echo $string;
file_put_contents($filegz, gzencode($string,9));
chmod($filegz, 0664);
$this->validateCompressedOutput($filegz);
} catch (Exception $e)
{
gzipperException::returnError($e);
}
}
private function setDestinationDirectory($file)
{
$fileinfo = pathinfo($file);
return $fileinfo['dirname'];
}
private function normalizeDirectoryPath($directory)
{
$lastcharacter = strrev($directory);
$lastcharacter = $lastcharacter{0};
if($lastcharacter !== DIRECTORY_SEPARATOR)
{
return $directory . DIRECTORY_SEPARATOR;
}
else
{
return $directory;
}
}
private function validateAsDecompressableFile($file)
{
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if(finfo_file($finfo, $file) !== "application/x-gzip") throw new gzipperException(gzipperError::NOTGZIPFILE);
$fileinfo = pathinfo($file);
if($fileinfo['extension'] !== 'gz') throw new gzipperException(gzipperError::NOGZEXTENSION);
}
private static function validateFile($file)
{
if(empty($file)) throw new gzipperException(gzipperError::FILENOTSET);
if(!file_exists($file)) throw new gzipperException(gzipperError::FILENOTFOUND);
if(!is_writable($file)) throw new gzipperException(gzipperError::FILENOTREADABLE);
}
private static function validateDestination($destination_directory)
{
if(empty($destination_directory)) throw new gzipperException(gzipperError::DIRECTORYNOTSET);
if(!file_exists($destination_directory))
{
if(!mkdir($destination_directory,0755,true)) throw new gzipperException(gzipperError::DIRECTORYNOTCREATABLE);
}
else
{
if(!is_writable($destination_directory)) throw new gzipperException(gzipperError::DIRECTORYNOTWRITABLE);
}
}
private static function validateCompressedOutput($destination_file)
{
if(!file_exists($destination_file)) throw new gzipperException(gzipperError::GZIPFAILED);
}
private function validateDecompressedOutput($destination_file)
{
echo $destination_file;
if(!file_exists($destination_file)) throw new gzipperException(gzipperError::GUNZIPFAILED);
}
private function gzdecode($data) {
$len = strlen($data);
if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
return null; // Not GZIP format (See RFC 1952)
}
$method = ord(substr($data,2,1)); // Compression method
$flags = ord(substr($data,3,1)); // Flags
if ($flags & 31 != $flags) {
// Reserved bits are set -- NOT ALLOWED by RFC 1952
return null;
}
// NOTE: $mtime may be negative (PHP integer limitations)
$mtime = unpack("V", substr($data,4,4));
$mtime = $mtime[1];
$xfl = substr($data,8,1);
$os = substr($data,8,1);
$headerlen = 10;
$extralen = 0;
$extra = "";
if ($flags & 4) {
// 2-byte length prefixed EXTRA data in header
if ($len - $headerlen - 2 < 8) {
return false; // Invalid format
}
$extralen = unpack("v",substr($data,8,2));
$extralen = $extralen[1];
if ($len - $headerlen - 2 - $extralen < 8) {
return false; // Invalid format
}
$extra = substr($data,10,$extralen);
$headerlen += 2 + $extralen;
}
$filenamelen = 0;
$filename = "";
if ($flags & 8) {
// C-style string file NAME data in header
if ($len - $headerlen - 1 < 8) {
return false; // Invalid format
}
$filenamelen = strpos(substr($data,8+$extralen),chr(0));
if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
return false; // Invalid format
}
$filename = substr($data,$headerlen,$filenamelen);
$headerlen += $filenamelen + 1;
}
$commentlen = 0;
$comment = "";
if ($flags & 16) {
// C-style string COMMENT data in header
if ($len - $headerlen - 1 < 8) {
return false; // Invalid format
}
$commentlen = strpos(substr($data,8+$extralen+$filenamelen),chr(0));
if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
return false; // Invalid header format
}
$comment = substr($data,$headerlen,$commentlen);
$headerlen += $commentlen + 1;
}
$headercrc = "";
if ($flags & 1) {
// 2-bytes (lowest order) of CRC32 on header present
if ($len - $headerlen - 2 < 8) {
return false; // Invalid format
}
$calccrc = crc32(substr($data,0,$headerlen)) & 0xffff;
$headercrc = unpack("v", substr($data,$headerlen,2));
$headercrc = $headercrc[1];
if ($headercrc != $calccrc) {
return false; // Bad header CRC
}
$headerlen += 2;
}
// GZIP FOOTER - These be negative due to PHP's limitations
$datacrc = unpack("V",substr($data,-8,4));
$datacrc = $datacrc[1];
$isize = unpack("V",substr($data,-4));
$isize = $isize[1];
// Perform the decompression:
$bodylen = $len-$headerlen-8;
if ($bodylen < 1) {
// This should never happen - IMPLEMENTATION BUG!
return null;
}
$body = substr($data,$headerlen,$bodylen);
$data = "";
if ($bodylen > 0) {
switch ($method) {
case 8:
// Currently the only supported compression method:
$data = gzinflate($body);
break;
default:
// Unknown compression method
return false;
}
} else {
// I'm not sure if zero-byte body content is allowed.
// Allow it for now... Do nothing...
}
// Verifiy decompressed size and CRC32:
// NOTE: This may fail with large data sizes depending on how
// PHP's integer limitations affect strlen() since $isize
// may be negative for large sizes.
if ($isize != strlen($data) || crc32($data) != $datacrc) {
// Bad format! Length or CRC doesn't match!
return false;
}
return $data;
}
}
class gzipperError
{
const FILENOTSET = 1401;
const FILENOTFOUND = 1404;
const FILENOTREADABLE = 1403;
const DIRECTORYNOTSET = 2401;
const DIRECTORYNOTCREATABLE = 2404;
const DIRECTORYNOTWRITABLE = 2403;
CONST GZIPFAILED = 9001;
CONST GUNZIPFAILED = 9002;
CONST NOTGZIPFILE = 9003;
CONST NOTGZIPEXTENSION = 9004;
CONST STRINGEMPTY = 3000;
public static function getErrorMessage($error_code)
{
switch ($error_code) {
case self::FILENOTSET:
return "No file was specified";
break;
case self::DIRECTORYNOTSET:
return "No directory was specified";
break;
case self::FILENOTFOUND:
return "The file cannot be found";
break;
case self::FILENOTREADABLE:
return "The file cannot be opened";
break;
case self::DIRECTORYNOTCREATABLE:
return "The destination directory does not exist and could not be created";
break;
case self::DIRECTORYNOTWRITABLE:
return "The destination directory exists, but is not writable";
break;
case self::GZIPFAILED:
return "Expected to find a gzipped file--however, none exists.";
break;
case self::GUNZIPFAILED:
return "Expected to find an unzipped file--however, none exists.";
break;
case self::NOTGZIPFILE:
return "The file specified for decompression is not of the correct MIME/Content-Type (application/x-gzip)";
break;
case self::NOTGZIPEXTENSION:
return "The file specified for decompression does not have the correct .gz extension";
break;
case self::STRINGEMPTY:
return "The string specified for compression is empty. Seriously, don't compress that";
break;
default:
return "Unknown error occured";
break;
}
}
}
class gzipperException extends Exception
{
public function __construct($code = 0, Exception $previous = null) {
parent::__construct(gzipperError::getErrorMessage($code), $code, $previous);
}
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
public static function returnError($e)
{
echo "<pre>";
echo $e->__toString() . "<br>";
echo $e->getTraceAsString() . "<br>";
echo "</pre>";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment