Failed Conditions
Push — master ( 5c622e...91b182 )
by Adrien
07:46
created

Mailer::createMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 22
nc 1
nop 5
dl 0
loc 30
ccs 23
cts 23
cp 1
crap 1
rs 9.568
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Service;
6
7
use Application\DBAL\Types\MessageTypeType;
8
use Application\Model\Bookable;
9
use Application\Model\Message;
10
use Application\Model\User;
11
use Cake\Chronos\Chronos;
12
use Doctrine\ORM\EntityManager;
13
use Zend\Mail;
14
use Zend\Mail\Transport\TransportInterface;
15
use Zend\Mime\Message as MimeMessage;
16
use Zend\Mime\Mime;
17
use Zend\Mime\Part as MimePart;
18
use Zend\View\Model\ViewModel;
19
use Zend\View\Renderer\RendererInterface;
20
21
class Mailer
22
{
23
    /**
24
     * @var EntityManager
25
     */
26
    private $entityManager;
27
28
    /**
29
     * @var TransportInterface
30
     */
31
    private $transport;
32
33
    /**
34
     * @var string
35
     */
36
    private $hostname;
37
38
    /**
39
     * @var null|string
40
     */
41
    private $emailOverride;
42
43
    /**
44
     * @var RendererInterface
45
     */
46
    private $viewRenderer;
47
48
    /**
49
     * @var string
50
     */
51
    private $fromEmail;
52
53
    /**
54
     * @var string
55
     */
56
    private $phpPath;
57
58 7
    public function __construct(EntityManager $entityManager, TransportInterface $transport, RendererInterface $viewRenderer, string $hostname, ?string $emailOverride, string $fromEmail, string $phpPath)
59
    {
60 7
        $this->entityManager = $entityManager;
61 7
        $this->transport = $transport;
62 7
        $this->hostname = $hostname;
63 7
        $this->emailOverride = $emailOverride;
64 7
        $this->viewRenderer = $viewRenderer;
65 7
        $this->fromEmail = $fromEmail;
66 7
        $this->phpPath = $phpPath;
67 7
    }
68
69 2
    public function queueRegister(User $user): Message
70
    {
71 2
        $subject = 'Demande de création de compte au Club Nautique Ichtus';
72
        $mailParams = [
73 2
            'token' => $user->createToken(),
74
        ];
75
76 2
        $message = $this->createMessage($user, $user->getEmail(), $subject, MessageTypeType::REGISTER, $mailParams);
0 ignored issues
show
Bug introduced by
It seems like $user->getEmail() can also be of type null; however, parameter $email of Application\Service\Mailer::createMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

76
        $message = $this->createMessage($user, /** @scrutinizer ignore-type */ $user->getEmail(), $subject, MessageTypeType::REGISTER, $mailParams);
Loading history...
77
78 2
        return $message;
79
    }
80
81 2
    public function queueUnregister(User $admin, User $unregisteredUser): Message
82
    {
83 2
        $subject = 'Démission';
84
        $mailParams = [
85 2
            'unregisteredUser' => $unregisteredUser,
86
        ];
87
88 2
        $message = $this->createMessage($admin, $admin->getEmail(), $subject, MessageTypeType::UNREGISTER, $mailParams);
0 ignored issues
show
Bug introduced by
It seems like $admin->getEmail() can also be of type null; however, parameter $email of Application\Service\Mailer::createMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

88
        $message = $this->createMessage($admin, /** @scrutinizer ignore-type */ $admin->getEmail(), $subject, MessageTypeType::UNREGISTER, $mailParams);
Loading history...
89
90 2
        return $message;
91
    }
92
93
    /**
94
     * Queue a reset password email to specified user
95
     *
96
     * @param User $user The user for which a password reset will be done
97
     * @param string $email the address to send the email to. Might be different than the user's email
98
     *
99
     * @return Message
100
     */
101 3
    public function queueResetPassword(User $user, string $email): Message
102
    {
103 3
        $subject = 'Demande de modification de mot de passe';
104
        $mailParams = [
105 3
            'token' => $user->createToken(),
106
        ];
107
108 3
        $message = $this->createMessage($user, $email, $subject, MessageTypeType::RESET_PASSWORD, $mailParams);
109
110 3
        return $message;
111
    }
112
113
    /**
114
     * @param User $user
115
     * @param Bookable[] $bookables
116
     *
117
     * @return Message
118
     */
119 2
    public function queueBalance(User $user, array $bookables): Message
120
    {
121 2
        $subject = 'Balance de compte';
122
        $mailParams = [
123 2
            'bookables' => $bookables,
124
        ];
125
126 2
        $message = $this->createMessage($user, $user->getEmail(), $subject, MessageTypeType::BALANCE, $mailParams);
0 ignored issues
show
Bug introduced by
It seems like $user->getEmail() can also be of type null; however, parameter $email of Application\Service\Mailer::createMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
        $message = $this->createMessage($user, /** @scrutinizer ignore-type */ $user->getEmail(), $subject, MessageTypeType::BALANCE, $mailParams);
Loading history...
127
128 2
        return $message;
129
    }
130
131
    /**
132
     * Create a message by rendering the template
133
     *
134
     * @param null|User $user
135
     * @param string $email
136
     * @param string $subject
137
     * @param string $type
138
     * @param array $mailParams
139
     *
140
     * @return Message
141
     */
142 9
    private function createMessage(?User $user, string $email, string $subject, string $type, array $mailParams): Message
143
    {
144
145
        // First render the view
146 9
        $serverUrl = 'https://' . $this->hostname;
147 9
        $model = new ViewModel($mailParams);
148 9
        $model->setTemplate(str_replace('_', '-', $type));
149 9
        $model->setVariable('email', $email);
150 9
        $model->setVariable('user', $user);
151 9
        $model->setVariable('serverUrl', $serverUrl);
152 9
        $partialContent = $this->viewRenderer->render($model);
153
154
        // Then inject it into layout
155 9
        $layoutModel = new ViewModel([$model->captureTo() => $partialContent]);
156 9
        $layoutModel->setTemplate('layout');
157 9
        $layoutModel->setVariable('subject', $subject);
158 9
        $layoutModel->setVariable('user', $user);
159 9
        $layoutModel->setVariable('serverUrl', $serverUrl);
160 9
        $layoutModel->setVariable('hostname', $this->hostname);
161 9
        $content = $this->viewRenderer->render($layoutModel);
162
163 9
        $message = new Message();
164 9
        $message->setType($type);
165 9
        $message->setRecipient($user);
166 9
        $message->setSubject($subject);
167 9
        $message->setBody($content);
168 9
        $message->setEmail($email);
169 9
        $this->entityManager->persist($message);
170
171 9
        return $message;
172
    }
173
174
    /**
175
     * Send a message asynchronously in a separate process.
176
     *
177
     * This should be the preferred way to send a message, unless if we are the cron.
178
     *
179
     * @param Message $message
180
     */
181 4
    public function sendMessageAsync(Message $message): void
182
    {
183
        // Be sure we have an ID before "forking" process
184 4
        if ($message->getId() === null) {
0 ignored issues
show
introduced by
The condition $message->getId() === null is always false.
Loading history...
185 4
            _em()->flush();
186
        }
187
188
        $args = [
189 4
            realpath('bin/send-message.php'),
190 4
            $message->getId(),
191
        ];
192
193 4
        $escapedArgs = array_map('escapeshellarg', $args);
194
195 4
        $cmd = escapeshellcmd($this->phpPath) . ' ' . implode(' ', $escapedArgs) . ' > /dev/null 2>&1 &';
196 4
        exec($cmd);
197 4
    }
198
199
    /**
200
     * Send a message
201
     *
202
     * @param Message $message
203
     */
204 1
    public function sendMessage(Message $message): void
205
    {
206 1
        $mailMessage = $this->modelMessageToMailMessage($message);
207
208 1
        $email = $message->getEmail();
209 1
        $overriddenBy = '';
210 1
        if ($this->emailOverride) {
211
            $email = $this->emailOverride;
212
            $overriddenBy = ' overridden by ' . $email;
213
        }
214
215 1
        $recipientName = $message->getRecipient() ? $message->getRecipient()->getName() : null;
216 1
        if ($email) {
217 1
            $mailMessage->addTo($email, $recipientName);
218 1
            $this->transport->send($mailMessage);
219
        }
220
221 1
        $message->setDateSent(new Chronos());
222 1
        $this->entityManager->flush();
223
224 1
        echo 'email sent to: ' . $message->getEmail() . "\t" . $overriddenBy . "\t" . $message->getSubject() . PHP_EOL;
225 1
    }
226
227
    /**
228
     * Convert our model message to a mail message
229
     *
230
     * @param Message $modelMessage
231
     *
232
     * @return Mail\Message
233
     */
234 1
    private function modelMessageToMailMessage(Message $modelMessage): Mail\Message
235
    {
236
        // set Mime type html
237 1
        $htmlPart = new MimePart($modelMessage->getBody());
238 1
        $htmlPart->type = Mime::TYPE_HTML;
239 1
        $body = new MimeMessage();
240 1
        $body->setParts([$htmlPart]);
241
242 1
        $mailMessage = new Mail\Message();
243 1
        $mailMessage->setSubject($modelMessage->getSubject());
244 1
        $mailMessage->setBody($body);
245 1
        $mailMessage->setFrom($this->fromEmail, 'Ichtus');
246
247 1
        return $mailMessage;
248
    }
249
250
    /**
251
     * Send all messages that are not sent yet
252
     */
253
    public function sendAllMessages(): void
254
    {
255
        $messages = $this->entityManager->getRepository(Message::class)->getAllMessageToSend();
256
        foreach ($messages as $message) {
257
            $this->sendMessage($message);
258
        }
259
    }
260
}
261