Element   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 235
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 58.12%

Importance

Changes 0
Metric Value
wmc 45
lcom 1
cbo 0
dl 0
loc 235
ccs 68
cts 117
cp 0.5812
rs 8.8
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A postValidation() 0 4 1
C standarize() 0 66 13
C isFieldInError() 0 39 14
A formater() 0 20 5
B numberFormat() 0 35 8
A build() 0 8 2
A __toString() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Element 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Element, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace NFePHP\EFD\Common;
4
5
use \stdClass;
6
use NFePHP\Common\Strings;
7
use Exception;
8
use function Safe\json_decode;
9
use function Safe\json_encode;
10
use function Safe\preg_match;
11
12
abstract class Element
13
{
14
15
    public $std;
16
    public $values;
17
    protected $parameters;
18
    private $reg;
19
20
    /**
21
     * Constructor
22
     * @param string $reg
23
     */
24 9
    public function __construct($reg)
25
    {
26 9
        $this->reg = $reg;
27 9
        $this->values = new stdClass();
28 9
    }
29
30
    public function postValidation()
31
    {
32
        return true;
33
    }
34
35
    /**
36
     * Valida e ajusta os dados de entrada para os padões estabelecidos
37
     * @param \stdClass $std
38
     */
39 9
    protected function standarize(\stdClass $std)
40
    {
41 9
        if (empty($this->parameters)) {
42
            throw new Exception('Parametros não estabelecidos na classe');
43
        }
44 9
        $errors = [];
45
        //passa todos as variáveis do stdClass para minusculo
46 9
        $arr = array_change_key_case(get_object_vars($std), CASE_LOWER);
47 9
        $std = json_decode(json_encode($arr));
48
        //paga as chaves dos parametros e passa para minusculo
49 9
        $stdParam = json_decode(json_encode($this->parameters));
50 9
        $this->parameters = array_change_key_case(get_object_vars($stdParam), CASE_LOWER);
51 9
        $paramKeys = array_keys($this->parameters);
52
        //passa os paramatros com as chaves modificadas para um stdClass
53 9
        if (!$json = json_encode($this->parameters)) {
54
            throw new \RuntimeException("Falta definir os parametros ou existe erro no array");
55
        }
56 9
        $stdParam = json_decode($json);
57 9
        if ($stdParam === null) {
58
            throw new \RuntimeException("Houve uma falha na converção para stdClass");
59
        }
60
        //verifica se foram passados os dados obrigatórios
61 9
        foreach ($std as $key => $value) {
62 9
            if (!isset($stdParam->$key)) {
63
                //ignore non defined params
64
                continue;
65
            }
66 9
            if ($stdParam->$key->required && $std->$key === null) {
67 3
                $errors[] = "$key é requerido.";
68
            }
69
        }
70 9
        $newstd = new \stdClass();
71 9
        foreach ($paramKeys as $key) {
72 9
            if (!key_exists($key, $arr)) {
73
                $newstd->$key = null;
74
            } else {
75 9
                if ($std->$key === null) {
76
                    $newstd->$key = null;
77
                    continue;
78
                }
79
                //se o valor para o parametro foi passado, então validar
80 9
                $resp = $this->isFieldInError(
81 9
                    $std->$key,
82 9
                    $stdParam->$key,
83 9
                    strtoupper($key),
84 9
                    $this->reg,
85 9
                    $stdParam->$key->required
86
                );
87 9
                if ($resp) {
88 6
                    $errors[] = $resp;
89
                }
90
                //e formatar o dado passado
91 9
                $formated = $this->formater(
92 9
                    $std->$key,
93 9
                    $stdParam->$key->format,
94 9
                    strtoupper($key)
95
                );
96 9
                $newstd->$key = $formated;
97
            }
98
        }
99
        //se algum erro for detectado disparar um Exception
100 9
        if (!empty($errors)) {
101 6
            throw new \InvalidArgumentException(implode("\n", $errors));
102
        }
103 3
        return $newstd;
104
    }
105
106
    /**
107
     * Verifica os campos com relação ao tipo e seu regex
108
     * @param string|integer|float|null $input
109
     * @param stdClass $param
110
     * @param string $fieldname
111
     * @return string|boolean
112
     */
113 9
    protected function isFieldInError($input, $param, $fieldname, $element, $required)
114
    {
115 9
        $type = $param->type;
116 9
        $regex = $param->regex;
117 9
        if (empty($regex)) {
118
            return false;
119
        }
120 9
        if (($input === null || $input === '') && !$required) {
121
            return false;
122
        }
123 6
        switch ($type) {
124 9
            case 'integer':
125
                if (!is_numeric($input)) {
126
                    return "[$this->reg] $element campo: $fieldname deve ser um valor numérico inteiro.";
127
                }
128
                break;
129 9
            case 'numeric':
130 9
                if (!is_numeric($input)) {
131 3
                    return "[$this->reg] $element campo: $fieldname deve ser um numero.";
132
                }
133 6
                break;
134
            case 'string':
135
                if (!is_string($input)) {
136
                    return "[$this->reg] $element campo: $fieldname deve ser uma string.";
137
                }
138
                break;
139
        }
140 6
        $input = (string) $input;
141 6
        if ($regex === 'email') {
142
            if (!filter_var($input, FILTER_VALIDATE_EMAIL)) {
143
                return "[$this->reg] $element campo: $fieldname Esse email [$input] está incorreto.";
144
            }
145
            return false;
146
        }
147 6
        if (!preg_match("/$regex/", $input)) {
148 3
            return "[$this->reg] $element campo: $fieldname valor incorreto [$input]. (validação: $regex)";
149
        }
150 3
        return false;
151
    }
152
153
    /**
154
     * Formata os campos float
155
     * @param string|integer|float|null $value
156
     * @param string $format
157
     * @param string $fieldname
158
     * @return int|string|float|null
159
     * @throws \InvalidArgumentException
160
     */
161 9
    protected function formater($value, $format = null, $fieldname = '')
162
    {
163 9
        if ($value === null) {
164
            return $value;
165
        }
166 9
        if (!is_numeric($value)) {
167
            //se não é numerico então permitir apenas ASCII
168 3
            return Strings::toASCII($value);
169
        }
170 6
        if (empty($format)) {
171 6
            return $value;
172
        }
173
        //gravar os valores numericos para possivel posterior validação complexa
174
        $name = strtolower($fieldname);
175
        if ($value === '') {
176
            $value = 0;
177
        }
178
        $this->values->$name = (float) $value;
179
        return $this->numberFormat(floatval($value), $format, $fieldname);
180
    }
181
182
    /**
183
     * Format number
184
     * @param float $value
185
     * @param string $format
186
     * @return string
187
     * @throws \InvalidArgumentException
188
     */
189
    private function numberFormat($value, $format, $fieldname)
190
    {
191
        $n = explode('v', $format);
192
        $mdec = strpos($n[1], '-');
193
        $p = explode('.', "{$value}");
194
        $ndec = !empty($p[1]) ? strlen($p[1]) : 0; //decimal digits
195
        $nint = strlen($p[0]); //integer digits
196
        $intdig = (int) $n[0];
197
        if ($nint > $intdig) {
198
            throw new \InvalidArgumentException("[$this->reg] O [$fieldname] é maior "
199
            . "que o permitido [$format].");
200
        }
201
        if ($mdec !== false) {
202
            //is multi decimal
203
            $mm = explode('-', $n[1]);
204
            $decmin = (int) $mm[0];
205
            $decmax = (int) $mm[1];
206
            //verificar a quantidade de decimais informada
207
            //se maior ou igual ao minimo e menor ou igual ao maximo
208
            if ($ndec >= $decmin && $ndec <= $decmax) {
209
                //deixa como está
210
                return number_format($value, $ndec, ',', '');
211
            }
212
            //se menor que o minimo, formata para o minimo
213
            if ($ndec < $decmin) {
214
                return number_format($value, $decmin, ',', '');
215
            }
216
            //se maior que o maximo, formata para o maximo
217
            if ($ndec > $decmax) {
218
                return number_format($value, $decmax, ',', '');
219
            }
220
        }
221
        $decplaces = (int) $n[1];
222
        return number_format($value, $decplaces, ',', '');
223
    }
224
225
    /**
226
     * Construtor do elemento
227
     * @return string
228
     */
229 3
    protected function build()
230
    {
231 3
        $register = '';
232 3
        foreach ($this->parameters as $key => $params) {
233 3
            $register .= $this->std->$key . '|';
234
        }
235 3
        return $register;
236
    }
237
238
    /**
239
     * Retorna o elemento formatado em uma string
240
     * @return string
241
     */
242 3
    public function __toString()
243
    {
244 3
        return '|' . $this->reg . '|' . $this->build();
245
    }
246
}
247