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 ''; |
|
|
|
|
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 $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 ($content === null) { |
174
|
|
|
return; |
175
|
|
|
} |
176
|
|
|
$content = (string) $content; |
177
|
|
|
$content = trim($content); |
178
|
|
|
if ($obrigatorio && $content === '' && !$force) { |
179
|
|
|
$this->errors[] = "Preenchimento Obrigatório! [$name] $descricao"; |
180
|
|
|
} |
181
|
|
|
if ($obrigatorio || $content !== '' || $force) { |
182
|
|
|
$content = htmlspecialchars($content, ENT_QUOTES); |
183
|
|
|
$temp = $this->createElement($name, $content); |
184
|
|
|
$parent->appendChild($temp); |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Acrescenta DOMElement a pai DOMElement |
190
|
|
|
* Caso o pai esteja vazio retorna uma exception com a mensagem |
191
|
|
|
* O parametro "child" pode ser vazio |
192
|
|
|
* @param DOMElement $parent |
193
|
|
|
* @param DOMElement $child |
194
|
|
|
* @param string $msg |
195
|
|
|
* @return void |
196
|
|
|
*/ |
197
|
|
|
public function appChild(DOMElement &$parent, DOMElement $child = null, $msg = '') |
198
|
|
|
{ |
199
|
|
|
if (empty($child)) { |
200
|
|
|
$this->errors[] = $msg; |
201
|
|
|
return; |
202
|
|
|
} |
203
|
|
|
$parent->appendChild($child); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Append DOMElement from external documento to local Node |
208
|
|
|
* @param \DOMElement $parent |
209
|
|
|
* @param \DOMElement $child |
210
|
|
|
* @return void |
211
|
|
|
*/ |
212
|
|
|
public function appExternalChild(DOMElement &$parent, DOMElement $child) |
213
|
|
|
{ |
214
|
|
|
$node = $this->importNode($child, true); |
215
|
|
|
$parent->appendChild($node); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Append DOMElement from external documento to local Node |
220
|
|
|
* before existent node |
221
|
|
|
* @param \DOMElement $parent |
222
|
|
|
* @param \DOMElement $child |
223
|
|
|
* @param string $before |
224
|
|
|
* @return void |
225
|
|
|
*/ |
226
|
|
|
public function appExternalChildBefore( |
227
|
|
|
DOMElement &$parent, |
228
|
|
|
DOMElement $child, |
229
|
|
|
$before |
230
|
|
|
) { |
231
|
|
|
if (empty($bnode = $parent->getElementsByTagName($before)->item(0))) { |
232
|
|
|
return; |
233
|
|
|
} |
234
|
|
|
$node = $this->importNode($child, true); |
235
|
|
|
$parent->insertBefore($node, $bnode); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* appChildBefore |
240
|
|
|
* Acrescenta DOMElement a pai DOMElement |
241
|
|
|
* Caso o pai esteja vazio retorna uma exception com a mensagem |
242
|
|
|
* O parametro "child" pode ser vazio |
243
|
|
|
* @param \DOMElement $parent |
244
|
|
|
* @param \DOMElement $child |
245
|
|
|
* @param string $before |
246
|
|
|
* @param string $msg |
247
|
|
|
* @return void |
248
|
|
|
*/ |
249
|
|
|
public function appChildBefore(DOMElement &$parent, DOMElement $child = null, $before = '', $msg = '') |
250
|
|
|
{ |
251
|
|
|
if (empty($child) || |
252
|
|
|
empty($before) || |
253
|
|
|
empty($bnode = $parent->getElementsByTagName($before)->item(0)) |
254
|
|
|
) { |
255
|
|
|
$this->errors[] = "$msg Node child vazio ou node <$before> não encontrado!!"; |
256
|
|
|
return; |
257
|
|
|
} |
258
|
|
|
$parent->insertBefore($child, $bnode); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* addArrayChild |
263
|
|
|
* Adiciona a um DOMElemt parent, outros elementos passados em um array de DOMElements |
264
|
|
|
* @param \DOMElement $parent |
265
|
|
|
* @param array $arr |
266
|
|
|
* @return int |
267
|
|
|
*/ |
268
|
|
|
public function addArrayChild(DOMElement &$parent, $arr) |
269
|
|
|
{ |
270
|
|
|
$num = 0; |
271
|
|
|
if (! empty($arr) && ! empty($parent)) { |
272
|
|
|
foreach ($arr as $node) { |
273
|
|
|
$this->appChild($parent, $node, ''); |
274
|
|
|
$num++; |
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
return $num; |
278
|
|
|
} |
279
|
|
|
} |
280
|
|
|
|
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.