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