Passed
Push — master ( bc7c66...e2dbde )
by
unknown
03:33 queued 01:31
created

Tools::getTypeOfPersonFromCertificate()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 0
cts 14
cp 0
rs 9.7
c 0
b 0
f 0
cc 3
nc 3
nop 0
crap 12
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
    ];
169
170
    /**
171
     * Constructor
172
     * load configurations,
173
     * load Digital Certificate,
174
     * map all paths,
175
     * set timezone and
176
     * @param string $configJson content of config in json format
177
     * @param Certificate $certificate
178
     */
179
    public function __construct($configJson, Certificate $certificate)
180
    {
181
        $this->config = json_decode($configJson);
182
        $this->pathwsfiles = realpath(
183
            __DIR__ . '/../../storage'
184
        ) . '/';
185
        $this->version($this->config->versao);
186
        $this->setEnvironmentTimeZone($this->config->siglaUF);
187
        $this->certificate = $certificate;
188
        $this->setEnvironment($this->config->tpAmb);
189
        $this->typePerson = $this->getTypeOfPersonFromCertificate();
190
    }
191
192
    /**
193
     * Sets environment time zone
194
     * @param string $acronym (ou seja a sigla do estado)
195
     * @return void
196
     */
197
    public function setEnvironmentTimeZone($acronym)
198
    {
199
        date_default_timezone_set(TimeZoneByUF::get($acronym));
200
    }
201
202
    /**
203
     * Set application version
204
     * @param string $ver
205
     */
206
    public function setVerAplic($ver)
207
    {
208
        $this->verAplic = $ver;
209
    }
210
211
    /**
212
     * Return J or F from existing type in ASN.1 certificate
213
     * J - pessoa juridica (CNPJ)
214
     * F - pessoa física (CPF)
215
     * @return string
216
     */
217
    public function getTypeOfPersonFromCertificate()
218
    {
219
        $cnpj = $this->certificate->getCnpj();
220
        $type = 'J';
221
        if (substr($cnpj, 0, 1) === 'N') {
222
            //não é CNPJ, então verificar se é CPF
223
            $cpf = $this->certificate->getCpf();
224
            if (substr($cpf, 0, 1) !== 'N') {
225
                $type = 'F';
226
            } else {
227
                //não foi localizado nem CNPJ e nem CPF esse certificado não é usável
228
                //throw new RuntimeException('Faltam elementos CNPJ/CPF no certificado digital.');
229
                $type = '';
230
            }
231
        }
232
        return $type;
233
    }
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);
345
        }
346
        $modal = (int) $dom->getElementsByTagName('modal')->item(0)->nodeValue;
347
        //validate
348
        $this->isValid($this->versao, $signed, $method);
349
        //validate modal
350
        switch ($modal) {
351
            case 1:
352
                $this->isValid($this->versao, $this->getModalXML($dom, 'rodo'), $method . 'ModalRodoviario');
353
                break;
354
            case 2:
355
                $this->isValid($this->versao, $this->getModalXML($dom, 'aereo'), $method . 'ModalAereo');
356
                break;
357
            case 3:
358
                $this->isValid($this->versao, $this->getModalXML($dom, 'aquav'), $method . 'ModalAquaviario');
359
                break;
360
            case 4:
361
                $this->isValid($this->versao, $this->getModalXML($dom, 'ferrov'), $method . 'ModalFerroviario');
362
                break;
363
        }
364
        return $signed;
365
    }
366
367
    public function getModalXML($dom, $modal)
368
    {
369
        $modal = $dom->getElementsByTagName($modal)->item(0);
370
        $modal->setAttribute("xmlns", "http://www.portalfiscal.inf.br/mdfe");
371
        return $dom->saveXML($modal);
372
    }
373
374
    /**
375
     * Performs xml validation with its respective
376
     * XSD structure definition document
377
     * NOTE: if dont exists the XSD file will return true
378
     * @param string $version layout version
379
     * @param string $body
380
     * @param string $method
381
     * @return boolean
382
     */
383
    protected function isValid($version, $body, $method)
384
    {
385
        $schema = $this->pathschemes . $method . "_v$version.xsd";
386
        if (!is_file($schema)) {
387
            return true;
388
        }
389
        return Validator::isValid(
390
            $body,
391
            $schema
392
        );
393
    }
394
395
    /**
396
     * Alter environment from "homologacao" to "producao" and vice-versa
397
     * @param int $tpAmb
398
     * @return void
399
     */
400
    public function setEnvironment($tpAmb = 2)
401
    {
402
        if (!empty($tpAmb) && ($tpAmb == 1 || $tpAmb == 2)) {
403
            $this->tpAmb = $tpAmb;
404
            $this->ambiente = ($tpAmb == 1) ? 'producao' : 'homologacao';
405
        }
406
    }
407
408
    /**
409
     * Set option for canonical transformation see C14n
410
     * @param array $opt
411
     * @return array
412
     */
413
    public function canonicalOptions($opt = [true, false, null, null])
414
    {
415
        if (!empty($opt) && is_array($opt)) {
416
            $this->canonical = $opt;
417
        }
418
        return $this->canonical;
419
    }
420
421
    /**
422
     * Assembles all the necessary parameters for soap communication
423
     * @param string $service
424
     * @param string $uf
425
     * @param string $tpAmb
426
     * @return void
427
     */
428
    protected function servico(
429
        $service,
430
        $uf,
431
        $tpAmb
432
    ) {
433
434
        $ambiente = $tpAmb == 1 ? "producao" : "homologacao";
435
        $webs = new Webservices($this->getXmlUrlPath());
436
        $sigla = $uf;
437
        $stdServ = $webs->get($sigla, $ambiente, $this->modelo);
438
        if ($stdServ === false) {
439
            throw new \RuntimeException(
440
                "Nenhum serviço foi localizado para esta unidade "
441
                    . "da federação [$sigla]."
442
            );
443
        }
444
        if (empty($stdServ->$service->url)) {
445
            throw new \RuntimeException(
446
                "Este serviço [$service] não está disponivel para esta "
447
                    . "unidade da federação [$uf] ou para este modelo"
448
            );
449
        }
450
        //recuperação do cUF
451
        $this->urlcUF = $this->getcUF($uf);
0 ignored issues
show
Documentation Bug introduced by
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...
452
        if ($this->urlcUF > 91) {
453
            //foi solicitado dado de SVRS ou SVSP
454
            $this->urlcUF = $this->getcUF($this->config->siglaUF);
0 ignored issues
show
Documentation Bug introduced by
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...
455
        }
456
        //recuperação da versão
457
        $this->urlVersion = $stdServ->$service->version;
458
        //recuperação da url do serviço
459
        $this->urlService = $stdServ->$service->url;
460
        //recuperação do método
461
        $this->urlMethod = $stdServ->$service->method;
462
        //recuperação da operação
463
        $this->urlOperation = $stdServ->$service->operation;
464
        //montagem do namespace do serviço
465
        $this->urlNamespace = sprintf(
466
            "%s/wsdl/%s",
467
            $this->urlPortal,
468
            $this->urlOperation
469
        );
470
        //montagem do cabeçalho da comunicação SOAP
471
        $this->urlHeader = Header::get(
472
            $this->urlNamespace,
473
            $this->urlcUF,
474
            $this->urlVersion
475
        );
476
        $this->urlAction = "\""
477
            . $this->urlNamespace
478
            . "/"
479
            . $this->urlMethod
480
            . "\"";
481
        //montagem do SOAP Header
482
        $this->objHeader = new \SOAPHeader(
483
            $this->urlNamespace,
484
            'mdfeCabecMsg',
485
            ['cUF' => $this->urlcUF, 'versaoDados' => $this->urlVersion]
486
        );
487
    }
488
489
    /**
490
     * Send request message to webservice
491
     * @param array $parameters
492
     * @param string $request
493
     * @return string
494
     */
495
    protected function sendRequest($request, array $parameters = [])
496
    {
497
        $this->checkSoap();
498
        return (string) $this->soap->send(
499
            $this->urlService,
500
            $this->urlMethod,
501
            $this->urlAction,
502
            SOAP_1_2,
503
            $parameters,
504
            $this->soapnamespaces,
505
            $request,
506
            $this->objHeader
507
        );
508
    }
509
510
    /**
511
     * Recover path to xml data base with list of soap services
512
     * @return string
513
     */
514
    protected function getXmlUrlPath()
515
    {
516
        $file = $this->pathwsfiles
517
            . DIRECTORY_SEPARATOR
518
            . "wsmdfe_" . $this->versao . ".xml";
519
        if (!file_exists($file)) {
520
            return '';
521
        }
522
        return file_get_contents($file);
523
    }
524
525
    /**
526
     * Add QRCode Tag to signed XML from a NFCe
527
     * @param DOMDocument $dom
528
     * @return string
529
     */
530
    protected function addQRCode(DOMDocument $dom)
531
    {
532
        $signed = QRCode::putQRTag(
533
            $dom
534
        );
535
        return Strings::clearXmlString($signed);
536
    }
537
538
    /**
539
     * Verify if SOAP class is loaded, if not, force load SoapCurl
540
     */
541
    protected function checkSoap()
542
    {
543
        if (empty($this->soap)) {
544
            $this->soap = new SoapCurl($this->certificate);
545
        }
546
    }
547
}
548