Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Issues (892)

Branch: main

src/app/Library/Widget.php (10 issues)

1
<?php
2
3
namespace Backpack\CRUD\app\Library;
4
5
use Backpack\CRUD\app\Exceptions\BackpackProRequiredException;
6
use Backpack\CRUD\ViewNamespaces;
7
use Illuminate\Support\Fluent;
8
9
/**
10
 * Adds fluent syntax to Backpack Widgets.
11
 */
12
class Widget extends Fluent
13
{
14
    protected $attributes = [];
15
16
    public function __construct($attributes)
17
    {
18
        $this->attributes = $attributes;
19
20
        $this->save();
21
    }
22
23
    /**
24
     * Add a new widget to the widgets collection in the Laravel Service Container.
25
     * If a widget with the same name exists, it will update the attributes of that one
26
     * instead of creating a new one.
27
     *
28
     * @param  string|array  $attributes  Either the name of the widget, or an array with the attributes the new widget should hold, including the name attribute.
29
     * @return Widget
30
     */
31
    public static function add($attributes = null)
32
    {
33
        // make sure the widget has a name
34
        $attributes = is_string($attributes) ? ['name' => $attributes] : $attributes;
35
        $attributes['name'] = $attributes['name'] ?? 'widget_'.rand(1, 999999999);
36
37
        // if that widget name already exists in the widgets collection
38
        // then pick up all widget attributes from that entry
39
        // and overwrite them with the ones passed in $attributes
40
        if ($existingItem = self::collection()->filter(function ($item) use ($attributes) {
41
            return $item->attributes['name'] === $attributes['name'];
42
        })->first()) {
43
            $attributes = array_merge($existingItem->attributes, $attributes);
0 ignored issues
show
It seems like $attributes can also be of type null; however, parameter $arrays of array_merge() does only seem to accept array, 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

43
            $attributes = array_merge($existingItem->attributes, /** @scrutinizer ignore-type */ $attributes);
Loading history...
44
        }
45
46
        // set defaults for other mandatory attributes
47
        $attributes['section'] = $attributes['section'] ?? 'before_content';
48
        $attributes['type'] = $attributes['type'] ?? 'card';
49
50
        return new static($attributes);
51
    }
52
53
    /**
54
     * Return the widget attribute value or null when it doesn't exist.
55
     *
56
     * @param  string  $attribute
57
     * @return mixed
58
     */
59
    public function getAttribute(string $attribute)
60
    {
61
        return $this->attributes[$attribute] ?? null;
62
    }
63
64
    /**
65
     * Check if widget has the attribute.
66
     *
67
     * @param  string  $attribute
68
     * @return bool
69
     */
70
    public function hasAttribute(string $attribute)
71
    {
72
        return array_key_exists($attribute, $this->attributes);
73
    }
74
75
    /**
76
     * This method allows one to creat a widget without attaching it to any 'real'
77
     * widget section, by moving it to a 'hidden' section.
78
     *
79
     * It exists for one reason: so that developers can add widgets to a custom array, without
80
     * adding them to one of the widget sections.
81
     *
82
     * Ex: when developers need to pass multiple widgets as contents of the
83
     * div widget. But they don't want them added to the before_content of after_content
84
     * sections. So what they do is basically add them to a 'hidden' section, that nobody will ever see.
85
     *
86
     * @return Widget
87
     */
88
    public static function make($attributes = null)
89
    {
90
        $widget = static::add($attributes);
91
        $widget->section('hidden');
0 ignored issues
show
The method section() does not exist on Backpack\CRUD\app\Library\Widget. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

91
        $widget->/** @scrutinizer ignore-call */ 
92
                 section('hidden');
Loading history...
92
93
        return $widget;
94
    }
95
96
    /**
97
     * Remove an attribute from the current definition array.
98
     *
99
     * @param  string  $attribute  Name of the attribute to forget (ex: class)
100
     * @return Widget
101
     */
102
    public function forget($attribute)
103
    {
104
        $this->offsetUnset($attribute);
0 ignored issues
show
$attribute of type string is incompatible with the type Illuminate\Support\TKey expected by parameter $offset of Illuminate\Support\Fluent::offsetUnset(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

104
        $this->offsetUnset(/** @scrutinizer ignore-type */ $attribute);
Loading history...
105
106
        return $this;
107
    }
108
109
    // TODO: add ability to push a widget right after another widget
110
    public function after($destination)
0 ignored issues
show
The parameter $destination is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

110
    public function after(/** @scrutinizer ignore-unused */ $destination)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
111
    {
112
    }
113
114
    // TODO: add ability to push a widget right before another widget
115
    public function before($destination)
0 ignored issues
show
The parameter $destination is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

115
    public function before(/** @scrutinizer ignore-unused */ $destination)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
116
    {
117
    }
118
119
    /**
120
     * Make this widget the first one in its section.
121
     *
122
     * @return Widget
123
     */
124
    public function makeFirst()
125
    {
126
        $this->collection()->pull($this->attributes['name']);
127
        $this->collection()->prepend($this);
128
129
        return $this;
130
    }
131
132
    /**
133
     * Make this widget the last one in its section.
134
     *
135
     * @return Widget
136
     */
137
    public function makeLast()
138
    {
139
        $this->collection()->pull($this->attributes['name']);
140
        $this->collection()->push($this);
141
142
        return $this;
143
    }
144
145
    /**
146
     * Get an array of full paths to the widget view, consisting of:
147
     * - the path given in the widget definition
148
     * - fallback view paths as configured in backpack/config/base.php.
149
     *
150
     * @return array
151
     */
152
    public function getFinalViewPath()
153
    {
154
        if (isset($this->attributes['viewNamespace'])) {
155
            $path = $this->attributes['viewNamespace'].'.'.$this->attributes['type'];
156
157
            if (view()->exists($path)) {
158
                return $path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $path returns the type string which is incompatible with the documented return type array.
Loading history...
159
            }
160
        }
161
        $type = $this->attributes['type'];
162
        $paths = array_map(function ($item) use ($type) {
163
            return $item.'.'.$type;
164
        }, ViewNamespaces::getWithFallbackFor('widgets', 'backpack.ui.component_view_namespaces.widgets'));
165
166
        foreach ($paths as $path) {
167
            if (view()->exists($path)) {
168
                return $path;
169
            }
170
        }
171
        // if no view exists, in any of the directories above... no bueno
172
        if (! backpack_pro()) {
173
            throw new BackpackProRequiredException('Cannot find the widget view: '.$this->attributes['type'].'. Please check for typos.'.(backpack_pro() ? '' : ' If you are trying to use a PRO widget, please first purchase and install the backpack/pro addon from backpackforlaravel.com'), 1);
174
        }
175
        abort(500, 'Cannot find the view for «'.$this->attributes['type'].'» widget type. Please check for typos.', ['developer-error-exception']);
176
    }
177
178
    // -------
179
    // ALIASES
180
    // -------
181
    // Aka convenience methods.
182
    // These method just call other methods.
183
184
    // Alias of add()
185
    public static function name(...$args)
186
    {
187
        return static::add(...$args);
0 ignored issues
show
$args is expanded, but the parameter $attributes of Backpack\CRUD\app\Library\Widget::add() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

187
        return static::add(/** @scrutinizer ignore-type */ ...$args);
Loading history...
188
    }
189
190
    // Alias of section()
191
    public function to(...$args)
192
    {
193
        return $this->section(...$args);
194
    }
195
196
    // Alias of section()
197
    public function group(...$args)
198
    {
199
        return $this->section(...$args);
200
    }
201
202
    // Alias of viewNamespace()
203
    public function from(...$args)
204
    {
205
        return $this->viewNamespace(...$args);
0 ignored issues
show
The method viewNamespace() does not exist on Backpack\CRUD\app\Library\Widget. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

205
        return $this->/** @scrutinizer ignore-call */ viewNamespace(...$args);
Loading history...
206
    }
207
208
    // ------------------
209
    // COLLECTION METHODS
210
    // ------------------
211
    // Manipulate the global widget collection.
212
213
    public static function collection()
214
    {
215
        return app('widgets');
216
    }
217
218
    /**
219
     * Remove the widget from its section.
220
     *
221
     * @return Widget
222
     */
223
    public function remove()
224
    {
225
        $this->collection()->pull($this->attributes['name']);
226
227
        return $this;
228
    }
229
230
    /**
231
     * This alias of remove() exists for one reason: so that developers can add
232
     * widgets to a custom array, instead of adding them to one of the widget
233
     * sections. Ex: when developers need to pass multiple widgets as contents of the
234
     * div widget. But they don't want them added to the before_content of after_content
235
     * sections. So what they do is basically add them to a section, then remove them.
236
     * What's left is the widget itself, but without being attached to any section.
237
     *
238
     * @return Widget
239
     */
240
    public function onlyHere(...$args)
241
    {
242
        return $this->remove(...$args);
0 ignored issues
show
The call to Backpack\CRUD\app\Library\Widget::remove() has too many arguments starting with $args. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

242
        return $this->/** @scrutinizer ignore-call */ remove(...$args);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
243
    }
244
245
    // ---------------
246
    // PRIVATE METHODS
247
    // ---------------
248
249
    /**
250
     * Update the global CrudPanel object with the current widget attributes.
251
     *
252
     * @return Widget
253
     */
254
    private function save()
255
    {
256
        $itemExists = $this->collection()->filter(function ($item) {
257
            return $item->attributes['name'] === $this->attributes['name'];
258
        })->isNotEmpty();
259
        if (! $itemExists) {
260
            $this->collection()->put($this->attributes['name'], $this);
261
        } else {
262
            $this->collection()[$this->attributes['name']] = $this;
263
        }
264
265
        return $this;
266
    }
267
268
    // -----------------
269
    // DEBUGGING METHODS
270
    // -----------------
271
272
    /**
273
     * Dump the current object to the screen,
274
     * so that the developer can see its contents.
275
     *
276
     * @return Widget
277
     */
278
    public function dump()
279
    {
280
        dump($this);
281
282
        return $this;
283
    }
284
285
    /**
286
     * Dump and die. Dumps the current object to the screen,
287
     * so that the developer can see its contents, then stops
288
     * the execution.
289
     *
290
     * @return Widget
291
     */
292
    public function dd()
293
    {
294
        dd($this);
295
296
        return $this;
297
    }
298
299
    /**
300
     * Overwritten methods to prevent BC in Laravel 11, since they introduced the `value()` method
301
     * in their Fluent class. Although the Widget class is Fluent, it does not behave the same
302
     * in regards to `value()`, since we use it as a key in widget definition.
303
     */
304
    public function value($value, $default = null)
0 ignored issues
show
The parameter $default is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

304
    public function value($value, /** @scrutinizer ignore-unused */ $default = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
305
    {
306
        $this->attributes['value'] = $value;
307
308
        return $this->save();
309
    }
310
311
    #[\ReturnTypeWillChange]
312
    public function offsetGet($offset): mixed
313
    {
314
        return $this->get($offset);
315
    }
316
317
    public function __get($key)
318
    {
319
        return $this->get($key);
320
    }
321
322
    // -------------
323
    // MAGIC METHODS
324
    // -------------
325
326
    /**
327
     * Any call to a non-existing method on this class will be assumed to be
328
     * an attribute that the developer wants to add to that particular widget.
329
     *
330
     * Eg: class('something') will set the "class" attribute to "something"
331
     *
332
     * @param  string  $method  The method being called that doesn't exist.
333
     * @param  array  $parameters  The arguments when that method was called.
334
     * @return Widget
335
     */
336
    public function __call($method, $parameters)
337
    {
338
        $this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true;
339
340
        return $this->save();
341
    }
342
}
343