| Current Path : /home/digilove/www/110/plugins/system/jch_optimize/jchoptimize/libs/ |
| Current File : /home/digilove/www/110/plugins/system/jch_optimize/jchoptimize/libs/SmushIt.php |
<?php
namespace JchOptimize;
use Exception;
use LogicException;
use InvalidArgumentException;
use curl_init;
use curl_setopt;
use curl_exec;
/**
* Smush.it PHP Library, a simple PHP library for accessing the Yahoo! Smush.it™
* lossless image compressor
* @author Ghislain PHU <contact@ghislainphu.fr>
* @version 1.0
*/
class SmushIt
{
/**
* Flag used to preserve objects with errors
*/
const KEEP_ERRORS = 0x01;
/**
* Flag used to throw exceptions when an error
* occurred
*/
const THROW_EXCEPTION = 0x02;
/**
* Internal flag used to notify that the current
* source is a local file
*/
const LOCAL_ORIGIN = 0x04;
/**
* Internal flag used to notify that the current
* source is a remote file (source is an URL)
*/
const REMOTE_ORIGIN = 0x08;
/**
* The base URL of the Yahoo! Smush.it™ API
*/
const SERVICE_API_URL = "http://www.smushit.com/ysmush.it/ws.php";
/**
* Maximum filesize allowed by Yahoo! Smush.it™ API
*/
const SERVICE_API_LIMIT = 1048576; // 1MB limitation
/**
* Error message
* @access public
* @var string | null
*/
public $error;
/**
* Source URI
* @access public
* @var string | null
*/
public $source;
/**
* Compressed image URL
* @access public
* @var string | null
*/
public $destination;
/**
* Filesize of the source image (in Bytes)
* @access public
* @var int | null
*/
public $sourceSize;
/**
* Filesize of the compressed image (in Bytes)
* @access public
* @var int | null
*/
public $destinationSize;
/**
* Saving percentage
* @access public
* @var float | null
*/
public $savings;
/**
* Effective flags
* @access private
* @var int | null
*/
private $flags = null;
/**
* Array of SmushIt objects
* @access private
* @var array
*/
private $items = array();
/**
* Smush.it constructor
* @access public
* @param array | string $sources List of files to compress
* @param int $flags List of flags
* @return object
* @see SmushIt::KEEP_ERRORS
* @see SmushIt::THROW_EXCEPTION
*/
public function __construct($sources, $flags = null)
{
$this->flags = $flags;
$sources = $this->clean($sources);
if (is_string($sources))
{
if ($this->check($sources))
{
$this->smush();
}
}
else
{
foreach ($sources as $source)
{
$smush = new SmushIt($source, $flags);
$smushResult = $smush->get();
if (!empty($smushResult))
{
$this->items[] = $smushResult[0];
}
}
}
}
/**
* Return the list of SmushIt objects
* @access public
* @return array
* @see SmushIt::$items
*/
public function get()
{
return $this->items;
}
/**
* Sometimes, Yahoo! Smush.it converts files (from gif to png, jpg to png)
* during compression process. This function check for this case, based on
* source and destination extensions.
* @access public
* @return bool
* @throws LogicException
*/
public function hasBeenConverted()
{
if ($this->hasFlag(self::THROW_EXCEPTION) AND ( empty($this->source) OR empty($this->destination)))
{
throw new LogicException($this->source . ': Can\'t compare extensions: source or destination is empty');
}
return mb_strtolower(pathinfo($this->source, PATHINFO_EXTENSION)) !== mb_strtolower(pathinfo($this->destination, PATHINFO_EXTENSION));
}
/**
* Clean the $sources parameter from SmushIt::__construct()
* (flatten array and remove non-string values)
* @access private
* @param string | array $sources List of files to compress
* @return string | array
* @throws InvalidArgumentException
*/
private function clean($sources)
{
if (is_array($sources))
{
$clean = array();
array_walk_recursive($sources,
function($line) use (&$clean)
{
$clean[] = $line;
});
$sources = array_filter(array_map(function($line)
{
if (!empty($line) AND is_string($line))
{
return $line;
}
}, array_unique($clean)));
}
else if (!is_string($sources))
{
$sources = null;
}
if (empty($sources) AND $this->hasFlag(self::THROW_EXCEPTION))
{
throw new InvalidArgumentException($this->source . ': Sources can\'t be empty');
}
return $sources;
}
/**
* Check if source is a readable local file and doesn't exceed filesize limit
* @access private
* @param string $path Location of the current image to compress
* @return bool
* @throws Exception
*/
private function check($path)
{
if ($this->setSource($path) === false)
{
$this->error = "$path is not a valid path";
}
else if ($this->hasFlag(self::LOCAL_ORIGIN))
{
if (!is_readable($path))
{
$this->error = "$path is not readable";
}
else if (filesize($path) > self::SERVICE_API_LIMIT)
{
$this->error = "$path exceeds 1MB size limit";
}
}
if (!empty($this->error))
{
if ($this->hasFlag(self::THROW_EXCEPTION))
{
throw new Exception($this->source . ': ' . $this->error);
}
return false;
}
return true;
}
/**
* Check if the flag $flag is set in the current object
* @access private
* @param int $flag The flag to check
* @return bool
*/
private function hasFlag($flag)
{
return (bool) ($this->flags & $flag);
}
/**
* Set SmushIt::$source and check if it's a local or remote file
* @access private
* @param string $flag The source to set
* @return false | null
*/
private function setSource($source)
{
$this->source = $source;
if (filter_var($this->source, FILTER_VALIDATE_URL) !== false)
{
$this->flags |= self::REMOTE_ORIGIN;
}
else if (file_exists($this->source) AND ! is_dir($this->source))
{
$this->flags |= self::LOCAL_ORIGIN;
}
else
{
return false;
}
}
/**
* Send current source to the API and get response
* @access private
* @throws Exception
*/
private function smush()
{
$handle = curl_init();
curl_setopt($handle, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
$post_file = function_exists('curl_file_create') ? curl_file_create($this->source) : '@' . $this->source;
if ($this->hasFlag(self::LOCAL_ORIGIN))
{
curl_setopt($handle, CURLOPT_URL, self::SERVICE_API_URL);
curl_setopt($handle, CURLOPT_POST, true);
curl_setopt($handle, CURLOPT_POSTFIELDS, array('files' => $post_file));
}
else
{
curl_setopt($handle, CURLOPT_URL, self::SERVICE_API_URL . '?img=' . $this->source);
}
$json = curl_exec($handle);
if ($json === false)
{
if (self::hasFlag(self::THROW_EXCEPTION))
{
throw new Exception($this->source . ': Curl error: ' . curl_error($handle));
}
return;
}
$this->set($json);
}
/**
* Set API response data to the current object
* @access private
* @throws Exception
*/
private function set($json)
{
$response = json_decode($json);
if (empty($response))
{
if (self::hasFlag(self::THROW_EXCEPTION))
{
throw new Exception($this->source . ': Empty JSON response');
}
$this->error = 'Empty JSON response';
return;
}
$this->error = empty($response->error) ? $this->error : $response->error;
$this->destination = empty($response->dest) ? null : $response->dest;
$this->sourceSize = empty($response->src_size) ? null : intval($response->src_size);
$this->destinationSize = empty($response->dest_size) ? null : intval($response->dest_size);
$this->savings = empty($response->percent) ? null : floatval($response->percent);
if (!empty($this->error) AND $this->hasFlag(self::THROW_EXCEPTION))
{
throw new Exception($this->source . ': ' . $this->error);
}
else if (empty($this->error) OR $this->hasFlag(self::KEEP_ERRORS))
{
$this->items[] = $this;
}
}
}