Passed
Push — master ( 767d4f...898805 )
by Roberto
44s
created

SoapBase::removeTemporarilyFiles()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 6.9849

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 3
cts 7
cp 0.4286
rs 9.2
c 0
b 0
f 0
cc 4
eloc 6
nc 3
nop 0
crap 6.9849
1
<?php
2
namespace NFePHP\Common\Soap;
3
4
use NFePHP\Common\Certificate;
5
use NFePHP\Common\Exception\RuntimeException;
6
use NFePHP\Common\Strings;
7
use League\Flysystem\Filesystem;
8
use League\Flysystem\Adapter\Local;
9
use Psr\Log\LoggerInterface;
10
11
/**
12
 * Soap base class
13
 *
14
 * @category  NFePHP
15
 * @package   NFePHP\Common\Soap\SoapBase
16
 * @copyright NFePHP Copyright (c) 2017
17
 * @author    Roberto L. Machado <linux.rlm at gmail dot com>
18
 * @license   http://www.gnu.org/licenses/lgpl.txt LGPLv3+
19
 * @license   https://opensource.org/licenses/MIT MIT
20
 * @license   http://www.gnu.org/licenses/gpl.txt GPLv3+
21
 * @link      http://github.com/nfephp-org/sped-nfse for the canonical source repository
22
 */
23
abstract class SoapBase implements SoapInterface
24
{
25
    /**
26
     * @var int
27
     */
28
    protected $soapprotocol = self::SSL_DEFAULT;
29
    /**
30
     * @var int
31
     */
32
    protected $soaptimeout = 20;
33
    /**
34
     * @var string
35
     */
36
    protected $proxyIP;
37
    /**
38
     * @var int
39
     */
40
    protected $proxyPort;
41
    /**
42
     * @var string
43
     */
44
    protected $proxyUser;
45
    /**
46
     * @var string
47
     */
48
    protected $proxyPass;
49
    /**
50
     * @var array
51
     */
52
    protected $prefixes = [1 => 'soapenv', 2 => 'soap'];
53
    /**
54
     * @var Certificate
55
     */
56
    protected $certificate;
57
    /**
58
     * @var LoggerInterface|null
59
     */
60
    protected $logger;
61
    /**
62
     * @var string
63
     */
64
    protected $tempdir;
65
    /**
66
     * @var string
67
     */
68
    protected $certsdir;
69
    /**
70
     * @var string
71
     */
72
    protected $debugdir;
73
    /**
74
     * @var string
75
     */
76
    protected $prifile;
77
    /**
78
     * @var string
79
     */
80
    protected $pubfile;
81
    /**
82
     * @var string
83
     */
84
    protected $certfile;
85
    /**
86
     * @var string
87
     */
88
    protected $casefaz;
89
    /**
90
     * @var bool
91
     */
92
    protected $disablesec = false;
93
    /**
94
     * @var bool
95
     */
96
    protected $disableCertValidation = false;
97
    /**
98
     * @var \League\Flysystem\Adapter\Local
99
     */
100
    protected $adapter;
101
    /**
102
     * @var \League\Flysystem\Filesystem
103
     */
104
    protected $filesystem;
105
    /**
106
     * @var string
107
     */
108
    protected $temppass = '';
109
    /**
110
     * @var bool
111
     */
112
    protected $encriptPrivateKey = false;
113
    /**
114
     * @var bool
115
     */
116
    protected $debugmode = false;
117
    /**
118
     * @var string
119
     */
120
    public $responseHead;
121
    /**
122
     * @var string
123
     */
124
    public $responseBody;
125
    /**
126
     * @var string
127
     */
128
    public $requestHead;
129
    /**
130
     * @var string
131
     */
132
    public $requestBody;
133
    /**
134
     * @var string
135
     */
136
    public $soaperror;
137
    /**
138
     * @var array
139
     */
140
    public $soapinfo = [];
141
    /**
142
     * @var int
143
     */
144
    public $waitingTime = 45;
145
146
    /**
147
     * SoapBase constructor.
148
     * @param Certificate|null $certificate
149
     * @param LoggerInterface|null $logger
150
     */
151 5
    public function __construct(
152
        Certificate $certificate = null,
153
        LoggerInterface $logger = null
154
    ) {
155 5
        $this->logger = $logger;
156 5
        $this->loadCertificate($certificate);
157 4
    }
158
159
    /**
160
     * Check if certificate is valid to currently used date
161
     * @param Certificate $certificate
162
     * @return void
163
     * @throws Certificate\Exception\Expired
164
     */
165 5
    private function isCertificateExpired(Certificate $certificate = null)
166
    {
167 5
        if (!$this->disableCertValidation) {
168 5
            if (null !== $certificate && $certificate->isExpired()) {
169 1
                throw new Certificate\Exception\Expired($certificate);
170
            }
171
        }
172 4
    }
173
174
    /**
175
     * Destructor
176
     * Clean temporary files
177
     */
178 4
    public function __destruct()
179
    {
180 4
        $this->removeTemporarilyFiles();
181 4
    }
182
183
    /**
184
     * Disables the security checking of host and peer certificates
185
     * @param bool $flag
186
     * @return bool
187
     */
188 1
    public function disableSecurity($flag = false)
189
    {
190 1
        return $this->disablesec = $flag;
191
    }
192
193
    /**
194
     * ONlY for tests
195
     * @param bool $flag
196
     * @return bool
197
     */
198 1
    public function disableCertValidation($flag = true)
199
    {
200 1
        return $this->disableCertValidation = $flag;
201
    }
202
203
    /**
204
     * Load path to CA and enable to use on SOAP
205
     * @param string $capath
206
     * @return void
207
     */
208
    public function loadCA($capath)
209
    {
210
        if (is_file($capath)) {
211
            $this->casefaz = $capath;
212
        }
213
    }
214
215
    /**
216
     * Set option to encrypt private key before save in filesystem
217
     * for an additional layer of protection
218
     * @param bool $encript
219
     * @return bool
220
     */
221
    public function setEncriptPrivateKey($encript = true)
222
    {
223
        $this->encriptPrivateKey = $encript;
224
        return $this->encriptPrivateKey;
225
    }
226
227
    /**
228
     * Set another temporayfolder for saving certificates for SOAP utilization
229
     * @param string | null $folderRealPath
230
     * @return void
231
     */
232
    public function setTemporaryFolder($folderRealPath = null)
233
    {
234
        if (empty($folderRealPath)) {
235
            $folderRealPath = sys_get_temp_dir() . '/sped/';
236
        }
237
        $this->tempdir = $folderRealPath;
238
        $this->setLocalFolder($folderRealPath);
239
    }
240
241
    /**
242
     * Set Local folder for flysystem
243
     * @param string $folder
244
     */
245
    protected function setLocalFolder($folder = '')
246
    {
247
        $this->adapter = new Local($folder);
248
        $this->filesystem = new Filesystem($this->adapter);
249
    }
250
251
    /**
252
     * Set debug mode, this mode will save soap envelopes in temporary directory
253
     * @param bool $value
254
     * @return bool
255
     */
256
    public function setDebugMode($value = false)
257
    {
258
        return $this->debugmode = $value;
259
    }
260
261
    /**
262
     * Set certificate class for SSL communications
263
     * @param Certificate $certificate
264
     * @return void
265
     */
266 5
    public function loadCertificate(Certificate $certificate = null)
267
    {
268 5
        $this->isCertificateExpired($certificate);
269 4
        if (null !== $certificate) {
270 1
            $this->certificate = $certificate;
271
        }
272 4
    }
273
274
    /**
275
     * Set logger class
276
     * @param LoggerInterface $logger
277
     * @return LoggerInterface
278
     */
279
    public function loadLogger(LoggerInterface $logger)
280
    {
281
        return $this->logger = $logger;
282
    }
283
284
    /**
285
     * Set timeout for communication
286
     * @param int $timesecs
287
     * @return int
288
     */
289
    public function timeout($timesecs)
290
    {
291
        return $this->soaptimeout = $timesecs;
292
    }
293
294
    /**
295
     * Set security protocol
296
     * @param int $protocol
297
     * @return int
298
     */
299
    public function protocol($protocol = self::SSL_DEFAULT)
300
    {
301
        return $this->soapprotocol = $protocol;
302
    }
303
304
    /**
305
     * Set prefixes
306
     * @param array $prefixes
307
     * @return string[]
308
     */
309
    public function setSoapPrefix($prefixes = [])
310
    {
311
        return $this->prefixes = $prefixes;
312
    }
313
314
    /**
315
     * Set proxy parameters
316
     * @param string $ip
317
     * @param int    $port
318
     * @param string $user
319
     * @param string $password
320
     * @return void
321
     */
322
    public function proxy($ip, $port, $user, $password)
323
    {
324
        $this->proxyIP = $ip;
325
        $this->proxyPort = $port;
326
        $this->proxyUser = $user;
327
        $this->proxyPass = $password;
328
    }
329
330
    /**
331
     * @param string $url
332
     * @param string $operation
333
     * @param string $action
334
     * @param int $soapver
335
     * @param array $parameters
336
     * @param array $namespaces
337
     * @param string $request
338
     * @param null $soapheader
339
     * @return mixed
340
     */
341
    abstract public function send(
342
        $url,
343
        $operation = '',
344
        $action = '',
345
        $soapver = SOAP_1_2,
346
        $parameters = [],
347
        $namespaces = [],
348
        $request = '',
349
        $soapheader = null
350
    );
351
352
    /**
353
     * Mount soap envelope
354
     * @param string $request
355
     * @param array $namespaces
356
     * @param int $soapVer
357
     * @param \SoapHeader $header
358
     * @return string
359
     */
360
    protected function makeEnvelopeSoap(
361
        $request,
362
        $namespaces,
363
        $soapVer = SOAP_1_2,
364
        $header = null
365
    ) {
366
        $prefix = $this->prefixes[$soapVer];
367
        $envelopeAttributes = $this->getStringAttributes($namespaces);
368
        return $this->mountEnvelopString(
369
            $prefix,
370
            $envelopeAttributes,
371
            $this->mountSoapHeaders($prefix, $header),
372
            $request
373
        );
374
    }
375
376
    /**
377
     * Create a envelop string
378
     * @param string $envelopPrefix
379
     * @param string $envelopAttributes
380
     * @param string $header
381
     * @param string $bodyContent
382
     * @return string
383
     */
384
    private function mountEnvelopString(
385
        $envelopPrefix,
386
        $envelopAttributes = '',
387
        $header = '',
388
        $bodyContent = ''
389
    ) {
390
        return sprintf(
391
            '<%s:Envelope %s>' . $header . '<%s:Body>%s</%s:Body></%s:Envelope>',
392
            $envelopPrefix,
393
            $envelopAttributes,
394
            $envelopPrefix,
395
            $bodyContent,
396
            $envelopPrefix,
397
            $envelopPrefix
398
        );
399
    }
400
401
    /**
402
     * Create a haeader tag
403
     * @param string $envelopPrefix
404
     * @param \SoapHeader $header
405
     * @return string
406
     */
407
    private function mountSoapHeaders($envelopPrefix, $header = null)
408
    {
409
        if (null === $header) {
410
            return '';
411
        }
412
        $headerItems = '';
413
        foreach ($header->data as $key => $value) {
0 ignored issues
show
Bug introduced by
The property data does not seem to exist in SoapHeader.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
414
            $headerItems .= '<' . $key . '>' . $value . '</' . $key . '>';
415
        }
416
        return sprintf(
417
            '<%s:Header><%s xmlns="%s">%s</%s></%s:Header>',
418
            $envelopPrefix,
419
            $header->name,
0 ignored issues
show
Bug introduced by
The property name does not seem to exist in SoapHeader.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
420
            $header->namespace === null ? '' : $header->namespace,
0 ignored issues
show
Bug introduced by
The property namespace does not seem to exist in SoapHeader.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
421
            $headerItems,
422
            $header->name,
423
            $envelopPrefix
424
        );
425
    }
426
427
    /**
428
     * Get attributes
429
     * @param array $namespaces
430
     * @return string
431
     */
432
    private function getStringAttributes($namespaces = [])
433
    {
434
        $envelopeAttributes = '';
435
        foreach ($namespaces as $key => $value) {
436
            $envelopeAttributes .= $key . '="' . $value . '" ';
437
        }
438
        return $envelopeAttributes;
439
    }
440
441
442
    /**
443
     * Temporarily saves the certificate keys for use cURL or SoapClient
444
     * @return void
445
     */
446
    public function saveTemporarilyKeyFiles()
447
    {
448
        //certs already exists
449
        if (!empty($this->certsdir)) {
450
            return;
451
        }
452
        if (!is_object($this->certificate)) {
453
            throw new RuntimeException(
454
                'Certificate not found.'
455
            );
456
        }
457
        if (empty($this->filesystem)) {
458
            $this->setTemporaryFolder();
459
        }
460
        //clear dir cert
461
        $this->removeTemporarilyFiles();
462
        $this->certsdir = $this->certificate->getCnpj() . '/certs/';
463
        $this->prifile = $this->certsdir . Strings::randomString(10) . '.pem';
464
        $this->pubfile = $this->certsdir . Strings::randomString(10) . '.pem';
465
        $this->certfile = $this->certsdir . Strings::randomString(10) . '.pem';
466
        $ret = true;
467
        //load private key pem
468
        $private = $this->certificate->privateKey;
469
        if ($this->encriptPrivateKey) {
470
            //replace private key pem with password
471
            $this->temppass = Strings::randomString(16);
472
            //encripta a chave privada entes da gravação do filesystem
473
            openssl_pkey_export(
474
                $this->certificate->privateKey,
475
                $private,
476
                $this->temppass
477
            );
478
        }
479
        $ret &= $this->filesystem->put(
480
            $this->prifile,
481
            $private
482
        );
483
        $ret &= $this->filesystem->put(
484
            $this->pubfile,
485
            $this->certificate->publicKey
486
        );
487
        $ret &= $this->filesystem->put(
488
            $this->certfile,
489
            $private . "{$this->certificate}"
490
        );
491
        if (!$ret) {
492
            throw new RuntimeException(
493
                'Unable to save temporary key files in folder.'
494
            );
495
        }
496
    }
497
498
    /**
499
     * Delete all files in folder
500
     * @return void
501
     */
502 4
    public function removeTemporarilyFiles()
503
    {
504 4
        if (empty($this->filesystem) || empty($this->certsdir)) {
505 4
            return;
506
        }
507
        $contents = $this->filesystem->listContents($this->certsdir, true);
508
        foreach ($contents as $item) {
509
            $this->filesystem->delete($item['path']);
510
        }
511
    }
512
513
    /**
514
     * Save request envelope and response for debug reasons
515
     * @param string $operation
516
     * @param string $request
517
     * @param string $response
518
     * @return void
519
     */
520
    public function saveDebugFiles($operation, $request, $response)
521
    {
522
        if (!$this->debugmode) {
523
            return;
524
        }
525
        $this->debugdir = $this->certificate->getCnpj() . '/debug/';
526
        $now = \DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', ''));
527
        $time = substr($now->format("ymdHisu"), 0, 16);
528
        try {
529
            $this->filesystem->put(
530
                $this->debugdir . $time . "_" . $operation . "_sol.txt",
531
                $request
532
            );
533
            $this->filesystem->put(
534
                $this->debugdir . $time . "_" . $operation . "_res.txt",
535
                $response
536
            );
537
        } catch (\Exception $e) {
538
            throw new RuntimeException(
539
                'Unable to create debug files.'
540
            );
541
        }
542
    }
543
}
544