Passed
Pull Request — master (#180)
by
unknown
02:15
created

SoapBase   C

Complexity

Total Complexity 44

Size/Duplication

Total Lines 525
Duplicated Lines 0 %

Coupling/Cohesion

Components 7
Dependencies 6

Test Coverage

Coverage 17.91%

Importance

Changes 0
Metric Value
wmc 44
lcom 7
cbo 6
dl 0
loc 525
ccs 24
cts 134
cp 0.1791
rs 6.8
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A mountEnvelopString() 0 16 1
A __construct() 0 7 1
A isCertificateExpired() 0 8 4
A __destruct() 0 4 1
A disableSecurity() 0 4 1
A disableCertValidation() 0 4 1
A loadCA() 0 6 2
A setEncriptPrivateKey() 0 5 1
A setTemporaryFolder() 0 12 3
A setLocalFolder() 0 5 1
A setDebugMode() 0 4 1
A loadCertificate() 0 7 2
A loadLogger() 0 4 1
A timeout() 0 4 1
A protocol() 0 4 1
A setSoapPrefix() 0 4 1
A proxy() 0 7 1
send() 0 10 ?
A makeEnvelopeSoap() 0 15 1
A mountSoapHeaders() 0 19 4
A getStringAttributes() 0 8 2
B saveTemporarilyKeyFiles() 0 51 6
A removeTemporarilyFiles() 0 10 4
A saveDebugFiles() 0 23 3

How to fix   Complexity   

Complex Class

Complex classes like SoapBase 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 SoapBase, and based on these observations, apply Extract Interface, too.

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
            $path = '/sped-'. getmyuid() .'/';
236
            if (function_exists('posix_getuid')) {
237
                $path = '/sped-'. posix_getuid() .'/';
238
            }
239
            $folderRealPath = sys_get_temp_dir() . $path;
240
        }
241
        $this->tempdir = $folderRealPath;
242
        $this->setLocalFolder($folderRealPath);
243
    }
244
245
    /**
246
     * Set Local folder for flysystem
247
     * @param string $folder
248
     */
249
    protected function setLocalFolder($folder = '')
250
    {
251
        $this->adapter = new Local($folder);
252
        $this->filesystem = new Filesystem($this->adapter);
253
    }
254
255
    /**
256
     * Set debug mode, this mode will save soap envelopes in temporary directory
257
     * @param bool $value
258
     * @return bool
259
     */
260
    public function setDebugMode($value = false)
261
    {
262
        return $this->debugmode = $value;
263
    }
264
265
    /**
266
     * Set certificate class for SSL communications
267
     * @param Certificate $certificate
268
     * @return void
269
     */
270 5
    public function loadCertificate(Certificate $certificate = null)
271
    {
272 5
        $this->isCertificateExpired($certificate);
273 4
        if (null !== $certificate) {
274 1
            $this->certificate = $certificate;
275
        }
276 4
    }
277
278
    /**
279
     * Set logger class
280
     * @param LoggerInterface $logger
281
     * @return LoggerInterface
282
     */
283
    public function loadLogger(LoggerInterface $logger)
284
    {
285
        return $this->logger = $logger;
286
    }
287
288
    /**
289
     * Set timeout for communication
290
     * @param int $timesecs
291
     * @return int
292
     */
293
    public function timeout($timesecs)
294
    {
295
        return $this->soaptimeout = $timesecs;
296
    }
297
298
    /**
299
     * Set security protocol
300
     * @param int $protocol
301
     * @return int
302
     */
303
    public function protocol($protocol = self::SSL_DEFAULT)
304
    {
305
        return $this->soapprotocol = $protocol;
306
    }
307
308
    /**
309
     * Set prefixes
310
     * @param array $prefixes
311
     * @return string[]
312
     */
313
    public function setSoapPrefix($prefixes = [])
314
    {
315
        return $this->prefixes = $prefixes;
316
    }
317
318
    /**
319
     * Set proxy parameters
320
     * @param string $ip
321
     * @param int    $port
322
     * @param string $user
323
     * @param string $password
324
     * @return void
325
     */
326
    public function proxy($ip, $port, $user, $password)
327
    {
328
        $this->proxyIP = $ip;
329
        $this->proxyPort = $port;
330
        $this->proxyUser = $user;
331
        $this->proxyPass = $password;
332
    }
333
334
    /**
335
     * @param string $url
336
     * @param string $operation
337
     * @param string $action
338
     * @param int $soapver
339
     * @param array $parameters
340
     * @param array $namespaces
341
     * @param string $request
342
     * @param null $soapheader
343
     * @return mixed
344
     */
345
    abstract public function send(
346
        $url,
347
        $operation = '',
348
        $action = '',
349
        $soapver = SOAP_1_2,
350
        $parameters = [],
351
        $namespaces = [],
352
        $request = '',
353
        $soapheader = null
354
    );
355
356
    /**
357
     * Mount soap envelope
358
     * @param string $request
359
     * @param array $namespaces
360
     * @param int $soapVer
361
     * @param \SoapHeader $header
362
     * @return string
363
     */
364
    protected function makeEnvelopeSoap(
365
        $request,
366
        $namespaces,
367
        $soapVer = SOAP_1_2,
368
        $header = null
369
    ) {
370
        $prefix = $this->prefixes[$soapVer];
371
        $envelopeAttributes = $this->getStringAttributes($namespaces);
372
        return $this->mountEnvelopString(
373
            $prefix,
374
            $envelopeAttributes,
375
            $this->mountSoapHeaders($prefix, $header),
376
            $request
377
        );
378
    }
379
380
    /**
381
     * Create a envelop string
382
     * @param string $envelopPrefix
383
     * @param string $envelopAttributes
384
     * @param string $header
385
     * @param string $bodyContent
386
     * @return string
387
     */
388
    private function mountEnvelopString(
389
        $envelopPrefix,
390
        $envelopAttributes = '',
391
        $header = '',
392
        $bodyContent = ''
393
    ) {
394
        return sprintf(
395
            '<%s:Envelope %s>' . $header . '<%s:Body>%s</%s:Body></%s:Envelope>',
396
            $envelopPrefix,
397
            $envelopAttributes,
398
            $envelopPrefix,
399
            $bodyContent,
400
            $envelopPrefix,
401
            $envelopPrefix
402
        );
403
    }
404
405
    /**
406
     * Create a haeader tag
407
     * @param string $envelopPrefix
408
     * @param \SoapHeader $header
409
     * @return string
410
     */
411
    private function mountSoapHeaders($envelopPrefix, $header = null)
412
    {
413
        if (null === $header) {
414
            return '';
415
        }
416
        $headerItems = '';
417
        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...
418
            $headerItems .= '<' . $key . '>' . $value . '</' . $key . '>';
419
        }
420
        return sprintf(
421
            '<%s:Header><%s xmlns="%s">%s</%s></%s:Header>',
422
            $envelopPrefix,
423
            $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...
424
            $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...
425
            $headerItems,
426
            $header->name,
427
            $envelopPrefix
428
        );
429
    }
430
431
    /**
432
     * Get attributes
433
     * @param array $namespaces
434
     * @return string
435
     */
436
    private function getStringAttributes($namespaces = [])
437
    {
438
        $envelopeAttributes = '';
439
        foreach ($namespaces as $key => $value) {
440
            $envelopeAttributes .= $key . '="' . $value . '" ';
441
        }
442
        return $envelopeAttributes;
443
    }
444
445
446
    /**
447
     * Temporarily saves the certificate keys for use cURL or SoapClient
448
     * @return void
449
     */
450
    public function saveTemporarilyKeyFiles()
451
    {
452
        //certs already exists
453
        if (!empty($this->certsdir)) {
454
            return;
455
        }
456
        if (!is_object($this->certificate)) {
457
            throw new RuntimeException(
458
                'Certificate not found.'
459
            );
460
        }
461
        if (empty($this->filesystem)) {
462
            $this->setTemporaryFolder();
463
        }
464
        //clear dir cert
465
        $this->removeTemporarilyFiles();
466
        $this->certsdir = $this->certificate->getCnpj() . '/certs/';
467
        $this->prifile = $this->certsdir . Strings::randomString(10) . '.pem';
468
        $this->pubfile = $this->certsdir . Strings::randomString(10) . '.pem';
469
        $this->certfile = $this->certsdir . Strings::randomString(10) . '.pem';
470
        $ret = true;
471
        //load private key pem
472
        $private = $this->certificate->privateKey;
473
        if ($this->encriptPrivateKey) {
474
            //replace private key pem with password
475
            $this->temppass = Strings::randomString(16);
476
            //encripta a chave privada entes da gravação do filesystem
477
            openssl_pkey_export(
478
                $this->certificate->privateKey,
479
                $private,
480
                $this->temppass
481
            );
482
        }
483
        $ret &= $this->filesystem->put(
484
            $this->prifile,
485
            $private
486
        );
487
        $ret &= $this->filesystem->put(
488
            $this->pubfile,
489
            $this->certificate->publicKey
490
        );
491
        $ret &= $this->filesystem->put(
492
            $this->certfile,
493
            $private . "{$this->certificate}"
494
        );
495
        if (!$ret) {
496
            throw new RuntimeException(
497
                'Unable to save temporary key files in folder.'
498
            );
499
        }
500
    }
501
502
    /**
503
     * Delete all files in folder
504
     * @return void
505
     */
506 4
    public function removeTemporarilyFiles()
507
    {
508 4
        if (empty($this->filesystem) || empty($this->certsdir)) {
509 4
            return;
510
        }
511
        $contents = $this->filesystem->listContents($this->certsdir, true);
512
        foreach ($contents as $item) {
513
            $this->filesystem->delete($item['path']);
514
        }
515
    }
516
517
    /**
518
     * Save request envelope and response for debug reasons
519
     * @param string $operation
520
     * @param string $request
521
     * @param string $response
522
     * @return void
523
     */
524
    public function saveDebugFiles($operation, $request, $response)
525
    {
526
        if (!$this->debugmode) {
527
            return;
528
        }
529
        $this->debugdir = $this->certificate->getCnpj() . '/debug/';
530
        $now = \DateTime::createFromFormat('U.u', number_format(microtime(true), 6, '.', ''));
531
        $time = substr($now->format("ymdHisu"), 0, 16);
532
        try {
533
            $this->filesystem->put(
534
                $this->debugdir . $time . "_" . $operation . "_sol.txt",
535
                $request
536
            );
537
            $this->filesystem->put(
538
                $this->debugdir . $time . "_" . $operation . "_res.txt",
539
                $response
540
            );
541
        } catch (\Exception $e) {
542
            throw new RuntimeException(
543
                'Unable to create debug files.'
544
            );
545
        }
546
    }
547
}
548