PHP 的 Web 推送库
WebPush 可用于将推送消息发送到端点,如 Web 推送协议中所述。
然后浏览器接收此推送消息,然后浏览器可以使用 Service Worker 和通知 API 创建通知。
PHP 8.1+ 和以下扩展:
对于较旧的 PHP 版本不提供支持和维护,但是您可以自由使用以下兼容版本:
v1.x
v2.x
v3.x-v5.x
v6.x
v7.x
v8.x
本自述文件仅与最新版本兼容。该库的每个版本都有一个 git 标签,可以在其中阅读相应的 README。
使用composer下载并安装库及其依赖项。
composer require minishlink/web-push
可以在此处找到使用web-push-php
html+JS 前端和 php 后端的完整示例:Minishlink/web-push-php-example
<?php
use Minishlink WebPush WebPush ;
use Minishlink WebPush Subscription ;
// store the client - side `PushSubscription` object ( calling ` . toJSON` on it) as - is and then create a WebPush Subscription from it
$ subscription = Subscription:: create ( json_decode ( $ clientSidePushSubscriptionJSON , true ));
// array of notifications
$ notifications = [
[
' subscription ' => $ subscription ,
' payload ' => ' {"message":"Hello World!"} ' ,
], [
// current PushSubscription format ( browsers might change this in the future)
' subscription ' => Subscription:: create ([
" endpoint " => " https://example.com/other/endpoint/of/another/vendor/abcdef... " ,
" keys " => [
' p256dh ' => ' (stringOf88Chars) ' ,
' auth ' => ' (stringOf24Chars) '
],
]),
' payload ' => ' {"message":"Hello World!"} ' ,
], [
// old Firefox PushSubscription format
' subscription ' => Subscription:: create ([
' endpoint ' => ' https://updates.push.services.mozilla.com/push/abc... ' , // Firefox 43 + ,
' publicKey ' => ' BPcMbnWQL5GOYX/5LKZXT6sLmHiMsJSiEvIFvfcDvX7IZ9qqtq68onpTPEYmyxSQNiH7UD/98AUcQ12kBoxz/0s= ' , // base 64 encoded , should be 88 chars
' authToken ' => ' CxVX6QsVToEGEcjfYPqXQw== ' , // base 64 encoded , should be 24 chars
]),
' payload ' => ' hello ! ' ,
], [
// old Chrome PushSubscription format
' subscription ' => Subscription:: create ([
' endpoint ' => ' https://fcm.googleapis.com/fcm/send/abcdef... ' ,
]),
' payload ' => null ,
], [
// old PushSubscription format
' subscription ' => Subscription:: create ([
' endpoint ' => ' https://example.com/other/endpoint/of/another/vendor/abcdef... ' ,
' publicKey ' => ' (stringOf88Chars) ' ,
' authToken ' => ' (stringOf24Chars) ' ,
' contentEncoding ' => ' aesgcm ' , // one of PushManager . supportedContentEncodings
]),
' payload ' => ' {"message":"test"} ' ,
]
];
$ webPush = new WebPush ();
// send multiple notifications with payload
foreach ( $ notifications as $ notification ) {
$ webPush -> queueNotification (
$ notification [ ' subscription ' ],
$ notification [ ' payload ' ] // optional ( defaults null)
);
}
/**
* Check sent results
* @ var MessageSentReport $ report
* /
foreach ( $ webPush -> flush () as $ report ) {
$ endpoint = $ report -> getRequest ()-> getUri ()-> __toString ();
if ( $ report -> isSuccess ()) {
echo " [v] Message sent successfully for subscription { $ endpoint } . " ;
} else {
echo " [x] Message failed to sent for subscription { $ endpoint } : { $ report -> getReason ()}" ;
}
}
/**
* send one notification and flush directly
* @ var MessageSentReport $ report
* /
$ report = $ webPush -> sendOneNotification (
$ notifications [ 0 ][ ' subscription ' ],
$ notifications [ 0 ][ ' payload ' ], // optional ( defaults null)
);
浏览器需要验证您的身份。名为 VAPID 的标准可以在所有浏览器上对您进行身份验证。您需要为您的服务器创建并提供公钥和私钥。这些密钥必须安全存储且不应更改。
您可以在实例化 WebPush 时指定身份验证详细信息。可以直接传递密钥(推荐),也可以加载 PEM 文件或其内容:
<?php
use Minishlink WebPush WebPush ;
$ endpoint = ' https://fcm.googleapis.com/fcm/send/abcdef... ' ; // Chrome
$ auth = [
' VAPID ' => [
' subject ' => ' mailto:[email protected] ' , // can be a mailto : or your website address
' publicKey ' => ' ~88 chars ' , // (recommended) uncompressed public key P - 256 encoded in Base64 - URL
' privateKey ' => ' ~44 chars ' , // (recommended) in fact the secret multiplier of the private key encoded in Base64 - URL
' pemFile ' => ' path/to/pem ' , // if you have a PEM file and can link to it on your filesystem
' pem ' => ' pemFileContent ' , // if you have a PEM file and want to hardcode its content
],
];
$ webPush = new WebPush ( $ auth );
$ webPush -> queueNotification (...);
为了生成以 Base64 编码的未压缩的公钥和密钥,请在 Linux bash 中输入以下内容:
$ openssl ecparam -genkey -name prime256v1 -out private_key.pem
$ openssl ec -in private_key.pem -pubout -outform DER | tail -c 65 | base64 | tr -d ' = ' | tr ' /+ ' ' _- ' >> public_key.txt
$ openssl ec -in private_key.pem -outform DER | tail -c +8 | head -c 32 | base64 | tr -d ' = ' | tr ' /+ ' ' _- ' >> private_key.txt
如果您无法访问 Linux bash,您可以打印createVapidKeys
函数的输出:
var_dump ( VAPID :: createVapidKeys ()); // store the keys afterwards
在客户端,不要忘记使用 VAPID 公钥作为applicationServerKey
进行订阅:(此处为urlBase64ToUint8Array
源)
serviceWorkerRegistration . pushManager . subscribe ( {
userVisibleOnly : true ,
applicationServerKey : urlBase64ToUint8Array ( vapidPublicKey )
} )
VAPID 标头使用 JSON Web 令牌 (JWT) 来验证您的身份。该令牌有效负载包括订阅中包含的端点的协议和主机名以及过期时间戳(通常在 12-24 小时之间),并且使用您的公钥和私钥对其进行签名。鉴于此,发送到同一推送服务的两个通知将使用相同的令牌,因此您可以使用以下方法将它们重复用于同一刷新会话以提高性能:
$ webPush -> setReuseVAPIDHeaders ( true );
每个通知都可以有特定的生存时间、紧急程度和主题。 WebPush 标准规定urgency
是可选的,但一些用户报告说,如果未指定紧急性,Safari 会抛出错误。这可能会在未来得到修复。您可以使用setDefaultOptions()
或在构造函数中更改默认选项:
<?php
use Minishlink WebPush WebPush ;
$ defaultOptions = [
' TTL ' => 300 , // defaults to 4 weeks
' urgency ' => ' normal ' , // protocol defaults to "normal" . ( very - low , low , normal , or high)
' topic ' => ' newEvent ' , // not defined by default . Max . 32 characters from the URL or filename - safe Base64 characters sets
' batchSize ' => 200 , // defaults to 1000
];
// for every notification
$ webPush = new WebPush ([], $ defaultOptions );
$ webPush -> setDefaultOptions ( $ defaultOptions );
// or for one notification
$ webPush -> sendOneNotification ( $ subscription , $ payload , [ ' TTL ' => 5000 ]);
生存时间(TTL,以秒为单位)是指在用户浏览器尚无法访问(例如未连接)的情况下推送服务(例如 Mozilla)保留推送消息的时间。您可能需要使用很长的时间来发送重要通知。默认 TTL 为 4 周。但是,如果您发送多个非必要通知,请将 TTL 设置为 0:仅当用户当前连接时才会发送推送通知。对于其他情况,如果您的用户有多个时区,您应该至少使用一天,如果没有,则几个小时就足够了。
紧急程度可以是“非常低”、“低”、“正常”或“高”。如果浏览器供应商实现了此功能,它将节省移动设备的电池寿命(参见协议)。
该字符串将使供应商仅向用户显示该主题的最后一个通知(参见协议)。
如果您一次发送数万条通知,则可能会由于 Guzzle 中端点的调用方式而导致内存溢出。为了解决这个问题,WebPush 批量发送通知。默认大小为 1000。根据您的服务器配置(内存),您可能需要减少此数字。在实例化 WebPush 或调用setDefaultOptions
时执行此操作。或者,如果您想针对特定刷新进行自定义,请将其作为参数提供: $webPush->flush($batchSize)
。
您可以查看浏览器供应商的服务器在遇到错误(推送订阅过期、参数错误...)时发回的内容。
sendOneNotification()
返回一个MessageSentReport
flush()
返回带有MessageSentReport
对象的Generator
。要循环结果,只需将其传递给foreach
即可。您还可以在调试时使用iterator_to_array
检查内容。 <?php
/** @var MinishlinkWebPush MessageSentReport $ report * /
foreach ( $ webPush -> flush () as $ report ) {
$ endpoint = $ report -> getEndpoint ();
if ( $ report -> isSuccess ()) {
echo " [v] Message sent successfully for subscription { $ endpoint } . " ;
} else {
echo " [x] Message failed to sent for subscription { $ endpoint } : { $ report -> getReason ()}" ;
// also available ( to get more info )
/** @var Psr Http Message RequestInterface $ requestToPushService * /
$ requestToPushService = $ report -> getRequest ();
/** @var Psr Http Message ResponseInterface $ responseOfPushService * /
$ responseOfPushService = $ report -> getResponse ();
/** @var string $ failReason */
$ failReason = $ report -> getReason ();
/** @var bool $ isTheEndpointWrongOrExpired */
$ isTheEndpointWrongOrExpired = $ report -> isSubscriptionExpired ();
}
}
请注意:您只能在Generator
对象上迭代一次。
自动推送文档中列出了 Firefox 错误。
有效负载由库加密。最大负载长度理论上为 4078 字节(或 ASCII 字符)。但出于兼容性原因(已存档),您的负载长度应小于 3052 字节。
库默认填充有效负载。这更安全,但会降低服务器和用户设备的性能。
当您加密特定长度的字符串时,无论您对初始字符串加密多少次,结果字符串将始终具有相同的长度。这可以使攻击者猜测有效负载的内容。为了避免这种情况,该库在初始有效负载中添加了一些空填充,以便加密过程的所有输入都具有相同的长度。这样,加密过程的所有输出也将具有相同的长度,攻击者将无法猜测有效负载的内容。
加密更多字节会在服务器上花费更多运行时间,并且解密也会减慢用户设备的速度。而且,发送和接收数据包会花费更多时间。对于数据计划有限的用户来说也不太友好。
您可以自定义自动填充,以更好地满足您的需求。
以下是一些设置的想法:
Encryption::MAX_COMPATIBILITY_PAYLOAD_LENGTH
(2820 字节),用于与 Android 版 Firefox 兼容(请参阅 #108)Encryption::MAX_PAYLOAD_LENGTH
(4078 字节)以实现最大安全性false
以获得最佳性能X
字节,请将其设置为X
以在安全性和性能之间取得最佳平衡。 <?php
use Minishlink WebPush WebPush ;
$ webPush = new WebPush ();
$ webPush -> setAutomaticPadding ( false ); // disable automatic padding
$ webPush -> setAutomaticPadding ( 512 ); // enable automatic padding to 512 bytes ( you should make sure that your payload is less than 512 bytes , or else an attacker could guess the content )
$ webPush -> setAutomaticPadding ( true ); // enable automatic padding to default maximum compatibility length
WebPush 使用 Guzzle。它将使用它找到的最合适的客户端,大多数时候它将是MultiCurl
,它允许并行发送多个通知。
您可以在实例化 WebPush 时自定义默认请求选项和超时:
<?php
use Minishlink WebPush WebPush ;
$ timeout = 20 ; // seconds
$ clientOptions = [
GuzzleHttp RequestOptions:: ALLOW_REDIRECTS => false ,
]; // see GuzzleHttp RequestOptions
$ webPush = new WebPush ([], [], $ timeout , $ clientOptions );
可用的有:
请随意添加您自己的!
有效负载根据 Web 推送消息加密标准进行加密,使用您可以通过遵循 Web 推送 API 规范获得的用户公钥和身份验证密钥。
在内部,WebPush 使用 WebToken 框架和 OpenSSL 来处理加密密钥生成和加密。
以下是一些想法:
defaultOptions
中设置或作为flush()
参数)flushPooled()
而不是flush()
。前者使用并发请求,加速进程并且通常使请求速度加倍。您的安装缺少一些证书。
php.ini
:在[curl]
之后,输入curl.cainfo = /path/to/cacert.pem
。您还可以强制使用无需对等验证的客户端。
确保需要 Composer 的自动加载器。
require __DIR__ . ' /path/to/vendor/autoload.php ' ;
确保数据库字段的大小足以满足您要存储的数据的长度(#233)。对于端点,用户报告 URL 长度不超过 500 个字符,但这可以发展,因此您可以将其设置为大多数浏览器的 2048 个字符限制。
请参阅第 58 期。
该服务已不存在。它已于 2019 年 5 月 29 日被 Google 的 Firebase Cloud Messaging (FCM) 取代。
此库不支持 Firebase 云消息传递 (FCM)。旧版 Chrome 订阅(2018 年之前和 VAPID)确实使用 Firebase Cloud Messaging (FCM) 的旧版 HTTP 协议,该协议自 2023 年起已弃用,并将于 2024 年 6 月停止工作。对此过时订阅的支持已删除。
请不要混淆,因为旧版 HTTP 协议和带有 VAPID 的 Web 推送使用相同的端点 URL:
https://fcm.googleapis.com/fcm/send
使用 VAPID 的 Web 推送将在此 URL 上保持可用。目前无需采取进一步行动。
浏览器供应商不允许在不创建通知的情况下使用 Push API 发送数据。使用一些替代 API,例如 WebSocket/WebTransport 或后台同步。
WebPush 适用于网络应用程序。您需要像 RMSPushNotificationsBundle (Symfony) 这样的东西。
该库的灵感来自 Node.js web-push-libs/web-push 库。
server.js
文件:它应该用此库替换为 PHP 服务器代码)麻省理工学院