ImageManipulations::web_p_enabled()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 6
c 1
b 0
f 0
nc 5
nop 0
dl 0
loc 13
rs 9.6111
1
<?php
2
3
namespace Sunnysideup\PerfectCmsImages\Api;
4
5
use SilverStripe\Assets\Image;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Core\Config\Config;
9
use SilverStripe\Core\Config\Configurable;
10
use SilverStripe\Core\Convert;
11
use SilverStripe\Core\Injector\Injectable;
12
use SilverStripe\ORM\DataObject;
13
use SilverStripe\SiteConfig\SiteConfig;
14
15
class ImageManipulations
16
{
17
    use Configurable;
18
    use Injectable;
19
20
    private static $webp_enabled = true;
21
22
    private static $webp_quality = 77;
23
24
    private static $imageLinkCache = [];
25
26
    /**
27
     * work out the best image link.
28
     *
29
     * There are basically three options:
30
     * a. if the height and/or width matches or is smaller than it should be
31
     *    then just return natural image
32
     * b. if crop is set to true then Fill
33
     * c. otherwise resize Height/Width/Both
34
     *
35
     * @param Image     $image
36
     * @param null|bool $useRetina     optional
37
     * @param null|bool $forMobile     optional
38
     * @param null|int  $resizeToWidth optional
39
     */
40
    public static function get_image_link($image, string $name, ?bool $useRetina = null, ?bool $forMobile = null, ?int $resizeToWidth = 0): string
41
    {
42
        // if specified to use retina, then check if it is needed at all.
43
        if ($useRetina) {
44
            $useRetina = PerfectCMSImages::use_retina($name);
45
        }
46
        $cacheKey =
47
            implode(
48
                '_',
49
                array_filter(
50
                    [
51
                        $image->ID,
52
                        $name,
53
                        ($useRetina ? 'Y' : 'N'),
54
                        ($forMobile ? 'MY' : 'MN'),
55
                        $resizeToWidth
56
                    ]
57
                )
58
            );
59
        if (!isset(self::$imageLinkCache[$cacheKey])) {
60
            $link = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $link is dead and can be removed.
Loading history...
61
            if ($forMobile) {
62
                $perfectWidth = (int) PerfectCMSImages::get_mobile_width($name, true);
63
                $perfectHeight = (int) PerfectCMSImages::get_mobile_height($name, true);
64
                if (!$perfectHeight && !$perfectWidth) {
65
                    self::$imageLinkCache[$cacheKey] = '';
66
                    return self::$imageLinkCache[$cacheKey];
67
                }
68
            } else {
69
                $perfectWidth = (int) PerfectCMSImages::get_width($name, true);
70
                $perfectHeight = (int) PerfectCMSImages::get_height($name, true);
71
            }
72
73
            $crop = PerfectCMSImages::is_crop($name);
74
75
            $multiplier = PerfectCMSImages::get_multiplier($useRetina);
0 ignored issues
show
Bug introduced by
It seems like $useRetina can also be of type null; however, parameter $useRetina of Sunnysideup\PerfectCmsIm...mages::get_multiplier() does only seem to accept boolean, 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

75
            $multiplier = PerfectCMSImages::get_multiplier(/** @scrutinizer ignore-type */ $useRetina);
Loading history...
76
            $perfectWidth *= $multiplier;
77
            $perfectHeight *= $multiplier;
78
79
            //get current width and height
80
            $myWidth = $image->getWidth();
81
            $myHeight = $image->getHeight();
82
            //if we are trying to resize to a width that is small than the perfect width
83
            //and the resize width is small than the current width, then lets resize...
84
            if (0 !== (int) $resizeToWidth) {
85
                if ($resizeToWidth < $perfectWidth && $resizeToWidth < $myWidth) {
86
                    $perfectWidth = $resizeToWidth;
87
                }
88
            }
89
90
            $link = null;
91
            if ($perfectWidth && $perfectHeight) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $perfectWidth of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
92
                //if the height or the width are already perfect then we can not do anything about it.
93
                if ($myWidth === $perfectWidth && $myHeight === $perfectHeight) {
94
                    $link = $image->getURL();
95
                } elseif ($myWidth < $perfectWidth || $myHeight < $perfectHeight) {
96
                    $link = $image->getImageLinkCachedIfExists(
97
                        'Pad',
98
                        $perfectWidth,
99
                        $perfectHeight,
100
                        PerfectCMSImages::get_padding_bg_colour($name)
101
                    );
102
                } elseif ($crop) {
103
                    $link = $image->getImageLinkCachedIfExists(
104
                        'Fill',
105
                        $perfectWidth,
106
                        $perfectHeight
107
                    );
108
                } else {
109
                    $link = $image->getImageLinkCachedIfExists(
110
                        'FitMax',
111
                        $perfectWidth,
112
                        $perfectHeight
113
                    );
114
                }
115
            } elseif ($perfectWidth) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $perfectWidth of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
116
                if ($myWidth === $perfectWidth) {
117
                    $link = $image->getURL();
118
                } elseif ($crop) {
119
                    $link = $image->getImageLinkCachedIfExists(
120
                        'Fill',
121
                        $perfectWidth,
122
                        $myHeight
123
                    );
124
                } else {
125
                    $link = $image->getImageLinkCachedIfExists(
126
                        'ScaleWidth',
127
                        $perfectWidth
128
                    );
129
                }
130
            } elseif ($perfectHeight) {
131
                if ($myHeight === $perfectHeight) {
132
                    $link = $image->getUrl();
133
                } elseif ($crop) {
134
                    $link = $image->getImageLinkCachedIfExists(
135
                        'Fill',
136
                        $myWidth,
137
                        $perfectHeight
138
                    );
139
                } else {
140
                    $link = $image->getImageLinkCachedIfExists(
141
                        'ScaleHeight',
142
                        $perfectHeight
143
                    );
144
                }
145
            } else {
146
                // not for mobile, we definitely want to have some sort of link!
147
                $link = $image->getUrl();
148
            }
149
            self::$imageLinkCache[$cacheKey] = (string) $link;
150
        }
151
152
        return self::$imageLinkCache[$cacheKey];
153
    }
154
155
    /**
156
     * back-up image.
157
     *
158
     * @return null|Image
159
     */
160
    public static function get_backup_image(string $name)
161
    {
162
        $image = null;
163
        $backupObject = SiteConfig::current_site_config();
164
        if ($backupObject->hasMethod($name)) {
165
            $image = $backupObject->{$name}();
166
        }
167
168
        return $image;
169
    }
170
171
    /**
172
     * placeholder image.
173
     */
174
    public static function get_placeholder_image_tag(string $name): string
175
    {
176
        $multiplier = (int) PerfectCMSImages::get_multiplier(true);
177
        $perfectWidth = (int) PerfectCMSImages::get_width($name, true);
178
        $perfectHeight = (int) PerfectCMSImages::get_height($name, true);
179
        $perfectWidth *= $multiplier;
180
        $perfectHeight *= $multiplier;
181
        if ($perfectWidth || $perfectHeight) {
182
            if (0 === $perfectWidth && $perfectHeight === 0) {
183
                $perfectWidth = 200;
184
            } elseif (0 === $perfectWidth) {
185
                $perfectWidth = $perfectHeight;
186
            } elseif ($perfectHeight === 0) {
187
                $perfectHeight = $perfectWidth;
188
            }
189
190
            $text = "{$perfectWidth} x {$perfectHeight} /2 = " . round($perfectWidth / 2) . ' x ' . round($perfectHeight / 2) . '';
191
192
            return 'https://placehold.it/' . $perfectWidth . 'x' . $perfectHeight . '?text=' . urlencode($text);
193
        }
194
195
        return 'https://placehold.it/1500x1500?text=' . urlencode('no size set');
196
    }
197
198
    public static function web_p_link(string $link): string
199
    {
200
        if (self::web_p_enabled() && $link) {
201
            $fileNameWithBaseFolder = Director::baseFolder() . '/public' . $link;
202
            $arrayOfLink = explode('.', $link);
203
            $extension = array_pop($arrayOfLink);
204
            $pathWithoutExtension = rtrim($link, '.' . $extension);
205
            $webPFileName = $pathWithoutExtension . '_' . $extension . '.webp';
206
            $webPFileNameWithBaseFolder = Director::baseFolder() . '/public' . $webPFileName;
207
            if (file_exists($fileNameWithBaseFolder)) {
208
                if (isset($_GET['flush']) && file_exists($webPFileNameWithBaseFolder)) {
209
                    unlink($webPFileNameWithBaseFolder);
210
                }
211
212
                if (file_exists($webPFileNameWithBaseFolder)) {
213
                    //todo: check that image is the same ...
214
                } else {
215
                    list($width, $height, $type) = getimagesize($fileNameWithBaseFolder);
216
                    $img = null;
217
                    if ($width && $height) {
218
                        if (2 === $type) {
219
                            $img = imagecreatefromjpeg($fileNameWithBaseFolder);
220
                        } elseif (3 === $type) {
221
                            $img = imagecreatefrompng($fileNameWithBaseFolder);
222
                            imagesavealpha($img, true);
223
                        }
224
225
                        if (null !== $img) {
226
                            $quality = Config::inst()->get(ImageManipulations::class, 'webp_quality');
227
                            imagewebp($img, $webPFileNameWithBaseFolder, $quality);
228
                        }
229
                    }
230
                }
231
232
                return $webPFileName;
233
            }
234
        }
235
236
        return $link;
237
    }
238
239
    public static function add_fake_parts($image, string $link): string
240
    {
241
        // first get the timestamp
242
        $time1 = strtotime((string) $image->LastEdited);
243
        $time2 = 0;
244
        $path = Controller::join_links(Director::baseFolder(), PUBLIC_DIR, $link);
245
        if (file_exists($path)) {
246
            $time2 = filemtime($path);
247
        }
248
249
        // first convert to hash extension
250
        if (class_exists('HashPathExtension')) {
251
            /** @var null|Controller $curr */
252
            $curr = Controller::curr();
253
            if ($curr) {
0 ignored issues
show
introduced by
$curr is of type SilverStripe\Control\Controller, thus it always evaluated to true.
Loading history...
254
                if ($curr->hasMethod('HashPath')) {
255
                    $link = $curr->HashPath($link, false);
256
                }
257
            }
258
        }
259
260
        // now you can add the time
261
        $link .= '?time=' . max($time1, $time2);
262
263
        // finally add the title
264
        if ($image->Title) {
265
            $imageClasses = Config::inst()->get(PerfectCMSImages::class, 'perfect_cms_images_append_title_to_image_links_classes');
266
            if (in_array($image->ClassName, $imageClasses, true)) {
267
                $link .= '&title=' . urlencode(Convert::raw2att($image->Title));
0 ignored issues
show
Bug introduced by
It seems like SilverStripe\Core\Convert::raw2att($image->Title) can also be of type array and array; however, parameter $string of urlencode() 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

267
                $link .= '&title=' . urlencode(/** @scrutinizer ignore-type */ Convert::raw2att($image->Title));
Loading history...
268
            }
269
        }
270
271
        return $link;
272
    }
273
274
    public static function web_p_enabled(): bool
275
    {
276
        if (Config::inst()->get(ImageManipulations::class, 'webp_enabled')) {
277
            if (function_exists('imagewebp')) {
278
                if (function_exists('imagecreatefromjpeg')) {
279
                    if (function_exists('imagecreatefrompng')) {
280
                        return true;
281
                    }
282
                }
283
            }
284
        }
285
286
        return false;
287
    }
288
}
289