<?php
/**
* Image compression ran tru command line using pngcrush and jpegtran
*
* @author Mitchelle Pascual
*/
class ImageCrusher {
private $imageType;
private $source;
private $destination;
private $crushedFilename;
const TMP_FILENAME_APPEND = '_tmp';
/**
* pngcrush command statement
*
* options:
* -rem alla
* removes all chunks except transparency
* -brute
* tries more than 100 different methods for optimization
* -reduce
* tries to reduce the number of colors if possible
*
* @var const
*/
const PNGCRUSH_CMD = 'pngcrush -rem alla -brute -reduce %s %s';
/**
* jpegtran command statement
*
* options:
* -rem alla
* removes all chunks except transparency
* -brute
* tries more than 100 different methods for optimization
* -reduce
* tries to reduce the number of colors if possible
*
* @var const
*/
const JPEGTRAN_CMD = 'jpegtran -copy none -optimize %s > %s';
/**
* Constructor
*
* @param string $source
* @param string $destination Empty string will create the temporary optimized image then delete the orginal file and rename it back.
* @param string $type Empty string will check for the extension filename from $source or $destination.
*/
public function __construct( $source, $destination = '', $type = '' ) {
$this->source = $source;
if( !empty( $destination ) ) {
$this->destination = $destination;
} else {
$this->crushedFilename = $source . self::TMP_FILENAME_APPEND;
}
$this->setImageType( $type );
}
/**
* Executes the legitimate image to optimize
*/
public function crush() {
self::checkRequirements();
switch( $this->imageType ) {
case 'png':
shell_exec( $this->command( self::PNGCRUSH_CMD ) );
break;
case 'jpg':
shell_exec( $this->command( self::JPEGTRAN_CMD ) );
break;
default:
// no image compression for gif images
break;
}
$this->renameTmpToOrigFilename();
}
/**
* Renames the temporary compressed image to it's original filename
*/
protected function renameTmpToOrigFilename() {
if( !empty( $this->crushedFilename ) ) {
@unlink( $this->source );
@rename( $this->crushedFilename, $this->source );
}
}
/**
* Set the suitable command line statement
*
* @param string $cmdStr
*/
protected function command( $cmdStr ) {
if( !empty( $this->crushedFilename ) ) {
return sprintf( $cmdStr, $this->source, $this->crushedFilename );
} else {
return sprintf( $cmdStr, $this->source, $this->destination );
}
}
/**
* Sets the image type or get the extension filename from source or destination
*
* @param string $imageType
*/
protected function setImageType( $imageType ) {
$type = '';
if( !empty( $imageType ) ) {
$type = $imageType;
} else {
if( !empty( $this->destination ) ) {
$filename = $this->destination;
} else {
$filename = $this->source;
}
$type = strtolower( end( explode( '.' , $filename ) ) );
}
$this->imageType = $type;
}
/**
* Checks if pngcrush and jpegtran are installed
*
* @throws Exception
*/
public function checkRequirements() {
//jpegtran is skipped since it always return null
if( is_null( shell_exec( 'pngcrush ' ) ) ) {
throw new Exception( 'Please install pngcrush and jpegtran first!' );
}
}
/**
* Boolean requirements check
*/
public function isRequirementsInstalled() {
try {
self::checkRequirements();
} catch( Exception $e ) {
return false;
}
return true;
}
/**
* Batch images compression
*
* @param string $directory
* @param boolean $isRecursive
*/
public static function batch( $directory, $isRecursive = false ) {
if( !is_dir( $directory ) ) {
throw new Exception( 'Directory "' . $directory . '" doesn\'t exist.' );
}
self::batchCompress( self::fetchImages( $directory, $isRecursive ) );
}
/**
* Gets all png|jpg images from defined directory
*
* @param string $directory
* @param boolean $isRecursive
*/
protected function fetchImages( $directory, $isRecursive ) {
$images = array();
if( $isRecursive === false ) {
$iterator = new DirectoryIterator( $directory );
foreach ( $iterator as $fileinfo ) {
if ( $fileinfo->isFile() && preg_match( '/\.(png|jpg)$/i', $fileinfo->getFilename() ) ) {
array_push($images, $fileinfo->getPathname() );
}
}
} else {
$iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $directory ) );
while( $iterator->valid() ) {
if (!$iterator->isDot() && preg_match( '/\.(png|jpg)$/i', $iterator->current()->getFilename() ) ) {
array_push($images, $iterator->current()->getPathname() );
}
$iterator->next();
}
}
return $images;
}
/**
* Does the compression of images
*
* @param array $images
*/
protected function batchCompress( $images ) {
foreach( $images as $image ) {
$crusher = new self( $image );
$crusher->crush();
}
}
}
|