Tools   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 518
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 11

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 43
lcom 2
cbo 11
dl 0
loc 518
ccs 0
cts 232
cp 0
rs 8.96
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A loadSoapClass() 0 5 1
A setSignAlgorithm() 0 4 1
A version() 0 14 3
A getcUF() 0 4 1
A getAcronym() 0 4 1
A validKeyByUF() 0 10 2
B signMDFe() 0 45 6
A getModalXML() 0 6 1
A isValid() 0 11 2
A setEnvironment() 0 7 5
A canonicalOptions() 0 7 3
A checkSoap() 0 6 2
A __construct() 0 12 1
A setEnvironmentTimeZone() 0 4 1
A setVerAplic() 0 4 1
A getTypeOfPersonFromCertificate() 0 17 3
B servico() 0 59 5
A sendRequest() 0 14 1
A getXmlUrlPath() 0 10 2
A addQRCode() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like Tools often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Tools, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace NFePHP\MDFe\Common;
4
5
/**
6
 * Class base responsible for communication with SEFAZ
7
 *
8
 * @author    Cleiton Perin <cperin20 at gmail dot com>
9
 * @package   NFePHP\MDFe\Common\Tools
10
 * @copyright NFePHP Copyright (c) 2008-2019
11
 * @license   http://www.gnu.org/licenses/lgpl.txt LGPLv3+
12
 * @license   https://opensource.org/licenses/MIT MIT
13
 * @license   http://www.gnu.org/licenses/gpl.txt GPLv3+
14
 * @category  NFePHP
15
 * @link      http://github.com/nfephp-org/sped-mdfe for the canonical source repository
16
 */
17
18
use DOMDocument;
19
use InvalidArgumentException;
20
use NFePHP\Common\Certificate;
21
use NFePHP\Common\Signer;
22
use NFePHP\Common\Soap\SoapCurl;
23
use NFePHP\Common\Soap\SoapInterface;
24
use NFePHP\Common\Strings;
25
use NFePHP\Common\TimeZoneByUF;
26
use NFePHP\Common\UFList;
27
use NFePHP\Common\Validator;
28
use NFePHP\MDFe\Factories\Header;
29
use NFePHP\MDFe\Factories\QRCode;
30
use RuntimeException;
31
32
class Tools
33
{
34
    /**
35
     * config class
36
     * @var \stdClass
37
     */
38
    public $config;
39
    /**
40
     * Path to storage folder
41
     * @var string
42
     */
43
    public $pathwsfiles = '';
44
    /**
45
     * Path to schemes folder
46
     * @var string
47
     */
48
    public $pathschemes = '';
49
    /**
50
     * ambiente
51
     * @var string
52
     */
53
    public $ambiente = 'homologacao';
54
    /**
55
     * Environment
56
     * @var int
57
     */
58
    public $tpAmb = 2;
59
    /**
60
     * soap class
61
     * @var SoapInterface
62
     */
63
    public $soap;
64
    /**
65
     * Application version
66
     * @var string
67
     */
68
    public $verAplic = '';
69
    /**
70
     * last soap request
71
     * @var string
72
     */
73
    public $lastRequest = '';
74
    /**
75
     * last soap response
76
     * @var string
77
     */
78
    public $lastResponse = '';
79
    /**
80
     * certificate class
81
     * @var Certificate
82
     */
83
    protected $certificate;
84
    /**
85
     * Sign algorithm from OPENSSL
86
     * @var int
87
     */
88
    protected $algorithm = OPENSSL_ALGO_SHA1;
89
    /**
90
     * Canonical conversion options
91
     * @var array
92
     */
93
    protected $canonical = [true, false, null, null];
94
    /**
95
     * Model of MDFe 58
96
     * @var int
97
     */
98
    protected $modelo = 58;
99
    /**
100
     * Version of layout
101
     * @var string
102
     */
103
    protected $versao = '3.00';
104
    /**
105
     * urlPortal
106
     * Instância do WebService
107
     *
108
     * @var string
109
     */
110
    protected $urlPortal = 'http://www.portalfiscal.inf.br/mdfe';
111
    /**
112
     * urlcUF
113
     * @var string
114
     */
115
    protected $urlcUF = '';
116
    /**
117
     * urlVersion
118
     * @var string
119
     */
120
    protected $urlVersion = '';
121
    /**
122
     * urlService
123
     * @var string
124
     */
125
    protected $urlService = '';
126
    /**
127
     * @var string
128
     */
129
    protected $urlMethod = '';
130
    /**
131
     * @var string
132
     */
133
    protected $urlOperation = '';
134
    /**
135
     * @var string
136
     */
137
    protected $urlNamespace = '';
138
    /**
139
     * @var string
140
     */
141
    protected $urlAction = '';
142
    /**
143
     * @var \SOAPHeader
144
     */
145
    protected $objHeader;
146
    /**
147
     * @var string
148
     */
149
    protected $urlHeader = '';
150
    /**
151
     * @var array
152
     */
153
    /**
154
     * @var string
155
     */
156
    protected $typePerson = 'J';
157
158
    protected $soapnamespaces = [
159
        'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
160
        'xmlns:xsd' => "http://www.w3.org/2001/XMLSchema",
161
        'xmlns:soap' => "http://www.w3.org/2003/05/soap-envelope"
162
    ];
163
    /**
164
     * @var array
165
     */
166
    protected $availableVersions = [
167
        '3.00' => 'PL_MDFe_300a',
168
        '1.00' => 'PL_MDFe_100'
169
    ];
170
171
    /**
172
     * Constructor
173
     * load configurations,
174
     * load Digital Certificate,
175
     * map all paths,
176
     * set timezone and
177
     * @param string $configJson content of config in json format
178
     * @param Certificate $certificate
179
     */
180
    public function __construct($configJson, Certificate $certificate)
181
    {
182
        $this->config = json_decode($configJson);
183
        $this->pathwsfiles = realpath(
184
            __DIR__ . '/../../storage'
185
        ) . '/';
186
        $this->version($this->config->versao);
187
        $this->setEnvironmentTimeZone($this->config->siglaUF);
188
        $this->certificate = $certificate;
189
        $this->setEnvironment($this->config->tpAmb);
190
        $this->typePerson = $this->getTypeOfPersonFromCertificate();
191
    }
192
193
    /**
194
     * Sets environment time zone
195
     * @param string $acronym (ou seja a sigla do estado)
196
     * @return void
197
     */
198
    public function setEnvironmentTimeZone($acronym)
199
    {
200
        date_default_timezone_set(TimeZoneByUF::get($acronym));
201
    }
202
203
    /**
204
     * Set application version
205
     * @param string $ver
206
     */
207
    public function setVerAplic($ver)
208
    {
209
        $this->verAplic = $ver;
210
    }
211
212
    /**
213
     * Return J or F from existing type in ASN.1 certificate
214
     * J - pessoa juridica (CNPJ)
215
     * F - pessoa física (CPF)
216
     * @return string
217
     */
218
    public function getTypeOfPersonFromCertificate()
219
    {
220
        $cnpj = $this->certificate->getCnpj();
221
        $type = 'J';
222
        if (substr($cnpj, 0, 1) === 'N') {
223
            //não é CNPJ, então verificar se é CPF
224
            $cpf = $this->certificate->getCpf();
225
            if (substr($cpf, 0, 1) !== 'N') {
226
                $type = 'F';
227
            } else {
228
                //não foi localizado nem CNPJ e nem CPF esse certificado não é usável
229
                //throw new RuntimeException('Faltam elementos CNPJ/CPF no certificado digital.');
230
                $type = '';
231
            }
232
        }
233
        return $type;
234
    }
235
236
    /**
237
     * Load Soap Class
238
     * Soap Class may be \NFePHP\Common\Soap\SoapNative
239
     * or \NFePHP\Common\Soap\SoapCurl
240
     * @param SoapInterface $soap
241
     * @return void
242
     */
243
    public function loadSoapClass(SoapInterface $soap)
244
    {
245
        $this->soap = $soap;
246
        $this->soap->loadCertificate($this->certificate);
247
    }
248
249
    /**
250
     * Set OPENSSL Algorithm using OPENSSL constants
251
     * @param int $algorithm
252
     * @return void
253
     */
254
    public function setSignAlgorithm($algorithm = OPENSSL_ALGO_SHA1)
255
    {
256
        $this->algorithm = $algorithm;
257
    }
258
259
    /**
260
     * Set or get parameter layout version
261
     * @param string $version
262
     * @return string
263
     * @throws InvalidArgumentException
264
     */
265
    public function version($version = '')
266
    {
267
        if (!empty($version)) {
268
            if (!array_key_exists($version, $this->availableVersions)) {
269
                throw new \InvalidArgumentException('Essa versão de layout não está disponível');
270
            }
271
            $this->versao = $version;
272
            $this->config->schemes = $this->availableVersions[$version];
273
            $this->pathschemes = realpath(
274
                __DIR__ . '/../../schemes/' . $this->config->schemes
275
            ) . '/';
276
        }
277
        return $this->versao;
278
    }
279
280
    /**
281
     * Recover cUF number from state acronym
282
     * @param string $acronym Sigla do estado
283
     * @return int number cUF
284
     */
285
    public function getcUF($acronym)
286
    {
287
        return UFlist::getCodeByUF($acronym);
288
    }
289
290
    /**
291
     * Recover state acronym from cUF number
292
     * @param int $cUF
293
     * @return string acronym sigla
294
     */
295
    public function getAcronym($cUF)
296
    {
297
        return UFlist::getUFByCode($cUF);
298
    }
299
300
    /**
301
     * Validate cUF from the key content and returns the state acronym
302
     * @param string $chave
303
     * @return string
304
     * @throws InvalidArgumentException
305
     */
306
    public function validKeyByUF($chave)
307
    {
308
        $uf = $this->config->siglaUF;
309
        if ($uf != UFList::getUFByCode(substr($chave, 0, 2))) {
310
            throw new \InvalidArgumentException(
311
                "A chave do MDFe indicado [$chave] não pertence a [$uf]."
312
            );
313
        }
314
        return $uf;
315
    }
316
317
    /**
318
     * Sign MDFe
319
     * @param string $xml MDFe xml content
320
     * @return string singed MDFe xml
321
     * @throws RuntimeException
322
     */
323
    public function signMDFe($xml)
324
    {
325
        //remove all invalid strings
326
        $xml = Strings::clearXmlString($xml);
327
        $signed = Signer::sign(
328
            $this->certificate,
329
            $xml,
330
            'infMDFe',
331
            'Id',
332
            $this->algorithm,
333
            $this->canonical
334
        );
335
        $dom = new DOMDocument('1.0', 'UTF-8');
336
        $dom->preserveWhiteSpace = false;
337
        $dom->formatOutput = false;
338
        $dom->loadXML($signed);
339
        //exception will be throw if MDFe is not valid
340
        $method = 'mdfe';
341
342
        $infMDFeSupl = !empty($dom->getElementsByTagName('infMDFeSupl')->item(0));
343
        if (!$infMDFeSupl) {
344
            $signed = $this->addQRCode($dom, $this->certificate);
345
        }
346
        //$qrCode = $dom->getElementsByTagName('qrCode')->item(0)->nodeValue;
347
348
        $modal = (int) $dom->getElementsByTagName('modal')->item(0)->nodeValue;
349
        //validate
350
        $this->isValid($this->versao, $signed, $method);
351
        //validate modal
352
        switch ($modal) {
353
            case 1:
354
                $this->isValid($this->versao, $this->getModalXML($dom, 'rodo'), $method . 'ModalRodoviario');
355
                break;
356
            case 2:
357
                $this->isValid($this->versao, $this->getModalXML($dom, 'aereo'), $method . 'ModalAereo');
358
                break;
359
            case 3:
360
                $this->isValid($this->versao, $this->getModalXML($dom, 'aquav'), $method . 'ModalAquaviario');
361
                break;
362
            case 4:
363
                $this->isValid($this->versao, $this->getModalXML($dom, 'ferrov'), $method . 'ModalFerroviario');
364
                break;
365
        }
366
        return $signed;
367
    }
368
369
    public function getModalXML($dom, $modal)
370
    {
371
        $modal = $dom->getElementsByTagName($modal)->item(0);
372
        $modal->setAttribute("xmlns", "http://www.portalfiscal.inf.br/mdfe");
373
        return $dom->saveXML($modal);
374
    }
375
376
    /**
377
     * Performs xml validation with its respective
378
     * XSD structure definition document
379
     * NOTE: if dont exists the XSD file will return true
380
     * @param string $version layout version
381
     * @param string $body
382
     * @param string $method
383
     * @return boolean
384
     */
385
    protected function isValid($version, $body, $method)
386
    {
387
        $schema = $this->pathschemes . $method . "_v$version.xsd";
388
        if (!is_file($schema)) {
389
            return true;
390
        }
391
        return Validator::isValid(
392
            $body,
393
            $schema
394
        );
395
    }
396
397
    /**
398
     * Alter environment from "homologacao" to "producao" and vice-versa
399
     * @param int $tpAmb
400
     * @return void
401
     */
402
    public function setEnvironment($tpAmb = 2)
403
    {
404
        if (!empty($tpAmb) && ($tpAmb == 1 || $tpAmb == 2)) {
405
            $this->tpAmb = $tpAmb;
406
            $this->ambiente = ($tpAmb == 1) ? 'producao' : 'homologacao';
407
        }
408
    }
409
410
    /**
411
     * Set option for canonical transformation see C14n
412
     * @param array $opt
413
     * @return array
414
     */
415
    public function canonicalOptions($opt = [true, false, null, null])
416
    {
417
        if (!empty($opt) && is_array($opt)) {
418
            $this->canonical = $opt;
419
        }
420
        return $this->canonical;
421
    }
422
423
    /**
424
     * Assembles all the necessary parameters for soap communication
425
     * @param string $service
426
     * @param string $uf
427
     * @param string $tpAmb
428
     * @return void
429
     */
430
    protected function servico(
431
        $service,
432
        $uf,
433
        $tpAmb
434
    ) {
435
        $ambiente = $tpAmb == 1 ? "producao" : "homologacao";
436
        $webs = new Webservices($this->getXmlUrlPath());
437
        $sigla = $uf;
438
        $stdServ = $webs->get($sigla, $ambiente, $this->modelo);
439
        if ($stdServ === false) {
440
            throw new \RuntimeException(
441
                "Nenhum serviço foi localizado para esta unidade "
442
                    . "da federação [$sigla]."
443
            );
444
        }
445
        if (empty($stdServ->$service->url)) {
446
            throw new \RuntimeException(
447
                "Este serviço [$service] não está disponivel para esta "
448
                    . "unidade da federação [$uf] ou para este modelo"
449
            );
450
        }
451
        //recuperação do cUF
452
        $this->urlcUF = $this->getcUF($uf);
0 ignored issues
show
Documentation Bug introduced by icompsoft
The property $urlcUF was declared of type string, but $this->getcUF($uf) is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
453
        if ($this->urlcUF > 91) {
454
            //foi solicitado dado de SVRS ou SVSP
455
            $this->urlcUF = $this->getcUF($this->config->siglaUF);
0 ignored issues
show
Documentation Bug introduced by icompsoft
The property $urlcUF was declared of type string, but $this->getcUF($this->config->siglaUF) is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
456
        }
457
        //recuperação da versão
458
        $this->urlVersion = $stdServ->$service->version;
459
        //recuperação da url do serviço
460
        $this->urlService = $stdServ->$service->url;
461
        //recuperação do método
462
        $this->urlMethod = $stdServ->$service->method;
463
        //recuperação da operação
464
        $this->urlOperation = $stdServ->$service->operation;
465
        //montagem do namespace do serviço
466
        $this->urlNamespace = sprintf(
467
            "%s/wsdl/%s",
468
            $this->urlPortal,
469
            $this->urlOperation
470
        );
471
        //montagem do cabeçalho da comunicação SOAP
472
        $this->urlHeader = Header::get(
473
            $this->urlNamespace,
474
            $this->urlcUF,
475
            $this->urlVersion
476
        );
477
        $this->urlAction = "\""
478
            . $this->urlNamespace
479
            . "/"
480
            . $this->urlMethod
481
            . "\"";
482
        //montagem do SOAP Header
483
        $this->objHeader = new \SOAPHeader(
484
            $this->urlNamespace,
485
            'mdfeCabecMsg',
486
            ['cUF' => $this->urlcUF, 'versaoDados' => $this->urlVersion]
487
        );
488
    }
489
490
    /**
491
     * Send request message to webservice
492
     * @param array $parameters
493
     * @param string $request
494
     * @return string
495
     */
496
    protected function sendRequest($request, array $parameters = [])
497
    {
498
        $this->checkSoap();
499
        return (string) $this->soap->send(
500
            $this->urlService,
501
            $this->urlMethod,
502
            $this->urlAction,
503
            SOAP_1_2,
504
            $parameters,
505
            $this->soapnamespaces,
506
            $request,
507
            $this->objHeader
508
        );
509
    }
510
511
    /**
512
     * Recover path to xml data base with list of soap services
513
     * @return string
514
     */
515
    protected function getXmlUrlPath()
516
    {
517
        $file = $this->pathwsfiles
518
            . DIRECTORY_SEPARATOR
519
            . "wsmdfe_" . $this->versao . ".xml";
520
        if (!file_exists($file)) {
521
            return '';
522
        }
523
        return file_get_contents($file);
524
    }
525
526
    /**
527
     * Add QRCode Tag to signed XML from a NFCe
528
     * @param DOMDocument $dom
529
     * @return string
530
     */
531
    protected function addQRCode(DOMDocument $dom, $certificate)
532
    {
533
        $signed = QRCode::putQRTag(
534
            $dom,
535
            $certificate
536
        );
537
        return Strings::clearXmlString($signed);
538
    }
539
540
    /**
541
     * Verify if SOAP class is loaded, if not, force load SoapCurl
542
     */
543
    protected function checkSoap()
544
    {
545
        if (empty($this->soap)) {
546
            $this->soap = new SoapCurl($this->certificate);
547
        }
548
    }
549
}
550