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 伺服器程式碼)麻省理工學院