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

Guided::getUrl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace ReliqArts\GuidedImage\Traits;
4
5
use File;
6
use Illuminate\Database\Eloquent\Relations\BelongsTo;
7
use ReliqArts\GuidedImage\Exceptions\BadImplementation;
8
use ReliqArts\GuidedImage\Helpers\Config;
9
use ReliqArts\GuidedImage\ViewModels\Result;
10
use URL;
11
use Validator;
12
13
/**
14
 * Get guided by acquiring these traits.
15
 *
16
 * @author Patrick Reid (@IAmReliQ)
17
 *
18
 * @since  2016
19
 *
20
 * @uses \ReliqArts\GuidedImage\ViewModels\Result;
21
 */
22
trait Guided
23
{
24
    /**
25
     * The rules that govern a guided image.
26
     */
27
    public static $rules = ['file' => 'required|mimes:png,gif,jpeg|max:2048'];
28
29
    /**
30
     * Class name.
31
     *
32
     * @var string
33
     */
34
    private $className;
35
36
    /**
37
     * Mandatory ancestor eloguent model.
38
     *
39
     * @var string
40
     */
41
    private $eloquentAncestor = 'Illuminate\Database\Eloquent\Model';
42
43
    /**
44
     * Ensure things are ready.
45
     */
46
    public function __construct(array $attributes = [])
47
    {
48
        $this->className = get_class($this);
49
        // Instance must be of class which extends Eloquent Model.
50
        if (!is_subclass_of($this, $this->eloquentAncestor)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $this->eloquentAncestor can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
51
            throw new BadImplementation("Guided model ({$this->className}) must extend {$this->eloquentAncestor}.");
52
        }
53
54
        parent::__construct($attributes);
55
    }
56
57
    /**
58
     * Retrieve the creator (uploader) of the image.
59
     */
60
    public function creator(): BelongsTo
61
    {
62
        return $this->belongsTo('App\User', 'creator_id');
0 ignored issues
show
Bug introduced by
It seems like belongsTo() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
63
    }
64
65
    /**
66
     * Whether image is safe for deleting.
67
     * Since a single image may be re-used this method is used to determine
68
     * when an image can be safely deleted from disk.
69
     *
70
     * @param int $safeAmount a photo is safe to delete if it is used by $safe_num amount of records
71
     *
72
     * @return bool whether image is safe for delete
73
     */
74
    public function isSafeForDelete(int $safeAmount = 1): bool
0 ignored issues
show
Unused Code introduced by
The parameter $safeAmount 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...
75
    {
76
        return true;
77
    }
78
79
    /**
80
     * Removes image from database, and filesystem, if not in use.
81
     *
82
     * @param bool $force override safety constraints
83
     *
84
     * @return Result
85
     */
86
    public function remove(bool $force = false): Result
87
    {
88
        $result = new Result();
89
        $img_name = $this->getName();
0 ignored issues
show
Unused Code introduced by
$img_name is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
90
        $safe = $this->isSafeForDelete();
91
92
        if ($safe || $force) {
93
            if (File::delete(urldecode($this->getFullPath()))) {
94
                $this->delete();
0 ignored issues
show
Bug introduced by
The method delete() does not exist on ReliqArts\GuidedImage\Traits\Guided. Did you maybe mean isSafeForDelete()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
95
            }
96
            $result->success = true;
97
        } else {
98
            $result->message = 'Not safe to delete, hence file not removed.';
99
        }
100
101
        return $result;
102
    }
103
104
    /**
105
     * Get routed link to photo.
106
     *
107
     * @param array  $params parameters to pass to route
0 ignored issues
show
Documentation introduced by
Should the type for parameter $params not be null|array? Also, consider making the array more specific, something like array<String>, or String[].

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. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

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

Loading history...
108
     * @param string $type   Operation to be performed on instance. (resize, thumb)
109
     *
110
     * @return string
111
     */
112
    public function routeResized(array $params = null, string $type = 'resize'): string
113
    {
114
        $guidedModel = strtolower(Config::getRouteModel(true));
115
116
        if (!(in_array($type, ['resize', 'thumb'], true) && is_array($params))) {
117
            return $this->url();
0 ignored issues
show
Bug introduced by
It seems like url() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
118
        }
119
        array_unshift($params, $this->id);
0 ignored issues
show
Bug introduced by
The property id does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
120
121
        return route("{$guidedModel}.{$type}", $params);
122
    }
123
124
    /**
125
     * Get class.
126
     *
127
     * @return string
128
     */
129
    public function getClassName(): string
130
    {
131
        return $this->className;
132
    }
133
134
    /**
135
     *  Get ready URL to image.
136
     *
137
     * @return string
138
     */
139
    public function getUrl(): string
140
    {
141
        return urldecode($this->getFullPath());
142
    }
143
144
    /**
145
     *  Get ready image title.
146
     *
147
     * @return string
148
     */
149
    public function getTitle(): string
150
    {
151
        return title_case(preg_replace('/[\\-_]/', ' ', $this->getName()));
152
    }
153
154
    /**
155
     * Get full path.
156
     *
157
     * @return string
158
     */
159
    public function getFullPath(): string
160
    {
161
        return $this->full_path;
0 ignored issues
show
Bug introduced by
The property full_path does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
162
    }
163
164
    /**
165
     * Get name.
166
     *
167
     * @return string
168
     */
169
    public function getName(): string
170
    {
171
        return $this->name;
0 ignored issues
show
Bug introduced by
The property name does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
172
    }
173
174
    /**
175
     * Get upload directory.
176
     *
177
     * @return string upload directory
178
     */
179
    public static function getUploadDir(): string
180
    {
181
        return Config::get('guidedimage.upload_dir');
182
    }
183
184
    /**
185
     *  Upload and save image.
186
     *
187
     * @param \Illuminate\Http\UploadedFile|Symfony\Component\HttpFoundation\File\UploadedFile $imageFile File
188
     *                                                                                                    from request.e.g. $request->file('image');
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 148 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...
189
     *
190
     * @return Result
191
     */
192
    public static function upload($imageFile): Result
193
    {
194
        $result = new Result();
195
        $validator = Validator::make(['file' => $imageFile], self::$rules);
196
        $extWhitelist = Config::get('guidedimage.allowed_extensions', ['gif', 'jpg', 'jpeg', 'png']);
197
        $result->message = 'Invalid file size or type.';
198
        $result->error = 'Invalid image.';
199
200
        if ($validator->passes()) {
201
            $size = $imageFile->getSize();
202
            $mimeType = $imageFile->getMimeType();
203
            $extension = $imageFile->getClientOriginalExtension();
204
            $fullName = $imageFile->getClientOriginalName();
205
            $filePathInfo = pathinfo($fullName);
206
            $filename = str_slug($filePathInfo['filename']);
207
            $existing = self::where('name', $filename)->where('size', $size);
208
209
            // explicitly check extension against whitelist
210
            if (in_array(strtolower($extension), $extWhitelist, true)) {
211
                if (!$existing->count()) {
212
                    $im['size'] = $size;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$im was never initialized. Although not strictly required by PHP, it is generally a good practice to add $im = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
213
                    $im['name'] = $filename;
214
                    $im['mime_type'] = $mimeType;
215
                    $im['extension'] = $extension;
216
                    $im['location'] = self::getUploadDir();
217
                    $im['creator_id'] = auth()->user()->id;
0 ignored issues
show
Bug introduced by
The method user does only exist in Illuminate\Contracts\Auth\Guard, but not in Illuminate\Contracts\Auth\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
218
                    $im['full_path'] = urlencode($im['location'] . '/' . $filename . '.' . $im['extension']);
219
                    list($im['width'], $im['height']) = getimagesize($imageFile);
220
221
                    try {
222
                        $file = $imageFile->move($im['location'], $im['name'] . '.' . $im['extension']);
0 ignored issues
show
Unused Code introduced by
$file is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
223
                        $newImage = new self();
224
225
                        // file moved, save
226
                        $newImage->fill($im);
0 ignored issues
show
Bug introduced by
It seems like fill() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
227
                        if ($newImage->save()) {
0 ignored issues
show
Bug introduced by
It seems like save() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
228
                            $result->extra = $newImage;
229
                            $result->success = true;
230
                            $result->error = null;
231
                        }
232
                    } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class ReliqArts\GuidedImage\Traits\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
233
                        $result->error = $e->getMessage();
234
                        $result->message = null;
235
                    }
236
                } else {
237
                    $result->extra = $existing->first();
238
                    $result->message = 'Image reused.';
239
                    $result->success = true;
240
                    $result->error = null;
241
                }
242
            }
243
        }
244
245
        return $result;
246
    }
247
}
248