PHP:向iOS发送推送通知

8
我正在使用PHP将推送通知发送给iOS设备。目前,我的代码已经运行良好,以下是我所使用的代码:
$passphrase = '';
$badge = 1;
$path = base_path('path/to/certificate.pem');
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $path);
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
$fp = stream_socket_client(
    'ssl://gateway.sandbox.push.apple.com:2195', $err,
    $errstr, 60, STREAM_CLIENT_CONNECT | STREAM_CLIENT_PERSISTENT, $ctx
);

if (!$fp) {
    self::SavePush($device_token, $message, $device_id, $device_type, $user_id, "pending", "normal", null);
}

//echo 'Connected to APNS' . PHP_EOL;
$body['aps'] = array(
    'alert' => $message,
    'badge' => $badge,
    'sound' => 'default'
);
$payload = json_encode($body);
$msg = chr(0) . pack('n', 32) . pack('H*', $device_token) . pack('n', strlen($payload)) . $payload;
$result = fwrite($fp, $msg, strlen($msg));

//print_r($result);exit;

if (!$result) {
    self::SavePush($device_token, $message, $device_id, $device_type, $user_id, "pending", "normal", null, $result);
} else {
    self::SavePush($device_token, $message, $device_id, $device_type, $user_id, "sent", "normal", null, $result);
}

fclose($fp);

现在我面临的问题是,我无法确定通知是否失败,因为$result在每种情况下都包含一个整数,无论成功还是失败。我传递了一个随机数字作为令牌,它返回像115或65这样的整数,并且每次都会更改。所以!$result不能工作。我如何知道通知是否失败?


如何了解推送通知的发送状态 - arunjos007
4个回答

6
调用fwrite()将返回成功发送的字节数,如果发送时出现错误,则返回FALSE。您的$result会因为所发送信息的大小而改变。因此,如果$result===FALSE,则表示发送失败,通知未能成功。如果$result!==FALSE,则表示通知已成功发送。这仅验证了消息是否通过网络发送。它不验证消息结构或令牌的有效性等任何内容。
现在,如果您想确定推送通知本身是否有效,以及苹果是否接受并正在处理它,那么您可能需要使用CURL和HTTP/2来完成。通过快速谷歌搜索,我找到了这个网站,其中展示了如何进行演示。我还没有测试过该网站http://coding.tabasoft.it/ios/sending-push-notification-with-http2-and-php/,因此无法告诉您演示是否正确。这只是一个快速搜索,但可能会让您走上正确的道路。

你的意思是它没起作用?你是说你确信fwrite失败了,但它没有返回false吗?只有当fwrite失败时,$result === false才会成立。http://php.net/manual/en/function.fwrite.php - Dan Sherwin
好的,我传递了这个设备令牌“ddd”,显然它不是一个有效的设备令牌,但$result === false失败了。 - Saani
好的,我了解你正在进行的测试和尝试达到的目标,但你无法这样做。当你执行fwrite时,你只是检查它是否为false,以查看你的通知是否已传输到苹果。它无法检查消息本身的结构或令牌的有效性。你使用的是单向通信技术,这种方式无法从Apple APN获得任何响应。你可能想要使用CURL来完成这个任务,而不是使用流套接字。快速搜索发现了这个网址http://coding.tabasoft.it/ios/sending-push-notification-with-http2-and-php/。 - Dan Sherwin
非常感谢您的解释。我会研究一下并告诉您。 - Saani

4

当你使用fwrite将某些东西发送到APNS之后,你需要使用fread(或stream_get_contents ;))读取响应。最好的方法是使用非阻塞连接(stream_set_blocking($fp, 0);)并在简单的时间限制循环中等待响应:

$read = '';
$to = time() + 2;  // max 2 seconds wait...
while (!feof($fp)) // ...or end of connection
{
    $r = stream_get_contents($fp);
    if (strlen($r) > 0)
        $read .= $r;
    if (time() >= $to)
        break;
    usleep(100000);
}

接下来,在$read中,您将会得到“something”或者是“nothing”。如果得到的是“something”(确切地说是6个字节),那么就意味着出现了错误($error = unpack('Ccommand/Cstatus/Nident', $read);)。如果得到的是“nothing”或者长度短于/长于6个字节,那么就意味着消息发送成功。


4

我希望能够澄清一个重要的观点:正如苹果所说,由于推送是以这种方式工作的,所以“没有交付保证”。

因此,你可以像其他人建议的那样测试网络错误,但你无法保证接收到了通知。为了实现这一点,唯一(长...)的方法是在iOS设备上触发一个URL请求,当它接收到推送时。这个URL请求将更新你的数据库。


0
class PushApple {


   function base64url_encode($data)
    {         
      $b64 = base64_encode($data); 
      if ($b64 === false) {
        return false;
      } 
      $url = strtr($b64, '+/', '-_'); 
      return rtrim($url, '=');
    }

    function base64url_decode($data, $strict = false)
    { 
      $b64 = strtr($data, '-_', '+/'); 
      return base64_decode($b64, $strict);
    }        


  
public function iOSNew($data, $devicetoken) {

            //https://jwt.io/ for testing..
 
            //https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns/
        
            $tokenTMP = 'dir.../cert/token_apn';
            $domain   = 'pl.my.app';
            
            $url = 'https://api.sandbox.push.apple.com:443'; //sandbox
           // $url = 'https://api.push.apple.com:443';
            

            //----------------
            //For security, APNs requires you to refresh your token regularly. Refresh your token no more than once every 20 minutes and no less than once every 60 minutes.
            
            $token = '';
            
            if(file_exists($tokenTMP))
            {
                $fileTime = filemtime($tokenTMP);
                $maxT     = time() - 1800; //max 30 min.
                
                if($fileTime < $maxT)
                {
                    $token = file_get_contents($tokenTMP);
                   // echo 'I get last generated token..';
                }                                       
            }
            
            
            //----------------
            
            if($token == '') //Let's generate new token...
            {
               // echo 'Generate new token...';
                
                //https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns
                $assocToken        = array();
                $assocToken['alg'] = 'ES256';       //The encryption algorithm you used to encrypt the token. APNs supports only the ES256 algorithm, so set the value of this key to ES256.
                $assocToken['kid'] = '******';  //The 10-character Key ID you obtained from your developer account
                $assocToken['iss'] = '******';  //The issuer key, the value for which is the 10-character Team ID you use for developing your company’s apps. Obtain this value from your developer account.
                $assocToken['iat'] = time();  //The “issued at” time, whose value indicates the time at which this JSON token was generated. Specify the value as the number of seconds since Epoch, in UTC. The value must be no more than one hour from the current time.


                $header = '{'
                        . ' "alg" : "'.$assocToken['alg'].'",'
                        . ' "kid" : "'.$assocToken['kid'].'"'
                        . '}';


                $payload = '{'
                        . ' "iss" : "'.$assocToken['iss'].'",'
                        . ' "iat" : "'.$assocToken['iat'].'"'
                        . '}';                

                $headerB64URL  = $this->base64url_encode($header);
                $payloadB64URL = $this->base64url_encode($payload);               
                $secret        = file_get_contents('dir.../cert/AuthKey_TG%234df%#$%.p8');

                $signature = '';
                openssl_sign($headerB64URL.'.'.$payloadB64URL, $signature, $secret, \OPENSSL_ALGO_SHA256);

                $token = $headerB64URL.'.'.$payloadB64URL.'.'.$this->base64url_encode($signature);
                
                //save to use next time..                    
                $fp = fopen($tokenTMP, 'w');
                fwrite($fp, $token); 
                fclose($fp);
            }
                           
          
            
            echo $url . '/3/device/'.$devicetoken;
            
            $apns_id = '8-4-4-5-12';
            
            $curl = curl_init();
            curl_setopt_array($curl, array(
            CURLOPT_URL => $url . '/3/device/'.$devicetoken,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 1,
            CURLOPT_TIMEOUT => 10,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1, 
            CURLOPT_HTTPHEADER => array(
            "authorization: bearer ".$token,
          //  "apns-id: ".$apns_id, //
            "apns-push-type: alert",
            "apns-expiration: 0",
            "apns-priority: 0",
            "apns-topic: ".$domain,
              ),
             CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,   
             CURLOPT_POST => '1',   
             CURLOPT_POSTFIELDS => '{ "aps" : { "alert" : "Hello" } }' //  || $devicetoken
             ));
            
    

            $response = curl_exec($curl);
            
            echo 'Error: '.curl_errno($curl).' | '. curl_error($curl).' || ';
            
            
            echo $response;
            
            $ret = json_decode($response, true);
                                                                                    
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接