Completed
Push — master ( d040ca...24a963 )
by Mihail
06:36
created

Model   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 30
c 1
b 0
f 0
lcom 1
cbo 8
dl 0
loc 193
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A before() 0 1 1
A getLabel() 0 19 4
A labels() 0 4 1
A rules() 0 4 1
A sources() 0 4 1
A types() 0 4 1
C validate() 0 44 8
A getAllProperties() 0 12 3
A clearProperties() 0 8 3
B getValidationRule() 0 21 6
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
        ModelValidator::initialize as private validatorConstructor;
21
    }
22
23
    public $_csrf_token;
24
25
    /**
26
     * Model constructor. Initialize before() method for extended objects and run validator initialization
27
     * @param bool $csrf
28
     */
29
    public function __construct($csrf = false)
30
    {
31
        $this->before();
32
        $this->validatorConstructor($csrf);
33
    }
34
35
    /**
36
     * Make any things before model is initialized
37
     */
38
    public function before() {}
39
40
    /**
41
     * Get label value by variable name
42
     * @param string $param
43
     * @return mixed
44
     */
45
    final public function getLabel($param)
46
    {
47
        $labels = $this->labels();
48
        $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...
49
        // maybe array-dotted declaration?
50
        if (Str::contains('.', $param)) {
51
            // not defined for all array-dotted nesting?
52
            if (Str::likeEmpty($labels[$param])) {
53
                // lets set default array label (before first dot-separation)
54
                $response = $labels[Str::firstIn($param, '.')];
55
            } else {
56
                $response = $labels[$param];
57
            }
58
        } else {
59
            $response = $labels[$param];
60
        }
61
62
        return (Str::likeEmpty($response) ? Str::replace('.', ' ', Str::splitCamelCase($param)) : $response);
63
    }
64
65
    /**
66
     * Set attribute labels for model variables
67
     * @return array
68
     */
69
    public function labels()
70
    {
71
        return [];
72
    }
73
74
    /**
75
     * Set of model validation rules
76
     * @return array
77
     */
78
    public function rules()
79
    {
80
        return [];
81
    }
82
83
    /**
84
     * Set model data sources for input data
85
     * Allowed sources: get, post, file
86
     * @return array
87
     */
88
    public function sources()
89
    {
90
        return [];
91
    }
92
93
    /**
94
     * Set model property types to advanced filtering
95
     * Allowed types: text, html, !secure
96
     * Ex: ['property1' => 'text', 'property2' => 'html']
97
     * @return array
98
     */
99
    public function types()
100
    {
101
        return [];
102
    }
103
104
    /**
105
     * Validate defined rules in app
106
     * @return bool
107
     * @throws SyntaxException
108
     */
109
    final public function validate()
110
    {
111
        // validate csrf token if required
112
        if ($this->_tokenRequired && !$this->_tokenOk) {
113
            App::$Session->getFlashBag()->add('warning', __('Hack attention: security token is wrong!'));
114
            return false;
115
        }
116
        // get all rules as array from method rules()
117
        $rules = $this->rules();
118
        // get default values of attributes
119
        $defaultAttr = $this->getAllProperties();
120
121
        // start validation: on this step class attribute values will be changed to input data if it valid
122
        $success = $this->runValidate($rules);
123
124
        // get not-passed validation fields as array
125
        $badAttributes = $this->getBadAttribute();
126
        // prevent warnings
127
        if (Obj::isArray($badAttributes) && count($badAttributes) > 0) {
128
            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...
129
                if (Str::contains('.', $attr)) { // sounds like dot-separated array attr
130
                    $attrName = strstr($attr, '.', true); // get attr name
131
                    $attrArray = trim(strstr($attr, '.'), '.'); // get dot-based array path
132
133
                    $defaultValue = new DotData($defaultAttr); // load default attr
134
135
                    $dotData = new DotData($this->{$attrName}); // load local attr variable
136
                    $dotData->set($attrArray, $defaultValue->get($attr)); // set to local prop. variable default value
137
138
                    $this->{$attrName} = $dotData->export(); // export to model
139
                } else {
140
                    $this->{$attr} = $defaultAttr[$attr]; // just set ;)
141
                }
142
                // add message about wrong attribute to session holder, later display it
143
                $attrLabel = $attr;
144
                if ($this->getLabel($attr) !== null) {
145
                    $attrLabel = $this->getLabel($attr);
146
                }
147
                App::$Session->getFlashBag()->add('warning', __('Field "%field%" is incorrect', ['field' => $attrLabel]));
148
            }
149
        }
150
151
        return $success;
152
    }
153
154
    /**
155
     * Get all properties for current model in key=>value array
156
     * @return array|null
157
     */
158
    public function getAllProperties()
159
    {
160
        $properties = null;
161
        // list all properties here, array_walk sucks on performance!
162
        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...
163
            if (Str::startsWith('_', $property)) {
164
                continue;
165
            }
166
            $properties[$property] = $value;
167
        }
168
        return $properties;
169
    }
170
171
    /**
172
     * Cleanup all public model properties
173
     */
174
    public function clearProperties()
175
    {
176
        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...
177
            if (!Str::startsWith('_', $property)) {
178
                $this->{$property} = null;
179
            }
180
        }
181
    }
182
183
    /**
184
     * Get validation rules for field
185
     * @param string $field
186
     * @return array
187
     */
188
    final public function getValidationRule($field)
189
    {
190
        $rules = $this->rules();
191
        $response = [];
192
193
        foreach ($rules as $rule) {
194
            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...
195
                foreach ($rule[0] as $tfield) {
196
                    if ($tfield == $field) {
197
                        $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...
198
                    }
199
                }
200
            } 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...
201
                if ($rule[0] === $field) {
202
                    $response[$rule[1]] = $rule[2];
203
                }
204
            }
205
        }
206
207
        return $response;
208
    }
209
}