Completed
Pull Request — master (#27)
by
unknown
09:26
created

Response::getErrorReason()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Pushok package.
5
 *
6
 * (c) Arthur Edamov <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Pushok;
13
14
/**
15
 * Class Response
16
 * @package Pushok
17
 *
18
 * @see http://bit.ly/communicating-with-apns
19
 */
20
class Response implements ApnsResponseInterface
21
{
22
    const APNS_SUCCESS = 200;
23
    const APNS_BAD_REQUEST = 400;
24
    const APNS_AUTH_PROVIDER_ERROR = 403;
25
    const APNS_METHOD_NOT_ALLOWED = 405;
26
    const APNS_NOT_ACTIVE_DEVICE_TOKEN_ = 410;
27
    const APNS_PAYLOAD_TOO_LARGE = 413;
28
    const APNS_TOO_MANY_REQUESTS = 429;
29
    const APNS_SERVER_ERROR = 500;
30
    const APNS_SERVER_UNAVAILABLE = 503;
31
32
    /**
33
     * Reason phrases by status code.
34
     *
35
     * @var array
36
     */
37
    private static $reasonPhrases = [
38
        200 => 'Success.',
39
        400 => 'Bad request.',
40
        403 => 'There was an error with the certificate or with the provider authentication token.',
41
        405 => 'The request used a bad :method value. Only POST requests are supported.',
42
        410 => 'The device token is no longer active for the topic.',
43
        413 => 'The notification payload was too large.',
44
        429 => 'The server received too many requests for the same device token.',
45
        500 => 'Internal server error.',
46
        503 => 'The server is shutting down and unavailable.',
47
    ];
48
49
    /**
50
     * Error reasons by status code.
51
     *
52
     * @var array
53
     */
54
    private static $errorReasons = [
55
        400 => [
56
            'BadCollapseId' => 'The collapse identifier exceeds the maximum allowed size',
57
            'BadDeviceToken' => 'The specified device token was bad.' .
58
                ' Verify that the request contains a valid token and that the token matches the environment',
59
            'BadExpirationDate' => 'The apns-expiration value is bad',
60
            'BadMessageId' => 'The apns-id value is bad',
61
            'BadPriority' => 'The apns-priority value is bad',
62
            'BadTopic' => 'The apns-topic was invalid',
63
            'DeviceTokenNotForTopic' => 'The device token does not match the specified topic',
64
            'DuplicateHeaders' => 'One or more headers were repeated',
65
            'IdleTimeout' => 'Idle time out',
66
            'MissingDeviceToken' => 'The device token is not specified in the request :path.' .
67
                ' Verify that the :path header contains the device token',
68
            'MissingTopic' => 'The apns-topic header of the request was not specified and was required.' .
69
                ' The apns-topic header is mandatory' .
70
                ' when the client is connected using a certificate that supports multiple topics',
71
            'PayloadEmpty' => 'The message payload was empty',
72
            'TopicDisallowed' => 'Pushing to this topic is not allowed',
73
        ],
74
        403 => [
75
            'BadCertificate' => 'The certificate was bad',
76
            'BadCertificateEnvironment' => 'The client certificate was for the wrong environment',
77
            'ExpiredProviderToken' => 'The provider token is stale and a new token should be generated',
78
            'Forbidden' => 'The specified action is not allowed',
79
            'InvalidProviderToken' => 'The provider token is not valid or the token signature could not be verified',
80
            'MissingProviderToken' => 'No provider certificate was used to connect to APNs' .
81
                ' and Authorization header was missing or no provider token was specified',
82
        ],
83
        404 => [
84
            'BadPath' => 'The request contained a bad :path value'
85
        ],
86
        405 => [
87
            'MethodNotAllowed' => 'The specified :method was not POST'
88
        ],
89
        410 => [
90
            'Unregistered' => 'The device token is inactive for the specified topic.'
91
        ],
92
        413 => [
93
            'PayloadTooLarge' => 'The message payload was too large.' .
94
                ' See The Remote Notification Payload for details on maximum payload size'
95
        ],
96
        429 => [
97
            'TooManyRequests' => 'Too many requests were made consecutively to the same device token'
98
        ],
99
        500 => [
100
            'InternalServerError' => 'An internal server error occurred'
101
        ],
102
        503 => [
103
            'ServiceUnavailable' => 'The service is unavailable',
104
            'Shutdown' => 'The server is shutting down',
105
        ],
106
    ];
107
108
    /**
109
     * APNs Id.
110
     *
111
     * @var string|null
112
     */
113
    private $apnsId;
114
115
    /**
116
     * Device token.
117
     *
118
     * @var string|null
119
     */
120
    private $deviceToken;
121
122
    /**
123
     * Response status code.
124
     *
125
     * @var int
126
     */
127
    private $statusCode;
128
129
    /**
130
     * Error reason.
131
     *
132
     * @var string
133
     */
134
    private $errorReason;
135
136
    /**
137
     * Response constructor.
138
     *
139
     * @param int $statusCode
140
     * @param string $headers
141
     * @param string $body
142
     * @param string $deviceToken
143
     */
144
    public function __construct(int $statusCode, string $headers, string $body, string $deviceToken = null)
145
    {
146
        $this->statusCode = $statusCode;
147
        $this->apnsId = self::fetchApnsId($headers);
148
        $this->errorReason = self::fetchErrorReason($body);
149
        $this->deviceToken = $deviceToken;
150
    }
151
152
    /**
153
     * Fetch APNs Id from response headers.
154
     *
155
     * @param string $headers
156
     * @return string
157
     */
158
    private static function fetchApnsId(string $headers): string
159
    {
160
        $data = explode("\n", trim($headers));
161
162
        foreach ($data as $part) {
163
            $middle = explode(":", $part);
164
165
            if ($middle[0] !== 'apns-id') {
166
                continue;
167
            }
168
169
            return $middle[1];
170
        }
171
172
        return '';
173
    }
174
175
    /**
176
     * Fetch error reason from response body.
177
     *
178
     * @param string $body
179
     * @return string
180
     */
181
    private static function fetchErrorReason(string $body): string
182
    {
183
        return json_decode($body, true)['reason'] ?: '';
184
    }
185
186
    /**
187
     * Get APNs Id
188
     *
189
     * @return string|null
190
     */
191
    public function getApnsId()
192
    {
193
        return $this->apnsId;
194
    }
195
196
    /**
197
     * Get device token
198
     *
199
     * @return string|null
200
     */
201
    public function getDeviceToken()
202
    {
203
        return $this->deviceToken;
204
    }
205
206
    /**
207
     * Get status code.
208
     *
209
     * @return int
210
     */
211
    public function getStatusCode(): int
212
    {
213
        return $this->statusCode;
214
    }
215
216
    /**
217
     * Get reason phrase.
218
     *
219
     * @return string
220
     */
221
    public function getReasonPhrase(): string
222
    {
223
        return self::$reasonPhrases[$this->statusCode] ?: '';
224
    }
225
226
    /**
227
     * Get error reason.
228
     *
229
     * @return string
230
     */
231
    public function getErrorReason(): string
232
    {
233
        return $this->errorReason;
234
    }
235
236
    /**
237
     * Get error description.
238
     *
239
     * @return string
240
     */
241
    public function getErrorDescription(): string
242
    {
243
        if (isset(self::$errorReasons[$this->statusCode][$this->errorReason])) {
244
            return self::$errorReasons[$this->statusCode][$this->errorReason];
245
        }
246
247
        return '';
248
    }
249
}
250