Completed
Push — master ( f5e736...3d6456 )
by Roberto
06:33
created

Mail::send()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 0
cts 21
cp 0
rs 9.2
c 0
b 0
f 0
cc 3
eloc 17
nc 4
nop 1
crap 12
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)) {
0 ignored issues
show
Bug introduced by
The variable $mail seems to never exist, and therefore empty should always return true. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() 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.

Loading history...
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