Completed
Push — master ( 3ff19d...4e3fcc )
by Konstantinos
04:38
created

EventSubscriber::notify()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 2
crap 1
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 string        $from      The FROM e-mail address
48
     * @param string        $siteTitle The title of the website
49
     */
50 1
    public function __construct(\Swift_Mailer $mailer, $from, $siteTitle)
51
    {
52 1
        $this->mailer = $mailer;
53 1
        $this->twig   = \Service::getTemplateEngine();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Service::getTemplateEngine() of type object<Twig_Environment> is incompatible with the declared type object<BZIon\Event\Twig_Environment> of property $twig.

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...
54 1
        $this->from   = $from;
55 1
        $this->siteTitle = $siteTitle;
56 1
    }
57
58
    /**
59
     * Returns all the events that this subscriber handles, and which method
60
     * handles each one
61
     *
62
     * @return array
63
     */
64 1
    public static function getSubscribedEvents()
65
    {
66
        return array(
67 1
            'conversation.abandon' => 'conversation',
68
            'conversation.join'    => 'conversation',
69
            'conversation.kick'    => 'conversation',
70
            'conversation.rename'  => 'conversation',
71
            'message.new'          => 'onNewMessage',
72
            'notification.new'     => 'onNewNotification',
73
            'team.delete'          => 'notify',
74
            'team.abandon'         => 'notify',
75
            'team.kick'            => 'notify',
76
            'team.join'            => 'notify',
77
            'team.invite'          => 'notify',
78
            'team.leader_change'   => 'notify',
79
            'welcome'              => 'notify',
80
        );
81
    }
82
83
    /**
84
     * Called every time a new message is sent
85
     * @param NewMessageEvent $event The event
86
     */
87 1
    public function onNewMessage(NewMessageEvent $event)
88
    {
89
        // Get a list of everyone who can see the message so we can notify them -
90
        // the sender of the message is excluded
91 1
        $conversation = $event->getMessage()->getConversation();
92 1
        $author = $event->getMessage()->getAuthor()->getId();
93 1
        $recipients = $conversation->getWaitingForEmailIDs($author);
94
95
        // The websocket will handle emails if it is enabled
96 1
        if (!WebSocketAdapter::isEnabled()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \BZIon\NotificationAdapt...ketAdapter::isEnabled() of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
97 1
            $this->sendEmails(
98 1
                'New message received',
99
                $recipients,
100 1
                'message',
101 1
                array('message' => $event->getMessage())
102
            );
103
        }
104
105 1
        $event->getMessage()->getConversation()->markUnread($author);
106 1
        \Notification::pushEvent('message', array(
107 1
            'message'    => $event->getMessage(),
108 1
            'recipients' => $recipients
109
        ));
110 1
    }
111
112
    /**
113
     * Called every time a new notification is sent
114
     * @param NewNotificationEvent $event The event
115
     */
116 1
    public function onNewNotification(NewNotificationEvent $event)
117
    {
118 1
        if ($event->getNotification()->getReceiver()->canReceive('notification')) {
119
            if (!WebSocketAdapter::isEnabled()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression \BZIon\NotificationAdapt...ketAdapter::isEnabled() of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
120
                $this->emailNotification($event->getNotification());
121
            }
122
        }
123
124
        // Show the notification to the currently logged in players in real time
125 1
        $event->getNotification()->push();
126 1
    }
127
128
    /**
129
     * Called when an event needs to notify a user
130
     *
131
     * @param Event  $event The event
132
     * @param string $name  The name of the event
133
     */
134 1
    public function notify(Event $event, $name)
135
    {
136 1
        $event->notify($name);
137 1
    }
138
139
    /**
140
     * Called when a conversation event needs to be stored in the database
141
     *
142
     * @param Event  $event The event
143
     * @param string $name  The name of the event
144
     */
145
    public function conversation(Event $event, $name)
146
    {
147
        \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...
148
    }
149
150
    /**
151
     * Notify the user about a notification by e-
152
     * @param \Notification $notification The notification that will be mailed
153
     */
154
    public function emailNotification(\Notification $notification)
155
    {
156
        $text = \Service::getTemplateEngine()->render(
157
            'Notification/item.html.twig',
158
            array('notification' => $notification)
159
        );
160
161
        return $this->sendEmails(
162
            trim(strip_tags($text)),
163
            array($notification->getReceiver()->getId()),
164
            'notification',
165
            array('notification' => $notification, 'text' => $text)
166
        );
167
    }
168
169
    /**
170
     * Send emails to a list of recipients
171
     *
172
     * @param  string $subject    The subject of the messages
173
     * @param  int[]  $recipients The IDs of the players to which the messages will be sent
174
     * @param  string $template   The twig template name for the e-mail body
175
     * @param  array  $params     Any extra parameters to pass to twig
176
     * @return void
177
     */
178 1
    public function sendEmails($subject, $recipients, $template, $params = array())
179
    {
180 1
        if (!$this->from) {
181
            return;
182
        }
183
184 1
        $message = \Swift_Message::newInstance()
185 1
            ->setSubject($subject)
186 1
            ->setFrom(array($this->from => $this->siteTitle))
187 1
            ->setBody($this->twig->render("Email/$template.txt.twig",  $params))
188 1
            ->addPart($this->twig->render("Email/$template.html.twig", $params), 'text/html');
189
190 1
        foreach ($recipients as $recipient) {
191
            $recipient = \Player::get($recipient);
192
193
            if (!$recipient->isVerified()) {
194
                continue;
195
            }
196
197
            $message->setTo($recipient->getEmailAddress());
198
            $this->mailer->send($message);
199
        }
200 1
    }
201
}
202