Completed
Push — master ( 28360b...e6d6d0 )
by Roberto
06:06 queued 10s
created

Tools::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 0
cts 11
cp 0
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 2
1
<?php
2
3
namespace NFePHP\BPe\Common;
4
5
/**
6
 * Class base responsible for communication with SEFAZ
7
 *
8
 * @category  NFePHP
9
 * @package   NFePHP\BPe\Common\Tools
10
 * @copyright NFePHP Copyright (c) 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
 * @author    Roberto L. Machado <linux.rlm at gmail dot com>
15
 * @author    Anderson Minuto Consoni Vaz <anderson at wdhouse dot com dot br>
16
 * @link      http://github.com/nfephp-org/sped-bpe for the canonical source repository
17
 */
18
19
use DOMDocument;
20
use InvalidArgumentException;
21
use RuntimeException;
22
use NFePHP\Common\Certificate;
23
use NFePHP\Common\Signer;
24
use NFePHP\Common\Soap\SoapCurl;
25
use NFePHP\Common\Soap\SoapInterface;
26
use NFePHP\Common\Strings;
27
use \NFePHP\Common\DOMImproved as Dom;
28
use NFePHP\Common\TimeZoneByUF;
29
use NFePHP\Common\UFList;
30
use NFePHP\Common\Validator;
31
use NFePHP\BPe\Factories\Header;
32
use NFePHP\BPe\Factories\QRCode;
33
34
class Tools
35
{
36
    /**
37
     * config class
38
     * @var \stdClass
39
     */
40
    public $config;
41
    /**
42
     * Path to storage folder
43
     * @var string
44
     */
45
    public $pathwsfiles = '';
46
    /**
47
     * Path to schemes folder
48
     * @var string
49
     */
50
    public $pathschemes = '';
51
    /**
52
     * ambiente
53
     * @var string
54
     */
55
    public $ambiente = 'homologacao';
56
    /**
57
     * Environment
58
     * @var int
59
     */
60
    public $tpAmb = 2;
61
    /**
62
     * contingency class
63
     * @var Contingency
64
     */
65
    public $contingency;
66
    /**
67
     * soap class
68
     * @var SoapInterface
69
     */
70
    public $soap;
71
    /**
72
     * Application version
73
     * @var string
74
     */
75
    public $verAplic = '';
76
    /**
77
     * last soap request
78
     * @var string
79
     */
80
    public $lastRequest = '';
81
    /**
82
     * last soap response
83
     * @var string
84
     */
85
    public $lastResponse = '';
86
    /**
87
     * certificate class
88
     * @var Certificate
89
     */
90
    protected $certificate;
91
    /**
92
     * Sign algorithm from OPENSSL
93
     * @var int
94
     */
95
    protected $algorithm = OPENSSL_ALGO_SHA1;
96
    /**
97
     * Canonical conversion options
98
     * @var array
99
     */
100
    protected $canonical = [true,false,null,null];
101
    /**
102
     * Model of BPe 63
103
     * @var int
104
     */
105
    protected $modelo = 63;
106
    /**
107
     * Version of layout
108
     * @var string
109
     */
110
    protected $versao = '1.00';
111
    /**
112
     * urlPortal
113
     * Inst�ncia do WebService
114
     *
115
     * @var string
116
     */
117
    protected $urlPortal = 'http://www.portalfiscal.inf.br/bpe';
118
    /**
119
     * urlcUF
120
     * @var string
121
     */
122
    protected $urlcUF = '';
123
    /**
124
     * urlVersion
125
     * @var string
126
     */
127
    protected $urlVersion = '';
128
    /**
129
     * urlService
130
     * @var string
131
     */
132
    protected $urlService = '';
133
    /**
134
     * @var string
135
     */
136
    protected $urlMethod = '';
137
    /**
138
     * @var string
139
     */
140
    protected $urlOperation = '';
141
    /**
142
     * @var string
143
     */
144
    protected $urlNamespace = '';
145
    /**
146
     * @var string
147
     */
148
    protected $urlAction = '';
149
    /**
150
     * @var \SOAPHeader
151
     */
152
    protected $objHeader;
153
    /**
154
     * @var string
155
     */
156
    protected $urlHeader = '';
157
    /**
158
     * @var bool
159
     */
160
    protected $allowSignatureInQRCode = false;
161
    /**
162
     * @var array
163
     */
164
    protected $soapnamespaces = [
165
        'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
166
        'xmlns:xsd' => "http://www.w3.org/2001/XMLSchema",
167
        'xmlns:soap' => "http://www.w3.org/2003/05/soap-envelope"
168
    ];
169
    /**
170
     * @var array
171
     */
172
    protected $availableVersions = [
173
        '1.00' => 'PL_BPe_100b'
174
    ];
175
176
    /**
177
     * Constructor
178
     * load configurations,
179
     * load Digital Certificate,
180
     * map all paths,
181
     * set timezone and
182
     * and instanciate Contingency::class
183
     * @param string $configJson content of config in json format
184
     * @param Certificate $certificate
185
     */
186
    public function __construct($configJson, Certificate $certificate)
187
    {
188
        $this->config = json_decode($configJson);
189
        $this->pathwsfiles = realpath(
190
            __DIR__ . '/../../storage'
191
        ).'/';
192
        $this->version($this->config->versao);
193
        $this->setEnvironmentTimeZone($this->config->siglaUF);
194
        $this->certificate = $certificate;
195
        $this->setEnvironment($this->config->tpAmb);
196
    }
197
198
    /**
199
     * Sets environment time zone
200
     * @param string $acronym (ou seja a sigla do estado)
201
     * @return void
202
     */
203
    public function setEnvironmentTimeZone($acronym)
204
    {
205
        date_default_timezone_set(TimeZoneByUF::get($acronym));
206
    }
207
208
    /**
209
     * Set application version
210
     * @param string $ver
211
     */
212
    public function setVerAplic($ver)
213
    {
214
        $this->verAplic = $ver;
215
    }
216
    
217
    /**
218
     * Set for inclusion of sign in qrcode string
219
     * @param bool $flag
220
     * @return bool
221
     */
222
    public function setSignatureInQRCode($flag = false)
223
    {
224
        return $this->allowSignatureInQRCode = $flag;
225
    }
226
227
    /**
228
     * Load Soap Class
229
     * Soap Class may be \NFePHP\Common\Soap\SoapNative
230
     * or \NFePHP\Common\Soap\SoapCurl
231
     * @param SoapInterface $soap
232
     * @return void
233
     */
234
    public function loadSoapClass(SoapInterface $soap)
235
    {
236
        $this->soap = $soap;
237
        $this->soap->loadCertificate($this->certificate);
238
    }
239
240
    /**
241
     * Set OPENSSL Algorithm using OPENSSL constants
242
     * @param int $algorithm
243
     * @return void
244
     */
245
    public function setSignAlgorithm($algorithm = OPENSSL_ALGO_SHA1)
246
    {
247
        $this->algorithm = $algorithm;
248
    }
249
250
    /**
251
     * Set or get model of document BPe = 63
252
     * @param int $model
253
     * @return int modelo class parameter
254
     */
255
    public function model($model = null)
256
    {
257
        if ($model == 63) {
258
            $this->modelo = $model;
259
        }
260
        return $this->modelo;
261
    }
262
263
    /**
264
     * Set or get parameter layout version
265
     * @param string $version
266
     * @return string
267
     * @throws InvalidArgumentException
268
     */
269
    public function version($version = '')
270
    {
271
        if (!empty($version)) {
272
            if (!array_key_exists($version, $this->availableVersions)) {
273
                throw new \InvalidArgumentException('Essa versão de layout não está disponível');
274
            }
275
            $this->versao = $version;
276
            $this->config->schemes = $this->availableVersions[$version];
277
            $this->pathschemes = realpath(
278
                __DIR__ . '/../../schemes/'. $this->config->schemes
279
            ).'/';
280
        }
281
        return $this->versao;
282
    }
283
284
    /**
285
     * Recover cUF number from state acronym
286
     * @param string $acronym Sigla do estado
287
     * @return int number cUF
288
     */
289
    public function getcUF($acronym)
290
    {
291
        return UFlist::getCodeByUF($acronym);
292
    }
293
294
    /**
295
     * Recover state acronym from cUF number
296
     * @param int $cUF
297
     * @return string acronym sigla
298
     */
299
    public function getAcronym($cUF)
300
    {
301
        return UFlist::getUFByCode($cUF);
302
    }
303
304
    /**
305
     * Validate cUF from the key content and returns the state acronym
306
     * @param string $chave
307
     * @return string
308
     * @throws InvalidArgumentException
309
     */
310
    public function validKeyByUF($chave)
311
    {
312
        $uf = $this->config->siglaUF;
313
        if ($uf != UFList::getUFByCode(substr($chave, 0, 2))) {
314
            throw new \InvalidArgumentException(
315
                "A chave do BPe indicado [$chave] não pertence a [$uf]."
316
            );
317
        }
318
        return $uf;
319
    }
320
321
    /**
322
     * Sign BPe
323
     * @param  string  $xml BPe xml content
324
     * @return string singed BPe xml
325
     * @throws RuntimeException
326
     */
327
    public function signBPe($xml)
328
    {
329
        //remove all invalid strings
330
        $xml = Strings::clearXmlString($xml);
331
        $dom = new Dom('1.0', 'UTF-8');
332
        $dom->preserveWhiteSpace = false;
333
        $dom->formatOutput = false;
334
        $dom->loadXML($xml);
335
        if (!empty($dom->getElementsByTagName('Signature')->item(0))) {
336
            return $xml;
337
        }
338
        $signed = Signer::sign(
339
            $this->certificate,
340
            $xml,
341
            'infBPe',
342
            'Id',
343
            $this->algorithm,
344
            $this->canonical
345
        );
346
        $signed = $this->addQRCode($signed);
347
        $method = 'bpe';
348
        $this->isValid($this->versao, $signed, $method);
349
        return $signed;
350
    }
351
    
352
    protected function addQRCode($signed)
353
    {
354
        $dom = new Dom('1.0', 'UTF-8');
355
        $dom->preserveWhiteSpace = false;
356
        $dom->formatOutput = false;
357
        $dom->loadXML($signed);
358
        $infBPeSupl = !empty($dom->getElementsByTagName('infBPeSupl')->item(0))
359
            ? $dom->getElementsByTagName('infBPeSupl')->item(0)
360
            : null;
361
        $qr = null;
362
        if ($infBPeSupl != null) {
363
            $qr = !empty($infBPeSupl->getElementsByTagName('qrCodBPe')->item(0))
364
                ? $infBPeSupl->getElementsByTagName('qrCodBPe')->item(0)->nodeValue
365
                : null;
366
        }
367
        if ($qr != null) {
368
            return $signed;
369
        }
370
        $bpe = $dom->documentElement;
371
        
372
        $infBPe = $dom->getElementsByTagName('infBPe')->item(0);
373
        $chave = preg_replace('/[^0-9]/', '', $infBPe->getAttribute("Id"));
374
        $ide = $dom->getElementsByTagName('ide')->item(0);
375
        $cUF = $ide->getElementsByTagName('cUF')->item(0)->nodeValue;
376
        $tpAmb = $ide->getElementsByTagName('tpAmb')->item(0)->nodeValue;
377
        $signature = $dom->getElementsByTagName('Signature')->item(0);
378
        $hash = base64_encode($this->certificate->privateKey->sign($chave));
379
        $uf = UFList::getUFByCode($cUF);
380
        $this->servico('BPeQrCode', $uf, $tpAmb);
381
        $url = $this->urlService;
382
        $qrcode = "{$url}?chBPe={$chave}&tpAmb={$tpAmb}";
383
        if ($this->allowSignatureInQRCode) {
384
            $qrcode .= "&sign={$hash}";
385
        }
386
        if (empty($infBPeSupl)) {
387
            //não existe a tag suplementar
388
            $infBPeSupl = $dom->createElement("infBPeSupl");
389
            $nodeqr = $infBPeSupl->appendChild($dom->createElement('qrCodBPe'));
390
            $nodeqr->appendChild($dom->createCDATASection($qrcode));
391
            $bpe->insertBefore($infBPeSupl, $signature);
392
            return $dom->saveXML();
393
        } else {
394
            //a tag já existe, então verificar se existe o node qrCodBPe
395
            $boardPassBPe = !empty($infBPeSupl->getElementsByTagName('boardPassBPe')->item(0)->nodeValue)
396
                ? $infBPeSupl->getElementsByTagName('boardPassBPe')->item(0)->nodeValue
397
                : null;
398
            //o node qr não existe
399
            $old = $bpe->removeChild($infBPeSupl);
0 ignored issues
show
Unused Code introduced by
$old is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
400
            $infBPeSupl = $dom->createElement("infBPeSupl");
401
            $nodeqr = $infBPeSupl->appendChild($dom->createElement('qrCodBPe'));
402
            $nodeqr->appendChild($dom->createCDATASection($qrcode));
403
            if ($boardPassBPe != null) {
404
                $infBPeSupl->appendChild($dom->createElement('boardPassBPe', $boardPassBPe));
405
            }
406
            $bpe->insertBefore($infBPeSupl, $signature);
407
            return $dom->saveXML();
408
        }
409
    }
410
411
    /**
412
     * Performs xml validation with its respective
413
     * XSD structure definition document
414
     * NOTE: if dont exists the XSD file will return true
415
     * @param string $version layout version
416
     * @param string $body
417
     * @param string $method
418
     * @return boolean
419
     */
420
    protected function isValid($version, $body, $method)
421
    {
422
        $schema = $this->pathschemes.$method."_v$version.xsd";
423
        if (!is_file($schema)) {
424
            return true;
425
        }
426
        return Validator::isValid(
427
            $body,
428
            $schema
429
        );
430
    }
431
432
    /**
433
     * Alter environment from "homologacao" to "producao" and vice-versa
434
     * @param int $tpAmb
435
     * @return void
436
     */
437
    public function setEnvironment($tpAmb = 2)
438
    {
439
        if (!empty($tpAmb) && ($tpAmb == 1 || $tpAmb == 2)) {
440
            $this->tpAmb = $tpAmb;
441
            $this->ambiente = ($tpAmb == 1) ? 'producao' : 'homologacao';
442
        }
443
    }
444
445
    /**
446
     * Set option for canonical transformation see C14n
447
     * @param array $opt
448
     * @return array
449
     */
450
    public function canonicalOptions($opt = [true,false,null,null])
451
    {
452
        if (!empty($opt) && is_array($opt)) {
453
            $this->canonical = $opt;
454
        }
455
        return $this->canonical;
456
    }
457
458
    /**
459
     * Assembles all the necessary parameters for soap communication
460
     * @param string $service
461
     * @param string $uf
462
     * @param string $tpAmb
463
     * @param bool $ignoreContingency
464
     * @return void
465
     */
466
    protected function servico(
467
        $service,
468
        $uf,
469
        $tpAmb,
470
        $ignoreContingency = false
0 ignored issues
show
Unused Code introduced by
The parameter $ignoreContingency is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
471
    ) {
472
        $ambiente = $tpAmb == 1 ? "producao" : "homologacao";
473
        $webs = new Webservices($this->getXmlUrlPath());
474
        $sigla = $uf;
475
        $stdServ = $webs->get($sigla, $ambiente, $this->modelo);
476
        if ($stdServ === false) {
477
            throw new \RuntimeException(
478
                "Nenhum serviço foi localizado para esta unidade "
479
                . "da federação [$sigla], com o modelo [$this->modelo]."
480
            );
481
        }
482
        if (empty($stdServ->$service->url)) {
483
            throw new \RuntimeException(
484
                "Este serviço [$service] não está disponivel para esta "
485
                . "unidade da federação [$uf] ou para este modelo ["
486
                . $this->modelo
487
                ."]."
488
            );
489
        }
490
        $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...
491
        $this->urlVersion = $stdServ->$service->version;
492
        $this->urlService = $stdServ->$service->url;
493
        $this->urlMethod = $stdServ->$service->method;
494
        $this->urlOperation = $stdServ->$service->operation;
495
        $this->urlNamespace = sprintf(
496
            "%s/wsdl/%s",
497
            $this->urlPortal,
498
            $this->urlOperation
499
        );
500
        $this->urlHeader = Header::get(
501
            $this->urlNamespace,
502
            $this->urlcUF,
503
            $this->urlVersion
504
        );
505
        $this->urlAction = "\""
506
            . $this->urlNamespace
507
            . "/"
508
            . $this->urlMethod
509
            . "\"";
510
    }
511
512
    /**
513
     * Send request message to webservice
514
     * @param array $parameters
515
     * @param string $request
516
     * @return string
517
     */
518
    protected function sendRequest($request, array $parameters = [])
519
    {
520
        $this->checkSoap();
521
        return (string) $this->soap->send(
522
            $this->urlService,
523
            $this->urlMethod,
524
            $this->urlAction,
525
            SOAP_1_2,
526
            $parameters,
527
            $this->soapnamespaces,
528
            $request
529
        );
530
    }
531
532
    /**
533
     * Recover path to xml data base with list of soap services
534
     * @return string
535
     */
536
    protected function getXmlUrlPath()
537
    {
538
        $file = $this->pathwsfiles
539
            . DIRECTORY_SEPARATOR
540
            . "wsbpe_".$this->versao."_mod63.xml";
541
        if (! file_exists($file)) {
542
            return '';
543
        }
544
        return file_get_contents($file);
545
    }
546
    
547
    /**
548
     * Verify if SOAP class is loaded, if not, force load SoapCurl
549
     */
550
    protected function checkSoap()
551
    {
552
        if (empty($this->soap)) {
553
            $this->soap = new SoapCurl($this->certificate);
554
        }
555
    }
556
}
557