elFinderPluginWatermark   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 250
Duplicated Lines 28.4 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 71
loc 250
rs 8.5454
c 0
b 0
f 0
wmc 49
lcom 1
cbo 2

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 18 1
C onUpLoadPreSave() 3 70 13
B watermarkPrint_imagick() 0 35 5
F watermarkPrint_gd() 68 119 30

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

1
<?php
2
/**
3
 * elFinder Plugin Watermark.
4
 *
5
 * Print watermark on file upload.
6
 *
7
 * ex. binding, configure on connector options
8
 *	$opts = array(
9
 *		'bind' => array(
10
 *			'upload.presave' => array(
11
 *				'Plugin.Watermark.onUpLoadPreSave'
12
 *			)
13
 *		),
14
 *		// global configure (optional)
15
 *		'plugin' => array(
16
 *			'Watermark' => array(
17
 *				'enable'         => true,       // For control by volume driver
18
 *				'source'         => 'logo.png', // Path to Water mark image
19
 *				'marginRight'    => 5,          // Margin right pixel
20
 *				'marginBottom'   => 5,          // Margin bottom pixel
21
 *				'quality'        => 95,         // JPEG image save quality
22
 *				'transparency'   => 70,         // Water mark image transparency ( other than PNG )
23
 *				'targetType'     => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field )
24
 *				'targetMinPixel' => 200,        // Target image minimum pixel size
25
 *				'offDropWith'    => null        // To disable it if it is dropped with pressing the meta key
26
 *				                                // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value
27
 *				                                // In case of using any key, specify it as an array
28
 *			)
29
 *		),
30
 *		// each volume configure (optional)
31
 *		'roots' => array(
32
 *			array(
33
 *				'driver' => 'LocalFileSystem',
34
 *				'path'   => '/path/to/files/',
35
 *				'URL'    => 'http://localhost/to/files/'
36
 *				'plugin' => array(
37
 *					'Watermark' => array(
38
 *			 			'enable'         => true,       // For control by volume driver
39
 *						'source'         => 'logo.png', // Path to Water mark image
40
 *						'marginRight'    => 5,          // Margin right pixel
41
 *						'marginBottom'   => 5,          // Margin bottom pixel
42
 *						'quality'        => 95,         // JPEG image save quality
43
 *						'transparency'   => 70,         // Water mark image transparency ( other than PNG )
44
 *						'targetType'     => IMG_GIF|IMG_JPG|IMG_PNG|IMG_WBMP, // Target image formats ( bit-field )
45
 *						'targetMinPixel' => 200,        // Target image minimum pixel size
46
 *						'offDropWith'    => null        // To disable it if it is dropped with pressing the meta key
47
 *						                                // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value
48
 *						                                // In case of using any key, specify it as an array
49
 *					)
50
 *				)
51
 *			)
52
 *		)
53
 *	);
54
 *
55
 * @author Naoki Sawada
56
 * @license New BSD
57
 */
58
class elFinderPluginWatermark extends elFinderPlugin
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
59
{
60
    private $watermarkImgInfo = null;
0 ignored issues
show
Unused Code introduced by
The property $watermarkImgInfo is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
61
62
    public function __construct($opts)
63
    {
64
        $defaults = [
65
            'enable' => true,       // For control by volume driver
66
            'source' => 'logo.png', // Path to Water mark image
67
            'marginRight' => 5,          // Margin right pixel
68
            'marginBottom' => 5,          // Margin bottom pixel
69
            'quality' => 95,         // JPEG image save quality
70
            'transparency' => 70,         // Water mark image transparency ( other than PNG )
71
            'targetType' => IMG_GIF | IMG_JPG | IMG_PNG | IMG_WBMP, // Target image formats ( bit-field )
72
            'targetMinPixel' => 200,        // Target image minimum pixel size
73
            'offDropWith' => null,        // To disable it if it is dropped with pressing the meta key
74
                                            // Alt: 8, Ctrl: 4, Meta: 2, Shift: 1 - sum of each value
75
                                            // In case of using any key, specify it as an array
76
        ];
77
78
        $this->opts = array_merge($defaults, $opts);
79
    }
80
81
    public function onUpLoadPreSave(&$thash, &$name, $src, $elfinder, $volume)
0 ignored issues
show
Unused Code introduced by
The parameter $thash is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $name is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $elfinder is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
82
    {
83
        $opts = $this->getCurrentOpts($volume);
84
85
        if (! $this->iaEnabled($opts)) {
86
            return false;
87
        }
88
89
        $mime = mime_content_type($src);
90
        if (substr($mime, 0, 5) !== 'image') {
91
            return false;
92
        }
93
94
        $srcImgInfo = getimagesize($src);
95
        if ($srcImgInfo === false) {
96
            return false;
97
        }
98
99
        // check Animation Gif
100
        if (elFinder::isAnimationGif($src)) {
101
            return false;
102
        }
103
104
        // check water mark image
105
        if (! file_exists($opts['source'])) {
106
            $opts['source'] = dirname(__FILE__).'/'.$opts['source'];
107
        }
108
        if (is_readable($opts['source'])) {
109
            $watermarkImgInfo = getimagesize($opts['source']);
110
            if (! $watermarkImgInfo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $watermarkImgInfo of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
111
                return false;
112
            }
113
        } else {
114
            return false;
115
        }
116
117
        $watermark = $opts['source'];
118
        $marginLeft = $opts['marginRight'];
119
        $marginBottom = $opts['marginBottom'];
120
        $quality = $opts['quality'];
121
        $transparency = $opts['transparency'];
122
123
        // check target image type
124
        $imgTypes = [
125
            IMAGETYPE_GIF => IMG_GIF,
126
            IMAGETYPE_JPEG => IMG_JPEG,
127
            IMAGETYPE_PNG => IMG_PNG,
128
            IMAGETYPE_BMP => IMG_WBMP,
129
            IMAGETYPE_WBMP => IMG_WBMP,
130
        ];
131 View Code Duplication
        if (! isset($imgTypes[$srcImgInfo[2]]) || ! ($opts['targetType'] & $imgTypes[$srcImgInfo[2]])) {
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...
132
            return false;
133
        }
134
135
        // check target image size
136
        if ($opts['targetMinPixel'] > 0 && $opts['targetMinPixel'] > min($srcImgInfo[0], $srcImgInfo[1])) {
137
            return false;
138
        }
139
140
        $watermark_width = $watermarkImgInfo[0];
141
        $watermark_height = $watermarkImgInfo[1];
142
        $dest_x = $srcImgInfo[0] - $watermark_width - $marginLeft;
143
        $dest_y = $srcImgInfo[1] - $watermark_height - $marginBottom;
144
145
        if (class_exists('Imagick', false)) {
146
            return $this->watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo);
147
        } else {
148
            return $this->watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo);
149
        }
150
    }
151
152
    private function watermarkPrint_imagick($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo)
0 ignored issues
show
Unused Code introduced by
The parameter $watermarkImgInfo is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
153
    {
154
        try {
155
            // Open the original image
156
            $img = new Imagick($src);
157
158
            // Open the watermark
159
            $watermark = new Imagick($watermark);
160
161
            // Set transparency
162
            if (strtoupper($watermark->getImageFormat()) !== 'PNG') {
163
                $watermark->setImageOpacity($transparency / 100);
164
            }
165
166
            // Overlay the watermark on the original image
167
            $img->compositeImage($watermark, imagick::COMPOSITE_OVER, $dest_x, $dest_y);
168
169
            // Set quality
170
            if (strtoupper($img->getImageFormat()) === 'JPEG') {
171
                $img->setImageCompression(imagick::COMPRESSION_JPEG);
172
                $img->setCompressionQuality($quality);
173
            }
174
175
            $result = $img->writeImage($src);
176
177
            $img->clear();
178
            $img->destroy();
179
            $watermark->clear();
180
            $watermark->destroy();
181
182
            return $result ? true : false;
183
        } catch (Exception $e) {
184
            return false;
185
        }
186
    }
187
188
    private function watermarkPrint_gd($src, $watermark, $dest_x, $dest_y, $quality, $transparency, $watermarkImgInfo, $srcImgInfo)
189
    {
190
        $watermark_width = $watermarkImgInfo[0];
191
        $watermark_height = $watermarkImgInfo[1];
192
193
        $ermsg = '';
194 View Code Duplication
        switch ($watermarkImgInfo['mime']) {
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...
195
            case 'image/gif':
196
                if (imagetypes() & IMG_GIF) {
197
                    $oWatermarkImg = imagecreatefromgif($watermark);
198
                } else {
199
                    $ermsg = 'GIF images are not supported';
200
                }
201
                break;
202
            case 'image/jpeg':
203
                if (imagetypes() & IMG_JPG) {
204
                    $oWatermarkImg = imagecreatefromjpeg($watermark);
205
                } else {
206
                    $ermsg = 'JPEG images are not supported';
207
                }
208
                break;
209
            case 'image/png':
210
                if (imagetypes() & IMG_PNG) {
211
                    $oWatermarkImg = imagecreatefrompng($watermark);
212
                } else {
213
                    $ermsg = 'PNG images are not supported';
214
                }
215
                break;
216
            case 'image/wbmp':
217
                if (imagetypes() & IMG_WBMP) {
218
                    $oWatermarkImg = imagecreatefromwbmp($watermark);
219
                } else {
220
                    $ermsg = 'WBMP images are not supported';
221
                }
222
                break;
223
            default:
224
                $oWatermarkImg = false;
225
                $ermsg = $watermarkImgInfo['mime'].' images are not supported';
226
                break;
227
        }
228
229
        if (! $ermsg) {
230 View Code Duplication
            switch ($srcImgInfo['mime']) {
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...
231
                case 'image/gif':
232
                    if (imagetypes() & IMG_GIF) {
233
                        $oSrcImg = imagecreatefromgif($src);
234
                    } else {
235
                        $ermsg = 'GIF images are not supported';
236
                    }
237
                    break;
238
                case 'image/jpeg':
239
                    if (imagetypes() & IMG_JPG) {
240
                        $oSrcImg = imagecreatefromjpeg($src);
241
                    } else {
242
                        $ermsg = 'JPEG images are not supported';
243
                    }
244
                    break;
245
                case 'image/png':
246
                    if (imagetypes() & IMG_PNG) {
247
                        $oSrcImg = imagecreatefrompng($src);
248
                    } else {
249
                        $ermsg = 'PNG images are not supported';
250
                    }
251
                    break;
252
                case 'image/wbmp':
253
                    if (imagetypes() & IMG_WBMP) {
254
                        $oSrcImg = imagecreatefromwbmp($src);
255
                    } else {
256
                        $ermsg = 'WBMP images are not supported';
257
                    }
258
                    break;
259
                default:
260
                    $oSrcImg = false;
261
                    $ermsg = $srcImgInfo['mime'].' images are not supported';
262
                    break;
263
            }
264
        }
265
266
        if ($ermsg || false === $oSrcImg || false === $oWatermarkImg) {
0 ignored issues
show
Bug introduced by
The variable $oSrcImg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $oWatermarkImg does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
267
            return false;
268
        }
269
270
        if ($srcImgInfo['mime'] === 'image/png') {
271
            if (function_exists('imagecolorallocatealpha')) {
272
                $bg = imagecolorallocatealpha($oSrcImg, 255, 255, 255, 127);
273
                imagefill($oSrcImg, 0, 0, $bg);
274
            }
275
        }
276
277
        if ($watermarkImgInfo['mime'] === 'image/png') {
278
            imagecopy($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height);
279
        } else {
280
            imagecopymerge($oSrcImg, $oWatermarkImg, $dest_x, $dest_y, 0, 0, $watermark_width, $watermark_height, $transparency);
281
        }
282
283
        switch ($srcImgInfo['mime']) {
284
            case 'image/gif':
285
                imagegif($oSrcImg, $src);
286
                break;
287
            case 'image/jpeg':
288
                imagejpeg($oSrcImg, $src, $quality);
289
                break;
290
            case 'image/png':
291
                if (function_exists('imagesavealpha') && function_exists('imagealphablending')) {
292
                    imagealphablending($oSrcImg, false);
293
                    imagesavealpha($oSrcImg, true);
294
                }
295
                imagepng($oSrcImg, $src);
296
                break;
297
            case 'image/wbmp':
298
                imagewbmp($oSrcImg, $src);
299
                break;
300
        }
301
302
        imagedestroy($oSrcImg);
303
        imagedestroy($oWatermarkImg);
304
305
        return true;
306
    }
307
}
308