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 ''; } /** * Magic method to return the file path * @return string */ public function __toString() { return $this->getCachedImagePath(true); } }