Completed
Branch v1 (ffe92e)
by Julián
02:44
created

ApnsAdapter::send()   B

Complexity

Conditions 4
Paths 7

Size

Total Lines 28
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 28
rs 8.5806
cc 4
eloc 15
nc 7
nop 1
1
<?php
2
/**
3
 * Push notification services abstraction (http://github.com/juliangut/tify)
4
 *
5
 * @link https://github.com/juliangut/tify for the canonical source repository
6
 *
7
 * @license https://github.com/juliangut/tify/blob/master/LICENSE
8
 */
9
10
namespace Jgut\Tify\Adapter\Apns;
11
12
use Jgut\Tify\Adapter\AbstractAdapter;
13
use Jgut\Tify\Adapter\FeedbackAdapter;
14
use Jgut\Tify\Adapter\SendAdapter;
15
use Jgut\Tify\Exception\AdapterException;
16
use Jgut\Tify\Exception\NotificationException;
17
use Jgut\Tify\Notification;
18
use Jgut\Tify\Receiver\ApnsReceiver;
19
use Jgut\Tify\Result;
20
use ZendService\Apple\Exception\RuntimeException as ApnsRuntimeException;
21
22
/**
23
 * Class ApnsAdapter
24
 */
25
class ApnsAdapter extends AbstractAdapter implements SendAdapter, FeedbackAdapter
26
{
27
    const RESULT_OK = 0;
28
29
    /**
30
     * Status codes mapping.
31
     *
32
     * @var array
33
     */
34
    protected static $statusCodes = [
35
        0 => 'OK',
36
        1 => 'Processing Error',
37
        2 => 'Missing Device Token',
38
        3 => 'Missing Topic',
39
        4 => 'Missing Payload',
40
        5 => 'Invalid Token Size',
41
        6 => 'Invalid Topic Size',
42
        7 => 'Invalid Payload Size',
43
        8 => 'Invalid Token',
44
        10 => 'Shutdown',
45
        255 => 'Unknown Error',
46
    ];
47
48
    /**
49
     * APNS service builder.
50
     *
51
     * @var \Jgut\Tify\Adapter\Apns\ApnsBuilder
52
     */
53
    protected $builder;
54
55
    /**
56
     * @var \ZendService\Apple\Apns\Client\Message
57
     */
58
    protected $pushClient;
59
60
    /**
61
     * @var \ZendService\Apple\Apns\Client\Feedback
62
     */
63
    protected $feedbackClient;
64
65
    /**
66
     * @param array                               $parameters
67
     * @param bool                                $sandbox
68
     * @param \Jgut\Tify\Adapter\Apns\ApnsBuilder $builder
0 ignored issues
show
Documentation introduced by
Should the type for parameter $builder not be null|ApnsBuilder?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
69
     *
70
     * @throws \Jgut\Tify\Exception\AdapterException
71
     */
72
    public function __construct(array $parameters = [], $sandbox = false, ApnsBuilder $builder = null)
73
    {
74
        parent::__construct($parameters, $sandbox);
75
76
        $certificatePath = $this->getParameter('certificate');
77
78
        if ($certificatePath === null || !file_exists($certificatePath) || !is_readable($certificatePath)) {
79
            throw new AdapterException(
80
                sprintf('Certificate file "%s" does not exist or is not readable', $certificatePath)
81
            );
82
        }
83
84
        // @codeCoverageIgnoreStart
85
        if ($builder === null) {
86
            $builder = new ApnsBuilder;
87
        }
88
        // @codeCoverageIgnoreEnd
89
        $this->builder = $builder;
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     *
95
     * @throws \InvalidArgumentException
96
     * @throws \Jgut\Tify\Exception\AdapterException
97
     * @throws \RuntimeException
98
     */
99
    public function send(Notification $notification)
100
    {
101
        $client = $this->getPushClient();
102
103
        /* @var \ZendService\Apple\Apns\Message $message */
104
        foreach ($this->getPushMessages($notification) as $message) {
105
            $result = new Result($message->getToken());
106
107
            try {
108
                $pushResponse = $client->send($message);
109
110
                if ($pushResponse->getCode() !== static::RESULT_OK) {
111
                    $result->setStatus(Result::STATUS_ERROR);
112
                    $result->setStatusMessage(self::$statusCodes[$pushResponse->getCode()]);
113
                }
114
            // @codeCoverageIgnoreStart
115
            } catch (ApnsRuntimeException $exception) {
116
                $result->setStatus(Result::STATUS_ERROR);
117
                $result->setStatusMessage($exception->getMessage());
118
            }
119
            // @codeCoverageIgnoreEnd
120
121
            $notification->addResult($result);
122
        }
123
124
        $client->close();
125
        $this->pushClient = null;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     *
131
     * @throws \Jgut\Tify\Exception\AdapterException
132
     * @throws \Jgut\Tify\Exception\NotificationException
133
     */
134
    public function feedback()
135
    {
136
        $client = $this->getFeedbackClient();
137
138
        try {
139
            /* @var \ZendService\Apple\Apns\Response\Feedback[] $feedbackResponses */
140
            $feedbackResponses = $client->feedback();
141
        // @codeCoverageIgnoreStart
142
        } catch (ApnsRuntimeException $exception) {
143
            throw new NotificationException($exception->getMessage(), $exception->getCode(), $exception);
144
        }
145
        // @codeCoverageIgnoreEnd
146
147
        $responses = [];
148
149
        foreach ($feedbackResponses as $response) {
150
            $responses[] = $response->getToken();
151
        }
152
153
        $client->close();
154
        $this->feedbackClient = null;
155
156
        return $responses;
157
    }
158
159
    /**
160
     * Get opened ServiceClient
161
     *
162
     * @throws \Jgut\Tify\Exception\AdapterException
163
     *
164
     * @return \ZendService\Apple\Apns\Client\Message
165
     */
166
    protected function getPushClient()
167
    {
168
        if ($this->pushClient === null) {
169
            $this->pushClient = $this->builder->buildPushClient(
170
                $this->getParameter('certificate'),
171
                $this->getParameter('pass_phrase'),
172
                $this->sandbox
173
            );
174
        }
175
176
        return $this->pushClient;
177
    }
178
179
    /**
180
     * Get opened ServiceFeedbackClient
181
     *
182
     * @throws \Jgut\Tify\Exception\AdapterException
183
     *
184
     * @return \ZendService\Apple\Apns\Client\Feedback
185
     */
186
    protected function getFeedbackClient()
187
    {
188
        if ($this->feedbackClient === null) {
189
            $this->feedbackClient = $this->builder->buildFeedbackClient(
190
                $this->getParameter('certificate'),
191
                $this->getParameter('pass_phrase'),
192
                $this->sandbox
193
            );
194
        }
195
196
        return $this->feedbackClient;
197
    }
198
199
    /**
200
     * Get push service formatted messages.
201
     *
202
     * @param \Jgut\Tify\Notification $notification
203
     *
204
     * @throws \InvalidArgumentException
205
     * @throws \RuntimeException
206
     *
207
     * @return \ZendService\Apple\Apns\Message
0 ignored issues
show
Documentation introduced by
Should the return type not be \Generator?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
208
     */
209
    protected function getPushMessages(Notification $notification)
210
    {
211
        /* @var \Jgut\Tify\Receiver\ApnsReceiver[] $receivers */
212
        $receivers = array_filter(
213
            $notification->getReceivers(),
214
            function ($receiver) {
215
                return $receiver instanceof ApnsReceiver;
216
            }
217
        );
218
219
        foreach ($receivers as $receiver) {
220
            yield $this->builder->buildPushMessage($receiver, $notification);
221
        }
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227
    protected function getDefinedParameters()
228
    {
229
        return ['certificate', 'pass_phrase'];
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235
    protected function getDefaultParameters()
236
    {
237
        return [
238
            'pass_phrase' => null,
239
        ];
240
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245
    protected function getRequiredParameters()
246
    {
247
        return ['certificate'];
248
    }
249
}
250