Factory   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 352
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 0
Metric Value
wmc 28
lcom 2
cbo 3
dl 0
loc 352
rs 10
c 0
b 0
f 0

16 Methods

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