• apns-http2-php,苹果push升级到http2


    最近公司push推送升级,用苹果http2进行推送,http2的好处就不说了,这些网上都可以查到,但是真正在项目中用的,用php写的还是特别少,因此,写出来跟大家分享,废话不说了,直接上代码:

    pushMessage.php

    <?php

    class PushMessage {
    //发送apns server时发送消息的键
    const APPLE_RESERVED_NAMESPACE = 'aps';

    /*
    * 连接apns地址
    * 'https://api.push.apple.com:443/3/device/', // 生产环境
    * 'https://api.development.push.apple.com:443/3/device/' // 沙盒环境
    *
    **/
    private $_appleServiceUrl;
    //证书
    private $_sProviderCertificateFile;
    //要发送到的device token
    private $_deviceTokens = array();
    //额外要发送的内容
    private $_customProperties;
    //私钥密码
    private $_passPhrase;
    //要推送的文字消息
    private $_pushMessage;
    //要推送的语音消息
    private $_pushSoundMessage;
    //设置角标
    private $_nBadge;
    //发送的头部信息
    private $_headers = array();
    private $_errors;
    //推送的超时时间,如果超过了这个时间,就自动不推送了,单位为秒
    private $_expiration;
    //apple 唯一标识
    private $_apns_topic;
    //10:立即接收,5:屏幕关闭,在省电时才会接收到的。如果是屏幕亮着,是不会接收到消息的。而且这种消息是没有声音提示的
    private $_priority;
    //cURL允许执行的最长秒数
    private $_timeout;
    //curl单连接
    private $_hSocket;
    //curl多连接
    private $_multi_hSocket;
    /**< @type integer Status code for internal error (not Apple). */
    const STATUS_CODE_INTERNAL_ERROR = 999;

    const ERROR_WRITE_TOKEN = 1000;
    //apple server 返回的错误信息
    protected $_aErrorResponseMessages = array(
    200 => 'Sussess',
    400 => 'Bad request',
    403 => 'There was an error with the certificate',
    405 => 'The request used a bad :method value. Only POST requests are supported',
    410 => 'The device token is no longer active for the topic',
    413 => 'The notification payload was too large',
    429 => 'The server received too many requests for the same device token',
    500 => 'Internal server error',
    503 => 'The server is shutting down and unavailable',
    self::STATUS_CODE_INTERNAL_ERROR => 'Internal error',
    self::ERROR_WRITE_TOKEN => 'Writing token error',
    );

    public function __construct() {}

    /*
    * 连接apple server
    * @params certificate_file 证书
    * @params pass_phrase 私钥密码
    * @params apple_service_url 要发送的apple apns service
    * @params expiration 推送的超时时间,如果超过了这个时间,就自动不推送了,单位为秒
    * @params apns-topic apple标识
    **/
    public function connServer($params) {
    if (empty($params['certificate_file'])) {
    return false;
    }
    $this->_sProviderCertificateFile = $params['certificate_file'];
    $this->_appleServiceUrl = $params['apple_service_url'];
    $this->_passPhrase = $params['pass_phrase'];
    $this->_apns_topic = $params['apns-topic'];
    $this->_expiration = $params['expiration'];
    $this->_priority = $params['priority'];
    $this->_timeout = $params['timeout'];

    $this->_headers = array(
    'apns-topic:'. $params['apns-topic'],
    'apns-priority'. $params['priority'],
    'apns-expiration'. $params['expiration']
    );

    $this->_multi_hSocket = curl_multi_init();

    if (!$this->_multi_hSocket) {
    $this->_errors['connServer']['cert'] = $this->_sProviderCertificateFile;
    $this->_errors['connServer']['desc'] = "Unable to connect to '{$this->_appleServiceUrl}': $this->_multi_hSocket";
    $this->_errors['connServer']['nums'] = isset($this->_errors['connServer']['nums']) ? intval($this->_errors['connServer']['nums']) : 0;
    $this->_errors['connServer']['nums'] += 1;

    return false;
    }

    return $this->_multi_hSocket;
    }

    /*
    * 断连
    *
    **/
    public function disconnect() {
    if (is_resource($this->_multi_hSocket)) {
    curl_multi_close($this->_multi_hSocket);
    }
    if (!empty($this->_hSocket)) {
    foreach ($this->_hSocket as $val) {
    if (is_resource($val)) {
    curl_close($val);
    }
    }

    $this->_hSocket = array();
    return true;
    }

    return false;
    }

    //设置发送文字消息
    public function setMessage($message) {
    $this->_pushMessage = $message;
    }

    //设置发送语音消息
    public function setSound($sound_message = 'default') {
    $this->_pushSoundMessage = $sound_message;
    }

    //获取要发送的文字消息
    public function getMessage() {
    if (!empty($this->_pushMessage)) {
    return $this->_pushMessage;
    }
    return '';
    }

    //获取语音消息
    public function getSoundMessage() {
    if (!empty($this->_pushSoundMessage)) {
    return $this->_pushSoundMessage;
    }
    return '';
    }

    /*
    * 接收device token 可以是数组,也可以是单个字符串
    *
    **/
    public function addDeviceToken($device_token) {
    if (is_array($device_token) && !empty($device_token)) {
    $this->_deviceTokens = $device_token;
    } else {
    $this->_deviceTokens[] = $device_token;
    }
    }

    //返回要获取的device token
    public function getDeviceToken($key = '') {
    if ($key !== '') {
    return isset($this->_deviceTokens[$key]) ? $this->_deviceTokens[$key] : array();
    }
    return $this->_deviceTokens;
    }

    //设置角标
    public function setBadge($nBadge) {
    $this->_nBadge = intval($nBadge);
    }

    //获取角标
    public function getBadge() {
    return $this->_nBadge;
    }

    /*
    * 用来设置额外的消息
    * @params custom_params array $name 不能和 self::APPLE_RESERVED_NAMESPACE('aps')样,
    *
    **/
    public function setCustomProperty($custom_params) {
    foreach ($custom_params as $name=>$value) {
    if (trim($name) == self::APPLE_RESERVED_NAMESPACE) {
    $this->_errors['setCustomProperty'][] = $name.'设置不成功,'.$name.'不可以设置成 aps.';
    }
    $this->_customProperties[trim($name)] = $value;
    }
    }

    /*
    * 用来获取额外设置的值
    * @params string $name
    *
    **/
    public function getCustomProperty($name = '') {
    if ($name !== '') {
    return isset($this->_customProperties[trim($name)]) ? $this->_customProperties[trim($name)] : '';
    }

    return $this->_customProperties;
    }

    /**
    * 组织发送的消息
    *
    */
    protected function getPayload() {
    $aPayload[self::APPLE_RESERVED_NAMESPACE] = array();

    if (isset($this->_pushMessage)) {
    $aPayload[self::APPLE_RESERVED_NAMESPACE]['alert'] = $this->_pushMessage;
    }
    if (isset($this->_pushSoundMessage)) {
    $aPayload[self::APPLE_RESERVED_NAMESPACE]['sound'] = (string)$this->_pushSoundMessage;
    }
    if (isset($this->_nBadge)) {
    $aPayload[self::APPLE_RESERVED_NAMESPACE]['badge'] = (int)$this->_nBadge;
    }

    if (is_array($this->_customProperties) && !empty($this->_customProperties)) {
    foreach($this->_customProperties as $sPropertyName => $mPropertyValue) {
    $aPayload[$sPropertyName] = $mPropertyValue;
    }
    }

    return json_encode($aPayload);
    }

    /*
    * 推送消息
    *
    **/
    public function send() {
    if (empty($this->_multi_hSocket)) {
    return false;
    }

    if (isset($this->_errors['connServer'])) {
    unset($this->_errors['connServer']);
    }

    if (empty($this->_deviceTokens)) {
    $this->_errors['send']['not_deviceTokens']['desc'] = 'No device tokens';
    $this->_errors['send']['not_deviceTokens']['time'] = date("Y-m-d H:i:s",time());
    return false;
    }

    if (empty($this->getPayload())) {
    $this->_errors['send']['not_message']['desc'] = 'No message to push';
    $this->_errors['send']['not_message']['time'] = date("Y-m-d H:i:s",time());
    return false;
    }

    $hArr = array();
    foreach ($this->_deviceTokens as $key=>$token) {
    $sendMessage = $this->getPayload();
    $this->_hSocket[$key] = curl_init();
    if (!defined(CURL_HTTP_VERSION_2_0)) {
    define(CURL_HTTP_VERSION_2_0, 3);
    }
    curl_setopt($this->_hSocket[$key], CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
    curl_setopt($this->_hSocket[$key], CURLOPT_SSLCERT, $this->_sProviderCertificateFile);
    curl_setopt($this->_hSocket[$key], CURLOPT_SSLCERTPASSWD, $this->_passPhrase);
    curl_setopt($this->_hSocket[$key], CURLOPT_SSLKEYTYPE, 'PEM');
    curl_setopt($this->_hSocket[$key], CURLOPT_TIMEOUT, $this->_timeout);
    curl_setopt($this->_hSocket[$key], CURLOPT_URL, $this->_appleServiceUrl . $token);
    curl_setopt($this->_hSocket[$key], CURLOPT_POSTFIELDS, $sendMessage);
    curl_setopt($this->_hSocket[$key], CURLOPT_HTTPHEADER, $this->_headers);
    curl_setopt($this->_hSocket[$key], CURLOPT_RETURNTRANSFER, 1);

    if (!$this->_hSocket[$key]) {
    $this->_errors['send']['cert'] = $this->_sProviderCertificateFile;
    $this->_errors['send']['desc'][$key] = "Unable to connect to '{$this->_appleServiceUrl}': $this->_hSocket[$key]";
    } else {
    array_push($hArr, $this->_hSocket[$key]);
    curl_multi_add_handle($this->_multi_hSocket,$this->_hSocket[$key]);
    }
    }
    if (empty($hArr)) {
    $this->_errors['send']['hSocket'] = "all socket link faild";
    $this->_errors['send']['time'] = date("Y-m-d H:i:s", time());

    return false;
    }

    $running = null;
    do {
    curl_multi_exec($this->_multi_hSocket, $running);
    } while ($running > 0);

    foreach ($hArr as $h) {
    $info = curl_getinfo($h);
    $response_errors = curl_multi_getcontent($h);
    if ($info['http_code'] !== 200) {
    $device_token = explode('/',$info['url']);
    $this->_writeErrorMessage(json_decode($response_errors, true), $info['http_code'], array_pop($device_token));
    }
    curl_multi_remove_handle($this->_multi_hSocket, $h);
    }

    $this->_deviceTokens = array();
    return true;
    }

    //获取发送过程中的错误
    public function getError() {
    return $this->_errors;
    }

    /*
    * 读取错误信息
    *@params res_errors 发送失败的具体信息
    *@params res_code 响应头返回的错误code
    *@params token 发送失败的device token
    **/
    protected function _writeErrorMessage($res_errors, $res_code, $token) {
    $errors = array(
    'reason' => $res_errors,
    'response_code' => $res_code,
    'token' => $token,
    'time' => date("Y-m-d H:i:s",time())
    );
    if (isset($this->_aErrorResponseMessages[$res_code])) {
    $errors['msg'] = $this->_aErrorResponseMessages[$res_code];
    }
    $this->_errors['send']['response'][] = $errors;

    $this->disconnect();
    sleep(0.5);
    $this->_resConnect();
    }

    //重新连接
    protected function _resConnect() {
    $conn_res = $this->connServer(array(
    'certificate_file' => $this->_sProviderCertificateFile,
    'apple_service_url' => $this->_appleServiceUrl,
    'pass_phrase' => $this->_passPhrase,
    'priority' => $this->_priority,
    'apns-topic' => $this->_apns_topic,
    'expiration' => $this->_expiration,
    'timeout' => $this->_timeout
    ));

    if (!$conn_res) {
    $this->_errors['connServer']['res_conn_nums'] = isset($this->_errors['connServer']['res_conn_nums']) ? intval($this->_errors['connServer']['res_conn_nums']) : 0;
    $this->_errors['connServer']['res_conn_nums'] += 1;
    if ($this->_errors['connServer']['res_conn_nums'] >=5) {
    return false;
    }

    return $this->_resConnect();
    }
    if (isset($this->_errors['connServer'])) {
    unset($this->_errors['connServer']);
    }
    return true;
    }

    }

    使用:

    include "pushMessage.php";

    $obj = new PushMessage();

    $params = array(

      'certificate_file' =>证书路径 ,// .pem 文件

      'apple_service_url' => // 生产环境: 'https://api.push.apple.com:443/3/device/'  沙盒环境 'https://api.development.push.apple.com:443/3/device/' 

      'pass_phrase' => //这个是证书的私钥密码
      'apns-topic'  => //apple唯一标识,这个不是随便的字符串,应该是申请苹果推送的时候有的吧,具体不清楚,这个要问负责人

      'expiration' =>0,  //推送的超时时间,如果超过了这个时间,就自动不推送了,单位为秒, 一般默认为0

      'priority'  => 10,//10:立即接收,5:屏幕关闭,在省电时才会接收到的。如果是屏幕亮着,是不会接收到消息的。而且这种消息是没有声音提示的,一般为10

      'timeout' => 30,//curl超时时间,单位为秒

    );

    //设置发送的文字还是声音或者角标什么的按自己的需求调用

    $obj->connServer($params);

    $obj->setMessage = "要发送的文字";

    $obj->setSound = "要发送的声音";

    $obj->addDeviceToken= array();//或者单个的device token, 要发送到的apple token

    $obj->setBadge = 1;//设置角标

    $obj->setCustomProperty = array('a'=>'b');//其他额外发送的参数

    $obj->getPayload(); //组织发送

    $obj->send();//发送

    $obj->getError();//获取发送过程中的错误

    $obj->disconnect();//断连

    注意:转载请注明出处,谢谢!

  • 相关阅读:
    [题解] LuoguP1587 [NOI2016]循环之美
    [题解] LuoguP3705 [SDOI2017]新生舞会
    [题解] LuoguP3702 [SDOI2017]序列计数
    [题解] LuoguP6476 [NOI Online 2 提高组]涂色游戏
    [题解] LuoguP4240 毒瘤之神的考验
    [题解] LuoguP6156简单题
    [题解] LuoguP6055 [RC-02] GCD
    [题解] LuoguP5050 【模板】多项式多点求值
    AtCoder Grand Contest 028题解
    Codeforces Round #421 (Div. 1) 题解
  • 原文地址:https://www.cnblogs.com/zhb-php/p/5786787.html
Copyright © 2020-2023  润新知