1

I have a PHP script that sends push notifications to an Apple APNS server. It is supposed to echo the response code 485, which shows that the message was successfully sent. However, when I run the code, it shows a different numeric code each time, and the message is not sent.

Here is the code:

$payload = array(
    'aps' => array(
        'alert' => $message,
        'T_ID' => $data["trip_id"],
        'S' => $state,
        'Api' => 2,
        'sound' => 'default'
));

$body = array();
$body['aps'] = array('alert' => "request trip");
$body['aps']['notifurl'] = $payload;
$body['aps']['badge'] = 2;
$payload = json_encode($body);
$ret = array(
    "error" => 0
);

$streamContext = stream_context_create();
stream_context_set_option($streamContext, 'ssl', 'local_cert', dirname(BASEPATH) . "/uploads/" . $this->_apnsCert);
stream_context_set_option($streamContext, 'ssl', 'passphrase', $this->_passphrase);
@$apns = stream_socket_client('tls://' . $this->_apnsHost . ':' . $this->_apnsPort, $error, $errorString, 200, STREAM_CLIENT_CONNECT, $streamContext);

if (!$apns) {
    //die('Error creating ssl socket ' . $error . ' ' . $errorString);
    $ret["error"] = 1;
    $ret["details"] = 'Error creating ssl socket ' . $error . ' ' . $errorString;
}

$apnsMessage = // Command "1"
    chr(1)
    // Identifier "88"
    . pack('N', 88)
    // Expiry "tomorrow"
    . pack('N', time() + 86400)
    // Token length
    . chr(0) . chr(32)
    // Device token
    . pack('H*', str_replace(' ', '', $deviceToken))
    // Payload length
    . chr(0) . chr(strlen($payload))
    // Actual payload
    . $payload . $payload;

echo fwrite($apns, $apnsMessage);
fclose($apns);

Why is this not working as expected?

Michael Gaskill
  • 7,913
  • 10
  • 38
  • 43
Wael Salah
  • 71
  • 1
  • 7

1 Answers1

4

I see at least 2 errors in the code:

1) the original value in $payload is overwritten when you encode the message body with json_encode($body), although this is likely not the final culprit

2) you're echoing the result from the fwrite, when you should be reading the response from the stream and echoing that.

See the example at Working with Apple Push Notification to understand how to handle the return values. The following code is derived from there:

// FUNCTION to check if there is an error response from Apple
// Returns TRUE if there was and FALSE if there was not
function checkAppleErrorResponse($fp) {
    //byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID). 
    // Should return nothing if OK.

    //NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait 
    // forever when there is no response to be sent. 

    $apple_error_response = fread($fp, 6);

    if ($apple_error_response) {
        // unpack the error response (first byte 'command" should always be 8)
        $error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response); 

        if ($error_response['status_code'] == '0') {
            $error_response['status_code'] = '0-No errors encountered';
        } else if ($error_response['status_code'] == '1') {
            $error_response['status_code'] = '1-Processing error';
        } else if ($error_response['status_code'] == '2') {
            $error_response['status_code'] = '2-Missing device token';
        } else if ($error_response['status_code'] == '3') {
            $error_response['status_code'] = '3-Missing topic';
        } else if ($error_response['status_code'] == '4') {
            $error_response['status_code'] = '4-Missing payload';
        } else if ($error_response['status_code'] == '5') {
            $error_response['status_code'] = '5-Invalid token size';
        } else if ($error_response['status_code'] == '6') {
            $error_response['status_code'] = '6-Invalid topic size';
        } else if ($error_response['status_code'] == '7') {
            $error_response['status_code'] = '7-Invalid payload size';
        } else if ($error_response['status_code'] == '8') {
            $error_response['status_code'] = '8-Invalid token';
        } else if ($error_response['status_code'] == '255') {
            $error_response['status_code'] = '255-None (unknown)';
        } else {
            $error_response['status_code'] = $error_response['status_code'].'-Not listed';
        }

        echo '<br><b>+ + + + + + ERROR</b> Response Command:<b>' . $error_response['command'] . '</b>&nbsp;&nbsp;&nbsp;Identifier:<b>' . $error_response['identifier'] . '</b>&nbsp;&nbsp;&nbsp;Status:<b>' . $error_response['status_code'] . '</b><br>';

        echo 'Identifier is the rowID (index) in the database that caused the problem, and Apple will disconnect you from server. To continue sending Push Notifications, just start at the next rowID after this Identifier.<br>';

        return true;
    }

    return false;
}

Note that the example recommends that you make sure to set the stream to non-blocking mode with stream_set_blocking($fp, 0) before reading from it, but that's only necessary if you want to do any interim error checking; your code example does not do this, so it may not be an issue for you.

Michael Gaskill
  • 7,913
  • 10
  • 38
  • 43
  • When i echo the response using fwrite i got response code of 485! what this mean? – Wael Salah Jun 19 '16 at 11:50
  • The [fwrite() function](http://php.net/manual/en/function.fwrite.php) returns the number of bytes written to the stream. What this means is that the length of `$apnsMessage` is 485 bytes, but has nothing to do with the response from the APNS server. You need to use `fread()` to get the server response, as shown above. – Michael Gaskill Jun 19 '16 at 15:57
  • This is a life saver! I just want to add that its best to put sleep(1); before the function as otherwise it comes up with an empty result... and if you are sending PUSH in a batch the error can show as if it is for the next one. – Виктор Иванов Sep 27 '16 at 09:03