Completed
Push — master ( 3d6456...22dadb )
by Roberto
06:51 queued 03:34
created

Mail::send()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 24
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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