Skip to main content
Image for post: Yii 2 image optimization

Yii 2 image optimization

An extension for your images

To optimize the images for your website, application or blog is very important for the performances. I recommend to use GIF images if possible and the same size for every images of your posts or articles. That's because It can be much easier to maintain your files and update them in any case. We need to be patient to do a good job but we all will be satisfied at the end. There are many tools and websites that let us to optimize our images online like Optimzilla. But if we have dozen of files and images the job can be very difficult and we cannot optimize our images manually.
Luckily Yii2 is really amazing and we can install a good extension with composer. This extension must work only offline because it's obviously a pure development tool. So let's install it:


composer require ps/image-optimizer

We need to install our image optimizers for our operative systems. I use only png, gif and jpg files so I have installed these the optimizers only for these file extensions: It seems strange because we need to run exe files to optimize our images. Don't worry: the tool will not run any executable files if the optimizer does not exists. Remember to put your vendor directory outside your root path in production or in your shared hosting! This is very important for your security.
Download these extensions and simply copy the executable files in a known directory. In my Windows 10 I have installed Cygwin and I have copied the optimizers in the bin directory C:\cygwin64\bin to make them available. All available optimizers are listed in the github repository of the main project. This is the project with our main dependencies.
Here are my optimizers:

Now we must configure Yii2 to let the framework know about the new extension: I have found this github repository before but the direct installation did not work for me and I have simply copied the controller and the service in my console project.
In your console\config\main.php:


use console\controllers\ImageOptimizeController;
use console\models\ImageOptimizerService;

'controllerMap' => [
      // ...
      'image-optimize' => [
          'class'           => ImageOptimizeController::class,
          'log'             => YII_DEBUG,
          'imageExtensions' => [
              ImageOptimizerService::IMAGE_PNG,
              ImageOptimizerService::IMAGE_JPG,
              ImageOptimizerService::IMAGE_JPEG,
              ImageOptimizerService::IMAGE_GIF,
          ],
          'folders'         => [
              '@frontend/web/img',
              // Other directories with image files must be listed here...
          ],
      ],
  ],

 

You will have your controller:


<?php

namespace console\controllers;

use yii\console\Controller;
use console\models\ImageOptimizerService;

/**
* Class    ImageOptimizeController
* @package MP\ImageOptimize
* @author  Yarmaliuk Mikhail
* @version 1.0
*/
class ImageOptimizeController extends Controller
{
   /**
    * Folders with images for optimizations
    *
    * @var array
    */
   public $folders = [];

   /**
    * Images extensions
    *
    * @var array
    */
   public $imageExtensions = [];

   /**
    * Print log
    *
    * @var bool
    */
   public $log = false;

   /**
    * @var array
    */
   public $optimizerOptions = [];

   /**
    * Optimize images
    *
    * @return void
    */
   public function actionIndex()
   {
       $optimizeService = new ImageOptimizerService($this->folders, $this->imageExtensions, $this->optimizerOptions);
       $optimizeService->log = $this->log;
       $optimizeService->handle();
   }
}

And the service:


<?php

namespace console\models;

use Yii;
use yii\base\ErrorException;
use ImageOptimizer\OptimizerFactory;

/**
* Class    ImageOptimizerService
* @author  Yarmaliuk Mikhail
* @version 1.0
*/
class ImageOptimizerService
{
   const IMAGE_GIF  = 'gif';
   const IMAGE_JPG  = 'jpg';
   const IMAGE_PNG  = 'png';
   const IMAGE_JPEG = 'jpeg';

   /**
    * Folders with images for optimizations
    *
    * @var array
    */
   public $folders = [];

   /**
    * Images extensions
    *
    * @var array
    */
   public $imageExtensions = [];

   /**
    * @var \ImageOptimizer\Optimizer|NULL
    */
   private $optimizer = NULL;

   /**
    * Print log
    *
    * @var bool
    */
   public $log = false;

   /**
    * ImageOptimizerService constructor.
    *
    * @param array $folders
    * @param array $imageExtensions
    * @param array $optimizerOptions
    *
    * @return void
    */
   public function __construct(array $folders = [], array $imageExtensions = [self::IMAGE_JPG, self::IMAGE_PNG, self::IMAGE_JPEG, self::IMAGE_GIF], array $optimizerOptions = [])
   {
       // Set memory limit
       ini_set('memory_limit', '1024M');
       ini_set('max_execution_time', 0);

       $this->folders         = $folders;
       $this->imageExtensions = $imageExtensions;

       $factory         = new OptimizerFactory($optimizerOptions);
       $this->optimizer = $factory->get();
   }

   /**
    * Optimize images
    *
    * @return void
    */
   public function handle()
   {
       if (!empty($this->imageExtensions) && !empty($this->folders)) {
           foreach ($this->folders as $key => $folder) {
               $options = [];

               if (is_array($folder)) {
                   $options = $folder;
                   $folder  = $key;
               }

               $this->optimizeFolder($folder, $options);
           }
       }
   }

   /**
    * Optimize folder
    *
    * @param string $folder
    * @param array  $options
    *
    * @return void
    */
   public function optimizeFolder($folder, array $options = [])
   {
       $path = Yii::getAlias(rtrim($folder, " \t\n\r\0\x0B/"));

       if (!$this->checkExeclude($folder, $options)) {
           return;
       }

       if (is_dir($path)) {
           $folders = array_filter(glob($path . '/*'), 'is_dir');
           $images  = glob($path . '/*.{' . implode(',', $this->imageExtensions) . '}', GLOB_BRACE);

           // Optimize image in folder
           foreach ($images as $image) {
               $this->optimizeImage($image);
           }

           // Optimize folders
           foreach ($folders as $f) {
               $this->optimizeFolder($f, $options);
           }
       } elseif (is_file($path) && file_exists($path)) {
           $this->optimizeImage($path);
       }
   }

   /**
    * Optimize image
    *
    * @param string $pathToImage
    */
   public function optimizeImage($pathToImage)
   {
       if ($this->log) {
           echo PHP_EOL . 'Optimize: ' . $pathToImage;
       }

       try {
           $this->optimizer->optimize(Yii::getAlias($pathToImage));
       } catch (ErrorException $e) {
           if ($this->log) {
               echo PHP_EOL . 'Failed optimize: ' . $pathToImage;
           }
       }
   }

   /**
    * Check execlude folder/file
    *
    * @param string $path
    * @param array  $options
    *
    * @return bool
    */
   private function checkExeclude($path, array $options)
   {
       if (isset($options['execlude'])) {
           foreach ($options['execlude'] as $execludeFolder) {
               $execludeFolder = rtrim($execludeFolder, " \t\n\r\0\x0B/");
               $path           = rtrim($path, " \t\n\r\0\x0B/");

               if (stripos(Yii::getAlias($execludeFolder), Yii::getAlias($path)) !== false) {
                   return false;
               }
           }
       }

       return true;
   }
}

It's time to launch our optimizer:


php yii image-optimize

Conclusions

An excellent tool was implemented to do an excellent work and save a LOT of time for you. I hope it will be useful for someone. Enjoy!

Share this post

I am tracking this website anonymously to improve the quality and monitor the network traffic.