Completed
Push — master ( a06a95...a1d542 )
by dan
15s
created

ResponsiveImageManager.php (12 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace IrishDan\ResponsiveImageBundle;
4
5
/**
6
 * Class ResponsiveImageManager
7
 * @package ResponsiveImageBundle
8
 */
9
class ResponsiveImageManager
10
{
11
    /**
12
     * @var
13
     */
14
    private $config;
15
16
    /**
17
     * @var
18
     */
19
    private $imager;
20
21
    /**
22
     * @var array
23
     */
24
    private $images = [];
25
26
    /**
27
     * @var array
28
     */
29
    private $s3;
30
31
    /**
32
     * @var
33
     */
34
    private $styleManager;
35
36
    /**
37
     * @var
38
     */
39
    private $system;
40
41
    /**
42
     * @var
43
     */
44
    private $uploader;
45
46
    /**
47
     * ImageManager constructor.
48
     *
49
     * @param $imager
50
     * @param $config
51
     */
52
    public function __construct(ImageMaker $imager, StyleManager $styleManager, array $config, $system, $s3, $uploader)
53
    {
54
        $this->imager = $imager;
55
        $this->styleManager = $styleManager;
56
        $this->config = $config;
57
        $this->system = $system;
58
        $this->s3 = $s3;
59
        $this->uploader = $uploader;
60
    }
61
62
    /**
63
     * Cleans out any temp files if needed after image generation.
64
     */
65
    private function cleanUp() {
66
        $s3Enabled = $this->s3enabled();
67
        if ($s3Enabled) {
68
            $remote_file_policy = empty($this->config['aws_S3']['remote_file_policy']) ? 'ALL' : $this->config['aws_S3']['remote_file_policy'];
69
            if ($remote_file_policy == 'ALL') {
70
                if (!empty($this->images[0])) {
71
                    unset ($this->images[0]);
72
                }
73
            }
74
            foreach ($this->images as $key => $pathArray) {
75
                $this->system->deleteFile($pathArray[0]);
76
            }
77
78
        }
79
    }
80
81
    /**
82
     * Creates styled images for an image object.
83
     * Handles generation from the controller or the form.
84
     *
85
     * @param $imageObject
86
     * @param $styleName
87
     * @return mixed
88
     */
89
    private function createImageDerivative($imageObject, $styleName = NULL)
90
    {
91
        $paths = $this->images;
92
        $original = $paths[0];
93
        $filePath = $original[0];
94
        $crop = empty($imageObject) ? null : $imageObject->getCropCoordinates();
95
96
        if (!empty($styleName)) {
97
            // $paths = [];
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
98
            $paths = [$styleName => $paths[$styleName]];
99
        }
100
        else {
101
            unset($paths[0]);
102
        }
103
104
        foreach ($paths as $styleKey => $files) {
105
            $style = $this->styleManager->getStyle($styleKey);
106
            $stylePath = $this->system->getStorageDirectory('styled', NULL, $styleKey);
107
            $image = $this->imager->createImage($filePath, $stylePath, $style, $crop);
0 ignored issues
show
$style is of type boolean, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
108
        }
109
110
        return $image;
0 ignored issues
show
The variable $image 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...
111
    }
112
113
    /**
114
     * Creates all styled images for a given image object.
115
     * If optional stylename if given only that style will be created.
116
     *
117
     * @param ResponsiveImageInterface $image
118
     * @paran string $stylename
119
     * @return image
120
     */
121
    public function createStyledImages(ResponsiveImageInterface $image, $stylename = NULL)
122
    {
123
        $this->setImages($image);
124
        $image = $this->createImageDerivative($image, $stylename);
125
126
        // Do the the transfer if required.
127
        if ($this->belongsOnS3('styled')) {
128
            if (!$this->belongsOnS3('original')) {
129
                if (!empty($this->images[0])) {
130
                    unset($this->images[0]);
131
                }
132
            }
133
            $this->doS3Transfer();
134
        }
135
136
        // Cleanup any temp files.
137
        $this->cleanUp();
138
139
        return $image;
140
    }
141
142
    /**
143
     * Deletes images files associated with an image object
144
     *
145
     * @param ResponsiveImageInterface $image
146
     * @param bool
147
     * @param bool
148
     */
149
    public function deleteImageFiles(ResponsiveImageInterface $image, $deleteOriginal = TRUE, $deleteStyled = TRUE) {
150
        // Create an array of images to work,
151
        $this->setImages($image, $deleteOriginal, $deleteStyled);
152
153
        // Delete the local files.
154
        foreach ($this->images as $paths) {
155
            $this->system->deleteFile($paths[0]);
156
        }
157
158
        // Delete S3 files.
159
        if ($this->belongsOnS3()) {
160
            $paths = $this->getS3ObjectKeys();
161
            if (!empty($paths)) {
162
                $this->s3->setPaths($paths);
0 ignored issues
show
The method setPaths cannot be called on $this->s3 (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
163
                $this->s3->removeFromS3();
0 ignored issues
show
The method removeFromS3 cannot be called on $this->s3 (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
164
            }
165
        }
166
    }
167
168
    /**
169
     * Delete an images styled derivatives.
170
     *
171
     * @param array $styles
172
     */
173
    public function deleteStyleFiles(array $styles)
174
    {
175
        // @TODO: at yet implemented.
176
        if (empty($styles)) {
0 ignored issues
show
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...
177
            // Delete all styled files.
178
        }
179
        else {
0 ignored issues
show
This else statement is empty and can be removed.

This check looks for the else branches 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 else branches can be removed.

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

could be turned into

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

This is much more concise to read.

Loading history...
180
            // Delete files for the given style.
181
        }
182
    }
183
184
    /**
185
     * Transfer files in the $images array to the configured S3 bucket.
186
     */
187
    private function doS3Transfer()
188
    {
189
        $paths = $this->getS3ObjectKeys();
190
        if (!empty($paths)) {
191
            $this->s3->setPaths($paths);
0 ignored issues
show
The method setPaths cannot be called on $this->s3 (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
192
            $this->s3->uploadToS3();
0 ignored issues
show
The method uploadToS3 cannot be called on $this->s3 (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
193
        }
194
195
        // Delete temp files.
196
        $this->cleanUp();
197
    }
198
199
    /**
200
     * Returns an array of keys and locations for S3 transfers.
201
     */
202
    private function getS3ObjectKeys() {
203
        $keys = [];
204
        foreach ($this->images as $style => $locations) {
205
            $keys[$locations[0]] = $locations[1];
206
        }
207
208
        return $keys;
209
    }
210
211
    /**
212
     * Returns the location fo the original source file and fetches if it's stored remotely.
213
     *
214
     * @param $image
215
     * @return string
216
     */
217
    private function findSourceFile($image) {
218
        $filename = $image->getPath();
219
        $fetchFromS3 = FALSE;
220
        if (!empty($this->images[0])) {
221
            return $this->images[0][0];
222
        }
223
        else {
224
            // The original file is in difference places depending on the local file policy.
225
            $directory = $this->system->getStorageDirectory('original');
226
            $path = $directory . $filename;
227
            
228
            // Check if the file exists on the server.
229
            if (!$this->system->fileExists($filename)) {
230
                $fetchFromS3 =  TRUE;
231
            }
232
            $tree = $this->system->getUploadsDirectory() . '/' . $filename;
233
            // If the policy was set to keep no files locally, then original should be downloaded from s3.
234
            if (!empty($fetchFromS3)) {
235
                $s3key = empty($this->config['aws_s3']['directory']) ? $tree :  $this->config['aws_s3']['directory'] . '/' . $tree;
236
                $this->system->directoryExists($directory , TRUE);
237
                $this->s3->fetchS3Object($path, $s3key);
0 ignored issues
show
The method fetchS3Object cannot be called on $this->s3 (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
238
            }
239
240
            $this->images[0] = [$path, $tree];
241
        }
242
243
        return $path;
244
    }
245
246
    /**
247
     * @return bool
248
     */
249
    private function s3enabled() {
250
        $enabled = FALSE;
251
        if (!empty($this->config['aws_s3'])) {
252
            $enabled = empty($this->config['aws_s3']['enabled']) ? FALSE : TRUE;
253
        }
254
255
        return $enabled;
256
    }
257
258
    /**
259
     * Checks if files should be transferred to S3 bucket or not.
260
     *
261
     * @Param string
262
     * @return bool
263
     */
264
    private function belongsOnS3($imageType = 'styled')
265
    {
266
        $enabled = $this->s3enabled();
267
        if ($enabled) {
268
            $aws_config = $this->config['aws_s3'];
269
            $remoteFilePolicy = empty($aws_config['remote_file_policy']) ? 'ALL': $aws_config['remote_file_policy'];
270
271
            // If AWS is enabled.
272
            if ($enabled) {
273
                // Styled images are always transferred.
274
                if ($imageType == 'styled') {
275
                    return TRUE;
276
                }
277
                // Originals are only transferred if remote_file_policy is set to ALL.
278
                else if ($imageType == 'original') {
279
                    if ($remoteFilePolicy == 'ALL') {
280
                        return TRUE;
281
                    }
282
                }
283
            }
284
        }
285
        return FALSE;
286
    }
287
288
    /**
289
     * Generates CSS for a background image with media queries.
290
     *
291
     * @param ResponsiveImageInterface $image
292
     * @param $pictureSet
293
     * @param $selector
294
     * @return string
295
     */
296
    public function createCSS(ResponsiveImageInterface $image, $pictureSet, $selector) {
297
        return $this->styleManager->createBackgroundImageCSS($image, $pictureSet, $selector);
0 ignored issues
show
The method createBackgroundImageCSS() does not seem to exist on object<IrishDan\Responsi...ageBundle\StyleManager>.

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...
298
    }
299
300
    /**
301
     * Builds an array of image paths needed for image creation, deletion and transferring.
302
     *
303
     * @param $image
304
     * @param bool $original
305
     * @param bool $styled
306
     */
307
    private function setImages($image, $original = TRUE, $styled = TRUE) {
308
        $filename = $image->getPath();
309
        $styles = $this->styleManager->getAllStyles();
310
311
        // This adds the orginal path and style tree to the $images array.
312
        if ($original) {
313
            $this->findSourceFile($image);
314
        }
315
316
        // Create an array of paths and styles trees
317
        if (!empty($filename) && $styled) {
318
            foreach ($styles as $stylename => $style) {
319
                $stylePath = $this->system->getStorageDirectory('styled', NULL, $stylename);
320
                $styleTree = $this->system->getStyleTree($stylename);
321
                $this->images[$stylename] = [$stylePath . $filename, $styleTree . '/' . $filename];
322
            }
323
        }
324
    }
325
326
    /**
327
     * Sets the image style for image rendering.
328
     *
329
     * @param ResponsiveImageInterface $image
330
     * @param $styleName
331
     * @return ResponsiveImageInterface
332
     */
333
    public function setImageStyle(ResponsiveImageInterface $image, $styleName) {
334
        $this->styleManager->setImageStyle($image, $styleName);
335
        return $image;
336
    }
337
338
    /**
339
     * Sets the picture set for image rendering.
340
     *
341
     * @param ResponsiveImageInterface $image
342
     * @param $pictureSet
343
     * @return ResponsiveImageInterface
344
     */
345
    public function setPictureSet(ResponsiveImageInterface $image, $pictureSet)
346
    {
347
        $this->styleManager->setPictureImage($image, $pictureSet);
0 ignored issues
show
The method setPictureImage() does not seem to exist on object<IrishDan\Responsi...ageBundle\StyleManager>.

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...
348
        return $image;
349
    }
350
351
    /**
352
     * Uploads an image file
353
     *
354
     * @param ResponsiveImageInterface $image
355
     * @return ResponsiveImageInterface
356
     */
357
    public function uploadImage(ResponsiveImageInterface $image)
358
    {
359
        $image = $this->uploader->upload($image);
360
361
        // Transfer to S3 if needed.
362
        if ($this->belongsOnS3('original')) {
363
            $this->setImages($image, TRUE, FALSE);
364
            $this->doS3Transfer();
365
        }
366
367
        $this->images = array();
368
369
        return $image;
370
    }
371
}