327 lines
9.3 KiB
PHP
327 lines
9.3 KiB
PHP
<?php namespace ToughDeveloper\ImageResizer\Classes;
|
|
|
|
use ToughDeveloper\ImageResizer\Models\Settings;
|
|
use October\Rain\Database\Attach\File;
|
|
use Tinify\Tinify;
|
|
use Tinify\Source;
|
|
|
|
class Image
|
|
{
|
|
/**
|
|
* File path of image
|
|
*/
|
|
protected $filePath;
|
|
|
|
/**
|
|
* Image Resizer Settings
|
|
*/
|
|
protected $settings;
|
|
|
|
/**
|
|
* File Object
|
|
*/
|
|
protected $file;
|
|
|
|
/**
|
|
* Options Array
|
|
*/
|
|
protected $options;
|
|
|
|
/**
|
|
* Thumb filename
|
|
*/
|
|
protected $thumbFilename;
|
|
|
|
public function __construct($filePath = false)
|
|
{
|
|
// Settings are needed often, so offset to variable
|
|
$this->settings = Settings::instance();
|
|
|
|
// Create a new file object
|
|
$this->file = new File;
|
|
|
|
if ($filePath instanceof File) {
|
|
$this->filePath = $filePath->getLocalPath();
|
|
return;
|
|
}
|
|
|
|
$this->filePath = (file_exists($filePath))
|
|
? $filePath
|
|
: $this->parseFileName($filePath);
|
|
}
|
|
|
|
/**
|
|
* Resizes an Image
|
|
*
|
|
* @param integer $width The target width
|
|
* @param integer $height The target height
|
|
* @param array $options The options
|
|
*
|
|
* @return string
|
|
*/
|
|
public function resize($width = false, $height = false, $options = [])
|
|
{
|
|
// Parse the default settings
|
|
$this->options = $this->parseDefaultSettings($options);
|
|
|
|
// Not a file? Display the not found image
|
|
if (!is_file($this->filePath)) {
|
|
return $this->notFoundImage($width, $height);
|
|
}
|
|
|
|
// If extension is auto, set the actual extension
|
|
if (strtolower($this->options['extension']) == 'auto') {
|
|
$this->options['extension'] = pathinfo($this->filePath)['extension'];
|
|
}
|
|
|
|
// Set a disk name, this enables caching
|
|
$this->file->disk_name = $this->diskName();
|
|
|
|
// Set the thumbfilename to save passing variables to many functions
|
|
$this->thumbFilename = $this->getThumbFilename($width, $height);
|
|
|
|
// If the image is cached, don't try resized it.
|
|
if (! $this->isImageCached()) {
|
|
// Set the file to be created from another file
|
|
$this->file->fromFile($this->filePath);
|
|
|
|
// Resize it
|
|
$thumb = $this->file->getThumb($width, $height, $this->options);
|
|
|
|
// Not a gif file? Compress with tinyPNG
|
|
if ($this->isCompressionEnabled()) {
|
|
$this->compressWithTinyPng();
|
|
}
|
|
|
|
// Touch the cached image with the original mtime to align them
|
|
touch($this->getCachedImagePath(), filemtime($this->filePath));
|
|
|
|
$this->deleteTempFile();
|
|
}
|
|
|
|
// Return the URL
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Gets the path for the thumbnail
|
|
* @return string
|
|
*/
|
|
public function getCachedImagePath($public = false)
|
|
{
|
|
$filePath = $this->file->getStorageDirectory() . $this->getPartitionDirectory() . $this->thumbFilename;
|
|
|
|
if ($public === true) {
|
|
return url('/storage/app/' . $filePath);
|
|
}
|
|
|
|
return storage_path('app/' . $filePath);
|
|
}
|
|
|
|
protected function deleteTempFile()
|
|
{
|
|
$path = storage_path('app/' . $this->file->getStorageDirectory() . $this->getPartitionDirectory() . $this->file->disk_name);
|
|
if (file_exists($path)) {
|
|
unlink($path);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse the file name to get a relative path for the file
|
|
* This is mostly required for scenarios where a twig filter, e.g. theme has been applied.
|
|
* @return string
|
|
*/
|
|
protected function parseFileName($filePath)
|
|
{
|
|
$path = urldecode(parse_url($filePath, PHP_URL_PATH));
|
|
|
|
// Create array of commonly used folders
|
|
// These will be used to try capture the actual file path to an image without the sub-directory path
|
|
$folders = [
|
|
config('cms.themesPath'),
|
|
config('cms.pluginsPath'),
|
|
config('cms.storage.uploads.path'),
|
|
config('cms.storage.media.path')
|
|
];
|
|
|
|
foreach($folders as $folder)
|
|
{
|
|
if (str_contains($path, $folder))
|
|
{
|
|
$paths = explode($folder, $path, 2);
|
|
return base_path($folder . end($paths));
|
|
}
|
|
}
|
|
|
|
return base_path($path);
|
|
}
|
|
|
|
/**
|
|
* Works out the default settings
|
|
* @return string
|
|
*/
|
|
protected function parseDefaultSettings($options = [])
|
|
{
|
|
if (!isset($options['mode']) && $this->settings->default_mode) {
|
|
$options['mode'] = $this->settings->default_mode;
|
|
}
|
|
if (!isset($options['offset']) && is_int($this->settings->default_offset_x) && is_int($this->settings->default_offset_y)) {
|
|
$options['offset'] = [$this->settings->default_offset_x, $this->settings->default_offset_y];
|
|
}
|
|
if (!isset($options['extension']) && $this->settings->default_extension) {
|
|
$options['extension'] = $this->settings->default_extension;
|
|
}
|
|
if (!isset($options['quality']) && is_int($this->settings->default_quality)) {
|
|
$options['quality'] = $this->settings->default_quality;
|
|
}
|
|
if (!isset($options['sharpen']) && is_int($this->settings->default_sharpen)) {
|
|
$options['sharpen'] = $this->settings->default_sharpen;
|
|
}
|
|
if (!isset($options['compress'])) {
|
|
$options['compress'] = true;
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* Creates a unique disk name for an image
|
|
* @return string
|
|
*/
|
|
protected function diskName()
|
|
{
|
|
$diskName = $this->filePath;
|
|
|
|
// Ensures a unique filepath when tinypng compression is enabled
|
|
if ($this->isCompressionEnabled()) {
|
|
$diskName .= 'tinypng';
|
|
}
|
|
|
|
return md5($diskName);
|
|
}
|
|
|
|
/**
|
|
* Serves a not found image
|
|
* @return string
|
|
*/
|
|
protected function notFoundImage($width, $height)
|
|
{
|
|
// Have we got a custom not found image? If so, serve this.
|
|
if ($this->settings->not_found_image) {
|
|
$imagePath = base_path() . config('cms.storage.media.path') . $this->settings->not_found_image;
|
|
}
|
|
|
|
// If we do not have an existing custom not found image, use the default from this plugin
|
|
if (!isset($imagePath) || !file_exists($imagePath)) {
|
|
$imagePath = plugins_path('toughdeveloper/imageresizer/assets/default-not-found.jpg');
|
|
}
|
|
|
|
// Create a new Image object to resize
|
|
$file = new Self($imagePath);
|
|
|
|
// Return in the specified dimensions
|
|
return $file->resize($width, $height, [
|
|
'mode' => 'crop'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Compresses a png image using tinyPNG
|
|
* @return string
|
|
*/
|
|
protected function compressWithTinyPng()
|
|
{
|
|
try {
|
|
Tinify::setKey($this->settings->tinypng_developer_key);
|
|
|
|
$filePath = $this->getCachedImagePath();
|
|
$source = Source::fromFile($filePath);
|
|
$source->toFile($filePath);
|
|
}
|
|
catch (\Exception $e) {
|
|
// Log error - may help debug
|
|
\Log::error('Tiny PNG compress failed', [
|
|
'message' => $e->getMessage(),
|
|
'code' => $e->getCode()
|
|
]);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Checks if the requested resize/compressed image is already cached.
|
|
* Removes the cached image if the original image has a different mtime.
|
|
*
|
|
* @return bool
|
|
*/
|
|
protected function isImageCached()
|
|
{
|
|
// if there is no cached image return false
|
|
if (!is_file($cached_img = $this->getCachedImagePath())) {
|
|
return false;
|
|
}
|
|
|
|
// if cached image mtime match, the image is already cached
|
|
if (filemtime($this->filePath) === filemtime($cached_img)) {
|
|
return true;
|
|
}
|
|
|
|
// delete older cached file
|
|
unlink($cached_img);
|
|
|
|
// generate new cache file
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if image compression is enabled for this image.
|
|
* @return bool
|
|
*/
|
|
protected function isCompressionEnabled()
|
|
{
|
|
return ($this->options['extension'] != 'gif' && $this->settings->enable_tinypng && $this->options['compress']);
|
|
}
|
|
|
|
/**
|
|
* Generates a partition for the file.
|
|
* return /ABC/DE1/234 for an name of ABCDE1234.
|
|
* @param Attachment $attachment
|
|
* @param string $styleName
|
|
* @return mixed
|
|
*/
|
|
protected function getPartitionDirectory()
|
|
{
|
|
return implode('/', array_slice(str_split($this->diskName(), 3), 0, 3)) . '/';
|
|
}
|
|
|
|
/**
|
|
* Generates a thumbnail filename.
|
|
* @return string
|
|
*/
|
|
protected function getThumbFilename($width, $height)
|
|
{
|
|
$width = (integer) $width;
|
|
$height = (integer) $height;
|
|
|
|
return 'thumb__' . $width . '_' . $height . '_' . $this->options['offset'][0] . '_' . $this->options['offset'][1] . '_' . $this->options['mode'] . '.' . $this->options['extension'];
|
|
}
|
|
|
|
/**
|
|
* Render an image tag
|
|
* @return string
|
|
*/
|
|
public function render()
|
|
{
|
|
return '<img src="' . $this . '" />';
|
|
}
|
|
|
|
/**
|
|
* Magic method to return the file path
|
|
* @return string
|
|
*/
|
|
public function __toString()
|
|
{
|
|
return $this->getCachedImagePath(true);
|
|
}
|
|
}
|