php APNS苹果推送通知服务的服务器端公共类

APNS(英文全称:Apple Push Notification Service),中文翻译为:苹果推送通知服务,[1] 该技术由苹果公司提供的APNS服务,下面来看一个比较完善的苹果推送通知服务的php服务器端公共类.

前段时间开发的一套APNS推送平台效率很差,通过再次深入研究苹果的消息推送服务,总结了不少经验,同时也参考了网上一些技术blog的博文,重新完善了此前写过的一个PHP类,代码如下:

  1. <?php
  2. /**
  3. * ApplePush 苹果消息推送公共类
  4. */
  5. class ApplePush
  6. {
  7. const STATUS_CODE_INTERNAL_ERROR = 999;
  8. const ERROR_RESPONSE_SIZE = 6;
  9. const ERROR_RESPONSE_COMMAND = 8;
  10. protected $_errorResponseMessages = array(
  11. 0 => 'No errors encountered',
  12. 1 => 'Processing error',
  13. 2 => 'Missing device token',
  14. 3 => 'Missing topic',
  15. 4 => 'Missing payload',
  16. 5 => 'Invalid token size',
  17. 6 => 'Invalid topic size',
  18. 7 => 'Invalid payload size',
  19. 8 => 'Invalid token',
  20. self::STATUS_CODE_INTERNAL_ERROR => 'Internal error'
  21. );
  22. /**
  23. * APNS server url
  24. *
  25. * @var string
  26. */
  27. protected $apns_url = 'ssl://gateway.push.apple.com:2195'; //沙盒地址:ssl://gateway.sandbox.push.apple.com:2195
  28. /**
  29. * 推送数据
  30. *
  31. * @var string
  32. */
  33. private $payload_json;
  34. /**
  35. * 数据流对象
  36. *
  37. * @var mixed
  38. */
  39. private $fp;
  40. /**
  41. * 设置APNS地址
  42. *
  43. * @param string $url
  44. */
  45. public function setApnsUrl($url)
  46. {
  47. if (emptyempty($url)) {
  48. return false;
  49. } else {
  50. $this->apns_url = $url;
  51. }
  52. return true;
  53. }
  54. /**
  55. * 设置推送的消息
  56. *
  57. * @param string $body
  58. */
  59. public function setBody($body)
  60. {
  61. if (emptyempty($body)) {
  62. return false;
  63. } else {
  64. $this->payload_json = json_encode($body);
  65. }
  66. return true;
  67. }
  68. /**
  69. * Open 打开APNS服务器连接
  70. *
  71. * @param string $pem 证书
  72. * @param string $passphrase 证书密钥
  73. */
  74. public function open($pem, $passphrase)
  75. {
  76. if (emptyempty($pem)) {
  77. return false;
  78. }
  79. if (emptyempty($passphrase)) {
  80. return false;
  81. }
  82. $ctx = stream_context_create();
  83. stream_context_set_option($ctx, 'ssl', 'local_cert', $pem);
  84. stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
  85. $fp = stream_socket_client($this->apns_url, $err, $errstr, 60, STREAM_CLIENT_CONNECT, $ctx);
  86. if (!$fp) {
  87. return false;
  88. }
  89. $this->fp = $fp;
  90. return true;
  91. }
  92. /**
  93. * Send 推送
  94. *
  95. * @param string $token
  96. */
  97. public function send($token, $id)
  98. {
  99. $msg = pack('CNNnH*', 1, $id, 864000, 32, $token) . pack('n', strlen($this->payload_json)) . $this->payload_json;
  100. // Send it to the server
  101. $result = fwrite($this->fp, $msg, strlen($msg));
  102. return $result;
  103. }
  104. public function readErrMsg()
  105. {
  106. $errInfo = @fread($this->fp, self::ERROR_RESPONSE_SIZE);
  107. if ($errInfo === false || strlen($errInfo) != self::ERROR_RESPONSE_SIZE) { //开源软件:phpfensi.com
  108. return true;
  109. }
  110. $errInfo = $this->parseErrMsg($errInfo);
  111. if (!is_array($errInfo) || emptyempty($errInfo)) {
  112. return true;
  113. }
  114. if (!isset($errInfo['command'], $errInfo['statusCode'], $errInfo['identifier'])) {
  115. return true;
  116. }
  117. if ($errInfo['command'] != self::ERROR_RESPONSE_COMMAND) {
  118. return true;
  119. }
  120. $errInfo['timeline'] = time();
  121. $errInfo['statusMessage'] = 'None (unknown)';
  122. $errInfo['errorIdentifier'] = $errInfo['identifier'];
  123. if (isset($this->_aErrorResponseMessages[$errInfo['statusCode']])) {
  124. $errInfo['statusMessage'] = $this->_errorResponseMessages[$errInfo['statusCode']];
  125. }
  126. return $errInfo;
  127. }
  128. protected function parseErrMsg($errorMessage)
  129. {
  130. return unpack('Ccommand/CstatusCode/Nidentifier', $errorMessage);
  131. }
  132. /**
  133. * Close APNS server 关闭APNS服务器连接
  134. *
  135. */
  136. public function close()
  137. {
  138. // Close the connection to the server
  139. fclose($this->fp);
  140. return true;
  141. }
  142. }
  143. ?>