43 0 =>
'No errors encountered',
44 1 =>
'Processing error',
45 2 =>
'Missing device token',
47 4 =>
'Missing payload',
48 5 =>
'Invalid token size',
49 6 =>
'Invalid topic size',
50 7 =>
'Invalid payload size',
52 self::STATUS_CODE_INTERNAL_ERROR =>
'Internal error'
58 'tls://gateway.push.apple.com:2195',
59 'tls://gateway.sandbox.push.apple.com:2195'
75 $this->_nSendRetryTimes = (int)$nRetryTimes;
98 $nMessageQueueLen = count($this->_aMessageQueue);
99 for ($i = 0; $i < $nRecipients; $i++) {
100 $nMessageID = $nMessageQueueLen + $i + 1;
101 $this->_aMessageQueue[$nMessageID] = array(
102 'MESSAGE' => $message,
122 if (!$this->_hSocket) {
124 'Not connected to Push Notification Service'
128 if (empty($this->_aMessageQueue)) {
130 'No notifications queued to be sent'
134 $this->_aErrors = array();
136 while (($nMessages = count($this->_aMessageQueue)) > 0) {
137 $this->
_log(
"INFO: Sending messages queue, run #{$nRun}: $nMessages message(s) left in queue.");
140 foreach($this->_aMessageQueue as $k => &$aMessage) {
141 if (function_exists(
'pcntl_signal_dispatch')) {
142 pcntl_signal_dispatch();
145 $message = $aMessage[
'MESSAGE'];
146 $sCustomIdentifier = (string)$message->getCustomIdentifier();
147 $sCustomIdentifier = sprintf(
'[custom identifier: %s]', empty($sCustomIdentifier) ?
'unset' : $sCustomIdentifier);
150 if (!empty($aMessage[
'ERRORS'])) {
151 foreach($aMessage[
'ERRORS'] as $aError) {
152 if ($aError[
'statusCode'] == 0) {
153 $this->
_log(
"INFO: Message ID {$k} {$sCustomIdentifier} has no error ({$aError['statusCode']}), removing from queue...");
156 }
else if ($aError[
'statusCode'] > 1 && $aError[
'statusCode'] <= 8) {
157 $this->
_log(
"WARNING: Message ID {$k} {$sCustomIdentifier} has an unrecoverable error ({$aError['statusCode']}), removing from queue without retrying...");
162 if (($nErrors = count($aMessage[
'ERRORS'])) >= $this->_nSendRetryTimes) {
164 "WARNING: Message ID {$k} {$sCustomIdentifier} has {$nErrors} errors, removing from queue..."
171 $nLen = strlen($aMessage[
'BINARY_NOTIFICATION']);
172 $this->
_log(
"STATUS: Sending message ID {$k} {$sCustomIdentifier} (" . ($nErrors + 1) .
"/{$this->_nSendRetryTimes}): {$nLen} bytes.");
174 $aErrorMessage = null;
175 if ($nLen !== ($nWritten = (
int)@fwrite($this->_hSocket, $aMessage[
'BINARY_NOTIFICATION']))) {
176 $aErrorMessage = array(
178 'statusCode' => self::STATUS_CODE_INTERNAL_ERROR,
179 'statusMessage' => sprintf(
'%s (%d bytes written instead of %d bytes)',
180 $this->_aErrorResponseMessages[self::STATUS_CODE_INTERNAL_ERROR], $nWritten, $nLen
184 usleep($this->_nWriteInterval);
193 $read = array($this->_hSocket);
195 $nChangedStreams = @stream_select($read, $null, $null, 0, $this->_nSocketSelectTimeout);
196 if ($nChangedStreams ===
false) {
197 $this->
_log(
'ERROR: Unable to wait for a stream availability.');
199 }
else if ($nChangedStreams > 0) {
202 $this->_aMessageQueue = array();
205 $this->_aMessageQueue = array();
227 $this->_aMessageQueue = array();
244 $this->_aErrors = array();
265 $nTokenLength = strlen($sDeviceToken);
266 $nPayloadLength = strlen($sPayload);
268 $sRet = pack(
'CNNnH*', self::COMMAND_PUSH, $nMessageID, $nExpire > 0 ? time() + $nExpire : 0, self::DEVICE_BINARY_SIZE, $sDeviceToken);
269 $sRet .= pack(
'n', $nPayloadLength);
283 return unpack(
'Ccommand/CstatusCode/Nidentifier', $sErrorMessage);
295 $sErrorResponse = @fread($this->_hSocket, self::ERROR_RESPONSE_SIZE);
296 if ($sErrorResponse ===
false || strlen($sErrorResponse) != self::ERROR_RESPONSE_SIZE) {
300 if (!is_array($aErrorResponse) || empty($aErrorResponse)) {
303 if (!isset($aErrorResponse[
'command'], $aErrorResponse[
'statusCode'], $aErrorResponse[
'identifier'])) {
306 if ($aErrorResponse[
'command'] != self::ERROR_RESPONSE_COMMAND) {
309 $aErrorResponse[
'time'] = time();
310 $aErrorResponse[
'statusMessage'] =
'None (unknown)';
311 if (isset($this->_aErrorResponseMessages[$aErrorResponse[
'statusCode']])) {
312 $aErrorResponse[
'statusMessage'] = $this->_aErrorResponseMessages[$aErrorResponse[
'statusCode']];
314 return $aErrorResponse;
330 if (!isset($aErrorMessage) && !isset($aStreamErrorMessage)) {
332 }
else if (isset($aErrorMessage, $aStreamErrorMessage)) {
333 if ($aStreamErrorMessage[
'identifier'] <= $aErrorMessage[
'identifier']) {
334 $aErrorMessage = $aStreamErrorMessage;
335 unset($aStreamErrorMessage);
337 }
else if (!isset($aErrorMessage) && isset($aStreamErrorMessage)) {
338 $aErrorMessage = $aStreamErrorMessage;
339 unset($aStreamErrorMessage);
342 $this->
_log(
'ERROR: Unable to send message ID ' .
343 $aErrorMessage[
'identifier'] .
': ' .
344 $aErrorMessage[
'statusMessage'] .
' (' . $aErrorMessage[
'statusCode'] .
').');
348 foreach($this->_aMessageQueue as $k => &$aMessage) {
349 if ($k < $aErrorMessage[
'identifier']) {
350 unset($this->_aMessageQueue[$k]);
351 }
else if ($k == $aErrorMessage[
'identifier']) {
352 $aMessage[
'ERRORS'][] = $aErrorMessage;
373 if (!is_numeric($nMessageID) || $nMessageID <= 0) {
375 'Message ID format is not valid.'
378 if (!isset($this->_aMessageQueue[$nMessageID])) {
380 "The Message ID {$nMessageID} does not exists."
384 $this->_aErrors[$nMessageID] = $this->_aMessageQueue[$nMessageID];
386 unset($this->_aMessageQueue[$nMessageID]);
const ERROR_RESPONSE_SIZE
integer Error-response packet size.
void _removeMessageFromQueue(integer $nMessageID, boolean $bError=false)
Remove a message from the message queue.
integer getSendRetryTimes()
Get the send retry time value.
integer getRecipientsNumber()
Get the number of recipients.
array null _readErrorMessage()
Reads an error message (if present) from the main stream.
string getPayload()
Convert the message in a JSON-encoded payload.
$_aErrorResponseMessages
array Error-response messages.
void connect()
Connects to Apple Push Notification service server.
string getRecipient(integer $nRecipient=0)
Get a recipient.
string _getBinaryNotification(string $sDeviceToken, string $sPayload, integer $nMessageID=0, integer $nExpire=604800)
Generate a binary notification from a device token and a JSON-encoded payload.
array $_aErrors
Error container.
const STATUS_CODE_INTERNAL_ERROR
integer Status code for internal error (not Apple).
Abstract class: this is the superclass for all Apple Push Notification Service classes.
boolean disconnect()
Disconnects from Apple Push Notifications service server.
void add(ApnsPHP_Message $message)
Adds a message to the message queue.
void _log(string $sMessage)
Logs a message through the Logger.
integer getExpiry()
Get the expiry value.
array $_aMessageQueue
Message queue.
const COMMAND_PUSH
integer Payload command.
array _parseErrorMessage(string $sErrorMessage)
Parses the error message.
The Push Notification Provider.
void setSendRetryTimes(integer $nRetryTimes)
Set the send retry times value.
$_aServiceURLs
array Service URLs environments.
array getErrors(boolean $bEmpty=true)
Returns messages not delivered to the end user because one (or more) error occurred.
integer $_nSendRetryTimes
Send retry times.
void send()
Sends all messages in the message queue to Apple Push Notification Service.
The Push Notification Message.
const ERROR_RESPONSE_COMMAND
integer Error-response command code.
array getQueue(boolean $bEmpty=true)
Returns messages in the message queue.
boolean _updateQueue(array $aErrorMessage=null)
Checks for error message and deletes messages successfully sent from message queue.