Completed
Push — master ( d2ae2e...9154e6 )
by Mihail
03:15
created

Model::clearProperties()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 3
eloc 4
nc 3
nop 0
1
<?php
2
3
namespace Ffcms\Core\Arch;
4
5
use Ffcms\Core\App;
6
use Ffcms\Core\Exception\SyntaxException;
7
use Dflydev\DotAccessData\Data as DotData;
8
use Ffcms\Core\Helper\Type\Obj;
9
use Ffcms\Core\Helper\Type\Str;
10
use Ffcms\Core\Traits\DynamicGlobal;
11
use Ffcms\Core\Traits\ModelValidator;
12
13
/**
14
 * Class Model. Classic constructor of models in MVC architecture with algorithm of passing attributes from user input data.
15
 * @package Ffcms\Core\Arch
16
 */
17
class Model
18
{
19
    use DynamicGlobal, ModelValidator;
20
21
    public function __construct()
22
    {
23
        $this->before();
24
    }
25
26
    public function before() {}
27
28
    /**
29
     * Get label value by variable name
30
     * @param string $param
31
     * @return mixed
32
     */
33
    final public function getLabel($param)
34
    {
35
        $labels = $this->labels();
36
        $response = null;
0 ignored issues
show
Unused Code introduced by
$response 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...
37
        // maybe array-dotted declaration?
38
        if (Str::contains('.', $param)) {
39
            // not defined for all array-dotted nesting?
40
            if (Str::likeEmpty($labels[$param])) {
41
                // lets set default array label (before first dot-separation)
42
                $response = $labels[Str::firstIn($param, '.')];
43
            } else {
44
                $response = $labels[$param];
45
            }
46
        } else {
47
            $response = $labels[$param];
48
        }
49
50
        return (Str::likeEmpty($response) ? Str::replace('.', ' ', Str::splitCamelCase($param)) : $response);
51
    }
52
53
    /**
54
     * Set attribute labels for model variables
55
     * @return array
56
     */
57
    public function labels()
58
    {
59
        return [];
60
    }
61
62
    /**
63
     * Set of model validation rules
64
     * @return array
65
     */
66
    public function rules()
67
    {
68
        return [];
69
    }
70
71
    /**
72
     * Set model data sources for input data
73
     * @return array
74
     */
75
    public function sources()
76
    {
77
        return [];
78
    }
79
80
    /**
81
     * Validate defined rules in app
82
     * @return bool
83
     * @throws SyntaxException
84
     */
85
    final public function validate()
86
    {
87
        // get all rules as array from method rules()
88
        $rules = $this->rules();
89
        // get default values of attributes
90
        $defaultAttr = $this->getAllProperties();
91
92
        // start validation as save boolean value
93
        $success = $this->runValidate($rules);
94
95
        // get not-passed validation fields as array
96
        $badAttributes = $this->getBadAttribute();
97
        // prevent warnings
98
        if (Obj::isArray($badAttributes) && count($badAttributes) > 0) {
99
            foreach ($badAttributes as $attr) {
0 ignored issues
show
Bug introduced by
The expression $badAttributes of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
100
                if (Str::contains('.', $attr)) { // sounds like dot-separated array attr
101
                    $attrName = strstr($attr, '.', true); // get attr name
102
                    $attrArray = trim(strstr($attr, '.'), '.'); // get dot-based array path
103
104
                    $defaultValue = new DotData($defaultAttr); // load default attr
105
106
                    $dotData = new DotData($this->{$attrName}); // load local attr variable
107
                    $dotData->set($attrArray, $defaultValue->get($attr)); // set to local prop. variable default value
108
109
                    $this->{$attrName} = $dotData->export(); // export to model
110
                } else {
111
                    $this->{$attr} = $defaultAttr[$attr]; // just set ;)
112
                }
113
                // add message about wrong attribute to session holder, later display it
114
                $attrLabel = $attr;
115
                if ($this->getLabel($attr) !== null) {
116
                    $attrLabel = $this->getLabel($attr);
117
                }
118
                App::$Session->getFlashBag()->add('warning', __('Field "%field%" is incorrect', ['field' => $attrLabel]));
119
            }
120
        }
121
122
        return $success;
123
    }
124
125
    /**
126
     * Filter model fields as text, html or secure obscured
127
     * @param array|null $fields
128
     * @return $this
129
     */
130
    public function filter(array $fields = null)
131
    {
132
        // list all model fields
133
        $allFields = $this->getAllProperties();
134
        if ($allFields !== null && Obj::isArray($allFields)) {
135
            foreach ($allFields as $f => $v) {
136
                // if attr is not passed - set from global as plaintext
137
                if (!isset($fields[$f])) {
138
                    $fields[$f] = 'text';
139
                }
140
            }
141
        }
142
143
        // if no fields is found - return
144
        if (!Obj::isArray($fields)) {
145
            return $this;
146
        }
147
148
        // each all properties
149
        foreach ($fields as $field => $security) {
0 ignored issues
show
Bug introduced by
The expression $fields of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
150
            // get property value
151
            $fieldValue = $this->{$field};
152
            // switch security levels
153
            switch ($security) {
154
                case '!secure': // is full security obscured field, skip it
155
                    break;
156
                case 'html':
157
                    $this->{$field} = App::$Security->secureHtml($fieldValue);
158
                    break;
159
                default: // text/plaintext
160
                    $this->{$field} = App::$Security->strip_tags($fieldValue);
161
                    break;
162
            }
163
        }
164
165
        return $this;
166
    }
167
168
    /**
169
     * Export model values for safe-using in HTML pages.
170
     * @deprecated
171
     * @return $this
172
     */
173
    final public function export()
174
    {
175
        return $this->filter(null);
176
    }
177
178
179
    /**
180
     * Get all properties for current model in key=>value array
181
     * @return array|null
182
     */
183
    public function getAllProperties()
184
    {
185
        $properties = null;
186
        // list all properties here, array_walk sucks on performance!
187
        foreach ($this as $property => $value) {
0 ignored issues
show
Bug introduced by
The expression $this of type this<Ffcms\Core\Arch\Model> is not traversable.
Loading history...
188
            if (Str::startsWith('_', $property)) {
189
                continue;
190
            }
191
            $properties[$property] = $value;
192
        }
193
        return $properties;
194
    }
195
196
    /**
197
     * Cleanup all public model properties
198
     */
199
    public function clearProperties()
200
    {
201
        foreach ($this as $property => $value) {
0 ignored issues
show
Bug introduced by
The expression $this of type this<Ffcms\Core\Arch\Model> is not traversable.
Loading history...
202
            if (!Str::startsWith('_', $property)) {
203
                $this->{$property} = null;
204
            }
205
        }
206
    }
207
208
    /**
209
     * Get validation rules for field
210
     * @param string $field
211
     * @return array
212
     */
213
    final public function getValidationRule($field)
214
    {
215
        $rules = $this->rules();
216
        $response = [];
217
218
        foreach ($rules as $rule) {
219
            if (Obj::isArray($rule[0])) { // 2 or more rules [['field1', 'field2'], 'filter', 'filter_argv']
0 ignored issues
show
Unused Code Comprehensibility introduced by
57% 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...
220
                foreach ($rule[0] as $tfield) {
221
                    if ($tfield == $field) {
222
                        $response[$rule[1]] = $rule[2]; // ['min_length' => 1, 'required' => null]
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
223
                    }
224
                }
225
            } else { // 1 rule ['field1', 'filter', 'filter_argv']
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% 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...
226
                if ($rule[0] === $field) {
227
                    $response[$rule[1]] = $rule[2];
228
                }
229
            }
230
        }
231
232
        return $response;
233
    }
234
235
}