Message   F
last analyzed

Complexity

Total Complexity 105

Size/Duplication

Total Lines 786
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 98.56%

Importance

Changes 0
Metric Value
wmc 105
lcom 1
cbo 2
dl 0
loc 786
ccs 206
cts 209
cp 0.9856
rs 1.814
c 0
b 0
f 0

46 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getClient() 0 4 1
A getText() 0 4 1
A setText() 0 6 2
A text() 0 4 1
A getNotification() 0 4 1
A setNotification() 0 6 2
A notification() 0 4 1
A getMarkdown() 0 4 1
A setMarkdown() 0 6 1
A markdown() 0 4 1
A getChannel() 0 4 1
A channel() 0 4 1
A getUser() 0 4 1
A user() 0 4 1
A getTarget() 0 10 3
A removeTarget() 0 6 1
A target() 0 4 1
A to() 0 4 1
A getAttachmentDefaults() 0 4 1
A getAttachments() 0 4 1
A attachments() 0 4 1
A add() 0 4 1
A addImage() 0 4 1
A remove() 0 4 1
A studlyCase() 0 4 1
A toJson() 0 4 1
A jsonSerialize() 0 4 1
A sendTo() 0 6 1
A __toString() 0 4 1
A setClient() 0 10 3
A setChannel() 0 10 2
A setUser() 0 10 2
A setTarget() 0 19 4
A setAttachmentDefaults() 0 10 2
A setAttachments() 0 12 3
A addAttachment() 0 19 5
A getAttachmentPayload() 0 22 5
A getImagesPayload() 0 16 5
A stringValue() 0 16 4
A removeAttachments() 0 16 4
B configureDefaults() 0 22 8
B fillDefaults() 0 21 8
A content() 0 23 5
A send() 0 19 6
A toArray() 0 21 5

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 ElfSundae\BearyChat;
4
5
use JsonSerializable;
6
7
class Message implements JsonSerializable
8
{
9
    /**
10
     * The BearyChat client for sending message.
11
     *
12
     * @var \ElfSundae\BearyChat\Client
13
     */
14
    protected $client;
15
16
    /**
17
     * The text to be sent with the message.
18
     *
19
     * @var string
20
     */
21
    protected $text;
22
23
    /**
24
     * The notification for the text.
25
     *
26
     * @var string
27
     */
28
    protected $notification;
29
30
    /**
31
     * Indicates the text field should be parsed as markdown syntax.
32
     *
33
     * @var bool
34
     */
35
    protected $markdown;
36
37
    /**
38
     * The channel that the message should be sent to.
39
     *
40
     * @var string
41
     */
42
    protected $channel;
43
44
    /**
45
     * The user that the message should be sent to.
46
     *
47
     * @var string
48
     */
49
    protected $user;
50
51
    /**
52
     * The attachments to be sent.
53
     *
54
     * @var array
55
     */
56
    protected $attachments = [];
57
58
    /**
59
     * The default values for each attachment.
60
     *
61
     * @var array
62
     */
63
    protected $attachmentDefaults = [];
64
65
    /**
66
     * Create a new message.
67
     *
68
     * @param  \ElfSundae\BearyChat\Client|null $client
69
     */
70 24
    public function __construct(Client $client = null)
71
    {
72 24
        $this->setClient($client);
73 24
    }
74
75
    /**
76
     * Get the BearyChat client for sending message.
77
     *
78
     * @return \ElfSundae\BearyChat\Client
79
     */
80 3
    public function getClient()
81
    {
82 3
        return $this->client;
83
    }
84
85
    /**
86
     * Set the BearyChat Client instance.
87
     *
88
     * @param  \ElfSundae\BearyChat\Client|null  $client
89
     * @return $this
90
     */
91 24
    public function setClient($client)
92
    {
93 24
        if ($client instanceof Client && $this->client != $client) {
94 6
            $this->configureDefaults($client->getMessageDefaults(), true);
95
        }
96
97 24
        $this->client = $client;
98
99 24
        return $this;
100
    }
101
102
    /**
103
     * Get the text.
104
     *
105
     * @return string
106
     */
107 8
    public function getText()
108
    {
109 8
        return $this->text;
110
    }
111
112
    /**
113
     * Set the text.
114
     *
115
     * @param  string  $text
116
     * @return $this
117
     */
118 7
    public function setText($text)
119
    {
120 7
        $this->text = $text ? (string) $text : null;
121
122 7
        return $this;
123
    }
124
125
    /**
126
     * Set the text.
127
     *
128
     * @param  string  $text
129
     * @return $this
130
     */
131 4
    public function text($text)
132
    {
133 4
        return $this->setText($text);
134
    }
135
136
    /**
137
     * Get the notification.
138
     *
139
     * @return string
140
     */
141 8
    public function getNotification()
142
    {
143 8
        return $this->notification;
144
    }
145
146
    /**
147
     * Set the notification.
148
     *
149
     * @param  string  $notification
150
     * @return $this
151
     */
152 5
    public function setNotification($notification)
153
    {
154 5
        $this->notification = $notification ? (string) $notification : null;
155
156 5
        return $this;
157
    }
158
159
    /**
160
     * Set the notification.
161
     *
162
     * @param  string  $notification
163
     * @return $this
164
     */
165 1
    public function notification($notification)
166
    {
167 1
        return $this->setNotification($notification);
168
    }
169
170
    /**
171
     * Get the markdown.
172
     *
173
     * @return bool
174
     */
175 9
    public function getMarkdown()
176
    {
177 9
        return $this->markdown;
178
    }
179
180
    /**
181
     * Set the markdown.
182
     *
183
     * @param  bool $markdown
184
     * @return $this
185
     */
186 5
    public function setMarkdown($markdown)
187
    {
188 5
        $this->markdown = (bool) $markdown;
189
190 5
        return $this;
191
    }
192
193
    /**
194
     * Set the markdown.
195
     *
196
     * @param  bool $markdown
197
     * @return $this
198
     */
199 1
    public function markdown($markdown = true)
200
    {
201 1
        return $this->setMarkdown($markdown);
202
    }
203
204
    /**
205
     * Get the channel which the message should be sent to.
206
     *
207
     * @return string
208
     */
209 11
    public function getChannel()
210
    {
211 11
        return $this->channel;
212
    }
213
214
    /**
215
     * Set the channel which the message should be sent to.
216
     *
217
     * @param  string  $channel
218
     * @return $this
219
     */
220 6
    public function setChannel($channel)
221
    {
222 6
        $this->removeTarget();
223
224 6
        if ($channel) {
225 6
            $this->channel = (string) $channel;
226
        }
227
228 6
        return $this;
229
    }
230
231
    /**
232
     * Set the channel which the message should be sent to.
233
     *
234
     * @param  string  $channel
235
     * @return $this
236
     */
237 4
    public function channel($channel)
238
    {
239 4
        return $this->setChannel($channel);
240
    }
241
242
    /**
243
     * Get the user which the message should be sent to.
244
     *
245
     * @return string
246
     */
247 12
    public function getUser()
248
    {
249 12
        return $this->user;
250
    }
251
252
    /**
253
     * Set the user which the message should be sent to.
254
     *
255
     * @param  string  $user
256
     * @return $this
257
     */
258 11
    public function setUser($user)
259
    {
260 11
        $this->removeTarget();
261
262 11
        if ($user) {
263 11
            $this->user = (string) $user;
264
        }
265
266 11
        return $this;
267
    }
268
269
    /**
270
     * Set the user which the message should be sent to.
271
     *
272
     * @param  string  $user
273
     * @return $this
274
     */
275 4
    public function user($user)
276
    {
277 4
        return $this->setUser($user);
278
    }
279
280
    /**
281
     * Get the target that the message should be sent to:
282
     * `#channel` or `@user` or null.
283
     *
284
     * @return string
285
     */
286 5
    public function getTarget()
287
    {
288 5
        if ($this->channel) {
289 1
            return '#'.$this->channel;
290
        }
291
292 5
        if ($this->user) {
293 3
            return '@'.$this->user;
294
        }
295 5
    }
296
297
    /**
298
     * Set the target (user or channel) that the message should be sent to.
299
     *
300
     * @param  string  $target  @user, #channel, channel, null
301
     * @return $this
302
     */
303 3
    public function setTarget($target)
304
    {
305 3
        $this->removeTarget();
306
307 3
        if ($target = (string) $target) {
308 3
            $mark = mb_substr($target, 0, 1);
309 3
            $to = mb_substr($target, 1);
310
311 3
            if ($mark === '@') {
312 3
                $this->setUser($to);
313 2
            } elseif ($mark === '#') {
314 1
                $this->setChannel($to);
315
            } else {
316 2
                $this->setChannel($target);
317
            }
318
        }
319
320 3
        return $this;
321
    }
322
323
    /**
324
     * Remove the target, then this message will be sent to
325
     * the webhook defined target.
326
     *
327
     * @return $this
328
     */
329 11
    public function removeTarget()
330
    {
331 11
        $this->channel = $this->user = null;
332
333 11
        return $this;
334
    }
335
336
    /**
337
     * Set the target.
338
     *
339
     * @param  string  $target
340
     * @return $this
341
     */
342 1
    public function target($target)
343
    {
344 1
        return $this->setTarget($target);
345
    }
346
347
    /**
348
     * Set the target.
349
     *
350
     * @param  string  $target
351
     * @return $this
352
     */
353 3
    public function to($target)
354
    {
355 3
        return $this->setTarget($target);
356
    }
357
358
    /**
359
     * Get the attachments defaults.
360
     *
361
     * @return array
362
     */
363 1
    public function getAttachmentDefaults()
364
    {
365 1
        return $this->attachmentDefaults;
366
    }
367
368
    /**
369
     * Set the attachments defaults.
370
     *
371
     * @param  array  $defaults
372
     * @return $this
373
     */
374 9
    public function setAttachmentDefaults($defaults)
375
    {
376 9
        if ($this->attachmentDefaults != ($defaults = (array) $defaults)) {
377 5
            $this->attachmentDefaults = $defaults;
378
379 5
            $this->setAttachments($this->attachments);
380
        }
381
382 9
        return $this;
383
    }
384
385
    /**
386
     * Get the attachments for the message.
387
     *
388
     * @return array
389
     */
390 11
    public function getAttachments()
391
    {
392 11
        return $this->attachments;
393
    }
394
395
    /**
396
     * Set the attachments for the message.
397
     *
398
     * @param  mixed  $attachments
399
     * @return $this
400
     */
401 6
    public function setAttachments($attachments)
402
    {
403 6
        $this->removeAttachments();
404
405 6
        if (is_array($attachments)) {
406 6
            foreach ($attachments as $attachment) {
407 3
                $this->addAttachment($attachment);
408
            }
409
        }
410
411 6
        return $this;
412
    }
413
414
    /**
415
     * Set the attachments for the message.
416
     *
417
     * @param  mixed  $attachments
418
     * @return $this
419
     */
420 1
    public function attachments($attachments)
421
    {
422 1
        return $this->setAttachments($attachments);
423
    }
424
425
    /**
426
     * Add an attachment to the message.
427
     *
428
     * The parameter can be an payload array that contains all of attachment's fields.
429
     * The parameters can also be attachment's fields that in order of
430
     * text, title, images and color. Except the text, other parameters
431
     * can be ignored.
432
     *
433
     * @param  mixed  $attachment
434
     * @return $this
435
     */
436 10
    public function addAttachment($attachment)
437
    {
438 10
        if (! is_array($attachment)) {
439 9
            $attachment = call_user_func_array([$this, 'getAttachmentPayload'], func_get_args());
440
        } elseif (
441 4
            ! empty($attachment['images']) &&
442 4
            $images = $this->getImagesPayload($attachment['images'])
443
        ) {
444 1
            $attachment['images'] = $images;
445
        }
446
447 10
        if (! empty($attachment)) {
448 10
            $attachment += $this->attachmentDefaults;
449
450 10
            $this->attachments[] = $attachment;
451
        }
452
453 10
        return $this;
454
    }
455
456
    /**
457
     * Get payload for an attachment.
458
     *
459
     * @param  mixed  $text
460
     * @param  mixed  $title
461
     * @param  mixed  $images
462
     * @param  mixed  $color
463
     * @return array
464
     */
465 9
    protected function getAttachmentPayload($text = null, $title = null, $images = null, $color = null)
466
    {
467 9
        $attachment = [];
468
469 9
        if ($text) {
470 9
            $attachment['text'] = $this->stringValue($text);
471
        }
472
473 9
        if ($title) {
474 5
            $attachment['title'] = $this->stringValue($title);
475
        }
476
477 9
        if ($images = $this->getImagesPayload($images)) {
478 4
            $attachment['images'] = $images;
479
        }
480
481 9
        if ($color) {
482 3
            $attachment['color'] = (string) $color;
483
        }
484
485 9
        return $attachment;
486
    }
487
488
    /**
489
     * Get payload for images.
490
     *
491
     * @param  mixed  $value
492
     * @return array
493
     */
494 9
    protected function getImagesPayload($value)
495
    {
496 9
        $images = [];
497
498 9
        foreach ((array) $value as $img) {
499 4
            if (! empty($img['url'])) {
500
                $img = $img['url'];
501
            }
502
503 4
            if (is_string($img) && ! empty($img)) {
504 4
                $images[] = ['url' => $img];
505
            }
506
        }
507
508 9
        return $images;
509
    }
510
511
    /**
512
     * Convert any type to string.
513
     *
514
     * @param  mixed  $value
515
     * @param  int  $jsonOptions
516
     * @return string
517
     */
518 9
    protected function stringValue($value, $jsonOptions = JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
519
    {
520 9
        if (is_string($value)) {
521 9
            return $value;
522
        }
523
524 1
        if (method_exists($value, '__toString')) {
525
            return (string) $value;
526
        }
527
528 1
        if (method_exists($value, 'toArray')) {
529
            $value = $value->toArray();
530
        }
531
532 1
        return json_encode($value, $jsonOptions);
533
    }
534
535
    /**
536
     * Add an attachment to the message.
537
     * It alias to `addAttachment`.
538
     *
539
     * @return $this
540
     */
541 6
    public function add()
542
    {
543 6
        return call_user_func_array([$this, 'addAttachment'], func_get_args());
544
    }
545
546
    /**
547
     * Add an image attachment to the message.
548
     *
549
     * @param  string|string[]  $image
550
     * @param  string  $desc
551
     * @param  string  $title
552
     * @return $this
553
     */
554 1
    public function addImage($image, $desc = null, $title = null)
555
    {
556 1
        return $this->addAttachment($desc, $title, $image);
557
    }
558
559
    /**
560
     * Remove attachment[s] for the message.
561
     *
562
     * @return $this
563
     */
564 7
    public function removeAttachments()
565
    {
566 7
        if (func_num_args() > 0) {
567 1
            $indices = is_array(func_get_arg(0)) ? func_get_arg(0) : func_get_args();
568
569 1
            foreach ($indices as $index) {
570 1
                unset($this->attachments[$index]);
571
            }
572
573 1
            $this->attachments = array_values($this->attachments);
574
        } else {
575 7
            $this->attachments = [];
576
        }
577
578 7
        return $this;
579
    }
580
581
    /**
582
     * Remove attachment[s] for the message.
583
     * It alias to `removeAttachments`.
584
     *
585
     * @return $this
586
     */
587 1
    public function remove()
588
    {
589 1
        return call_user_func_array([$this, 'removeAttachments'], func_get_args());
590
    }
591
592
    /**
593
     * Configure message defaults.
594
     *
595
     * @param  array  $defaults
596
     * @param  bool  $force
597
     * @return $this
598
     */
599 7
    public function configureDefaults(array $defaults, $force = false)
600
    {
601 7
        if ($force || empty($this->toArray())) {
602 7
            $attachmentDefaults = $this->attachmentDefaults;
603
604 7
            foreach (MessageDefaults::allKeys() as $key) {
605 7
                if (isset($defaults[$key]) && ! is_null($value = $defaults[$key])) {
606 4
                    if (strpos($key, 'attachment_') !== false) {
607 3
                        if ($key = substr($key, strlen('attachment_'))) {
608 3
                            $attachmentDefaults[$key] = $value;
609
                        }
610
                    } else {
611 7
                        $this->fillDefaults($key, $value);
612
                    }
613
                }
614
            }
615
616 7
            $this->setAttachmentDefaults($attachmentDefaults);
617
        }
618
619 7
        return $this;
620
    }
621
622
    /**
623
     * Fill with message defaults.
624
     *
625
     * @param  string  $key
626
     * @param  mixed  $value
627
     * @return void
628
     */
629 4
    protected function fillDefaults($key, $value)
630
    {
631
        if (
632 4
            ($key === MessageDefaults::USER || $key === MessageDefaults::CHANNEL) &&
633 4
            (! is_null($this->getTarget()))
634
        ) {
635 2
            return;
636
        }
637
638 4
        if ($suffix = $this->studlyCase($key)) {
639 4
            $getMethod = 'get'.$suffix;
640 4
            $setMethod = 'set'.$suffix;
641
            if (
642 4
                method_exists($this, $getMethod) &&
643 4
                is_null($this->{$getMethod}()) &&
644 4
                method_exists($this, $setMethod)
645
            ) {
646 4
                $this->{$setMethod}($value);
647
            }
648
        }
649 4
    }
650
651
    /**
652
     * Convert a string to studly caps case.
653
     *
654
     * @param  string  $string
655
     * @return string
656
     */
657 4
    protected function studlyCase($string)
658
    {
659 4
        return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $string)));
660
    }
661
662
    /**
663
     * Conveniently set message content.
664
     *
665
     * The parameters may be:
666
     * `($text, $markdown, $notification)`
667
     * or `($text, $attachment_text, $attachment_title, $attachment_images, $attachment_color)`.
668
     *
669
     * @return $this
670
     */
671 3
    public function content()
672
    {
673 3
        $arguments = func_get_args();
674 3
        $count = count($arguments);
675
676 3
        if ($count > 0) {
677 3
            $this->setText($arguments[0]);
678
        }
679
680 3
        if ($count > 1) {
681 3
            if (is_bool($arguments[1])) {
682 3
                $this->setMarkdown($arguments[1]);
683
684 3
                if ($count > 2) {
685 3
                    $this->setNotification($arguments[2]);
686
                }
687
            } else {
688 2
                call_user_func_array([$this, 'addAttachment'], array_slice($arguments, 1));
689
            }
690
        }
691
692 3
        return $this;
693
    }
694
695
    /**
696
     * Convert the message to an array.
697
     *
698
     * @return array
699
     */
700 7
    public function toArray()
701
    {
702 7
        return array_filter(
703
            [
704 7
                'text' => $this->getText(),
705 7
                'notification' => $this->getNotification(),
706 7
                'markdown' => $this->getMarkdown(),
707 7
                'channel' => $this->getChannel(),
708 7
                'user' => $this->getUser(),
709 7
                'attachments' => $this->getAttachments(),
710
            ],
711
            function ($value, $key) {
712
                return ! (
713 7
                    is_null($value) ||
714 7
                    ($key === 'markdown' && $value === true) ||
715 7
                    (is_array($value) && empty($value))
716
                );
717 7
            },
718 7
            ARRAY_FILTER_USE_BOTH
719
        );
720
    }
721
722
    /**
723
     * Convert the message to JSON string.
724
     *
725
     * @param  int  $options
726
     * @return string
727
     */
728 1
    public function toJson($options = 0)
729
    {
730 1
        return json_encode($this->jsonSerialize(), $options);
731
    }
732
733
    /**
734
     * Serializes the object to a value that can be serialized natively by json_encode().
735
     *
736
     * @return array
737
     */
738 1
    public function jsonSerialize()
739
    {
740 1
        return $this->toArray();
741
    }
742
743
    /**
744
     * Send the message.
745
     *
746
     * The parameters accepts the same format of `content` method.
747
     *
748
     * @return bool
749
     */
750 2
    public function send()
751
    {
752 2
        if (! $this->client) {
753 1
            return false;
754
        }
755
756
        if (
757 2
            1 == func_num_args() &&
758 2
            (is_array(func_get_arg(0)) || is_object(func_get_arg(0)))
759
        ) {
760 1
            return $this->client->sendMessage(func_get_arg(0));
761
        }
762
763 2
        if (func_num_args() > 0) {
764 2
            call_user_func_array([$this, 'content'], func_get_args());
765
        }
766
767 2
        return $this->client->sendMessage($this);
768
    }
769
770
    /**
771
     * Send the message to the given target.
772
     *
773
     * @param  mixed  $target
774
     * @return bool
775
     */
776 1
    public function sendTo($target)
777
    {
778 1
        $this->to($target);
779
780 1
        return call_user_func_array([$this, 'send'], array_slice(func_get_args(), 1));
781
    }
782
783
    /**
784
     * Convert the message to its string representation.
785
     *
786
     * @return string
787
     */
788 1
    public function __toString()
789
    {
790 1
        return $this->toJson();
791
    }
792
}
793