本文目录导读:

我来详细介绍PHP项目对接推送服务的方法,涵盖常见推送服务类型和具体实现方案。
常见推送服务类型
第三方推送服务
- 极光推送 - 国内主流
- 个推 - 覆盖广
- 友盟+ - 阿里旗下
- 信鸽/腾讯移动推送 - 腾讯系
平台原生推送
- APNs - Apple Push Notification service
- FCM - Firebase Cloud Messaging
实时通信推送
- WebSocket
- SSE (Server-Sent Events)
- 长轮询
PHP对接实现方案
使用第三方推送服务SDK
极光推送示例
// 1. 安装SDK
composer require jpush/jpush
// 2. 基础推送代码
<?php
require_once 'vendor/autoload.php';
use JPush\Client;
class PushService {
private $client;
public function __construct() {
$appKey = 'your_app_key';
$masterSecret = 'your_master_secret';
$this->client = new Client($appKey, $masterSecret);
}
// 推送通知
public function pushNotification($title, $content, $audience = 'all') {
try {
$push = $this->client->push();
$push->setPlatform('all')
->addAllAudience()
->iosNotification($content, [
'title' => $title,
'sound' => 'default',
'badge' => '+1',
'extras' => ['key' => 'value']
])
->androidNotification($title, [
'title' => $title,
'builder_id' => 1,
'extras' => ['key' => 'value']
])
->message($content, [
'title' => $title,
'content_type' => 'text',
'extras' => ['key' => 'value']
]);
$response = $push->send();
return $response;
} catch (\JPush\Exceptions\APIConnectionException $e) {
// 网络连接错误
error_log('Push connection error: ' . $e->getMessage());
return false;
} catch (\JPush\Exceptions\APIRequestException $e) {
// API请求错误
error_log('Push API error: ' . $e->getMessage());
return false;
}
}
// 按标签推送
public function pushByTags($tags, $title, $content) {
$push = $this->client->push();
$push->setPlatform('all')
->addTag($tags)
->iosNotification($content, ['title' => $title])
->androidNotification($title, ['title' => $title]);
return $push->send();
}
// 推送统计
public function getPushReport($pushId) {
try {
$report = $this->client->report();
return $report->getReceived($pushId);
} catch (\Exception $e) {
return false;
}
}
}
// 使用示例
$push = new PushService();
$result = $push->pushNotification('新消息', '您有一条新通知');
个推推送示例
<?php
require_once 'vendor/autoload.php';
use Getui\IGtPush;
use Getui\IGtNotification;
use Getui\IGtSingleMessage;
class GetuiPushService {
private $push;
private $appId;
private $appKey;
private $masterSecret;
public function __construct() {
$this->appId = 'your_app_id';
$this->appKey = 'your_app_key';
$this->masterSecret = 'your_master_secret';
$host = 'https://api.getui.com';
$this->push = new IGtPush($host, $this->appKey, $this->masterSecret);
}
// 单推
public function pushToSingle($cid, $title, $content) {
$template = $this->createNotificationTemplate($title, $content);
$message = new IGtSingleMessage();
$message->setData($template);
$message->setOffline(true);
$message->setOfflineExpireTime(24 * 3600 * 1000); // 离线有效期
return $this->push->pushMessageToSingle($message, $cid);
}
// 群推
public function pushToList($cids, $title, $content) {
$template = $this->createNotificationTemplate($title, $content);
$message = new IGtListMessage();
$message->setData($template);
$message->setOffline(true);
$contentId = $this->push->getContentId($message, 'toList_task');
return $this->push->pushMessageToList($contentId, $cids);
}
private function createNotificationTemplate($title, $content) {
$template = new IGtNotification();
$template->setAppId($this->appId);
$template->setAppKey($this->appKey);
$template->setTitle($title);
$template->setText($content);
$template->setLogo('push.png');
$template->setLogoUrl('');
$template->setIsRing(true);
$template->setIsVibrate(true);
$template->setIsClearable(true);
$template->setTransmissionType(2);
$template->setTransmissionContent(json_encode([
'type' => 'message',
'data' => $content
]));
return $template;
}
}
使用cURL直接调用API
<?php
class PushApiClient {
private $apiUrl;
private $appKey;
private $appSecret;
public function __construct($config) {
$this->apiUrl = $config['api_url'];
$this->appKey = $config['app_key'];
$this->appSecret = $config['app_secret'];
}
// 通用API请求
private function request($endpoint, $data = []) {
$url = $this->apiUrl . $endpoint;
$headers = [
'Content-Type: application/json',
'Authorization: ' . $this->getAuthToken()
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode == 200) {
return json_decode($response, true);
}
return false;
}
private function getAuthToken() {
// 实现token获取逻辑
return base64_encode($this->appKey . ':' . $this->appSecret);
}
// 发送推送
public function sendPush($title, $content, $deviceTokens = []) {
$data = [
'title' => $title,
'content' => $content,
'device_tokens' => $deviceTokens,
'platform' => 'all'
];
return $this->request('/push/send', $data);
}
}
WebSocket推送
<?php
// 使用 Ratchet 库实现 WebSocket 服务器
// 安装: composer require cboden/ratchet
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class PushWebSocket implements MessageComponentInterface {
protected $clients;
protected $userConnections; // 用户ID -> 连接映射
public function __construct() {
$this->clients = new \SplObjectStorage;
$this->userConnections = [];
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "New connection: {$conn->resourceId}\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
$data = json_decode($msg, true);
switch ($data['type']) {
case 'auth':
// 用户认证,绑定用户ID
$userId = $data['user_id'];
$this->userConnections[$userId] = $from;
$from->userId = $userId;
break;
case 'subscribe':
// 订阅特定频道
$channel = $data['channel'];
$from->channels[] = $channel;
break;
}
}
// 向特定用户推送
public function pushToUser($userId, $data) {
if (isset($this->userConnections[$userId])) {
$conn = $this->userConnections[$userId];
$conn->send(json_encode($data));
}
}
// 广播消息
public function broadcast($data) {
foreach ($this->clients as $client) {
$client->send(json_encode($data));
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
// 清理用户连接
if (isset($conn->userId)) {
unset($this->userConnections[$conn->userId]);
}
echo "Connection {$conn->resourceId} disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "Error: {$e->getMessage()}\n";
$conn->close();
}
}
// WebSocket 服务启动脚本
// server.php
require 'vendor/autoload.php';
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use React\EventLoop\Factory;
$loop = Factory::create();
$pusher = new PushWebSocket();
// WebSocket服务器
$webSock = new React\Socket\Server('0.0.0.0:8080', $loop);
$webServer = new IoServer(
new HttpServer(
new WsServer(
$pusher
)
),
$webSock
);
// HTTP API服务器(用于内部推送调用)
$httpServer = new React\Http\Server($loop, function ($request, $response) use ($pusher) {
$body = $request->getBody();
$data = json_decode($body, true);
if ($data && isset($data['user_id'])) {
$pusher->pushToUser($data['user_id'], $data['message']);
$response->writeHead(200, ['Content-Type' => 'application/json']);
$response->end(json_encode(['status' => 'success']));
} else {
$response->writeHead(400);
$response->end('Bad Request');
}
});
$httpServer->listen('127.0.0.1:8081');
echo "WebSocket server started on port 8080\n";
echo "Internal API server started on port 8081\n";
$loop->run();
// PHP客户端调用推送
// push_client.php
function sendInternalPush($userId, $message) {
$ch = curl_init('http://127.0.0.1:8081');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'user_id' => $userId,
'message' => $message
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_exec($ch);
curl_close($ch);
}
SSE (Server-Sent Events) 推送
<?php
// server_sse.php - SSE推送服务端
class SSEServer {
public function handle() {
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('Access-Control-Allow-Origin: *');
// 获取用户ID(通过token验证)
$userId = $this->authenticate();
// 创建数据库连接检查新消息
$db = new PDO('mysql:host=localhost;dbname=push', 'user', 'pass');
$lastCheck = time();
while (true) {
// 检查是否有新消息
$stmt = $db->prepare("SELECT * FROM messages WHERE user_id = ? AND created_at > ?");
$stmt->execute([$userId, date('Y-m-d H:i:s', $lastCheck)]);
$messages = $stmt->fetchAll();
foreach ($messages as $msg) {
// 发送SSE事件
echo "event: message\n";
echo "id: {$msg['id']}\n";
echo "data: " . json_encode($msg) . "\n\n";
ob_flush();
flush();
}
$lastCheck = time();
// 发送心跳
echo ": heartbeat\n\n";
ob_flush();
flush();
// 检查客户端是否断开
if (connection_aborted()) {
break;
}
sleep(1); // 间隔1秒检查
}
}
private function authenticate() {
// 实现身份验证逻辑
return 1; // 返回用户ID
}
}
// index.php - 入口
$sse = new SSEServer();
$sse->handle();
// 客户端JavaScript
/*
const eventSource = new EventSource('/sse/server_sse.php');
eventSource.addEventListener('message', function(e) {
const data = JSON.parse(e.data);
console.log('New message:', data);
});
eventSource.onerror = function(e) {
console.error('SSE error:', e);
eventSource.close();
};
*/
推送服务管理类
<?php
class PushManager {
private $config;
private $providers = [];
public function __construct($config) {
$this->config = $config;
$this->initProviders();
}
private function initProviders() {
// 初始化不同推送服务商
if (isset($this->config['jiguang'])) {
$this->providers['jiguang'] = new JiguangPush($this->config['jiguang']);
}
if (isset($this->config['getui'])) {
$this->providers['getui'] = new GetuiPush($this->config['getui']);
}
if (isset($this->config['websocket'])) {
$this->providers['websocket'] = new WebSocketPush($this->config['websocket']);
}
}
// 智能推送:根据用户设备选择最优通道
public function smartPush($userId, $title, $content, $options = []) {
$results = [];
// 获取用户设备信息
$devices = $this->getUserDevices($userId);
foreach ($devices as $device) {
switch ($device['platform']) {
case 'ios':
// iOS设备使用APNs
$results[] = $this->pushApns($device['token'], $title, $content);
break;
case 'android':
// Android设备使用FCM或第三方
$results[] = $this->pushFcm($device['token'], $title, $content);
break;
case 'web':
// Web端使用WebSocket或SSE
$results[] = $this->pushWebSocket($userId, $title, $content);
break;
}
}
// 记录推送日志
$this->logPush($userId, $title, $content, $results);
return $results;
}
// 消息队列处理(异步推送)
public function pushToQueue($userId, $title, $content) {
// 使用Redis队列
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$pushData = [
'user_id' => $userId,
'title' => $title,
'content' => $content,
'created_at' => time()
];
$redis->lPush('push_queue', json_encode($pushData));
return true;
}
// 处理队列中的推送
public function processQueue() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
while ($data = $redis->rPop('push_queue')) {
$pushData = json_decode($data, true);
// 处理推送
$this->smartPush(
$pushData['user_id'],
$pushData['title'],
$pushData['content']
);
// 避免CPU占用过高
usleep(100000); // 0.1秒
}
}
// 推送失败重试机制
public function retryFailedPush($pushId) {
$failedPushes = $this->getFailedPushes();
foreach ($failedPushes as $push) {
if ($push['retry_count'] < 3) {
$result = $this->smartPush(
$push['user_id'],
$push['title'],
$push['content']
);
if ($result) {
$this->markPushSuccess($push['id']);
} else {
$this->incrementRetryCount($push['id']);
}
}
}
}
private function getUserDevices($userId) {
// 从数据库获取用户设备信息
$db = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
$stmt = $db->prepare("SELECT * FROM user_devices WHERE user_id = ?");
$stmt->execute([$userId]);
return $stmt->fetchAll();
}
private function logPush($userId, $title, $content, $results) {
// 记录推送日志到数据库
$db = new PDO('mysql:host=localhost;dbname=your_db', 'user', 'pass');
$stmt = $db->prepare("
INSERT INTO push_logs (user_id, title, content, results, created_at)
VALUES (?, ?, ?, ?, NOW())
");
$stmt->execute([$userId, $title, $content, json_encode($results)]);
}
}
最佳实践建议
错误处理
try {
$result = $pushService->pushNotification($title, $content);
} catch (Exception $e) {
// 记录错误日志
Log::error('Push failed: ' . $e->getMessage());
// 放入重试队列
Queue::push('RetryPush', [
'title' => $title,
'content' => $content,
'error' => $e->getMessage()
]);
}
速度控制
// 使用令牌桶算法控制推送速率
class RateLimiter {
private $redis;
private $maxTokens;
private $refillRate;
public function __construct($maxTokens = 100, $refillRate = 10) {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
$this->maxTokens = $maxTokens;
$this->refillRate = $refillRate;
}
public function acquire($key) {
$tokens = $this->redis->get("rate_limit:$key");
if ($tokens === false) {
$this->redis->set("rate_limit:$key", $this->maxTokens - 1);
$this->redis->expire("rate_limit:$key", 1);
return true;
}
if ($tokens > 0) {
$this->redis->decr("rate_limit:$key");
return true;
}
return false;
}
}
推送模板管理
class PushTemplate {
private $templates = [];
public function __construct() {
$this->loadTemplates();
}
public function render($templateName, $data = []) {
if (!isset($this->templates[$templateName])) {
throw new Exception("Template not found: $templateName");
}
$template = $this->templates[$templateName];
// 替换变量
foreach ($data as $key => $value) {
$template['title'] = str_replace("{{$key}}", $value, $template['title']);
$template['content'] = str_replace("{{$key}}", $value, $template['content']);
}
return $template;
}
private function loadTemplates() {
// 从数据库或配置文件加载模板
$this->templates = [
'order_notify' => [
'title' => '订单通知',
'content' => '您的订单{{order_no}}状态已更新为{{status}}'
],
'new_message' => [
'title' => '新消息',
'content' => '您收到来自{{sender}}的新消息'
]
];
}
}
配置文件示例
// config/push.php
return [
'default' => 'jiguang',
'providers' => [
'jiguang' => [
'app_key' => env('JPUSH_APP_KEY'),
'master_secret' => env('JPUSH_MASTER_SECRET'),
'apns_production' => env('APP_ENV') === 'production'
],
'getui' => [
'app_id' => env('GETUI_APP_ID'),
'app_key' => env('GETUI_APP_KEY'),
'master_secret' => env('GETUI_MASTER_SECRET')
],
'apns' => [
'certificate' => storage_path('apns-cert.pem'),
'passphrase' => env('APNS_PASSPHRASE'),
'sandbox' => env('APP_ENV') !== 'production'
],
'fcm' => [
'server_key' => env('FCM_SERVER_KEY'),
'sender_id' => env('FCM_SENDER_ID')
]
],
'rate_limit' => [
'max_per_second' => 100,
'max_per_minute' => 5000
],
'retry' => [
'max_attempts' => 3,
'delay' => 300 // 5分钟
]
];
选择推送方案时,建议根据项目规模、用户量、实时性要求和预算来综合考虑,对于大多数PHP项目,使用第三方推送服务是最快速可靠的方案。