1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace NFePHP\EFDReinf\Common; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Classe Factory, performs build events |
7
|
|
|
* |
8
|
|
|
* @category API |
9
|
|
|
* @package NFePHP\EFDReinf |
10
|
|
|
* @copyright Copyright (c) 2017 |
11
|
|
|
* @license https://www.gnu.org/licenses/lgpl-3.0.txt LGPLv3 |
12
|
|
|
* @license https://www.gnu.org/licenses/gpl-3.0.txt GPLv3 |
13
|
|
|
* @license https://opensource.org/licenses/mit-license.php MIT |
14
|
|
|
* @author Roberto L. Machado <linux.rlm at gmail dot com> |
15
|
|
|
* @link http://github.com/nfephp-org/sped-efdreinf for the canonical source repository |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
use DateTime; |
19
|
|
|
use DOMDocument; |
20
|
|
|
use DOMElement; |
21
|
|
|
use JsonSchema\Validator as JsonValid; |
22
|
|
|
use NFePHP\Common\Certificate; |
23
|
|
|
use NFePHP\Common\DOMImproved as Dom; |
24
|
|
|
use NFePHP\Common\Signer; |
25
|
|
|
use NFePHP\Common\Strings; |
26
|
|
|
use NFePHP\Common\Validator; |
27
|
|
|
use NFePHP\EFDReinf\Exception\EventsException; |
28
|
|
|
use stdClass; |
29
|
|
|
|
30
|
|
|
abstract class Factory |
31
|
|
|
{ |
32
|
|
|
/** |
33
|
|
|
* @var int |
34
|
|
|
*/ |
35
|
|
|
public $tpInsc; |
36
|
|
|
/** |
37
|
|
|
* @var string |
38
|
|
|
*/ |
39
|
|
|
public $nrInsc; |
40
|
|
|
/** |
41
|
|
|
* @var string |
42
|
|
|
*/ |
43
|
|
|
public $nmRazao; |
44
|
|
|
/** |
45
|
|
|
* @var DateTime |
46
|
|
|
*/ |
47
|
|
|
public $date; |
48
|
|
|
/** |
49
|
|
|
* @var int |
50
|
|
|
*/ |
51
|
|
|
public $tpAmb = 3; |
52
|
|
|
/** |
53
|
|
|
* @var int |
54
|
|
|
*/ |
55
|
|
|
public $procEmi = 1; |
56
|
|
|
/** |
57
|
|
|
* @var string |
58
|
|
|
*/ |
59
|
|
|
public $verProc = ''; |
60
|
|
|
/** |
61
|
|
|
* @var string |
62
|
|
|
*/ |
63
|
|
|
public $layout = ''; |
64
|
|
|
/** |
65
|
|
|
* @var string |
66
|
|
|
*/ |
67
|
|
|
public $layoutStr = ''; |
68
|
|
|
/** |
69
|
|
|
* @var string |
70
|
|
|
*/ |
71
|
|
|
public $schema = ''; |
72
|
|
|
/** |
73
|
|
|
* @var string |
74
|
|
|
*/ |
75
|
|
|
public $jsonschema = ''; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @var string |
79
|
|
|
*/ |
80
|
|
|
protected $xmlns = "http://www.reinf.esocial.gov.br/schemas/"; |
81
|
|
|
/** |
82
|
|
|
* @var Dom |
83
|
|
|
*/ |
84
|
|
|
protected $dom; |
85
|
|
|
/** |
86
|
|
|
* @var stdClass |
87
|
|
|
*/ |
88
|
|
|
protected $std; |
89
|
|
|
/** |
90
|
|
|
* @var string |
91
|
|
|
*/ |
92
|
|
|
protected $xml; |
93
|
|
|
/** |
94
|
|
|
* @var \DOMElement |
95
|
|
|
*/ |
96
|
|
|
protected $reinf; |
97
|
|
|
/** |
98
|
|
|
* @var \DOMElement |
99
|
|
|
*/ |
100
|
|
|
protected $node; |
101
|
|
|
/** |
102
|
|
|
* @var string |
103
|
|
|
*/ |
104
|
|
|
public $evtid = ''; |
105
|
|
|
/** |
106
|
|
|
* @var string |
107
|
|
|
*/ |
108
|
|
|
protected $evtTag; |
109
|
|
|
/** |
110
|
|
|
* @var string |
111
|
|
|
*/ |
112
|
|
|
protected $evtName = ''; |
113
|
|
|
/** |
114
|
|
|
* @var string |
115
|
|
|
*/ |
116
|
|
|
protected $evtAlias = ''; |
117
|
|
|
/** |
118
|
|
|
* @var Certificate|null |
119
|
|
|
*/ |
120
|
|
|
protected $certificate; |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Constructor |
124
|
|
|
* @param string $config |
125
|
|
|
* @param stdClass $std |
126
|
|
|
* @param Certificate $certificate |
127
|
|
|
* @param string $date |
128
|
|
|
*/ |
129
|
|
|
public function __construct( |
130
|
|
|
$config, |
131
|
|
|
stdClass $std, |
132
|
|
|
Certificate $certificate = null, |
133
|
|
|
$date = '' |
134
|
|
|
) { |
135
|
|
|
//set properties from config |
136
|
|
|
$stdConf = json_decode($config); |
137
|
|
|
$this->date = new DateTime(); |
138
|
|
|
if (!empty($date)) { |
139
|
|
|
$this->date = new DateTime($date); |
140
|
|
|
} |
141
|
|
|
$this->tpAmb = $stdConf->tpAmb; |
142
|
|
|
$this->verProc = $stdConf->verProc; |
143
|
|
|
$this->layout = $stdConf->eventoVersion; |
144
|
|
|
$this->tpInsc = $stdConf->empregador->tpInsc; |
145
|
|
|
$this->nrInsc = $stdConf->empregador->nrInsc; |
146
|
|
|
$this->nmRazao = $stdConf->empregador->nmRazao; |
147
|
|
|
$this->layoutStr = $this->strLayoutVer($this->layout); |
148
|
|
|
$this->certificate = $certificate; |
149
|
|
|
if (empty($std) || !is_object($std)) { |
150
|
|
|
throw EventsException::wrongArgument(1003, ''); |
151
|
|
|
} |
152
|
|
|
$this->jsonschema = realpath( |
153
|
|
|
__DIR__ |
154
|
|
|
. "/../../jsonSchemes/$this->layoutStr/" |
155
|
|
|
. $this->evtName |
156
|
|
|
. ".schema" |
157
|
|
|
); |
158
|
|
|
$this->schema = realpath( |
159
|
|
|
__DIR__ |
160
|
|
|
. "/../../schemes/$this->layoutStr/" |
161
|
|
|
. $this->evtName |
162
|
|
|
. "-" . $this->layoutStr |
163
|
|
|
. ".xsd" |
164
|
|
|
); |
165
|
|
|
//convert all data fields to lower case |
166
|
|
|
$this->std = $this->propertiesToLower($std); |
167
|
|
|
//validate input data with schema |
168
|
|
|
$this->validInputData($this->std); |
169
|
|
|
//Adding forgotten or unnecessary fields. |
170
|
|
|
//This is done for standardization purposes. |
171
|
|
|
//Fields with no value will not be included by the builder. |
172
|
|
|
//$this->std = $this->standardizeProperties($this->std); |
|
|
|
|
173
|
|
|
$this->init(); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* Stringfy layout number |
178
|
|
|
* @param string $layout |
179
|
|
|
* @return string |
180
|
|
|
*/ |
181
|
|
|
protected function strLayoutVer($layout) |
182
|
|
|
{ |
183
|
|
|
$fils = explode('.', $layout); |
184
|
|
|
$str = 'v'; |
185
|
|
|
foreach ($fils as $fil) { |
186
|
|
|
$str .= str_pad($fil, 2, '0', STR_PAD_LEFT) . '_'; |
187
|
|
|
} |
188
|
|
|
return substr($str, 0, -1); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Change properties names of stdClass to lower case |
193
|
|
|
* @param stdClass $data |
194
|
|
|
* @return stdClass |
195
|
|
|
*/ |
196
|
|
|
protected static function propertiesToLower(stdClass $data) |
197
|
|
|
{ |
198
|
|
|
$properties = get_object_vars($data); |
199
|
|
|
$clone = new stdClass(); |
200
|
|
|
foreach ($properties as $key => $value) { |
201
|
|
|
if ($value instanceof stdClass) { |
202
|
|
|
$value = self::propertiesToLower($value); |
203
|
|
|
} |
204
|
|
|
$nk = strtolower($key); |
205
|
|
|
$clone->{$nk} = $value; |
206
|
|
|
} |
207
|
|
|
return $clone; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Validation json data from json Schema |
212
|
|
|
* @param stdClass $data |
213
|
|
|
* @return boolean |
214
|
|
|
* @throws \RuntimeException |
215
|
|
|
*/ |
216
|
|
|
protected function validInputData($data) |
217
|
|
|
{ |
218
|
|
|
if (!is_file($this->jsonschema)) { |
219
|
|
|
return true; |
220
|
|
|
} |
221
|
|
|
$validator = new JsonValid(); |
222
|
|
|
$validator->check($data, (object)['$ref' => 'file://' . $this->jsonschema]); |
223
|
|
|
if (!$validator->isValid()) { |
224
|
|
|
$msg = ""; |
225
|
|
|
foreach ($validator->getErrors() as $error) { |
226
|
|
|
$msg .= sprintf("[%s] %s\n", $error['property'], $error['message']); |
227
|
|
|
} |
228
|
|
|
throw EventsException::wrongArgument(1004, $msg); |
229
|
|
|
} |
230
|
|
|
return true; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Initialize DOM |
235
|
|
|
*/ |
236
|
|
|
protected function init() |
237
|
|
|
{ |
238
|
|
|
if (empty($this->dom)) { |
239
|
|
|
$this->dom = new Dom('1.0', 'UTF-8'); |
240
|
|
|
$this->dom->preserveWhiteSpace = false; |
241
|
|
|
$this->dom->formatOutput = false; |
242
|
|
|
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" |
243
|
|
|
. "<Reinf xmlns=\"$this->xmlns" |
244
|
|
|
. $this->evtName |
245
|
|
|
. "/$this->layoutStr\">" |
246
|
|
|
. "</Reinf>"; |
247
|
|
|
$this->dom->loadXML($xml); |
248
|
|
|
$this->reinf = $this->dom->getElementsByTagName('Reinf')->item(0); |
249
|
|
|
$this->evtid = FactoryId::build( |
250
|
|
|
$this->tpInsc, |
251
|
|
|
$this->nrInsc, |
252
|
|
|
$this->date, |
253
|
|
|
$this->std->sequencial |
254
|
|
|
); |
255
|
|
|
$this->node = $this->dom->createElement($this->evtTag); |
256
|
|
|
$att = $this->dom->createAttribute('id'); |
257
|
|
|
$att->value = $this->evtid; |
258
|
|
|
$this->node->appendChild($att); |
259
|
|
|
$ideContri = $this->dom->createElement("ideContri"); |
260
|
|
|
$this->dom->addChild( |
261
|
|
|
$ideContri, |
262
|
|
|
"tpInsc", |
263
|
|
|
$this->tpInsc, |
264
|
|
|
true |
265
|
|
|
); |
266
|
|
|
$this->dom->addChild( |
267
|
|
|
$ideContri, |
268
|
|
|
"nrInsc", |
269
|
|
|
$this->nrInsc, |
270
|
|
|
true |
271
|
|
|
); |
272
|
|
|
$this->node->appendChild($ideContri); |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Returns alias of event |
278
|
|
|
* @return string |
279
|
|
|
*/ |
280
|
|
|
public function alias() |
281
|
|
|
{ |
282
|
|
|
return $this->evtAlias; |
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
/** |
286
|
|
|
* Returns the Certificate::class |
287
|
|
|
* @return Certificate|null |
288
|
|
|
*/ |
289
|
|
|
public function getCertificate() |
290
|
|
|
{ |
291
|
|
|
return $this->certificate; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Insert Certificate::class |
296
|
|
|
* @param Certificate $certificate |
297
|
|
|
*/ |
298
|
|
|
public function setCertificate(Certificate $certificate) |
299
|
|
|
{ |
300
|
|
|
$this->certificate = $certificate; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Recover calculate ID |
305
|
|
|
* @return string |
306
|
|
|
*/ |
307
|
|
|
public function getId() |
308
|
|
|
{ |
309
|
|
|
return $this->evtid; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Return xml of event |
314
|
|
|
* @return string |
315
|
|
|
*/ |
316
|
|
|
public function toXML() |
317
|
|
|
{ |
318
|
|
|
if (empty($this->xml)) { |
319
|
|
|
$this->toNode(); |
320
|
|
|
} |
321
|
|
|
return $this->clearXml($this->xml); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
abstract protected function toNode(); |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Remove XML declaration from XML string |
328
|
|
|
* @param string $xml |
329
|
|
|
* @return string |
330
|
|
|
*/ |
331
|
|
|
protected function clearXml($xml) |
332
|
|
|
{ |
333
|
|
|
$dom = new DOMDocument('1.0', 'UTF-8'); |
334
|
|
|
$dom->formatOutput = false; |
335
|
|
|
$dom->preserveWhiteSpace = false; |
336
|
|
|
$dom->loadXML($xml); |
337
|
|
|
return $dom->saveXML($dom->documentElement); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
/** |
341
|
|
|
* Convert xml of event to array |
342
|
|
|
* @return array |
343
|
|
|
*/ |
344
|
|
|
public function toArray() |
345
|
|
|
{ |
346
|
|
|
return json_decode($this->toJson(), true); |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
/** |
350
|
|
|
* Convert xml to json string |
351
|
|
|
* @return string |
352
|
|
|
*/ |
353
|
|
|
public function toJson() |
354
|
|
|
{ |
355
|
|
|
if (empty($this->xml)) { |
356
|
|
|
$this->toNode(); |
357
|
|
|
} |
358
|
|
|
//signature only makes sense in XML, other formats should not contain |
359
|
|
|
//signature data |
360
|
|
|
$xml = Signer::removeSignature($this->xml); |
361
|
|
|
$dom = new \DOMDocument(); |
362
|
|
|
$dom->loadXML($xml); |
363
|
|
|
$sxml = simplexml_load_string($dom->saveXML()); |
364
|
|
|
return str_replace( |
365
|
|
|
'@attributes', |
366
|
|
|
'attributes', |
367
|
|
|
json_encode($sxml, JSON_PRETTY_PRINT) |
368
|
|
|
); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* Convert xml to stdClass |
373
|
|
|
* @return stdClass |
374
|
|
|
*/ |
375
|
|
|
public function toStd() |
376
|
|
|
{ |
377
|
|
|
return json_decode($this->toJson()); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* Adjust missing properties form original data according schema |
382
|
|
|
* @param \stdClass $data |
383
|
|
|
* @return \stdClass |
384
|
|
|
*/ |
385
|
|
|
//public function standardizeProperties(stdClass $data) |
|
|
|
|
386
|
|
|
//{ |
387
|
|
|
// if (!is_file($this->jsonschema)) { |
|
|
|
|
388
|
|
|
// return $data; |
389
|
|
|
// } |
390
|
|
|
// $jsonSchemaObj = json_decode(file_get_contents($this->jsonschema)); |
|
|
|
|
391
|
|
|
// $sc = new ParamsStandardize($jsonSchemaObj); |
|
|
|
|
392
|
|
|
// return $sc->stdData($data); |
|
|
|
|
393
|
|
|
//} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* Sign and validate XML with XSD, can throw Exception |
397
|
|
|
* @param string $tagsigned tag to be base of signature |
398
|
|
|
*/ |
399
|
|
|
protected function sign($tagsigned = '') |
400
|
|
|
{ |
401
|
|
|
$xml = $this->dom->saveXML($this->reinf); |
402
|
|
|
$xml = Strings::clearXmlString($xml); |
403
|
|
|
if (!empty($this->certificate)) { |
404
|
|
|
$xml = Signer::sign( |
405
|
|
|
$this->certificate, |
406
|
|
|
$xml, |
407
|
|
|
$tagsigned, |
408
|
|
|
'id', |
409
|
|
|
OPENSSL_ALGO_SHA256, |
410
|
|
|
[true, false, null, null] |
411
|
|
|
); |
412
|
|
|
//validation by XSD schema throw Exception if dont pass |
413
|
|
|
Validator::isValid($xml, $this->schema); |
414
|
|
|
} |
415
|
|
|
$this->xml = $xml; |
416
|
|
|
} |
417
|
|
|
} |
418
|
|
|
|
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.