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

Test Failed
Pull Request — main (#5518)
by Pedro
34:25 queued 23:25
created

BackpackCustomRule   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Importance

Changes 4
Bugs 4 Features 0
Metric Value
eloc 79
c 4
b 4
f 0
dl 0
loc 218
rs 8.96
wmc 43

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getFieldRules() 0 8 2
A getRulesAsArray() 0 11 3
A validatesArrays() 0 3 1
A field() 0 12 3
A validateAndGetErrors() 0 7 2
A ensureValueIsValid() 0 11 4
A setValidator() 0 5 1
A setData() 0 5 1
A validate() 0 13 3
A validateRules() 0 3 1
A validateOnSubmit() 0 3 1
A validateFileRules() 0 23 5
A getValidatorCustomAttributes() 0 8 3
A validateFieldAndFile() 0 7 1
A getValidationAttributeString() 0 5 2
A validateFieldRules() 0 7 1
B prepareValidatorData() 0 15 9

How to fix   Complexity   

Complex Class

Complex classes like BackpackCustomRule often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BackpackCustomRule, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Backpack\CRUD\app\Library\Validation\Rules;
4
5
use Backpack\CRUD\app\Library\Validation\Rules\Support\ValidateArrayContract;
6
use Backpack\Pro\Uploads\Validation\ValidGenericAjaxEndpoint;
0 ignored issues
show
Bug introduced by
The type Backpack\Pro\Uploads\Val...alidGenericAjaxEndpoint was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
7
use Closure;
8
use Illuminate\Contracts\Validation\DataAwareRule;
9
use Illuminate\Contracts\Validation\Rule;
10
use Illuminate\Contracts\Validation\ValidationRule;
11
use Illuminate\Contracts\Validation\ValidatorAwareRule;
12
use Illuminate\Http\UploadedFile;
13
use Illuminate\Support\Arr;
14
use Illuminate\Support\Facades\Validator;
15
use Illuminate\Support\Str;
16
17
abstract class BackpackCustomRule implements ValidationRule, DataAwareRule, ValidatorAwareRule
18
{
19
    use Support\HasFiles;
20
21
    /**
22
     * @var \Illuminate\Contracts\Validation\Validator
23
     */
24
    protected $validator;
25
26
    protected array $data;
27
28
    public array $fieldRules = [];
29
30
    public bool $implicit = true;
31
32
    public static function field(string|array|ValidationRule|Rule $rules = []): self
33
    {
34
        $instance = new static();
35
        $instance->fieldRules = self::getRulesAsArray($rules);
36
37
        if ($instance->validatesArrays()) {
38
            if (! in_array('array', $instance->getFieldRules())) {
39
                $instance->fieldRules[] = 'array';
40
            }
41
        }
42
43
        return $instance;
44
    }
45
46
    /**
47
     * Run the validation rule.
48
     *
49
     * @param  string  $attribute
50
     * @param  mixed  $value
51
     * @param  Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
52
     * @return void
53
     */
54
    public function validate(string $attribute, mixed $value, Closure $fail): void
55
    {
56
        $value = $this->ensureValueIsValid($value);
57
58
        if ($value === false) {
59
            $fail('Invalid value for the attribute.')->translate();
60
61
            return;
62
        }
63
64
        $errors = $this->validateOnSubmit($attribute, $value);
65
        foreach ($errors as $error) {
66
            $fail($error)->translate();
67
        }
68
    }
69
70
    /**
71
     * Set the performing validator.
72
     *
73
     * @param  \Illuminate\Contracts\Validation\Validator  $validator
74
     * @return $this
75
     */
76
    public function setValidator($validator)
77
    {
78
        $this->validator = $validator;
79
80
        return $this;
81
    }
82
83
    /**
84
     * Set the data under validation.
85
     *
86
     * @param  array  $data
87
     * @return $this
88
     */
89
    public function setData($data)
90
    {
91
        $this->data = $data;
92
93
        return $this;
94
    }
95
96
    public function getFieldRules(): array
97
    {
98
        return tap($this->fieldRules, function ($rule) {
99
            if (is_a($rule, BackpackCustomRule::class, true)) {
100
                $rule = $rule->getFieldRules();
101
            }
102
103
            return $rule;
104
        });
105
    }
106
107
    protected static function getRulesAsArray($rules)
108
    {
109
        if (is_string($rules)) {
110
            $rules = explode('|', $rules);
111
        }
112
113
        if (! is_array($rules)) {
114
            $rules = [$rules];
115
        }
116
117
        return $rules;
118
    }
119
120
    protected function ensureValueIsValid($value)
121
    {
122
        if ($this->validatesArrays() && ! is_array($value)) {
123
            try {
124
                $value = json_decode($value, true) ?? [];
125
            } catch(\Exception $e) {
126
                return false;
127
            }
128
        }
129
130
        return $value;
131
    }
132
133
    private function validatesArrays(): bool
134
    {
135
        return is_a($this, ValidateArrayContract::class);
136
    }
137
138
    private function validateAndGetErrors(string $attribute, mixed $value, array $rules): array
139
    {
140
        $validator = Validator::make($value, [
141
            $attribute => $rules,
142
        ], $this->validator->customMessages, $this->getValidatorCustomAttributes($attribute));
0 ignored issues
show
Bug introduced by
Accessing customMessages on the interface Illuminate\Contracts\Validation\Validator suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
143
144
        return $validator->errors()->messages()[$attribute] ?? (! empty($validator->errors()->messages()) ? current($validator->errors()->messages()) : []);
145
    }
146
147
    private function getValidatorCustomAttributes(string $attribute): array
148
    {
149
        if (! is_a($this, ValidGenericAjaxEndpoint::class) && ! Str::contains($attribute, '.*.')) {
150
            return $this->validator->customAttributes;
0 ignored issues
show
Bug introduced by
Accessing customAttributes on the interface Illuminate\Contracts\Validation\Validator suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
151
        }
152
153
        // generic fallback to `profile picture` from `profile.*.picture`
154
        return [$attribute => Str::replace('.*.', ' ', $attribute)];
155
    }
156
157
    protected function getValidationAttributeString(string $attribute)
158
    {
159
        return Str::substrCount($attribute, '.') > 1 ?
160
                Str::before($attribute, '.').'.*.'.Str::afterLast($attribute, '.') :
161
                $attribute;
162
    }
163
164
    protected function validateOnSubmit(string $attribute, mixed $value): array
165
    {
166
        return $this->validateRules($attribute, $value);
167
    }
168
169
    protected function validateFieldAndFile(string $attribute, null|array $data = null, array|null $customRules = null): array
170
    {
171
        $fieldErrors = $this->validateFieldRules($attribute, $data, $customRules);
172
173
        $fileErrors = $this->validateFileRules($attribute, $data);
174
175
        return array_merge($fieldErrors, $fileErrors);
176
    }
177
178
    /**
179
     * Implementation.
180
     */
181
    public function validateFieldRules(string $attribute, null|array|string|UploadedFile $data = null, array|null $customRules = null): array
182
    {
183
        $data = $data ?? $this->data;
184
        $validationRuleAttribute = $this->getValidationAttributeString($attribute);
185
        $data = $this->prepareValidatorData($data, $attribute);
186
187
        return $this->validateAndGetErrors($validationRuleAttribute, $data, $customRules ?? $this->getFieldRules());
188
    }
189
190
    protected function prepareValidatorData(array|string|UploadedFile $data, string $attribute): array
191
    {
192
        if ($this->validatesArrays() && is_array($data) && ! Str::contains($attribute, '.')) {
193
            return Arr::has($data, $attribute) ? $data : [$attribute => $data];
194
        }
195
196
        if (Str::contains($attribute, '.')) {
197
            $validData = [];
198
199
            Arr::set($validData, $attribute, ! is_array($data) ? $data : Arr::get($data, $attribute));
200
201
            return $validData;
202
        }
203
204
        return [$attribute => is_array($data) ? (Arr::has($data, $attribute) ? Arr::get($data, $attribute) : $data) : $data];
205
    }
206
207
    protected function validateFileRules(string $attribute, mixed $data): array
208
    {
209
        $items = $this->prepareValidatorData($data ?? $this->data, $attribute);
210
        $items = is_array($items) ? $items : [$items];
0 ignored issues
show
introduced by
The condition is_array($items) is always true.
Loading history...
211
        $validationRuleAttribute = $this->getValidationAttributeString($attribute);
212
213
        $filesToValidate = Arr::get($items, $attribute);
214
        $filesToValidate = is_array($filesToValidate) ? array_filter($filesToValidate, function ($item) {
215
            return $item instanceof UploadedFile;
216
        }) : (is_a($filesToValidate, UploadedFile::class, true) ? [$filesToValidate] : []);
217
218
        Arr::set($items, $attribute, $filesToValidate);
219
220
        $errors = [];
221
222
        // validate each file individually
223
        foreach ($filesToValidate as $key => $file) {
224
            $fileToValidate = [];
225
            Arr::set($fileToValidate, $attribute, $file);
226
            $errors[] = $this->validateAndGetErrors($validationRuleAttribute, $fileToValidate, $this->getFileRules());
227
        }
228
229
        return array_unique(array_merge(...$errors));
230
    }
231
232
    public function validateRules(string $attribute, mixed $value): array
233
    {
234
        return $this->validateFieldAndFile($attribute, $value);
235
    }
236
}
237