Completed
Push — master ( 5f0168...32dcd4 )
by Lars
07:33 queued 03:06
created

BounceMailHandler   D

Complexity

Total Complexity 103

Size/Duplication

Total Lines 883
Duplicated Lines 5.78 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 56.08%

Importance

Changes 0
Metric Value
dl 51
loc 883
ccs 203
cts 362
cp 0.5608
rs 4.4444
c 0
b 0
f 0
wmc 103
lcom 1
cbo 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getVersion() 0 4 1
C globalDelete() 0 50 7
A output() 0 10 3
B openLocal() 0 21 5
C openMailbox() 0 35 7
F processMailbox() 0 200 38
A isParameter() 0 14 4
F processBounce() 51 170 30
C mailboxExist() 0 47 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BounceMailHandler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use BounceMailHandler, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 32 and the first side effect is on line 19.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Bounce Mail Handler (formerly known as BMH and PHPMailer-BMH)
4
 *
5
 * @copyright 2008-2009 Andry Prevost. All Rights Reserved.
6
 * @copyright 2011-2012 Anthon Pang.
7
 * @copyright 2015-2016 Lars Moelleken.
8
 *
9
 * @license   GPL
10
 *
11
 * @package   BounceMailHandler
12
 *
13
 * @author    Andy Prevost <[email protected]>
14
 * @author    Anthon Pang <[email protected]>
15
 * @author    Lars Moelleken <[email protected]>
16
 */
17
namespace BounceMailHandler;
18
19 1
require_once __DIR__ . '/phpmailer-bmh_rules.php';
20
21
/**
22
 * BounceMailHandler class
23
 *
24
 * BounceMailHandler is a PHP program to check your IMAP/POP3 inbox and
25
 * delete all 'hard' bounced emails. It features a callback function where
26
 * you can create a custom action. This provides you the ability to write
27
 * a script to match your database records and either set inactive or
28
 * delete records with email addresses that match the 'hard' bounce results.
29
 *
30
 * @package BounceMailHandler
31
 */
32
class BounceMailHandler
33
{
34
  const VERBOSE_QUIET  = 0; // suppress output
35
  const VERBOSE_SIMPLE = 1; // simple report
36
  const VERBOSE_REPORT = 2; // detailed report
37
  const VERBOSE_DEBUG  = 3; // detailed report plus debug info
38
39
  /**
40
   * mail-server
41
   *
42
   * @var string
43
   */
44
  public $mailhost = 'localhost';
45
46
  /**
47
   * the username of mailbox
48
   *
49
   * @var string
50
   */
51
  public $mailboxUserName;
52
53
  /**
54
   * the password needed to access mailbox
55
   *
56
   * @var string
57
   */
58
  public $mailboxPassword;
59
60
  /**
61
   * the last error msg
62
   *
63
   * @var string
64
   */
65
  public $errorMessage;
66
67
  /**
68
   * maximum limit messages processed in one batch
69
   *
70
   * @var int
71
   */
72
  public $maxMessages = 3000;
73
74
  /**
75
   * callback Action function name the function that handles the bounce mail. Parameters:
76
   *
77
   * int     $msgnum        the message number returned by Bounce Mail Handler
78
   * string  $bounce_type   the bounce type:
79
   *       'antispam',
80
   *       'autoreply',
81
   *       'concurrent',
82
   *       'content_reject',
83
   *       'command_reject',
84
   *       'internal_error',
85
   *       'defer',
86
   *       'delayed'
87
   *       =>
88
   *       array(
89
   *           'remove' => 0,
90
   *           'bounce_type' => 'temporary'
91
   *       ),
92
   *       'dns_loop',
93
   *       'dns_unknown',
94
   *       'full',
95
   *       'inactive',
96
   *       'latin_only',
97
   *       'other',
98
   *       'oversize',
99
   *       'outofoffice',
100
   *       'unknown',
101
   *       'unrecognized',
102
   *       'user_reject',
103
   *       'warning'
104
   * string  $email         the target email address string  $subject       the subject, ignore now string  $xheader
105
   * the XBounceHeader from the mail
106
   * 1 or 0  $remove        delete status, 0 is not deleted, 1 is deleted
107
   * string  $rule_no       bounce mail detect rule no.
108
   * string  $rule_cat      bounce mail detect rule category
109
   * int     $totalFetched  total number of messages in the mailbox
110
   *
111
   * @var mixed
112
   */
113
  public $actionFunction = 'callbackAction';
114
115
  /**
116
   * Callback custom body rules
117
   * ```
118
   * function customBodyRulesCallback( $result, $body, $structure, $debug )
119
   * {
120
   *    return $result;
121
   * }
122
   * ```
123
   *
124
   * @var null|callable
125
   */
126
  public $customBodyRulesCallback = null;
127
128
  /**
129
   * Callback custom DSN (Delivery Status Notification) rules
130
   * ```
131
   * function customDSNRulesCallback( $result, $dsnMsg, $dsnReport, $debug )
132
   * {
133
   *    return $result;
134
   * }
135
   * ```
136
   *
137
   * @var null|callable
138
   */
139
  public $customDSNRulesCallback = null;
140
141
  /**
142
   * test-mode, if true will not delete messages
143
   *
144
   * @var boolean
145
   */
146
  public $testMode = false;
147
148
  /**
149
   * purge the unknown messages (or not)
150
   *
151
   * @var boolean
152
   */
153
  public $purgeUnprocessed = false;
154
155
  /**
156
   * control the debug output, default is VERBOSE_SIMPLE
157
   *
158
   * @var int
159
   */
160
  public $verbose = self::VERBOSE_SIMPLE;
161
162
  /**
163
   * control the failed DSN rules output
164
   *
165
   * @var boolean
166
   */
167
  public $debugDsnRule = false;
168
169
  /**
170
   * control the failed BODY rules output
171
   *
172
   * @var boolean
173
   */
174
  public $debugBodyRule = false;
175
176
  /**
177
   * Control the method to process the mail header
178
   * if set true, uses the imap_fetchstructure function
179
   * otherwise, detect message type directly from headers,
180
   * a bit faster than imap_fetchstructure function and take less resources.
181
   *
182
   * however - the difference is negligible
183
   *
184
   * @var boolean
185
   */
186
  public $useFetchstructure = true;
187
188
  /**
189
   * If disableDelete is equal to true, it will disable the delete function.
190
   *
191
   * @var boolean
192
   */
193
  public $disableDelete = false;
194
195
  /**
196
   * defines new line ending
197
   *
198
   * @var string
199
   */
200
  public $bmhNewLine = "<br />\n";
201
202
  /**
203
   * defines port number, default is '143', other common choices are '110' (pop3), '993' (gmail)
204
   *
205
   * @var integer
206
   */
207
  public $port = 143;
208
209
  /**
210
   * defines service, default is 'imap', choice includes 'pop3'
211
   *
212
   * @var string
213
   */
214
  public $service = 'imap';
215
216
  /**
217
   * defines service option, default is 'notls', other choices are 'tls', 'ssl'
218
   *
219
   * @var string
220
   */
221
  public $serviceOption = 'notls';
222
223
  /**
224
   * mailbox type, default is 'INBOX', other choices are (Tasks, Spam, Replies, etc.)
225
   *
226
   * @var string
227
   */
228
  public $boxname = 'INBOX';
229
230
  /**
231
   * determines if soft bounces will be moved to another mailbox folder
232
   *
233
   * @var boolean
234
   */
235
  public $moveSoft = false;
236
237
  /**
238
   * mailbox folder to move soft bounces to, default is 'soft'
239
   *
240
   * @var string
241
   */
242
  public $softMailbox = 'INBOX.soft';
243
244
  /**
245
   * determines if hard bounces will be moved to another mailbox folder
246
   *
247
   * NOTE: If true, this will disable delete and perform a move operation instead
248
   *
249
   * @var boolean
250
   */
251
  public $moveHard = false;
252
253
  /**
254
   * mailbox folder to move hard bounces to, default is 'hard'
255
   *
256
   * @var string
257
   */
258
  public $hardMailbox = 'INBOX.hard';
259
260
  /*
261
   * Mailbox folder to move unprocessed mails
262
   * @var string
263
   */
264
  public $unprocessedBox = 'INBOX.unprocessed';
265
266
  /**
267
   * deletes messages globally prior to date in variable
268
   *
269
   * NOTE: excludes any message folder that includes 'sent' in mailbox name
270
   * format is same as MySQL: 'yyyy-mm-dd'
271
   * if variable is blank, will not process global delete
272
   *
273
   * @var string
274
   */
275
  public $deleteMsgDate = '';
276
277
  /**
278
   * Holds Bounce Mail Handler version.
279
   *
280
   * @var string
281
   */
282
  private $version = '5.3-dev';
283
284
  /**
285
   * (internal variable)
286
   *
287
   * The resource handler for the opened mailbox (POP3/IMAP/NNTP/etc.)
288
   *
289
   * @var resource
290
   */
291
  private $mailboxLink = false;
292
293
  /**
294
   * get version
295
   *
296
   * @return string
297
   */
298
  public function getVersion()
299
  {
300
    return $this->version;
301
  }
302
303
  /**
304
   * open a mail box
305
   *
306
   * @return boolean
307
   */
308 1
  public function openMailbox()
309
  {
310
    // before starting the processing, let's check the delete flag and do global deletes if true
311 1
    if (trim($this->deleteMsgDate) != '') {
312
      echo 'processing global delete based on date of ' . $this->deleteMsgDate . '<br />';
313
      $this->globalDelete();
314
    }
315
316
    // disable move operations if server is Gmail ... Gmail does not support mailbox creation
317 1
    if (false !== stripos($this->mailhost, 'gmail')) {
318
      $this->moveSoft = false;
319
      $this->moveHard = false;
320
    }
321
322 1
    $port = $this->port . '/' . $this->service . '/' . $this->serviceOption;
323
324 1
    set_time_limit(6000);
325
326 1
    if (!$this->testMode) {
327 1
      $this->mailboxLink = imap_open('{' . $this->mailhost . ':' . $port . '}' . $this->boxname, $this->mailboxUserName, $this->mailboxPassword, CL_EXPUNGE | ($this->testMode ? OP_READONLY : 0));
328 1
    } else {
329
      $this->mailboxLink = imap_open('{' . $this->mailhost . ':' . $port . '}' . $this->boxname, $this->mailboxUserName, $this->mailboxPassword, ($this->testMode ? OP_READONLY : 0));
330
    }
331
332 1
    if (!$this->mailboxLink) {
333
      $this->errorMessage = 'Cannot create ' . $this->service . ' connection to ' . $this->mailhost . $this->bmhNewLine . 'Error MSG: ' . imap_last_error();
334
      $this->output();
335
336
      return false;
337
    } else {
338 1
      $this->output('Connected to: ' . $this->mailhost . ' (' . $this->mailboxUserName . ')');
0 ignored issues
show
Documentation introduced by
'Connected to: ' . $this...->mailboxUserName . ')' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
339
340 1
      return true;
341
    }
342
  }
343
344
  /**
345
   * Function to delete messages in a mailbox, based on date
346
   *
347
   * NOTE: this is global ... will affect all mailboxes except any that have 'sent' in the mailbox name
348
   */
349
  public function globalDelete()
350
  {
351
    $dateArr = explode('-', $this->deleteMsgDate); // date format is yyyy-mm-dd
352
    $delDate = mktime(0, 0, 0, $dateArr[1], $dateArr[2], $dateArr[0]);
353
354
    $port = $this->port . '/' . $this->service . '/' . $this->serviceOption;
355
    $mboxt = imap_open('{' . $this->mailhost . ':' . $port . '}', $this->mailboxUserName, $this->mailboxPassword, OP_HALFOPEN);
356
357
    if ($mboxt === false) {
358
      return false;
359
    }
360
361
    $list = imap_getmailboxes($mboxt, '{' . $this->mailhost . ':' . $port . '}', '*');
362
363
    if (is_array($list)) {
364
      foreach ($list as $key => $val) {
365
        // get the mailbox name only
366
        $nameArr = explode('}', imap_utf7_decode($val->name));
367
        $nameRaw = $nameArr[count($nameArr) - 1];
368
369
        if (false === stripos($nameRaw, 'sent')) {
370
          $mboxd = imap_open('{' . $this->mailhost . ':' . $port . '}' . $nameRaw, $this->mailboxUserName, $this->mailboxPassword, CL_EXPUNGE);
371
          $messages = imap_sort($mboxd, SORTDATE, 0);
372
          $i = 0;
373
374
          foreach ($messages as $message) {
375
            $header = imap_header($mboxd, $message);
376
377
            // purge if prior to global delete date
378
            if ($header->udate < $delDate) {
379
              imap_delete($mboxd, $message);
380
            }
381
            $i++;
382
          }
383
384
          imap_expunge($mboxd);
385
          imap_close($mboxd);
386
        }
387
      }
388
389
      imap_close($mboxt);
390
391
      return true;
392
393
    } else {
394
      imap_close($mboxt);
395
396
      return false;
397
    }
398
  }
399
400
  /**
401
   * output additional msg for debug
402
   *
403
   * @param bool|false $msg          if not given, output the last error msg
404
   * @param int        $verboseLevel the output level of this message
405
   */
406 3
  public function output($msg = false, $verboseLevel = self::VERBOSE_SIMPLE)
407
  {
408 3
    if ($this->verbose >= $verboseLevel) {
409 3
      if (empty($msg)) {
410
        echo $this->errorMessage . $this->bmhNewLine;
411
      } else {
412 3
        echo $msg . $this->bmhNewLine;
413
      }
414 3
    }
415 3
  }
416
417
  /**
418
   * open a mail box in local file system
419
   *
420
   * @param string $filePath The local mailbox file path
421
   *
422
   * @return boolean
423
   */
424 2
  public function openLocal($filePath)
425
  {
426 2
    set_time_limit(6000);
427
428 2
    if (!$this->testMode) {
429
      $this->mailboxLink = imap_open($filePath, '', '', CL_EXPUNGE | ($this->testMode ? OP_READONLY : 0));
430
    } else {
431 2
      $this->mailboxLink = imap_open($filePath, '', '', ($this->testMode ? OP_READONLY : 0));
432
    }
433
434 2
    if (!$this->mailboxLink) {
435
      $this->errorMessage = 'Cannot open the mailbox file to ' . $filePath . $this->bmhNewLine . 'Error MSG: ' . imap_last_error();
436
      $this->output();
437
438
      return false;
439
    } else {
440 2
      $this->output('Opened ' . $filePath);
0 ignored issues
show
Documentation introduced by
'Opened ' . $filePath is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
441
442 2
      return true;
443
    }
444
  }
445
446
  /**
447
   * process the messages in a mailbox
448
   *
449
   * @param bool|false $max $max maximum limit messages processed in one batch, if not given uses the property
450
   *                        $maxMessages
451
   *
452
   * @return bool
453
   */
454 3
  public function processMailbox($max = false)
455
  {
456 3
    if (empty($this->actionFunction) || !is_callable($this->actionFunction)) {
457
      $this->errorMessage = 'Action function not found!';
458
      $this->output();
459
460
      return false;
461
    }
462
463 3
    if ($this->moveHard && ($this->disableDelete === false)) {
464
      $this->disableDelete = true;
465
    }
466
467 3
    if (!empty($max)) {
468
      $this->maxMessages = $max;
0 ignored issues
show
Documentation Bug introduced by
The property $maxMessages was declared of type integer, but $max is of type boolean. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
469
    }
470
471
    // initialize counters
472 3
    $totalCount = imap_num_msg($this->mailboxLink);
473 3
    $fetchedCount = $totalCount;
474 3
    $processedCount = 0;
475 3
    $unprocessedCount = 0;
476 3
    $deletedCount = 0;
477 3
    $movedCount = 0;
478 3
    $this->output('Total: ' . $totalCount . ' messages ');
0 ignored issues
show
Documentation introduced by
'Total: ' . $totalCount . ' messages ' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
479
480
    // process maximum number of messages
481 3
    if ($fetchedCount > $this->maxMessages) {
482
      $fetchedCount = $this->maxMessages;
483
      $this->output('Processing first ' . $fetchedCount . ' messages ');
0 ignored issues
show
Documentation introduced by
'Processing first ' . $f...hedCount . ' messages ' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
484
    }
485
486 3
    if ($this->testMode) {
487 2
      $this->output('Running in test mode, not deleting messages from mailbox<br />');
0 ignored issues
show
Documentation introduced by
'Running in test mode, n...ges from mailbox<br />' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
488 2
    } else {
489 1
      if ($this->disableDelete) {
490 1
        if ($this->moveHard) {
491 1
          $this->output('Running in move mode<br />');
0 ignored issues
show
Documentation introduced by
'Running in move mode<br />' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
492 1
        } else {
493
          $this->output('Running in disableDelete mode, not deleting messages from mailbox<br />');
0 ignored issues
show
Documentation introduced by
'Running in disableDelet...ges from mailbox<br />' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
494
        }
495 1
      } else {
496
        $this->output('Processed messages will be deleted from mailbox<br />');
0 ignored issues
show
Documentation introduced by
'Processed messages will...ted from mailbox<br />' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
497
      }
498
    }
499
500 3
    for ($x = 1; $x <= $fetchedCount; $x++) {
501
502
      // fetch the messages one at a time
503 3
      if ($this->useFetchstructure) {
504
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
505 3
        $structure = @imap_fetchstructure($this->mailboxLink, $x);
506
507
        if (
508
            $structure
509 3
            &&
510 3
            is_object($structure)
511 3
            &&
512 3
            $structure->type == 1
513 3
            &&
514 3
            $structure->ifsubtype
515 3
            &&
516 3
            $structure->ifparameters
517 3
            &&
518 3
            strtoupper($structure->subtype) == 'REPORT'
519 3
            &&
520 2
            $this->isParameter($structure->parameters, 'REPORT-TYPE', 'delivery-status')
521 3
        ) {
522 2
          $processed = $this->processBounce($x, 'DSN', $totalCount);
523 2
        } else {
524
          // not standard DSN msg
525 3
          $this->output('Msg #' . $x . ' is not a standard DSN message', self::VERBOSE_REPORT);
0 ignored issues
show
Documentation introduced by
'Msg #' . $x . ' is not a standard DSN message' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
526
527 3
          if ($this->debugBodyRule) {
528
            if ($structure->ifdescription) {
529
              $this->output("  Content-Type : {$structure->description}", self::VERBOSE_DEBUG);
530
            } else {
531
              $this->output('  Content-Type : unsupported', self::VERBOSE_DEBUG);
0 ignored issues
show
Documentation introduced by
' Content-Type : unsupported' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
532
            }
533
          }
534
535 3
          $processed = $this->processBounce($x, 'BODY', $totalCount);
536
        }
537 3
      } else {
538
        $header = imap_fetchheader($this->mailboxLink, $x);
539
540
        // Could be multi-line, if the new line begins with SPACE or HTAB
541
        if (preg_match("/Content-Type:((?:[^\n]|\n[\t ])+)(?:\n[^\t ]|$)/i", $header, $match)) {
542
          if (
543
              preg_match("/multipart\/report/i", $match[1])
544
              &&
545
              preg_match("/report-type=[\"']?delivery-status[\"']?/i", $match[1])
546
          ) {
547
            // standard DSN msg
548
            $processed = $this->processBounce($x, 'DSN', $totalCount);
549
          } else {
550
            // not standard DSN msg
551
            $this->output('Msg #' . $x . ' is not a standard DSN message', self::VERBOSE_REPORT);
0 ignored issues
show
Documentation introduced by
'Msg #' . $x . ' is not a standard DSN message' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
552
553
            if ($this->debugBodyRule) {
554
              $this->output("  Content-Type : {$match[1]}", self::VERBOSE_DEBUG);
555
            }
556
557
            $processed = $this->processBounce($x, 'BODY', $totalCount);
558
          }
559
        } else {
560
          // didn't get content-type header
561
          $this->output('Msg #' . $x . ' is not a well-formatted MIME mail, missing Content-Type', self::VERBOSE_REPORT);
0 ignored issues
show
Documentation introduced by
'Msg #' . $x . ' is not ..., missing Content-Type' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
562
563
          if ($this->debugBodyRule) {
564
            $this->output('  Headers: ' . $this->bmhNewLine . $header . $this->bmhNewLine, self::VERBOSE_DEBUG);
0 ignored issues
show
Documentation introduced by
' Headers: ' . $this->b...der . $this->bmhNewLine is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
565
          }
566
567
          $processed = $this->processBounce($x, 'BODY', $totalCount);
568
        }
569
      }
570
571 3
      $deleteFlag[$x] = false;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$deleteFlag was never initialized. Although not strictly required by PHP, it is generally a good practice to add $deleteFlag = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
572 3
      $moveFlag[$x] = false;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$moveFlag was never initialized. Although not strictly required by PHP, it is generally a good practice to add $moveFlag = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
573
574 3
      if ($processed) {
575 2
        $processedCount++;
576
577 2
        if (!$this->disableDelete) {
578
          // delete the bounce if not in disableDelete mode
579 2
          if (!$this->testMode) {
580
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
581
            @imap_delete($this->mailboxLink, $x);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
582
          }
583
584 2
          $deleteFlag[$x] = true;
0 ignored issues
show
Bug introduced by
The variable $deleteFlag does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
585 2
          $deletedCount++;
586 2
        } elseif ($this->moveHard) {
587
          // check if the move directory exists, if not create it
588
          if (!$this->testMode) {
589
            $this->mailboxExist($this->hardMailbox);
590
          }
591
592
          // move the message
593
          if (!$this->testMode) {
594
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
595
            @imap_mail_move($this->mailboxLink, $x, $this->hardMailbox);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
596
          }
597
598
          $moveFlag[$x] = true;
0 ignored issues
show
Bug introduced by
The variable $moveFlag does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
599
          $movedCount++;
600
        } elseif ($this->moveSoft) {
601
          // check if the move directory exists, if not create it
602
          if (!$this->testMode) {
603
            $this->mailboxExist($this->softMailbox);
604
          }
605
606
          // move the message
607
          if (!$this->testMode) {
608
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
609
            @imap_mail_move($this->mailboxLink, $x, $this->softMailbox);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
610
          }
611
612
          $moveFlag[$x] = true;
613
          $movedCount++;
614
        }
615 2
      } else {
616
        // not processed
617 3
        $unprocessedCount++;
618 3
        if (!$this->disableDelete && $this->purgeUnprocessed) {
619
          // delete this bounce if not in disableDelete mode, and the flag BOUNCE_PURGE_UNPROCESSED is set
620
          if (!$this->testMode) {
621
            /** @noinspection PhpUsageOfSilenceOperatorInspection */
622
            @imap_delete($this->mailboxLink, $x);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
623
          }
624
625
          $deleteFlag[$x] = true;
626
          $deletedCount++;
627
        }
628
629
        // check if the move directory exists, if not create it
630 3
        $this->mailboxExist($this->unprocessedBox);
631
        // move the message
632
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
633 3
        @imap_mail_move($this->mailboxLink, $x, $this->unprocessedBox);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
634 3
        $moveFlag[$x] = true;
635
      }
636
637 3
      flush();
638 3
    }
639
640 3
    $this->output($this->bmhNewLine . 'Closing mailbox, and purging messages');
0 ignored issues
show
Documentation introduced by
$this->bmhNewLine . 'Clo..., and purging messages' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
641
642
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
643 3
    @imap_expunge($this->mailboxLink);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
644 3
    imap_close($this->mailboxLink);
645
646 3
    $this->output('Read: ' . $fetchedCount . ' messages');
0 ignored issues
show
Documentation introduced by
'Read: ' . $fetchedCount . ' messages' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
647 3
    $this->output($processedCount . ' action taken');
0 ignored issues
show
Documentation introduced by
$processedCount . ' action taken' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
648 3
    $this->output($unprocessedCount . ' no action taken');
0 ignored issues
show
Documentation introduced by
$unprocessedCount . ' no action taken' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
649 3
    $this->output($deletedCount . ' messages deleted');
0 ignored issues
show
Documentation introduced by
$deletedCount . ' messages deleted' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
650 3
    $this->output($movedCount . ' messages moved');
0 ignored issues
show
Documentation introduced by
$movedCount . ' messages moved' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
651
652 3
    return true;
653
  }
654
655
  /**
656
   * Function to determine if a particular value is found in a imap_fetchstructure key.
657
   *
658
   * @param array  $currParameters imap_fetstructure parameters
659
   * @param string $varKey         imap_fetstructure key
660
   * @param string $varValue       value to check for
661
   *
662
   * @return boolean
663
   */
664 2
  public function isParameter($currParameters, $varKey, $varValue)
665
  {
666 2
    foreach ($currParameters as $object) {
667
      if (
668 2
          strtolower($object->attribute) == strtolower($varKey)
669 2
          &&
670 2
          strtolower($object->value) == strtolower($varValue)
671 2
      ) {
672 2
        return true;
673
      }
674
    }
675
676
    return false;
677
  }
678
679
  /**
680
   * Function to process each individual message.
681
   *
682
   * @param int    $pos          message number
683
   * @param string $type         DNS or BODY type
684
   * @param string $totalFetched total number of messages in mailbox
685
   *
686
   * @return boolean
687
   */
688 3
  public function processBounce($pos, $type, $totalFetched)
689
  {
690 3
    $header = imap_header($this->mailboxLink, $pos);
691 3
    $subject = isset($header->subject) ? strip_tags($header->subject) : '[NO SUBJECT]';
692 3
    $body = '';
693 3
    $headerFull = imap_fetchheader($this->mailboxLink, $pos);
694 3
    $bodyFull = imap_body($this->mailboxLink, $pos);
695
696 3
    if ($type == 'DSN') {
697
      // first part of DSN (Delivery Status Notification), human-readable explanation
698 2
      $dsnMsg = imap_fetchbody($this->mailboxLink, $pos, '1');
699 2
      $dsnMsgStructure = imap_bodystruct($this->mailboxLink, $pos, '1');
700
701 2
      if ($dsnMsgStructure->encoding == 4) {
702 1
        $dsnMsg = quoted_printable_decode($dsnMsg);
703 2
      } elseif ($dsnMsgStructure->encoding == 3) {
704
        $dsnMsg = base64_decode($dsnMsg);
705
      }
706
707
      // second part of DSN (Delivery Status Notification), delivery-status
708 2
      $dsnReport = imap_fetchbody($this->mailboxLink, $pos, '2');
709
710
      // process bounces by rules
711 2
      $result = bmhDSNRules($dsnMsg, $dsnReport, $this->debugDsnRule);
712 2
      $result = is_callable($this->customDSNRulesCallback) ? call_user_func($this->customDSNRulesCallback, $result, $dsnMsg, $dsnReport, $this->debugDsnRule) : $result;
713
714 3
    } elseif ($type == 'BODY') {
715
      /** @noinspection PhpUsageOfSilenceOperatorInspection */
716 3
      $structure = @imap_fetchstructure($this->mailboxLink, $pos);
717
718 3
      if (!is_object($structure)) {
719 1
        return false;
720
      }
721
722 3
      switch ($structure->type) {
723 3
        case 0: // Content-type = text
724 3
          $body = imap_fetchbody($this->mailboxLink, $pos, '1');
725 3
          $result = bmhBodyRules($body, $structure, $this->debugBodyRule);
0 ignored issues
show
Documentation introduced by
$structure is of type object, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
726 3
          $result = is_callable($this->customBodyRulesCallback) ? call_user_func($this->customBodyRulesCallback, $result, $body, $structure, $this->debugBodyRule) : $result;
727 3
          break;
728
729 1
        case 1: // Content-type = multipart
730 1
          $body = imap_fetchbody($this->mailboxLink, $pos, '1');
731
732
          // Detect encoding and decode - only base64
733 1
          if ($structure->parts[0]->encoding == 4) {
734 1
            $body = quoted_printable_decode($body);
735 1
          } elseif ($structure->parts[0]->encoding == 3) {
736
            $body = base64_decode($body);
737
          }
738
739 1
          $result = bmhBodyRules($body, $structure, $this->debugBodyRule);
0 ignored issues
show
Documentation introduced by
$structure is of type object, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
740 1
          $result = is_callable($this->customBodyRulesCallback) ? call_user_func($this->customBodyRulesCallback, $result, $body, $structure, $this->debugBodyRule) : $result;
741 1
          break;
742
743
        case 2: // Content-type = message
744
          $body = imap_body($this->mailboxLink, $pos);
745
746
          if ($structure->encoding == 4) {
747
            $body = quoted_printable_decode($body);
748
          } elseif ($structure->encoding == 3) {
749
            $body = base64_decode($body);
750
          }
751
752
          $body = substr($body, 0, 1000);
753
          $result = bmhBodyRules($body, $structure, $this->debugBodyRule);
0 ignored issues
show
Documentation introduced by
$structure is of type object, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
754
          $result = is_callable($this->customBodyRulesCallback) ? call_user_func($this->customBodyRulesCallback, $result, $body, $structure, $this->debugBodyRule) : $result;
755
          break;
756
757
        default: // unsupport Content-type
758
          $this->output('Msg #' . $pos . ' is unsupported Content-Type:' . $structure->type, self::VERBOSE_REPORT);
0 ignored issues
show
Documentation introduced by
'Msg #' . $pos . ' is un...pe:' . $structure->type is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
759
760
          return false;
761 3
      }
762 3
    } else {
763
      // internal error
764
      $this->errorMessage = 'Internal Error: unknown type';
765
766
      return false;
767
    }
768
769 3
    $email = $result['email'];
770 3
    $bounceType = $result['bounce_type'];
771
772
    // workaround: I think there is a error in one of the reg-ex in "phpmailer-bmh_rules.php".
773 3
    if ($email && strpos($email, 'TO:<')) {
774
      $email = str_replace('TO:<', '', $email);
775
    }
776
777 3
    if ($this->moveHard && $result['remove'] == 1) {
778
      $remove = 'moved (hard)';
779 3
    } elseif ($this->moveSoft && $result['remove'] == 1) {
780
      $remove = 'moved (soft)';
781 3
    } elseif ($this->disableDelete) {
782 1
      $remove = 0;
783 1
    } else {
784 2
      $remove = $result['remove'];
785
    }
786
787 3
    $ruleNumber = $result['rule_no'];
788 3
    $ruleCategory = $result['rule_cat'];
789 3
    $status_code = $result['status_code'];
790 3
    $action = $result['action'];
791 3
    $diagnostic_code = $result['diagnostic_code'];
792 3
    $xheader = false;
793
794 3
    if ($ruleNumber === '0000') {
795
      // unrecognized
796
      if (
797 3
          trim($email) == ''
798 3
          &&
799 3
          property_exists($header, 'fromaddress') === true
800 3
      ) {
801 3
        $email = $header->fromaddress;
802 3
      }
803
804 3 View Code Duplication
      if ($this->testMode) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
805 2
        $this->output('Match: ' . $ruleNumber . ':' . $ruleCategory . '; ' . $bounceType . '; ' . $email);
0 ignored issues
show
Documentation introduced by
'Match: ' . $ruleNumber ...nceType . '; ' . $email is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
806 2
      } else {
807
        // code below will use the Callback function, but return no value
808
        $params = array(
809 1
            $pos,
810 1
            $bounceType,
811 1
            $email,
812 1
            $subject,
813 1
            $header,
814 1
            $remove,
815 1
            $ruleNumber,
816 1
            $ruleCategory,
817 1
            $totalFetched,
818 1
            $body,
819 1
            $headerFull,
820 1
            $bodyFull,
821 1
            $status_code,
822 1
            $action,
823 1
            $diagnostic_code,
824 1
        );
825 1
        call_user_func_array($this->actionFunction, $params);
826
      }
827 3 View Code Duplication
    } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
828
      // match rule, do bounce action
829 2
      if ($this->testMode) {
830 2
        $this->output('Match: ' . $ruleNumber . ':' . $ruleCategory . '; ' . $bounceType . '; ' . $email);
0 ignored issues
show
Documentation introduced by
'Match: ' . $ruleNumber ...nceType . '; ' . $email is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
831
832 2
        return true;
833
      } else {
834
        $params = array(
835
            $pos,
836
            $bounceType,
837
            $email,
838
            $subject,
839
            $xheader,
840
            $remove,
841
            $ruleNumber,
842
            $ruleCategory,
843
            $totalFetched,
844
            $body,
845
            $headerFull,
846
            $bodyFull,
847
            $status_code,
848
            $action,
849
            $diagnostic_code,
850
        );
851
852
        return call_user_func_array($this->actionFunction, $params);
853
      }
854
    }
855
856 3
    return false;
857
  }
858
859
  /**
860
   * Function to check if a mailbox exists - if not found, it will create it.
861
   *
862
   * @param string  $mailbox the mailbox name, must be in 'INBOX.checkmailbox' format
863
   * @param boolean $create  whether or not to create the checkmailbox if not found, defaults to true
864
   *
865
   * @return boolean
866
   */
867 3
  public function mailboxExist($mailbox, $create = true)
868
  {
869 3
    if (trim($mailbox) == '') {
870
      // this is a critical error with either the mailbox name blank or an invalid mailbox name
871
      // need to stop processing and exit at this point
872
      echo 'Invalid mailbox name for move operation. Cannot continue: ' . $mailbox . "<br />\n";
873
      exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The method mailboxExist() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
874
    }
875
876 3
    $port = $this->port . '/' . $this->service . '/' . $this->serviceOption;
877
    /** @noinspection PhpUsageOfSilenceOperatorInspection */
878 3
    $mbox = @imap_open('{' . $this->mailhost . ':' . $port . '}', $this->mailboxUserName, $this->mailboxPassword, OP_HALFOPEN);
879
880 3
    if ($mbox === false) {
881 2
      return false;
882
    }
883
884 1
    $list = imap_getmailboxes($mbox, '{' . $this->mailhost . ':' . $port . '}', '*');
885 1
    $mailboxFound = false;
886
887 1
    if (is_array($list)) {
888 1
      foreach ($list as $key => $val) {
889
        // get the mailbox name only
890 1
        $nameArr = explode('}', imap_utf7_decode($val->name));
891 1
        $nameRaw = $nameArr[count($nameArr) - 1];
892 1
        if ($mailbox == $nameRaw) {
893 1
          $mailboxFound = true;
894 1
        }
895 1
      }
896
897 1
      if (($mailboxFound === false) && $create) {
898
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
899 1
        @imap_createmailbox($mbox, imap_utf7_encode('{' . $this->mailhost . ':' . $port . '}' . $mailbox));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
900 1
        imap_close($mbox);
901
902 1
        return true;
903
      } else {
904 1
        imap_close($mbox);
905
906 1
        return false;
907
      }
908
    } else {
909
      imap_close($mbox);
910
911
      return false;
912
    }
913
  }
914
}
915