Completed
Push — master ( 96d6a1...124067 )
by Nicolaas
01:14
created

PerfectCMSImageDataExtension   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 341
Duplicated Lines 5.87 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 55
lcom 1
cbo 7
dl 20
loc 341
rs 6.8
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A PerfectCMSImageLinkNonRetina() 0 4 1
A PerfectCMSImageLinkRetina() 0 4 1
A PerfectCMSImageTag() 0 23 3
F PerfectCMSImageLink() 0 107 33
A image_info_available() 0 7 2
A use_retina() 0 4 1
A get_width() 10 10 2
A get_height() 10 10 2
A get_folder() 0 4 1
A max_size_in_kilobytes() 0 4 1
A get_file_type() 0 4 1
A get_enforce_size() 0 4 1
A get_one_value_for_image() 0 14 3
A get_all_values_for_images() 0 4 1
A replaceLastInstance() 0 11 2

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

1
<?php
2
3
/**
4
 * defines the image sizes
5
 * and default upload folder.
6
 */
7
class PerfectCMSImageDataExtension extends DataExtension
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...
8
{
9
    /**
10
     * background image for padded images...
11
     *
12
     * @var string
13
     */
14
    private static $perfect_cms_images_background_padding_color = '#cccccc';
0 ignored issues
show
Unused Code introduced by
The property $perfect_cms_images_background_padding_color 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...
15
16
    /***
17
     * sizes of the images
18
     *     width: 3200
19
     *     height: 3200
20
     *     folder: "myfolder"
21
     *     filetype: "try jpg"
22
     *
23
     * @var array
24
     *
25
     */
26
    private static $perfect_cms_images_image_definitions = array();
0 ignored issues
show
Unused Code introduced by
The property $perfect_cms_images_image_definitions 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...
27
28
    /***
29
     *  Images Titles will be appended to the links only
30
     *  if the ClassName of the Image is in this array
31
     * @var array
32
     *
33
     */
34
    private static $perfect_cms_images_append_title_to_image_links_classes = array();
0 ignored issues
show
Unused Code introduced by
The property $perfect_cms_images_appe..._to_image_links_classes 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...
35
36
    /**
37
     * @var string $name name of Image Field template
38
     * @return string (link)
39
     */
40
    public function PerfectCMSImageLinkNonRetina($name)
41
    {
42
        return $this->PerfectCMSImageLink($name, null, '', false);
43
    }
44
45
    /**
46
     * @var string $name name of Image Field template
47
     * @return string (link)
48
     */
49
    public function PerfectCMSImageLinkRetina($name)
50
    {
51
        return $this->PerfectCMSImageLink($name, null, '', true);
52
    }
53
54
    /**
55
     *
56
     * @param       string $name
57
     * @return string (HTML)
58
     */
59
    public function PerfectCMSImageTag($name)
60
    {
61
        $nonRetina = $this->PerfectCMSImageLinkNonRetina($name);
62
        $retina = $this->PerfectCMSImageLinkRetina($name);
63
        $width = self::get_width($name, true);
64
        $widthString = '';
65
        if($width) {
66
            $widthString = ' width="'.$width.'"';
67
        }
68
        $heightString = '';
69
        $height = self::get_height($name, true);
70
        if($height) {
71
            $heightString = ' height="'.$height.'"';
72
        }
73
        return
74
            '<img src="'.$nonRetina.'"'.
75
            ' srcset="'.$nonRetina.' 1x, '.$retina.' 2x" '.
76
            ' alt="'.Convert::raw2att($this->owner->Title).'"'.
77
            $widthString.
78
            $heightString.
79
80
            ' />';
81
    }
82
83
    /**
84
     * @param string            $name
85
     * @param object (optional) $backupObject
86
     * @param string (optional) $backupField
87
     *
88
     * @return string
89
     */
90
    public function PerfectCMSImageLink(
0 ignored issues
show
Coding Style introduced by
PerfectCMSImageLink uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
91
        $name,
92
        $backupObject = null,
93
        $backupField = '',
94
        $isRetina = null
95
    ) {
96
        if(isset($_GET['flush'])) {
97
            if (! Config::inst()->get('Image', 'force_resample')) {
98
                Config::inst()->update('Image', 'force_resample', true);
99
            }
100
        }
101
        $image = $this->owner;
102
        if ($image && $image->exists()) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
103
            //we are all good ...
104
        } else {
105
            if (!$backupObject) {
106
                $backupObject = SiteConfig::current_site_config();
107
            }
108
            if (!$backupField) {
109
                $backupField = $name;
110
            }
111
            if ($backupObject->hasMethod($backupField)) {
112
                $image = $backupObject->$backupField();
113
            }
114
        }
115
116
        $perfectWidth = self::get_width($name, true);
117
        $perfectHeight = self::get_height($name, true);
118
119
        if ($image) {
120
            if ($image instanceof Image) {
121
                if ($image->exists()) {
122
                    //work out perfect with and height
123
                    if(!$isRetina) {
124
                        $isRetina = PerfectCMSImageDataExtension::use_retina($name);
125
                    } else {
126
                        $isRetina = $isRetina;
0 ignored issues
show
Bug introduced by
Why assign $isRetina to itself?

This checks looks for cases where a variable has been assigned to itself.

This assignement can be removed without consequences.

Loading history...
127
                    }
128
                    $multiplier = 1;
129
                    if ($isRetina) {
130
                        $multiplier = 2;
131
                    }
132
                    $perfectWidth = $perfectWidth * $multiplier;
133
                    $perfectHeight = $perfectHeight  * $multiplier;
134
135
                    //get current width and height
136
                    $myWidth = $image->getWidth();
137
                    $myHeight = $image->getHeight();
138
                    // $backEndString = Image::get_backend();
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
139
                    // $backend = Injector::inst()->get($backEndString);
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
140
                    if ($perfectWidth && $perfectHeight) {
141
                        if ($myWidth == $perfectWidth || $myHeight ==  $perfectHeight) {
142
                            $link = $image->ScaleWidth($myWidth)->Link();
143
                        } elseif ($myWidth < $perfectWidth || $myHeight < $perfectHeight) {
144
                            $link = $image->Pad(
145
                                $perfectWidth,
146
                                $perfectHeight,
147
                                Config::inst()->get('PerfectCMSImageDataExtension', 'perfect_cms_images_background_padding_color')
148
                            )->Link();
149
                        } elseif ($myWidth > $perfectWidth || $myHeight > $perfectHeight) {
150
                            $link = $image->FitMax($perfectWidth, $perfectHeight)->Link();
151
                        }
152
                    } elseif ($perfectWidth) {
153
                        $link = $image->ScaleWidth($perfectWidth)->Link();
154
                    } elseif ($perfectHeight) {
155
                        $link = $image->ScaleHeight($perfectHeight)->Link();
156
                    } else {
157
                        $link = $image->ScaleWidth($myWidth)->Link();
158
                    }
159
                    $path_parts = pathinfo($link);
0 ignored issues
show
Bug introduced by
The variable $link 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...
160
161
                    if (class_exists('HashPathExtension')) {
162
                        if ($curr = Controller::curr()) {
163
                            if ($curr->hasMethod('HashPath')) {
164
                                $link = $curr->HashPath($link, false);
165
                            }
166
                        }
167
                    }
168
                    $imageClasses = Config::inst()->get('PerfectCMSImageDataExtension', 'perfect_cms_images_append_title_to_image_links_classes');
169
                    if(in_array($image->ClassName, $imageClasses) && $image->Title){
170
                        $link = $this->replaceLastInstance(
171
                            '.'.$path_parts['extension'],
172
                            '.pci/'.$image->Title.'.'.$path_parts['extension'],
173
                            $link
174
                        );
175
                    }
176
                    return $link;
177
                }
178
            }
179
        }
180
        // no image -> provide placeholder if in DEV MODE only!!!
181
        if(Director::isDev()) {
182
            if ($perfectWidth || $perfectHeight) {
183
                if (!$perfectWidth) {
184
                    $perfectWidth = $perfectHeight;
185
                }
186
                if (!$perfectHeight) {
187
                    $perfectHeight = $perfectWidth;
188
                }
189
                $text = "$perfectWidth x $perfectHeight /2 = ".round($perfectWidth/2)." x ".round($perfectHeight/2)."";
190
191
                return 'https://placehold.it/'.($perfectWidth).'x'.($perfectHeight).'?text='.urlencode($text);
192
            } else {
193
                return 'https://placehold.it/500x500?text='.urlencode('no size set');
194
            }
195
        }
196
    }
197
198
    /**
199
     * @param string           $name
200
     *
201
     * @return boolean
202
     */
203
    public static function image_info_available($name)
204
    {
205
        $sizes = self::get_all_values_for_images();
206
        //print_r($sizes);die();
0 ignored issues
show
Unused Code Comprehensibility introduced by
89% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
207
        return isset($sizes[$name]) ? true : false;
208
209
    }
210
211
212
    /**
213
     * @param string           $name
214
     *
215
     * @return boolean
216
     */
217
    public static function use_retina($name)
218
    {
219
        return self::get_one_value_for_image($name, "use_retina", true);
220
    }
221
222
    /**
223
     * @param string           $name
224
     * @param bool             $forceInteger
225
     *
226
     * @return int
227
     */
228 View Code Duplication
    public static function get_width($name, $forceInteger = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
229
    {
230
        $v = self::get_one_value_for_image($name, "width", 0);
231
        if($forceInteger) {
232
            $v = intval($v) - 0;
233
        }
234
235
        return $v;
236
237
    }
238
239
    /**
240
     * @param string           $name
241
     * @param bool             $forceInteger
242
     *
243
     * @return int
244
     */
245 View Code Duplication
    public static function get_height($name, $forceInteger)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
246
    {
247
248
        $v = self::get_one_value_for_image($name, "height", 0);
249
        if($forceInteger) {
250
            $v = intval($v) - 0;
251
        }
252
253
        return $v;
254
    }
255
256
    /**
257
     * @param string           $name
258
     *
259
     * @return string
260
     */
261
    public static function get_folder($name)
262
    {
263
        return self::get_one_value_for_image($name, "folder", 'other-images');
264
    }
265
266
    /**
267
     * @param string           $name
268
     *
269
     * @return int
270
     */
271
    public static function max_size_in_kilobytes($name)
272
    {
273
        return self::get_one_value_for_image($name, "max_size_in_kilobytes", 0);
274
    }
275
276
    /**
277
     * @param string           $name
278
     *
279
     * @return string
280
     */
281
    public static function get_file_type($name)
282
    {
283
        return self::get_one_value_for_image($name, "filetype", 'jpg');
284
    }
285
286
    /**
287
     * @param string           $name
288
     *
289
     * @return boolean
290
     */
291
    public static function get_enforce_size($name)
292
    {
293
        return self::get_one_value_for_image($name, "enforce_size", false);
294
    }
295
296
    /**
297
     * @param string $name
298
     * @param int    $key
299
     * @param mixed  $default
300
     *
301
     * @return mixed
302
     */
303
    private static function get_one_value_for_image($name, $key, $default = '')
304
    {
305
        $sizes = self::get_all_values_for_images();
306
        //print_r($sizes);die();
0 ignored issues
show
Unused Code Comprehensibility introduced by
89% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
307
        if (isset($sizes[$name])) {
308
            if (isset($sizes[$name][$key])) {
309
                return $sizes[$name][$key];
310
            }
311
        } else {
312
            user_error('no information for image with name: '.$name);
313
        }
314
315
        return $default;
316
    }
317
318
    /**
319
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|integer|double|string|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
320
     */
321
    private static function get_all_values_for_images()
322
    {
323
        return Config::inst()->get('PerfectCMSImageDataExtension', 'perfect_cms_images_image_definitions');
324
    }
325
326
    /**
327
     * replace the last instance of a string occurence.
328
     *
329
     * @param  string $search  needle
330
     * @param  string $replace new needle
331
     * @param  string $subject haystack
332
     *
333
     * @return string
334
     */
335
    private function replaceLastInstance($search, $replace, $subject)
336
    {
337
        $pos = strrpos($subject, $search);
338
339
        if($pos !== false)
340
        {
341
            $subject = substr_replace($subject, $replace, $pos, strlen($search));
342
        }
343
344
        return $subject;
345
    }
346
347
}
348