Bibliothèque Web Push pour PHP
WebPush peut être utilisé pour envoyer des messages push aux points de terminaison comme décrit dans le protocole Web Push.
Ce message push est ensuite reçu par le navigateur, qui peut ensuite créer une notification à l'aide du service Worker et de l'API Notifications.
PHP 8.1+ et les extensions suivantes :
Il n'y a pas de support ni de maintenance pour les anciennes versions de PHP, mais vous êtes libre d'utiliser les versions compatibles suivantes :
v1.x
v2.x
v3.x-v5.x
v6.x
v7.x
v8.x
Ce README est uniquement compatible avec la dernière version. Chaque version de la bibliothèque possède une balise git où le README correspondant peut être lu.
Utilisez composer pour télécharger et installer la bibliothèque et ses dépendances.
composer require minishlink/web-push
Un exemple complet avec le frontend html+JS et le backend php utilisant web-push-php
peut être trouvé ici : 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)
);
Les navigateurs doivent vérifier votre identité. Un standard appelé VAPID peut vous authentifier pour tous les navigateurs. Vous devrez créer et fournir une clé publique et privée pour votre serveur. Ces clés doivent être conservées en toute sécurité et ne doivent pas changer.
Vous pouvez spécifier vos détails d'authentification lors de l'instanciation de WebPush. Les clés peuvent être passées directement (recommandé), ou vous pouvez charger un fichier PEM ou son contenu :
<?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 (...);
Afin de générer la clé publique et secrète non compressée, codée en Base64, saisissez ce qui suit dans votre bash Linux :
$ 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
Si vous ne pouvez pas accéder à un bash Linux, vous pouvez imprimer le résultat de la fonction createVapidKeys
:
var_dump ( VAPID :: createVapidKeys ()); // store the keys afterwards
Côté client, n'oubliez pas de vous abonner avec la clé publique VAPID comme applicationServerKey
: (source urlBase64ToUint8Array
ici)
serviceWorkerRegistration . pushManager . subscribe ( {
userVisibleOnly : true ,
applicationServerKey : urlBase64ToUint8Array ( vapidPublicKey )
} )
Les en-têtes VAPID utilisent un jeton Web JSON (JWT) pour vérifier votre identité. Cette charge utile du jeton comprend le protocole et le nom d'hôte du point de terminaison inclus dans l'abonnement ainsi qu'un horodatage d'expiration (généralement entre 12 et 24 heures), et elle est signée à l'aide de votre clé publique et privée. Compte tenu de cela, deux notifications envoyées au même service push utiliseront le même jeton, vous pourrez donc les réutiliser pour la même session de vidage afin d'améliorer les performances en utilisant :
$ webPush -> setReuseVAPIDHeaders ( true );
Chaque notification peut avoir une durée de vie, une urgence et un sujet spécifiques. La norme WebPush indique que urgency
est facultative, mais certains utilisateurs signalent que Safari génère des erreurs lorsqu'elle n'est pas spécifiée. Cela pourrait être corrigé à l’avenir. Vous pouvez modifier les options par défaut avec setDefaultOptions()
ou dans le constructeur :
<?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 ]);
La durée de vie (TTL, en secondes) est la durée pendant laquelle un message push est conservé par le service push (par exemple Mozilla) au cas où le navigateur de l'utilisateur n'est pas encore accessible (par exemple s'il n'est pas connecté). Vous souhaiterez peut-être utiliser une période très longue pour les notifications importantes. La durée de vie par défaut est de 4 semaines. Cependant, si vous envoyez plusieurs notifications non essentielles, définissez un TTL de 0 : la notification push ne sera envoyée que si l'utilisateur est actuellement connecté. Dans les autres cas, vous devez utiliser au minimum une journée si vos utilisateurs ont plusieurs fuseaux horaires, et s'ils n'en ont pas, plusieurs heures suffiront.
L'urgence peut être « très faible », « faible », « normale » ou « élevée ». Si l'éditeur du navigateur a implémenté cette fonctionnalité, cela permettra d'économiser la batterie des appareils mobiles (cf. protocole).
Cette chaîne fera que le vendeur n'affichera à l'utilisateur que la dernière notification de ce sujet (cf. protocole).
Si vous envoyez des dizaines de milliers de notifications à la fois, vous risquez d'avoir des débordements de mémoire en raison de la façon dont les points de terminaison sont appelés dans Guzzle. Afin de résoudre ce problème, WebPush envoie des notifications par lots. La taille par défaut est 1 000. En fonction de la configuration de votre serveur (mémoire), vous souhaiterez peut-être diminuer ce nombre. Faites-le lors de l'instanciation de WebPush ou de l'appel de setDefaultOptions
. Ou, si vous souhaitez personnaliser cela pour un vidage spécifique, donnez-le en paramètre : $webPush->flush($batchSize)
.
Vous pouvez voir ce que le serveur de l'éditeur du navigateur renvoie en cas d'erreur (expiration de l'abonnement push, paramètres erronés...).
sendOneNotification()
renvoie un MessageSentReport
flush()
renvoie un Generator
avec des objets MessageSentReport
. Pour parcourir les résultats, transmettez-les simplement dans foreach
. Vous pouvez également utiliser iterator_to_array
pour vérifier le contenu lors du débogage. <?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 ();
}
}
VEUILLEZ NOTER : Vous ne pouvez parcourir qu'une seule fois l'objet Generator
.
Les erreurs de Firefox sont répertoriées dans la documentation Autopush.
Les charges utiles sont chiffrées par la bibliothèque. La longueur maximale de la charge utile est théoriquement de 4 078 octets (ou caractères ASCII). Pour des raisons de compatibilité (archivées), votre charge utile doit toutefois faire moins de 3 052 octets.
La bibliothèque complète la charge utile par défaut. C'est plus sécurisé mais cela diminue les performances de votre serveur et de l'appareil de votre utilisateur.
Lorsque vous chiffrez une chaîne d'une certaine longueur, la chaîne résultante aura toujours la même longueur, quel que soit le nombre de fois que vous chiffrez la chaîne initiale. Cela peut amener les attaquants à deviner le contenu de la charge utile. Afin d'éviter cela, cette bibliothèque ajoute un remplissage nul à la charge utile initiale, de sorte que toutes les entrées du processus de chiffrement aient la même longueur. De cette façon, tous les résultats du processus de chiffrement auront également la même longueur et les attaquants ne pourront pas deviner le contenu de votre charge utile.
Le chiffrement de plus d'octets prend plus de temps d'exécution sur votre serveur et ralentit également l'appareil de l'utilisateur lors du décryptage. De plus, l’envoi et la réception du paquet prendront plus de temps. Ce n’est pas non plus très convivial pour les utilisateurs disposant de forfaits de données limités.
Vous pouvez personnaliser le remplissage automatique afin de mieux répondre à vos besoins.
Voici quelques idées de réglages :
Encryption::MAX_COMPATIBILITY_PAYLOAD_LENGTH
(2820 octets) à des fins de compatibilité avec Firefox pour Android (Voir #108)Encryption::MAX_PAYLOAD_LENGTH
(4078 octets) pour une sécurité maximalefalse
pour des performances maximalesX
octets, définissez-les sur X
pour obtenir le meilleur équilibre entre sécurité et performances. <?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 utilise Guzzle. Il utilisera le client le plus approprié qu'il trouvera, et la plupart du temps ce sera MultiCurl
, qui permet d'envoyer plusieurs notifications en parallèle.
Vous pouvez personnaliser les options de requête par défaut et le délai d'expiration lors de l'instanciation de WebPush :
<?php
use Minishlink WebPush WebPush ;
$ timeout = 20 ; // seconds
$ clientOptions = [
GuzzleHttp RequestOptions:: ALLOW_REDIRECTS => false ,
]; // see GuzzleHttp RequestOptions
$ webPush = new WebPush ([], [], $ timeout , $ clientOptions );
Les éléments suivants sont disponibles :
N'hésitez pas à ajouter les vôtres !
La charge utile est chiffrée conformément à la norme Message Encryption for Web Push, à l'aide de la clé publique de l'utilisateur et du secret d'authentification que vous pouvez obtenir en suivant la spécification de l'API Web Push.
En interne, WebPush utilise le framework WebToken et OpenSSL pour gérer la génération et le chiffrement des clés de chiffrement.
Voici quelques idées :
defaultOptions
ou en tant que paramètre pour flush()
)flushPooled()
au lieu de flush()
. Le premier utilise des requêtes simultanées, accélérant le processus et doublant souvent la vitesse des requêtes.Votre installation ne dispose pas de certains certificats.
php.ini
: après [curl]
, tapez curl.cainfo = /path/to/cacert.pem
.Vous pouvez également forcer l'utilisation d'un client sans vérification par les pairs.
Assurez-vous d'exiger le chargeur automatique de Composer.
require __DIR__ . ' /path/to/vendor/autoload.php ' ;
Assurez-vous d'avoir des champs de base de données suffisamment grands pour la longueur des données que vous stockez (#233). Pour le point de terminaison, les utilisateurs ont signalé que la longueur de l'URL ne dépasse pas 500 caractères, mais cela peut évoluer afin que vous puissiez la définir sur la limite de 2 048 caractères de la plupart des navigateurs.
Voir le numéro 58.
Ce service n'existe plus. Il a été remplacé par Firebase Cloud Messaging (FCM) de Google le 29 mai 2019.
Cette bibliothèque ne prend pas en charge Firebase Cloud Messaging (FCM). Les anciens abonnements Chrome (avant 2018 et VAPID) utilisent le protocole HTTP hérité de Firebase Cloud Messaging (FCM), obsolète depuis 2023 et cessera de fonctionner en juin 2024. La prise en charge de cet abonnement obsolète est supprimée.
Ne vous y trompez pas, car le protocole HTTP hérité et Web Push avec VAPID utilisent la même URL de point de terminaison :
https://fcm.googleapis.com/fcm/send
Web Push avec VAPID restera disponible à cette URL. Aucune autre mesure n’est actuellement requise.
Les fournisseurs de navigateurs n'autorisent pas l'envoi de données à l'aide de l'API Push sans créer de notification. Utilisez des API alternatives telles que WebSocket/WebTransport ou Background Synchronization.
WebPush est destiné aux applications Web. Vous avez besoin de quelque chose comme RMSPushNotificationsBundle (Symfony).
Cette bibliothèque a été inspirée de la bibliothèque web-push-libs/web-push de Node.js.
server.js
: il doit être remplacé par le code de votre serveur PHP avec cette bibliothèque)MIT