Completed
Push — master ( e8f5e1...4684bd )
by Rasmus
03:38
created

Message   D

Complexity

Total Complexity 51

Size/Duplication

Total Lines 429
Duplicated Lines 0 %

Coupling/Cohesion

Components 9
Dependencies 2

Test Coverage

Coverage 83.02%

Importance

Changes 2
Bugs 0 Features 2
Metric Value
wmc 51
c 2
b 0
f 2
lcom 9
cbo 2
dl 0
loc 429
ccs 88
cts 106
cp 0.8302
rs 4.4489

33 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getTo() 0 4 1
A setTo() 0 4 2
A addTo() 0 4 2
A getFrom() 0 4 1
A setFrom() 0 4 2
A addFrom() 0 4 2
A getSender() 0 12 3
A setSender() 0 4 1
A getCC() 0 4 1
A setCC() 0 4 2
A addCC() 0 4 2
A getBCC() 0 4 1
A setBCC() 0 4 2
A addBCC() 0 4 2
A getReplyTo() 0 4 1
A setReplyTo() 0 4 2
A addReplyTo() 0 4 2
A getSubject() 0 4 1
A setSubject() 0 4 1
A getDate() 0 4 1
A setDate() 0 14 4
A getText() 0 4 1
A setText() 0 8 2
A getHTML() 0 4 1
A setHTML() 0 8 2
A getAttachments() 0 4 1
A addAttachment() 0 4 1
A getInlineAttachments() 0 4 1
A addInlineAttachment() 0 8 1
A getHeaders() 0 10 2
A setHeader() 0 4 1
A addHeader() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Message 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 Message, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Kodus\Mail;
4
5
use DateTimeImmutable;
6
use DateTimeInterface;
7
use InvalidArgumentException;
8
9
/**
10
 * This class represents an e-mail Message.
11
 */
12
class Message
13
{
14
    /**
15
     * @var Address[]
16
     */
17
    private $to = [];
18
19
    /**
20
     * @var Address[]
21
     */
22
    private $from = [];
23
24
    /**
25
     * @var Address
26
     */
27
    private $sender;
28
29
    /**
30
     * @var Address[]
31
     */
32
    private $cc = [];
33
34
    /**
35
     * @var Address[]
36
     */
37
    private $bcc = [];
38
39
    /**
40
     * @var Address[]
41
     */
42
    private $reply_to = [];
43
44
    /**
45
     * @var string
46
     */
47
    private $subject;
48
49
    /**
50
     * @var DateTimeInterface
51
     */
52
    private $date;
53
54
    /**
55
     * @var string|null
56
     */
57
    private $text;
58
59
    /**
60
     * @var string|null
61
     */
62
    private $html;
63
64
    /**
65
     * @var Attachment[]
66
     */
67
    private $attachments = [];
68
69
    /**
70
     * @var InlineAttachment[]
71
     */
72
    private $inline_attachments = [];
73
74
    /**
75
     * @var Header[]
76
     */
77
    private $headers = [];
78
79
    /**
80
     * @param Address|Address[] $to
81
     * @param Address|Address[] $from
82
     * @param string            $subject
83
     * @param string|null       $text
84 19
     * @param string|null       $html
85
     */
86 19
    public function __construct($to, $from, $subject, $text, $html = null)
87 19
    {
88 19
        $this->setTo($to);
89 19
        $this->setFrom($from);
90 19
        $this->setSubject($subject);
91 19
        $this->setText($text);
92 19
        $this->setHTML($html);
93
        $this->setDate(time());
94
    }
95
96
    /**
97 17
     * @return Address|Address[]
98
     */
99 17
    public function getTo()
100
    {
101
        return $this->to;
102
    }
103
104
    /**
105
     * This field contains the identity of the primary recipients of the Message.
106
     *
107 19
     * @param Address|Address[] $address
108
     */
109 19
    public function setTo($address)
110 19
    {
111
        $this->to = is_array($address) ? $address : [$address];
112
    }
113
114
    /**
115
     * @param Address|Address[] $address
116
     */
117
    public function addTo($address)
118
    {
119
        $this->to = array_merge($this->to, is_array($address) ? $address : [$address]);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->to, i...ress : array($address)) of type array is incompatible with the declared type array<integer,object<Kodus\Mail\Address>> of property $to.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
120
    }
121
122
    /**
123 17
     * @return Address|Address[]
124
     */
125 17
    public function getFrom()
126
    {
127
        return $this->from;
128
    }
129
130
    /**
131
     * Specifies the author(s) of the message; that is, the mailbox(es)
132
     * of the person(s) or system(s) responsible for the writing of the
133
     * message.
134
     *
135 19
     * @param Address|Address[] $address
136
     */
137 19
    public function setFrom($address)
138 19
    {
139
        $this->from = is_array($address) ? $address : [$address];
140
    }
141
142
    /**
143
     * @param Address|Address[] $address
144
     */
145
    public function addFrom($address)
146
    {
147
        $this->from = array_merge($this->from, is_array($address) ? $address : [$address]);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->from,...ress : array($address)) of type array is incompatible with the declared type array<integer,object<Kodus\Mail\Address>> of property $from.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
148
    }
149
150
    /**
151
     * Return the Sender
152
     *
153 15
     * @return Address|null
154
     */
155 15
    public function getSender()
156 2
    {
157
        if ($this->sender) {
158
            return $this->sender;
159 14
        }
160 2
161
        if (count($this->from) > 1) {
162
            return $this->from[0];
163 13
        }
164
165
        return null;
166
    }
167
168
    /**
169
     * Specifies the mailbox of the agent responsible for the actual transmission of the message.
170
     *
171
     * This field contains the authenticated identity of the "agent" (person, system or process)
172
     * that sends the message. It is intended for use when the sender is *not* the author of
173
     * the message, or to indicate who among a group of authors actually sent the message.
174
     *
175
     * If the contents of this field would be completely redundant with the "From" field, then
176
     * the "Sender" field need not be present and its use is discouraged, though still permitted.
177
     *
178
     * In particular, the "Sender" field *must* be present if it is *not* the same as the "From" Field.
179
     *
180 2
     * @param Address|null $sender
181
     */
182 2
    public function setSender(Address $sender)
183 2
    {
184
        $this->sender = $sender;
185
    }
186
187
    /**
188 17
     * @return Address|Address[]
189
     */
190 17
    public function getCC()
191
    {
192
        return $this->cc;
193
    }
194
195
    /**
196
     * This field contains the identity of any secondary recipients of the Message.
197
     *
198
     * @param Address|Address[] $address
199
     */
200
    public function setCC($address)
201
    {
202
        $this->cc = is_array($address) ? $address : [$address];
203
    }
204
205
    /**
206 2
     * @param Address|Address[] $address
207
     */
208 2
    public function addCC($address)
209 2
    {
210
        $this->cc = array_merge($this->cc, is_array($address) ? $address : [$address]);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->cc, i...ress : array($address)) of type array is incompatible with the declared type array<integer,object<Kodus\Mail\Address>> of property $cc.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
211
    }
212
213
    /**
214 17
     * @return Address|Address[]
215
     */
216 17
    public function getBCC()
217
    {
218
        return $this->bcc;
219
    }
220
221
    /**
222
     * This field contains the identity of additional recipients of the message.
223
     *
224
     * The contents of this field are not included in copies of the Message sent to the primary
225
     * or secondary recipients, e.g. the "To" and "CC" fields.
226
     *
227
     * Some systems may choose to include the text of the "BCC" field only in the author's copy,
228
     * while others may also include it in the text sent to all those indicated in the "BCC" list.
229
     *
230
     * @param Address|Address[] $address
231
     */
232
    public function setBCC($address)
233
    {
234
        $this->bcc = is_array($address) ? $address : [$address];
235
    }
236
237
    /**
238 2
     * @param Address|Address[] $address
239
     */
240 2
    public function addBCC($address)
241 2
    {
242
        $this->bcc = array_merge($this->bcc, is_array($address) ? $address : [$address]);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->bcc, ...ress : array($address)) of type array is incompatible with the declared type array<integer,object<Kodus\Mail\Address>> of property $bcc.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
243
    }
244
245
    /**
246 15
     * @return Address|Address[]
247
     */
248 15
    public function getReplyTo()
249
    {
250
        return $this->reply_to;
251
    }
252
253
    /**
254
     * @param Address|Address[] $address
255
     */
256
    public function setReplyTo($address)
257
    {
258
        $this->reply_to = is_array($address) ? $address : [$address];
259
    }
260
261
    /**
262
     * @param Address|Address[] $address
263
     */
264
    public function addReplyTo($address)
265
    {
266
        $this->reply_to = array_merge($this->reply_to, is_array($address) ? $address : [$address]);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($this->reply...ress : array($address)) of type array is incompatible with the declared type array<integer,object<Kodus\Mail\Address>> of property $reply_to.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
267
    }
268
269
    /**
270 17
     * @return string
271
     */
272 17
    public function getSubject()
273
    {
274
        return $this->subject;
275
    }
276
277
    /**
278 19
     * @param string $subject
279
     */
280 19
    public function setSubject($subject)
281 19
    {
282
        $this->subject = $subject;
283
    }
284
285
    /**
286 15
     * @return DateTimeInterface
287
     */
288 15
    public function getDate()
289
    {
290
        return $this->date;
291
    }
292
293
    /**
294 19
     * @param int|string|DateTimeInterface $date DateTime in Sender's timezone (or a UNIX integer timestamp;
295
     *                                           or a string that is compatible with the strtotime() function)
296 19
     */
297 19
    public function setDate($date)
298 19
    {
299 19
        if ($date instanceof DateTimeInterface) {
300
            $this->date = $date;
301
        } elseif (is_int($date)) {
302
            $this->date = DateTimeImmutable::createFromFormat("U", $date)
0 ignored issues
show
Documentation Bug introduced by
It seems like \DateTimeImmutable::crea...efault_timezone_get())) can also be of type false. However, the property $date is declared as type object<DateTimeInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
303
                ->setTimezone(timezone_open(date_default_timezone_get()));
304 15
        } elseif (is_string($date)) {
305
            $this->date = DateTimeImmutable::createFromFormat("U", strtotime($date))
0 ignored issues
show
Documentation Bug introduced by
It seems like \DateTimeImmutable::crea...efault_timezone_get())) can also be of type false. However, the property $date is declared as type object<DateTimeInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
306 15
                ->setTimezone(timezone_open(date_default_timezone_get()));
307
        } else {
308
            throw new InvalidArgumentException("invalid date given: " . var_export($date, true));
309
        }
310
    }
311
312
    /**
313
     * @return string|null plain text message body
314 19
     */
315
    public function getText()
316 19
    {
317 1
        return $this->text;
318
    }
319
320 19
    /**
321 19
     * @param string|null $text plain text message body
322
     *
323
     * @throws InvalidArgumentException if the given message body is not valid UTF-8
324
     */
325
    public function setText($text)
326 15
    {
327
        if (preg_match('//u', $text) !== 1) {
328 15
            throw new InvalidArgumentException("message body contains an invalid UTF-8 byte sequence");
329
        }
330
331
        $this->text = $text;
332
    }
333
334
    /**
335
     * @return string|null HTML message body
336 19
     */
337
    public function getHTML()
338 19
    {
339 1
        return $this->html;
340
    }
341
342 19
    /**
343 19
     * @param string|null $html HTML message body
344
     *
345
     * @throws InvalidArgumentException if the given message body is not valid UTF-8
346
     */
347
    public function setHTML($html)
348 15
    {
349
        if (preg_match('//u', $html) !== 1) {
350 15
            throw new InvalidArgumentException("message body contains an invalid UTF-8 byte sequence");
351
        }
352
353
        $this->html = $html;
354
    }
355
356 7
    /**
357
     * @return Attachment[]
358 7
     */
359 7
    public function getAttachments()
360
    {
361
        return $this->attachments;
362
    }
363
364 15
    /**
365
     * @param Attachment $attachment
366 15
     */
367
    public function addAttachment(Attachment $attachment)
368
    {
369
        $this->attachments[] = $attachment;
370
    }
371
372
    /**
373
     * @return InlineAttachment[]
374
     */
375
    public function getInlineAttachments()
376
    {
377
        return $this->inline_attachments;
378
    }
379
380
    /**
381
     * Add an inline Attachment, e.g. an image you wish to display in the HTML body of your Message.
382
     *
383
     * This method returns a URI for the inline Attachment - you should substitute a placeholder,
384
     * e.g. for the `src` attribute of an `img` tag, in the body of your HTML Message content -
385 5
     * for example:
386
     *
387 5
     *     $html = '<img src="#logo-image"/>';
388
     *     $uri = $message->addInlineAttachment(Attachment::fromFile(__DIR__ . '/logo.png'));
389 5
     *     $html = strtr($html, ["#logo-image" => $uri]);
390
     *     $message->setHTML($html);
391 5
     *
392
     * @param Attachment $attachment
393
     *
394
     * @return string inline Attachment URI
395
     */
396
    public function addInlineAttachment(Attachment $attachment)
397 16
    {
398
        $inline_attachment = new InlineAttachment($attachment);
399 16
400
        $this->inline_attachments[] = $inline_attachment;
401 16
402 3
        return "cid:" . $inline_attachment->getContentID();
403 16
    }
404
405 16
    /**
406
     * @return Header[]
407
     */
408
    public function getHeaders()
409
    {
410
        $all_headers = [];
411
412
        foreach ($this->headers as $headers) {
413
            $all_headers = array_merge($all_headers, $headers);
414
        }
415
416 1
        return $all_headers;
417
    }
418 1
419 1
    /**
420
     * Set a custom MIME message header - for example, you may wish to set special headers
421
     * such as `Message-ID`, `X-Priority` or `X-Mailer` headers, but be aware that some
422
     * headers (such as `Message-ID`) have syntax that you need to comply with.
423
     *
424
     * @param string $name
425 3
     * @param string $value
426
     */
427 3
    public function setHeader($name, $value)
428 3
    {
429
        $this->headers[strtolower($name)] = [new Header($name, $value)];
430
    }
431
432
    /**
433
     * @param string $name
434
     * @param string $value
435
     */
436
    public function addHeader($name, $value)
437
    {
438
        $this->headers[strtolower($name)][] = new Header($name, $value);
439
    }
440
}
441