1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace NFePHP\Mail; |
4
|
|
|
|
5
|
|
|
/** |
6
|
|
|
* Class for sending emails related to SPED services |
7
|
|
|
* |
8
|
|
|
* @category NFePHP |
9
|
|
|
* @package NFePHP\Mail\Mail |
10
|
|
|
* @copyright NFePHP Copyright (c) 2016 |
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-mail for the canonical source repository |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
use stdClass; |
19
|
|
|
use DOMDocument; |
20
|
|
|
use InvalidArgumentException; |
21
|
|
|
use RuntimeException; |
22
|
|
|
use NFePHP\Mail\Base; |
23
|
|
|
use PHPMailer; |
24
|
|
|
use Html2Text\Html2Text; |
25
|
|
|
|
26
|
|
|
class Mail extends Base |
27
|
|
|
{ |
28
|
|
|
/** |
29
|
|
|
* config |
30
|
|
|
* @var stdClass |
31
|
|
|
*/ |
32
|
|
|
protected $config; |
33
|
|
|
/** |
34
|
|
|
* template user-defined |
35
|
|
|
* @var string |
36
|
|
|
*/ |
37
|
|
|
protected $template; |
38
|
|
|
/** |
39
|
|
|
* Type from xml document NFe, CTe or CCe |
40
|
|
|
* @var string |
41
|
|
|
*/ |
42
|
|
|
protected $type; |
43
|
|
|
/** |
44
|
|
|
* Addresses to send mail |
45
|
|
|
* This array should be repeated fields removed |
46
|
|
|
* @var array |
47
|
|
|
*/ |
48
|
|
|
protected $addresses; |
49
|
|
|
/** |
50
|
|
|
* Fields from xml |
51
|
|
|
* @var stdClass |
52
|
|
|
*/ |
53
|
|
|
protected $fields; |
54
|
|
|
/** |
55
|
|
|
* Html Body mail message |
56
|
|
|
* @var string |
57
|
|
|
*/ |
58
|
|
|
protected $body; |
59
|
|
|
/** |
60
|
|
|
* Subject for email |
61
|
|
|
* @var string |
62
|
|
|
*/ |
63
|
|
|
protected $subject; |
64
|
|
|
/** |
65
|
|
|
* PHPMailer class |
66
|
|
|
* @var \PHPMailer |
67
|
|
|
*/ |
68
|
|
|
protected $mail; |
69
|
|
|
/** |
70
|
|
|
* Xml content |
71
|
|
|
* @var string |
72
|
|
|
*/ |
73
|
|
|
protected $xml; |
74
|
|
|
/** |
75
|
|
|
* PDF content |
76
|
|
|
* @var string |
77
|
|
|
*/ |
78
|
|
|
protected $pdf; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Constructor |
82
|
|
|
* @param \stdClass $config |
83
|
|
|
*/ |
84
|
|
|
public function __construct(stdClass $config, PHPMailer $mailer = null) |
85
|
|
|
{ |
86
|
|
|
$this->mail = $mailer; |
87
|
|
|
if (is_null($mailer)) { |
88
|
|
|
$this->mail = new \PHPMailer(); |
89
|
|
|
} |
90
|
|
|
$this->config = $config; |
91
|
|
|
$this->loadService($config); |
92
|
|
|
$this->fields = new stdClass(); |
93
|
|
|
$this->fields->destinatario = ''; |
94
|
|
|
$this->fields->data = ''; |
95
|
|
|
$this->fields->numero = ''; |
96
|
|
|
$this->fields->valor = 0; |
97
|
|
|
$this->fields->chave = ''; |
98
|
|
|
$this->fields->data = ''; |
99
|
|
|
$this->fields->correcao = ''; |
100
|
|
|
$this->fields->conduso = ''; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* Load parameters to PHPMailer class |
105
|
|
|
* @param stdClass $config |
106
|
|
|
*/ |
107
|
|
|
private function loadService(stdClass $config) |
108
|
|
|
{ |
109
|
|
|
$this->mail->CharSet = 'UTF-8'; |
110
|
|
|
$this->mail->isSMTP(); |
111
|
|
|
$this->mail->Host = $config->host; |
112
|
|
|
$this->mail->SMTPAuth = true; |
113
|
|
|
$this->mail->Username = $config->user; |
114
|
|
|
$this->mail->Password = $config->password; |
115
|
|
|
$this->mail->SMTPSecure = $config->secure; |
116
|
|
|
$this->mail->Port = $config->port; |
117
|
|
|
$this->mail->setFrom($config->from, $config->fantasy); |
118
|
|
|
$this->mail->addReplyTo($config->replyTo, $config->replyName); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Sets a template for body mail |
123
|
|
|
* If no template is passed, it will be used a standard template |
124
|
|
|
* see Base::class |
125
|
|
|
* @param string $htmlTemplate |
126
|
|
|
*/ |
127
|
|
|
public function setTemplate($htmlTemplate) |
128
|
|
|
{ |
129
|
|
|
if ($htmlTemplate != '') { |
130
|
|
|
$this->template = $htmlTemplate; |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Load the documents to send |
136
|
|
|
* XML document is required, but PDF is not |
137
|
|
|
* @param string $xml content or path NFe, CTe or CCe in XML |
138
|
|
|
* @param string $pdf content or path document from NFe, CTe or CCe |
139
|
|
|
*/ |
140
|
|
|
public function loadDocuments($xml, $pdf = '') |
141
|
|
|
{ |
142
|
|
|
$this->xml = $xml; |
143
|
|
|
$this->pdf = $pdf; |
144
|
|
|
if (is_file($xml)) { |
145
|
|
|
$this->xml = file_get_contents($xml); |
146
|
|
|
} |
147
|
|
|
if (is_file($pdf)) { |
148
|
|
|
$this->pdf = file_get_contents($pdf); |
149
|
|
|
} |
150
|
|
|
//get xml data |
151
|
|
|
$this->getXmlData($this->xml); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Search xml for data |
156
|
|
|
* @param string $xml |
157
|
|
|
* @throws InvalidArgumentException |
158
|
|
|
*/ |
159
|
|
|
private function getXmlData($xml) |
160
|
|
|
{ |
161
|
|
|
$dom = new DOMDocument('1.0', 'UTF-8'); |
162
|
|
|
$dom->preserveWhiteSpace = false; |
163
|
|
|
$dom->loadXML($xml); |
164
|
|
|
$root = $dom->documentElement; |
165
|
|
|
$name = $root->tagName; |
166
|
|
|
switch ($name) { |
167
|
|
|
case 'nfeProc': |
168
|
|
|
case 'NFe': |
169
|
|
|
$type = 'NFe'; |
170
|
|
|
$this->fields->destinatario = $dom->getElementsByTagName('dest')->item(0) |
171
|
|
|
->getElementsByTagName('xNome')->item(0)->nodeValue; |
172
|
|
|
$this->fields->data = $dom->getElementsByTagName('ide')->item(0) |
173
|
|
|
->getElementsByTagName('dhEmi')->item(0)->nodeValue; |
174
|
|
|
$this->fields->numero = $dom->getElementsByTagName('ide')->item(0) |
175
|
|
|
->getElementsByTagName('nNF')->item(0)->nodeValue; |
176
|
|
|
$this->fields->valor = $dom->getElementsByTagName('vNF')->item(0)->nodeValue; |
177
|
|
|
$this->subject = "NFe n. ".$this->fields->numero." - ".$this->config->fantasy; |
178
|
|
|
break; |
179
|
|
|
case 'cteProc': |
180
|
|
|
case 'CTe': |
181
|
|
|
$type = 'CTe'; |
182
|
|
|
$this->fields->destinatario = $dom->getElementsByTagName('dest')->item(0) |
183
|
|
|
->getElementsByTagName('xNome')->item(0)->nodeValue; |
184
|
|
|
$this->fields->data = $dom->getElementsByTagName('ide')->item(0) |
185
|
|
|
->getElementsByTagName('dhEmi')->item(0)->nodeValue; |
186
|
|
|
$this->fields->numero = $dom->getElementsByTagName('ide')->item(0) |
187
|
|
|
->getElementsByTagName('nCT')->item(0)->nodeValue; |
188
|
|
|
$this->fields->valor = $dom->getElementsByTagName('vRec')->item(0)->nodeValue; |
189
|
|
|
$this->subject = "CTe n. ".$this->fields->numero." - ".$this->config->fantasy; |
190
|
|
|
break; |
191
|
|
|
case 'procEventoNFe': |
192
|
|
|
case 'procEventoCTe': |
193
|
|
|
$type = 'CCe'; |
194
|
|
|
$this->fields->chave = $dom->getElementsByTagName('chNFe')->item(0)->nodeValue; |
195
|
|
|
$this->fields->data = $dom->getElementsByTagName('dhEvento')->item(0)->nodeValue; |
196
|
|
|
$this->fields->correcao = $dom->getElementsByTagName('xCorrecao')->item(0)->nodeValue; |
197
|
|
|
$this->fields->conduso = $dom->getElementsByTagName('xCondUso')->item(0)->nodeValue; |
198
|
|
|
if (empty($this->fields->chave)) { |
199
|
|
|
$this->fields->chave = $dom->getElementsByTagName('chCTe')->item(0)->nodeValue; |
200
|
|
|
} |
201
|
|
|
$this->subject = "Carta de Correção ". $this->config->fantasy; |
202
|
|
|
break; |
203
|
|
|
default: |
204
|
|
|
$type = ''; |
205
|
|
|
} |
206
|
|
|
//get email adresses from xml, if exists |
207
|
|
|
//may have one address in <dest><email> |
208
|
|
|
$email = !empty($dom->getElementsByTagName('email')->item(0)->nodeValue) ? |
209
|
|
|
$dom->getElementsByTagName('email')->item(0)->nodeValue : ''; |
210
|
|
|
if (! empty($mail)) { |
|
|
|
|
211
|
|
|
$this->addresses[] = $email; |
212
|
|
|
} |
213
|
|
|
//may have others in <obsCont xCampo="email"><xTexto>[email protected]</xTexto> |
214
|
|
|
$obs = $dom->getElementsByTagName('obsCont'); |
215
|
|
|
foreach ($obs as $ob) { |
216
|
|
|
if (strtoupper($ob->getAttribute('xCampo')) === 'EMAIL') { |
217
|
|
|
$this->addresses[] = $ob->getElementsByTagName('xTexto')->item(0)->nodeValue; |
218
|
|
|
} |
219
|
|
|
} |
220
|
|
|
//xml may be a NFe or a CTe or a CCe nothing else |
221
|
|
|
if ($type != 'NFe' && $type != 'CTe' && $type != 'CCe') { |
222
|
|
|
$msg = "Você deve passar apenas uma NFe ou um CTe ou um CCe. " |
223
|
|
|
. "Esse documento não foi reconhecido."; |
224
|
|
|
throw new InvalidArgumentException($msg); |
225
|
|
|
} |
226
|
|
|
$this->type = $type; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Render a template with valid data |
231
|
|
|
* @param string $template |
232
|
|
|
* @param string $destinatario |
233
|
|
|
* @param string $data |
234
|
|
|
* @param string $numero |
235
|
|
|
* @param string $valor |
236
|
|
|
* @param string $chave |
237
|
|
|
* @param string $correcao |
238
|
|
|
* @param string $conduso |
239
|
|
|
* @return string |
240
|
|
|
*/ |
241
|
|
|
private function renderTemplate( |
242
|
|
|
$template, |
243
|
|
|
$destinatario = '', |
244
|
|
|
$data = '', |
245
|
|
|
$numero = '', |
246
|
|
|
$valor = 0, |
247
|
|
|
$chave = '', |
248
|
|
|
$correcao = '', |
249
|
|
|
$conduso = '' |
250
|
|
|
) { |
251
|
|
|
$dt = new \DateTime(str_replace('T', ' ', $data)); |
252
|
|
|
$search = array( |
253
|
|
|
'{destinatario}', |
254
|
|
|
'{data}', |
255
|
|
|
'{numero}', |
256
|
|
|
'{valor}', |
257
|
|
|
'{emitente}', |
258
|
|
|
'{chave}', |
259
|
|
|
'{correcao}', |
260
|
|
|
'{conduso}' |
261
|
|
|
); |
262
|
|
|
$replace = array( |
263
|
|
|
$destinatario, |
264
|
|
|
$dt->format('d/m/Y'), |
265
|
|
|
$numero, |
266
|
|
|
number_format($valor, 2, ',', '.'), |
267
|
|
|
$this->config->fantasy, |
268
|
|
|
$chave, |
269
|
|
|
$correcao, |
270
|
|
|
$conduso |
271
|
|
|
); |
272
|
|
|
$template = str_replace($search, $replace, $template); |
273
|
|
|
return $template; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Set all addresses including those that exists in the xml document |
278
|
|
|
* Send email only to listed addresses ignoring all email in xml |
279
|
|
|
* @param array $addresses |
280
|
|
|
*/ |
281
|
|
|
private function setAddresses(array $addresses = []) |
282
|
|
|
{ |
283
|
|
|
if (!empty($addresses)) { |
284
|
|
|
$this->addresses = $addresses; |
285
|
|
|
} |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Send mail |
290
|
|
|
* If no parameter was passed, only the email address contained in |
291
|
|
|
* the xml will be used |
292
|
|
|
* @param array $addresses |
293
|
|
|
* @return boolean |
294
|
|
|
* @throws RuntimeException |
295
|
|
|
*/ |
296
|
|
|
public function send(array $addresses = []) |
297
|
|
|
{ |
298
|
|
|
$this->setAddresses($addresses); |
299
|
|
|
//This resulted array should be repeated fields removed |
300
|
|
|
$this->addresses = array_unique($this->addresses); |
301
|
|
|
foreach ($this->addresses as $address) { |
302
|
|
|
$this->mail->addAddress($address); |
303
|
|
|
} |
304
|
|
|
$body = $this->render(); |
305
|
|
|
$this->mail->isHTML(true); |
306
|
|
|
$this->mail->Subject = $this->subject; |
307
|
|
|
$this->mail->Body = $body; |
308
|
|
|
$this->mail->AltBody = Html2Text::convert($body); |
309
|
|
|
$this->attach(); |
310
|
|
|
if (!$this->mail->send()) { |
311
|
|
|
$msg = 'A menssagem não pode ser enviada. Error: ' . $this->mail->ErrorInfo; |
312
|
|
|
throw new RuntimeException($msg); |
313
|
|
|
} |
314
|
|
|
$this->mail->ClearAllRecipients(); |
315
|
|
|
$this->mail->ClearAttachments(); |
316
|
|
|
return true; |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
/** |
320
|
|
|
* Build Message |
321
|
|
|
* @return string |
322
|
|
|
*/ |
323
|
|
|
private function render() |
324
|
|
|
{ |
325
|
|
|
//depending on the document a different template should be loaded |
326
|
|
|
//and having data patterns appropriately substituted |
327
|
|
|
$template = $this->templates[$this->type]; |
328
|
|
|
if (! empty($this->template)) { |
329
|
|
|
$template = $this->template; |
330
|
|
|
} |
331
|
|
|
return $this->renderTemplate( |
332
|
|
|
$template, |
333
|
|
|
$this->fields->destinatario, |
334
|
|
|
$this->fields->data, |
335
|
|
|
$this->fields->numero, |
336
|
|
|
$this->fields->valor, |
337
|
|
|
$this->fields->chave, |
338
|
|
|
$this->fields->correcao, |
339
|
|
|
$this->fields->conduso |
340
|
|
|
); |
341
|
|
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Attach all documents to message |
345
|
|
|
*/ |
346
|
|
|
private function attach() |
347
|
|
|
{ |
348
|
|
|
$this->mail->addStringAttachment( |
349
|
|
|
$this->xml, |
350
|
|
|
$this->type . '.xml' |
351
|
|
|
); |
352
|
|
|
if (! empty($this->pdf)) { |
353
|
|
|
$this->mail->addStringAttachment( |
354
|
|
|
$this->xml, |
355
|
|
|
$this->type . '.pdf' |
356
|
|
|
); |
357
|
|
|
} |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* Configure and send documents |
362
|
|
|
* @param stdClass $config |
363
|
|
|
* @param type $xml |
364
|
|
|
* @param type $pdf |
365
|
|
|
* @param array $addresses |
366
|
|
|
* @param type $htmltemplate |
367
|
|
|
* @param PHPMailer $mailer |
368
|
|
|
* @return \static |
369
|
|
|
*/ |
370
|
|
|
public static function sendMail( |
371
|
|
|
stdClass $config, |
372
|
|
|
$xml, |
373
|
|
|
$pdf = '', |
374
|
|
|
array $addresses = [], |
375
|
|
|
$htmltemplate = '', |
376
|
|
|
PHPMailer $mailer = null |
377
|
|
|
) { |
378
|
|
|
$mail = new static($config, $mailer); |
379
|
|
|
$mail->loadDocuments($xml, $pdf); |
380
|
|
|
$mail->setTemplate($htmltemplate); |
381
|
|
|
$mail->send($addresses); |
382
|
|
|
return $mail; |
383
|
|
|
} |
384
|
|
|
} |
385
|
|
|
|
This check looks for calls to
isset(...)
orempty()
on variables that are yet undefined. These calls will always produce the same result and can be removed.This is most likely caused by the renaming of a variable or the removal of a function/method parameter.