Completed
Push — feature/sass-standard ( a5bd48...aade0e )
by Vladimir
14:40 queued 11:12
created

EventSubscriber::onNewMessage()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 2.0011

Importance

Changes 0
Metric Value
dl 0
loc 24
ccs 14
cts 15
cp 0.9333
rs 8.9713
c 0
b 0
f 0
cc 2
eloc 14
nc 2
nop 1
crap 2.0011
1
<?php
2
/**
3
 * This file contains a class that responds to events
4
 *
5
 * @license    https://github.com/allejo/bzion/blob/master/LICENSE.md GNU General Public License Version 3
6
 */
7
8
namespace BZIon\Event;
9
10
use BZIon\NotificationAdapter\WebSocketAdapter;
11
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12
13
/**
14
 * An event subscriber for bzion events
15
 */
16
class EventSubscriber implements EventSubscriberInterface
17
{
18
    /**
19
     * @var \Swift_Mailer
20
     */
21
    protected $mailer;
22
23
    /**
24
     * @var \Twig_Environment
25
     */
26
    protected $twig;
27
28
    /**
29
     * The FROM e-mail address
30
     * @var string
31
     */
32
    protected $from;
33
34
    /**
35
     * The title of the website
36
     * @var string
37
     */
38
    protected $siteTitle;
39
40
    /**
41
     * Constructor
42
     *
43
     * You will probably not need to instantiate an object of this class,
44
     * Symfony already does the hard work for us
45
     *
46
     * @param \Swift_Mailer $mailer    The mailer
47
     * @param \Twig_Environment $twig  The twig environment
48
     * @param string        $from      The FROM e-mail address
49
     * @param string        $siteTitle The title of the website
50
     */
51 1
    public function __construct(\Swift_Mailer $mailer, $twig, $from, $siteTitle)
52
    {
53 1
        $this->mailer = $mailer;
54 1
        $this->twig   = $twig;
55 1
        $this->from   = $from;
56 1
        $this->siteTitle = $siteTitle;
57 1
    }
58
59
    /**
60
     * Returns all the events that this subscriber handles, and which method
61
     * handles each one
62
     *
63
     * @return array
64
     */
65 1
    public static function getSubscribedEvents()
66
    {
67
        return array(
68 1
            'conversation.abandon' => 'conversation',
69
            'conversation.join'    => 'conversation',
70
            'conversation.kick'    => 'conversation',
71
            'conversation.rename'  => 'conversation',
72
            'message.new'          => 'onNewMessage',
73
            'notification.new'     => 'onNewNotification',
74
            'team.delete'          => 'notify',
75
            'team.abandon'         => 'notify',
76
            'team.kick'            => 'notify',
77
            'team.join'            => 'notify',
78
            'team.invite'          => 'notify',
79
            'team.leader_change'   => 'notify',
80
            'welcome'              => 'notify',
81
        );
82
    }
83
84
    /**
85
     * Called every time a new message is sent
86
     * @param NewMessageEvent $event The event
87
     */
88 1
    public function onNewMessage(NewMessageEvent $event)
89
    {
90
        // Get a list of everyone who can see the message so we can notify them -
91
        // the sender of the message is excluded
92 1
        $conversation = $event->getMessage()->getConversation();
93 1
        $author = $event->getMessage()->getAuthor()->getId();
94 1
        $recipients = $conversation->getWaitingForEmailIDs($author, !$event->isFirst());
95
96
        // The websocket will handle emails if it is enabled
97 1
        if (!WebSocketAdapter::isEnabled()) {
98 1
            $this->sendEmails(
99 1
                'New message received',
100
                $recipients,
101 1
                'message',
102 1
                array('message' => $event->getMessage())
103
            );
104
        }
105
106 1
        $event->getMessage()->getConversation()->markUnread($author);
107 1
        \Notification::pushEvent('message', array(
108 1
            'message'    => $event->getMessage(),
109 1
            'recipients' => $recipients
110
        ));
111 1
    }
112
113
    /**
114
     * Called every time a new notification is sent
115
     * @param NewNotificationEvent $event The event
116
     */
117
    public function onNewNotification(NewNotificationEvent $event)
118
    {
119
        if ($event->getNotification()->getReceiver()->canReceive('notification')) {
120
            if (!WebSocketAdapter::isEnabled()) {
121
                $this->emailNotification($event->getNotification());
122
            }
123
        }
124
125
        // Show the notification to the currently logged in players in real time
126
        $event->getNotification()->push();
127
    }
128
129
    /**
130
     * Called when an event needs to notify a user
131
     *
132
     * @param Event  $event The event
133
     * @param string $name  The name of the event
134
     */
135
    public function notify(Event $event, $name)
136
    {
137
        $event->notify($name);
138
    }
139
140
    /**
141
     * Called when a conversation event needs to be stored in the database
142
     *
143
     * @param Event  $event The event
144
     * @param string $name  The name of the event
145
     */
146
    public function conversation(Event $event, $name)
147
    {
148
        \ConversationEvent::storeEvent($event->getConversation()->getId(), $event, $name);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class BZIon\Event\Event as the method getConversation() does only exist in the following sub-classes of BZIon\Event\Event: BZIon\Event\ConversationAbandonEvent, BZIon\Event\ConversationJoinEvent, BZIon\Event\ConversationKickEvent, BZIon\Event\ConversationRenameEvent. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
149
    }
150
151
    /**
152
     * Notify the user about a notification by e-
153
     * @param \Notification $notification The notification that will be mailed
154
     */
155
    public function emailNotification(\Notification $notification)
156
    {
157
        $text = $this->twig->render(
158
            'Notification/item.html.twig',
159
            array('notification' => $notification)
160
        );
161
162
        return $this->sendEmails(
163
            trim(strip_tags($text)),
164
            array($notification->getReceiver()->getId()),
165
            'notification',
166
            array('notification' => $notification, 'text' => $text)
167
        );
168
    }
169
170
    /**
171
     * Send emails to a list of recipients
172
     *
173
     * @param  string $subject    The subject of the messages
174
     * @param  int[]  $recipients The IDs of the players to which the messages will be sent
175
     * @param  string $template   The twig template name for the e-mail body
176
     * @param  array  $params     Any extra parameters to pass to twig
177
     * @return void
178
     */
179 1
    public function sendEmails($subject, $recipients, $template, $params = array())
180
    {
181 1
        if (!$this->from) {
182
            return;
183
        }
184
185 1
        $message = \Swift_Message::newInstance()
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Swift_Mime_MimePart as the method addPart() does only exist in the following sub-classes of Swift_Mime_MimePart: Swift_Message, Swift_SignedMessage. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
186 1
            ->setSubject($subject)
187 1
            ->setFrom(array($this->from => $this->siteTitle))
188 1
            ->setBody($this->twig->render("Email/$template.txt.twig",  $params))
189 1
            ->addPart($this->twig->render("Email/$template.html.twig", $params), 'text/html');
190
191 1
        foreach ($recipients as $recipient) {
192
            $recipient = \Player::get($recipient);
193
194
            if (!$recipient->isVerified()) {
195
                continue;
196
            }
197
198
            $message->setTo($recipient->getEmailAddress());
199
            $this->mailer->send($message);
200
        }
201 1
    }
202
}
203