Home » Php » error handling – Method for processing device tokens for Apple Push Notification Service with PHP

error handling – Method for processing device tokens for Apple Push Notification Service with PHP

Posted by: admin July 12, 2020 Leave a comment


There is a lot of info on this site about how to handle errors returned asynchronously from the Apple Push Notification Service in PHP. I came up with a method in PHP that seems to work pretty well, but I’d like some feedback.

  1. Is the use of fflush() correct? I’ve seen it in some examples but not all.
  2. I can’t get it to give an error for a purposely bad device token. Why?
  3. Is this solution scalable to thousands of devices (assume PHP max memory is increased sufficiently)?
  4. Other issues?

– Device tokens for the notification are stored in an array at the start.
– It’s not asynchronous but it checks for (past) errors after sending each notification and checks one more time a full second after the last notification.
– It uses the newer “modern” notification format as opposed to the original or extended formats.
– It sends the index of the token array as the identifier to APNS.
– It uses a checkAppleErrorResponse() function which reads the first 6 bytes and returns either false or the identifier (index) that failed so it can back up and continue with the next token. (All tokens sent after a failure are invalidated.)

$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'passphrase', $iosCertPassphrase);
stream_context_set_option($ctx, "ssl", "local_cert", $iosCertKey);
$fp = NULL;
$errno = NULL;
$errstr = NULL;

// same payload for all
$item2 = chr(2) . pack("n", strlen($payload)) . $payload; // payload item has id 2, a 2-byte length ("n") containing length of payload, then payload

$errorID = -1;

while ($errorID !== false) {
    $fp = stream_socket_client($iosHost . ':' . $iosPort, $errno, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);        

    if ($fp === FALSE) {
        echo('Failed to create socket');

    stream_set_blocking($fp, 0);

    for ($id = $errorID + 1 ; $id < sizeof($iosTokens); $id++) {
        $errorID = false;
        $item1 = chr(1) . pack('n', 32) . pack("H*", $iosTokens[$id]['device_token']); // device token item has 1-byte id 1, 2-byte length ("n") containing 32, then 32-byte device token

        $item3 = chr(3) . pack('n', 4) . pack('N', $id); // notification identifier has 1-byte id 3, 2-byte length ("n") containing 4, then 4-byte identifier

        $frame = $item1 . $item2 . $item3;

        $msg = chr(2) . pack("N", strlen($frame)) . $frame; // for "modern" push notification format, msg has 1-byte id 2, 4-byte length ("N") containing the length of the frame, then frame

        fwrite($fp, $msg);

        $errorID = checkAppleErrorResponse($fp);

        if ($errorID !== false) // if there's an error, stop now

    // if done with for loop and no errors, pause for a sec and check one last time
    if ($errorID === false) {
        $read = array($fp);
        $null = null;
        $changedStreams = stream_select($read, $null, $null, 0, 1000000);

        //check if it is actually false
        if ($changedStreams === false) 
            //close stream when done.
        elseif ($changedStreams > 0) 
            // set the error and redo starting after errorID index
            $errorID = checkAppleErrorResponse($fp);
How to&Answers:

Hopefully related. I used this. It’s basic but solid gold. With a little sprinkle or security and some more intense error handling, it wasn’t long before i’d advanced it into a system that could handle everything I needed it to.