ApnsAdapter::getFeedbackClient()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 7
nc 2
nop 0
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\PushAdapter;
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 PushAdapter, FeedbackAdapter
26
{
27
    const RESPONSE_OK = 0;
28
    const RESPONSE_PROCESSING_ERROR = 1;
29
    const RESPONSE_MISSING_DEVICE_TOKEN = 2;
30
    const RESPONSE_MISSING_TOPIC = 3;
31
    const RESPONSE_MISSING_PAYLOAD = 4;
32
    const RESPONSE_INVALID_TOKEN_SIZE = 5;
33
    const RESPONSE_INVALID_TOPIC_SIZE = 6;
34
    const RESPONSE_INVALID_PAYLOAD_SIZE = 7;
35
    const RESPONSE_INVALID_TOKEN = 8;
36
    const RESPONSE_UNKNOWN_ERROR = 255;
37
    const RESPONSE_UNAVAILABLE = 2048;
38
39
    /**
40
     * Status codes mapping.
41
     *
42
     * @var array
43
     */
44
    protected static $statusCodes = [
45
        self::RESPONSE_OK => Result::STATUS_SUCCESS,
46
47
        self::RESPONSE_MISSING_DEVICE_TOKEN => Result::STATUS_INVALID_MESSAGE,
48
        self::RESPONSE_MISSING_TOPIC => Result::STATUS_INVALID_MESSAGE,
49
        self::RESPONSE_MISSING_PAYLOAD => Result::STATUS_INVALID_MESSAGE,
50
        self::RESPONSE_INVALID_TOKEN_SIZE => Result::STATUS_INVALID_MESSAGE,
51
        self::RESPONSE_INVALID_TOPIC_SIZE => Result::STATUS_INVALID_MESSAGE,
52
        self::RESPONSE_INVALID_PAYLOAD_SIZE => Result::STATUS_INVALID_MESSAGE,
53
54
        self::RESPONSE_INVALID_TOKEN => Result::STATUS_INVALID_DEVICE,
55
56
        self::RESPONSE_PROCESSING_ERROR => Result::STATUS_SERVER_ERROR,
57
        self::RESPONSE_UNAVAILABLE => Result::STATUS_SERVER_ERROR,
58
59
        self::RESPONSE_UNKNOWN_ERROR => Result::STATUS_UNKNOWN_ERROR,
60
    ];
61
62
    /**
63
     * Status messages mapping.
64
     *
65
     * @var array
66
     */
67
    protected static $statusMessages = [
68
        self::RESPONSE_OK => 'OK',
69
70
        self::RESPONSE_MISSING_DEVICE_TOKEN => 'Missing Device Token',
71
        self::RESPONSE_MISSING_TOPIC => 'Missing Topic',
72
        self::RESPONSE_MISSING_PAYLOAD => 'Missing Payload',
73
        self::RESPONSE_INVALID_TOKEN_SIZE => 'Invalid Token Size',
74
        self::RESPONSE_INVALID_TOPIC_SIZE => 'Invalid Topic Size',
75
        self::RESPONSE_INVALID_PAYLOAD_SIZE => 'Invalid Payload Size',
76
77
        self::RESPONSE_INVALID_TOKEN => 'Invalid Token',
78
79
        self::RESPONSE_PROCESSING_ERROR => 'Processing Error',
80
        self::RESPONSE_UNAVAILABLE => 'Server Unavailable',
81
82
        self::RESPONSE_UNKNOWN_ERROR => 'Unknown Error',
83
    ];
84
85
    /**
86
     * APNS service builder.
87
     *
88
     * @var \Jgut\Tify\Adapter\Apns\ApnsBuilder
89
     */
90
    protected $builder;
91
92
    /**
93
     * @var \ZendService\Apple\Apns\Client\Message
94
     */
95
    protected $pushClient;
96
97
    /**
98
     * @var \ZendService\Apple\Apns\Client\Feedback
99
     */
100
    protected $feedbackClient;
101
102
    /**
103
     * @param array                                    $parameters
104
     * @param bool                                     $sandbox
105
     * @param \Jgut\Tify\Adapter\Apns\ApnsBuilder|null $builder
106
     *
107
     * @throws \Jgut\Tify\Exception\AdapterException
108
     */
109
    public function __construct(array $parameters = [], $sandbox = false, ApnsBuilder $builder = null)
110
    {
111
        parent::__construct($parameters, $sandbox);
112
113
        $certificatePath = $this->getParameter('certificate');
114
115
        if (!file_exists($certificatePath) || !is_readable($certificatePath)) {
116
            throw new AdapterException(
117
                sprintf('Certificate file "%s" does not exist or is not readable', $certificatePath)
118
            );
119
        }
120
121
        // @codeCoverageIgnoreStart
122
        if ($builder === null) {
123
            $builder = new ApnsBuilder;
124
        }
125
        // @codeCoverageIgnoreEnd
126
        $this->builder = $builder;
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     *
132
     * @throws \InvalidArgumentException
133
     * @throws \Jgut\Tify\Exception\AdapterException
134
     * @throws \ZendService\Apple\Exception\RuntimeException
135
     */
136
    public function push(Notification $notification)
137
    {
138
        $client = $this->getPushClient();
139
140
        $pushResults = [];
141
142
        /* @var \ZendService\Apple\Apns\Message $message */
143
        foreach ($this->getPushMessages($notification) as $message) {
144
            try {
145
                $statusCode = $client->send($message)->getCode();
146
            // @codeCoverageIgnoreStart
147
            } catch (ApnsRuntimeException $exception) {
148
                $statusCode = $this->getErrorCodeFromException($exception);
149
            }
150
            // @codeCoverageIgnoreEnd
151
152
            $pushResults[] = new Result(
153
                $message->getToken(),
154
                null,
155
                self::$statusCodes[$statusCode],
156
                self::$statusMessages[$statusCode]
157
            );
158
        }
159
160
        $client->close();
161
        $this->pushClient = null;
162
163
        return $pushResults;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     *
169
     * @throws \InvalidArgumentException
170
     * @throws \Jgut\Tify\Exception\AdapterException
171
     * @throws \Jgut\Tify\Exception\NotificationException
172
     */
173
    public function feedback()
174
    {
175
        $client = $this->getFeedbackClient();
176
177
        $feedbackResults = [];
178
179
        try {
180
            /* @var \ZendService\Apple\Apns\Response\Feedback[] $feedbackResponses */
181
            $feedbackResponses = $client->feedback();
182
        // @codeCoverageIgnoreStart
183
        } catch (ApnsRuntimeException $exception) {
184
            throw new NotificationException($exception->getMessage(), $exception->getCode(), $exception);
185
        }
186
        // @codeCoverageIgnoreEnd
187
188
        foreach ($feedbackResponses as $response) {
189
            $feedbackResults[] = new Result($response->getToken(), $response->getTime());
190
        }
191
192
        $client->close();
193
        $this->feedbackClient = null;
194
195
        return $feedbackResults;
196
    }
197
198
    /**
199
     * Get opened ServiceClient
200
     *
201
     * @throws \Jgut\Tify\Exception\AdapterException
202
     *
203
     * @return \ZendService\Apple\Apns\Client\Message
204
     */
205
    protected function getPushClient()
206
    {
207
        if ($this->pushClient === null) {
208
            $this->pushClient = $this->builder->buildPushClient(
209
                $this->getParameter('certificate'),
210
                $this->getParameter('pass_phrase'),
211
                $this->sandbox
212
            );
213
        }
214
215
        return $this->pushClient;
216
    }
217
218
    /**
219
     * Get opened ServiceFeedbackClient
220
     *
221
     * @throws \Jgut\Tify\Exception\AdapterException
222
     *
223
     * @return \ZendService\Apple\Apns\Client\Feedback
224
     */
225
    protected function getFeedbackClient()
226
    {
227
        if ($this->feedbackClient === null) {
228
            $this->feedbackClient = $this->builder->buildFeedbackClient(
229
                $this->getParameter('certificate'),
230
                $this->getParameter('pass_phrase'),
231
                $this->sandbox
232
            );
233
        }
234
235
        return $this->feedbackClient;
236
    }
237
238
    /**
239
     * Get service formatted push messages.
240
     *
241
     * @param \Jgut\Tify\Notification $notification
242
     *
243
     * @throws \ZendService\Apple\Exception\RuntimeException
244
     *
245
     * @return \Generator
246
     */
247
    protected function getPushMessages(Notification $notification)
248
    {
249
        foreach ($notification->getReceivers() as $receiver) {
250
            if ($receiver instanceof ApnsReceiver) {
251
                yield $this->builder->buildPushMessage($receiver, $notification);
252
            }
253
        }
254
    }
255
256
    /**
257
     * Extract error code from exception.
258
     *
259
     * @param \ZendService\Apple\Exception\RuntimeException $exception
260
     *
261
     * @return int
262
     */
263
    protected function getErrorCodeFromException(ApnsRuntimeException $exception)
264
    {
265
        $message = $exception->getMessage();
266
267
        if (preg_match('/^Server is unavailable/', $message)) {
268
            return self::RESPONSE_UNAVAILABLE;
269
        }
270
271
        return self::RESPONSE_UNKNOWN_ERROR;
272
    }
273
274
    /**
275
     * {@inheritdoc}
276
     */
277
    protected function getDefinedParameters()
278
    {
279
        return ['certificate', 'pass_phrase'];
280
    }
281
282
    /**
283
     * {@inheritdoc}
284
     */
285
    protected function getDefaultParameters()
286
    {
287
        return [
288
            'pass_phrase' => null,
289
        ];
290
    }
291
292
    /**
293
     * {@inheritdoc}
294
     */
295
    protected function getRequiredParameters()
296
    {
297
        return ['certificate'];
298
    }
299
}
300