Completed
Push — master ( e68275...36cb31 )
by Sebastian
02:48
created

src/Log/Mail.php (13 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
namespace phpbu\App\Log;
3
4
use phpbu\App\Cli\Statistics;
5
use phpbu\App\Exception;
6
use phpbu\App\Event;
7
use phpbu\App\Listener;
8
use phpbu\App\Result;
9
use phpbu\App\Log\MailTemplate as TPL;
10
use phpbu\App\Util\Arr;
11
use phpbu\App\Util\Str;
12
use PHPMailer\PHPMailer\PHPMailer;
13
14
/**
15
 * Mail Logger
16
 *
17
 * @package    phpbu
18
 * @subpackage Log
19
 * @author     Sebastian Feldmann <[email protected]>
20
 * @copyright  Sebastian Feldmann <[email protected]>
21
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
22
 * @link       http://phpbu.de/
23
 * @since      Class available since Release 1.0.0
24
 */
25
class Mail implements Listener, Logger
26
{
27
    /**
28
     * Mailer instance
29
     *
30
     * @var \PHPMailer\PHPMailer\PHPMailer;
31
     */
32
    protected $mailer;
33
34
    /**
35
     * Mail subject
36
     *
37
     * @var string
38
     */
39
    protected $subject;
40
41
    /**
42
     * From email address
43
     *
44
     * @var string
45
     */
46
    protected $senderMail;
47
48
    /**
49
     * From name
50
     *
51
     * @var string
52
     */
53
    protected $senderName;
54
55
    /**
56
     * Transport type [mail|smtp|null]
57
     *
58
     * @var string
59
     */
60
    protected $transportType;
61
62
    /**
63
     * List of mail recipients
64
     *
65
     * @var array<string>
66
     */
67
    protected $recipients = [];
68
69
    /**
70
     * Amount of executed backups
71
     *
72
     * @var integer
73
     */
74
    private $numBackups = 0;
75
76
    /**
77
     * Amount of executed checks
78
     *
79
     * @var integer
80
     */
81
    private $numChecks = 0;
82
83
    /**
84
     * Amount of executed Syncs
85
     *
86
     * @var integer
87
     */
88
    private $numSyncs = 0;
89
90
    /**
91
     * Amount of executed Crypts
92
     *
93
     * @var integer
94
     */
95
    private $numCrypts = 0;
96
97
    /**
98
     * Amount of executed Cleanups
99
     *
100
     * @var integer
101
     */
102
    private $numCleanups = 0;
103
104
    /**
105
     * Send mail only if there was an error
106
     *
107
     * @var bool
108
     */
109
    private $sendOnlyOnError = false;
110
111
    /**
112
     * Send mails on simulation runs.
113
     *
114
     * @var bool
115
     */
116
    private $sendSimulating = true;
117
118
    /**
119
     * Is current execution a simulation.
120
     *
121
     * @var bool
122
     */
123
    private $isSimulation = false;
124
125
    /**
126
     * Returns an array of event names this subscriber wants to listen to.
127
     *
128
     * The array keys are event names and the value can be:
129
     *
130
     *  * The method name to call (priority defaults to 0)
131
     *  * An array composed of the method name to call and the priority
132
     *  * An array of arrays composed of the method names to call and respective
133
     *    priorities, or 0 if unset
134
     *
135
     * @return array The event names to listen to
136
     */
137 1
    public static function getSubscribedEvents()
138
    {
139
        return [
140 1
            'phpbu.backup_start'  => 'onBackupStart',
141
            'phpbu.check_start'   => 'onCheckStart',
142
            'phpbu.crypt_start'   => 'onCryptStart',
143
            'phpbu.sync_start'    => 'onSyncStart',
144
            'phpbu.cleanup_start' => 'onCleanupStart',
145
            'phpbu.app_end'       => 'onPhpbuEnd',
146
        ];
147
    }
148
149
    /**
150
     * Setup the Logger.
151
     *
152
     * @see    \phpbu\App\Log\Logger::setup
153
     * @param  array $options
154
     * @throws \phpbu\App\Exception
155
     */
156 12
    public function setup(array $options)
157
    {
158 12
        if (empty($options['recipients'])) {
159 1
            throw new Exception('no recipients given');
160
        }
161 11
        $mails                 = $options['recipients'];
162 11
        $server                = gethostname();
163 11
        $this->sendOnlyOnError = Str::toBoolean(Arr::getValue($options, 'sendOnlyOnError'), false);
164 11
        $this->sendSimulating  = Str::toBoolean(Arr::getValue($options, 'sendOnSimulation'), true);
165 11
        $this->subject         = Arr::getValue($options, 'subject', 'PHPBU backup report from ' . $server);
166 11
        $this->senderMail      = Arr::getValue($options, 'sender.mail', 'phpbu@' . $server);
167 11
        $this->senderName      = Arr::getValue($options, 'sender.name');
168 11
        $this->transportType   = Arr::getValue($options, 'transport', 'mail');
169 11
        $this->recipients      = array_map('trim', explode(';', $mails));
170 11
        $this->isSimulation    = Arr::getValue($options, '__simulate__', false);
171
172
        // create transport an mailer
173 11
        $this->mailer = new PHPMailer();
174 11
        $this->setupMailer($this->transportType, $options);
175 9
    }
176
177
    /**
178
     * Handle the phpbu end event.
179
     *
180
     * @param  \phpbu\App\Event\App\End $event
181
     * @throws \phpbu\App\Exception
182
     */
183 4
    public function onPhpbuEnd(Event\App\End $event)
184
    {
185 4
        $result = $event->getResult();
186
187 4
        if ($this->shouldMailBeSend($result)) {
188 4
            $header  = $this->getHeaderHtml();
189 4
            $status  = $this->getStatusHtml($result);
190 4
            $errors  = $this->getErrorHtml($result);
191 4
            $info    = $this->getInfoHtml($result);
192 4
            $footer  = $this->getFooterHtml();
193 4
            $body    = '<html><body ' . TPL::getSnippet('sBody') . '>'
194 4
                     . $header
195 4
                     . $status
196 4
                     . $errors
197 4
                     . $info
198 4
                     . $footer
199 4
                     . '</body></html>';
200 4
            $state   = $result->allOk() ? 'OK' : ($result->backupOkButSkipsOrFails() ? 'WARNING' : 'ERROR');
201
202 4
            $this->mailer->Subject = $this->subject . ' [' . $state . ']';
203 4
            $this->mailer->setFrom($this->senderMail, $this->senderName);
204 4
            $this->mailer->msgHTML($body);
205
206 4
            foreach ($this->recipients as $recipient) {
207 4
                $this->mailer->addAddress($recipient);
208
            }
209
210 4
            if ($this->transportType !== 'null') {
211
                $this->sendMail();
212
            }
213
        }
214 4
    }
215
216
    /**
217
     * Backup start event.
218
     *
219
     * @param \phpbu\App\Event\Backup\Start $event
220
     */
221 4
    public function onBackupStart(Event\Backup\Start $event)
222
    {
223 4
        $this->numBackups++;
224 4
    }
225
226
    /**
227
     * Check start event.
228
     *
229
     * @param \phpbu\App\Event\Check\Start $event
230
     */
231 1
    public function onCheckStart(Event\Check\Start $event)
232
    {
233 1
        $this->numChecks++;
234 1
    }
235
236
    /**
237
     * Crypt start event.
238
     *
239
     * @param \phpbu\App\Event\Crypt\Start $event
240
     */
241 1
    public function onCryptStart(Event\Crypt\Start $event)
242
    {
243 1
        $this->numCrypts++;
244 1
    }
245
246
    /**
247
     * Sync start event.
248
     *
249
     * @param \phpbu\App\Event\Sync\Start $event
250
     */
251 1
    public function onSyncStart(Event\Sync\Start $event)
252
    {
253 1
        $this->numSyncs++;
254 1
    }
255
256
    /**
257
     * Cleanup start event.
258
     *
259
     * @param \phpbu\App\Event\Cleanup\Start $event
260
     */
261 1
    public function onCleanupStart(Event\Cleanup\Start $event)
262
    {
263 1
        $this->numCleanups++;
264 1
    }
265
266
    /**
267
     * Configure PHPMailer
268
     *
269
     * @param  string                         $type
270
     * @param  array                          $options
271
     * @throws \phpbu\App\Exception
272
     */
273 11
    protected function setupMailer($type, array $options)
274
    {
275 11
        switch ($type) {
276 11
            case 'null':
277 4
                $this->isSimulation = true;
278 4
                break;
279
280 7
            case 'smtp':
281 2
                $this->setupSmtpMailer($options);
282 1
                break;
283
284 5
            case 'mail':
285 3
            case 'sendmail':
286 4
                $this->setupSendmailMailer($options);
287 4
                break;
288
289
            // UPS! no transport given
290
            default:
291 1
                throw new Exception(sprintf('mail transport not supported: \'%s\'', $type));
292
        }
293 9
    }
294
295
    /**
296
     * Should a mail be send
297
     *
298
     * @param  \phpbu\App\Result $result
299
     * @return bool
300
     */
301 4
    protected function shouldMailBeSend(Result $result) : bool
302
    {
303
        // send mails if
304
        // there is an error or send error only is inactive
305
        // and
306
        // simulation settings do not prevent sending
307 4
        return (!$this->sendOnlyOnError || !$result->allOk()) && ($this->sendSimulating || !$this->isSimulation);
308
    }
309
310
    /**
311
     * Setup smtp mailing
312
     *
313
     * @param  array $options
314
     * @return void
315
     * @throws \phpbu\App\Exception
316
     */
317 2
    protected function setupSmtpMailer(array $options)
318
    {
319 2
        if (!isset($options['smtp.host'])) {
320 1
            throw new Exception('option \'smtp.host\' ist missing');
321
        }
322 1
        $host       = $options['smtp.host'];
323 1
        $port       = Arr::getValue($options, 'smtp.port', 25);
324 1
        $username   = Arr::getValue($options, 'smtp.username');
325 1
        $password   = Arr::getValue($options, 'smtp.password');
326 1
        $encryption = Arr::getValue($options, 'smtp.encryption');
327
328 1
        $this->mailer->isSMTP();
329 1
        $this->mailer->Host     = $host;
330 1
        $this->mailer->Port     = $port;
331
332 1
        if ($username && $password) {
333 1
            $this->mailer->SMTPAuth = true;
334 1
            $this->mailer->Username = $username;
335 1
            $this->mailer->Password = $password;
336
        }
337 1
        if ($encryption) {
338 1
            $this->mailer->SMTPSecure = $encryption;
339
        }
340 1
    }
341
342
    /**
343
     * Setup the php mail transport
344
     *
345
     * @param  array $options
346
     * @return void
347
     */
348 4
    protected function setupSendmailMailer(array $options)
349
    {
350
        // nothing to do here
351 4
    }
352
353
    /**
354
     * Send the email
355
     *
356
     * @throws \phpbu\App\Exception
357
     */
358
    protected function sendMail()
359
    {
360
        try {
361
            if (!$this->mailer->send()) {
362
                throw new Exception($this->mailer->ErrorInfo);
363
            }
364
        } catch (\Exception $e)  {
365
            throw new Exception($e->getMessage());
366
        }
367
    }
368
369
    /**
370
     * Return mail header html
371
     *
372
     * @return string
373
     * @throws \phpbu\App\Exception
374
     */
375 4
    protected function getHeaderHtml()
376
    {
377 4
        return '<table ' . TPL::getSnippet('sTableContent') . '><tr><td ' . TPL::getSnippet('sTableContentCol') . '>' .
378 4
               '<table ' . TPL::getSnippet('sTableHeader') . '><tr><td>PHPBU - backup report</td></tr></table>';
379
    }
380
381
    /**
382
     * Return mail status html
383
     *
384
     * @param  \phpbu\App\Result $result
385
     * @return string
386
     * @throws \phpbu\App\Exception
387
     */
388 4
    protected function getStatusHtml(Result $result)
389
    {
390 4
        if (count($result->getBackups()) === 0) {
391 1
            $color  = TPL::getSnippet('cStatusWARN');
392 1
            $status = 'WARNING';
393 3
        } elseif ($result->allOk()) {
394 1
            $color  = TPL::getSnippet('cStatusOK');
395 1
            $status = 'OK';
396 2
        } elseif ($result->backupOkButSkipsOrFails()) {
397 1
            $color  = TPL::getSnippet('cStatusWARN');
398 1
            $status = 'WARNING';
399
        } else {
400 1
            $color  = TPL::getSnippet('cStatusFAIL');
401 1
            $status = 'FAILURE';
402
        }
403 4
        $info = sprintf(
404 4
            '(%d %s, %d %s, %d %s, %d %s, %d %s)',
405 4
            count($result->getBackups()),
406 4
            Str::appendPluralS('backup', count($result->getBackups())),
407 4
            $this->numChecks,
408 4
            Str::appendPluralS('check', $this->numChecks),
409 4
            $this->numCrypts,
410 4
            Str::appendPluralS('crypt', $this->numCrypts),
411 4
            $this->numSyncs,
412 4
            Str::appendPluralS('sync', $this->numSyncs),
413 4
            $this->numCleanups,
414 4
            Str::appendPluralS('cleanup', $this->numCleanups)
415
        );
416 4
        $html = '<table ' . sprintf(TPL::getSnippet('sTableStatus'), $color) . '>' .
417 4
                 '<tr><td>' .
418 4
                  '<span ' . TPL::getSnippet('sTableStatusText') . '>' . date('Y-m-d H:i') . '</span>' .
419 4
                  '<h1 ' . TPL::getSnippet('sTableStatusHead') . '>' . $status . '</h1>' .
420 4
                  '<span ' . TPL::getSnippet('sTableStatusText') . '>' . $info . '</span>' .
421 4
                 '</td></tr>' .
422 4
                '</table>';
423
424 4
        return $html;
425
    }
426
427
    /**
428
     * Get error information
429
     *
430
     * @param  \phpbu\App\Result $result
431
     * @return string
432
     * @throws \phpbu\App\Exception
433
     */
434 4
    protected function getErrorHtml(Result $result)
435
    {
436 4
        $html   = '';
437 4
        $errors = $result->getErrors();
438 4
        if (count($errors)) {
439 1
            $html .= '<table ' . TPL::getSnippet('sTableError') . '>';
440
            /* @var $e Exception */
441 1
            foreach ($errors as $e) {
442 1
                $html .= '<tr><td ' . TPL::getSnippet('sTableErrorCol') . '>' .
443 1
                    sprintf(
444 1
                        "Exception '%s' with message '%s' in %s:%d",
445 1
                        get_class($e),
446 1
                        $e->getMessage(),
447 1
                        $e->getFile(),
448 1
                        $e->getLine()
449
                    ) .
450 1
                    '</td></tr>';
451
452
            }
453 1
            $html .= '</table>';
454
        }
455 4
        return $html;
456
    }
457
458
    /**
459
     * Return backup html information
460
     *
461
     * @param  \phpbu\App\Result $result
462
     * @return string
463
     * @throws \phpbu\App\Exception
464
     */
465 4
    protected function getInfoHtml(Result $result)
466
    {
467 4
        $html    = '';
468 4
        $backups = $result->getBackups();
469 4
        if (count($backups)) {
470 3
            $html .= '<table ' . TPL::getSnippet('sTableBackup') . '>';
471
            /** @var \phpbu\App\Result\Backup $backup */
472 3
            foreach ($backups as $backup) {
473 3
                if ($backup->allOk()) {
0 ignored issues
show
The method allOk() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
474 1
                    $color  = TPL::getSnippet('cStatusOK');
475 1
                    $status = 'OK';
476 2
                } elseif ($backup->okButSkipsOrFails()) {
0 ignored issues
show
The method okButSkipsOrFails() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
477 1
                    $color  = TPL::getSnippet('cStatusWARN');
478 1
                    $status = 'WARNING';
479
                } else {
480 1
                    $color  = TPL::getSnippet('cStatusFAIL');
481 1
                    $status = 'FAILURE';
482
                }
483
                $html .= '<tr>' .
484 3
                          '<td ' . sprintf(TPL::getSnippet('sTableBackupStatusColumn'), $color) . ' colspan="4">' .
485 3
                          sprintf('backup <em>%s</em>', $backup->getName()) .
486 3
                          ' <span ' . TPL::getSnippet('sTableBackupStatusText') . '>' . $status . '</span>' .
487 3
                          '</td>' .
488 3
                         '</tr>' .
489 3
                         '<tr>' .
490 3
                          '<td ' . TPL::getSnippet('sRowHead') . '>&nbsp;</td>' .
491 3
                          '<td ' . TPL::getSnippet('sRowHead') . ' align="right">executed</td>' .
492 3
                          '<td ' . TPL::getSnippet('sRowHead') . ' align="right">skipped</td>' .
493 3
                          '<td ' . TPL::getSnippet('sRowHead') . ' align="right">failed</td>' .
494 3
                         '</tr>';
495
496
                $html .= '<tr>' .
497 3
                          '<td ' . TPL::getSnippet('sRowCheck') . '>checks</td>' .
498 3
                          '<td ' . TPL::getSnippet('sRowCheck') . ' align="right">' .
499 3
                            $backup->checkCount() . '
0 ignored issues
show
The method checkCount() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
500
                           </td>' .
501 3
                          '<td ' . TPL::getSnippet('sRowCheck') . ' align="right">
502
                            &nbsp;
503
                           </td>' .
504 3
                          '<td ' . TPL::getSnippet('sRowCheck') . ' align="right">' .
505 3
                            $backup->checkCountFailed() .
0 ignored issues
show
The method checkCountFailed() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
506 3
                          '</td>' .
507 3
                         '</tr>' .
508 3
                         '<tr>' .
509 3
                          '<td ' . TPL::getSnippet('sRowCrypt') . '>crypts</td>' .
510 3
                          '<td ' . TPL::getSnippet('sRowCrypt') . ' align="right">' .
511 3
                            $backup->cryptCount() .
0 ignored issues
show
The method cryptCount() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
512 3
                          '</td>' .
513 3
                          '<td ' . TPL::getSnippet('sRowCrypt') . ' align="right">' .
514 3
                            $backup->cryptCountSkipped() .
0 ignored issues
show
The method cryptCountSkipped() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
515 3
                          '</td>' .
516 3
                          '<td ' . TPL::getSnippet('sRowCrypt') . ' align="right">' .
517 3
                            $backup->cryptCountFailed() .
0 ignored issues
show
The method cryptCountFailed() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
518 3
                          '</td>' .
519 3
                         '</tr>' .
520 3
                         '<tr>' .
521 3
                          '<td ' . TPL::getSnippet('sRowSync') . '>syncs</td>' .
522 3
                          '<td ' . TPL::getSnippet('sRowSync') . ' align="right">' .
523 3
                            $backup->syncCount() . '</td>' .
0 ignored issues
show
The method syncCount() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
524 3
                          '<td ' . TPL::getSnippet('sRowSync') . ' align="right">' .
525 3
                            $backup->syncCountSkipped() .
0 ignored issues
show
The method syncCountSkipped() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
526 3
                          '</td>' .
527 3
                          '<td ' . TPL::getSnippet('sRowSync') . ' align="right">' .
528 3
                            $backup->syncCountFailed() .
0 ignored issues
show
The method syncCountFailed() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
529 3
                          '</td>' .
530 3
                         '</tr>' .
531 3
                         '<tr>' .
532 3
                          '<td ' . TPL::getSnippet('sRowCleanup') . '>cleanups</td>' .
533 3
                          '<td ' . TPL::getSnippet('sRowCleanup') . ' align="right">' .
534 3
                            $backup->cleanupCount() .
0 ignored issues
show
The method cleanupCount() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
535 3
                          '</td>' .
536 3
                          '<td ' . TPL::getSnippet('sRowCleanup') . ' align="right">' .
537 3
                            $backup->cleanupCountSkipped() .
0 ignored issues
show
The method cleanupCountSkipped() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
538 3
                          '</td>' .
539 3
                          '<td ' . TPL::getSnippet('sRowCleanup') . ' align="right">' .
540 3
                            $backup->cleanupCountFailed() .
0 ignored issues
show
The method cleanupCountFailed() does not seem to exist on object<phpbu\App\Configuration\Backup>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
541 3
                          '</td>' .
542 3
                         '</tr>';
543
544
            }
545 3
            $html .= '</table>';
546
        }
547 4
        return $html;
548
    }
549
550
    /**
551
     * Return mail body footer
552
     *
553
     * @return string
554
     * @throws \phpbu\App\Exception
555
     */
556 4
    protected function getFooterHtml()
557
    {
558 4
        return '<p ' . TPL::getSnippet('sStats') . '>' . Statistics::resourceUsage() . '</p>' .
559 4
               '</td></tr></table>';
560
    }
561
}
562