Passed
Push — master ( dc5ea8...031b8b )
by Roberto
04:06 queued 02:02
created

Factory::getId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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);
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
386
    //{
387
    //    if (!is_file($this->jsonschema)) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
388
    //        return $data;
389
    //    }
390
    //    $jsonSchemaObj = json_decode(file_get_contents($this->jsonschema));
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
391
    //    $sc = new ParamsStandardize($jsonSchemaObj);
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
392
    //    return $sc->stdData($data);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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