Imagickをちょっと良い感じに使いたい。

**！実施環境を見てもらえば分かると思いますが、いろいろ古いです。**

## 実施環境

* OS: Ubuntu 14.04.5 LTS
* php: 5.5.9
* php5-imagick: 3.1.2-1build1

## 本題

こんな感じのclassを作る（とりあえず雰囲気が伝われば幸いです）。

```php:ImagickHelper.php
<?php
class ImagickHelper {
  private $imagick = null;
  /**
   *
   * @param $file
   * @return
   * @throw Exception
   */
  public function begin($file) {
    if ($file === null) {
      throw new Exception('$file must not be null.');
    }
    try {
      if ($this->imagick === null) {
        $this->imagick = new Imagick();
      } else {
        $this->imagick->clear();
      }
      $this->imagick->readImage($file);
      return $this;
    } catch(ImagickException $e) {
      throw new Exception($e->getMessage().' file: '.basename(__FILE__).' line: '.__LINE__);
    }
  }
  /**
   * JPEG画像の回転補正（ExifのOrientation対応）
   * @param $file
   * @return
   * @throw Exception
   */
  public function adjustExifOrientation() {
    $this->verify();
    try {
      if (strtolower($this->imagick->getImageFormat()) != 'jpeg') {
        return $this;
      }
      $orientation = $this->imagick->getImageOrientation();
      $isRotated = true;
      switch($orientation) {
        case Imagick::ORIENTATION_BOTTOMRIGHT:
          $this->imagick->rotateimage('none', 180);
          break;
        case Imagick::ORIENTATION_RIGHTTOP:
          $this->imagick->rotateimage('none', 90);
          break;
        case Imagick::ORIENTATION_LEFTBOTTOM:
          $this->imagick->rotateimage('none', -90);
          break;
        default:
          $isRotated = false;
      }
      if ($isRotated) {
        $this->imagick->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
      }
      return $this;
    } catch(ImagickException $e) {
      throw new Exception($e->getMessage().' file: '.basename(__FILE__).' line: '.__LINE__);
    }
  }
  /**
   *
   * @param
   * @param
   * @param
   * @return
   * @throw Exception
   */
  public function reduce($width, $height, $aspectFit = true) {
    $this->verify();
    try {
      // オリジナルの画像サイズ
      $imgWidth = $this->imagick->getImageWidth();
      $imgHeight = $this->imagick->getImageHeight();
      // 画像が指定サイズより大きい場合
      if ($imgWidth > $width || $imgHeight > $height) {
        $newWidth = $width;
        $newHeight = $height;
        if ($aspectFit) {
          if (($width / $height) > ($imgWidth / $imgHeight)) {
            $newWidth = 0;
          } else {
            $newHeight = 0;
          }
        }
        $this->imagick->resizeImage($newWidth, $newHeight, imagick::FILTER_LANCZOS, 1);
      }
      return $this;
    } catch(ImagickException $e) {
      throw new Exception($e->getMessage().' file: '.basename(__FILE__).' line: '.__LINE__);
    }
  }
  /**
   *
   * @param　$fn 品質を返す関数（引数に画像サイズ（byte）を取る）
   * @return
   * @throw Exception
   */
  public function compress(callable $fn = null) {
    $this->verify();
    try {
      $quality = is_callable($fn) ? call_user_func($fn, strlen($this->imagick->getImageBlob())) : 0;
      $comp = call_user_func(function($format) {
        switch($format) {
          case 'jpeg':
            return imagick::COMPRESSION_JPEG;
            break;
          default:
            return imagick::COMPRESSION_UNDEFINED;
        }
      }, strtolower($this->imagick->getImageFormat()));
      $this->imagick->setImageCompression($comp);
      if (!$this->imagick->setImageCompressionQuality($quality)) {
        throw new Exception("Failed to set image compression quality. quality: {$quality}");
      }
      return $this;
    } catch(ImagickException $e) {
      throw new Exception($e->getMessage().' file: '.basename(__FILE__).' line: '.__LINE__);
    }
  }
  /**
   *
   * @param　$quality 品質
   * @return
   * @throw Exception
   */
  public function cropThumbnail($width, $height) {
    if ($width === null || $width === null) {
      throw new Exception('$width and $height must not be null.');
    }
    $this->verify();
    try {
      if (!$this->imagick->cropThumbnailImage($width, $height)) {
        throw new Exception("Failed to crop thumbnail image. wdith: {$width} height: {$height}");
      }
      return $this;
    } catch(ImagickException $e) {
      throw new Exception($e->getMessage().' file: '.basename(__FILE__).' line: '.__LINE__);
    }
  }
  /**
   *
   * @param $output 出力先（ファイル名まで）
   * @return
   * @throw Exception
   */
  public function write($output = null) {
    $this->verify();
    try {
      $this->imagick->writeImage($output);
      return $this;
    } catch(ImagickException $e) {
      throw new Exception($e->getMessage().' file: '.basename(__FILE__).' line: '.__LINE__);
    }
  }
  /**
   *
   * @return
   * @throw Exception
   */
  public function clear() {
    $this->verify();
    try {
      $this->imagick->clear();
      return $this;
    } catch(ImagickException $e) {
      throw new Exception($e->getMessage().' file: '.basename(__FILE__).' line: '.__LINE__);
    }
  }
  /**
   *
   * @return $this
   */
  public function discard() {
    $this->imagick = null;
    return $this;
  }
  /**
   *
   * @throw Exception
   */
  private function verify() {
    if ($this->imagick === null) {
      throw new Exception('To start the imagick process, call begin() at first.');
    }
  }
}
```

使う側はこんな感じ。

```php
/**
 * 画像の加工処理
 */
private function imageProcessing($file) {
  $im = new ImagickHelper();
  try {
    // EXIF Orientationを調整して、縮小して、圧縮して書き出す
    $im->begin($file)->adjustExifOrientation()
    ->reduce($this->image_max_width, $this->image_max_height)
    ->compress(function($imageLength) {
      // 1MB以上だったら品質を80%に設定
      switch ($imageLength) {
        case 1000000 <= $imageLength:
          return 80;
          break;
        default:
          return 0;
      }
    })->write($file)->clear();
    return true;
  } catch(Exception $e) {
    echo "An error occurred. {$e->getMessage()}";
    return false;
  } finally {
    $im->discard();
  }
}
```