Completed
Push — master ( 80eb51...d7d63e )
by Oscar
7s
created

Imagick   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 338
Duplicated Lines 4.14 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 35
Bugs 10 Features 5
Metric Value
wmc 49
c 35
b 10
f 5
lcom 1
cbo 2
dl 14
loc 338
rs 8.5454

21 Methods

Rating   Name   Duplication   Size   Complexity  
A checkCompatibility() 0 4 1
A createFromFile() 0 10 2
A createFromString() 0 8 1
A __construct() 0 18 3
A __destruct() 0 4 1
A flip() 0 6 2
A flop() 0 6 2
A save() 0 16 4
A getImage() 0 4 1
A getString() 0 22 3
A getMimeType() 0 8 2
A getWidth() 0 4 1
A getHeight() 0 4 1
A format() 0 13 3
A resize() 7 18 4
A getCropOffsets() 0 10 2
A crop() 7 19 4
A rotate() 0 8 2
B getCompressed() 0 39 6
A watermark() 0 4 1
A opacity() 0 13 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Imagick 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 Imagick, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Imagecow\Libs;
4
5
use Imagick as BaseImagick;
6
use Imagecow\ImageException;
7
use Imagecow\Utils;
8
9
/**
10
 * Imagick library.
11
 */
12
class Imagick extends AbstractLib implements LibInterface
13
{
14
    protected $image;
15
16
    /**
17
     * {@inheritdoc}
18
     */
19
    public static function checkCompatibility()
20
    {
21
        return extension_loaded('imagick');
22
    }
23
24
    /**
25
     * {@inheritdoc}
26
     *
27
     * @return Imagick
28
     */
29
    public static function createFromFile($filename)
30
    {
31
        $imagick = new BaseImagick();
32
33
        if ($imagick->readImage($filename) !== true) {
34
            throw new ImageException("The image file '{$filename}' cannot be loaded");
35
        }
36
37
        return new static($imagick);
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     *
43
     * @return Imagick
44
     */
45
    public static function createFromString($string)
46
    {
47
        $imagick = new BaseImagick();
48
49
        $imagick->readImageBlob($string);
50
51
        return new static($imagick);
52
    }
53
54
    /**
55
     * Constructor of the class.
56
     *
57
     * @param BaseImagick $image The Imagick instance
58
     */
59
    public function __construct(BaseImagick $image)
60
    {
61
        $this->image = $image;
62
63
        //Convert CMYK to RGB
64
        if ($this->image->getImageColorspace() !== BaseImagick::COLORSPACE_CMYK) {
65
            return $this;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
66
        }
67
68
        $profiles = $this->image->getImageProfiles('*', false);
69
70
        if (array_search('icc', $profiles) === false) {
71
            $this->image->profileImage('icc', file_get_contents(__DIR__.'/icc/us_web_uncoated.icc'));
72
        }
73
74
        $this->image->profileImage('icm', file_get_contents(__DIR__.'/icc/srgb.icm'));
75
        $this->image->transformImageColorspace(BaseImagick::COLORSPACE_SRGB);
76
    }
77
78
    /**
79
     * Destroy the image.
80
     */
81
    public function __destruct()
82
    {
83
        $this->image->destroy();
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function flip()
90
    {
91
        if ($this->image->flipImage() !== true) {
92
            throw new ImageException('There was an error on flip the image');
93
        }
94
    }
95
96
    /**
97
     * {@inheritdoc}
98
     */
99
    public function flop()
100
    {
101
        if ($this->image->flopImage() !== true) {
102
            throw new ImageException('There was an error on flop the image');
103
        }
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function save($filename)
110
    {
111
        $image = $this->getCompressed();
112
113
        if ($this->animated) {
114
            if (!($fp = fopen($filename, 'w'))) {
115
                throw new ImageException("The image file '{$filename}' cannot be saved");
116
            }
117
118
            $image->writeImagesFile($fp);
119
120
            fclose($fp);
121
        } elseif (!$image->writeImage($filename)) {
122
            throw new ImageException("The image file '{$filename}' cannot be saved");
123
        }
124
    }
125
126
    /**
127
     * Gets the original image object.
128
     *
129
     * @return object
130
     */
131
    public function getImage()
132
    {
133
        return $this->image;
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139
    public function getString()
140
    {
141
        $image = $this->getCompressed();
142
143
        if (!$this->animated) {
144
            return $image->getImageBlob();
145
        }
146
147
        if (!($fp = fopen($file = tempnam(sys_get_temp_dir(), 'imagick'), 'w'))) {
148
            throw new ImageException('Cannot create a temp file to generate the string data image');
149
        }
150
151
        $image->writeImagesFile($fp);
152
153
        fclose($fp);
154
155
        $string = file_get_contents($file);
156
157
        unlink($file);
158
159
        return $string;
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function getMimeType()
166
    {
167
        $format = strtolower($this->image->getImageFormat());
168
169
        if (in_array($format, ['jpeg', 'jpg', 'gif', 'png'], true)) {
170
            return "image/$format";
171
        }
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function getWidth()
178
    {
179
        return $this->image->getImageWidth();
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185
    public function getHeight()
186
    {
187
        return $this->image->getImageHeight();
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193
    public function format($format)
194
    {
195
        if (preg_match('/jpe?g/i', $format)) {
196
            list($r, $g, $b) = $this->background;
197
198
            $this->image->setImageBackgroundColor("rgb($r,$g,$b)");
199
            $this->image = $this->image->mergeImageLayers(BaseImagick::LAYERMETHOD_FLATTEN);
200
        }
201
202
        if ($this->image->setImageFormat($format) !== true) {
203
            throw new ImageException("The image format '{$format}' is not valid");
204
        }
205
    }
206
207
    /**
208
     * {@inheritdoc}
209
     */
210
    public function resize($width, $height)
211
    {
212
        if ($this->animated) {
213
            $this->image = $this->image->coalesceImages();
214
215
            foreach ($this->image as $frame) {
216
                $frame->scaleImage($width, $height);
217
            }
218
219
            $this->image = $this->image->deconstructImages();
220 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
            if ($this->image->scaleImage($width, $height) !== true) {
222
                throw new ImageException('There was an error resizing the image');
223
            }
224
225
            $this->image->setImagePage(0, 0, 0, 0);
226
        }
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232
    public function getCropOffsets($width, $height, $method)
233
    {
234
        $class = 'Imagecow\\Crops\\'.ucfirst(strtolower($method));
235
236
        if (!class_exists($class)) {
237
            throw new ImageException("The crop method '$method' is not available for Imagick");
238
        }
239
240
        return $class::getOffsets($this->image, $width, $height);
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     */
246
    public function crop($width, $height, $x, $y)
247
    {
248
        if ($this->animated) {
249
            $this->image = $this->image->coalesceImages();
250
251
            foreach ($this->image as $frame) {
252
                $frame->cropImage($width, $height, $x, $y);
253
                $frame->setImagePage(0, 0, 0, 0);
254
            }
255
256
            $this->image = $this->image->deconstructImages();
257 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
258
            if ($this->image->cropImage($width, $height, $x, $y) !== true) {
259
                throw new ImageException('There was an error cropping the image');
260
            }
261
262
            $this->image->setImagePage(0, 0, 0, 0);
263
        }
264
    }
265
266
    /**
267
     * {@inheritdoc}
268
     */
269
    public function rotate($angle)
270
    {
271
        if ($this->image->rotateImage(new BaseImagickPixel(), $angle) !== true) {
272
            throw new ImageException('There was an error rotating the image');
273
        }
274
275
        $this->image->setImagePage(0, 0, 0, 0);
276
    }
277
278
    /**
279
     * Returns a copy of the image compressed and ready to save or print.
280
     *
281
     * @return BaseImagick The instance of the image
282
     */
283
    private function getCompressed()
284
    {
285
        $image = $this->image;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->image; of type Imagick|boolean adds the type boolean to the return on line 320 which is incompatible with the return type documented by Imagecow\Libs\Imagick::getCompressed of type Imagick.
Loading history...
286
287
        if ($this->animated) {
288
            $image = $image->coalesceImages();
289
290
            foreach ($image as $frame) {
291
                $frame->stripImage();
292
                $frame->setImageUnits(1);
293
                $frame->setImageCompressionQuality($this->quality);
294
            }
295
296
            return $image->deconstructImages();
297
        }
298
299
        $format = strtolower($image->getImageFormat());
300
301
        $image->stripImage();
302
        $image->setImageUnits(1);
303
        $image->setImageCompressionQuality($this->quality);
304
305
        switch ($format) {
306
            case 'jpeg':
307
                $image->setInterlaceScheme(BaseImagick::INTERLACE_JPEG);
308
                $image->setImageCompression(BaseImagick::COMPRESSION_JPEG);
309
                break;
310
311
            case 'gif':
312
                $image->setInterlaceScheme(BaseImagick::INTERLACE_GIF);
313
                break;
314
315
            case 'png':
316
                $image->setInterlaceScheme(BaseImagick::INTERLACE_PNG);
317
                break;
318
        }
319
320
        return $image;
321
    }
322
323
    /**
324
     * @param object $watermark Watermark image object
325
     * @param array  $x         Position x
326
     * @param array  $y         Position y
327
     */
328
    public function watermark($watermark, $x, $y)
329
    {
330
        $this->image->compositeImage($watermark, BaseImagick::COMPOSITE_DISSOLVE, $x, $y);
331
    }
332
333
    /**
334
     * {@inheritdoc}
335
     */
336
    public function opacity($opacity)
337
    {
338
        if ($opacity === 100) {
339
            return;
340
        }
341
342
        if ($this->image->getImageAlphaChannel() !== BaseImagick::ALPHACHANNEL_ACTIVATE) {
343
            $this->image->setImageAlphaChannel(BaseImagick::ALPHACHANNEL_OPAQUE);
344
        }
345
346
        // NOTE: Using setImageOpacity will destroy current alpha channels!
347
        $this->image->evaluateImage(BaseImagick::EVALUATE_MULTIPLY, $opacity / 100, BaseImagick::CHANNEL_ALPHA);
348
    }
349
}
350