Total Complexity | 44 |
Total Lines | 645 |
Duplicated Lines | 0 % |
Changes | 0 |
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.
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 declare(strict_types=1); |
||
24 | class Message implements MessageInterface, MessageOptionsInterface |
||
25 | { |
||
26 | use HeaderTrait; |
||
27 | |||
28 | const MERGE_LANGUAGE_MAILCHIMP = 'mailchimp'; |
||
29 | const MERGE_LANGUAGE_HANDLEBARS = 'handlebars'; |
||
30 | |||
31 | /** |
||
32 | * whether or not this message is important, |
||
33 | * and should be delivered ahead of non-important messages |
||
34 | * |
||
35 | * @var boolean $isImportant |
||
36 | */ |
||
37 | protected $isImportant = false; |
||
38 | |||
39 | /** |
||
40 | * whether or not to turn on open tracking for the message |
||
41 | * |
||
42 | * @var boolean $trackOpens |
||
43 | */ |
||
44 | protected $trackOpens; |
||
45 | |||
46 | /** |
||
47 | * whether or not to turn on click tracking for the message |
||
48 | * |
||
49 | * @var boolean $trackClicks |
||
50 | */ |
||
51 | protected $trackClicks; |
||
52 | |||
53 | /** |
||
54 | * whether or not to automatically generate a text part for messages that are not given text |
||
55 | * |
||
56 | * @var boolean $autoText |
||
57 | */ |
||
58 | protected $autoText; |
||
59 | |||
60 | /** |
||
61 | * whether or not to automatically generate an HTML part for messages that are not given HTML |
||
62 | * |
||
63 | * @var boolean $autoHtml |
||
64 | */ |
||
65 | protected $autoHtml; |
||
66 | |||
67 | /** |
||
68 | * -- only for HTML documents less than 256KB in size -- |
||
69 | * whether or not to automatically inline all CSS styles provided in the message HTML |
||
70 | * |
||
71 | * @var boolean $inlineCss |
||
72 | */ |
||
73 | protected $inlineCss; |
||
74 | |||
75 | /** |
||
76 | * whether or not to strip the query string from URLs when aggregating tracked URL data |
||
77 | * |
||
78 | * @var boolean $urlStripQs |
||
79 | */ |
||
80 | protected $urlStripQs; |
||
81 | |||
82 | /** |
||
83 | * whether or not to expose all recipients in to "To" header for each email |
||
84 | * |
||
85 | * @var boolean $preserveRecipients |
||
86 | */ |
||
87 | protected $preserveRecipients; |
||
88 | |||
89 | /** |
||
90 | * set to false to remove content logging for sensitive emails |
||
91 | * (disables the 'view content' link on in the mandrill app) |
||
92 | * |
||
93 | * @var boolean $viewContentLink |
||
94 | */ |
||
95 | protected $viewContentLink; |
||
96 | |||
97 | /** |
||
98 | * an optional address to receive an exact copy of each recipient's email |
||
99 | * |
||
100 | * @var string $bccAddress |
||
101 | */ |
||
102 | protected $bccAddress; |
||
103 | |||
104 | /** |
||
105 | * a custom domain to use for tracking opens and clicks instead of mandrillapp.com |
||
106 | * |
||
107 | * @var string $trackingDomain |
||
108 | */ |
||
109 | protected $trackingDomain; |
||
110 | |||
111 | /** |
||
112 | * a custom domain to use for SPF/DKIM signing instead of mandrill (for "via" or "on behalf of" in email clients) |
||
113 | * |
||
114 | * @var string $signingDomain |
||
115 | */ |
||
116 | protected $signingDomain; |
||
117 | |||
118 | /** |
||
119 | * a custom domain to use for the messages's return-path |
||
120 | * |
||
121 | * @var string $returnPathDomain |
||
122 | */ |
||
123 | protected $returnPathDomain; |
||
124 | |||
125 | /** |
||
126 | * metadata an associative array of user metadata. |
||
127 | * Mandrill will store this metadata and make it available for retrieval. |
||
128 | * In addition, you can select up to 10 metadata fields to index and make searchable using the Mandrill search api. |
||
129 | * |
||
130 | * @var array $metadata |
||
131 | */ |
||
132 | protected $metadata = []; |
||
133 | |||
134 | /** |
||
135 | * global merge variables to use for all recipients. You can override these per recipient. |
||
136 | * |
||
137 | * @var array $globalMergeVars |
||
138 | */ |
||
139 | protected $globalMergeVars = []; |
||
140 | |||
141 | /** |
||
142 | * the full HTML content to be sent |
||
143 | * |
||
144 | * @var string $html |
||
145 | */ |
||
146 | protected $html = ''; |
||
147 | |||
148 | /** |
||
149 | * optional full text content to be sent |
||
150 | * |
||
151 | * @var string $text |
||
152 | */ |
||
153 | protected $text; |
||
154 | |||
155 | /** |
||
156 | * the message subject |
||
157 | * |
||
158 | * @var string $subject |
||
159 | */ |
||
160 | protected $subject; |
||
161 | |||
162 | /** |
||
163 | * the sender email address |
||
164 | * |
||
165 | * @var string $fromEmail |
||
166 | */ |
||
167 | protected $fromEmail; |
||
168 | |||
169 | /** |
||
170 | * optional from name to be used |
||
171 | * |
||
172 | * @var string $fromName |
||
173 | */ |
||
174 | protected $fromName; |
||
175 | |||
176 | /** |
||
177 | * a collection of RecipientInterface - useful for easily building metadata and merge vars for recipients |
||
178 | * |
||
179 | * @var RecipientInterface[] $to |
||
180 | */ |
||
181 | protected $to = []; |
||
182 | |||
183 | /** |
||
184 | * an array of strings indicating for which any matching URLs |
||
185 | * will automatically have Google Analytics parameters appended to their query string automatically. |
||
186 | * |
||
187 | * @var string[] $googleAnalyticsDomains |
||
188 | */ |
||
189 | protected $googleAnalyticsDomains = []; |
||
190 | |||
191 | /** |
||
192 | * optional string indicating the value to set for the utm_campaign tracking parameter. |
||
193 | * If this isn't provided the email's from address will be used instead. |
||
194 | * |
||
195 | * @var string $googleAnalyticsCampaign |
||
196 | */ |
||
197 | protected $googleAnalyticsCampaign; |
||
198 | |||
199 | /** |
||
200 | * an array of strings to tag the message with. Stats are accumulated using tags, |
||
201 | * though we only store the first 100 we see, so this should not be unique or change frequently. |
||
202 | * Tags should be 50 characters or less. |
||
203 | * Any tags starting with an underscore are reserved for internal use and will cause errors. |
||
204 | * |
||
205 | * @var array $tags |
||
206 | */ |
||
207 | protected $tags = []; |
||
208 | |||
209 | /** |
||
210 | * the unique id of a subaccount for this message - must already exist or will fail with an error |
||
211 | * |
||
212 | * @var string $subaccount |
||
213 | */ |
||
214 | protected $subaccount; |
||
215 | |||
216 | /** |
||
217 | * whether to evaluate merge tags in the message. |
||
218 | * Will automatically be set to true if either merge_vars or global_merge_vars are provided. |
||
219 | * |
||
220 | * @var bool $merge |
||
221 | */ |
||
222 | protected $merge = false; |
||
223 | |||
224 | /** |
||
225 | * the merge tag language to use when evaluating merge tags, either mailchimp or handlebars |
||
226 | * one of mailchimp|handlebars |
||
227 | * |
||
228 | * @var string $mergeLanguage |
||
229 | */ |
||
230 | protected $mergeLanguage = self::MERGE_LANGUAGE_MAILCHIMP; |
||
231 | |||
232 | /** |
||
233 | * an array of supported attachments to add to the message |
||
234 | * |
||
235 | * @var array $attachments |
||
236 | */ |
||
237 | protected $attachments = []; |
||
238 | |||
239 | /** |
||
240 | * an array of embedded images to add to the message |
||
241 | * |
||
242 | * @var array $images |
||
243 | */ |
||
244 | protected $images = []; |
||
245 | |||
246 | /** |
||
247 | * @param string $subject |
||
248 | */ |
||
249 | public function setSubject(string $subject) |
||
250 | { |
||
251 | $this->subject = $subject; |
||
252 | } |
||
253 | |||
254 | /** |
||
255 | * @param string $email |
||
256 | * @param string|null $name |
||
257 | */ |
||
258 | public function setFrom(string $email, string $name = null) |
||
259 | { |
||
260 | $this->fromEmail = $email; |
||
261 | $this->fromName = $name; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @param string $html |
||
266 | */ |
||
267 | public function setHtml(string $html) |
||
268 | { |
||
269 | $this->html = $html; |
||
270 | } |
||
271 | |||
272 | /** |
||
273 | * @param string $text |
||
274 | */ |
||
275 | public function setText(string $text) |
||
276 | { |
||
277 | $this->text = $text; |
||
278 | } |
||
279 | |||
280 | /** |
||
281 | * @param string $email |
||
282 | */ |
||
283 | public function setReplyTo(string $email) |
||
284 | { |
||
285 | $this->addHeader('Reply-To', $email); |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * @param string $email |
||
290 | * @param string $name |
||
291 | * |
||
292 | * @return RecipientBuilderInterface |
||
293 | * @throws ValidationException |
||
294 | */ |
||
295 | public function addTo(string $email, string $name = ''): RecipientBuilderInterface |
||
296 | { |
||
297 | return $this->to[] = new ToRecipient($email, $name); |
||
298 | } |
||
299 | |||
300 | /** |
||
301 | * @param string $email |
||
302 | * @param string $name |
||
303 | * |
||
304 | * @return RecipientBuilderInterface |
||
305 | * @throws ValidationException |
||
306 | */ |
||
307 | public function addCc(string $email, string $name = ''): RecipientBuilderInterface |
||
308 | { |
||
309 | return $this->to[] = new CcRecipient($email, $name); |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * @param string $email |
||
314 | * @param string $name |
||
315 | * |
||
316 | * @return RecipientBuilderInterface |
||
317 | * @throws ValidationException |
||
318 | */ |
||
319 | public function addBcc(string $email, string $name = ''): RecipientBuilderInterface |
||
320 | { |
||
321 | return $this->to[] = new BccRecipient($email, $name); |
||
322 | } |
||
323 | |||
324 | /** |
||
325 | * set important headers (I'm pretty sure mandrill does this already, but it can't hurt) |
||
326 | */ |
||
327 | public function isImportant() |
||
328 | { |
||
329 | $this->isImportant = true; |
||
330 | |||
331 | $this->addHeader('X-Priority', 1); |
||
332 | $this->addHeader('X-MSMail-Priority', 'high'); |
||
333 | $this->addHeader('Importance', 'high'); |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * turn on tracking when an email is opened |
||
338 | */ |
||
339 | public function trackOpens() |
||
340 | { |
||
341 | $this->trackOpens = true; |
||
342 | } |
||
343 | |||
344 | /** |
||
345 | * turn on click tracking |
||
346 | */ |
||
347 | public function trackClicks() |
||
348 | { |
||
349 | $this->trackClicks = true; |
||
350 | } |
||
351 | |||
352 | /** |
||
353 | * tell mandrill to automatically generate text parts for messages that are not given text |
||
354 | */ |
||
355 | public function autoGenerateText() |
||
356 | { |
||
357 | $this->autoText = true; |
||
358 | } |
||
359 | |||
360 | /** |
||
361 | * tell mandrill to automatically generate text parts for messages that are not given text |
||
362 | */ |
||
363 | public function autoGenerateHtml() |
||
364 | { |
||
365 | $this->autoHtml = true; |
||
366 | } |
||
367 | |||
368 | /** |
||
369 | * tell mandrill to automatically inline all CSS styles provided in the message HTML |
||
370 | */ |
||
371 | public function inlineCss() |
||
372 | { |
||
373 | $this->inlineCss = true; |
||
374 | } |
||
375 | |||
376 | /** |
||
377 | * tell mandrill to strip the query string from URLs when aggregating tracked URL data |
||
378 | */ |
||
379 | public function urlStripQs() |
||
380 | { |
||
381 | $this->urlStripQs = true; |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * tell mandrill to expose all recipients in to "To" header for each email |
||
386 | */ |
||
387 | public function preserveRecipients() |
||
388 | { |
||
389 | $this->preserveRecipients = true; |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * disables the view content link for sensitive emails (will disable the link in the mandrill app) |
||
394 | */ |
||
395 | public function disableContentLink() |
||
396 | { |
||
397 | $this->viewContentLink = false; |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * an optional address to receive an exact copy of each recipient's email |
||
402 | * |
||
403 | * @param string $email |
||
404 | */ |
||
405 | public function setBccAddress(string $email) |
||
406 | { |
||
407 | $this->bccAddress = $email; |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * set a custom domain to use for tracking opens and clicks instead of mandrillapp.com |
||
412 | * |
||
413 | * @param string $domain |
||
414 | */ |
||
415 | public function setTrackingDomain(string $domain) |
||
416 | { |
||
417 | $this->trackingDomain = $domain; |
||
418 | } |
||
419 | |||
420 | /** |
||
421 | * set a custom domain to use for SPF/DKIM signing instead of mandrill (for "via" or "on behalf of" in email clients) |
||
422 | * |
||
423 | * @param string $domain |
||
424 | */ |
||
425 | public function setSigningDomain(string $domain) |
||
426 | { |
||
427 | $this->signingDomain = $domain; |
||
428 | } |
||
429 | |||
430 | /** |
||
431 | * set a custom domain to use for the messages's return-path |
||
432 | * |
||
433 | * @param string $domain |
||
434 | */ |
||
435 | public function setReturnPathDomain(string $domain) |
||
436 | { |
||
437 | $this->returnPathDomain = $domain; |
||
438 | } |
||
439 | |||
440 | /** |
||
441 | * using this method will overwrite all global metadata |
||
442 | * but allows the user of this method to easily set metadata without a loop |
||
443 | * |
||
444 | * @param array $metadata |
||
445 | */ |
||
446 | public function setMetadata(array $metadata) |
||
447 | { |
||
448 | $this->metadata = $metadata; |
||
449 | } |
||
450 | |||
451 | /** |
||
452 | * add a key to the metadata array |
||
453 | * |
||
454 | * @param $key |
||
455 | * @param $value |
||
456 | */ |
||
457 | public function addMetadata($key, $value) |
||
460 | } |
||
461 | |||
462 | /** |
||
463 | * add a global merge variable |
||
464 | * |
||
465 | * @param string $name |
||
466 | * @param $content |
||
467 | * |
||
468 | * @throws ValidationException |
||
469 | */ |
||
470 | public function addMergeVar(string $name, $content) |
||
471 | { |
||
472 | $this->merge = true; |
||
473 | if ($this->stringStartsWithUnderscore($name)) { |
||
474 | throw new ValidationException('Merge variables may not start with an underscore'); |
||
475 | } |
||
476 | $this->globalMergeVars[] = ['name' => $name, 'content' => $content]; |
||
477 | } |
||
478 | |||
479 | /** |
||
480 | * A string to tag the message with. Stats are accumulated using tags, |
||
481 | * though we only store the first 100 we see, so this should not be unique or change frequently. |
||
482 | * Tags should be 50 characters or less. |
||
483 | * Any tags starting with an underscore are reserved for internal use and will cause errors. |
||
484 | * |
||
485 | * @param string $tag |
||
486 | * |
||
487 | * @throws ValidationException |
||
488 | */ |
||
489 | public function addTag(string $tag) |
||
490 | { |
||
491 | if ($this->stringStartsWithUnderscore($tag)) { |
||
492 | throw new ValidationException('Tags may not start with an underscore'); |
||
493 | } |
||
494 | $this->tags[] = $tag; |
||
495 | } |
||
496 | |||
497 | /** |
||
498 | * an array of strings indicating for which any matching URLs |
||
499 | * will automatically have Google Analytics parameters appended to their query string automatically. |
||
500 | * |
||
501 | * @param string $domain |
||
502 | */ |
||
503 | public function addGoogleAnalyticsDomain(string $domain) |
||
504 | { |
||
505 | $this->googleAnalyticsDomains[] = $domain; |
||
506 | } |
||
507 | |||
508 | /** |
||
509 | * optional string indicating the value to set for the utm_campaign tracking parameter. |
||
510 | * If this isn't provided the email's from address will be used instead. |
||
511 | * |
||
512 | * @param string $campaign |
||
513 | */ |
||
514 | public function setGoogleAnalyticsCampaign(string $campaign) |
||
515 | { |
||
516 | $this->googleAnalyticsCampaign = $campaign; |
||
517 | } |
||
518 | |||
519 | /** |
||
520 | * the unique id of a subaccount for this message - must already exist or will fail with an error |
||
521 | * |
||
522 | * @param string $subaccount |
||
523 | */ |
||
524 | public function setSubaccount(string $subaccount) |
||
525 | { |
||
526 | $this->subaccount = $subaccount; |
||
527 | } |
||
528 | |||
529 | /** |
||
530 | * set the merge language |
||
531 | * mailchimp|handlebars |
||
532 | * |
||
533 | * @param string $mergeLanguage |
||
534 | */ |
||
535 | public function setMergeLanguage(string $mergeLanguage) |
||
536 | { |
||
537 | $this->mergeLanguage = $mergeLanguage; |
||
538 | } |
||
539 | |||
540 | /** |
||
541 | * a single supported attachment |
||
542 | * |
||
543 | * @param string $type the MIME type of the attachment |
||
544 | * @param string $name the file name of the attachment |
||
545 | * @param string $content the content of the attachment as a base64-encoded string |
||
546 | */ |
||
547 | public function addAttachment(string $type, string $name, string $content) |
||
553 | ]; |
||
554 | } |
||
555 | |||
556 | /** |
||
557 | * an array of embedded images to add to the message |
||
558 | * |
||
559 | * @param string $type the MIME type of the image - must start with "image/" |
||
560 | * @param string $name the Content ID of the image |
||
561 | * use <img src="cid:THIS_VALUE"> to reference the image in your HTML content |
||
562 | * @param string $content the content of the image as a base64-encoded string |
||
563 | */ |
||
564 | public function addImage(string $type, string $name, string $content) |
||
565 | { |
||
566 | $this->images[] = [ |
||
567 | 'type' => $type, |
||
568 | 'name' => $name, |
||
569 | 'content' => $content |
||
570 | ]; |
||
571 | } |
||
572 | |||
573 | /** |
||
574 | * @return array |
||
575 | */ |
||
576 | public function toArray(): array |
||
577 | { |
||
578 | return [ |
||
579 | 'html' => $this->html, |
||
580 | 'text' => $this->text, |
||
581 | 'subject' => $this->subject, |
||
582 | 'from_email' => $this->fromEmail, |
||
583 | 'from_name' => $this->fromName, |
||
584 | 'to' => $this->extractRecipients(), |
||
585 | 'headers' => $this->headers, |
||
586 | 'merge_vars' => $this->extractRecipientMergeVars(), |
||
587 | 'recipient_metadata' => $this->extractRecipientMetadata(), |
||
588 | 'important' => $this->isImportant, |
||
589 | 'track_opens' => $this->trackOpens, |
||
590 | 'track_clicks' => $this->trackClicks, |
||
591 | 'auto_text' => $this->autoText, |
||
592 | 'auto_html' => $this->autoHtml, |
||
593 | 'inline_css' => $this->inlineCss, |
||
594 | 'url_strip_qs' => $this->urlStripQs, |
||
595 | 'preserve_recipients' => $this->preserveRecipients, |
||
596 | 'view_content_link' => $this->viewContentLink, |
||
597 | 'bcc_address' => $this->bccAddress, |
||
598 | 'tracking_domain' => $this->trackingDomain, |
||
599 | 'signing_domain' => $this->signingDomain, |
||
600 | 'return_path_domain' => $this->returnPathDomain, |
||
601 | 'metadata' => $this->metadata, |
||
602 | 'global_merge_vars' => $this->globalMergeVars, |
||
603 | 'google_analytics_domains' => $this->googleAnalyticsDomains, |
||
604 | 'google_analytics_campaign' => $this->googleAnalyticsCampaign, |
||
605 | 'tags' => $this->tags, |
||
606 | 'subaccount' => $this->subaccount, |
||
607 | 'merge' => $this->merge, |
||
608 | 'merge_language' => $this->mergeLanguage, |
||
609 | 'attachments' => $this->attachments, |
||
610 | 'images' => $this->images |
||
611 | ]; |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * build the 'to' array for sending off to Mandrill. |
||
616 | * |
||
617 | * @return array |
||
618 | */ |
||
619 | private function extractRecipients() |
||
620 | { |
||
621 | $ret = []; |
||
622 | foreach ($this->to as $recipient) { |
||
623 | $ret[] = [ |
||
624 | 'email' => $recipient->getEmail(), |
||
625 | 'name' => $recipient->getName(), |
||
626 | 'type' => $recipient->getType() |
||
627 | ]; |
||
628 | } |
||
629 | return $ret; |
||
630 | } |
||
631 | |||
632 | private function extractRecipientMergeVars(): array |
||
633 | { |
||
634 | $ret = []; |
||
635 | foreach ($this->to as $recipient) { |
||
636 | if ($mergeVars = $recipient->getMergeVars()) { |
||
637 | $ret[] = [ |
||
638 | 'rcpt' => $recipient->getEmail(), |
||
639 | 'vars' => $mergeVars |
||
640 | ]; |
||
641 | } |
||
642 | } |
||
643 | $this->merge = $this->merge ?: !empty($ret); |
||
644 | return $ret; |
||
645 | } |
||
646 | |||
647 | private function extractRecipientMetadata(): array |
||
659 | } |
||
660 | |||
661 | /** |
||
662 | * @param string $str |
||
663 | * |
||
664 | * @return bool |
||
665 | */ |
||
666 | private function stringStartsWithUnderscore(string $str) |
||
667 | { |
||
668 | return substr($str, 0, 1) === '_'; |
||
669 | } |
||
670 | } |
||
671 |