Passed
Push — master ( eeace4...fe6676 )
by Roberto
03:31 queued 01:44
created

DOMImproved   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 0

Test Coverage

Coverage 5.43%

Importance

Changes 0
Metric Value
wmc 40
lcom 2
cbo 0
dl 0
loc 262
ccs 5
cts 92
cp 0.0543
rs 9.2
c 0
b 0
f 0

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A insertAfter() 0 8 2
A loadXMLString() 0 11 3
A loadXMLFile() 0 9 2
A getNodeValue() 0 9 2
A getValue() 0 9 3
A getNode() 0 8 2
A getChave() 0 10 2
B addChild() 0 25 10
A appExternalChild() 0 5 1
A appExternalChildBefore() 0 11 2
A appChildBefore() 0 11 4
A addArrayChild() 0 11 4
A appChild() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like DOMImproved 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 DOMImproved, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace NFePHP\Common;
4
5
/**
6
 * Extends DOMDocument
7
 * @category   NFePHP
8
 * @package    NFePHP\Common\DOMImproved
9
 * @copyright  Copyright (c) 2008-2017
10
 * @license    http://www.gnu.org/licenses/lesser.html LGPL v3
11
 * @license    https://opensource.org/licenses/MIT MIT
12
 * @license    http://www.gnu.org/licenses/gpl.txt GPLv3+
13
 * @author     Roberto L. Machado <linux.rlm at gmail dot com>
14
 * @link       http://github.com/nfephp-org/sped-common for the canonical source repository
15
 */
16
17
use DOMDocument;
18
use DOMNode;
19
use DOMElement;
20
21
class DOMImproved extends DOMDocument
22
{
23
    /**
24
     * @var array
25
     */
26
    public $errors = [];
27
    
28
    /**
29
     * @param string $version
30
     * @param string $charset
31
     */
32 1
    public function __construct($version = '1.0', $charset = 'utf-8')
33
    {
34 1
        parent::__construct($version, $charset);
35 1
        $this->formatOutput = false;
36 1
        $this->preserveWhiteSpace = false;
37 1
    }
38
    
39
    /**
40
     * Insert node AFTER reference node
41
     * @param \DOMNode $newNode
42
     * @param \DOMNode $referenceNode
43
     * @return \DOMNode
44
     */
45
    public function insertAfter(\DOMNode $newNode, \DOMNode $referenceNode)
46
    {
47
        if ($referenceNode->nextSibling === null) {
48
            return $referenceNode->parentNode->appendChild($newNode);
49
        } else {
50
            return $referenceNode->parentNode->insertBefore($newNode, $referenceNode->nextSibling);
51
        }
52
    }
53
    
54
    /**
55
     * Loads string in DOMDocument
56
     * @param string $content content of xml
57
     * @return bool
58
     */
59
    public function loadXMLString($content)
60
    {
61
        $msg = "O arquivo indicado não é um XML ou contêm B.O.M. no inicio do arquivo !";
62
        if (substr($content, 0, 1) != '<' ||
63
            !$this->loadXML($content, LIBXML_NOBLANKS | LIBXML_NOEMPTYTAG)
64
        ) {
65
            $this->errors[] = $msg;
66
            return false;
67
        }
68
        return true;
69
    }
70
    
71
    /**
72
     * Load xml from path
73
     * @param string $filename
74
     * @return bool
75
     */
76
    public function loadXMLFile($filename)
77
    {
78
        if (!is_file($filename)) {
79
            $this->errors[] = 'Arquivo não encontrado!';
80
            return false;
81
        }
82
        $content = file_get_contents($filename);
83
        $this->loadXMLString($content);
84
    }
85
            
86
    /**
87
     * Extrai o valor do node DOM
88
     * @param string $nodeName identificador da TAG do xml
89
     * @param int $itemNum numero do item a ser retornado
90
     * @param string $extraTextBefore prefixo do retorno
91
     * @param string $extraTextAfter sufixo do retorno
92
     * @return string
93
     */
94
    public function getNodeValue($nodeName, $itemNum = 0, $extraTextBefore = '', $extraTextAfter = '')
95
    {
96
        $node = $this->getElementsByTagName($nodeName)->item($itemNum);
97
        if (isset($node)) {
98
            $texto = html_entity_decode(trim($node->nodeValue), ENT_QUOTES, 'UTF-8');
99
            return $extraTextBefore . $texto . $extraTextAfter;
100
        }
101
        return '';
102
    }
103
    
104
    /**
105
     * getValue
106
     * @param DOMElement $node
107
     * @param string $name
108
     * @return string
109
     */
110
    public function getValue(DOMElement $node, $name)
111
    {
112
        if (empty($node)) {
113
            return '';
114
        }
115
        $texto = !empty($node->getElementsByTagName($name)->item(0)->nodeValue) ?
116
            $node->getElementsByTagName($name)->item(0)->nodeValue : '';
117
        return html_entity_decode($texto, ENT_QUOTES, 'UTF-8');
118
    }
119
    
120
    /**
121
     * getNode
122
     * Retorna o node solicitado
123
     * @param string $nodeName
124
     * @param integer $itemNum
125
     * @return DOMElement | string
126
     */
127
    public function getNode($nodeName, $itemNum = 0)
128
    {
129
        $node = $this->getElementsByTagName($nodeName)->item($itemNum);
130
        if (isset($node)) {
131
            return $node;
132
        }
133
        return '';
0 ignored issues
show
Bug Best Practice introduced by
The return type of return ''; (string) is incompatible with the return type documented by NFePHP\Common\DOMImproved::getNode of type DOMElement.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
134
    }
135
    
136
    /**
137
     * getChave
138
     * @param string $nodeName
139
     * @return string
140
     */
141
    public function getChave($nodeName = 'infNFe')
142
    {
143
        $node = $this->getElementsByTagName($nodeName)->item(0);
144
        if (! empty($node)) {
145
            $chaveId = $node->getAttribute("Id");
146
            $chave =  preg_replace('/[^0-9]/', '', $chaveId);
147
            return $chave;
148
        }
149
        return '';
150
    }
151
    
152
    /**
153
     * addChild
154
     * Adiciona um elemento ao node xml passado como referencia
155
     * Serão inclusos erros na array $erros[] sempre que a tag for obrigatória e
156
     * nenhum parâmetro for passado na variável $content e $force for false
157
     * @param \DOMElement $parent
158
     * @param string|null $name
159
     * @param string|float|null $content
160
     * @param boolean $obrigatorio
161
     * @param string $descricao
162
     * @param boolean $force força a criação do elemento mesmo sem dados e não considera como erro
163
     * @return void
164
     */
165
    public function addChild(
166
        DOMElement &$parent,
167
        $name,
168
        $content,
169
        $obrigatorio = false,
170
        $descricao = '',
171
        $force = false
172
    ) {
173
        if (empty($name)) {
174
            $this->errors[] = "O nome da TAG é Obrigatório!";
175
            return;
176
        }
177
        if (!$obrigatorio && $content === null) {
178
            return;
179
        } elseif ($obrigatorio && ($content === null || $content === '')) {
180
            $this->errors[] = "Preenchimento Obrigatório! [$name] $descricao";
181
        }
182
        $content = (string) $content;
183
        $content = trim($content);
184
        if ($obrigatorio || $content !== '' || $force) {
185
            $content = htmlspecialchars($content, ENT_QUOTES);
186
            $temp = $this->createElement($name, $content);
187
            $parent->appendChild($temp);
188
        }
189
    }
190
    
191
    /**
192
     * Acrescenta DOMElement a pai DOMElement
193
     * Caso o pai esteja vazio retorna uma exception com a mensagem
194
     * O parametro "child" pode ser null
195
     * @param DOMElement $parent
196
     * @param DOMElement|null $child
197
     * @param string $msg
198
     * @return void
199
     */
200
    public function appChild(DOMElement &$parent, DOMElement $child = null, $msg = '')
0 ignored issues
show
Unused Code introduced by
The parameter $msg is not used and could be removed.

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

Loading history...
201
    {
202
        if (empty($child)) {
203
            //$this->errors[] = $msg;
204
            return;
205
        }
206
        $parent->appendChild($child);
207
    }
208
    
209
    /**
210
     * Append DOMElement from external documento to local Node
211
     * @param \DOMElement $parent
212
     * @param \DOMElement $child
213
     * @return void
214
     */
215
    public function appExternalChild(DOMElement &$parent, DOMElement $child)
216
    {
217
        $node = $this->importNode($child, true);
218
        $parent->appendChild($node);
219
    }
220
    
221
    /**
222
     * Append DOMElement from external documento to local Node
223
     * before existent node
224
     * @param \DOMElement $parent
225
     * @param \DOMElement $child
226
     * @param string $before
227
     * @return void
228
     */
229
    public function appExternalChildBefore(
230
        DOMElement &$parent,
231
        DOMElement $child,
232
        $before
233
    ) {
234
        if (empty($bnode = $parent->getElementsByTagName($before)->item(0))) {
235
            return;
236
        }
237
        $node = $this->importNode($child, true);
238
        $parent->insertBefore($node, $bnode);
239
    }
240
        
241
    /**
242
     * appChildBefore
243
     * Acrescenta DOMElement a pai DOMElement
244
     * Caso o pai esteja vazio retorna uma exception com a mensagem
245
     * O parametro "child" pode ser vazio
246
     * @param \DOMElement $parent
247
     * @param \DOMElement $child
248
     * @param string $before
249
     * @param string $msg
250
     * @return void
251
     */
252
    public function appChildBefore(DOMElement &$parent, DOMElement $child = null, $before = '', $msg = '')
253
    {
254
        if (empty($child) ||
255
            empty($before) ||
256
            empty($bnode = $parent->getElementsByTagName($before)->item(0))
257
        ) {
258
            $this->errors[] = "$msg Node child vazio ou node <$before> não encontrado!!";
259
            return;
260
        }
261
        $parent->insertBefore($child, $bnode);
262
    }
263
    
264
    /**
265
     * addArrayChild
266
     * Adiciona a um DOMElemt parent, outros elementos passados em um array de DOMElements
267
     * @param \DOMElement $parent
268
     * @param array $arr
269
     * @return int
270
     */
271
    public function addArrayChild(DOMElement &$parent, $arr)
272
    {
273
        $num = 0;
274
        if (! empty($arr) && ! empty($parent)) {
275
            foreach ($arr as $node) {
276
                $this->appChild($parent, $node, '');
277
                $num++;
278
            }
279
        }
280
        return $num;
281
    }
282
}
283