Completed
Push — master ( a5450c...7ec76e )
by ReliQ
09:15 queued 10s
created

Guider::dummy()   B

Complexity

Conditions 8
Paths 128

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 8.2111
c 0
b 0
f 0
cc 8
nc 128
nop 5
1
<?php
2
3
namespace ReliqArts\GuidedImage\Traits;
4
5
use File;
6
use Illuminate\Config\Repository as Config;
7
use Illuminate\Http\JsonResponse;
8
use Illuminate\Http\Request;
9
use Illuminate\Http\Response;
10
use Intervention\Image\Exception\NotReadableException;
11
use Intervention\Image\Facades\Image;
12
use Intervention\Image\Image as InterventionImage;
13
use ReliqArts\GuidedImage\Contracts\Guided as GuidedContract;
14
use ReliqArts\GuidedImage\ViewModels\Result;
15
16
/**
17
 * Guide by acquiring these traits.
18
 *
19
 * @author Patrick Reid (@IAmReliQ)
20
 *
21
 * @since  2016
22
 *
23
 * @uses \Intervention\Image\Facades\Image to manipulate images.
24
 * @uses \ReliqArts\GuidedImage\ViewModels\Result
25
 */
26
trait Guider
27
{
28
    /**
29
     * List of headers.
30
     *
31
     * @var array
32
     */
33
    protected $headers = [];
34
35
    /**
36
     * Guided image cache directory.
37
     *
38
     * @var string
39
     */
40
    protected $skimDir;
41
42
    /**
43
     * Thumbnail cache directory.
44
     *
45
     * @var string
46
     */
47
    protected $skimThumbs;
48
49
    /**
50
     * Resized images cache directory.
51
     *
52
     * @var string
53
     */
54
    protected $skimResized;
55
56
    /**
57
     * Route values to be treated as null.
58
     *
59
     * @var array
60
     */
61
    protected $nulls = [false, null, 'null'];
62
63
    /**
64
     * Constructor. Some prep.
65
     *
66
     * @param Config $config
67
     */
68
    public function __construct(Config $config)
69
    {
70
        $this->skimDir = storage_path($config->get('guidedimage.storage.skim_dir'));
71
        $this->skimThumbs = "{$this->skimDir}/" . $config->get('guidedimage.storage.skim_thumbs');
72
        $this->skimResized = "{$this->skimDir}/" . $config->get('guidedimage.storage.skim_resized');
73
        $this->nulls = array_merge($this->nulls, $config->get('guidedimage.routes.nulls', []));
74
75
        // create or avail needed directories
76
        if (!File::isDirectory($this->skimThumbs)) {
77
            File::makeDirectory($this->skimThumbs, 0777, true);
78
        }
79
        if (!File::isDirectory($this->skimResized)) {
80
            File::makeDirectory($this->skimResized, 0777, true);
81
        }
82
83
        // setup preliminary headers
84
        $maxAge = 60 * 60 * 24 * $config->get('guidedimage.headers.cache_days'); // x days
85
        // default headers
86
        $this->headers = array_merge([
87
            'Cache-Control' => "public, max-age=${maxAge}",
88
        ], $config->get('guidedimage.headers.additional', []));
89
    }
90
91
    /**
92
     * Empty skim cache by removing SkimDir.
93
     *
94
     * @param Request $request
95
     *
96
     * @return JsonResponse
97
     */
98
    public function emptyCache(Request $request): JsonResponse
99
    {
100
        if (!$request->ajax()) {
101
            return 'Use JSON.';
102
        }
103
104
        $result = new Result();
105
        if (File::deleteDirectory($this->skimDir, true)) {
106
            $result->success = true;
107
            $result->message = 'Image directory cache successfully cleared.';
108
        } else {
109
            $result->message = 'Image directory cache could not be cleared.';
110
            $result->error = $result->message;
111
        }
112
113
        return response()->json($result);
0 ignored issues
show
Documentation introduced by
$result is of type object<ReliqArts\GuidedImage\ViewModels\Result>, but the function expects a string|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...
114
    }
115
116
    /**
117
     * Get a thumbnail.
118
     *
119
     * @param Request     $request
120
     * @param Guided      $guidedImage
0 ignored issues
show
introduced by
The type Guided for parameter $guidedImage is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
121
     * @param string      $method      crop|fit
122
     * @param int|string  $width
123
     * @param int|string  $height
124
     * @param bool|string $object      whether Intervention Image should be returned
125
     *
126
     * @return \Intervention\Image\Facades\Image|string intervention Image object or actual image url
127
     */
128
    public function thumb(Request $request, GuidedContract $guidedImage, string $method, $width, $height, $object = false)
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 122 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
129
    {
130
        $width = (in_array($width, $this->nulls, true)) ? null : $width;
131
        $height = (in_array($height, $this->nulls, true)) ? null : $height;
132
        $object = (in_array($object, $this->nulls, true)) ? null : true;
133
134
        $skimFile = "{$this->skimThumbs}/${width}-${height}-_-_" . $guidedImage->getName();
135
136
        // accept methods crop and thumb
137
        $acceptMethods = ['crop', 'fit'];
138
        if (!in_array($method, $acceptMethods, true)) {
139
            abort(404);
140
        }
141
        // Get intervention image
142
        try {
143
            if (!File::exists($skimFile)) {
144
                $image = Image::make($guidedImage->getUrl())->{$method}($width, $height);
145
                $image->save($skimFile);
146
            } else {
147
                $image = Image::make($skimFile);
148
            }
149
        } catch (NotReadableException $e) {
150
            abort(404);
151
        }
152
153
        // Setup response with appropriate headers
154
        return ($object) ? $image : new Response(
0 ignored issues
show
Bug introduced by
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...
155
            File::get($skimFile),
156
            200,
157
            $this->getImageHeaders($request, $image) ?: []
158
        );
159
    }
160
161
    /**
162
     * Get a resized Guided Image.
163
     *
164
     * @param Request     $request
165
     * @param Guided      $guidedImage
0 ignored issues
show
introduced by
The type Guided for parameter $guidedImage is a trait, and thus cannot be used for type-hinting in PHP. Maybe consider adding an interface and use that for type-hinting?
Loading history...
166
     * @param int|string  $width
167
     * @param int|string  $height
168
     * @param bool|string $aspect      Keep aspect ratio?
169
     * @param bool|string $upsize      Allow upsize?
170
     * @param bool|string $object      whether Intervention Image should be returned
171
     *
172
     * @return \Intervention\Image\Facades\Image|string intervention Image object or actual image url
173
     */
174
    public function resized(
175
        Request $request,
176
        GuidedContract $guidedImage,
177
        $width,
178
        $height,
179
        $aspect = true,
180
        $upsize = false,
181
        $object = false
182
    ) {
183
        $width = (in_array($width, $this->nulls, true)) ? null : $width;
184
        $height = (in_array($height, $this->nulls, true)) ? null : $height;
185
        $aspect = (in_array($aspect, $this->nulls, true)) ? true : false;
186
        $upsize = (in_array($upsize, $this->nulls, true)) ? false : true;
187
        $object = (in_array($object, $this->nulls, true)) ? false : true;
188
189
        $skimFile = "{$this->skimResized}/${width}-${height}-_-_" . $guidedImage->getName();
190
        $image = false;
191
192
        // Get intervention image
193
        try {
194
            if (!File::exists($skimFile)) {
195
                $image = Image::make($guidedImage->getUrl());
196
                $image->resize($width, $height, function ($constraint) use ($aspect, $upsize) {
197
                    if ($aspect) {
198
                        $constraint->aspectRatio();
199
                    }
200
                    if ($upsize) {
201
                        $constraint->upsize();
202
                    }
203
                });
204
                $image->save($skimFile);
205
            } else {
206
                $image = Image::make($skimFile);
207
            }
208
        } catch (NotReadableException $e) {
209
            $image = false;
210
        }
211
212
        // if no image; abort
213
        if (!$image) {
214
            abort(404);
215
        }
216
217
        // Setup response with appropriate headers
218
        return ($object) ? $image : new Response(
219
            File::get($skimFile),
220
            200,
221
            $this->getImageHeaders($request, $image) ?: []
222
        );
223
224
        // Return object or actual image
225
    }
226
227
    /**
228
     * Get dummy Guided Image.
229
     *
230
     * @param int|string   $width
231
     * @param int|string   $height
232
     * @param string       $color
233
     * @param bool|string  $fill
234
     * @param mixed|string $object
235
     *
236
     * @return \Intervention\Image\Facades\Image|string intervention Image object or actual image url
237
     */
238
    public function dummy($width, $height, $color = '#eefefe', $fill = false, $object = false)
239
    {
240
        $width = (in_array($width, $this->nulls, true)) ? null : $width;
241
        $height = (in_array($height, $this->nulls, true)) ? null : $height;
242
        $color = (in_array($color, $this->nulls, true)) ? null : $color;
243
        $fill = (in_array($fill, $this->nulls, true)) ? null : $fill;
244
        $object = (in_array($object, $this->nulls, true)) ? false : true;
245
246
        $img = Image::canvas($width, $height, $color);
247
        $image = ($fill) ? $img->fill($fill) : $img;
248
249
        // Return object or actual image
250
        return ($object) ? $image : $image->response();
251
    }
252
253
    /**
254
     * Get image headers. Improved caching
255
     * If the image has not been modified say 304 Not Modified.
256
     *
257
     * @param \Intervention\Image\Facades\Image $image
0 ignored issues
show
Documentation introduced by
Should the type for parameter $image not be InterventionImage?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
258
     *
259
     * @return array image headers
260
     */
261
    private function getImageHeaders(Request $request, InterventionImage $image): array
262
    {
263
        $filePath = "{$image->dirname}/{$image->basename}";
264
        $lastModified = File::lastModified($filePath);
265
        $modifiedSince = ($request->header('If-Modified-Since')) ? $request->header('If-Modified-Since') : false;
266
        $etagHeader = ($request->header('If-None-Match')) ? trim($request->header('If-None-Match')) : null;
267
        $etagFile = md5_file($filePath);
268
269
        // check if image hasn't changed
270
        if (@strtotime($modifiedSince) === $lastModified || $etagFile === $etagHeader) {
271
            // Say not modified and kill script
272
            header('HTTP/1.1 304 Not Modified');
273
            header("ETag: ${etagFile}");
274
            exit;
275
        }
276
277
        // adjust headers and return
278
        return $this->headers = array_merge($this->headers, [
279
            'Content-Type' => $image->mime,
280
            'Content-Disposition' => 'inline; filename=' . $image->filename,
281
            'Last-Modified' => date(DATE_RFC822, $lastModified),
282
            'Etag' => $etagFile,
283
        ]);
284
    }
285
}
286