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) { |
|
|
|
|
414
|
|
|
$headerItems .= '<' . $key . '>' . $value . '</' . $key . '>'; |
415
|
|
|
} |
416
|
|
|
return sprintf( |
417
|
|
|
'<%s:Header><%s xmlns="%s">%s</%s></%s:Header>', |
418
|
|
|
$envelopPrefix, |
419
|
|
|
$header->name, |
|
|
|
|
420
|
|
|
$header->namespace === null ? '' : $header->namespace, |
|
|
|
|
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
|
|
|
|
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.