Image_Transform_Driver_Imagick3   B
last analyzed

Complexity

Total Complexity 46

Size/Duplication

Total Lines 386
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 123
dl 0
loc 386
rs 8.72
c 1
b 0
f 0
wmc 46

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A rotate() 0 21 4
A _resize() 0 14 3
A flip() 0 9 2
B save() 0 28 8
A mirror() 0 9 2
A Image_Transform_Driver_Imagick3() 0 3 1
B display() 0 27 7
A load() 0 20 3
A gamma() 0 7 2
A addText() 0 28 5
A greyscale() 0 10 1
A crop() 0 19 3
A free() 0 5 2
A raiseError() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Image_Transform_Driver_Imagick3 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.

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 Image_Transform_Driver_Imagick3, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* vim: set expandtab tabstop=4 shiftwidth=4: */
4
5
/**
6
 * imagick PECL extension implementation for Image_Transform package
7
 *
8
 *
9
 * LICENSE: This source file is subject to version 3.0 of the PHP license
10
 * that is available through the world-wide-web at the following URI:
11
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
12
 * the PHP License and are unable to obtain it through the web, please
13
 * send a note to [email protected] so we can mail you a copy immediately.
14
 *
15
 * @category   Image
16
 * @package    Image_Transform
17
 * @subpackage Image_Transform_Driver_Imagick3
18
 * @author     Philippe Jausions <[email protected]>
19
 * @copyright  2007 The PHP Group
20
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
21
 * @version    CVS: $Id: Imagick3.php 266908 2008-10-01 21:11:07Z dufuz $
22
 * @link       http://pear.php.net/package/Image_Transform
23
 */
24
require_once __DIR__ . '/Image/Transform.php';
25
26
/**
27
 * imagick PECL extension implementation for Image_Transform package
28
 *
29
 * For use of version 2+ of the extension. For version 0.9.* use Imagick2 driver
30
 * instead
31
 *
32
 * @category   Image
33
 * @package    Image_Transform
34
 * @subpackage Image_Transform_Driver_Imagick3
35
 * @author     Philippe Jausions <[email protected]>
36
 * @copyright  2007 The PHP Group
37
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
38
 * @version    Release: @package_version@
39
 * @link       http://pear.php.net/package/Image_Transform
40
 * @since      0.9.2
41
 * @since      PHP 5.1.3
42
 * @since      PECL Imagick 2.0.0a1
43
 */
44
class Image_Transform_Driver_Imagick3 extends Image_Transform
45
{
46
    /**
47
     * Instance of imagick
48
     * @var Imagick
49
     */
50
    public $imagick = null;
51
    /**
52
     * Handler of the image resource before
53
     * the last transformation
54
     * @var array
55
     */
56
    public $oldImage;
57
58
    /**
59
     * @see __construct()
60
     */
61
    public function Image_Transform_Driver_Imagick3()
62
    {
63
        $this->__construct();
64
    }
65
66
    /**
67
     * @see http://www.imagemagick.org/www/formats.html
68
     */
69
    public function __construct()
70
    {
71
        if (PEAR::loadExtension('imagick')) {
72
            require_once __DIR__ . '/Image/Transform/Driver/Imagick/ImageTypes.php';
73
        } else {
74
            $this->isError(PEAR::raiseError('Could not find the imagick extension.', IMAGE_TRANSFORM_ERROR_UNSUPPORTED));
0 ignored issues
show
Bug introduced by
The method raiseError() does not exist on PEAR. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

74
            $this->isError(PEAR::/** @scrutinizer ignore-call */ raiseError('Could not find the imagick extension.', IMAGE_TRANSFORM_ERROR_UNSUPPORTED));
Loading history...
75
        }
76
    }
77
78
    /**
79
     * Loads an image
80
     *
81
     * @param string $image filename
82
     *
83
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
84
     * @access public
85
     */
86
    public function load($image)
87
    {
88
        $this->free();
89
        $this->imagick = new Imagick();
90
91
        try {
92
            $this->imagick->readImage($image);
93
        } catch (ImagickException $e) {
94
            $this->free();
95
96
            return $this->raiseError('Could not load image:' . $e->getMessage(), IMAGE_TRANSFORM_ERROR_IO);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...AGE_TRANSFORM_ERROR_IO) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
97
        }
98
99
        $this->image = $image;
100
        $result      = $this->_get_image_details($image);
101
        if (PEAR::isError($result)) {
102
            return $result;
103
        }
104
105
        return true;
106
    }
107
108
    /**
109
     * Resizes the image
110
     *
111
     * @param int   $new_x     New width
112
     * @param int   $new_y     New height
113
     * @param mixed $options   Optional parameters
114
     *                         <ul>
115
     *                         <li>'scaleMethod': "pixel" or "smooth"</li>
116
     *                         </ul>
117
     *
118
     * @return bool|PEAR_Error TRUE or PEAR_Error object on error
119
     * @access protected
120
     */
121
    public function _resize($new_x, $new_y, $options = null)
122
    {
123
        try {
124
            $scaleMethod = $this->_getOption('scaleMethod', $options, 'smooth');
125
            $blur        = ('pixel' == $scaleMethod) ? 0 : 1;
126
            $this->imagick->resizeImage($new_x, $new_y, Imagick::FILTER_UNDEFINED, $blur);
127
        } catch (ImagickException $e) {
128
            return $this->raiseError('Could not resize image.', IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
129
        }
130
131
        $this->new_x = $new_x;
132
        $this->new_y = $new_y;
133
134
        return true;
135
    }
136
137
    // End resize
138
139
    /**
140
     * Rotates the current image
141
     *
142
     * @param float $angle   Rotation angle in degree
143
     * @param array $options Supported options:
144
     *                       <ul>
145
     *                       <li>'canvasColor' : array(r ,g, b), named color or #rrggbb</li>
146
     *                       </ul>
147
     *
148
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
149
     * @access public
150
     */
151
    public function rotate($angle, $options = null)
152
    {
153
        if (0 == ($angle % 360)) {
154
            return true;
155
        }
156
        $color = $this->_getColor('canvasColor', $options, [255, 255, 255]);
157
        if (is_array($color)) {
0 ignored issues
show
introduced by
The condition is_array($color) is always true.
Loading history...
158
            $color = $this->colorarray2colorhex($color);
159
        }
160
        $pixel = new ImagickPixel($color);
0 ignored issues
show
Bug introduced by
It seems like $color can also be of type false; however, parameter $color of ImagickPixel::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

160
        $pixel = new ImagickPixel(/** @scrutinizer ignore-type */ $color);
Loading history...
161
162
        try {
163
            $this->imagick->rotateImage($pixel, $angle);
164
        } catch (ImagickException $e) {
165
            return $this->raiseError('Cannot create a new imagick image for the rotation: ' . $e->getMessage(), IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
166
        }
167
        $info        = $this->imagick->getImageGeometry();
168
        $this->new_x = $info['width'];
169
        $this->new_y = $info['height'];
170
171
        return true;
172
    }
173
174
    // End rotate
175
176
    /**
177
     * Adds text to the image
178
     *
179
     * @param array $params   Array contains options:
180
     *                        <ul>
181
     *                        <li>'text' (string) The string to draw</li>
182
     *                        <li>'x'    (integer) Horizontal position</li>
183
     *                        <li>'y'    (integer) Vertical Position</li>
184
     *                        <li>'Color' (mixed) Font color</li>
185
     *                        <li>'font' (string) Font to be used</li>
186
     *                        <li>'size' (integer) Size of the fonts in pixel</li>
187
     *                        </ul>
188
     *
189
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
190
     * @access public
191
     */
192
    public function addText($params)
193
    {
194
        $this->oldImage = clone $this->imagick;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->imagick of type Imagick is incompatible with the declared type array of property $oldImage.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
195
        $params         = array_merge($this->_get_default_text_params(), $params);
196
197
        if (is_array($params['color'])) {
198
            $params['color'] = $this->colorarray2colorhex($params['color']);
199
        } else {
200
            $params['color'] = mb_strtolower($params['color']);
201
        }
202
203
        static $cmds = [
204
            'setFillColor' => 'color',
205
            'setFontSize'  => 'size',
206
            'setFontFace'  => 'font',
207
        ];
208
        $this->imagick->beginDraw();
0 ignored issues
show
Bug introduced by
The method beginDraw() does not exist on Imagick. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

208
        $this->imagick->/** @scrutinizer ignore-call */ 
209
                        beginDraw();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
209
210
        foreach ($cmds as $cmd => $v) {
211
            if (!$this->imagick->$cmd($params[$v])) {
212
                return $this->raiseError("Problem with adding Text::{$v} = {$params[$v]}", IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
213
            }
214
        }
215
        if (!$this->imagick->drawAnnotation($params['x'], $params['y'], $params['text'])) {
0 ignored issues
show
Bug introduced by
The method drawAnnotation() does not exist on Imagick. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

215
        if (!$this->imagick->/** @scrutinizer ignore-call */ drawAnnotation($params['x'], $params['y'], $params['text'])) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
216
            return $this->raiseError('Problem with adding Text', IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
217
        }
218
219
        return true;
220
    }
221
222
    // End addText
223
224
    /**
225
     * Saves the image to a file
226
     *
227
     * @param string $filename the name of the file to write to
228
     *
229
     * @param string $type
230
     * @param null   $quality
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $quality is correct as it would always require null to be passed?
Loading history...
231
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
232
     * @access public
233
     */
234
    public function save($filename, $type = '', $quality = null)
235
    {
236
        $options = is_array($quality) ? $quality : [];
0 ignored issues
show
introduced by
The condition is_array($quality) is always false.
Loading history...
237
        if (is_numeric($quality)) {
0 ignored issues
show
introduced by
The condition is_numeric($quality) is always false.
Loading history...
238
            $options['quality'] = $quality;
239
        }
240
        $quality = $this->_getOption('quality', $options, 75);
241
        $this->imagick->setImageCompression($quality);
242
243
        if ($type && strcasecmp($type, $this->type)) {
244
            try {
245
                $this->imagick->setImageFormat($type);
246
            } catch (ImagickException $e) {
247
                return $this->raiseError('Could not save image to file (conversion failed).', IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
248
            }
249
        }
250
251
        try {
252
            $this->imagick->writeImage($filename);
253
        } catch (ImagickException $e) {
254
            return $this->raiseError('Could not save image to file: ' . $e->getMessage(), IMAGE_TRANSFORM_ERROR_IO);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...AGE_TRANSFORM_ERROR_IO) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
255
        }
256
257
        if (!$this->keep_settings_on_save) {
258
            $this->free();
259
        }
260
261
        return true;
262
    }
263
264
    // End save
265
266
    /**
267
     * Displays image without saving and lose changes
268
     *
269
     * This method adds the Content-type HTTP header
270
     *
271
     * @param mixed      $type
272
     * @param null|mixed $quality
273
     *
274
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
275
     * @access public
276
     */
277
    public function display($type = '', $quality = null)
278
    {
279
        $options = is_array($quality) ? $quality : [];
280
        if (is_numeric($quality)) {
281
            $options['quality'] = $quality;
282
        }
283
        $quality = $this->_getOption('quality', $options, 75);
284
        $this->imagick->setImageCompression($quality);
285
286
        if ($type && strcasecmp($type, $this->type)) {
287
            try {
288
                $this->imagick->setImageFormat($type);
289
            } catch (ImagickException $e) {
290
                return $this->raiseError('Could not save image to file (conversion failed).', IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
291
            }
292
        }
293
294
        try {
295
            $image = $this->imagick->getImageBlob();
296
        } catch (ImagickException $e) {
297
            return $this->raiseError('Could not display image.', IMAGE_TRANSFORM_ERROR_IO);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...AGE_TRANSFORM_ERROR_IO) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
298
        }
299
        header('Content-type: ' . $this->getMimeType($type));
300
        echo $image;
301
        $this->free();
302
303
        return true;
304
    }
305
306
    /**
307
     * Adjusts the image gamma
308
     *
309
     * @param float $outputgamma
310
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
311
     * @access public
312
     */
313
    public function gamma($outputgamma = 1.0)
314
    {
315
        if (1.0 != $outputgamma) {
316
            $this->imagick->setImageGamma($outputgamma);
317
        }
318
319
        return true;
320
    }
321
322
    /**
323
     * Crops the image
324
     *
325
     * @param int $width  Cropped image width
326
     * @param int $height Cropped image height
327
     * @param int $x      X-coordinate to crop at
328
     * @param int $y      Y-coordinate to crop at
329
     *
330
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
331
     * @access public
332
     */
333
    public function crop($width, $height, $x = 0, $y = 0)
334
    {
335
        // Sanity check
336
        if (!$this->intersects($width, $height, $x, $y)) {
337
            return PEAR::raiseError('Nothing to crop', IMAGE_TRANSFORM_ERROR_OUTOFBOUND);
338
        }
339
340
        try {
341
            $this->imagick->cropImage($width, $height, $x, $y);
342
        } catch (ImagickException $e) {
343
            return $this->raiseError('Could not crop image', IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
344
        }
345
346
        // I think that setting img_x/y is wrong, but scaleByLength() & friends
347
        // mess up the aspect after a crop otherwise.
348
        $this->new_x = $width;
349
        $this->new_y = $height;
350
351
        return true;
352
    }
353
354
    /**
355
     * Converts the image to greyscale
356
     *
357
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
358
     * @access public
359
     */
360
    public function greyscale()
361
    {
362
        $this->imagick->setImageType(Imagick::IMGTYPE_GRAYSCALE);
363
364
        /*$this->imagick->setImageColorSpace(Imagick::COLORSPACE_GRAY);
365
        $this->imagick->setImageDepth(8);
366
        $this->imagick->separateImageChannel(Imagick::CHANNEL_GRAY);
367
        $this->imagick->setImageChannelDepth(Imagick::CHANNEL_GRAY, 8);*/
368
369
        return true;
370
    }
371
372
    /**
373
     * Horizontal mirroring
374
     *
375
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
376
     * @access public
377
     */
378
    public function mirror()
379
    {
380
        try {
381
            $this->imagick->flopImage();
382
        } catch (ImagickException $e) {
383
            return $this->raiseError('Could not mirror the image.', IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
384
        }
385
386
        return true;
387
    }
388
389
    /**
390
     * Vertical mirroring
391
     *
392
     * @return bool|PEAR_Error TRUE or a PEAR_Error object on error
393
     * @access public
394
     */
395
    public function flip()
396
    {
397
        try {
398
            $this->imagick->flipImage();
399
        } catch (ImagickException $e) {
400
            return $this->raiseError('Could not flip the image.', IMAGE_TRANSFORM_ERROR_FAILED);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->raiseError...TRANSFORM_ERROR_FAILED) returns the type PEAR which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
401
        }
402
403
        return true;
404
    }
405
406
    /**
407
     * Destroy image handle
408
     *
409
     * @access public
410
     */
411
    public function free()
412
    {
413
        if (isset($this->imagick)) {
414
            $this->imagick->destroy();
415
            $this->imagick = null;
416
        }
417
    }
418
419
    /**
420
     * RaiseError Method - shows imagick Raw errors.
421
     *
422
     * @param string $message message = prefixed message..
423
     * @param int    $code    error code
424
     * @return PEAR error object
425
     * @access protected
426
     */
427
    public function raiseError($message, $code = 0)
428
    {
429
        return PEAR::raiseError($message, $code);
430
    }
431
}
432