Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
84.87% covered (success)
84.87%
101 / 119
71.43% covered (success)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Google
84.87% covered (success)
84.87%
101 / 119
71.43% covered (success)
71.43%
5 / 7
57.31
0.00% covered (danger)
0.00%
0 / 1
 gmailService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMessages
74.24% covered (success)
74.24%
49 / 66
0.00% covered (danger)
0.00%
0 / 1
47.42
 getMessage
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 getAttachments
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
6
 getAttachment
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 markAsRead
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
 markAsUnread
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <dev@noladev.com>
8 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Mail\Client;
16
17use GuzzleHttp\Psr7\Request;
18use Pop\Http;
19use Pop\Mail\Api\AbstractGoogle;
20use Google\Service\Gmail;
21/**
22 * Google mail client class
23 *
24 * @category   Pop
25 * @package    Pop\Mail
26 * @author     Nick Sagona, III <dev@noladev.com>
27 * @copyright  Copyright (c) 2009-2027 NOLA Interactive, LLC.
28 * @license    https://www.popphp.org/license     New BSD License
29 * @version    5.0.0
30 */
31class Google extends AbstractGoogle implements HttpClientInterface
32{
33
34    /**
35     * Create the Gmail service object
36     *
37     * Exists as a seam so tests can substitute a mocked Gmail service instead
38     * of one backed by a real Google\Client - none of this class's public
39     * methods construct Gmail directly anymore.
40     *
41     * @return Gmail
42     */
43    protected function gmailService(): Gmail
44    {
45        return new Gmail($this->client);
46    }
47
48    /**
49     * Get messages
50     *
51     * @param  string $folder
52     * @param  array  $search
53     * @param  int    $limit
54     * @throws Exception|Http\Exception|Http\Client\Exception|Http\Client\Handler\Exception
55     * @return mixed
56     */
57    public function getMessages(string $folder = 'Inbox', array $search = [], int $limit = 10): mixed
58    {
59        if ($this->client === null) {
60            throw new Exception('Error: The client object has not been instantiated yet.');
61        }
62
63        $this->verifyToken();
64        $this->client->setAccessToken($this->token);
65
66        $messages      = [];
67        $options       = [];
68        $filterStrings = ['in:' . $folder];
69
70        if (!empty($limit)) {
71            $options['maxResults'] = $limit;
72        }
73
74        if (!empty($search)) {
75            foreach ($search as $key => $value) {
76                switch (strtolower($key)) {
77                    case 'unread':
78                        $filterStrings[] = "is:" . (($value) ? "unread" : "read");
79                        break;
80                    case 'sent after':
81                    case 'sent before':
82                    case 'sent older':
83                    case 'sent newer':
84                        $filterStrings[] = "in:" . $key . " " . date('m/d/Y', strtotime($value));
85                        break;
86                    default:
87                        $filterStrings[] = $key . ":" . $value;
88                }
89            }
90        }
91
92        $options['q'] = implode(' ', $filterStrings);
93
94        $gmail       = $this->gmailService();
95        $messageList = $gmail->users_messages->listUsersMessages($this->username, $options);
96        $batch       = $gmail->createBatch();
97
98        foreach ($messageList as $message) {
99            $batch->add(new Request('GET', 'https://gmail.googleapis.com/gmail/v1/users/' . $this->username . '/messages/' . $message->id));
100        }
101
102        $responses = $batch->execute();
103
104        foreach ($responses as $response) {
105            $responseBody = ($response->getStatusCode() == 200) ? json_decode((string)$response->getBody(), true) : [];
106            if (isset($responseBody['id'])) {
107                $hasAttachments = false;
108                if (isset($responseBody['payload'])) {
109                    if (isset($responseBody['payload']['headers'])) {
110                        foreach ($responseBody['payload']['headers'] as $header) {
111                            switch ($header['name']) {
112                                case 'Subject':
113                                    $responseBody['Subject'] = $header['value'];
114                                    break;
115                                case 'Date':
116                                    $responseBody['Date'] = $header['value'];
117                                    break;
118                                case 'To':
119                                    $responseBody['To'] = $header['value'];
120                                    break;
121                                case 'From':
122                                    $responseBody['From'] = $header['value'];
123                                    break;
124                                case 'Reply-To':
125                                    $responseBody['Reply-To'] = $header['value'];
126                                    break;
127                            }
128                        }
129                    }
130
131                    if (isset($responseBody['payload']['parts'])) {
132                        foreach ($responseBody['payload']['parts'] as $part) {
133                            if (!empty($part['filename'])) {
134                                $hasAttachments = true;
135                                break;
136                            } else if (isset($part['parts'])) {
137                                foreach ($part['parts'] as $p) {
138                                    if (!empty($p['filename'])) {
139                                        $hasAttachments = true;
140                                        break;
141                                    }
142                                }
143                            }
144                        }
145                    }
146                }
147
148                $responseBody['attachments'] = $hasAttachments;
149                $responseBody['unread']      = (isset($responseBody['labelIds']) && in_array('UNREAD', $responseBody['labelIds']));
150
151                $messages[$responseBody['id']] = $responseBody;
152            }
153
154        }
155
156        return $messages;
157    }
158
159    /**
160     * Get messages
161     *
162     * @param  string $messageId
163     * @param  bool   $raw
164     * @throws Exception|Http\Exception|Http\Client\Exception|Http\Client\Handler\Exception
165     * @return mixed
166     */
167    public function getMessage(string $messageId, bool $raw = false): mixed
168    {
169        if ($this->client === null) {
170            throw new Exception('Error: The client object has not been instantiated yet.');
171        }
172
173        $this->verifyToken();
174        $this->client->setAccessToken($this->token);
175
176        $gmail   = $this->gmailService();
177        $message = $gmail->users_messages->get($this->username, $messageId, ['format' => (($raw) ? 'raw' : 'full')]);
178
179        return (($raw) && isset($message['raw'])) ? base64_decode(strtr($message['raw'], '-_.', '+/=')) : $message;
180    }
181
182    /**
183     * Get message attachments
184     *
185     * @param  string $messageId
186     * @param  string $folder
187     * @throws Exception|Http\Exception|Http\Client\Exception|Http\Client\Handler\Exception
188     * @return mixed
189     */
190    public function getAttachments(string $messageId, string $folder = 'Inbox'): mixed
191    {
192        if ($this->client === null) {
193            throw new Exception('Error: The client object has not been instantiated yet.');
194        }
195
196        $this->verifyToken();
197        $this->client->setAccessToken($this->token);
198
199        $gmail   = $this->gmailService();
200        $message = $gmail->users_messages->get($this->username, $messageId, ['format' => 'full']);
201
202        $attachments = [];
203        $payload     = $message->getPayload();
204        $parts       = $payload->getParts();
205
206        foreach ($parts as $part) {
207            if (!empty($part->getFilename())) {
208                $attachments[$part->getBody()->getAttachmentId()] = [
209                    'id'       => $part->getBody()->getAttachmentId(),
210                    'filename' => $part->getFilename(),
211                    'mimeType' => $part->getMimeType(),
212                    'size'     => $part->getBody()->getSize()
213                ];
214            } else {
215                foreach ($part->getParts() as $p) {
216                    if (!empty($p->getFilename())) {
217                        $attachments[$p->getBody()->getAttachmentId()] = [
218                            'id'       => $p->getBody()->getAttachmentId(),
219                            'filename' => $p->getFilename(),
220                            'mimeType' => $p->getMimeType(),
221                            'size'     => $p->getBody()->getSize()
222                        ];
223                    }
224                }
225            }
226        }
227
228        return $attachments;
229    }
230
231    /**
232     * Get message attachment
233     *
234     * @param  string $messageId
235     * @param  string $attachmentId
236     * @param  string $folder
237     * @throws Exception|Http\Exception|Http\Client\Exception|Http\Client\Handler\Exception
238     * @return mixed
239     */
240    public function getAttachment(string $messageId, string $attachmentId, string $folder = 'Inbox'): mixed
241    {
242        if ($this->client === null) {
243            throw new Exception('Error: The client object has not been instantiated yet.');
244        }
245
246        $this->verifyToken();
247        $this->client->setAccessToken($this->token);
248
249        $gmail      = $this->gmailService();
250        $attachment =  $gmail->users_messages_attachments->get($this->username, $messageId, $attachmentId);
251
252        return base64_decode(strtr($attachment->getData(), '-_', '+/'));
253    }
254
255    /**
256     * Mark message as read
257     *
258     * @param  string $messageId
259     * @param  bool   $isRead
260     * @throws Exception|Http\Exception|Http\Client\Exception|Http\Client\Handler\Exception
261     * @return Google
262     */
263    public function markAsRead(string $messageId, bool $isRead = true): Google
264    {
265        if ($this->client === null) {
266            throw new Exception('Error: The client object has not been instantiated yet.');
267        }
268
269        $this->verifyToken();
270        $this->client->setAccessToken($this->token);
271
272        $messageRequest = new Gmail\ModifyMessageRequest();
273
274        if ($isRead) {
275            $messageRequest->setRemoveLabelIds(['UNREAD']);
276        } else {
277            $messageRequest->setAddLabelIds(['UNREAD']);
278        }
279
280        $gmail      = $this->gmailService();
281        $gmail->users_messages->modify($this->username, $messageId, $messageRequest);
282
283        return $this;
284    }
285
286    /**
287     * Mark message as unread
288     *
289     * @param  string $messageId
290     * @throws Exception|Http\Exception|Http\Client\Exception|Http\Client\Handler\Exception
291     * @return Google
292     */
293    public function markAsUnread(string $messageId): Google
294    {
295        return $this->markAsRead($messageId, false);
296    }
297    
298}