Passed
Pull Request — master (#118)
by Roberto
02:45
created

SoapBase::send()

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 0
cp 0
c 0
b 0
f 0
nc 1
1
<?php
2
3
namespace NFePHP\Common\Soap;
4
5
/**
6
 * Soap base class
7
 *
8
 * @category  NFePHP
9
 * @package   NFePHP\Common\Soap\SoapBase
10
 * @copyright NFePHP Copyright (c) 2017
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
 * @link      http://github.com/nfephp-org/sped-nfse for the canonical source repository
16
 */
17
18
use NFePHP\Common\Certificate;
19
use NFePHP\Common\Soap\SoapInterface;
20
use NFePHP\Common\Exception\SoapException;
21
use NFePHP\Common\Exception\RuntimeException;
22
use NFePHP\Common\Strings;
23
use League\Flysystem\Filesystem;
24
use League\Flysystem\Adapter\Local;
25
use Psr\Log\LoggerInterface;
26
27
abstract class SoapBase implements SoapInterface
28
{
29
    /**
30
     * @var int
31
     */
32
    protected $soapprotocol = self::SSL_DEFAULT;
33
    /**
34
     * @var int
35
     */
36
    protected $soaptimeout = 20;
37
    /**
38
     * @var string
39
     */
40
    protected $proxyIP;
41
    /**
42
     * @var string
43
     */
44
    protected $proxyPort;
45
    /**
46
     * @var string
47
     */
48
    protected $proxyUser;
49
    /**
50
     * @var string
51
     */
52
    protected $proxyPass;
53
    /**
54
     * @var array
55
     */
56
    protected $prefixes = [1 => 'soapenv', 2 => 'soap'];
57
    /**
58
     * @var Certificate
59
     */
60
    protected $certificate;
61
    /**
62
     * @var LoggerInterface
63
     */
64
    protected $logger;
65
    /**
66
     * @var string
67
     */
68
    protected $tempdir;
69
    /**
70
     * @var string
71
     */
72
    protected $certsdir;
73
    /**
74
     * @var string
75
     */
76
    protected $debugdir;
77
    /**
78
     * @var string
79
     */
80
    protected $prifile;
81
    /**
82
     * @var string
83
     */
84
    protected $pubfile;
85
    /**
86
     * @var string
87
     */
88
    protected $certfile;
89
    /**
90
     * @var string
91
     */
92
    protected $casefaz;
93
    /**
94
     * @var bool
95
     */
96
    protected $disablesec = false;
97
    /**
98
     * @var bool
99
     */
100
    protected $disableCertValidation = false;
101
    /**
102
     * @var League\Flysystem\Adapter\Local
103
     */
104
    protected $adapter;
105
    /**
106
     * @var League\Flysystem\Filesystem
107
     */
108
    protected $filesystem;
109
    /**
110
     * @var string
111
     */
112
    protected $temppass = '';
113
    /**
114
     * @var string
115
     */
116
    public $responseHead;
117
    /**
118
     * @var string
119
     */
120
    public $responseBody;
121
    /**
122
     * @var string
123
     */
124
    public $requestHead;
125
    /**
126
     * @var string
127
     */
128
    public $requestBody;
129
    /**
130
     * @var string
131
     */
132
    public $soaperror;
133
    /**
134
     * @var array
135
     */
136
    public $soapinfo = [];
137
    /**
138
     * @var bool
139
     */
140
    public $debugmode = false;
141
142
    /**
143
     * Constructor
144
     * @param Certificate $certificate
145
     * @param LoggerInterface $logger
146
     */
147 4
    public function __construct(
148
        Certificate $certificate = null,
149
        LoggerInterface $logger = null
150
    ) {
151 4
        $this->logger = $logger;
152 4
        $this->certificate = $this->checkCertValidity($certificate);
153 3
        $this->setTemporaryFolder(sys_get_temp_dir() . '/sped/');
154 3
    }
155
    
156
    /**
157
     * Check if certificate is valid
158
     * @param Certificate $certificate
159
     * @return Certificate
160
     * @throws RuntimeException
161
     */
162 4
    private function checkCertValidity(Certificate $certificate = null)
163
    {
164 4
        if ($this->disableCertValidation) {
165 1
            return $certificate;
166
        }
167 4
        if (!empty($certificate)) {
168 1
            if ($certificate->isExpired()) {
169 1
                throw new RuntimeException(
170 1
                    'The validity of the certificate has expired.'
171
                );
172
            }
173
        }
174 3
        return $certificate;
175
    }
176
    
177
    /**
178
     * Destructor
179
     * Clean temporary files
180
     */
181 3
    public function __destruct()
182
    {
183 3
        $this->removeTemporarilyFiles($this->certsdir);
0 ignored issues
show
Unused Code introduced by
The call to SoapBase::removeTemporarilyFiles() has too many arguments starting with $this->certsdir.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
184 3
    }
185
    
186
    /**
187
     * Disables the security checking of host and peer certificates
188
     * @param bool $flag
189
     */
190 1
    public function disableSecurity($flag = false)
191
    {
192 1
        $this->disablesec = $flag;
193 1
        return $this->disablesec;
194
    }
195
    
196
    /**
197
     * ONlY for tests
198
     * @param bool $flag
199
     * @return bool
200
     */
201 1
    public function disableCertValidation($flag = true)
202
    {
203 1
        $this->disableCertValidation = $flag;
204 1
        return $this->disableCertValidation;
205
    }
206
207
    /**
208
     * Load path to CA and enable to use on SOAP
209
     * @param string $capath
210
     */
211
    public function loadCA($capath)
212
    {
213
        if (is_file($capath)) {
214
            $this->casefaz = $capath;
215
        }
216
    }
217
    
218
    /**
219
     * Set another temporayfolder for saving certificates for SOAP utilization
220
     * @param string $folderRealPath
221
     */
222 3
    public function setTemporaryFolder($folderRealPath)
223
    {
224 3
        $this->tempdir = $folderRealPath;
225 3
        $this->setLocalFolder($folderRealPath);
226 3
    }
227
    
228
    /**
229
     * Set Local folder for flysystem
230
     * @param string $folder
231
     */
232 3
    protected function setLocalFolder($folder = '')
233
    {
234 3
        $this->adapter = new Local($folder);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \League\Flysystem\Adapter\Local($folder) of type object<League\Flysystem\Adapter\Local> is incompatible with the declared type object<NFePHP\Common\Soa...lysystem\Adapter\Local> of property $adapter.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
235 3
        $this->filesystem = new Filesystem($this->adapter);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \League\Flysystem\Filesystem($this->adapter) of type object<League\Flysystem\Filesystem> is incompatible with the declared type object<NFePHP\Common\Soa...e\Flysystem\Filesystem> of property $filesystem.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
236 3
    }
237
238
    /**
239
     * Set debug mode, this mode will save soap envelopes in temporary directory
240
     * @param bool $value
241
     * @return bool
242
     */
243
    public function setDebugMode($value = false)
244
    {
245
        return $this->debugmode = $value;
246
    }
247
    
248
    /**
249
     * Set certificate class for SSL comunications
250
     * @param Certificate $certificate
251
     */
252 1
    public function loadCertificate(Certificate $certificate)
253
    {
254 1
        $this->certificate = $this->checkCertValidity($certificate);
255 1
    }
256
    
257
    /**
258
     * Set logger class
259
     * @param LoggerInterface $logger
260
     */
261
    public function loadLogger(LoggerInterface $logger)
262
    {
263
        return $this->logger = $logger;
264
    }
265
    
266
    /**
267
     * Set timeout for communication
268
     * @param int $timesecs
269
     */
270
    public function timeout($timesecs)
271
    {
272
        return $this->soaptimeout = $timesecs;
273
    }
274
    
275
    /**
276
     * Set security protocol
277
     * @param int $protocol
278
     * @return type Description
279
     */
280
    public function protocol($protocol = self::SSL_DEFAULT)
281
    {
282
        return $this->soapprotocol = $protocol;
283
    }
284
    
285
    /**
286
     * Set prefixes
287
     * @param string $prefixes
288
     * @return string
289
     */
290
    public function setSoapPrefix($prefixes)
291
    {
292
        return $this->prefixes = $prefixes;
0 ignored issues
show
Documentation Bug introduced by
It seems like $prefixes of type string is incompatible with the declared type array of property $prefixes.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
293
    }
294
    
295
    /**
296
     * Set proxy parameters
297
     * @param string $ip
298
     * @param int $port
299
     * @param string $user
300
     * @param string $password
301
     */
302
    public function proxy($ip, $port, $user, $password)
303
    {
304
        $this->proxyIP = $ip;
305
        $this->proxyPort = $port;
0 ignored issues
show
Documentation Bug introduced by
The property $proxyPort was declared of type string, but $port 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...
306
        $this->proxyUser = $user;
307
        $this->proxyPass = $password;
308
    }
309
    
310
    /**
311
     * Send message to webservice
312
     */
313
    abstract public function send(
314
        $url,
315
        $operation = '',
316
        $action = '',
317
        $soapver = SOAP_1_2,
318
        $parameters = [],
319
        $namespaces = [],
320
        $request = '',
321
        $soapheader = null
322
    );
323
    
324
    /**
325
     * Mount soap envelope
326
     * @param string $request
327
     * @param string $operation
328
     * @param array $namespaces
329
     * @param \SOAPHeader $header
330
     * @return string
331
     */
332
    protected function makeEnvelopeSoap(
333
        $request,
334
        $operation,
0 ignored issues
show
Unused Code introduced by
The parameter $operation 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...
335
        $namespaces,
336
        $soapver = SOAP_1_2,
337
        $header = null
338
    ) {
339
        $prefix = $this->prefixes[$soapver];
340
        $envelope = "<$prefix:Envelope";
341
        foreach ($namespaces as $key => $value) {
342
            $envelope .= " $key=\"$value\"";
343
        }
344
        $envelope .= ">";
345
        $soapheader = "<$prefix:Header/>";
346
        if (!empty($header)) {
347
            $ns = !empty($header->namespace) ? $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...
348
            $name = $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...
349
            $soapheader = "<$prefix:Header>";
350
            $soapheader .= "<$name xmlns=\"$ns\">";
351
            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...
352
                $soapheader .= "<$key>$value</$key>";
353
            }
354
            $soapheader .= "</$name></$prefix:Header>";
355
        }
356
        $envelope .= $soapheader;
357
        $envelope .= "<$prefix:Body>$request</$prefix:Body>"
358
            . "</$prefix:Envelope>";
359
        return $envelope;
360
    }
361
    
362
    /**
363
     * Temporarily saves the certificate keys for use cURL or SoapClient
364
     */
365
    public function saveTemporarilyKeyFiles()
366
    {
367
        if (!is_object($this->certificate)) {
368
            throw new RuntimeException(
369
                'Certificate not found.'
370
            );
371
        }
372
        $this->certsdir = $this->certificate->getCnpj() . '/certs/';
373
        $this->prifile = $this->certsdir. Strings::randomString(10).'.pem';
374
        $this->pubfile = $this->certsdir . Strings::randomString(10).'.pem';
375
        $this->certfile = $this->certsdir . Strings::randomString(10).'.pem';
376
        $ret = true;
377
        //cria uma senha temporária ALEATÓRIA para salvar a chave primaria
378
        //portanto mesmo que localizada e identificada não estará acessível
379
        //pois sua senha não existe além do tempo de execução desta classe
380
        $this->temppass = Strings::randomString(16);
381
        openssl_pkey_export(
382
            $this->certificate->privateKey,
383
            $private,
384
            $this->temppass
385
        );
386
        $ret &= $this->filesystem->put(
387
            $this->prifile,
388
            $private
389
        );
390
        $ret &= $this->filesystem->put(
391
            $this->pubfile,
392
            $this->certificate->publicKey
393
        );
394
        $ret &= $this->filesystem->put(
395
            $this->certfile,
396
            "{$this->certificate}"
397
        );
398
        if (!$ret) {
399
            throw new RuntimeException(
400
                'Unable to save temporary key files in folder.'
401
            );
402
        }
403
    }
404
    
405
    /**
406
     * Delete all files in folder
407
     */
408 3
    public function removeTemporarilyFiles()
409
    {
410 3
        $contents = $this->filesystem->listContents($this->certsdir, true);
411
        //define um limite de 5 min, ou seja qualquer arquivo criado a mais
412
        //de 5 min será removido
413
        //NOTA: qunando ocorre algum erro interno na execução do script, alguns
414
        //arquivos temporários podem permanecer
415 3
        $dt = new \DateTime();
416 3
        $tint = new \DateInterval("PT5M");
417 3
        $tint->invert = true;
0 ignored issues
show
Documentation Bug introduced by
The property $invert was declared of type integer, but true is of type boolean. 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...
418 3
        $tsLimit = $dt->add($tint)->getTimestamp();
419 3
        foreach ($contents as $item) {
420
            if ($item['type'] == 'file') {
421
                if ($item['path'] == $this->prifile
422
                    || $item['path'] == $this->pubfile
423
                    || $item['path'] == $this->certfile
424
                ) {
425
                    $this->filesystem->delete($item['path']);
426
                    continue;
427
                }
428
                $timestamp = $this->filesystem->getTimestamp($item['path']);
429
                if ($timestamp < $tsLimit) {
430
                    $this->filesystem->delete($item['path']);
431
                }
432
            }
433
        }
434 3
    }
435
    
436
    /**
437
     * Save request envelope and response for debug reasons
438
     * @param string $operation
439
     * @param string $request
440
     * @param string $response
441
     * @return void
442
     */
443
    public function saveDebugFiles($operation, $request, $response)
444
    {
445
        if (!$this->debugmode) {
446
            return;
447
        }
448
        $this->debugdir = $this->certificate->getCnpj() . '/debug/';
449
        $now = \DateTime::createFromFormat('U.u', microtime(true));
450
        $time = substr($now->format("ymdHisu"), 0, 16);
451
        try {
452
            $this->filesystem->put(
453
                $this->debugdir . $time . "_" . $operation . "_sol.txt",
454
                $request
455
            );
456
            $this->filesystem->put(
457
                $this->debugdir . $time . "_" . $operation . "_res.txt",
458
                $response
459
            );
460
        } catch (Exception $e) {
0 ignored issues
show
Bug introduced by
The class NFePHP\Common\Soap\Exception does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
461
            throw new RuntimeException(
462
                'Unable to create debug files.'
463
            );
464
        }
465
    }
466
}
467