Completed
Push — master ( 7ad84d...d34fd3 )
by Renato
05:15
created

ImagineImagick   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 261
rs 8.8
wmc 45
lcom 1
cbo 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A execute() 0 17 3
A filesize() 0 4 1
B resize() 0 24 9
A opacity() 0 12 3
A watermark() 0 19 4
C watermarkCanvas() 0 66 16
A isImage() 0 4 3
A crop() 0 6 1
A encode() 0 4 1
A save() 0 10 3

How to fix   Complexity   

Complex Class

Complex classes like ImagineImagick often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ImagineImagick, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace NwLaravel\FileStorage;
4
5
use Imagick;
6
7
class ImagineImagick implements Imagine
8
{
9
    /**
10
     * @var Imagick
11
     */
12
    protected $image;
13
14
    /**
15
     * Construct
16
     *
17
     * @param string $path
18
     */
19
    public function __construct($path)
20
    {
21
        $this->image = new Imagick($path);
22
    }
23
24
    /**
25
     * Execute in Canvas
26
     *
27
     * @param \Closure $callback
28
     *
29
     * @return Imagine
30
     */
31
    protected function execute($callback)
32
    {
33
        $format = strtolower($this->image->getImageFormat());
34
35
        if ($format == 'gif') {
36
          $this->image = $this->image->coalesceImages();
37
          do {
38
              $callback($this->image);
39
          } while ($this->image->nextImage());
40
41
          $this->image = $this->image->deconstructImages();
42
        } else {
43
          $callback($this->image);
44
        }
45
46
        return $this;
47
    }
48
49
    /**
50
     * Filesize
51
     *
52
     * @return int
53
     */
54
    public function filesize()
55
    {
56
        return $this->image->getImageLength();
57
    }
58
59
    /**
60
     * Define Resize
61
     *
62
     * @param int     $maxWidth
63
     * @param int     $maxHeight
64
     * @param boolean $force
65
     *
66
     * @return Imagine
67
     */
68
    public function resize($maxWidth, $maxHeight, $force = false)
69
    {
70
        return $this->execute(function ($image) use ($maxWidth, $maxHeight, $force) {
71
            $width = $maxWidth;
72
            $height = $maxHeight;
73
            $imageWidth = $image->getImageWidth();
74
            $imageHeight = $image->getImageHeight();
75
76
            if(($maxWidth && $imageWidth > $maxWidth) || !$maxHeight) {
77
                $height = floor(($imageHeight/$imageWidth)*$maxWidth);
78
                if (!$height) {
79
                    $height = $imageHeight;
80
                }
81
82
            } else if(($maxHeight && $imageHeight > $maxHeight) || !$maxWidth) {
83
                $width = floor(($imageWidth/$imageHeight)*$maxHeight);
84
                if (!$width) {
85
                    $width = $imageWidth;
86
                }
87
            }
88
89
            $image->scaleImage($width, $height, !$force);
90
        });
91
    }
92
93
    /**
94
     * Opacity
95
     *
96
     * @return Imagine
97
     */
98
    public function opacity($opacity)
99
    {
100
        $opacity = intval($opacity);
101
102
        if ($opacity > 0 && $opacity < 100) {
103
            $this->execute(function ($image) use ($opacity) {
104
                $image->setImageOpacity($opacity/100);
105
            });
106
        }
107
108
        return $this;
109
    }
110
111
    /**
112
     * Watermark
113
     *
114
     * @param string  $path
115
     * @param string  $position
116
     * @param integer $opacity
117
     *
118
     * @return Imagine
119
     */
120
    public function watermark($path, $position = 'center', $opacity = null)
121
    {
122
        if ($this->isImage($path)) {
123
            $watermark = new \Imagick($path);
124
125
            $opacity = intval($opacity);
126
            if ($opacity > 0 && $opacity < 100) {
127
                $watermark->setImageOpacity($opacity/100);
128
            }
129
130
            $self = $this;
131
132
            $this->execute(function ($image) use ($watermark, $position, $self) {
133
                $self->watermarkCanvas($image, $watermark, $position);
134
            });
135
        }
136
137
        return $this;
138
    }
139
140
    protected function watermarkCanvas(Imagick $image, Imagick $watermark, $position = 'center')
141
    {
142
        // how big are the images?
143
        $iWidth = $image->getImageWidth();
144
        $iHeight = $image->getImageHeight();
145
        $wWidth = $watermark->getImageWidth();
146
        $wHeight = $watermark->getImageHeight();
147
148
        if ($iHeight < $wHeight || $iWidth < $wWidth) {
149
          // resize the watermark
150
          $watermark->scaleImage($iWidth, $iHeight, true);
151
152
          // get new size
153
          $wWidth = $watermark->getImageWidth();
154
          $wHeight = $watermark->getImageHeight();
155
        }
156
157
        $xOffset = 0;
0 ignored issues
show
Unused Code introduced by
$xOffset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
158
        $yOffset = 0;
0 ignored issues
show
Unused Code introduced by
$yOffset is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
159
160
        switch ($position) {
161
          case 'center':
162
          default:
163
            $x = ($iWidth - $wWidth) / 2;
164
            $y = ($iHeight - $wHeight) / 2;
165
            break;
166
          case 'topLeft':
0 ignored issues
show
Unused Code introduced by
case 'topLeft': $x =... = $yOffset; break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
167
            $x = $xOffset;
168
            $y = $yOffset;
169
            break;
170
          case 'top':
171
          case 'topCenter':
172
            $x = ($iWidth - $wWidth) / 2;
173
            $y = $yOffset;
174
            break;
175
          case 'topRight':
176
            $x = $iWidth - $wWidth - $xOffset;
177
            $y = $yOffset;
178
            break;
179
          case 'right':
180
          case 'rightCenter':
181
            $x = $iWidth - $wWidth - $xOffset;
182
            $y = ($iHeight - $wHeight) / 2;
183
            break;
184
          case 'bottomRight':
185
            $x = $iWidth - $wWidth - $xOffset;
186
            $y = $iHeight - $wHeight - $yOffset;
187
            break;
188
          case 'bottom':
189
          case 'bottomCenter':
190
            $x = ($iWidth - $wWidth) / 2;
191
            $y = $iHeight - $wHeight - $yOffset;
192
            break;
193
          case 'bottomLeft':
194
            $x = $xOffset;
195
            $y = $iHeight - $wHeight - $yOffset;
196
            break;
197
          case 'left':
198
          case 'leftCenter':
199
            $x = $xOffset;
200
            $y = ($iHeight - $wHeight) / 2;
201
            break;
202
        }
203
204
        $image->compositeImage($watermark, Imagick::COMPOSITE_OVER, $x, $y);
205
    }
206
207
    /**
208
     * Is Image
209
     *
210
     * @param string $path
211
     *
212
     * @return boolean
213
     */
214
    protected function isImage($path)
215
    {
216
        return (bool) ($path && is_file($path) && strpos(mime_content_type($path), 'image/')===0);
217
    }
218
219
    /**
220
     * Crop
221
     *
222
     * @param integer $width
223
     * @param integer $height
224
     * @param integer $x
225
     * @param integer $y
226
     *
227
     * @return binary
228
     */
229
    public function crop($width, $height, $x, $y)
230
    {
231
        return $this->execute(function ($image) use ($width, $height, $x, $y) {
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->execute(fu..., $height, $x, $y); }); (NwLaravel\FileStorage\Imagine) is incompatible with the return type declared by the interface NwLaravel\FileStorage\Imagine::crop of type NwLaravel\FileStorage\binary.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
232
            $image->cropImage($width, $height, $x, $y);
233
        });
234
    }
235
236
    /**
237
     * Encode
238
     *
239
     * @param string  $format
240
     * @param integer $quality
241
     *
242
     * @return binary
243
     */
244
    public function encode($format = null, $quality = null)
245
    {
246
        return $this->image->getImagesBlob();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->image->getImagesBlob(); (string) is incompatible with the return type declared by the interface NwLaravel\FileStorage\Imagine::encode of type NwLaravel\FileStorage\binary.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
247
    }
248
249
    /**
250
     * Save
251
     *
252
     * @param string  $path
253
     * @param integer $quality
254
     *
255
     * @return binary
256
     */
257
    public function save($path, $quality = null)
258
    {
259
        $quality = intval($quality);
260
        if ($quality > 0 && $quality <= 100) {
261
            $this->image->setImageCompressionQuality($quality);
262
        }
263
        $this->image->writeImage($path);
264
265
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (NwLaravel\FileStorage\ImagineImagick) is incompatible with the return type declared by the interface NwLaravel\FileStorage\Imagine::save of type NwLaravel\FileStorage\binary.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
266
    }
267
}
268