8
votes

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?

Notes:
- 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');
        break;
    }

    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);
        fflush($fp);

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

    // 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.
            socket_close($fp);
            fclose($fp);
            }
        elseif ($changedStreams > 0) 
            {
            // set the error and redo starting after errorID index
            $errorID = checkAppleErrorResponse($fp);
            }
        }
    }
}
1
Note, unrelated to your actual question: you can make bullet points by putting a space in front of each of your dashes. It might make it look a bit nicer. - Nic
When I tried that, it wouldn't recognize the first dozen lines of code as code, so I left in the dashes... - ScottyB
...Huh. That's odd. Probably because Markdown thinks you want to add the code as part of the bullet points. It's not really a big deal, anyway. Just thought I'd point it out. - Nic

1 Answers

0
votes

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.

http://www.raywenderlich.com/2941/how-to-write-a-simple-phpmysql-web-service-for-an-ios-app